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