Debugger: Make CliContext use BLooper for message passing
This commit replaces the custom implementation of message passing with BLooper. This commit does not fully transition the code to the BLooper paradigm. WaitForEvent still exists, when, ideally, message passing should be used to notify the waiting class that the results are ready. Change-Id: Id223b9fc0ac1aa9a5a5aeb4af63da04f2cc7574b Reviewed-on: https://review.haiku-os.org/c/haiku/+/6400 Reviewed-by: John Scipione <[email protected]> Reviewed-by: Rene Gollent <[email protected]>
This commit is contained in:
@@ -29,7 +29,7 @@ static CliContext* sCurrentContext;
|
|||||||
|
|
||||||
|
|
||||||
struct CliContext::Event : DoublyLinkedListLinkImpl<CliContext::Event> {
|
struct CliContext::Event : DoublyLinkedListLinkImpl<CliContext::Event> {
|
||||||
Event(int type, Thread* thread = NULL, TeamMemoryBlock* block = NULL,
|
Event(int type, ::Thread* thread = NULL, TeamMemoryBlock* block = NULL,
|
||||||
ExpressionInfo* info = NULL, status_t expressionResult = B_OK,
|
ExpressionInfo* info = NULL, status_t expressionResult = B_OK,
|
||||||
ExpressionResult* expressionValue = NULL)
|
ExpressionResult* expressionValue = NULL)
|
||||||
:
|
:
|
||||||
@@ -47,7 +47,7 @@ struct CliContext::Event : DoublyLinkedListLinkImpl<CliContext::Event> {
|
|||||||
return fType;
|
return fType;
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread* GetThread() const
|
::Thread* GetThread() const
|
||||||
{
|
{
|
||||||
return fThreadReference.Get();
|
return fThreadReference.Get();
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ struct CliContext::Event : DoublyLinkedListLinkImpl<CliContext::Event> {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
int fType;
|
int fType;
|
||||||
BReference<Thread> fThreadReference;
|
BReference< ::Thread> fThreadReference;
|
||||||
BReference<TeamMemoryBlock> fMemoryBlockReference;
|
BReference<TeamMemoryBlock> fMemoryBlockReference;
|
||||||
BReference<ExpressionInfo> fExpressionInfo;
|
BReference<ExpressionInfo> fExpressionInfo;
|
||||||
status_t fExpressionResult;
|
status_t fExpressionResult;
|
||||||
@@ -88,6 +88,7 @@ private:
|
|||||||
|
|
||||||
CliContext::CliContext()
|
CliContext::CliContext()
|
||||||
:
|
:
|
||||||
|
BLooper("CliContext"),
|
||||||
fLock("CliContext"),
|
fLock("CliContext"),
|
||||||
fTeam(NULL),
|
fTeam(NULL),
|
||||||
fListener(NULL),
|
fListener(NULL),
|
||||||
@@ -95,10 +96,8 @@ CliContext::CliContext()
|
|||||||
fEditLine(NULL),
|
fEditLine(NULL),
|
||||||
fHistory(NULL),
|
fHistory(NULL),
|
||||||
fPrompt(NULL),
|
fPrompt(NULL),
|
||||||
fBlockingSemaphore(-1),
|
fWaitForEventSemaphore(-1),
|
||||||
fInputLoopWaitingForEvents(0),
|
fEventOccurred(0),
|
||||||
fEventsOccurred(0),
|
|
||||||
fInputLoopWaiting(false),
|
|
||||||
fTerminating(false),
|
fTerminating(false),
|
||||||
fStoppedThread(NULL),
|
fStoppedThread(NULL),
|
||||||
fCurrentThread(NULL),
|
fCurrentThread(NULL),
|
||||||
@@ -118,14 +117,16 @@ CliContext::~CliContext()
|
|||||||
Cleanup();
|
Cleanup();
|
||||||
sCurrentContext = NULL;
|
sCurrentContext = NULL;
|
||||||
|
|
||||||
if (fBlockingSemaphore >= 0)
|
if (fWaitForEventSemaphore >= 0)
|
||||||
delete_sem(fBlockingSemaphore);
|
delete_sem(fWaitForEventSemaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
CliContext::Init(Team* team, UserInterfaceListener* listener)
|
CliContext::Init(::Team* team, UserInterfaceListener* listener)
|
||||||
{
|
{
|
||||||
|
AutoLocker<BLocker> locker(fLock);
|
||||||
|
|
||||||
fTeam = team;
|
fTeam = team;
|
||||||
fListener = listener;
|
fListener = listener;
|
||||||
|
|
||||||
@@ -135,9 +136,9 @@ CliContext::Init(Team* team, UserInterfaceListener* listener)
|
|||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
fBlockingSemaphore = create_sem(0, "CliContext block");
|
fWaitForEventSemaphore = create_sem(0, "CliContext wait for event");
|
||||||
if (fBlockingSemaphore < 0)
|
if (fWaitForEventSemaphore < 0)
|
||||||
return fBlockingSemaphore;
|
return fWaitForEventSemaphore;
|
||||||
|
|
||||||
fEditLine = el_init("Debugger", stdin, stdout, stderr);
|
fEditLine = el_init("Debugger", stdin, stdout, stderr);
|
||||||
if (fEditLine == NULL)
|
if (fEditLine == NULL)
|
||||||
@@ -171,11 +172,9 @@ CliContext::Init(Team* team, UserInterfaceListener* listener)
|
|||||||
void
|
void
|
||||||
CliContext::Cleanup()
|
CliContext::Cleanup()
|
||||||
{
|
{
|
||||||
|
AutoLocker<BLocker> locker(fLock);
|
||||||
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;
|
||||||
@@ -208,13 +207,16 @@ CliContext::Cleanup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Use the lifecycle methods of BLooper instead
|
||||||
void
|
void
|
||||||
CliContext::Terminating()
|
CliContext::Terminating()
|
||||||
{
|
{
|
||||||
AutoLocker<BLocker> locker(fLock);
|
AutoLocker<BLocker> locker(fLock);
|
||||||
|
|
||||||
fTerminating = true;
|
fTerminating = true;
|
||||||
_SignalInputLoop(EVENT_QUIT);
|
|
||||||
|
BMessage message(MSG_QUIT);
|
||||||
|
PostMessage(&message);
|
||||||
|
|
||||||
// TODO: Signal the input loop, should it be in PromptUser()!
|
// TODO: Signal the input loop, should it be in PromptUser()!
|
||||||
}
|
}
|
||||||
@@ -223,12 +225,13 @@ CliContext::Terminating()
|
|||||||
thread_id
|
thread_id
|
||||||
CliContext::CurrentThreadID() const
|
CliContext::CurrentThreadID() const
|
||||||
{
|
{
|
||||||
|
AutoLocker<BLocker> locker(fLock);
|
||||||
return fCurrentThread != NULL ? fCurrentThread->ID() : -1;
|
return fCurrentThread != NULL ? fCurrentThread->ID() : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CliContext::SetCurrentThread(Thread* thread)
|
CliContext::SetCurrentThread(::Thread* thread)
|
||||||
{
|
{
|
||||||
AutoLocker<BLocker> locker(fLock);
|
AutoLocker<BLocker> locker(fLock);
|
||||||
|
|
||||||
@@ -262,7 +265,8 @@ CliContext::SetCurrentThread(Thread* thread)
|
|||||||
void
|
void
|
||||||
CliContext::PrintCurrentThread()
|
CliContext::PrintCurrentThread()
|
||||||
{
|
{
|
||||||
AutoLocker<Team> teamLocker(fTeam);
|
AutoLocker< ::Team> teamLocker(fTeam);
|
||||||
|
AutoLocker<BLocker> locker(fLock);
|
||||||
|
|
||||||
if (fCurrentThread != NULL) {
|
if (fCurrentThread != NULL) {
|
||||||
printf("current thread: %" B_PRId32 " \"%s\"\n", fCurrentThread->ID(),
|
printf("current thread: %" B_PRId32 " \"%s\"\n", fCurrentThread->ID(),
|
||||||
@@ -294,11 +298,12 @@ status_t
|
|||||||
CliContext::EvaluateExpression(const char* expression,
|
CliContext::EvaluateExpression(const char* expression,
|
||||||
SourceLanguage* language, target_addr_t& address)
|
SourceLanguage* language, target_addr_t& address)
|
||||||
{
|
{
|
||||||
|
AutoLocker<BLocker> locker(fLock);
|
||||||
fExpressionInfo->SetTo(expression);
|
fExpressionInfo->SetTo(expression);
|
||||||
|
|
||||||
fListener->ExpressionEvaluationRequested(
|
fListener->ExpressionEvaluationRequested(
|
||||||
language, fExpressionInfo);
|
language, fExpressionInfo);
|
||||||
WaitForEvents(CliContext::EVENT_EXPRESSION_EVALUATED);
|
_WaitForEvent(MSG_EXPRESSION_EVALUATED);
|
||||||
if (fTerminating)
|
if (fTerminating)
|
||||||
return B_INTERRUPTED;
|
return B_INTERRUPTED;
|
||||||
|
|
||||||
@@ -329,9 +334,10 @@ CliContext::EvaluateExpression(const char* expression,
|
|||||||
status_t
|
status_t
|
||||||
CliContext::GetMemoryBlock(target_addr_t address, TeamMemoryBlock*& block)
|
CliContext::GetMemoryBlock(target_addr_t address, TeamMemoryBlock*& block)
|
||||||
{
|
{
|
||||||
|
AutoLocker<BLocker> locker(fLock);
|
||||||
if (fCurrentBlock == NULL || !fCurrentBlock->Contains(address)) {
|
if (fCurrentBlock == NULL || !fCurrentBlock->Contains(address)) {
|
||||||
GetUserInterfaceListener()->InspectRequested(address, this);
|
GetUserInterfaceListener()->InspectRequested(address, this);
|
||||||
WaitForEvents(CliContext::EVENT_TEAM_MEMORY_BLOCK_RETRIEVED);
|
_WaitForEvent(MSG_TEAM_MEMORY_BLOCK_RETRIEVED);
|
||||||
if (fTerminating)
|
if (fTerminating)
|
||||||
return B_INTERRUPTED;
|
return B_INTERRUPTED;
|
||||||
}
|
}
|
||||||
@@ -351,8 +357,6 @@ CliContext::PromptUser(const char* prompt)
|
|||||||
|
|
||||||
fPrompt = NULL;
|
fPrompt = NULL;
|
||||||
|
|
||||||
ProcessPendingEvents();
|
|
||||||
|
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,14 +372,12 @@ CliContext::AddLineToInputHistory(const char* line)
|
|||||||
void
|
void
|
||||||
CliContext::QuitSession(bool killTeam)
|
CliContext::QuitSession(bool killTeam)
|
||||||
{
|
{
|
||||||
_PrepareToWaitForEvents(EVENT_QUIT);
|
|
||||||
|
|
||||||
fListener->UserInterfaceQuitRequested(
|
fListener->UserInterfaceQuitRequested(
|
||||||
killTeam
|
killTeam
|
||||||
? UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM
|
? UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM
|
||||||
: UserInterfaceListener::QUIT_OPTION_ASK_RESUME_TEAM);
|
: UserInterfaceListener::QUIT_OPTION_ASK_RESUME_TEAM);
|
||||||
|
|
||||||
_WaitForEvents();
|
WaitForEvent(MSG_QUIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -384,19 +386,10 @@ CliContext::WaitForThreadOrUser()
|
|||||||
{
|
{
|
||||||
// TODO: Deal with SIGINT as well!
|
// TODO: Deal with SIGINT as well!
|
||||||
|
|
||||||
// if no thread has been stopped, wait till one is
|
AutoLocker<BLocker> locker(fLock);
|
||||||
while (fStoppedThread == NULL) {
|
|
||||||
_PrepareToWaitForEvents(
|
|
||||||
EVENT_USER_INTERRUPT | EVENT_THREAD_STATE_CHANGED);
|
|
||||||
uint32 events = _WaitForEvents();
|
|
||||||
|
|
||||||
ProcessPendingEvents();
|
while (fStoppedThread == NULL)
|
||||||
// updates fStoppedThread if any thread has been stopped
|
_WaitForEvent(MSG_THREAD_STATE_CHANGED);
|
||||||
|
|
||||||
if ((events & EVENT_QUIT) != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fCurrentThread == NULL)
|
if (fCurrentThread == NULL)
|
||||||
SetCurrentThread(fStoppedThread);
|
SetCurrentThread(fStoppedThread);
|
||||||
@@ -404,116 +397,138 @@ CliContext::WaitForThreadOrUser()
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CliContext::WaitForEvents(int32 eventMask)
|
CliContext::WaitForEvent(uint32 event) {
|
||||||
{
|
AutoLocker<BLocker> locker(fLock);
|
||||||
for (;;) {
|
_WaitForEvent(event);
|
||||||
_PrepareToWaitForEvents(eventMask | EVENT_USER_INTERRUPT);
|
|
||||||
uint32 events = fEventsOccurred;
|
|
||||||
if ((events & eventMask) == 0) {
|
|
||||||
events = _WaitForEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((events & EVENT_QUIT) != 0 || (events & eventMask) != 0) {
|
|
||||||
_SignalInputLoop(eventMask);
|
|
||||||
ProcessPendingEvents();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CliContext::ProcessPendingEvents()
|
CliContext::MessageReceived(BMessage* message)
|
||||||
{
|
{
|
||||||
AutoLocker<Team> teamLocker(fTeam);
|
fLock.Lock();
|
||||||
|
|
||||||
for (;;) {
|
int32 threadID;
|
||||||
// get the next event
|
message->FindInt32("thread", &threadID);
|
||||||
AutoLocker<BLocker> locker(fLock);
|
|
||||||
Event* event = fPendingEvents.RemoveHead();
|
const char* threadName;
|
||||||
locker.Unlock();
|
message->FindString("threadName", &threadName);
|
||||||
if (event == NULL)
|
|
||||||
|
switch (message->what) {
|
||||||
|
case MSG_THREAD_ADDED:
|
||||||
|
printf("[new thread: %" B_PRId32 " \"%s\"]\n", threadID,
|
||||||
|
threadName);
|
||||||
break;
|
break;
|
||||||
ObjectDeleter<Event> eventDeleter(event);
|
case MSG_THREAD_REMOVED:
|
||||||
|
printf("[thread terminated: %" B_PRId32 " \"%s\"]\n",
|
||||||
|
threadID, threadName);
|
||||||
|
break;
|
||||||
|
case MSG_THREAD_STATE_CHANGED:
|
||||||
|
{
|
||||||
|
AutoLocker< ::Team> locker(fTeam);
|
||||||
|
::Thread* thread = fTeam->ThreadByID(threadID);
|
||||||
|
|
||||||
// process the event
|
if (thread->State() == THREAD_STATE_STOPPED) {
|
||||||
Thread* thread = event->GetThread();
|
printf("[thread stopped: %" B_PRId32 " \"%s\"]\n",
|
||||||
|
threadID, threadName);
|
||||||
switch (event->Type()) {
|
fStoppedThread.SetTo(thread);
|
||||||
case EVENT_QUIT:
|
} else {
|
||||||
case EVENT_DEBUG_REPORT_CHANGED:
|
fStoppedThread = NULL;
|
||||||
case EVENT_USER_INTERRUPT:
|
}
|
||||||
break;
|
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_STATE_CHANGED:
|
|
||||||
if (thread->State() == THREAD_STATE_STOPPED) {
|
|
||||||
printf("[thread stopped: %" B_PRId32 " \"%s\"]\n",
|
|
||||||
thread->ID(), thread->Name());
|
|
||||||
fStoppedThread.SetTo(thread);
|
|
||||||
} else {
|
|
||||||
fStoppedThread = NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case EVENT_THREAD_STACK_TRACE_CHANGED:
|
|
||||||
if (thread == fCurrentThread) {
|
|
||||||
fCurrentStackTrace = thread->GetStackTrace();
|
|
||||||
fCurrentStackTrace->AcquireReference();
|
|
||||||
SetCurrentStackFrameIndex(0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case EVENT_TEAM_MEMORY_BLOCK_RETRIEVED:
|
|
||||||
if (fCurrentBlock != NULL) {
|
|
||||||
fCurrentBlock->ReleaseReference();
|
|
||||||
fCurrentBlock = NULL;
|
|
||||||
}
|
|
||||||
fCurrentBlock = event->GetMemoryBlock();
|
|
||||||
break;
|
|
||||||
case EVENT_EXPRESSION_EVALUATED:
|
|
||||||
fExpressionResult = event->GetExpressionResult();
|
|
||||||
if (fExpressionValue != NULL) {
|
|
||||||
fExpressionValue->ReleaseReference();
|
|
||||||
fExpressionValue = NULL;
|
|
||||||
}
|
|
||||||
fExpressionValue = event->GetExpressionValue();
|
|
||||||
if (fExpressionValue != NULL)
|
|
||||||
fExpressionValue->AcquireReference();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
case MSG_THREAD_STACK_TRACE_CHANGED:
|
||||||
|
if (threadID == fCurrentThread->ID()) {
|
||||||
|
AutoLocker< ::Team> locker(fTeam);
|
||||||
|
::Thread* thread = fTeam->ThreadByID(threadID);
|
||||||
|
|
||||||
|
fCurrentStackTrace = thread->GetStackTrace();
|
||||||
|
fCurrentStackTrace->AcquireReference();
|
||||||
|
SetCurrentStackFrameIndex(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MSG_TEAM_MEMORY_BLOCK_RETRIEVED:
|
||||||
|
{
|
||||||
|
TeamMemoryBlock* block = NULL;
|
||||||
|
if (message->FindPointer("block",
|
||||||
|
reinterpret_cast<void **>(&block)) != B_OK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fCurrentBlock != NULL) {
|
||||||
|
fCurrentBlock->ReleaseReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
// reference acquired in MemoryBlockRetrieved
|
||||||
|
fCurrentBlock = block;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MSG_EXPRESSION_EVALUATED:
|
||||||
|
{
|
||||||
|
status_t result;
|
||||||
|
if (message->FindInt32("result", &result) != B_OK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fExpressionResult = result;
|
||||||
|
|
||||||
|
ExpressionResult* value = NULL;
|
||||||
|
message->FindPointer("value", reinterpret_cast<void**>(&value));
|
||||||
|
|
||||||
|
if (fExpressionValue != NULL) {
|
||||||
|
fExpressionValue->ReleaseReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
// reference acquired in ExpressionEvaluated
|
||||||
|
fExpressionValue = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
BLooper::MessageReceived(message);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fEventOccurred = message->what;
|
||||||
|
|
||||||
|
fLock.Unlock();
|
||||||
|
|
||||||
|
release_sem(fWaitForEventSemaphore);
|
||||||
|
// all of the code that was waiting on the semaphore runs
|
||||||
|
acquire_sem(fWaitForEventSemaphore);
|
||||||
|
|
||||||
|
fLock.Lock();
|
||||||
|
fEventOccurred = 0;
|
||||||
|
fLock.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CliContext::ThreadAdded(const Team::ThreadEvent& threadEvent)
|
CliContext::ThreadAdded(const Team::ThreadEvent& threadEvent)
|
||||||
{
|
{
|
||||||
_QueueEvent(
|
BMessage message(MSG_THREAD_ADDED);
|
||||||
new(std::nothrow) Event(EVENT_THREAD_ADDED, threadEvent.GetThread()));
|
message.AddInt32("thread", threadEvent.GetThread()->ID());
|
||||||
_SignalInputLoop(EVENT_THREAD_ADDED);
|
message.AddString("threadName", threadEvent.GetThread()->Name());
|
||||||
|
PostMessage(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CliContext::ThreadRemoved(const Team::ThreadEvent& threadEvent)
|
CliContext::ThreadRemoved(const Team::ThreadEvent& threadEvent)
|
||||||
{
|
{
|
||||||
_QueueEvent(
|
BMessage message(MSG_THREAD_REMOVED);
|
||||||
new(std::nothrow) Event(EVENT_THREAD_REMOVED, threadEvent.GetThread()));
|
message.AddInt32("thread", threadEvent.GetThread()->ID());
|
||||||
_SignalInputLoop(EVENT_THREAD_REMOVED);
|
message.AddString("threadName", threadEvent.GetThread()->Name());
|
||||||
|
PostMessage(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CliContext::ThreadStateChanged(const Team::ThreadEvent& threadEvent)
|
CliContext::ThreadStateChanged(const Team::ThreadEvent& threadEvent)
|
||||||
{
|
{
|
||||||
_QueueEvent(
|
BMessage message(MSG_THREAD_STATE_CHANGED);
|
||||||
new(std::nothrow) Event(EVENT_THREAD_STATE_CHANGED, threadEvent.GetThread()));
|
message.AddInt32("thread", threadEvent.GetThread()->ID());
|
||||||
_SignalInputLoop(EVENT_THREAD_STATE_CHANGED);
|
message.AddString("threadName", threadEvent.GetThread()->Name());
|
||||||
|
PostMessage(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -523,10 +538,10 @@ CliContext::ThreadStackTraceChanged(const Team::ThreadEvent& threadEvent)
|
|||||||
if (threadEvent.GetThread()->State() != THREAD_STATE_STOPPED)
|
if (threadEvent.GetThread()->State() != THREAD_STATE_STOPPED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_QueueEvent(
|
BMessage message(MSG_THREAD_STACK_TRACE_CHANGED);
|
||||||
new(std::nothrow) Event(EVENT_THREAD_STACK_TRACE_CHANGED,
|
message.AddInt32("thread", threadEvent.GetThread()->ID());
|
||||||
threadEvent.GetThread()));
|
message.AddString("threadName", threadEvent.GetThread()->Name());
|
||||||
_SignalInputLoop(EVENT_THREAD_STACK_TRACE_CHANGED);
|
PostMessage(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -534,10 +549,15 @@ void
|
|||||||
CliContext::ExpressionEvaluated(ExpressionInfo* info, status_t result,
|
CliContext::ExpressionEvaluated(ExpressionInfo* info, status_t result,
|
||||||
ExpressionResult* value)
|
ExpressionResult* value)
|
||||||
{
|
{
|
||||||
_QueueEvent(
|
BMessage message(MSG_EXPRESSION_EVALUATED);
|
||||||
new(std::nothrow) Event(EVENT_EXPRESSION_EVALUATED,
|
message.AddInt32("result", result);
|
||||||
NULL, NULL, info, result, value));
|
|
||||||
_SignalInputLoop(EVENT_EXPRESSION_EVALUATED);
|
if (value != NULL) {
|
||||||
|
value->AcquireReference();
|
||||||
|
message.AddPointer("value", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
PostMessage(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -551,9 +571,6 @@ CliContext::DebugReportChanged(const Team::DebugReportEvent& event)
|
|||||||
fprintf(stderr, "Failed to write debug report: %s\n", strerror(
|
fprintf(stderr, "Failed to write debug report: %s\n", strerror(
|
||||||
event.GetFinalStatus()));
|
event.GetFinalStatus()));
|
||||||
}
|
}
|
||||||
|
|
||||||
_QueueEvent(new(std::nothrow) Event(EVENT_DEBUG_REPORT_CHANGED));
|
|
||||||
_SignalInputLoop(EVENT_DEBUG_REPORT_CHANGED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -562,19 +579,18 @@ CliContext::CoreFileChanged(const Team::CoreFileChangedEvent& event)
|
|||||||
{
|
{
|
||||||
printf("Successfully saved core file to %s\n",
|
printf("Successfully saved core file to %s\n",
|
||||||
event.GetTargetPath());
|
event.GetTargetPath());
|
||||||
|
|
||||||
_QueueEvent(new(std::nothrow) Event(EVENT_CORE_FILE_CHANGED));
|
|
||||||
_SignalInputLoop(EVENT_CORE_FILE_CHANGED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CliContext::MemoryBlockRetrieved(TeamMemoryBlock* block)
|
CliContext::MemoryBlockRetrieved(TeamMemoryBlock* block)
|
||||||
{
|
{
|
||||||
_QueueEvent(
|
if (block != NULL)
|
||||||
new(std::nothrow) Event(EVENT_TEAM_MEMORY_BLOCK_RETRIEVED,
|
block->AcquireReference();
|
||||||
NULL, block));
|
|
||||||
_SignalInputLoop(EVENT_TEAM_MEMORY_BLOCK_RETRIEVED);
|
BMessage message(MSG_TEAM_MEMORY_BLOCK_RETRIEVED);
|
||||||
|
message.AddPointer("block", block);
|
||||||
|
PostMessage(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -582,92 +598,32 @@ void
|
|||||||
CliContext::ValueNodeChanged(ValueNodeChild* nodeChild, ValueNode* oldNode,
|
CliContext::ValueNodeChanged(ValueNodeChild* nodeChild, ValueNode* oldNode,
|
||||||
ValueNode* newNode)
|
ValueNode* newNode)
|
||||||
{
|
{
|
||||||
_SignalInputLoop(EVENT_VALUE_NODE_CHANGED);
|
BMessage message(MSG_VALUE_NODE_CHANGED);
|
||||||
|
PostMessage(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CliContext::ValueNodeChildrenCreated(ValueNode* node)
|
CliContext::ValueNodeChildrenCreated(ValueNode* node)
|
||||||
{
|
{
|
||||||
_SignalInputLoop(EVENT_VALUE_NODE_CHANGED);
|
BMessage message(MSG_VALUE_NODE_CHANGED);
|
||||||
|
PostMessage(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CliContext::ValueNodeChildrenDeleted(ValueNode* node)
|
CliContext::ValueNodeChildrenDeleted(ValueNode* node)
|
||||||
{
|
{
|
||||||
_SignalInputLoop(EVENT_VALUE_NODE_CHANGED);
|
BMessage message(MSG_VALUE_NODE_CHANGED);
|
||||||
|
PostMessage(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CliContext::ValueNodeValueChanged(ValueNode* oldNode)
|
CliContext::ValueNodeValueChanged(ValueNode* oldNode)
|
||||||
{
|
{
|
||||||
_SignalInputLoop(EVENT_VALUE_NODE_CHANGED);
|
BMessage message(MSG_VALUE_NODE_CHANGED);
|
||||||
}
|
PostMessage(&message);
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
CliContext::_QueueEvent(Event* event)
|
|
||||||
{
|
|
||||||
if (event == NULL) {
|
|
||||||
// no memory -- can't do anything about it
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoLocker<BLocker> locker(fLock);
|
|
||||||
fPendingEvents.Add(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
CliContext::_PrepareToWaitForEvents(uint32 eventMask)
|
|
||||||
{
|
|
||||||
// Set the events we're going to wait for -- always wait for "quit".
|
|
||||||
AutoLocker<BLocker> locker(fLock);
|
|
||||||
fInputLoopWaitingForEvents = eventMask | EVENT_QUIT;
|
|
||||||
fEventsOccurred = fTerminating ? EVENT_QUIT : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint32
|
|
||||||
CliContext::_WaitForEvents()
|
|
||||||
{
|
|
||||||
AutoLocker<BLocker> locker(fLock);
|
|
||||||
|
|
||||||
if (fEventsOccurred == 0) {
|
|
||||||
sem_id blockingSemaphore = fBlockingSemaphore;
|
|
||||||
fInputLoopWaiting = true;
|
|
||||||
|
|
||||||
locker.Unlock();
|
|
||||||
|
|
||||||
while (acquire_sem(blockingSemaphore) == B_INTERRUPTED) {
|
|
||||||
}
|
|
||||||
|
|
||||||
locker.Lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 events = fEventsOccurred;
|
|
||||||
fEventsOccurred = 0;
|
|
||||||
return events;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
CliContext::_SignalInputLoop(uint32 events)
|
|
||||||
{
|
|
||||||
AutoLocker<BLocker> locker(fLock);
|
|
||||||
|
|
||||||
if ((fInputLoopWaitingForEvents & events) == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
fEventsOccurred = fInputLoopWaitingForEvents & events;
|
|
||||||
fInputLoopWaitingForEvents = 0;
|
|
||||||
|
|
||||||
if (fInputLoopWaiting) {
|
|
||||||
fInputLoopWaiting = false;
|
|
||||||
release_sem(fBlockingSemaphore);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -676,3 +632,19 @@ CliContext::_GetPrompt(EditLine* editLine)
|
|||||||
{
|
{
|
||||||
return sCurrentContext != NULL ? sCurrentContext->fPrompt : NULL;
|
return sCurrentContext != NULL ? sCurrentContext->fPrompt : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
CliContext::_WaitForEvent(uint32 event) {
|
||||||
|
if (fTerminating)
|
||||||
|
return;
|
||||||
|
|
||||||
|
do {
|
||||||
|
fLock.Unlock();
|
||||||
|
while (acquire_sem(fWaitForEventSemaphore) == B_INTERRUPTED) {
|
||||||
|
}
|
||||||
|
fLock.Lock();
|
||||||
|
release_sem(fWaitForEventSemaphore);
|
||||||
|
} while (fEventOccurred != event && !fTerminating);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <histedit.h>
|
#include <histedit.h>
|
||||||
|
|
||||||
#include <Locker.h>
|
#include <Locker.h>
|
||||||
|
#include <Looper.h>
|
||||||
|
|
||||||
#include "ExpressionInfo.h"
|
#include "ExpressionInfo.h"
|
||||||
#include "Team.h"
|
#include "Team.h"
|
||||||
@@ -31,27 +32,28 @@ class ValueNodeManager;
|
|||||||
class CliContext : private Team::Listener,
|
class CliContext : private Team::Listener,
|
||||||
public TeamMemoryBlock::Listener,
|
public TeamMemoryBlock::Listener,
|
||||||
public ExpressionInfo::Listener,
|
public ExpressionInfo::Listener,
|
||||||
private ValueNodeContainer::Listener {
|
private ValueNodeContainer::Listener,
|
||||||
|
public BLooper {
|
||||||
public:
|
public:
|
||||||
enum {
|
enum {
|
||||||
EVENT_QUIT = 0x01,
|
MSG_QUIT = 'quit',
|
||||||
EVENT_USER_INTERRUPT = 0x02,
|
MSG_USER_INTERRUPT = 'uint',
|
||||||
EVENT_THREAD_ADDED = 0x04,
|
MSG_THREAD_ADDED = 'thad',
|
||||||
EVENT_THREAD_REMOVED = 0x08,
|
MSG_THREAD_REMOVED = 'thar',
|
||||||
EVENT_THREAD_STATE_CHANGED = 0x10,
|
MSG_THREAD_STATE_CHANGED = 'tsch',
|
||||||
EVENT_THREAD_STACK_TRACE_CHANGED = 0x20,
|
MSG_THREAD_STACK_TRACE_CHANGED = 'tstc',
|
||||||
EVENT_VALUE_NODE_CHANGED = 0x40,
|
MSG_VALUE_NODE_CHANGED = 'vnch',
|
||||||
EVENT_TEAM_MEMORY_BLOCK_RETRIEVED = 0x80,
|
MSG_TEAM_MEMORY_BLOCK_RETRIEVED = 'tmbr',
|
||||||
EVENT_EXPRESSION_EVALUATED = 0x100,
|
MSG_EXPRESSION_EVALUATED = 'exev',
|
||||||
EVENT_DEBUG_REPORT_CHANGED = 0x200,
|
MSG_DEBUG_REPORT_CHANGED = 'drch',
|
||||||
EVENT_CORE_FILE_CHANGED = 0x400
|
MSG_CORE_FILE_CHANGED = 'cfch'
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CliContext();
|
CliContext();
|
||||||
~CliContext();
|
~CliContext();
|
||||||
|
|
||||||
status_t Init(Team* team,
|
status_t Init(::Team* team,
|
||||||
UserInterfaceListener* listener);
|
UserInterfaceListener* listener);
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
|
|
||||||
@@ -59,9 +61,11 @@ public:
|
|||||||
|
|
||||||
bool IsTerminating() const { return fTerminating; }
|
bool IsTerminating() const { return fTerminating; }
|
||||||
|
|
||||||
|
virtual void MessageReceived(BMessage* message);
|
||||||
|
|
||||||
// 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
|
UserInterfaceListener* GetUserInterfaceListener() const
|
||||||
{ return fListener; }
|
{ return fListener; }
|
||||||
ValueNodeManager* GetValueNodeManager() const
|
ValueNodeManager* GetValueNodeManager() const
|
||||||
@@ -69,9 +73,9 @@ public:
|
|||||||
StackTrace* GetStackTrace() const
|
StackTrace* GetStackTrace() const
|
||||||
{ return fCurrentStackTrace; }
|
{ return fCurrentStackTrace; }
|
||||||
|
|
||||||
Thread* CurrentThread() const { return fCurrentThread; }
|
::Thread* CurrentThread() const { return fCurrentThread; }
|
||||||
thread_id CurrentThreadID() const;
|
thread_id CurrentThreadID() const;
|
||||||
void SetCurrentThread(Thread* thread);
|
void SetCurrentThread(::Thread* thread);
|
||||||
void PrintCurrentThread();
|
void PrintCurrentThread();
|
||||||
|
|
||||||
int32 CurrentStackFrameIndex() const
|
int32 CurrentStackFrameIndex() const
|
||||||
@@ -90,8 +94,7 @@ public:
|
|||||||
void QuitSession(bool killTeam);
|
void QuitSession(bool killTeam);
|
||||||
|
|
||||||
void WaitForThreadOrUser();
|
void WaitForThreadOrUser();
|
||||||
void WaitForEvents(int32 eventMask);
|
void WaitForEvent(uint32 event);
|
||||||
void ProcessPendingEvents();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Event;
|
struct Event;
|
||||||
@@ -129,30 +132,23 @@ private:
|
|||||||
virtual void ValueNodeValueChanged(ValueNode* node);
|
virtual void ValueNodeValueChanged(ValueNode* node);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _QueueEvent(Event* event);
|
|
||||||
|
|
||||||
void _PrepareToWaitForEvents(uint32 eventMask);
|
|
||||||
uint32 _WaitForEvents();
|
|
||||||
void _SignalInputLoop(uint32 events);
|
|
||||||
|
|
||||||
static const char* _GetPrompt(EditLine* editLine);
|
static const char* _GetPrompt(EditLine* editLine);
|
||||||
|
void _WaitForEvent(uint32 event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BLocker fLock;
|
mutable BLocker fLock;
|
||||||
Team* fTeam;
|
::Team* fTeam;
|
||||||
UserInterfaceListener* fListener;
|
UserInterfaceListener* fListener;
|
||||||
ValueNodeManager* fNodeManager;
|
ValueNodeManager* fNodeManager;
|
||||||
EditLine* fEditLine;
|
EditLine* fEditLine;
|
||||||
History* fHistory;
|
History* fHistory;
|
||||||
const char* fPrompt;
|
const char* fPrompt;
|
||||||
sem_id fBlockingSemaphore;
|
sem_id fWaitForEventSemaphore;
|
||||||
uint32 fInputLoopWaitingForEvents;
|
uint32 fEventOccurred;
|
||||||
uint32 fEventsOccurred;
|
|
||||||
bool fInputLoopWaiting;
|
|
||||||
volatile bool fTerminating;
|
volatile bool fTerminating;
|
||||||
|
|
||||||
BReference<Thread> fStoppedThread;
|
BReference< ::Thread> fStoppedThread;
|
||||||
Thread* fCurrentThread;
|
::Thread* fCurrentThread;
|
||||||
StackTrace* fCurrentStackTrace;
|
StackTrace* fCurrentStackTrace;
|
||||||
int32 fCurrentStackFrameIndex;
|
int32 fCurrentStackFrameIndex;
|
||||||
TeamMemoryBlock* fCurrentBlock;
|
TeamMemoryBlock* fCurrentBlock;
|
||||||
@@ -160,8 +156,6 @@ private:
|
|||||||
ExpressionInfo* fExpressionInfo;
|
ExpressionInfo* fExpressionInfo;
|
||||||
status_t fExpressionResult;
|
status_t fExpressionResult;
|
||||||
ExpressionResult* fExpressionValue;
|
ExpressionResult* fExpressionValue;
|
||||||
|
|
||||||
EventList fPendingEvents;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ private:
|
|||||||
|
|
||||||
CommandLineUserInterface::CommandLineUserInterface()
|
CommandLineUserInterface::CommandLineUserInterface()
|
||||||
:
|
:
|
||||||
|
fContext(new CliContext()),
|
||||||
fCommands(20, true),
|
fCommands(20, true),
|
||||||
fShowSemaphore(-1),
|
fShowSemaphore(-1),
|
||||||
fShown(false),
|
fShown(false),
|
||||||
@@ -122,7 +123,7 @@ CommandLineUserInterface::ID() const
|
|||||||
status_t
|
status_t
|
||||||
CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
|
CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
|
||||||
{
|
{
|
||||||
status_t error = fContext.Init(team, listener);
|
status_t error = fContext->Init(team, listener);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@@ -152,7 +153,7 @@ CommandLineUserInterface::Terminate()
|
|||||||
fTerminating = true;
|
fTerminating = true;
|
||||||
|
|
||||||
if (fShown) {
|
if (fShown) {
|
||||||
fContext.Terminating();
|
fContext->Terminating();
|
||||||
|
|
||||||
// Wait for input loop to finish.
|
// Wait for input loop to finish.
|
||||||
while (acquire_sem(fShowSemaphore) == B_INTERRUPTED) {
|
while (acquire_sem(fShowSemaphore) == B_INTERRUPTED) {
|
||||||
@@ -163,7 +164,10 @@ CommandLineUserInterface::Terminate()
|
|||||||
fShowSemaphore = -1;
|
fShowSemaphore = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fContext.Cleanup();
|
fContext->Cleanup();
|
||||||
|
|
||||||
|
BMessage message(B_QUIT_REQUESTED);
|
||||||
|
fContext->PostMessage(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -236,6 +240,7 @@ CommandLineUserInterface::Run()
|
|||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
fContext->Run();
|
||||||
_InputLoop();
|
_InputLoop();
|
||||||
// Release the Show() semaphore to signal Terminate().
|
// Release the Show() semaphore to signal Terminate().
|
||||||
release_sem(fShowSemaphore);
|
release_sem(fShowSemaphore);
|
||||||
@@ -248,21 +253,19 @@ CommandLineUserInterface::_InputLoop()
|
|||||||
thread_id currentThread = -1;
|
thread_id currentThread = -1;
|
||||||
|
|
||||||
while (!fTerminating) {
|
while (!fTerminating) {
|
||||||
fContext.ProcessPendingEvents();
|
|
||||||
|
|
||||||
// Wait for a thread or Ctrl-C.
|
// Wait for a thread or Ctrl-C.
|
||||||
fContext.WaitForThreadOrUser();
|
fContext->WaitForThreadOrUser();
|
||||||
if (fContext.IsTerminating())
|
if (fContext->IsTerminating())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Print the active thread, if it changed.
|
// Print the active thread, if it changed.
|
||||||
if (fContext.CurrentThreadID() != currentThread) {
|
if (fContext->CurrentThreadID() != currentThread) {
|
||||||
fContext.PrintCurrentThread();
|
fContext->PrintCurrentThread();
|
||||||
currentThread = fContext.CurrentThreadID();
|
currentThread = fContext->CurrentThreadID();
|
||||||
}
|
}
|
||||||
|
|
||||||
// read a command line
|
// read a command line
|
||||||
const char* line = fContext.PromptUser(kDebuggerPrompt);
|
const char* line = fContext->PromptUser(kDebuggerPrompt);
|
||||||
if (line == NULL)
|
if (line == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -288,7 +291,7 @@ CommandLineUserInterface::_InputLoop()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// add line to history
|
// add line to history
|
||||||
fContext.AddLineToInputHistory(line);
|
fContext->AddLineToInputHistory(line);
|
||||||
|
|
||||||
// execute command
|
// execute command
|
||||||
_ExecuteCommand(args.ArgumentCount(), args.Arguments());
|
_ExecuteCommand(args.ArgumentCount(), args.Arguments());
|
||||||
@@ -369,7 +372,7 @@ CommandLineUserInterface::_ExecuteCommand(int argc, const char* const* argv)
|
|||||||
{
|
{
|
||||||
CommandEntry* commandEntry = _FindCommand(argv[0]);
|
CommandEntry* commandEntry = _FindCommand(argv[0]);
|
||||||
if (commandEntry != NULL)
|
if (commandEntry != NULL)
|
||||||
commandEntry->Command()->Execute(argc, argv, fContext);
|
commandEntry->Command()->Execute(argc, argv, *fContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ private:
|
|||||||
const CommandEntry* command2);
|
const CommandEntry* command2);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CliContext fContext;
|
CliContext* fContext;
|
||||||
CommandList fCommands;
|
CommandList fCommands;
|
||||||
sem_id fShowSemaphore;
|
sem_id fShowSemaphore;
|
||||||
bool fShown;
|
bool fShown;
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ CliPrintVariableCommand::_ResolveValueIfNeeded(ValueNode* node,
|
|||||||
while (node->LocationAndValueResolutionState()
|
while (node->LocationAndValueResolutionState()
|
||||||
== VALUE_NODE_UNRESOLVED) {
|
== VALUE_NODE_UNRESOLVED) {
|
||||||
containerLocker.Unlock();
|
containerLocker.Unlock();
|
||||||
context.WaitForEvents(CliContext::EVENT_VALUE_NODE_CHANGED);
|
context.WaitForEvent(CliContext::MSG_VALUE_NODE_CHANGED);
|
||||||
containerLocker.Lock();
|
containerLocker.Lock();
|
||||||
if (context.IsTerminating())
|
if (context.IsTerminating())
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ CliStackTraceCommand::Execute(int argc, const char* const* argv,
|
|||||||
// get its stack trace
|
// get its stack trace
|
||||||
StackTrace* stackTrace = thread->GetStackTrace();
|
StackTrace* stackTrace = thread->GetStackTrace();
|
||||||
while (stackTrace == NULL) {
|
while (stackTrace == NULL) {
|
||||||
context.WaitForEvents(CliContext::EVENT_THREAD_STACK_TRACE_CHANGED);
|
context.WaitForEvent(CliContext::MSG_THREAD_STACK_TRACE_CHANGED);
|
||||||
if (context.IsTerminating())
|
if (context.IsTerminating())
|
||||||
return;
|
return;
|
||||||
stackTrace = thread->GetStackTrace();
|
stackTrace = thread->GetStackTrace();
|
||||||
|
|||||||
Reference in New Issue
Block a user