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:
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "CliContext.h"
|
#include "CliContext.h"
|
||||||
|
|
||||||
|
#include <AutoDeleter.h>
|
||||||
#include <AutoLocker.h>
|
#include <AutoLocker.h>
|
||||||
|
|
||||||
#include "UserInterface.h"
|
#include "UserInterface.h"
|
||||||
@@ -19,6 +20,36 @@
|
|||||||
static CliContext* sCurrentContext;
|
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()
|
CliContext::CliContext()
|
||||||
:
|
:
|
||||||
fLock("CliContext"),
|
fLock("CliContext"),
|
||||||
@@ -31,7 +62,8 @@ CliContext::CliContext()
|
|||||||
fInputLoopWaitingForEvents(0),
|
fInputLoopWaitingForEvents(0),
|
||||||
fEventsOccurred(0),
|
fEventsOccurred(0),
|
||||||
fInputLoopWaiting(false),
|
fInputLoopWaiting(false),
|
||||||
fTerminating(false)
|
fTerminating(false),
|
||||||
|
fCurrentThread(NULL)
|
||||||
{
|
{
|
||||||
sCurrentContext = this;
|
sCurrentContext = this;
|
||||||
}
|
}
|
||||||
@@ -87,6 +119,9 @@ CliContext::Cleanup()
|
|||||||
{
|
{
|
||||||
Terminating();
|
Terminating();
|
||||||
|
|
||||||
|
while (Event* event = fPendingEvents.RemoveHead())
|
||||||
|
delete event;
|
||||||
|
|
||||||
if (fEditLine != NULL) {
|
if (fEditLine != NULL) {
|
||||||
el_end(fEditLine);
|
el_end(fEditLine);
|
||||||
fEditLine = NULL;
|
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*
|
const char*
|
||||||
CliContext::PromptUser(const char* prompt)
|
CliContext::PromptUser(const char* prompt)
|
||||||
{
|
{
|
||||||
@@ -126,6 +196,8 @@ CliContext::PromptUser(const char* prompt)
|
|||||||
|
|
||||||
fPrompt = NULL;
|
fPrompt = NULL;
|
||||||
|
|
||||||
|
ProcessPendingEvents();
|
||||||
|
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,39 +227,124 @@ CliContext::QuitSession(bool killTeam)
|
|||||||
void
|
void
|
||||||
CliContext::WaitForThreadOrUser()
|
CliContext::WaitForThreadOrUser()
|
||||||
{
|
{
|
||||||
|
ProcessPendingEvents();
|
||||||
|
|
||||||
// TODO: Deal with SIGINT as well!
|
// TODO: Deal with SIGINT as well!
|
||||||
for (;;) {
|
for (;;) {
|
||||||
_PrepareToWaitForEvents(
|
_PrepareToWaitForEvents(
|
||||||
EVENT_USER_INTERRUPT | EVENT_THREAD_STATE_CHANGED);
|
EVENT_USER_INTERRUPT | EVENT_THREAD_STOPPED);
|
||||||
|
|
||||||
// check whether there are any threads stopped already
|
// check whether there are any threads stopped already
|
||||||
thread_id stoppedThread = -1;
|
Thread* stoppedThread = NULL;
|
||||||
|
BReference<Thread> stoppedThreadReference;
|
||||||
|
|
||||||
AutoLocker<Team> teamLocker(fTeam);
|
AutoLocker<Team> teamLocker(fTeam);
|
||||||
|
|
||||||
for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator();
|
for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator();
|
||||||
Thread* thread = it.Next();) {
|
Thread* thread = it.Next();) {
|
||||||
if (thread->State() == THREAD_STATE_STOPPED) {
|
if (thread->State() == THREAD_STATE_STOPPED) {
|
||||||
stoppedThread = thread->ID();
|
stoppedThread = thread;
|
||||||
|
stoppedThreadReference.SetTo(thread);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
teamLocker.Unlock();
|
teamLocker.Unlock();
|
||||||
|
|
||||||
if (stoppedThread >= 0)
|
if (stoppedThread != NULL) {
|
||||||
_SignalInputLoop(EVENT_THREAD_STATE_CHANGED);
|
if (fCurrentThread == NULL)
|
||||||
|
fCurrentThread = stoppedThread;
|
||||||
|
|
||||||
|
_SignalInputLoop(EVENT_THREAD_STOPPED);
|
||||||
|
}
|
||||||
|
|
||||||
uint32 events = _WaitForEvents();
|
uint32 events = _WaitForEvents();
|
||||||
if ((events & EVENT_QUIT) != 0 || stoppedThread >= 0)
|
if ((events & EVENT_QUIT) != 0 || stoppedThread != NULL) {
|
||||||
|
ProcessPendingEvents();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ public:
|
|||||||
enum {
|
enum {
|
||||||
EVENT_QUIT = 0x01,
|
EVENT_QUIT = 0x01,
|
||||||
EVENT_USER_INTERRUPT = 0x02,
|
EVENT_USER_INTERRUPT = 0x02,
|
||||||
EVENT_THREAD_STATE_CHANGED = 0x04,
|
EVENT_THREAD_ADDED = 0x04,
|
||||||
|
EVENT_THREAD_REMOVED = 0x08,
|
||||||
|
EVENT_THREAD_STOPPED = 0x10,
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -37,9 +39,18 @@ public:
|
|||||||
|
|
||||||
void Terminating();
|
void Terminating();
|
||||||
|
|
||||||
|
bool IsTerminating() const { return fTerminating; }
|
||||||
|
|
||||||
// service methods for the input loop thread follow
|
// service methods for the input loop thread follow
|
||||||
|
|
||||||
Team* GetTeam() const { return fTeam; }
|
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);
|
const char* PromptUser(const char* prompt);
|
||||||
void AddLineToInputHistory(const char* line);
|
void AddLineToInputHistory(const char* line);
|
||||||
@@ -47,13 +58,24 @@ public:
|
|||||||
void QuitSession(bool killTeam);
|
void QuitSession(bool killTeam);
|
||||||
|
|
||||||
void WaitForThreadOrUser();
|
void WaitForThreadOrUser();
|
||||||
|
void ProcessPendingEvents();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Event;
|
||||||
|
|
||||||
|
typedef DoublyLinkedList<Event> EventList;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Team::Listener
|
// Team::Listener
|
||||||
|
virtual void ThreadAdded(const Team::ThreadEvent& event);
|
||||||
|
virtual void ThreadRemoved(const Team::ThreadEvent& event);
|
||||||
|
|
||||||
virtual void ThreadStateChanged(
|
virtual void ThreadStateChanged(
|
||||||
const Team::ThreadEvent& event);
|
const Team::ThreadEvent& event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void _QueueEvent(Event* event);
|
||||||
|
|
||||||
void _PrepareToWaitForEvents(uint32 eventMask);
|
void _PrepareToWaitForEvents(uint32 eventMask);
|
||||||
uint32 _WaitForEvents();
|
uint32 _WaitForEvents();
|
||||||
void _SignalInputLoop(uint32 events);
|
void _SignalInputLoop(uint32 events);
|
||||||
@@ -72,6 +94,10 @@ private:
|
|||||||
uint32 fEventsOccurred;
|
uint32 fEventsOccurred;
|
||||||
bool fInputLoopWaiting;
|
bool fInputLoopWaiting;
|
||||||
volatile bool fTerminating;
|
volatile bool fTerminating;
|
||||||
|
|
||||||
|
Thread* fCurrentThread;
|
||||||
|
|
||||||
|
EventList fPendingEvents;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -214,9 +214,19 @@ CommandLineUserInterface::_InputLoopEntry(void* data)
|
|||||||
status_t
|
status_t
|
||||||
CommandLineUserInterface::_InputLoop()
|
CommandLineUserInterface::_InputLoop()
|
||||||
{
|
{
|
||||||
|
thread_id currentThread = -1;
|
||||||
|
|
||||||
while (!fTerminating) {
|
while (!fTerminating) {
|
||||||
// Wait for a thread or Ctrl-C.
|
// Wait for a thread or Ctrl-C.
|
||||||
fContext.WaitForThreadOrUser();
|
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
|
// read a command line
|
||||||
const char* line = fContext.PromptUser(kDebuggerPrompt);
|
const char* line = fContext.PromptUser(kDebuggerPrompt);
|
||||||
|
|||||||
Reference in New Issue
Block a user