xref: /haiku/src/kits/debugger/user_interface/util/UiUtils.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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*
35 UiUtils::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*
65 UiUtils::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*
102 UiUtils::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*
130 UiUtils::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*
155 UiUtils::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&
190 UiUtils::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_SHARED_AREA, protection, "S", _output, "");
233 
234 	if (protection != 0) {
235 		char buffer[32];
236 		snprintf(buffer, sizeof(buffer), ", u:(%#04" B_PRIx32 ")",
237 			protection);
238 		_output += buffer;
239 	}
240 
241 	return _output;
242 }
243 
244 
245 /*static*/ const char*
246 UiUtils::ReportNameForTeam(::Team* team, char* buffer, size_t bufferSize)
247 {
248 	BPath teamPath(team->Name());
249 	BDateTime currentTime;
250 	currentTime.SetTime_t(time(NULL));
251 	snprintf(buffer, bufferSize, "%s-%" B_PRId32 "-debug-%02" B_PRId32 "-%02"
252 		B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02"
253 		B_PRId32 ".report", teamPath.Leaf(), team->ID(),
254 		currentTime.Date().Day(), currentTime.Date().Month(),
255 		currentTime.Date().Year(), currentTime.Time().Hour(),
256 		currentTime.Time().Minute(), currentTime.Time().Second());
257 
258 	return buffer;
259 }
260 
261 
262 /*static*/ const char*
263 UiUtils::CoreFileNameForTeam(::Team* team, char* buffer, size_t bufferSize)
264 {
265 	BPath teamPath(team->Name());
266 	BDateTime currentTime;
267 	currentTime.SetTime_t(time(NULL));
268 	snprintf(buffer, bufferSize, "%s-%" B_PRId32 "-debug-%02" B_PRId32 "-%02"
269 		B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02"
270 		B_PRId32 ".core", teamPath.Leaf(), team->ID(),
271 		currentTime.Date().Day(), currentTime.Date().Month(),
272 		currentTime.Date().Year(), currentTime.Time().Hour(),
273 		currentTime.Time().Minute(), currentTime.Time().Second());
274 
275 	return buffer;
276 
277 }
278 
279 
280 /*static*/ void
281 UiUtils::PrintValueNodeGraph(BString& _output, ValueNodeChild* child,
282 	int32 indentLevel, int32 maxDepth)
283 {
284 	_output.Append('\t', indentLevel);
285 	_output << child->Name();
286 
287 	ValueNode* node = child->Node();
288 	if (node == NULL) {
289 		_output << ": Unavailable\n";
290 		return;
291 	}
292 
293 	if (node->GetType()->Kind() != TYPE_COMPOUND) {
294 		_output << ": ";
295 		status_t resolutionState = node->LocationAndValueResolutionState();
296 		if (resolutionState == VALUE_NODE_UNRESOLVED)
297 			_output << "Unresolved";
298 		else if (resolutionState == B_OK) {
299 			Value* value = node->GetValue();
300 			if (value != NULL) {
301 				BString valueData;
302 				value->ToString(valueData);
303 				_output << valueData;
304 			} else
305 				_output << "Unavailable";
306 		} else
307 			_output << strerror(resolutionState);
308 	}
309 
310 	if (maxDepth == 0 || node->CountChildren() == 0) {
311 		_output << "\n";
312 		return;
313 	}
314 
315 	if (node->CountChildren() == 1
316 		&& node->GetType()->ResolveRawType(false)->Kind() == TYPE_ADDRESS
317 		&& node->ChildAt(0)->GetType()->ResolveRawType(false)->Kind()
318 			== TYPE_COMPOUND) {
319 		// for the case of a pointer to a compound type,
320 		// we want to hide the intervening compound node and print
321 		// the children directly.
322 		node = node->ChildAt(0)->Node();
323 	}
324 
325 	if (node != NULL) {
326 		_output << " {\n";
327 
328 		for (int32 i = 0; i < node->CountChildren(); i++) {
329 			// don't dump compound nodes if our depth limit won't allow
330 			// us to traverse into their children anyways, and the top
331 			// level node contains no data of intereest.
332 			if (node->ChildAt(i)->GetType()->Kind() != TYPE_COMPOUND
333 				|| maxDepth > 1) {
334 				PrintValueNodeGraph(_output, node->ChildAt(i),
335 					indentLevel + 1, maxDepth - 1);
336 			}
337 		}
338 		_output.Append('\t', indentLevel);
339 		_output << "}\n";
340 	} else
341 		_output << "\n";
342 
343 	return;
344 }
345 
346 
347 /*static*/ void
348 UiUtils::DumpMemory(BString& _output, int32 indentLevel,
349 	TeamMemoryBlock* block, target_addr_t address, int32 itemSize,
350 	int32 displayWidth, int32 count)
351 {
352 	BString data;
353 
354 	int32 j;
355 	_output.Append('\t', indentLevel);
356 	for (int32 i = 0; i < count; i++) {
357 		if (!block->Contains(address + i * itemSize))
358 			break;
359 
360 		uint8* value;
361 
362 		if ((i % displayWidth) == 0) {
363 			int32 displayed = min_c(displayWidth, (count-i)) * itemSize;
364 			if (i != 0) {
365 				_output.Append("\n");
366 				_output.Append('\t', indentLevel);
367 			}
368 
369 			data.SetToFormat("[%#" B_PRIx64 "]  ", address + i * itemSize);
370 			_output += data;
371 			char c;
372 			for (j = 0; j < displayed; j++) {
373 				c = *(block->Data() + address - block->BaseAddress()
374 					+ (i * itemSize) + j);
375 				if (!isprint(c))
376 					c = '.';
377 
378 				_output += c;
379 			}
380 			if (count > displayWidth) {
381 				// make sure the spacing in the last line is correct
382 				for (j = displayed; j < displayWidth * itemSize; j++)
383 					_output += ' ';
384 			}
385 			_output.Append("  ");
386 		}
387 
388 		value = block->Data() + address - block->BaseAddress()
389 			+ i * itemSize;
390 
391 		switch (itemSize) {
392 			case 1:
393 				data.SetToFormat(" %02" B_PRIx8, *(uint8*)value);
394 				break;
395 			case 2:
396 				data.SetToFormat(" %04" B_PRIx16, *(uint16*)value);
397 				break;
398 			case 4:
399 				data.SetToFormat(" %08" B_PRIx32, *(uint32*)value);
400 				break;
401 			case 8:
402 				data.SetToFormat(" %016" B_PRIx64, *(uint64*)value);
403 				break;
404 		}
405 
406 		_output += data;
407 	}
408 
409 	_output.Append("\n");
410 }
411 
412 
413 static status_t ParseRangeString(BString& rangeString, int32& lowerBound,
414 	int32& upperBound)
415 {
416 	lowerBound = atoi(rangeString.String());
417 	int32 index = rangeString.FindFirst('-');
418 	if (index >= 0) {
419 		rangeString.Remove(0, index + 1);
420 		upperBound = atoi(rangeString.String());
421 	} else
422 		upperBound = lowerBound;
423 
424 	if (lowerBound > upperBound)
425 		return B_BAD_VALUE;
426 
427 	return B_OK;
428 }
429 
430 
431 /*static*/ status_t
432 UiUtils::ParseRangeExpression(const BString& rangeExpression, int32 lowerBound,
433 	int32 upperBound, bool fixedRange, RangeList& _output)
434 {
435 	if (rangeExpression.IsEmpty())
436 		return B_BAD_DATA;
437 
438 	BString dataString = rangeExpression;
439 	dataString.RemoveAll(" ");
440 
441 	// first, tokenize the range list to its constituent child ranges.
442 	int32 index;
443 	int32 lowValue;
444 	int32 highValue;
445 	BString tempRange;
446 	while (!dataString.IsEmpty()) {
447 		index = dataString.FindFirst(',');
448 		if (index == 0)
449 			return B_BAD_VALUE;
450 		else if (index > 0) {
451 			dataString.MoveInto(tempRange, 0, index);
452 			dataString.Remove(0, 1);
453 		} else {
454 			tempRange = dataString;
455 			dataString.Truncate(0);
456 		}
457 
458 		status_t result = ParseRangeString(tempRange, lowValue, highValue);
459 		if (result != B_OK)
460 			return result;
461 
462 
463 		if (fixedRange && (lowValue < lowerBound || highValue > upperBound))
464 			return B_BAD_VALUE;
465 
466 		result = _output.AddRange(lowValue, highValue);
467 		if (result != B_OK)
468 			return result;
469 
470 		tempRange.Truncate(0);
471 	}
472 
473 	return B_OK;
474 }
475 
476 
477 /*static*/ const char*
478 UiUtils::TypeCodeToString(type_code type)
479 {
480 	switch (type) {
481 		case B_INT8_TYPE:
482 			return "int8";
483 		case B_UINT8_TYPE:
484 			return "uint8";
485 		case B_INT16_TYPE:
486 			return "int16";
487 		case B_UINT16_TYPE:
488 			return "uint16";
489 		case B_INT32_TYPE:
490 			return "int32";
491 		case B_UINT32_TYPE:
492 			return "uint32";
493 		case B_INT64_TYPE:
494 			return "int64";
495 		case B_UINT64_TYPE:
496 			return "uint64";
497 		case B_FLOAT_TYPE:
498 			return "float";
499 		case B_DOUBLE_TYPE:
500 			return "double";
501 		case B_STRING_TYPE:
502 			return "string";
503 		default:
504 			return "unknown";
505 	}
506 }
507 
508 
509 template<typename T>
510 T GetSIMDValueAtOffset(char* data, int32 index)
511 {
512 	return ((T*)data)[index];
513 }
514 
515 
516 static int32 GetSIMDFormatByteSize(uint32 format)
517 {
518 	switch (format) {
519 		case SIMD_RENDER_FORMAT_INT8:
520 			return sizeof(char);
521 		case SIMD_RENDER_FORMAT_INT16:
522 			return sizeof(int16);
523 		case SIMD_RENDER_FORMAT_INT32:
524 			return sizeof(int32);
525 		case SIMD_RENDER_FORMAT_INT64:
526 			return sizeof(int64);
527 		case SIMD_RENDER_FORMAT_FLOAT:
528 			return sizeof(float);
529 		case SIMD_RENDER_FORMAT_DOUBLE:
530 			return sizeof(double);
531 	}
532 
533 	return 0;
534 }
535 
536 
537 /*static*/
538 const BString&
539 UiUtils::FormatSIMDValue(const BVariant& value, uint32 bitSize,
540 	uint32 format, BString& _output)
541 {
542 	_output.SetTo("{");
543 	char* data = (char*)value.ToPointer();
544 	uint32 count = bitSize / (GetSIMDFormatByteSize(format) * 8);
545 	for (uint32 i = 0; i < count; i ++) {
546 		BString temp;
547 		switch (format) {
548 			case SIMD_RENDER_FORMAT_INT8:
549 				temp.SetToFormat("%#" B_PRIx8,
550 					GetSIMDValueAtOffset<uint8>(data, i));
551 				break;
552 			case SIMD_RENDER_FORMAT_INT16:
553 				temp.SetToFormat("%#" B_PRIx16,
554 					GetSIMDValueAtOffset<uint16>(data, i));
555 				break;
556 			case SIMD_RENDER_FORMAT_INT32:
557 				temp.SetToFormat("%#" B_PRIx32,
558 					GetSIMDValueAtOffset<uint32>(data, i));
559 				break;
560 			case SIMD_RENDER_FORMAT_INT64:
561 				temp.SetToFormat("%#" B_PRIx64,
562 					GetSIMDValueAtOffset<uint64>(data, i));
563 				break;
564 			case SIMD_RENDER_FORMAT_FLOAT:
565 				temp.SetToFormat("%.3g",
566 					(double)GetSIMDValueAtOffset<float>(data, i));
567 				break;
568 			case SIMD_RENDER_FORMAT_DOUBLE:
569 				temp.SetToFormat("%.3g",
570 					GetSIMDValueAtOffset<double>(data, i));
571 				break;
572 		}
573 		_output += temp;
574 		if (i < count - 1)
575 			_output += ", ";
576 	}
577 	_output += "}";
578 
579 	return _output;
580 }
581 
582 
583 const char*
584 UiUtils::SignalNameToString(int32 signal, BString& _output)
585 {
586 	#undef DEFINE_SIGNAL_STRING
587 	#define DEFINE_SIGNAL_STRING(x)										\
588 		case x:															\
589 			_output = #x;												\
590 			return _output.String();
591 
592 	switch (signal) {
593 		DEFINE_SIGNAL_STRING(SIGHUP)
594 		DEFINE_SIGNAL_STRING(SIGINT)
595 		DEFINE_SIGNAL_STRING(SIGQUIT)
596 		DEFINE_SIGNAL_STRING(SIGILL)
597 		DEFINE_SIGNAL_STRING(SIGCHLD)
598 		DEFINE_SIGNAL_STRING(SIGABRT)
599 		DEFINE_SIGNAL_STRING(SIGPIPE)
600 		DEFINE_SIGNAL_STRING(SIGFPE)
601 		DEFINE_SIGNAL_STRING(SIGKILL)
602 		DEFINE_SIGNAL_STRING(SIGSTOP)
603 		DEFINE_SIGNAL_STRING(SIGSEGV)
604 		DEFINE_SIGNAL_STRING(SIGCONT)
605 		DEFINE_SIGNAL_STRING(SIGTSTP)
606 		DEFINE_SIGNAL_STRING(SIGALRM)
607 		DEFINE_SIGNAL_STRING(SIGTERM)
608 		DEFINE_SIGNAL_STRING(SIGTTIN)
609 		DEFINE_SIGNAL_STRING(SIGTTOU)
610 		DEFINE_SIGNAL_STRING(SIGUSR1)
611 		DEFINE_SIGNAL_STRING(SIGUSR2)
612 		DEFINE_SIGNAL_STRING(SIGWINCH)
613 		DEFINE_SIGNAL_STRING(SIGKILLTHR)
614 		DEFINE_SIGNAL_STRING(SIGTRAP)
615 		DEFINE_SIGNAL_STRING(SIGPOLL)
616 		DEFINE_SIGNAL_STRING(SIGPROF)
617 		DEFINE_SIGNAL_STRING(SIGSYS)
618 		DEFINE_SIGNAL_STRING(SIGURG)
619 		DEFINE_SIGNAL_STRING(SIGVTALRM)
620 		DEFINE_SIGNAL_STRING(SIGXCPU)
621 		DEFINE_SIGNAL_STRING(SIGXFSZ)
622 		DEFINE_SIGNAL_STRING(SIGBUS)
623 		default:
624 			break;
625 	}
626 
627 	if (signal == SIGRTMIN)
628 		_output = "SIGRTMIN";
629 	else if (signal == SIGRTMAX)
630 		_output = "SIGRTMAX";
631 	else
632 		_output.SetToFormat("SIGRTMIN+%" B_PRId32, signal - SIGRTMIN);
633 
634 	return _output.String();
635 }
636 
637 
638 const char*
639 UiUtils::SignalDispositionToString(int disposition)
640 {
641 	switch (disposition) {
642 		case SIGNAL_DISPOSITION_IGNORE:
643 			return "Ignore";
644 		case SIGNAL_DISPOSITION_STOP_AT_RECEIPT:
645 			return "Stop at receipt";
646 		case SIGNAL_DISPOSITION_STOP_AT_SIGNAL_HANDLER:
647 			return "Stop at signal handler";
648 		default:
649 			break;
650 	}
651 
652 	return "Unknown";
653 }
654