xref: /haiku/src/apps/debugger/user_interface/cli/CliContext.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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> {
32 	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 
45 	int Type() const
46 	{
47 		return fType;
48 	}
49 
50 	::Thread* GetThread() const
51 	{
52 		return fThreadReference.Get();
53 	}
54 
55 	TeamMemoryBlock* GetMemoryBlock() const
56 	{
57 		return fMemoryBlockReference.Get();
58 	}
59 
60 	ExpressionInfo* GetExpressionInfo() const
61 	{
62 		return fExpressionInfo;
63 	}
64 
65 	status_t GetExpressionResult() const
66 	{
67 		return fExpressionResult;
68 	}
69 
70 	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 
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 
115 CliContext::~CliContext()
116 {
117 	Cleanup();
118 	sCurrentContext = NULL;
119 
120 	if (fWaitForEventSemaphore >= 0)
121 		delete_sem(fWaitForEventSemaphore);
122 }
123 
124 
125 status_t
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
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
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
226 CliContext::CurrentThreadID() const
227 {
228 	AutoLocker<BLocker> locker(fLock);
229 	return fCurrentThread != NULL ? fCurrentThread->ID() : -1;
230 }
231 
232 
233 void
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
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
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
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
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*
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
365 CliContext::AddLineToInputHistory(const char* line)
366 {
367 	HistEvent historyEvent;
368 	history(fHistory, &historyEvent, H_ENTER, line);
369 }
370 
371 
372 void
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
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
400 CliContext::WaitForEvent(uint32 event) {
401 	AutoLocker<BLocker> locker(fLock);
402 	_WaitForEvent(event);
403 }
404 
405 
406 void
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
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
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
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
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
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
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
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
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
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
607 CliContext::ValueNodeChildrenCreated(ValueNode* node)
608 {
609 	BMessage message(MSG_VALUE_NODE_CHANGED);
610 	PostMessage(&message);
611 }
612 
613 
614 void
615 CliContext::ValueNodeChildrenDeleted(ValueNode* node)
616 {
617 	BMessage message(MSG_VALUE_NODE_CHANGED);
618 	PostMessage(&message);
619 }
620 
621 
622 void
623 CliContext::ValueNodeValueChanged(ValueNode* oldNode)
624 {
625 	BMessage message(MSG_VALUE_NODE_CHANGED);
626 	PostMessage(&message);
627 }
628 
629 
630 /*static*/ const char*
631 CliContext::_GetPrompt(EditLine* editLine)
632 {
633 	return sCurrentContext != NULL ? sCurrentContext->fPrompt : NULL;
634 }
635 
636 
637 void
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