diff options
author | Enrico Granata <egranata@apple.com> | 2012-06-07 00:17:18 +0000 |
---|---|---|
committer | Enrico Granata <egranata@apple.com> | 2012-06-07 00:17:18 +0000 |
commit | 79cc6f7a26649cc8812d6c97641fbfe5c37f7dc4 (patch) | |
tree | 0d814e9d7d7b6d48cf71ba0e4ec5a3eec14a1ea4 /lldb/source/Interpreter/ScriptInterpreterPython.cpp | |
parent | 3e8cf2be79396b83969b8363d22c0ce5ecf3cbff (diff) | |
download | bcm5719-llvm-79cc6f7a26649cc8812d6c97641fbfe5c37f7dc4.tar.gz bcm5719-llvm-79cc6f7a26649cc8812d6c97641fbfe5c37f7dc4.zip |
<rdar://problem/11538779> Fixing issues where Python scripts were not able to read user input and/or display output to the user in certain situations - This fix introduces a Python InputReader manager class that mimics the behavior of the interactive interpreter in terms of access to I/O and ensures access to the input and output flows
llvm-svn: 158124
Diffstat (limited to 'lldb/source/Interpreter/ScriptInterpreterPython.cpp')
-rw-r--r-- | lldb/source/Interpreter/ScriptInterpreterPython.cpp | 309 |
1 files changed, 291 insertions, 18 deletions
diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp index 89e5cb3cbd2..e1aa378f138 100644 --- a/lldb/source/Interpreter/ScriptInterpreterPython.cpp +++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -252,6 +252,253 @@ private: bool m_old_value; }; +ScriptInterpreterPython::PythonInputReaderManager::PythonInputReaderManager (ScriptInterpreterPython *interpreter) : +m_interpreter(interpreter), +m_debugger_sp(), +m_reader_sp(), +m_error(false) +{ + if (m_interpreter == NULL) + { + m_error = true; + return; + } + + m_debugger_sp = m_interpreter->GetCommandInterpreter().GetDebugger().shared_from_this(); + + if (!m_debugger_sp) + { + m_error = true; + return; + } + + m_reader_sp = InputReaderSP(new InputReader(*m_debugger_sp.get())); + + if (!m_reader_sp) + { + m_error = true; + return; + } + + Error error (m_reader_sp->Initialize (ScriptInterpreterPython::PythonInputReaderManager::InputReaderCallback, + m_interpreter, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + NULL, // prompt + true)); // echo input + if (error.Fail()) + m_error = true; + else + { + m_debugger_sp->PushInputReader (m_reader_sp); + m_interpreter->m_embedded_thread_input_reader_sp = m_reader_sp; + } +} + +ScriptInterpreterPython::PythonInputReaderManager::~PythonInputReaderManager() +{ + if (m_interpreter) + { + if (m_interpreter->m_embedded_thread_input_reader_sp) + m_interpreter->m_embedded_thread_input_reader_sp->SetIsDone (true); + m_interpreter->m_embedded_python_pty.CloseSlaveFileDescriptor(); + } + + + if (m_reader_sp) + { + m_reader_sp->SetIsDone (true); + if (m_debugger_sp) + m_debugger_sp->PopInputReader(m_reader_sp); + } + + if (m_interpreter) + m_interpreter->m_embedded_thread_input_reader_sp.reset(); +} + +size_t +ScriptInterpreterPython::PythonInputReaderManager::InputReaderCallback +( + void *baton, + InputReader &reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len + ) +{ + lldb::thread_t embedded_interpreter_thread; + LogSP log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT)); + + if (baton == NULL) + return 0; + + ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + + if (script_interpreter->m_script_lang != eScriptLanguagePython) + return 0; + + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + + switch (notification) + { + case eInputReaderActivate: + { + // Save terminal settings if we can + int input_fd = reader.GetDebugger().GetInputFile().GetDescriptor(); + if (input_fd == File::kInvalidDescriptor) + input_fd = STDIN_FILENO; + + script_interpreter->SaveTerminalState(input_fd); + + { + ScriptInterpreterPython::Locker locker(script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + } + + char error_str[1024]; + if (script_interpreter->m_embedded_python_pty.OpenFirstAvailableMaster (O_RDWR|O_NOCTTY, error_str, + sizeof(error_str))) + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, succeeded in opening master pty (fd = %d).", + script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor()); + { + StreamString run_string; + char error_str[1024]; + const char *pty_slave_name = script_interpreter->m_embedded_python_pty.GetSlaveName (error_str, sizeof (error_str)); + if (pty_slave_name != NULL) + { + 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 (); + } + } + embedded_interpreter_thread = Host::ThreadCreate ("<lldb.script-interpreter.noninteractive-python>", + ScriptInterpreterPython::PythonInputReaderManager::RunPythonInputReader, + script_interpreter, NULL); + if (IS_VALID_LLDB_HOST_THREAD(embedded_interpreter_thread)) + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, succeeded in creating thread (thread_t = %p)", embedded_interpreter_thread); + Error detach_error; + Host::ThreadDetach (embedded_interpreter_thread, &detach_error); + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, failed in creating thread"); + reader.SetIsDone (true); + } + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, failed to open master pty "); + reader.SetIsDone (true); + } + } + break; + + case eInputReaderDeactivate: + // When another input reader is pushed, don't leave the session... + //script_interpreter->LeaveSession (); + break; + + case eInputReaderReactivate: + { + // Don't try and acquire the interpreter lock here because code like + // this: + // + // (lldb) script + // >>> v = lldb.frame.EvaluateExpression("collection->get_at_index(12)") + // + // This will cause the process to run. The interpreter lock is taken + // by the input reader for the "script" command. If we try and acquire + // the lock here, when the process runs it might deactivate this input + // reader (if STDIN is hooked up to the inferior process) and + // reactivate it when the process stops which will deadlock. + //ScriptInterpreterPython::Locker locker(script_interpreter, + // ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession, + // ScriptInterpreterPython::Locker::FreeAcquiredLock); + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderInterrupt: + reader.SetIsDone(true); + break; + + case eInputReaderEndOfFile: + reader.SetIsDone(true); + break; + + case eInputReaderGotToken: + if (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor() != -1) + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, GotToken, bytes='%s', byte_len = %lu", bytes, + bytes_len); + if (bytes && bytes_len) + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), bytes, bytes_len); + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "\n", 1); + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, GotToken, bytes='%s', byte_len = %lu, Master File Descriptor is bad.", + bytes, + bytes_len); + reader.SetIsDone (true); + } + + break; + + case eInputReaderDone: + { + StreamString run_string; + char error_str[1024]; + const char *pty_slave_name = script_interpreter->m_embedded_python_pty.GetSlaveName (error_str, sizeof (error_str)); + if (pty_slave_name != NULL) + { + 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(); + + 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(); + } + } + + // Restore terminal settings if they were validly saved + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Done, closing down input reader."); + + script_interpreter->RestoreTerminalState (); + + script_interpreter->m_embedded_python_pty.CloseMasterFileDescriptor(); + break; + } + + return bytes_len; +} + ScriptInterpreterPython::ScriptInterpreterPython (CommandInterpreter &interpreter) : ScriptInterpreter (interpreter, eScriptLanguagePython), m_embedded_python_pty (), @@ -514,7 +761,7 @@ GenerateUniqueName (const char* base_name_wanted, } bool -ScriptInterpreterPython::ExecuteOneLine (const char *command, CommandReturnObject *result) +ScriptInterpreterPython::ExecuteOneLine (const char *command, CommandReturnObject *result, bool enable_io) { if (!m_valid_session) return false; @@ -567,7 +814,11 @@ ScriptInterpreterPython::ExecuteOneLine (const char *command, CommandReturnObjec PyObject *pargs = Py_BuildValue("(Os)",script_interpreter_dict,command); if (pargs != NULL) { - PyObject *pvalue = PyObject_CallObject (pfunc, pargs); + PyObject *pvalue = NULL; + { // scope for PythonInputReaderManager + PythonInputReaderManager py_input(enable_io ? this : NULL); + pvalue = PyObject_CallObject (pfunc, pargs); + } Py_DECREF (pargs); if (pvalue != NULL) { @@ -794,7 +1045,8 @@ ScriptInterpreterPython::ExecuteInterpreterLoop () bool ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string, ScriptInterpreter::ScriptReturnType return_type, - void *ret_value) + void *ret_value, + bool enable_io) { Locker locker(this, @@ -830,14 +1082,17 @@ ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string, 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); + { // scope for PythonInputReaderManager + PythonInputReaderManager py_input(enable_io ? this : 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 (locals != NULL @@ -956,7 +1211,7 @@ ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string, } bool -ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string) +ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string, bool enable_io) { @@ -998,7 +1253,10 @@ ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string) PyCodeObject *compiled_code = PyNode_Compile (compiled_node, "temp.py"); if (compiled_code) { - py_return = PyEval_EvalCode (compiled_code, globals, locals); + { // scope for PythonInputReaderManager + PythonInputReaderManager py_input(enable_io ? this : NULL); + py_return = PyEval_EvalCode (compiled_code, globals, locals); + } if (py_return != NULL) { success = true; @@ -1191,7 +1449,7 @@ ScriptInterpreterPython::ExportFunctionDefinitionToInterpreter (StringList &func // Convert StringList to one long, newline delimited, const char *. std::string function_def_string(function_def.CopyList()); - return ExecuteMultipleLines (function_def_string.c_str()); + return ExecuteMultipleLines (function_def_string.c_str(), false); } bool @@ -1597,6 +1855,19 @@ ScriptInterpreterPython::RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton) return NULL; } +lldb::thread_result_t +ScriptInterpreterPython::PythonInputReaderManager::RunPythonInputReader (lldb::thread_arg_t baton) +{ + ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + + const InputReaderSP reader_sp = script_interpreter->m_embedded_thread_input_reader_sp; + + if (reader_sp) + reader_sp->WaitOnReaderIsDone(); + + return NULL; +} + uint32_t ScriptInterpreterPython::CalculateNumChildren (const lldb::ScriptInterpreterObjectSP& implementor_sp) { @@ -1751,7 +2022,7 @@ ScriptInterpreterPython::LoadScriptingModule (const char* pathname, command_stream.Printf("if not (sys.path.__contains__('%s')):\n sys.path.append('%s');\n\n", directory, directory); - bool syspath_retval = ExecuteMultipleLines(command_stream.GetData()); + bool syspath_retval = ExecuteMultipleLines(command_stream.GetData(), false); if (!syspath_retval) { error.SetErrorString("Python sys.path handling failed"); @@ -1772,7 +2043,7 @@ ScriptInterpreterPython::LoadScriptingModule (const char* pathname, // this call will fail if the module does not exist (because the parameter to it is not a string // but an actual Python module object, which is non-existant if the module was not imported before) bool was_imported = (ExecuteOneLineWithReturn(command_stream.GetData(), - ScriptInterpreterPython::eScriptReturnTypeInt, &refcount) && refcount > 0); + ScriptInterpreterPython::eScriptReturnTypeInt, &refcount, false) && refcount > 0); if (was_imported == true && can_reload == false) { error.SetErrorString("module already imported"); @@ -1782,7 +2053,7 @@ ScriptInterpreterPython::LoadScriptingModule (const char* pathname, // now actually do the import command_stream.Clear(); command_stream.Printf("import %s",basename.c_str()); - bool import_retval = ExecuteOneLine(command_stream.GetData(), NULL); + bool import_retval = ExecuteOneLine(command_stream.GetData(), NULL, false); if (!import_retval) { error.SetErrorString("Python import statement failed"); @@ -1861,6 +2132,8 @@ ScriptInterpreterPython::RunScriptBasedCommand(const char* impl_function, SynchronicityHandler synch_handler(debugger_sp, synchronicity); + PythonInputReaderManager py_input(this); + ret_val = g_swig_call_command (impl_function, m_dictionary_name.c_str(), debugger_sp, @@ -1890,7 +2163,7 @@ ScriptInterpreterPython::GetDocumentationForItem(const char* item) if (ExecuteOneLineWithReturn (command.c_str(), ScriptInterpreter::eScriptReturnTypeCharStrOrNone, - &result_ptr) && result_ptr) + &result_ptr, false) && result_ptr) { return std::string(result_ptr); } |