diff options
-rw-r--r-- | lldb/include/lldb/Core/Debugger.h | 4 | ||||
-rw-r--r-- | lldb/include/lldb/Interpreter/CommandInterpreter.h | 1 | ||||
-rw-r--r-- | lldb/include/lldb/Interpreter/ScriptInterpreter.h | 26 | ||||
-rw-r--r-- | lldb/include/lldb/Interpreter/ScriptInterpreterPython.h | 38 | ||||
-rw-r--r-- | lldb/scripts/lldb.swig | 66 | ||||
-rw-r--r-- | lldb/source/Core/Debugger.cpp | 8 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandInterpreter.cpp | 27 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandObjectScript.cpp | 27 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandObjectScript.h | 4 | ||||
-rw-r--r-- | lldb/source/Interpreter/ScriptInterpreter.cpp | 25 | ||||
-rw-r--r-- | lldb/source/Interpreter/ScriptInterpreterPython.cpp | 562 | ||||
-rw-r--r-- | lldb/source/Interpreter/embedded_interpreter.py | 13 | ||||
-rw-r--r-- | lldb/source/lldb.cpp | 5 |
13 files changed, 618 insertions, 188 deletions
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 0787a017520..490c2f3f716 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -166,7 +166,6 @@ public: m_auto_confirm_on = auto_confirm_on; } - protected: void @@ -375,6 +374,9 @@ public: void CleanUpInputReaders (); + static int + TestDebuggerRefCount (); + protected: static void diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h index 865761ffaa8..968c11438c7 100644 --- a/lldb/include/lldb/Interpreter/CommandInterpreter.h +++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h @@ -262,6 +262,7 @@ private: OptionArgMap m_alias_options; // Stores any options (with or without arguments) that go with any alias. std::vector<std::string> m_command_history; std::string m_repeat_command; // Stores the command that will be executed for an empty command string. + std::auto_ptr<ScriptInterpreter> m_script_interpreter_ap; }; diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index ca30baa93aa..da8bce60b7e 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -90,16 +90,23 @@ public: int GetMasterFileDescriptor (); - CommandInterpreter * - GetCommandInterpreter (); + CommandInterpreter & + GetCommandInterpreter (); - static std::string - LanguageToString (lldb::ScriptLanguage); + static std::string + LanguageToString (lldb::ScriptLanguage language); + + static void + Initialize (); + + static void + Terminate (); + + virtual void + ResetOutputFileHandle (FILE *new_fh) { } //By default, do nothing. protected: CommandInterpreter &m_interpreter; - -private: lldb::ScriptLanguage m_script_lang; // Scripting languages may need to use stdin for their interactive loops; @@ -108,8 +115,11 @@ private: // embedded scripting loops. Therefore we need to set up a pseudoterminal and use that // as stdin for the script interpreter interactive loops/prompts. - lldb_utility::PseudoTerminal m_interpreter_pty; - std::string m_pty_slave_name; + lldb_utility::PseudoTerminal m_interpreter_pty; // m_session_pty + std::string m_pty_slave_name; //m_session_pty_slave_name + +private: + }; } // namespace lldb_private diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h index 99ad5c3bf50..0828936099f 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h @@ -11,6 +11,12 @@ #ifndef liblldb_ScriptInterpreterPython_h_ #define liblldb_ScriptInterpreterPython_h_ +#if defined (__APPLE__) +#include <Python/Python.h> +#else +#include <Python.h> +#endif + #include "lldb/lldb-private.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Core/InputReader.h" @@ -70,9 +76,26 @@ public: StringList ReadCommandInputFromUser (FILE *in_file); + virtual void + ResetOutputFileHandle (FILE *new_fh); + static lldb::thread_result_t RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton); + static void + Initialize (); + + static void + Terminate (); + +protected: + + void + EnterSession (); + + void + LeaveSession (); + private: static size_t @@ -81,12 +104,19 @@ private: lldb::InputReaderAction notification, const char *bytes, size_t bytes_len); - - void *m_compiled_module; - struct termios m_termios; - bool m_termios_valid; + + lldb_utility::PseudoTerminal m_embedded_python_pty; lldb::InputReaderSP m_embedded_thread_input_reader_sp; + FILE *m_dbg_stdout; + PyObject *m_new_sysout; + std::string m_dictionary_name; + struct termios m_termios; + bool m_termios_valid; + bool m_session_is_active; + bool m_pty_slave_is_open; + bool m_valid_session; + }; } // namespace lldb_private diff --git a/lldb/scripts/lldb.swig b/lldb/scripts/lldb.swig index 16177f83301..ad938195226 100644 --- a/lldb/scripts/lldb.swig +++ b/lldb/scripts/lldb.swig @@ -1,8 +1,6 @@ /* lldb.swig - Created by Caroline Tice 1/18/2010 - This is the input file for SWIG, to create the appropriate C++ wrappers and functions for various scripting languages, to enable them to call the liblldb Script Bridge functions. @@ -151,6 +149,7 @@ SWIGEXPORT bool LLDBSWIGPythonBreakpointCallbackFunction ( const char *python_function_name, + const char *session_dictionary_name, lldb::SBFrame& sb_frame, lldb::SBBreakpointLocation& sb_bp_loc ) @@ -161,20 +160,70 @@ LLDBSWIGPythonBreakpointCallbackFunction if (Frame_PyObj == NULL || Bp_Loc_PyObj == NULL) return stop_at_breakpoint; + + if (!python_function_name || !session_dictionary_name) + return stop_at_breakpoint; - PyObject *pmodule, *pdict, *pfunc; + PyObject *pmodule, *main_dict, *session_dict, *pfunc; PyObject *pargs, *pvalue; pmodule = PyImport_AddModule ("__main__"); if (pmodule != NULL) { - pdict = PyModule_GetDict (pmodule); - if (pdict != NULL) + main_dict = PyModule_GetDict (pmodule); + if (main_dict != NULL) { - pfunc = PyObject_GetAttrString (pmodule, python_function_name); + PyObject *key, *value; + Py_ssize_t pos = 0; + + // Find the current session's dictionary in the main module's dictionary. + + if (PyDict_Check (main_dict)) + + { + session_dict = NULL; + while (PyDict_Next (main_dict, &pos, &key, &value)) + { + // We have stolen references to the key and value objects in the dictionary; we need to increment + // them now so that Python's garbage collector doesn't collect them out from under us. + Py_INCREF (key); + Py_INCREF (value); + if (strcmp (PyString_AsString (key), session_dictionary_name) == 0) + { + session_dict = value; + break; + } + } + } + + if (!session_dict || !PyDict_Check (session_dict)) + return stop_at_breakpoint; + + // Find the function we need to call in the current session's dictionary. + + pos = 0; + pfunc = NULL; + while (PyDict_Next (session_dict, &pos, &key, &value)) + { + if (PyString_Check (key)) + { + // We have stolen references to the key and value objects in the dictionary; we need to increment + // them now so that Python's garbage collector doesn't collect them out from under us. + Py_INCREF (key); + Py_INCREF (value); + if (strcmp (PyString_AsString (key), python_function_name) == 0) + { + pfunc = value; + break; + } + } + } + + // Set up the arguments and call the function. + if (pfunc && PyCallable_Check (pfunc)) { - pargs = PyTuple_New (2); + pargs = PyTuple_New (3); if (pargs == NULL) { if (PyErr_Occurred()) @@ -184,6 +233,7 @@ LLDBSWIGPythonBreakpointCallbackFunction PyTuple_SetItem (pargs, 0, Frame_PyObj); // This "steals" a reference to Frame_PyObj PyTuple_SetItem (pargs, 1, Bp_Loc_PyObj); // This "steals" a reference to Bp_Loc_PyObj + PyTuple_SetItem (pargs, 2, session_dict); // This "steals" a reference to session_dict pvalue = PyObject_CallObject (pfunc, pargs); Py_DECREF (pargs); @@ -195,7 +245,7 @@ LLDBSWIGPythonBreakpointCallbackFunction { PyErr_Clear(); } - Py_DECREF (pfunc); + Py_INCREF (session_dict); } else if (PyErr_Occurred()) { diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 18dcd82e350..eceaf83091f 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -59,6 +59,12 @@ Debugger::GetSettingsController () return g_settings_controller; } +int +Debugger::TestDebuggerRefCount () +{ + return g_shared_debugger_refcount; +} + void Debugger::Initialize () { @@ -252,6 +258,8 @@ Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) m_output_file.SetFileHandle (fh, tranfer_ownership); if (m_output_file.GetFileHandle() == NULL) m_output_file.SetFileHandle (stdin, false); + + GetCommandInterpreter().GetScriptInterpreter()->ResetOutputFileHandle (fh); } FILE * diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 9e5e6a5e597..129dd611eaf 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -50,6 +50,8 @@ #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterNone.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" using namespace lldb; using namespace lldb_private; @@ -63,7 +65,8 @@ CommandInterpreter::CommandInterpreter Broadcaster ("lldb.command-interpreter"), m_debugger (debugger), m_synchronous_execution (synchronous_execution), - m_skip_lldbinit_files (false) + m_skip_lldbinit_files (false), + m_script_interpreter_ap () { const char *dbg_name = debugger.GetInstanceName().AsCString(); std::string lang_name = ScriptInterpreter::LanguageToString (script_language); @@ -1460,15 +1463,23 @@ CommandInterpreter::SourceInitFile (bool in_cwd, CommandReturnObject &result) ScriptInterpreter * CommandInterpreter::GetScriptInterpreter () { - CommandObject::CommandMap::iterator pos; + if (m_script_interpreter_ap.get() != NULL) + return m_script_interpreter_ap.get(); - pos = m_command_dict.find ("script"); - if (pos != m_command_dict.end()) + lldb::ScriptLanguage script_lang = GetDebugger().GetScriptLanguage(); + switch (script_lang) { - CommandObject *script_cmd_obj = pos->second.get(); - return ((CommandObjectScript *) script_cmd_obj)->GetInterpreter (); - } - return NULL; + case eScriptLanguageNone: + m_script_interpreter_ap.reset (new ScriptInterpreterNone (*this)); + break; + case eScriptLanguagePython: + m_script_interpreter_ap.reset (new ScriptInterpreterPython (*this)); + break; + default: + break; + }; + + return m_script_interpreter_ap.get(); } diff --git a/lldb/source/Interpreter/CommandObjectScript.cpp b/lldb/source/Interpreter/CommandObjectScript.cpp index 8d9f3362473..70a4eadda2f 100644 --- a/lldb/source/Interpreter/CommandObjectScript.cpp +++ b/lldb/source/Interpreter/CommandObjectScript.cpp @@ -17,8 +17,7 @@ #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/ScriptInterpreter.h" -#include "lldb/Interpreter/ScriptInterpreterPython.h" -#include "lldb/Interpreter/ScriptInterpreterNone.h" +#include "lldb/Interpreter/CommandInterpreter.h" using namespace lldb; using namespace lldb_private; @@ -32,8 +31,7 @@ CommandObjectScript::CommandObjectScript (CommandInterpreter &interpreter, Scrip "script", "Pass an expression to the script interpreter for evaluation and return the results. Drop into the interactive interpreter if no expression is given.", "script [<script-expression-for-evaluation>]"), - m_script_lang (script_lang), - m_interpreter_ap () + m_script_lang (script_lang) { } @@ -48,7 +46,7 @@ CommandObjectScript::ExecuteRawCommandString CommandReturnObject &result ) { - ScriptInterpreter *script_interpreter = GetInterpreter (); + ScriptInterpreter *script_interpreter = m_interpreter.GetScriptInterpreter (); if (script_interpreter == NULL) { @@ -88,22 +86,3 @@ CommandObjectScript::Execute return false; } - -ScriptInterpreter * -CommandObjectScript::GetInterpreter () -{ - if (m_interpreter_ap.get() == NULL) - { - switch (m_script_lang) - { - case eScriptLanguagePython: - m_interpreter_ap.reset (new ScriptInterpreterPython (m_interpreter)); - break; - - case eScriptLanguageNone: - m_interpreter_ap.reset (new ScriptInterpreterNone (m_interpreter)); - break; - } - } - return m_interpreter_ap.get(); -} diff --git a/lldb/source/Interpreter/CommandObjectScript.h b/lldb/source/Interpreter/CommandObjectScript.h index eacd3a6b3ea..b9fa759bb7b 100644 --- a/lldb/source/Interpreter/CommandObjectScript.h +++ b/lldb/source/Interpreter/CommandObjectScript.h @@ -42,12 +42,8 @@ public: Execute (Args& command, CommandReturnObject &result); - ScriptInterpreter * - GetInterpreter (); - private: lldb::ScriptLanguage m_script_lang; - std::auto_ptr<ScriptInterpreter> m_interpreter_ap; }; } // namespace lldb_private diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index aedeff705c2..cfc2572997d 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -17,15 +17,17 @@ #include "lldb/Core/Stream.h" #include "lldb/Core/StringList.h" #include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" #include "lldb/Utility/PseudoTerminal.h" using namespace lldb; using namespace lldb_private; -ScriptInterpreter::ScriptInterpreter (CommandInterpreter &interpreter, ScriptLanguage script_lang) : +ScriptInterpreter::ScriptInterpreter (CommandInterpreter &interpreter, lldb::ScriptLanguage script_lang) : m_interpreter (interpreter), m_script_lang (script_lang), - m_interpreter_pty () + m_interpreter_pty (), + m_pty_slave_name () { if (m_interpreter_pty.OpenFirstAvailableMaster (O_RDWR|O_NOCTTY, NULL, 0)) { @@ -40,6 +42,12 @@ ScriptInterpreter::~ScriptInterpreter () m_interpreter_pty.CloseMasterFileDescriptor(); } +CommandInterpreter & +ScriptInterpreter::GetCommandInterpreter () +{ + return m_interpreter; +} + const char * ScriptInterpreter::GetScriptInterpreterPtyName () { @@ -81,3 +89,16 @@ ScriptInterpreter::LanguageToString (lldb::ScriptLanguage language) return return_value; } + +void +ScriptInterpreter::Initialize () +{ + ScriptInterpreterPython::Initialize (); +} + +void +ScriptInterpreter::Terminate () +{ + ScriptInterpreterPython::Terminate (); +} + diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp index a2c31165ae8..c6d4504b170 100644 --- a/lldb/source/Interpreter/ScriptInterpreterPython.cpp +++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -52,6 +52,7 @@ extern "C" bool LLDBSWIGPythonBreakpointCallbackFunction ( const char *python_function_name, + const char *session_dictionary_name, lldb::SBFrame& sb_frame, lldb::SBBreakpointLocation& sb_bp_loc ); @@ -146,10 +147,22 @@ class SimpleREPL(code.InteractiveConsole):\n\ if not more:\n\ break\n\ \n\ + def one_line (self, input):\n\ + line = self.process_input (input)\n\ + more = self.push(line)\n\ + if more:\n\ + self.write (\"Input not a complete line.\")\n\ + self.resetbuffer()\n\ + more = 0\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"; + repl.interact()\n\ +\n\ +def run_one_line (dict, input_string):\n\ + repl = SimpleREPL ('', dict)\n\ + repl.one_line (input_string)\n"; static int _check_and_flush (FILE *stream) @@ -158,146 +171,171 @@ _check_and_flush (FILE *stream) return fflush (stream) || prev_fail ? EOF : 0; } +static Mutex & +GetPythonMutex () +{ + static Mutex g_python_mutex (Mutex::eMutexTypeRecursive); + return g_python_mutex; +} + ScriptInterpreterPython::ScriptInterpreterPython (CommandInterpreter &interpreter) : ScriptInterpreter (interpreter, eScriptLanguagePython), - m_compiled_module (NULL), + m_embedded_python_pty (), + m_embedded_thread_input_reader_sp (), + m_dbg_stdout (interpreter.GetDebugger().GetOutputFileHandle()), + m_new_sysout (NULL), + m_dictionary_name (interpreter.GetDebugger().GetInstanceName().AsCString()), m_termios (), m_termios_valid (false), - m_embedded_python_pty (), - m_embedded_thread_input_reader_sp () + m_session_is_active (false), + m_pty_slave_is_open (false), + m_valid_session (true) { + Mutex::Locker locker (GetPythonMutex()); + + m_dictionary_name.append("_dict"); + StreamString run_string; + run_string.Printf ("%s = dict()", m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + + run_string.Clear(); + run_string.Printf ("run_one_line (%s, 'import sys')", m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + + // Importing 'lldb' module calls SBDebugger::Initialize, which calls Debugger::Initialize, which increments a + // global debugger ref-count; therefore we need to check the ref-count before and after importing lldb, and if the + // ref-count increased we need to call Debugger::Terminate here to decrement the ref-count so that when the final + // call to Debugger::Terminate is made, the ref-count has the correct value. + // + // Bonus question: Why doesn't the ref-count always increase? Because sometimes lldb has already been imported, in + // which case the code inside it, including the call to SBDebugger::Initialize(), does not get executed. + + int old_count = Debugger::TestDebuggerRefCount(); - Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + run_string.Clear(); + run_string.Printf ("run_one_line (%s, 'import lldb')", m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); - // Save terminal settings if we can - int input_fd; - FILE *input_fh = m_interpreter.GetDebugger().GetInputFileHandle(); - if (input_fh != NULL) - input_fd = ::fileno (input_fh); - else - input_fd = STDIN_FILENO; + int new_count = Debugger::TestDebuggerRefCount(); - m_termios_valid = ::tcgetattr (input_fd, &m_termios) == 0; - - // Find the module that owns this code and use that path we get to - // set the PYTHONPATH appropriately. + if (new_count > old_count) + Debugger::Terminate(); - FileSpec file_spec; - char python_dir_path[PATH_MAX]; - if (Host::GetLLDBPath (ePathTypePythonDir, file_spec)) - { - std::string python_path; - const char *curr_python_path = ::getenv ("PYTHONPATH"); - if (curr_python_path) - { - // We have a current value for PYTHONPATH, so lets append to it - python_path.append (curr_python_path); - } + run_string.Clear(); + run_string.Printf ("run_one_line (%s, 'import copy')", m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); - if (file_spec.GetPath(python_dir_path, sizeof (python_dir_path))) - { - if (!python_path.empty()) - python_path.append (1, ':'); - python_path.append (python_dir_path); - } - - if (Host::GetLLDBPath (ePathTypeLLDBShlibDir, file_spec)) - { - if (file_spec.GetPath(python_dir_path, sizeof (python_dir_path))) - { - if (!python_path.empty()) - python_path.append (1, ':'); - python_path.append (python_dir_path); - } - } - const char *pathon_path_env_cstr = python_path.c_str(); - ::setenv ("PYTHONPATH", pathon_path_env_cstr, 1); + run_string.Clear(); + run_string.Printf ("run_one_line (%s, 'lldb.debugger_unique_id = %d')", m_dictionary_name.c_str(), + interpreter.GetDebugger().GetID()); + PyRun_SimpleString (run_string.GetData()); + + if (m_dbg_stdout != NULL) + { + m_new_sysout = PyFile_FromFile (m_dbg_stdout, (char *) "", (char *) "w", _check_and_flush); } +} - Py_Initialize (); - - PyObject *compiled_module = Py_CompileString (embedded_interpreter_string, - "embedded_interpreter.py", - Py_file_input); +ScriptInterpreterPython::~ScriptInterpreterPython () +{ + Debugger &debugger = GetCommandInterpreter().GetDebugger(); - m_compiled_module = static_cast<void*>(compiled_module); + if (m_embedded_thread_input_reader_sp.get() != NULL) + { + m_embedded_thread_input_reader_sp->SetIsDone (true); + m_embedded_python_pty.CloseSlaveFileDescriptor(); + m_pty_slave_is_open = false; + const InputReaderSP reader_sp = m_embedded_thread_input_reader_sp; + m_embedded_thread_input_reader_sp.reset(); + debugger.PopInputReader (reader_sp); + } + + if (m_new_sysout) + { + Mutex::Locker locker(GetPythonMutex()); + Py_DECREF (m_new_sysout); + } +} - // 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 (); +void +ScriptInterpreterPython::ResetOutputFileHandle (FILE *fh) +{ + if (fh == NULL) + return; + + m_dbg_stdout = fh; + Mutex::Locker locker (GetPythonMutex()); - // Update the path python uses to search for modules to include the current directory. + EnterSession (); + m_new_sysout = PyFile_FromFile (m_dbg_stdout, (char *) "", (char *) "w", _check_and_flush); + LeaveSession (); +} - 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"); - } +void +ScriptInterpreterPython::LeaveSession () +{ + m_session_is_active = false; +} - const char *pty_slave_name = GetScriptInterpreterPtyName (); - FILE *out_fh = interpreter.GetDebugger().GetOutputFileHandle(); +void +ScriptInterpreterPython::EnterSession () +{ + // If we have already entered the session, without having officially 'left' it, then there is no need to + // 'enter' it again. - PyObject *pmod = PyImport_ExecCodeModule (const_cast<char*> ("embedded_interpreter"), - static_cast<PyObject*>(m_compiled_module)); + if (m_session_is_active) + return; - 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 *"); - - 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); - } + m_session_is_active = true; - if (PyErr_Occurred()) - PyErr_Clear(); - } + Mutex::Locker locker (GetPythonMutex()); - StreamString run_string; - run_string.Printf ("new_stdin = open('%s', 'r')", pty_slave_name); + PyObject *sysmod = PyImport_AddModule ("sys"); + PyObject *sysdict = PyModule_GetDict (sysmod); + + if ((m_new_sysout != NULL) + && (sysmod != NULL) + && (sysdict != NULL)) + PyDict_SetItemString (sysdict, "stdout", m_new_sysout); + + if (PyErr_Occurred()) + PyErr_Clear (); + + StreamString run_string; + if (!m_pty_slave_is_open) + { + run_string.Printf ("run_one_line (%s, \"new_stdin = open('%s', 'r')\")", m_dictionary_name.c_str(), + m_pty_slave_name.c_str()); PyRun_SimpleString (run_string.GetData()); - PyRun_SimpleString ("sys.stdin = new_stdin"); - + m_pty_slave_is_open = true; + run_string.Clear(); - run_string.Printf ("lldb.debugger_unique_id = %d", interpreter.GetDebugger().GetID()); + run_string.Printf ("run_one_line (%s, 'sys.stdin = new_stdin')", m_dictionary_name.c_str()); PyRun_SimpleString (run_string.GetData()); } +} - // Restore terminal settings if they were validly saved - if (m_termios_valid) - { - ::tcsetattr (input_fd, TCSANOW, &m_termios); - } -} - -ScriptInterpreterPython::~ScriptInterpreterPython () -{ - Py_Finalize (); -} - bool ScriptInterpreterPython::ExecuteOneLine (const char *command, CommandReturnObject *result) { + if (!m_valid_session) + return false; + + EnterSession (); + + Mutex::Locker locker (GetPythonMutex()); + if (command) { int success; - success = PyRun_SimpleString (command); + StreamString sstr; + sstr.Printf ("run_one_line (%s, '%s')", m_dictionary_name.c_str(), command); + success = PyRun_SimpleString (sstr.GetData()); + + LeaveSession (); + if (success == 0) return true; @@ -307,6 +345,7 @@ ScriptInterpreterPython::ExecuteOneLine (const char *command, CommandReturnObjec return false; } + LeaveSession (); if (result) result->AppendError ("empty command passed to python\n"); return false; @@ -329,12 +368,16 @@ ScriptInterpreterPython::InputReaderCallback if (baton == NULL) return 0; - + + ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + + if (script_interpreter->m_script_lang != eScriptLanguagePython) + return 0; + FILE *out_fh = reader.GetDebugger().GetOutputFileHandle (); if (out_fh == NULL) out_fh = stdout; - ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; switch (notification) { case eInputReaderActivate: @@ -384,14 +427,17 @@ ScriptInterpreterPython::InputReaderCallback log->Printf ("ScriptInterpreterPython::InputReaderCallback, Activate, failed to open master pty "); reader.SetIsDone (true); } + script_interpreter->EnterSession (); } break; case eInputReaderDeactivate: + script_interpreter->LeaveSession (); break; case eInputReaderReactivate: + script_interpreter->EnterSession (); break; case eInputReaderInterrupt: @@ -429,10 +475,8 @@ ScriptInterpreterPython::InputReaderCallback 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); + script_interpreter->LeaveSession (); + // Restore terminal settings if they were validly saved if (log) log->Printf ("ScriptInterpreterPython::InputReaderCallback, Done, closing down input reader."); @@ -460,7 +504,7 @@ ScriptInterpreterPython::ExecuteInterpreterLoop () { Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); - Debugger &debugger = m_interpreter.GetDebugger(); + Debugger &debugger = GetCommandInterpreter().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 @@ -493,14 +537,54 @@ ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string, ScriptInterpreter::ReturnType return_type, void *ret_value) { + EnterSession (); + + Mutex::Locker locker (GetPythonMutex()); + PyObject *py_return = NULL; PyObject *mainmod = PyImport_AddModule ("__main__"); PyObject *globals = PyModule_GetDict (mainmod); - PyObject *locals = globals; + PyObject *locals = NULL; PyObject *py_error = NULL; bool ret_success; + bool should_decrement_locals = false; int success; + + if (PyDict_Check (globals)) + { + PyObject *key, *value; + Py_ssize_t pos = 0; + + int i = 0; + while (PyDict_Next (globals, &pos, &key, &value)) + { + // We have stolen references to the key and value objects in the dictionary; we need to increment them now + // so that Python's garbage collector doesn't collect them out from under us. + Py_INCREF (key); + Py_INCREF (value); + char *c_str = PyString_AsString (key); + if (strcmp (c_str, m_dictionary_name.c_str()) == 0) + locals = value; + ++i; + } + } + if (locals == NULL) + { + locals = PyObject_GetAttrString (globals, m_dictionary_name.c_str()); + should_decrement_locals = true; + } + + if (locals == NULL) + { + locals = globals; + should_decrement_locals = false; + } + + py_error = PyErr_Occurred(); + if (py_error != NULL) + PyErr_Clear(); + if (in_string != NULL) { py_return = PyRun_String (in_string, Py_eval_input, globals, locals); @@ -513,6 +597,10 @@ ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string, py_return = PyRun_String (in_string, Py_single_input, globals, locals); } + if (locals != NULL + && should_decrement_locals) + Py_DECREF (locals); + if (py_return != NULL) { switch (return_type) @@ -614,6 +702,8 @@ ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string, PyErr_Clear(); ret_success = false; } + + LeaveSession (); return ret_success; } @@ -621,13 +711,50 @@ ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string, bool ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string) { + EnterSession (); + + Mutex::Locker locker (GetPythonMutex()); + bool success = false; PyObject *py_return = NULL; PyObject *mainmod = PyImport_AddModule ("__main__"); PyObject *globals = PyModule_GetDict (mainmod); - PyObject *locals = globals; + PyObject *locals = NULL; PyObject *py_error = NULL; + bool should_decrement_locals = false; + if (PyDict_Check (globals)) + { + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next (globals, &pos, &key, &value)) + { + // We have stolen references to the key and value objects in the dictionary; we need to increment them now + // so that Python's garbage collector doesn't collect them out from under us. + Py_INCREF (key); + Py_INCREF (value); + if (strcmp (PyString_AsString (key), m_dictionary_name.c_str()) == 0) + locals = value; + } + } + + if (locals == NULL) + { + locals = PyObject_GetAttrString (globals, m_dictionary_name.c_str()); + should_decrement_locals = true; + } + + if (locals == NULL) + { + locals = globals; + should_decrement_locals = false; + } + + py_error = PyErr_Occurred(); + if (py_error != NULL) + PyErr_Clear(); + if (in_string != NULL) { struct _node *compiled_node = PyParser_SimpleParseString (in_string, Py_file_input); @@ -642,6 +769,8 @@ ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string) success = true; Py_DECREF (py_return); } + if (locals && should_decrement_locals) + Py_DECREF (locals); } } } @@ -655,6 +784,8 @@ ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string) success = false; } + LeaveSession (); + return success; } @@ -764,7 +895,8 @@ void ScriptInterpreterPython::CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, CommandReturnObject &result) { - Debugger &debugger = m_interpreter.GetDebugger(); + Debugger &debugger = GetCommandInterpreter().GetDebugger(); + InputReaderSP reader_sp (new InputReader (debugger)); if (reader_sp) @@ -861,8 +993,16 @@ ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (StringList &user // Create the function name & definition string. - sstr.Printf ("def %s (frame, bp_loc):", auto_generated_function_name.c_str()); + sstr.Printf ("def %s (frame, bp_loc, dict):", auto_generated_function_name.c_str()); auto_generated_function.AppendString (sstr.GetData()); + + // Pre-pend code for setting up the session dictionary. + + auto_generated_function.AppendString (" global_dict = globals()"); // Grab the global dictionary + auto_generated_function.AppendString (" new_keys = dict.keys()"); // Make a list of keys in the session dict + auto_generated_function.AppendString (" old_keys = global_dict.keys()"); // Save list of keys in global dict + auto_generated_function.AppendString (" global_dict.update (dict)"); // Add the session dictionary to the + // global dictionary. // Wrap everything up inside the function, increasing the indentation. @@ -873,6 +1013,14 @@ ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (StringList &user auto_generated_function.AppendString (sstr.GetData()); } + // Append code to clean up the global dictionary and update the session dictionary (all updates in the function + // got written to the values in the global dictionary, not the session dictionary). + + auto_generated_function.AppendString (" for key in new_keys:"); // Iterate over all the keys from session dict + auto_generated_function.AppendString (" dict[key] = global_dict[key]"); // Update session dict values + auto_generated_function.AppendString (" if key not in old_keys:"); // If key was not originally in global dict + auto_generated_function.AppendString (" del global_dict[key]"); // ...then remove key/value from global dict + // Verify that the results are valid Python. if (!ExportFunctionDefinitionToInterpreter (auto_generated_function)) @@ -897,6 +1045,21 @@ ScriptInterpreterPython::BreakpointCallbackFunction { BreakpointOptions::CommandData *bp_option_data = (BreakpointOptions::CommandData *) baton; const char *python_function_name = bp_option_data->script_source.GetStringAtIndex (0); + + if (!context) + return true; + + Target *target = context->exe_ctx.target; + + if (!target) + return true; + + Debugger &debugger = target->GetDebugger(); + ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); + ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *) script_interpreter; + + if (!script_interpreter) + return true; if (python_function_name != NULL && python_function_name[0] != '\0') @@ -911,7 +1074,15 @@ ScriptInterpreterPython::BreakpointCallbackFunction SBBreakpointLocation sb_bp_loc (bp_loc_sp); if (sb_bp_loc.IsValid() || sb_frame.IsValid()) - return LLDBSWIGPythonBreakpointCallbackFunction (python_function_name, sb_frame, sb_bp_loc); + { + python_interpreter->EnterSession (); + Mutex::Locker locker (GetPythonMutex()); + bool ret_val = LLDBSWIGPythonBreakpointCallbackFunction(python_function_name, + python_interpreter->m_dictionary_name.c_str(), + sb_frame, sb_bp_loc); + python_interpreter->LeaveSession (); + return ret_val; + } } // We currently always true so we stop in case anything goes wrong when // trying to call the script function @@ -923,6 +1094,8 @@ ScriptInterpreterPython::RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton) { ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + script_interpreter->EnterSession (); + LogSP log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT)); if (log) @@ -932,39 +1105,172 @@ ScriptInterpreterPython::RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton) const char *pty_slave_name = script_interpreter->m_embedded_python_pty.GetSlaveName (error_str, sizeof (error_str)); if (pty_slave_name != NULL) { + Mutex::Locker locker (GetPythonMutex()); + StreamString run_string; - PyRun_SimpleString ("save_stderr = sys.stderr"); - PyRun_SimpleString ("sys.stderr = sys.stdout"); - PyRun_SimpleString ("save_stdin = sys.stdin"); - run_string.Printf ("sys.stdin = open ('%s', 'r')", pty_slave_name); + + run_string.Printf ("run_one_line (%s, 'save_stderr = sys.stderr')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'sys.stderr = sys.stdout')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'save_stdin = sys.stdin')", script_interpreter->m_dictionary_name.c_str()); PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + run_string.Printf ("run_one_line (%s, \"sys.stdin = open ('%s', 'r')\")", script_interpreter->m_dictionary_name.c_str(), + pty_slave_name); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + // The following call drops into the embedded interpreter loop and stays there until the // user chooses to exit from the Python interpreter. - script_interpreter->ExecuteOneLine ("run_python_interpreter(ConsoleDict)", NULL); + + run_string.Printf ("run_python_interpreter (%s)", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'sys.stdin = save_stdin')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear(); - PyRun_SimpleString ("sys.stdin = save_stdin"); - PyRun_SimpleString ("sys.stderr = save_stderr"); + run_string.Printf ("run_one_line (%s, 'sys.stderr = save_stderr')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear(); } if (script_interpreter->m_embedded_thread_input_reader_sp) script_interpreter->m_embedded_thread_input_reader_sp->SetIsDone (true); script_interpreter->m_embedded_python_pty.CloseSlaveFileDescriptor(); + + script_interpreter->m_pty_slave_is_open = false; log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT); if (log) log->Printf ("%p ScriptInterpreterPython::RunEmbeddedPythonInterpreter () thread exiting...", baton); - // Clean up the input reader and make the debugger pop it off the stack. - Debugger &debugger = script_interpreter->m_interpreter.GetDebugger(); + // Clean up the input reader and make the debugger pop it off the stack. + Debugger &debugger = script_interpreter->GetCommandInterpreter().GetDebugger(); const InputReaderSP reader_sp = script_interpreter->m_embedded_thread_input_reader_sp; script_interpreter->m_embedded_thread_input_reader_sp.reset(); debugger.PopInputReader (reader_sp); + + script_interpreter->LeaveSession (); return NULL; } +void +ScriptInterpreterPython::Initialize () +{ + + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + int input_fd = STDIN_FILENO; + + struct termios stdin_termios; + bool valid_termios = ::tcgetattr (input_fd, &stdin_termios) == 0; + + // Find the module that owns this code and use that path we get to + // set the PYTHONPATH appropriately. + FileSpec file_spec; + char python_dir_path[PATH_MAX]; + if (Host::GetLLDBPath (ePathTypePythonDir, file_spec)) + { + std::string python_path; + const char *curr_python_path = ::getenv ("PYTHONPATH"); + if (curr_python_path) + { + // We have a current value for PYTHONPATH, so lets append to it + python_path.append (curr_python_path); + } + + if (file_spec.GetPath(python_dir_path, sizeof (python_dir_path))) + { + if (!python_path.empty()) + python_path.append (1, ':'); + python_path.append (python_dir_path); + } + + if (Host::GetLLDBPath (ePathTypeLLDBShlibDir, file_spec)) + { + if (file_spec.GetPath(python_dir_path, sizeof (python_dir_path))) + { + if (!python_path.empty()) + python_path.append (1, ':'); + python_path.append (python_dir_path); + } + } + const char *pathon_path_env_cstr = python_path.c_str(); + ::setenv ("PYTHONPATH", pathon_path_env_cstr, 1); + } + + Py_Initialize (); + + PyObject *compiled_module = Py_CompileString (embedded_interpreter_string, + "embedded_interpreter.py", + Py_file_input); + + PyObject *py_error = PyErr_Occurred (); + if (py_error != NULL) + { + PyErr_Print(); + PyErr_Clear(); + } + + + // 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 ('.')"); + + PyObject *pmod = NULL; + + if (compiled_module) + { + pmod = PyImport_ExecCodeModule (const_cast<char*> ("embedded_interpreter"), + compiled_module); + Py_DECREF (compiled_module); + } + + if (pmod != NULL) + { + PyRun_SimpleString ("from embedded_interpreter import run_python_interpreter"); + PyRun_SimpleString ("from embedded_interpreter import run_one_line"); + PyRun_SimpleString ("import sys"); + PyRun_SimpleString ("from termios import *"); + Py_DECREF (pmod); + } + + if (valid_termios) + ::tcsetattr (input_fd, TCSANOW, &stdin_termios); +} + +void +ScriptInterpreterPython::Terminate () +{ + // We are intentionally NOT calling Py_Finalize here (this would be the logical place to call it). Calling + // Py_Finalize here causes test suite runs to seg fault: The test suite runs in Python. It registers + // SBDebugger::Terminate to be called 'at_exit'. When the test suite Python harness finishes up, it calls + // Py_Finalize, which calls all the 'at_exit' registered functions. SBDebugger::Terminate calls Debugger::Terminate, + // which calls lldb::Terminate, which calls ScriptInterpreter::Terminate, which calls + // ScriptInterpreterPython::Terminate. So if we call Py_Finalize here, we end up with Py_Finalize being called from + // within Py_Finalize, which results in a seg fault. + // + // Since this function only gets called when lldb is shutting down and going away anyway, the fact that we don't + // actually call Py_Finalize should not cause any problems (everything should shut down/go away anyway when the + // process exits). + // +// Py_Finalize (); +} diff --git a/lldb/source/Interpreter/embedded_interpreter.py b/lldb/source/Interpreter/embedded_interpreter.py index 549283348e5..0e57c1e4aec 100644 --- a/lldb/source/Interpreter/embedded_interpreter.py +++ b/lldb/source/Interpreter/embedded_interpreter.py @@ -84,7 +84,20 @@ class SimpleREPL(code.InteractiveConsole): if not more: break + def one_line (self, input): + line = self.process_input (input) + more = self.push(line) + if more: + self.write ("Input not a complete line.") + self.resetbuffer() + more = 0 + def run_python_interpreter (dict): # Pass in the dictionary, for continuity from one session to the next. repl = SimpleREPL('>>> ', dict) repl.interact() + +def run_one_line (dict, input_string): + repl = SimpleREPL ('', dict) + repl.one_line (input_string) + diff --git a/lldb/source/lldb.cpp b/lldb/source/lldb.cpp index 47f403d3fdf..05c7f4b4517 100644 --- a/lldb/source/lldb.cpp +++ b/lldb/source/lldb.cpp @@ -14,6 +14,7 @@ #include "lldb/Core/Timer.h" #include "lldb/Host/Host.h" #include "lldb/Host/Mutex.h" +#include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" @@ -53,7 +54,7 @@ void lldb_private::Initialize () { // Make sure we inialize only once - static Mutex g_inited_mutex(Mutex::eMutexTypeNormal); + static Mutex g_inited_mutex(Mutex::eMutexTypeRecursive); static bool g_inited = false; Mutex::Locker locker(g_inited_mutex); @@ -75,6 +76,7 @@ lldb_private::Initialize () UnwindAssemblyProfiler_x86::Initialize(); ArchDefaultUnwindPlan_x86::Initialize(); ArchVolatileRegs_x86::Initialize(); + ScriptInterpreter::Initialize (); #ifdef __APPLE__ ABIMacOSX_i386::Initialize(); @@ -114,6 +116,7 @@ lldb_private::Terminate () UnwindAssemblyProfiler_x86::Terminate(); ArchDefaultUnwindPlan_x86::Terminate(); ArchVolatileRegs_x86::Terminate(); + ScriptInterpreter::Terminate (); #ifdef __APPLE__ DynamicLoaderMacOSXDYLD::Terminate(); |