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