bool ThreadHandler::_DoStepOver(CpuState* cpuState) { TRACE_CONTROL("ThreadHandler::_DoStepOver()\n"); // The basic strategy is to single-step out of the statement like for // "step into", only we have to avoid stepping into subroutines. Hence we // check whether the current instruction is a subroutine call. If not, we // just single-step, otherwise we set a breakpoint after the instruction. InstructionInfo info; if (fDebuggerInterface->GetArchitecture()->GetInstructionInfo( cpuState->InstructionPointer(), info, cpuState) != B_OK) { TRACE_CONTROL(" failed to get instruction info\n"); return false; } if (info.Type() != INSTRUCTION_TYPE_SUBROUTINE_CALL) { _SingleStepThread(cpuState->InstructionPointer()); TRACE_CONTROL(" not a subroutine call\n"); return true; } TRACE_CONTROL(" subroutine call -- installing breakpoint at address " "%#" B_PRIx64 "\n", info.Address() + info.Size()); if (_InstallTemporaryBreakpoint(info.Address() + info.Size()) != B_OK) return false; fSteppedOverFunctionAddress = info.TargetAddress(); _RunThread(cpuState->InstructionPointer()); return true; }
status_t BreakpointManager::_UpdateBreakpointInstallation(Breakpoint* breakpoint) { bool shouldBeInstalled = breakpoint->ShouldBeInstalled(); TRACE_CONTROL("BreakpointManager::_UpdateBreakpointInstallation(%p): " "should be installed: %d, is installed: %d\n", breakpoint, shouldBeInstalled, breakpoint->IsInstalled()); if (shouldBeInstalled == breakpoint->IsInstalled()) return B_OK; if (shouldBeInstalled) { // install status_t error = fDebuggerInterface->InstallBreakpoint( breakpoint->Address()); if (error != B_OK) return error; TRACE_CONTROL("BREAKPOINT at %#llx installed: %s\n", breakpoint->Address(), strerror(error)); breakpoint->SetInstalled(true); } else { // uninstall fDebuggerInterface->UninstallBreakpoint(breakpoint->Address()); TRACE_CONTROL("BREAKPOINT at %#llx uninstalled\n", breakpoint->Address()); breakpoint->SetInstalled(false); } return B_OK; }
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; } 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; } }
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 TeamDebugger::_HandleClearUserBreakpoint(target_addr_t address) { TRACE_CONTROL("TeamDebugger::_HandleClearUserBreakpoint(%#llx)\n", address); AutoLocker< ::Team> locker(fTeam); Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address); if (breakpoint == NULL || breakpoint->FirstUserBreakpoint() == NULL) return; UserBreakpoint* userBreakpoint = breakpoint->FirstUserBreakpoint()->GetUserBreakpoint(); BReference<UserBreakpoint> userBreakpointReference(userBreakpoint); locker.Unlock(); _HandleClearUserBreakpoint(userBreakpoint); }
void TeamDebugger::_HandleInspectAddress(target_addr_t address, TeamMemoryBlock::Listener* listener) { TRACE_CONTROL("TeamDebugger::_HandleInspectAddress(%" B_PRIx64 ", %p)\n", address, listener); TeamMemoryBlock* memoryBlock = fMemoryBlockManager ->GetMemoryBlock(address); if (memoryBlock == NULL) { _NotifyUser("Inspect Address", "Failed to allocate memory block"); return; } if (!memoryBlock->HasListener(listener)) memoryBlock->AddListener(listener); if (!memoryBlock->IsValid()) { AutoLocker< ::Team> teamLocker(fTeam); TeamMemory* memory = fTeam->GetTeamMemory(); // schedule the job status_t result; if ((result = fWorker->ScheduleJob( new(std::nothrow) RetrieveMemoryBlockJob(fTeam, memory, memoryBlock), this)) != B_OK) { memoryBlock->ReleaseReference(); _NotifyUser("Inspect Address", "Failed to retrieve memory data: %s", strerror(result)); } } else memoryBlock->NotifyDataRetrieved(); }
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(); } }
status_t BreakpointManager::InstallUserBreakpoint(UserBreakpoint* userBreakpoint, bool enabled) { TRACE_CONTROL("BreakpointManager::InstallUserBreakpoint(%p, %d)\n", userBreakpoint, enabled); AutoLocker<BLocker> installLocker(fLock); AutoLocker<Team> teamLocker(fTeam); bool oldEnabled = userBreakpoint->IsEnabled(); if (userBreakpoint->IsValid() && enabled == oldEnabled) { TRACE_CONTROL(" user breakpoint already valid and with same enabled " "state\n"); return B_OK; } // get/create the breakpoints for all instances TRACE_CONTROL(" creating breakpoints for breakpoint instances\n"); status_t error = B_OK; for (int32 i = 0; UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); i++) { TRACE_CONTROL(" breakpoint instance %p\n", instance); if (instance->GetBreakpoint() != NULL) { TRACE_CONTROL(" -> already has breakpoint\n"); continue; } target_addr_t address = instance->Address(); Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address); if (breakpoint == NULL) { TRACE_CONTROL(" -> no breakpoint at that address yet\n"); Image* image = fTeam->ImageByAddress(address); if (image == NULL) { TRACE_CONTROL(" -> no image at that address\n"); error = B_BAD_ADDRESS; break; } breakpoint = new(std::nothrow) Breakpoint(image, address); if (breakpoint == NULL || !fTeam->AddBreakpoint(breakpoint)) { delete breakpoint; error = B_NO_MEMORY; break; } } TRACE_CONTROL(" -> adding instance to breakpoint %p\n", breakpoint); breakpoint->AddUserBreakpoint(instance); instance->SetBreakpoint(breakpoint); } // If everything looks good so far mark the user breakpoint according to // its new state. if (error == B_OK) userBreakpoint->SetEnabled(enabled); // notify user breakpoint listeners if (error == B_OK) fTeam->NotifyUserBreakpointChanged(userBreakpoint); teamLocker.Unlock(); // install/uninstall the breakpoints as needed TRACE_CONTROL(" updating breakpoints\n"); if (error == B_OK) { for (int32 i = 0; UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); i++) { TRACE_CONTROL(" breakpoint instance %p\n", instance); error = _UpdateBreakpointInstallation(instance->GetBreakpoint()); if (error != B_OK) break; } } if (error == B_OK) { TRACE_CONTROL(" success, marking user breakpoint valid\n"); // everything went fine -- mark the user breakpoint valid if (!userBreakpoint->IsValid()) { teamLocker.Lock(); userBreakpoint->SetValid(true); userBreakpoint->AcquireReference(); fTeam->AddUserBreakpoint(userBreakpoint); fTeam->NotifyUserBreakpointChanged(userBreakpoint); // notify again -- the breakpoint hadn't been added before teamLocker.Unlock(); } } else { // something went wrong -- revert the situation TRACE_CONTROL(" error, reverting\n"); teamLocker.Lock(); userBreakpoint->SetEnabled(oldEnabled); teamLocker.Unlock(); if (!oldEnabled || !userBreakpoint->IsValid()) { for (int32 i = 0; UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); i++) { Breakpoint* breakpoint = instance->GetBreakpoint(); if (breakpoint == NULL) continue; if (!userBreakpoint->IsValid()) { instance->SetBreakpoint(NULL); breakpoint->RemoveUserBreakpoint(instance); } _UpdateBreakpointInstallation(breakpoint); teamLocker.Lock(); if (breakpoint->IsUnused()) fTeam->RemoveBreakpoint(breakpoint); teamLocker.Unlock(); } teamLocker.Lock(); fTeam->NotifyUserBreakpointChanged(userBreakpoint); teamLocker.Unlock(); } } installLocker.Unlock(); return error; }
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(); } }
void TeamDebugger::_HandleSetUserBreakpoint(target_addr_t address, bool enabled) { TRACE_CONTROL("TeamDebugger::_HandleSetUserBreakpoint(%#llx, %d)\n", address, enabled); // check whether there already is a breakpoint AutoLocker< ::Team> locker(fTeam); Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address); UserBreakpoint* userBreakpoint = NULL; if (breakpoint != NULL && breakpoint->FirstUserBreakpoint() != NULL) userBreakpoint = breakpoint->FirstUserBreakpoint()->GetUserBreakpoint(); BReference<UserBreakpoint> userBreakpointReference(userBreakpoint); if (userBreakpoint == NULL) { TRACE_CONTROL(" no breakpoint yet\n"); // get the function at the address Image* image = fTeam->ImageByAddress(address); TRACE_CONTROL(" image: %p\n", image); if (image == NULL) return; ImageDebugInfo* imageDebugInfo = image->GetImageDebugInfo(); TRACE_CONTROL(" image debug info: %p\n", imageDebugInfo); if (imageDebugInfo == NULL) return; // TODO: Handle this case by loading the debug info, if possible! FunctionInstance* functionInstance = imageDebugInfo->FunctionAtAddress(address); TRACE_CONTROL(" function instance: %p\n", functionInstance); if (functionInstance == NULL) return; Function* function = functionInstance->GetFunction(); TRACE_CONTROL(" function: %p\n", function); // get the source location for the address FunctionDebugInfo* functionDebugInfo = functionInstance->GetFunctionDebugInfo(); SourceLocation sourceLocation; Statement* breakpointStatement = NULL; if (functionDebugInfo->GetSpecificImageDebugInfo()->GetStatement( functionDebugInfo, address, breakpointStatement) != B_OK) { return; } sourceLocation = breakpointStatement->StartSourceLocation(); breakpointStatement->ReleaseReference(); target_addr_t relativeAddress = address - functionInstance->Address(); TRACE_CONTROL(" relative address: %#llx, source location: " "(%ld, %ld)\n", relativeAddress, sourceLocation.Line(), sourceLocation.Column()); // get function id FunctionID* functionID = functionInstance->GetFunctionID(); if (functionID == NULL) return; BReference<FunctionID> functionIDReference(functionID, true); // create the user breakpoint userBreakpoint = new(std::nothrow) UserBreakpoint( UserBreakpointLocation(functionID, function->SourceFile(), sourceLocation, relativeAddress)); if (userBreakpoint == NULL) return; userBreakpointReference.SetTo(userBreakpoint, true); TRACE_CONTROL(" created user breakpoint: %p\n", userBreakpoint); // iterate through all function instances and create // UserBreakpointInstances for (FunctionInstanceList::ConstIterator it = function->Instances().GetIterator(); FunctionInstance* instance = it.Next();) { TRACE_CONTROL(" function instance %p: range: %#llx - %#llx\n", instance, instance->Address(), instance->Address() + instance->Size()); // get the breakpoint address for the instance target_addr_t instanceAddress = 0; if (instance == functionInstance) { instanceAddress = address; } else if (functionInstance->SourceFile() != NULL) { // We have a source file, so get the address for the source // location. Statement* statement = NULL; functionDebugInfo = instance->GetFunctionDebugInfo(); functionDebugInfo->GetSpecificImageDebugInfo() ->GetStatementAtSourceLocation(functionDebugInfo, sourceLocation, statement); if (statement != NULL) { instanceAddress = statement->CoveringAddressRange().Start(); // TODO: What about BreakpointAllowed()? statement->ReleaseReference(); } } TRACE_CONTROL(" breakpoint address using source info: %llx\n", instanceAddress); if (instanceAddress == 0) { // No source file (or we failed getting the statement), so try // to use the same relative address. if (relativeAddress > instance->Size()) continue; instanceAddress = instance->Address() + relativeAddress; } TRACE_CONTROL(" final breakpoint address: %llx\n", instanceAddress); UserBreakpointInstance* breakpointInstance = new(std::nothrow) UserBreakpointInstance(userBreakpoint, instanceAddress); if (breakpointInstance == NULL || !userBreakpoint->AddInstance(breakpointInstance)) { delete breakpointInstance; return; } TRACE_CONTROL(" breakpoint instance: %p\n", breakpointInstance); } } locker.Unlock(); _HandleSetUserBreakpoint(userBreakpoint, enabled); }