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