diff options
Diffstat (limited to 'source/Commands/CommandObjectFrame.cpp')
| -rw-r--r-- | source/Commands/CommandObjectFrame.cpp | 414 | 
1 files changed, 404 insertions, 10 deletions
diff --git a/source/Commands/CommandObjectFrame.cpp b/source/Commands/CommandObjectFrame.cpp index 64de14f2edbf0..f8318a38e28d8 100644 --- a/source/Commands/CommandObjectFrame.cpp +++ b/source/Commands/CommandObjectFrame.cpp @@ -7,12 +7,8 @@  //  //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes  #include <string> -// Other libraries and framework includes -// Project includes  #include "CommandObjectFrame.h"  #include "lldb/Core/Debugger.h"  #include "lldb/Core/Module.h" @@ -24,6 +20,7 @@  #include "lldb/DataFormatters/ValueObjectPrinter.h"  #include "lldb/Host/Host.h"  #include "lldb/Host/OptionParser.h" +#include "lldb/Host/StringConvert.h"  #include "lldb/Interpreter/CommandInterpreter.h"  #include "lldb/Interpreter/CommandReturnObject.h"  #include "lldb/Interpreter/OptionGroupFormat.h" @@ -40,6 +37,7 @@  #include "lldb/Symbol/VariableList.h"  #include "lldb/Target/Process.h"  #include "lldb/Target/StackFrame.h" +#include "lldb/Target/StackFrameRecognizer.h"  #include "lldb/Target/StopInfo.h"  #include "lldb/Target/Target.h"  #include "lldb/Target/Thread.h" @@ -61,11 +59,11 @@ using namespace lldb_private;  // CommandObjectFrameDiagnose  //------------------------------------------------------------------------- -static OptionDefinition g_frame_diag_options[] = { +static constexpr OptionDefinition g_frame_diag_options[] = {      // clang-format off -  { LLDB_OPT_SET_1, false, "register", 'r', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeRegisterName,    "A register to diagnose." }, -  { LLDB_OPT_SET_1, false, "address",  'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddress,         "An address to diagnose." }, -  { LLDB_OPT_SET_1, false, "offset",   'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOffset,          "An optional offset.  Requires --register." } +  { LLDB_OPT_SET_1, false, "register", 'r', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeRegisterName,    "A register to diagnose." }, +  { LLDB_OPT_SET_1, false, "address",  'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddress,         "An address to diagnose." }, +  { LLDB_OPT_SET_1, false, "offset",   'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOffset,          "An optional offset.  Requires --register." }      // clang-format on  }; @@ -251,7 +249,7 @@ protected:  static OptionDefinition g_frame_select_options[] = {      // clang-format off -  { LLDB_OPT_SET_1, false, "relative", 'r', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOffset, "A relative frame index offset from the current frame index." }, +  { LLDB_OPT_SET_1, false, "relative", 'r', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOffset, "A relative frame index offset from the current frame index." },      // clang-format on  }; @@ -427,7 +425,17 @@ public:              "arguments and local variables in scope. Names of argument, "              "local, file static and file global variables can be specified. "              "Children of aggregate variables can be specified such as " -            "'var->child.x'.", +            "'var->child.x'.  The -> and [] operators in 'frame variable' do " +            "not invoke operator overloads if they exist, but directly access " +            "the specified element.  If you want to trigger operator overloads " +            "use the expression command to print the variable instead." +            "\nIt is worth noting that except for overloaded " +            "operators, when printing local variables 'expr local_var' and " +            "'frame var local_var' produce the same " +            "results.  However, 'frame variable' is more efficient, since it " +            "uses debug information and memory reads directly, rather than " +            "parsing and evaluating an expression, which may even involve " +            "JITing and running code in the target program.",              nullptr, eCommandRequiresFrame | eCommandTryTargetAPILock |                           eCommandProcessMustBeLaunched |                           eCommandProcessMustBePaused | eCommandRequiresProcess), @@ -705,6 +713,23 @@ protected:        result.SetStatus(eReturnStatusSuccessFinishResult);      } +    if (m_option_variable.show_recognized_args) { +      auto recognized_frame = frame->GetRecognizedFrame(); +      if (recognized_frame) { +        ValueObjectListSP recognized_arg_list = +            recognized_frame->GetRecognizedArguments(); +        if (recognized_arg_list) { +          for (auto &rec_value_sp : recognized_arg_list->GetObjects()) { +            options.SetFormat(m_option_format.GetFormat()); +            options.SetVariableFormatDisplayLanguage( +                rec_value_sp->GetPreferredDisplayLanguage()); +            options.SetRootValueObjectName(rec_value_sp->GetName().AsCString()); +            rec_value_sp->Dump(result.GetOutputStream(), options); +          } +        } +      } +    } +      if (m_interpreter.TruncationWarningNecessary()) {        result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(),                                        m_cmd_name.c_str()); @@ -728,6 +753,370 @@ protected:    OptionGroupValueObjectDisplay m_varobj_options;  }; +#pragma mark CommandObjectFrameRecognizer + +static OptionDefinition g_frame_recognizer_add_options[] = { +    // clang-format off +  { LLDB_OPT_SET_ALL, false, "shlib",         's', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eModuleCompletion, eArgTypeShlibName,   "Name of the module or shared library that this recognizer applies to." }, +  { LLDB_OPT_SET_ALL, false, "function",      'n', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeName,        "Name of the function that this recognizer applies to." }, +  { LLDB_OPT_SET_2,   false, "python-class",  'l', OptionParser::eRequiredArgument, nullptr, {}, 0,                                     eArgTypePythonClass, "Give the name of a Python class to use for this frame recognizer." }, +  { LLDB_OPT_SET_ALL, false, "regex",         'x', OptionParser::eNoArgument,       nullptr, {}, 0,                                     eArgTypeNone,        "Function name and module name are actually regular expressions." } +    // clang-format on +}; + +class CommandObjectFrameRecognizerAdd : public CommandObjectParsed { +private: +  class CommandOptions : public Options { +  public: +    CommandOptions() : Options() {} +    ~CommandOptions() override = default; + +    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, +                          ExecutionContext *execution_context) override { +      Status error; +      const int short_option = m_getopt_table[option_idx].val; + +      switch (short_option) { +      case 'l': +        m_class_name = std::string(option_arg); +        break; +      case 's': +        m_module = std::string(option_arg); +        break; +      case 'n': +        m_function = std::string(option_arg); +        break; +      case 'x': +        m_regex = true; +        break; +      default: +        error.SetErrorStringWithFormat("unrecognized option '%c'", +                                       short_option); +        break; +      } + +      return error; +    } + +    void OptionParsingStarting(ExecutionContext *execution_context) override { +      m_module = ""; +      m_function = ""; +      m_class_name = ""; +      m_regex = false; +    } + +    llvm::ArrayRef<OptionDefinition> GetDefinitions() override { +      return llvm::makeArrayRef(g_frame_recognizer_add_options); +    } + +    // Instance variables to hold the values for command options. +    std::string m_class_name; +    std::string m_module; +    std::string m_function; +    bool m_regex; +  }; + +  CommandOptions m_options; + +  Options *GetOptions() override { return &m_options; } + +protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override; + +public: +  CommandObjectFrameRecognizerAdd(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "frame recognizer add", +                            "Add a new frame recognizer.", nullptr), +        m_options() { +    SetHelpLong(R"( +Frame recognizers allow for retrieving information about special frames based on +ABI, arguments or other special properties of that frame, even without source +code or debug info. Currently, one use case is to extract function arguments +that would otherwise be unaccesible, or augment existing arguments. + +Adding a custom frame recognizer is possible by implementing a Python class +and using the 'frame recognizer add' command. The Python class should have a +'get_recognized_arguments' method and it will receive an argument of type +lldb.SBFrame representing the current frame that we are trying to recognize. +The method should return a (possibly empty) list of lldb.SBValue objects that +represent the recognized arguments. + +An example of a recognizer that retrieves the file descriptor values from libc +functions 'read', 'write' and 'close' follows: + +  class LibcFdRecognizer(object): +    def get_recognized_arguments(self, frame): +      if frame.name in ["read", "write", "close"]: +        fd = frame.EvaluateExpression("$arg1").unsigned +        value = lldb.target.CreateValueFromExpression("fd", "(int)%d" % fd) +        return [value] +      return [] + +The file containing this implementation can be imported via 'command script +import' and then we can register this recognizer with 'frame recognizer add'. +It's important to restrict the recognizer to the libc library (which is +libsystem_kernel.dylib on macOS) to avoid matching functions with the same name +in other modules: + +(lldb) command script import .../fd_recognizer.py +(lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib + +When the program is stopped at the beginning of the 'read' function in libc, we +can view the recognizer arguments in 'frame variable': + +(lldb) b read +(lldb) r +Process 1234 stopped +* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3 +    frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read +(lldb) frame variable +(int) fd = 3 + +    )"); +  } +  ~CommandObjectFrameRecognizerAdd() override = default; +}; + +bool CommandObjectFrameRecognizerAdd::DoExecute(Args &command, +                                                CommandReturnObject &result) { +#ifndef LLDB_DISABLE_PYTHON +  if (m_options.m_class_name.empty()) { +    result.AppendErrorWithFormat( +        "%s needs a Python class name (-l argument).\n", m_cmd_name.c_str()); +    result.SetStatus(eReturnStatusFailed); +    return false; +  } + +  if (m_options.m_module.empty()) { +    result.AppendErrorWithFormat("%s needs a module name (-s argument).\n", +                                 m_cmd_name.c_str()); +    result.SetStatus(eReturnStatusFailed); +    return false; +  } + +  if (m_options.m_function.empty()) { +    result.AppendErrorWithFormat("%s needs a function name (-n argument).\n", +                                 m_cmd_name.c_str()); +    result.SetStatus(eReturnStatusFailed); +    return false; +  } + +  ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + +  if (interpreter && +      !interpreter->CheckObjectExists(m_options.m_class_name.c_str())) { +    result.AppendWarning( +        "The provided class does not exist - please define it " +        "before attempting to use this frame recognizer"); +  } + +  StackFrameRecognizerSP recognizer_sp = +      StackFrameRecognizerSP(new ScriptedStackFrameRecognizer( +          interpreter, m_options.m_class_name.c_str())); +  if (m_options.m_regex) { +    auto module = +        RegularExpressionSP(new RegularExpression(m_options.m_module)); +    auto func = +        RegularExpressionSP(new RegularExpression(m_options.m_function)); +    StackFrameRecognizerManager::AddRecognizer(recognizer_sp, module, func); +  } else { +    auto module = ConstString(m_options.m_module); +    auto func = ConstString(m_options.m_function); +    StackFrameRecognizerManager::AddRecognizer(recognizer_sp, module, func); +  } +#endif + +  result.SetStatus(eReturnStatusSuccessFinishNoResult); +  return result.Succeeded(); +} + +class CommandObjectFrameRecognizerClear : public CommandObjectParsed { +public: +  CommandObjectFrameRecognizerClear(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "frame recognizer clear", +                           "Delete all frame recognizers.", nullptr) {} + +  ~CommandObjectFrameRecognizerClear() override = default; + +protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    StackFrameRecognizerManager::RemoveAllRecognizers(); +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return result.Succeeded(); +  } +}; + +class CommandObjectFrameRecognizerDelete : public CommandObjectParsed { + public: +  CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "frame recognizer delete", +                            "Delete an existing frame recognizer.", nullptr) {} + +  ~CommandObjectFrameRecognizerDelete() override = default; + + protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    if (command.GetArgumentCount() == 0) { +      if (!m_interpreter.Confirm( +              "About to delete all frame recognizers, do you want to do that?", +              true)) { +        result.AppendMessage("Operation cancelled..."); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } + +      StackFrameRecognizerManager::RemoveAllRecognizers(); +      result.SetStatus(eReturnStatusSuccessFinishResult); +      return result.Succeeded(); +    } + +    if (command.GetArgumentCount() != 1) { +      result.AppendErrorWithFormat("'%s' takes zero or one arguments.\n", +                                   m_cmd_name.c_str()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    uint32_t recognizer_id = +        StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0); + +    StackFrameRecognizerManager::RemoveRecognizerWithID(recognizer_id); +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return result.Succeeded(); +  } +}; + +class CommandObjectFrameRecognizerList : public CommandObjectParsed { + public: +  CommandObjectFrameRecognizerList(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "frame recognizer list", +                            "Show a list of active frame recognizers.", +                            nullptr) {} + +  ~CommandObjectFrameRecognizerList() override = default; + + protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    bool any_printed = false; +    StackFrameRecognizerManager::ForEach( +        [&result, &any_printed](uint32_t recognizer_id, std::string name, +                                std::string function, std::string symbol, +                                bool regexp) { +          if (name == "") name = "(internal)"; +          result.GetOutputStream().Printf( +              "%d: %s, module %s, function %s%s\n", recognizer_id, name.c_str(), +              function.c_str(), symbol.c_str(), regexp ? " (regexp)" : ""); +          any_printed = true; +        }); + +    if (any_printed) +      result.SetStatus(eReturnStatusSuccessFinishResult); +    else { +      result.GetOutputStream().PutCString("no matching results found.\n"); +      result.SetStatus(eReturnStatusSuccessFinishNoResult); +    } +    return result.Succeeded(); +  } +}; + +class CommandObjectFrameRecognizerInfo : public CommandObjectParsed { + public: +  CommandObjectFrameRecognizerInfo(CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "frame recognizer info", +            "Show which frame recognizer is applied a stack frame (if any).", +            nullptr) { +    CommandArgumentEntry arg; +    CommandArgumentData index_arg; + +    // Define the first (and only) variant of this arg. +    index_arg.arg_type = eArgTypeFrameIndex; +    index_arg.arg_repetition = eArgRepeatPlain; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg.push_back(index_arg); + +    // Push the data for the first argument into the m_arguments vector. +    m_arguments.push_back(arg); +  } + +  ~CommandObjectFrameRecognizerInfo() override = default; + + protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    Process *process = m_exe_ctx.GetProcessPtr(); +    if (process == nullptr) { +      result.AppendError("no process"); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } +    Thread *thread = m_exe_ctx.GetThreadPtr(); +    if (thread == nullptr) { +      result.AppendError("no thread"); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } +    if (command.GetArgumentCount() != 1) { +      result.AppendErrorWithFormat( +          "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    uint32_t frame_index = +        StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0); +    StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index); +    if (!frame_sp) { +      result.AppendErrorWithFormat("no frame with index %u", frame_index); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    auto recognizer = +        StackFrameRecognizerManager::GetRecognizerForFrame(frame_sp); + +    Stream &output_stream = result.GetOutputStream(); +    output_stream.Printf("frame %d ", frame_index); +    if (recognizer) { +      output_stream << "is recognized by "; +      output_stream << recognizer->GetName(); +    } else { +      output_stream << "not recognized by any recognizer"; +    } +    output_stream.EOL(); +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return result.Succeeded(); +  } +}; + +class CommandObjectFrameRecognizer : public CommandObjectMultiword { + public: +  CommandObjectFrameRecognizer(CommandInterpreter &interpreter) +      : CommandObjectMultiword( +            interpreter, "frame recognizer", +            "Commands for editing and viewing frame recognizers.", +            "frame recognizer [<sub-command-options>] ") { +    LoadSubCommand( +        "add", +        CommandObjectSP(new CommandObjectFrameRecognizerAdd(interpreter))); +    LoadSubCommand( +        "clear", +        CommandObjectSP(new CommandObjectFrameRecognizerClear(interpreter))); +    LoadSubCommand( +        "delete", +        CommandObjectSP(new CommandObjectFrameRecognizerDelete(interpreter))); +    LoadSubCommand( +        "list", +        CommandObjectSP(new CommandObjectFrameRecognizerList(interpreter))); +    LoadSubCommand( +        "info", +        CommandObjectSP(new CommandObjectFrameRecognizerInfo(interpreter))); +  } + +  ~CommandObjectFrameRecognizer() override = default; +}; +  #pragma mark CommandObjectMultiwordFrame  //------------------------------------------------------------------------- @@ -748,6 +1137,11 @@ CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(                   CommandObjectSP(new CommandObjectFrameSelect(interpreter)));    LoadSubCommand("variable",                   CommandObjectSP(new CommandObjectFrameVariable(interpreter))); +#ifndef LLDB_DISABLE_PYTHON +  LoadSubCommand( +      "recognizer", +      CommandObjectSP(new CommandObjectFrameRecognizer(interpreter))); +#endif  }  CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default;  | 
