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