RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / bin / debug / profile / profile.cpp
blob22f6338be3d4563147cbf3ce76c3911bb7976485
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 */
8 #include <ctype.h>
9 #include <errno.h>
10 #include <getopt.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
15 #include <algorithm>
16 #include <map>
17 #include <new>
18 #include <string>
20 #include <debugger.h>
21 #include <FindDirectory.h>
22 #include <OS.h>
23 #include <Path.h>
24 #include <String.h>
26 #include <syscalls.h>
27 #include <system_profiler_defs.h>
29 #include <AutoDeleter.h>
30 #include <debug_support.h>
31 #include <ObjectList.h>
32 #include <Referenceable.h>
34 #include <util/DoublyLinkedList.h>
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"
45 // size of the sample buffer area for system profiling
46 #define PROFILE_ALL_SAMPLE_AREA_SIZE (4 * 1024 * 1024)
49 extern const char* __progname;
50 const char* kCommandName = __progname;
53 class Image;
54 class Team;
55 class Thread;
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"
98 Options gOptions;
100 static bool sCaughtDeadlySignal = false;
103 class ThreadManager : private ProfiledEntity {
104 public:
105 ThreadManager(port_id debuggerPort)
107 fTeams(20),
108 fThreads(20, true),
109 fKernelTeam(NULL),
110 fDebuggerPort(debuggerPort),
111 fSummaryProfileResult(NULL)
115 virtual ~ThreadManager()
117 // release image references
118 for (ImageMap::iterator it = fImages.begin(); it != fImages.end(); ++it)
119 it->second->ReleaseReference();
121 if (fSummaryProfileResult != NULL)
122 fSummaryProfileResult->ReleaseReference();
124 for (int32 i = 0; Team* team = fTeams.ItemAt(i); i++)
125 team->ReleaseReference();
128 status_t Init()
130 if (!gOptions.summary_result)
131 return B_OK;
133 ProfileResult* profileResult;
134 status_t error = _CreateProfileResult(this, profileResult);
135 if (error != B_OK)
136 return error;
138 BReference<ProfileResult> profileResultReference(profileResult, true);
140 fSummaryProfileResult = new(std::nothrow) SummaryProfileResult(
141 profileResult);
142 if (fSummaryProfileResult == NULL)
143 return B_NO_MEMORY;
145 return fSummaryProfileResult->Init(profileResult->Entity());
148 status_t AddTeam(team_id teamID, Team** _team = NULL)
150 return _AddTeam(teamID, NULL, _team);
153 status_t AddTeam(system_profiler_team_added* addedInfo, Team** _team = NULL)
155 return _AddTeam(addedInfo->team, addedInfo, _team);
158 status_t AddThread(thread_id threadID)
160 thread_info threadInfo;
161 status_t error = get_thread_info(threadID, &threadInfo);
162 if (error != B_OK)
163 return error;
165 return AddThread(threadInfo.team, threadID, threadInfo.name);
168 status_t AddThread(team_id teamID, thread_id threadID, const char* name)
170 if (FindThread(threadID) != NULL)
171 return B_BAD_VALUE;
173 Team* team = FindTeam(teamID);
174 if (team == NULL)
175 return B_BAD_TEAM_ID;
177 Thread* thread = new(std::nothrow) Thread(threadID, name, team);
178 if (thread == NULL)
179 return B_NO_MEMORY;
181 status_t error = _CreateThreadProfileResult(thread);
182 if (error != B_OK) {
183 delete thread;
184 return error;
187 error = team->InitThread(thread);
188 if (error != B_OK) {
189 delete thread;
190 return error;
193 fThreads.AddItem(thread);
194 return B_OK;
197 void RemoveTeam(team_id teamID)
199 if (Team* team = FindTeam(teamID)) {
200 if (team == fKernelTeam)
201 fKernelTeam = NULL;
202 fTeams.RemoveItem(team);
203 team->ReleaseReference();
207 void RemoveThread(thread_id threadID)
209 if (Thread* thread = FindThread(threadID)) {
210 thread->GetTeam()->RemoveThread(thread);
211 fThreads.RemoveItem(thread, true);
215 Team* FindTeam(team_id teamID) const
217 for (int32 i = 0; Team* team = fTeams.ItemAt(i); i++) {
218 if (team->ID() == teamID)
219 return team;
221 return NULL;
224 Thread* FindThread(thread_id threadID) const
226 for (int32 i = 0; Thread* thread = fThreads.ItemAt(i); i++) {
227 if (thread->ID() == threadID)
228 return thread;
230 return NULL;
233 int32 CountThreads() const
235 return fThreads.CountItems();
238 Thread* ThreadAt(int32 index) const
240 return fThreads.ItemAt(index);
243 status_t AddImage(team_id teamID, const image_info& imageInfo, int32 event)
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;
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);
260 // a userland team image -- add it to that image
261 if (Team* team = FindTeam(teamID))
262 return team->AddImage(sharedImage, imageInfo, teamID, event);
264 return B_BAD_TEAM_ID;
267 void RemoveImage(team_id teamID, image_id imageID, int32 event)
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);
281 void PrintSummaryResults()
283 if (fSummaryProfileResult != NULL)
284 fSummaryProfileResult->PrintSummaryResults();
287 private:
288 virtual int32 EntityID() const
290 return 1;
293 virtual const char* EntityName() const
295 return "all";
298 virtual const char* EntityType() const
300 return "summary";
303 private:
304 status_t _AddTeam(team_id teamID, system_profiler_team_added* addedInfo,
305 Team** _team = NULL)
307 if (FindTeam(teamID) != NULL)
308 return B_BAD_VALUE;
310 Team* team = new(std::nothrow) Team;
311 if (team == NULL)
312 return B_NO_MEMORY;
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;
322 fTeams.AddItem(team);
324 if (teamID == B_SYSTEM_TEAM)
325 fKernelTeam = team;
327 if (_team != NULL)
328 *_team = team;
330 return B_OK;
333 status_t _InitDebuggedTeam(Team* team, team_id teamID)
335 // init the team
336 status_t error = team->Init(teamID, fDebuggerPort);
337 if (error != B_OK)
338 return error;
340 // add the team's images
341 error = _LoadTeamImages(team, teamID);
342 if (error != B_OK)
343 return error;
345 // add the kernel images
346 return _LoadTeamImages(team, B_SYSTEM_TEAM);
349 status_t _InitUndebuggedTeam(Team* team,
350 system_profiler_team_added* addedInfo)
352 // init the team
353 status_t error = team->Init(addedInfo);
354 if (error != B_OK)
355 return error;
357 // in case of a user team, add the kernel images
358 if (team->ID() == B_SYSTEM_TEAM || fKernelTeam == NULL)
359 return B_OK;
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);
368 return B_OK;
371 status_t _LoadTeamImages(Team* team, team_id teamID)
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;
383 // add the image to the team
384 error = team->AddImage(sharedImage, imageInfo, teamID, 0);
385 if (error != B_OK)
386 return error;
389 return B_OK;
392 status_t _CreateThreadProfileResult(Thread* thread)
394 if (fSummaryProfileResult != NULL) {
395 thread->SetProfileResult(fSummaryProfileResult);
396 return B_OK;
399 ProfileResult* profileResult;
400 status_t error = _CreateProfileResult(thread, profileResult);
401 if (error != B_OK)
402 return error;
404 thread->SetProfileResult(profileResult);
406 return B_OK;
409 status_t _CreateProfileResult(ProfiledEntity* profiledEntity,
410 ProfileResult*& _profileResult)
412 ProfileResult* profileResult;
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;
421 if (profileResult == NULL)
422 return B_NO_MEMORY;
424 BReference<ProfileResult> profileResultReference(profileResult, true);
426 status_t error = profileResult->Init(profiledEntity);
427 if (error != B_OK)
428 return error;
430 _profileResult = profileResultReference.Detach();
431 return B_OK;
434 status_t _GetSharedImage(team_id teamID, const image_info& imageInfo,
435 SharedImage** _sharedImage)
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;
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);
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;
469 error = sharedImage->Init(name.String());
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;
478 try {
479 fImages[sharedImage->Name()] = sharedImage;
480 } catch (std::bad_alloc) {
481 return B_NO_MEMORY;
484 imageDeleter.Detach();
485 *_sharedImage = sharedImage;
486 return B_OK;
489 private:
490 typedef std::map<std::string, SharedImage*> ImageMap;
492 private:
493 BObjectList<Team> fTeams;
494 BObjectList<Thread> fThreads;
495 ImageMap fImages;
496 Team* fKernelTeam;
497 port_id fDebuggerPort;
498 SummaryProfileResult* fSummaryProfileResult;
502 static void
503 print_usage_and_exit(bool error)
505 fprintf(error ? stderr : stdout, kUsage, __progname);
506 exit(error ? 1 : 0);
511 // get_id
512 static bool
513 get_id(const char *str, int32 &id)
515 int32 len = strlen(str);
516 for (int32 i = 0; i < len; i++) {
517 if (!isdigit(str[i]))
518 return false;
521 id = atol(str);
522 return true;
527 static bool
528 process_event_buffer(ThreadManager& threadManager, uint8* buffer,
529 size_t bufferSize, team_id mainTeam)
531 //printf("process_event_buffer(%p, %lu)\n", buffer, bufferSize);
532 const uint8* bufferEnd = buffer + bufferSize;
534 while (buffer < bufferEnd) {
535 system_profiler_event_header* header
536 = (system_profiler_event_header*)buffer;
538 buffer += sizeof(system_profiler_event_header);
540 switch (header->event) {
541 case B_SYSTEM_PROFILER_TEAM_ADDED:
543 system_profiler_team_added* event
544 = (system_profiler_team_added*)buffer;
546 if (threadManager.AddTeam(event) != B_OK)
547 exit(1);
548 break;
551 case B_SYSTEM_PROFILER_TEAM_REMOVED:
553 system_profiler_team_removed* event
554 = (system_profiler_team_removed*)buffer;
556 threadManager.RemoveTeam(event->team);
558 // quit, if the main team we're interested in is gone
559 if (mainTeam >= 0 && event->team == mainTeam)
560 return true;
562 break;
565 case B_SYSTEM_PROFILER_TEAM_EXEC:
567 system_profiler_team_exec* event
568 = (system_profiler_team_exec*)buffer;
570 if (Team* team = threadManager.FindTeam(event->team))
571 team->Exec(0, event->args, event->thread_name);
572 break;
575 case B_SYSTEM_PROFILER_THREAD_ADDED:
577 system_profiler_thread_added* event
578 = (system_profiler_thread_added*)buffer;
580 if (threadManager.AddThread(event->team, event->thread,
581 event->name) != B_OK) {
582 exit(1);
584 break;
587 case B_SYSTEM_PROFILER_THREAD_REMOVED:
589 system_profiler_thread_removed* event
590 = (system_profiler_thread_removed*)buffer;
592 if (Thread* thread = threadManager.FindThread(event->thread)) {
593 thread->PrintResults();
594 threadManager.RemoveThread(event->thread);
596 break;
599 case B_SYSTEM_PROFILER_IMAGE_ADDED:
601 system_profiler_image_added* event
602 = (system_profiler_image_added*)buffer;
604 threadManager.AddImage(event->team, event->info, 0);
605 break;
608 case B_SYSTEM_PROFILER_IMAGE_REMOVED:
610 system_profiler_image_removed* event
611 = (system_profiler_image_removed*)buffer;
613 threadManager.RemoveImage(event->team, event->image, 0);
614 break;
617 case B_SYSTEM_PROFILER_SAMPLES:
619 system_profiler_samples* event
620 = (system_profiler_samples*)buffer;
622 Thread* thread = threadManager.FindThread(event->thread);
623 if (thread != NULL) {
624 thread->AddSamples(event->samples,
625 (addr_t*)(buffer + header->size) - event->samples);
628 break;
631 case B_SYSTEM_PROFILER_BUFFER_END:
633 // Marks the end of the ring buffer -- we need to ignore the
634 // remaining bytes.
635 return false;
639 buffer += header->size;
642 return false;
646 static void
647 signal_handler(int signal, void* data)
649 sCaughtDeadlySignal = true;
653 static void
654 profile_all(const char* const* programArgs, int programArgCount)
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);
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);
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);
692 uint8* bufferBase = (uint8*)(bufferHeader + 1);
693 size_t totalBufferSize = PROFILE_ALL_SAMPLE_AREA_SIZE
694 - (bufferBase - (uint8*)bufferHeader);
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);
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;
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);
721 // resume the loaded team, if we have one
722 if (threadID >= 0)
723 resume_thread(threadID);
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);
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);
745 if (quit)
746 break;
748 // get next buffer
749 uint64 droppedEvents = 0;
750 error = _kern_system_profiler_next_buffer(bufferSize, &droppedEvents);
752 if (error != B_OK) {
753 if (error == B_INTERRUPTED) {
754 if (sCaughtDeadlySignal)
755 break;
756 continue;
759 fprintf(stderr, "%s: Failed to get next sample buffer: %s\n",
760 kCommandName, strerror(error));
761 break;
765 // stop profiling
766 _kern_system_profiler_stop();
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();
775 threadManager.PrintSummaryResults();
779 static void
780 dump_recorded()
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);
791 // set global options to those of the profiler parameters
792 gOptions.interval = profilerParameters.interval;
793 gOptions.stack_depth = profilerParameters.stack_depth;
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);
804 system_profiler_buffer_header* bufferHeader
805 = (system_profiler_buffer_header*)info.address;
807 uint8* bufferBase = (uint8*)(bufferHeader + 1);
808 size_t totalBufferSize = info.size - (bufferBase - (uint8*)bufferHeader);
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);
819 // get the current buffer
820 size_t bufferStart = bufferHeader->start;
821 size_t bufferSize = bufferHeader->size;
822 uint8* buffer = bufferBase + bufferStart;
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);
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();
841 threadManager.PrintSummaryResults();
845 static void
846 profile_single(const char* const* programArgs, int programArgCount)
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);
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);
865 team_id teamID = threadInfo.team;
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);
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);
884 if (threadManager.AddTeam(teamID) != B_OK
885 || threadManager.AddThread(threadID) != B_OK) {
886 exit(1);
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));
897 if (messageSize < 0) {
898 if (messageSize == B_INTERRUPTED)
899 continue;
901 fprintf(stderr, "%s: Reading from debugger port failed: %s\n",
902 kCommandName, strerror(messageSize));
903 exit(1);
906 switch (code) {
907 case B_DEBUGGER_MESSAGE_PROFILER_UPDATE:
909 Thread* thread = threadManager.FindThread(
910 message.profiler_update.origin.thread);
911 if (thread == NULL)
912 break;
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);
920 if (message.profiler_update.stopped) {
921 thread->PrintResults();
922 threadManager.RemoveThread(thread->ID());
924 break;
927 case B_DEBUGGER_MESSAGE_TEAM_CREATED:
928 if (!gOptions.profile_teams)
929 break;
931 if (threadManager.AddTeam(message.team_created.new_team)
932 == B_OK) {
933 threadManager.AddThread(message.team_created.new_team);
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);
952 break;
954 case B_DEBUGGER_MESSAGE_THREAD_CREATED:
955 if (!gOptions.profile_threads)
956 break;
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;
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;
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;
987 if (quitLoop)
988 break;
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);
996 // prints summary results
997 threadManager.PrintSummaryResults();
1002 main(int argc, const char* const* argv)
1004 int32 stackDepth = 0;
1005 bool dumpRecorded = false;
1006 const char* outputFile = NULL;
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 }
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;
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;
1071 if ((!gOptions.profile_all && !dumpRecorded && optind >= argc)
1072 || (dumpRecorded && optind != argc))
1073 print_usage_and_exit(true);
1075 if (stackDepth != 0)
1076 gOptions.stack_depth = stackDepth;
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);
1085 } else
1086 gOptions.output = stdout;
1088 if (dumpRecorded) {
1089 dump_recorded();
1090 return 0;
1093 const char* const* programArgs = argv + optind;
1094 int programArgCount = argc - optind;
1096 if (gOptions.profile_all) {
1097 profile_all(programArgs, programArgCount);
1098 return 0;
1101 profile_single(programArgs, programArgCount);
1102 return 0;