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 TTG_UTIL_MISC_BUG_H_
29 #define TTG_UTIL_MISC_BUG_H_
30 
31 #include <cassert>
32 #include <memory>
33 #include <stdexcept>
34 #include <string>
35 #include <unordered_map>
36 #include <vector>
37 
38 #include <pthread.h>
39 #if __APPLE__
40 #include <mach/mach.h>
41 #endif
42 
43 namespace ttg {
44  namespace detail {
45 
51  public:
52  // x86 debugging registers are described in see
53  // https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-system-programming-manual-325384.html
54  enum DebugRegister { kDR0 = 0, kDR1 = 1, kDR2 = 2, kDR3 = 3 };
55 
56  enum Size {
57  kByte = 0, // 1 byte - 00
58  kHalfWord = 1, // 2 bytes - 01
59  kWord = 3, // 4 bytes - 11
60  // kDoubleWord = 2, // 8 bytes - 10 NOT SUPPORTED BY SOME CHIPS!
61  kSizeMask = 3 // mask 11
62  };
63 
64  enum BreakState {
65  kDisabled = 0, // disabled - 00
66  kEnabledLocally = 1, // task local - 01
67  kEnabledGlobally = 2, // global - 10
68  kBreakStateMask = 3 // mask 11
69  };
70 
71  enum Condition {
72  kWhenExecuted = 0, // on execution - 00
73  kWhenWritten = 1, // on write - 01
74  kWhenWrittenOrRead = 3, // on read or write - 11
75  kConditionMask = 3 // mask 11
76  };
77 
79  class Pool {
80  public:
81  static constexpr const size_t nwatchpoints_per_thread = 4;
82 
83  ~Pool() = default;
84 
89  static void initialize_instance(const std::vector<const pthread_t *> &threads) {
90  get_instance() = std::shared_ptr<Pool>(new Pool(threads));
91  }
92 
94  static std::shared_ptr<Pool> instance() {
95  auto result = get_instance();
96  assert(result && "Pool::instance() called but Pool::initialize_instance() had not been called");
97  return result;
98  }
99 
107  Pool &set(void *addr, Size size, Condition cond, const pthread_t *thread) {
108  const auto it = pool_.find(thread);
109  assert(it != pool_.end());
110  // make sure there is no watchpoint for this address already
111  for (auto &watchpt_ptr : it->second) {
112  if (watchpt_ptr && watchpt_ptr->address() == addr) return *this;
113  }
114  // now create a watchpoint
115  for (auto dr = 0; dr != nwatchpoints_per_thread; ++dr) {
116  auto &watchpt_ptr = it->second[dr];
117  if (!watchpt_ptr) {
118  watchpt_ptr =
119  std::make_shared<MemoryWatchpoint_x86_64>(addr, size, cond, static_cast<DebugRegister>(dr), thread);
120  return *this;
121  }
122  }
123  return *this;
124  }
125 
130  MemoryWatchpoint_x86_64 *find(void *addr, const pthread_t *thread) {
131  const auto it = pool_.find(thread);
132  assert(it != pool_.end());
133  for (auto &watchpt_ptr : it->second) {
134  if (watchpt_ptr && watchpt_ptr->address() == addr) return watchpt_ptr.get();
135  }
136  return nullptr;
137  }
138 
142  Pool &clear(void *addr, const pthread_t *thread) {
143  const auto it = pool_.find(thread);
144  assert(it != pool_.end());
145  for (auto &watchpt_ptr : it->second) {
146  if (watchpt_ptr && watchpt_ptr->address() == addr) {
147  watchpt_ptr.reset();
148  return *this;
149  }
150  }
151  return *this;
152  }
153 
154  private:
155  std::unordered_map<const pthread_t *, std::vector<std::shared_ptr<MemoryWatchpoint_x86_64>>> pool_;
156 
162  explicit Pool(const std::vector<const pthread_t *> &threads) {
163  for (const auto &thread : threads) {
164  assert(thread != nullptr);
165  pool_[thread].resize(nwatchpoints_per_thread);
166  }
167  }
168 
169  static std::shared_ptr<Pool> &get_instance() {
170  static std::shared_ptr<Pool> instance_;
171  return instance_;
172  }
173  };
174 
184  MemoryWatchpoint_x86_64(void *addr, Size size, Condition cond, DebugRegister dr, const pthread_t *thread)
185  : addr_(addr), size_(size), cond_(cond), dr_(dr), thread_(thread) {
186  init(true);
187  }
188 
189  ~MemoryWatchpoint_x86_64() { init(false); }
190 
191  void *address() const { return addr_; }
192  Size size() const { return size_; }
193  Condition condition() const { return cond_; }
194  DebugRegister debug_register() const { return dr_; }
195 
196  private:
197  void *addr_;
198  Size size_;
199  Condition cond_;
200  DebugRegister dr_;
201  const pthread_t *thread_;
202 
203  inline uint64_t MakeFlags(DebugRegister reg, BreakState state, Condition cond, Size size) {
204  // N.B. each register takes 2 bits in DR7
205  return (state | cond << 16 | size << 24) << (2 * reg);
206  }
207 
208  inline uint64_t MakeMask(DebugRegister reg) { return MakeFlags(reg, kBreakStateMask, kConditionMask, kSizeMask); }
209 
210  friend class MemoryWatchPool;
211 
212  void init(bool create) {
213 #if defined(__APPLE__) && defined(__x86_64__)
214  x86_debug_state dr;
215  mach_msg_type_number_t dr_count = x86_DEBUG_STATE_COUNT;
216 
217  mach_port_t target_mach_thread = pthread_mach_thread_np(*thread_);
218 
219  kern_return_t rc =
220  thread_get_state(target_mach_thread, x86_DEBUG_STATE, reinterpret_cast<thread_state_t>(&dr), &dr_count);
221 
222  if (create && rc != KERN_SUCCESS)
223  throw std::runtime_error("MemoryWatchpoint_x86_64::MemoryWatchpoint_x86_64(): thread_get_state failed");
224 
225  switch (dr_) {
226  case kDR0:
227  dr.uds.ds64.__dr0 = reinterpret_cast<uint64_t>(addr_);
228  break;
229  case kDR1:
230  dr.uds.ds64.__dr1 = reinterpret_cast<uint64_t>(addr_);
231  break;
232  case kDR2:
233  dr.uds.ds64.__dr2 = reinterpret_cast<uint64_t>(addr_);
234  break;
235  case kDR3:
236  dr.uds.ds64.__dr3 = reinterpret_cast<uint64_t>(addr_);
237  break;
238  }
239 
240  dr.uds.ds64.__dr7 &= ~MakeMask(dr_);
241 
242  dr.uds.ds64.__dr7 |= MakeFlags(dr_, create ? kEnabledLocally : kDisabled, cond_, size_);
243 
244  rc = thread_set_state(target_mach_thread, x86_DEBUG_STATE, reinterpret_cast<thread_state_t>(&dr), dr_count);
245 
246  if (create && rc != KERN_SUCCESS)
247  throw std::runtime_error("MemoryWatchpoint_x86_64::MemoryWatchpoint_x86_64(): thread_set_state failed");
248 #endif // defined(__APPLE__) && defined(__x86_64__)
249  }
250  };
251 
252  } // namespace detail
253 
261  void initialize_fpe();
262 
270  class Debugger {
271  protected:
272  std::string prefix_;
273  std::string exec_;
274  std::string cmd_;
275  volatile int debugger_ready_;
276 
277  bool debug_;
280  bool sleep_;
283  int *mysigs_;
284 
285  void init();
286 
287  static std::shared_ptr<Debugger> default_debugger_;
288 
295  static void __traceback(const std::string &prefix, const char *reason = nullptr);
296 
297  public:
301  explicit Debugger(const char *exec = nullptr);
302  virtual ~Debugger();
303 
306  virtual void debug(const char *reason);
314  virtual void traceback(const char *reason);
316  virtual void set_debug_on_signal(int);
318  virtual void set_traceback_on_signal(int);
320  virtual void set_exit_on_signal(int);
324  virtual void set_wait_for_debugger(int);
325 
327  virtual void handle(int sig);
329  virtual void release(int sig);
331  virtual void handle_defaults();
332 
334  virtual void set_prefix(const char *p);
336  virtual void set_prefix(int p);
337 
343  virtual void set_cmd(const char *);
345  virtual void default_cmd();
349  virtual void set_exec(const char *);
350 
352  virtual void got_signal(int sig);
353 
355  static void set_default_debugger(const std::shared_ptr<Debugger> &);
357  static std::shared_ptr<Debugger> default_debugger();
358 
359  private:
361  void resolve_cmd_alias();
362  };
363 
364 } // namespace ttg
365 
366 namespace ttg {
367  void launch_debugger(int rank, const char *exec_name, const char *cmd);
368 
369  void launch_lldb(int rank = 0, const char *exec_name = "");
370  void launch_gdb(int rank = 0, const char *exec_name = "");
371 
372 } // namespace ttg
373 
374 #endif // TTG_UTIL_MISC_BUG_H_
375 
376 // Local Variables:
377 // mode: c++
378 // c-file-style: "CLJ"
379 // End:
virtual void set_cmd(const char *)
Definition: bug.cpp:289
virtual void set_prefix(const char *p)
This sets a prefix which preceeds all messages printing by Debugger.
Definition: bug.cpp:257
virtual ~Debugger()
Definition: bug.cpp:174
virtual void got_signal(int sig)
Called when signal sig is received. This is mainly for internal use.
Definition: bug.cpp:360
int * mysigs_
Definition: bug.h:283
static void set_default_debugger(const std::shared_ptr< Debugger > &)
Set the global default debugger. The initial value is null.
Definition: bug.cpp:406
static void __traceback(const std::string &prefix, const char *reason=nullptr)
Definition: bug.cpp:414
volatile int debugger_ready_
Definition: bug.h:275
virtual void handle_defaults()
This calls handle(int) with all of the major signals.
Definition: bug.cpp:219
virtual void set_wait_for_debugger(int)
Definition: bug.cpp:402
bool debug_
Definition: bug.h:277
std::string cmd_
Definition: bug.h:274
virtual void traceback(const char *reason)
Definition: bug.cpp:412
bool handle_sigint_
Definition: bug.h:282
static std::shared_ptr< Debugger > default_debugger()
Return the global default debugger.
Definition: bug.cpp:408
std::string exec_
Definition: bug.h:273
bool exit_on_signal_
Definition: bug.h:279
virtual void default_cmd()
Calls set_cmd with a hopefully suitable default.
Definition: bug.cpp:271
virtual void set_exit_on_signal(int)
Turn on or off exit after a signel. The default is on.
Definition: bug.cpp:404
void init()
Definition: bug.cpp:181
std::string prefix_
Definition: bug.h:272
bool traceback_
Definition: bug.h:278
virtual void handle(int sig)
The Debugger will be activated when sig is caught.
Definition: bug.cpp:204
bool sleep_
Definition: bug.h:280
Debugger(const char *exec=nullptr)
Programmatic construction of Debugger.
Definition: bug.cpp:157
virtual void debug(const char *reason)
Definition: bug.cpp:298
virtual void release(int sig)
Reverts the effect of handle(sig) , i.e. the Debugger will not be activated when sig is caught.
Definition: bug.cpp:212
static std::shared_ptr< Debugger > default_debugger_
Definition: bug.h:287
virtual void set_debug_on_signal(int)
Turn on or off debugging on a signel. The default is on.
Definition: bug.cpp:398
bool wait_for_debugger_
Definition: bug.h:281
virtual void set_exec(const char *)
Definition: bug.cpp:249
virtual void set_traceback_on_signal(int)
Turn on or off traceback on a signel. The default is on.
Definition: bug.cpp:400
a singleton pool of MemoryWatchpoint objects
Definition: bug.h:79
Pool & set(void *addr, Size size, Condition cond, const pthread_t *thread)
Definition: bug.h:107
static void initialize_instance(const std::vector< const pthread_t * > &threads)
Definition: bug.h:89
static constexpr const size_t nwatchpoints_per_thread
Definition: bug.h:81
MemoryWatchpoint_x86_64 * find(void *addr, const pthread_t *thread)
Definition: bug.h:130
static std::shared_ptr< Pool > instance()
accesses the unique pool; asserts that the default instance has been initialized by calling initializ...
Definition: bug.h:94
Pool & clear(void *addr, const pthread_t *thread)
Definition: bug.h:142
MemoryWatchpoint represents a hardware watchpoint for a memory location Implements a memory watchpoin...
Definition: bug.h:50
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:184
Condition condition() const
Definition: bug.h:193
DebugRegister debug_register() const
Definition: bug.h:194
top-level TTG namespace contains runtime-neutral functionality
Definition: keymap.h:8
void launch_lldb(int rank, const char *exec_name)
Definition: bug.cpp:457
void launch_debugger(int rank, const char *exec_name, const char *cmd)
Definition: bug.cpp:445
int rank(World world=default_execution_context())
Definition: run.h:85
void initialize_fpe()
Initializes the floating point exceptions.
Definition: bug.cpp:51
void launch_gdb(int rank, const char *exec_name)
Definition: bug.cpp:458