656 lines · cpp
1//===-- CommandObjectBreakpointCommand.cpp --------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "CommandObjectBreakpointCommand.h"10#include "CommandObjectBreakpoint.h"11#include "lldb/Breakpoint/Breakpoint.h"12#include "lldb/Breakpoint/BreakpointIDList.h"13#include "lldb/Breakpoint/BreakpointLocation.h"14#include "lldb/Core/IOHandler.h"15#include "lldb/Host/OptionParser.h"16#include "lldb/Interpreter/CommandInterpreter.h"17#include "lldb/Interpreter/CommandOptionArgumentTable.h"18#include "lldb/Interpreter/CommandReturnObject.h"19#include "lldb/Interpreter/OptionArgParser.h"20#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"21#include "lldb/Target/Target.h"22 23using namespace lldb;24using namespace lldb_private;25 26#define LLDB_OPTIONS_breakpoint_command_add27#include "CommandOptions.inc"28 29class CommandObjectBreakpointCommandAdd : public CommandObjectParsed,30 public IOHandlerDelegateMultiline {31public:32 CommandObjectBreakpointCommandAdd(CommandInterpreter &interpreter)33 : CommandObjectParsed(interpreter, "add",34 "Add LLDB commands to a breakpoint, to be executed "35 "whenever the breakpoint is hit. "36 "The commands added to the breakpoint replace any "37 "commands previously added to it."38 " If no breakpoint is specified, adds the "39 "commands to the last created breakpoint.",40 nullptr),41 IOHandlerDelegateMultiline("DONE",42 IOHandlerDelegate::Completion::LLDBCommand),43 m_func_options("breakpoint command", false, 'F') {44 SetHelpLong(45 R"(46General information about entering breakpoint commands47------------------------------------------------------48 49)"50 "This command will prompt for commands to be executed when the specified \51breakpoint is hit. Each command is typed on its own line following the '> ' \52prompt until 'DONE' is entered."53 R"(54 55)"56 "Syntactic errors may not be detected when initially entered, and many \57malformed commands can silently fail when executed. If your breakpoint commands \58do not appear to be executing, double-check the command syntax."59 R"(60 61)"62 "Note: You may enter any debugger command exactly as you would at the debugger \63prompt. There is no limit to the number of commands supplied, but do NOT enter \64more than one command per line."65 R"(66 67Special information about PYTHON breakpoint commands68----------------------------------------------------69 70)"71 "You may enter either one or more lines of Python, including function \72definitions or calls to functions that will have been imported by the time \73the code executes. Single line breakpoint commands will be interpreted 'as is' \74when the breakpoint is hit. Multiple lines of Python will be wrapped in a \75generated function, and a call to the function will be attached to the breakpoint."76 R"(77 78This auto-generated function is passed in three arguments:79 80 frame: an lldb.SBFrame object for the frame which hit breakpoint.81 82 bp_loc: an lldb.SBBreakpointLocation object that represents the breakpoint location that was hit.83 84 dict: the python session dictionary hit.85 86)"87 "When specifying a python function with the --python-function option, you need \88to supply the function name prepended by the module name:"89 R"(90 91 --python-function myutils.breakpoint_callback92 93The function itself must have either of the following prototypes:94 95def breakpoint_callback(frame, bp_loc, internal_dict):96 # Your code goes here97 98or:99 100def breakpoint_callback(frame, bp_loc, extra_args, internal_dict):101 # Your code goes here102 103)"104 "The arguments are the same as the arguments passed to generated functions as \105described above. In the second form, any -k and -v pairs provided to the command will \106be packaged into a SBDictionary in an SBStructuredData and passed as the extra_args parameter. \107\n\n\108Note that the global variable 'lldb.frame' will NOT be updated when \109this function is called, so be sure to use the 'frame' argument. The 'frame' argument \110can get you to the thread via frame.GetThread(), the thread can get you to the \111process via thread.GetProcess(), and the process can get you back to the target \112via process.GetTarget()."113 R"(114 115)"116 "Important Note: As Python code gets collected into functions, access to global \117variables requires explicit scoping using the 'global' keyword. Be sure to use correct \118Python syntax, including indentation, when entering Python breakpoint commands."119 R"(120 121Example Python one-line breakpoint command:122 123(lldb) breakpoint command add -s python 1124Enter your Python command(s). Type 'DONE' to end.125def function (frame, bp_loc, internal_dict):126 """frame: the lldb.SBFrame for the location at which you stopped127 bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information128 internal_dict: an LLDB support object not to be used"""129 print("Hit this breakpoint!")130 DONE131 132As a convenience, this also works for a short Python one-liner:133 134(lldb) breakpoint command add -s python 1 -o 'import time; print(time.asctime())'135(lldb) run136Launching '.../a.out' (x86_64)137(lldb) Fri Sep 10 12:17:45 2010138Process 21778 Stopped139* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = breakpoint 1.1, queue = com.apple.main-thread140 36141 37 int c(int val)142 38 {143 39 -> return val + 3;144 40 }145 41146 42 int main (int argc, char const *argv[])147 148Example multiple line Python breakpoint command:149 150(lldb) breakpoint command add -s p 1151Enter your Python command(s). Type 'DONE' to end.152def function (frame, bp_loc, internal_dict):153 """frame: the lldb.SBFrame for the location at which you stopped154 bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information155 internal_dict: an LLDB support object not to be used"""156 global bp_count157 bp_count = bp_count + 1158 print("Hit this breakpoint " + repr(bp_count) + " times!")159 DONE160 161)"162 "In this case, since there is a reference to a global variable, \163'bp_count', you will also need to make sure 'bp_count' exists and is \164initialized:"165 R"(166 167(lldb) script168>>> bp_count = 0169>>> quit()170 171)"172 "Your Python code, however organized, can optionally return a value. \173If the returned value is False, that tells LLDB not to stop at the breakpoint \174to which the code is associated. Returning anything other than False, or even \175returning None, or even omitting a return statement entirely, will cause \176LLDB to stop."177 R"(178 179)"180 "Final Note: A warning that no breakpoint command was generated when there \181are no syntax errors may indicate that a function was declared but never called.");182 183 m_all_options.Append(&m_options);184 m_all_options.Append(&m_func_options, LLDB_OPT_SET_2 | LLDB_OPT_SET_3,185 LLDB_OPT_SET_2);186 m_all_options.Finalize();187 188 AddSimpleArgumentList(eArgTypeBreakpointID, eArgRepeatOptional);189 }190 191 ~CommandObjectBreakpointCommandAdd() override = default;192 193 Options *GetOptions() override { return &m_all_options; }194 195 void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {196 if (interactive) {197 if (lldb::LockableStreamFileSP output_sp =198 io_handler.GetOutputStreamFileSP()) {199 LockedStreamFile locked_stream = output_sp->Lock();200 locked_stream.PutCString(g_reader_instructions);201 }202 }203 }204 205 void IOHandlerInputComplete(IOHandler &io_handler,206 std::string &line) override {207 io_handler.SetIsDone(true);208 209 std::vector<std::reference_wrapper<BreakpointOptions>> *bp_options_vec =210 (std::vector<std::reference_wrapper<BreakpointOptions>> *)211 io_handler.GetUserData();212 for (BreakpointOptions &bp_options : *bp_options_vec) {213 auto cmd_data = std::make_unique<BreakpointOptions::CommandData>();214 cmd_data->user_source.SplitIntoLines(line.c_str(), line.size());215 bp_options.SetCommandDataCallback(cmd_data);216 }217 }218 219 void CollectDataForBreakpointCommandCallback(220 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,221 CommandReturnObject &result) {222 m_interpreter.GetLLDBCommandsFromIOHandler(223 "> ", // Prompt224 *this, // IOHandlerDelegate225 &bp_options_vec); // Baton for the "io_handler" that will be passed back226 // into our IOHandlerDelegate functions227 }228 229 /// Set a one-liner as the callback for the breakpoint.230 void SetBreakpointCommandCallback(231 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,232 const char *oneliner) {233 for (BreakpointOptions &bp_options : bp_options_vec) {234 auto cmd_data = std::make_unique<BreakpointOptions::CommandData>();235 236 cmd_data->user_source.AppendString(oneliner);237 cmd_data->stop_on_error = m_options.m_stop_on_error;238 239 bp_options.SetCommandDataCallback(cmd_data);240 }241 }242 243 class CommandOptions : public OptionGroup {244 public:245 CommandOptions() = default;246 247 ~CommandOptions() override = default;248 249 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,250 ExecutionContext *execution_context) override {251 Status error;252 const int short_option =253 g_breakpoint_command_add_options[option_idx].short_option;254 255 switch (short_option) {256 case 'o':257 m_use_one_liner = true;258 m_one_liner = std::string(option_arg);259 break;260 261 case 's':262 m_script_language = (lldb::ScriptLanguage)OptionArgParser::ToOptionEnum(263 option_arg,264 g_breakpoint_command_add_options[option_idx].enum_values,265 eScriptLanguageNone, error);266 switch (m_script_language) {267 case eScriptLanguagePython:268 case eScriptLanguageLua:269 m_use_script_language = true;270 break;271 case eScriptLanguageNone:272 case eScriptLanguageUnknown:273 m_use_script_language = false;274 break;275 }276 break;277 278 case 'e': {279 bool success = false;280 m_stop_on_error =281 OptionArgParser::ToBoolean(option_arg, false, &success);282 if (!success)283 return Status::FromErrorStringWithFormatv(284 "invalid value for stop-on-error: \"{0}\"", option_arg);285 } break;286 287 case 'D':288 m_use_dummy = true;289 break;290 291 default:292 llvm_unreachable("Unimplemented option");293 }294 return error;295 }296 297 void OptionParsingStarting(ExecutionContext *execution_context) override {298 m_use_commands = true;299 m_use_script_language = false;300 m_script_language = eScriptLanguageNone;301 302 m_use_one_liner = false;303 m_stop_on_error = true;304 m_one_liner.clear();305 m_use_dummy = false;306 }307 308 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {309 return llvm::ArrayRef(g_breakpoint_command_add_options);310 }311 312 // Instance variables to hold the values for command options.313 314 bool m_use_commands = false;315 bool m_use_script_language = false;316 lldb::ScriptLanguage m_script_language = eScriptLanguageNone;317 318 // Instance variables to hold the values for one_liner options.319 bool m_use_one_liner = false;320 std::string m_one_liner;321 bool m_stop_on_error;322 bool m_use_dummy;323 };324 325protected:326 void DoExecute(Args &command, CommandReturnObject &result) override {327 Target &target = m_options.m_use_dummy ? GetDummyTarget() : GetTarget();328 329 const BreakpointList &breakpoints = target.GetBreakpointList();330 size_t num_breakpoints = breakpoints.GetSize();331 332 if (num_breakpoints == 0) {333 result.AppendError("No breakpoints exist to have commands added");334 return;335 }336 337 if (!m_func_options.GetName().empty()) {338 m_options.m_use_one_liner = false;339 if (!m_options.m_use_script_language) {340 m_options.m_script_language = GetDebugger().GetScriptLanguage();341 m_options.m_use_script_language = true;342 }343 }344 345 BreakpointIDList valid_bp_ids;346 CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(347 command, target, result, &valid_bp_ids,348 BreakpointName::Permissions::PermissionKinds::listPerm);349 350 m_bp_options_vec.clear();351 352 if (result.Succeeded()) {353 const size_t count = valid_bp_ids.GetSize();354 355 for (size_t i = 0; i < count; ++i) {356 BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);357 if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {358 Breakpoint *bp =359 target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();360 if (cur_bp_id.GetLocationID() == LLDB_INVALID_BREAK_ID) {361 // This breakpoint does not have an associated location.362 m_bp_options_vec.push_back(bp->GetOptions());363 } else {364 BreakpointLocationSP bp_loc_sp(365 bp->FindLocationByID(cur_bp_id.GetLocationID()));366 // This breakpoint does have an associated location. Get its367 // breakpoint options.368 if (bp_loc_sp)369 m_bp_options_vec.push_back(bp_loc_sp->GetLocationOptions());370 }371 }372 }373 374 // If we are using script language, get the script interpreter in order375 // to set or collect command callback. Otherwise, call the methods376 // associated with this object.377 if (m_options.m_use_script_language) {378 Status error;379 ScriptInterpreter *script_interp = GetDebugger().GetScriptInterpreter(380 /*can_create=*/true, m_options.m_script_language);381 // Special handling for one-liner specified inline.382 if (m_options.m_use_one_liner) {383 error = script_interp->SetBreakpointCommandCallback(384 m_bp_options_vec, m_options.m_one_liner.c_str());385 } else if (!m_func_options.GetName().empty()) {386 error = script_interp->SetBreakpointCommandCallbackFunction(387 m_bp_options_vec, m_func_options.GetName().c_str(),388 m_func_options.GetStructuredData());389 } else {390 script_interp->CollectDataForBreakpointCommandCallback(391 m_bp_options_vec, result);392 }393 if (!error.Success())394 result.SetError(std::move(error));395 } else {396 // Special handling for one-liner specified inline.397 if (m_options.m_use_one_liner)398 SetBreakpointCommandCallback(m_bp_options_vec,399 m_options.m_one_liner.c_str());400 else401 CollectDataForBreakpointCommandCallback(m_bp_options_vec, result);402 }403 }404 }405 406private:407 CommandOptions m_options;408 OptionGroupPythonClassWithDict m_func_options;409 OptionGroupOptions m_all_options;410 411 std::vector<std::reference_wrapper<BreakpointOptions>>412 m_bp_options_vec; // This stores the413 // breakpoint options that414 // we are currently415 // collecting commands for. In the CollectData... calls we need to hand this416 // off to the IOHandler, which may run asynchronously. So we have to have417 // some way to keep it alive, and not leak it. Making it an ivar of the418 // command object, which never goes away achieves this. Note that if we were419 // able to run the same command concurrently in one interpreter we'd have to420 // make this "per invocation". But there are many more reasons why it is not421 // in general safe to do that in lldb at present, so it isn't worthwhile to422 // come up with a more complex mechanism to address this particular weakness423 // right now.424 static const char *g_reader_instructions;425};426 427const char *CommandObjectBreakpointCommandAdd::g_reader_instructions =428 "Enter your debugger command(s). Type 'DONE' to end.\n";429 430// CommandObjectBreakpointCommandDelete431 432#define LLDB_OPTIONS_breakpoint_command_delete433#include "CommandOptions.inc"434 435class CommandObjectBreakpointCommandDelete : public CommandObjectParsed {436public:437 CommandObjectBreakpointCommandDelete(CommandInterpreter &interpreter)438 : CommandObjectParsed(interpreter, "delete",439 "Delete the set of commands from a breakpoint.",440 nullptr) {441 AddSimpleArgumentList(eArgTypeBreakpointID);442 }443 444 ~CommandObjectBreakpointCommandDelete() override = default;445 446 Options *GetOptions() override { return &m_options; }447 448 class CommandOptions : public Options {449 public:450 CommandOptions() = default;451 452 ~CommandOptions() override = default;453 454 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,455 ExecutionContext *execution_context) override {456 Status error;457 const int short_option = m_getopt_table[option_idx].val;458 459 switch (short_option) {460 case 'D':461 m_use_dummy = true;462 break;463 464 default:465 llvm_unreachable("Unimplemented option");466 }467 468 return error;469 }470 471 void OptionParsingStarting(ExecutionContext *execution_context) override {472 m_use_dummy = false;473 }474 475 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {476 return llvm::ArrayRef(g_breakpoint_command_delete_options);477 }478 479 // Instance variables to hold the values for command options.480 bool m_use_dummy = false;481 };482 483protected:484 void DoExecute(Args &command, CommandReturnObject &result) override {485 Target &target = m_options.m_use_dummy ? GetDummyTarget() : GetTarget();486 487 const BreakpointList &breakpoints = target.GetBreakpointList();488 size_t num_breakpoints = breakpoints.GetSize();489 490 if (num_breakpoints == 0) {491 result.AppendError("No breakpoints exist to have commands deleted");492 return;493 }494 495 if (command.empty()) {496 result.AppendError(497 "No breakpoint specified from which to delete the commands");498 return;499 }500 501 BreakpointIDList valid_bp_ids;502 CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(503 command, target, result, &valid_bp_ids,504 BreakpointName::Permissions::PermissionKinds::listPerm);505 506 if (result.Succeeded()) {507 const size_t count = valid_bp_ids.GetSize();508 for (size_t i = 0; i < count; ++i) {509 BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);510 if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {511 Breakpoint *bp =512 target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();513 if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {514 BreakpointLocationSP bp_loc_sp(515 bp->FindLocationByID(cur_bp_id.GetLocationID()));516 if (bp_loc_sp)517 bp_loc_sp->ClearCallback();518 else {519 result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",520 cur_bp_id.GetBreakpointID(),521 cur_bp_id.GetLocationID());522 return;523 }524 } else {525 bp->ClearCallback();526 }527 }528 }529 }530 }531 532private:533 CommandOptions m_options;534};535 536// CommandObjectBreakpointCommandList537 538class CommandObjectBreakpointCommandList : public CommandObjectParsed {539public:540 CommandObjectBreakpointCommandList(CommandInterpreter &interpreter)541 : CommandObjectParsed(interpreter, "list",542 "List the script or set of commands to be "543 "executed when the breakpoint is hit.",544 nullptr, eCommandRequiresTarget) {545 AddSimpleArgumentList(eArgTypeBreakpointID);546 }547 548 ~CommandObjectBreakpointCommandList() override = default;549 550protected:551 void DoExecute(Args &command, CommandReturnObject &result) override {552 Target &target = GetTarget();553 554 const BreakpointList &breakpoints = target.GetBreakpointList();555 size_t num_breakpoints = breakpoints.GetSize();556 557 if (num_breakpoints == 0) {558 result.AppendError("No breakpoints exist for which to list commands");559 return;560 }561 562 if (command.empty()) {563 result.AppendError(564 "No breakpoint specified for which to list the commands");565 return;566 }567 568 BreakpointIDList valid_bp_ids;569 CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(570 command, target, result, &valid_bp_ids,571 BreakpointName::Permissions::PermissionKinds::listPerm);572 573 if (result.Succeeded()) {574 const size_t count = valid_bp_ids.GetSize();575 for (size_t i = 0; i < count; ++i) {576 BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);577 if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {578 Breakpoint *bp =579 target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();580 581 if (bp) {582 BreakpointLocationSP bp_loc_sp;583 if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {584 bp_loc_sp = bp->FindLocationByID(cur_bp_id.GetLocationID());585 if (!bp_loc_sp) {586 result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",587 cur_bp_id.GetBreakpointID(),588 cur_bp_id.GetLocationID());589 return;590 }591 }592 593 StreamString id_str;594 BreakpointID::GetCanonicalReference(&id_str,595 cur_bp_id.GetBreakpointID(),596 cur_bp_id.GetLocationID());597 const Baton *baton = nullptr;598 if (bp_loc_sp)599 baton =600 bp_loc_sp601 ->GetOptionsSpecifyingKind(BreakpointOptions::eCallback)602 .GetBaton();603 else604 baton = bp->GetOptions().GetBaton();605 606 if (baton) {607 result.GetOutputStream().Printf("Breakpoint %s:\n",608 id_str.GetData());609 baton->GetDescription(result.GetOutputStream().AsRawOstream(),610 eDescriptionLevelFull,611 result.GetOutputStream().GetIndentLevel() +612 2);613 } else {614 result.AppendMessageWithFormat(615 "Breakpoint %s does not have an associated command.\n",616 id_str.GetData());617 }618 }619 result.SetStatus(eReturnStatusSuccessFinishResult);620 } else {621 result.AppendErrorWithFormat("Invalid breakpoint ID: %u.\n",622 cur_bp_id.GetBreakpointID());623 }624 }625 }626 }627};628 629// CommandObjectBreakpointCommand630 631CommandObjectBreakpointCommand::CommandObjectBreakpointCommand(632 CommandInterpreter &interpreter)633 : CommandObjectMultiword(634 interpreter, "command",635 "Commands for adding, removing and listing "636 "LLDB commands executed when a breakpoint is "637 "hit.",638 "command <sub-command> [<sub-command-options>] <breakpoint-id>") {639 CommandObjectSP add_command_object(640 new CommandObjectBreakpointCommandAdd(interpreter));641 CommandObjectSP delete_command_object(642 new CommandObjectBreakpointCommandDelete(interpreter));643 CommandObjectSP list_command_object(644 new CommandObjectBreakpointCommandList(interpreter));645 646 add_command_object->SetCommandName("breakpoint command add");647 delete_command_object->SetCommandName("breakpoint command delete");648 list_command_object->SetCommandName("breakpoint command list");649 650 LoadSubCommand("add", add_command_object);651 LoadSubCommand("delete", delete_command_object);652 LoadSubCommand("list", list_command_object);653}654 655CommandObjectBreakpointCommand::~CommandObjectBreakpointCommand() = default;656