33using std::string_view;
38 : motherBoard(motherBoard_)
39 , cmd(motherBoard.getCommandController(),
40 motherBoard.getStateChangeDistributor(),
41 motherBoard.getScheduler())
48 assert(debuggables.empty());
53 assert(!debuggables.contains(name));
54 debuggables.emplace_noDuplicateCheck(std::move(name), &debuggable);
59 assert(debuggables.contains(name));
60 assert(debuggables[name] == &debuggable); (void)debuggable;
61 debuggables.erase(name);
66 auto* v =
lookup(debuggables, name);
67 return v ? *v :
nullptr;
70Debuggable& Debugger::getDebuggable(string_view name)
81 assert(!probes.contains(probe.
getName()));
82 probes.insert_noDuplicateCheck(&probe);
87 assert(probes.contains(probe.
getName()));
93 auto it = probes.find(name);
94 return (it != std::end(probes)) ? *it :
nullptr;
97ProbeBase& Debugger::getProbe(string_view name)
106unsigned Debugger::insertProbeBreakPoint(
107 TclObject command, TclObject condition,
108 ProbeBase& probe,
bool once,
unsigned newId )
110 auto bp = std::make_unique<ProbeBreakPoint>(
111 std::move(command), std::move(condition), *
this, probe, once, newId);
112 unsigned result = bp->getId();
114 probeBreakPoints.push_back(std::move(bp));
120 if (name.starts_with(
"pp#")) {
122 if (
auto id = StringOp::stringToBase<10, unsigned>(name.substr(3))) {
124 it != std::end(probeBreakPoints)) {
131 throw CommandException(
"No such breakpoint: ", name);
134 auto it =
ranges::find(probeBreakPoints, name, [](
auto& bp) {
135 return bp->getProbe().getName();
137 if (it == std::end(probeBreakPoints)) {
138 throw CommandException(
139 "No (unconditional) breakpoint for probe: ", name);
152 [](
auto& v) {
return v.get(); }));
157 unsigned beginAddr,
unsigned endAddr,
158 bool once,
unsigned newId )
160 std::shared_ptr<WatchPoint> wp;
162 wp = std::make_shared<WatchIO>(
163 motherBoard, type, beginAddr, endAddr,
164 std::move(command), std::move(condition), once, newId);
166 wp = std::make_shared<WatchPoint>(
167 std::move(command), std::move(condition), type, beginAddr, endAddr, once, newId);
179 wp->getType(), wp->getBeginAddress(),
180 wp->getEndAddress(), wp->onlyOnce(),
185 assert(probeBreakPoints.empty());
186 for (
auto& bp : other.probeBreakPoints) {
188 insertProbeBreakPoint(bp->getCommandObj(),
189 bp->getConditionObj(),
190 *probe, bp->onlyOnce(),
204 unsigned addr = token.
getInt(interp);
205 if (addr >= 0x10000) {
208 return narrow_cast<word>(addr);
211Debugger::Cmd::Cmd(CommandController& commandController_,
212 StateChangeDistributor& stateChangeDistributor_,
213 Scheduler& scheduler_)
214 : RecordedCommand(commandController_, stateChangeDistributor_,
219bool Debugger::Cmd::needRecord(std::span<const TclObject> tokens)
const
225 if (tokens.size() < 2)
return false;
226 return tokens[1].getString() ==
one_of(
"write",
"write_block");
229void Debugger::Cmd::execute(
230 std::span<const TclObject> tokens, TclObject& result, EmuTime::param )
232 checkNumArgs(tokens, AtLeast{2},
"subcommand ?arg ...?");
233 executeSubCommand(tokens[1].getString(),
234 "read", [&]{ read(tokens, result); },
235 "read_block", [&]{ readBlock(tokens, result); },
236 "write", [&]{ write(tokens, result); },
237 "write_block", [&]{ writeBlock(tokens, result); },
238 "size", [&]{
size(tokens, result); },
239 "desc", [&]{ desc(tokens, result); },
240 "list", [&]{ list(result); },
241 "step", [&]{ debugger().motherBoard.getCPUInterface().doStep(); },
242 "cont", [&]{ debugger().motherBoard.getCPUInterface().doContinue(); },
243 "disasm", [&]{ debugger().cpu->disasmCommand(getInterpreter(), tokens, result); },
244 "break", [&]{ debugger().motherBoard.getCPUInterface().doBreak(); },
246 "set_bp", [&]{ setBreakPoint(tokens, result); },
247 "remove_bp", [&]{ removeBreakPoint(tokens, result); },
248 "list_bp", [&]{ listBreakPoints(tokens, result); },
249 "set_watchpoint", [&]{ setWatchPoint(tokens, result); },
250 "remove_watchpoint", [&]{ removeWatchPoint(tokens, result); },
251 "list_watchpoints", [&]{ listWatchPoints(tokens, result); },
252 "set_condition", [&]{ setCondition(tokens, result); },
253 "remove_condition", [&]{ removeCondition(tokens, result); },
254 "list_conditions", [&]{ listConditions(tokens, result); },
255 "probe", [&]{ probe(tokens, result); },
256 "symbols", [&]{ symbols(tokens, result); });
259void Debugger::Cmd::list(TclObject& result)
261 result.addListElements(
view::keys(debugger().debuggables));
264void Debugger::Cmd::desc(std::span<const TclObject> tokens, TclObject& result)
266 checkNumArgs(tokens, 3,
"debuggable");
267 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
268 result = device.getDescription();
271void Debugger::Cmd::size(std::span<const TclObject> tokens, TclObject& result)
273 checkNumArgs(tokens, 3,
"debuggable");
274 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
275 result = device.getSize();
278void Debugger::Cmd::read(std::span<const TclObject> tokens, TclObject& result)
280 checkNumArgs(tokens, 4, Prefix{2},
"debuggable address");
281 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
282 unsigned addr = tokens[3].getInt(getInterpreter());
283 if (addr >= device.getSize()) {
284 throw CommandException(
"Invalid address");
286 result = device.read(addr);
289void Debugger::Cmd::readBlock(std::span<const TclObject> tokens, TclObject& result)
291 checkNumArgs(tokens, 5, Prefix{2},
"debuggable address size");
292 auto& interp = getInterpreter();
293 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
294 unsigned devSize = device.getSize();
295 unsigned addr = tokens[3].getInt(interp);
296 if (addr >= devSize) {
297 throw CommandException(
"Invalid address");
299 unsigned num = tokens[4].getInt(interp);
300 if (num > (devSize - addr)) {
301 throw CommandException(
"Invalid size");
304 MemBuffer<byte> buf(num);
305 for (
auto i :
xrange(num)) {
306 buf[i] = device.read(addr + i);
308 result = std::span<byte>{buf.data(), num};
311void Debugger::Cmd::write(std::span<const TclObject> tokens, TclObject& )
313 checkNumArgs(tokens, 5, Prefix{2},
"debuggable address value");
314 auto& interp = getInterpreter();
315 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
316 unsigned addr = tokens[3].getInt(interp);
317 if (addr >= device.getSize()) {
318 throw CommandException(
"Invalid address");
320 unsigned value = tokens[4].getInt(interp);
322 throw CommandException(
"Invalid value");
325 device.write(addr, narrow_cast<byte>(value));
328void Debugger::Cmd::writeBlock(std::span<const TclObject> tokens, TclObject& )
330 checkNumArgs(tokens, 5, Prefix{2},
"debuggable address values");
331 Debuggable& device = debugger().getDebuggable(tokens[2].getString());
332 unsigned devSize = device.getSize();
333 unsigned addr = tokens[3].getInt(getInterpreter());
334 if (addr >= devSize) {
335 throw CommandException(
"Invalid address");
337 auto buf = tokens[4].getBinary();
338 if ((buf.size() + addr) > devSize) {
339 throw CommandException(
"Invalid size");
343 device.write(
unsigned(addr + i), buf[i]);
347void Debugger::Cmd::setBreakPoint(std::span<const TclObject> tokens, TclObject& result)
349 checkNumArgs(tokens, AtLeast{3},
"address ?-once? ?condition? ?command?");
350 TclObject command(
"debug break");
354 std::array info = {
flagArg(
"-once", once)};
355 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(2), info);
356 if ((arguments.size() < 1) || (arguments.size() > 3)) {
360 switch (arguments.size()) {
362 command = arguments[2];
365 condition = arguments[1];
368 word addr = getAddress(getInterpreter(), arguments[0]);
369 BreakPoint bp(addr, command, condition, once);
371 debugger().motherBoard.getCPUInterface().insertBreakPoint(std::move(bp));
377void Debugger::Cmd::removeBreakPoint(
378 std::span<const TclObject> tokens, TclObject& )
380 checkNumArgs(tokens, 3,
"id|address");
381 auto&
interface = debugger().motherBoard.getCPUInterface();
384 string_view tmp = tokens[2].getString();
385 if (tmp.starts_with(
"bp#")) {
387 if (
auto id = StringOp::stringToBase<10, unsigned>(tmp.substr(3))) {
389 it != std::end(breakPoints)) {
390 interface.removeBreakPoint(*it);
394 throw CommandException(
"No such breakpoint: ", tmp);
397 word addr = getAddress(getInterpreter(), tokens[2]);
399 auto it = std::find_if(first, last, [](
auto& bp) {
400 return bp.getCondition().empty();
403 throw CommandException(
404 "No (unconditional) breakpoint at address: ", tmp);
406 interface.removeBreakPoint(*it);
410void Debugger::Cmd::listBreakPoints(
411 std::span<const TclObject> , TclObject& result)
417 tmpStrCat(
"0x", hex_string<4>(bp.getAddress())),
426void Debugger::Cmd::setWatchPoint(std::span<const TclObject> tokens, TclObject& result)
428 checkNumArgs(tokens, AtLeast{4}, Prefix{2},
"type address ?-once? ?condition? ?command?");
429 TclObject command(
"debug break");
431 unsigned beginAddr, endAddr;
435 std::array info = {
flagArg(
"-once", once)};
436 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(2), info);
437 if ((arguments.size() < 2) || (arguments.size() > 4)) {
441 switch (arguments.size()) {
443 command = arguments[3];
446 condition = arguments[2];
449 string_view typeStr = arguments[0].getString();
451 if (typeStr ==
"read_io") {
454 }
else if (typeStr ==
"write_io") {
457 }
else if (typeStr ==
"read_mem") {
460 }
else if (typeStr ==
"write_mem") {
464 throw CommandException(
"Invalid type: ", typeStr);
467 auto& interp = getInterpreter();
468 if (arguments[1].getListLength(interp) == 2) {
469 beginAddr = arguments[1].getListIndex(interp, 0).getInt(interp);
470 endAddr = arguments[1].getListIndex(interp, 1).getInt(interp);
471 if (endAddr < beginAddr) {
472 throw CommandException(
473 "Not a valid range: end address may "
474 "not be smaller than begin address.");
477 beginAddr = endAddr = arguments[1].getInt(interp);
479 if (endAddr >= max) {
480 throw CommandException(
"Invalid address: out of range");
487 unsigned id = debugger().setWatchPoint(
488 command, condition, type, beginAddr, endAddr, once);
492void Debugger::Cmd::removeWatchPoint(
493 std::span<const TclObject> tokens, TclObject& )
495 checkNumArgs(tokens, 3,
"id");
496 string_view tmp = tokens[2].getString();
497 if (tmp.starts_with(
"wp#")) {
499 if (
auto id = StringOp::stringToBase<10, unsigned>(tmp.substr(3))) {
500 auto&
interface = debugger().motherBoard.getCPUInterface();
501 for (
auto& wp : interface.getWatchPoints()) {
502 if (wp->getId() == *
id) {
503 interface.removeWatchPoint(wp);
509 throw CommandException(
"No such watchpoint: ", tmp);
512void Debugger::Cmd::listWatchPoints(
513 std::span<const TclObject> , TclObject& result)
516 auto&
interface = debugger().motherBoard.getCPUInterface();
517 for (
const auto& wp : interface.getWatchPoints()) {
520 switch (wp->getType()) {
536 line.addListElement(type);
537 unsigned beginAddr = wp->getBeginAddress();
538 unsigned endAddr = wp->getEndAddress();
539 if (beginAddr == endAddr) {
540 line.addListElement(
tmpStrCat(
"0x", hex_string<4>(beginAddr)));
543 tmpStrCat(
"0x", hex_string<4>(beginAddr)),
544 tmpStrCat(
"0x", hex_string<4>(endAddr))));
546 line.addListElement(wp->getCondition(),
554void Debugger::Cmd::setCondition(std::span<const TclObject> tokens, TclObject& result)
556 checkNumArgs(tokens, AtLeast{3},
"condition ?-once? ?command?");
557 TclObject command(
"debug break");
561 std::array info = {
flagArg(
"-once", once)};
562 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(2), info);
563 if ((arguments.size() < 1) || (arguments.size() > 2)) {
567 switch (arguments.size()) {
569 command = arguments[1];
572 condition = arguments[0];
573 DebugCondition dc(command, condition, once);
575 debugger().motherBoard.getCPUInterface().setCondition(std::move(dc));
581void Debugger::Cmd::removeCondition(
582 std::span<const TclObject> tokens, TclObject& )
584 checkNumArgs(tokens, 3,
"id");
586 string_view tmp = tokens[2].getString();
587 if (tmp.starts_with(
"cond#")) {
589 if (
auto id = StringOp::stringToBase<10, unsigned>(tmp.substr(5))) {
591 if (c.getId() == *
id) {
592 auto&
interface = debugger().motherBoard.getCPUInterface();
593 interface.removeCondition(c);
599 throw CommandException(
"No such condition: ", tmp);
602void Debugger::Cmd::listConditions(
603 std::span<const TclObject> , TclObject& result)
616void Debugger::Cmd::probe(std::span<const TclObject> tokens, TclObject& result)
618 checkNumArgs(tokens, AtLeast{3},
"subcommand ?arg ...?");
619 executeSubCommand(tokens[2].getString(),
620 "list", [&]{ probeList(tokens, result); },
621 "desc", [&]{ probeDesc(tokens, result); },
622 "read", [&]{ probeRead(tokens, result); },
623 "set_bp", [&]{ probeSetBreakPoint(tokens, result); },
624 "remove_bp", [&]{ probeRemoveBreakPoint(tokens, result); },
625 "list_bp", [&]{ probeListBreakPoints(tokens, result); });
627void Debugger::Cmd::probeList(std::span<const TclObject> , TclObject& result)
630 [](
auto* p) {
return p->getName(); }));
632void Debugger::Cmd::probeDesc(std::span<const TclObject> tokens, TclObject& result)
634 checkNumArgs(tokens, 4,
"probe");
635 result = debugger().getProbe(tokens[3].getString()).getDescription();
637void Debugger::Cmd::probeRead(std::span<const TclObject> tokens, TclObject& result)
639 checkNumArgs(tokens, 4,
"probe");
640 result = debugger().getProbe(tokens[3].getString()).getValue();
642void Debugger::Cmd::probeSetBreakPoint(
643 std::span<const TclObject> tokens, TclObject& result)
645 checkNumArgs(tokens, AtLeast{4},
"probe ?-once? ?condition? ?command?");
646 TclObject command(
"debug break");
651 std::array info = {
flagArg(
"-once", once)};
652 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(3), info);
653 if ((arguments.size() < 1) || (arguments.size() > 3)) {
657 switch (arguments.size()) {
659 command = arguments[2];
662 condition = arguments[1];
665 p = &debugger().getProbe(arguments[0].getString());
672 unsigned id = debugger().insertProbeBreakPoint(command, condition, *p, once);
675void Debugger::Cmd::probeRemoveBreakPoint(
676 std::span<const TclObject> tokens, TclObject& )
678 checkNumArgs(tokens, 4,
"id");
679 debugger().removeProbeBreakPoint(tokens[3].getString());
681void Debugger::Cmd::probeListBreakPoints(
682 std::span<const TclObject> , TclObject& result)
685 for (
auto& p : debugger().probeBreakPoints) {
687 p->getProbe().getName(),
695SymbolManager& Debugger::Cmd::getSymbolManager()
697 return debugger().getMotherBoard().getReactor().getSymbolManager();
699void Debugger::Cmd::symbols(std::span<const TclObject> tokens, TclObject& result)
701 checkNumArgs(tokens, AtLeast{3},
"subcommand ?arg ...?");
702 executeSubCommand(tokens[2].getString(),
703 "types", [&]{ symbolsTypes(tokens, result); },
704 "load", [&]{ symbolsLoad(tokens, result); },
705 "remove", [&]{ symbolsRemove(tokens, result); },
706 "files", [&]{ symbolsFiles(tokens, result); },
707 "lookup", [&]{ symbolsLookup(tokens, result); });
709void Debugger::Cmd::symbolsTypes(std::span<const TclObject> tokens, TclObject& result)
const
711 checkNumArgs(tokens, 3,
"");
718void Debugger::Cmd::symbolsLoad(std::span<const TclObject> tokens, TclObject& )
720 checkNumArgs(tokens, Between{4, 5},
"filename ?type?");
721 auto filename = std::string(tokens[3].getString());
724 auto str = tokens[4].getString();
726 if (!
t)
throw CommandException(
"Invalid symbol file type: ", str);
731 }
catch (MSXException& e) {
732 throw CommandException(
"Couldn't load symbol file '", filename,
"': ",
e.getMessage());
735void Debugger::Cmd::symbolsRemove(std::span<const TclObject> tokens, TclObject& )
737 checkNumArgs(tokens, 4,
"filename");
738 auto filename = tokens[3].getString();
739 getSymbolManager().removeFile(filename);
741void Debugger::Cmd::symbolsFiles(std::span<const TclObject> tokens, TclObject& result)
743 checkNumArgs(tokens, 3,
"");
744 for (
const auto& file : getSymbolManager().getFiles()) {
745 result.addListElement(TclObject(TclObject::MakeDictTag{},
746 "filename", file.filename,
750void Debugger::Cmd::symbolsLookup(std::span<const TclObject> tokens, TclObject& result)
752 std::string_view filename;
753 std::string_view name;
754 std::optional<int> value;
755 std::array info = {
valueArg(
"-filename", filename),
758 auto args =
parseTclArgs(getInterpreter(), tokens.subspan(3), info);
759 if (!args.empty())
throw SyntaxError();
760 if (!filename.empty() && !name.empty() && value)
throw SyntaxError();
762 for (
const auto& file : getSymbolManager().getFiles()) {
763 if (!filename.empty() && (file.filename != filename))
continue;
764 for (
const auto& sym : file.symbols) {
765 if (!name.empty() && (name != sym.name))
continue;
766 if (value && (sym.value != *value))
continue;
769 if (filename.empty()) elem.addDictKeyValue(
"filename", file.filename);
770 if (name.empty()) elem.addDictKeyValue(
"name", sym.name);
771 if (!value) elem.addDictKeyValue(
"value", sym.value);
772 result.addListElement(std::move(elem));
777string Debugger::Cmd::help(std::span<const TclObject> tokens)
const
780 "debug <subcommand> [<arguments>]\n"
781 " Possible subcommands are:\n"
782 " list returns a list of all debuggables\n"
783 " desc returns a description of this debuggable\n"
784 " size returns the size of this debuggable\n"
785 " read read a byte from a debuggable\n"
786 " write write a byte to a debuggable\n"
787 " read_block read a whole block at once\n"
788 " write_block write a whole block at once\n"
789 " set_bp insert a new breakpoint\n"
790 " remove_bp remove a certain breakpoint\n"
791 " list_bp list the active breakpoints\n"
792 " set_watchpoint insert a new watchpoint\n"
793 " remove_watchpoint remove a certain watchpoint\n"
794 " list_watchpoints list the active watchpoints\n"
795 " set_condition insert a new condition\n"
796 " remove_condition remove a certain condition\n"
797 " list_conditions list the active conditions\n"
798 " probe probe related subcommands\n"
799 " cont continue execution after break\n"
800 " step execute one instruction\n"
801 " break break CPU at current position\n"
802 " breaked query CPU breaked status\n"
803 " disasm disassemble instructions\n"
804 " symbols manage debug symbols\n"
805 " The arguments are specific for each subcommand.\n"
806 " Type 'help debug <subcommand>' for help about a specific subcommand.\n";
810 " Returns a list with the names of all 'debuggables'.\n"
811 " These names are used in other debug subcommands.\n";
813 "debug desc <name>\n"
814 " Returns a description for the debuggable with given name.\n";
816 "debug size <name>\n"
817 " Returns the size (in bytes) of the debuggable with given name.\n";
819 "debug read <name> <addr>\n"
820 " Read a byte at offset <addr> from the given debuggable.\n"
821 " The offset must be smaller than the value returned from the "
822 "'size' subcommand\n"
823 " Note that openMSX comes with a bunch of Tcl scripts that make "
824 "some of the debug reads much more convenient (e.g. reading from "
825 "Z80 or VDP registers). See the Console Command Reference for more "
826 "details about these.\n";
828 "debug write <name> <addr> <val>\n"
829 " Write a byte to the given debuggable at a certain offset.\n"
830 " The offset must be smaller than the value returned from the "
831 "'size' subcommand\n";
833 "debug read_block <name> <addr> <size>\n"
834 " Read a whole block at once. This is equivalent with repeated "
835 "invocations of the 'read' subcommand, but using this subcommand "
836 "may be faster. The result is a Tcl binary string (see Tcl manual).\n"
837 " The block is specified as size/offset in the debuggable. The "
838 "complete block must fit in the debuggable (see the 'size' "
840 auto writeBlockHelp =
841 "debug write_block <name> <addr> <values>\n"
842 " Write a whole block at once. This is equivalent with repeated "
843 "invocations of the 'write' subcommand, but using this subcommand "
844 "may be faster. The <values> argument must be a Tcl binary string "
845 "(see Tcl manual).\n"
846 " The block has a size and an offset in the debuggable. The "
847 "complete block must fit in the debuggable (see the 'size' "
850 "debug set_bp [-once] <addr> [<cond>] [<cmd>]\n"
851 " Insert a new breakpoint at given address. When the CPU is about "
852 "to execute the instruction at this address, execution will be "
853 "breaked. At least this is the default behaviour, see next "
855 " When the -once flag is given, the breakpoint is automatically "
856 "removed after it triggered. In other words: it only triggers once.\n"
857 " Optionally you can specify a condition. When the CPU reaches "
858 "the breakpoint this condition is evaluated, only when the condition "
859 "evaluated to true execution will be breaked.\n"
860 " A condition must be specified as a Tcl expression. For example\n"
861 " debug set_bp 0xf37d {[reg C] == 0x2F}\n"
862 " This breaks on address 0xf37d but only when Z80 register C has the "
864 " Also optionally you can specify a command that should be "
865 "executed when the breakpoint is reached (and condition is true). "
866 "By default this command is 'debug break'.\n"
867 " The result of this command is a breakpoint ID. This ID can "
868 "later be used to remove this breakpoint again.\n";
870 "debug remove_bp <id>\n"
871 " Remove the breakpoint with given ID again. You can use the "
872 "'list_bp' subcommand to see all valid IDs.\n";
875 " Lists all active breakpoints. The result is printed in 4 "
876 "columns. The first column contains the breakpoint ID. The "
877 "second one has the address. The third has the condition "
878 "(default condition is empty). And the last column contains "
879 "the command that will be executed (default is 'debug break').\n";
880 auto setWatchPointHelp =
881 "debug set_watchpoint [-once] <type> <region> [<cond>] [<cmd>]\n"
882 " Insert a new watchpoint of given type on the given region, "
883 "there can be an optional -once flag, a condition and alternative "
884 "command. See the 'set_bp' subcommand for details about these.\n"
885 " Type must be one of the following:\n"
886 " read_io break when CPU reads from given IO port(s)\n"
887 " write_io break when CPU writes to given IO port(s)\n"
888 " read_mem break when CPU reads from given memory location(s)\n"
889 " write_mem break when CPU writes to given memory location(s)\n"
890 " Region is either a single value, this corresponds to a single "
891 "memory location or IO port. Otherwise region must be a list of "
892 "two values (enclosed in braces) that specify a begin and end "
893 "point of a whole memory region or a range of IO ports.\n"
894 "During the execution of <cmd>, the following global Tcl "
895 "variables are set:\n"
896 " ::wp_last_address this is the actual address of the mem/io "
897 "read/write that triggered the watchpoint\n"
898 " ::wp_last_value this is the actual value that was written "
899 "by the mem/io write that triggered the watchpoint\n"
901 " debug set_watchpoint write_io 0x99 {[reg A] == 0x81}\n"
902 " debug set_watchpoint read_mem {0xfbe5 0xfbef}\n";
903 auto removeWatchPointHelp =
904 "debug remove_watchpoint <id>\n"
905 " Remove the watchpoint with given ID again. You can use the "
906 "'list_watchpoints' subcommand to see all valid IDs.\n";
907 auto listWatchPointsHelp =
908 "debug list_watchpoints\n"
909 " Lists all active watchpoints. The result is similar to the "
910 "'list_bp' subcommand, but there is an extra column (2nd column) "
911 "that contains the type of the watchpoint.\n";
913 "debug set_condition [-once] <cond> [<cmd>]\n"
914 " Insert a new condition. These are much like breakpoints, "
915 "except that they are checked before every instruction "
916 "(breakpoints are tied to a specific address).\n"
917 " Conditions will slow down simulation MUCH more than "
918 "breakpoints. So only use them when you don't care about "
919 "simulation speed (when you're debugging this is usually not "
921 " See 'help debug set_bp' for more details.\n";
922 auto removeCondHelp =
923 "debug remove_condition <id>\n"
924 " Remove the condition with given ID again. You can use the "
925 "'list_conditions' subcommand to see all valid IDs.\n";
927 "debug list_conditions\n"
928 " Lists all active conditions. The result is similar to the "
929 "'list_bp' subcommand, but without the 2nd column that would "
930 "show the address.\n";
932 "debug probe <subcommand> [<arguments>]\n"
933 " Possible subcommands are:\n"
934 " list returns a list of all probes\n"
935 " desc <probe> returns a description of this probe\n"
936 " read <probe> returns the current value of this probe\n"
937 " set_bp <probe> [-once] [<cond>] [<cmd>] set a breakpoint on the given probe\n"
938 " remove_bp <id> remove the given breakpoint\n"
939 " list_bp returns a list of breakpoints that are set on probes\n";
942 " Continue execution after CPU was breaked.\n";
945 " Execute one instruction. This command is only meaningful in "
949 " Immediately break CPU execution. When CPU was already breaked "
950 "this command has no effect.\n";
953 " Query the CPU breaked status. Returns '1' when CPU was "
954 "breaked, '0' otherwise.\n";
956 "debug disasm <addr>\n"
957 " Disassemble the instruction at the given address. The result "
958 "is a Tcl list. The first element in the list contains a textual "
959 "representation of the instruction, the next elements contain the "
960 "bytes that make up this instruction (thus the length of the "
961 "resulting list can be used to derive the number of bytes in the "
963 " Note that openMSX comes with a 'disasm' Tcl script that is much "
964 "more convenient to use than this subcommand.";
966 "debug symbols <subcommand> [<arguments>]\n"
967 " Possible subcommands are:\n"
968 " types returns a list of symbol file types\n"
969 " load <filename> [<type>] load a symbol file, auto-detect type if none is given\n"
970 " remove <filename> remove a previously loaded symbol file\n"
971 " files returns a list of all loaded symbol files\n"
972 " lookup [-filename <filename>] [-name <name>] [-value <value>]\n"
973 " returns a list of symbols in an optionally given file\n"
974 " and/or with an optionally given name\n"
975 " and/or with an optionally given value\n"
976 " Note: an easier syntax to lookup a symbol value based on the name is:\n"
979 "Unknown subcommand, use 'help debug' to see a list of valid "
982 if (tokens.size() == 1) {
984 }
else if (tokens[1] ==
"list") {
986 }
else if (tokens[1] ==
"desc") {
988 }
else if (tokens[1] ==
"size") {
990 }
else if (tokens[1] ==
"read") {
992 }
else if (tokens[1] ==
"write") {
994 }
else if (tokens[1] ==
"read_block") {
995 return readBlockHelp;
996 }
else if (tokens[1] ==
"write_block") {
997 return writeBlockHelp;
998 }
else if (tokens[1] ==
"set_bp") {
1000 }
else if (tokens[1] ==
"remove_bp") {
1001 return removeBpHelp;
1002 }
else if (tokens[1] ==
"list_bp") {
1004 }
else if (tokens[1] ==
"set_watchpoint") {
1005 return setWatchPointHelp;
1006 }
else if (tokens[1] ==
"remove_watchpoint") {
1007 return removeWatchPointHelp;
1008 }
else if (tokens[1] ==
"list_watchpoints") {
1009 return listWatchPointsHelp;
1010 }
else if (tokens[1] ==
"set_condition") {
1012 }
else if (tokens[1] ==
"remove_condition") {
1013 return removeCondHelp;
1014 }
else if (tokens[1] ==
"list_conditions") {
1015 return listCondHelp;
1016 }
else if (tokens[1] ==
"probe") {
1018 }
else if (tokens[1] ==
"cont") {
1020 }
else if (tokens[1] ==
"step") {
1022 }
else if (tokens[1] ==
"break") {
1024 }
else if (tokens[1] ==
"breaked") {
1026 }
else if (tokens[1] ==
"disasm") {
1028 }
else if (tokens[1] ==
"symbols") {
1035std::vector<string> Debugger::Cmd::getBreakPointIds()
const
1039 [](
auto& bp) {
return strCat(
"bp#", bp.getId()); }));
1041std::vector<string> Debugger::Cmd::getWatchPointIds()
const
1044 debugger().motherBoard.getCPUInterface().getWatchPoints(),
1045 [](
auto& w) { return strCat(
"wp#", w->getId()); }));
1047std::vector<string> Debugger::Cmd::getConditionIds()
const
1051 [](
auto& c) {
return strCat(
"cond#", c.getId()); }));
1054void Debugger::Cmd::tabCompletion(std::vector<string>& tokens)
const
1056 using namespace std::literals;
1057 static constexpr std::array singleArgCmds = {
1058 "list"sv,
"step"sv,
"cont"sv,
"break"sv,
"breaked"sv,
1059 "list_bp"sv,
"list_watchpoints"sv,
"list_conditions"sv,
1061 static constexpr std::array debuggableArgCmds = {
1062 "desc"sv,
"size"sv,
"read"sv,
"read_block"sv,
1063 "write"sv,
"write_block"sv,
1065 static constexpr std::array otherCmds = {
1066 "disasm"sv,
"set_bp"sv,
"remove_bp"sv,
"set_watchpoint"sv,
1067 "remove_watchpoint"sv,
"set_condition"sv,
"remove_condition"sv,
1068 "probe"sv,
"symbols"sv,
1070 switch (tokens.size()) {
1072 completeString(tokens,
concatArray(singleArgCmds, debuggableArgCmds, otherCmds));
1076 if (!
contains(singleArgCmds, tokens[1])) {
1078 if (
contains(debuggableArgCmds, tokens[1])) {
1080 completeString(tokens,
view::keys(debugger().debuggables));
1081 }
else if (tokens[1] ==
"remove_bp") {
1083 completeString(tokens, getBreakPointIds());
1084 }
else if (tokens[1] ==
"remove_watchpoint") {
1086 completeString(tokens, getWatchPointIds());
1087 }
else if (tokens[1] ==
"remove_condition") {
1089 completeString(tokens, getConditionIds());
1090 }
else if (tokens[1] ==
"set_watchpoint") {
1091 static constexpr std::array types = {
1092 "write_io"sv,
"write_mem"sv,
1093 "read_io"sv,
"read_mem"sv,
1095 completeString(tokens, types);
1096 }
else if (tokens[1] ==
"probe") {
1097 static constexpr std::array subCmds = {
1098 "list"sv,
"desc"sv,
"read"sv,
"set_bp"sv,
1099 "remove_bp"sv,
"list_bp"sv,
1101 completeString(tokens, subCmds);
1102 }
else if (tokens[1] ==
"symbols") {
1103 static constexpr std::array subCmds = {
1104 "types"sv,
"load"sv,
"remove"sv,
1105 "files"sv,
"lookup"sv,
1107 completeString(tokens, subCmds);
1112 if ((tokens[1] ==
"probe") &&
1113 (tokens[2] ==
one_of(
"desc",
"read",
"set_bp"))) {
1116 [](
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)
static const Conditions & getConditions()
const WatchPoints & getWatchPoints() const
static const BreakPoints & getBreakPoints()
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)