forked from OSchip/llvm-project
Revert "Add the ability to write target stop-hooks using the ScriptInterpreter."
This temporarily reverts commit b65966cff6
while Jim figures out why the test is failing on the bots.
This commit is contained in:
parent
5dbf80cad9
commit
f775fe5964
|
@ -152,10 +152,3 @@ SBTypeToSWIGWrapper (lldb::SBSymbolContext* sym_ctx_sb)
|
|||
{
|
||||
return SWIG_NewPointerObj((void *) sym_ctx_sb, SWIGTYPE_p_lldb__SBSymbolContext, 0);
|
||||
}
|
||||
|
||||
template <>
|
||||
PyObject*
|
||||
SBTypeToSWIGWrapper (lldb::SBStream* stream_sb)
|
||||
{
|
||||
return SWIG_NewPointerObj((void *) stream_sb, SWIGTYPE_p_lldb__SBStream, 0);
|
||||
}
|
||||
|
|
|
@ -468,124 +468,6 @@ LLDBSwigPythonCallBreakpointResolver
|
|||
return ret_val;
|
||||
}
|
||||
|
||||
SWIGEXPORT void *
|
||||
LLDBSwigPythonCreateScriptedStopHook
|
||||
(
|
||||
lldb::TargetSP target_sp,
|
||||
const char *python_class_name,
|
||||
const char *session_dictionary_name,
|
||||
lldb_private::StructuredDataImpl *args_impl,
|
||||
Status &error
|
||||
)
|
||||
{
|
||||
if (python_class_name == NULL || python_class_name[0] == '\0') {
|
||||
error.SetErrorString("Empty class name.");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
if (!session_dictionary_name) {
|
||||
error.SetErrorString("No session dictionary");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyErr_Cleaner py_err_cleaner(true);
|
||||
|
||||
auto dict =
|
||||
PythonModule::MainModule().ResolveName<PythonDictionary>(
|
||||
session_dictionary_name);
|
||||
auto pfunc =
|
||||
PythonObject::ResolveNameWithDictionary<PythonCallable>(
|
||||
python_class_name, dict);
|
||||
|
||||
if (!pfunc.IsAllocated()) {
|
||||
error.SetErrorStringWithFormat("Could not find class: %s.",
|
||||
python_class_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
lldb::SBTarget *target_val
|
||||
= new lldb::SBTarget(target_sp);
|
||||
|
||||
PythonObject target_arg(PyRefType::Owned, SBTypeToSWIGWrapper(target_val));
|
||||
|
||||
lldb::SBStructuredData *args_value = new lldb::SBStructuredData(args_impl);
|
||||
PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(args_value));
|
||||
|
||||
PythonObject result = pfunc(target_arg, args_arg, dict);
|
||||
|
||||
if (result.IsAllocated())
|
||||
{
|
||||
// Check that the handle_stop callback is defined:
|
||||
auto callback_func = result.ResolveName<PythonCallable>("handle_stop");
|
||||
if (callback_func.IsAllocated()) {
|
||||
if (auto args_info = callback_func.GetArgInfo()) {
|
||||
if ((*args_info).max_positional_args < 2) {
|
||||
error.SetErrorStringWithFormat("Wrong number of args for "
|
||||
"handle_stop callback, should be 2 (excluding self), got: %d",
|
||||
(*args_info).max_positional_args);
|
||||
} else
|
||||
return result.release();
|
||||
} else {
|
||||
error.SetErrorString("Couldn't get num arguments for handle_stop "
|
||||
"callback.");
|
||||
}
|
||||
return result.release();
|
||||
}
|
||||
else {
|
||||
error.SetErrorStringWithFormat("Class \"%s\" is missing the required "
|
||||
"handle_stop callback.",
|
||||
python_class_name);
|
||||
result.release();
|
||||
}
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
SWIGEXPORT bool
|
||||
LLDBSwigPythonStopHookCallHandleStop
|
||||
(
|
||||
void *implementor,
|
||||
lldb::ExecutionContextRefSP exc_ctx_sp,
|
||||
lldb::StreamSP stream
|
||||
)
|
||||
{
|
||||
// handle_stop will return a bool with the meaning "should_stop"...
|
||||
// If you return nothing we'll assume we are going to stop.
|
||||
// Also any errors should return true, since we should stop on error.
|
||||
|
||||
PyErr_Cleaner py_err_cleaner(false);
|
||||
PythonObject self(PyRefType::Borrowed, static_cast<PyObject*>(implementor));
|
||||
auto pfunc = self.ResolveName<PythonCallable>("handle_stop");
|
||||
|
||||
if (!pfunc.IsAllocated())
|
||||
return true;
|
||||
|
||||
PythonObject result;
|
||||
lldb::SBExecutionContext sb_exc_ctx(exc_ctx_sp);
|
||||
PythonObject exc_ctx_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_exc_ctx));
|
||||
lldb::SBStream sb_stream;
|
||||
PythonObject sb_stream_arg(PyRefType::Owned,
|
||||
SBTypeToSWIGWrapper(sb_stream));
|
||||
result = pfunc(exc_ctx_arg, sb_stream_arg);
|
||||
|
||||
if (PyErr_Occurred())
|
||||
{
|
||||
stream->PutCString("Python error occurred handling stop-hook.");
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now add the result to the output stream. SBStream only
|
||||
// makes an internally help StreamString which I can't interpose, so I
|
||||
// have to copy it over here.
|
||||
stream->PutCString(sb_stream.GetData());
|
||||
|
||||
if (result.get() == Py_False)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
// wrapper that calls an optional instance member of an object taking no arguments
|
||||
static PyObject*
|
||||
LLDBSwigPython_CallOptionalMember
|
||||
|
|
|
@ -819,49 +819,3 @@ When the program is stopped at the beginning of the 'read' function in libc, we
|
|||
frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
|
||||
(lldb) frame variable
|
||||
(int) fd = 3
|
||||
|
||||
Writing Target Stop-Hooks in Python:
|
||||
------------------------------------
|
||||
|
||||
Stop hooks fire whenever the process stops just before control is returned to the
|
||||
user. Stop hooks can either be a set of lldb command-line commands, or can
|
||||
be implemented by a suitably defined Python class. The Python based stop-hooks
|
||||
can also be passed as set of -key -value pairs when they are added, and those
|
||||
will get packaged up into a SBStructuredData Dictionary and passed to the
|
||||
constructor of the Python object managing the stop hook. This allows for
|
||||
parametrization of the stop hooks.
|
||||
|
||||
To add a Python-based stop hook, first define a class with the following methods:
|
||||
|
||||
+--------------------+---------------------------------------+------------------------------------------------------------------------------------------------------------------+
|
||||
| Name | Arguments | Description |
|
||||
+--------------------+---------------------------------------+------------------------------------------------------------------------------------------------------------------+
|
||||
| **__init__** | **target: lldb.SBTarget** | This is the constructor for the new stop-hook. |
|
||||
| | **extra_args: lldb.SBStructuredData** | |
|
||||
| | | |
|
||||
| | | **target** is the SBTarget to which the stop hook is added. |
|
||||
| | | |
|
||||
| | | **extra_args** is an SBStructuredData object that the user can pass in when creating instances of this |
|
||||
| | | breakpoint. It is not required, but allows for reuse of stop-hook classes. |
|
||||
+--------------------+---------------------------------------+------------------------------------------------------------------------------------------------------------------+
|
||||
| **handle_stop** | **exe_ctx: lldb.SBExecutionContext** | This is the called when the target stops. |
|
||||
| | **stream: lldb.SBStream** | |
|
||||
| | | **exe_ctx** argument will be filled with the current stop point for which the stop hook is |
|
||||
| | | being evaluated. |
|
||||
| | | |
|
||||
| | | **stream** an lldb.SBStream, anything written to this stream will be written to the debugger console. |
|
||||
| | | |
|
||||
| | | The return value is a "Should Stop" vote from this thread. If the method returns either True or no return |
|
||||
| | | this thread votes to stop. If it returns False, then the thread votes to continue after all the stop-hooks |
|
||||
| | | are evaluated. |
|
||||
| | | Note, the --auto-continue flag to 'target stop-hook add' overrides a True return value from the method. |
|
||||
+--------------------+---------------------------------------+------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
To use this class in lldb, run the command:
|
||||
|
||||
::
|
||||
|
||||
(lldb) command script import MyModule.py
|
||||
(lldb) target stop-hook add -P MyModule.MyStopHook -k first -v 1 -k second -v 2
|
||||
|
||||
where MyModule.py is the file containing the class definition MyStopHook.
|
||||
|
|
|
@ -298,23 +298,6 @@ public:
|
|||
return lldb::eSearchDepthModule;
|
||||
}
|
||||
|
||||
virtual StructuredData::GenericSP
|
||||
CreateScriptedStopHook(lldb::TargetSP target_sp, const char *class_name,
|
||||
StructuredDataImpl *args_data, Status &error) {
|
||||
error.SetErrorString("Creating scripted stop-hooks with the current "
|
||||
"script interpreter is not supported.");
|
||||
return StructuredData::GenericSP();
|
||||
}
|
||||
|
||||
// This dispatches to the handle_stop method of the stop-hook class. It
|
||||
// returns a "should_stop" bool.
|
||||
virtual bool
|
||||
ScriptedStopHookHandleStop(StructuredData::GenericSP implementor_sp,
|
||||
ExecutionContext &exc_ctx,
|
||||
lldb::StreamSP stream_sp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual StructuredData::ObjectSP
|
||||
LoadPluginModule(const FileSpec &file_spec, lldb_private::Status &error) {
|
||||
return StructuredData::ObjectSP();
|
||||
|
|
|
@ -340,7 +340,7 @@ public:
|
|||
|
||||
void Clear();
|
||||
|
||||
bool SymbolContextMatches(const SymbolContext &sc);
|
||||
bool SymbolContextMatches(SymbolContext &sc);
|
||||
|
||||
bool AddressMatches(lldb::addr_t addr);
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include "lldb/Target/ExecutionContextScope.h"
|
||||
#include "lldb/Target/PathMappingList.h"
|
||||
#include "lldb/Target/SectionLoadHistory.h"
|
||||
#include "lldb/Target/ThreadSpec.h"
|
||||
#include "lldb/Utility/ArchSpec.h"
|
||||
#include "lldb/Utility/Broadcaster.h"
|
||||
#include "lldb/Utility/LLDBAssert.h"
|
||||
|
@ -509,8 +508,6 @@ public:
|
|||
|
||||
static void SetDefaultArchitecture(const ArchSpec &arch);
|
||||
|
||||
bool IsDummyTarget() const { return m_is_dummy_target; }
|
||||
|
||||
/// Find a binary on the system and return its Module,
|
||||
/// or return an existing Module that is already in the Target.
|
||||
///
|
||||
|
@ -1142,27 +1139,23 @@ public:
|
|||
class StopHook : public UserID {
|
||||
public:
|
||||
StopHook(const StopHook &rhs);
|
||||
virtual ~StopHook() = default;
|
||||
|
||||
enum class StopHookKind : uint32_t { CommandBased = 0, ScriptBased };
|
||||
~StopHook();
|
||||
|
||||
StringList *GetCommandPointer() { return &m_commands; }
|
||||
|
||||
const StringList &GetCommands() { return m_commands; }
|
||||
|
||||
lldb::TargetSP &GetTarget() { return m_target_sp; }
|
||||
|
||||
void SetCommands(StringList &in_commands) { m_commands = in_commands; }
|
||||
|
||||
// Set the specifier. The stop hook will own the specifier, and is
|
||||
// responsible for deleting it when we're done.
|
||||
void SetSpecifier(SymbolContextSpecifier *specifier);
|
||||
|
||||
SymbolContextSpecifier *GetSpecifier() { return m_specifier_sp.get(); }
|
||||
|
||||
bool ExecutionContextPasses(const ExecutionContext &exe_ctx);
|
||||
|
||||
// Called on stop, this gets passed the ExecutionContext for each "stop
|
||||
// with a reason" thread. It should add to the stream whatever text it
|
||||
// wants to show the user, and return False to indicate it wants the target
|
||||
// not to stop.
|
||||
virtual bool HandleStop(ExecutionContext &exe_ctx,
|
||||
lldb::StreamSP output) = 0;
|
||||
|
||||
// Set the Thread Specifier. The stop hook will own the thread specifier,
|
||||
// and is responsible for deleting it when we're done.
|
||||
void SetThreadSpecifier(ThreadSpec *specifier);
|
||||
|
@ -1180,79 +1173,26 @@ public:
|
|||
bool GetAutoContinue() const { return m_auto_continue; }
|
||||
|
||||
void GetDescription(Stream *s, lldb::DescriptionLevel level) const;
|
||||
virtual void GetSubclassDescription(Stream *s,
|
||||
lldb::DescriptionLevel level) const = 0;
|
||||
|
||||
protected:
|
||||
private:
|
||||
lldb::TargetSP m_target_sp;
|
||||
StringList m_commands;
|
||||
lldb::SymbolContextSpecifierSP m_specifier_sp;
|
||||
std::unique_ptr<ThreadSpec> m_thread_spec_up;
|
||||
bool m_active = true;
|
||||
bool m_auto_continue = false;
|
||||
|
||||
StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid);
|
||||
};
|
||||
|
||||
class StopHookCommandLine : public StopHook {
|
||||
public:
|
||||
virtual ~StopHookCommandLine() = default;
|
||||
|
||||
StringList &GetCommands() { return m_commands; }
|
||||
void SetActionFromString(const std::string &strings);
|
||||
void SetActionFromStrings(const std::vector<std::string> &strings);
|
||||
|
||||
bool HandleStop(ExecutionContext &exc_ctx,
|
||||
lldb::StreamSP output_sp) override;
|
||||
void GetSubclassDescription(Stream *s,
|
||||
lldb::DescriptionLevel level) const override;
|
||||
|
||||
private:
|
||||
StringList m_commands;
|
||||
// Use CreateStopHook to make a new empty stop hook. The GetCommandPointer
|
||||
// and fill it with commands, and SetSpecifier to set the specifier shared
|
||||
// pointer (can be null, that will match anything.)
|
||||
StopHookCommandLine(lldb::TargetSP target_sp, lldb::user_id_t uid)
|
||||
: StopHook(target_sp, uid) {}
|
||||
StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid);
|
||||
friend class Target;
|
||||
};
|
||||
|
||||
class StopHookScripted : public StopHook {
|
||||
public:
|
||||
virtual ~StopHookScripted() = default;
|
||||
bool HandleStop(ExecutionContext &exc_ctx, lldb::StreamSP output) override;
|
||||
|
||||
Status SetScriptCallback(std::string class_name,
|
||||
StructuredData::ObjectSP extra_args_sp);
|
||||
|
||||
void GetSubclassDescription(Stream *s,
|
||||
lldb::DescriptionLevel level) const override;
|
||||
|
||||
private:
|
||||
std::string m_class_name;
|
||||
/// This holds the dictionary of keys & values that can be used to
|
||||
/// parametrize any given callback's behavior.
|
||||
StructuredDataImpl *m_extra_args; // We own this structured data,
|
||||
// but the SD itself manages the UP.
|
||||
/// This holds the python callback object.
|
||||
StructuredData::GenericSP m_implementation_sp;
|
||||
|
||||
/// Use CreateStopHook to make a new empty stop hook. The GetCommandPointer
|
||||
/// and fill it with commands, and SetSpecifier to set the specifier shared
|
||||
/// pointer (can be null, that will match anything.)
|
||||
StopHookScripted(lldb::TargetSP target_sp, lldb::user_id_t uid)
|
||||
: StopHook(target_sp, uid) {}
|
||||
friend class Target;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<StopHook> StopHookSP;
|
||||
|
||||
/// Add an empty stop hook to the Target's stop hook list, and returns a
|
||||
/// shared pointer to it in new_hook. Returns the id of the new hook.
|
||||
StopHookSP CreateStopHook(StopHook::StopHookKind kind);
|
||||
|
||||
/// If you tried to create a stop hook, and that failed, call this to
|
||||
/// remove the stop hook, as it will also reset the stop hook counter.
|
||||
void UndoCreateStopHook(lldb::user_id_t uid);
|
||||
// Add an empty stop hook to the Target's stop hook list, and returns a
|
||||
// shared pointer to it in new_hook. Returns the id of the new hook.
|
||||
StopHookSP CreateStopHook();
|
||||
|
||||
void RunStopHooks();
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "lldb/Interpreter/OptionGroupFile.h"
|
||||
#include "lldb/Interpreter/OptionGroupFormat.h"
|
||||
#include "lldb/Interpreter/OptionGroupPlatform.h"
|
||||
#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
|
||||
#include "lldb/Interpreter/OptionGroupString.h"
|
||||
#include "lldb/Interpreter/OptionGroupUInt64.h"
|
||||
#include "lldb/Interpreter/OptionGroupUUID.h"
|
||||
|
@ -4443,10 +4442,10 @@ private:
|
|||
class CommandObjectTargetStopHookAdd : public CommandObjectParsed,
|
||||
public IOHandlerDelegateMultiline {
|
||||
public:
|
||||
class CommandOptions : public OptionGroup {
|
||||
class CommandOptions : public Options {
|
||||
public:
|
||||
CommandOptions()
|
||||
: OptionGroup(), m_line_start(0), m_line_end(UINT_MAX),
|
||||
: Options(), m_line_start(0), m_line_end(UINT_MAX),
|
||||
m_func_name_type_mask(eFunctionNameTypeAuto),
|
||||
m_sym_ctx_specified(false), m_thread_specified(false),
|
||||
m_use_one_liner(false), m_one_liner() {}
|
||||
|
@ -4460,8 +4459,7 @@ public:
|
|||
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
|
||||
ExecutionContext *execution_context) override {
|
||||
Status error;
|
||||
const int short_option =
|
||||
g_target_stop_hook_add_options[option_idx].short_option;
|
||||
const int short_option = m_getopt_table[option_idx].val;
|
||||
|
||||
switch (short_option) {
|
||||
case 'c':
|
||||
|
@ -4591,75 +4589,20 @@ public:
|
|||
// Instance variables to hold the values for one_liner options.
|
||||
bool m_use_one_liner;
|
||||
std::vector<std::string> m_one_liner;
|
||||
|
||||
bool m_auto_continue;
|
||||
};
|
||||
|
||||
CommandObjectTargetStopHookAdd(CommandInterpreter &interpreter)
|
||||
: CommandObjectParsed(interpreter, "target stop-hook add",
|
||||
"Add a hook to be executed when the target stops."
|
||||
"The hook can either be a list of commands or an "
|
||||
"appropriately defined Python class. You can also "
|
||||
"add filters so the hook only runs a certain stop "
|
||||
"points.",
|
||||
"Add a hook to be executed when the target stops.",
|
||||
"target stop-hook add"),
|
||||
IOHandlerDelegateMultiline("DONE",
|
||||
IOHandlerDelegate::Completion::LLDBCommand),
|
||||
m_options(), m_python_class_options("scripted stop-hook", true, 'P') {
|
||||
SetHelpLong(
|
||||
R"(
|
||||
Command Based stop-hooks:
|
||||
-------------------------
|
||||
Stop hooks can run a list of lldb commands by providing one or more
|
||||
--one-line-command options. The commands will get run in the order they are
|
||||
added. Or you can provide no commands, in which case you will enter a
|
||||
command editor where you can enter the commands to be run.
|
||||
|
||||
Python Based Stop Hooks:
|
||||
------------------------
|
||||
Stop hooks can be implemented with a suitably defined Python class, whose name
|
||||
is passed in the --python-class option.
|
||||
|
||||
When the stop hook is added, the class is initialized by calling:
|
||||
|
||||
def __init__(self, target, extra_args, dict):
|
||||
|
||||
target: The target that the stop hook is being added to.
|
||||
extra_args: An SBStructuredData Dictionary filled with the -key -value
|
||||
option pairs passed to the command.
|
||||
dict: An implementation detail provided by lldb.
|
||||
|
||||
Then when the stop-hook triggers, lldb will run the 'handle_stop' method.
|
||||
The method has the signature:
|
||||
|
||||
def handle_stop(self, exe_ctx, stream):
|
||||
|
||||
exe_ctx: An SBExecutionContext for the thread that has stopped.
|
||||
stream: An SBStream, anything written to this stream will be printed in the
|
||||
the stop message when the process stops.
|
||||
|
||||
Return Value: The method returns "should_stop". If should_stop is false
|
||||
from all the stop hook executions on threads that stopped
|
||||
with a reason, then the process will continue. Note that this
|
||||
will happen only after all the stop hooks are run.
|
||||
|
||||
Filter Options:
|
||||
---------------
|
||||
Stop hooks can be set to always run, or to only run when the stopped thread
|
||||
matches the filter options passed on the command line. The available filter
|
||||
options include a shared library or a thread or queue specification,
|
||||
a line range in a source file, a function name or a class name.
|
||||
)");
|
||||
m_all_options.Append(&m_python_class_options,
|
||||
LLDB_OPT_SET_1 | LLDB_OPT_SET_2,
|
||||
LLDB_OPT_SET_FROM_TO(4, 6));
|
||||
m_all_options.Append(&m_options);
|
||||
m_all_options.Finalize();
|
||||
}
|
||||
m_options() {}
|
||||
|
||||
~CommandObjectTargetStopHookAdd() override = default;
|
||||
|
||||
Options *GetOptions() override { return &m_all_options; }
|
||||
Options *GetOptions() override { return &m_options; }
|
||||
|
||||
protected:
|
||||
void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
|
||||
|
@ -4683,15 +4626,10 @@ protected:
|
|||
error_sp->Flush();
|
||||
}
|
||||
Target *target = GetDebugger().GetSelectedTarget().get();
|
||||
if (target) {
|
||||
target->UndoCreateStopHook(m_stop_hook_sp->GetID());
|
||||
}
|
||||
if (target)
|
||||
target->RemoveStopHookByID(m_stop_hook_sp->GetID());
|
||||
} else {
|
||||
// The IOHandler editor is only for command lines stop hooks:
|
||||
Target::StopHookCommandLine *hook_ptr =
|
||||
static_cast<Target::StopHookCommandLine *>(m_stop_hook_sp.get());
|
||||
|
||||
hook_ptr->SetActionFromString(line);
|
||||
m_stop_hook_sp->GetCommandPointer()->SplitIntoLines(line);
|
||||
StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
|
||||
if (output_sp) {
|
||||
output_sp->Printf("Stop hook #%" PRIu64 " added.\n",
|
||||
|
@ -4708,10 +4646,7 @@ protected:
|
|||
m_stop_hook_sp.reset();
|
||||
|
||||
Target &target = GetSelectedOrDummyTarget();
|
||||
Target::StopHookSP new_hook_sp =
|
||||
target.CreateStopHook(m_python_class_options.GetName().empty() ?
|
||||
Target::StopHook::StopHookKind::CommandBased
|
||||
: Target::StopHook::StopHookKind::ScriptBased);
|
||||
Target::StopHookSP new_hook_sp = target.CreateStopHook();
|
||||
|
||||
// First step, make the specifier.
|
||||
std::unique_ptr<SymbolContextSpecifier> specifier_up;
|
||||
|
@ -4780,30 +4715,11 @@ protected:
|
|||
|
||||
new_hook_sp->SetAutoContinue(m_options.m_auto_continue);
|
||||
if (m_options.m_use_one_liner) {
|
||||
// This is a command line stop hook:
|
||||
Target::StopHookCommandLine *hook_ptr =
|
||||
static_cast<Target::StopHookCommandLine *>(new_hook_sp.get());
|
||||
hook_ptr->SetActionFromStrings(m_options.m_one_liner);
|
||||
// Use one-liners.
|
||||
for (auto cmd : m_options.m_one_liner)
|
||||
new_hook_sp->GetCommandPointer()->AppendString(cmd.c_str());
|
||||
result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n",
|
||||
new_hook_sp->GetID());
|
||||
} else if (!m_python_class_options.GetName().empty()) {
|
||||
// This is a scripted stop hook:
|
||||
Target::StopHookScripted *hook_ptr =
|
||||
static_cast<Target::StopHookScripted *>(new_hook_sp.get());
|
||||
Status error = hook_ptr->SetScriptCallback(
|
||||
m_python_class_options.GetName(),
|
||||
m_python_class_options.GetStructuredData());
|
||||
if (error.Success())
|
||||
result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n",
|
||||
new_hook_sp->GetID());
|
||||
else {
|
||||
// FIXME: Set the stop hook ID counter back.
|
||||
result.AppendErrorWithFormat("Couldn't add stop hook: %s",
|
||||
error.AsCString());
|
||||
result.SetStatus(eReturnStatusFailed);
|
||||
target.UndoCreateStopHook(new_hook_sp->GetID());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
m_stop_hook_sp = new_hook_sp;
|
||||
m_interpreter.GetLLDBCommandsFromIOHandler("> ", // Prompt
|
||||
|
@ -4816,9 +4732,6 @@ protected:
|
|||
|
||||
private:
|
||||
CommandOptions m_options;
|
||||
OptionGroupPythonClassWithDict m_python_class_options;
|
||||
OptionGroupOptions m_all_options;
|
||||
|
||||
Target::StopHookSP m_stop_hook_sp;
|
||||
};
|
||||
|
||||
|
|
|
@ -879,7 +879,7 @@ let Command = "target modules lookup" in {
|
|||
}
|
||||
|
||||
let Command = "target stop hook add" in {
|
||||
def target_stop_hook_add_one_liner : Option<"one-liner", "o">, GroupRange<1,3>,
|
||||
def target_stop_hook_add_one_liner : Option<"one-liner", "o">,
|
||||
Arg<"OneLiner">, Desc<"Add a command for the stop hook. Can be specified "
|
||||
"more than once, and commands will be run in the order they appear.">;
|
||||
def target_stop_hook_add_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
|
||||
|
@ -897,19 +897,19 @@ let Command = "target stop hook add" in {
|
|||
def target_stop_hook_add_queue_name : Option<"queue-name", "q">,
|
||||
Arg<"QueueName">, Desc<"The stop hook is run only for threads in the queue "
|
||||
"whose name is given by this argument.">;
|
||||
def target_stop_hook_add_file : Option<"file", "f">, Groups<[1,4]>,
|
||||
def target_stop_hook_add_file : Option<"file", "f">, Group<1>,
|
||||
Arg<"Filename">, Desc<"Specify the source file within which the stop-hook "
|
||||
"is to be run.">, Completion<"SourceFile">;
|
||||
def target_stop_hook_add_start_line : Option<"start-line", "l">, Groups<[1,4]>,
|
||||
def target_stop_hook_add_start_line : Option<"start-line", "l">, Group<1>,
|
||||
Arg<"LineNum">, Desc<"Set the start of the line range for which the "
|
||||
"stop-hook is to be run.">;
|
||||
def target_stop_hook_add_end_line : Option<"end-line", "e">, Groups<[1,4]>,
|
||||
def target_stop_hook_add_end_line : Option<"end-line", "e">, Group<1>,
|
||||
Arg<"LineNum">, Desc<"Set the end of the line range for which the stop-hook"
|
||||
" is to be run.">;
|
||||
def target_stop_hook_add_classname : Option<"classname", "c">, Groups<[2,5]>,
|
||||
def target_stop_hook_add_classname : Option<"classname", "c">, Group<2>,
|
||||
Arg<"ClassName">,
|
||||
Desc<"Specify the class within which the stop-hook is to be run.">;
|
||||
def target_stop_hook_add_name : Option<"name", "n">, Groups<[3,6]>,
|
||||
def target_stop_hook_add_name : Option<"name", "n">, Group<3>,
|
||||
Arg<"FunctionName">, Desc<"Set the function name within which the stop hook"
|
||||
" will be run.">, Completion<"Symbol">;
|
||||
def target_stop_hook_add_auto_continue : Option<"auto-continue", "G">,
|
||||
|
|
|
@ -127,16 +127,6 @@ extern "C" unsigned int
|
|||
LLDBSwigPythonCallBreakpointResolver(void *implementor, const char *method_name,
|
||||
lldb_private::SymbolContext *sym_ctx);
|
||||
|
||||
extern "C" void *LLDBSwigPythonCreateScriptedStopHook(
|
||||
TargetSP target_sp, const char *python_class_name,
|
||||
const char *session_dictionary_name, lldb_private::StructuredDataImpl *args,
|
||||
lldb_private::Status &error);
|
||||
|
||||
extern "C" unsigned int
|
||||
LLDBSwigPythonStopHookCallHandleStop(void *implementor,
|
||||
lldb::ExecutionContextRefSP exc_ctx,
|
||||
lldb::StreamSP stream);
|
||||
|
||||
extern "C" size_t LLDBSwigPython_CalculateNumChildren(void *implementor,
|
||||
uint32_t max);
|
||||
|
||||
|
@ -1989,60 +1979,6 @@ ScriptInterpreterPythonImpl::ScriptedBreakpointResolverSearchDepth(
|
|||
return lldb::eSearchDepthModule;
|
||||
}
|
||||
|
||||
StructuredData::GenericSP ScriptInterpreterPythonImpl::CreateScriptedStopHook(
|
||||
TargetSP target_sp, const char *class_name, StructuredDataImpl *args_data,
|
||||
Status &error) {
|
||||
|
||||
if (!target_sp) {
|
||||
error.SetErrorString("No target for scripted stop-hook.");
|
||||
return StructuredData::GenericSP();
|
||||
}
|
||||
|
||||
if (class_name == nullptr || class_name[0] == '\0') {
|
||||
error.SetErrorString("No class name for scripted stop-hook.");
|
||||
return StructuredData::GenericSP();
|
||||
}
|
||||
|
||||
ScriptInterpreter *script_interpreter = m_debugger.GetScriptInterpreter();
|
||||
ScriptInterpreterPythonImpl *python_interpreter =
|
||||
static_cast<ScriptInterpreterPythonImpl *>(script_interpreter);
|
||||
|
||||
if (!script_interpreter) {
|
||||
error.SetErrorString("No script interpreter for scripted stop-hook.");
|
||||
return StructuredData::GenericSP();
|
||||
}
|
||||
|
||||
void *ret_val;
|
||||
|
||||
{
|
||||
Locker py_lock(this,
|
||||
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
|
||||
|
||||
ret_val = LLDBSwigPythonCreateScriptedStopHook(
|
||||
target_sp, class_name, python_interpreter->m_dictionary_name.c_str(),
|
||||
args_data, error);
|
||||
}
|
||||
|
||||
return StructuredData::GenericSP(new StructuredPythonObject(ret_val));
|
||||
}
|
||||
|
||||
bool ScriptInterpreterPythonImpl::ScriptedStopHookHandleStop(
|
||||
StructuredData::GenericSP implementor_sp, ExecutionContext &exc_ctx,
|
||||
lldb::StreamSP stream_sp) {
|
||||
assert(implementor_sp &&
|
||||
"can't call a stop hook with an invalid implementor");
|
||||
assert(stream_sp && "can't call a stop hook with an invalid stream");
|
||||
|
||||
Locker py_lock(this,
|
||||
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
|
||||
|
||||
lldb::ExecutionContextRefSP exc_ctx_ref_sp(new ExecutionContextRef(exc_ctx));
|
||||
|
||||
bool ret_val = LLDBSwigPythonStopHookCallHandleStop(
|
||||
implementor_sp->GetValue(), exc_ctx_ref_sp, stream_sp);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
StructuredData::ObjectSP
|
||||
ScriptInterpreterPythonImpl::LoadPluginModule(const FileSpec &file_spec,
|
||||
lldb_private::Status &error) {
|
||||
|
|
|
@ -105,14 +105,6 @@ public:
|
|||
lldb::SearchDepth ScriptedBreakpointResolverSearchDepth(
|
||||
StructuredData::GenericSP implementor_sp) override;
|
||||
|
||||
StructuredData::GenericSP
|
||||
CreateScriptedStopHook(lldb::TargetSP target_sp, const char *class_name,
|
||||
StructuredDataImpl *args_data, Status &error) override;
|
||||
|
||||
bool ScriptedStopHookHandleStop(StructuredData::GenericSP implementor_sp,
|
||||
ExecutionContext &exc_ctx,
|
||||
lldb::StreamSP stream_sp) override;
|
||||
|
||||
StructuredData::GenericSP
|
||||
CreateFrameRecognizer(const char *class_name) override;
|
||||
|
||||
|
|
|
@ -1010,15 +1010,11 @@ void SymbolContextSpecifier::Clear() {
|
|||
m_type = eNothingSpecified;
|
||||
}
|
||||
|
||||
bool SymbolContextSpecifier::SymbolContextMatches(const SymbolContext &sc) {
|
||||
bool SymbolContextSpecifier::SymbolContextMatches(SymbolContext &sc) {
|
||||
if (m_type == eNothingSpecified)
|
||||
return true;
|
||||
|
||||
// Only compare targets if this specifier has one and it's not the Dummy
|
||||
// target. Otherwise if a specifier gets made in the dummy target and
|
||||
// copied over we'll artificially fail the comparision.
|
||||
if (m_target_sp && !m_target_sp->IsDummyTarget() &&
|
||||
m_target_sp != sc.target_sp)
|
||||
if (m_target_sp.get() != sc.target_sp.get())
|
||||
return false;
|
||||
|
||||
if (m_type & eModuleSpecified) {
|
||||
|
|
|
@ -2484,28 +2484,13 @@ ClangModulesDeclVendor *Target::GetClangModulesDeclVendor() {
|
|||
return m_clang_modules_decl_vendor_up.get();
|
||||
}
|
||||
|
||||
Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind) {
|
||||
Target::StopHookSP Target::CreateStopHook() {
|
||||
lldb::user_id_t new_uid = ++m_stop_hook_next_id;
|
||||
Target::StopHookSP stop_hook_sp;
|
||||
switch (kind) {
|
||||
case StopHook::StopHookKind::CommandBased:
|
||||
stop_hook_sp.reset(new StopHookCommandLine(shared_from_this(), new_uid));
|
||||
break;
|
||||
case StopHook::StopHookKind::ScriptBased:
|
||||
stop_hook_sp.reset(new StopHookScripted(shared_from_this(), new_uid));
|
||||
break;
|
||||
}
|
||||
Target::StopHookSP stop_hook_sp(new StopHook(shared_from_this(), new_uid));
|
||||
m_stop_hooks[new_uid] = stop_hook_sp;
|
||||
return stop_hook_sp;
|
||||
}
|
||||
|
||||
void Target::UndoCreateStopHook(lldb::user_id_t user_id) {
|
||||
if (!RemoveStopHookByID(user_id))
|
||||
return;
|
||||
if (user_id == m_stop_hook_next_id)
|
||||
m_stop_hook_next_id--;
|
||||
}
|
||||
|
||||
bool Target::RemoveStopHookByID(lldb::user_id_t user_id) {
|
||||
size_t num_removed = m_stop_hooks.erase(user_id);
|
||||
return (num_removed != 0);
|
||||
|
@ -2561,18 +2546,25 @@ void Target::RunStopHooks() {
|
|||
if (m_stop_hooks.empty())
|
||||
return;
|
||||
|
||||
StopHookCollection::iterator pos, end = m_stop_hooks.end();
|
||||
|
||||
// If there aren't any active stop hooks, don't bother either.
|
||||
// Also see if any of the active hooks want to auto-continue.
|
||||
bool any_active_hooks = false;
|
||||
bool auto_continue = false;
|
||||
for (auto hook : m_stop_hooks) {
|
||||
if (hook.second->IsActive()) {
|
||||
any_active_hooks = true;
|
||||
break;
|
||||
auto_continue |= hook.second->GetAutoContinue();
|
||||
}
|
||||
}
|
||||
if (!any_active_hooks)
|
||||
return;
|
||||
|
||||
CommandReturnObject result(m_debugger.GetUseColor());
|
||||
|
||||
std::vector<ExecutionContext> exc_ctx_with_reasons;
|
||||
std::vector<SymbolContext> sym_ctx_with_reasons;
|
||||
|
||||
ThreadList &cur_threadlist = m_process_sp->GetThreadList();
|
||||
size_t num_threads = cur_threadlist.GetSize();
|
||||
|
@ -2580,8 +2572,10 @@ void Target::RunStopHooks() {
|
|||
lldb::ThreadSP cur_thread_sp = cur_threadlist.GetThreadAtIndex(i);
|
||||
if (cur_thread_sp->ThreadStoppedForAReason()) {
|
||||
lldb::StackFrameSP cur_frame_sp = cur_thread_sp->GetStackFrameAtIndex(0);
|
||||
exc_ctx_with_reasons.emplace_back(m_process_sp.get(), cur_thread_sp.get(),
|
||||
cur_frame_sp.get());
|
||||
exc_ctx_with_reasons.push_back(ExecutionContext(
|
||||
m_process_sp.get(), cur_thread_sp.get(), cur_frame_sp.get()));
|
||||
sym_ctx_with_reasons.push_back(
|
||||
cur_frame_sp->GetSymbolContext(eSymbolContextEverything));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2590,86 +2584,91 @@ void Target::RunStopHooks() {
|
|||
if (num_exe_ctx == 0)
|
||||
return;
|
||||
|
||||
StreamSP output_sp = m_debugger.GetAsyncOutputStream();
|
||||
result.SetImmediateOutputStream(m_debugger.GetAsyncOutputStream());
|
||||
result.SetImmediateErrorStream(m_debugger.GetAsyncErrorStream());
|
||||
|
||||
bool auto_continue = false;
|
||||
bool keep_going = true;
|
||||
bool hooks_ran = false;
|
||||
bool print_hook_header = (m_stop_hooks.size() != 1);
|
||||
bool print_thread_header = (num_exe_ctx != 1);
|
||||
bool should_stop = false;
|
||||
bool somebody_restarted = false;
|
||||
bool did_restart = false;
|
||||
|
||||
for (auto stop_entry : m_stop_hooks) {
|
||||
StopHookSP cur_hook_sp = stop_entry.second;
|
||||
for (pos = m_stop_hooks.begin(); keep_going && pos != end; pos++) {
|
||||
// result.Clear();
|
||||
StopHookSP cur_hook_sp = (*pos).second;
|
||||
if (!cur_hook_sp->IsActive())
|
||||
continue;
|
||||
|
||||
bool any_thread_matched = false;
|
||||
for (auto exc_ctx : exc_ctx_with_reasons) {
|
||||
// We detect somebody restarted in the stop-hook loop, and broke out of
|
||||
// that loop back to here. So break out of here too.
|
||||
if (somebody_restarted)
|
||||
break;
|
||||
for (size_t i = 0; keep_going && i < num_exe_ctx; i++) {
|
||||
if ((cur_hook_sp->GetSpecifier() == nullptr ||
|
||||
cur_hook_sp->GetSpecifier()->SymbolContextMatches(
|
||||
sym_ctx_with_reasons[i])) &&
|
||||
(cur_hook_sp->GetThreadSpecifier() == nullptr ||
|
||||
cur_hook_sp->GetThreadSpecifier()->ThreadPassesBasicTests(
|
||||
exc_ctx_with_reasons[i].GetThreadRef()))) {
|
||||
if (!hooks_ran) {
|
||||
hooks_ran = true;
|
||||
}
|
||||
if (print_hook_header && !any_thread_matched) {
|
||||
const char *cmd =
|
||||
(cur_hook_sp->GetCommands().GetSize() == 1
|
||||
? cur_hook_sp->GetCommands().GetStringAtIndex(0)
|
||||
: nullptr);
|
||||
if (cmd)
|
||||
result.AppendMessageWithFormat("\n- Hook %" PRIu64 " (%s)\n",
|
||||
cur_hook_sp->GetID(), cmd);
|
||||
else
|
||||
result.AppendMessageWithFormat("\n- Hook %" PRIu64 "\n",
|
||||
cur_hook_sp->GetID());
|
||||
any_thread_matched = true;
|
||||
}
|
||||
|
||||
if (!cur_hook_sp->ExecutionContextPasses(exc_ctx))
|
||||
continue;
|
||||
if (print_thread_header)
|
||||
result.AppendMessageWithFormat(
|
||||
"-- Thread %d\n",
|
||||
exc_ctx_with_reasons[i].GetThreadPtr()->GetIndexID());
|
||||
|
||||
// We only consult the auto-continue for a stop hook if it matched the
|
||||
// specifier.
|
||||
auto_continue |= cur_hook_sp->GetAutoContinue();
|
||||
CommandInterpreterRunOptions options;
|
||||
options.SetStopOnContinue(true);
|
||||
options.SetStopOnError(true);
|
||||
options.SetEchoCommands(false);
|
||||
options.SetPrintResults(true);
|
||||
options.SetPrintErrors(true);
|
||||
options.SetAddToHistory(false);
|
||||
|
||||
if (!hooks_ran)
|
||||
hooks_ran = true;
|
||||
|
||||
if (print_hook_header && !any_thread_matched) {
|
||||
StreamString s;
|
||||
cur_hook_sp->GetDescription(&s, eDescriptionLevelBrief);
|
||||
if (s.GetSize() != 0)
|
||||
output_sp->Printf("\n- Hook %" PRIu64 " (%s)\n", cur_hook_sp->GetID(),
|
||||
s.GetData());
|
||||
else
|
||||
output_sp->Printf("\n- Hook %" PRIu64 "\n", cur_hook_sp->GetID());
|
||||
any_thread_matched = true;
|
||||
}
|
||||
|
||||
if (print_thread_header)
|
||||
output_sp->Printf("-- Thread %d\n",
|
||||
exc_ctx.GetThreadPtr()->GetIndexID());
|
||||
|
||||
bool this_should_stop = cur_hook_sp->HandleStop(exc_ctx, output_sp);
|
||||
// If this hook is set to auto-continue that should override the
|
||||
// HandleStop result...
|
||||
if (cur_hook_sp->GetAutoContinue())
|
||||
this_should_stop = false;
|
||||
|
||||
// If anybody wanted to stop, we should all stop.
|
||||
if (!should_stop)
|
||||
should_stop = this_should_stop;
|
||||
|
||||
// We don't have a good way to prohibit people from restarting the target
|
||||
// willy nilly in a stop hook. So see if the private state is running
|
||||
// here and bag out if it is.
|
||||
// FIXME: when we are doing non-stop mode for realz we'll have to instead
|
||||
// track each thread, and only bag out if a thread is set running.
|
||||
if (m_process_sp->GetPrivateState() != eStateStopped) {
|
||||
output_sp->Printf("\nAborting stop hooks, hook %" PRIu64
|
||||
" set the program running.\n"
|
||||
" Consider using '-G true' to make "
|
||||
"stop hooks auto-continue.\n",
|
||||
cur_hook_sp->GetID());
|
||||
somebody_restarted = true;
|
||||
break;
|
||||
// Force Async:
|
||||
bool old_async = GetDebugger().GetAsyncExecution();
|
||||
GetDebugger().SetAsyncExecution(true);
|
||||
GetDebugger().GetCommandInterpreter().HandleCommands(
|
||||
cur_hook_sp->GetCommands(), &exc_ctx_with_reasons[i], options,
|
||||
result);
|
||||
GetDebugger().SetAsyncExecution(old_async);
|
||||
// If the command started the target going again, we should bag out of
|
||||
// running the stop hooks.
|
||||
if ((result.GetStatus() == eReturnStatusSuccessContinuingNoResult) ||
|
||||
(result.GetStatus() == eReturnStatusSuccessContinuingResult)) {
|
||||
// But only complain if there were more stop hooks to do:
|
||||
StopHookCollection::iterator tmp = pos;
|
||||
if (++tmp != end)
|
||||
result.AppendMessageWithFormat(
|
||||
"\nAborting stop hooks, hook %" PRIu64
|
||||
" set the program running.\n"
|
||||
" Consider using '-G true' to make "
|
||||
"stop hooks auto-continue.\n",
|
||||
cur_hook_sp->GetID());
|
||||
keep_going = false;
|
||||
did_restart = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output_sp->Flush();
|
||||
|
||||
// Finally, if auto-continue was requested, do it now:
|
||||
// We only compute should_stop against the hook results if a hook got to run
|
||||
// which is why we have to do this conjoint test.
|
||||
if (!somebody_restarted && ((hooks_ran && !should_stop) || auto_continue))
|
||||
if (!did_restart && auto_continue)
|
||||
m_process_sp->PrivateResume();
|
||||
|
||||
result.GetImmediateOutputStream()->Flush();
|
||||
result.GetImmediateErrorStream()->Flush();
|
||||
}
|
||||
|
||||
const TargetPropertiesSP &Target::GetGlobalProperties() {
|
||||
|
@ -3129,17 +3128,20 @@ void Target::FinalizeFileActions(ProcessLaunchInfo &info) {
|
|||
|
||||
// Target::StopHook
|
||||
Target::StopHook::StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid)
|
||||
: UserID(uid), m_target_sp(target_sp), m_specifier_sp(),
|
||||
: UserID(uid), m_target_sp(target_sp), m_commands(), m_specifier_sp(),
|
||||
m_thread_spec_up() {}
|
||||
|
||||
Target::StopHook::StopHook(const StopHook &rhs)
|
||||
: UserID(rhs.GetID()), m_target_sp(rhs.m_target_sp),
|
||||
m_specifier_sp(rhs.m_specifier_sp), m_thread_spec_up(),
|
||||
m_active(rhs.m_active), m_auto_continue(rhs.m_auto_continue) {
|
||||
m_commands(rhs.m_commands), m_specifier_sp(rhs.m_specifier_sp),
|
||||
m_thread_spec_up(), m_active(rhs.m_active),
|
||||
m_auto_continue(rhs.m_auto_continue) {
|
||||
if (rhs.m_thread_spec_up)
|
||||
m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);
|
||||
}
|
||||
|
||||
Target::StopHook::~StopHook() = default;
|
||||
|
||||
void Target::StopHook::SetSpecifier(SymbolContextSpecifier *specifier) {
|
||||
m_specifier_sp.reset(specifier);
|
||||
}
|
||||
|
@ -3148,31 +3150,8 @@ void Target::StopHook::SetThreadSpecifier(ThreadSpec *specifier) {
|
|||
m_thread_spec_up.reset(specifier);
|
||||
}
|
||||
|
||||
bool Target::StopHook::ExecutionContextPasses(const ExecutionContext &exc_ctx) {
|
||||
SymbolContextSpecifier *specifier = GetSpecifier();
|
||||
if (!specifier)
|
||||
return true;
|
||||
|
||||
bool will_run = true;
|
||||
if (exc_ctx.GetFramePtr())
|
||||
will_run = GetSpecifier()->SymbolContextMatches(
|
||||
exc_ctx.GetFramePtr()->GetSymbolContext(eSymbolContextEverything));
|
||||
if (will_run && GetThreadSpecifier() != nullptr)
|
||||
will_run =
|
||||
GetThreadSpecifier()->ThreadPassesBasicTests(exc_ctx.GetThreadRef());
|
||||
|
||||
return will_run;
|
||||
}
|
||||
|
||||
void Target::StopHook::GetDescription(Stream *s,
|
||||
lldb::DescriptionLevel level) const {
|
||||
|
||||
// For brief descriptions, only print the subclass description:
|
||||
if (level == eDescriptionLevelBrief) {
|
||||
GetSubclassDescription(s, level);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned indent_level = s->GetIndentLevel();
|
||||
|
||||
s->SetIndentLevel(indent_level + 2);
|
||||
|
@ -3203,148 +3182,15 @@ void Target::StopHook::GetDescription(Stream *s,
|
|||
s->PutCString("\n");
|
||||
s->SetIndentLevel(indent_level + 2);
|
||||
}
|
||||
GetSubclassDescription(s, level);
|
||||
}
|
||||
|
||||
void Target::StopHookCommandLine::GetSubclassDescription(
|
||||
Stream *s, lldb::DescriptionLevel level) const {
|
||||
// The brief description just prints the first command.
|
||||
if (level == eDescriptionLevelBrief) {
|
||||
if (m_commands.GetSize() == 1)
|
||||
s->PutCString(m_commands.GetStringAtIndex(0));
|
||||
return;
|
||||
}
|
||||
s->Indent("Commands: \n");
|
||||
s->SetIndentLevel(s->GetIndentLevel() + 4);
|
||||
s->SetIndentLevel(indent_level + 4);
|
||||
uint32_t num_commands = m_commands.GetSize();
|
||||
for (uint32_t i = 0; i < num_commands; i++) {
|
||||
s->Indent(m_commands.GetStringAtIndex(i));
|
||||
s->PutCString("\n");
|
||||
}
|
||||
s->SetIndentLevel(s->GetIndentLevel() - 4);
|
||||
}
|
||||
|
||||
// Target::StopHookCommandLine
|
||||
void Target::StopHookCommandLine::SetActionFromString(const std::string &string) {
|
||||
GetCommands().SplitIntoLines(string);
|
||||
}
|
||||
|
||||
void Target::StopHookCommandLine::SetActionFromStrings(
|
||||
const std::vector<std::string> &strings) {
|
||||
for (auto string : strings)
|
||||
GetCommands().AppendString(string.c_str());
|
||||
}
|
||||
|
||||
bool Target::StopHookCommandLine::HandleStop(ExecutionContext &exc_ctx,
|
||||
StreamSP output_sp) {
|
||||
assert(exc_ctx.GetTargetPtr() && "Can't call PerformAction on a context "
|
||||
"with no target");
|
||||
|
||||
if (!m_commands.GetSize())
|
||||
return true;
|
||||
|
||||
CommandReturnObject result(false);
|
||||
result.SetImmediateOutputStream(output_sp);
|
||||
Debugger &debugger = exc_ctx.GetTargetPtr()->GetDebugger();
|
||||
CommandInterpreterRunOptions options;
|
||||
options.SetStopOnContinue(true);
|
||||
options.SetStopOnError(true);
|
||||
options.SetEchoCommands(false);
|
||||
options.SetPrintResults(true);
|
||||
options.SetPrintErrors(true);
|
||||
options.SetAddToHistory(false);
|
||||
|
||||
// Force Async:
|
||||
bool old_async = debugger.GetAsyncExecution();
|
||||
debugger.SetAsyncExecution(true);
|
||||
debugger.GetCommandInterpreter().HandleCommands(GetCommands(), &exc_ctx,
|
||||
options, result);
|
||||
debugger.SetAsyncExecution(old_async);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Target::StopHookScripted
|
||||
Status Target::StopHookScripted::SetScriptCallback(
|
||||
std::string class_name, StructuredData::ObjectSP extra_args_sp) {
|
||||
Status error;
|
||||
|
||||
ScriptInterpreter *script_interp =
|
||||
GetTarget()->GetDebugger().GetScriptInterpreter();
|
||||
if (!script_interp) {
|
||||
error.SetErrorString("No script interpreter installed.");
|
||||
return error;
|
||||
}
|
||||
|
||||
m_class_name = class_name;
|
||||
|
||||
m_extra_args = new StructuredDataImpl();
|
||||
|
||||
if (extra_args_sp)
|
||||
m_extra_args->SetObjectSP(extra_args_sp);
|
||||
|
||||
m_implementation_sp = script_interp->CreateScriptedStopHook(
|
||||
GetTarget(), m_class_name.c_str(), m_extra_args, error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
bool Target::StopHookScripted::HandleStop(ExecutionContext &exc_ctx,
|
||||
StreamSP output_sp) {
|
||||
assert(exc_ctx.GetTargetPtr() && "Can't call HandleStop on a context "
|
||||
"with no target");
|
||||
|
||||
ScriptInterpreter *script_interp =
|
||||
GetTarget()->GetDebugger().GetScriptInterpreter();
|
||||
if (!script_interp)
|
||||
return true;
|
||||
|
||||
bool should_stop = script_interp->ScriptedStopHookHandleStop(
|
||||
m_implementation_sp, exc_ctx, output_sp);
|
||||
|
||||
return should_stop;
|
||||
}
|
||||
|
||||
void Target::StopHookScripted::GetSubclassDescription(
|
||||
Stream *s, lldb::DescriptionLevel level) const {
|
||||
if (level == eDescriptionLevelBrief) {
|
||||
s->PutCString(m_class_name);
|
||||
return;
|
||||
}
|
||||
s->Indent("Class:");
|
||||
s->Printf("%s\n", m_class_name.c_str());
|
||||
|
||||
// Now print the extra args:
|
||||
// FIXME: We should use StructuredData.GetDescription on the m_extra_args
|
||||
// but that seems to rely on some printing plugin that doesn't exist.
|
||||
if (!m_extra_args->IsValid())
|
||||
return;
|
||||
StructuredData::ObjectSP object_sp = m_extra_args->GetObjectSP();
|
||||
if (!object_sp || !object_sp->IsValid())
|
||||
return;
|
||||
|
||||
StructuredData::Dictionary *as_dict = object_sp->GetAsDictionary();
|
||||
if (!as_dict || !as_dict->IsValid())
|
||||
return;
|
||||
|
||||
uint32_t num_keys = as_dict->GetSize();
|
||||
if (num_keys == 0)
|
||||
return;
|
||||
|
||||
s->Indent("Args:\n");
|
||||
s->SetIndentLevel(s->GetIndentLevel() + 4);
|
||||
|
||||
auto print_one_element = [&s](ConstString key,
|
||||
StructuredData::Object *object) {
|
||||
s->Indent();
|
||||
s->Printf("%s : %s\n", key.GetCString(),
|
||||
object->GetStringValue().str().c_str());
|
||||
return true;
|
||||
};
|
||||
|
||||
as_dict->ForEach(print_one_element);
|
||||
|
||||
s->SetIndentLevel(s->GetIndentLevel() - 4);
|
||||
s->SetIndentLevel(indent_level);
|
||||
}
|
||||
|
||||
static constexpr OptionEnumValueElement g_dynamic_value_types[] = {
|
||||
|
|
|
@ -1,131 +0,0 @@
|
|||
"""
|
||||
Test stop hook functionality
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import lldb
|
||||
import lldbsuite.test.lldbutil as lldbutil
|
||||
from lldbsuite.test.lldbtest import *
|
||||
|
||||
|
||||
class TestStopHooks(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
# If your test case doesn't stress debug info, the
|
||||
# set this to true. That way it won't be run once for
|
||||
# each debug info format.
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
def setUp(self):
|
||||
TestBase.setUp(self)
|
||||
self.build()
|
||||
self.main_source_file = lldb.SBFileSpec("main.c")
|
||||
full_path = os.path.join(self.getSourceDir(), "main.c")
|
||||
self.main_start_line = line_number(full_path, "main()")
|
||||
|
||||
def not_test_stop_hooks_scripted(self):
|
||||
"""Test that a scripted stop hook works with no specifiers"""
|
||||
self.stop_hooks_scripted(5)
|
||||
|
||||
def not_test_stop_hooks_scripted_right_func(self):
|
||||
"""Test that a scripted stop hook fires when there is a function match"""
|
||||
self.stop_hooks_scripted(5, "-n step_out_of_me")
|
||||
|
||||
def not_test_stop_hooks_scripted_wrong_func(self):
|
||||
"""Test that a scripted stop hook doesn't fire when the function does not match"""
|
||||
self.stop_hooks_scripted(0, "-n main")
|
||||
|
||||
def not_test_stop_hooks_scripted_right_lines(self):
|
||||
"""Test that a scripted stop hook fires when there is a function match"""
|
||||
self.stop_hooks_scripted(5, "-f main.c -l 1 -e %d"%(self.main_start_line))
|
||||
|
||||
def not_test_stop_hooks_scripted_wrong_lines(self):
|
||||
"""Test that a scripted stop hook doesn't fire when the function does not match"""
|
||||
self.stop_hooks_scripted(0, "-f main.c -l %d -e 100"%(self.main_start_line))
|
||||
|
||||
def not_test_stop_hooks_scripted_auto_continue(self):
|
||||
"""Test that the --auto-continue flag works"""
|
||||
self.do_test_auto_continue(False)
|
||||
|
||||
def test_stop_hooks_scripted_return_false(self):
|
||||
"""Test that the returning False from a stop hook works"""
|
||||
self.do_test_auto_continue(True)
|
||||
|
||||
def do_test_auto_continue(self, return_true):
|
||||
"""Test that auto-continue works."""
|
||||
# We set auto-continue to 1 but the stop hook only applies to step_out_of_me,
|
||||
# so we should end up stopped in main, having run the expression only once.
|
||||
self.script_setup()
|
||||
|
||||
result = lldb.SBCommandReturnObject()
|
||||
|
||||
if return_true:
|
||||
command = "target stop-hook add -P stop_hook.stop_handler -k increment -v 5 -k return_false -v 1 -n step_out_of_me"
|
||||
else:
|
||||
command = "target stop-hook add -G 1 -P stop_hook.stop_handler -k increment -v 5 -n step_out_of_me"
|
||||
|
||||
print("Running command: %s"%(command))
|
||||
|
||||
self.interp.HandleCommand(command, result)
|
||||
self.assertTrue(result.Succeeded, "Set the target stop hook")
|
||||
|
||||
# First run to main. If we go straight to the first stop hook hit,
|
||||
# run_to_source_breakpoint will fail because we aren't at original breakpoint
|
||||
|
||||
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
|
||||
"Stop here first", self.main_source_file)
|
||||
|
||||
# Now set the breakpoint on step_out_of_me, and make sure we run the
|
||||
# expression, then continue back to main.
|
||||
bkpt = target.BreakpointCreateBySourceRegex("Set a breakpoint here and step out", self.main_source_file)
|
||||
self.assertTrue(bkpt.GetNumLocations() > 0, "Got breakpoints in step_out_of_me")
|
||||
process.Continue()
|
||||
|
||||
var = target.FindFirstGlobalVariable("g_var")
|
||||
self.assertTrue(var.IsValid())
|
||||
self.assertEqual(var.GetValueAsUnsigned(), 5, "Updated g_var")
|
||||
|
||||
func_name = process.GetSelectedThread().frames[0].GetFunctionName()
|
||||
self.assertEqual("main", func_name, "Didn't stop at the expected function.")
|
||||
|
||||
def script_setup(self):
|
||||
self.interp = self.dbg.GetCommandInterpreter()
|
||||
result = lldb.SBCommandReturnObject()
|
||||
|
||||
# Bring in our script file:
|
||||
script_name = os.path.join(self.getSourceDir(), "stop_hook.py")
|
||||
command = "command script import " + script_name
|
||||
self.interp.HandleCommand(command, result)
|
||||
self.assertTrue(result.Succeeded(), "com scr imp failed: %s"%(result.GetError()))
|
||||
|
||||
# set a breakpoint at the end of main to catch our auto-continue tests.
|
||||
# Do it in the dummy target so it will get copied to our target even when
|
||||
# we don't have a chance to stop.
|
||||
dummy_target = self.dbg.GetDummyTarget()
|
||||
dummy_target.BreakpointCreateBySourceRegex("return result", self.main_source_file)
|
||||
|
||||
|
||||
def stop_hooks_scripted(self, g_var_value, specifier = None):
|
||||
self.script_setup()
|
||||
|
||||
result = lldb.SBCommandReturnObject()
|
||||
|
||||
command = "target stop-hook add -P stop_hook.stop_handler -k increment -v 5 "
|
||||
if specifier:
|
||||
command += specifier
|
||||
print("Running command: %s"%(command))
|
||||
|
||||
self.interp.HandleCommand(command, result)
|
||||
self.assertTrue(result.Succeeded, "Set the target stop hook")
|
||||
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
|
||||
"Set a breakpoint here", self.main_source_file)
|
||||
# At this point we've hit our stop hook so we should have run our expression,
|
||||
# which increments g_var by the amount specified by the increment key's value.
|
||||
while process.GetState() == lldb.eStateRunning:
|
||||
continue
|
||||
|
||||
var = target.FindFirstGlobalVariable("g_var")
|
||||
self.assertTrue(var.IsValid())
|
||||
self.assertEqual(var.GetValueAsUnsigned(), g_var_value, "Updated g_var")
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
Test stop hook functionality
|
||||
Test that stop hooks trigger on "step-out"
|
||||
"""
|
||||
|
||||
|
||||
|
@ -18,15 +18,10 @@ class TestStopHooks(TestBase):
|
|||
# each debug info format.
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
def setUp(self):
|
||||
TestBase.setUp(self)
|
||||
self.build()
|
||||
self.main_source_file = lldb.SBFileSpec("main.c")
|
||||
full_path = os.path.join(self.getSourceDir(), "main.c")
|
||||
self.main_start_line = line_number(full_path, "main()")
|
||||
|
||||
def test_stop_hooks_step_out(self):
|
||||
"""Test that stop hooks fire on step-out."""
|
||||
self.build()
|
||||
self.main_source_file = lldb.SBFileSpec("main.c")
|
||||
self.step_out_test()
|
||||
|
||||
def step_out_test(self):
|
||||
|
@ -42,3 +37,4 @@ class TestStopHooks(TestBase):
|
|||
self.assertTrue(var.IsValid())
|
||||
self.assertEqual(var.GetValueAsUnsigned(), 1, "Updated g_var")
|
||||
|
||||
|
||||
|
|
|
@ -10,6 +10,5 @@ int step_out_of_me()
|
|||
int
|
||||
main()
|
||||
{
|
||||
int result = step_out_of_me(); // Stop here first
|
||||
return result;
|
||||
return step_out_of_me();
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import lldb
|
||||
|
||||
class stop_handler:
|
||||
def __init__(self, target, extra_args, dict):
|
||||
self.extra_args = extra_args
|
||||
self.target = target
|
||||
self.counter = 0
|
||||
ret_val = self.extra_args.GetValueForKey("return_false")
|
||||
if ret_val:
|
||||
self.ret_val = False
|
||||
else:
|
||||
self.ret_val = True
|
||||
|
||||
def handle_stop(self, exe_ctx, stream):
|
||||
self.counter += 1
|
||||
stream.Print("I have stopped %d times.\n"%(self.counter))
|
||||
increment = 1
|
||||
value = self.extra_args.GetValueForKey("increment")
|
||||
if value:
|
||||
incr_as_str = value.GetStringValue(100)
|
||||
increment = int(incr_as_str)
|
||||
else:
|
||||
stream.Print("Could not find increment in extra_args\n")
|
||||
frame = exe_ctx.GetFrame()
|
||||
expression = "g_var += %d"%(increment)
|
||||
expr_result = frame.EvaluateExpression(expression)
|
||||
if not expr_result.GetError().Success():
|
||||
stream.Print("Error running expression: %s"%(expr_result.GetError().GetCString()))
|
||||
value = exe_ctx.target.FindFirstGlobalVariable("g_var")
|
||||
if not value.IsValid():
|
||||
stream.Print("Didn't get a valid value for g_var.")
|
||||
else:
|
||||
int_val = value.GetValueAsUnsigned()
|
||||
return self.ret_val
|
|
@ -1,10 +0,0 @@
|
|||
import lldb
|
||||
|
||||
class stop_handler:
|
||||
def __init__(self, target, extra_args, dict):
|
||||
self.extra_args = extra_args
|
||||
self.target = target
|
||||
|
||||
def handle_stop(self, exe_ctx, stream):
|
||||
stream.Print("I did indeed run\n")
|
||||
return True
|
|
@ -1,18 +0,0 @@
|
|||
# RUN: %clang_host -g %S/Inputs/main.c -o %t
|
||||
# RUN: %lldb %t -O 'command script import %S/Inputs/stop_hook.py' -s %s -o exit | FileCheck %s
|
||||
|
||||
b main
|
||||
# CHECK-LABEL: b main
|
||||
# CHECK: Breakpoint 1: where = {{.*}}`main
|
||||
|
||||
target stop-hook add -P stop_hook.stop_handler
|
||||
# CHECK-LABEL: target stop-hook add -P stop_hook.stop_handler
|
||||
# CHECK: Stop hook #1 added.
|
||||
|
||||
run
|
||||
# CHECK-LABEL: run
|
||||
# CHECK: I did indeed run
|
||||
# CHECK: Process {{.*}} stopped
|
||||
# CHECK: stop reason = breakpoint 1
|
||||
# CHECK: frame #0: {{.*}}`main at main.c
|
||||
|
|
@ -254,17 +254,3 @@ LLDBSWIGPython_GetDynamicSetting(void *module, const char *setting,
|
|||
const lldb::TargetSP &target_sp) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern "C" void *LLDBSwigPythonCreateScriptedStopHook(
|
||||
lldb::TargetSP target_sp, const char *python_class_name,
|
||||
const char *session_dictionary_name,
|
||||
lldb_private::StructuredDataImpl *args_impl, Status &error) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern "C" bool
|
||||
LLDBSwigPythonStopHookCallHandleStop(void *implementor,
|
||||
lldb::ExecutionContextRefSP exc_ctx_sp,
|
||||
lldb::StreamSP stream) {
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue