30using std::string_view;
35 : motherBoard(motherBoard_)
36 , cmd(motherBoard.getCommandController(),
37 motherBoard.getStateChangeDistributor(),
38 motherBoard.getScheduler())
45 assert(debuggables.empty());
50 assert(!debuggables.contains(name));
51 debuggables.emplace_noDuplicateCheck(std::move(name), &debuggable);
56 assert(debuggables.contains(name));
57 assert(debuggables[name] == &debuggable); (void)debuggable;
58 debuggables.erase(name);
63 auto* v =
lookup(debuggables, name);
64 return v ? *v :
nullptr;
67Debuggable& Debugger::getDebuggable(string_view name)
78 assert(!probes.contains(probe.
getName()));
79 probes.insert_noDuplicateCheck(&probe);
84 assert(probes.contains(probe.
getName()));
90 auto it = probes.find(name);
91 return (it != std::end(probes)) ? *it :
nullptr;
94ProbeBase& Debugger::getProbe(string_view name)
103unsigned Debugger::insertProbeBreakPoint(
104 TclObject command, TclObject condition,
105 ProbeBase& probe,
bool once,
unsigned newId )
107 auto bp = std::make_unique<ProbeBreakPoint>(
108 std::move(command), std::move(condition), *
this, probe, once, newId);
109 unsigned result = bp->getId();
111 probeBreakPoints.push_back(std::move(bp));
117 if (name.starts_with(
"pp#")) {
119 if (
auto id = StringOp::stringToBase<10, unsigned>(name.substr(3))) {
121 it != std::end(probeBreakPoints)) {
128 throw CommandException(
"No such breakpoint: ", name);
131 auto it =
ranges::find(probeBreakPoints, name, [](
auto& bp) {
132 return bp->getProbe().getName();
134 if (it == std::end(probeBreakPoints)) {
135 throw CommandException(
136 "No (unconditional) breakpoint for probe: ", name);
149 [](
auto& v) {
return v.get(); }));
154 unsigned beginAddr,
unsigned endAddr,
155 bool once,
unsigned newId )
157 std::shared_ptr<WatchPoint> wp;
159 wp = std::make_shared<WatchIO>(
160 motherBoard, type, beginAddr, endAddr,
161 std::move(command), std::move(condition), once, newId);
163 wp = std::make_shared<WatchPoint>(
164 std::move(command), std::move(condition), type, beginAddr, endAddr, once, newId);
176 wp->getType(), wp->getBeginAddress(),
177 wp->getEndAddress(), wp->onlyOnce(),
182 assert(probeBreakPoints.empty());
183 for (
auto& bp : other.probeBreakPoints) {
185 insertProbeBreakPoint(bp->getCommandObj(),
186 bp->getConditionObj(),
187 *probe, bp->onlyOnce(),
201 unsigned addr = token.
getInt(interp);
202 if (addr >= 0x10000) {
205 return narrow_cast<word>(addr);
208Debugger::Cmd::Cmd(CommandController& commandController_,
209 StateChangeDistributor& stateChangeDistributor_,
210 Scheduler& scheduler_)
211 : RecordedCommand(commandController_, stateChangeDistributor_,
216bool Debugger::Cmd::needRecord(std::span<const TclObject> tokens)
const
222 if (tokens.size() < 2)
return false;
223 return tokens[1].getString() ==
one_of(
"write",
"write_block");
226void Debugger::Cmd::execute(
227 std::span<const TclObject> tokens, TclObject& result, EmuTime::param )
229 checkNumArgs(tokens, AtLeast{2},
"subcommand ?arg ...?");
230 executeSubCommand(tokens[1].getString(),
231 "read", [&]{ read(tokens, result); },
232 "read_block", [&]{ readBlock(tokens, result); },
233 "write", [&]{ write(tokens, result); },
234 "write_block", [&]{ writeBlock(tokens, result); },
235 "size", [&]{
size(tokens, result); },
236 "desc", [&]{ desc(tokens, result); },
237 "list", [&]{ list(result); },
238 "step", [&]{ debugger().motherBoard.getCPUInterface().doStep(); },
239 "cont", [&]{ debugger().motherBoard.getCPUInterface().doContinue(); },
240 "disasm", [&]{ debugger().cpu->disasmCommand(getInterpreter(), tokens, result); },
241 "break", [&]{ debugger().motherBoard.getCPUInterface().doBreak(); },
242 "breaked", [&]{ result = debugger().motherBoard.getCPUInterface().isBreaked(); },
243 "set_bp", [&]{ setBreakPoint(tokens, result); },
244 "remove_bp", [&]{ removeBreakPoint(tokens, result); },
245 "list_bp", [&]{ listBreakPoints(tokens, result); },
246 "set_watchpoint", [&]{ setWatchPoint(tokens, result); },
247 "remove_watchpoint", [&]{ removeWatchPoint(tokens, result); },
248 "list_watchpoints", [&]{ listWatchPoints(tokens, result); },
249 "set_condition", [&]{ setCondition(tokens, result); },
250 "remove_condition", [&]{ removeCondition(tokens, result); },
251 "list_conditions", [&]{ listConditions(tokens, result); },
252 "probe", [&]{ probe(tokens, result); },
253 "symbols", [&]{ symbols(tokens, result); });
256void Debugger::Cmd::list(TclObject& result)
258 result.addListElements(
view::keys(debugger().debuggables));
261void Debugger::Cmd::desc(std::span<const TclObject> tokens, TclObject& result)
263 checkNumArgs(tokens, 3,
"debuggable");
264 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
265 result = device.getDescription();
268void Debugger::Cmd::size(std::span<const TclObject> tokens, TclObject& result)
270 checkNumArgs(tokens, 3,
"debuggable");
271 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
272 result = device.getSize();
275void Debugger::Cmd::read(std::span<const TclObject> tokens, TclObject& result)
277 checkNumArgs(tokens, 4, Prefix{2},
"debuggable address");
278 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
279 unsigned addr = tokens[3].getInt(getInterpreter());
280 if (addr >= device.getSize()) {
281 throw CommandException(
"Invalid address");
283 result = device.read(addr);
286void Debugger::Cmd::readBlock(std::span<const TclObject> tokens, TclObject& result)
288 checkNumArgs(tokens, 5, Prefix{2},
"debuggable address size");
289 auto& interp = getInterpreter();
290 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
291 unsigned devSize = device.getSize();
292 unsigned addr = tokens[3].getInt(interp);
293 if (addr >= devSize) {
294 throw CommandException(
"Invalid address");
296 unsigned num = tokens[4].getInt(interp);
297 if (num > (devSize - addr)) {
298 throw CommandException(
"Invalid size");
301 MemBuffer<byte> buf(num);
302 for (
auto i :
xrange(num)) {
303 buf[i] = device.read(addr + i);
305 result = std::span<byte>{buf.data(), num};
308void Debugger::Cmd::write(std::span<const TclObject> tokens, TclObject& )
310 checkNumArgs(tokens, 5, Prefix{2},
"debuggable address value");
311 auto& interp = getInterpreter();
312 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
313 unsigned addr = tokens[3].getInt(interp);
314 if (addr >= device.getSize()) {
315 throw CommandException(
"Invalid address");
317 unsigned value = tokens[4].getInt(interp);
319 throw CommandException(
"Invalid value");
322 device.write(addr, narrow_cast<byte>(value));
325void Debugger::Cmd::writeBlock(std::span<const TclObject> tokens, TclObject& )
327 checkNumArgs(tokens, 5, Prefix{2},
"debuggable address values");
328 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
329 unsigned devSize = device.getSize();
330 unsigned addr = tokens[3].getInt(getInterpreter());
331 if (addr >= devSize) {
332 throw CommandException(
"Invalid address");
334 auto buf = tokens[4].getBinary();
335 if ((buf.size() + addr) > devSize) {
336 throw CommandException(
"Invalid size");
340 device.write(
unsigned(addr + i), buf[i]);
344void Debugger::Cmd::setBreakPoint(std::span<const TclObject> tokens, TclObject& result)
346 checkNumArgs(tokens, AtLeast{3},
"address ?-once? ?condition? ?command?");
347 TclObject command(
"debug break");
351 std::array info = {
flagArg(
"-once", once)};
352 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(2), info);
353 if ((arguments.size() < 1) || (arguments.size() > 3)) {
357 switch (arguments.size()) {
359 command = arguments[2];
362 condition = arguments[1];
365 word addr = getAddress(getInterpreter(), arguments[0]);
366 BreakPoint bp(addr, command, condition, once);
368 debugger().motherBoard.getCPUInterface().insertBreakPoint(std::move(bp));
374void Debugger::Cmd::removeBreakPoint(
375 std::span<const TclObject> tokens, TclObject& )
377 checkNumArgs(tokens, 3,
"id|address");
378 auto&
interface = debugger().motherBoard.getCPUInterface();
379 auto& breakPoints = interface.getBreakPoints();
381 string_view tmp = tokens[2].getString();
382 if (tmp.starts_with(
"bp#")) {
384 if (
auto id = StringOp::stringToBase<10, unsigned>(tmp.substr(3))) {
386 it != std::end(breakPoints)) {
387 interface.removeBreakPoint(*it);
391 throw CommandException(
"No such breakpoint: ", tmp);
394 word addr = getAddress(getInterpreter(), tokens[2]);
396 auto it = std::find_if(first, last, [](
auto& bp) {
397 return bp.getCondition().empty();
400 throw CommandException(
401 "No (unconditional) breakpoint at address: ", tmp);
403 interface.removeBreakPoint(*it);
407void Debugger::Cmd::listBreakPoints(
408 std::span<const TclObject> , TclObject& result)
411 auto&
interface = debugger().motherBoard.getCPUInterface();
412 for (
const auto& bp : interface.getBreakPoints()) {
415 tmpStrCat(
"0x", hex_string<4>(bp.getAddress())),
424void Debugger::Cmd::setWatchPoint(std::span<const TclObject> tokens, TclObject& result)
426 checkNumArgs(tokens, AtLeast{4}, Prefix{2},
"type address ?-once? ?condition? ?command?");
427 TclObject command(
"debug break");
429 unsigned beginAddr, endAddr;
433 std::array info = {
flagArg(
"-once", once)};
434 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(2), info);
435 if ((arguments.size() < 2) || (arguments.size() > 4)) {
439 switch (arguments.size()) {
441 command = arguments[3];
444 condition = arguments[2];
447 string_view typeStr = arguments[0].getString();
449 if (typeStr ==
"read_io") {
452 }
else if (typeStr ==
"write_io") {
455 }
else if (typeStr ==
"read_mem") {
458 }
else if (typeStr ==
"write_mem") {
462 throw CommandException(
"Invalid type: ", typeStr);
465 auto& interp = getInterpreter();
466 if (arguments[1].getListLength(interp) == 2) {
467 beginAddr = arguments[1].getListIndex(interp, 0).getInt(interp);
468 endAddr = arguments[1].getListIndex(interp, 1).getInt(interp);
469 if (endAddr < beginAddr) {
470 throw CommandException(
471 "Not a valid range: end address may "
472 "not be smaller than begin address.");
475 beginAddr = endAddr = arguments[1].getInt(interp);
477 if (endAddr >= max) {
478 throw CommandException(
"Invalid address: out of range");
485 unsigned id = debugger().setWatchPoint(
486 command, condition, type, beginAddr, endAddr, once);
490void Debugger::Cmd::removeWatchPoint(
491 std::span<const TclObject> tokens, TclObject& )
493 checkNumArgs(tokens, 3,
"id");
494 string_view tmp = tokens[2].getString();
495 if (tmp.starts_with(
"wp#")) {
497 if (
auto id = StringOp::stringToBase<10, unsigned>(tmp.substr(3))) {
498 auto&
interface = debugger().motherBoard.getCPUInterface();
499 for (
auto& wp : interface.getWatchPoints()) {
500 if (wp->getId() == *
id) {
501 interface.removeWatchPoint(wp);
507 throw CommandException(
"No such watchpoint: ", tmp);
510void Debugger::Cmd::listWatchPoints(
511 std::span<const TclObject> , TclObject& result)
514 auto&
interface = debugger().motherBoard.getCPUInterface();
515 for (
const auto& wp : interface.getWatchPoints()) {
518 switch (wp->getType()) {
534 line.addListElement(type);
535 unsigned beginAddr = wp->getBeginAddress();
536 unsigned endAddr = wp->getEndAddress();
537 if (beginAddr == endAddr) {
538 line.addListElement(
tmpStrCat(
"0x", hex_string<4>(beginAddr)));
541 tmpStrCat(
"0x", hex_string<4>(beginAddr)),
542 tmpStrCat(
"0x", hex_string<4>(endAddr))));
544 line.addListElement(wp->getCondition(),
552void Debugger::Cmd::setCondition(std::span<const TclObject> tokens, TclObject& result)
554 checkNumArgs(tokens, AtLeast{3},
"condition ?-once? ?command?");
555 TclObject command(
"debug break");
559 std::array info = {
flagArg(
"-once", once)};
560 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(2), info);
561 if ((arguments.size() < 1) || (arguments.size() > 2)) {
565 switch (arguments.size()) {
567 command = arguments[1];
570 condition = arguments[0];
571 DebugCondition dc(command, condition, once);
573 debugger().motherBoard.getCPUInterface().setCondition(std::move(dc));
579void Debugger::Cmd::removeCondition(
580 std::span<const TclObject> tokens, TclObject& )
582 checkNumArgs(tokens, 3,
"id");
584 string_view tmp = tokens[2].getString();
585 if (tmp.starts_with(
"cond#")) {
587 if (
auto id = StringOp::stringToBase<10, unsigned>(tmp.substr(5))) {
588 auto&
interface = debugger().motherBoard.getCPUInterface();
589 for (
auto& c : interface.getConditions()) {
590 if (c.getId() == *
id) {
591 interface.removeCondition(c);
597 throw CommandException(
"No such condition: ", tmp);
600void Debugger::Cmd::listConditions(
601 std::span<const TclObject> , TclObject& result)
604 auto&
interface = debugger().motherBoard.getCPUInterface();
605 for (
const auto& c : interface.getConditions()) {
615void Debugger::Cmd::probe(std::span<const TclObject> tokens, TclObject& result)
617 checkNumArgs(tokens, AtLeast{3},
"subcommand ?arg ...?");
618 executeSubCommand(tokens[2].getString(),
619 "list", [&]{ probeList(tokens, result); },
620 "desc", [&]{ probeDesc(tokens, result); },
621 "read", [&]{ probeRead(tokens, result); },
622 "set_bp", [&]{ probeSetBreakPoint(tokens, result); },
623 "remove_bp", [&]{ probeRemoveBreakPoint(tokens, result); },
624 "list_bp", [&]{ probeListBreakPoints(tokens, result); });
626void Debugger::Cmd::probeList(std::span<const TclObject> , TclObject& result)
629 [](
auto* p) {
return p->getName(); }));
631void Debugger::Cmd::probeDesc(std::span<const TclObject> tokens, TclObject& result)
633 checkNumArgs(tokens, 4,
"probe");
634 result = debugger().getProbe(tokens[3].getString()).getDescription();
636void Debugger::Cmd::probeRead(std::span<const TclObject> tokens, TclObject& result)
638 checkNumArgs(tokens, 4,
"probe");
639 result = debugger().getProbe(tokens[3].getString()).getValue();
641void Debugger::Cmd::probeSetBreakPoint(
642 std::span<const TclObject> tokens, TclObject& result)
644 checkNumArgs(tokens, AtLeast{4},
"probe ?-once? ?condition? ?command?");
645 TclObject command(
"debug break");
650 std::array info = {
flagArg(
"-once", once)};
651 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(3), info);
652 if ((arguments.size() < 1) || (arguments.size() > 3)) {
656 switch (arguments.size()) {
658 command = arguments[2];
661 condition = arguments[1];
664 p = &debugger().getProbe(arguments[0].getString());
671 unsigned id = debugger().insertProbeBreakPoint(command, condition, *p, once);
674void Debugger::Cmd::probeRemoveBreakPoint(
675 std::span<const TclObject> tokens, TclObject& )
677 checkNumArgs(tokens, 4,
"id");
678 debugger().removeProbeBreakPoint(tokens[3].getString());
680void Debugger::Cmd::probeListBreakPoints(
681 std::span<const TclObject> , TclObject& result)
684 for (
auto& p : debugger().probeBreakPoints) {
686 p->getProbe().getName(),
694SymbolManager& Debugger::Cmd::getSymbolManager()
696 return debugger().getMotherBoard().getReactor().getSymbolManager();
698void Debugger::Cmd::symbols(std::span<const TclObject> tokens, TclObject& result)
700 checkNumArgs(tokens, AtLeast{3},
"subcommand ?arg ...?");
701 executeSubCommand(tokens[2].getString(),
702 "types", [&]{ symbolsTypes(tokens, result); },
703 "load", [&]{ symbolsLoad(tokens, result); },
704 "remove", [&]{ symbolsRemove(tokens, result); },
705 "files", [&]{ symbolsFiles(tokens, result); },
706 "lookup", [&]{ symbolsLookup(tokens, result); });
708void Debugger::Cmd::symbolsTypes(std::span<const TclObject> tokens, TclObject& result)
710 checkNumArgs(tokens, 3,
"");
717void Debugger::Cmd::symbolsLoad(std::span<const TclObject> tokens, TclObject& )
719 checkNumArgs(tokens, Between{4, 5},
"filename ?type?");
720 auto filename = std::string(tokens[3].getString());
723 auto str = tokens[4].getString();
725 if (!
t)
throw CommandException(
"Invalid symbol file type: ", str);
730 }
catch (MSXException& e) {
731 throw CommandException(
"Couldn't load symbol file '", filename,
"': ",
e.getMessage());
734void Debugger::Cmd::symbolsRemove(std::span<const TclObject> tokens, TclObject& )
736 checkNumArgs(tokens, 4,
"filename");
737 auto filename = tokens[3].getString();
738 getSymbolManager().removeFile(filename);
740void Debugger::Cmd::symbolsFiles(std::span<const TclObject> tokens, TclObject& result)
742 checkNumArgs(tokens, 3,
"");
743 for (
const auto& file : getSymbolManager().getFiles()) {
744 result.addListElement(TclObject(TclObject::MakeDictTag{},
745 "filename", file.filename,
749void Debugger::Cmd::symbolsLookup(std::span<const TclObject> tokens, TclObject& result)
751 std::string_view filename;
752 std::string_view name;
753 std::optional<int> value;
754 std::array info = {
valueArg(
"-filename", filename),
757 auto args =
parseTclArgs(getInterpreter(), tokens.subspan(3), info);
758 if (!args.empty())
throw SyntaxError();
759 if (!filename.empty() && !name.empty() && value)
throw SyntaxError();
761 for (
const auto& file : getSymbolManager().getFiles()) {
762 if (!filename.empty() && (file.filename != filename))
continue;
763 for (
const auto& sym : file.symbols) {
764 if (!name.empty() && (name != sym.name))
continue;
765 if (value && (sym.value != *value))
continue;
768 if (filename.empty()) elem.addDictKeyValue(
"filename", file.filename);
769 if (name.empty()) elem.addDictKeyValue(
"name", sym.name);
770 if (!value) elem.addDictKeyValue(
"value", sym.value);
771 result.addListElement(std::move(elem));
776string Debugger::Cmd::help(std::span<const TclObject> tokens)
const
779 "debug <subcommand> [<arguments>]\n"
780 " Possible subcommands are:\n"
781 " list returns a list of all debuggables\n"
782 " desc returns a description of this debuggable\n"
783 " size returns the size of this debuggable\n"
784 " read read a byte from a debuggable\n"
785 " write write a byte to a debuggable\n"
786 " read_block read a whole block at once\n"
787 " write_block write a whole block at once\n"
788 " set_bp insert a new breakpoint\n"
789 " remove_bp remove a certain breakpoint\n"
790 " list_bp list the active breakpoints\n"
791 " set_watchpoint insert a new watchpoint\n"
792 " remove_watchpoint remove a certain watchpoint\n"
793 " list_watchpoints list the active watchpoints\n"
794 " set_condition insert a new condition\n"
795 " remove_condition remove a certain condition\n"
796 " list_conditions list the active conditions\n"
797 " probe probe related subcommands\n"
798 " cont continue execution after break\n"
799 " step execute one instruction\n"
800 " break break CPU at current position\n"
801 " breaked query CPU breaked status\n"
802 " disasm disassemble instructions\n"
803 " symbols manage debug symbols\n"
804 " The arguments are specific for each subcommand.\n"
805 " Type 'help debug <subcommand>' for help about a specific subcommand.\n";
809 " Returns a list with the names of all 'debuggables'.\n"
810 " These names are used in other debug subcommands.\n";
812 "debug desc <name>\n"
813 " Returns a description for the debuggable with given name.\n";
815 "debug size <name>\n"
816 " Returns the size (in bytes) of the debuggable with given name.\n";
818 "debug read <name> <addr>\n"
819 " Read a byte at offset <addr> from the given debuggable.\n"
820 " The offset must be smaller than the value returned from the "
821 "'size' subcommand\n"
822 " Note that openMSX comes with a bunch of Tcl scripts that make "
823 "some of the debug reads much more convenient (e.g. reading from "
824 "Z80 or VDP registers). See the Console Command Reference for more "
825 "details about these.\n";
827 "debug write <name> <addr> <val>\n"
828 " Write a byte to the given debuggable at a certain offset.\n"
829 " The offset must be smaller than the value returned from the "
830 "'size' subcommand\n";
832 "debug read_block <name> <addr> <size>\n"
833 " Read a whole block at once. This is equivalent with repeated "
834 "invocations of the 'read' subcommand, but using this subcommand "
835 "may be faster. The result is a Tcl binary string (see Tcl manual).\n"
836 " The block is specified as size/offset in the debuggable. The "
837 "complete block must fit in the debuggable (see the 'size' "
839 auto writeBlockHelp =
840 "debug write_block <name> <addr> <values>\n"
841 " Write a whole block at once. This is equivalent with repeated "
842 "invocations of the 'write' subcommand, but using this subcommand "
843 "may be faster. The <values> argument must be a Tcl binary string "
844 "(see Tcl manual).\n"
845 " The block has a size and an offset in the debuggable. The "
846 "complete block must fit in the debuggable (see the 'size' "
849 "debug set_bp [-once] <addr> [<cond>] [<cmd>]\n"
850 " Insert a new breakpoint at given address. When the CPU is about "
851 "to execute the instruction at this address, execution will be "
852 "breaked. At least this is the default behaviour, see next "
854 " When the -once flag is given, the breakpoint is automatically "
855 "removed after it triggered. In other words: it only triggers once.\n"
856 " Optionally you can specify a condition. When the CPU reaches "
857 "the breakpoint this condition is evaluated, only when the condition "
858 "evaluated to true execution will be breaked.\n"
859 " A condition must be specified as a Tcl expression. For example\n"
860 " debug set_bp 0xf37d {[reg C] == 0x2F}\n"
861 " This breaks on address 0xf37d but only when Z80 register C has the "
863 " Also optionally you can specify a command that should be "
864 "executed when the breakpoint is reached (and condition is true). "
865 "By default this command is 'debug break'.\n"
866 " The result of this command is a breakpoint ID. This ID can "
867 "later be used to remove this breakpoint again.\n";
869 "debug remove_bp <id>\n"
870 " Remove the breakpoint with given ID again. You can use the "
871 "'list_bp' subcommand to see all valid IDs.\n";
874 " Lists all active breakpoints. The result is printed in 4 "
875 "columns. The first column contains the breakpoint ID. The "
876 "second one has the address. The third has the condition "
877 "(default condition is empty). And the last column contains "
878 "the command that will be executed (default is 'debug break').\n";
879 auto setWatchPointHelp =
880 "debug set_watchpoint [-once] <type> <region> [<cond>] [<cmd>]\n"
881 " Insert a new watchpoint of given type on the given region, "
882 "there can be an optional -once flag, a condition and alternative "
883 "command. See the 'set_bp' subcommand for details about these.\n"
884 " Type must be one of the following:\n"
885 " read_io break when CPU reads from given IO port(s)\n"
886 " write_io break when CPU writes to given IO port(s)\n"
887 " read_mem break when CPU reads from given memory location(s)\n"
888 " write_mem break when CPU writes to given memory location(s)\n"
889 " Region is either a single value, this corresponds to a single "
890 "memory location or IO port. Otherwise region must be a list of "
891 "two values (enclosed in braces) that specify a begin and end "
892 "point of a whole memory region or a range of IO ports.\n"
893 "During the execution of <cmd>, the following global Tcl "
894 "variables are set:\n"
895 " ::wp_last_address this is the actual address of the mem/io "
896 "read/write that triggered the watchpoint\n"
897 " ::wp_last_value this is the actual value that was written "
898 "by the mem/io write that triggered the watchpoint\n"
900 " debug set_watchpoint write_io 0x99 {[reg A] == 0x81}\n"
901 " debug set_watchpoint read_mem {0xfbe5 0xfbef}\n";
902 auto removeWatchPointHelp =
903 "debug remove_watchpoint <id>\n"
904 " Remove the watchpoint with given ID again. You can use the "
905 "'list_watchpoints' subcommand to see all valid IDs.\n";
906 auto listWatchPointsHelp =
907 "debug list_watchpoints\n"
908 " Lists all active watchpoints. The result is similar to the "
909 "'list_bp' subcommand, but there is an extra column (2nd column) "
910 "that contains the type of the watchpoint.\n";
912 "debug set_condition [-once] <cond> [<cmd>]\n"
913 " Insert a new condition. These are much like breakpoints, "
914 "except that they are checked before every instruction "
915 "(breakpoints are tied to a specific address).\n"
916 " Conditions will slow down simulation MUCH more than "
917 "breakpoints. So only use them when you don't care about "
918 "simulation speed (when you're debugging this is usually not "
920 " See 'help debug set_bp' for more details.\n";
921 auto removeCondHelp =
922 "debug remove_condition <id>\n"
923 " Remove the condition with given ID again. You can use the "
924 "'list_conditions' subcommand to see all valid IDs.\n";
926 "debug list_conditions\n"
927 " Lists all active conditions. The result is similar to the "
928 "'list_bp' subcommand, but without the 2nd column that would "
929 "show the address.\n";
931 "debug probe <subcommand> [<arguments>]\n"
932 " Possible subcommands are:\n"
933 " list returns a list of all probes\n"
934 " desc <probe> returns a description of this probe\n"
935 " read <probe> returns the current value of this probe\n"
936 " set_bp <probe> [-once] [<cond>] [<cmd>] set a breakpoint on the given probe\n"
937 " remove_bp <id> remove the given breakpoint\n"
938 " list_bp returns a list of breakpoints that are set on probes\n";
941 " Continue execution after CPU was breaked.\n";
944 " Execute one instruction. This command is only meaningful in "
948 " Immediately break CPU execution. When CPU was already breaked "
949 "this command has no effect.\n";
952 " Query the CPU breaked status. Returns '1' when CPU was "
953 "breaked, '0' otherwise.\n";
955 "debug disasm <addr>\n"
956 " Disassemble the instruction at the given address. The result "
957 "is a Tcl list. The first element in the list contains a textual "
958 "representation of the instruction, the next elements contain the "
959 "bytes that make up this instruction (thus the length of the "
960 "resulting list can be used to derive the number of bytes in the "
962 " Note that openMSX comes with a 'disasm' Tcl script that is much "
963 "more convenient to use than this subcommand.";
965 "debug symbols <subcommand> [<arguments>]\n"
966 " Possible subcommands are:\n"
967 " types returns a list of symbol file types\n"
968 " load <filename> [<type>] load a symbol file, auto-detect type if none is given\n"
969 " remove <filename> remove a previously loaded symbol file\n"
970 " files returns a list of all loaded symbol files\n"
971 " lookup [-filename <filename>] [-name <name>] [-value <value>]\n"
972 " returns a list of symbols in an optionally given file\n"
973 " and/or with an optionally given name\n"
974 " and/or with an optionally given value\n"
975 " Note: an easier syntax to lookup a symbol value based on the name is:\n"
978 "Unknown subcommand, use 'help debug' to see a list of valid "
981 if (tokens.size() == 1) {
983 }
else if (tokens[1] ==
"list") {
985 }
else if (tokens[1] ==
"desc") {
987 }
else if (tokens[1] ==
"size") {
989 }
else if (tokens[1] ==
"read") {
991 }
else if (tokens[1] ==
"write") {
993 }
else if (tokens[1] ==
"read_block") {
994 return readBlockHelp;
995 }
else if (tokens[1] ==
"write_block") {
996 return writeBlockHelp;
997 }
else if (tokens[1] ==
"set_bp") {
999 }
else if (tokens[1] ==
"remove_bp") {
1000 return removeBpHelp;
1001 }
else if (tokens[1] ==
"list_bp") {
1003 }
else if (tokens[1] ==
"set_watchpoint") {
1004 return setWatchPointHelp;
1005 }
else if (tokens[1] ==
"remove_watchpoint") {
1006 return removeWatchPointHelp;
1007 }
else if (tokens[1] ==
"list_watchpoints") {
1008 return listWatchPointsHelp;
1009 }
else if (tokens[1] ==
"set_condition") {
1011 }
else if (tokens[1] ==
"remove_condition") {
1012 return removeCondHelp;
1013 }
else if (tokens[1] ==
"list_conditions") {
1014 return listCondHelp;
1015 }
else if (tokens[1] ==
"probe") {
1017 }
else if (tokens[1] ==
"cont") {
1019 }
else if (tokens[1] ==
"step") {
1021 }
else if (tokens[1] ==
"break") {
1023 }
else if (tokens[1] ==
"breaked") {
1025 }
else if (tokens[1] ==
"disasm") {
1027 }
else if (tokens[1] ==
"symbols") {
1034std::vector<string> Debugger::Cmd::getBreakPointIds()
const
1037 debugger().motherBoard.getCPUInterface().getBreakPoints(),
1038 [](
auto& bp) { return strCat(
"bp#", bp.getId()); }));
1040std::vector<string> Debugger::Cmd::getWatchPointIds()
const
1043 debugger().motherBoard.getCPUInterface().getWatchPoints(),
1044 [](
auto& w) { return strCat(
"wp#", w->getId()); }));
1046std::vector<string> Debugger::Cmd::getConditionIds()
const
1049 debugger().motherBoard.getCPUInterface().getConditions(),
1050 [](
auto& c) { return strCat(
"cond#", c.getId()); }));
1053void Debugger::Cmd::tabCompletion(std::vector<string>& tokens)
const
1055 using namespace std::literals;
1056 static constexpr std::array singleArgCmds = {
1057 "list"sv,
"step"sv,
"cont"sv,
"break"sv,
"breaked"sv,
1058 "list_bp"sv,
"list_watchpoints"sv,
"list_conditions"sv,
1060 static constexpr std::array debuggableArgCmds = {
1061 "desc"sv,
"size"sv,
"read"sv,
"read_block"sv,
1062 "write"sv,
"write_block"sv,
1064 static constexpr std::array otherCmds = {
1065 "disasm"sv,
"set_bp"sv,
"remove_bp"sv,
"set_watchpoint"sv,
1066 "remove_watchpoint"sv,
"set_condition"sv,
"remove_condition"sv,
1067 "probe"sv,
"symbols"sv,
1069 switch (tokens.size()) {
1071 completeString(tokens,
concatArray(singleArgCmds, debuggableArgCmds, otherCmds));
1075 if (!
contains(singleArgCmds, tokens[1])) {
1077 if (
contains(debuggableArgCmds, tokens[1])) {
1079 completeString(tokens,
view::keys(debugger().debuggables));
1080 }
else if (tokens[1] ==
"remove_bp") {
1082 completeString(tokens, getBreakPointIds());
1083 }
else if (tokens[1] ==
"remove_watchpoint") {
1085 completeString(tokens, getWatchPointIds());
1086 }
else if (tokens[1] ==
"remove_condition") {
1088 completeString(tokens, getConditionIds());
1089 }
else if (tokens[1] ==
"set_watchpoint") {
1090 static constexpr std::array types = {
1091 "write_io"sv,
"write_mem"sv,
1092 "read_io"sv,
"read_mem"sv,
1094 completeString(tokens, types);
1095 }
else if (tokens[1] ==
"probe") {
1096 static constexpr std::array subCmds = {
1097 "list"sv,
"desc"sv,
"read"sv,
"set_bp"sv,
1098 "remove_bp"sv,
"list_bp"sv,
1100 completeString(tokens, subCmds);
1101 }
else if (tokens[1] ==
"symbols") {
1102 static constexpr std::array subCmds = {
1103 "types"sv,
"load"sv,
"remove"sv,
1104 "files"sv,
"lookup"sv,
1106 completeString(tokens, subCmds);
1111 if ((tokens[1] ==
"probe") &&
1112 (tokens[2] ==
one_of(
"desc",
"read",
"set_bp"))) {
1115 [](
auto* p) -> std::string_view {
return p->getName(); }));
void registerProbe(ProbeBase &probe)
Debuggable * findDebuggable(std::string_view name)
Debugger(const Debugger &)=delete
void transfer(Debugger &other)
void unregisterProbe(ProbeBase &probe)
void unregisterDebuggable(std::string_view name, Debuggable &debuggable)
ProbeBase * findProbe(std::string_view name)
void removeProbeBreakPoint(ProbeBreakPoint &bp)
void registerDebuggable(std::string name, Debuggable &debuggable)
unsigned setWatchPoint(TclObject command, TclObject condition, WatchPoint::Type type, unsigned beginAddr, unsigned endAddr, bool once, unsigned newId=-1)
void setWatchPoint(const std::shared_ptr< WatchPoint > &watchPoint)
const WatchPoints & getWatchPoints() const
void update(UpdateType type, std::string_view name, std::string_view value) override
MSXCPUInterface & getCPUInterface()
MSXCliComm & getMSXCliComm()
const std::string & getName() const
int getInt(Interpreter &interp) const
const Value * lookup(const hash_map< Key, Value, Hasher, Equal > &map, const Key2 &key)
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
This file implemented 3 utility functions:
ArgsInfo valueArg(std::string_view name, T &value)
std::vector< TclObject > parseTclArgs(Interpreter &interp, std::span< const TclObject > inArgs, std::span< const ArgsInfo > table)
uint16_t word
16 bit unsigned integer
ArgsInfo flagArg(std::string_view name, bool &flag)
TclObject makeTclList(Args &&... args)
auto find(InputRange &&range, const T &value)
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
size_t size(std::string_view utf8)
constexpr auto transform(Range &&range, UnaryOp op)
constexpr auto keys(Map &&map)
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))> >
constexpr auto concatArray(const std::array< T, X > &x, const std::array< T, Y > &y)
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
TemporaryString tmpStrCat(Ts &&... ts)
void strAppend(std::string &result, Ts &&...ts)
static std::optional< Type > parseType(std::string_view str)
static zstring_view toString(Type type)
constexpr auto xrange(T e)