bug.h
Go to the documentation of this file.
1 //
2 // bug.h
3 //
4 // Copyright (C) 1996 Limit Point Systems, Inc.
5 //
6 // Author: Curtis Janssen <cljanss@limitpt.com>
7 // Maintainer: LPS
8 //
9 // This file is part of the SC Toolkit.
10 //
11 // The SC Toolkit is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU Library General Public License as published by
13 // the Free Software Foundation; either version 2, or (at your option)
14 // any later version.
15 //
16 // The SC Toolkit is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU Library General Public License for more details.
20 //
21 // You should have received a copy of the GNU Library General Public License
22 // along with the SC Toolkit; see the file COPYING.LIB. If not, write to
23 // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 //
25 // The U.S. Government is granted a limited license as per AL 91-7.
26 //
27 
28 #ifndef MPQC4_SRC_MPQC_UTIL_MISC_BUG_H_
29 #define MPQC4_SRC_MPQC_UTIL_MISC_BUG_H_
30 
31 #include <cassert>
32 #include <memory>
33 #include <string>
34 #include <unordered_map>
35 #include <vector>
36 
37 #include <pthread.h>
38 #if defined(__APPLE__) && defined(__x86_64__)
39 #include <mach/mach.h>
40 #include <stdexcept>
41 #endif
42 
43 namespace TiledArray {
44 namespace detail {
45 
52  public:
53  // x86 debugging registers are described in see
54  // https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-system-programming-manual-325384.html
55  enum DebugRegister { kDR0 = 0, kDR1 = 1, kDR2 = 2, kDR3 = 3 };
56 
57  enum Size {
58  kByte = 0, // 1 byte - 00
59  kHalfWord = 1, // 2 bytes - 01
60  kWord = 3, // 4 bytes - 11
61  // kDoubleWord = 2, // 8 bytes - 10 NOT SUPPORTED BY
62  // SOME CHIPS!
63  kSizeMask = 3 // mask 11
64  };
65 
66  enum BreakState {
67  kDisabled = 0, // disabled - 00
68  kEnabledLocally = 1, // task local - 01
69  kEnabledGlobally = 2, // global - 10
70  kBreakStateMask = 3 // mask 11
71  };
72 
73  enum Condition {
74  kWhenExecuted = 0, // on execution - 00
75  kWhenWritten = 1, // on write - 01
76  kWhenWrittenOrRead = 3, // on read or write - 11
77  kConditionMask = 3 // mask 11
78  };
79 
81  class Pool {
82  public:
83  static constexpr const size_t nwatchpoints_per_thread = 4;
84 
85  ~Pool() = default;
86 
91  static void initialize_instance(
92  const std::vector<const pthread_t *> &threads) {
93  get_instance() = std::shared_ptr<Pool>(new Pool(threads));
94  }
95 
98  static std::shared_ptr<Pool> instance() {
99  auto result = get_instance();
100  assert(result &&
101  "Pool::instance() called but Pool::initialize_instance() had not "
102  "been called");
103  return result;
104  }
105 
110  Pool &set(void *addr, Size size, Condition cond, const pthread_t *thread) {
111  const auto it = pool_.find(thread);
112  assert(it != pool_.end());
113  // make sure there is no watchpoint for this address already
114  for (auto &watchpt_ptr : it->second) {
115  if (watchpt_ptr && watchpt_ptr->address() == addr) return *this;
116  }
117  // now create a watchpoint
118  for (auto dr = 0; dr != nwatchpoints_per_thread; ++dr) {
119  auto &watchpt_ptr = it->second[dr];
120  if (!watchpt_ptr) {
121  watchpt_ptr = std::make_shared<MemoryWatchpoint_x86_64>(
122  addr, size, cond, static_cast<DebugRegister>(dr), thread);
123  return *this;
124  }
125  }
126  return *this;
127  }
128 
133  MemoryWatchpoint_x86_64 *find(void *addr, const pthread_t *thread) {
134  const auto it = pool_.find(thread);
135  assert(it != pool_.end());
136  for (auto &watchpt_ptr : it->second) {
137  if (watchpt_ptr && watchpt_ptr->address() == addr)
138  return watchpt_ptr.get();
139  }
140  return nullptr;
141  }
142 
144  Pool &clear(void *addr, const pthread_t *thread) {
145  const auto it = pool_.find(thread);
146  assert(it != pool_.end());
147  for (auto &watchpt_ptr : it->second) {
148  if (watchpt_ptr && watchpt_ptr->address() == addr) {
149  watchpt_ptr.reset();
150  return *this;
151  }
152  }
153  return *this;
154  }
155 
156  private:
157  std::unordered_map<const pthread_t *,
158  std::vector<std::shared_ptr<MemoryWatchpoint_x86_64>>>
159  pool_;
160 
167  explicit Pool(const std::vector<const pthread_t *> &threads) {
168  for (const auto &thread : threads) {
169  assert(thread != nullptr);
170  pool_[thread].resize(nwatchpoints_per_thread);
171  }
172  }
173 
174  static std::shared_ptr<Pool> &get_instance() {
175  static std::shared_ptr<Pool> instance_;
176  return instance_;
177  }
178  };
179 
190  DebugRegister dr, const pthread_t *thread)
191  : addr_(addr), size_(size), cond_(cond), dr_(dr), thread_(thread) {
192  init(true);
193  }
194 
195  ~MemoryWatchpoint_x86_64() { init(false); }
196 
197  void *address() const { return addr_; }
198  Size size() const { return size_; }
199  Condition condition() const { return cond_; }
200  DebugRegister debug_register() const { return dr_; }
201 
202  private:
203  void *addr_;
204  Size size_;
205  Condition cond_;
206  DebugRegister dr_;
207  const pthread_t *thread_;
208 
209  inline uint64_t MakeFlags(DebugRegister reg, BreakState state, Condition cond,
210  Size size) {
211  // N.B. each register takes 2 bits in DR7
212  return (state | cond << 16 | size << 24) << (2 * reg);
213  }
214 
215  inline uint64_t MakeMask(DebugRegister reg) {
216  return MakeFlags(reg, kBreakStateMask, kConditionMask, kSizeMask);
217  }
218 
219  friend class MemoryWatchPool;
220 
221  void init(bool create) {
222 #if defined(__APPLE__) && defined(__x86_64__)
223  x86_debug_state dr;
224  mach_msg_type_number_t dr_count = x86_DEBUG_STATE_COUNT;
225 
226  mach_port_t target_mach_thread = pthread_mach_thread_np(*thread_);
227 
228  kern_return_t rc =
229  thread_get_state(target_mach_thread, x86_DEBUG_STATE,
230  reinterpret_cast<thread_state_t>(&dr), &dr_count);
231 
232  if (create && rc != KERN_SUCCESS)
233  throw std::runtime_error(
234  "MemoryWatchpoint_x86_64::MemoryWatchpoint_x86_64(): "
235  "thread_get_state failed");
236 
237  switch (dr_) {
238  case kDR0:
239  dr.uds.ds64.__dr0 = reinterpret_cast<uint64_t>(addr_);
240  break;
241  case kDR1:
242  dr.uds.ds64.__dr1 = reinterpret_cast<uint64_t>(addr_);
243  break;
244  case kDR2:
245  dr.uds.ds64.__dr2 = reinterpret_cast<uint64_t>(addr_);
246  break;
247  case kDR3:
248  dr.uds.ds64.__dr3 = reinterpret_cast<uint64_t>(addr_);
249  break;
250  }
251 
252  dr.uds.ds64.__dr7 &= ~MakeMask(dr_);
253 
254  dr.uds.ds64.__dr7 |=
255  MakeFlags(dr_, create ? kEnabledLocally : kDisabled, cond_, size_);
256 
257  rc = thread_set_state(target_mach_thread, x86_DEBUG_STATE,
258  reinterpret_cast<thread_state_t>(&dr), dr_count);
259 
260  if (create && rc != KERN_SUCCESS)
261  throw std::runtime_error(
262  "MemoryWatchpoint_x86_64::MemoryWatchpoint_x86_64(): "
263  "thread_set_state failed");
264 #endif // defined(__APPLE__) && defined(__x86_64__)
265  }
266 };
267 
268 } // namespace detail
269 } // namespace TiledArray
270 
271 namespace TiledArray {
272 
280 class Debugger {
281  protected:
282  std::string prefix_;
283  std::string exec_;
284  std::string cmd_;
285  volatile int debugger_ready_;
286 
287  bool debug_;
290  bool sleep_;
293  int *mysigs_;
294 
295  void init();
296 
297  static std::shared_ptr<Debugger> default_debugger_;
298 
306  static void __traceback(const std::string &prefix,
307  const char *reason = nullptr);
308 
309  public:
313  explicit Debugger(const char *exec = nullptr);
314  virtual ~Debugger();
315 
318  virtual void debug(const char *reason);
326  virtual void traceback(const char *reason);
328  virtual void set_debug_on_signal(int);
330  virtual void set_traceback_on_signal(int);
332  virtual void set_exit_on_signal(int);
336  virtual void set_wait_for_debugger(int);
337 
339  virtual void handle(int sig);
342  virtual void release(int sig);
344  virtual void handle_defaults();
345 
347  virtual void set_prefix(const char *p);
349  virtual void set_prefix(int p);
350 
356  virtual void set_cmd(const char *);
358  virtual void default_cmd();
362  virtual void set_exec(const char *);
363 
365  virtual void got_signal(int sig);
366 
368  static void set_default_debugger(const std::shared_ptr<Debugger> &);
370  static std::shared_ptr<Debugger> default_debugger();
371 
372  private:
374  void resolve_cmd_alias();
375 };
376 
378 void launch_gdb_xterm();
380 void launch_lldb_xterm();
381 
382 } // namespace TiledArray
383 
384 #endif // MPQC4_SRC_MPQC_UTIL_MISC_BUG_H_
385 
386 // Local Variables:
387 // mode: c++
388 // c-file-style: "CLJ"
389 // End:
std::string exec_
Definition: bug.h:283
void launch_gdb_xterm()
Use this to launch GNU debugger in xterm.
Definition: bug.cpp:357
a singleton pool of MemoryWatchpoint objects
Definition: bug.h:81
virtual void set_cmd(const char *)
Definition: bug.cpp:194
static std::shared_ptr< Debugger > default_debugger()
Return the global default debugger.
Definition: bug.cpp:317
virtual void set_traceback_on_signal(int)
Turn on or off traceback on a signel. The default is on.
Definition: bug.cpp:307
MemoryWatchpoint_x86_64 * find(void *addr, const pthread_t *thread)
Definition: bug.h:133
DebugRegister debug_register() const
Definition: bug.h:200
bool exit_on_signal_
Definition: bug.h:289
MemoryWatchpoint represents a hardware watchpoint for a memory location Implements a memory watchpoin...
Definition: bug.h:51
virtual void set_prefix(const char *p)
This sets a prefix which preceeds all messages printing by Debugger.
Definition: bug.cpp:158
std::string cmd_
Definition: bug.h:284
volatile int debugger_ready_
Definition: bug.h:285
virtual void set_debug_on_signal(int)
Turn on or off debugging on a signel. The default is on.
Definition: bug.cpp:305
static std::shared_ptr< Debugger > default_debugger_
Definition: bug.h:297
static constexpr const size_t nwatchpoints_per_thread
Definition: bug.h:83
MemoryWatchpoint_x86_64(void *addr, Size size, Condition cond, DebugRegister dr, const pthread_t *thread)
creates a MemoryWatchpoint watching memory window [addr,addr+size) for condition cond from threads th...
Definition: bug.h:189
virtual void set_exit_on_signal(int)
Turn on or off exit after a signel. The default is on.
Definition: bug.cpp:311
static void initialize_instance(const std::vector< const pthread_t * > &threads)
Definition: bug.h:91
Pool & clear(void *addr, const pthread_t *thread)
Definition: bug.h:144
virtual void debug(const char *reason)
Definition: bug.cpp:203
virtual void set_exec(const char *)
Definition: bug.cpp:150
Pool & set(void *addr, Size size, Condition cond, const pthread_t *thread)
Definition: bug.h:110
virtual void traceback(const char *reason)
Definition: bug.cpp:324
virtual void default_cmd()
Calls set_cmd with a hopefully suitable default.
Definition: bug.cpp:172
virtual ~Debugger()
Definition: bug.cpp:75
virtual void handle(int sig)
The Debugger will be activated when sig is caught.
Definition: bug.cpp:105
static void __traceback(const std::string &prefix, const char *reason=nullptr)
Definition: bug.cpp:328
bool wait_for_debugger_
Definition: bug.h:291
bool handle_sigint_
Definition: bug.h:292
virtual void release(int sig)
Definition: bug.cpp:113
Debugger(const char *exec=nullptr)
Programmatic construction of Debugger.
Definition: bug.cpp:58
virtual void got_signal(int sig)
Called when signal sig is received. This is mainly for internal use.
Definition: bug.cpp:267
std::string prefix_
Definition: bug.h:282
virtual void set_wait_for_debugger(int)
Definition: bug.cpp:309
void launch_lldb_xterm()
Use this to launch LLVM debugger in xterm.
Definition: bug.cpp:362
static std::shared_ptr< Pool > instance()
Definition: bug.h:98
static void set_default_debugger(const std::shared_ptr< Debugger > &)
Set the global default debugger. The initial value is null.
Definition: bug.cpp:313
virtual void handle_defaults()
This calls handle(int) with all of the major signals.
Definition: bug.cpp:120