1 /* 2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2011-2014, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "StackTraceView.h" 9 10 #include <stdio.h> 11 12 #include <new> 13 14 #include <ControlLook.h> 15 #include <Window.h> 16 17 #include <AutoDeleter.h> 18 19 #include "table/TableColumns.h" 20 21 #include "FunctionInstance.h" 22 #include "GuiSettingsUtils.h" 23 #include "Image.h" 24 #include "StackTrace.h" 25 #include "TargetAddressTableColumn.h" 26 #include "UiUtils.h" 27 28 29 // #pragma mark - FramesTableModel 30 31 32 class StackTraceView::FramesTableModel : public TableModel { 33 public: 34 FramesTableModel() 35 : 36 fStackTrace(NULL) 37 { 38 } 39 40 ~FramesTableModel() 41 { 42 SetStackTrace(NULL); 43 } 44 45 void SetStackTrace(StackTrace* stackTrace) 46 { 47 // unset old frames 48 if (fStackTrace != NULL && fStackTrace->CountFrames()) 49 NotifyRowsRemoved(0, fStackTrace->CountFrames()); 50 51 fStackTrace = stackTrace; 52 53 // set new frames 54 if (fStackTrace != NULL && fStackTrace->CountFrames() > 0) 55 NotifyRowsAdded(0, fStackTrace->CountFrames()); 56 } 57 58 virtual int32 CountColumns() const 59 { 60 return 3; 61 } 62 63 virtual int32 CountRows() const 64 { 65 return fStackTrace != NULL ? fStackTrace->CountFrames() : 0; 66 } 67 68 virtual bool GetValueAt(int32 rowIndex, int32 columnIndex, BVariant& value) 69 { 70 StackFrame* frame 71 = fStackTrace != NULL ? fStackTrace->FrameAt(rowIndex) : NULL; 72 if (frame == NULL) 73 return false; 74 75 switch (columnIndex) { 76 case 0: 77 value.SetTo(frame->FrameAddress()); 78 return true; 79 case 1: 80 value.SetTo(frame->InstructionPointer()); 81 return true; 82 case 2: 83 { 84 char buffer[512]; 85 value.SetTo(UiUtils::FunctionNameForFrame(frame, buffer, 86 sizeof(buffer))); 87 return true; 88 } 89 default: 90 return false; 91 } 92 } 93 94 StackFrame* FrameAt(int32 index) const 95 { 96 return fStackTrace != NULL ? fStackTrace->FrameAt(index) : NULL; 97 } 98 99 private: 100 StackTrace* fStackTrace; 101 }; 102 103 104 // #pragma mark - StackTraceKey 105 106 107 struct StackTraceView::StackTraceKey { 108 StackTrace* stackTrace; 109 110 StackTraceKey(StackTrace* stackTrace) 111 : 112 stackTrace(stackTrace) 113 { 114 } 115 116 uint32 HashValue() const 117 { 118 return *(uint32*)stackTrace; 119 } 120 121 bool operator==(const StackTraceKey& other) const 122 { 123 return stackTrace == other.stackTrace; 124 } 125 }; 126 127 128 // #pragma mark - StackTraceSelectionEntry 129 130 131 struct StackTraceView::StackTraceSelectionEntry : StackTraceKey { 132 StackTraceSelectionEntry* next; 133 int32 selectedFrameIndex; 134 135 StackTraceSelectionEntry(StackTrace* stackTrace) 136 : 137 StackTraceKey(stackTrace), 138 selectedFrameIndex(0) 139 { 140 } 141 142 inline int32 SelectedFrameIndex() const 143 { 144 return selectedFrameIndex; 145 } 146 147 void SetSelectedFrameIndex(int32 index) 148 { 149 selectedFrameIndex = index; 150 } 151 }; 152 153 154 // #pragma mark - StackTraceSelectionEntryHashDefinition 155 156 157 struct StackTraceView::StackTraceSelectionEntryHashDefinition { 158 typedef StackTraceKey KeyType; 159 typedef StackTraceSelectionEntry ValueType; 160 161 size_t HashKey(const StackTraceKey& key) const 162 { 163 return key.HashValue(); 164 } 165 166 size_t Hash(const StackTraceSelectionEntry* value) const 167 { 168 return value->HashValue(); 169 } 170 171 bool Compare(const StackTraceKey& key, 172 const StackTraceSelectionEntry* value) const 173 { 174 return key == *value; 175 } 176 177 StackTraceSelectionEntry*& GetLink(StackTraceSelectionEntry* value) const 178 { 179 return value->next; 180 } 181 }; 182 183 184 // #pragma mark - StackTraceView 185 186 187 StackTraceView::StackTraceView(Listener* listener) 188 : 189 BGroupView(B_VERTICAL), 190 fStackTrace(NULL), 191 fFramesTable(NULL), 192 fFramesTableModel(NULL), 193 fTraceClearPending(false), 194 fSelectionInfoTable(NULL), 195 fListener(listener) 196 { 197 SetName("Stack Trace"); 198 } 199 200 201 StackTraceView::~StackTraceView() 202 { 203 SetStackTrace(NULL); 204 fFramesTable->SetTableModel(NULL); 205 delete fFramesTableModel; 206 delete fSelectionInfoTable; 207 } 208 209 210 /*static*/ StackTraceView* 211 StackTraceView::Create(Listener* listener) 212 { 213 StackTraceView* self = new StackTraceView(listener); 214 215 try { 216 self->_Init(); 217 } catch (...) { 218 delete self; 219 throw; 220 } 221 222 return self; 223 } 224 225 226 void 227 StackTraceView::UnsetListener() 228 { 229 fListener = NULL; 230 } 231 232 233 void 234 StackTraceView::SetStackTrace(StackTrace* stackTrace) 235 { 236 fTraceClearPending = false; 237 if (stackTrace == fStackTrace) 238 return; 239 240 if (fStackTrace != NULL) 241 fStackTrace->ReleaseReference(); 242 243 fStackTrace = stackTrace; 244 245 if (fStackTrace != NULL) 246 fStackTrace->AcquireReference(); 247 248 fFramesTableModel->SetStackTrace(fStackTrace); 249 } 250 251 252 void 253 StackTraceView::SetStackFrame(StackFrame* stackFrame) 254 { 255 if (fStackTrace != NULL && stackFrame != NULL) { 256 int32 selectedIndex = -1; 257 StackTraceSelectionEntry* entry = fSelectionInfoTable->Lookup( 258 fStackTrace); 259 if (entry != NULL) 260 selectedIndex = entry->SelectedFrameIndex(); 261 else { 262 for (int32 i = 0; StackFrame* other = fStackTrace->FrameAt(i); 263 i++) { 264 if (stackFrame == other) { 265 selectedIndex = i; 266 break; 267 } 268 } 269 } 270 271 if (selectedIndex >= 0) { 272 fFramesTable->SelectRow(selectedIndex, false); 273 return; 274 } 275 } 276 277 fFramesTable->DeselectAllRows(); 278 } 279 280 281 void 282 StackTraceView::LoadSettings(const BMessage& settings) 283 { 284 BMessage tableSettings; 285 if (settings.FindMessage("framesTable", &tableSettings) == B_OK) { 286 GuiSettingsUtils::UnarchiveTableSettings(tableSettings, 287 fFramesTable); 288 } 289 } 290 291 292 status_t 293 StackTraceView::SaveSettings(BMessage& settings) 294 { 295 settings.MakeEmpty(); 296 297 BMessage tableSettings; 298 status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings, 299 fFramesTable); 300 if (result == B_OK) 301 result = settings.AddMessage("framesTable", &tableSettings); 302 303 return result; 304 } 305 306 307 void 308 StackTraceView::SetStackTraceClearPending() 309 { 310 fTraceClearPending = true; 311 StackTraceSelectionEntry* entry = fSelectionInfoTable->Lookup(fStackTrace); 312 if (entry != NULL) { 313 fSelectionInfoTable->Remove(entry); 314 delete entry; 315 } 316 } 317 318 319 void 320 StackTraceView::TableSelectionChanged(Table* table) 321 { 322 if (fListener == NULL || fTraceClearPending) 323 return; 324 325 int32 selectedIndex = table->SelectionModel()->RowAt(0); 326 StackFrame* frame = fFramesTableModel->FrameAt(selectedIndex); 327 328 StackTraceSelectionEntry* entry = fSelectionInfoTable->Lookup(fStackTrace); 329 if (entry == NULL) { 330 entry = new(std::nothrow) StackTraceSelectionEntry(fStackTrace); 331 if (entry == NULL) 332 return; 333 334 ObjectDeleter<StackTraceSelectionEntry> entryDeleter(entry); 335 if (fSelectionInfoTable->Insert(entry) != B_OK) 336 return; 337 338 entryDeleter.Detach(); 339 } 340 341 entry->SetSelectedFrameIndex(selectedIndex); 342 343 fListener->StackFrameSelectionChanged(frame); 344 } 345 346 347 void 348 StackTraceView::_Init() 349 { 350 fSelectionInfoTable = new StackTraceSelectionInfoTable; 351 if (fSelectionInfoTable->Init() != B_OK) { 352 delete fSelectionInfoTable; 353 fSelectionInfoTable = NULL; 354 throw std::bad_alloc(); 355 } 356 357 fFramesTable = new Table("stack trace", 0, B_FANCY_BORDER); 358 AddChild(fFramesTable->ToView()); 359 fFramesTable->SetSortingEnabled(false); 360 361 float addressWidth = be_plain_font->StringWidth("0x00000000") 362 + be_control_look->DefaultLabelSpacing() * 2 + 5; 363 364 // columns 365 fFramesTable->AddColumn(new TargetAddressTableColumn(0, "Frame", 366 addressWidth, 40, 1000, B_TRUNCATE_END, B_ALIGN_RIGHT)); 367 fFramesTable->AddColumn(new TargetAddressTableColumn(1, "IP", addressWidth, 368 40, 1000, B_TRUNCATE_END, B_ALIGN_RIGHT)); 369 fFramesTable->AddColumn(new StringTableColumn(2, "Function", 300, 100, 1000, 370 B_TRUNCATE_END, B_ALIGN_LEFT)); 371 372 fFramesTableModel = new FramesTableModel(); 373 fFramesTable->SetTableModel(fFramesTableModel); 374 375 fFramesTable->SetSelectionMode(B_SINGLE_SELECTION_LIST); 376 fFramesTable->AddTableListener(this); 377 } 378 379 380 // #pragma mark - Listener 381 382 383 StackTraceView::Listener::~Listener() 384 { 385 } 386