forked from OSchip/llvm-project
896 lines
30 KiB
C++
896 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 (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 (CommandInterpreter &interpreter,
|
|
const char *command,
|
|
CommandReturnObject *result = 0)
|
|
{
|
|
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 (CommandInterpreter &interpreter)
|
|
{
|
|
Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__);
|
|
|
|
Debugger &debugger = 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 (interpreter, "run_python_interpreter(ConsoleDict)");
|
|
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 (CommandInterpreter &interpreter,
|
|
BreakpointOptions *bp_options,
|
|
CommandReturnObject &result)
|
|
{
|
|
Debugger &debugger = 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 (CommandInterpreter &interpreter,
|
|
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;
|
|
}
|