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_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* 248 UiUtils::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* 265 UiUtils::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 283 UiUtils::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 350 UiUtils::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 415 static 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 434 UiUtils::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* 480 UiUtils::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 511 template<typename T> 512 T GetSIMDValueAtOffset(char* data, int32 index) 513 { 514 return ((T*)data)[index]; 515 } 516 517 518 static 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*/ 540 const BString& 541 UiUtils::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 585 const char* 586 UiUtils::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 640 const char* 641 UiUtils::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