[Lldb-commits] [lldb] r115059 - in /lldb/trunk/tools/driver: Driver.cpp Driver.h IOChannel.cpp IOChannel.h

Caroline Tice ctice at apple.com
Wed Sep 29 11:35:42 PDT 2010


Author: ctice
Date: Wed Sep 29 13:35:42 2010
New Revision: 115059

URL: http://llvm.org/viewvc/llvm-project?rev=115059&view=rev
Log:
Fix various timing/threading problems in IOChannel & Driver that
were causing the prompt to be stomped on, mangled or omitted occasionally.


Modified:
    lldb/trunk/tools/driver/Driver.cpp
    lldb/trunk/tools/driver/Driver.h
    lldb/trunk/tools/driver/IOChannel.cpp
    lldb/trunk/tools/driver/IOChannel.h

Modified: lldb/trunk/tools/driver/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/Driver.cpp?rev=115059&r1=115058&r2=115059&view=diff
==============================================================================
--- lldb/trunk/tools/driver/Driver.cpp (original)
+++ lldb/trunk/tools/driver/Driver.cpp Wed Sep 29 13:35:42 2010
@@ -625,24 +625,34 @@
     return error;
 }
 
-void
+size_t
 Driver::GetProcessSTDOUT ()
 {
     //  The process has stuff waiting for stdout; get it and write it out to the appropriate place.
     char stdio_buffer[1024];
     size_t len;
+    size_t total_bytes = 0;
     while ((len = m_debugger.GetSelectedTarget().GetProcess().GetSTDOUT (stdio_buffer, sizeof (stdio_buffer))) > 0)
+    {
         m_io_channel_ap->OutWrite (stdio_buffer, len);
+        total_bytes += len;
+    }
+    return total_bytes;
 }
 
-void
+size_t
 Driver::GetProcessSTDERR ()
 {
     //  The process has stuff waiting for stderr; get it and write it out to the appropriate place.
     char stdio_buffer[1024];
     size_t len;
+    size_t total_bytes = 0;
     while ((len = m_debugger.GetSelectedTarget().GetProcess().GetSTDERR (stdio_buffer, sizeof (stdio_buffer))) > 0)
+    {
         m_io_channel_ap->ErrWrite (stdio_buffer, len);
+        total_bytes += len;
+    }
+    return total_bytes;
 }
 
 void
@@ -721,20 +731,23 @@
     {
         // The process has stdout available, get it and write it out to the
         // appropriate place.
-        GetProcessSTDOUT ();
+        if (GetProcessSTDOUT ())
+            m_io_channel_ap->RefreshPrompt();
     }
     else if (event_type & SBProcess::eBroadcastBitSTDERR)
     {
         // The process has stderr available, get it and write it out to the
         // appropriate place.
-        GetProcessSTDERR ();
+        if (GetProcessSTDERR ())
+            m_io_channel_ap->RefreshPrompt();
     }
     else if (event_type & SBProcess::eBroadcastBitStateChanged)
     {
         // Drain all stout and stderr so we don't see any output come after
         // we print our prompts
-        GetProcessSTDOUT ();
-        GetProcessSTDERR ();
+        if (GetProcessSTDOUT ()
+            || GetProcessSTDERR ())
+            m_io_channel_ap->RefreshPrompt();
 
         // Something changed in the process;  get the event and report the process's current status and location to
         // the user.
@@ -766,8 +779,13 @@
             break;
 
         case eStateExited:
-            m_debugger.HandleCommand("process status");
-            m_io_channel_ap->RefreshPrompt();
+            {
+                SBCommandReturnObject result;
+                m_debugger.GetCommandInterpreter().HandleCommand("process status", result, false);
+                m_io_channel_ap->ErrWrite (result.GetError(), result.GetErrorSize());
+                m_io_channel_ap->OutWrite (result.GetOutput(), result.GetOutputSize());
+                m_io_channel_ap->RefreshPrompt();
+            }
             break;
 
         case eStateStopped:
@@ -781,12 +799,16 @@
                 int message_len = ::snprintf (message, sizeof(message), "Process %d stopped and was programmatically restarted.\n",
                                               process.GetProcessID());
                 m_io_channel_ap->OutWrite(message, message_len);
+                m_io_channel_ap->RefreshPrompt ();
             }
             else
             {
+                SBCommandReturnObject result;
                 UpdateSelectedThread ();
-                m_debugger.HandleCommand("process status");
-                m_io_channel_ap->RefreshPrompt();
+                m_debugger.GetCommandInterpreter().HandleCommand("process status", result, false);
+                m_io_channel_ap->ErrWrite (result.GetError(), result.GetErrorSize());
+                m_io_channel_ap->OutWrite (result.GetOutput(), result.GetOutputSize());
+                m_io_channel_ap->RefreshPrompt ();
             }
             break;
         }

Modified: lldb/trunk/tools/driver/Driver.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/Driver.h?rev=115059&r1=115058&r2=115059&view=diff
==============================================================================
--- lldb/trunk/tools/driver/Driver.h (original)
+++ lldb/trunk/tools/driver/Driver.h Wed Sep 29 13:35:42 2010
@@ -133,10 +133,10 @@
     void
     ResetOptionValues ();
 
-    void
+    size_t
     GetProcessSTDOUT ();
 
-    void
+    size_t
     GetProcessSTDERR ();
 
     void

Modified: lldb/trunk/tools/driver/IOChannel.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/IOChannel.cpp?rev=115059&r1=115058&r2=115059&view=diff
==============================================================================
--- lldb/trunk/tools/driver/IOChannel.cpp (original)
+++ lldb/trunk/tools/driver/IOChannel.cpp Wed Sep 29 13:35:42 2010
@@ -29,6 +29,10 @@
 const char *g_default_prompt = "(lldb) ";
 PromptMap g_prompt_map;
 
+#define NSEC_PER_USEC   1000ull
+#define USEC_PER_SEC    1000000ull
+#define NSEC_PER_SEC    1000000000ull
+
 static const char*
 el_prompt(EditLine *el)
 {
@@ -156,6 +160,8 @@
     Driver *driver
 ) :
     SBBroadcaster ("IOChannel"),
+    m_output_mutex (),
+    m_enter_elgets_time (),
     m_driver (driver),
     m_read_thread (LLDB_INVALID_HOST_THREAD),
     m_read_thread_should_exit (false),
@@ -187,6 +193,25 @@
     ::history (m_history, &m_history_event, H_SETUNIQUE, 1);
     // Load history
     HistorySaveLoad (false);
+
+    // Set up mutex to make sure OutErr, OutWrite and RefreshPrompt do not interfere
+    // with each other when writing.
+
+    int error;
+    ::pthread_mutexattr_t attr;
+    error = ::pthread_mutexattr_init (&attr);
+    assert (error == 0);
+    error = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
+    assert (error == 0);
+    error = ::pthread_mutex_init (&m_output_mutex, &attr);
+    assert (error == 0);
+    error = ::pthread_mutexattr_destroy (&attr);
+    assert (error == 0);
+
+    // Initialize time that ::el_gets was last called.
+
+    m_enter_elgets_time.tv_sec = 0;
+    m_enter_elgets_time.tv_usec = 0;
 }
 
 IOChannel::~IOChannel ()
@@ -205,6 +230,8 @@
         ::el_end (m_edit_line);
         m_edit_line = NULL;
     }
+
+    ::pthread_mutex_destroy (&m_output_mutex);
 }
 
 void
@@ -231,7 +258,23 @@
     if (m_edit_line != NULL)
     {
         int line_len = 0;
+
+        // Set boolean indicating whether or not el_gets is trying to get input (i.e. whether or not to attempt
+        // to refresh the prompt after writing data).
+        SetGettingCommand (true);
+        
+        // Get the current time just before calling el_gets; this is used by OutWrite, ErrWrite, and RefreshPrompt
+        // to make sure they have given el_gets enough time to write the prompt before they attempt to write
+        // anything.
+
+        ::gettimeofday (&m_enter_elgets_time, NULL);
+
+        // Call el_gets to prompt the user and read the user's input.
         const char *line = ::el_gets (m_edit_line, &line_len);
+        
+        // Re-set the boolean indicating whether or not el_gets is trying to get input.
+        SetGettingCommand (false);
+
         if (line)
         {
             // strip any newlines off the end of the string...
@@ -399,6 +442,23 @@
 void
 IOChannel::RefreshPrompt ()
 {
+    // If we are not in the middle of getting input from the user, there is no need to 
+    // refresh the prompt.
+
+    if (! IsGettingCommand())
+        return;
+
+    // Compare the current time versus the last time el_gets was called.  If less than
+    // 10000 microseconds (10000000 nanoseconds) have elapsed, wait 10000 microseconds, to ensure el_gets had time
+    // to finish writing the prompt before we start writing here.
+
+    if (ElapsedNanoSecondsSinceEnteringElGets() < 10000000)
+        usleep (10000);
+
+    // Use the mutex to make sure OutWrite, ErrWrite and Refresh prompt do not interfere with
+    // each other's output.
+
+    IOLocker locker (m_output_mutex);
     ::el_set (m_edit_line, EL_REFRESH);
 }
 
@@ -407,7 +467,20 @@
 {
     if (len == 0)
         return;
-    ::fwrite (buffer, 1, len, m_out_file);
+
+    // Compare the current time versus the last time el_gets was called.  If less than
+    // 10000 microseconds (10000000 nanoseconds) have elapsed, wait 10000 microseconds, to ensure el_gets had time
+    // to finish writing the prompt before we start writing here.
+
+    if (ElapsedNanoSecondsSinceEnteringElGets() < 10000000)
+        usleep (10000);
+
+    {
+        // Use the mutex to make sure OutWrite, ErrWrite and Refresh prompt do not interfere with
+        // each other's output.
+        IOLocker locker (m_output_mutex);
+        ::fwrite (buffer, 1, len, m_out_file);
+    }
 }
 
 void
@@ -415,7 +488,20 @@
 {
     if (len == 0)
         return;
-    ::fwrite (buffer, 1, len, m_err_file);
+
+    // Compare the current time versus the last time el_gets was called.  If less than
+    // 10000 microseconds (10000000 nanoseconds) have elapsed, wait 10000 microseconds, to ensure el_gets had time
+    // to finish writing the prompt before we start writing here.
+
+    if (ElapsedNanoSecondsSinceEnteringElGets() < 10000000)
+        usleep (10000);
+
+    {
+        // Use the mutex to make sure OutWrite, ErrWrite and Refresh prompt do not interfere with
+        // each other's output.
+        IOLocker locker (m_output_mutex);
+        ::fwrite (buffer, 1, len, m_err_file);
+    }
 }
 
 void
@@ -452,3 +538,48 @@
 {
     return m_command_queue.empty();
 }
+
+bool
+IOChannel::IsGettingCommand () const
+{
+    return m_getting_command;
+}
+
+void
+IOChannel::SetGettingCommand (bool new_value)
+{
+    m_getting_command = new_value;
+}
+
+uint64_t
+IOChannel::Nanoseconds (const struct timeval &time_val) const
+{
+    uint64_t nanoseconds = time_val.tv_sec * NSEC_PER_SEC + time_val.tv_usec * NSEC_PER_USEC;
+
+    return nanoseconds;
+}
+
+uint64_t
+IOChannel::ElapsedNanoSecondsSinceEnteringElGets ()
+{
+    if (! IsGettingCommand())
+        return 0;
+
+    struct timeval current_time;
+    ::gettimeofday (&current_time, NULL);
+    return (Nanoseconds (current_time) - Nanoseconds (m_enter_elgets_time));
+}
+
+IOLocker::IOLocker (pthread_mutex_t &mutex) :
+    m_mutex_ptr (&mutex)
+{
+    if (m_mutex_ptr)
+        ::pthread_mutex_lock (m_mutex_ptr);
+        
+}
+
+IOLocker::~IOLocker ()
+{
+    if (m_mutex_ptr)
+        ::pthread_mutex_unlock (m_mutex_ptr);
+}

Modified: lldb/trunk/tools/driver/IOChannel.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/IOChannel.h?rev=115059&r1=115058&r2=115059&view=diff
==============================================================================
--- lldb/trunk/tools/driver/IOChannel.h (original)
+++ lldb/trunk/tools/driver/IOChannel.h Wed Sep 29 13:35:42 2010
@@ -15,6 +15,8 @@
 
 #include <editline/readline.h>
 #include <histedit.h>
+#include <pthread.h>
+#include <sys/time.h>
 
 #include "Driver.h"
 
@@ -89,11 +91,25 @@
     static unsigned char 
     ElCompletionFn (EditLine *e, int ch);
 
+protected:
+
     bool
     IsGettingCommand () const;
 
+    void
+    SetGettingCommand (bool new_value);
+
+    uint64_t
+    Nanoseconds (const struct timeval &time_val) const;
+
+    uint64_t
+    ElapsedNanoSecondsSinceEnteringElGets ();
+    
 private:
 
+    pthread_mutex_t m_output_mutex;
+    struct timeval m_enter_elgets_time;
+
     Driver *m_driver;
     lldb::thread_t m_read_thread;
     bool m_read_thread_should_exit;
@@ -114,4 +130,22 @@
     HandleCompletion (EditLine *e, int ch);
 };
 
+class IOLocker 
+{
+public:
+
+    IOLocker (pthread_mutex_t &mutex);
+
+    ~IOLocker ();
+
+protected:
+
+    pthread_mutex_t *m_mutex_ptr;
+
+private:
+
+    IOLocker (const IOLocker&);
+    const IOLocker& operator= (const IOLocker&);
+};
+
 #endif  // lldb_IOChannel_h_





More information about the lldb-commits mailing list