2 * Copyright 2012-2016, Rene Gollent, rene@gollent.com.
3 * Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
8 #include "CliContext.h"
10 #include <AutoDeleter.h>
11 #include <AutoLocker.h>
13 #include "StackTrace.h"
14 #include "UserInterface.h"
15 #include "ValueNodeManager.h"
19 // NOTE: This is a simple work-around for EditLine not having any kind of user
20 // data field. Hence in _GetPrompt() we don't have access to the context object.
21 // ATM only one CLI is possible in Debugger, so a static variable works well
22 // enough. Should that ever change, we would need a thread-safe
23 // EditLine* -> CliContext* map.
24 static CliContext
* sCurrentContext
;
27 // #pragma mark - Event
30 struct CliContext::Event
: DoublyLinkedListLinkImpl
<CliContext::Event
> {
31 Event(int type
, Thread
* thread
= NULL
, TeamMemoryBlock
* block
= NULL
,
32 ExpressionInfo
* info
= NULL
, status_t expressionResult
= B_OK
,
33 ExpressionResult
* expressionValue
= NULL
)
36 fThreadReference(thread
),
37 fMemoryBlockReference(block
),
38 fExpressionInfo(info
),
39 fExpressionResult(expressionResult
),
40 fExpressionValue(expressionValue
)
49 Thread
* GetThread() const
51 return fThreadReference
.Get();
54 TeamMemoryBlock
* GetMemoryBlock() const
56 return fMemoryBlockReference
.Get();
59 ExpressionInfo
* GetExpressionInfo() const
61 return fExpressionInfo
;
64 status_t
GetExpressionResult() const
66 return fExpressionResult
;
69 ExpressionResult
* GetExpressionValue() const
71 return fExpressionValue
.Get();
77 BReference
<Thread
> fThreadReference
;
78 BReference
<TeamMemoryBlock
> fMemoryBlockReference
;
79 BReference
<ExpressionInfo
> fExpressionInfo
;
80 status_t fExpressionResult
;
81 BReference
<ExpressionResult
> fExpressionValue
;
85 // #pragma mark - CliContext
88 CliContext::CliContext()
97 fBlockingSemaphore(-1),
98 fInputLoopWaitingForEvents(0),
100 fInputLoopWaiting(false),
102 fCurrentThread(NULL
),
103 fCurrentStackTrace(NULL
),
104 fCurrentStackFrameIndex(-1),
106 fExpressionInfo(NULL
),
107 fExpressionResult(B_OK
),
108 fExpressionValue(NULL
)
110 sCurrentContext
= this;
114 CliContext::~CliContext()
117 sCurrentContext
= NULL
;
119 if (fBlockingSemaphore
>= 0)
120 delete_sem(fBlockingSemaphore
);
125 CliContext::Init(Team
* team
, UserInterfaceListener
* listener
)
128 fListener
= listener
;
130 fTeam
->AddListener(this);
132 status_t error
= fLock
.InitCheck();
136 fBlockingSemaphore
= create_sem(0, "CliContext block");
137 if (fBlockingSemaphore
< 0)
138 return fBlockingSemaphore
;
140 fEditLine
= el_init("Debugger", stdin
, stdout
, stderr
);
141 if (fEditLine
== NULL
)
144 fHistory
= history_init();
145 if (fHistory
== NULL
)
148 HistEvent historyEvent
;
149 history(fHistory
, &historyEvent
, H_SETSIZE
, 100);
151 el_set(fEditLine
, EL_HIST
, &history
, fHistory
);
152 el_set(fEditLine
, EL_EDITOR
, "emacs");
153 el_set(fEditLine
, EL_PROMPT
, &_GetPrompt
);
155 fNodeManager
= new(std::nothrow
) ValueNodeManager();
156 if (fNodeManager
== NULL
)
158 fNodeManager
->AddListener(this);
160 fExpressionInfo
= new(std::nothrow
) ExpressionInfo();
161 if (fExpressionInfo
== NULL
)
163 fExpressionInfo
->AddListener(this);
170 CliContext::Cleanup()
174 while (Event
* event
= fPendingEvents
.RemoveHead())
177 if (fEditLine
!= NULL
) {
182 if (fHistory
!= NULL
) {
183 history_end(fHistory
);
188 fTeam
->RemoveListener(this);
192 if (fNodeManager
!= NULL
) {
193 fNodeManager
->ReleaseReference();
197 if (fCurrentBlock
!= NULL
) {
198 fCurrentBlock
->ReleaseReference();
199 fCurrentBlock
= NULL
;
202 if (fExpressionInfo
!= NULL
) {
203 fExpressionInfo
->ReleaseReference();
204 fExpressionInfo
= NULL
;
210 CliContext::Terminating()
212 AutoLocker
<BLocker
> locker(fLock
);
215 _SignalInputLoop(EVENT_QUIT
);
217 // TODO: Signal the input loop, should it be in PromptUser()!
222 CliContext::CurrentThreadID() const
224 return fCurrentThread
!= NULL
? fCurrentThread
->ID() : -1;
229 CliContext::SetCurrentThread(Thread
* thread
)
231 AutoLocker
<BLocker
> locker(fLock
);
233 if (fCurrentThread
!= NULL
)
234 fCurrentThread
->ReleaseReference();
236 fCurrentThread
= thread
;
238 if (fCurrentStackTrace
!= NULL
) {
239 fCurrentStackTrace
->ReleaseReference();
240 fCurrentStackTrace
= NULL
;
241 fCurrentStackFrameIndex
= -1;
242 fNodeManager
->SetStackFrame(NULL
, NULL
);
245 if (fCurrentThread
!= NULL
) {
246 fCurrentThread
->AcquireReference();
247 StackTrace
* stackTrace
= fCurrentThread
->GetStackTrace();
248 // if the thread's stack trace has already been loaded,
249 // set it, otherwise we'll set it when we process the thread's
250 // stack trace changed event.
251 if (stackTrace
!= NULL
) {
252 fCurrentStackTrace
= stackTrace
;
253 fCurrentStackTrace
->AcquireReference();
254 SetCurrentStackFrameIndex(0);
261 CliContext::PrintCurrentThread()
263 AutoLocker
<Team
> teamLocker(fTeam
);
265 if (fCurrentThread
!= NULL
) {
266 printf("current thread: %" B_PRId32
" \"%s\"\n", fCurrentThread
->ID(),
267 fCurrentThread
->Name());
269 printf("no current thread\n");
274 CliContext::SetCurrentStackFrameIndex(int32 index
)
276 AutoLocker
<BLocker
> locker(fLock
);
278 if (fCurrentStackTrace
== NULL
)
280 else if (index
< 0 || index
>= fCurrentStackTrace
->CountFrames())
283 fCurrentStackFrameIndex
= index
;
285 StackFrame
* frame
= fCurrentStackTrace
->FrameAt(index
);
287 fNodeManager
->SetStackFrame(fCurrentThread
, frame
);
292 CliContext::PromptUser(const char* prompt
)
297 const char* line
= el_gets(fEditLine
, &count
);
301 ProcessPendingEvents();
308 CliContext::AddLineToInputHistory(const char* line
)
310 HistEvent historyEvent
;
311 history(fHistory
, &historyEvent
, H_ENTER
, line
);
316 CliContext::QuitSession(bool killTeam
)
318 _PrepareToWaitForEvents(EVENT_QUIT
);
320 fListener
->UserInterfaceQuitRequested(
322 ? UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM
323 : UserInterfaceListener::QUIT_OPTION_ASK_RESUME_TEAM
);
330 CliContext::WaitForThreadOrUser()
332 ProcessPendingEvents();
334 // TODO: Deal with SIGINT as well!
336 _PrepareToWaitForEvents(
337 EVENT_USER_INTERRUPT
| EVENT_THREAD_STOPPED
);
339 // check whether there are any threads stopped already
340 Thread
* stoppedThread
= NULL
;
341 BReference
<Thread
> stoppedThreadReference
;
343 AutoLocker
<Team
> teamLocker(fTeam
);
345 for (ThreadList::ConstIterator it
= fTeam
->Threads().GetIterator();
346 Thread
* thread
= it
.Next();) {
347 if (thread
->State() == THREAD_STATE_STOPPED
) {
348 stoppedThread
= thread
;
349 stoppedThreadReference
.SetTo(thread
);
356 if (stoppedThread
!= NULL
) {
357 if (fCurrentThread
== NULL
)
358 SetCurrentThread(stoppedThread
);
360 _SignalInputLoop(EVENT_THREAD_STOPPED
);
363 uint32 events
= _WaitForEvents();
364 if ((events
& EVENT_QUIT
) != 0 || stoppedThread
!= NULL
) {
365 ProcessPendingEvents();
373 CliContext::WaitForEvents(int32 eventMask
)
376 _PrepareToWaitForEvents(eventMask
| EVENT_USER_INTERRUPT
);
377 uint32 events
= fEventsOccurred
;
378 if ((events
& eventMask
) == 0) {
379 events
= _WaitForEvents();
382 if ((events
& EVENT_QUIT
) != 0 || (events
& eventMask
) != 0) {
383 _SignalInputLoop(eventMask
);
384 ProcessPendingEvents();
392 CliContext::ProcessPendingEvents()
394 AutoLocker
<Team
> teamLocker(fTeam
);
397 // get the next event
398 AutoLocker
<BLocker
> locker(fLock
);
399 Event
* event
= fPendingEvents
.RemoveHead();
403 ObjectDeleter
<Event
> eventDeleter(event
);
406 Thread
* thread
= event
->GetThread();
408 switch (event
->Type()) {
410 case EVENT_DEBUG_REPORT_CHANGED
:
411 case EVENT_USER_INTERRUPT
:
413 case EVENT_THREAD_ADDED
:
414 printf("[new thread: %" B_PRId32
" \"%s\"]\n", thread
->ID(),
417 case EVENT_THREAD_REMOVED
:
418 printf("[thread terminated: %" B_PRId32
" \"%s\"]\n",
419 thread
->ID(), thread
->Name());
421 case EVENT_THREAD_STOPPED
:
422 printf("[thread stopped: %" B_PRId32
" \"%s\"]\n",
423 thread
->ID(), thread
->Name());
425 case EVENT_THREAD_STACK_TRACE_CHANGED
:
426 if (thread
== fCurrentThread
) {
427 fCurrentStackTrace
= thread
->GetStackTrace();
428 fCurrentStackTrace
->AcquireReference();
429 SetCurrentStackFrameIndex(0);
432 case EVENT_TEAM_MEMORY_BLOCK_RETRIEVED
:
433 if (fCurrentBlock
!= NULL
) {
434 fCurrentBlock
->ReleaseReference();
435 fCurrentBlock
= NULL
;
437 fCurrentBlock
= event
->GetMemoryBlock();
439 case EVENT_EXPRESSION_EVALUATED
:
440 fExpressionResult
= event
->GetExpressionResult();
441 if (fExpressionValue
!= NULL
) {
442 fExpressionValue
->ReleaseReference();
443 fExpressionValue
= NULL
;
445 fExpressionValue
= event
->GetExpressionValue();
446 if (fExpressionValue
!= NULL
)
447 fExpressionValue
->AcquireReference();
455 CliContext::ThreadAdded(const Team::ThreadEvent
& threadEvent
)
458 new(std::nothrow
) Event(EVENT_THREAD_ADDED
, threadEvent
.GetThread()));
459 _SignalInputLoop(EVENT_THREAD_ADDED
);
464 CliContext::ThreadRemoved(const Team::ThreadEvent
& threadEvent
)
467 new(std::nothrow
) Event(EVENT_THREAD_REMOVED
, threadEvent
.GetThread()));
468 _SignalInputLoop(EVENT_THREAD_REMOVED
);
473 CliContext::ThreadStateChanged(const Team::ThreadEvent
& threadEvent
)
475 if (threadEvent
.GetThread()->State() != THREAD_STATE_STOPPED
)
479 new(std::nothrow
) Event(EVENT_THREAD_STOPPED
, threadEvent
.GetThread()));
480 _SignalInputLoop(EVENT_THREAD_STOPPED
);
485 CliContext::ThreadStackTraceChanged(const Team::ThreadEvent
& threadEvent
)
487 if (threadEvent
.GetThread()->State() != THREAD_STATE_STOPPED
)
491 new(std::nothrow
) Event(EVENT_THREAD_STACK_TRACE_CHANGED
,
492 threadEvent
.GetThread()));
493 _SignalInputLoop(EVENT_THREAD_STACK_TRACE_CHANGED
);
498 CliContext::ExpressionEvaluated(ExpressionInfo
* info
, status_t result
,
499 ExpressionResult
* value
)
502 new(std::nothrow
) Event(EVENT_EXPRESSION_EVALUATED
,
503 NULL
, NULL
, info
, result
, value
));
504 _SignalInputLoop(EVENT_EXPRESSION_EVALUATED
);
509 CliContext::DebugReportChanged(const Team::DebugReportEvent
& event
)
511 if (event
.GetFinalStatus() == B_OK
) {
512 printf("Successfully saved debug report to %s\n",
513 event
.GetReportPath());
515 fprintf(stderr
, "Failed to write debug report: %s\n", strerror(
516 event
.GetFinalStatus()));
519 _QueueEvent(new(std::nothrow
) Event(EVENT_DEBUG_REPORT_CHANGED
));
520 _SignalInputLoop(EVENT_DEBUG_REPORT_CHANGED
);
525 CliContext::CoreFileChanged(const Team::CoreFileChangedEvent
& event
)
527 printf("Successfully saved core file to %s\n",
528 event
.GetTargetPath());
530 _QueueEvent(new(std::nothrow
) Event(EVENT_CORE_FILE_CHANGED
));
531 _SignalInputLoop(EVENT_CORE_FILE_CHANGED
);
536 CliContext::MemoryBlockRetrieved(TeamMemoryBlock
* block
)
539 new(std::nothrow
) Event(EVENT_TEAM_MEMORY_BLOCK_RETRIEVED
,
541 _SignalInputLoop(EVENT_TEAM_MEMORY_BLOCK_RETRIEVED
);
546 CliContext::ValueNodeChanged(ValueNodeChild
* nodeChild
, ValueNode
* oldNode
,
549 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED
);
554 CliContext::ValueNodeChildrenCreated(ValueNode
* node
)
556 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED
);
561 CliContext::ValueNodeChildrenDeleted(ValueNode
* node
)
563 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED
);
568 CliContext::ValueNodeValueChanged(ValueNode
* oldNode
)
570 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED
);
575 CliContext::_QueueEvent(Event
* event
)
578 // no memory -- can't do anything about it
582 AutoLocker
<BLocker
> locker(fLock
);
583 fPendingEvents
.Add(event
);
588 CliContext::_PrepareToWaitForEvents(uint32 eventMask
)
590 // Set the events we're going to wait for -- always wait for "quit".
591 AutoLocker
<BLocker
> locker(fLock
);
592 fInputLoopWaitingForEvents
= eventMask
| EVENT_QUIT
;
593 fEventsOccurred
= fTerminating
? EVENT_QUIT
: 0;
598 CliContext::_WaitForEvents()
600 AutoLocker
<BLocker
> locker(fLock
);
602 if (fEventsOccurred
== 0) {
603 sem_id blockingSemaphore
= fBlockingSemaphore
;
604 fInputLoopWaiting
= true;
608 while (acquire_sem(blockingSemaphore
) == B_INTERRUPTED
) {
614 uint32 events
= fEventsOccurred
;
621 CliContext::_SignalInputLoop(uint32 events
)
623 AutoLocker
<BLocker
> locker(fLock
);
625 if ((fInputLoopWaitingForEvents
& events
) == 0)
628 fEventsOccurred
= fInputLoopWaitingForEvents
& events
;
629 fInputLoopWaitingForEvents
= 0;
631 if (fInputLoopWaiting
) {
632 fInputLoopWaiting
= false;
633 release_sem(fBlockingSemaphore
);
638 /*static*/ const char*
639 CliContext::_GetPrompt(EditLine
* editLine
)
641 return sCurrentContext
!= NULL
? sCurrentContext
->fPrompt
: NULL
;