
Add a warning if no command was attached to the breakpoint. Update the help slightly. llvm-svn: 114467
937 lines
33 KiB
C++
937 lines
33 KiB
C++
//===-- ScriptInterpreterPython.cpp -----------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// In order to guarantee correct working with Python, Python.h *MUST* be
|
|
// the *FIRST* header file included:
|
|
|
|
#include <Python.h>
|
|
|
|
#include "lldb/Interpreter/ScriptInterpreterPython.h"
|
|
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <termios.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include <string>
|
|
|
|
#include "lldb/Breakpoint/Breakpoint.h"
|
|
#include "lldb/Breakpoint/BreakpointLocation.h"
|
|
#include "lldb/Breakpoint/StoppointCallbackContext.h"
|
|
#include "lldb/Core/Debugger.h"
|
|
#include "lldb/Core/FileSpec.h"
|
|
#include "lldb/Core/InputReader.h"
|
|
#include "lldb/Core/Stream.h"
|
|
#include "lldb/Core/StreamString.h"
|
|
#include "lldb/Core/Timer.h"
|
|
#include "lldb/Host/Host.h"
|
|
#include "lldb/Interpreter/CommandInterpreter.h"
|
|
#include "lldb/Interpreter/CommandReturnObject.h"
|
|
#include "lldb/Core/Debugger.h"
|
|
#include "lldb/Target/Process.h"
|
|
|
|
// This function is in the C++ output file generated by SWIG after it is
|
|
// run on all of the headers in "lldb/API/SB*.h"
|
|
extern "C" void init_lldb (void);
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
const char embedded_interpreter_string[] =
|
|
"import readline\n\
|
|
import code\n\
|
|
import sys\n\
|
|
import traceback\n\
|
|
\n\
|
|
class SimpleREPL(code.InteractiveConsole):\n\
|
|
def __init__(self, prompt, dict):\n\
|
|
code.InteractiveConsole.__init__(self,dict)\n\
|
|
self.prompt = prompt\n\
|
|
self.loop_exit = False\n\
|
|
self.dict = dict\n\
|
|
\n\
|
|
def interact(self):\n\
|
|
try:\n\
|
|
sys.ps1\n\
|
|
except AttributeError:\n\
|
|
sys.ps1 = \">>> \"\n\
|
|
try:\n\
|
|
sys.ps2\n\
|
|
except AttributeError:\n\
|
|
sys.ps2 = \"... \"\n\
|
|
\n\
|
|
while not self.loop_exit:\n\
|
|
try:\n\
|
|
self.read_py_command()\n\
|
|
except (SystemExit, EOFError):\n\
|
|
# EOF while in Python just breaks out to top level.\n\
|
|
self.write('\\n')\n\
|
|
self.loop_exit = True\n\
|
|
break\n\
|
|
except KeyboardInterrupt:\n\
|
|
self.write(\"\\nKeyboardInterrupt\\n\")\n\
|
|
self.resetbuffer()\n\
|
|
more = 0\n\
|
|
except:\n\
|
|
traceback.print_exc()\n\
|
|
\n\
|
|
def process_input (self, in_str):\n\
|
|
# Canonicalize the format of the input string\n\
|
|
temp_str = in_str\n\
|
|
temp_str.strip(' \t')\n\
|
|
words = temp_str.split()\n\
|
|
temp_str = ('').join(words)\n\
|
|
\n\
|
|
# Check the input string to see if it was the quit\n\
|
|
# command. If so, intercept it, so that it doesn't\n\
|
|
# close stdin on us!\n\
|
|
if (temp_str.lower() == \"quit()\" or temp_str.lower() == \"exit()\"):\n\
|
|
self.loop_exit = True\n\
|
|
in_str = \"raise SystemExit \"\n\
|
|
return in_str\n\
|
|
\n\
|
|
def my_raw_input (self, prompt):\n\
|
|
stream = sys.stdout\n\
|
|
stream.write (prompt)\n\
|
|
stream.flush ()\n\
|
|
try:\n\
|
|
line = sys.stdin.readline()\n\
|
|
except KeyboardInterrupt:\n\
|
|
line = \" \\n\"\n\
|
|
except (SystemExit, EOFError):\n\
|
|
line = \"quit()\\n\"\n\
|
|
if not line:\n\
|
|
raise EOFError\n\
|
|
if line[-1] == '\\n':\n\
|
|
line = line[:-1]\n\
|
|
return line\n\
|
|
\n\
|
|
def read_py_command(self):\n\
|
|
# Read off a complete Python command.\n\
|
|
more = 0\n\
|
|
while 1:\n\
|
|
if more:\n\
|
|
prompt = sys.ps2\n\
|
|
else:\n\
|
|
prompt = sys.ps1\n\
|
|
line = self.my_raw_input(prompt)\n\
|
|
# Can be None if sys.stdin was redefined\n\
|
|
encoding = getattr(sys.stdin, \"encoding\", None)\n\
|
|
if encoding and not isinstance(line, unicode):\n\
|
|
line = line.decode(encoding)\n\
|
|
line = self.process_input (line)\n\
|
|
more = self.push(line)\n\
|
|
if not more:\n\
|
|
break\n\
|
|
\n\
|
|
def run_python_interpreter (dict):\n\
|
|
# Pass in the dictionary, for continuity from one session to the next.\n\
|
|
repl = SimpleREPL('>>> ', dict)\n\
|
|
repl.interact()\n";
|
|
|
|
static int
|
|
_check_and_flush (FILE *stream)
|
|
{
|
|
int prev_fail = ferror (stream);
|
|
return fflush (stream) || prev_fail ? EOF : 0;
|
|
}
|
|
|
|
ScriptInterpreterPython::ScriptInterpreterPython (CommandInterpreter &interpreter) :
|
|
ScriptInterpreter (interpreter, eScriptLanguagePython),
|
|
m_compiled_module (NULL),
|
|
m_termios_valid (false)
|
|
{
|
|
|
|
Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__);
|
|
// Find the module that owns this code and use that path we get to
|
|
// set the PYTHONPATH appropriately.
|
|
|
|
FileSpec this_module (Host::GetModuleFileSpecForHostAddress ((void *)init_lldb));
|
|
std::string python_path;
|
|
|
|
if (this_module.GetDirectory())
|
|
{
|
|
// Append the directory that the module that loaded this code
|
|
// belongs to
|
|
python_path += this_module.GetDirectory().AsCString("");
|
|
|
|
#if defined (__APPLE__)
|
|
// If we are running on MacOSX we might be in a framework and should
|
|
// add an appropriate path so Resource can be found in a bundle
|
|
|
|
if (::strstr(this_module.GetDirectory().AsCString(""), ".framework"))
|
|
{
|
|
python_path.append(1, ':');
|
|
python_path.append(this_module.GetDirectory().AsCString(""));
|
|
python_path.append("/Resources/Python");
|
|
}
|
|
#endif
|
|
// The the PYTHONPATH environment variable so that Python can find
|
|
// our lldb.py module and our _lldb.so.
|
|
::setenv ("PYTHONPATH", python_path.c_str(), 1);
|
|
}
|
|
|
|
Py_Initialize ();
|
|
|
|
PyObject *compiled_module = Py_CompileString (embedded_interpreter_string, "embedded_interpreter.py",
|
|
Py_file_input);
|
|
|
|
m_compiled_module = static_cast<void*>(compiled_module);
|
|
|
|
// This function is in the C++ output file generated by SWIG after it is
|
|
// run on all of the headers in "lldb/API/SB*.h"
|
|
init_lldb ();
|
|
|
|
// Update the path python uses to search for modules to include the current directory.
|
|
|
|
int success = PyRun_SimpleString ("import sys");
|
|
success = PyRun_SimpleString ("sys.path.append ('.')");
|
|
if (success == 0)
|
|
{
|
|
// Import the Script Bridge module.
|
|
success = PyRun_SimpleString ("import lldb");
|
|
}
|
|
|
|
const char *pty_slave_name = GetScriptInterpreterPtyName ();
|
|
FILE *out_fh = interpreter.GetDebugger().GetOutputFileHandle();
|
|
|
|
PyObject *pmod = PyImport_ExecCodeModule(
|
|
const_cast<char*>("embedded_interpreter"),
|
|
static_cast<PyObject*>(m_compiled_module));
|
|
if (pmod != NULL)
|
|
{
|
|
PyRun_SimpleString ("ConsoleDict = locals()");
|
|
PyRun_SimpleString ("from embedded_interpreter import run_python_interpreter");
|
|
PyRun_SimpleString ("import sys");
|
|
PyRun_SimpleString ("from termios import *");
|
|
|
|
StreamString run_string;
|
|
run_string.Printf ("new_stdin = open('%s', 'r')", pty_slave_name);
|
|
PyRun_SimpleString (run_string.GetData());
|
|
PyRun_SimpleString ("sys.stdin = new_stdin");
|
|
|
|
if (out_fh != NULL)
|
|
{
|
|
PyObject *new_sysout = PyFile_FromFile (out_fh, (char *) "", (char *) "w",
|
|
_check_and_flush);
|
|
PyObject *sysmod = PyImport_AddModule ("sys");
|
|
PyObject *sysdict = PyModule_GetDict (sysmod);
|
|
|
|
if ((new_sysout != NULL)
|
|
&& (sysmod != NULL)
|
|
&& (sysdict != NULL))
|
|
{
|
|
PyDict_SetItemString (sysdict, "stdout", new_sysout);
|
|
}
|
|
|
|
if (PyErr_Occurred())
|
|
PyErr_Clear();
|
|
}
|
|
|
|
PyRun_SimpleString ("new_mode = tcgetattr(new_stdin)");
|
|
PyRun_SimpleString ("new_mode[3] = new_mode[3] | ECHO | ICANON");
|
|
PyRun_SimpleString ("new_mode[6][VEOF] = 255");
|
|
PyRun_SimpleString ("tcsetattr (new_stdin, TCSANOW, new_mode)");
|
|
|
|
run_string.Clear();
|
|
run_string.Printf ("lldb.debugger_unique_id = %d", interpreter.GetDebugger().GetID());
|
|
PyRun_SimpleString (run_string.GetData());
|
|
}
|
|
|
|
|
|
}
|
|
|
|
ScriptInterpreterPython::~ScriptInterpreterPython ()
|
|
{
|
|
Py_Finalize ();
|
|
}
|
|
|
|
bool
|
|
ScriptInterpreterPython::ExecuteOneLine (const char *command, CommandReturnObject *result)
|
|
{
|
|
if (command)
|
|
{
|
|
int success;
|
|
|
|
success = PyRun_SimpleString (command);
|
|
if (success == 0)
|
|
return true;
|
|
|
|
// The one-liner failed. Append the error message.
|
|
if (result)
|
|
result->AppendErrorWithFormat ("python failed attempting to evaluate '%s'\n", command);
|
|
return false;
|
|
}
|
|
|
|
if (result)
|
|
result->AppendError ("empty command passed to python\n");
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
ScriptInterpreterPython::InputReaderCallback
|
|
(
|
|
void *baton,
|
|
InputReader &reader,
|
|
lldb::InputReaderAction notification,
|
|
const char *bytes,
|
|
size_t bytes_len
|
|
)
|
|
{
|
|
if (baton == NULL)
|
|
return 0;
|
|
|
|
ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton;
|
|
switch (notification)
|
|
{
|
|
case eInputReaderActivate:
|
|
{
|
|
// Save terminal settings if we can
|
|
int input_fd;
|
|
FILE *input_fh = reader.GetDebugger().GetInputFileHandle();
|
|
if (input_fh != NULL)
|
|
input_fd = ::fileno (input_fh);
|
|
else
|
|
input_fd = STDIN_FILENO;
|
|
|
|
script_interpreter->m_termios_valid = ::tcgetattr (input_fd, &script_interpreter->m_termios) == 0;
|
|
struct termios tmp_termios;
|
|
if (::tcgetattr (input_fd, &tmp_termios) == 0)
|
|
{
|
|
tmp_termios.c_cc[VEOF] = _POSIX_VDISABLE;
|
|
::tcsetattr (input_fd, TCSANOW, &tmp_termios);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case eInputReaderDeactivate:
|
|
break;
|
|
|
|
case eInputReaderReactivate:
|
|
break;
|
|
|
|
case eInputReaderGotToken:
|
|
if (bytes && bytes_len)
|
|
{
|
|
if ((int) bytes[0] == 4)
|
|
::write (script_interpreter->GetMasterFileDescriptor(), "quit()", 6);
|
|
else
|
|
::write (script_interpreter->GetMasterFileDescriptor(), bytes, bytes_len);
|
|
}
|
|
::write (script_interpreter->GetMasterFileDescriptor(), "\n", 1);
|
|
break;
|
|
|
|
case eInputReaderDone:
|
|
// Send a control D to the script interpreter
|
|
//::write (interpreter->GetMasterFileDescriptor(), "\nquit()\n", strlen("\nquit()\n"));
|
|
// Write a newline out to the reader output
|
|
//::fwrite ("\n", 1, 1, out_fh);
|
|
// Restore terminal settings if they were validly saved
|
|
if (script_interpreter->m_termios_valid)
|
|
{
|
|
int input_fd;
|
|
FILE *input_fh = reader.GetDebugger().GetInputFileHandle();
|
|
if (input_fh != NULL)
|
|
input_fd = ::fileno (input_fh);
|
|
else
|
|
input_fd = STDIN_FILENO;
|
|
|
|
::tcsetattr (input_fd, TCSANOW, &script_interpreter->m_termios);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return bytes_len;
|
|
}
|
|
|
|
|
|
void
|
|
ScriptInterpreterPython::ExecuteInterpreterLoop ()
|
|
{
|
|
Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__);
|
|
|
|
Debugger &debugger = m_interpreter.GetDebugger();
|
|
|
|
// At the moment, the only time the debugger does not have an input file handle is when this is called
|
|
// directly from Python, in which case it is both dangerous and unnecessary (not to mention confusing) to
|
|
// try to embed a running interpreter loop inside the already running Python interpreter loop, so we won't
|
|
// do it.
|
|
|
|
if (debugger.GetInputFileHandle() == NULL)
|
|
return;
|
|
|
|
InputReaderSP reader_sp (new InputReader(debugger));
|
|
if (reader_sp)
|
|
{
|
|
Error error (reader_sp->Initialize (ScriptInterpreterPython::InputReaderCallback,
|
|
this, // baton
|
|
eInputReaderGranularityLine, // token size, to pass to callback function
|
|
NULL, // end token
|
|
NULL, // prompt
|
|
true)); // echo input
|
|
|
|
if (error.Success())
|
|
{
|
|
debugger.PushInputReader (reader_sp);
|
|
ExecuteOneLine ("run_python_interpreter(ConsoleDict)", NULL);
|
|
debugger.PopInputReader (reader_sp);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string,
|
|
ScriptInterpreter::ReturnType return_type,
|
|
void *ret_value)
|
|
{
|
|
PyObject *py_return = NULL;
|
|
PyObject *mainmod = PyImport_AddModule ("__main__");
|
|
PyObject *globals = PyModule_GetDict (mainmod);
|
|
PyObject *locals = globals;
|
|
PyObject *py_error = NULL;
|
|
bool ret_success;
|
|
int success;
|
|
|
|
if (in_string != NULL)
|
|
{
|
|
py_return = PyRun_String (in_string, Py_eval_input, globals, locals);
|
|
if (py_return == NULL)
|
|
{
|
|
py_error = PyErr_Occurred ();
|
|
if (py_error != NULL)
|
|
PyErr_Clear ();
|
|
|
|
py_return = PyRun_String (in_string, Py_single_input, globals, locals);
|
|
}
|
|
|
|
if (py_return != NULL)
|
|
{
|
|
switch (return_type)
|
|
{
|
|
case eCharPtr: // "char *"
|
|
{
|
|
const char format[3] = "s#";
|
|
success = PyArg_Parse (py_return, format, (char **) &ret_value);
|
|
break;
|
|
}
|
|
case eBool:
|
|
{
|
|
const char format[2] = "b";
|
|
success = PyArg_Parse (py_return, format, (bool *) ret_value);
|
|
break;
|
|
}
|
|
case eShortInt:
|
|
{
|
|
const char format[2] = "h";
|
|
success = PyArg_Parse (py_return, format, (short *) ret_value);
|
|
break;
|
|
}
|
|
case eShortIntUnsigned:
|
|
{
|
|
const char format[2] = "H";
|
|
success = PyArg_Parse (py_return, format, (unsigned short *) ret_value);
|
|
break;
|
|
}
|
|
case eInt:
|
|
{
|
|
const char format[2] = "i";
|
|
success = PyArg_Parse (py_return, format, (int *) ret_value);
|
|
break;
|
|
}
|
|
case eIntUnsigned:
|
|
{
|
|
const char format[2] = "I";
|
|
success = PyArg_Parse (py_return, format, (unsigned int *) ret_value);
|
|
break;
|
|
}
|
|
case eLongInt:
|
|
{
|
|
const char format[2] = "l";
|
|
success = PyArg_Parse (py_return, format, (long *) ret_value);
|
|
break;
|
|
}
|
|
case eLongIntUnsigned:
|
|
{
|
|
const char format[2] = "k";
|
|
success = PyArg_Parse (py_return, format, (unsigned long *) ret_value);
|
|
break;
|
|
}
|
|
case eLongLong:
|
|
{
|
|
const char format[2] = "L";
|
|
success = PyArg_Parse (py_return, format, (long long *) ret_value);
|
|
break;
|
|
}
|
|
case eLongLongUnsigned:
|
|
{
|
|
const char format[2] = "K";
|
|
success = PyArg_Parse (py_return, format, (unsigned long long *) ret_value);
|
|
break;
|
|
}
|
|
case eFloat:
|
|
{
|
|
const char format[2] = "f";
|
|
success = PyArg_Parse (py_return, format, (float *) ret_value);
|
|
break;
|
|
}
|
|
case eDouble:
|
|
{
|
|
const char format[2] = "d";
|
|
success = PyArg_Parse (py_return, format, (double *) ret_value);
|
|
break;
|
|
}
|
|
case eChar:
|
|
{
|
|
const char format[2] = "c";
|
|
success = PyArg_Parse (py_return, format, (char *) ret_value);
|
|
break;
|
|
}
|
|
default:
|
|
{}
|
|
}
|
|
Py_DECREF (py_return);
|
|
if (success)
|
|
ret_success = true;
|
|
else
|
|
ret_success = false;
|
|
}
|
|
}
|
|
|
|
py_error = PyErr_Occurred();
|
|
if (py_error != NULL)
|
|
{
|
|
if (PyErr_GivenExceptionMatches (py_error, PyExc_SyntaxError))
|
|
PyErr_Print ();
|
|
PyErr_Clear();
|
|
ret_success = false;
|
|
}
|
|
|
|
return ret_success;
|
|
}
|
|
|
|
bool
|
|
ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string)
|
|
{
|
|
bool success = false;
|
|
PyObject *py_return = NULL;
|
|
PyObject *mainmod = PyImport_AddModule ("__main__");
|
|
PyObject *globals = PyModule_GetDict (mainmod);
|
|
PyObject *locals = globals;
|
|
PyObject *py_error = NULL;
|
|
|
|
if (in_string != NULL)
|
|
{
|
|
struct _node *compiled_node = PyParser_SimpleParseString (in_string, Py_file_input);
|
|
if (compiled_node)
|
|
{
|
|
PyCodeObject *compiled_code = PyNode_Compile (compiled_node, "temp.py");
|
|
if (compiled_code)
|
|
{
|
|
py_return = PyEval_EvalCode (compiled_code, globals, locals);
|
|
if (py_return != NULL)
|
|
{
|
|
success = true;
|
|
Py_DECREF (py_return);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
py_error = PyErr_Occurred ();
|
|
if (py_error != NULL)
|
|
{
|
|
if (PyErr_GivenExceptionMatches (py_error, PyExc_SyntaxError))
|
|
PyErr_Print ();
|
|
PyErr_Clear();
|
|
success = false;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
static const char *g_reader_instructions = "Enter your Python command(s). Type 'DONE' to end.";
|
|
|
|
size_t
|
|
ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback
|
|
(
|
|
void *baton,
|
|
InputReader &reader,
|
|
lldb::InputReaderAction notification,
|
|
const char *bytes,
|
|
size_t bytes_len
|
|
)
|
|
{
|
|
static StringList commands_in_progress;
|
|
|
|
FILE *out_fh = reader.GetDebugger().GetOutputFileHandle();
|
|
if (out_fh == NULL)
|
|
out_fh = stdout;
|
|
|
|
switch (notification)
|
|
{
|
|
case eInputReaderActivate:
|
|
{
|
|
commands_in_progress.Clear();
|
|
if (out_fh)
|
|
{
|
|
::fprintf (out_fh, "%s\n", g_reader_instructions);
|
|
if (reader.GetPrompt())
|
|
::fprintf (out_fh, "%s", reader.GetPrompt());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case eInputReaderDeactivate:
|
|
break;
|
|
|
|
case eInputReaderReactivate:
|
|
if (reader.GetPrompt() && out_fh)
|
|
::fprintf (out_fh, "%s", reader.GetPrompt());
|
|
break;
|
|
|
|
case eInputReaderGotToken:
|
|
{
|
|
std::string temp_string (bytes, bytes_len);
|
|
commands_in_progress.AppendString (temp_string.c_str());
|
|
if (out_fh && !reader.IsDone() && reader.GetPrompt())
|
|
::fprintf (out_fh, "%s", reader.GetPrompt());
|
|
}
|
|
break;
|
|
|
|
case eInputReaderDone:
|
|
{
|
|
BreakpointOptions *bp_options = (BreakpointOptions *)baton;
|
|
std::auto_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData());
|
|
data_ap->user_source.AppendList (commands_in_progress);
|
|
if (data_ap.get())
|
|
{
|
|
ScriptInterpreter *interpreter = reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
|
|
if (interpreter)
|
|
{
|
|
if (interpreter->GenerateBreakpointCommandCallbackData (data_ap->user_source,
|
|
data_ap->script_source))
|
|
{
|
|
if (data_ap->script_source.GetSize() == 1)
|
|
{
|
|
BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release()));
|
|
bp_options->SetCallback (ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp);
|
|
}
|
|
}
|
|
else
|
|
::fprintf (out_fh, "Warning: No command attached to breakpoint.\n");
|
|
}
|
|
else
|
|
{
|
|
// FIXME: Error processing.
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
return bytes_len;
|
|
}
|
|
|
|
void
|
|
ScriptInterpreterPython::CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options,
|
|
CommandReturnObject &result)
|
|
{
|
|
Debugger &debugger = m_interpreter.GetDebugger();
|
|
InputReaderSP reader_sp (new InputReader (debugger));
|
|
|
|
if (reader_sp)
|
|
{
|
|
Error err = reader_sp->Initialize (
|
|
ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback,
|
|
bp_options, // baton
|
|
eInputReaderGranularityLine, // token size, for feeding data to callback function
|
|
"DONE", // end token
|
|
"> ", // prompt
|
|
true); // echo input
|
|
|
|
if (err.Success())
|
|
debugger.PushInputReader (reader_sp);
|
|
else
|
|
{
|
|
result.AppendError (err.AsCString());
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError("out of memory");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
|
|
// Set a Python one-liner as the callback for the breakpoint.
|
|
void
|
|
ScriptInterpreterPython::SetBreakpointCommandCallback (BreakpointOptions *bp_options,
|
|
const char *oneliner)
|
|
{
|
|
std::auto_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData());
|
|
|
|
// It's necessary to set both user_source and script_source to the oneliner.
|
|
// The former is used to generate callback description (as in breakpoint command list)
|
|
// while the latter is used for Python to interpret during the actual callback.
|
|
data_ap->user_source.AppendString (oneliner);
|
|
data_ap->script_source.AppendString (oneliner);
|
|
|
|
BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release()));
|
|
bp_options->SetCallback (ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp);
|
|
|
|
return;
|
|
}
|
|
|
|
bool
|
|
ScriptInterpreterPython::ExportFunctionDefinitionToInterpreter (StringList &function_def)
|
|
{
|
|
// Convert StringList to one long, newline delimited, const char *.
|
|
std::string function_def_string;
|
|
|
|
int num_lines = function_def.GetSize();
|
|
|
|
for (int i = 0; i < num_lines; ++i)
|
|
{
|
|
function_def_string.append (function_def.GetStringAtIndex(i));
|
|
if (function_def_string.at (function_def_string.length() - 1) != '\n')
|
|
function_def_string.append ("\n");
|
|
|
|
}
|
|
|
|
return ExecuteMultipleLines (function_def_string.c_str());
|
|
}
|
|
|
|
bool
|
|
ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (StringList &user_input, StringList &callback_data)
|
|
{
|
|
static int num_created_functions = 0;
|
|
user_input.RemoveBlankLines ();
|
|
int num_lines = user_input.GetSize();
|
|
|
|
if (num_lines == 1)
|
|
{
|
|
callback_data.AppendString (user_input.GetStringAtIndex (0));
|
|
return true;
|
|
}
|
|
|
|
// 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";
|
|
|
|
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_");
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
if (auto_generated_function.GetSize() > 0)
|
|
{
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|