brintos

brintos / llvm-project-archived public Read only

0
0
Text · 23.8 KiB · a913ed5 Raw
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