1 /* 2 * Copyright 2012-2016, Rene Gollent, rene@gollent.com. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "DebugReportGenerator.h" 8 9 #include <cpu_type.h> 10 11 #include <AutoLocker.h> 12 #include <Entry.h> 13 #include <File.h> 14 #include <Path.h> 15 #include <StringForSize.h> 16 17 #include "Architecture.h" 18 #include "AreaInfo.h" 19 #include "AutoDeleter.h" 20 #include "CpuState.h" 21 #include "DebuggerInterface.h" 22 #include "DisassembledCode.h" 23 #include "FunctionInstance.h" 24 #include "Image.h" 25 #include "ImageDebugInfo.h" 26 #include "MessageCodes.h" 27 #include "Register.h" 28 #include "SemaphoreInfo.h" 29 #include "StackFrame.h" 30 #include "StackTrace.h" 31 #include "Statement.h" 32 #include "SystemInfo.h" 33 #include "Team.h" 34 #include "TeamDebugInfo.h" 35 #include "Thread.h" 36 #include "Type.h" 37 #include "UiUtils.h" 38 #include "UserInterface.h" 39 #include "Value.h" 40 #include "ValueLoader.h" 41 #include "ValueLocation.h" 42 #include "ValueNode.h" 43 #include "ValueNodeManager.h" 44 45 46 #define WRITE_AND_CHECK(output, data) \ 47 { \ 48 ssize_t error = output.Write(data.String(), data.Length()); \ 49 if (error < 0) \ 50 return error; \ 51 } 52 53 54 DebugReportGenerator::DebugReportGenerator(::Team* team, 55 UserInterfaceListener* listener, DebuggerInterface* interface) 56 : 57 BLooper("DebugReportGenerator"), 58 fTeam(team), 59 fArchitecture(team->GetArchitecture()), 60 fDebuggerInterface(interface), 61 fTeamDataSem(-1), 62 fNodeManager(NULL), 63 fListener(listener), 64 fWaitingNode(NULL), 65 fCurrentBlock(NULL), 66 fBlockRetrievalStatus(B_OK), 67 fTraceWaitingThread(NULL), 68 fSourceWaitingFunction(NULL) 69 { 70 fTeam->AddListener(this); 71 fArchitecture->AcquireReference(); 72 } 73 74 75 DebugReportGenerator::~DebugReportGenerator() 76 { 77 fTeam->RemoveListener(this); 78 fArchitecture->ReleaseReference(); 79 if (fNodeManager != NULL) { 80 fNodeManager->RemoveListener(this); 81 fNodeManager->ReleaseReference(); 82 } 83 84 if (fCurrentBlock != NULL) 85 fCurrentBlock->ReleaseReference(); 86 87 if (fTeamDataSem >= 0) 88 delete_sem(fTeamDataSem); 89 } 90 91 92 status_t 93 DebugReportGenerator::Init() 94 { 95 fTeamDataSem = create_sem(0, "debug_controller_data_wait"); 96 if (fTeamDataSem < B_OK) 97 return fTeamDataSem; 98 99 fNodeManager = new(std::nothrow) ValueNodeManager(); 100 if (fNodeManager == NULL) 101 return B_NO_MEMORY; 102 103 fNodeManager->AddListener(this); 104 105 Run(); 106 107 return B_OK; 108 } 109 110 111 DebugReportGenerator* 112 DebugReportGenerator::Create(::Team* team, UserInterfaceListener* listener, 113 DebuggerInterface* interface) 114 { 115 DebugReportGenerator* self = new DebugReportGenerator(team, listener, 116 interface); 117 118 try { 119 self->Init(); 120 } catch (...) { 121 delete self; 122 throw; 123 } 124 125 return self; 126 } 127 128 129 status_t 130 DebugReportGenerator::_GenerateReport(const char* outputPath) 131 { 132 BFile file(outputPath, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 133 status_t result = file.InitCheck(); 134 if (result != B_OK) 135 return result; 136 137 result = _GenerateReportHeader(file); 138 if (result != B_OK) 139 return result; 140 141 result = _DumpRunningThreads(file); 142 if (result != B_OK) 143 return result; 144 145 result = _DumpLoadedImages(file); 146 if (result != B_OK) 147 return result; 148 149 result = _DumpAreas(file); 150 if (result != B_OK) 151 return result; 152 153 result = _DumpSemaphores(file); 154 if (result != B_OK) 155 return result; 156 157 AutoLocker< ::Team> teamLocker(fTeam); 158 fTeam->NotifyDebugReportChanged(outputPath, B_OK); 159 160 return B_OK; 161 } 162 163 164 void 165 DebugReportGenerator::MessageReceived(BMessage* message) 166 { 167 switch (message->what) { 168 case MSG_GENERATE_DEBUG_REPORT: 169 { 170 entry_ref ref; 171 if (message->FindRef("target", &ref) == B_OK) { 172 BPath path(&ref); 173 status_t error = _GenerateReport(path.Path()); 174 if (error != B_OK) 175 fTeam->NotifyDebugReportChanged(path.Path(), error); 176 } 177 break; 178 } 179 180 default: 181 BLooper::MessageReceived(message); 182 break; 183 } 184 } 185 186 187 void 188 DebugReportGenerator::ThreadStackTraceChanged(const ::Team::ThreadEvent& event) 189 { 190 if (fTraceWaitingThread == event.GetThread()) { 191 fTraceWaitingThread = NULL; 192 release_sem(fTeamDataSem); 193 } 194 } 195 196 197 void 198 DebugReportGenerator::MemoryBlockRetrieved(TeamMemoryBlock* block) 199 { 200 _HandleMemoryBlockRetrieved(block, B_OK); 201 } 202 203 204 void 205 DebugReportGenerator::MemoryBlockRetrievalFailed(TeamMemoryBlock* block, 206 status_t result) 207 { 208 _HandleMemoryBlockRetrieved(block, result); 209 } 210 211 212 void 213 DebugReportGenerator::ValueNodeValueChanged(ValueNode* node) 214 { 215 if (node == fWaitingNode) { 216 fWaitingNode = NULL; 217 release_sem(fTeamDataSem); 218 } 219 } 220 221 222 void 223 DebugReportGenerator::FunctionSourceCodeChanged(Function* function) 224 { 225 AutoLocker< ::Team> teamLocker(fTeam); 226 if (function == fSourceWaitingFunction) { 227 function_source_state state = function->SourceCodeState(); 228 229 switch (state) { 230 case FUNCTION_SOURCE_LOADED: 231 case FUNCTION_SOURCE_SUPPRESSED: 232 case FUNCTION_SOURCE_UNAVAILABLE: 233 { 234 fSourceWaitingFunction->RemoveListener(this); 235 fSourceWaitingFunction = NULL; 236 release_sem(fTeamDataSem); 237 // fall through 238 } 239 default: 240 break; 241 } 242 } 243 } 244 245 status_t 246 DebugReportGenerator::_GenerateReportHeader(BFile& _output) 247 { 248 AutoLocker< ::Team> locker(fTeam); 249 250 BString data; 251 data.SetToFormat("Debug information for team %s (%" B_PRId32 "):\n", 252 fTeam->Name(), fTeam->ID()); 253 WRITE_AND_CHECK(_output, data); 254 255 cpu_platform platform = B_CPU_UNKNOWN; 256 cpu_vendor cpuVendor = B_CPU_VENDOR_UNKNOWN; 257 uint32 cpuModel = 0; 258 uint32 topologyNodeCount = 0; 259 cpu_topology_node_info* topology = NULL; 260 get_cpu_topology_info(NULL, &topologyNodeCount); 261 if (topologyNodeCount != 0) { 262 topology = new(std::nothrow) cpu_topology_node_info[topologyNodeCount]; 263 if (topology == NULL) 264 return B_NO_MEMORY; 265 266 BPrivate::ArrayDeleter<cpu_topology_node_info> deleter(topology); 267 get_cpu_topology_info(topology, &topologyNodeCount); 268 269 for (uint32 i = 0; i < topologyNodeCount; i++) { 270 switch (topology[i].type) { 271 case B_TOPOLOGY_ROOT: 272 platform = topology[i].data.root.platform; 273 break; 274 275 case B_TOPOLOGY_PACKAGE: 276 cpuVendor = topology[i].data.package.vendor; 277 break; 278 279 case B_TOPOLOGY_CORE: 280 cpuModel = topology[i].data.core.model; 281 break; 282 283 default: 284 break; 285 } 286 } 287 } 288 289 SystemInfo sysInfo; 290 if (fDebuggerInterface->GetSystemInfo(sysInfo) == B_OK) { 291 const system_info &info = sysInfo.GetSystemInfo(); 292 const char* vendor = get_cpu_vendor_string(cpuVendor); 293 const char* model = get_cpu_model_string(platform, cpuVendor, cpuModel); 294 295 data.SetToFormat("CPU(s): %" B_PRId32 "x %s %s\n", 296 info.cpu_count, vendor != NULL ? vendor : "unknown", 297 model != NULL ? model : "unknown"); 298 WRITE_AND_CHECK(_output, data); 299 300 char maxSize[32]; 301 char usedSize[32]; 302 303 data.SetToFormat("Memory: %s total, %s used\n", 304 BPrivate::string_for_size((int64)info.max_pages * B_PAGE_SIZE, 305 maxSize, sizeof(maxSize)), 306 BPrivate::string_for_size((int64)info.used_pages * B_PAGE_SIZE, 307 usedSize, sizeof(usedSize))); 308 WRITE_AND_CHECK(_output, data); 309 310 const utsname& name = sysInfo.GetSystemName(); 311 data.SetToFormat("Haiku revision: %s (%s)\n", name.version, 312 name.machine); 313 WRITE_AND_CHECK(_output, data); 314 } 315 316 return B_OK; 317 } 318 319 320 status_t 321 DebugReportGenerator::_DumpLoadedImages(BFile& _output) 322 { 323 AutoLocker< ::Team> locker(fTeam); 324 325 BString data("\nLoaded Images:\n"); 326 WRITE_AND_CHECK(_output, data); 327 BObjectList<Image> images; 328 for (ImageList::ConstIterator it = fTeam->Images().GetIterator(); 329 Image* image = it.Next();) { 330 images.AddItem(image); 331 } 332 333 images.SortItems(&_CompareImages); 334 335 Image* image = NULL; 336 data.SetToFormat("\tID\t\tText Base\tText End\tData Base\tData" 337 " End\tType\tName\n\t"); 338 WRITE_AND_CHECK(_output, data); 339 data.Truncate(0L); 340 data.Append('-', 80); 341 data.Append("\n"); 342 WRITE_AND_CHECK(_output, data); 343 for (int32 i = 0; (image = images.ItemAt(i)) != NULL; i++) { 344 const ImageInfo& info = image->Info(); 345 char buffer[32]; 346 try { 347 target_addr_t textBase = info.TextBase(); 348 target_addr_t dataBase = info.DataBase(); 349 350 data.SetToFormat("\t%" B_PRId32 "\t0x%08" B_PRIx64 "\t" 351 "0x%08" B_PRIx64 "\t0x%08" B_PRIx64 "\t0x%08" B_PRIx64 "\t" 352 "%-7s\t%s\n", info.ImageID(), textBase, textBase + info.TextSize(), 353 dataBase, dataBase + info.DataSize(), 354 UiUtils::ImageTypeToString(info.Type(), buffer, 355 sizeof(buffer)), info.Name().String()); 356 357 WRITE_AND_CHECK(_output, data); 358 } catch (...) { 359 return B_NO_MEMORY; 360 } 361 } 362 363 return B_OK; 364 } 365 366 367 status_t 368 DebugReportGenerator::_DumpAreas(BFile& _output) 369 { 370 BObjectList<AreaInfo> areas(20, true); 371 status_t result = fDebuggerInterface->GetAreaInfos(areas); 372 if (result != B_OK) 373 return result; 374 375 areas.SortItems(&_CompareAreas); 376 377 BString data("\nAreas:\n"); 378 WRITE_AND_CHECK(_output, data); 379 data.SetToFormat("\tID\t\tBase\t\tEnd\t\t\tSize (KiB)\tProtection\tLocking\t\t\tName\n\t"); 380 WRITE_AND_CHECK(_output, data); 381 data.Truncate(0L); 382 data.Append('-', 80); 383 data.Append("\n"); 384 WRITE_AND_CHECK(_output, data); 385 AreaInfo* info; 386 BString protectionBuffer; 387 char lockingBuffer[32]; 388 for (int32 i = 0; (info = areas.ItemAt(i)) != NULL; i++) { 389 try { 390 data.SetToFormat("\t%" B_PRId32 "\t0x%08" B_PRIx64 "\t" 391 "0x%08" B_PRIx64 "\t%10" B_PRId64 "\t%-11s\t%-14s\t%s\n", 392 info->AreaID(), info->BaseAddress(), info->BaseAddress() 393 + info->Size(), info->Size() / 1024, 394 UiUtils::AreaProtectionFlagsToString(info->Protection(), 395 protectionBuffer).String(), 396 UiUtils::AreaLockingFlagsToString(info->Lock(), lockingBuffer, 397 sizeof(lockingBuffer)), info->Name().String()); 398 399 WRITE_AND_CHECK(_output, data); 400 } catch (...) { 401 return B_NO_MEMORY; 402 } 403 } 404 405 data = "\nProtection Flags: r - read, w - write, x - execute, " 406 "s - stack, o - overcommit, c - cloneable, S - shared, k - kernel\n"; 407 WRITE_AND_CHECK(_output, data); 408 409 return B_OK; 410 } 411 412 413 status_t 414 DebugReportGenerator::_DumpSemaphores(BFile& _output) 415 { 416 BObjectList<SemaphoreInfo> semaphores(20, true); 417 status_t error = fDebuggerInterface->GetSemaphoreInfos(semaphores); 418 if (error != B_OK) 419 return error; 420 421 semaphores.SortItems(&_CompareSemaphores); 422 423 BString data = "\nSemaphores:\n"; 424 WRITE_AND_CHECK(_output, data); 425 data.SetToFormat("\tID\t\tCount\tLast Holder\tName\n\t"); 426 WRITE_AND_CHECK(_output, data); 427 data.Truncate(0L); 428 data.Append('-', 60); 429 data.Append("\n"); 430 WRITE_AND_CHECK(_output, data); 431 SemaphoreInfo* info; 432 for (int32 i = 0; (info = semaphores.ItemAt(i)) != NULL; i++) { 433 try { 434 data.SetToFormat("\t%" B_PRId32 "\t%5" B_PRId32 "\t%11" B_PRId32 435 "\t%s\n", info->SemID(), info->Count(), 436 info->LatestHolder(), info->Name().String()); 437 438 WRITE_AND_CHECK(_output, data); 439 } catch (...) { 440 return B_NO_MEMORY; 441 } 442 } 443 444 return B_OK; 445 } 446 447 448 status_t 449 DebugReportGenerator::_DumpRunningThreads(BFile& _output) 450 { 451 AutoLocker< ::Team> locker(fTeam); 452 453 BString data("\nActive Threads:\n"); 454 WRITE_AND_CHECK(_output, data); 455 BObjectList< ::Thread> threads; 456 ::Thread* thread; 457 for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator(); 458 (thread = it.Next());) { 459 threads.AddItem(thread); 460 thread->AcquireReference(); 461 } 462 463 status_t error = B_OK; 464 threads.SortItems(&_CompareThreads); 465 for (int32 i = 0; (thread = threads.ItemAt(i)) != NULL; i++) { 466 try { 467 data.SetToFormat("\tthread %" B_PRId32 ": %s %s\n", thread->ID(), 468 thread->Name(), thread->IsMainThread() 469 ? "(main)" : ""); 470 WRITE_AND_CHECK(_output, data); 471 472 if (thread->State() == THREAD_STATE_STOPPED) { 473 data.SetToFormat("\t\tstate: %s", 474 UiUtils::ThreadStateToString(thread->State(), 475 thread->StoppedReason())); 476 const BString& stoppedInfo = thread->StoppedReasonInfo(); 477 if (stoppedInfo.Length() != 0) 478 data << " (" << stoppedInfo << ")"; 479 data << "\n\n"; 480 WRITE_AND_CHECK(_output, data); 481 482 // we need to release our lock on the team here 483 // since we might need to block and wait 484 // on the stack trace. 485 locker.Unlock(); 486 error = _DumpDebuggedThreadInfo(_output, thread); 487 locker.Lock(); 488 if (error != B_OK) 489 break; 490 } 491 } catch (...) { 492 error = B_NO_MEMORY; 493 } 494 } 495 496 for (int32 i = 0; (thread = threads.ItemAt(i)) != NULL; i++) 497 thread->ReleaseReference(); 498 499 return error; 500 } 501 502 503 status_t 504 DebugReportGenerator::_DumpDebuggedThreadInfo(BFile& _output, 505 ::Thread* thread) 506 { 507 AutoLocker< ::Team> locker; 508 if (thread->State() != THREAD_STATE_STOPPED) 509 return B_OK; 510 511 status_t error; 512 StackTrace* trace = NULL; 513 for (;;) { 514 trace = thread->GetStackTrace(); 515 if (trace != NULL) 516 break; 517 518 locker.Unlock(); 519 fTraceWaitingThread = thread; 520 do { 521 error = acquire_sem(fTeamDataSem); 522 } while (error == B_INTERRUPTED); 523 524 if (error != B_OK) 525 return error; 526 527 locker.Lock(); 528 } 529 530 BString data("\t\tFrame\t\tIP\t\t\tFunction Name\n"); 531 WRITE_AND_CHECK(_output, data); 532 data = "\t\t-----------------------------------------------\n"; 533 WRITE_AND_CHECK(_output, data); 534 for (int32 i = 0; StackFrame* frame = trace->FrameAt(i); i++) { 535 char functionName[512]; 536 BString sourcePath; 537 538 target_addr_t ip = frame->InstructionPointer(); 539 Image* image = fTeam->ImageByAddress(ip); 540 FunctionInstance* functionInstance = NULL; 541 if (image != NULL && image->ImageDebugInfoState() 542 == IMAGE_DEBUG_INFO_LOADED) { 543 ImageDebugInfo* info = image->GetImageDebugInfo(); 544 functionInstance = info->FunctionAtAddress(ip); 545 } 546 547 if (functionInstance != NULL) { 548 Function* function = functionInstance->GetFunction(); 549 if (function->SourceCodeState() == FUNCTION_SOURCE_NOT_LOADED 550 && functionInstance->SourceCodeState() 551 == FUNCTION_SOURCE_NOT_LOADED) { 552 fSourceWaitingFunction = function; 553 fSourceWaitingFunction->AddListener(this); 554 fListener->FunctionSourceCodeRequested(functionInstance); 555 556 locker.Unlock(); 557 558 do { 559 error = acquire_sem(fTeamDataSem); 560 } while (error == B_INTERRUPTED); 561 562 if (error != B_OK) 563 return error; 564 565 locker.Lock(); 566 } 567 } 568 569 Statement* statement; 570 if (fTeam->GetStatementAtAddress(ip, 571 functionInstance, statement) == B_OK) { 572 BReference<Statement> statementReference(statement, true); 573 574 int32 line = statement->StartSourceLocation().Line(); 575 LocatableFile* sourceFile = functionInstance->GetFunction() 576 ->SourceFile(); 577 if (sourceFile != NULL) { 578 sourceFile->GetPath(sourcePath); 579 sourcePath.SetToFormat("(%s:%" B_PRId32 ")", 580 sourcePath.String(), line); 581 } 582 } 583 584 585 data.SetToFormat("\t\t%#08" B_PRIx64 "\t%#08" B_PRIx64 "\t%s %s\n", 586 frame->FrameAddress(), ip, UiUtils::FunctionNameForFrame( 587 frame, functionName, sizeof(functionName)), 588 sourcePath.String()); 589 590 WRITE_AND_CHECK(_output, data); 591 592 // only dump the topmost frame 593 if (i == 0) { 594 locker.Unlock(); 595 error = _DumpFunctionDisassembly(_output, 596 frame->InstructionPointer()); 597 if (error != B_OK) 598 return error; 599 error = _DumpStackFrameMemory(_output, thread->GetCpuState(), 600 frame->FrameAddress(), thread->GetTeam()->GetArchitecture() 601 ->StackGrowthDirection()); 602 if (error != B_OK) 603 return error; 604 locker.Lock(); 605 } 606 607 if (frame->CountParameters() == 0 && frame->CountLocalVariables() == 0) 608 continue; 609 610 data = "\t\t\tVariables:\n"; 611 WRITE_AND_CHECK(_output, data); 612 error = fNodeManager->SetStackFrame(thread, frame); 613 if (error != B_OK) 614 continue; 615 616 ValueNodeContainer* container = fNodeManager->GetContainer(); 617 AutoLocker<ValueNodeContainer> containerLocker(container); 618 for (int32 i = 0; i < container->CountChildren(); i++) { 619 data.Truncate(0L); 620 ValueNodeChild* child = container->ChildAt(i); 621 containerLocker.Unlock(); 622 _ResolveValueIfNeeded(child->Node(), frame, 1); 623 containerLocker.Lock(); 624 UiUtils::PrintValueNodeGraph(data, child, 3, 1); 625 WRITE_AND_CHECK(_output, data); 626 } 627 data = "\n"; 628 WRITE_AND_CHECK(_output, data); 629 } 630 631 data = "\n\t\tRegisters:\n"; 632 WRITE_AND_CHECK(_output, data); 633 634 CpuState* state = thread->GetCpuState(); 635 BVariant value; 636 const Register* reg = NULL; 637 for (int32 i = 0; i < fArchitecture->CountRegisters(); i++) { 638 reg = fArchitecture->Registers() + i; 639 state->GetRegisterValue(reg, value); 640 641 if (reg->Format() == REGISTER_FORMAT_SIMD) { 642 data.SetToFormat("\t\t\t%5s:\t%s\n", reg->Name(), 643 UiUtils::FormatSIMDValue(value, reg->BitSize(), 644 SIMD_RENDER_FORMAT_INT16, data).String()); 645 } else { 646 char buffer[64]; 647 data.SetToFormat("\t\t\t%5s:\t%s\n", reg->Name(), 648 UiUtils::VariantToString(value, buffer, sizeof(buffer))); 649 } 650 651 WRITE_AND_CHECK(_output, data); 652 } 653 654 return B_OK; 655 } 656 657 658 status_t 659 DebugReportGenerator::_DumpFunctionDisassembly(BFile& _output, 660 target_addr_t instructionPointer) 661 { 662 AutoLocker< ::Team> teamLocker(fTeam); 663 BString data; 664 FunctionInstance* instance = NULL; 665 Image* image = fTeam->ImageByAddress(instructionPointer); 666 if (image == NULL) { 667 data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#" 668 B_PRIx64 ": address not contained in any valid image.\n", 669 instructionPointer); 670 WRITE_AND_CHECK(_output, data); 671 return B_OK; 672 } 673 674 ImageDebugInfo* info = image->GetImageDebugInfo(); 675 if (info == NULL) { 676 data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#" 677 B_PRIx64 ": no debug info available for image '%s'.\n", 678 instructionPointer, image->Name().String()); 679 WRITE_AND_CHECK(_output, data); 680 return B_OK; 681 } 682 683 instance = info->FunctionAtAddress(instructionPointer); 684 if (instance == NULL) { 685 data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#" 686 B_PRIx64 ": address does not point to a function.\n", 687 instructionPointer); 688 WRITE_AND_CHECK(_output, data); 689 return B_OK; 690 } 691 692 Statement* statement = NULL; 693 DisassembledCode* code = instance->GetSourceCode(); 694 BReference<DisassembledCode> codeReference; 695 if (code == NULL) { 696 status_t error = fTeam->DebugInfo()->DisassembleFunction(instance, 697 code); 698 if (error != B_OK) { 699 data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#" 700 B_PRIx64 ": %s.\n", instructionPointer, strerror(error)); 701 WRITE_AND_CHECK(_output, data); 702 return B_OK; 703 } 704 705 codeReference.SetTo(code, true); 706 } else 707 codeReference.SetTo(code); 708 709 statement = code->StatementAtAddress(instructionPointer); 710 if (statement == NULL) { 711 data.SetToFormat("\t\t\tUnable to retrieve disassembly for IP %#" 712 B_PRIx64 ": address does not map to a valid instruction.\n", 713 instructionPointer); 714 WRITE_AND_CHECK(_output, data); 715 return B_OK; 716 } 717 718 SourceLocation location = statement->StartSourceLocation(); 719 720 data = "\t\t\tDisassembly:\n"; 721 WRITE_AND_CHECK(_output, data); 722 for (int32 i = 0; i <= location.Line(); i++) { 723 data = "\t\t\t\t"; 724 data << code->LineAt(i); 725 if (i == location.Line()) 726 data << " <--"; 727 data << "\n"; 728 WRITE_AND_CHECK(_output, data); 729 } 730 data = "\n"; 731 WRITE_AND_CHECK(_output, data); 732 733 return B_OK; 734 } 735 736 737 status_t 738 DebugReportGenerator::_DumpStackFrameMemory(BFile& _output, 739 CpuState* state, target_addr_t framePointer, uint8 stackDirection) 740 { 741 target_addr_t startAddress; 742 target_addr_t endAddress; 743 if (stackDirection == STACK_GROWTH_DIRECTION_POSITIVE) { 744 startAddress = framePointer; 745 endAddress = state->StackPointer(); 746 } else { 747 startAddress = state->StackPointer(); 748 endAddress = framePointer; 749 } 750 751 if (endAddress <= startAddress) 752 return B_OK; 753 754 if (fCurrentBlock == NULL || !fCurrentBlock->Contains(startAddress)) { 755 status_t error; 756 fListener->InspectRequested(startAddress, this); 757 do { 758 error = acquire_sem(fTeamDataSem); 759 } while (error == B_INTERRUPTED); 760 761 if (error != B_OK) 762 return error; 763 } 764 765 BString data("\t\t\tFrame memory:\n"); 766 WRITE_AND_CHECK(_output, data); 767 if (fBlockRetrievalStatus == B_OK) { 768 data.Truncate(0L); 769 UiUtils::DumpMemory(data, 4, fCurrentBlock, startAddress, 1, 16, 770 endAddress - startAddress); 771 WRITE_AND_CHECK(_output, data); 772 } else { 773 data.SetToFormat("\t\t\t\tUnavailable (%s)\n", strerror( 774 fBlockRetrievalStatus)); 775 WRITE_AND_CHECK(_output, data); 776 } 777 778 return B_OK; 779 } 780 781 782 status_t 783 DebugReportGenerator::_ResolveValueIfNeeded(ValueNode* node, StackFrame* frame, 784 int32 maxDepth) 785 { 786 status_t result = B_OK; 787 if (node->LocationAndValueResolutionState() == VALUE_NODE_UNRESOLVED) { 788 fWaitingNode = node; 789 fListener->ValueNodeValueRequested(frame->GetCpuState(), 790 fNodeManager->GetContainer(), node); 791 do { 792 result = acquire_sem(fTeamDataSem); 793 } while (result == B_INTERRUPTED); 794 } 795 796 if (node->LocationAndValueResolutionState() == B_OK && maxDepth > 0) { 797 AutoLocker<ValueNodeContainer> containerLocker( 798 fNodeManager->GetContainer()); 799 for (int32 i = 0; i < node->CountChildren(); i++) { 800 ValueNodeChild* child = node->ChildAt(i); 801 containerLocker.Unlock(); 802 result = fNodeManager->AddChildNodes(child); 803 if (result != B_OK) 804 continue; 805 806 // since in the case of a pointer to a compound we hide 807 // the intervening compound, don't consider the hidden node 808 // a level for the purposes of depth traversal 809 if (node->GetType()->ResolveRawType(false)->Kind() == TYPE_ADDRESS 810 && child->GetType()->ResolveRawType(false)->Kind() 811 == TYPE_COMPOUND) { 812 _ResolveValueIfNeeded(child->Node(), frame, maxDepth); 813 } else 814 _ResolveValueIfNeeded(child->Node(), frame, maxDepth - 1); 815 containerLocker.Lock(); 816 } 817 } 818 819 return result; 820 } 821 822 823 void 824 DebugReportGenerator::_HandleMemoryBlockRetrieved(TeamMemoryBlock* block, 825 status_t result) 826 { 827 if (fCurrentBlock != NULL) { 828 fCurrentBlock->ReleaseReference(); 829 fCurrentBlock = NULL; 830 } 831 832 fBlockRetrievalStatus = result; 833 834 fCurrentBlock = block; 835 release_sem(fTeamDataSem); 836 } 837 838 839 840 /*static*/ int 841 DebugReportGenerator::_CompareAreas(const AreaInfo* a, const AreaInfo* b) 842 { 843 if (a->BaseAddress() < b->BaseAddress()) 844 return -1; 845 846 return 1; 847 } 848 849 850 /*static*/ int 851 DebugReportGenerator::_CompareImages(const Image* a, const Image* b) 852 { 853 if (a->Info().TextBase() < b->Info().TextBase()) 854 return -1; 855 856 return 1; 857 } 858 859 860 /*static*/ int 861 DebugReportGenerator::_CompareSemaphores(const SemaphoreInfo* a, 862 const SemaphoreInfo* b) 863 { 864 if (a->SemID() < b->SemID()) 865 return -1; 866 867 return 1; 868 } 869 870 871 /*static*/ int 872 DebugReportGenerator::_CompareThreads(const ::Thread* a, 873 const ::Thread* b) 874 { 875 // sort stopped threads last, otherwise sort by thread ID 876 if (a->State() == b->State()) 877 return a->ID() < b->ID() ? -1 : 1; 878 879 if (a->State() == THREAD_STATE_STOPPED && b->State() 880 != THREAD_STATE_STOPPED) { 881 return 1; 882 } 883 884 return -1; 885 } 886