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