xref: /haiku/src/kits/debugger/controllers/ThreadHandler.cpp (revision 022f01ea4dde29ecab1d6217087a224a777cb93c)
1 /*
2  * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2010-2016, 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 <AutoDeleter.h>
15 #include <AutoLocker.h>
16 #include <Variant.h>
17 
18 #include "Architecture.h"
19 #include "BreakpointManager.h"
20 #include "CpuState.h"
21 #include "DebugEvent.h"
22 #include "DebuggerInterface.h"
23 #include "ExpressionInfo.h"
24 #include "FunctionInstance.h"
25 #include "ImageDebugInfo.h"
26 #include "InstructionInfo.h"
27 #include "Jobs.h"
28 #include "MessageCodes.h"
29 #include "Register.h"
30 #include "SignalDispositionTypes.h"
31 #include "SourceCode.h"
32 #include "SourceLanguage.h"
33 #include "SpecificImageDebugInfo.h"
34 #include "StackTrace.h"
35 #include "Statement.h"
36 #include "SyntheticPrimitiveType.h"
37 #include "Team.h"
38 #include "Tracing.h"
39 #include "Value.h"
40 #include "ValueLocation.h"
41 #include "Worker.h"
42 
43 
44 // step modes
45 enum {
46 	STEP_NONE,
47 	STEP_OVER,
48 	STEP_INTO,
49 	STEP_OUT,
50 	STEP_UNTIL
51 };
52 
53 
54 class ExpressionEvaluationListener : public ExpressionInfo::Listener {
55 public:
ExpressionEvaluationListener(ThreadHandler * handler)56 	ExpressionEvaluationListener(ThreadHandler* handler)
57 	:
58 	fHandler(handler)
59 	{
60 		fHandler->AcquireReference();
61 	}
62 
~ExpressionEvaluationListener()63 	~ExpressionEvaluationListener()
64 	{
65 		fHandler->ReleaseReference();
66 	}
67 
ExpressionEvaluated(ExpressionInfo * info,status_t result,ExpressionResult * value)68 	virtual void ExpressionEvaluated(ExpressionInfo* info, status_t result,
69 		ExpressionResult* value)
70 	{
71 		fHandler->_HandleBreakpointConditionEvaluated(value);
72 	}
73 
74 private:
75 	ThreadHandler* fHandler;
76 };
77 
78 
ThreadHandler(::Thread * thread,Worker * worker,DebuggerInterface * debuggerInterface,JobListener * jobListener,BreakpointManager * breakpointManager)79 ThreadHandler::ThreadHandler(::Thread* thread, Worker* worker,
80 	DebuggerInterface* debuggerInterface, JobListener* jobListener,
81 	BreakpointManager* breakpointManager)
82 	:
83 	fThread(thread),
84 	fWorker(worker),
85 	fDebuggerInterface(debuggerInterface),
86 	fJobListener(jobListener),
87 	fBreakpointManager(breakpointManager),
88 	fStepMode(STEP_NONE),
89 	fStepStatement(NULL),
90 	fBreakpointAddress(0),
91 	fSteppedOverFunctionAddress(0),
92 	fPreviousInstructionPointer(0),
93 	fPreviousFrameAddress(0),
94 	fSingleStepping(false),
95 	fConditionWaitSem(-1),
96 	fConditionResult(NULL)
97 {
98 	fDebuggerInterface->AcquireReference();
99 }
100 
101 
~ThreadHandler()102 ThreadHandler::~ThreadHandler()
103 {
104 	_ClearContinuationState();
105 	fDebuggerInterface->ReleaseReference();
106 
107 	if (fConditionWaitSem > 0)
108 		delete_sem(fConditionWaitSem);
109 }
110 
111 
112 void
Init()113 ThreadHandler::Init()
114 {
115 	fWorker->ScheduleJob(new(std::nothrow) GetThreadStateJob(fDebuggerInterface,
116 		fThread), fJobListener);
117 	fConditionWaitSem = create_sem(0, "breakpoint condition waiter");
118 }
119 
120 
121 status_t
SetBreakpointAndRun(target_addr_t address)122 ThreadHandler::SetBreakpointAndRun(target_addr_t address)
123 {
124 	status_t error = _InstallTemporaryBreakpoint(address);
125 	if (error != B_OK)
126 		return error;
127 
128 	fPreviousInstructionPointer = 0;
129 	fDebuggerInterface->ContinueThread(ThreadID());
130 
131 	// Pretend "step out" mode, so that the temporary breakpoint hit will not
132 	// be ignored.
133 	fStepMode = STEP_OUT;
134 	fSingleStepping = false;
135 
136 	return B_OK;
137 }
138 
139 
140 bool
HandleThreadDebugged(ThreadDebuggedEvent * event,const BString & stoppedReason)141 ThreadHandler::HandleThreadDebugged(ThreadDebuggedEvent* event,
142 	const BString& stoppedReason)
143 {
144 	return _HandleThreadStopped(NULL, THREAD_STOPPED_DEBUGGED, stoppedReason);
145 }
146 
147 
148 bool
HandleDebuggerCall(DebuggerCallEvent * event)149 ThreadHandler::HandleDebuggerCall(DebuggerCallEvent* event)
150 {
151 	BString message;
152 	fDebuggerInterface->ReadMemoryString(event->Message(), 1024, message);
153 	return _HandleThreadStopped(NULL, THREAD_STOPPED_DEBUGGER_CALL, message);
154 }
155 
156 
157 bool
HandleBreakpointHit(BreakpointHitEvent * event)158 ThreadHandler::HandleBreakpointHit(BreakpointHitEvent* event)
159 {
160 	CpuState* cpuState = event->GetCpuState();
161 	target_addr_t instructionPointer = cpuState->InstructionPointer();
162 
163 	TRACE_EVENTS("ThreadHandler::HandleBreakpointHit(): ip: %" B_PRIx64 "\n",
164 		instructionPointer);
165 
166 	// check whether this is a temporary breakpoint we're waiting for
167 	if (fBreakpointAddress != 0 && instructionPointer == fBreakpointAddress
168 		&& fStepMode != STEP_NONE) {
169 		if (fStepMode != STEP_UNTIL && _HandleBreakpointHitStep(cpuState))
170 			return true;
171 	} else {
172 		// Might be a user breakpoint, but could as well be a temporary
173 		// breakpoint of another thread.
174 		AutoLocker<Team> locker(fThread->GetTeam());
175 		Breakpoint* breakpoint = fThread->GetTeam()->BreakpointAtAddress(
176 			cpuState->InstructionPointer());
177 		bool continueThread = false;
178 		if (breakpoint == NULL) {
179 			// spurious breakpoint -- might be a temporary breakpoint, that has
180 			// already been uninstalled
181 			continueThread = true;
182 		} else if (!breakpoint->HasEnabledUserBreakpoint()) {
183 			// breakpoint of another thread or one that has been disabled in
184 			// the meantime
185 			continueThread = true;
186 		}
187 
188 		if (continueThread) {
189 			if (fSingleStepping) {
190 				// We might have hit a just-installed software breakpoint and
191 				// thus haven't stepped at all. Just try again.
192 				if (fPreviousInstructionPointer == instructionPointer) {
193 					fDebuggerInterface->SingleStepThread(ThreadID());
194 					return true;
195 				}
196 
197 				// That shouldn't happen. Try something reasonable anyway.
198 				if (fStepMode != STEP_NONE) {
199 					if (_HandleSingleStepStep(cpuState))
200 						return true;
201 				}
202 			}
203 
204 			return false;
205 		} else {
206 			locker.Unlock();
207 			if (_HandleBreakpointConditionIfNeeded(cpuState))
208 				return true;
209 
210 			locker.Lock();
211 		}
212 	}
213 
214 	return _HandleThreadStopped(cpuState, THREAD_STOPPED_BREAKPOINT);
215 }
216 
217 
218 bool
HandleWatchpointHit(WatchpointHitEvent * event)219 ThreadHandler::HandleWatchpointHit(WatchpointHitEvent* event)
220 {
221 	return _HandleThreadStopped(event->GetCpuState(),
222 		THREAD_STOPPED_WATCHPOINT);
223 }
224 
225 
226 bool
HandleSingleStep(SingleStepEvent * event)227 ThreadHandler::HandleSingleStep(SingleStepEvent* event)
228 {
229 	// Check whether we're stepping automatically.
230 	if (fStepMode != STEP_NONE) {
231 		if (_HandleSingleStepStep(event->GetCpuState()))
232 			return true;
233 	}
234 
235 	return _HandleThreadStopped(event->GetCpuState(),
236 		THREAD_STOPPED_SINGLE_STEP);
237 }
238 
239 
240 bool
HandleExceptionOccurred(ExceptionOccurredEvent * event)241 ThreadHandler::HandleExceptionOccurred(ExceptionOccurredEvent* event)
242 {
243 	char buffer[256];
244 	get_debug_exception_string(event->Exception(), buffer, sizeof(buffer));
245 	return _HandleThreadStopped(NULL, THREAD_STOPPED_EXCEPTION, buffer);
246 }
247 
248 
249 bool
HandleSignalReceived(SignalReceivedEvent * event)250 ThreadHandler::HandleSignalReceived(SignalReceivedEvent* event)
251 {
252 	::Team* team = fThread->GetTeam();
253 	AutoLocker<Team> locker(team);
254 
255 	const SignalInfo& info = event->GetSignalInfo();
256 	int32 signal = info.Signal();
257 	int32 disposition = team->SignalDispositionFor(signal);
258 
259 	switch (disposition) {
260 		case SIGNAL_DISPOSITION_IGNORE:
261 			return false;
262 		case SIGNAL_DISPOSITION_STOP_AT_SIGNAL_HANDLER:
263 		{
264 			const struct sigaction& handlerInfo = info.Handler();
265 			target_addr_t address = 0;
266 			if ((handlerInfo.sa_flags & SA_SIGINFO) != 0)
267 				address = (target_addr_t)handlerInfo.sa_sigaction;
268 			else
269 				address = (target_addr_t)handlerInfo.sa_handler;
270 
271 			if (address == (target_addr_t)SIG_DFL
272 				|| address == (target_addr_t)SIG_IGN
273 				|| address == (target_addr_t)SIG_HOLD) {
274 				address = 0;
275 			}
276 
277 			if (address != 0 && _InstallTemporaryBreakpoint(address) == B_OK
278 				&& fDebuggerInterface->ContinueThread(ThreadID()) == B_OK) {
279 				fStepMode = STEP_UNTIL;
280 				return true;
281 			}
282 
283 			// fall through if no handler or if we failed to
284 			// set a breakpoint at the handler
285 		}
286 		case SIGNAL_DISPOSITION_STOP_AT_RECEIPT:
287 		{
288 			BString stopReason;
289 			stopReason.SetToFormat("Received signal %" B_PRId32 " (%s)",
290 				signal, strsignal(signal));
291 			return _HandleThreadStopped(NULL, THREAD_STOPPED_DEBUGGED,
292 				stopReason);
293 		}
294 		default:
295 			break;
296 	}
297 
298 	return false;
299 }
300 
301 
302 void
HandleThreadAction(uint32 action,target_addr_t address)303 ThreadHandler::HandleThreadAction(uint32 action, target_addr_t address)
304 {
305 	AutoLocker<Team> locker(fThread->GetTeam());
306 
307 	if (fThread->State() == THREAD_STATE_UNKNOWN)
308 		return;
309 
310 	// When stop is requested, thread must be running, otherwise stopped.
311 	if (action == MSG_THREAD_STOP
312 			? fThread->State() != THREAD_STATE_RUNNING
313 			: fThread->State() != THREAD_STATE_STOPPED) {
314 		return;
315 	}
316 
317 	// When stepping we need a stack trace. Save it before unsetting the state.
318 	CpuState* cpuState = fThread->GetCpuState();
319 	StackTrace* stackTrace = fThread->GetStackTrace();
320 	BReference<CpuState> cpuStateReference(cpuState);
321 	BReference<StackTrace> stackTraceReference(stackTrace);
322 
323 	if (action == MSG_THREAD_SET_ADDRESS) {
324 		_HandleSetAddress(cpuState, address);
325 		return;
326 	}
327 
328 	// When continuing the thread update thread state before actually issuing
329 	// the command, since we need to unlock.
330 	if (action != MSG_THREAD_STOP) {
331 		_SetThreadState(THREAD_STATE_RUNNING, NULL, THREAD_STOPPED_UNKNOWN,
332 			BString());
333 	}
334 
335 	locker.Unlock();
336 
337 	switch (action) {
338 		case MSG_THREAD_RUN:
339 			fStepMode = address != 0 ? STEP_UNTIL : STEP_NONE;
340 			if (address != 0)
341 				_InstallTemporaryBreakpoint(address);
342 			_RunThread(0);
343 			return;
344 		case MSG_THREAD_STOP:
345 			fStepMode = STEP_NONE;
346 			if (fDebuggerInterface->StopThread(ThreadID()) == B_OK)
347 				fThread->SetStopRequestPending();
348 			return;
349 		case MSG_THREAD_STEP_OVER:
350 		case MSG_THREAD_STEP_INTO:
351 		case MSG_THREAD_STEP_OUT:
352 			break;
353 	}
354 
355 	TRACE_CONTROL("ThreadHandler::HandleThreadAction(MSG_THREAD_STEP_*)\n");
356 
357 	// We want to step. We need a stack trace for that purpose. If we don't
358 	// have one yet, get it. Start with the CPU state.
359 	if (stackTrace == NULL && cpuState == NULL) {
360 		if (fDebuggerInterface->GetCpuState(fThread->ID(), cpuState) == B_OK)
361 			cpuStateReference.SetTo(cpuState, true);
362 	}
363 
364 	if (stackTrace == NULL && cpuState != NULL) {
365 		if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
366 				fThread->GetTeam(), this, cpuState, stackTrace, NULL, 1,
367 				false, false) == B_OK) {
368 			stackTraceReference.SetTo(stackTrace, true);
369 		}
370 	}
371 
372 	if (stackTrace == NULL || stackTrace->CountFrames() == 0) {
373 		_StepFallback();
374 		return;
375 	}
376 
377 	StackFrame* frame = stackTrace->FrameAt(0);
378 
379 	TRACE_CONTROL("  ip: %#" B_PRIx64 "\n", frame->InstructionPointer());
380 
381 	target_addr_t frameIP = frame->GetCpuState()->InstructionPointer();
382 	// When the thread is in a syscall, do the same for all step kinds: Stop it
383 	// when it returns by means of a breakpoint.
384 	if (frame->Type() == STACK_FRAME_TYPE_SYSCALL) {
385 		// set a breakpoint at the CPU state's instruction pointer (points to
386 		// the return address, unlike the stack frame's instruction pointer)
387 // TODO: This is doesn't work correctly anymore. When stepping over a "syscall"
388 // instruction the thread is stopped twice. The after the first step the PC is
389 // incorrectly shown at the "syscall" instruction. Then we step again and are
390 // stopped at the temporary breakpoint after the "syscall" instruction. There
391 // are two problems. The first one is that we don't (cannot?) discriminate
392 // between the thread being in a syscall (like in a blocking syscall) and the
393 // thread having been stopped (or singled-stepped) at the end of the syscall.
394 // The second issue is that the temporary breakpoint is probably not necessary
395 // anymore, since single-stepping over "syscall" instructions should just work
396 // as expected.
397 		status_t error = _InstallTemporaryBreakpoint(frameIP);
398 		if (error != B_OK) {
399 			_StepFallback();
400 			return;
401 		}
402 
403 		fStepMode = STEP_OUT;
404 		_RunThread(frameIP);
405 		return;
406 	}
407 
408 	// For "step out" just set a temporary breakpoint on the return address.
409 	if (action == MSG_THREAD_STEP_OUT) {
410 		status_t error = _InstallTemporaryBreakpoint(frame->ReturnAddress());
411 		if (error != B_OK) {
412 			_StepFallback();
413 			return;
414 		}
415 		fPreviousInstructionPointer = frameIP;
416 		fPreviousFrameAddress = frame->FrameAddress();
417 		fStepMode = STEP_OUT;
418 		_RunThread(frameIP);
419 		return;
420 	}
421 
422 	// For "step in" and "step over" we also need the source code statement at
423 	// the current instruction pointer.
424 	fStepStatement = _GetStatementAtInstructionPointer(frame);
425 	if (fStepStatement == NULL) {
426 		_StepFallback();
427 		return;
428 	}
429 
430 	TRACE_CONTROL("  statement: %#" B_PRIx64 " - %#" B_PRIx64 "\n",
431 		fStepStatement->CoveringAddressRange().Start(),
432 		fStepStatement->CoveringAddressRange().End());
433 
434 	if (action == MSG_THREAD_STEP_INTO) {
435 		// step into
436 		fStepMode = STEP_INTO;
437 		_SingleStepThread(frameIP);
438 	} else {
439 		fPreviousFrameAddress = frame->FrameAddress();
440 		// step over
441 		fStepMode = STEP_OVER;
442 		if (!_DoStepOver(frame->GetCpuState()))
443 			_StepFallback();
444 	}
445 }
446 
447 
448 void
HandleThreadStateChanged()449 ThreadHandler::HandleThreadStateChanged()
450 {
451 	AutoLocker<Team> locker(fThread->GetTeam());
452 
453 	// cancel jobs for this thread
454 	fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_CPU_STATE));
455 	fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_STACK_TRACE));
456 
457 	// If the thread is stopped and has no CPU state yet, schedule a job.
458 	if (fThread->State() == THREAD_STATE_STOPPED
459 			&& fThread->GetCpuState() == NULL) {
460 		fWorker->ScheduleJob(
461 			new(std::nothrow) GetCpuStateJob(fDebuggerInterface, fThread),
462 			fJobListener);
463 	}
464 }
465 
466 
467 void
HandleCpuStateChanged()468 ThreadHandler::HandleCpuStateChanged()
469 {
470 	AutoLocker<Team> locker(fThread->GetTeam());
471 
472 	// cancel stack trace job for this thread
473 	fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_STACK_TRACE));
474 
475 	// If the thread has a CPU state, but no stack trace yet, schedule a job.
476 	if (fThread->GetCpuState() != NULL && fThread->GetStackTrace() == NULL) {
477 		fWorker->ScheduleJob(
478 			new(std::nothrow) GetStackTraceJob(fDebuggerInterface,
479 				fJobListener, fDebuggerInterface->GetArchitecture(),
480 				fThread), fJobListener);
481 	}
482 }
483 
484 
485 void
HandleStackTraceChanged()486 ThreadHandler::HandleStackTraceChanged()
487 {
488 }
489 
490 
491 status_t
GetImageDebugInfo(Image * image,ImageDebugInfo * & _info)492 ThreadHandler::GetImageDebugInfo(Image* image, ImageDebugInfo*& _info)
493 {
494 	AutoLocker<Team> teamLocker(fThread->GetTeam());
495 
496 	if (image->GetImageDebugInfo() != NULL) {
497 		_info = image->GetImageDebugInfo();
498 		_info->AcquireReference();
499 		return B_OK;
500 	}
501 
502 	// Let's be lazy. If the image debug info has not been loaded yet, the user
503 	// can't have seen any source code either.
504 	return B_ENTRY_NOT_FOUND;
505 }
506 
507 
508 bool
_HandleThreadStopped(CpuState * cpuState,uint32 stoppedReason,const BString & stoppedReasonInfo)509 ThreadHandler::_HandleThreadStopped(CpuState* cpuState, uint32 stoppedReason,
510 	const BString& stoppedReasonInfo)
511 {
512 	_ClearContinuationState();
513 
514 	AutoLocker<Team> locker(fThread->GetTeam());
515 
516 	_SetThreadState(THREAD_STATE_STOPPED, cpuState, stoppedReason,
517 		stoppedReasonInfo);
518 
519 	return true;
520 }
521 
522 
523 bool
_HandleSetAddress(CpuState * state,target_addr_t address)524 ThreadHandler::_HandleSetAddress(CpuState* state, target_addr_t address)
525 {
526 	CpuState* newState = NULL;
527 	if (state->Clone(newState) != B_OK)
528 		return false;
529 	BReference<CpuState> stateReference(newState, true);
530 
531 	newState->SetInstructionPointer(address);
532 	if (fDebuggerInterface->SetCpuState(fThread->ID(), newState) != B_OK)
533 		return false;
534 
535 	AutoLocker<Team> locker(fThread->GetTeam());
536 	fThread->SetStackTrace(NULL);
537 	fThread->SetCpuState(newState);
538 
539 	return true;
540 }
541 
542 
543 void
_SetThreadState(uint32 state,CpuState * cpuState,uint32 stoppedReason,const BString & stoppedReasonInfo)544 ThreadHandler::_SetThreadState(uint32 state, CpuState* cpuState,
545 	uint32 stoppedReason, const BString& stoppedReasonInfo)
546 {
547 	fThread->SetState(state, stoppedReason, stoppedReasonInfo);
548 	fThread->SetCpuState(cpuState);
549 }
550 
551 
552 Statement*
_GetStatementAtInstructionPointer(StackFrame * frame)553 ThreadHandler::_GetStatementAtInstructionPointer(StackFrame* frame)
554 {
555 	AutoLocker<Team> locker(fThread->GetTeam());
556 
557 	FunctionInstance* functionInstance = frame->Function();
558 	if (functionInstance == NULL)
559 		return NULL;
560 	FunctionDebugInfo* function = functionInstance->GetFunctionDebugInfo();
561 
562 	// If there's source code attached to the function, we can just get the
563 	// statement.
564 //	SourceCode* sourceCode = function->GetSourceCode();
565 //	if (sourceCode != NULL) {
566 //		Statement* statement = sourceCode->StatementAtAddress(
567 //			frame->InstructionPointer());
568 //		if (statement != NULL)
569 //			statement->AcquireReference();
570 //		return statement;
571 //	}
572 
573 	locker.Unlock();
574 
575 	// We need to get the statement from the debug info of the function.
576 	Statement* statement;
577 	if (function->GetSpecificImageDebugInfo()->GetStatement(function,
578 			frame->InstructionPointer(), statement) != B_OK) {
579 		return NULL;
580 	}
581 
582 	return statement;
583 }
584 
585 
586 void
_StepFallback()587 ThreadHandler::_StepFallback()
588 {
589 	fStepMode = STEP_NONE;
590 	_SingleStepThread(0);
591 }
592 
593 
594 bool
_DoStepOver(CpuState * cpuState)595 ThreadHandler::_DoStepOver(CpuState* cpuState)
596 {
597 	TRACE_CONTROL("ThreadHandler::_DoStepOver()\n");
598 
599 	// The basic strategy is to single-step out of the statement like for
600 	// "step into", only we have to avoid stepping into subroutines. Hence we
601 	// check whether the current instruction is a subroutine call. If not, we
602 	// just single-step, otherwise we set a breakpoint after the instruction.
603 	InstructionInfo info;
604 	if (fDebuggerInterface->GetArchitecture()->GetInstructionInfo(
605 			cpuState->InstructionPointer(), info, cpuState) != B_OK) {
606 		TRACE_CONTROL("  failed to get instruction info\n");
607 		return false;
608 	}
609 
610 	if (info.Type() != INSTRUCTION_TYPE_SUBROUTINE_CALL) {
611 		_SingleStepThread(cpuState->InstructionPointer());
612 
613 		TRACE_CONTROL("  not a subroutine call\n");
614 		return true;
615 	}
616 
617 	TRACE_CONTROL("  subroutine call -- installing breakpoint at address "
618 		"%#" B_PRIx64 "\n", info.Address() + info.Size());
619 
620 	if (_InstallTemporaryBreakpoint(info.Address() + info.Size()) != B_OK)
621 		return false;
622 
623 	fSteppedOverFunctionAddress = info.TargetAddress();
624 
625 	_RunThread(cpuState->InstructionPointer());
626 	return true;
627 }
628 
629 
630 status_t
_InstallTemporaryBreakpoint(target_addr_t address)631 ThreadHandler::_InstallTemporaryBreakpoint(target_addr_t address)
632 {
633 	_UninstallTemporaryBreakpoint();
634 
635 	status_t error = fBreakpointManager->InstallTemporaryBreakpoint(address,
636 		this);
637 	if (error != B_OK)
638 		return error;
639 
640 	fBreakpointAddress = address;
641 	return B_OK;
642 }
643 
644 
645 void
_UninstallTemporaryBreakpoint()646 ThreadHandler::_UninstallTemporaryBreakpoint()
647 {
648 	if (fBreakpointAddress == 0)
649 		return;
650 
651 	fBreakpointManager->UninstallTemporaryBreakpoint(fBreakpointAddress, this);
652 	fBreakpointAddress = 0;
653 }
654 
655 
656 void
_ClearContinuationState()657 ThreadHandler::_ClearContinuationState()
658 {
659 	_UninstallTemporaryBreakpoint();
660 
661 	if (fStepStatement != NULL) {
662 		fStepStatement->ReleaseReference();
663 		fStepStatement = NULL;
664 	}
665 
666 	fStepMode = STEP_NONE;
667 	fSingleStepping = false;
668 }
669 
670 
671 void
_RunThread(target_addr_t instructionPointer)672 ThreadHandler::_RunThread(target_addr_t instructionPointer)
673 {
674 	fPreviousInstructionPointer = instructionPointer;
675 	fDebuggerInterface->ContinueThread(ThreadID());
676 	fSingleStepping = false;
677 }
678 
679 
680 void
_SingleStepThread(target_addr_t instructionPointer)681 ThreadHandler::_SingleStepThread(target_addr_t instructionPointer)
682 {
683 	fPreviousInstructionPointer = instructionPointer;
684 	fDebuggerInterface->SingleStepThread(ThreadID());
685 	fSingleStepping = true;
686 }
687 
688 
689 bool
_HandleBreakpointHitStep(CpuState * cpuState)690 ThreadHandler::_HandleBreakpointHitStep(CpuState* cpuState)
691 {
692 	// in any case uninstall the temporary breakpoint
693 	_UninstallTemporaryBreakpoint();
694 
695 	switch (fStepMode) {
696 		case STEP_OVER:
697 		{
698 			StackTrace* stackTrace = fThread->GetStackTrace();
699 			BReference<StackTrace> stackTraceReference(stackTrace);
700 
701 			if (stackTrace == NULL && cpuState != NULL) {
702 				if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
703 						fThread->GetTeam(), this, cpuState, stackTrace, NULL,
704 						1, false, false) == B_OK) {
705 					stackTraceReference.SetTo(stackTrace, true);
706 				}
707 			}
708 			if (stackTrace != NULL) {
709 				StackFrame* frame = stackTrace->FrameAt(0);
710 				// If we're not in the same frame we started in,
711 				// keep executing.
712 				if (frame != NULL && fPreviousFrameAddress
713 						!= frame->FrameAddress()) {
714 					status_t error = _InstallTemporaryBreakpoint(
715 						cpuState->InstructionPointer());
716 					if (error != B_OK)
717 						_StepFallback();
718 					else
719 						_RunThread(cpuState->InstructionPointer());
720 					return true;
721 				}
722 			}
723 
724 			if (fPreviousFrameAddress != 0 && fSteppedOverFunctionAddress != 0
725 					&& fSteppedOverFunctionAddress != cpuState->InstructionPointer()) {
726 				TRACE_CONTROL("STEP_OVER: called function address %#" B_PRIx64
727 					", previous frame address: %#" B_PRIx64 ", frame address: %#"
728 					B_PRIx64 ", adding return info\n", fSteppedOverFunctionAddress,
729 					fPreviousFrameAddress, stackTrace->FrameAt(0)->FrameAddress());
730 				ReturnValueInfo* returnInfo = new(std::nothrow) ReturnValueInfo(
731 					fSteppedOverFunctionAddress, cpuState);
732 				if (returnInfo == NULL)
733 					return false;
734 
735 				BReference<ReturnValueInfo> returnInfoReference(returnInfo, true);
736 
737 				if (fThread->AddReturnValueInfo(returnInfo) != B_OK)
738 					return false;
739 
740 				returnInfoReference.Detach();
741 				fSteppedOverFunctionAddress = 0;
742 			}
743 
744 			// If we're still in the statement, we continue single-stepping,
745 			// otherwise we're done.
746 			if (fStepStatement->ContainsAddress(
747 					cpuState->InstructionPointer())) {
748 				if (!_DoStepOver(cpuState))
749 					_StepFallback();
750 				return true;
751 			}
752 			fPreviousFrameAddress = 0;
753 			return false;
754 		}
755 
756 		case STEP_INTO:
757 			// Should never happen -- we don't set a breakpoint in this case.
758 			return false;
759 
760 		case STEP_OUT:
761 		{
762 			// That's the return address, so we're done in theory,
763 			// unless we're a recursive function. Check if we've actually
764 			// exited the previous stack frame or not
765 			if (!_HasExitedFrame(cpuState->StackFramePointer())) {
766 				status_t error = _InstallTemporaryBreakpoint(
767 					cpuState->InstructionPointer());
768 				if (error != B_OK)
769 					_StepFallback();
770 				else
771 					_RunThread(cpuState->InstructionPointer());
772 				return true;
773 			}
774 
775 			if (fPreviousFrameAddress == 0)
776 				return false;
777 
778 			TRACE_CONTROL("ThreadHandler::_HandleBreakpointHitStep() - "
779 				"frame pointer 0x%#" B_PRIx64 ", previous: 0x%#" B_PRIx64
780 				" - step out adding return value\n", cpuState
781 					->StackFramePointer(), fPreviousFrameAddress);
782 			ReturnValueInfo* info = new(std::nothrow) ReturnValueInfo(
783 				fPreviousInstructionPointer, cpuState);
784 			if (info == NULL)
785 				return false;
786 			BReference<ReturnValueInfo> infoReference(info, true);
787 			if (fThread->AddReturnValueInfo(info) != B_OK)
788 				return false;
789 
790 			infoReference.Detach();
791 			fPreviousFrameAddress = 0;
792 		}
793 
794 		default:
795 			return false;
796 	}
797 }
798 
799 
800 bool
_HandleSingleStepStep(CpuState * cpuState)801 ThreadHandler::_HandleSingleStepStep(CpuState* cpuState)
802 {
803 	TRACE_CONTROL("ThreadHandler::_HandleSingleStepStep(): ip: %" B_PRIx64 "\n",
804 		cpuState->InstructionPointer());
805 
806 	switch (fStepMode) {
807 		case STEP_INTO:
808 		{
809 			// We continue stepping as long as we're in the statement.
810 			if (fStepStatement->ContainsAddress(cpuState->InstructionPointer())) {
811 				_SingleStepThread(cpuState->InstructionPointer());
812 				return true;
813 			}
814 
815 			StackTrace* stackTrace = fThread->GetStackTrace();
816 			BReference<StackTrace> stackTraceReference(stackTrace);
817 
818 			if (stackTrace == NULL && cpuState != NULL) {
819 				if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
820 						fThread->GetTeam(), this, cpuState, stackTrace, NULL,
821 						1, false, false) == B_OK) {
822 					stackTraceReference.SetTo(stackTrace, true);
823 				}
824 			}
825 
826 			if (stackTrace != NULL) {
827 				StackFrame* frame = stackTrace->FrameAt(0);
828 				Image* image = frame->GetImage();
829 				if (image == NULL)
830 					return false;
831 
832 				ImageDebugInfo* info = NULL;
833 				if (GetImageDebugInfo(image, info) != B_OK)
834 					return false;
835 
836 				BReference<ImageDebugInfo>(info, true);
837 				if (info->GetAddressSectionType(
838 						cpuState->InstructionPointer())
839 						== ADDRESS_SECTION_TYPE_PLT) {
840 					_SingleStepThread(cpuState->InstructionPointer());
841 					return true;
842 				}
843 			}
844 			return false;
845 		}
846 
847 		case STEP_OVER:
848 		{
849 			// If we have stepped out of the statement, we're done.
850 			if (!fStepStatement->ContainsAddress(cpuState->InstructionPointer())) {
851 				StackTrace* stackTrace = fThread->GetStackTrace();
852 				BReference<StackTrace> stackTraceReference(stackTrace);
853 				if (stackTrace == NULL && cpuState != NULL) {
854 					if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
855 							fThread->GetTeam(), this, cpuState, stackTrace,
856 							NULL, 1, false, false) == B_OK) {
857 						stackTraceReference.SetTo(stackTrace, true);
858 					}
859 				}
860 
861 
862 				if (stackTrace != NULL) {
863 					if (_HasExitedFrame(stackTrace->FrameAt(0)
864 						->FrameAddress())) {
865 						TRACE_CONTROL("ThreadHandler::_HandleSingleStepStep() "
866 							" - adding return value for STEP_OVER\n");
867 						ReturnValueInfo* info = new(std::nothrow)
868 							ReturnValueInfo(fStepStatement
869 								->CoveringAddressRange().Start(), cpuState);
870 						if (info == NULL)
871 							return false;
872 						BReference<ReturnValueInfo> infoReference(info, true);
873 						if (fThread->AddReturnValueInfo(info) != B_OK)
874 							return false;
875 
876 						infoReference.Detach();
877 					}
878 				}
879 				return false;
880 			}
881 			return _DoStepOver(cpuState);
882 		}
883 
884 		case STEP_OUT:
885 			// We never single-step in this case.
886 		default:
887 			return false;
888 	}
889 }
890 
891 
892 bool
_HandleBreakpointConditionIfNeeded(CpuState * cpuState)893 ThreadHandler::_HandleBreakpointConditionIfNeeded(CpuState* cpuState)
894 {
895 	AutoLocker< ::Team> teamLocker(fThread->GetTeam());
896 	Breakpoint* breakpoint = fThread->GetTeam()->BreakpointAtAddress(
897 		cpuState->InstructionPointer());
898 
899 	if (breakpoint == NULL)
900 		return false;
901 
902 	if (!breakpoint->HasEnabledUserBreakpoint())
903 		return false;
904 
905 	const UserBreakpointInstanceList& breakpoints
906 		= breakpoint->UserBreakpoints();
907 
908 	for (UserBreakpointInstanceList::ConstIterator it
909 			= breakpoints.GetIterator(); it.HasNext();) {
910 		UserBreakpoint* userBreakpoint = it.Next()->GetUserBreakpoint();
911 		if (!userBreakpoint->IsValid())
912 			continue;
913 		if (!userBreakpoint->IsEnabled())
914 			continue;
915 		if (!userBreakpoint->HasCondition())
916 			continue;
917 
918 		StackTrace* stackTrace = fThread->GetStackTrace();
919 		BReference<StackTrace> stackTraceReference;
920 		if (stackTrace == NULL) {
921 			if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
922 				fThread->GetTeam(), this, cpuState, stackTrace, NULL, 1,
923 				false, true) == B_OK) {
924 				stackTraceReference.SetTo(stackTrace, true);
925 			} else
926 				return false;
927 		}
928 
929 		StackFrame* frame = stackTrace->FrameAt(0);
930 		FunctionDebugInfo* info = frame->Function()->GetFunctionDebugInfo();
931 		if (info == NULL)
932 			return false;
933 
934 		SpecificImageDebugInfo* specificInfo
935 			= info->GetSpecificImageDebugInfo();
936 		if (specificInfo == NULL)
937 			return false;
938 
939 		SourceLanguage* language;
940 		if (specificInfo->GetSourceLanguage(info, language) != B_OK)
941 			return false;
942 
943 		BReference<SourceLanguage> reference(language, true);
944 		ExpressionEvaluationListener* listener
945 			= new(std::nothrow) ExpressionEvaluationListener(this);
946 		if (listener == NULL)
947 			return false;
948 
949 		ExpressionInfo* expressionInfo = new(std::nothrow) ExpressionInfo(
950 			userBreakpoint->Condition());
951 
952 		if (expressionInfo == NULL) {
953 			delete listener;
954 			return false;
955 		}
956 
957 		BReference<ExpressionInfo> expressionReference(expressionInfo, true);
958 
959 		expressionInfo->AddListener(listener);
960 
961 		status_t error = fWorker->ScheduleJob(
962 			new(std::nothrow) ExpressionEvaluationJob(fThread->GetTeam(),
963 				fDebuggerInterface, language, expressionInfo, frame, fThread),
964 			fJobListener);
965 
966 		BPrivate::ObjectDeleter<ExpressionEvaluationListener> deleter(
967 			listener);
968 		if (error == B_OK) {
969 			teamLocker.Unlock();
970 			do {
971 				error = acquire_sem(fConditionWaitSem);
972 			} while (error == B_INTERRUPTED);
973 
974 			teamLocker.Lock();
975 
976 			if (_CheckStopCondition()) {
977 				if (fConditionResult != NULL) {
978 					fConditionResult->ReleaseReference();
979 					fConditionResult = NULL;
980 				}
981 				_SetThreadState(THREAD_STATE_STOPPED, cpuState,
982 					THREAD_STOPPED_BREAKPOINT, BString());
983 				return false;
984 			} else {
985 				fDebuggerInterface->ContinueThread(ThreadID());
986 				return true;
987 			}
988 		}
989 	}
990 
991 	return false;
992 }
993 
994 
995 void
_HandleBreakpointConditionEvaluated(ExpressionResult * value)996 ThreadHandler::_HandleBreakpointConditionEvaluated(ExpressionResult* value)
997 {
998 	fConditionResult = value;
999 	if (fConditionResult != NULL)
1000 		fConditionResult->AcquireReference();
1001 	release_sem(fConditionWaitSem);
1002 }
1003 
1004 
1005 bool
_CheckStopCondition()1006 ThreadHandler::_CheckStopCondition()
1007 {
1008 	// if we we're unable to properly assess the expression result
1009 	// in any way, fall back to behaving like an unconditional breakpoint.
1010 	if (fConditionResult == NULL)
1011 		return true;
1012 
1013 	if (fConditionResult->Kind() != EXPRESSION_RESULT_KIND_PRIMITIVE)
1014 		return true;
1015 
1016 	BVariant value;
1017 	if (!fConditionResult->PrimitiveValue()->ToVariant(value))
1018 		return true;
1019 
1020 	return value.ToBool();
1021 }
1022 
1023 
1024 bool
_HasExitedFrame(target_addr_t framePointer) const1025 ThreadHandler::_HasExitedFrame(target_addr_t framePointer) const
1026 {
1027 	return fDebuggerInterface->GetArchitecture()->StackGrowthDirection()
1028 			== STACK_GROWTH_DIRECTION_POSITIVE
1029 				? framePointer < fPreviousFrameAddress
1030 				: framePointer > fPreviousFrameAddress;
1031 }
1032