1/*
2 * Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2012, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "UiUtils.h"
9
10#include <ctype.h>
11#include <stdio.h>
12
13#include <DateTime.h>
14#include <Path.h>
15#include <String.h>
16#include <Variant.h>
17
18#include "FunctionInstance.h"
19#include "Image.h"
20#include "StackFrame.h"
21#include "Team.h"
22#include "TeamMemoryBlock.h"
23#include "Thread.h"
24#include "Type.h"
25#include "Value.h"
26#include "ValueNode.h"
27
28
29/*static*/ const char*
30UiUtils::ThreadStateToString(int state, int stoppedReason)
31{
32	switch (state) {
33		case THREAD_STATE_RUNNING:
34			return "Running";
35		case THREAD_STATE_STOPPED:
36			break;
37		case THREAD_STATE_UNKNOWN:
38		default:
39			return "?";
40	}
41
42	// thread is stopped -- get the reason
43	switch (stoppedReason) {
44		case THREAD_STOPPED_DEBUGGER_CALL:
45			return "Call";
46		case THREAD_STOPPED_EXCEPTION:
47			return "Exception";
48		case THREAD_STOPPED_BREAKPOINT:
49		case THREAD_STOPPED_WATCHPOINT:
50		case THREAD_STOPPED_SINGLE_STEP:
51		case THREAD_STOPPED_DEBUGGED:
52		case THREAD_STOPPED_UNKNOWN:
53		default:
54			return "Debugged";
55	}
56}
57
58
59/*static*/ const char*
60UiUtils::VariantToString(const BVariant& value, char* buffer,
61	size_t bufferSize)
62{
63	if (!value.IsNumber())
64		return value.ToString();
65
66	switch (value.Type()) {
67		case B_FLOAT_TYPE:
68		case B_DOUBLE_TYPE:
69			snprintf(buffer, bufferSize, "%g", value.ToDouble());
70			break;
71		case B_INT8_TYPE:
72		case B_UINT8_TYPE:
73			snprintf(buffer, bufferSize, "0x%02x", value.ToUInt8());
74			break;
75		case B_INT16_TYPE:
76		case B_UINT16_TYPE:
77			snprintf(buffer, bufferSize, "0x%04x", value.ToUInt16());
78			break;
79		case B_INT32_TYPE:
80		case B_UINT32_TYPE:
81			snprintf(buffer, bufferSize, "0x%08" B_PRIx32,
82				value.ToUInt32());
83			break;
84		case B_INT64_TYPE:
85		case B_UINT64_TYPE:
86		default:
87			snprintf(buffer, bufferSize, "0x%016" B_PRIx64,
88				value.ToUInt64());
89			break;
90	}
91
92	return buffer;
93}
94
95
96/*static*/ const char*
97UiUtils::FunctionNameForFrame(StackFrame* frame, char* buffer,
98	size_t bufferSize)
99{
100	Image* image = frame->GetImage();
101	FunctionInstance* function = frame->Function();
102	if (image == NULL && function == NULL) {
103		snprintf(buffer, bufferSize, "?");
104		return buffer;
105	}
106
107	BString name;
108	target_addr_t baseAddress;
109	if (function != NULL) {
110		name = function->PrettyName();
111		baseAddress = function->Address();
112	} else {
113		name = image->Name();
114		baseAddress = image->Info().TextBase();
115	}
116
117	snprintf(buffer, bufferSize, "%s + %#" B_PRIx64,
118		name.String(), frame->InstructionPointer() - baseAddress);
119
120	return buffer;
121}
122
123
124/*static*/ const char*
125UiUtils::ImageTypeToString(image_type type, char* buffer, size_t bufferSize)
126{
127	switch (type) {
128		case B_APP_IMAGE:
129			snprintf(buffer, bufferSize, "Application");
130			break;
131		case B_LIBRARY_IMAGE:
132			snprintf(buffer, bufferSize, "Library");
133			break;
134		case B_ADD_ON_IMAGE:
135			snprintf(buffer, bufferSize, "Add-on");
136			break;
137		case B_SYSTEM_IMAGE:
138			snprintf(buffer, bufferSize, "System");
139			break;
140		default:
141			snprintf(buffer, bufferSize, "Unknown");
142			break;
143	}
144
145	return buffer;
146}
147
148
149/*static*/ const char*
150UiUtils::ReportNameForTeam(::Team* team, char* buffer, size_t bufferSize)
151{
152	BPath teamPath(team->Name());
153	BDateTime currentTime;
154	currentTime.SetTime_t(time(NULL));
155	snprintf(buffer, bufferSize, "%s-%" B_PRId32 "-debug-%02" B_PRId32 "-%02"
156		B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02"
157		B_PRId32 ".report", teamPath.Leaf(), team->ID(),
158		currentTime.Date().Day(), currentTime.Date().Month(),
159		currentTime.Date().Year(), currentTime.Time().Hour(),
160		currentTime.Time().Minute(), currentTime.Time().Second());
161
162	return buffer;
163
164}
165
166
167/*static*/ void
168UiUtils::PrintValueNodeGraph(BString& _output, ValueNodeChild* child,
169	int32 indentLevel, int32 maxDepth)
170{
171	_output.Append('\t', indentLevel);
172	_output << child->Name();
173
174	ValueNode* node = child->Node();
175	if (node == NULL) {
176		_output << ": Unavailable\n";
177		return;
178	}
179
180	if (node->GetType()->Kind() != TYPE_COMPOUND) {
181		_output << ": ";
182		status_t resolutionState = node->LocationAndValueResolutionState();
183		if (resolutionState == VALUE_NODE_UNRESOLVED)
184			_output << "Unresolved";
185		else if (resolutionState == B_OK) {
186			Value* value = node->GetValue();
187			if (value != NULL) {
188				BString valueData;
189				value->ToString(valueData);
190				_output << valueData;
191			} else
192				_output << "Unavailable";
193		} else
194			_output << strerror(resolutionState);
195	}
196
197	if (maxDepth == 0 || node->CountChildren() == 0) {
198		_output << "\n";
199		return;
200	}
201
202	if (node->CountChildren() == 1
203		&& node->GetType()->Kind() == TYPE_ADDRESS
204		&& node->ChildAt(0)->GetType()->Kind() == TYPE_COMPOUND) {
205		// for the case of a pointer to a compound type,
206		// we want to hide the intervening compound node and print
207		// the children directly.
208		node = node->ChildAt(0)->Node();
209	}
210
211	if (node != NULL) {
212		_output << " {\n";
213
214		for (int32 i = 0; i < node->CountChildren(); i++) {
215			// don't dump compound nodes if our depth limit won't allow
216			// us to traverse into their children anyways, and the top
217			// level node contains no data of intereest.
218			if (node->ChildAt(i)->GetType()->Kind() != TYPE_COMPOUND
219				|| maxDepth > 1) {
220				PrintValueNodeGraph(_output, node->ChildAt(i),
221					indentLevel + 1, maxDepth - 1);
222			}
223		}
224		_output.Append('\t', indentLevel);
225		_output << "}\n";
226	} else
227		_output << "\n";
228
229	return;
230}
231
232
233/*static*/ void
234UiUtils::DumpMemory(BString& _output, int32 indentLevel,
235	TeamMemoryBlock* block, target_addr_t address, int32 itemSize,
236	int32 displayWidth, int32 count)
237{
238	BString data;
239
240	int32 j;
241	_output.Append('\t', indentLevel);
242	for (int32 i = 0; i < count; i++) {
243		uint8* value;
244
245		if ((i % displayWidth) == 0) {
246			int32 displayed = min_c(displayWidth, (count-i)) * itemSize;
247			if (i != 0) {
248				_output.Append("\n");
249				_output.Append('\t', indentLevel);
250			}
251
252			data.SetToFormat("[%#" B_PRIx64 "]  ", address + i * itemSize);
253			_output += data;
254			char c;
255			for (j = 0; j < displayed; j++) {
256				if (!block->Contains(address + displayed))
257					break;
258				c = *(block->Data() + address - block->BaseAddress()
259					+ (i * itemSize) + j);
260				if (!isprint(c))
261					c = '.';
262
263				_output += c;
264			}
265			if (count > displayWidth) {
266				// make sure the spacing in the last line is correct
267				for (j = displayed; j < displayWidth * itemSize; j++)
268					_output += ' ';
269			}
270			_output.Append("  ");
271		}
272
273		value = block->Data() + address - block->BaseAddress()
274			+ i * itemSize;
275
276		switch (itemSize) {
277			case 1:
278				data.SetToFormat(" %02" B_PRIx8, *(uint8*)value);
279				break;
280			case 2:
281				data.SetToFormat(" %04" B_PRIx16, *(uint16*)value);
282				break;
283			case 4:
284				data.SetToFormat(" %08" B_PRIx32, *(uint32*)value);
285				break;
286			case 8:
287				data.SetToFormat(" %016" B_PRIx64, *(uint64*)value);
288				break;
289		}
290
291		_output += data;
292	}
293
294	_output.Append("\n");
295}
296