2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
19 #include <system_profiler_defs.h>
21 #include <DebugEventStream.h>
23 #include "debug_utils.h"
26 #define SCHEDULING_RECORDING_AREA_SIZE (4 * 1024 * 1024)
28 #define DEBUG_EVENT_MASK \
29 (B_SYSTEM_PROFILER_TEAM_EVENTS | B_SYSTEM_PROFILER_THREAD_EVENTS \
30 | B_SYSTEM_PROFILER_SCHEDULING_EVENTS \
31 | B_SYSTEM_PROFILER_IO_SCHEDULING_EVENTS)
34 extern const char* __progname
;
35 const char* kCommandName
= __progname
;
38 static const char* kUsage
=
39 "Usage: %s [ <options> ] <output file> [ <command line> ]\n"
40 "Records thread scheduling information to a file for later analysis.\n"
41 "If a command line <command line> is given, recording starts right before\n"
42 "executing the command and stops when the respective team quits.\n"
45 " -l - When a command line is given: Start recording before\n"
46 " executable has been loaded.\n"
47 " -h, --help - Print this usage info.\n"
52 print_usage_and_exit(bool error
)
54 fprintf(error
? stderr
: stdout
, kUsage
, kCommandName
);
65 fCaughtDeadlySignal(false)
75 status_t
Init(const char* outputFile
)
78 status_t error
= fOutputFile
.SetTo(outputFile
,
79 B_READ_WRITE
| B_CREATE_FILE
| B_ERASE_FILE
);
81 fprintf(stderr
, "Error: Failed to open \"%s\": %s\n", outputFile
,
86 // create output stream
87 error
= fOutput
.SetTo(&fOutputFile
, 0, DEBUG_EVENT_MASK
);
89 fprintf(stderr
, "Error: Failed to initialize the output "
90 "stream: %s\n", strerror(error
));
97 void SetSkipLoading(bool skipLoading
)
99 fSkipLoading
= skipLoading
;
102 void Run(const char* const* programArgs
, int programArgCount
)
104 // Load the executable, if we have to.
105 thread_id threadID
= -1;
106 if (programArgCount
>= 1) {
107 threadID
= load_program(programArgs
, programArgCount
,
110 fprintf(stderr
, "%s: Failed to start `%s': %s\n", kCommandName
,
111 programArgs
[0], strerror(threadID
));
114 fMainTeam
= threadID
;
117 // install signal handlers so we can exit gracefully
118 struct sigaction action
;
119 action
.sa_handler
= (__sighandler_t
)_SignalHandler
;
121 sigemptyset(&action
.sa_mask
);
122 action
.sa_userdata
= this;
123 if (sigaction(SIGHUP
, &action
, NULL
) < 0
124 || sigaction(SIGINT
, &action
, NULL
) < 0
125 || sigaction(SIGQUIT
, &action
, NULL
) < 0) {
126 fprintf(stderr
, "%s: Failed to install signal handlers: %s\n",
127 kCommandName
, strerror(errno
));
131 // create an area for the sample buffer
132 system_profiler_buffer_header
* bufferHeader
;
133 area_id area
= create_area("profiling buffer", (void**)&bufferHeader
,
134 B_ANY_ADDRESS
, SCHEDULING_RECORDING_AREA_SIZE
, B_NO_LOCK
,
135 B_READ_AREA
| B_WRITE_AREA
);
137 fprintf(stderr
, "%s: Failed to create sample area: %s\n",
138 kCommandName
, strerror(area
));
142 uint8
* bufferBase
= (uint8
*)(bufferHeader
+ 1);
143 size_t totalBufferSize
= SCHEDULING_RECORDING_AREA_SIZE
144 - (bufferBase
- (uint8
*)bufferHeader
);
147 system_profiler_parameters profilerParameters
;
148 profilerParameters
.buffer_area
= area
;
149 profilerParameters
.flags
= DEBUG_EVENT_MASK
;
150 profilerParameters
.locking_lookup_size
= 64 * 1024;
152 status_t error
= _kern_system_profiler_start(&profilerParameters
);
154 fprintf(stderr
, "%s: Failed to start profiling: %s\n", kCommandName
,
159 // resume the loaded team, if we have one
161 resume_thread(threadID
);
165 // get the current buffer
166 size_t bufferStart
= bufferHeader
->start
;
167 size_t bufferSize
= bufferHeader
->size
;
168 uint8
* buffer
= bufferBase
+ bufferStart
;
169 //printf("processing buffer of size %lu bytes\n", bufferSize);
172 if (bufferStart
+ bufferSize
<= totalBufferSize
) {
173 quit
= _ProcessEventBuffer(buffer
, bufferSize
);
175 size_t remainingSize
= bufferStart
+ bufferSize
177 quit
= _ProcessEventBuffer(buffer
, bufferSize
- remainingSize
)
178 || _ProcessEventBuffer(bufferBase
, remainingSize
);
181 if (quit
|| fCaughtDeadlySignal
)
185 uint64 droppedEvents
= 0;
186 error
= _kern_system_profiler_next_buffer(bufferSize
,
190 if (error
== B_INTERRUPTED
)
193 fprintf(stderr
, "%s: Failed to get next sample buffer: %s\n",
194 kCommandName
, strerror(error
));
198 if (droppedEvents
> 0)
199 fprintf(stderr
, "%llu events dropped\n", droppedEvents
);
203 _kern_system_profiler_stop();
207 bool _ProcessEventBuffer(uint8
* buffer
, size_t bufferSize
)
209 //printf("_ProcessEventBuffer(%p, %lu)\n", buffer, bufferSize);
210 const uint8
* bufferStart
= buffer
;
211 const uint8
* bufferEnd
= buffer
+ bufferSize
;
212 size_t usableBufferSize
= bufferSize
;
215 while (buffer
< bufferEnd
) {
216 system_profiler_event_header
* header
217 = (system_profiler_event_header
*)buffer
;
219 buffer
+= sizeof(system_profiler_event_header
);
221 if (header
->event
== B_SYSTEM_PROFILER_BUFFER_END
) {
222 // Marks the end of the ring buffer -- we need to ignore the
224 usableBufferSize
= (uint8
*)header
- bufferStart
;
228 if (header
->event
== B_SYSTEM_PROFILER_TEAM_REMOVED
) {
229 system_profiler_team_removed
* event
230 = (system_profiler_team_removed
*)buffer
;
232 // quit, if the main team we're interested in is gone
233 if (fMainTeam
>= 0 && event
->team
== fMainTeam
) {
234 usableBufferSize
= buffer
+ header
->size
- bufferStart
;
240 buffer
+= header
->size
;
243 // write buffer to file
244 if (usableBufferSize
> 0) {
245 status_t error
= fOutput
.Write(bufferStart
, usableBufferSize
);
247 fprintf(stderr
, "%s: Failed to write buffer: %s\n",
248 kCommandName
, strerror(error
));
257 static void _SignalHandler(int signal
, void* data
)
259 Recorder
* self
= (Recorder
*)data
;
260 self
->fCaughtDeadlySignal
= true;
265 BDebugEventOutputStream fOutput
;
268 bool fCaughtDeadlySignal
;
273 main(int argc
, const char* const* argv
)
278 static struct option sLongOptions
[] = {
279 { "help", no_argument
, 0, 'h' },
283 opterr
= 0; // don't print errors
284 int c
= getopt_long(argc
, (char**)argv
, "+hl", sLongOptions
, NULL
);
290 print_usage_and_exit(false);
293 recorder
.SetSkipLoading(false);
297 print_usage_and_exit(true);
302 // Remaining arguments should be the output file and the optional command
305 print_usage_and_exit(true);
307 const char* outputFile
= argv
[optind
++];
308 const char* const* programArgs
= argv
+ optind
;
309 int programArgCount
= argc
- optind
;
311 // prepare for battle
312 if (recorder
.Init(outputFile
) != B_OK
)
316 recorder
.Run(programArgs
, programArgCount
);