Debugger CliContext: More event handling, current thread

* Introduce the notion of a current thread. That's the one certain
  commands will target (by default).
* Add more event handling in CliContext. There's now a queue of pending
  events which are printed in the input loop at convenient times to
  inform the user about what happened (new/deleted/stopped threads,
  etc.).
This commit is contained in:
Ingo Weinhold 2012-08-05 01:04:44 +02:00
parent 59dbbd147e
commit 86b1039b42
3 changed files with 203 additions and 10 deletions

View File

@ -6,6 +6,7 @@
#include "CliContext.h"
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include "UserInterface.h"
@ -19,6 +20,36 @@
static CliContext* sCurrentContext;
// #pragma mark - Event
struct CliContext::Event : DoublyLinkedListLinkImpl<CliContext::Event> {
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<Thread> 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<BLocker> locker(fLock);
if (fCurrentThread != NULL)
fCurrentThread->ReleaseReference();
fCurrentThread = thread;
if (fCurrentThread != NULL)
fCurrentThread->AcquireReference();
}
void
CliContext::PrintCurrentThread()
{
AutoLocker<Team> 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<Thread> stoppedThreadReference;
AutoLocker<Team> 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<Team> teamLocker(fTeam);
for (;;) {
// get the next event
AutoLocker<BLocker> locker(fLock);
Event* event = fPendingEvents.RemoveHead();
locker.Unlock();
if (event == NULL)
break;
ObjectDeleter<Event> 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<BLocker> locker(fLock);
fPendingEvents.Add(event);
}

View File

@ -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<Event> 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;
};

View File

@ -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);