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