forked from OSchip/llvm-project
Automatically wrap *all* Python code entered for a breakpoint command inside
an auto-generated Python function, and pass the stoppoint context frame and breakpoint location as parameters to the function (named 'frame' and 'bp_loc'), to be used inside the breakpoint command Python code, if desired. llvm-svn: 114849
This commit is contained in:
parent
30ad985b6b
commit
18474c933c
|
@ -76,6 +76,7 @@ public:
|
|||
|
||||
private:
|
||||
friend class SBBreakpoint;
|
||||
friend class lldb_private::ScriptInterpreterPython;
|
||||
|
||||
SBBreakpointLocation (const lldb::BreakpointLocationSP &break_loc_sp);
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@ protected:
|
|||
|
||||
private:
|
||||
friend class SBThread;
|
||||
friend class lldb_private::ScriptInterpreterPython;
|
||||
|
||||
#ifndef SWIG
|
||||
|
||||
|
|
|
@ -99,6 +99,15 @@
|
|||
#include "lldb/API/SBType.h"
|
||||
#include "lldb/API/SBValue.h"
|
||||
#include "lldb/API/SBValueList.h"
|
||||
#include "lldb/Interpreter/ScriptInterpreterPython.h"
|
||||
#include "lldb/Breakpoint/Breakpoint.h"
|
||||
#include "lldb/Breakpoint/BreakpointLocation.h"
|
||||
#include "lldb/Breakpoint/StoppointCallbackContext.h"
|
||||
#include "lldb/Target/ExecutionContext.h"
|
||||
#include "lldb/Target/StackFrame.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
#include "lldb/Target/Thread.h"
|
||||
#include "lldb/lldb-forward-rtti.h"
|
||||
using namespace lldb_private;
|
||||
%}
|
||||
|
||||
|
@ -157,3 +166,97 @@ typedef int StopReason;
|
|||
%include "lldb/lldb-types.h"
|
||||
|
||||
%include "./Python/python-extensions.swig"
|
||||
|
||||
|
||||
%wrapper %{
|
||||
|
||||
|
||||
bool
|
||||
ScriptInterpreterPython::BreakpointCallbackFunction (void *baton,
|
||||
StoppointCallbackContext *context,
|
||||
lldb::user_id_t break_id,
|
||||
lldb::user_id_t break_loc_id)
|
||||
{
|
||||
bool ret_value = true;
|
||||
|
||||
BreakpointOptions::CommandData *bp_option_data = (BreakpointOptions::CommandData *) baton;
|
||||
const char *python_function_name = bp_option_data->script_source.GetStringAtIndex (0);
|
||||
|
||||
if (python_function_name != NULL
|
||||
&& python_function_name[0] != '\0')
|
||||
{
|
||||
Thread *thread = context->exe_ctx.thread;
|
||||
Target *target = context->exe_ctx.target;
|
||||
const lldb::StackFrameSP stop_frame_sp = thread->GetStackFrameSPForStackFramePtr (context->exe_ctx.frame);
|
||||
lldb::BreakpointSP breakpoint_sp = target->GetBreakpointByID (break_id);
|
||||
const lldb::BreakpointLocationSP bp_loc_sp = breakpoint_sp->FindLocationByID (break_loc_id);
|
||||
|
||||
lldb::SBFrame sb_frame (stop_frame_sp);
|
||||
lldb::SBBreakpointLocation sb_bp_loc (bp_loc_sp);
|
||||
|
||||
if (!sb_bp_loc.IsValid() || !sb_frame.IsValid())
|
||||
return ret_value;
|
||||
|
||||
|
||||
PyObject *Frame_PyObj = SWIG_NewPointerObj((void *) &sb_frame, SWIGTYPE_p_lldb__SBFrame, 0);
|
||||
PyObject *Bp_Loc_PyObj = SWIG_NewPointerObj ((void *) &sb_bp_loc, SWIGTYPE_p_lldb__SBBreakpointLocation, 0);
|
||||
|
||||
if (Frame_PyObj == NULL
|
||||
|| Bp_Loc_PyObj == NULL)
|
||||
return ret_value;
|
||||
|
||||
PyObject *pmodule, *pdict, *pfunc;
|
||||
PyObject *pargs, *pvalue;
|
||||
|
||||
pmodule = PyImport_AddModule ("__main__");
|
||||
if (pmodule != NULL)
|
||||
{
|
||||
pdict = PyModule_GetDict (pmodule);
|
||||
if (pdict != NULL)
|
||||
{
|
||||
pfunc = PyObject_GetAttrString (pmodule, python_function_name);
|
||||
if (pfunc && PyCallable_Check (pfunc))
|
||||
{
|
||||
pargs = PyTuple_New (2);
|
||||
if (pargs == NULL)
|
||||
{
|
||||
if (PyErr_Occurred())
|
||||
PyErr_Clear();
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
PyTuple_SetItem (pargs, 0, Frame_PyObj); // This "steals" a reference to Frame_PyObj
|
||||
PyTuple_SetItem (pargs, 1, Bp_Loc_PyObj); // This "steals" a reference to Bp_Loc_PyObj
|
||||
pvalue = PyObject_CallObject (pfunc, pargs);
|
||||
Py_DECREF (pargs);
|
||||
|
||||
if (pvalue != NULL)
|
||||
{
|
||||
Py_DECREF (pvalue);
|
||||
}
|
||||
else if (PyErr_Occurred ())
|
||||
{
|
||||
PyErr_Clear();
|
||||
}
|
||||
Py_DECREF (pfunc);
|
||||
}
|
||||
else if (PyErr_Occurred())
|
||||
{
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
else if (PyErr_Occurred())
|
||||
{
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
else if (PyErr_Occurred ())
|
||||
{
|
||||
PyErr_Clear ();
|
||||
}
|
||||
}
|
||||
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
%}
|
||||
|
|
|
@ -717,220 +717,44 @@ ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (StringList &user
|
|||
{
|
||||
static int num_created_functions = 0;
|
||||
user_input.RemoveBlankLines ();
|
||||
int num_lines = user_input.GetSize();
|
||||
int num_lines = user_input.GetSize ();
|
||||
StreamString sstr;
|
||||
|
||||
if (num_lines == 1)
|
||||
{
|
||||
callback_data.AppendString (user_input.GetStringAtIndex (0));
|
||||
return true;
|
||||
}
|
||||
// Take what the user wrote, wrap it all up inside one big auto-generated Python function, passing in the
|
||||
// frame and breakpoint location as parameters to the function.
|
||||
|
||||
// Traverse user_input exactly once. At each line, either copy line into new, auto-generated function,
|
||||
// increasing indentation by 5 spaces or copy it exactly as is into the user-written
|
||||
// currently-to-be-pushed-to-Python function def. At the end of each Python function def, push the function
|
||||
// to Python, and clear the function string, to start again. At the end of it all, if there is anything in
|
||||
// the auto-generated function, push it to Python and add the function call to it to the callback data.
|
||||
|
||||
bool inside_user_python_function_def = false;
|
||||
std::string whitespace = " \t";
|
||||
sstr.Printf ("lldb_autogen_python_bp_callback_func_%d", num_created_functions);
|
||||
++num_created_functions;
|
||||
std::string auto_generated_function_name = sstr.GetData();
|
||||
|
||||
sstr.Clear();
|
||||
StringList auto_generated_function;
|
||||
StringList user_defined_function;
|
||||
|
||||
StringList *current_function_def = &auto_generated_function;
|
||||
std::string auto_generated_function_name ("lldb_autogen_python_bp_callback_func_");
|
||||
// Create the function name & definition string.
|
||||
|
||||
sstr.Printf ("def %s (frame, bp_loc):", auto_generated_function_name.c_str());
|
||||
auto_generated_function.AppendString (sstr.GetData());
|
||||
|
||||
// Wrap everything up inside the function, increasing the indentation.
|
||||
|
||||
for (int i = 0; i < num_lines; ++i)
|
||||
{
|
||||
std::string current_line (user_input.GetStringAtIndex(i));
|
||||
size_t idx = current_line.find_first_of (whitespace);
|
||||
if (idx != std::string::npos)
|
||||
{
|
||||
if (idx == 0) // line starts with indentation...
|
||||
{
|
||||
if (inside_user_python_function_def)
|
||||
{
|
||||
// Add this line to the user's python function definition.
|
||||
current_function_def->AppendString (current_line.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add this line to our auto-generated function; increase original indentation by 5.
|
||||
StreamString tmp_str;
|
||||
tmp_str.Printf (" %s", current_line.c_str());
|
||||
current_function_def->AppendString (tmp_str.GetData());
|
||||
}
|
||||
}
|
||||
else // line does not start with indentation...
|
||||
{
|
||||
// First, check to see if we just finished a user-written function definition; if so,
|
||||
// wrap it up and send it to Python.
|
||||
|
||||
if (inside_user_python_function_def && (user_defined_function.GetSize() > 0))
|
||||
{
|
||||
if (! ExportFunctionDefinitionToInterpreter (user_defined_function))
|
||||
{
|
||||
// User entered incorrect Python syntax. We should not attempt to continue.
|
||||
// Clear the callback data, and return immediately.
|
||||
callback_data.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// User defined function was successfully sent to Python. Clean up after it.
|
||||
user_defined_function.Clear();
|
||||
inside_user_python_function_def = false;
|
||||
current_function_def = &auto_generated_function;
|
||||
}
|
||||
|
||||
// Next, check to see if we are at the start of a user-defined Python function.
|
||||
std::string first_word = current_line.substr (0, idx);
|
||||
if (first_word.compare ("def") == 0)
|
||||
{
|
||||
// Start the user defined function properly:
|
||||
inside_user_python_function_def = true;
|
||||
current_function_def = &user_defined_function;
|
||||
current_function_def->AppendString (current_line.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are in "loose" Python code that we need to collect and put into the auto-generated
|
||||
// function.
|
||||
StreamString tmp_str;
|
||||
current_function_def = &auto_generated_function;
|
||||
if (current_function_def->GetSize() == 0)
|
||||
{
|
||||
// Create the function name, and add insert the function def line.
|
||||
tmp_str.Printf ("%d", num_created_functions);
|
||||
++num_created_functions;
|
||||
auto_generated_function_name.append (tmp_str.GetData());
|
||||
|
||||
tmp_str.Clear();
|
||||
tmp_str.Printf ("def %s ():", auto_generated_function_name.c_str());
|
||||
current_function_def->AppendString (tmp_str.GetData());
|
||||
}
|
||||
tmp_str.Clear();
|
||||
|
||||
// Indent the line an extra 5 spaces and add it to our auto-generated function.
|
||||
tmp_str.Printf (" %s", current_line.c_str());
|
||||
current_function_def->AppendString (tmp_str.GetData());
|
||||
} // else we are in loose Python code
|
||||
} // else current line does not start with indentatin
|
||||
}
|
||||
else
|
||||
{
|
||||
// There was no white space on the line (and therefore no indentation either).
|
||||
|
||||
// First, check to see if we just finished a user-written function definition; if so,
|
||||
// wrap it up and send it to Python.
|
||||
|
||||
if (inside_user_python_function_def && (user_defined_function.GetSize() > 0))
|
||||
{
|
||||
if (! ExportFunctionDefinitionToInterpreter (user_defined_function))
|
||||
{
|
||||
// User entered incorrect Python syntax. We should not attempt to continue.
|
||||
// Clear the callback data, and return immediately.
|
||||
callback_data.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// User defined function was successfully sent to Python. Clean up after it.
|
||||
user_defined_function.Clear();
|
||||
inside_user_python_function_def = false;
|
||||
current_function_def = &auto_generated_function;
|
||||
}
|
||||
|
||||
// We cannot be at the start of a function definition (they contain white space) so we
|
||||
// must have "loose" python code.
|
||||
|
||||
StreamString tmp_str;
|
||||
current_function_def = &auto_generated_function;
|
||||
if (current_function_def->GetSize() == 0)
|
||||
{
|
||||
// Create the function name, and add insert the function def line.
|
||||
tmp_str.Printf ("%d", num_created_functions);
|
||||
++num_created_functions;
|
||||
auto_generated_function_name.append (tmp_str.GetData());
|
||||
|
||||
tmp_str.Clear();
|
||||
tmp_str.Printf ("def %s ():", auto_generated_function_name.c_str());
|
||||
current_function_def->AppendString (tmp_str.GetData());
|
||||
}
|
||||
tmp_str.Clear();
|
||||
|
||||
// Indent the line an extra 5 spaces and add it to our auto-generated function.
|
||||
tmp_str.Printf (" %s", current_line.c_str());
|
||||
current_function_def->AppendString (tmp_str.GetData());
|
||||
|
||||
} // else there was no white space on the line.
|
||||
}
|
||||
|
||||
// Perhaps the last line of input was also the last line of a user-defined function; if so,
|
||||
// attempt to push the function down to Python.
|
||||
|
||||
if (inside_user_python_function_def && (user_defined_function.GetSize() > 0))
|
||||
{
|
||||
if (! ExportFunctionDefinitionToInterpreter (user_defined_function))
|
||||
{
|
||||
callback_data.Clear();
|
||||
return false;
|
||||
}
|
||||
sstr.Clear ();
|
||||
sstr.Printf (" %s", user_input.GetStringAtIndex (i));
|
||||
auto_generated_function.AppendString (sstr.GetData());
|
||||
}
|
||||
|
||||
// Verify that the results are valid Python.
|
||||
|
||||
if (auto_generated_function.GetSize() > 0)
|
||||
if (!ExportFunctionDefinitionToInterpreter (auto_generated_function))
|
||||
{
|
||||
// Export the auto-generated function to Python.
|
||||
if (ExportFunctionDefinitionToInterpreter (auto_generated_function))
|
||||
{
|
||||
// The export succeeded; the syntax must be ok. Generate the function call and put
|
||||
// it in the callback data.
|
||||
StreamString tmp_str;
|
||||
tmp_str.Printf ("%s ()", auto_generated_function_name.c_str());
|
||||
callback_data.AppendString (tmp_str.GetData());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Syntax error!
|
||||
callback_data.Clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there was any code, it consisted entirely of function defs, without any calls to the functions.
|
||||
// No actual exectuable code was therefore generated. (Function calls would have looked like "loose" python,
|
||||
// and would have been collected into the auto-generated function.)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the name of the auto-generated function to be called.
|
||||
|
||||
callback_data.AppendString (auto_generated_function_name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptInterpreterPython::BreakpointCallbackFunction
|
||||
(
|
||||
void *baton,
|
||||
StoppointCallbackContext *context,
|
||||
lldb::user_id_t break_id,
|
||||
lldb::user_id_t break_loc_id
|
||||
)
|
||||
{
|
||||
bool ret_value = true;
|
||||
bool temp_bool;
|
||||
|
||||
BreakpointOptions::CommandData *bp_option_data = (BreakpointOptions::CommandData *) baton;
|
||||
|
||||
const char *python_string = bp_option_data->script_source.GetStringAtIndex(0);
|
||||
|
||||
if (python_string != NULL)
|
||||
{
|
||||
bool success = context->exe_ctx.target->GetDebugger().
|
||||
GetCommandInterpreter().
|
||||
GetScriptInterpreter()->ExecuteOneLineWithReturn (python_string,
|
||||
ScriptInterpreter::eBool,
|
||||
(void *) &temp_bool);
|
||||
if (success)
|
||||
ret_value = temp_bool;
|
||||
}
|
||||
|
||||
return ret_value;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue