brintos

brintos / llvm-project-archived public Read only

0
0
Text · 21.2 KiB · 062bf75 Raw
592 lines · cpp
1//===-- CommandObjectWatchpointCommand.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 <vector>10 11#include "CommandObjectWatchpoint.h"12#include "CommandObjectWatchpointCommand.h"13#include "lldb/Breakpoint/StoppointCallbackContext.h"14#include "lldb/Breakpoint/Watchpoint.h"15#include "lldb/Core/IOHandler.h"16#include "lldb/Host/OptionParser.h"17#include "lldb/Host/StreamFile.h"18#include "lldb/Interpreter/CommandInterpreter.h"19#include "lldb/Interpreter/CommandOptionArgumentTable.h"20#include "lldb/Interpreter/CommandReturnObject.h"21#include "lldb/Interpreter/OptionArgParser.h"22#include "lldb/Target/Target.h"23#include "lldb/lldb-forward.h"24 25using namespace lldb;26using namespace lldb_private;27 28#define LLDB_OPTIONS_watchpoint_command_add29#include "CommandOptions.inc"30 31class CommandObjectWatchpointCommandAdd : public CommandObjectParsed,32                                          public IOHandlerDelegateMultiline {33public:34  CommandObjectWatchpointCommandAdd(CommandInterpreter &interpreter)35      : CommandObjectParsed(interpreter, "add",36                            "Add a set of LLDB commands to a watchpoint, to be "37                            "executed whenever the watchpoint is hit.  "38                            "The commands added to the watchpoint replace any "39                            "commands previously added to it.",40                            nullptr, eCommandRequiresTarget),41        IOHandlerDelegateMultiline("DONE",42                                   IOHandlerDelegate::Completion::LLDBCommand) {43    SetHelpLong(44        R"(45General information about entering watchpoint commands46------------------------------------------------------47 48)"49        "This command will prompt for commands to be executed when the specified \50watchpoint is hit.  Each command is typed on its own line following the '> ' \51prompt until 'DONE' is entered."52        R"(53 54)"55        "Syntactic errors may not be detected when initially entered, and many \56malformed commands can silently fail when executed.  If your watchpoint commands \57do not appear to be executing, double-check the command syntax."58        R"(59 60)"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."64        R"(65 66Special information about PYTHON watchpoint commands67----------------------------------------------------68 69)"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 watchpoint commands will be interpreted 'as is' \73when the watchpoint is hit.  Multiple lines of Python will be wrapped in a \74generated function, and a call to the function will be attached to the watchpoint."75        R"(76 77This auto-generated function is passed in three arguments:78 79    frame:  an lldb.SBFrame object for the frame which hit the watchpoint.80 81    wp:     the watchpoint that was hit.82 83)"84        "When specifying a python function with the --python-function option, you need \85to supply the function name prepended by the module name:"86        R"(87 88    --python-function myutils.watchpoint_callback89 90The function itself must have the following prototype:91 92def watchpoint_callback(frame, wp):93  # Your code goes here94 95)"96        "The arguments are the same as the arguments passed to generated functions as \97described above.  Note that the global variable 'lldb.frame' will NOT be updated when \98this function is called, so be sure to use the 'frame' argument. The 'frame' argument \99can get you to the thread via frame.GetThread(), the thread can get you to the \100process via thread.GetProcess(), and the process can get you back to the target \101via process.GetTarget()."102        R"(103 104)"105        "Important Note: As Python code gets collected into functions, access to global \106variables requires explicit scoping using the 'global' keyword.  Be sure to use correct \107Python syntax, including indentation, when entering Python watchpoint commands."108        R"(109 110Example Python one-line watchpoint command:111 112(lldb) watchpoint command add -s python 1113Enter your Python command(s). Type 'DONE' to end.114> print "Hit this watchpoint!"115> DONE116 117As a convenience, this also works for a short Python one-liner:118 119(lldb) watchpoint command add -s python 1 -o 'import time; print time.asctime()'120(lldb) run121Launching '.../a.out'  (x86_64)122(lldb) Fri Sep 10 12:17:45 2010123Process 21778 Stopped124* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = watchpoint 1.1, queue = com.apple.main-thread125  36126  37   	int c(int val)127  38   	{128  39 ->	    return val + 3;129  40   	}130  41131  42   	int main (int argc, char const *argv[])132 133Example multiple line Python watchpoint command, using function definition:134 135(lldb) watchpoint command add -s python 1136Enter your Python command(s). Type 'DONE' to end.137> def watchpoint_output (wp_no):138>     out_string = "Hit watchpoint number " + repr (wp_no)139>     print out_string140>     return True141> watchpoint_output (1)142> DONE143 144Example multiple line Python watchpoint command, using 'loose' Python:145 146(lldb) watchpoint command add -s p 1147Enter your Python command(s). Type 'DONE' to end.148> global wp_count149> wp_count = wp_count + 1150> print "Hit this watchpoint " + repr(wp_count) + " times!"151> DONE152 153)"154        "In this case, since there is a reference to a global variable, \155'wp_count', you will also need to make sure 'wp_count' exists and is \156initialized:"157        R"(158 159(lldb) script160>>> wp_count = 0161>>> quit()162 163)"164        "Final Note: A warning that no watchpoint command was generated when there \165are no syntax errors may indicate that a function was declared but never called.");166 167    AddSimpleArgumentList(eArgTypeWatchpointID);168  }169 170  ~CommandObjectWatchpointCommandAdd() override = default;171 172  Options *GetOptions() override { return &m_options; }173 174  void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {175    if (interactive) {176      if (lldb::LockableStreamFileSP output_sp =177              io_handler.GetOutputStreamFileSP()) {178        LockedStreamFile locked_stream = output_sp->Lock();179        locked_stream.PutCString(180            "Enter your debugger command(s).  Type 'DONE' to end.\n");181      }182    }183  }184 185  void IOHandlerInputComplete(IOHandler &io_handler,186                              std::string &line) override {187    io_handler.SetIsDone(true);188 189    // The WatchpointOptions object is owned by the watchpoint or watchpoint190    // location191    WatchpointOptions *wp_options =192        (WatchpointOptions *)io_handler.GetUserData();193    if (wp_options) {194      std::unique_ptr<WatchpointOptions::CommandData> data_up(195          new WatchpointOptions::CommandData());196      if (data_up) {197        data_up->user_source.SplitIntoLines(line);198        auto baton_sp = std::make_shared<WatchpointOptions::CommandBaton>(199            std::move(data_up));200        wp_options->SetCallback(WatchpointOptionsCallbackFunction, baton_sp);201      }202    }203  }204 205  void CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options,206                                               CommandReturnObject &result) {207    m_interpreter.GetLLDBCommandsFromIOHandler(208        "> ",        // Prompt209        *this,       // IOHandlerDelegate210        wp_options); // Baton for the "io_handler" that will be passed back into211                     // our IOHandlerDelegate functions212  }213 214  /// Set a one-liner as the callback for the watchpoint.215  void SetWatchpointCommandCallback(WatchpointOptions *wp_options,216                                    const char *oneliner) {217    std::unique_ptr<WatchpointOptions::CommandData> data_up(218        new WatchpointOptions::CommandData());219 220    // It's necessary to set both user_source and script_source to the221    // oneliner. The former is used to generate callback description (as in222    // watchpoint command list) while the latter is used for Python to223    // interpret during the actual callback.224    data_up->user_source.AppendString(oneliner);225    data_up->script_source.assign(oneliner);226    data_up->stop_on_error = m_options.m_stop_on_error;227 228    auto baton_sp =229        std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up));230    wp_options->SetCallback(WatchpointOptionsCallbackFunction, baton_sp);231  }232 233  static bool234  WatchpointOptionsCallbackFunction(void *baton,235                                    StoppointCallbackContext *context,236                                    lldb::user_id_t watch_id) {237    bool ret_value = true;238    if (baton == nullptr)239      return true;240 241    WatchpointOptions::CommandData *data =242        (WatchpointOptions::CommandData *)baton;243    StringList &commands = data->user_source;244 245    if (commands.GetSize() > 0) {246      ExecutionContext exe_ctx(context->exe_ctx_ref);247      Target *target = exe_ctx.GetTargetPtr();248      if (target) {249        Debugger &debugger = target->GetDebugger();250        CommandReturnObject result(debugger.GetUseColor());251 252        // Rig up the results secondary output stream to the debugger's, so the253        // output will come out synchronously if the debugger is set up that254        // way.255        result.SetImmediateOutputStream(debugger.GetAsyncOutputStream());256        result.SetImmediateErrorStream(debugger.GetAsyncErrorStream());257 258        CommandInterpreterRunOptions options;259        options.SetStopOnContinue(true);260        options.SetStopOnError(data->stop_on_error);261        options.SetEchoCommands(false);262        options.SetPrintResults(true);263        options.SetPrintErrors(true);264        options.SetAddToHistory(false);265 266        debugger.GetCommandInterpreter().HandleCommands(commands, exe_ctx,267                                                        options, result);268        result.GetImmediateOutputStream()->Flush();269        result.GetImmediateErrorStream()->Flush();270      }271    }272    return ret_value;273  }274 275  class CommandOptions : public Options {276  public:277    CommandOptions() = default;278 279    ~CommandOptions() override = default;280 281    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,282                          ExecutionContext *execution_context) override {283      Status error;284      const int short_option = m_getopt_table[option_idx].val;285 286      switch (short_option) {287      case 'o':288        m_use_one_liner = true;289        m_one_liner = std::string(option_arg);290        break;291 292      case 's':293        m_script_language = (lldb::ScriptLanguage)OptionArgParser::ToOptionEnum(294            option_arg, GetDefinitions()[option_idx].enum_values,295            eScriptLanguageNone, error);296 297        switch (m_script_language) {298        case eScriptLanguagePython:299        case eScriptLanguageLua:300          m_use_script_language = true;301          break;302        case eScriptLanguageNone:303        case eScriptLanguageUnknown:304          m_use_script_language = false;305          break;306        }307        break;308 309      case 'e': {310        bool success = false;311        m_stop_on_error =312            OptionArgParser::ToBoolean(option_arg, false, &success);313        if (!success)314          return Status::FromErrorStringWithFormatv(315              "invalid value for stop-on-error: \"{0}\"", option_arg);316      } break;317 318      case 'F':319        m_use_one_liner = false;320        m_function_name.assign(std::string(option_arg));321        break;322 323      default:324        llvm_unreachable("Unimplemented option");325      }326      return error;327    }328 329    void OptionParsingStarting(ExecutionContext *execution_context) override {330      m_use_commands = true;331      m_use_script_language = false;332      m_script_language = eScriptLanguageNone;333 334      m_use_one_liner = false;335      m_stop_on_error = true;336      m_one_liner.clear();337      m_function_name.clear();338    }339 340    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {341      return llvm::ArrayRef(g_watchpoint_command_add_options);342    }343 344    // Instance variables to hold the values for command options.345 346    bool m_use_commands = false;347    bool m_use_script_language = false;348    lldb::ScriptLanguage m_script_language = eScriptLanguageNone;349 350    // Instance variables to hold the values for one_liner options.351    bool m_use_one_liner = false;352    std::string m_one_liner;353    bool m_stop_on_error;354    std::string m_function_name;355  };356 357protected:358  void DoExecute(Args &command, CommandReturnObject &result) override {359    Target &target = GetTarget();360 361    const WatchpointList &watchpoints = target.GetWatchpointList();362    size_t num_watchpoints = watchpoints.GetSize();363 364    if (num_watchpoints == 0) {365      result.AppendError("No watchpoints exist to have commands added");366      return;367    }368 369    if (!m_options.m_function_name.empty()) {370      if (!m_options.m_use_script_language) {371        m_options.m_script_language = GetDebugger().GetScriptLanguage();372        m_options.m_use_script_language = true;373      }374    }375 376    std::vector<uint32_t> valid_wp_ids;377    if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,378                                                               valid_wp_ids)) {379      result.AppendError("invalid watchpoints specification");380      return;381    }382 383    result.SetStatus(eReturnStatusSuccessFinishNoResult);384    const size_t count = valid_wp_ids.size();385    for (size_t i = 0; i < count; ++i) {386      uint32_t cur_wp_id = valid_wp_ids.at(i);387      if (cur_wp_id != LLDB_INVALID_WATCH_ID) {388        Watchpoint *wp = target.GetWatchpointList().FindByID(cur_wp_id).get();389        // Sanity check wp first.390        if (wp == nullptr)391          continue;392 393        WatchpointOptions *wp_options = wp->GetOptions();394        // Skip this watchpoint if wp_options is not good.395        if (wp_options == nullptr)396          continue;397 398        // If we are using script language, get the script interpreter in order399        // to set or collect command callback.  Otherwise, call the methods400        // associated with this object.401        if (m_options.m_use_script_language) {402          ScriptInterpreter *script_interp = GetDebugger().GetScriptInterpreter(403              /*can_create=*/true, m_options.m_script_language);404          // Special handling for one-liner specified inline.405          if (m_options.m_use_one_liner) {406            script_interp->SetWatchpointCommandCallback(407                wp_options, m_options.m_one_liner.c_str(),408                /*is_callback=*/false);409          }410          // Special handling for using a Python function by name instead of411          // extending the watchpoint callback data structures, we just412          // automatize what the user would do manually: make their watchpoint413          // command be a function call414          else if (!m_options.m_function_name.empty()) {415            std::string function_signature = m_options.m_function_name;416            function_signature += "(frame, wp, internal_dict)";417            script_interp->SetWatchpointCommandCallback(418                wp_options, function_signature.c_str(), /*is_callback=*/true);419          } else {420            script_interp->CollectDataForWatchpointCommandCallback(wp_options,421                                                                   result);422          }423        } else {424          // Special handling for one-liner specified inline.425          if (m_options.m_use_one_liner)426            SetWatchpointCommandCallback(wp_options,427                                         m_options.m_one_liner.c_str());428          else429            CollectDataForWatchpointCommandCallback(wp_options, result);430        }431      }432    }433  }434 435private:436  CommandOptions m_options;437};438 439// CommandObjectWatchpointCommandDelete440 441class CommandObjectWatchpointCommandDelete : public CommandObjectParsed {442public:443  CommandObjectWatchpointCommandDelete(CommandInterpreter &interpreter)444      : CommandObjectParsed(interpreter, "delete",445                            "Delete the set of commands from a watchpoint.",446                            nullptr, eCommandRequiresTarget) {447    AddSimpleArgumentList(eArgTypeWatchpointID);448  }449 450  ~CommandObjectWatchpointCommandDelete() override = default;451 452protected:453  void DoExecute(Args &command, CommandReturnObject &result) override {454    Target &target = GetTarget();455 456    const WatchpointList &watchpoints = target.GetWatchpointList();457    size_t num_watchpoints = watchpoints.GetSize();458 459    if (num_watchpoints == 0) {460      result.AppendError("No watchpoints exist to have commands deleted");461      return;462    }463 464    if (command.GetArgumentCount() == 0) {465      result.AppendError(466          "No watchpoint specified from which to delete the commands");467      return;468    }469 470    std::vector<uint32_t> valid_wp_ids;471    if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,472                                                               valid_wp_ids)) {473      result.AppendError("invalid watchpoints specification");474      return;475    }476 477    result.SetStatus(eReturnStatusSuccessFinishNoResult);478    const size_t count = valid_wp_ids.size();479    for (size_t i = 0; i < count; ++i) {480      uint32_t cur_wp_id = valid_wp_ids.at(i);481      if (cur_wp_id != LLDB_INVALID_WATCH_ID) {482        Watchpoint *wp = target.GetWatchpointList().FindByID(cur_wp_id).get();483        if (wp)484          wp->ClearCallback();485      } else {486        result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", cur_wp_id);487        return;488      }489    }490  }491};492 493// CommandObjectWatchpointCommandList494 495class CommandObjectWatchpointCommandList : public CommandObjectParsed {496public:497  CommandObjectWatchpointCommandList(CommandInterpreter &interpreter)498      : CommandObjectParsed(interpreter, "list",499                            "List the script or set of commands to be executed "500                            "when the watchpoint is hit.",501                            nullptr, eCommandRequiresTarget) {502    AddSimpleArgumentList(eArgTypeWatchpointID);503  }504 505  ~CommandObjectWatchpointCommandList() override = default;506 507protected:508  void DoExecute(Args &command, CommandReturnObject &result) override {509    Target &target = GetTarget();510 511    const WatchpointList &watchpoints = target.GetWatchpointList();512    size_t num_watchpoints = watchpoints.GetSize();513 514    if (num_watchpoints == 0) {515      result.AppendError("No watchpoints exist for which to list commands");516      return;517    }518 519    if (command.GetArgumentCount() == 0) {520      result.AppendError(521          "No watchpoint specified for which to list the commands");522      return;523    }524 525    std::vector<uint32_t> valid_wp_ids;526    if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,527                                                               valid_wp_ids)) {528      result.AppendError("invalid watchpoints specification");529      return;530    }531 532    result.SetStatus(eReturnStatusSuccessFinishNoResult);533    const size_t count = valid_wp_ids.size();534    for (size_t i = 0; i < count; ++i) {535      uint32_t cur_wp_id = valid_wp_ids.at(i);536      if (cur_wp_id != LLDB_INVALID_WATCH_ID) {537        Watchpoint *wp = target.GetWatchpointList().FindByID(cur_wp_id).get();538 539        if (wp) {540          const WatchpointOptions *wp_options = wp->GetOptions();541          if (wp_options) {542            // Get the callback baton associated with the current watchpoint.543            const Baton *baton = wp_options->GetBaton();544            if (baton) {545              result.GetOutputStream().Printf("Watchpoint %u:\n", cur_wp_id);546              baton->GetDescription(result.GetOutputStream().AsRawOstream(),547                                    eDescriptionLevelFull,548                                    result.GetOutputStream().GetIndentLevel() +549                                        2);550            } else {551              result.AppendMessageWithFormat(552                  "Watchpoint %u does not have an associated command.\n",553                  cur_wp_id);554            }555          }556          result.SetStatus(eReturnStatusSuccessFinishResult);557        } else {558          result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n",559                                       cur_wp_id);560        }561      }562    }563  }564};565 566// CommandObjectWatchpointCommand567 568CommandObjectWatchpointCommand::CommandObjectWatchpointCommand(569    CommandInterpreter &interpreter)570    : CommandObjectMultiword(571          interpreter, "command",572          "Commands for adding, removing and examining LLDB commands "573          "executed when the watchpoint is hit (watchpoint 'commands').",574          "command <sub-command> [<sub-command-options>] <watchpoint-id>") {575  CommandObjectSP add_command_object(576      new CommandObjectWatchpointCommandAdd(interpreter));577  CommandObjectSP delete_command_object(578      new CommandObjectWatchpointCommandDelete(interpreter));579  CommandObjectSP list_command_object(580      new CommandObjectWatchpointCommandList(interpreter));581 582  add_command_object->SetCommandName("watchpoint command add");583  delete_command_object->SetCommandName("watchpoint command delete");584  list_command_object->SetCommandName("watchpoint command list");585 586  LoadSubCommand("add", add_command_object);587  LoadSubCommand("delete", delete_command_object);588  LoadSubCommand("list", list_command_object);589}590 591CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand() = default;592