xref: /haiku/src/kits/debugger/debug_managers/BreakpointManager.cpp (revision fce4895d1884da5ae6fb299d23c735c598e690b1)
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