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