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