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.
21 #include <FindDirectory.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"
41 #include "SummaryProfileResult.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
;
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"
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"
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"
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"
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"
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"
100 static bool sCaughtDeadlySignal
= false;
103 class ThreadManager
: private ProfiledEntity
{
105 ThreadManager(port_id debuggerPort
)
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();
130 if (!gOptions
.summary_result
)
133 ProfileResult
* profileResult
;
134 status_t error
= _CreateProfileResult(this, profileResult
);
138 BReference
<ProfileResult
> profileResultReference(profileResult
, true);
140 fSummaryProfileResult
= new(std::nothrow
) SummaryProfileResult(
142 if (fSummaryProfileResult
== NULL
)
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
);
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
)
173 Team
* team
= FindTeam(teamID
);
175 return B_BAD_TEAM_ID
;
177 Thread
* thread
= new(std::nothrow
) Thread(threadID
, name
, team
);
181 status_t error
= _CreateThreadProfileResult(thread
);
187 error
= team
->InitThread(thread
);
193 fThreads
.AddItem(thread
);
197 void RemoveTeam(team_id teamID
)
199 if (Team
* team
= FindTeam(teamID
)) {
200 if (team
== fKernelTeam
)
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
)
224 Thread
* FindThread(thread_id threadID
) const
226 for (int32 i
= 0; Thread
* thread
= fThreads
.ItemAt(i
); i
++) {
227 if (thread
->ID() == threadID
)
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
);
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
,
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
);
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();
288 virtual int32
EntityID() const
293 virtual const char* EntityName() const
298 virtual const char* EntityType() const
304 status_t
_AddTeam(team_id teamID
, system_profiler_team_added
* addedInfo
,
307 if (FindTeam(teamID
) != NULL
)
310 Team
* team
= new(std::nothrow
) Team
;
314 status_t error
= addedInfo
!= NULL
315 ? _InitUndebuggedTeam(team
, addedInfo
)
316 : _InitDebuggedTeam(team
, teamID
);
318 team
->ReleaseReference();
322 fTeams
.AddItem(team
);
324 if (teamID
== B_SYSTEM_TEAM
)
333 status_t
_InitDebuggedTeam(Team
* team
, team_id teamID
)
336 status_t error
= team
->Init(teamID
, fDebuggerPort
);
340 // add the team's images
341 error
= _LoadTeamImages(team
, teamID
);
345 // add the kernel images
346 return _LoadTeamImages(team
, B_SYSTEM_TEAM
);
349 status_t
_InitUndebuggedTeam(Team
* team
,
350 system_profiler_team_added
* addedInfo
)
353 status_t error
= team
->Init(addedInfo
);
357 // in case of a user team, add the kernel images
358 if (team
->ID() == B_SYSTEM_TEAM
|| fKernelTeam
== NULL
)
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);
371 status_t
_LoadTeamImages(Team
* team
, team_id teamID
)
373 // iterate through the team's images and collect the symbols
374 image_info imageInfo
;
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
);
383 // add the image to the team
384 error
= team
->AddImage(sharedImage
, imageInfo
, teamID
, 0);
392 status_t
_CreateThreadProfileResult(Thread
* thread
)
394 if (fSummaryProfileResult
!= NULL
) {
395 thread
->SetProfileResult(fSummaryProfileResult
);
399 ProfileResult
* profileResult
;
400 status_t error
= _CreateProfileResult(thread
, profileResult
);
404 thread
->SetProfileResult(profileResult
);
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
;
419 profileResult
= new(std::nothrow
) ExclusiveProfileResult
;
421 if (profileResult
== NULL
)
424 BReference
<ProfileResult
> profileResultReference(profileResult
, true);
426 status_t error
= profileResult
->Init(profiledEntity
);
430 _profileResult
= profileResultReference
.Detach();
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
;
444 // create the shared image
445 SharedImage
* sharedImage
= new(std::nothrow
) SharedImage
;
446 if (sharedImage
== NULL
)
448 ObjectDeleter
<SharedImage
> imageDeleter(sharedImage
);
452 if (teamID
== B_SYSTEM_TEAM
) {
453 error
= sharedImage
->Init(teamID
, imageInfo
.id
);
455 // The image has obviously been unloaded already, try to get
457 BString name
= imageInfo
.name
;
458 if (name
.FindFirst('/') == -1) {
459 // modules without a path are likely to be boot modules
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
);
474 error
= sharedImage
->Init(imageInfo
.name
);
479 fImages
[sharedImage
->Name()] = sharedImage
;
480 } catch (std::bad_alloc
) {
484 imageDeleter
.Detach();
485 *_sharedImage
= sharedImage
;
490 typedef std::map
<std::string
, SharedImage
*> ImageMap
;
493 BObjectList
<Team
> fTeams
;
494 BObjectList
<Thread
> fThreads
;
497 port_id fDebuggerPort
;
498 SummaryProfileResult
* fSummaryProfileResult
;
503 print_usage_and_exit(bool error
)
505 fprintf(error
? stderr
: stdout
, kUsage
, __progname
);
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]))
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
)
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
)
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
);
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
) {
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
);
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);
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);
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
);
631 case B_SYSTEM_PROFILER_BUFFER_END
:
633 // Marks the end of the ring buffer -- we need to ignore the
639 buffer
+= header
->size
;
647 signal_handler(int signal
, void* data
)
649 sCaughtDeadlySignal
= true;
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
);
662 fprintf(stderr
, "%s: Failed to start `%s': %s\n", kCommandName
,
663 programArgs
[0], strerror(threadID
));
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
));
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
);
687 fprintf(stderr
, "%s: Failed to create sample area: %s\n", kCommandName
,
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();
700 fprintf(stderr
, "%s: Failed to init thread manager: %s\n", kCommandName
,
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
);
716 fprintf(stderr
, "%s: Failed to start profiling: %s\n", kCommandName
,
721 // resume the loaded team, if we have one
723 resume_thread(threadID
);
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);
734 if (bufferStart
+ bufferSize
<= totalBufferSize
) {
735 quit
= process_event_buffer(threadManager
, buffer
, bufferSize
,
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
);
749 uint64 droppedEvents
= 0;
750 error
= _kern_system_profiler_next_buffer(bufferSize
, &droppedEvents
);
753 if (error
== B_INTERRUPTED
) {
754 if (sCaughtDeadlySignal
)
759 fprintf(stderr
, "%s: Failed to get next sample buffer: %s\n",
760 kCommandName
, strerror(error
));
766 _kern_system_profiler_stop();
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();
782 // retrieve recorded samples and parameters
783 system_profiler_parameters profilerParameters
;
784 status_t error
= _kern_system_profiler_recorded(&profilerParameters
);
786 fprintf(stderr
, "%s: Failed to get recorded profiling buffer: %s\n",
787 kCommandName
, strerror(error
));
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
797 error
= get_area_info(profilerParameters
.buffer_area
, &info
);
799 fprintf(stderr
, "%s: Recorded profiling buffer invalid: %s\n",
800 kCommandName
, strerror(error
));
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();
814 fprintf(stderr
, "%s: Failed to init thread manager: %s\n", kCommandName
,
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);
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);
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();
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
);
852 fprintf(stderr
, "%s: Failed to start `%s': %s\n", kCommandName
,
853 programArgs
[0], strerror(threadID
));
858 thread_info threadInfo
;
859 status_t error
= get_thread_info(threadID
, &threadInfo
);
861 fprintf(stderr
, "%s: Failed to get info for thread %ld: %s\n",
862 kCommandName
, threadID
, strerror(error
));
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
));
875 // add team and thread to the thread manager
876 ThreadManager
threadManager(debuggerPort
);
877 error
= threadManager
.Init();
879 fprintf(stderr
, "%s: Failed to init thread manager: %s\n", kCommandName
,
884 if (threadManager
.AddTeam(teamID
) != B_OK
885 || threadManager
.AddThread(threadID
) != B_OK
) {
891 debug_debugger_message_data message
;
892 bool quitLoop
= false;
894 ssize_t messageSize
= read_port(debuggerPort
, &code
, &message
,
897 if (messageSize
< 0) {
898 if (messageSize
== B_INTERRUPTED
)
901 fprintf(stderr
, "%s: Reading from debugger port failed: %s\n",
902 kCommandName
, strerror(messageSize
));
907 case B_DEBUGGER_MESSAGE_PROFILER_UPDATE
:
909 Thread
* thread
= threadManager
.FindThread(
910 message
.profiler_update
.origin
.thread
);
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());
927 case B_DEBUGGER_MESSAGE_TEAM_CREATED
:
928 if (!gOptions
.profile_teams
)
931 if (threadManager
.AddTeam(message
.team_created
.new_team
)
933 threadManager
.AddThread(message
.team_created
.new_team
);
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
;
941 case B_DEBUGGER_MESSAGE_TEAM_EXEC
:
942 if (Team
* team
= threadManager
.FindTeam(message
.origin
.team
)) {
944 thread_info threadInfo
;
945 if (get_team_info(message
.origin
.team
, &teamInfo
) == B_OK
946 && get_thread_info(message
.origin
.team
, &threadInfo
)
948 team
->Exec(message
.team_exec
.image_event
, teamInfo
.args
,
954 case B_DEBUGGER_MESSAGE_THREAD_CREATED
:
955 if (!gOptions
.profile_threads
)
958 threadManager
.AddThread(message
.thread_created
.new_thread
);
960 case B_DEBUGGER_MESSAGE_THREAD_DELETED
:
961 threadManager
.RemoveThread(message
.origin
.thread
);
964 case B_DEBUGGER_MESSAGE_IMAGE_CREATED
:
965 threadManager
.AddImage(message
.origin
.team
,
966 message
.image_created
.info
,
967 message
.image_created
.image_event
);
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
);
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
:
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
;
1009 static struct option sLongOptions
[] = {
1010 { "all", no_argument
, 0, 'a' },
1011 { "help", no_argument
, 0, 'h' },
1012 { "recorded", no_argument
, 0, 'r' },
1016 opterr
= 0; // don't print errors
1017 int c
= getopt_long(argc
, (char**)argv
, "+acCfhi:klo:rs:Sv:",
1018 sLongOptions
, NULL
);
1024 gOptions
.profile_all
= true;
1027 gOptions
.profile_threads
= false;
1030 gOptions
.profile_teams
= false;
1033 gOptions
.stack_depth
= 64;
1034 gOptions
.analyze_full_stack
= true;
1037 print_usage_and_exit(false);
1040 gOptions
.interval
= atol(optarg
);
1043 gOptions
.profile_kernel
= false;
1046 gOptions
.profile_loading
= true;
1049 outputFile
= optarg
;
1052 dumpRecorded
= true;
1055 stackDepth
= atol(optarg
);
1058 gOptions
.summary_result
= true;
1061 gOptions
.callgrind_directory
= optarg
;
1062 gOptions
.analyze_full_stack
= true;
1063 gOptions
.stack_depth
= 64;
1066 print_usage_and_exit(true);
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
));
1086 gOptions
.output
= stdout
;
1093 const char* const* programArgs
= argv
+ optind
;
1094 int programArgCount
= argc
- optind
;
1096 if (gOptions
.profile_all
) {
1097 profile_all(programArgs
, programArgCount
);
1101 profile_single(programArgs
, programArgCount
);