status_t Architecture::CreateStackTrace(Team* team, ImageDebugInfoProvider* imageInfoProvider, CpuState* cpuState, StackTrace*& _stackTrace, int32 maxStackDepth, bool useExistingTrace) { BReference<CpuState> cpuStateReference(cpuState); StackTrace* stackTrace = NULL; ObjectDeleter<StackTrace> stackTraceDeleter; StackFrame* frame = NULL; if (useExistingTrace) stackTrace = _stackTrace; else { // create the object stackTrace = new(std::nothrow) StackTrace; if (stackTrace == NULL) return B_NO_MEMORY; stackTraceDeleter.SetTo(stackTrace); } // if we're passed an already existing partial stack trace, // attempt to continue building it from where it left off. if (stackTrace->CountFrames() > 0) { frame = stackTrace->FrameAt(stackTrace->CountFrames() - 1); cpuState = frame->GetCpuState(); } while (cpuState != NULL) { // get the instruction pointer target_addr_t instructionPointer = cpuState->InstructionPointer(); if (instructionPointer == 0) break; // get the image for the instruction pointer AutoLocker<Team> teamLocker(team); Image* image = team->ImageByAddress(instructionPointer); BReference<Image> imageReference(image); teamLocker.Unlock(); // get the image debug info ImageDebugInfo* imageDebugInfo = NULL; if (image != NULL) imageInfoProvider->GetImageDebugInfo(image, imageDebugInfo); BReference<ImageDebugInfo> imageDebugInfoReference(imageDebugInfo, true); // get the function teamLocker.Lock(); FunctionInstance* function = NULL; FunctionDebugInfo* functionDebugInfo = NULL; if (imageDebugInfo != NULL) { function = imageDebugInfo->FunctionAtAddress(instructionPointer); if (function != NULL) functionDebugInfo = function->GetFunctionDebugInfo(); } BReference<FunctionInstance> functionReference(function); teamLocker.Unlock(); // If the CPU state's instruction pointer is actually the return address // of the next frame, we let the architecture fix that. if (frame != NULL && frame->ReturnAddress() == cpuState->InstructionPointer()) { UpdateStackFrameCpuState(frame, image, functionDebugInfo, cpuState); } // create the frame using the debug info StackFrame* previousFrame = NULL; CpuState* previousCpuState = NULL; if (function != NULL) { status_t error = functionDebugInfo->GetSpecificImageDebugInfo() ->CreateFrame(image, function, cpuState, previousFrame, previousCpuState); if (error != B_OK && error != B_UNSUPPORTED) break; } // If we have no frame yet, let the architecture create it. if (previousFrame == NULL) { status_t error = CreateStackFrame(image, functionDebugInfo, cpuState, frame == NULL, previousFrame, previousCpuState); if (error != B_OK) break; } cpuStateReference.SetTo(previousCpuState, true); previousFrame->SetImage(image); previousFrame->SetFunction(function); if (!stackTrace->AddFrame(previousFrame)) { delete previousFrame; return B_NO_MEMORY; } frame = previousFrame; cpuState = previousCpuState; if (--maxStackDepth == 0) break; } stackTraceDeleter.Detach(); _stackTrace = stackTrace; return B_OK; }
void ThreadHandler::HandleThreadAction(uint32 action, target_addr_t address) { AutoLocker<Team> locker(fThread->GetTeam()); if (fThread->State() == THREAD_STATE_UNKNOWN) return; // When stop is requested, thread must be running, otherwise stopped. if (action == MSG_THREAD_STOP ? fThread->State() != THREAD_STATE_RUNNING : fThread->State() != THREAD_STATE_STOPPED) { return; } // When stepping we need a stack trace. Save it before unsetting the state. CpuState* cpuState = fThread->GetCpuState(); StackTrace* stackTrace = fThread->GetStackTrace(); BReference<CpuState> cpuStateReference(cpuState); BReference<StackTrace> stackTraceReference(stackTrace); if (action == MSG_THREAD_SET_ADDRESS) { _HandleSetAddress(cpuState, address); return; } // When continuing the thread update thread state before actually issuing // the command, since we need to unlock. if (action != MSG_THREAD_STOP) { _SetThreadState(THREAD_STATE_RUNNING, NULL, THREAD_STOPPED_UNKNOWN, BString()); } locker.Unlock(); switch (action) { case MSG_THREAD_RUN: fStepMode = address != 0 ? STEP_UNTIL : STEP_NONE; if (address != 0) _InstallTemporaryBreakpoint(address); _RunThread(0); return; case MSG_THREAD_STOP: fStepMode = STEP_NONE; if (fDebuggerInterface->StopThread(ThreadID()) == B_OK) fThread->SetStopRequestPending(); return; case MSG_THREAD_STEP_OVER: case MSG_THREAD_STEP_INTO: case MSG_THREAD_STEP_OUT: break; } TRACE_CONTROL("ThreadHandler::HandleThreadAction(MSG_THREAD_STEP_*)\n"); // We want to step. We need a stack trace for that purpose. If we don't // have one yet, get it. Start with the CPU state. if (stackTrace == NULL && cpuState == NULL) { if (fDebuggerInterface->GetCpuState(fThread->ID(), cpuState) == B_OK) cpuStateReference.SetTo(cpuState, true); } if (stackTrace == NULL && cpuState != NULL) { if (fDebuggerInterface->GetArchitecture()->CreateStackTrace( fThread->GetTeam(), this, cpuState, stackTrace, NULL, 1, false, false) == B_OK) { stackTraceReference.SetTo(stackTrace, true); } } if (stackTrace == NULL || stackTrace->CountFrames() == 0) { _StepFallback(); return; } StackFrame* frame = stackTrace->FrameAt(0); TRACE_CONTROL(" ip: %#" B_PRIx64 "\n", frame->InstructionPointer()); target_addr_t frameIP = frame->GetCpuState()->InstructionPointer(); // When the thread is in a syscall, do the same for all step kinds: Stop it // when it returns by means of a breakpoint. if (frame->Type() == STACK_FRAME_TYPE_SYSCALL) { // set a breakpoint at the CPU state's instruction pointer (points to // the return address, unlike the stack frame's instruction pointer) // TODO: This is doesn't work correctly anymore. When stepping over a "syscall" // instruction the thread is stopped twice. The after the first step the PC is // incorrectly shown at the "syscall" instruction. Then we step again and are // stopped at the temporary breakpoint after the "syscall" instruction. There // are two problems. The first one is that we don't (cannot?) discriminate // between the thread being in a syscall (like in a blocking syscall) and the // thread having been stopped (or singled-stepped) at the end of the syscall. // The second issue is that the temporary breakpoint is probably not necessary // anymore, since single-stepping over "syscall" instructions should just work // as expected. status_t error = _InstallTemporaryBreakpoint(frameIP); if (error != B_OK) { _StepFallback(); return; } fStepMode = STEP_OUT; _RunThread(frameIP); return; } // For "step out" just set a temporary breakpoint on the return address. if (action == MSG_THREAD_STEP_OUT) { status_t error = _InstallTemporaryBreakpoint(frame->ReturnAddress()); if (error != B_OK) { _StepFallback(); return; } fPreviousInstructionPointer = frameIP; fPreviousFrameAddress = frame->FrameAddress(); fStepMode = STEP_OUT; _RunThread(frameIP); return; } // For "step in" and "step over" we also need the source code statement at // the current instruction pointer. fStepStatement = _GetStatementAtInstructionPointer(frame); if (fStepStatement == NULL) { _StepFallback(); return; } TRACE_CONTROL(" statement: %#" B_PRIx64 " - %#" B_PRIx64 "\n", fStepStatement->CoveringAddressRange().Start(), fStepStatement->CoveringAddressRange().End()); if (action == MSG_THREAD_STEP_INTO) { // step into fStepMode = STEP_INTO; _SingleStepThread(frameIP); } else { fPreviousFrameAddress = frame->FrameAddress(); // step over fStepMode = STEP_OVER; if (!_DoStepOver(frame->GetCpuState())) _StepFallback(); } }
void ThreadHandler::HandleThreadAction(uint32 action) { AutoLocker<Team> locker(fThread->GetTeam()); if (fThread->State() == THREAD_STATE_UNKNOWN) return; // When stop is requested, thread must be running, otherwise stopped. if (action == MSG_THREAD_STOP ? fThread->State() != THREAD_STATE_RUNNING : fThread->State() != THREAD_STATE_STOPPED) { return; } // When stepping we need a stack trace. Save it before unsetting the state. CpuState* cpuState = fThread->GetCpuState(); StackTrace* stackTrace = fThread->GetStackTrace(); Reference<CpuState> cpuStateReference(cpuState); Reference<StackTrace> stackTraceReference(stackTrace); // When continuing the thread update thread state before actually issuing // the command, since we need to unlock. if (action != MSG_THREAD_STOP) { _SetThreadState(THREAD_STATE_RUNNING, NULL, THREAD_STOPPED_UNKNOWN, BString()); } locker.Unlock(); switch (action) { case MSG_THREAD_RUN: fStepMode = STEP_NONE; _RunThread(0); return; case MSG_THREAD_STOP: fStepMode = STEP_NONE; fDebuggerInterface->StopThread(ThreadID()); return; case MSG_THREAD_STEP_OVER: case MSG_THREAD_STEP_INTO: case MSG_THREAD_STEP_OUT: break; } TRACE_CONTROL("ThreadHandler::HandleThreadAction(MSG_THREAD_STEP_*)\n"); // We want to step. We need a stack trace for that purpose. If we don't // have one yet, get it. Start with the CPU state. if (stackTrace == NULL && cpuState == NULL) { if (fDebuggerInterface->GetCpuState(fThread->ID(), cpuState) == B_OK) cpuStateReference.SetTo(cpuState, true); } if (stackTrace == NULL && cpuState != NULL) { if (fDebuggerInterface->GetArchitecture()->CreateStackTrace( fThread->GetTeam(), this, cpuState, stackTrace) == B_OK) { stackTraceReference.SetTo(stackTrace, true); } } if (stackTrace == NULL || stackTrace->CountFrames() == 0) { _StepFallback(); return; } StackFrame* frame = stackTrace->FrameAt(0); TRACE_CONTROL(" ip: %#llx\n", frame->InstructionPointer()); // When the thread is in a syscall, do the same for all step kinds: Stop it // when it return by means of a breakpoint. if (frame->Type() == STACK_FRAME_TYPE_SYSCALL) { // set a breakpoint at the CPU state's instruction pointer (points to // the return address, unlike the stack frame's instruction pointer) status_t error = _InstallTemporaryBreakpoint( frame->GetCpuState()->InstructionPointer()); if (error != B_OK) { _StepFallback(); return; } fStepMode = STEP_OUT; _RunThread(frame->GetCpuState()->InstructionPointer()); return; } // For "step out" just set a temporary breakpoint on the return address. if (action == MSG_THREAD_STEP_OUT) { // TODO: That's OK in principle, but needs additional work with recursive // functions. We need to store some information that allows us to determine // whether we've actually stepped out of the current frame when we have hit // the breakpoint. status_t error = _InstallTemporaryBreakpoint(frame->ReturnAddress()); if (error != B_OK) { _StepFallback(); return; } fStepMode = STEP_OUT; _RunThread(frame->GetCpuState()->InstructionPointer()); return; } // For "step in" and "step over" we also need the source code statement at // the current instruction pointer. fStepStatement = _GetStatementAtInstructionPointer(frame); if (fStepStatement == NULL) { _StepFallback(); return; } TRACE_CONTROL(" statement: %#llx - %#llx\n", fStepStatement->CoveringAddressRange().Start(), fStepStatement->CoveringAddressRange().End()); if (action == MSG_THREAD_STEP_INTO) { // step into fStepMode = STEP_INTO; _SingleStepThread(frame->GetCpuState()->InstructionPointer()); } else { // step over fStepMode = STEP_OVER; if (!_DoStepOver(frame->GetCpuState())) _StepFallback(); } }