blob: 6b2e3b2e7ad14a8aa1e1679792dfdef2de824423 [file] [log] [blame]
//===-- 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/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/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()\"):\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 () :
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 = 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 = Debugger::GetSharedInstance().GetOutputFileHandle();
PyObject *pmod = PyImport_ExecCodeModule((char *)"embedded_interpreter", 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 (const std::string& line, FILE *out, FILE *err)
{
int success;
success = PyRun_SimpleString (line.c_str());
if (success != 0)
{
fprintf (err, "error: python failed attempting to evaluate '%s'\n", line.c_str());
}
}
size_t
ScriptInterpreterPython::InputReaderCallback
(
void *baton,
InputReader *reader,
lldb::InputReaderAction notification,
const char *bytes,
size_t bytes_len
)
{
if (baton == NULL)
return 0;
ScriptInterpreterPython *interpreter = (ScriptInterpreterPython *) baton;
switch (notification)
{
case eInputReaderActivate:
{
// Save terminal settings if we can
interpreter->m_termios_valid = ::tcgetattr (::fileno (reader->GetInputFileHandle()),
&interpreter->m_termios) == 0;
struct termios tmp_termios;
if (::tcgetattr (::fileno (reader->GetInputFileHandle()), &tmp_termios) == 0)
{
tmp_termios.c_cc[VEOF] = _POSIX_VDISABLE;
::tcsetattr (::fileno (reader->GetInputFileHandle()), TCSANOW, &tmp_termios);
}
}
break;
case eInputReaderDeactivate:
break;
case eInputReaderReactivate:
break;
case eInputReaderGotToken:
if (bytes && bytes_len)
{
if ((int) bytes[0] == 4)
::write (interpreter->GetMasterFileDescriptor(), "quit()", 6);
else
::write (interpreter->GetMasterFileDescriptor(), bytes, bytes_len);
}
::write (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 (interpreter->m_termios_valid)
{
::tcsetattr (::fileno (reader->GetInputFileHandle()),
TCSANOW,
&interpreter->m_termios);
}
break;
}
return bytes_len;
}
void
ScriptInterpreterPython::ExecuteInterpreterLoop (FILE *out, FILE *err)
{
Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__);
InputReaderSP reader_sp (new InputReader());
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::GetSharedInstance().PushInputReader (reader_sp);
ExecuteOneLine ("run_python_interpreter(ConsoleDict)", out, err);
Debugger::GetSharedInstance().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->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 = Debugger::GetSharedInstance().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)
{
InputReaderSP reader_sp (new InputReader ());
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::GetSharedInstance().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 =
Debugger::GetSharedInstance().GetCommandInterpreter().GetScriptInterpreter()->ExecuteOneLineWithReturn
(python_string,
ScriptInterpreter::eBool,
(void *) &temp_bool);
if (success)
ret_value = temp_bool;
}
return ret_value;
}