1 /*
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.
5 */
6
7
8 #include "CliContext.h"
9
10 #include <AutoDeleter.h>
11 #include <AutoLocker.h>
12
13 #include "StackTrace.h"
14 #include "UserInterface.h"
15 #include "Value.h"
16 #include "ValueNodeManager.h"
17 #include "Variable.h"
18
19
20 // NOTE: This is a simple work-around for EditLine not having any kind of user
21 // data field. Hence in _GetPrompt() we don't have access to the context object.
22 // ATM only one CLI is possible in Debugger, so a static variable works well
23 // enough. Should that ever change, we would need a thread-safe
24 // EditLine* -> CliContext* map.
25 static CliContext* sCurrentContext;
26
27
28 // #pragma mark - Event
29
30
31 struct CliContext::Event : DoublyLinkedListLinkImpl<CliContext::Event> {
EventCliContext::Event32 Event(int type, ::Thread* thread = NULL, TeamMemoryBlock* block = NULL,
33 ExpressionInfo* info = NULL, status_t expressionResult = B_OK,
34 ExpressionResult* expressionValue = NULL)
35 :
36 fType(type),
37 fThreadReference(thread),
38 fMemoryBlockReference(block),
39 fExpressionInfo(info),
40 fExpressionResult(expressionResult),
41 fExpressionValue(expressionValue)
42 {
43 }
44
TypeCliContext::Event45 int Type() const
46 {
47 return fType;
48 }
49
GetThreadCliContext::Event50 ::Thread* GetThread() const
51 {
52 return fThreadReference.Get();
53 }
54
GetMemoryBlockCliContext::Event55 TeamMemoryBlock* GetMemoryBlock() const
56 {
57 return fMemoryBlockReference.Get();
58 }
59
GetExpressionInfoCliContext::Event60 ExpressionInfo* GetExpressionInfo() const
61 {
62 return fExpressionInfo;
63 }
64
GetExpressionResultCliContext::Event65 status_t GetExpressionResult() const
66 {
67 return fExpressionResult;
68 }
69
GetExpressionValueCliContext::Event70 ExpressionResult* GetExpressionValue() const
71 {
72 return fExpressionValue.Get();
73 }
74
75
76 private:
77 int fType;
78 BReference< ::Thread> fThreadReference;
79 BReference<TeamMemoryBlock> fMemoryBlockReference;
80 BReference<ExpressionInfo> fExpressionInfo;
81 status_t fExpressionResult;
82 BReference<ExpressionResult> fExpressionValue;
83 };
84
85
86 // #pragma mark - CliContext
87
88
CliContext()89 CliContext::CliContext()
90 :
91 BLooper("CliContext"),
92 fLock("CliContext"),
93 fTeam(NULL),
94 fListener(NULL),
95 fNodeManager(NULL),
96 fEditLine(NULL),
97 fHistory(NULL),
98 fPrompt(NULL),
99 fWaitForEventSemaphore(-1),
100 fEventOccurred(0),
101 fTerminating(false),
102 fStoppedThread(NULL),
103 fCurrentThread(NULL),
104 fCurrentStackTrace(NULL),
105 fCurrentStackFrameIndex(-1),
106 fCurrentBlock(NULL),
107 fExpressionInfo(NULL),
108 fExpressionResult(B_OK),
109 fExpressionValue(NULL)
110 {
111 sCurrentContext = this;
112 }
113
114
~CliContext()115 CliContext::~CliContext()
116 {
117 Cleanup();
118 sCurrentContext = NULL;
119
120 if (fWaitForEventSemaphore >= 0)
121 delete_sem(fWaitForEventSemaphore);
122 }
123
124
125 status_t
Init(::Team * team,UserInterfaceListener * listener)126 CliContext::Init(::Team* team, UserInterfaceListener* listener)
127 {
128 AutoLocker<BLocker> locker(fLock);
129
130 fTeam = team;
131 fListener = listener;
132
133 fTeam->AddListener(this);
134
135 status_t error = fLock.InitCheck();
136 if (error != B_OK)
137 return error;
138
139 fWaitForEventSemaphore = create_sem(0, "CliContext wait for event");
140 if (fWaitForEventSemaphore < 0)
141 return fWaitForEventSemaphore;
142
143 fEditLine = el_init("Debugger", stdin, stdout, stderr);
144 if (fEditLine == NULL)
145 return B_ERROR;
146
147 fHistory = history_init();
148 if (fHistory == NULL)
149 return B_ERROR;
150
151 HistEvent historyEvent;
152 history(fHistory, &historyEvent, H_SETSIZE, 100);
153
154 el_set(fEditLine, EL_HIST, &history, fHistory);
155 el_set(fEditLine, EL_EDITOR, "emacs");
156 el_set(fEditLine, EL_PROMPT, &_GetPrompt);
157
158 fNodeManager = new(std::nothrow) ValueNodeManager();
159 if (fNodeManager == NULL)
160 return B_NO_MEMORY;
161 fNodeManager->AddListener(this);
162
163 fExpressionInfo = new(std::nothrow) ExpressionInfo();
164 if (fExpressionInfo == NULL)
165 return B_NO_MEMORY;
166 fExpressionInfo->AddListener(this);
167
168 return B_OK;
169 }
170
171
172 void
Cleanup()173 CliContext::Cleanup()
174 {
175 AutoLocker<BLocker> locker(fLock);
176 Terminating();
177
178 if (fEditLine != NULL) {
179 el_end(fEditLine);
180 fEditLine = NULL;
181 }
182
183 if (fHistory != NULL) {
184 history_end(fHistory);
185 fHistory = NULL;
186 }
187
188 if (fTeam != NULL) {
189 fTeam->RemoveListener(this);
190 fTeam = NULL;
191 }
192
193 if (fNodeManager != NULL) {
194 fNodeManager->ReleaseReference();
195 fNodeManager = NULL;
196 }
197
198 if (fCurrentBlock != NULL) {
199 fCurrentBlock->ReleaseReference();
200 fCurrentBlock = NULL;
201 }
202
203 if (fExpressionInfo != NULL) {
204 fExpressionInfo->ReleaseReference();
205 fExpressionInfo = NULL;
206 }
207 }
208
209
210 // TODO: Use the lifecycle methods of BLooper instead
211 void
Terminating()212 CliContext::Terminating()
213 {
214 AutoLocker<BLocker> locker(fLock);
215
216 fTerminating = true;
217
218 BMessage message(MSG_QUIT);
219 PostMessage(&message);
220
221 // TODO: Signal the input loop, should it be in PromptUser()!
222 }
223
224
225 thread_id
CurrentThreadID() const226 CliContext::CurrentThreadID() const
227 {
228 AutoLocker<BLocker> locker(fLock);
229 return fCurrentThread != NULL ? fCurrentThread->ID() : -1;
230 }
231
232
233 void
SetCurrentThread(::Thread * thread)234 CliContext::SetCurrentThread(::Thread* thread)
235 {
236 AutoLocker<BLocker> locker(fLock);
237
238 if (fCurrentThread != NULL)
239 fCurrentThread->ReleaseReference();
240
241 fCurrentThread = thread;
242
243 if (fCurrentStackTrace != NULL) {
244 fCurrentStackTrace->ReleaseReference();
245 fCurrentStackTrace = NULL;
246 fCurrentStackFrameIndex = -1;
247 fNodeManager->SetStackFrame(NULL, NULL);
248 }
249
250 if (fCurrentThread != NULL) {
251 fCurrentThread->AcquireReference();
252 StackTrace* stackTrace = fCurrentThread->GetStackTrace();
253 // if the thread's stack trace has already been loaded,
254 // set it, otherwise we'll set it when we process the thread's
255 // stack trace changed event.
256 if (stackTrace != NULL) {
257 fCurrentStackTrace = stackTrace;
258 fCurrentStackTrace->AcquireReference();
259 SetCurrentStackFrameIndex(0);
260 }
261 }
262 }
263
264
265 void
PrintCurrentThread()266 CliContext::PrintCurrentThread()
267 {
268 AutoLocker< ::Team> teamLocker(fTeam);
269 AutoLocker<BLocker> locker(fLock);
270
271 if (fCurrentThread != NULL) {
272 printf("current thread: %" B_PRId32 " \"%s\"\n", fCurrentThread->ID(),
273 fCurrentThread->Name());
274 } else
275 printf("no current thread\n");
276 }
277
278
279 void
SetCurrentStackFrameIndex(int32 index)280 CliContext::SetCurrentStackFrameIndex(int32 index)
281 {
282 AutoLocker<BLocker> locker(fLock);
283
284 if (fCurrentStackTrace == NULL)
285 return;
286 else if (index < 0 || index >= fCurrentStackTrace->CountFrames())
287 return;
288
289 fCurrentStackFrameIndex = index;
290
291 StackFrame* frame = fCurrentStackTrace->FrameAt(index);
292 if (frame != NULL)
293 fNodeManager->SetStackFrame(fCurrentThread, frame);
294 }
295
296
297 status_t
EvaluateExpression(const char * expression,SourceLanguage * language,target_addr_t & address)298 CliContext::EvaluateExpression(const char* expression,
299 SourceLanguage* language, target_addr_t& address)
300 {
301 AutoLocker<BLocker> locker(fLock);
302 fExpressionInfo->SetTo(expression);
303
304 fListener->ExpressionEvaluationRequested(
305 language, fExpressionInfo);
306 _WaitForEvent(MSG_EXPRESSION_EVALUATED);
307 if (fTerminating)
308 return B_INTERRUPTED;
309
310 BString errorMessage;
311 if (fExpressionValue != NULL) {
312 if (fExpressionValue->Kind() == EXPRESSION_RESULT_KIND_PRIMITIVE) {
313 Value* value = fExpressionValue->PrimitiveValue();
314 BVariant variantValue;
315 value->ToVariant(variantValue);
316 if (variantValue.Type() == B_STRING_TYPE)
317 errorMessage.SetTo(variantValue.ToString());
318 else
319 address = variantValue.ToUInt64();
320 }
321 } else
322 errorMessage = strerror(fExpressionResult);
323
324 if (!errorMessage.IsEmpty()) {
325 printf("Unable to evaluate expression: %s\n",
326 errorMessage.String());
327 return B_ERROR;
328 }
329
330 return B_OK;
331 }
332
333
334 status_t
GetMemoryBlock(target_addr_t address,TeamMemoryBlock * & block)335 CliContext::GetMemoryBlock(target_addr_t address, TeamMemoryBlock*& block)
336 {
337 AutoLocker<BLocker> locker(fLock);
338 if (fCurrentBlock == NULL || !fCurrentBlock->Contains(address)) {
339 GetUserInterfaceListener()->InspectRequested(address, this);
340 _WaitForEvent(MSG_TEAM_MEMORY_BLOCK_RETRIEVED);
341 if (fTerminating)
342 return B_INTERRUPTED;
343 }
344
345 block = fCurrentBlock;
346 return B_OK;
347 }
348
349
350 const char*
PromptUser(const char * prompt)351 CliContext::PromptUser(const char* prompt)
352 {
353 fPrompt = prompt;
354
355 int count;
356 const char* line = el_gets(fEditLine, &count);
357
358 fPrompt = NULL;
359
360 return line;
361 }
362
363
364 void
AddLineToInputHistory(const char * line)365 CliContext::AddLineToInputHistory(const char* line)
366 {
367 HistEvent historyEvent;
368 history(fHistory, &historyEvent, H_ENTER, line);
369 }
370
371
372 void
QuitSession(bool killTeam)373 CliContext::QuitSession(bool killTeam)
374 {
375 fListener->UserInterfaceQuitRequested(
376 killTeam
377 ? UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM
378 : UserInterfaceListener::QUIT_OPTION_ASK_RESUME_TEAM);
379
380 WaitForEvent(MSG_QUIT);
381 }
382
383
384 void
WaitForThreadOrUser()385 CliContext::WaitForThreadOrUser()
386 {
387 // TODO: Deal with SIGINT as well!
388
389 AutoLocker<BLocker> locker(fLock);
390
391 while (fStoppedThread == NULL)
392 _WaitForEvent(MSG_THREAD_STATE_CHANGED);
393
394 if (fCurrentThread == NULL)
395 SetCurrentThread(fStoppedThread);
396 }
397
398
399 void
WaitForEvent(uint32 event)400 CliContext::WaitForEvent(uint32 event) {
401 AutoLocker<BLocker> locker(fLock);
402 _WaitForEvent(event);
403 }
404
405
406 void
MessageReceived(BMessage * message)407 CliContext::MessageReceived(BMessage* message)
408 {
409 fLock.Lock();
410
411 int32 threadID;
412 message->FindInt32("thread", &threadID);
413
414 const char* threadName;
415 message->FindString("threadName", &threadName);
416
417 switch (message->what) {
418 case MSG_THREAD_ADDED:
419 printf("[new thread: %" B_PRId32 " \"%s\"]\n", threadID,
420 threadName);
421 break;
422 case MSG_THREAD_REMOVED:
423 printf("[thread terminated: %" B_PRId32 " \"%s\"]\n",
424 threadID, threadName);
425 break;
426 case MSG_THREAD_STATE_CHANGED:
427 {
428 AutoLocker< ::Team> locker(fTeam);
429 ::Thread* thread = fTeam->ThreadByID(threadID);
430
431 if (thread->State() == THREAD_STATE_STOPPED) {
432 printf("[thread stopped: %" B_PRId32 " \"%s\"]\n",
433 threadID, threadName);
434 fStoppedThread.SetTo(thread);
435 } else {
436 fStoppedThread = NULL;
437 }
438 break;
439 }
440 case MSG_THREAD_STACK_TRACE_CHANGED:
441 if (threadID == fCurrentThread->ID()) {
442 AutoLocker< ::Team> locker(fTeam);
443 ::Thread* thread = fTeam->ThreadByID(threadID);
444
445 fCurrentStackTrace = thread->GetStackTrace();
446 fCurrentStackTrace->AcquireReference();
447 SetCurrentStackFrameIndex(0);
448 }
449 break;
450 case MSG_TEAM_MEMORY_BLOCK_RETRIEVED:
451 {
452 TeamMemoryBlock* block = NULL;
453 if (message->FindPointer("block",
454 reinterpret_cast<void **>(&block)) != B_OK) {
455 break;
456 }
457
458 if (fCurrentBlock != NULL) {
459 fCurrentBlock->ReleaseReference();
460 }
461
462 // reference acquired in MemoryBlockRetrieved
463 fCurrentBlock = block;
464 break;
465 }
466 case MSG_EXPRESSION_EVALUATED:
467 {
468 status_t result;
469 if (message->FindInt32("result", &result) != B_OK) {
470 break;
471 }
472
473 fExpressionResult = result;
474
475 ExpressionResult* value = NULL;
476 message->FindPointer("value", reinterpret_cast<void**>(&value));
477
478 if (fExpressionValue != NULL) {
479 fExpressionValue->ReleaseReference();
480 }
481
482 // reference acquired in ExpressionEvaluated
483 fExpressionValue = value;
484 break;
485 }
486 default:
487 BLooper::MessageReceived(message);
488 break;
489 }
490
491 fEventOccurred = message->what;
492
493 fLock.Unlock();
494
495 release_sem(fWaitForEventSemaphore);
496 // all of the code that was waiting on the semaphore runs
497 acquire_sem(fWaitForEventSemaphore);
498
499 fLock.Lock();
500 fEventOccurred = 0;
501 fLock.Unlock();
502 }
503
504
505 void
ThreadAdded(const Team::ThreadEvent & threadEvent)506 CliContext::ThreadAdded(const Team::ThreadEvent& threadEvent)
507 {
508 BMessage message(MSG_THREAD_ADDED);
509 message.AddInt32("thread", threadEvent.GetThread()->ID());
510 message.AddString("threadName", threadEvent.GetThread()->Name());
511 PostMessage(&message);
512 }
513
514
515 void
ThreadRemoved(const Team::ThreadEvent & threadEvent)516 CliContext::ThreadRemoved(const Team::ThreadEvent& threadEvent)
517 {
518 BMessage message(MSG_THREAD_REMOVED);
519 message.AddInt32("thread", threadEvent.GetThread()->ID());
520 message.AddString("threadName", threadEvent.GetThread()->Name());
521 PostMessage(&message);
522 }
523
524
525 void
ThreadStateChanged(const Team::ThreadEvent & threadEvent)526 CliContext::ThreadStateChanged(const Team::ThreadEvent& threadEvent)
527 {
528 BMessage message(MSG_THREAD_STATE_CHANGED);
529 message.AddInt32("thread", threadEvent.GetThread()->ID());
530 message.AddString("threadName", threadEvent.GetThread()->Name());
531 PostMessage(&message);
532 }
533
534
535 void
ThreadStackTraceChanged(const Team::ThreadEvent & threadEvent)536 CliContext::ThreadStackTraceChanged(const Team::ThreadEvent& threadEvent)
537 {
538 if (threadEvent.GetThread()->State() != THREAD_STATE_STOPPED)
539 return;
540
541 BMessage message(MSG_THREAD_STACK_TRACE_CHANGED);
542 message.AddInt32("thread", threadEvent.GetThread()->ID());
543 message.AddString("threadName", threadEvent.GetThread()->Name());
544 PostMessage(&message);
545 }
546
547
548 void
ExpressionEvaluated(ExpressionInfo * info,status_t result,ExpressionResult * value)549 CliContext::ExpressionEvaluated(ExpressionInfo* info, status_t result,
550 ExpressionResult* value)
551 {
552 BMessage message(MSG_EXPRESSION_EVALUATED);
553 message.AddInt32("result", result);
554
555 if (value != NULL) {
556 value->AcquireReference();
557 message.AddPointer("value", value);
558 }
559
560 PostMessage(&message);
561 }
562
563
564 void
DebugReportChanged(const Team::DebugReportEvent & event)565 CliContext::DebugReportChanged(const Team::DebugReportEvent& event)
566 {
567 if (event.GetFinalStatus() == B_OK) {
568 printf("Successfully saved debug report to %s\n",
569 event.GetReportPath());
570 } else {
571 fprintf(stderr, "Failed to write debug report: %s\n", strerror(
572 event.GetFinalStatus()));
573 }
574 }
575
576
577 void
CoreFileChanged(const Team::CoreFileChangedEvent & event)578 CliContext::CoreFileChanged(const Team::CoreFileChangedEvent& event)
579 {
580 printf("Successfully saved core file to %s\n",
581 event.GetTargetPath());
582 }
583
584
585 void
MemoryBlockRetrieved(TeamMemoryBlock * block)586 CliContext::MemoryBlockRetrieved(TeamMemoryBlock* block)
587 {
588 if (block != NULL)
589 block->AcquireReference();
590
591 BMessage message(MSG_TEAM_MEMORY_BLOCK_RETRIEVED);
592 message.AddPointer("block", block);
593 PostMessage(&message);
594 }
595
596
597 void
ValueNodeChanged(ValueNodeChild * nodeChild,ValueNode * oldNode,ValueNode * newNode)598 CliContext::ValueNodeChanged(ValueNodeChild* nodeChild, ValueNode* oldNode,
599 ValueNode* newNode)
600 {
601 BMessage message(MSG_VALUE_NODE_CHANGED);
602 PostMessage(&message);
603 }
604
605
606 void
ValueNodeChildrenCreated(ValueNode * node)607 CliContext::ValueNodeChildrenCreated(ValueNode* node)
608 {
609 BMessage message(MSG_VALUE_NODE_CHANGED);
610 PostMessage(&message);
611 }
612
613
614 void
ValueNodeChildrenDeleted(ValueNode * node)615 CliContext::ValueNodeChildrenDeleted(ValueNode* node)
616 {
617 BMessage message(MSG_VALUE_NODE_CHANGED);
618 PostMessage(&message);
619 }
620
621
622 void
ValueNodeValueChanged(ValueNode * oldNode)623 CliContext::ValueNodeValueChanged(ValueNode* oldNode)
624 {
625 BMessage message(MSG_VALUE_NODE_CHANGED);
626 PostMessage(&message);
627 }
628
629
630 /*static*/ const char*
_GetPrompt(EditLine * editLine)631 CliContext::_GetPrompt(EditLine* editLine)
632 {
633 return sCurrentContext != NULL ? sCurrentContext->fPrompt : NULL;
634 }
635
636
637 void
_WaitForEvent(uint32 event)638 CliContext::_WaitForEvent(uint32 event) {
639 if (fTerminating)
640 return;
641
642 do {
643 fLock.Unlock();
644 while (acquire_sem(fWaitForEventSemaphore) == B_INTERRUPTED) {
645 }
646 fLock.Lock();
647 release_sem(fWaitForEventSemaphore);
648 } while (fEventOccurred != event && !fTerminating);
649 }
650
651