headers/bsd: Add sys/queue.h.
[haiku.git] / src / kits / debugger / debug_managers / BreakpointManager.cpp
blob71dd19328113880ddc3daf7b925a0baaddc90685
1 /*
2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include "BreakpointManager.h"
8 #include <stdio.h>
10 #include <new>
12 #include <AutoLocker.h>
14 #include "DebuggerInterface.h"
15 #include "Function.h"
16 #include "SpecificImageDebugInfo.h"
17 #include "Statement.h"
18 #include "Team.h"
19 #include "Tracing.h"
22 BreakpointManager::BreakpointManager(Team* team,
23 DebuggerInterface* debuggerInterface)
25 fLock("breakpoint manager"),
26 fTeam(team),
27 fDebuggerInterface(debuggerInterface)
29 fDebuggerInterface->AcquireReference();
33 BreakpointManager::~BreakpointManager()
35 fDebuggerInterface->ReleaseReference();
39 status_t
40 BreakpointManager::Init()
42 return fLock.InitCheck();
46 status_t
47 BreakpointManager::InstallUserBreakpoint(UserBreakpoint* userBreakpoint,
48 bool enabled)
50 TRACE_CONTROL("BreakpointManager::InstallUserBreakpoint(%p, %d)\n",
51 userBreakpoint, enabled);
53 AutoLocker<BLocker> installLocker(fLock);
54 AutoLocker<Team> teamLocker(fTeam);
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;
63 // get/create the breakpoints for all instances
64 TRACE_CONTROL(" creating breakpoints for breakpoint instances\n");
66 status_t error = B_OK;
67 for (int32 i = 0;
68 UserBreakpointInstance* instance = userBreakpoint->InstanceAt(i); i++) {
70 TRACE_CONTROL(" breakpoint instance %p\n", instance);
72 if (instance->GetBreakpoint() != NULL) {
73 TRACE_CONTROL(" -> already has breakpoint\n");
74 continue;
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");
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;
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;
97 TRACE_CONTROL(" -> adding instance to breakpoint %p\n", breakpoint);
99 breakpoint->AddUserBreakpoint(instance);
100 instance->SetBreakpoint(breakpoint);
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);
108 // notify user breakpoint listeners
109 if (error == B_OK)
110 fTeam->NotifyUserBreakpointChanged(userBreakpoint);
112 teamLocker.Unlock();
114 // install/uninstall the breakpoints as needed
115 TRACE_CONTROL(" updating breakpoints\n");
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);
123 error = _UpdateBreakpointInstallation(instance->GetBreakpoint());
124 if (error != B_OK)
125 break;
129 if (error == B_OK) {
130 TRACE_CONTROL(" success, marking user breakpoint valid\n");
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();
142 } else {
143 // something went wrong -- revert the situation
144 TRACE_CONTROL(" error, reverting\n");
146 teamLocker.Lock();
147 userBreakpoint->SetEnabled(oldEnabled);
148 teamLocker.Unlock();
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;
158 if (!userBreakpoint->IsValid()) {
159 instance->SetBreakpoint(NULL);
160 breakpoint->RemoveUserBreakpoint(instance);
163 _UpdateBreakpointInstallation(breakpoint);
165 teamLocker.Lock();
167 if (breakpoint->IsUnused())
168 fTeam->RemoveBreakpoint(breakpoint);
169 teamLocker.Unlock();
172 teamLocker.Lock();
173 fTeam->NotifyUserBreakpointChanged(userBreakpoint);
174 teamLocker.Unlock();
178 installLocker.Unlock();
180 return error;
184 void
185 BreakpointManager::UninstallUserBreakpoint(UserBreakpoint* userBreakpoint)
187 AutoLocker<BLocker> installLocker(fLock);
188 AutoLocker<Team> teamLocker(fTeam);
190 if (!userBreakpoint->IsValid())
191 return;
193 fTeam->RemoveUserBreakpoint(userBreakpoint);
195 userBreakpoint->SetValid(false);
196 userBreakpoint->SetEnabled(false);
198 teamLocker.Unlock();
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);
207 teamLocker.Lock();
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);
216 if (breakpoint->IsUnused())
217 fTeam->RemoveBreakpoint(breakpoint);
221 fTeam->NotifyUserBreakpointChanged(userBreakpoint);
223 teamLocker.Unlock();
224 installLocker.Unlock();
226 // release the reference from InstallUserBreakpoint()
227 userBreakpoint->ReleaseReference();
231 status_t
232 BreakpointManager::InstallTemporaryBreakpoint(target_addr_t address,
233 BreakpointClient* client)
235 AutoLocker<BLocker> installLocker(fLock);
236 AutoLocker<Team> teamLocker(fTeam);
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;
245 breakpoint = new(std::nothrow) Breakpoint(image, address);
246 if (breakpoint == NULL)
247 return B_NO_MEMORY;
249 if (!fTeam->AddBreakpoint(breakpoint))
250 return B_NO_MEMORY;
253 BReference<Breakpoint> breakpointReference(breakpoint);
255 // add the client
256 status_t error;
257 if (breakpoint->AddClient(client)) {
258 if (breakpoint->IsInstalled())
259 return B_OK;
261 // install
262 teamLocker.Unlock();
264 error = fDebuggerInterface->InstallBreakpoint(address);
265 if (error == B_OK) {
266 breakpoint->SetInstalled(true);
267 return B_OK;
270 teamLocker.Lock();
272 breakpoint->RemoveClient(client);
273 } else
274 error = B_NO_MEMORY;
276 // clean up on error
277 if (breakpoint->IsUnused())
278 fTeam->RemoveBreakpoint(breakpoint);
280 return error;
284 void
285 BreakpointManager::UninstallTemporaryBreakpoint(target_addr_t address,
286 BreakpointClient* client)
288 AutoLocker<BLocker> installLocker(fLock);
289 AutoLocker<Team> teamLocker(fTeam);
291 Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address);
292 if (breakpoint == NULL)
293 return;
295 // remove the client
296 breakpoint->RemoveClient(client);
298 // check whether the breakpoint needs to be uninstalled
299 bool uninstall = !breakpoint->ShouldBeInstalled()
300 && breakpoint->IsInstalled();
302 // if unused remove it
303 BReference<Breakpoint> breakpointReference(breakpoint);
304 if (breakpoint->IsUnused())
305 fTeam->RemoveBreakpoint(breakpoint);
307 teamLocker.Unlock();
309 if (uninstall) {
310 fDebuggerInterface->UninstallBreakpoint(address);
311 breakpoint->SetInstalled(false);
316 void
317 BreakpointManager::UpdateImageBreakpoints(Image* image)
319 _UpdateImageBreakpoints(image, false);
323 void
324 BreakpointManager::RemoveImageBreakpoints(Image* image)
326 _UpdateImageBreakpoints(image, true);
330 void
331 BreakpointManager::_UpdateImageBreakpoints(Image* image, bool removeOnly)
333 AutoLocker<BLocker> installLocker(fLock);
334 AutoLocker<Team> teamLocker(fTeam);
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;
348 userBreakpoint->RemoveInstanceAt(i);
349 breakpoint->RemoveUserBreakpoint(instance);
351 if (!breakpointsToUpdate.AddItem(breakpoint)) {
352 _UpdateBreakpointInstallation(breakpoint);
353 if (breakpoint->IsUnused())
354 fTeam->RemoveBreakpoint(breakpoint);
357 delete instance;
361 // update breakpoints
362 teamLocker.Unlock();
363 for (int32 i = 0; Breakpoint* breakpoint = breakpointsToUpdate.ItemAt(i);
364 i++) {
365 _UpdateBreakpointInstallation(breakpoint);
368 teamLocker.Lock();
369 for (int32 i = 0; Breakpoint* breakpoint = breakpointsToUpdate.ItemAt(i);
370 i++) {
371 if (breakpoint->IsUnused())
372 fTeam->RemoveBreakpoint(breakpoint);
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;
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;
391 const SourceLocation& sourceLocation
392 = userBreakpoint->Location().GetSourceLocation();
393 target_addr_t relativeAddress
394 = userBreakpoint->Location().RelativeAddress();
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;
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!
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!
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;
439 if (!userBreakpoint->AddInstance(instance)) {
440 newInstances.RemoveItemAt(newInstances.CountItems() - 1);
441 delete instance;
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;
455 breakpoint->AddUserBreakpoint(instance);
456 instance->SetBreakpoint(breakpoint);
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();
470 instance->GetUserBreakpoint()->RemoveInstance(instance);
471 if (breakpoint != NULL) {
472 breakpoint->AddUserBreakpoint(instance);
473 if (breakpoint->IsUnused())
474 fTeam->RemoveBreakpoint(breakpoint);
477 teamLocker.Unlock();
483 status_t
484 BreakpointManager::_UpdateBreakpointInstallation(Breakpoint* breakpoint)
486 bool shouldBeInstalled = breakpoint->ShouldBeInstalled();
488 TRACE_CONTROL("BreakpointManager::_UpdateBreakpointInstallation(%p): "
489 "should be installed: %d, is installed: %d\n", breakpoint,
490 shouldBeInstalled, breakpoint->IsInstalled());
492 if (shouldBeInstalled == breakpoint->IsInstalled())
493 return B_OK;
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());
504 if (error != B_OK)
505 return error;
507 TRACE_CONTROL("BREAKPOINT at %#" B_PRIx64 " installed: %s\n",
508 breakpoint->Address(), strerror(error));
510 breakpoint->SetInstalled(true);
511 } else {
512 // uninstall
513 fDebuggerInterface->UninstallBreakpoint(breakpoint->Address());
515 TRACE_CONTROL("BREAKPOINT at %#" B_PRIx64 " uninstalled\n",
516 breakpoint->Address());
518 breakpoint->SetInstalled(false);
521 return B_OK;