diff --git a/src/apps/debugger/user_interface/cli/CliContext.cpp b/src/apps/debugger/user_interface/cli/CliContext.cpp index 68b8e77d33..6623300ea1 100644 --- a/src/apps/debugger/user_interface/cli/CliContext.cpp +++ b/src/apps/debugger/user_interface/cli/CliContext.cpp @@ -6,6 +6,7 @@ #include "CliContext.h" +#include #include #include "UserInterface.h" @@ -19,6 +20,36 @@ static CliContext* sCurrentContext; +// #pragma mark - Event + + +struct CliContext::Event : DoublyLinkedListLinkImpl { + Event(int type, Thread* thread = NULL) + : + fType(type), + fThreadReference(thread) + { + } + + int Type() const + { + return fType; + } + + Thread* GetThread() const + { + return fThreadReference.Get(); + } + +private: + int fType; + BReference fThreadReference; +}; + + +// #pragma mark - CliContext + + CliContext::CliContext() : fLock("CliContext"), @@ -31,7 +62,8 @@ CliContext::CliContext() fInputLoopWaitingForEvents(0), fEventsOccurred(0), fInputLoopWaiting(false), - fTerminating(false) + fTerminating(false), + fCurrentThread(NULL) { sCurrentContext = this; } @@ -87,6 +119,9 @@ CliContext::Cleanup() { Terminating(); + while (Event* event = fPendingEvents.RemoveHead()) + delete event; + if (fEditLine != NULL) { el_end(fEditLine); fEditLine = NULL; @@ -116,6 +151,41 @@ CliContext::Terminating() } +thread_id +CliContext::CurrentThreadID() const +{ + return fCurrentThread != NULL ? fCurrentThread->ID() : -1; +} + + +void +CliContext::SetCurrentThread(Thread* thread) +{ + AutoLocker locker(fLock); + + if (fCurrentThread != NULL) + fCurrentThread->ReleaseReference(); + + fCurrentThread = thread; + + if (fCurrentThread != NULL) + fCurrentThread->AcquireReference(); +} + + +void +CliContext::PrintCurrentThread() +{ + AutoLocker teamLocker(fTeam); + + if (fCurrentThread != NULL) { + printf("current thread: %" B_PRId32 " \"%s\"\n", fCurrentThread->ID(), + fCurrentThread->Name()); + } else + printf("no current thread\n"); +} + + const char* CliContext::PromptUser(const char* prompt) { @@ -126,6 +196,8 @@ CliContext::PromptUser(const char* prompt) fPrompt = NULL; + ProcessPendingEvents(); + return line; } @@ -155,39 +227,124 @@ CliContext::QuitSession(bool killTeam) void CliContext::WaitForThreadOrUser() { + ProcessPendingEvents(); + // TODO: Deal with SIGINT as well! for (;;) { _PrepareToWaitForEvents( - EVENT_USER_INTERRUPT | EVENT_THREAD_STATE_CHANGED); + EVENT_USER_INTERRUPT | EVENT_THREAD_STOPPED); // check whether there are any threads stopped already - thread_id stoppedThread = -1; + Thread* stoppedThread = NULL; + BReference stoppedThreadReference; + AutoLocker teamLocker(fTeam); for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator(); Thread* thread = it.Next();) { if (thread->State() == THREAD_STATE_STOPPED) { - stoppedThread = thread->ID(); + stoppedThread = thread; + stoppedThreadReference.SetTo(thread); break; } } teamLocker.Unlock(); - if (stoppedThread >= 0) - _SignalInputLoop(EVENT_THREAD_STATE_CHANGED); + if (stoppedThread != NULL) { + if (fCurrentThread == NULL) + fCurrentThread = stoppedThread; + + _SignalInputLoop(EVENT_THREAD_STOPPED); + } uint32 events = _WaitForEvents(); - if ((events & EVENT_QUIT) != 0 || stoppedThread >= 0) + if ((events & EVENT_QUIT) != 0 || stoppedThread != NULL) { + ProcessPendingEvents(); return; + } } } void -CliContext::ThreadStateChanged(const Team::ThreadEvent& event) +CliContext::ProcessPendingEvents() { - _SignalInputLoop(EVENT_THREAD_STATE_CHANGED); + AutoLocker teamLocker(fTeam); + + for (;;) { + // get the next event + AutoLocker locker(fLock); + Event* event = fPendingEvents.RemoveHead(); + locker.Unlock(); + if (event == NULL) + break; + ObjectDeleter eventDeleter(event); + + // process the event + Thread* thread = event->GetThread(); + + switch (event->Type()) { + case EVENT_QUIT: + case EVENT_USER_INTERRUPT: + break; + case EVENT_THREAD_ADDED: + printf("[new thread: %" B_PRId32 " \"%s\"]\n", thread->ID(), + thread->Name()); + break; + case EVENT_THREAD_REMOVED: + printf("[thread terminated: %" B_PRId32 " \"%s\"]\n", + thread->ID(), thread->Name()); + break; + case EVENT_THREAD_STOPPED: + printf("[thread stopped: %" B_PRId32 " \"%s\"]\n", + thread->ID(), thread->Name()); + break; + } + } +} + + +void +CliContext::ThreadAdded(const Team::ThreadEvent& threadEvent) +{ + _QueueEvent( + new(std::nothrow) Event(EVENT_THREAD_ADDED, threadEvent.GetThread())); + _SignalInputLoop(EVENT_THREAD_ADDED); +} + + +void +CliContext::ThreadRemoved(const Team::ThreadEvent& threadEvent) +{ + _QueueEvent( + new(std::nothrow) Event(EVENT_THREAD_REMOVED, threadEvent.GetThread())); + _SignalInputLoop(EVENT_THREAD_REMOVED); +} + + +void +CliContext::ThreadStateChanged(const Team::ThreadEvent& threadEvent) +{ + if (threadEvent.GetThread()->State() != THREAD_STATE_STOPPED) + return; + + _QueueEvent( + new(std::nothrow) Event(EVENT_THREAD_STOPPED, threadEvent.GetThread())); + _SignalInputLoop(EVENT_THREAD_STOPPED); +} + + +void +CliContext::_QueueEvent(Event* event) +{ + if (event == NULL) { + // no memory -- can't do anything about it + return; + } + + AutoLocker locker(fLock); + fPendingEvents.Add(event); } diff --git a/src/apps/debugger/user_interface/cli/CliContext.h b/src/apps/debugger/user_interface/cli/CliContext.h index 0d5771edab..3a7c98d879 100644 --- a/src/apps/debugger/user_interface/cli/CliContext.h +++ b/src/apps/debugger/user_interface/cli/CliContext.h @@ -24,7 +24,9 @@ public: enum { EVENT_QUIT = 0x01, EVENT_USER_INTERRUPT = 0x02, - EVENT_THREAD_STATE_CHANGED = 0x04, + EVENT_THREAD_ADDED = 0x04, + EVENT_THREAD_REMOVED = 0x08, + EVENT_THREAD_STOPPED = 0x10, }; public: @@ -37,9 +39,18 @@ public: void Terminating(); + bool IsTerminating() const { return fTerminating; } + // service methods for the input loop thread follow Team* GetTeam() const { return fTeam; } + UserInterfaceListener* GetUserInterfaceListener() const + { return fListener; } + + Thread* CurrentThread() const { return fCurrentThread; } + thread_id CurrentThreadID() const; + void SetCurrentThread(Thread* thread); + void PrintCurrentThread(); const char* PromptUser(const char* prompt); void AddLineToInputHistory(const char* line); @@ -47,13 +58,24 @@ public: void QuitSession(bool killTeam); void WaitForThreadOrUser(); + void ProcessPendingEvents(); + +private: + struct Event; + + typedef DoublyLinkedList EventList; private: // Team::Listener + virtual void ThreadAdded(const Team::ThreadEvent& event); + virtual void ThreadRemoved(const Team::ThreadEvent& event); + virtual void ThreadStateChanged( const Team::ThreadEvent& event); private: + void _QueueEvent(Event* event); + void _PrepareToWaitForEvents(uint32 eventMask); uint32 _WaitForEvents(); void _SignalInputLoop(uint32 events); @@ -72,6 +94,10 @@ private: uint32 fEventsOccurred; bool fInputLoopWaiting; volatile bool fTerminating; + + Thread* fCurrentThread; + + EventList fPendingEvents; }; diff --git a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp index 0d1c92b24b..dcf2c6c237 100644 --- a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp +++ b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp @@ -214,9 +214,19 @@ CommandLineUserInterface::_InputLoopEntry(void* data) status_t CommandLineUserInterface::_InputLoop() { + thread_id currentThread = -1; + while (!fTerminating) { // Wait for a thread or Ctrl-C. fContext.WaitForThreadOrUser(); + if (fContext.IsTerminating()) + break; + + // Print the active thread, if it changed. + if (fContext.CurrentThreadID() != currentThread) { + fContext.PrintCurrentThread(); + currentThread = fContext.CurrentThreadID(); + } // read a command line const char* line = fContext.PromptUser(kDebuggerPrompt);