1 /* 2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "BreakpointManager.h" 7 8 #include <stdio.h> 9 10 #include <new> 11 12 #include <AutoLocker.h> 13 14 #include "DebuggerInterface.h" 15 #include "Function.h" 16 #include "SpecificImageDebugInfo.h" 17 #include "Statement.h" 18 #include "Team.h" 19 #include "Tracing.h" 20 21 22 BreakpointManager::BreakpointManager(Team* team, 23 DebuggerInterface* debuggerInterface) 24 : 25 fLock("breakpoint manager"), 26 fTeam(team), 27 fDebuggerInterface(debuggerInterface) 28 { 29 fDebuggerInterface->AcquireReference(); 30 } 31 32 33 BreakpointManager::~BreakpointManager() 34 { 35 fDebuggerInterface->ReleaseReference(); 36 } 37 38 39 status_t 40 BreakpointManager::Init() 41 { 42 return fLock.InitCheck(); 43 } 44 45 46 status_t 47 BreakpointManager::InstallUserBreakpoint(UserBreakpoint* userBreakpoint, 48 bool enabled) 49 { 50 TRACE_CONTROL("BreakpointManager::InstallUserBreakpoint(%p, %d)\n", 51 userBreakpoint, enabled); 52 53 AutoLocker<BLocker> installLocker(fLock); 54 AutoLocker<Team> teamLocker(fTeam); 55 56 bool oldEnabled = userBreakpoint->IsEnabled(); 57 if (userBreakpoint->IsValid() && enabled == oldEnabled) { 58 TRACE_CONTROL(" user breakpoint already valid and with same enabled " 59 "state\n"); 60 return B_OK; 61 } 62 63 // get/create the breakpoints for all instances 64 TRACE_CONTROL(" creating breakpoints for breakpoint instances\n"); 65 66 status_t error = B_OK; 67 for (int32 i = 0; 68 UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); i++) { 69 70 TRACE_CONTROL(" breakpoint instance %p\n", instance); 71 72 if (instance->GetBreakpoint() != NULL) { 73 TRACE_CONTROL(" -> already has breakpoint\n"); 74 continue; 75 } 76 77 target_addr_t address = instance->Address(); 78 Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address); 79 if (breakpoint == NULL) { 80 TRACE_CONTROL(" -> no breakpoint at that address yet\n"); 81 82 Image* image = fTeam->ImageByAddress(address); 83 if (image == NULL) { 84 TRACE_CONTROL(" -> no image at that address\n"); 85 error = B_BAD_ADDRESS; 86 break; 87 } 88 89 breakpoint = new(std::nothrow) Breakpoint(image, address); 90 if (breakpoint == NULL || !fTeam->AddBreakpoint(breakpoint)) { 91 delete breakpoint; 92 error = B_NO_MEMORY; 93 break; 94 } 95 } 96 97 TRACE_CONTROL(" -> adding instance to breakpoint %p\n", breakpoint); 98 99 breakpoint->AddUserBreakpoint(instance); 100 instance->SetBreakpoint(breakpoint); 101 } 102 103 // If everything looks good so far mark the user breakpoint according to 104 // its new state. 105 if (error == B_OK) 106 userBreakpoint->SetEnabled(enabled); 107 108 // notify user breakpoint listeners 109 if (error == B_OK) 110 fTeam->NotifyUserBreakpointChanged(userBreakpoint); 111 112 teamLocker.Unlock(); 113 114 // install/uninstall the breakpoints as needed 115 TRACE_CONTROL(" updating breakpoints\n"); 116 117 if (error == B_OK) { 118 for (int32 i = 0; 119 UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); 120 i++) { 121 TRACE_CONTROL(" breakpoint instance %p\n", instance); 122 123 error = _UpdateBreakpointInstallation(instance->GetBreakpoint()); 124 if (error != B_OK) 125 break; 126 } 127 } 128 129 if (error == B_OK) { 130 TRACE_CONTROL(" success, marking user breakpoint valid\n"); 131 132 // everything went fine -- mark the user breakpoint valid 133 if (!userBreakpoint->IsValid()) { 134 teamLocker.Lock(); 135 userBreakpoint->SetValid(true); 136 userBreakpoint->AcquireReference(); 137 fTeam->AddUserBreakpoint(userBreakpoint); 138 fTeam->NotifyUserBreakpointChanged(userBreakpoint); 139 // notify again -- the breakpoint hadn't been added before 140 teamLocker.Unlock(); 141 } 142 } else { 143 // something went wrong -- revert the situation 144 TRACE_CONTROL(" error, reverting\n"); 145 146 teamLocker.Lock(); 147 userBreakpoint->SetEnabled(oldEnabled); 148 teamLocker.Unlock(); 149 150 if (!oldEnabled || !userBreakpoint->IsValid()) { 151 for (int32 i = 0; UserBreakpointInstance* instance 152 = userBreakpoint->InstanceAt(i); 153 i++) { 154 Breakpoint* breakpoint = instance->GetBreakpoint(); 155 if (breakpoint == NULL) 156 continue; 157 158 if (!userBreakpoint->IsValid()) { 159 instance->SetBreakpoint(NULL); 160 breakpoint->RemoveUserBreakpoint(instance); 161 } 162 163 _UpdateBreakpointInstallation(breakpoint); 164 165 teamLocker.Lock(); 166 167 if (breakpoint->IsUnused()) 168 fTeam->RemoveBreakpoint(breakpoint); 169 teamLocker.Unlock(); 170 } 171 172 teamLocker.Lock(); 173 fTeam->NotifyUserBreakpointChanged(userBreakpoint); 174 teamLocker.Unlock(); 175 } 176 } 177 178 installLocker.Unlock(); 179 180 return error; 181 } 182 183 184 void 185 BreakpointManager::UninstallUserBreakpoint(UserBreakpoint* userBreakpoint) 186 { 187 AutoLocker<BLocker> installLocker(fLock); 188 AutoLocker<Team> teamLocker(fTeam); 189 190 if (!userBreakpoint->IsValid()) 191 return; 192 193 fTeam->RemoveUserBreakpoint(userBreakpoint); 194 195 userBreakpoint->SetValid(false); 196 userBreakpoint->SetEnabled(false); 197 198 teamLocker.Unlock(); 199 200 // uninstall the breakpoints as needed 201 for (int32 i = 0; 202 UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); i++) { 203 if (Breakpoint* breakpoint = instance->GetBreakpoint()) 204 _UpdateBreakpointInstallation(breakpoint); 205 } 206 207 teamLocker.Lock(); 208 209 // detach the breakpoints from the user breakpoint instances 210 for (int32 i = 0; 211 UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); i++) { 212 if (Breakpoint* breakpoint = instance->GetBreakpoint()) { 213 instance->SetBreakpoint(NULL); 214 breakpoint->RemoveUserBreakpoint(instance); 215 216 if (breakpoint->IsUnused()) 217 fTeam->RemoveBreakpoint(breakpoint); 218 } 219 } 220 221 fTeam->NotifyUserBreakpointChanged(userBreakpoint); 222 223 teamLocker.Unlock(); 224 installLocker.Unlock(); 225 226 // release the reference from InstallUserBreakpoint() 227 userBreakpoint->ReleaseReference(); 228 } 229 230 231 status_t 232 BreakpointManager::InstallTemporaryBreakpoint(target_addr_t address, 233 BreakpointClient* client) 234 { 235 AutoLocker<BLocker> installLocker(fLock); 236 AutoLocker<Team> teamLocker(fTeam); 237 238 // create a breakpoint, if it doesn't exist yet 239 Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address); 240 if (breakpoint == NULL) { 241 Image* image = fTeam->ImageByAddress(address); 242 if (image == NULL) 243 return B_BAD_ADDRESS; 244 245 breakpoint = new(std::nothrow) Breakpoint(image, address); 246 if (breakpoint == NULL) 247 return B_NO_MEMORY; 248 249 if (!fTeam->AddBreakpoint(breakpoint)) 250 return B_NO_MEMORY; 251 } 252 253 BReference<Breakpoint> breakpointReference(breakpoint); 254 255 // add the client 256 status_t error; 257 if (breakpoint->AddClient(client)) { 258 if (breakpoint->IsInstalled()) 259 return B_OK; 260 261 // install 262 teamLocker.Unlock(); 263 264 error = fDebuggerInterface->InstallBreakpoint(address); 265 if (error == B_OK) { 266 breakpoint->SetInstalled(true); 267 return B_OK; 268 } 269 270 teamLocker.Lock(); 271 272 breakpoint->RemoveClient(client); 273 } else 274 error = B_NO_MEMORY; 275 276 // clean up on error 277 if (breakpoint->IsUnused()) 278 fTeam->RemoveBreakpoint(breakpoint); 279 280 return error; 281 } 282 283 284 void 285 BreakpointManager::UninstallTemporaryBreakpoint(target_addr_t address, 286 BreakpointClient* client) 287 { 288 AutoLocker<BLocker> installLocker(fLock); 289 AutoLocker<Team> teamLocker(fTeam); 290 291 Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address); 292 if (breakpoint == NULL) 293 return; 294 295 // remove the client 296 breakpoint->RemoveClient(client); 297 298 // check whether the breakpoint needs to be uninstalled 299 bool uninstall = !breakpoint->ShouldBeInstalled() 300 && breakpoint->IsInstalled(); 301 302 // if unused remove it 303 BReference<Breakpoint> breakpointReference(breakpoint); 304 if (breakpoint->IsUnused()) 305 fTeam->RemoveBreakpoint(breakpoint); 306 307 teamLocker.Unlock(); 308 309 if (uninstall) { 310 fDebuggerInterface->UninstallBreakpoint(address); 311 breakpoint->SetInstalled(false); 312 } 313 } 314 315 316 void 317 BreakpointManager::UpdateImageBreakpoints(Image* image) 318 { 319 _UpdateImageBreakpoints(image, false); 320 } 321 322 323 void 324 BreakpointManager::RemoveImageBreakpoints(Image* image) 325 { 326 _UpdateImageBreakpoints(image, true); 327 } 328 329 330 void 331 BreakpointManager::_UpdateImageBreakpoints(Image* image, bool removeOnly) 332 { 333 AutoLocker<BLocker> installLocker(fLock); 334 AutoLocker<Team> teamLocker(fTeam); 335 336 // remove obsolete user breakpoint instances 337 BObjectList<Breakpoint> breakpointsToUpdate; 338 for (UserBreakpointList::ConstIterator it 339 = fTeam->UserBreakpoints().GetIterator(); 340 UserBreakpoint* userBreakpoint = it.Next();) { 341 int32 instanceCount = userBreakpoint->CountInstances(); 342 for (int32 i = instanceCount - 1; i >= 0; i--) { 343 UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); 344 Breakpoint* breakpoint = instance->GetBreakpoint(); 345 if (breakpoint == NULL || breakpoint->GetImage() != image) 346 continue; 347 348 userBreakpoint->RemoveInstanceAt(i); 349 breakpoint->RemoveUserBreakpoint(instance); 350 351 if (!breakpointsToUpdate.AddItem(breakpoint)) { 352 _UpdateBreakpointInstallation(breakpoint); 353 if (breakpoint->IsUnused()) 354 fTeam->RemoveBreakpoint(breakpoint); 355 } 356 357 delete instance; 358 } 359 } 360 361 // update breakpoints 362 teamLocker.Unlock(); 363 for (int32 i = 0; Breakpoint* breakpoint = breakpointsToUpdate.ItemAt(i); 364 i++) { 365 _UpdateBreakpointInstallation(breakpoint); 366 } 367 368 teamLocker.Lock(); 369 for (int32 i = 0; Breakpoint* breakpoint = breakpointsToUpdate.ItemAt(i); 370 i++) { 371 if (breakpoint->IsUnused()) 372 fTeam->RemoveBreakpoint(breakpoint); 373 } 374 375 // add breakpoint instances for function instances in the image (if we have 376 // an image debug info) 377 BObjectList<UserBreakpointInstance> newInstances; 378 ImageDebugInfo* imageDebugInfo = image->GetImageDebugInfo(); 379 if (imageDebugInfo == NULL) 380 return; 381 382 for (UserBreakpointList::ConstIterator it 383 = fTeam->UserBreakpoints().GetIterator(); 384 UserBreakpoint* userBreakpoint = it.Next();) { 385 // get the function 386 Function* function = fTeam->FunctionByID( 387 userBreakpoint->Location().GetFunctionID()); 388 if (function == NULL) 389 continue; 390 391 const SourceLocation& sourceLocation 392 = userBreakpoint->Location().GetSourceLocation(); 393 target_addr_t relativeAddress 394 = userBreakpoint->Location().RelativeAddress(); 395 396 // iterate through the function instances 397 for (FunctionInstanceList::ConstIterator it 398 = function->Instances().GetIterator(); 399 FunctionInstance* functionInstance = it.Next();) { 400 if (functionInstance->GetImageDebugInfo() != imageDebugInfo) 401 continue; 402 403 // get the breakpoint address for the instance 404 target_addr_t instanceAddress = 0; 405 if (functionInstance->SourceFile() != NULL) { 406 // We have a source file, so get the address for the source 407 // location. 408 Statement* statement = NULL; 409 FunctionDebugInfo* functionDebugInfo 410 = functionInstance->GetFunctionDebugInfo(); 411 functionDebugInfo->GetSpecificImageDebugInfo() 412 ->GetStatementAtSourceLocation(functionDebugInfo, 413 sourceLocation, statement); 414 if (statement != NULL) { 415 instanceAddress = statement->CoveringAddressRange().Start(); 416 // TODO: What about BreakpointAllowed()? 417 statement->ReleaseReference(); 418 // TODO: Make sure we do hit the function in question! 419 } 420 } 421 422 if (instanceAddress == 0) { 423 // No source file (or we failed getting the statement), so try 424 // to use the same relative address. 425 if (relativeAddress > functionInstance->Size()) 426 continue; 427 instanceAddress = functionInstance->Address() + relativeAddress; 428 // TODO: Make sure it does at least hit an instruction! 429 } 430 431 // create the user breakpoint instance 432 UserBreakpointInstance* instance = new(std::nothrow) 433 UserBreakpointInstance(userBreakpoint, instanceAddress); 434 if (instance == NULL || !newInstances.AddItem(instance)) { 435 delete instance; 436 continue; 437 } 438 439 if (!userBreakpoint->AddInstance(instance)) { 440 newInstances.RemoveItemAt(newInstances.CountItems() - 1); 441 delete instance; 442 } 443 444 // get/create the breakpoint for the address 445 target_addr_t address = instance->Address(); 446 Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address); 447 if (breakpoint == NULL) { 448 breakpoint = new(std::nothrow) Breakpoint(image, address); 449 if (breakpoint == NULL || !fTeam->AddBreakpoint(breakpoint)) { 450 delete breakpoint; 451 break; 452 } 453 } 454 455 breakpoint->AddUserBreakpoint(instance); 456 instance->SetBreakpoint(breakpoint); 457 } 458 } 459 460 // install the breakpoints for the new user breakpoint instances 461 teamLocker.Unlock(); 462 for (int32 i = 0; UserBreakpointInstance* instance = newInstances.ItemAt(i); 463 i++) { 464 Breakpoint* breakpoint = instance->GetBreakpoint(); 465 if (breakpoint == NULL 466 || _UpdateBreakpointInstallation(breakpoint) != B_OK) { 467 // something went wrong -- remove the instance 468 teamLocker.Lock(); 469 470 instance->GetUserBreakpoint()->RemoveInstance(instance); 471 if (breakpoint != NULL) { 472 breakpoint->AddUserBreakpoint(instance); 473 if (breakpoint->IsUnused()) 474 fTeam->RemoveBreakpoint(breakpoint); 475 } 476 477 teamLocker.Unlock(); 478 } 479 } 480 } 481 482 483 status_t 484 BreakpointManager::_UpdateBreakpointInstallation(Breakpoint* breakpoint) 485 { 486 bool shouldBeInstalled = breakpoint->ShouldBeInstalled(); 487 488 TRACE_CONTROL("BreakpointManager::_UpdateBreakpointInstallation(%p): " 489 "should be installed: %d, is installed: %d\n", breakpoint, 490 shouldBeInstalled, breakpoint->IsInstalled()); 491 492 if (shouldBeInstalled == breakpoint->IsInstalled()) 493 return B_OK; 494 495 if (shouldBeInstalled) { 496 // install 497 status_t error = B_OK; 498 // if we're not actually connected to a team, silently 499 // allow setting the breakpoint so it's saved to settings 500 // for when we do connect/have the team in the debugger. 501 if (fDebuggerInterface->Connected()) 502 fDebuggerInterface->InstallBreakpoint(breakpoint->Address()); 503 504 if (error != B_OK) 505 return error; 506 507 TRACE_CONTROL("BREAKPOINT at %#" B_PRIx64 " installed: %s\n", 508 breakpoint->Address(), strerror(error)); 509 510 breakpoint->SetInstalled(true); 511 } else { 512 // uninstall 513 fDebuggerInterface->UninstallBreakpoint(breakpoint->Address()); 514 515 TRACE_CONTROL("BREAKPOINT at %#" B_PRIx64 " uninstalled\n", 516 breakpoint->Address()); 517 518 breakpoint->SetInstalled(false); 519 } 520 521 return B_OK; 522 } 523