/* * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #include "BreakpointManager.h" #include #include #include #include "DebuggerInterface.h" #include "Function.h" #include "SpecificImageDebugInfo.h" #include "Statement.h" #include "Team.h" #include "Tracing.h" BreakpointManager::BreakpointManager(Team* team, DebuggerInterface* debuggerInterface) : fLock("breakpoint manager"), fTeam(team), fDebuggerInterface(debuggerInterface) { fDebuggerInterface->AcquireReference(); } BreakpointManager::~BreakpointManager() { fDebuggerInterface->ReleaseReference(); } status_t BreakpointManager::Init() { return fLock.InitCheck(); } status_t BreakpointManager::InstallUserBreakpoint(UserBreakpoint* userBreakpoint, bool enabled) { TRACE_CONTROL("BreakpointManager::InstallUserBreakpoint(%p, %d)\n", userBreakpoint, enabled); AutoLocker installLocker(fLock); AutoLocker 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 BreakpointManager::UninstallUserBreakpoint(UserBreakpoint* userBreakpoint) { AutoLocker installLocker(fLock); AutoLocker teamLocker(fTeam); if (!userBreakpoint->IsValid()) return; fTeam->RemoveUserBreakpoint(userBreakpoint); userBreakpoint->SetValid(false); userBreakpoint->SetEnabled(false); teamLocker.Unlock(); // uninstall the breakpoints as needed for (int32 i = 0; UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); i++) { if (Breakpoint* breakpoint = instance->GetBreakpoint()) _UpdateBreakpointInstallation(breakpoint); } teamLocker.Lock(); // detach the breakpoints from the user breakpoint instances for (int32 i = 0; UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); i++) { if (Breakpoint* breakpoint = instance->GetBreakpoint()) { instance->SetBreakpoint(NULL); breakpoint->RemoveUserBreakpoint(instance); if (breakpoint->IsUnused()) fTeam->RemoveBreakpoint(breakpoint); } } fTeam->NotifyUserBreakpointChanged(userBreakpoint); teamLocker.Unlock(); installLocker.Unlock(); // release the reference from InstallUserBreakpoint() userBreakpoint->ReleaseReference(); } status_t BreakpointManager::InstallTemporaryBreakpoint(target_addr_t address, BreakpointClient* client) { AutoLocker installLocker(fLock); AutoLocker teamLocker(fTeam); // create a breakpoint, if it doesn't exist yet Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address); if (breakpoint == NULL) { Image* image = fTeam->ImageByAddress(address); if (image == NULL) return B_BAD_ADDRESS; breakpoint = new(std::nothrow) Breakpoint(image, address); if (breakpoint == NULL) return B_NO_MEMORY; if (!fTeam->AddBreakpoint(breakpoint)) return B_NO_MEMORY; } BReference breakpointReference(breakpoint); // add the client status_t error; if (breakpoint->AddClient(client)) { if (breakpoint->IsInstalled()) return B_OK; // install teamLocker.Unlock(); error = fDebuggerInterface->InstallBreakpoint(address); if (error == B_OK) { breakpoint->SetInstalled(true); return B_OK; } teamLocker.Lock(); breakpoint->RemoveClient(client); } else error = B_NO_MEMORY; // clean up on error if (breakpoint->IsUnused()) fTeam->RemoveBreakpoint(breakpoint); return error; } void BreakpointManager::UninstallTemporaryBreakpoint(target_addr_t address, BreakpointClient* client) { AutoLocker installLocker(fLock); AutoLocker teamLocker(fTeam); Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address); if (breakpoint == NULL) return; // remove the client breakpoint->RemoveClient(client); // check whether the breakpoint needs to be uninstalled bool uninstall = !breakpoint->ShouldBeInstalled() && breakpoint->IsInstalled(); // if unused remove it BReference breakpointReference(breakpoint); if (breakpoint->IsUnused()) fTeam->RemoveBreakpoint(breakpoint); teamLocker.Unlock(); if (uninstall) { fDebuggerInterface->UninstallBreakpoint(address); breakpoint->SetInstalled(false); } } void BreakpointManager::UpdateImageBreakpoints(Image* image) { _UpdateImageBreakpoints(image, false); } void BreakpointManager::RemoveImageBreakpoints(Image* image) { _UpdateImageBreakpoints(image, true); } void BreakpointManager::_UpdateImageBreakpoints(Image* image, bool removeOnly) { AutoLocker installLocker(fLock); AutoLocker teamLocker(fTeam); // remove obsolete user breakpoint instances BObjectList breakpointsToUpdate; for (UserBreakpointList::ConstIterator it = fTeam->UserBreakpoints().GetIterator(); UserBreakpoint* userBreakpoint = it.Next();) { int32 instanceCount = userBreakpoint->CountInstances(); for (int32 i = instanceCount - 1; i >= 0; i--) { UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); Breakpoint* breakpoint = instance->GetBreakpoint(); if (breakpoint == NULL || breakpoint->GetImage() != image) continue; userBreakpoint->RemoveInstanceAt(i); breakpoint->RemoveUserBreakpoint(instance); if (!breakpointsToUpdate.AddItem(breakpoint)) { _UpdateBreakpointInstallation(breakpoint); if (breakpoint->IsUnused()) fTeam->RemoveBreakpoint(breakpoint); } delete instance; } } // update breakpoints teamLocker.Unlock(); for (int32 i = 0; Breakpoint* breakpoint = breakpointsToUpdate.ItemAt(i); i++) { _UpdateBreakpointInstallation(breakpoint); } teamLocker.Lock(); for (int32 i = 0; Breakpoint* breakpoint = breakpointsToUpdate.ItemAt(i); i++) { if (breakpoint->IsUnused()) fTeam->RemoveBreakpoint(breakpoint); } // add breakpoint instances for function instances in the image (if we have // an image debug info) BObjectList newInstances; ImageDebugInfo* imageDebugInfo = image->GetImageDebugInfo(); if (imageDebugInfo == NULL) return; for (UserBreakpointList::ConstIterator it = fTeam->UserBreakpoints().GetIterator(); UserBreakpoint* userBreakpoint = it.Next();) { // get the function Function* function = fTeam->FunctionByID( userBreakpoint->Location().GetFunctionID()); if (function == NULL) continue; const SourceLocation& sourceLocation = userBreakpoint->Location().GetSourceLocation(); target_addr_t relativeAddress = userBreakpoint->Location().RelativeAddress(); // iterate through the function instances for (FunctionInstanceList::ConstIterator it = function->Instances().GetIterator(); FunctionInstance* functionInstance = it.Next();) { if (functionInstance->GetImageDebugInfo() != imageDebugInfo) continue; // get the breakpoint address for the instance target_addr_t instanceAddress = 0; if (functionInstance->SourceFile() != NULL) { // We have a source file, so get the address for the source // location. Statement* statement = NULL; FunctionDebugInfo* functionDebugInfo = functionInstance->GetFunctionDebugInfo(); functionDebugInfo->GetSpecificImageDebugInfo() ->GetStatementAtSourceLocation(functionDebugInfo, sourceLocation, statement); if (statement != NULL) { instanceAddress = statement->CoveringAddressRange().Start(); // TODO: What about BreakpointAllowed()? statement->ReleaseReference(); // TODO: Make sure we do hit the function in question! } } if (instanceAddress == 0) { // No source file (or we failed getting the statement), so try // to use the same relative address. if (relativeAddress > functionInstance->Size()) continue; instanceAddress = functionInstance->Address() + relativeAddress; // TODO: Make sure it does at least hit an instruction! } // create the user breakpoint instance UserBreakpointInstance* instance = new(std::nothrow) UserBreakpointInstance(userBreakpoint, instanceAddress); if (instance == NULL || !newInstances.AddItem(instance)) { delete instance; continue; } if (!userBreakpoint->AddInstance(instance)) { newInstances.RemoveItemAt(newInstances.CountItems() - 1); delete instance; } // get/create the breakpoint for the address target_addr_t address = instance->Address(); Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address); if (breakpoint == NULL) { breakpoint = new(std::nothrow) Breakpoint(image, address); if (breakpoint == NULL || !fTeam->AddBreakpoint(breakpoint)) { delete breakpoint; break; } } breakpoint->AddUserBreakpoint(instance); instance->SetBreakpoint(breakpoint); } } // install the breakpoints for the new user breakpoint instances teamLocker.Unlock(); for (int32 i = 0; UserBreakpointInstance* instance = newInstances.ItemAt(i); i++) { Breakpoint* breakpoint = instance->GetBreakpoint(); if (breakpoint == NULL || _UpdateBreakpointInstallation(breakpoint) != B_OK) { // something went wrong -- remove the instance teamLocker.Lock(); instance->GetUserBreakpoint()->RemoveInstance(instance); if (breakpoint != NULL) { breakpoint->AddUserBreakpoint(instance); if (breakpoint->IsUnused()) fTeam->RemoveBreakpoint(breakpoint); } teamLocker.Unlock(); } } } 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 = B_OK; // if we're not actually connected to a team, silently // allow setting the breakpoint so it's saved to settings // for when we do connect/have the team in the debugger. if (fDebuggerInterface->Connected()) fDebuggerInterface->InstallBreakpoint(breakpoint->Address()); if (error != B_OK) return error; TRACE_CONTROL("BREAKPOINT at %#" B_PRIx64 " installed: %s\n", breakpoint->Address(), strerror(error)); breakpoint->SetInstalled(true); } else { // uninstall fDebuggerInterface->UninstallBreakpoint(breakpoint->Address()); TRACE_CONTROL("BREAKPOINT at %#" B_PRIx64 " uninstalled\n", breakpoint->Address()); breakpoint->SetInstalled(false); } return B_OK; }