1 /* 2 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <ctype.h> 8 #include <errno.h> 9 #include <getopt.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 14 #include <algorithm> 15 #include <map> 16 #include <new> 17 #include <string> 18 19 #include <debugger.h> 20 #include <OS.h> 21 #include <String.h> 22 23 #include <syscalls.h> 24 #include <system_profiler_defs.h> 25 26 #include <AutoDeleter.h> 27 #include <debug_support.h> 28 #include <ObjectList.h> 29 #include <Referenceable.h> 30 31 #include <util/DoublyLinkedList.h> 32 33 #include "BasicProfileResult.h" 34 #include "CallgrindProfileResult.h" 35 #include "debug_utils.h" 36 #include "Image.h" 37 #include "Options.h" 38 #include "SummaryProfileResult.h" 39 #include "Team.h" 40 41 42 // size of the sample buffer area for system profiling 43 #define PROFILE_ALL_SAMPLE_AREA_SIZE (4 * 1024 * 1024) 44 45 46 extern const char* __progname; 47 const char* kCommandName = __progname; 48 49 50 class Image; 51 class Team; 52 class Thread; 53 54 55 static const char* kUsage = 56 "Usage: %s [ <options> ] [ <command line> ]\n" 57 "Profiles threads by periodically sampling the program counter. There are\n" 58 "two different modes: One profiles the complete system. The other starts\n" 59 "a program and profiles that and (optionally) its children. When a thread\n" 60 "terminates, a list of the functions where the thread was encountered is\n" 61 "printed.\n" 62 "\n" 63 "Options:\n" 64 " -a, --all - Profile all teams.\n" 65 " -c - Don't profile child threads. Default is to\n" 66 " recursively profile all threads created by a profiled\n" 67 " thread.\n" 68 " -C - Don't profile child teams. Default is to recursively\n" 69 " profile all teams created by a profiled team.\n" 70 " -f - Always analyze the full caller stack. The hit count\n" 71 " for every encountered function will be incremented.\n" 72 " This increases the default for the caller stack depth\n" 73 " (\"-s\") to 64.\n" 74 " -h, --help - Print this usage info.\n" 75 " -i <interval> - Use a tick interval of <interval> microseconds.\n" 76 " Default is 1000 (1 ms). On a fast machine, a shorter\n" 77 " interval might lead to better results, while it might\n" 78 " make them worse on slow machines.\n" 79 " -k - Don't check kernel images for hits.\n" 80 " -l - Also profile loading the executable.\n" 81 " -o <output> - Print the results to file <output>.\n" 82 " -r, --recorded - Don't profile, but evaluate a recorded kernel profile\n" 83 " data.\n" 84 " -s <depth> - Number of return address samples to take from the\n" 85 " caller stack per tick. If the topmost address doesn't\n" 86 " hit a known image, the next address will be matched\n" 87 " (and so on).\n" 88 " -S - Don't output results for individual threads, but\n" 89 " produce a combined output at the end.\n" 90 " -v <directory> - Create valgrind/callgrind output. <directory> is the\n" 91 " directory where to put the output files.\n" 92 ; 93 94 95 Options gOptions; 96 97 static bool sCaughtDeadlySignal = false; 98 99 100 class ThreadManager : private ProfiledEntity { 101 public: 102 ThreadManager(port_id debuggerPort) 103 : 104 fTeams(20), 105 fThreads(20, true), 106 fKernelTeam(NULL), 107 fDebuggerPort(debuggerPort), 108 fSummaryProfileResult(NULL) 109 { 110 } 111 112 virtual ~ThreadManager() 113 { 114 // release image references 115 for (ImageMap::iterator it = fImages.begin(); it != fImages.end(); ++it) 116 it->second->RemoveReference(); 117 118 if (fSummaryProfileResult != NULL) 119 fSummaryProfileResult->ReleaseReference(); 120 121 for (int32 i = 0; Team* team = fTeams.ItemAt(i); i++) 122 team->ReleaseReference(); 123 } 124 125 status_t Init() 126 { 127 if (!gOptions.summary_result) 128 return B_OK; 129 130 ProfileResult* profileResult; 131 status_t error = _CreateProfileResult(this, profileResult); 132 if (error != B_OK) 133 return error; 134 135 BReference<ProfileResult> profileResultReference(profileResult, true); 136 137 fSummaryProfileResult = new(std::nothrow) SummaryProfileResult( 138 profileResult); 139 if (fSummaryProfileResult == NULL) 140 return B_NO_MEMORY; 141 142 return fSummaryProfileResult->Init(profileResult->Entity()); 143 } 144 145 status_t AddTeam(team_id teamID, Team** _team = NULL) 146 { 147 return _AddTeam(teamID, NULL, _team); 148 } 149 150 status_t AddTeam(system_profiler_team_added* addedInfo, Team** _team = NULL) 151 { 152 return _AddTeam(addedInfo->team, addedInfo, _team); 153 } 154 155 status_t AddThread(thread_id threadID) 156 { 157 thread_info threadInfo; 158 status_t error = get_thread_info(threadID, &threadInfo); 159 if (error != B_OK) 160 return error; 161 162 return AddThread(threadInfo.team, threadID, threadInfo.name); 163 } 164 165 status_t AddThread(team_id teamID, thread_id threadID, const char* name) 166 { 167 if (FindThread(threadID) != NULL) 168 return B_BAD_VALUE; 169 170 Team* team = FindTeam(teamID); 171 if (team == NULL) 172 return B_BAD_TEAM_ID; 173 174 Thread* thread = new(std::nothrow) Thread(threadID, name, team); 175 if (thread == NULL) 176 return B_NO_MEMORY; 177 178 status_t error = _CreateThreadProfileResult(thread); 179 if (error != B_OK) { 180 delete thread; 181 return error; 182 } 183 184 error = team->InitThread(thread); 185 if (error != B_OK) { 186 delete thread; 187 return error; 188 } 189 190 fThreads.AddItem(thread); 191 return B_OK; 192 } 193 194 void RemoveTeam(team_id teamID) 195 { 196 if (Team* team = FindTeam(teamID)) { 197 if (team == fKernelTeam) 198 fKernelTeam = NULL; 199 fTeams.RemoveItem(team); 200 team->ReleaseReference(); 201 } 202 } 203 204 void RemoveThread(thread_id threadID) 205 { 206 if (Thread* thread = FindThread(threadID)) { 207 thread->GetTeam()->RemoveThread(thread); 208 fThreads.RemoveItem(thread, true); 209 } 210 } 211 212 Team* FindTeam(team_id teamID) const 213 { 214 for (int32 i = 0; Team* team = fTeams.ItemAt(i); i++) { 215 if (team->ID() == teamID) 216 return team; 217 } 218 return NULL; 219 } 220 221 Thread* FindThread(thread_id threadID) const 222 { 223 for (int32 i = 0; Thread* thread = fThreads.ItemAt(i); i++) { 224 if (thread->ID() == threadID) 225 return thread; 226 } 227 return NULL; 228 } 229 230 int32 CountThreads() const 231 { 232 return fThreads.CountItems(); 233 } 234 235 Thread* ThreadAt(int32 index) const 236 { 237 return fThreads.ItemAt(index); 238 } 239 240 status_t AddImage(team_id teamID, const image_info& imageInfo, int32 event) 241 { 242 // get a shared image 243 SharedImage* sharedImage = NULL; 244 status_t error = _GetSharedImage(teamID, imageInfo, &sharedImage); 245 if (error != B_OK) 246 return error; 247 248 if (teamID == B_SYSTEM_TEAM) { 249 // a kernel image -- add it to all teams 250 int32 count = fTeams.CountItems(); 251 for (int32 i = 0; i < count; i++) { 252 fTeams.ItemAt(i)->AddImage(sharedImage, imageInfo, teamID, 253 event); 254 } 255 } 256 257 // a userland team image -- add it to that image 258 if (Team* team = FindTeam(teamID)) 259 return team->AddImage(sharedImage, imageInfo, teamID, event); 260 261 return B_BAD_TEAM_ID; 262 } 263 264 void RemoveImage(team_id teamID, image_id imageID, int32 event) 265 { 266 if (teamID == B_SYSTEM_TEAM) { 267 // a kernel image -- remove it from all teams 268 int32 count = fTeams.CountItems(); 269 for (int32 i = 0; i < count; i++) 270 fTeams.ItemAt(i)->RemoveImage(imageID, event); 271 } else { 272 // a userland team image -- add it to that image 273 if (Team* team = FindTeam(teamID)) 274 team->RemoveImage(imageID, event); 275 } 276 } 277 278 void PrintSummaryResults() 279 { 280 if (fSummaryProfileResult != NULL) 281 fSummaryProfileResult->PrintSummaryResults(); 282 } 283 284 private: 285 virtual int32 EntityID() const 286 { 287 return 1; 288 } 289 290 virtual const char* EntityName() const 291 { 292 return "all"; 293 } 294 295 virtual const char* EntityType() const 296 { 297 return "summary"; 298 } 299 300 private: 301 status_t _AddTeam(team_id teamID, system_profiler_team_added* addedInfo, 302 Team** _team = NULL) 303 { 304 if (FindTeam(teamID) != NULL) 305 return B_BAD_VALUE; 306 307 Team* team = new(std::nothrow) Team; 308 if (team == NULL) 309 return B_NO_MEMORY; 310 311 status_t error = addedInfo != NULL 312 ? _InitUndebuggedTeam(team, addedInfo) 313 : _InitDebuggedTeam(team, teamID); 314 if (error != B_OK) { 315 team->ReleaseReference(); 316 return error; 317 } 318 319 fTeams.AddItem(team); 320 321 if (teamID == B_SYSTEM_TEAM) 322 fKernelTeam = team; 323 324 if (_team != NULL) 325 *_team = team; 326 327 return B_OK; 328 } 329 330 status_t _InitDebuggedTeam(Team* team, team_id teamID) 331 { 332 // init the team 333 status_t error = team->Init(teamID, fDebuggerPort); 334 if (error != B_OK) 335 return error; 336 337 // add the team's images 338 error = _LoadTeamImages(team, teamID); 339 if (error != B_OK) 340 return error; 341 342 // add the kernel images 343 return _LoadTeamImages(team, B_SYSTEM_TEAM); 344 } 345 346 status_t _InitUndebuggedTeam(Team* team, 347 system_profiler_team_added* addedInfo) 348 { 349 // init the team 350 status_t error = team->Init(addedInfo); 351 if (error != B_OK) 352 return error; 353 354 // in case of a user team, add the kernel images 355 if (team->ID() == B_SYSTEM_TEAM || fKernelTeam == NULL) 356 return B_OK; 357 358 const BObjectList<Image>& kernelImages = fKernelTeam->Images(); 359 int32 count = kernelImages.CountItems(); 360 for (int32 i = 0; i < count; i++) { 361 SharedImage* sharedImage = kernelImages.ItemAt(i)->GetSharedImage(); 362 team->AddImage(sharedImage, sharedImage->Info(), B_SYSTEM_TEAM, 0); 363 } 364 365 return B_OK; 366 } 367 368 status_t _LoadTeamImages(Team* team, team_id teamID) 369 { 370 // iterate through the team's images and collect the symbols 371 image_info imageInfo; 372 int32 cookie = 0; 373 while (get_next_image_info(teamID, &cookie, &imageInfo) == B_OK) { 374 // get a shared image 375 SharedImage* sharedImage; 376 status_t error = _GetSharedImage(teamID, imageInfo, &sharedImage); 377 if (error != B_OK) 378 return error; 379 380 // add the image to the team 381 error = team->AddImage(sharedImage, imageInfo, teamID, 0); 382 if (error != B_OK) 383 return error; 384 } 385 386 return B_OK; 387 } 388 389 status_t _CreateThreadProfileResult(Thread* thread) 390 { 391 if (fSummaryProfileResult != NULL) { 392 thread->SetProfileResult(fSummaryProfileResult); 393 return B_OK; 394 } 395 396 ProfileResult* profileResult; 397 status_t error = _CreateProfileResult(thread, profileResult); 398 if (error != B_OK) 399 return error; 400 401 thread->SetProfileResult(profileResult); 402 403 return B_OK; 404 } 405 406 status_t _CreateProfileResult(ProfiledEntity* profiledEntity, 407 ProfileResult*& _profileResult) 408 { 409 ProfileResult* profileResult; 410 411 if (gOptions.callgrind_directory != NULL) 412 profileResult = new(std::nothrow) CallgrindProfileResult; 413 else if (gOptions.analyze_full_stack) 414 profileResult = new(std::nothrow) InclusiveProfileResult; 415 else 416 profileResult = new(std::nothrow) ExclusiveProfileResult; 417 418 if (profileResult == NULL) 419 return B_NO_MEMORY; 420 421 BReference<ProfileResult> profileResultReference(profileResult, true); 422 423 status_t error = profileResult->Init(profiledEntity); 424 if (error != B_OK) 425 return error; 426 427 _profileResult = profileResultReference.Detach(); 428 return B_OK; 429 } 430 431 status_t _GetSharedImage(team_id teamID, const image_info& imageInfo, 432 SharedImage** _sharedImage) 433 { 434 // check whether the image has already been loaded 435 ImageMap::iterator it = fImages.find(imageInfo.name); 436 if (it != fImages.end()) { 437 *_sharedImage = it->second; 438 return B_OK; 439 } 440 441 // create the shared image 442 SharedImage* sharedImage = new(std::nothrow) SharedImage; 443 if (sharedImage == NULL) 444 return B_NO_MEMORY; 445 ObjectDeleter<SharedImage> imageDeleter(sharedImage); 446 447 // load the symbols 448 status_t error; 449 if (teamID == B_SYSTEM_TEAM) { 450 error = sharedImage->Init(teamID, imageInfo.id); 451 if (error != B_OK) { 452 // The image has obviously been unloaded already, try to get 453 // it by path. 454 BString name = imageInfo.name; 455 if (name.FindFirst('/') == -1) { 456 // modules without a path are likely to be boot modules 457 name.Prepend("/system/add-ons/kernel/boot/"); 458 } 459 460 error = sharedImage->Init(name.String()); 461 } 462 } else 463 error = sharedImage->Init(imageInfo.name); 464 if (error != B_OK) 465 return error; 466 467 try { 468 fImages[sharedImage->Name()] = sharedImage; 469 } catch (std::bad_alloc) { 470 return B_NO_MEMORY; 471 } 472 473 imageDeleter.Detach(); 474 *_sharedImage = sharedImage; 475 return B_OK; 476 } 477 478 private: 479 typedef std::map<std::string, SharedImage*> ImageMap; 480 481 private: 482 BObjectList<Team> fTeams; 483 BObjectList<Thread> fThreads; 484 ImageMap fImages; 485 Team* fKernelTeam; 486 port_id fDebuggerPort; 487 SummaryProfileResult* fSummaryProfileResult; 488 }; 489 490 491 static void 492 print_usage_and_exit(bool error) 493 { 494 fprintf(error ? stderr : stdout, kUsage, __progname); 495 exit(error ? 1 : 0); 496 } 497 498 499 /* 500 // get_id 501 static bool 502 get_id(const char *str, int32 &id) 503 { 504 int32 len = strlen(str); 505 for (int32 i = 0; i < len; i++) { 506 if (!isdigit(str[i])) 507 return false; 508 } 509 510 id = atol(str); 511 return true; 512 } 513 */ 514 515 516 static bool 517 process_event_buffer(ThreadManager& threadManager, uint8* buffer, 518 size_t bufferSize, team_id mainTeam) 519 { 520 //printf("process_event_buffer(%p, %lu)\n", buffer, bufferSize); 521 const uint8* bufferEnd = buffer + bufferSize; 522 523 while (buffer < bufferEnd) { 524 system_profiler_event_header* header 525 = (system_profiler_event_header*)buffer; 526 527 buffer += sizeof(system_profiler_event_header); 528 529 switch (header->event) { 530 case B_SYSTEM_PROFILER_TEAM_ADDED: 531 { 532 system_profiler_team_added* event 533 = (system_profiler_team_added*)buffer; 534 535 threadManager.AddTeam(event); 536 break; 537 } 538 539 case B_SYSTEM_PROFILER_TEAM_REMOVED: 540 { 541 system_profiler_team_removed* event 542 = (system_profiler_team_removed*)buffer; 543 544 threadManager.RemoveTeam(event->team); 545 546 // quit, if the main team we're interested in is gone 547 if (mainTeam >= 0 && event->team == mainTeam) 548 return true; 549 550 break; 551 } 552 553 case B_SYSTEM_PROFILER_TEAM_EXEC: 554 { 555 system_profiler_team_exec* event 556 = (system_profiler_team_exec*)buffer; 557 558 if (Team* team = threadManager.FindTeam(event->team)) 559 team->Exec(0, event->args, event->thread_name); 560 break; 561 } 562 563 case B_SYSTEM_PROFILER_THREAD_ADDED: 564 { 565 system_profiler_thread_added* event 566 = (system_profiler_thread_added*)buffer; 567 568 threadManager.AddThread(event->team, event->thread, 569 event->name); 570 break; 571 } 572 573 case B_SYSTEM_PROFILER_THREAD_REMOVED: 574 { 575 system_profiler_thread_removed* event 576 = (system_profiler_thread_removed*)buffer; 577 578 if (Thread* thread = threadManager.FindThread(event->thread)) { 579 thread->PrintResults(); 580 threadManager.RemoveThread(event->thread); 581 } 582 break; 583 } 584 585 case B_SYSTEM_PROFILER_IMAGE_ADDED: 586 { 587 system_profiler_image_added* event 588 = (system_profiler_image_added*)buffer; 589 590 threadManager.AddImage(event->team, event->info, 0); 591 break; 592 } 593 594 case B_SYSTEM_PROFILER_IMAGE_REMOVED: 595 { 596 system_profiler_image_removed* event 597 = (system_profiler_image_removed*)buffer; 598 599 threadManager.RemoveImage(event->team, event->image, 0); 600 break; 601 } 602 603 case B_SYSTEM_PROFILER_SAMPLES: 604 { 605 system_profiler_samples* event 606 = (system_profiler_samples*)buffer; 607 608 Thread* thread = threadManager.FindThread(event->thread); 609 if (thread != NULL) { 610 thread->AddSamples(event->samples, 611 (addr_t*)(buffer + header->size) - event->samples); 612 } 613 614 break; 615 } 616 617 case B_SYSTEM_PROFILER_BUFFER_END: 618 { 619 // Marks the end of the ring buffer -- we need to ignore the 620 // remaining bytes. 621 return false; 622 } 623 } 624 625 buffer += header->size; 626 } 627 628 return false; 629 } 630 631 632 static void 633 signal_handler(int signal, void* data) 634 { 635 sCaughtDeadlySignal = true; 636 } 637 638 639 static void 640 profile_all(const char* const* programArgs, int programArgCount) 641 { 642 // Load the executable, if we have to. 643 thread_id threadID = -1; 644 if (programArgCount >= 1) { 645 threadID = load_program(programArgs, programArgCount, 646 gOptions.profile_loading); 647 if (threadID < 0) { 648 fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName, 649 programArgs[0], strerror(threadID)); 650 exit(1); 651 } 652 } 653 654 // install signal handlers so we can exit gracefully 655 struct sigaction action; 656 action.sa_handler = (sighandler_t)signal_handler; 657 sigemptyset(&action.sa_mask); 658 action.sa_userdata = NULL; 659 if (sigaction(SIGHUP, &action, NULL) < 0 660 || sigaction(SIGINT, &action, NULL) < 0 661 || sigaction(SIGQUIT, &action, NULL) < 0) { 662 fprintf(stderr, "%s: Failed to install signal handlers: %s\n", 663 kCommandName, strerror(errno)); 664 exit(1); 665 } 666 667 // create an area for the sample buffer 668 system_profiler_buffer_header* bufferHeader; 669 area_id area = create_area("profiling buffer", (void**)&bufferHeader, 670 B_ANY_ADDRESS, PROFILE_ALL_SAMPLE_AREA_SIZE, B_NO_LOCK, 671 B_READ_AREA | B_WRITE_AREA); 672 if (area < 0) { 673 fprintf(stderr, "%s: Failed to create sample area: %s\n", kCommandName, 674 strerror(area)); 675 exit(1); 676 } 677 678 uint8* bufferBase = (uint8*)(bufferHeader + 1); 679 size_t totalBufferSize = PROFILE_ALL_SAMPLE_AREA_SIZE 680 - (bufferBase - (uint8*)bufferHeader); 681 682 // create a thread manager 683 ThreadManager threadManager(-1); // TODO: We don't need a debugger port! 684 status_t error = threadManager.Init(); 685 if (error != B_OK) { 686 fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName, 687 strerror(error)); 688 exit(1); 689 } 690 691 // start profiling 692 system_profiler_parameters profilerParameters; 693 profilerParameters.buffer_area = area; 694 profilerParameters.flags = B_SYSTEM_PROFILER_TEAM_EVENTS 695 | B_SYSTEM_PROFILER_THREAD_EVENTS | B_SYSTEM_PROFILER_IMAGE_EVENTS 696 | B_SYSTEM_PROFILER_SAMPLING_EVENTS; 697 profilerParameters.interval = gOptions.interval; 698 profilerParameters.stack_depth = gOptions.stack_depth; 699 700 error = _kern_system_profiler_start(&profilerParameters); 701 if (error != B_OK) { 702 fprintf(stderr, "%s: Failed to start profiling: %s\n", kCommandName, 703 strerror(error)); 704 exit(1); 705 } 706 707 // resume the loaded team, if we have one 708 if (threadID >= 0) 709 resume_thread(threadID); 710 711 // main event loop 712 while (true) { 713 // get the current buffer 714 size_t bufferStart = bufferHeader->start; 715 size_t bufferSize = bufferHeader->size; 716 uint8* buffer = bufferBase + bufferStart; 717 //printf("processing buffer of size %lu bytes\n", bufferSize); 718 719 bool quit; 720 if (bufferStart + bufferSize <= totalBufferSize) { 721 quit = process_event_buffer(threadManager, buffer, bufferSize, 722 threadID); 723 } else { 724 size_t remainingSize = bufferStart + bufferSize - totalBufferSize; 725 quit = process_event_buffer(threadManager, buffer, 726 bufferSize - remainingSize, threadID) 727 || process_event_buffer(threadManager, bufferBase, 728 remainingSize, threadID); 729 } 730 731 if (quit) 732 break; 733 734 // get next buffer 735 uint64 droppedEvents = 0; 736 error = _kern_system_profiler_next_buffer(bufferSize, &droppedEvents); 737 738 if (error != B_OK) { 739 if (error == B_INTERRUPTED) { 740 if (sCaughtDeadlySignal) 741 break; 742 continue; 743 } 744 745 fprintf(stderr, "%s: Failed to get next sample buffer: %s\n", 746 kCommandName, strerror(error)); 747 break; 748 } 749 } 750 751 // stop profiling 752 _kern_system_profiler_stop(); 753 754 // print results 755 int32 threadCount = threadManager.CountThreads(); 756 for (int32 i = 0; i < threadCount; i++) { 757 Thread* thread = threadManager.ThreadAt(i); 758 thread->PrintResults(); 759 } 760 761 threadManager.PrintSummaryResults(); 762 } 763 764 765 static void 766 dump_recorded() 767 { 768 // retrieve recorded samples and parameters 769 system_profiler_parameters profilerParameters; 770 status_t error = _kern_system_profiler_recorded(&profilerParameters); 771 if (error != B_OK) { 772 fprintf(stderr, "%s: Failed to get recorded profiling buffer: %s\n", 773 kCommandName, strerror(error)); 774 exit(1); 775 } 776 777 // set global options to those of the profiler parameters 778 gOptions.interval = profilerParameters.interval; 779 gOptions.stack_depth = profilerParameters.stack_depth; 780 781 // create an area for the sample buffer 782 area_info info; 783 error = get_area_info(profilerParameters.buffer_area, &info); 784 if (error != B_OK) { 785 fprintf(stderr, "%s: Recorded profiling buffer invalid: %s\n", 786 kCommandName, strerror(error)); 787 exit(1); 788 } 789 790 system_profiler_buffer_header* bufferHeader 791 = (system_profiler_buffer_header*)info.address; 792 793 uint8* bufferBase = (uint8*)(bufferHeader + 1); 794 size_t totalBufferSize = info.size - (bufferBase - (uint8*)bufferHeader); 795 796 // create a thread manager 797 ThreadManager threadManager(-1); // TODO: We don't need a debugger port! 798 error = threadManager.Init(); 799 if (error != B_OK) { 800 fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName, 801 strerror(error)); 802 exit(1); 803 } 804 805 // get the current buffer 806 size_t bufferStart = bufferHeader->start; 807 size_t bufferSize = bufferHeader->size; 808 uint8* buffer = bufferBase + bufferStart; 809 810 if (bufferStart + bufferSize <= totalBufferSize) { 811 process_event_buffer(threadManager, buffer, bufferSize, -1); 812 } else { 813 size_t remainingSize = bufferStart + bufferSize - totalBufferSize; 814 if (!process_event_buffer(threadManager, buffer, 815 bufferSize - remainingSize, -1)) { 816 process_event_buffer(threadManager, bufferBase, remainingSize, -1); 817 } 818 } 819 820 // print results 821 int32 threadCount = threadManager.CountThreads(); 822 for (int32 i = 0; i < threadCount; i++) { 823 Thread* thread = threadManager.ThreadAt(i); 824 thread->PrintResults(); 825 } 826 827 threadManager.PrintSummaryResults(); 828 } 829 830 831 static void 832 profile_single(const char* const* programArgs, int programArgCount) 833 { 834 // get thread/team to be debugged 835 thread_id threadID = load_program(programArgs, programArgCount, 836 gOptions.profile_loading); 837 if (threadID < 0) { 838 fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName, 839 programArgs[0], strerror(threadID)); 840 exit(1); 841 } 842 843 // get the team ID 844 thread_info threadInfo; 845 status_t error = get_thread_info(threadID, &threadInfo); 846 if (error != B_OK) { 847 fprintf(stderr, "%s: Failed to get info for thread %ld: %s\n", 848 kCommandName, threadID, strerror(error)); 849 exit(1); 850 } 851 team_id teamID = threadInfo.team; 852 853 // create a debugger port 854 port_id debuggerPort = create_port(10, "debugger port"); 855 if (debuggerPort < 0) { 856 fprintf(stderr, "%s: Failed to create debugger port: %s\n", 857 kCommandName, strerror(debuggerPort)); 858 exit(1); 859 } 860 861 // add team and thread to the thread manager 862 ThreadManager threadManager(debuggerPort); 863 error = threadManager.Init(); 864 if (error != B_OK) { 865 fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName, 866 strerror(error)); 867 exit(1); 868 } 869 870 if (threadManager.AddTeam(teamID) != B_OK 871 || threadManager.AddThread(threadID) != B_OK) { 872 exit(1); 873 } 874 875 // debug loop 876 while (true) { 877 debug_debugger_message_data message; 878 bool quitLoop = false; 879 int32 code; 880 ssize_t messageSize = read_port(debuggerPort, &code, &message, 881 sizeof(message)); 882 883 if (messageSize < 0) { 884 if (messageSize == B_INTERRUPTED) 885 continue; 886 887 fprintf(stderr, "%s: Reading from debugger port failed: %s\n", 888 kCommandName, strerror(messageSize)); 889 exit(1); 890 } 891 892 switch (code) { 893 case B_DEBUGGER_MESSAGE_PROFILER_UPDATE: 894 { 895 Thread* thread = threadManager.FindThread( 896 message.profiler_update.origin.thread); 897 if (thread == NULL) 898 break; 899 900 thread->AddSamples(message.profiler_update.sample_count, 901 message.profiler_update.dropped_ticks, 902 message.profiler_update.stack_depth, 903 message.profiler_update.variable_stack_depth, 904 message.profiler_update.image_event); 905 906 if (message.profiler_update.stopped) { 907 thread->PrintResults(); 908 threadManager.RemoveThread(thread->ID()); 909 } 910 break; 911 } 912 913 case B_DEBUGGER_MESSAGE_TEAM_CREATED: 914 if (!gOptions.profile_teams) 915 break; 916 917 if (threadManager.AddTeam(message.team_created.new_team) 918 == B_OK) { 919 threadManager.AddThread(message.team_created.new_team); 920 } 921 break; 922 case B_DEBUGGER_MESSAGE_TEAM_DELETED: 923 // a debugged team is gone -- quit, if it is our team 924 threadManager.RemoveTeam(message.origin.team); 925 quitLoop = message.origin.team == teamID; 926 break; 927 case B_DEBUGGER_MESSAGE_TEAM_EXEC: 928 if (Team* team = threadManager.FindTeam(message.origin.team)) { 929 team_info teamInfo; 930 thread_info threadInfo; 931 if (get_team_info(message.origin.team, &teamInfo) == B_OK 932 && get_thread_info(message.origin.team, &threadInfo) 933 == B_OK) { 934 team->Exec(message.team_exec.image_event, teamInfo.args, 935 threadInfo.name); 936 } 937 } 938 break; 939 940 case B_DEBUGGER_MESSAGE_THREAD_CREATED: 941 if (!gOptions.profile_threads) 942 break; 943 944 threadManager.AddThread(message.thread_created.new_thread); 945 break; 946 case B_DEBUGGER_MESSAGE_THREAD_DELETED: 947 threadManager.RemoveThread(message.origin.thread); 948 break; 949 950 case B_DEBUGGER_MESSAGE_IMAGE_CREATED: 951 threadManager.AddImage(message.origin.team, 952 message.image_created.info, 953 message.image_created.image_event); 954 break; 955 case B_DEBUGGER_MESSAGE_IMAGE_DELETED: 956 threadManager.RemoveImage(message.origin.team, 957 message.image_deleted.info.id, 958 message.image_deleted.image_event); 959 break; 960 961 case B_DEBUGGER_MESSAGE_POST_SYSCALL: 962 case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED: 963 case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED: 964 case B_DEBUGGER_MESSAGE_DEBUGGER_CALL: 965 case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT: 966 case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT: 967 case B_DEBUGGER_MESSAGE_SINGLE_STEP: 968 case B_DEBUGGER_MESSAGE_PRE_SYSCALL: 969 case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED: 970 break; 971 } 972 973 if (quitLoop) 974 break; 975 976 // tell the thread to continue (only when there is a thread and the 977 // message was synchronous) 978 if (message.origin.thread >= 0 && message.origin.nub_port >= 0) 979 continue_thread(message.origin.nub_port, message.origin.thread); 980 } 981 982 // prints summary results 983 threadManager.PrintSummaryResults(); 984 } 985 986 987 int 988 main(int argc, const char* const* argv) 989 { 990 int32 stackDepth = 0; 991 bool dumpRecorded = false; 992 const char* outputFile = NULL; 993 994 while (true) { 995 static struct option sLongOptions[] = { 996 { "all", no_argument, 0, 'a' }, 997 { "help", no_argument, 0, 'h' }, 998 { "recorded", no_argument, 0, 'r' }, 999 { 0, 0, 0, 0 } 1000 }; 1001 1002 opterr = 0; // don't print errors 1003 int c = getopt_long(argc, (char**)argv, "+acCfhi:klo:rs:Sv:", 1004 sLongOptions, NULL); 1005 if (c == -1) 1006 break; 1007 1008 switch (c) { 1009 case 'a': 1010 gOptions.profile_all = true; 1011 break; 1012 case 'c': 1013 gOptions.profile_threads = false; 1014 break; 1015 case 'C': 1016 gOptions.profile_teams = false; 1017 break; 1018 case 'f': 1019 gOptions.stack_depth = 64; 1020 gOptions.analyze_full_stack = true; 1021 break; 1022 case 'h': 1023 print_usage_and_exit(false); 1024 break; 1025 case 'i': 1026 gOptions.interval = atol(optarg); 1027 break; 1028 case 'k': 1029 gOptions.profile_kernel = false; 1030 break; 1031 case 'l': 1032 gOptions.profile_loading = true; 1033 break; 1034 case 'o': 1035 outputFile = optarg; 1036 break; 1037 case 'r': 1038 dumpRecorded = true; 1039 break; 1040 case 's': 1041 stackDepth = atol(optarg); 1042 break; 1043 case 'S': 1044 gOptions.summary_result = true; 1045 break; 1046 case 'v': 1047 gOptions.callgrind_directory = optarg; 1048 gOptions.analyze_full_stack = true; 1049 gOptions.stack_depth = 64; 1050 break; 1051 default: 1052 print_usage_and_exit(true); 1053 break; 1054 } 1055 } 1056 1057 if ((!gOptions.profile_all && !dumpRecorded && optind >= argc) 1058 || (dumpRecorded && optind != argc)) 1059 print_usage_and_exit(true); 1060 1061 if (stackDepth != 0) 1062 gOptions.stack_depth = stackDepth; 1063 1064 if (outputFile != NULL) { 1065 gOptions.output = fopen(outputFile, "w+"); 1066 if (gOptions.output == NULL) { 1067 fprintf(stderr, "%s: Failed to open output file \"%s\": %s\n", 1068 kCommandName, outputFile, strerror(errno)); 1069 exit(1); 1070 } 1071 } else 1072 gOptions.output = stdout; 1073 1074 if (dumpRecorded) { 1075 dump_recorded(); 1076 return 0; 1077 } 1078 1079 const char* const* programArgs = argv + optind; 1080 int programArgCount = argc - optind; 1081 1082 if (gOptions.profile_all) { 1083 profile_all(programArgs, programArgCount); 1084 return 0; 1085 } 1086 1087 profile_single(programArgs, programArgCount); 1088 return 0; 1089 } 1090