xref: /haiku/src/apps/debugger/user_interface/gui/team_window/BreakpointListView.cpp (revision 97f11716bfaa0f385eb0e28a52bf56a5023b9e99)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2011-2013, Rene Gollent, rene@gollent.com.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include "BreakpointListView.h"
9 
10 #include <stdio.h>
11 
12 #include <new>
13 
14 #include <MessageFilter.h>
15 
16 #include <AutoLocker.h>
17 #include <ObjectList.h>
18 
19 #include "Architecture.h"
20 #include "FunctionID.h"
21 #include "GuiSettingsUtils.h"
22 #include "LocatableFile.h"
23 #include "MessageCodes.h"
24 #include "table/TableColumns.h"
25 #include "TargetAddressTableColumn.h"
26 #include "Team.h"
27 #include "UserBreakpoint.h"
28 #include "Watchpoint.h"
29 
30 
31 // #pragma mark - BreakpointProxy
32 
33 
BreakpointProxy(UserBreakpoint * breakpoint,Watchpoint * watchpoint)34 BreakpointProxy::BreakpointProxy(UserBreakpoint* breakpoint,
35 	Watchpoint* watchpoint)
36 	:
37 	fBreakpoint(breakpoint),
38 	fWatchpoint(watchpoint)
39 {
40 	if (fBreakpoint != NULL)
41 		fBreakpoint->AcquireReference();
42 
43 	if (fWatchpoint != NULL)
44 		fWatchpoint->AcquireReference();
45 }
46 
47 
~BreakpointProxy()48 BreakpointProxy::~BreakpointProxy()
49 {
50 	if (fBreakpoint != NULL)
51 		fBreakpoint->ReleaseReference();
52 
53 	if (fWatchpoint != NULL)
54 		fWatchpoint->ReleaseReference();
55 }
56 
57 
58 breakpoint_proxy_type
Type() const59 BreakpointProxy::Type() const
60 {
61 	return fBreakpoint != NULL ? BREAKPOINT_PROXY_TYPE_BREAKPOINT
62 			: BREAKPOINT_PROXY_TYPE_WATCHPOINT;
63 }
64 
65 
66 // #pragma mark - ListInputFilter
67 
68 
69 class BreakpointListView::ListInputFilter : public BMessageFilter {
70 public:
ListInputFilter(BView * view)71 	ListInputFilter(BView* view)
72 		:
73 		BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, B_KEY_DOWN),
74 		fTargetView(view)
75 	{
76 	}
77 
~ListInputFilter()78 	~ListInputFilter()
79 	{
80 	}
81 
Filter(BMessage * message,BHandler ** target)82 	filter_result Filter(BMessage* message, BHandler** target)
83 	{
84 		const char* bytes;
85 		if (message->FindString("bytes", &bytes) == B_OK
86 			&& bytes[0] == B_DELETE) {
87 			BMessenger(fTargetView).SendMessage(MSG_CLEAR_BREAKPOINT);
88 		}
89 
90 		return B_DISPATCH_MESSAGE;
91 	}
92 
93 private:
94 	BView*			fTargetView;
95 };
96 
97 
98 // #pragma mark - BreakpointsTableModel
99 
100 
101 class BreakpointListView::BreakpointsTableModel : public TableModel {
102 public:
BreakpointsTableModel(Team * team)103 	BreakpointsTableModel(Team* team)
104 		:
105 		fTeam(team)
106 	{
107 		UpdateBreakpoint(NULL);
108 	}
109 
~BreakpointsTableModel()110 	~BreakpointsTableModel()
111 	{
112 		fTeam = NULL;
113 		UpdateBreakpoint(NULL);
114 	}
115 
UpdateBreakpoint(BreakpointProxy * proxy)116 	bool UpdateBreakpoint(BreakpointProxy* proxy)
117 	{
118 		if (fTeam == NULL) {
119 			for (int32 i = 0;
120 				BreakpointProxy* proxy = fBreakpointProxies.ItemAt(i);
121 				i++) {
122 				proxy->ReleaseReference();
123 			}
124 			fBreakpointProxies.MakeEmpty();
125 
126 			return true;
127 		}
128 
129 		AutoLocker<Team> locker(fTeam);
130 
131 		UserBreakpointList::ConstIterator it
132 			= fTeam->UserBreakpoints().GetIterator();
133 		int32 watchpointIndex = 0;
134 		UserBreakpoint* newBreakpoint = it.Next();
135 		Watchpoint* newWatchpoint = fTeam->WatchpointAt(watchpointIndex);
136 		int32 index = 0;
137 		bool remove;
138 
139 		// remove no longer existing breakpoints
140 		while (BreakpointProxy* oldProxy = fBreakpointProxies.ItemAt(index)) {
141 			remove = false;
142 			switch (oldProxy->Type()) {
143 				case BREAKPOINT_PROXY_TYPE_BREAKPOINT:
144 				{
145 					UserBreakpoint* breakpoint = oldProxy->GetBreakpoint();
146 					if (breakpoint == newBreakpoint) {
147 						if (breakpoint == proxy->GetBreakpoint())
148 							NotifyRowsChanged(index, 1);
149 						++index;
150 						newBreakpoint = it.Next();
151 					} else
152 						remove = true;
153 				}
154 				break;
155 
156 				case BREAKPOINT_PROXY_TYPE_WATCHPOINT:
157 				{
158 					Watchpoint* watchpoint = oldProxy->GetWatchpoint();
159 					if (watchpoint == newWatchpoint) {
160 						if (watchpoint == proxy->GetWatchpoint())
161 							NotifyRowsChanged(index, 1);
162 						++watchpointIndex;
163 						++index;
164 						newWatchpoint = fTeam->WatchpointAt(watchpointIndex);
165 					} else
166 						remove = true;
167 				}
168 				break;
169 			}
170 
171 			if (remove) {
172 				// TODO: Not particularly efficient!
173 				fBreakpointProxies.RemoveItemAt(index);
174 				oldProxy->ReleaseReference();
175 				NotifyRowsRemoved(index, 1);
176 			}
177 		}
178 
179 		// add new breakpoints
180 		int32 countBefore = fBreakpointProxies.CountItems();
181 		BreakpointProxy* newProxy = NULL;
182 		BReference<BreakpointProxy> proxyReference;
183 		while (newBreakpoint != NULL) {
184 			newProxy = new(std::nothrow) BreakpointProxy(newBreakpoint, NULL);
185 			if (newProxy == NULL)
186 				return false;
187 
188 			proxyReference.SetTo(newProxy, true);
189 			if (!fBreakpointProxies.AddItem(newProxy))
190 				return false;
191 
192 			proxyReference.Detach();
193 			newBreakpoint = it.Next();
194 		}
195 
196 		// add new watchpoints
197 		while (newWatchpoint != NULL) {
198 			newProxy = new(std::nothrow) BreakpointProxy(NULL, newWatchpoint);
199 			if (newProxy == NULL)
200 				return false;
201 
202 			proxyReference.SetTo(newProxy, true);
203 			if (!fBreakpointProxies.AddItem(newProxy))
204 				return false;
205 
206 			proxyReference.Detach();
207 			newWatchpoint = fTeam->WatchpointAt(++watchpointIndex);
208 		}
209 
210 
211 		int32 count = fBreakpointProxies.CountItems();
212 		if (count > countBefore)
213 			NotifyRowsAdded(countBefore, count - countBefore);
214 
215 		return true;
216 	}
217 
CountColumns() const218 	virtual int32 CountColumns() const
219 	{
220 		return 4;
221 	}
222 
CountRows() const223 	virtual int32 CountRows() const
224 	{
225 		return fBreakpointProxies.CountItems();
226 	}
227 
GetValueAt(int32 rowIndex,int32 columnIndex,BVariant & value)228 	virtual bool GetValueAt(int32 rowIndex, int32 columnIndex, BVariant& value)
229 	{
230 		BreakpointProxy* proxy = fBreakpointProxies.ItemAt(rowIndex);
231 		if (proxy == NULL)
232 			return false;
233 
234 		if (proxy->Type() == BREAKPOINT_PROXY_TYPE_BREAKPOINT) {
235 			return _GetBreakpointValueAt(proxy->GetBreakpoint(), rowIndex,
236 				columnIndex, value);
237 		}
238 
239 		return _GetWatchpointValueAt(proxy->GetWatchpoint(), rowIndex,
240 			columnIndex, value);
241 	}
242 
BreakpointProxyAt(int32 index) const243 	BreakpointProxy* BreakpointProxyAt(int32 index) const
244 	{
245 		return fBreakpointProxies.ItemAt(index);
246 	}
247 
248 private:
249 
_GetBreakpointValueAt(UserBreakpoint * breakpoint,int32 rowIndex,int32 columnIndex,BVariant & value)250 	bool _GetBreakpointValueAt(UserBreakpoint* breakpoint, int32 rowIndex,
251 		int32 columnIndex, BVariant &value)
252 	{
253 		const UserBreakpointLocation& location = breakpoint->Location();
254 
255 		switch (columnIndex) {
256 			case 0:
257 				value.SetTo((int32)breakpoint->IsEnabled());
258 				return true;
259 			case 1:
260 				value.SetTo(location.GetFunctionID()->FunctionName(),
261 					B_VARIANT_DONT_COPY_DATA);
262 				return true;
263 			case 2:
264 			{
265 				LocatableFile* sourceFile = location.SourceFile();
266 				BString data;
267 				if (sourceFile != NULL) {
268 					data.SetToFormat("%s:%" B_PRId32, sourceFile->Name(),
269 						location.GetSourceLocation().Line() + 1);
270 				} else {
271 					AutoLocker<Team> teamLocker(fTeam);
272 					if (UserBreakpointInstance* instance
273 							= breakpoint->InstanceAt(0)) {
274 						data.SetToFormat("%#" B_PRIx64, instance->Address());
275 					}
276 				}
277 				value.SetTo(data);
278 				return true;
279 			}
280 			case 3:
281 			{
282 				value.SetTo(breakpoint->Condition(),
283 					B_VARIANT_DONT_COPY_DATA);
284 				return true;
285 			}
286 			default:
287 				return false;
288 		}
289 	}
290 
_GetWatchpointValueAt(Watchpoint * watchpoint,int32 rowIndex,int32 columnIndex,BVariant & value)291 	bool _GetWatchpointValueAt(Watchpoint* watchpoint, int32 rowIndex,
292 		int32 columnIndex, BVariant &value)
293 	{
294 		switch (columnIndex) {
295 			case 0:
296 				value.SetTo((int32)watchpoint->IsEnabled());
297 				return true;
298 			case 1:
299 			{
300 				BString data;
301 				data.SetToFormat("%s at 0x%" B_PRIx64 " (%" B_PRId32 " bytes)",
302 					_WatchpointTypeToString(watchpoint->Type()),
303 					watchpoint->Address(), watchpoint->Length());
304 				value.SetTo(data);
305 				return true;
306 			}
307 			case 2:
308 			{
309 				return false;
310 			}
311 			default:
312 				return false;
313 		}
314 	}
315 
_WatchpointTypeToString(uint32 type) const316 	const char* _WatchpointTypeToString(uint32 type) const
317 	{
318 		switch (type) {
319 			case WATCHPOINT_CAPABILITY_FLAG_READ:
320 			{
321 				return "read";
322 			}
323 			case WATCHPOINT_CAPABILITY_FLAG_WRITE:
324 			{
325 				return "write";
326 			}
327 			case WATCHPOINT_CAPABILITY_FLAG_READ_WRITE:
328 			{
329 				return "read/write";
330 			}
331 			default:
332 				return NULL;
333 		}
334 	}
335 
336 private:
337 	Team*						fTeam;
338 	BreakpointProxyList			fBreakpointProxies;
339 };
340 
341 
342 // #pragma mark - BreakpointListView
343 
344 
BreakpointListView(Team * team,Listener * listener)345 BreakpointListView::BreakpointListView(Team* team, Listener* listener)
346 	:
347 	BGroupView(B_VERTICAL),
348 	fTeam(team),
349 	fBreakpointsTable(NULL),
350 	fBreakpointsTableModel(NULL),
351 	fListener(listener)
352 {
353 }
354 
355 
~BreakpointListView()356 BreakpointListView::~BreakpointListView()
357 {
358 	fBreakpointsTable->SetTableModel(NULL);
359 	delete fBreakpointsTableModel;
360 }
361 
362 
363 /*static*/ BreakpointListView*
Create(Team * team,Listener * listener,BView * filterTarget)364 BreakpointListView::Create(Team* team, Listener* listener, BView* filterTarget)
365 {
366 	BreakpointListView* self = new BreakpointListView(team, listener);
367 
368 	try {
369 		self->_Init(filterTarget);
370 	} catch (...) {
371 		delete self;
372 		throw;
373 	}
374 
375 	return self;
376 }
377 
378 
379 void
UnsetListener()380 BreakpointListView::UnsetListener()
381 {
382 	fListener = NULL;
383 }
384 
385 
386 void
UserBreakpointChanged(UserBreakpoint * breakpoint)387 BreakpointListView::UserBreakpointChanged(UserBreakpoint* breakpoint)
388 {
389 	if (breakpoint->IsHidden())
390 		return;
391 
392 	BreakpointProxy proxy(breakpoint, NULL);
393 	fBreakpointsTableModel->UpdateBreakpoint(&proxy);
394 }
395 
396 
397 void
WatchpointChanged(Watchpoint * watchpoint)398 BreakpointListView::WatchpointChanged(Watchpoint* watchpoint)
399 {
400 	BreakpointProxy proxy(NULL, watchpoint);
401 	fBreakpointsTableModel->UpdateBreakpoint(&proxy);
402 }
403 
404 
405 void
LoadSettings(const BMessage & settings)406 BreakpointListView::LoadSettings(const BMessage& settings)
407 {
408 	BMessage tableSettings;
409 	if (settings.FindMessage("breakpointsTable", &tableSettings) == B_OK) {
410 		GuiSettingsUtils::UnarchiveTableSettings(tableSettings,
411 			fBreakpointsTable);
412 	}
413 }
414 
415 
416 status_t
SaveSettings(BMessage & settings)417 BreakpointListView::SaveSettings(BMessage& settings)
418 {
419 	settings.MakeEmpty();
420 
421 	BMessage tableSettings;
422 	status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings,
423 		fBreakpointsTable);
424 	if (result == B_OK)
425 		result = settings.AddMessage("breakpointsTable", &tableSettings);
426 
427 	return result;
428 }
429 
430 
431 void
TableSelectionChanged(Table * table)432 BreakpointListView::TableSelectionChanged(Table* table)
433 {
434 	if (fListener == NULL)
435 		return;
436 
437 	TableSelectionModel* selectionModel = table->SelectionModel();
438 	BreakpointProxyList proxyList;
439 	for (int32 i = 0; i < selectionModel->CountRows(); i++) {
440 		BreakpointProxy* proxy = fBreakpointsTableModel->BreakpointProxyAt(
441 			selectionModel->RowAt(i));
442 		if (proxy == NULL)
443 			continue;
444 		if (!proxyList.AddItem(proxy))
445 			return;
446 	}
447 
448 	fListener->BreakpointSelectionChanged(proxyList);
449 }
450 
451 
452 void
_Init(BView * filterTarget)453 BreakpointListView::_Init(BView* filterTarget)
454 {
455 	fBreakpointsTable = new Table("breakpoints list", 0, B_FANCY_BORDER);
456 	fBreakpointsTable->SetFont(B_FONT_ROW, be_fixed_font);
457 	AddChild(fBreakpointsTable->ToView());
458 
459 	// columns
460 	fBreakpointsTable->AddColumn(new BoolStringTableColumn(0, "State", 70, 20,
461 		1000, "Enabled", "Disabled"));
462 	fBreakpointsTable->AddColumn(new StringTableColumn(1, "Location", 250, 40,
463 		1000, B_TRUNCATE_END, B_ALIGN_LEFT));
464 	fBreakpointsTable->AddColumn(new StringTableColumn(2, "File:Line/Address",
465 		250, 40, 1000, B_TRUNCATE_END, B_ALIGN_LEFT));
466 	fBreakpointsTable->AddColumn(new StringTableColumn(3, "Condition",
467 		250, 40, 1000, B_TRUNCATE_END, B_ALIGN_LEFT));
468 
469 	fBreakpointsTable->SetSelectionMode(B_MULTIPLE_SELECTION_LIST);
470 	fBreakpointsTable->AddTableListener(this);
471 	fBreakpointsTable->AddFilter(new ListInputFilter(filterTarget));
472 
473 
474 	fBreakpointsTableModel = new BreakpointsTableModel(fTeam);
475 	fBreakpointsTable->SetTableModel(fBreakpointsTableModel);
476 }
477 
478 
479 // #pragma mark - Listener
480 
481 
~Listener()482 BreakpointListView::Listener::~Listener()
483 {
484 }
485