summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lldb/tools/driver/Driver.cpp42
-rw-r--r--lldb/tools/driver/Driver.h4
-rw-r--r--lldb/tools/driver/IOChannel.cpp135
-rw-r--r--lldb/tools/driver/IOChannel.h34
4 files changed, 201 insertions, 14 deletions
diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp
index c4f4aad4753..8e39eb67633 100644
--- a/lldb/tools/driver/Driver.cpp
+++ b/lldb/tools/driver/Driver.cpp
@@ -625,24 +625,34 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exit)
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 @@ Driver::HandleProcessEvent (const SBEvent &event)
{
// 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 @@ Driver::HandleProcessEvent (const SBEvent &event)
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 @@ Driver::HandleProcessEvent (const SBEvent &event)
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;
}
diff --git a/lldb/tools/driver/Driver.h b/lldb/tools/driver/Driver.h
index d1e77549dab..756b6b0499d 100644
--- a/lldb/tools/driver/Driver.h
+++ b/lldb/tools/driver/Driver.h
@@ -133,10 +133,10 @@ private:
void
ResetOptionValues ();
- void
+ size_t
GetProcessSTDOUT ();
- void
+ size_t
GetProcessSTDERR ();
void
diff --git a/lldb/tools/driver/IOChannel.cpp b/lldb/tools/driver/IOChannel.cpp
index 50fdf730d46..004e1da9140 100644
--- a/lldb/tools/driver/IOChannel.cpp
+++ b/lldb/tools/driver/IOChannel.cpp
@@ -29,6 +29,10 @@ typedef std::map<EditLine *, std::string> PromptMap;
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 @@ IOChannel::IOChannel
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 @@ IOChannel::IOChannel
::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 @@ IOChannel::~IOChannel ()
::el_end (m_edit_line);
m_edit_line = NULL;
}
+
+ ::pthread_mutex_destroy (&m_output_mutex);
}
void
@@ -231,7 +258,23 @@ IOChannel::LibeditGetInput (std::string &new_line)
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 @@ IOChannel::Stop ()
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 @@ IOChannel::OutWrite (const char *buffer, size_t len)
{
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 @@ IOChannel::ErrWrite (const char *buffer, size_t len)
{
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 @@ IOChannel::CommandQueueIsEmpty () const
{
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);
+}
diff --git a/lldb/tools/driver/IOChannel.h b/lldb/tools/driver/IOChannel.h
index 90e76fbbc65..188e2a20975 100644
--- a/lldb/tools/driver/IOChannel.h
+++ b/lldb/tools/driver/IOChannel.h
@@ -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 @@ public:
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 @@ private:
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_
OpenPOWER on IntegriCloud