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