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
34BreakpointProxy::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
48BreakpointProxy::~BreakpointProxy()
49{
50	if (fBreakpoint != NULL)
51		fBreakpoint->ReleaseReference();
52
53	if (fWatchpoint != NULL)
54		fWatchpoint->ReleaseReference();
55}
56
57
58breakpoint_proxy_type
59BreakpointProxy::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
69class BreakpointListView::ListInputFilter : public BMessageFilter {
70public:
71	ListInputFilter(BView* view)
72		:
73		BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, B_KEY_DOWN),
74		fTargetView(view)
75	{
76	}
77
78	~ListInputFilter()
79	{
80	}
81
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
93private:
94	BView*			fTargetView;
95};
96
97
98// #pragma mark - BreakpointsTableModel
99
100
101class BreakpointListView::BreakpointsTableModel : public TableModel {
102public:
103	BreakpointsTableModel(Team* team)
104		:
105		fTeam(team)
106	{
107		UpdateBreakpoint(NULL);
108	}
109
110	~BreakpointsTableModel()
111	{
112		fTeam = NULL;
113		UpdateBreakpoint(NULL);
114	}
115
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
218	virtual int32 CountColumns() const
219	{
220		return 4;
221	}
222
223	virtual int32 CountRows() const
224	{
225		return fBreakpointProxies.CountItems();
226	}
227
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
243	BreakpointProxy* BreakpointProxyAt(int32 index) const
244	{
245		return fBreakpointProxies.ItemAt(index);
246	}
247
248private:
249
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
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
316	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
336private:
337	Team*						fTeam;
338	BreakpointProxyList			fBreakpointProxies;
339};
340
341
342// #pragma mark - BreakpointListView
343
344
345BreakpointListView::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
356BreakpointListView::~BreakpointListView()
357{
358	fBreakpointsTable->SetTableModel(NULL);
359	delete fBreakpointsTableModel;
360}
361
362
363/*static*/ BreakpointListView*
364BreakpointListView::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
379void
380BreakpointListView::UnsetListener()
381{
382	fListener = NULL;
383}
384
385
386void
387BreakpointListView::UserBreakpointChanged(UserBreakpoint* breakpoint)
388{
389	if (breakpoint->IsHidden())
390		return;
391
392	BreakpointProxy proxy(breakpoint, NULL);
393	fBreakpointsTableModel->UpdateBreakpoint(&proxy);
394}
395
396
397void
398BreakpointListView::WatchpointChanged(Watchpoint* watchpoint)
399{
400	BreakpointProxy proxy(NULL, watchpoint);
401	fBreakpointsTableModel->UpdateBreakpoint(&proxy);
402}
403
404
405void
406BreakpointListView::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
416status_t
417BreakpointListView::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
431void
432BreakpointListView::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
452void
453BreakpointListView::_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
482BreakpointListView::Listener::~Listener()
483{
484}
485