
to the debugger from GUI windows. Previously there was one global debugger instance that could be accessed that had its own command interpreter and current state (current target/process/thread/frame). When a GUI debugger was attached, if it opened more than one window that each had a console window, there were issues where the last one to setup the global debugger object won and got control of the debugger. To avoid this we now create instances of the lldb_private::Debugger that each has its own state: - target list for targets the debugger instance owns - current process/thread/frame - its own command interpreter - its own input, output and error file handles to avoid conflicts - its own input reader stack So now clients should call: SBDebugger::Initialize(); // (static function) SBDebugger debugger (SBDebugger::Create()); // Use which ever file handles you wish debugger.SetErrorFileHandle (stderr, false); debugger.SetOutputFileHandle (stdout, false); debugger.SetInputFileHandle (stdin, true); // main loop SBDebugger::Terminate(); // (static function) SBDebugger::Initialize() and SBDebugger::Terminate() are ref counted to ensure nothing gets destroyed too early when multiple clients might be attached. Cleaned up the command interpreter and the CommandObject and all subclasses to take more appropriate arguments. llvm-svn: 106615
840 lines
28 KiB
C++
840 lines
28 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"
|
|
|
|
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);
|
|
|
|
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 ("from lldb import *");
|
|
}
|
|
|
|
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 *");
|
|
PyRun_SimpleString ("old_stdin = sys.stdin");
|
|
|
|
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");
|
|
|
|
PyRun_SimpleString ("old_stdout = sys.stdout");
|
|
|
|
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)");
|
|
}
|
|
|
|
|
|
}
|
|
|
|
ScriptInterpreterPython::~ScriptInterpreterPython ()
|
|
{
|
|
PyRun_SimpleString ("sys.stdin = old_stdin");
|
|
PyRun_SimpleString ("sys.stdout = old_stdout");
|
|
Py_Finalize ();
|
|
}
|
|
|
|
void
|
|
ScriptInterpreterPython::ExecuteOneLine (CommandInterpreter &interpreter, const char *command)
|
|
{
|
|
if (command)
|
|
{
|
|
int success;
|
|
|
|
success = PyRun_SimpleString (command);
|
|
if (success != 0)
|
|
interpreter.GetDebugger().GetErrorStream().Printf ("error: python failed attempting to evaluate '%s'\n", command);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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
|
|
FILE *input_fh = reader.GetDebugger().GetInputFileHandle();
|
|
int input_fd = ::fileno (input_fh);
|
|
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)
|
|
{
|
|
::tcsetattr (::fileno (reader.GetDebugger().GetInputFileHandle()),
|
|
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();
|
|
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();
|
|
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);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|