1/*
2 * Copyright 2009-2012, 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 "StackTraceView.h"
9
10#include <stdio.h>
11
12#include <new>
13
14#include <ControlLook.h>
15#include <Window.h>
16
17#include "table/TableColumns.h"
18
19#include "FunctionInstance.h"
20#include "GuiSettingsUtils.h"
21#include "Image.h"
22#include "StackTrace.h"
23#include "TargetAddressTableColumn.h"
24#include "UiUtils.h"
25
26
27// #pragma mark - FramesTableModel
28
29
30class StackTraceView::FramesTableModel : public TableModel {
31public:
32	FramesTableModel()
33		:
34		fStackTrace(NULL)
35	{
36	}
37
38	~FramesTableModel()
39	{
40		SetStackTrace(NULL);
41	}
42
43	void SetStackTrace(StackTrace* stackTrace)
44	{
45		// unset old frames
46		if (fStackTrace != NULL && fStackTrace->CountFrames())
47			NotifyRowsRemoved(0, fStackTrace->CountFrames());
48
49		fStackTrace = stackTrace;
50
51		// set new frames
52		if (fStackTrace != NULL && fStackTrace->CountFrames() > 0)
53			NotifyRowsAdded(0, fStackTrace->CountFrames());
54	}
55
56	virtual int32 CountColumns() const
57	{
58		return 3;
59	}
60
61	virtual int32 CountRows() const
62	{
63		return fStackTrace != NULL ? fStackTrace->CountFrames() : 0;
64	}
65
66	virtual bool GetValueAt(int32 rowIndex, int32 columnIndex, BVariant& value)
67	{
68		StackFrame* frame
69			= fStackTrace != NULL ? fStackTrace->FrameAt(rowIndex) : NULL;
70		if (frame == NULL)
71			return false;
72
73		switch (columnIndex) {
74			case 0:
75				value.SetTo(frame->FrameAddress());
76				return true;
77			case 1:
78				value.SetTo(frame->InstructionPointer());
79				return true;
80			case 2:
81			{
82				char buffer[512];
83				value.SetTo(UiUtils::FunctionNameForFrame(frame, buffer,
84						sizeof(buffer)));
85				return true;
86			}
87			default:
88				return false;
89		}
90	}
91
92	StackFrame* FrameAt(int32 index) const
93	{
94		return fStackTrace != NULL ? fStackTrace->FrameAt(index) : NULL;
95	}
96
97private:
98	StackTrace*				fStackTrace;
99};
100
101
102// #pragma mark - StackTraceView
103
104
105StackTraceView::StackTraceView(Listener* listener)
106	:
107	BGroupView(B_VERTICAL),
108	fStackTrace(NULL),
109	fFramesTable(NULL),
110	fFramesTableModel(NULL),
111	fTraceClearPending(false),
112	fListener(listener)
113{
114	SetName("Stack Trace");
115}
116
117
118StackTraceView::~StackTraceView()
119{
120	SetStackTrace(NULL);
121	fFramesTable->SetTableModel(NULL);
122	delete fFramesTableModel;
123}
124
125
126/*static*/ StackTraceView*
127StackTraceView::Create(Listener* listener)
128{
129	StackTraceView* self = new StackTraceView(listener);
130
131	try {
132		self->_Init();
133	} catch (...) {
134		delete self;
135		throw;
136	}
137
138	return self;
139}
140
141
142void
143StackTraceView::UnsetListener()
144{
145	fListener = NULL;
146}
147
148
149void
150StackTraceView::SetStackTrace(StackTrace* stackTrace)
151{
152	fTraceClearPending = false;
153	if (stackTrace == fStackTrace)
154		return;
155
156	if (fStackTrace != NULL)
157		fStackTrace->ReleaseReference();
158
159	fStackTrace = stackTrace;
160
161	if (fStackTrace != NULL)
162		fStackTrace->AcquireReference();
163
164	fFramesTableModel->SetStackTrace(fStackTrace);
165}
166
167
168void
169StackTraceView::SetStackFrame(StackFrame* stackFrame)
170{
171	if (fStackTrace != NULL && stackFrame != NULL) {
172		for (int32 i = 0; StackFrame* other = fStackTrace->FrameAt(i); i++) {
173			if (stackFrame == other) {
174				fFramesTable->SelectRow(i, false);
175				return;
176			}
177		}
178	}
179
180	fFramesTable->DeselectAllRows();
181}
182
183
184void
185StackTraceView::LoadSettings(const BMessage& settings)
186{
187	BMessage tableSettings;
188	if (settings.FindMessage("framesTable", &tableSettings) == B_OK) {
189		GuiSettingsUtils::UnarchiveTableSettings(tableSettings,
190			fFramesTable);
191	}
192}
193
194
195status_t
196StackTraceView::SaveSettings(BMessage& settings)
197{
198	settings.MakeEmpty();
199
200	BMessage tableSettings;
201	status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings,
202		fFramesTable);
203	if (result == B_OK)
204		result = settings.AddMessage("framesTable", &tableSettings);
205
206	return result;
207}
208
209
210void
211StackTraceView::SetStackTraceClearPending()
212{
213	fTraceClearPending = true;
214}
215
216
217void
218StackTraceView::TableSelectionChanged(Table* table)
219{
220	if (fListener == NULL || fTraceClearPending)
221		return;
222
223	StackFrame* frame
224		= fFramesTableModel->FrameAt(table->SelectionModel()->RowAt(0));
225
226	fListener->StackFrameSelectionChanged(frame);
227}
228
229
230void
231StackTraceView::_Init()
232{
233	fFramesTable = new Table("stack trace", 0, B_FANCY_BORDER);
234	fFramesTable->SetFont(B_FONT_ROW, be_fixed_font);
235	AddChild(fFramesTable->ToView());
236	fFramesTable->SetSortingEnabled(false);
237
238	float addressWidth = be_fixed_font->StringWidth("0xffffffff00000000")
239		+ be_control_look->DefaultLabelSpacing();
240
241	// columns
242	fFramesTable->AddColumn(new TargetAddressTableColumn(0, "Frame",
243		addressWidth, 40, 1000, B_TRUNCATE_END, B_ALIGN_RIGHT));
244	fFramesTable->AddColumn(new TargetAddressTableColumn(1, "IP", addressWidth,
245		40, 1000, B_TRUNCATE_END, B_ALIGN_RIGHT));
246	fFramesTable->AddColumn(new StringTableColumn(2, "Function", 300, 100, 1000,
247		B_TRUNCATE_END, B_ALIGN_LEFT));
248
249	fFramesTableModel = new FramesTableModel();
250	fFramesTable->SetTableModel(fFramesTableModel);
251
252	fFramesTable->SetSelectionMode(B_SINGLE_SELECTION_LIST);
253	fFramesTable->AddTableListener(this);
254}
255
256
257// #pragma mark - Listener
258
259
260StackTraceView::Listener::~Listener()
261{
262}
263