[Lldb-commits] [lldb] r158124 - in /lldb/trunk: include/lldb/Interpreter/ScriptInterpreter.h include/lldb/Interpreter/ScriptInterpreterNone.h include/lldb/Interpreter/ScriptInterpreterPython.h source/Interpreter/CommandObjectScript.cpp source/Interpreter/ScriptInterpreterNone.cpp source/Interpreter/ScriptInterpreterPython.cpp

Enrico Granata egranata at apple.com
Wed Jun 6 17:17:18 PDT 2012


Author: enrico
Date: Wed Jun  6 19:17:18 2012
New Revision: 158124

URL: http://llvm.org/viewvc/llvm-project?rev=158124&view=rev
Log:
<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

Modified:
    lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h
    lldb/trunk/include/lldb/Interpreter/ScriptInterpreterNone.h
    lldb/trunk/include/lldb/Interpreter/ScriptInterpreterPython.h
    lldb/trunk/source/Interpreter/CommandObjectScript.cpp
    lldb/trunk/source/Interpreter/ScriptInterpreterNone.cpp
    lldb/trunk/source/Interpreter/ScriptInterpreterPython.cpp

Modified: lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h?rev=158124&r1=158123&r2=158124&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h (original)
+++ lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h Wed Jun  6 19:17:18 2012
@@ -118,19 +118,19 @@
     virtual ~ScriptInterpreter ();
 
     virtual bool
-    ExecuteOneLine (const char *command, CommandReturnObject *result) = 0;
+    ExecuteOneLine (const char *command, CommandReturnObject *result, bool enable_io) = 0;
 
     virtual void
     ExecuteInterpreterLoop () = 0;
 
     virtual bool
-    ExecuteOneLineWithReturn (const char *in_string, ScriptReturnType return_type, void *ret_value)
+    ExecuteOneLineWithReturn (const char *in_string, ScriptReturnType return_type, void *ret_value, bool enable_io)
     {
         return true;
     }
 
     virtual bool
-    ExecuteMultipleLines (const char *in_string)
+    ExecuteMultipleLines (const char *in_string, bool enable_io)
     {
         return true;
     }

Modified: lldb/trunk/include/lldb/Interpreter/ScriptInterpreterNone.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Interpreter/ScriptInterpreterNone.h?rev=158124&r1=158123&r2=158124&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Interpreter/ScriptInterpreterNone.h (original)
+++ lldb/trunk/include/lldb/Interpreter/ScriptInterpreterNone.h Wed Jun  6 19:17:18 2012
@@ -23,7 +23,7 @@
     ~ScriptInterpreterNone ();
 
     bool
-    ExecuteOneLine (const char *command, CommandReturnObject *result);
+    ExecuteOneLine (const char *command, CommandReturnObject *result, bool enable_io);
 
     void
     ExecuteInterpreterLoop ();

Modified: lldb/trunk/include/lldb/Interpreter/ScriptInterpreterPython.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Interpreter/ScriptInterpreterPython.h?rev=158124&r1=158123&r2=158124&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Interpreter/ScriptInterpreterPython.h (original)
+++ lldb/trunk/include/lldb/Interpreter/ScriptInterpreterPython.h Wed Jun  6 19:17:18 2012
@@ -39,7 +39,7 @@
     ~ScriptInterpreterPython ();
 
     bool
-    ExecuteOneLine (const char *command, CommandReturnObject *result);
+    ExecuteOneLine (const char *command, CommandReturnObject *result, bool enable_io);
 
     void
     ExecuteInterpreterLoop ();
@@ -47,10 +47,11 @@
     bool
     ExecuteOneLineWithReturn (const char *in_string, 
                               ScriptInterpreter::ScriptReturnType return_type,
-                              void *ret_value);
+                              void *ret_value,
+                              bool enable_io);
 
     bool
-    ExecuteMultipleLines (const char *in_string);
+    ExecuteMultipleLines (const char *in_string, bool enable_io);
 
     bool
     ExportFunctionDefinitionToInterpreter (StringList &function_def);
@@ -257,6 +258,36 @@
     	ScriptInterpreterPython *m_python_interpreter;
     	FILE*                    m_tmp_fh;
 	};
+    
+    class PythonInputReaderManager
+    {
+    public:
+        PythonInputReaderManager (ScriptInterpreterPython *interpreter);
+        
+        operator bool()
+        {
+            return m_error;
+        }
+        
+        ~PythonInputReaderManager();
+        
+    private:
+        
+        static size_t
+        InputReaderCallback (void *baton,
+                                           InputReader &reader,
+                                           lldb::InputReaderAction notification,
+                                           const char *bytes,
+                                           size_t bytes_len);
+        
+        static lldb::thread_result_t
+        RunPythonInputReader (lldb::thread_arg_t baton);
+        
+        ScriptInterpreterPython *m_interpreter;
+        lldb::DebuggerSP m_debugger_sp;
+        lldb::InputReaderSP m_reader_sp;
+        bool m_error;
+    };
 
     static size_t
     InputReaderCallback (void *baton, 

Modified: lldb/trunk/source/Interpreter/CommandObjectScript.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Interpreter/CommandObjectScript.cpp?rev=158124&r1=158123&r2=158124&view=diff
==============================================================================
--- lldb/trunk/source/Interpreter/CommandObjectScript.cpp (original)
+++ lldb/trunk/source/Interpreter/CommandObjectScript.cpp Wed Jun  6 19:17:18 2012
@@ -67,7 +67,7 @@
     }
 
     // We can do better when reporting the status of one-liner script execution.
-    if (script_interpreter->ExecuteOneLine (command, &result))
+    if (script_interpreter->ExecuteOneLine (command, &result, true))
         result.SetStatus(eReturnStatusSuccessFinishNoResult);
     else
         result.SetStatus(eReturnStatusFailed);

Modified: lldb/trunk/source/Interpreter/ScriptInterpreterNone.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Interpreter/ScriptInterpreterNone.cpp?rev=158124&r1=158123&r2=158124&view=diff
==============================================================================
--- lldb/trunk/source/Interpreter/ScriptInterpreterNone.cpp (original)
+++ lldb/trunk/source/Interpreter/ScriptInterpreterNone.cpp Wed Jun  6 19:17:18 2012
@@ -26,7 +26,7 @@
 }
 
 bool
-ScriptInterpreterNone::ExecuteOneLine (const char *command, CommandReturnObject *)
+ScriptInterpreterNone::ExecuteOneLine (const char *command, CommandReturnObject *, bool enable_io)
 {
     m_interpreter.GetDebugger().GetErrorStream().PutCString ("error: there is no embedded script interpreter in this mode.\n");
     return false;

Modified: lldb/trunk/source/Interpreter/ScriptInterpreterPython.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Interpreter/ScriptInterpreterPython.cpp?rev=158124&r1=158123&r2=158124&view=diff
==============================================================================
--- lldb/trunk/source/Interpreter/ScriptInterpreterPython.cpp (original)
+++ lldb/trunk/source/Interpreter/ScriptInterpreterPython.cpp Wed Jun  6 19:17:18 2012
@@ -252,6 +252,253 @@
     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 @@
 }
 
 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 @@
                         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 @@
 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 @@
     
     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 ();
+        { // 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);
+                py_return = PyRun_String (in_string, Py_single_input, globals, locals);
+            }
         }
 
         if (locals != NULL
@@ -956,7 +1211,7 @@
 }
 
 bool
-ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string)
+ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string, bool enable_io)
 {
     
     
@@ -998,7 +1253,10 @@
             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 @@
     // 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 @@
     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 @@
         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 @@
         // 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 @@
         // 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 @@
         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 @@
     
     if (ExecuteOneLineWithReturn (command.c_str(),
                                  ScriptInterpreter::eScriptReturnTypeCharStrOrNone,
-                                 &result_ptr) && result_ptr)
+                                 &result_ptr, false) && result_ptr)
     {
         return std::string(result_ptr);
     }





More information about the lldb-commits mailing list