void TeamWindow::_SetActiveThread(::Thread* thread) { if (thread == fActiveThread) return; if (fActiveThread != NULL) fActiveThread->ReleaseReference(); fActiveThread = thread; if (fActiveThread != NULL) fActiveThread->AcquireReference(); AutoLocker< ::Team> locker(fTeam); _UpdateRunButtons(); StackTrace* stackTrace = fActiveThread != NULL ? fActiveThread->GetStackTrace() : NULL; BReference<StackTrace> stackTraceReference(stackTrace); // hold a reference until we've set it locker.Unlock(); fThreadListView->SetThread(fActiveThread); _SetActiveStackTrace(stackTrace); _UpdateCpuState(); }
void TeamWindow::_HandleStackTraceChanged(thread_id threadID) { // We're only interested in the currently selected thread if (fActiveThread == NULL || threadID != fActiveThread->ID()) return; AutoLocker< ::Team> locker(fTeam); StackTrace* stackTrace = fActiveThread != NULL ? fActiveThread->GetStackTrace() : NULL; BReference<StackTrace> stackTraceReference(stackTrace); // hold a reference until the register view has one locker.Unlock(); if (stackTrace == NULL) { if (fTraceUpdateRunner != NULL) return; BMessage message(MSG_CLEAR_STACK_TRACE); fTraceUpdateRunner = new(std::nothrow) BMessageRunner(this, message, 250000, 1); if (fTraceUpdateRunner != NULL && fTraceUpdateRunner->InitCheck() == B_OK) { fStackTraceView->SetStackTraceClearPending(); fVariablesView->SetStackFrameClearPending(); return; } } _SetActiveStackTrace(stackTrace); }
bool ThreadHandler::_HandleSingleStepStep(CpuState* cpuState) { TRACE_CONTROL("ThreadHandler::_HandleSingleStepStep(): ip: %llx\n", cpuState->InstructionPointer()); switch (fStepMode) { case STEP_INTO: { // We continue stepping as long as we're in the statement. if (fStepStatement->ContainsAddress(cpuState->InstructionPointer())) { _SingleStepThread(cpuState->InstructionPointer()); return true; } StackTrace* stackTrace = fThread->GetStackTrace(); BReference<StackTrace> stackTraceReference(stackTrace); if (stackTrace == NULL && cpuState != NULL) { if (fDebuggerInterface->GetArchitecture()->CreateStackTrace( fThread->GetTeam(), this, cpuState, stackTrace) == B_OK) { stackTraceReference.SetTo(stackTrace, true); } } if (stackTrace != NULL) { StackFrame* frame = stackTrace->FrameAt(0); Image* image = frame->GetImage(); ImageDebugInfo* info = NULL; if (GetImageDebugInfo(image, info) != B_OK) return false; BReference<ImageDebugInfo>(info, true); if (info->GetAddressSectionType( cpuState->InstructionPointer()) == ADDRESS_SECTION_TYPE_PLT) { _SingleStepThread(cpuState->InstructionPointer()); return true; } } return false; } case STEP_OVER: { // If we have stepped out of the statement, we're done. if (!fStepStatement->ContainsAddress(cpuState->InstructionPointer())) return false; return _DoStepOver(cpuState); } case STEP_OUT: // We never single-step in this case. default: return false; } }
void TeamWindow::_HandleStackTraceChanged(thread_id threadID) { // We're only interested in the currently selected thread if (fActiveThread == NULL || threadID != fActiveThread->ID()) return; AutoLocker< ::Team> locker(fTeam); StackTrace* stackTrace = fActiveThread != NULL ? fActiveThread->GetStackTrace() : NULL; BReference<StackTrace> stackTraceReference(stackTrace); // hold a reference until the register view has one locker.Unlock(); _SetActiveStackTrace(stackTrace); }
void CliStackTraceCommand::Execute(int argc, const char* const* argv, CliContext& context) { // get the current thread Team* team = context.GetTeam(); AutoLocker<Team> teamLocker(team); Thread* thread = context.CurrentThread(); if (thread == NULL) { printf("no current thread\n"); return; } if (thread->State() != THREAD_STATE_STOPPED) { printf("Current thread is not stopped. Can't get stack trace.\n"); return; } // get its stack trace StackTrace* stackTrace = thread->GetStackTrace(); while (stackTrace == NULL) { context.WaitForEvents(CliContext::EVENT_THREAD_STACK_TRACE_CHANGED); if (context.IsTerminating()) return; stackTrace = thread->GetStackTrace(); } BReference<StackTrace> stackTraceReference(stackTrace); // hold a reference until we're done teamLocker.Unlock(); // print the stack trace int32 frameCount = stackTrace->CountFrames(); for (int32 i = 0; i < frameCount; i++) { StackFrame* frame = stackTrace->FrameAt(i); printf("%3" B_PRId32 " %#" B_PRIx64 " %#" B_PRIx64, i, (uint64)frame->FrameAddress(), (uint64)frame->InstructionPointer()); char functionName[512]; UiUtils::FunctionNameForFrame(frame, functionName, sizeof(functionName)); printf(" %s\n", functionName); } }
status_t GetStackTraceJob::Do() { if (fCpuState == NULL) return B_BAD_VALUE; // get the stack trace StackTrace* stackTrace; status_t error = fArchitecture->CreateStackTrace(fThread->GetTeam(), this, fCpuState, stackTrace); if (error != B_OK) return error; BReference<StackTrace> stackTraceReference(stackTrace, true); // set the stack trace, unless something has changed AutoLocker<Team> locker(fThread->GetTeam()); if (fThread->GetCpuState() == fCpuState) fThread->SetStackTrace(stackTrace); return B_OK; }
bool ThreadHandler::_HandleBreakpointHitStep(CpuState* cpuState) { // in any case uninstall the temporary breakpoint _UninstallTemporaryBreakpoint(); switch (fStepMode) { case STEP_OVER: { StackTrace* stackTrace = fThread->GetStackTrace(); BReference<StackTrace> stackTraceReference(stackTrace); 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) { StackFrame* frame = stackTrace->FrameAt(0); // If we're not in the same frame we started in, // keep executing. if (frame != NULL && fPreviousFrameAddress != frame->FrameAddress()) { status_t error = _InstallTemporaryBreakpoint( cpuState->InstructionPointer()); if (error != B_OK) _StepFallback(); else _RunThread(cpuState->InstructionPointer()); return true; } } if (fPreviousFrameAddress != 0 && fSteppedOverFunctionAddress != cpuState->InstructionPointer()) { TRACE_CONTROL("STEP_OVER: called function address %#" B_PRIx64 ", previous frame address: %#" B_PRIx64 ", frame address: %#" B_PRIx64 ", adding return info\n", fSteppedOverFunctionAddress, fPreviousFrameAddress, stackTrace->FrameAt(0)->FrameAddress()); ReturnValueInfo* returnInfo = new(std::nothrow) ReturnValueInfo( fSteppedOverFunctionAddress, cpuState); if (returnInfo == NULL) return false; BReference<ReturnValueInfo> returnInfoReference(returnInfo, true); if (fThread->AddReturnValueInfo(returnInfo) != B_OK) return false; returnInfoReference.Detach(); fSteppedOverFunctionAddress = 0; } // If we're still in the statement, we continue single-stepping, // otherwise we're done. if (fStepStatement->ContainsAddress( cpuState->InstructionPointer())) { if (!_DoStepOver(cpuState)) _StepFallback(); return true; } fPreviousFrameAddress = 0; return false; } case STEP_INTO: // Should never happen -- we don't set a breakpoint in this case. return false; case STEP_OUT: { // That's the return address, so we're done in theory, // unless we're a recursive function. Check if we've actually // exited the previous stack frame or not if (!_HasExitedFrame(cpuState->StackFramePointer())) { status_t error = _InstallTemporaryBreakpoint( cpuState->InstructionPointer()); if (error != B_OK) _StepFallback(); else _RunThread(cpuState->InstructionPointer()); return true; } if (fPreviousFrameAddress == 0) return false; TRACE_CONTROL("ThreadHandler::_HandleBreakpointHitStep() - " "frame pointer 0x%#" B_PRIx64 ", previous: 0x%#" B_PRIx64 " - step out adding return value\n", cpuState ->StackFramePointer(), fPreviousFrameAddress); ReturnValueInfo* info = new(std::nothrow) ReturnValueInfo( fPreviousInstructionPointer, cpuState); if (info == NULL) return false; BReference<ReturnValueInfo> infoReference(info, true); if (fThread->AddReturnValueInfo(info) != B_OK) return false; infoReference.Detach(); fPreviousFrameAddress = 0; } default: return false; } }
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(); } }
bool ThreadHandler::_HandleBreakpointHitStep(CpuState* cpuState) { // in any case uninstall the temporary breakpoint _UninstallTemporaryBreakpoint(); switch (fStepMode) { case STEP_OVER: { StackTrace* stackTrace = fThread->GetStackTrace(); BReference<StackTrace> stackTraceReference(stackTrace); if (stackTrace == NULL && cpuState != NULL) { if (fDebuggerInterface->GetArchitecture()->CreateStackTrace( fThread->GetTeam(), this, cpuState, stackTrace, 0, 1, false, false) == B_OK) { stackTraceReference.SetTo(stackTrace, true); } } if (stackTrace != NULL) { StackFrame* frame = stackTrace->FrameAt(0); // If we're not in the same frame we started in, // keep executing. if (frame != NULL && fPreviousFrameAddress != frame->FrameAddress()) { status_t error = _InstallTemporaryBreakpoint( cpuState->InstructionPointer()); if (error != B_OK) _StepFallback(); else _RunThread(cpuState->InstructionPointer()); return true; } } // If we're still in the statement, we continue single-stepping, // otherwise we're done. if (fStepStatement->ContainsAddress( cpuState->InstructionPointer())) { if (!_DoStepOver(cpuState)) _StepFallback(); return true; } fPreviousFrameAddress = 0; return false; } case STEP_INTO: // Should never happen -- we don't set a breakpoint in this case. return false; case STEP_OUT: { // That's the return address, so we're done in theory, // unless we're a recursive function. Check if we've actually // exited the previous stack frame or not. fThread->SetExecutedSubroutine(cpuState->InstructionPointer()); target_addr_t framePointer = cpuState->StackFramePointer(); bool hasExitedFrame = fDebuggerInterface->GetArchitecture() ->StackGrowthDirection() == STACK_GROWTH_DIRECTION_POSITIVE ? framePointer < fPreviousFrameAddress : framePointer > fPreviousFrameAddress; if (!hasExitedFrame) { status_t error = _InstallTemporaryBreakpoint( cpuState->InstructionPointer()); if (error != B_OK) _StepFallback(); else _RunThread(cpuState->InstructionPointer()); return true; } fPreviousFrameAddress = 0; } default: return false; } }
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(); } }