openMSX
Debugger.cc
Go to the documentation of this file.
1 #include "Debugger.hh"
2 #include "Debuggable.hh"
3 #include "Probe.hh"
4 #include "ProbeBreakPoint.hh"
5 #include "MSXMotherBoard.hh"
6 #include "Reactor.hh"
7 #include "MSXCPU.hh"
8 #include "MSXCPUInterface.hh"
9 #include "BreakPoint.hh"
10 #include "DebugCondition.hh"
11 #include "MSXWatchIODevice.hh"
12 #include "TclObject.hh"
13 #include "RecordedCommand.hh"
14 #include "CommandException.hh"
15 #include "MemBuffer.hh"
16 #include "StringOp.hh"
17 #include "KeyRange.hh"
18 #include "unreachable.hh"
19 #include "memory.hh"
20 #include <cassert>
21 
22 using std::map;
23 using std::shared_ptr;
24 using std::make_shared;
25 using std::string;
26 using std::vector;
27 using std::begin;
28 using std::end;
29 
30 namespace openmsx {
31 
33 
34 class DebugCmd : public RecordedCommand
35 {
36 public:
37  DebugCmd(CommandController& commandController,
38  StateChangeDistributor& stateChangeDistributor,
39  Scheduler& scheduler, GlobalCliComm& cliComm,
40  Debugger& debugger);
41  virtual bool needRecord(const vector<TclObject>& tokens) const;
42  virtual void execute(const vector<TclObject>& tokens,
43  TclObject& result, EmuTime::param time);
44  virtual string help(const vector<string>& tokens) const;
45  virtual void tabCompletion(vector<string>& tokens) const;
46 
47 private:
48  void list(TclObject& result);
49  void desc(const vector<TclObject>& tokens,
50  TclObject& result);
51  void size(const vector<TclObject>& tokens,
52  TclObject& result);
53  void read(const vector<TclObject>& tokens,
54  TclObject& result);
55  void readBlock(const vector<TclObject>& tokens,
56  TclObject& result);
57  void write(const vector<TclObject>& tokens,
58  TclObject& result);
59  void writeBlock(const vector<TclObject>& tokens,
60  TclObject& result);
61  void setBreakPoint(const vector<TclObject>& tokens,
62  TclObject& result);
63  void removeBreakPoint(const vector<TclObject>& tokens,
64  TclObject& result);
65  void listBreakPoints(const vector<TclObject>& tokens,
66  TclObject& result);
67  vector<string> getBreakPointIds() const;
68  vector<string> getWatchPointIds() const;
69  vector<string> getConditionIds() const;
70  void setWatchPoint(const vector<TclObject>& tokens,
71  TclObject& result);
72  void removeWatchPoint(const vector<TclObject>& tokens,
73  TclObject& result);
74  void listWatchPoints(const vector<TclObject>& tokens,
75  TclObject& result);
76  void setCondition(const vector<TclObject>& tokens,
77  TclObject& result);
78  void removeCondition(const vector<TclObject>& tokens,
79  TclObject& result);
80  void listConditions(const vector<TclObject>& tokens,
81  TclObject& result);
82  void probe(const vector<TclObject>& tokens,
83  TclObject& result);
84  void probeList(const vector<TclObject>& tokens,
85  TclObject& result);
86  void probeDesc(const vector<TclObject>& tokens,
87  TclObject& result);
88  void probeRead(const vector<TclObject>& tokens,
89  TclObject& result);
90  void probeSetBreakPoint(const vector<TclObject>& tokens,
91  TclObject& result);
92  void probeRemoveBreakPoint(const vector<TclObject>& tokens,
93  TclObject& result);
94  void probeListBreakPoints(const vector<TclObject>& tokens,
95  TclObject& result);
96 
97  GlobalCliComm& cliComm;
98  Debugger& debugger;
99 };
100 
101 
103  : motherBoard(motherBoard_)
104  , debugCmd(make_unique<DebugCmd>(
105  motherBoard.getCommandController(),
106  motherBoard.getStateChangeDistributor(),
107  motherBoard.getScheduler(),
108  motherBoard.getReactor().getGlobalCliComm(), *this))
109  , cpu(nullptr)
110 {
111 }
112 
114 {
115  assert(!cpu);
116  assert(debuggables.empty());
117 }
118 
120 {
121  cpu = cpu_;
122 }
123 
125 {
126  assert(debuggables.find(name) == debuggables.end());
127  debuggables[name] = &debuggable;
128 }
129 
131 {
132  (void)debuggable;
133  auto it = debuggables.find(name);
134  assert(it != debuggables.end() && (it->second == &debuggable));
135  debuggables.erase(it);
136 }
137 
139 {
140  auto it = debuggables.find(name);
141  return (it != debuggables.end()) ? it->second : nullptr;
142 }
143 
144 Debuggable& Debugger::getDebuggable(string_ref name)
145 {
146  Debuggable* result = findDebuggable(name);
147  if (!result) {
148  throw CommandException("No such debuggable: " + name);
149  }
150  return *result;
151 }
152 
154 {
155  assert(probes.find(name) == probes.end());
156  probes[name] = &probe;
157 }
158 
160 {
161  (void)probe;
162  auto it = probes.find(name);
163  assert(it != probes.end() && (it->second == &probe));
164  probes.erase(it);
165 }
166 
168 {
169  auto it = probes.find(name);
170  return (it != probes.end()) ? it->second : nullptr;
171 }
172 
173 ProbeBase& Debugger::getProbe(string_ref name)
174 {
175  ProbeBase* result = findProbe(name);
176  if (!result) {
177  throw CommandException("No such probe: " + name);
178  }
179  return *result;
180 }
181 
182 unsigned Debugger::insertProbeBreakPoint(
183  TclObject command, TclObject condition,
184  ProbeBase& probe, unsigned newId /*= -1*/)
185 {
186  auto bp = make_unique<ProbeBreakPoint>(
187  motherBoard.getReactor().getGlobalCliComm(),
188  command, condition, *this, probe, newId);
189  unsigned result = bp->getId();
190  probeBreakPoints.push_back(std::move(bp));
191  return result;
192 }
193 
195 {
196  if (name.starts_with("pp#")) {
197  // remove by id
198  unsigned id = stoi(name.substr(3));
199  for (auto it = probeBreakPoints.begin();
200  it != probeBreakPoints.end(); ++it) {
201  if ((*it)->getId() == id) {
202  probeBreakPoints.erase(it);
203  return;
204  }
205  }
206  throw CommandException("No such breakpoint: " + name);
207  } else {
208  // remove by probe, only works for unconditional bp
209  for (auto it = probeBreakPoints.begin();
210  it != probeBreakPoints.end(); ++it) {
211  if ((*it)->getProbe().getName() == name) {
212  probeBreakPoints.erase(it);
213  return;
214  }
215  }
216  throw CommandException(
217  "No (unconditional) breakpoint for probe: " + name);
218  }
219 }
220 
222 {
223  auto it = std::find_if(probeBreakPoints.begin(), probeBreakPoints.end(),
224  [&](ProbeBreakPoints::value_type& v) { return v.get() == &bp; });
225  assert(it != probeBreakPoints.end());
226  probeBreakPoints.erase(it);
227 }
228 
229 unsigned Debugger::setWatchPoint(TclObject command, TclObject condition,
230  WatchPoint::Type type,
231  unsigned beginAddr, unsigned endAddr,
232  unsigned newId /*= -1*/)
233 {
234  shared_ptr<WatchPoint> wp;
235  if ((type == WatchPoint::READ_IO) || (type == WatchPoint::WRITE_IO)) {
236  // Workaround visual studio 2012 limitation:
237  // True variadic templates are not yet supported. Instead the
238  // visual stdio's standard library supports make_shared with
239  // (only) upto 5 parameters. It is possible to increase this
240  // limit to 10 (by defining _VARIADIC_MAX) but that also
241  // slows down compilation. It's easy enough to work around.
242  // Also the next version of visual studio (still to be
243  // release in 2012) will properly support this.
244  //wp = make_shared<WatchIO>(
245  // motherBoard, type, beginAddr, endAddr,
246  // command, condition, newId);
247  wp.reset(new WatchIO(
248  motherBoard, type, beginAddr, endAddr,
249  command, condition, newId));
250  } else {
251  //wp = make_shared<WatchPoint>(
252  // motherBoard.getReactor().getGlobalCliComm(),
253  // command, condition, type, beginAddr, endAddr, newId);
254  wp.reset(new WatchPoint(
255  motherBoard.getReactor().getGlobalCliComm(),
256  command, condition, type, beginAddr, endAddr, newId));
257  }
258  motherBoard.getCPUInterface().setWatchPoint(wp);
259  return wp->getId();
260 }
261 
263 {
264  // Copy watchpoints to new machine.
265  assert(motherBoard.getCPUInterface().getWatchPoints().empty());
266  for (auto& wp : other.motherBoard.getCPUInterface().getWatchPoints()) {
267  setWatchPoint(wp->getCommandObj(), wp->getConditionObj(),
268  wp->getType(), wp->getBeginAddress(),
269  wp->getEndAddress(), wp->getId());
270  }
271 
272  // Copy probes to new machine.
273  assert(probeBreakPoints.empty());
274  for (auto& bp : other.probeBreakPoints) {
275  if (ProbeBase* probe = findProbe(bp->getProbe().getName())) {
276  insertProbeBreakPoint(bp->getCommandObj(),
277  bp->getConditionObj(),
278  *probe, bp->getId());
279  }
280  }
281 
282  // Breakpoints and conditions are (currently) global, so no need to
283  // copy those.
284 }
285 
286 
287 // class DebugCmd
288 
289 static word getAddress(const vector<TclObject>& tokens)
290 {
291  if (tokens.size() < 3) {
292  throw CommandException("Missing argument");
293  }
294  unsigned addr = tokens[2].getInt();
295  if (addr >= 0x10000) {
296  throw CommandException("Invalid address");
297  }
298  return addr;
299 }
300 
302  StateChangeDistributor& stateChangeDistributor,
303  Scheduler& scheduler, GlobalCliComm& cliComm_,
304  Debugger& debugger_)
305  : RecordedCommand(commandController, stateChangeDistributor,
306  scheduler, "debug")
307  , cliComm(cliComm_)
308  , debugger(debugger_)
309 {
310 }
311 
312 bool DebugCmd::needRecord(const vector<TclObject>& tokens) const
313 {
314  // Note: it's crucial for security that only the write and write_block
315  // subcommands are recorded and replayed. The 'set_bp' command for
316  // example would allow to set a callback that can execute arbitrary Tcl
317  // code. See comments in RecordedCommand for more details.
318  if (tokens.size() < 2) return false;
319  string_ref subCmd = tokens[1].getString();
320  return (subCmd == "write") || (subCmd == "write_block");
321 }
322 
323 void DebugCmd::execute(const vector<TclObject>& tokens,
324  TclObject& result, EmuTime::param /*time*/)
325 {
326  if (tokens.size() < 2) {
327  throw CommandException("Missing argument");
328  }
329  string_ref subCmd = tokens[1].getString();
330  if (subCmd == "read") {
331  read(tokens, result);
332  } else if (subCmd == "read_block") {
333  readBlock(tokens, result);
334  } else if (subCmd == "write") {
335  write(tokens, result);
336  } else if (subCmd == "write_block") {
337  writeBlock(tokens, result);
338  } else if (subCmd == "size") {
339  size(tokens, result);
340  } else if (subCmd == "desc") {
341  desc(tokens, result);
342  } else if (subCmd == "list") {
343  list(result);
344  } else if (subCmd == "step") {
345  debugger.motherBoard.getCPUInterface().doStep();
346  } else if (subCmd == "cont") {
347  debugger.motherBoard.getCPUInterface().doContinue();
348  } else if (subCmd == "disasm") {
349  debugger.cpu->disasmCommand(tokens, result);
350  } else if (subCmd == "break") {
351  debugger.motherBoard.getCPUInterface().doBreak();
352  } else if (subCmd == "breaked") {
353  result.setInt(debugger.motherBoard.getCPUInterface().isBreaked());
354  } else if (subCmd == "set_bp") {
355  setBreakPoint(tokens, result);
356  } else if (subCmd == "remove_bp") {
357  removeBreakPoint(tokens, result);
358  } else if (subCmd == "list_bp") {
359  listBreakPoints(tokens, result);
360  } else if (subCmd == "set_watchpoint") {
361  setWatchPoint(tokens, result);
362  } else if (subCmd == "remove_watchpoint") {
363  removeWatchPoint(tokens, result);
364  } else if (subCmd == "list_watchpoints") {
365  listWatchPoints(tokens, result);
366  } else if (subCmd == "set_condition") {
367  setCondition(tokens, result);
368  } else if (subCmd == "remove_condition") {
369  removeCondition(tokens, result);
370  } else if (subCmd == "list_conditions") {
371  listConditions(tokens, result);
372  } else if (subCmd == "probe") {
373  probe(tokens, result);
374  } else {
375  throw SyntaxError();
376  }
377 }
378 
379 void DebugCmd::list(TclObject& result)
380 {
381  result.addListElements(keys(debugger.debuggables));
382 }
383 
384 void DebugCmd::desc(const vector<TclObject>& tokens, TclObject& result)
385 {
386  if (tokens.size() != 3) {
387  throw SyntaxError();
388  }
389  Debuggable& device = debugger.getDebuggable(tokens[2].getString());
390  result.setString(device.getDescription());
391 }
392 
393 void DebugCmd::size(const vector<TclObject>& tokens, TclObject& result)
394 {
395  if (tokens.size() != 3) {
396  throw SyntaxError();
397  }
398  Debuggable& device = debugger.getDebuggable(tokens[2].getString());
399  result.setInt(device.getSize());
400 }
401 
402 void DebugCmd::read(const vector<TclObject>& tokens, TclObject& result)
403 {
404  if (tokens.size() != 4) {
405  throw SyntaxError();
406  }
407  Debuggable& device = debugger.getDebuggable(tokens[2].getString());
408  unsigned addr = tokens[3].getInt();
409  if (addr >= device.getSize()) {
410  throw CommandException("Invalid address");
411  }
412  result.setInt(device.read(addr));
413 }
414 
415 void DebugCmd::readBlock(const vector<TclObject>& tokens, TclObject& result)
416 {
417  if (tokens.size() != 5) {
418  throw SyntaxError();
419  }
420  Debuggable& device = debugger.getDebuggable(tokens[2].getString());
421  unsigned size = device.getSize();
422  unsigned addr = tokens[3].getInt();
423  if (addr >= size) {
424  throw CommandException("Invalid address");
425  }
426  unsigned num = tokens[4].getInt();
427  if (num > (size - addr)) {
428  throw CommandException("Invalid size");
429  }
430 
431  MemBuffer<byte> buf(num);
432  for (unsigned i = 0; i < num; ++i) {
433  buf[i] = device.read(addr + i);
434  }
435  result.setBinary(buf.data(), num);
436 }
437 
438 void DebugCmd::write(const vector<TclObject>& tokens,
439  TclObject& /*result*/)
440 {
441  if (tokens.size() != 5) {
442  throw SyntaxError();
443  }
444  Debuggable& device = debugger.getDebuggable(tokens[2].getString());
445  unsigned addr = tokens[3].getInt();
446  if (addr >= device.getSize()) {
447  throw CommandException("Invalid address");
448  }
449  unsigned value = tokens[4].getInt();
450  if (value >= 256) {
451  throw CommandException("Invalid value");
452  }
453 
454  device.write(addr, value);
455 }
456 
457 void DebugCmd::writeBlock(const vector<TclObject>& tokens,
458  TclObject& /*result*/)
459 {
460  if (tokens.size() != 5) {
461  throw SyntaxError();
462  }
463  Debuggable& device = debugger.getDebuggable(tokens[2].getString());
464  unsigned size = device.getSize();
465  unsigned addr = tokens[3].getInt();
466  if (addr >= size) {
467  throw CommandException("Invalid address");
468  }
469  unsigned num;
470  const byte* buf = tokens[4].getBinary(num);
471  if ((num + addr) > size) {
472  throw CommandException("Invalid size");
473  }
474 
475  for (unsigned i = 0; i < num; ++i) {
476  device.write(addr + i, static_cast<byte>(buf[i]));
477  }
478 }
479 
480 void DebugCmd::setBreakPoint(const vector<TclObject>& tokens,
481  TclObject& result)
482 {
483  shared_ptr<BreakPoint> bp;
484  TclObject command (result.getInterpreter(), "debug break");
485  TclObject condition(result.getInterpreter());
486  word addr;
487 
488  switch (tokens.size()) {
489  case 5: // command
490  command = tokens[4];
491  command.checkCommand();
492  // fall-through
493  case 4: // condition
494  condition = tokens[3];
495  if (!condition.getString().empty()) {
496  condition.checkExpression();
497  }
498  // fall-through
499  case 3: // address
500  addr = getAddress(tokens);
501  bp = make_shared<BreakPoint>(cliComm, addr, command, condition);
502  break;
503  default:
504  if (tokens.size() < 3) {
505  throw CommandException("Too few arguments.");
506  } else {
507  throw CommandException("Too many arguments.");
508  }
509  }
510  result.setString(StringOp::Builder() << "bp#" << bp->getId());
511  debugger.motherBoard.getCPUInterface().insertBreakPoint(bp);
512 }
513 
514 void DebugCmd::removeBreakPoint(const vector<TclObject>& tokens,
515  TclObject& /*result*/)
516 {
517  if (tokens.size() != 3) {
518  throw SyntaxError();
519  }
520  auto& interface = debugger.motherBoard.getCPUInterface();
521  auto& breakPoints = interface.getBreakPoints();
522 
523  string_ref tmp = tokens[2].getString();
524  if (tmp.starts_with("bp#")) {
525  // remove by id
526  unsigned id = stoi(tmp.substr(3));
527  for (auto& p : breakPoints) {
528  const BreakPoint& bp = *p.second;
529  if (bp.getId() == id) {
530  interface.removeBreakPoint(bp);
531  return;
532  }
533  }
534  throw CommandException("No such breakpoint: " + tmp);
535  } else {
536  // remove by addr, only works for unconditional bp
537  word addr = getAddress(tokens);
538  auto range = breakPoints.equal_range(addr);
539  for (auto it = range.first; it != range.second; ++it) {
540  const BreakPoint& bp = *it->second;
541  if (bp.getCondition().empty()) {
542  interface.removeBreakPoint(bp);
543  return;
544  }
545  }
546  throw CommandException(
547  "No (unconditional) breakpoint at address: " + tmp);
548  }
549 }
550 
551 void DebugCmd::listBreakPoints(const vector<TclObject>& /*tokens*/,
552  TclObject& result)
553 {
554  string res;
555  auto& interface = debugger.motherBoard.getCPUInterface();
556  for (auto& p : interface.getBreakPoints()) {
557  const BreakPoint& bp = *p.second;
558  TclObject line(result.getInterpreter());
559  line.addListElement(StringOp::Builder() << "bp#" << bp.getId());
560  line.addListElement("0x" + StringOp::toHexString(bp.getAddress(), 4));
561  line.addListElement(bp.getCondition());
562  line.addListElement(bp.getCommand());
563  res += line.getString() + '\n';
564  }
565  result.setString(res);
566 }
567 
568 
569 void DebugCmd::setWatchPoint(const vector<TclObject>& tokens,
570  TclObject& result)
571 {
572  TclObject command (result.getInterpreter(), "debug break");
573  TclObject condition(result.getInterpreter());
574  unsigned beginAddr, endAddr;
575  WatchPoint::Type type;
576 
577  switch (tokens.size()) {
578  case 6: // command
579  command = tokens[5];
580  command.checkCommand();
581  // fall-through
582  case 5: // condition
583  condition = tokens[4];
584  if (!condition.getString().empty()) {
585  condition.checkExpression();
586  }
587  // fall-through
588  case 4: { // address + type
589  string_ref typeStr = tokens[2].getString();
590  unsigned max;
591  if (typeStr == "read_io") {
592  type = WatchPoint::READ_IO;
593  max = 0x100;
594  } else if (typeStr == "write_io") {
595  type = WatchPoint::WRITE_IO;
596  max = 0x100;
597  } else if (typeStr == "read_mem") {
598  type = WatchPoint::READ_MEM;
599  max = 0x10000;
600  } else if (typeStr == "write_mem") {
601  type = WatchPoint::WRITE_MEM;
602  max = 0x10000;
603  } else {
604  throw CommandException("Invalid type: " + typeStr);
605  }
606  if (tokens[3].getListLength() == 2) {
607  beginAddr = tokens[3].getListIndex(0).getInt();
608  endAddr = tokens[3].getListIndex(1).getInt();
609  if (endAddr < beginAddr) {
610  throw CommandException(
611  "Not a valid range: end address may "
612  "not be smaller than begin address.");
613  }
614  } else {
615  beginAddr = endAddr = tokens[3].getInt();
616  }
617  if (endAddr >= max) {
618  throw CommandException("Invalid address: out of range");
619  }
620  break;
621  }
622  default:
623  if (tokens.size() < 4) {
624  throw CommandException("Too few arguments.");
625  } else {
626  throw CommandException("Too many arguments.");
627  }
628  }
629  unsigned id = debugger.setWatchPoint(
630  command, condition, type, beginAddr, endAddr);
631  result.setString(StringOp::Builder() << "wp#" << id);
632 }
633 
634 void DebugCmd::removeWatchPoint(const vector<TclObject>& tokens,
635  TclObject& /*result*/)
636 {
637  if (tokens.size() != 3) {
638  throw SyntaxError();
639  }
640  string_ref tmp = tokens[2].getString();
641  if (tmp.starts_with("wp#")) {
642  // remove by id
643  unsigned id = stoi(tmp.substr(3));
644  auto& interface = debugger.motherBoard.getCPUInterface();
645  for (auto& wp : interface.getWatchPoints()) {
646  if (wp->getId() == id) {
647  interface.removeWatchPoint(wp);
648  return;
649  }
650  }
651  }
652  throw CommandException("No such watchpoint: " + tmp);
653 }
654 
655 void DebugCmd::listWatchPoints(const vector<TclObject>& /*tokens*/,
656  TclObject& result)
657 {
658  string res;
659  auto& interface = debugger.motherBoard.getCPUInterface();
660  for (auto& wp : interface.getWatchPoints()) {
661  TclObject line(result.getInterpreter());
662  line.addListElement(StringOp::Builder() << "wp#" << wp->getId());
663  string type;
664  switch (wp->getType()) {
665  case WatchPoint::READ_IO:
666  type = "read_io";
667  break;
669  type = "write_io";
670  break;
672  type = "read_mem";
673  break;
675  type = "write_mem";
676  break;
677  default:
678  UNREACHABLE; break;
679  }
680  line.addListElement(type);
681  unsigned beginAddr = wp->getBeginAddress();
682  unsigned endAddr = wp->getEndAddress();
683  if (beginAddr == endAddr) {
684  line.addListElement("0x" + StringOp::toHexString(beginAddr, 4));
685  } else {
686  TclObject range(result.getInterpreter());
687  range.addListElement("0x" + StringOp::toHexString(beginAddr, 4));
688  range.addListElement("0x" + StringOp::toHexString(endAddr, 4));
689  line.addListElement(range);
690  }
691  line.addListElement(wp->getCondition());
692  line.addListElement(wp->getCommand());
693  res += line.getString() + '\n';
694  }
695  result.setString(res);
696 }
697 
698 
699 void DebugCmd::setCondition(const vector<TclObject>& tokens,
700  TclObject& result)
701 {
702  shared_ptr<DebugCondition> dc;
703  TclObject command (result.getInterpreter(), "debug break");
704  TclObject condition(result.getInterpreter());
705 
706  switch (tokens.size()) {
707  case 4: // command
708  command = tokens[3];
709  command.checkCommand();
710  // fall-through
711  case 3: // condition
712  condition = tokens[2];
713  if (!condition.getString().empty()) {
714  condition.checkExpression();
715  }
716  dc = make_shared<DebugCondition>(cliComm, command, condition);
717  break;
718  default:
719  if (tokens.size() < 3) {
720  throw CommandException("Too few arguments.");
721  } else {
722  throw CommandException("Too many arguments.");
723  }
724  }
725  result.setString(StringOp::Builder() << "cond#" << dc->getId());
726  debugger.motherBoard.getCPUInterface().setCondition(dc);
727 }
728 
729 void DebugCmd::removeCondition(const vector<TclObject>& tokens,
730  TclObject& /*result*/)
731 {
732  if (tokens.size() != 3) {
733  throw SyntaxError();
734  }
735 
736  string_ref tmp = tokens[2].getString();
737  if (tmp.starts_with("cond#")) {
738  // remove by id
739  unsigned id = stoi(tmp.substr(5));
740  auto& interface = debugger.motherBoard.getCPUInterface();
741  for (auto& c : interface.getConditions()) {
742  if (c->getId() == id) {
743  interface.removeCondition(*c);
744  return;
745  }
746  }
747  }
748  throw CommandException("No such condition: " + tmp);
749 }
750 
751 void DebugCmd::listConditions(const vector<TclObject>& /*tokens*/,
752  TclObject& result)
753 {
754  string res;
755  auto& interface = debugger.motherBoard.getCPUInterface();
756  for (auto& c : interface.getConditions()) {
757  TclObject line(result.getInterpreter());
758  line.addListElement(StringOp::Builder() << "cond#" << c->getId());
759  line.addListElement(c->getCondition());
760  line.addListElement(c->getCommand());
761  res += line.getString() + '\n';
762  }
763  result.setString(res);
764 }
765 
766 
767 void DebugCmd::probe(const vector<TclObject>& tokens,
768  TclObject& result)
769 {
770  if (tokens.size() < 3) {
771  throw CommandException("Missing argument");
772  }
773  string_ref subCmd = tokens[2].getString();
774  if (subCmd == "list") {
775  probeList(tokens, result);
776  } else if (subCmd == "desc") {
777  probeDesc(tokens, result);
778  } else if (subCmd == "read") {
779  probeRead(tokens, result);
780  } else if (subCmd == "set_bp") {
781  probeSetBreakPoint(tokens, result);
782  } else if (subCmd == "remove_bp") {
783  probeRemoveBreakPoint(tokens, result);
784  } else if (subCmd == "list_bp") {
785  probeListBreakPoints(tokens, result);
786  } else {
787  throw SyntaxError();
788  }
789 }
790 void DebugCmd::probeList(const vector<TclObject>& /*tokens*/,
791  TclObject& result)
792 {
793  result.addListElements(keys(debugger.probes));
794 }
795 void DebugCmd::probeDesc(const vector<TclObject>& tokens,
796  TclObject& result)
797 {
798  if (tokens.size() != 4) {
799  throw SyntaxError();
800  }
801  ProbeBase& probe = debugger.getProbe(tokens[3].getString());
802  result.setString(probe.getDescription());
803 }
804 void DebugCmd::probeRead(const vector<TclObject>& tokens,
805  TclObject& result)
806 {
807  if (tokens.size() != 4) {
808  throw SyntaxError();
809  }
810  ProbeBase& probe = debugger.getProbe(tokens[3].getString());
811  result.setString(probe.getValue());
812 }
813 void DebugCmd::probeSetBreakPoint(const vector<TclObject>& tokens,
814  TclObject& result)
815 {
816  TclObject command (result.getInterpreter(), "debug break");
817  TclObject condition(result.getInterpreter());
818  ProbeBase* probe;
819 
820  switch (tokens.size()) {
821  case 6: // command
822  command = tokens[5];
823  command.checkCommand();
824  // fall-through
825  case 5: // condition
826  condition = tokens[4];
827  if (!condition.getString().empty()) {
828  condition.checkExpression();
829  }
830  // fall-through
831  case 4: { // probe
832  probe = &debugger.getProbe(tokens[3].getString());
833  break;
834  }
835  default:
836  if (tokens.size() < 4) {
837  throw CommandException("Too few arguments.");
838  } else {
839  throw CommandException("Too many arguments.");
840  }
841  }
842 
843  unsigned id = debugger.insertProbeBreakPoint(command, condition, *probe);
844  result.setString(StringOp::Builder() << "pp#" << id);
845 }
846 void DebugCmd::probeRemoveBreakPoint(const vector<TclObject>& tokens,
847  TclObject& /*result*/)
848 {
849  if (tokens.size() != 4) {
850  throw SyntaxError();
851  }
852  debugger.removeProbeBreakPoint(tokens[3].getString());
853 }
854 void DebugCmd::probeListBreakPoints(const vector<TclObject>& /*tokens*/,
855  TclObject& result)
856 {
857  string res;
858  for (auto& p : debugger.probeBreakPoints) {
859  TclObject line(result.getInterpreter());
860  line.addListElement(StringOp::Builder() << "pp#" << p->getId());
861  line.addListElement(p->getProbe().getName());
862  line.addListElement(p->getCondition());
863  line.addListElement(p->getCommand());
864  res += line.getString() + '\n';
865  }
866  result.setString(res);
867 }
868 
869 string DebugCmd::help(const vector<string>& tokens) const
870 {
871  static const string generalHelp =
872  "debug <subcommand> [<arguments>]\n"
873  " Possible subcommands are:\n"
874  " list returns a list of all debuggables\n"
875  " desc returns a description of this debuggable\n"
876  " size returns the size of this debuggable\n"
877  " read read a byte from a debuggable\n"
878  " write write a byte to a debuggable\n"
879  " read_block read a whole block at once\n"
880  " write_block write a whole block at once\n"
881  " set_bp insert a new breakpoint\n"
882  " remove_bp remove a certain breakpoint\n"
883  " list_bp list the active breakpoints\n"
884  " set_watchpoint insert a new watchpoint\n"
885  " remove_watchpoint remove a certain watchpoint\n"
886  " list_watchpoints list the active watchpoints\n"
887  " set_condition insert a new condition\n"
888  " remove_condition remove a certain condition\n"
889  " list_conditions list the active conditions\n"
890  " probe probe related subcommands\n"
891  " cont continue execution after break\n"
892  " step execute one instruction\n"
893  " break break CPU at current position\n"
894  " breaked query CPU breaked status\n"
895  " disasm disassemble instructions\n"
896  " The arguments are specific for each subcommand.\n"
897  " Type 'help debug <subcommand>' for help about a specific subcommand.\n";
898 
899  static const string listHelp =
900  "debug list\n"
901  " Returns a list with the names of all 'debuggables'.\n"
902  " These names are used in other debug subcommands.\n";
903  static const string descHelp =
904  "debug desc <name>\n"
905  " Returns a description for the debuggable with given name.\n";
906  static const string sizeHelp =
907  "debug size <name>\n"
908  " Returns the size (in bytes) of the debuggable with given name.\n";
909  static const string readHelp =
910  "debug read <name> <addr>\n"
911  " Read a byte at offset <addr> from the given debuggable.\n"
912  " The offset must be smaller than the value returned from the "
913  "'size' subcommand\n"
914  " Note that openMSX comes with a bunch of Tcl scripts that make "
915  "some of the debug reads much more convenient (e.g. reading from "
916  "Z80 or VDP registers). See the Console Command Reference for more "
917  "details about these.\n";
918  static const string writeHelp =
919  "debug write <name> <addr> <val>\n"
920  " Write a byte to the given debuggable at a certain offset.\n"
921  " The offset must be smaller than the value returned from the "
922  "'size' subcommand\n";
923  static const string readBlockHelp =
924  "debug read_block <name> <addr> <size>\n"
925  " Read a whole block at once. This is equivalent with repeated "
926  "invocations of the 'read' subcommand, but using this subcommand "
927  "may be faster. The result is a Tcl binary string (see Tcl manual).\n"
928  " The block is specified as size/offset in the debuggable. The "
929  "complete block must fit in the debuggable (see the 'size' "
930  "subcommand).\n";
931  static const string writeBlockHelp =
932  "debug write_block <name> <addr> <values>\n"
933  " Write a whole block at once. This is equivalent with repeated "
934  "invocations of the 'write' subcommand, but using this subcommand "
935  "may be faster. The <values> argument must be a Tcl binary string "
936  "(see Tcl manual).\n"
937  " The block has a size and an offset in the debuggable. The "
938  "complete block must fit in the debuggable (see the 'size' "
939  "subcommand).\n";
940  static const string setBpHelp =
941  "debug set_bp <addr> [<cond>] [<cmd>]\n"
942  " Insert a new breakpoint at given address. When the CPU is about "
943  "to execute the instruction at this address, execution will be "
944  "breaked. At least this is the default behaviour, see next "
945  "paragraphs.\n"
946  " Optionally you can specify a condition. When the CPU reaches "
947  "the breakpoint this condition is evaluated, only when the condition "
948  "evaluated to true execution will be breaked.\n"
949  " A condition must be specified as a Tcl expression. For example\n"
950  " debug set_bp 0xf37d {[reg C] == 0x2F}\n"
951  " This breaks on address 0xf37d but only when Z80 register C has the "
952  "value 0x2F.\n"
953  " Also optionally you can specify a command that should be "
954  "executed when the breakpoint is reached (and condition is true). "
955  "By default this command is 'debug break'.\n"
956  " The result of this command is a breakpoint ID. This ID can "
957  "later be used to remove this breakpoint again.\n";
958  static const string removeBpHelp =
959  "debug remove_bp <id>\n"
960  " Remove the breakpoint with given ID again. You can use the "
961  "'list_bp' subcommand to see all valid IDs.\n";
962  static const string listBpHelp =
963  "debug list_bp\n"
964  " Lists all active breakpoints. The result is printed in 4 "
965  "columns. The first column contains the breakpoint ID. The "
966  "second one has the address. The third has the condition "
967  "(default condition is empty). And the last column contains "
968  "the command that will be executed (default is 'debug break').\n";
969  static const string setWatchPointHelp =
970  "debug set_watchpoint <type> <region> [<cond>] [<cmd>]\n"
971  " Insert a new watchpoint of given type on the given region, "
972  "there can be an optional condition and alternative command. See "
973  "the 'set_bp' subcommand for details about these last two.\n"
974  " Type must be one of the following:\n"
975  " read_io break when CPU reads from given IO port(s)\n"
976  " write_io break when CPU writes to given IO port(s)\n"
977  " read_mem break when CPU reads from given memory location(s)\n"
978  " write_mem break when CPU writes to given memory location(s)\n"
979  " Region is either a single value, this corresponds to a single "
980  "memory location or IO port. Otherwise region must be a list of "
981  "two values (enclosed in braces) that specify a begin and end "
982  "point of a whole memory region or a range of IO ports.\n"
983  "During the execution of <cmd>, the following global Tcl "
984  "variables are set:\n"
985  " ::wp_last_address this is the actual address of the mem/io "
986  "read/write that triggered the watchpoint\n"
987  " ::wp_last_value this is the actual value that was written "
988  "by the mem/io write that triggered the watchpoint\n"
989  "Examples:\n"
990  " debug set_watchpoint write_io 0x99 {[reg A] == 0x81}\n"
991  " debug set_watchpoint read_mem {0xfbe5 0xfbef}\n";
992  static const string removeWatchPointHelp =
993  "debug remove_watchpoint <id>\n"
994  " Remove the watchpoint with given ID again. You can use the "
995  "'list_watchpoints' subcommand to see all valid IDs.\n";
996  static const string listWatchPointsHelp =
997  "debug list_watchpoints\n"
998  " Lists all active watchpoints. The result is similar to the "
999  "'list_bp' subcommand, but there is an extra column (2nd column) "
1000  "that contains the type of the watchpoint.\n";
1001  static const string setCondHelp =
1002  "debug set_condition <cond> [<cmd>]\n"
1003  " Insert a new condition. These are much like breakpoints, "
1004  "except that they are checked before every instruction "
1005  "(breakpoints are tied to a specific address).\n"
1006  " Conditions will slow down simulation MUCH more than "
1007  "breakpoints. So only use them when you don't care about "
1008  "simulation speed (when you're debugging this is usually not "
1009  "a problem).\n"
1010  " See 'help debug set_bp' for more details.\n";
1011  static const string removeCondHelp =
1012  "debug remove_condition <id>\n"
1013  " Remove the condition with given ID again. You can use the "
1014  "'list_conditions' subcommand to see all valid IDs.\n";
1015  static const string listCondHelp =
1016  "debug list_conditions\n"
1017  " Lists all active conditions. The result is similar to the "
1018  "'list_bp' subcommand, but without the 2nd column that would "
1019  "show the address.\n";
1020  static const string probeHelp =
1021  "debug probe <subcommand> [<arguments>]\n"
1022  " Possible subcommands are:\n"
1023  " list returns a list of all probes\n"
1024  " desc <probe> returns a description of this probe\n"
1025  " read <probe> returns the current value of this probe\n"
1026  " set_bp <probe> [<cond>] [<cmd>] set a breakpoint on the given probe\n"
1027  " remove_bp <id> remove the given breakpoint\n"
1028  " list_bp returns a list of breakpoints that are set on probes\n";
1029  static const string contHelp =
1030  "debug cont\n"
1031  " Continue execution after CPU was breaked.\n";
1032  static const string stepHelp =
1033  "debug step\n"
1034  " Execute one instruction. This command is only meaningful in "
1035  "break mode.\n";
1036  static const string breakHelp =
1037  "debug break\n"
1038  " Immediately break CPU execution. When CPU was already breaked "
1039  "this command has no effect.\n";
1040  static const string breakedHelp =
1041  "debug breaked\n"
1042  " Query the CPU breaked status. Returns '1' when CPU was "
1043  "breaked, '0' otherwise.\n";
1044  static const string disasmHelp =
1045  "debug disasm <addr>\n"
1046  " Disassemble the instruction at the given address. The result "
1047  "is a Tcl list. The first element in the list contains a textual "
1048  "representation of the instruction, the next elements contain the "
1049  "bytes that make up this instruction (thus the length of the "
1050  "resulting list can be used to derive the number of bytes in the "
1051  "instruction).\n"
1052  " Note that openMSX comes with a 'disasm' Tcl script that is much "
1053  "more convenient to use than this subcommand.";
1054  static const string unknownHelp =
1055  "Unknown subcommand, use 'help debug' to see a list of valid "
1056  "subcommands.\n";
1057 
1058  if (tokens.size() == 1) {
1059  return generalHelp;
1060  } else if (tokens[1] == "list") {
1061  return listHelp;
1062  } else if (tokens[1] == "desc") {
1063  return descHelp;
1064  } else if (tokens[1] == "size") {
1065  return sizeHelp;
1066  } else if (tokens[1] == "read") {
1067  return readHelp;
1068  } else if (tokens[1] == "write") {
1069  return writeHelp;
1070  } else if (tokens[1] == "read_block") {
1071  return readBlockHelp;
1072  } else if (tokens[1] == "write_block") {
1073  return writeBlockHelp;
1074  } else if (tokens[1] == "set_bp") {
1075  return setBpHelp;
1076  } else if (tokens[1] == "remove_bp") {
1077  return removeBpHelp;
1078  } else if (tokens[1] == "list_bp") {
1079  return listBpHelp;
1080  } else if (tokens[1] == "set_watchpoint") {
1081  return setWatchPointHelp;
1082  } else if (tokens[1] == "remove_watchpoint") {
1083  return removeWatchPointHelp;
1084  } else if (tokens[1] == "list_watchpoints") {
1085  return listWatchPointsHelp;
1086  } else if (tokens[1] == "set_condition") {
1087  return setCondHelp;
1088  } else if (tokens[1] == "remove_condition") {
1089  return removeCondHelp;
1090  } else if (tokens[1] == "list_conditions") {
1091  return listCondHelp;
1092  } else if (tokens[1] == "probe") {
1093  return probeHelp;
1094  } else if (tokens[1] == "cont") {
1095  return contHelp;
1096  } else if (tokens[1] == "step") {
1097  return stepHelp;
1098  } else if (tokens[1] == "break") {
1099  return breakHelp;
1100  } else if (tokens[1] == "breaked") {
1101  return breakedHelp;
1102  } else if (tokens[1] == "disasm") {
1103  return disasmHelp;
1104  } else {
1105  return unknownHelp;
1106  }
1107 }
1108 
1109 vector<string> DebugCmd::getBreakPointIds() const
1110 {
1111  vector<string> bpids;
1112  auto& interface = debugger.motherBoard.getCPUInterface();
1113  for (auto& p : interface.getBreakPoints()) {
1114  bpids.push_back(StringOp::Builder() << "bp#" << p.second->getId());
1115  }
1116  return bpids;
1117 }
1118 vector<string> DebugCmd::getWatchPointIds() const
1119 {
1120  vector<string> wpids;
1121  auto& interface = debugger.motherBoard.getCPUInterface();
1122  for (auto& w : interface.getWatchPoints()) {
1123  wpids.push_back(StringOp::Builder() << "wp#" << w->getId());
1124  }
1125  return wpids;
1126 }
1127 vector<string> DebugCmd::getConditionIds() const
1128 {
1129  vector<string> condids;
1130  auto& interface = debugger.motherBoard.getCPUInterface();
1131  for (auto& c : interface.getConditions()) {
1132  condids.push_back(StringOp::Builder() << "cond#" << c->getId());
1133  }
1134  return condids;
1135 }
1136 
1137 void DebugCmd::tabCompletion(vector<string>& tokens) const
1138 {
1139  static const char* const singleArgCmds[] = {
1140  "list", "step", "cont", "break", "breaked",
1141  "list_bp", "list_watchpoints", "list_conditions",
1142  };
1143  static const char* const debuggableArgCmds[] = {
1144  "desc", "size", "read", "read_block",
1145  "write", "write_block",
1146  };
1147  static const char* const otherCmds[] = {
1148  "disasm", "set_bp", "remove_bp", "set_watchpoint",
1149  "remove_watchpoint", "set_condition", "remove_condition",
1150  "probe",
1151  };
1152  switch (tokens.size()) {
1153  case 2: {
1154  vector<const char*> cmds;
1155  cmds.insert(end(cmds), begin(singleArgCmds), end(singleArgCmds));
1156  cmds.insert(end(cmds), begin(debuggableArgCmds), end(debuggableArgCmds));
1157  cmds.insert(end(cmds), begin(otherCmds), end(otherCmds));
1158  completeString(tokens, cmds);
1159  break;
1160  }
1161  case 3:
1162  if (find(begin(singleArgCmds), end(singleArgCmds), tokens[1]) ==
1163  end(singleArgCmds)) {
1164  // this command takes (an) argument(s)
1165  if (find(begin(debuggableArgCmds), end(debuggableArgCmds),
1166  tokens[1]) !=
1167  end(debuggableArgCmds)) {
1168  // it takes a debuggable here
1169  completeString(tokens, keys(debugger.debuggables));
1170  } else if (tokens[1] == "remove_bp") {
1171  // this one takes a bp id
1172  completeString(tokens, getBreakPointIds());
1173  } else if (tokens[1] == "remove_watchpoint") {
1174  // this one takes a wp id
1175  completeString(tokens, getWatchPointIds());
1176  } else if (tokens[1] == "remove_condition") {
1177  // this one takes a cond id
1178  completeString(tokens, getConditionIds());
1179  } else if (tokens[1] == "set_watchpoint") {
1180  static const char* const types[] = {
1181  "write_io", "write_mem",
1182  "read_io", "read_mem",
1183  };
1184  completeString(tokens, types);
1185  } else if (tokens[1] == "probe") {
1186  static const char* const subCmds[] = {
1187  "list", "desc", "read", "set_bp",
1188  "remove_bp", "list_bp",
1189  };
1190  completeString(tokens, subCmds);
1191  }
1192  }
1193  break;
1194  case 4:
1195  if ((tokens[1] == "probe") &&
1196  ((tokens[2] == "desc") || (tokens[2] == "read") ||
1197  (tokens[2] == "set_bp"))) {
1198  completeString(tokens, keys(debugger.probes));
1199  }
1200  break;
1201  }
1202 }
1203 
1204 } // namespace openmsx