1/*
2 * Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2012-2016, 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 <KernelExport.h>
15#include <Path.h>
16#include <String.h>
17#include <Variant.h>
18
19#include <vm_defs.h>
20
21#include "FunctionInstance.h"
22#include "Image.h"
23#include "RangeList.h"
24#include "SignalDispositionTypes.h"
25#include "StackFrame.h"
26#include "Team.h"
27#include "TeamMemoryBlock.h"
28#include "Thread.h"
29#include "Type.h"
30#include "Value.h"
31#include "ValueNode.h"
32
33
34/*static*/ const char*
35UiUtils::ThreadStateToString(int state, int stoppedReason)
36{
37	switch (state) {
38		case THREAD_STATE_RUNNING:
39			return "Running";
40		case THREAD_STATE_STOPPED:
41			break;
42		case THREAD_STATE_UNKNOWN:
43		default:
44			return "?";
45	}
46
47	// thread is stopped -- get the reason
48	switch (stoppedReason) {
49		case THREAD_STOPPED_DEBUGGER_CALL:
50			return "Call";
51		case THREAD_STOPPED_EXCEPTION:
52			return "Exception";
53		case THREAD_STOPPED_BREAKPOINT:
54		case THREAD_STOPPED_WATCHPOINT:
55		case THREAD_STOPPED_SINGLE_STEP:
56		case THREAD_STOPPED_DEBUGGED:
57		case THREAD_STOPPED_UNKNOWN:
58		default:
59			return "Debugged";
60	}
61}
62
63
64/*static*/ const char*
65UiUtils::VariantToString(const BVariant& value, char* buffer,
66	size_t bufferSize)
67{
68	if (!value.IsNumber())
69		return value.ToString();
70
71	switch (value.Type()) {
72		case B_FLOAT_TYPE:
73		case B_DOUBLE_TYPE:
74			snprintf(buffer, bufferSize, "%.3g", value.ToDouble());
75			break;
76		case B_INT8_TYPE:
77		case B_UINT8_TYPE:
78			snprintf(buffer, bufferSize, "0x%02x", value.ToUInt8());
79			break;
80		case B_INT16_TYPE:
81		case B_UINT16_TYPE:
82			snprintf(buffer, bufferSize, "0x%04x", value.ToUInt16());
83			break;
84		case B_INT32_TYPE:
85		case B_UINT32_TYPE:
86			snprintf(buffer, bufferSize, "0x%08" B_PRIx32,
87				value.ToUInt32());
88			break;
89		case B_INT64_TYPE:
90		case B_UINT64_TYPE:
91		default:
92			snprintf(buffer, bufferSize, "0x%016" B_PRIx64,
93				value.ToUInt64());
94			break;
95	}
96
97	return buffer;
98}
99
100
101/*static*/ const char*
102UiUtils::FunctionNameForFrame(StackFrame* frame, char* buffer,
103	size_t bufferSize)
104{
105	Image* image = frame->GetImage();
106	FunctionInstance* function = frame->Function();
107	if (image == NULL && function == NULL) {
108		snprintf(buffer, bufferSize, "?");
109		return buffer;
110	}
111
112	BString name;
113	target_addr_t baseAddress;
114	if (function != NULL) {
115		name = function->PrettyName();
116		baseAddress = function->Address();
117	} else {
118		name = image->Name();
119		baseAddress = image->Info().TextBase();
120	}
121
122	snprintf(buffer, bufferSize, "%s + %#" B_PRIx64,
123		name.String(), frame->InstructionPointer() - baseAddress);
124
125	return buffer;
126}
127
128
129/*static*/ const char*
130UiUtils::ImageTypeToString(image_type type, char* buffer, size_t bufferSize)
131{
132	switch (type) {
133		case B_APP_IMAGE:
134			snprintf(buffer, bufferSize, "app");
135			break;
136		case B_LIBRARY_IMAGE:
137			snprintf(buffer, bufferSize, "lib");
138			break;
139		case B_ADD_ON_IMAGE:
140			snprintf(buffer, bufferSize, "add-on");
141			break;
142		case B_SYSTEM_IMAGE:
143			snprintf(buffer, bufferSize, "system");
144			break;
145		default:
146			snprintf(buffer, bufferSize, "unknown");
147			break;
148	}
149
150	return buffer;
151}
152
153
154/*static*/ const char*
155UiUtils::AreaLockingFlagsToString(uint32 flags, char* buffer,
156	size_t bufferSize)
157{
158	switch (flags) {
159		case B_NO_LOCK:
160			snprintf(buffer, bufferSize, "none");
161			break;
162		case B_LAZY_LOCK:
163			snprintf(buffer, bufferSize, "lazy");
164			break;
165		case B_FULL_LOCK:
166			snprintf(buffer, bufferSize, "full");
167			break;
168		case B_CONTIGUOUS:
169			snprintf(buffer, bufferSize, "contiguous");
170			break;
171		case B_LOMEM:
172			snprintf(buffer, bufferSize, "lo-mem");
173			break;
174		case B_32_BIT_FULL_LOCK:
175			snprintf(buffer, bufferSize, "32-bit full");
176			break;
177		case B_32_BIT_CONTIGUOUS:
178			snprintf(buffer, bufferSize, "32-bit contig.");
179			break;
180		default:
181			snprintf(buffer, bufferSize, "unknown");
182			break;
183	}
184
185	return buffer;
186}
187
188
189/*static*/ const BString&
190UiUtils::AreaProtectionFlagsToString(uint32 protection, BString& _output)
191{
192	#undef ADD_AREA_FLAG_IF_PRESENT
193	#define ADD_AREA_FLAG_IF_PRESENT(flag, protection, name, output, missing)\
194		if ((protection & flag) != 0) { \
195			_output += name; \
196			protection &= ~flag; \
197		} else \
198			_output += missing; \
199
200	_output.Truncate(0);
201	uint32 userFlags = protection & B_USER_PROTECTION;
202	bool userProtectionPresent = userFlags != 0;
203	ADD_AREA_FLAG_IF_PRESENT(B_READ_AREA, protection, "r", _output,
204		userProtectionPresent ? "-" : " ");
205	ADD_AREA_FLAG_IF_PRESENT(B_WRITE_AREA, protection, "w", _output,
206		userProtectionPresent ? "-" : " ");
207	ADD_AREA_FLAG_IF_PRESENT(B_EXECUTE_AREA, protection, "x", _output,
208		userProtectionPresent ? "-" : " ");
209
210	// if the user versions of these flags are present,
211	// filter out their kernel equivalents since they're implied.
212	if ((userFlags & B_READ_AREA) != 0)
213		protection &= ~B_KERNEL_READ_AREA;
214	if ((userFlags & B_WRITE_AREA) != 0)
215		protection &= ~B_KERNEL_WRITE_AREA;
216	if ((userFlags & B_EXECUTE_AREA) != 0)
217		protection &= ~B_KERNEL_EXECUTE_AREA;
218
219	if ((protection & B_KERNEL_PROTECTION) != 0) {
220		ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_READ_AREA, protection, "r",
221			_output, "-");
222		ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_WRITE_AREA, protection, "w",
223			_output, "-");
224		ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_EXECUTE_AREA, protection, "x",
225			_output, "-");
226	}
227
228	ADD_AREA_FLAG_IF_PRESENT(B_STACK_AREA, protection, "s", _output, "");
229	ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_STACK_AREA, protection, "s", _output, "");
230	ADD_AREA_FLAG_IF_PRESENT(B_OVERCOMMITTING_AREA, protection, _output, "o",
231		"");
232	ADD_AREA_FLAG_IF_PRESENT(B_CLONEABLE_AREA, protection, "c", _output, "");
233	ADD_AREA_FLAG_IF_PRESENT(B_SHARED_AREA, protection, "S", _output, "");
234	ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_AREA, protection, "k", _output, "");
235
236	if (protection != 0) {
237		char buffer[32];
238		snprintf(buffer, sizeof(buffer), ", u:(%#04" B_PRIx32 ")",
239			protection);
240		_output += buffer;
241	}
242
243	return _output;
244}
245
246
247/*static*/ const char*
248UiUtils::ReportNameForTeam(::Team* team, char* buffer, size_t bufferSize)
249{
250	BPath teamPath(team->Name());
251	BDateTime currentTime;
252	currentTime.SetTime_t(time(NULL));
253	snprintf(buffer, bufferSize, "%s-%" B_PRId32 "-debug-%02" B_PRId32 "-%02"
254		B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02"
255		B_PRId32 ".report", teamPath.Leaf(), team->ID(),
256		currentTime.Date().Day(), currentTime.Date().Month(),
257		currentTime.Date().Year(), currentTime.Time().Hour(),
258		currentTime.Time().Minute(), currentTime.Time().Second());
259
260	return buffer;
261}
262
263
264/*static*/ const char*
265UiUtils::CoreFileNameForTeam(::Team* team, char* buffer, size_t bufferSize)
266{
267	BPath teamPath(team->Name());
268	BDateTime currentTime;
269	currentTime.SetTime_t(time(NULL));
270	snprintf(buffer, bufferSize, "%s-%" B_PRId32 "-debug-%02" B_PRId32 "-%02"
271		B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02"
272		B_PRId32 ".core", teamPath.Leaf(), team->ID(),
273		currentTime.Date().Day(), currentTime.Date().Month(),
274		currentTime.Date().Year(), currentTime.Time().Hour(),
275		currentTime.Time().Minute(), currentTime.Time().Second());
276
277	return buffer;
278
279}
280
281
282/*static*/ void
283UiUtils::PrintValueNodeGraph(BString& _output, ValueNodeChild* child,
284	int32 indentLevel, int32 maxDepth)
285{
286	_output.Append('\t', indentLevel);
287	_output << child->Name();
288
289	ValueNode* node = child->Node();
290	if (node == NULL) {
291		_output << ": Unavailable\n";
292		return;
293	}
294
295	if (node->GetType()->Kind() != TYPE_COMPOUND) {
296		_output << ": ";
297		status_t resolutionState = node->LocationAndValueResolutionState();
298		if (resolutionState == VALUE_NODE_UNRESOLVED)
299			_output << "Unresolved";
300		else if (resolutionState == B_OK) {
301			Value* value = node->GetValue();
302			if (value != NULL) {
303				BString valueData;
304				value->ToString(valueData);
305				_output << valueData;
306			} else
307				_output << "Unavailable";
308		} else
309			_output << strerror(resolutionState);
310	}
311
312	if (maxDepth == 0 || node->CountChildren() == 0) {
313		_output << "\n";
314		return;
315	}
316
317	if (node->CountChildren() == 1
318		&& node->GetType()->ResolveRawType(false)->Kind() == TYPE_ADDRESS
319		&& node->ChildAt(0)->GetType()->ResolveRawType(false)->Kind()
320			== TYPE_COMPOUND) {
321		// for the case of a pointer to a compound type,
322		// we want to hide the intervening compound node and print
323		// the children directly.
324		node = node->ChildAt(0)->Node();
325	}
326
327	if (node != NULL) {
328		_output << " {\n";
329
330		for (int32 i = 0; i < node->CountChildren(); i++) {
331			// don't dump compound nodes if our depth limit won't allow
332			// us to traverse into their children anyways, and the top
333			// level node contains no data of intereest.
334			if (node->ChildAt(i)->GetType()->Kind() != TYPE_COMPOUND
335				|| maxDepth > 1) {
336				PrintValueNodeGraph(_output, node->ChildAt(i),
337					indentLevel + 1, maxDepth - 1);
338			}
339		}
340		_output.Append('\t', indentLevel);
341		_output << "}\n";
342	} else
343		_output << "\n";
344
345	return;
346}
347
348
349/*static*/ void
350UiUtils::DumpMemory(BString& _output, int32 indentLevel,
351	TeamMemoryBlock* block, target_addr_t address, int32 itemSize,
352	int32 displayWidth, int32 count)
353{
354	BString data;
355
356	int32 j;
357	_output.Append('\t', indentLevel);
358	for (int32 i = 0; i < count; i++) {
359		if (!block->Contains(address + i * itemSize))
360			break;
361
362		uint8* value;
363
364		if ((i % displayWidth) == 0) {
365			int32 displayed = min_c(displayWidth, (count-i)) * itemSize;
366			if (i != 0) {
367				_output.Append("\n");
368				_output.Append('\t', indentLevel);
369			}
370
371			data.SetToFormat("[%#" B_PRIx64 "]  ", address + i * itemSize);
372			_output += data;
373			char c;
374			for (j = 0; j < displayed; j++) {
375				c = *(block->Data() + address - block->BaseAddress()
376					+ (i * itemSize) + j);
377				if (!isprint(c))
378					c = '.';
379
380				_output += c;
381			}
382			if (count > displayWidth) {
383				// make sure the spacing in the last line is correct
384				for (j = displayed; j < displayWidth * itemSize; j++)
385					_output += ' ';
386			}
387			_output.Append("  ");
388		}
389
390		value = block->Data() + address - block->BaseAddress()
391			+ i * itemSize;
392
393		switch (itemSize) {
394			case 1:
395				data.SetToFormat(" %02" B_PRIx8, *(uint8*)value);
396				break;
397			case 2:
398				data.SetToFormat(" %04" B_PRIx16, *(uint16*)value);
399				break;
400			case 4:
401				data.SetToFormat(" %08" B_PRIx32, *(uint32*)value);
402				break;
403			case 8:
404				data.SetToFormat(" %016" B_PRIx64, *(uint64*)value);
405				break;
406		}
407
408		_output += data;
409	}
410
411	_output.Append("\n");
412}
413
414
415static status_t ParseRangeString(BString& rangeString, int32& lowerBound,
416	int32& upperBound)
417{
418	lowerBound = atoi(rangeString.String());
419	int32 index = rangeString.FindFirst('-');
420	if (index >= 0) {
421		rangeString.Remove(0, index + 1);
422		upperBound = atoi(rangeString.String());
423	} else
424		upperBound = lowerBound;
425
426	if (lowerBound > upperBound)
427		return B_BAD_VALUE;
428
429	return B_OK;
430}
431
432
433/*static*/ status_t
434UiUtils::ParseRangeExpression(const BString& rangeExpression, int32 lowerBound,
435	int32 upperBound, bool fixedRange, RangeList& _output)
436{
437	if (rangeExpression.IsEmpty())
438		return B_BAD_DATA;
439
440	BString dataString = rangeExpression;
441	dataString.RemoveAll(" ");
442
443	// first, tokenize the range list to its constituent child ranges.
444	int32 index;
445	int32 lowValue;
446	int32 highValue;
447	BString tempRange;
448	while (!dataString.IsEmpty()) {
449		index = dataString.FindFirst(',');
450		if (index == 0)
451			return B_BAD_VALUE;
452		else if (index > 0) {
453			dataString.MoveInto(tempRange, 0, index);
454			dataString.Remove(0, 1);
455		} else {
456			tempRange = dataString;
457			dataString.Truncate(0);
458		}
459
460		status_t result = ParseRangeString(tempRange, lowValue, highValue);
461		if (result != B_OK)
462			return result;
463
464
465		if (fixedRange && (lowValue < lowerBound || highValue > upperBound))
466			return B_BAD_VALUE;
467
468		result = _output.AddRange(lowValue, highValue);
469		if (result != B_OK)
470			return result;
471
472		tempRange.Truncate(0);
473	}
474
475	return B_OK;
476}
477
478
479/*static*/ const char*
480UiUtils::TypeCodeToString(type_code type)
481{
482	switch (type) {
483		case B_INT8_TYPE:
484			return "int8";
485		case B_UINT8_TYPE:
486			return "uint8";
487		case B_INT16_TYPE:
488			return "int16";
489		case B_UINT16_TYPE:
490			return "uint16";
491		case B_INT32_TYPE:
492			return "int32";
493		case B_UINT32_TYPE:
494			return "uint32";
495		case B_INT64_TYPE:
496			return "int64";
497		case B_UINT64_TYPE:
498			return "uint64";
499		case B_FLOAT_TYPE:
500			return "float";
501		case B_DOUBLE_TYPE:
502			return "double";
503		case B_STRING_TYPE:
504			return "string";
505		default:
506			return "unknown";
507	}
508}
509
510
511template<typename T>
512T GetSIMDValueAtOffset(char* data, int32 index)
513{
514	return ((T*)data)[index];
515}
516
517
518static int32 GetSIMDFormatByteSize(uint32 format)
519{
520	switch (format) {
521		case SIMD_RENDER_FORMAT_INT8:
522			return sizeof(char);
523		case SIMD_RENDER_FORMAT_INT16:
524			return sizeof(int16);
525		case SIMD_RENDER_FORMAT_INT32:
526			return sizeof(int32);
527		case SIMD_RENDER_FORMAT_INT64:
528			return sizeof(int64);
529		case SIMD_RENDER_FORMAT_FLOAT:
530			return sizeof(float);
531		case SIMD_RENDER_FORMAT_DOUBLE:
532			return sizeof(double);
533	}
534
535	return 0;
536}
537
538
539/*static*/
540const BString&
541UiUtils::FormatSIMDValue(const BVariant& value, uint32 bitSize,
542	uint32 format, BString& _output)
543{
544	_output.SetTo("{");
545	char* data = (char*)value.ToPointer();
546	uint32 count = bitSize / (GetSIMDFormatByteSize(format) * 8);
547	for (uint32 i = 0; i < count; i ++) {
548		BString temp;
549		switch (format) {
550			case SIMD_RENDER_FORMAT_INT8:
551				temp.SetToFormat("%#" B_PRIx8,
552					GetSIMDValueAtOffset<uint8>(data, i));
553				break;
554			case SIMD_RENDER_FORMAT_INT16:
555				temp.SetToFormat("%#" B_PRIx16,
556					GetSIMDValueAtOffset<uint16>(data, i));
557				break;
558			case SIMD_RENDER_FORMAT_INT32:
559				temp.SetToFormat("%#" B_PRIx32,
560					GetSIMDValueAtOffset<uint32>(data, i));
561				break;
562			case SIMD_RENDER_FORMAT_INT64:
563				temp.SetToFormat("%#" B_PRIx64,
564					GetSIMDValueAtOffset<uint64>(data, i));
565				break;
566			case SIMD_RENDER_FORMAT_FLOAT:
567				temp.SetToFormat("%.3g",
568					(double)GetSIMDValueAtOffset<float>(data, i));
569				break;
570			case SIMD_RENDER_FORMAT_DOUBLE:
571				temp.SetToFormat("%.3g",
572					GetSIMDValueAtOffset<double>(data, i));
573				break;
574		}
575		_output += temp;
576		if (i < count - 1)
577			_output += ", ";
578	}
579	_output += "}";
580
581	return _output;
582}
583
584
585const char*
586UiUtils::SignalNameToString(int32 signal, BString& _output)
587{
588	#undef DEFINE_SIGNAL_STRING
589	#define DEFINE_SIGNAL_STRING(x)										\
590		case x:															\
591			_output = #x;												\
592			return _output.String();
593
594	switch (signal) {
595		DEFINE_SIGNAL_STRING(SIGHUP)
596		DEFINE_SIGNAL_STRING(SIGINT)
597		DEFINE_SIGNAL_STRING(SIGQUIT)
598		DEFINE_SIGNAL_STRING(SIGILL)
599		DEFINE_SIGNAL_STRING(SIGCHLD)
600		DEFINE_SIGNAL_STRING(SIGABRT)
601		DEFINE_SIGNAL_STRING(SIGPIPE)
602		DEFINE_SIGNAL_STRING(SIGFPE)
603		DEFINE_SIGNAL_STRING(SIGKILL)
604		DEFINE_SIGNAL_STRING(SIGSTOP)
605		DEFINE_SIGNAL_STRING(SIGSEGV)
606		DEFINE_SIGNAL_STRING(SIGCONT)
607		DEFINE_SIGNAL_STRING(SIGTSTP)
608		DEFINE_SIGNAL_STRING(SIGALRM)
609		DEFINE_SIGNAL_STRING(SIGTERM)
610		DEFINE_SIGNAL_STRING(SIGTTIN)
611		DEFINE_SIGNAL_STRING(SIGTTOU)
612		DEFINE_SIGNAL_STRING(SIGUSR1)
613		DEFINE_SIGNAL_STRING(SIGUSR2)
614		DEFINE_SIGNAL_STRING(SIGWINCH)
615		DEFINE_SIGNAL_STRING(SIGKILLTHR)
616		DEFINE_SIGNAL_STRING(SIGTRAP)
617		DEFINE_SIGNAL_STRING(SIGPOLL)
618		DEFINE_SIGNAL_STRING(SIGPROF)
619		DEFINE_SIGNAL_STRING(SIGSYS)
620		DEFINE_SIGNAL_STRING(SIGURG)
621		DEFINE_SIGNAL_STRING(SIGVTALRM)
622		DEFINE_SIGNAL_STRING(SIGXCPU)
623		DEFINE_SIGNAL_STRING(SIGXFSZ)
624		DEFINE_SIGNAL_STRING(SIGBUS)
625		default:
626			break;
627	}
628
629	if (signal == SIGRTMIN)
630		_output = "SIGRTMIN";
631	else if (signal == SIGRTMAX)
632		_output = "SIGRTMAX";
633	else
634		_output.SetToFormat("SIGRTMIN+%" B_PRId32, signal - SIGRTMIN);
635
636	return _output.String();
637}
638
639
640const char*
641UiUtils::SignalDispositionToString(int disposition)
642{
643	switch (disposition) {
644		case SIGNAL_DISPOSITION_IGNORE:
645			return "Ignore";
646		case SIGNAL_DISPOSITION_STOP_AT_RECEIPT:
647			return "Stop at receipt";
648		case SIGNAL_DISPOSITION_STOP_AT_SIGNAL_HANDLER:
649			return "Stop at signal handler";
650		default:
651			break;
652	}
653
654	return "Unknown";
655}
656