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
BreakpointManager(Team * team,DebuggerInterface * debuggerInterface)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
~BreakpointManager()33 BreakpointManager::~BreakpointManager()
34 {
35 fDebuggerInterface->ReleaseReference();
36 }
37
38
39 status_t
Init()40 BreakpointManager::Init()
41 {
42 return fLock.InitCheck();
43 }
44
45
46 status_t
InstallUserBreakpoint(UserBreakpoint * userBreakpoint,bool enabled)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
UninstallUserBreakpoint(UserBreakpoint * userBreakpoint)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
InstallTemporaryBreakpoint(target_addr_t address,BreakpointClient * client)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
UninstallTemporaryBreakpoint(target_addr_t address,BreakpointClient * client)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
UpdateImageBreakpoints(Image * image)317 BreakpointManager::UpdateImageBreakpoints(Image* image)
318 {
319 _UpdateImageBreakpoints(image, false);
320 }
321
322
323 void
RemoveImageBreakpoints(Image * image)324 BreakpointManager::RemoveImageBreakpoints(Image* image)
325 {
326 _UpdateImageBreakpoints(image, true);
327 }
328
329
330 void
_UpdateImageBreakpoints(Image * image,bool removeOnly)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
_UpdateBreakpointInstallation(Breakpoint * breakpoint)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