
accessed by the objects that own the settings. The previous approach wasn't very usable and made for a lot of unnecessary code just to access variables that were already owned by the objects. While I fixed those things, I saw that CommandObject objects should really have a reference to their command interpreter so they can access the terminal with if they want to output usaage. Fixed up all CommandObjects to take an interpreter and cleaned up the API to not need the interpreter to be passed in. Fixed the disassemble command to output the usage if no options are passed down and arguments are passed (all disassebmle variants take options, there are no "args only"). llvm-svn: 114252
892 lines
30 KiB
C++
892 lines
30 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
|
|
{
|
|
// 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();
|
|
std::string last_function_call;
|
|
|
|
// Go through lines of input looking for any function definitions. For each function definition found,
|
|
// export the function definition to Python, create a potential function call for the function, and
|
|
// mark the lines of the function to be removed from the user input.
|
|
|
|
for (int i = 0; i < num_lines; ++i)
|
|
{
|
|
int function_start = i;
|
|
std::string current_str = user_input.GetStringAtIndex (i);
|
|
const char *current_line = current_str.c_str();
|
|
int len = 0;
|
|
if (current_line)
|
|
len = strlen (current_line);
|
|
|
|
// Check to see if the current line is the start of a Python function definition.
|
|
if (len > 4 && strncmp (current_line, "def ", 4) == 0)
|
|
{
|
|
// We've found the first line of a function. First, get the function name.
|
|
|
|
// Skip over the 'def '.
|
|
char *start = (char *) current_line + 4;
|
|
|
|
// Skip over white space.
|
|
while (start[0] == ' ' || start[0] == '\t')
|
|
++start;
|
|
|
|
// Find the end of the function name.
|
|
char *end = start;
|
|
while (isalnum (end[0]) || end[0] == '_')
|
|
++end;
|
|
|
|
int name_len = end - start;
|
|
std::string func_name = current_str.substr (4, name_len);
|
|
|
|
// Now to find the last line of the function. That will be the first line that does not begin with
|
|
// any white space (thanks to Python's indentation rules).
|
|
++i;
|
|
bool found = false;
|
|
while (i < num_lines && !found)
|
|
{
|
|
std::string next_str = user_input.GetStringAtIndex (i);
|
|
const char *next_line = next_str.c_str();
|
|
if (next_line[0] != ' ' && next_line[0] != '\t')
|
|
found = true;
|
|
else
|
|
++i;
|
|
}
|
|
if (found)
|
|
--i; // Make 'i' correspond to the last line of the function.
|
|
int function_end = i;
|
|
|
|
// Special case: All of user_input is one big function definition.
|
|
if ((function_start == 0) && (function_end == (num_lines - 1)))
|
|
{
|
|
ExportFunctionDefinitionToInterpreter (user_input);
|
|
last_function_call = func_name + " ()";
|
|
callback_data.AppendString (last_function_call.c_str());
|
|
return callback_data.GetSize() > 0;
|
|
}
|
|
else
|
|
{
|
|
// Make a copy of the function definition:
|
|
StringList new_function;
|
|
for (int k = function_start; k <= function_end; ++k)
|
|
{
|
|
new_function.AppendString (user_input.GetStringAtIndex (k));
|
|
// Mark the string to be deleted from user_input.
|
|
user_input.DeleteStringAtIndex (k);
|
|
user_input.InsertStringAtIndex (k, "<lldb_delete>");
|
|
}
|
|
ExportFunctionDefinitionToInterpreter (new_function);
|
|
last_function_call = func_name + " ()";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now instead of trying to really delete the marked lines from user_input, we will just copy all the
|
|
// unmarked lines into a new StringList.
|
|
|
|
StringList new_user_input;
|
|
|
|
for (int i = 0; i < num_lines; ++i)
|
|
{
|
|
std::string current_string = user_input.GetStringAtIndex (i);
|
|
if (current_string.compare (0, 13, "<lldb_delete>") == 0)
|
|
continue;
|
|
|
|
new_user_input.AppendString (current_string.c_str());
|
|
}
|
|
|
|
num_lines = new_user_input.GetSize();
|
|
|
|
if (num_lines > 0)
|
|
{
|
|
if (num_lines == 1
|
|
&& strchr (new_user_input.GetStringAtIndex(0), '\n') == NULL)
|
|
{
|
|
// If there's only one line of input, and it doesn't contain any newline characters....
|
|
callback_data.AppendString (new_user_input.GetStringAtIndex (0));
|
|
}
|
|
else
|
|
{
|
|
// Create the new function name.
|
|
StreamString func_name;
|
|
func_name.Printf ("lldb_bp_callback_func_%d", num_created_functions);
|
|
//std::string func_name = "lldb_bp_callback_func_" + num_created_functions;
|
|
++num_created_functions;
|
|
|
|
// Create the function call for the new function.
|
|
last_function_call = func_name.GetString() + " ()";
|
|
|
|
// Create the Python function definition line (which will have to be inserted at the beginning of
|
|
// the function).
|
|
std::string def_line = "def " + func_name.GetString() + " ():";
|
|
|
|
|
|
// Indent all lines an additional four spaces (as they are now being put inside a function definition).
|
|
for (int i = 0; i < num_lines; ++i)
|
|
{
|
|
const char *temp_cstring = new_user_input.GetStringAtIndex(i);
|
|
std::string temp2 = " ";
|
|
temp2.append(temp_cstring);
|
|
new_user_input.DeleteStringAtIndex (i);
|
|
new_user_input.InsertStringAtIndex (i, temp2.c_str());
|
|
}
|
|
|
|
// Insert the function definition line at the top of the new function.
|
|
new_user_input.InsertStringAtIndex (0, def_line.c_str());
|
|
|
|
ExportFunctionDefinitionToInterpreter (new_user_input);
|
|
callback_data.AppendString (last_function_call.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!last_function_call.empty())
|
|
callback_data.AppendString (last_function_call.c_str());
|
|
}
|
|
|
|
return callback_data.GetSize() > 0;
|
|
}
|
|
|
|
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;
|
|
}
|