xref: /haiku/src/apps/debugger/user_interface/gui/team_window/StackTraceView.cpp (revision 529cd177b573aaba391c8adc9c9f5ad76a14bf81)
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