1/*
2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2010-2011, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "ThreadHandler.h"
9
10#include <stdio.h>
11
12#include <new>
13
14#include <AutoLocker.h>
15
16#include "Architecture.h"
17#include "BreakpointManager.h"
18#include "CpuState.h"
19#include "DebuggerInterface.h"
20#include "FunctionInstance.h"
21#include "ImageDebugInfo.h"
22#include "InstructionInfo.h"
23#include "Jobs.h"
24#include "MessageCodes.h"
25#include "Register.h"
26#include "SourceCode.h"
27#include "SpecificImageDebugInfo.h"
28#include "StackTrace.h"
29#include "Statement.h"
30#include "Team.h"
31#include "Tracing.h"
32#include "Worker.h"
33
34
35// step modes
36enum {
37	STEP_NONE,
38	STEP_OVER,
39	STEP_INTO,
40	STEP_OUT
41};
42
43
44ThreadHandler::ThreadHandler(Thread* thread, Worker* worker,
45	DebuggerInterface* debuggerInterface,
46	BreakpointManager* breakpointManager)
47	:
48	fThread(thread),
49	fWorker(worker),
50	fDebuggerInterface(debuggerInterface),
51	fBreakpointManager(breakpointManager),
52	fStepMode(STEP_NONE),
53	fStepStatement(NULL),
54	fBreakpointAddress(0),
55	fPreviousInstructionPointer(0),
56	fPreviousFrameAddress(0),
57	fSingleStepping(false)
58{
59	fDebuggerInterface->AcquireReference();
60}
61
62
63ThreadHandler::~ThreadHandler()
64{
65	_ClearContinuationState();
66	fDebuggerInterface->ReleaseReference();
67}
68
69
70void
71ThreadHandler::Init()
72{
73	fWorker->ScheduleJob(new(std::nothrow) GetThreadStateJob(fDebuggerInterface,
74		fThread));
75}
76
77
78status_t
79ThreadHandler::SetBreakpointAndRun(target_addr_t address)
80{
81	status_t error = _InstallTemporaryBreakpoint(address);
82	if (error != B_OK)
83		return error;
84
85	fPreviousInstructionPointer = 0;
86	resume_thread(ThreadID());
87		// TODO: This should probably better be a DebuggerInterface method,
88		// but this method is used only when debugging a local team anyway.
89	// Pretend "step out" mode, so that the temporary breakpoint hit will not
90	// be ignored.
91	fStepMode = STEP_OUT;
92	fSingleStepping = false;
93
94	return B_OK;
95}
96
97
98bool
99ThreadHandler::HandleThreadDebugged(ThreadDebuggedEvent* event)
100{
101	return _HandleThreadStopped(NULL, THREAD_STOPPED_DEBUGGED);
102}
103
104
105bool
106ThreadHandler::HandleDebuggerCall(DebuggerCallEvent* event)
107{
108	BString message;
109	fDebuggerInterface->ReadMemoryString(event->Message(), 1024, message);
110	return _HandleThreadStopped(NULL, THREAD_STOPPED_DEBUGGER_CALL, message);
111}
112
113
114bool
115ThreadHandler::HandleBreakpointHit(BreakpointHitEvent* event)
116{
117	CpuState* cpuState = event->GetCpuState();
118	target_addr_t instructionPointer = cpuState->InstructionPointer();
119
120	TRACE_EVENTS("ThreadHandler::HandleBreakpointHit(): ip: %" B_PRIx64 "\n",
121		instructionPointer);
122
123	// check whether this is a temporary breakpoint we're waiting for
124	if (fBreakpointAddress != 0 && instructionPointer == fBreakpointAddress
125		&& fStepMode != STEP_NONE) {
126		if (_HandleBreakpointHitStep(cpuState))
127			return true;
128	} else {
129		// Might be a user breakpoint, but could as well be a temporary
130		// breakpoint of another thread.
131		AutoLocker<Team> locker(fThread->GetTeam());
132		Breakpoint* breakpoint = fThread->GetTeam()->BreakpointAtAddress(
133			cpuState->InstructionPointer());
134		bool continueThread = false;
135		if (breakpoint == NULL) {
136			// spurious breakpoint -- might be a temporary breakpoint, that has
137			// already been uninstalled
138			continueThread = true;
139		} else if (!breakpoint->HasEnabledUserBreakpoint()) {
140			// breakpoint of another thread or one that has been disabled in
141			// the meantime
142			continueThread = true;
143		}
144
145		if (continueThread) {
146			if (fSingleStepping) {
147				// We might have hit a just-installed software breakpoint and
148				// thus haven't stepped at all. Just try again.
149				if (fPreviousInstructionPointer == instructionPointer) {
150					fDebuggerInterface->SingleStepThread(ThreadID());
151					return true;
152				}
153
154				// That shouldn't happen. Try something reasonable anyway.
155				if (fStepMode != STEP_NONE) {
156					if (_HandleSingleStepStep(cpuState))
157						return true;
158				}
159			}
160
161			return false;
162		}
163	}
164
165	return _HandleThreadStopped(cpuState, THREAD_STOPPED_BREAKPOINT);
166}
167
168
169bool
170ThreadHandler::HandleWatchpointHit(WatchpointHitEvent* event)
171{
172	return _HandleThreadStopped(event->GetCpuState(),
173		THREAD_STOPPED_WATCHPOINT);
174}
175
176
177bool
178ThreadHandler::HandleSingleStep(SingleStepEvent* event)
179{
180	// Check whether we're stepping automatically.
181	if (fStepMode != STEP_NONE) {
182		if (_HandleSingleStepStep(event->GetCpuState()))
183			return true;
184	}
185
186	return _HandleThreadStopped(event->GetCpuState(),
187		THREAD_STOPPED_SINGLE_STEP);
188}
189
190
191bool
192ThreadHandler::HandleExceptionOccurred(ExceptionOccurredEvent* event)
193{
194	char buffer[256];
195	get_debug_exception_string(event->Exception(), buffer, sizeof(buffer));
196	return _HandleThreadStopped(NULL, THREAD_STOPPED_EXCEPTION, buffer);
197}
198
199
200void
201ThreadHandler::HandleThreadAction(uint32 action)
202{
203	AutoLocker<Team> locker(fThread->GetTeam());
204
205	if (fThread->State() == THREAD_STATE_UNKNOWN)
206		return;
207
208	// When stop is requested, thread must be running, otherwise stopped.
209	if (action == MSG_THREAD_STOP
210			? fThread->State() != THREAD_STATE_RUNNING
211			: fThread->State() != THREAD_STATE_STOPPED) {
212		return;
213	}
214
215	// When stepping we need a stack trace. Save it before unsetting the state.
216	CpuState* cpuState = fThread->GetCpuState();
217	StackTrace* stackTrace = fThread->GetStackTrace();
218	BReference<CpuState> cpuStateReference(cpuState);
219	BReference<StackTrace> stackTraceReference(stackTrace);
220
221	// When continuing the thread update thread state before actually issuing
222	// the command, since we need to unlock.
223	if (action != MSG_THREAD_STOP) {
224		_SetThreadState(THREAD_STATE_RUNNING, NULL, THREAD_STOPPED_UNKNOWN,
225			BString());
226	}
227
228	locker.Unlock();
229
230	switch (action) {
231		case MSG_THREAD_RUN:
232			fStepMode = STEP_NONE;
233			_RunThread(0);
234			return;
235		case MSG_THREAD_STOP:
236			fStepMode = STEP_NONE;
237			fDebuggerInterface->StopThread(ThreadID());
238			return;
239		case MSG_THREAD_STEP_OVER:
240		case MSG_THREAD_STEP_INTO:
241		case MSG_THREAD_STEP_OUT:
242			break;
243	}
244
245	TRACE_CONTROL("ThreadHandler::HandleThreadAction(MSG_THREAD_STEP_*)\n");
246
247	// We want to step. We need a stack trace for that purpose. If we don't
248	// have one yet, get it. Start with the CPU state.
249	if (stackTrace == NULL && cpuState == NULL) {
250		if (fDebuggerInterface->GetCpuState(fThread->ID(), cpuState) == B_OK)
251			cpuStateReference.SetTo(cpuState, true);
252	}
253
254	if (stackTrace == NULL && cpuState != NULL) {
255		if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
256				fThread->GetTeam(), this, cpuState, stackTrace, 0, 1,
257				false, false) == B_OK) {
258			stackTraceReference.SetTo(stackTrace, true);
259		}
260	}
261
262	if (stackTrace == NULL || stackTrace->CountFrames() == 0) {
263		_StepFallback();
264		return;
265	}
266
267	StackFrame* frame = stackTrace->FrameAt(0);
268
269	TRACE_CONTROL("  ip: %#" B_PRIx64 "\n", frame->InstructionPointer());
270
271	// When the thread is in a syscall, do the same for all step kinds: Stop it
272	// when it returns by means of a breakpoint.
273	if (frame->Type() == STACK_FRAME_TYPE_SYSCALL) {
274		// set a breakpoint at the CPU state's instruction pointer (points to
275		// the return address, unlike the stack frame's instruction pointer)
276// TODO: This is doesn't work correctly anymore. When stepping over a "syscall"
277// instruction the thread is stopped twice. The after the first step the PC is
278// incorrectly shown at the "syscall" instruction. Then we step again and are
279// stopped at the temporary breakpoint after the "syscall" instruction. There
280// are two problems. The first one is that we don't (cannot?) discriminate
281// between the thread being in a syscall (like in a blocking syscall) and the
282// thread having been stopped (or singled-stepped) at the end of the syscall.
283// The second issue is that the temporary breakpoint is probably not necessary
284// anymore, since single-stepping over "syscall" instructions should just work
285// as expected.
286		status_t error = _InstallTemporaryBreakpoint(
287			frame->GetCpuState()->InstructionPointer());
288		if (error != B_OK) {
289			_StepFallback();
290			return;
291		}
292
293		fStepMode = STEP_OUT;
294		_RunThread(frame->GetCpuState()->InstructionPointer());
295		return;
296	}
297
298	// For "step out" just set a temporary breakpoint on the return address.
299	if (action == MSG_THREAD_STEP_OUT) {
300		status_t error = _InstallTemporaryBreakpoint(frame->ReturnAddress());
301		if (error != B_OK) {
302			_StepFallback();
303			return;
304		}
305		fPreviousFrameAddress = frame->FrameAddress();
306		fStepMode = STEP_OUT;
307		_RunThread(frame->GetCpuState()->InstructionPointer());
308		return;
309	}
310
311	// For "step in" and "step over" we also need the source code statement at
312	// the current instruction pointer.
313	fStepStatement = _GetStatementAtInstructionPointer(frame);
314	if (fStepStatement == NULL) {
315		_StepFallback();
316		return;
317	}
318
319	TRACE_CONTROL("  statement: %#" B_PRIx64 " - %#" B_PRIx64 "\n",
320		fStepStatement->CoveringAddressRange().Start(),
321		fStepStatement->CoveringAddressRange().End());
322
323	if (action == MSG_THREAD_STEP_INTO) {
324		// step into
325		fStepMode = STEP_INTO;
326		_SingleStepThread(frame->GetCpuState()->InstructionPointer());
327	} else {
328		fPreviousFrameAddress = frame->FrameAddress();
329		// step over
330		fStepMode = STEP_OVER;
331		if (!_DoStepOver(frame->GetCpuState()))
332			_StepFallback();
333	}
334}
335
336
337void
338ThreadHandler::HandleThreadStateChanged()
339{
340	AutoLocker<Team> locker(fThread->GetTeam());
341
342	// cancel jobs for this thread
343	fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_CPU_STATE));
344	fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_STACK_TRACE));
345
346	// If the thread is stopped and has no CPU state yet, schedule a job.
347	if (fThread->State() == THREAD_STATE_STOPPED
348			&& fThread->GetCpuState() == NULL) {
349		fWorker->ScheduleJob(
350			new(std::nothrow) GetCpuStateJob(fDebuggerInterface, fThread));
351	}
352}
353
354
355void
356ThreadHandler::HandleCpuStateChanged()
357{
358	AutoLocker<Team> locker(fThread->GetTeam());
359
360	// cancel stack trace job for this thread
361	fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_STACK_TRACE));
362
363	// If the thread has a CPU state, but no stack trace yet, schedule a job.
364	if (fThread->GetCpuState() != NULL && fThread->GetStackTrace() == NULL) {
365		fWorker->ScheduleJob(
366			new(std::nothrow) GetStackTraceJob(fDebuggerInterface,
367				fDebuggerInterface->GetArchitecture(), fThread));
368	}
369}
370
371
372void
373ThreadHandler::HandleStackTraceChanged()
374{
375}
376
377
378status_t
379ThreadHandler::GetImageDebugInfo(Image* image, ImageDebugInfo*& _info)
380{
381	AutoLocker<Team> teamLocker(fThread->GetTeam());
382
383	if (image->GetImageDebugInfo() != NULL) {
384		_info = image->GetImageDebugInfo();
385		_info->AcquireReference();
386		return B_OK;
387	}
388
389	// Let's be lazy. If the image debug info has not been loaded yet, the user
390	// can't have seen any source code either.
391	return B_ENTRY_NOT_FOUND;
392}
393
394
395bool
396ThreadHandler::_HandleThreadStopped(CpuState* cpuState, uint32 stoppedReason,
397	const BString& stoppedReasonInfo)
398{
399	_ClearContinuationState();
400
401	AutoLocker<Team> locker(fThread->GetTeam());
402
403	_SetThreadState(THREAD_STATE_STOPPED, cpuState, stoppedReason,
404		stoppedReasonInfo);
405
406	return true;
407}
408
409
410void
411ThreadHandler::_SetThreadState(uint32 state, CpuState* cpuState,
412	uint32 stoppedReason, const BString& stoppedReasonInfo)
413{
414	fThread->SetState(state, stoppedReason, stoppedReasonInfo);
415	fThread->SetCpuState(cpuState);
416}
417
418
419Statement*
420ThreadHandler::_GetStatementAtInstructionPointer(StackFrame* frame)
421{
422	AutoLocker<Team> locker(fThread->GetTeam());
423
424	FunctionInstance* functionInstance = frame->Function();
425	if (functionInstance == NULL)
426		return NULL;
427	FunctionDebugInfo* function = functionInstance->GetFunctionDebugInfo();
428
429	// If there's source code attached to the function, we can just get the
430	// statement.
431//	SourceCode* sourceCode = function->GetSourceCode();
432//	if (sourceCode != NULL) {
433//		Statement* statement = sourceCode->StatementAtAddress(
434//			frame->InstructionPointer());
435//		if (statement != NULL)
436//			statement->AcquireReference();
437//		return statement;
438//	}
439
440	locker.Unlock();
441
442	// We need to get the statement from the debug info of the function.
443	Statement* statement;
444	if (function->GetSpecificImageDebugInfo()->GetStatement(function,
445			frame->InstructionPointer(), statement) != B_OK) {
446		return NULL;
447	}
448
449	return statement;
450}
451
452
453void
454ThreadHandler::_StepFallback()
455{
456	fStepMode = STEP_NONE;
457	_SingleStepThread(0);
458}
459
460
461bool
462ThreadHandler::_DoStepOver(CpuState* cpuState)
463{
464	TRACE_CONTROL("ThreadHandler::_DoStepOver()\n");
465
466	// The basic strategy is to single-step out of the statement like for
467	// "step into", only we have to avoid stepping into subroutines. Hence we
468	// check whether the current instruction is a subroutine call. If not, we
469	// just single-step, otherwise we set a breakpoint after the instruction.
470	InstructionInfo info;
471	if (fDebuggerInterface->GetArchitecture()->GetInstructionInfo(
472			cpuState->InstructionPointer(), info, cpuState) != B_OK) {
473		TRACE_CONTROL("  failed to get instruction info\n");
474		return false;
475	}
476
477	if (info.Type() != INSTRUCTION_TYPE_SUBROUTINE_CALL) {
478		_SingleStepThread(cpuState->InstructionPointer());
479
480		TRACE_CONTROL("  not a subroutine call\n");
481		return true;
482	}
483
484	TRACE_CONTROL("  subroutine call -- installing breakpoint at address "
485		"%#" B_PRIx64 "\n", info.Address() + info.Size());
486
487	fThread->SetExecutedSubroutine(info.TargetAddress());
488	if (_InstallTemporaryBreakpoint(info.Address() + info.Size()) != B_OK)
489		return false;
490
491	_RunThread(cpuState->InstructionPointer());
492	return true;
493}
494
495
496status_t
497ThreadHandler::_InstallTemporaryBreakpoint(target_addr_t address)
498{
499	_UninstallTemporaryBreakpoint();
500
501	status_t error = fBreakpointManager->InstallTemporaryBreakpoint(address,
502		this);
503	if (error != B_OK)
504		return error;
505
506	fBreakpointAddress = address;
507	return B_OK;
508}
509
510
511void
512ThreadHandler::_UninstallTemporaryBreakpoint()
513{
514	if (fBreakpointAddress == 0)
515		return;
516
517	fBreakpointManager->UninstallTemporaryBreakpoint(fBreakpointAddress, this);
518	fBreakpointAddress = 0;
519}
520
521
522void
523ThreadHandler::_ClearContinuationState()
524{
525	_UninstallTemporaryBreakpoint();
526
527	if (fStepStatement != NULL) {
528		fStepStatement->ReleaseReference();
529		fStepStatement = NULL;
530	}
531
532	fStepMode = STEP_NONE;
533	fSingleStepping = false;
534}
535
536
537void
538ThreadHandler::_RunThread(target_addr_t instructionPointer)
539{
540	fPreviousInstructionPointer = instructionPointer;
541	fDebuggerInterface->ContinueThread(ThreadID());
542	fSingleStepping = false;
543}
544
545
546void
547ThreadHandler::_SingleStepThread(target_addr_t instructionPointer)
548{
549	fPreviousInstructionPointer = instructionPointer;
550	fDebuggerInterface->SingleStepThread(ThreadID());
551	fSingleStepping = true;
552}
553
554
555bool
556ThreadHandler::_HandleBreakpointHitStep(CpuState* cpuState)
557{
558	// in any case uninstall the temporary breakpoint
559	_UninstallTemporaryBreakpoint();
560
561	switch (fStepMode) {
562		case STEP_OVER:
563		{
564			StackTrace* stackTrace = fThread->GetStackTrace();
565			BReference<StackTrace> stackTraceReference(stackTrace);
566
567			if (stackTrace == NULL && cpuState != NULL) {
568				if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
569						fThread->GetTeam(), this, cpuState, stackTrace, 0, 1,
570						false, false) == B_OK) {
571					stackTraceReference.SetTo(stackTrace, true);
572				}
573			}
574			if (stackTrace != NULL) {
575				StackFrame* frame = stackTrace->FrameAt(0);
576				// If we're not in the same frame we started in,
577				// keep executing.
578				if (frame != NULL && fPreviousFrameAddress
579						!= frame->FrameAddress()) {
580					status_t error = _InstallTemporaryBreakpoint(
581						cpuState->InstructionPointer());
582					if (error != B_OK)
583						_StepFallback();
584					else
585						_RunThread(cpuState->InstructionPointer());
586					return true;
587				}
588			}
589
590			// If we're still in the statement, we continue single-stepping,
591			// otherwise we're done.
592			if (fStepStatement->ContainsAddress(
593					cpuState->InstructionPointer())) {
594				if (!_DoStepOver(cpuState))
595					_StepFallback();
596				return true;
597			}
598			fPreviousFrameAddress = 0;
599			return false;
600		}
601
602		case STEP_INTO:
603			// Should never happen -- we don't set a breakpoint in this case.
604			return false;
605
606		case STEP_OUT:
607		{
608			// That's the return address, so we're done in theory,
609			// unless we're a recursive function. Check if we've actually
610			// exited the previous stack frame or not.
611			fThread->SetExecutedSubroutine(cpuState->InstructionPointer());
612			target_addr_t framePointer = cpuState->StackFramePointer();
613			bool hasExitedFrame = fDebuggerInterface->GetArchitecture()
614				->StackGrowthDirection() == STACK_GROWTH_DIRECTION_POSITIVE
615					? framePointer < fPreviousFrameAddress
616					: framePointer > fPreviousFrameAddress;
617
618			if (!hasExitedFrame) {
619				status_t error = _InstallTemporaryBreakpoint(
620					cpuState->InstructionPointer());
621				if (error != B_OK)
622					_StepFallback();
623				else
624					_RunThread(cpuState->InstructionPointer());
625				return true;
626			}
627			fPreviousFrameAddress = 0;
628		}
629
630		default:
631			return false;
632	}
633}
634
635
636bool
637ThreadHandler::_HandleSingleStepStep(CpuState* cpuState)
638{
639	TRACE_CONTROL("ThreadHandler::_HandleSingleStepStep(): ip: %" B_PRIx64 "\n",
640		cpuState->InstructionPointer());
641
642	switch (fStepMode) {
643		case STEP_INTO:
644		{
645			// We continue stepping as long as we're in the statement.
646			if (fStepStatement->ContainsAddress(cpuState->InstructionPointer())) {
647				_SingleStepThread(cpuState->InstructionPointer());
648				return true;
649			}
650
651			StackTrace* stackTrace = fThread->GetStackTrace();
652			BReference<StackTrace> stackTraceReference(stackTrace);
653
654			if (stackTrace == NULL && cpuState != NULL) {
655				if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
656						fThread->GetTeam(), this, cpuState, stackTrace, 0, 1,
657						false, false) == B_OK) {
658					stackTraceReference.SetTo(stackTrace, true);
659				}
660			}
661
662			if (stackTrace != NULL) {
663				StackFrame* frame = stackTrace->FrameAt(0);
664				Image* image = frame->GetImage();
665				ImageDebugInfo* info = NULL;
666				if (GetImageDebugInfo(image, info) != B_OK)
667					return false;
668
669				BReference<ImageDebugInfo>(info, true);
670				if (info->GetAddressSectionType(
671						cpuState->InstructionPointer())
672						== ADDRESS_SECTION_TYPE_PLT) {
673					_SingleStepThread(cpuState->InstructionPointer());
674					return true;
675				}
676			}
677			return false;
678		}
679
680		case STEP_OVER:
681		{
682			// If we have stepped out of the statement, we're done.
683			if (!fStepStatement->ContainsAddress(cpuState->InstructionPointer())) {
684				StackTrace* stackTrace = fThread->GetStackTrace();
685				BReference<StackTrace> stackTraceReference(stackTrace);
686				if (stackTrace == NULL && cpuState != NULL) {
687					if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
688							fThread->GetTeam(), this, cpuState, stackTrace, 0,
689							1, false, false) == B_OK) {
690						stackTraceReference.SetTo(stackTrace, true);
691					}
692				}
693
694				if (stackTrace != NULL && stackTrace->FrameAt(0)
695						->FrameAddress() != fPreviousFrameAddress) {
696					fThread->SetExecutedSubroutine(
697						cpuState->InstructionPointer());
698				}
699
700				return false;
701			}
702			return _DoStepOver(cpuState);
703		}
704
705		case STEP_OUT:
706			// We never single-step in this case.
707		default:
708			return false;
709	}
710}
711