Path: blob/main/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
39587 views
//===-- CommandObjectBreakpointCommand.cpp --------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "CommandObjectBreakpointCommand.h"9#include "CommandObjectBreakpoint.h"10#include "lldb/Breakpoint/Breakpoint.h"11#include "lldb/Breakpoint/BreakpointIDList.h"12#include "lldb/Breakpoint/BreakpointLocation.h"13#include "lldb/Core/IOHandler.h"14#include "lldb/Host/OptionParser.h"15#include "lldb/Interpreter/CommandInterpreter.h"16#include "lldb/Interpreter/CommandOptionArgumentTable.h"17#include "lldb/Interpreter/CommandReturnObject.h"18#include "lldb/Interpreter/OptionArgParser.h"19#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"20#include "lldb/Target/Target.h"2122using namespace lldb;23using namespace lldb_private;2425#define LLDB_OPTIONS_breakpoint_command_add26#include "CommandOptions.inc"2728class CommandObjectBreakpointCommandAdd : public CommandObjectParsed,29public IOHandlerDelegateMultiline {30public:31CommandObjectBreakpointCommandAdd(CommandInterpreter &interpreter)32: CommandObjectParsed(interpreter, "add",33"Add LLDB commands to a breakpoint, to be executed "34"whenever the breakpoint is hit. "35"The commands added to the breakpoint replace any "36"commands previously added to it."37" If no breakpoint is specified, adds the "38"commands to the last created breakpoint.",39nullptr),40IOHandlerDelegateMultiline("DONE",41IOHandlerDelegate::Completion::LLDBCommand),42m_func_options("breakpoint command", false, 'F') {43SetHelpLong(44R"(45General information about entering breakpoint commands46------------------------------------------------------4748)"49"This command will prompt for commands to be executed when the specified \50breakpoint is hit. Each command is typed on its own line following the '> ' \51prompt until 'DONE' is entered."52R"(5354)"55"Syntactic errors may not be detected when initially entered, and many \56malformed commands can silently fail when executed. If your breakpoint commands \57do not appear to be executing, double-check the command syntax."58R"(5960)"61"Note: You may enter any debugger command exactly as you would at the debugger \62prompt. There is no limit to the number of commands supplied, but do NOT enter \63more than one command per line."64R"(6566Special information about PYTHON breakpoint commands67----------------------------------------------------6869)"70"You may enter either one or more lines of Python, including function \71definitions or calls to functions that will have been imported by the time \72the code executes. Single line breakpoint commands will be interpreted 'as is' \73when the breakpoint is hit. Multiple lines of Python will be wrapped in a \74generated function, and a call to the function will be attached to the breakpoint."75R"(7677This auto-generated function is passed in three arguments:7879frame: an lldb.SBFrame object for the frame which hit breakpoint.8081bp_loc: an lldb.SBBreakpointLocation object that represents the breakpoint location that was hit.8283dict: the python session dictionary hit.8485)"86"When specifying a python function with the --python-function option, you need \87to supply the function name prepended by the module name:"88R"(8990--python-function myutils.breakpoint_callback9192The function itself must have either of the following prototypes:9394def breakpoint_callback(frame, bp_loc, internal_dict):95# Your code goes here9697or:9899def breakpoint_callback(frame, bp_loc, extra_args, internal_dict):100# Your code goes here101102)"103"The arguments are the same as the arguments passed to generated functions as \104described above. In the second form, any -k and -v pairs provided to the command will \105be packaged into a SBDictionary in an SBStructuredData and passed as the extra_args parameter. \106\n\n\107Note that the global variable 'lldb.frame' will NOT be updated when \108this function is called, so be sure to use the 'frame' argument. The 'frame' argument \109can get you to the thread via frame.GetThread(), the thread can get you to the \110process via thread.GetProcess(), and the process can get you back to the target \111via process.GetTarget()."112R"(113114)"115"Important Note: As Python code gets collected into functions, access to global \116variables requires explicit scoping using the 'global' keyword. Be sure to use correct \117Python syntax, including indentation, when entering Python breakpoint commands."118R"(119120Example Python one-line breakpoint command:121122(lldb) breakpoint command add -s python 1123Enter your Python command(s). Type 'DONE' to end.124def function (frame, bp_loc, internal_dict):125"""frame: the lldb.SBFrame for the location at which you stopped126bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information127internal_dict: an LLDB support object not to be used"""128print("Hit this breakpoint!")129DONE130131As a convenience, this also works for a short Python one-liner:132133(lldb) breakpoint command add -s python 1 -o 'import time; print(time.asctime())'134(lldb) run135Launching '.../a.out' (x86_64)136(lldb) Fri Sep 10 12:17:45 2010137Process 21778 Stopped138* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = breakpoint 1.1, queue = com.apple.main-thread1393614037 int c(int val)14138 {14239 -> return val + 3;14340 }1444114542 int main (int argc, char const *argv[])146147Example multiple line Python breakpoint command:148149(lldb) breakpoint command add -s p 1150Enter your Python command(s). Type 'DONE' to end.151def function (frame, bp_loc, internal_dict):152"""frame: the lldb.SBFrame for the location at which you stopped153bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information154internal_dict: an LLDB support object not to be used"""155global bp_count156bp_count = bp_count + 1157print("Hit this breakpoint " + repr(bp_count) + " times!")158DONE159160)"161"In this case, since there is a reference to a global variable, \162'bp_count', you will also need to make sure 'bp_count' exists and is \163initialized:"164R"(165166(lldb) script167>>> bp_count = 0168>>> quit()169170)"171"Your Python code, however organized, can optionally return a value. \172If the returned value is False, that tells LLDB not to stop at the breakpoint \173to which the code is associated. Returning anything other than False, or even \174returning None, or even omitting a return statement entirely, will cause \175LLDB to stop."176R"(177178)"179"Final Note: A warning that no breakpoint command was generated when there \180are no syntax errors may indicate that a function was declared but never called.");181182m_all_options.Append(&m_options);183m_all_options.Append(&m_func_options, LLDB_OPT_SET_2 | LLDB_OPT_SET_3,184LLDB_OPT_SET_2);185m_all_options.Finalize();186187AddSimpleArgumentList(eArgTypeBreakpointID, eArgRepeatOptional);188}189190~CommandObjectBreakpointCommandAdd() override = default;191192Options *GetOptions() override { return &m_all_options; }193194void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {195StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());196if (output_sp && interactive) {197output_sp->PutCString(g_reader_instructions);198output_sp->Flush();199}200}201202void IOHandlerInputComplete(IOHandler &io_handler,203std::string &line) override {204io_handler.SetIsDone(true);205206std::vector<std::reference_wrapper<BreakpointOptions>> *bp_options_vec =207(std::vector<std::reference_wrapper<BreakpointOptions>> *)208io_handler.GetUserData();209for (BreakpointOptions &bp_options : *bp_options_vec) {210auto cmd_data = std::make_unique<BreakpointOptions::CommandData>();211cmd_data->user_source.SplitIntoLines(line.c_str(), line.size());212bp_options.SetCommandDataCallback(cmd_data);213}214}215216void CollectDataForBreakpointCommandCallback(217std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,218CommandReturnObject &result) {219m_interpreter.GetLLDBCommandsFromIOHandler(220"> ", // Prompt221*this, // IOHandlerDelegate222&bp_options_vec); // Baton for the "io_handler" that will be passed back223// into our IOHandlerDelegate functions224}225226/// Set a one-liner as the callback for the breakpoint.227void SetBreakpointCommandCallback(228std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,229const char *oneliner) {230for (BreakpointOptions &bp_options : bp_options_vec) {231auto cmd_data = std::make_unique<BreakpointOptions::CommandData>();232233cmd_data->user_source.AppendString(oneliner);234cmd_data->stop_on_error = m_options.m_stop_on_error;235236bp_options.SetCommandDataCallback(cmd_data);237}238}239240class CommandOptions : public OptionGroup {241public:242CommandOptions() = default;243244~CommandOptions() override = default;245246Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,247ExecutionContext *execution_context) override {248Status error;249const int short_option =250g_breakpoint_command_add_options[option_idx].short_option;251252switch (short_option) {253case 'o':254m_use_one_liner = true;255m_one_liner = std::string(option_arg);256break;257258case 's':259m_script_language = (lldb::ScriptLanguage)OptionArgParser::ToOptionEnum(260option_arg,261g_breakpoint_command_add_options[option_idx].enum_values,262eScriptLanguageNone, error);263switch (m_script_language) {264case eScriptLanguagePython:265case eScriptLanguageLua:266m_use_script_language = true;267break;268case eScriptLanguageNone:269case eScriptLanguageUnknown:270m_use_script_language = false;271break;272}273break;274275case 'e': {276bool success = false;277m_stop_on_error =278OptionArgParser::ToBoolean(option_arg, false, &success);279if (!success)280error.SetErrorStringWithFormat(281"invalid value for stop-on-error: \"%s\"",282option_arg.str().c_str());283} break;284285case 'D':286m_use_dummy = true;287break;288289default:290llvm_unreachable("Unimplemented option");291}292return error;293}294295void OptionParsingStarting(ExecutionContext *execution_context) override {296m_use_commands = true;297m_use_script_language = false;298m_script_language = eScriptLanguageNone;299300m_use_one_liner = false;301m_stop_on_error = true;302m_one_liner.clear();303m_use_dummy = false;304}305306llvm::ArrayRef<OptionDefinition> GetDefinitions() override {307return llvm::ArrayRef(g_breakpoint_command_add_options);308}309310// Instance variables to hold the values for command options.311312bool m_use_commands = false;313bool m_use_script_language = false;314lldb::ScriptLanguage m_script_language = eScriptLanguageNone;315316// Instance variables to hold the values for one_liner options.317bool m_use_one_liner = false;318std::string m_one_liner;319bool m_stop_on_error;320bool m_use_dummy;321};322323protected:324void DoExecute(Args &command, CommandReturnObject &result) override {325Target &target = GetSelectedOrDummyTarget(m_options.m_use_dummy);326327const BreakpointList &breakpoints = target.GetBreakpointList();328size_t num_breakpoints = breakpoints.GetSize();329330if (num_breakpoints == 0) {331result.AppendError("No breakpoints exist to have commands added");332return;333}334335if (!m_func_options.GetName().empty()) {336m_options.m_use_one_liner = false;337if (!m_options.m_use_script_language) {338m_options.m_script_language = GetDebugger().GetScriptLanguage();339m_options.m_use_script_language = true;340}341}342343BreakpointIDList valid_bp_ids;344CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(345command, &target, result, &valid_bp_ids,346BreakpointName::Permissions::PermissionKinds::listPerm);347348m_bp_options_vec.clear();349350if (result.Succeeded()) {351const size_t count = valid_bp_ids.GetSize();352353for (size_t i = 0; i < count; ++i) {354BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);355if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {356Breakpoint *bp =357target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();358if (cur_bp_id.GetLocationID() == LLDB_INVALID_BREAK_ID) {359// This breakpoint does not have an associated location.360m_bp_options_vec.push_back(bp->GetOptions());361} else {362BreakpointLocationSP bp_loc_sp(363bp->FindLocationByID(cur_bp_id.GetLocationID()));364// This breakpoint does have an associated location. Get its365// breakpoint options.366if (bp_loc_sp)367m_bp_options_vec.push_back(bp_loc_sp->GetLocationOptions());368}369}370}371372// If we are using script language, get the script interpreter in order373// to set or collect command callback. Otherwise, call the methods374// associated with this object.375if (m_options.m_use_script_language) {376Status error;377ScriptInterpreter *script_interp = GetDebugger().GetScriptInterpreter(378/*can_create=*/true, m_options.m_script_language);379// Special handling for one-liner specified inline.380if (m_options.m_use_one_liner) {381error = script_interp->SetBreakpointCommandCallback(382m_bp_options_vec, m_options.m_one_liner.c_str());383} else if (!m_func_options.GetName().empty()) {384error = script_interp->SetBreakpointCommandCallbackFunction(385m_bp_options_vec, m_func_options.GetName().c_str(),386m_func_options.GetStructuredData());387} else {388script_interp->CollectDataForBreakpointCommandCallback(389m_bp_options_vec, result);390}391if (!error.Success())392result.SetError(error);393} else {394// Special handling for one-liner specified inline.395if (m_options.m_use_one_liner)396SetBreakpointCommandCallback(m_bp_options_vec,397m_options.m_one_liner.c_str());398else399CollectDataForBreakpointCommandCallback(m_bp_options_vec, result);400}401}402}403404private:405CommandOptions m_options;406OptionGroupPythonClassWithDict m_func_options;407OptionGroupOptions m_all_options;408409std::vector<std::reference_wrapper<BreakpointOptions>>410m_bp_options_vec; // This stores the411// breakpoint options that412// we are currently413// collecting commands for. In the CollectData... calls we need to hand this414// off to the IOHandler, which may run asynchronously. So we have to have415// some way to keep it alive, and not leak it. Making it an ivar of the416// command object, which never goes away achieves this. Note that if we were417// able to run the same command concurrently in one interpreter we'd have to418// make this "per invocation". But there are many more reasons why it is not419// in general safe to do that in lldb at present, so it isn't worthwhile to420// come up with a more complex mechanism to address this particular weakness421// right now.422static const char *g_reader_instructions;423};424425const char *CommandObjectBreakpointCommandAdd::g_reader_instructions =426"Enter your debugger command(s). Type 'DONE' to end.\n";427428// CommandObjectBreakpointCommandDelete429430#define LLDB_OPTIONS_breakpoint_command_delete431#include "CommandOptions.inc"432433class CommandObjectBreakpointCommandDelete : public CommandObjectParsed {434public:435CommandObjectBreakpointCommandDelete(CommandInterpreter &interpreter)436: CommandObjectParsed(interpreter, "delete",437"Delete the set of commands from a breakpoint.",438nullptr) {439AddSimpleArgumentList(eArgTypeBreakpointID);440}441442~CommandObjectBreakpointCommandDelete() override = default;443444Options *GetOptions() override { return &m_options; }445446class CommandOptions : public Options {447public:448CommandOptions() = default;449450~CommandOptions() override = default;451452Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,453ExecutionContext *execution_context) override {454Status error;455const int short_option = m_getopt_table[option_idx].val;456457switch (short_option) {458case 'D':459m_use_dummy = true;460break;461462default:463llvm_unreachable("Unimplemented option");464}465466return error;467}468469void OptionParsingStarting(ExecutionContext *execution_context) override {470m_use_dummy = false;471}472473llvm::ArrayRef<OptionDefinition> GetDefinitions() override {474return llvm::ArrayRef(g_breakpoint_command_delete_options);475}476477// Instance variables to hold the values for command options.478bool m_use_dummy = false;479};480481protected:482void DoExecute(Args &command, CommandReturnObject &result) override {483Target &target = GetSelectedOrDummyTarget(m_options.m_use_dummy);484485const BreakpointList &breakpoints = target.GetBreakpointList();486size_t num_breakpoints = breakpoints.GetSize();487488if (num_breakpoints == 0) {489result.AppendError("No breakpoints exist to have commands deleted");490return;491}492493if (command.empty()) {494result.AppendError(495"No breakpoint specified from which to delete the commands");496return;497}498499BreakpointIDList valid_bp_ids;500CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(501command, &target, result, &valid_bp_ids,502BreakpointName::Permissions::PermissionKinds::listPerm);503504if (result.Succeeded()) {505const size_t count = valid_bp_ids.GetSize();506for (size_t i = 0; i < count; ++i) {507BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);508if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {509Breakpoint *bp =510target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();511if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {512BreakpointLocationSP bp_loc_sp(513bp->FindLocationByID(cur_bp_id.GetLocationID()));514if (bp_loc_sp)515bp_loc_sp->ClearCallback();516else {517result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",518cur_bp_id.GetBreakpointID(),519cur_bp_id.GetLocationID());520return;521}522} else {523bp->ClearCallback();524}525}526}527}528}529530private:531CommandOptions m_options;532};533534// CommandObjectBreakpointCommandList535536class CommandObjectBreakpointCommandList : public CommandObjectParsed {537public:538CommandObjectBreakpointCommandList(CommandInterpreter &interpreter)539: CommandObjectParsed(interpreter, "list",540"List the script or set of commands to be "541"executed when the breakpoint is hit.",542nullptr, eCommandRequiresTarget) {543AddSimpleArgumentList(eArgTypeBreakpointID);544}545546~CommandObjectBreakpointCommandList() override = default;547548protected:549void DoExecute(Args &command, CommandReturnObject &result) override {550Target *target = &GetSelectedTarget();551552const BreakpointList &breakpoints = target->GetBreakpointList();553size_t num_breakpoints = breakpoints.GetSize();554555if (num_breakpoints == 0) {556result.AppendError("No breakpoints exist for which to list commands");557return;558}559560if (command.empty()) {561result.AppendError(562"No breakpoint specified for which to list the commands");563return;564}565566BreakpointIDList valid_bp_ids;567CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(568command, target, result, &valid_bp_ids,569BreakpointName::Permissions::PermissionKinds::listPerm);570571if (result.Succeeded()) {572const size_t count = valid_bp_ids.GetSize();573for (size_t i = 0; i < count; ++i) {574BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);575if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {576Breakpoint *bp =577target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();578579if (bp) {580BreakpointLocationSP bp_loc_sp;581if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {582bp_loc_sp = bp->FindLocationByID(cur_bp_id.GetLocationID());583if (!bp_loc_sp) {584result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",585cur_bp_id.GetBreakpointID(),586cur_bp_id.GetLocationID());587return;588}589}590591StreamString id_str;592BreakpointID::GetCanonicalReference(&id_str,593cur_bp_id.GetBreakpointID(),594cur_bp_id.GetLocationID());595const Baton *baton = nullptr;596if (bp_loc_sp)597baton =598bp_loc_sp599->GetOptionsSpecifyingKind(BreakpointOptions::eCallback)600.GetBaton();601else602baton = bp->GetOptions().GetBaton();603604if (baton) {605result.GetOutputStream().Printf("Breakpoint %s:\n",606id_str.GetData());607baton->GetDescription(result.GetOutputStream().AsRawOstream(),608eDescriptionLevelFull,609result.GetOutputStream().GetIndentLevel() +6102);611} else {612result.AppendMessageWithFormat(613"Breakpoint %s does not have an associated command.\n",614id_str.GetData());615}616}617result.SetStatus(eReturnStatusSuccessFinishResult);618} else {619result.AppendErrorWithFormat("Invalid breakpoint ID: %u.\n",620cur_bp_id.GetBreakpointID());621}622}623}624}625};626627// CommandObjectBreakpointCommand628629CommandObjectBreakpointCommand::CommandObjectBreakpointCommand(630CommandInterpreter &interpreter)631: CommandObjectMultiword(632interpreter, "command",633"Commands for adding, removing and listing "634"LLDB commands executed when a breakpoint is "635"hit.",636"command <sub-command> [<sub-command-options>] <breakpoint-id>") {637CommandObjectSP add_command_object(638new CommandObjectBreakpointCommandAdd(interpreter));639CommandObjectSP delete_command_object(640new CommandObjectBreakpointCommandDelete(interpreter));641CommandObjectSP list_command_object(642new CommandObjectBreakpointCommandList(interpreter));643644add_command_object->SetCommandName("breakpoint command add");645delete_command_object->SetCommandName("breakpoint command delete");646list_command_object->SetCommandName("breakpoint command list");647648LoadSubCommand("add", add_command_object);649LoadSubCommand("delete", delete_command_object);650LoadSubCommand("list", list_command_object);651}652653CommandObjectBreakpointCommand::~CommandObjectBreakpointCommand() = default;654655656