RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / bin / debug / scheduling_recorder / scheduling_recorder.cpp
blob4b68d99c96a2631c5fbe8d9f6c464664b83fc4d1
1 /*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <getopt.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
16 #include <File.h>
18 #include <syscalls.h>
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"
43 "\n"
44 "Options:\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"
51 static void
52 print_usage_and_exit(bool error)
54 fprintf(error ? stderr : stdout, kUsage, kCommandName);
55 exit(error ? 1 : 0);
59 class Recorder {
60 public:
61 Recorder()
63 fMainTeam(-1),
64 fSkipLoading(true),
65 fCaughtDeadlySignal(false)
69 ~Recorder()
71 fOutput.Flush();
75 status_t Init(const char* outputFile)
77 // open file
78 status_t error = fOutputFile.SetTo(outputFile,
79 B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
80 if (error != B_OK) {
81 fprintf(stderr, "Error: Failed to open \"%s\": %s\n", outputFile,
82 strerror(error));
83 return error;
86 // create output stream
87 error = fOutput.SetTo(&fOutputFile, 0, DEBUG_EVENT_MASK);
88 if (error != B_OK) {
89 fprintf(stderr, "Error: Failed to initialize the output "
90 "stream: %s\n", strerror(error));
91 return error;
94 return B_OK;
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,
108 !fSkipLoading);
109 if (threadID < 0) {
110 fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName,
111 programArgs[0], strerror(threadID));
112 exit(1);
114 fMainTeam = threadID;
117 // install signal handlers so we can exit gracefully
118 struct sigaction action;
119 action.sa_handler = (__sighandler_t)_SignalHandler;
120 action.sa_flags = 0;
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));
128 exit(1);
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);
136 if (area < 0) {
137 fprintf(stderr, "%s: Failed to create sample area: %s\n",
138 kCommandName, strerror(area));
139 exit(1);
142 uint8* bufferBase = (uint8*)(bufferHeader + 1);
143 size_t totalBufferSize = SCHEDULING_RECORDING_AREA_SIZE
144 - (bufferBase - (uint8*)bufferHeader);
146 // start profiling
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);
153 if (error != B_OK) {
154 fprintf(stderr, "%s: Failed to start profiling: %s\n", kCommandName,
155 strerror(error));
156 exit(1);
159 // resume the loaded team, if we have one
160 if (threadID >= 0)
161 resume_thread(threadID);
163 // main event loop
164 while (true) {
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);
171 bool quit;
172 if (bufferStart + bufferSize <= totalBufferSize) {
173 quit = _ProcessEventBuffer(buffer, bufferSize);
174 } else {
175 size_t remainingSize = bufferStart + bufferSize
176 - totalBufferSize;
177 quit = _ProcessEventBuffer(buffer, bufferSize - remainingSize)
178 || _ProcessEventBuffer(bufferBase, remainingSize);
181 if (quit || fCaughtDeadlySignal)
182 break;
184 // get next buffer
185 uint64 droppedEvents = 0;
186 error = _kern_system_profiler_next_buffer(bufferSize,
187 &droppedEvents);
189 if (error != B_OK) {
190 if (error == B_INTERRUPTED)
191 continue;
193 fprintf(stderr, "%s: Failed to get next sample buffer: %s\n",
194 kCommandName, strerror(error));
195 break;
198 if (droppedEvents > 0)
199 fprintf(stderr, "%llu events dropped\n", droppedEvents);
202 // stop profiling
203 _kern_system_profiler_stop();
206 private:
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;
213 bool quit = false;
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
223 // remaining bytes.
224 usableBufferSize = (uint8*)header - bufferStart;
225 break;
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;
235 quit = true;
236 break;
240 buffer += header->size;
243 // write buffer to file
244 if (usableBufferSize > 0) {
245 status_t error = fOutput.Write(bufferStart, usableBufferSize);
246 if (error != B_OK) {
247 fprintf(stderr, "%s: Failed to write buffer: %s\n",
248 kCommandName, strerror(error));
249 quit = true;
253 return quit;
257 static void _SignalHandler(int signal, void* data)
259 Recorder* self = (Recorder*)data;
260 self->fCaughtDeadlySignal = true;
263 private:
264 BFile fOutputFile;
265 BDebugEventOutputStream fOutput;
266 team_id fMainTeam;
267 bool fSkipLoading;
268 bool fCaughtDeadlySignal;
273 main(int argc, const char* const* argv)
275 Recorder recorder;
277 while (true) {
278 static struct option sLongOptions[] = {
279 { "help", no_argument, 0, 'h' },
280 { 0, 0, 0, 0 }
283 opterr = 0; // don't print errors
284 int c = getopt_long(argc, (char**)argv, "+hl", sLongOptions, NULL);
285 if (c == -1)
286 break;
288 switch (c) {
289 case 'h':
290 print_usage_and_exit(false);
291 break;
292 case 'l':
293 recorder.SetSkipLoading(false);
294 break;
296 default:
297 print_usage_and_exit(true);
298 break;
302 // Remaining arguments should be the output file and the optional command
303 // line.
304 if (optind >= argc)
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)
313 exit(1);
315 // start the action
316 recorder.Run(programArgs, programArgCount);
318 return 0;