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
22BreakpointManager::BreakpointManager(Team* team,
23	DebuggerInterface* debuggerInterface)
24	:
25	fLock("breakpoint manager"),
26	fTeam(team),
27	fDebuggerInterface(debuggerInterface)
28{
29	fDebuggerInterface->AcquireReference();
30}
31
32
33BreakpointManager::~BreakpointManager()
34{
35	fDebuggerInterface->ReleaseReference();
36}
37
38
39status_t
40BreakpointManager::Init()
41{
42	return fLock.InitCheck();
43}
44
45
46status_t
47BreakpointManager::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
184void
185BreakpointManager::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
231status_t
232BreakpointManager::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
284void
285BreakpointManager::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
316void
317BreakpointManager::UpdateImageBreakpoints(Image* image)
318{
319	_UpdateImageBreakpoints(image, false);
320}
321
322
323void
324BreakpointManager::RemoveImageBreakpoints(Image* image)
325{
326	_UpdateImageBreakpoints(image, true);
327}
328
329
330void
331BreakpointManager::_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
483status_t
484BreakpointManager::_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