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.
25static CliContext* sCurrentContext;
26
27
28// #pragma mark - Event
29
30
31struct 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
76private:
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
89CliContext::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
115CliContext::~CliContext()
116{
117	Cleanup();
118	sCurrentContext = NULL;
119
120	if (fWaitForEventSemaphore >= 0)
121		delete_sem(fWaitForEventSemaphore);
122}
123
124
125status_t
126CliContext::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
172void
173CliContext::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
211void
212CliContext::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
225thread_id
226CliContext::CurrentThreadID() const
227{
228	AutoLocker<BLocker> locker(fLock);
229	return fCurrentThread != NULL ? fCurrentThread->ID() : -1;
230}
231
232
233void
234CliContext::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
265void
266CliContext::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
279void
280CliContext::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
297status_t
298CliContext::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
334status_t
335CliContext::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
350const char*
351CliContext::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
364void
365CliContext::AddLineToInputHistory(const char* line)
366{
367	HistEvent historyEvent;
368	history(fHistory, &historyEvent, H_ENTER, line);
369}
370
371
372void
373CliContext::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
384void
385CliContext::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
399void
400CliContext::WaitForEvent(uint32 event) {
401	AutoLocker<BLocker> locker(fLock);
402	_WaitForEvent(event);
403}
404
405
406void
407CliContext::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
505void
506CliContext::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
515void
516CliContext::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
525void
526CliContext::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
535void
536CliContext::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
548void
549CliContext::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
564void
565CliContext::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
577void
578CliContext::CoreFileChanged(const Team::CoreFileChangedEvent& event)
579{
580	printf("Successfully saved core file to %s\n",
581		event.GetTargetPath());
582}
583
584
585void
586CliContext::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
597void
598CliContext::ValueNodeChanged(ValueNodeChild* nodeChild, ValueNode* oldNode,
599	ValueNode* newNode)
600{
601	BMessage message(MSG_VALUE_NODE_CHANGED);
602	PostMessage(&message);
603}
604
605
606void
607CliContext::ValueNodeChildrenCreated(ValueNode* node)
608{
609	BMessage message(MSG_VALUE_NODE_CHANGED);
610	PostMessage(&message);
611}
612
613
614void
615CliContext::ValueNodeChildrenDeleted(ValueNode* node)
616{
617	BMessage message(MSG_VALUE_NODE_CHANGED);
618	PostMessage(&message);
619}
620
621
622void
623CliContext::ValueNodeValueChanged(ValueNode* oldNode)
624{
625	BMessage message(MSG_VALUE_NODE_CHANGED);
626	PostMessage(&message);
627}
628
629
630/*static*/ const char*
631CliContext::_GetPrompt(EditLine* editLine)
632{
633	return sCurrentContext != NULL ? sCurrentContext->fPrompt : NULL;
634}
635
636
637void
638CliContext::_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