umount: getopt return value is int, not char
[minix.git] / commands / profile / profile.c
blobbdd4ed65a0f59eada494a4f5628a36464d7a709a
1 /* profile - profile operating system
3 * The profile command is used to control Statistical and Call Profiling.
4 * It writes the profiling data collected by the kernel to a file.
6 * Changes:
7 * 14 Aug, 2006 Created (Rogier Meurs)
8 */
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <sys/stat.h>
17 #undef SPROFILE
18 #define SPROFILE 1
19 #include <minix/profile.h>
21 #define EHELP 1
22 #define ESYNTAX 2
23 #define EMEM 3
24 #define EOUTFILE 4
25 #define EFREQ 5
26 #define EACTION 6
28 #define START 1
29 #define STOP 2
30 #define GET 3
31 #define RESET 4
33 #define SPROF (action==START||action==STOP)
34 #define CPROF (!SPROF)
35 #define DEF_OUTFILE_S "profile.stat.out"
36 #define DEF_OUTFILE_C "profile.call.out"
37 #define DEF_OUTFILE (SPROF?DEF_OUTFILE_S:DEF_OUTFILE_C)
38 #define NPIPE "/tmp/profile.npipe"
39 #define MIN_MEMSIZE 1
40 #define DEF_MEMSIZE 64
41 #define MIN_FREQ 3
42 #define MAX_FREQ 15
43 #define DEF_FREQ 6
44 #define BUFSIZE 1024
45 #define MB (1024*1024)
46 #define SYNCING "SYNC"
47 #define DEV_LOG "/dev/log"
49 int action = 0;
50 int mem_size = 0;
51 int mem_used = 0;
52 int freq = 0;
53 int intr_type = PROF_RTC;
54 char *outfile = "";
55 char *mem_ptr;
56 int outfile_fd, npipe_fd;
57 struct sprof_info_s sprof_info;
58 struct cprof_info_s cprof_info;
60 #define HASH_MOD 128
61 struct sproc {
62 endpoint_t ep;
63 char name[8];
64 struct sproc * next;
67 static struct sproc * proc_hash[HASH_MOD];
69 int handle_args(int argc, char *argv[]);
70 int start(void);
71 int stop(void);
72 int get(void);
73 int reset(void);
74 int create_named_pipe(void);
75 int alloc_mem(void);
76 int init_outfile(void);
77 int write_outfile(void);
78 int write_outfile_cprof(void);
79 int write_outfile_sprof(void);
80 void detach(void);
82 int main(int argc, char *argv[])
84 int res;
86 if ((res = handle_args(argc, argv))) {
87 switch(res) {
88 case ESYNTAX:
89 printf("Error in parameters.\n");
90 return 1;
91 break;
92 case EACTION:
93 printf("Specify one of start|stop|get|reset.\n");
94 return 1;
95 break;
96 case EMEM:
97 printf("Incorrect memory size.\n");
98 return 1;
99 break;
100 case EFREQ:
101 printf("Incorrect frequency.\n");
102 return 1;
103 break;
104 case EOUTFILE:
105 printf("Output filename missing.\n");
106 return 1;
107 break;
108 default:
109 break;
113 * Check the frequency when we know the intr type. Only selected values
114 * are correct for RTC
116 if (action == START && intr_type == PROF_RTC &&
117 (freq < MIN_FREQ || freq > MAX_FREQ)) {
118 printf("Incorrect frequency.\n");
119 return 1;
122 printf("Statistical Profiling:\n");
123 printf(" profile start [--rtc | --nmi] "
124 "[-m memsize] [-o outfile] [-f frequency]\n");
125 printf(" profile stop\n\n");
126 printf(" --rtc is default, --nmi allows kernel profiling\n");
127 printf("Call Profiling:\n");
128 printf(" profile get [-m memsize] [-o outfile]\n");
129 printf(" profile reset\n\n");
130 printf(" - memsize in MB, default: %u\n", DEF_MEMSIZE);
131 printf(" - default output file: profile.{stat|call}.out\n");
132 printf( " - sample frequencies for --rtc (default: %u):\n", DEF_FREQ);
133 printf(" 3 8192 Hz 10 64 Hz\n");
134 printf(" 4 4096 Hz 11 32 Hz\n");
135 printf(" 5 2048 Hz 12 16 Hz\n");
136 printf(" 6 1024 Hz 13 8 Hz\n");
137 printf(" 7 512 Hz 14 4 Hz\n");
138 printf(" 8 256 Hz 15 2 Hz\n");
139 printf(" 9 128 Hz\n\n");
140 printf("Use [sc]profalyze.pl to analyze output file.\n");
141 return 1;
144 switch(action) {
145 case START:
146 if (start()) return 1;
147 break;
148 case STOP:
149 if (stop()) return 1;
150 break;
151 case GET:
152 if (get()) return 1;
153 break;
154 case RESET:
155 if (reset()) return 1;
156 break;
157 default:
158 break;
160 return 0;
164 int handle_args(int argc, char *argv[])
166 while (--argc) {
167 ++argv;
169 if (strcmp(*argv, "-h") == 0 || strcmp(*argv, "help") == 0 ||
170 strcmp(*argv, "--help") == 0) {
171 return EHELP;
172 } else
173 if (strcmp(*argv, "-m") == 0) {
174 if (--argc == 0) return ESYNTAX;
175 if (sscanf(*++argv, "%u", &mem_size) != 1 ||
176 mem_size < MIN_MEMSIZE ) return EMEM;
177 } else
178 if (strcmp(*argv, "-f") == 0) {
179 if (--argc == 0) return ESYNTAX;
180 if (sscanf(*++argv, "%u", &freq) != 1)
181 return EFREQ;
182 } else
183 if (strcmp(*argv, "-o") == 0) {
184 if (--argc == 0) return ESYNTAX;
185 outfile = *++argv;
186 } else
187 if (strcmp(*argv, "--rtc") == 0) {
188 intr_type = PROF_RTC;
189 } else
190 if (strcmp(*argv, "--nmi") == 0) {
191 intr_type = PROF_NMI;
192 } else
193 if (strcmp(*argv, "start") == 0) {
194 if (action) return EACTION;
195 action = START;
196 } else
197 if (strcmp(*argv, "stop") == 0) {
198 if (action) return EACTION;
199 action = STOP;
200 } else
201 if (strcmp(*argv, "get") == 0) {
202 if (action) return EACTION;
203 action = GET;
204 } else
205 if (strcmp(*argv, "reset") == 0) {
206 if (action) return EACTION;
207 action = RESET;
211 /* No action specified. */
212 if (!action) return EHELP;
214 /* Init unspecified parameters. */
215 if (action == START || action == GET) {
216 if (strcmp(outfile, "") == 0) outfile = DEF_OUTFILE;
217 if (mem_size == 0) mem_size = DEF_MEMSIZE;
218 mem_size *= MB; /* mem_size in bytes */
220 if (action == START) {
221 mem_size -= mem_size % sizeof(struct sprof_sample); /* align to sample size */
222 if (freq == 0) freq = DEF_FREQ; /* default frequency */
224 return 0;
228 int start()
230 /* This is the "starter process" for statistical profiling.
232 * Create output file for profiling data. Create named pipe to
233 * synchronize with stopper process. Fork so the parent can exit.
234 * Allocate memory for profiling data. Start profiling in kernel.
235 * Complete detachment from terminal. Write known string to named
236 * pipe, which blocks until read by stopper process. Redirect
237 * stdout/stderr to the named pipe. Write profiling data to file.
238 * Clean up.
240 int log_fd;
242 if (init_outfile() || create_named_pipe()) return 1;
244 printf("Starting statistical profiling.\n");
246 if (fork() != 0) exit(0);
248 if (alloc_mem()) return 1;
250 if (sprofile(PROF_START, mem_size, freq, intr_type, &sprof_info, mem_ptr)) {
251 perror("sprofile");
252 fprintf(stderr, "Error starting profiling.\n");
253 return 1;
256 detach();
258 /* Temporarily redirect to system log to catch errors. */
259 log_fd = open(DEV_LOG, O_WRONLY);
260 dup2(log_fd, 1);
261 dup2(log_fd, 2);
263 if ((npipe_fd = open(NPIPE, O_WRONLY)) < 0) {
264 fprintf(stderr, "Unable to open named pipe %s.\n", NPIPE);
265 return 1;
266 } else
267 /* Synchronize with stopper process. */
268 write(npipe_fd, SYNCING, strlen(SYNCING));
270 /* Now redirect to named pipe. */
271 dup2(npipe_fd, 1);
272 dup2(npipe_fd, 2);
274 mem_used = sprof_info.mem_used;
276 if (mem_used == -1) {
277 fprintf(stderr, "WARNING: Profiling was stopped prematurely due to ");
278 fprintf(stderr, "insufficient memory.\n");
279 fprintf(stderr, "Try increasing available memory using the -m switch.\n");
282 if (write_outfile()) return 1;
284 close(log_fd);
285 close(npipe_fd);
286 unlink(NPIPE);
287 close(outfile_fd);
288 free(mem_ptr);
290 return 0;
294 int stop()
296 /* This is the "stopper" process for statistical profiling.
298 * Stop profiling in kernel. Read known string from named pipe
299 * to synchronize with starter proces. Read named pipe until EOF
300 * and write to stdout, this allows feedback from started process
301 * to be printed.
303 int n;
304 char buf[BUFSIZE];
306 if (sprofile(PROF_STOP, 0, 0, 0, 0, 0)) {
307 perror("sprofile");
308 fprintf(stderr, "Error stopping profiling.\n");
309 return 1;
310 } else printf("Statistical profiling stopped.\n");
312 if ((npipe_fd = open(NPIPE, O_RDONLY)) < 0) {
313 fprintf(stderr, "Unable to open named pipe %s.\n", NPIPE);
314 return 1;
315 } else
316 /* Synchronize with starter process. */
317 read(npipe_fd, buf, strlen(SYNCING));
319 while ((n = read(npipe_fd, buf, BUFSIZE)) > 0)
320 write(1, buf, n);
322 close(npipe_fd);
324 return 0;
328 int get()
330 /* Get function for call profiling.
332 * Create output file. Allocate memory. Perform system call to get
333 * profiling table and write it to file. Clean up.
335 if (init_outfile()) return 1;
337 printf("Getting call profiling data.\n");
339 if (alloc_mem()) return 1;
341 if (cprofile(PROF_GET, mem_size, &cprof_info, mem_ptr)) {
342 perror("cprofile");
343 fprintf(stderr, "Error getting data.\n");
344 return 1;
347 mem_used = cprof_info.mem_used;
349 if (mem_used == -1) {
350 fprintf(stderr, "ERROR: unable to get data due to insufficient memory.\n");
351 fprintf(stderr, "Try increasing available memory using the -m switch.\n");
352 } else
353 if (cprof_info.err) {
354 fprintf(stderr, "ERROR: the following error(s) happened during profiling:\n");
355 if (cprof_info.err & CPROF_CPATH_OVERRUN)
356 fprintf(stderr, " call path overrun\n");
357 if (cprof_info.err & CPROF_STACK_OVERRUN)
358 fprintf(stderr, " call stack overrun\n");
359 if (cprof_info.err & CPROF_TABLE_OVERRUN)
360 fprintf(stderr, " hash table overrun\n");
361 fprintf(stderr, "Try changing values in /usr/src/include/minix/profile.h ");
362 fprintf(stderr, "and then rebuild the system.\n");
363 } else
364 if (write_outfile()) return 1;
366 close(outfile_fd);
367 free(mem_ptr);
369 return 0;
373 int reset()
375 /* Reset function for call profiling.
377 * Perform system call to reset profiling table.
379 printf("Resetting call profiling data.\n");
381 if (cprofile(PROF_RESET, 0, 0, 0)) {
382 perror("cprofile");
383 fprintf(stderr, "Error resetting data.\n");
384 return 1;
387 return 0;
391 int alloc_mem()
393 if ((mem_ptr = malloc(mem_size)) == 0) {
394 fprintf(stderr, "Unable to allocate memory.\n");
395 fprintf(stderr, "Used chmem to increase available proces memory?\n");
396 return 1;
397 } else memset(mem_ptr, '\0', mem_size);
399 return 0;
403 int init_outfile()
405 if ((outfile_fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY)) <= 0) {
406 fprintf(stderr, "Unable to create outfile %s.\n", outfile);
407 return 1;
408 } else chmod(outfile, S_IRUSR | S_IWUSR);
410 return 0;
414 int create_named_pipe()
416 if ((mkfifo(NPIPE, S_IRUSR | S_IWUSR) == -1) && (errno != EEXIST)) {
417 fprintf(stderr, "Unable to create named pipe %s.\n", NPIPE);
418 return 1;
419 } else
420 return 0;
424 void detach()
426 setsid();
427 (void) chdir("/");
428 close(0);
429 close(1);
430 close(2);
433 static void add_proc(struct sprof_proc * p)
435 struct sproc * n;
436 int slot = ((unsigned)(p->proc)) % HASH_MOD;
438 n = malloc(sizeof(struct sproc));
439 if (!n)
440 abort();
441 n->ep = p->proc;
442 memcpy(n->name, p->name, 8);
443 n->next = proc_hash[slot];
444 proc_hash[slot] = n;
447 static char * get_proc_name(endpoint_t ep)
449 struct sproc * p;
451 for (p = proc_hash[((unsigned)ep) % HASH_MOD]; p; p = p->next) {
452 if (p->ep == ep)
453 return p->name;
456 return NULL;
459 int write_outfile()
461 ssize_t n;
462 int written;
463 char header[80];
465 printf("Writing to %s ...", outfile);
467 /* Write header. */
468 if (SPROF)
469 sprintf(header, "stat\n%u %u %u\n", sizeof(struct sprof_info_s),
470 sizeof(struct sprof_sample),
471 sizeof(struct sprof_proc));
472 else
473 sprintf(header, "call\n%u %u\n",
474 CPROF_CPATH_MAX_LEN, CPROF_PROCNAME_LEN);
476 n = write(outfile_fd, header, strlen(header));
478 if (n < 0) {
479 fprintf(stderr, "Error writing to outfile %s.\n", outfile);
480 return 1;
483 /* for sprofile, raw data will do; cprofile is handled by a script that needs
484 * some preprocessing to be done by us
486 if (SPROF) {
487 written = write_outfile_sprof();
488 } else {
489 written = write_outfile_cprof();
491 if (written < 0) return -1;
493 printf(" header %d bytes, data %d bytes.\n", strlen(header), written);
494 return 0;
497 int write_outfile_cprof()
499 int towrite, written = 0;
500 struct sprof_sample *sample;
502 /* Write data. */
503 towrite = mem_used == -1 ? mem_size : mem_used;
505 sample = (struct sprof_sample *) mem_ptr;
506 while (towrite > 0) {
507 unsigned bytes;
508 char entry[12];
509 char * name;
511 name = get_proc_name(sample->proc);
512 if (!name) {
513 add_proc((struct sprof_proc *)sample);
514 bytes = sizeof(struct sprof_proc);
515 towrite -= bytes;
516 sample = (struct sprof_sample *)(((char *) sample) + bytes);
517 continue;
520 memset(entry, 0, 12);
521 memcpy(entry, name, strlen(name));
522 memcpy(entry + 8, &sample->pc, 4);
524 if (write(outfile_fd, entry, 12) != 12) {
525 fprintf(stderr, "Error writing to outfile %s.\n", outfile);
526 return -1;
528 towrite -= sizeof(struct sprof_sample);
529 sample++;
531 written += 12;
534 return written;
537 int write_outfile_sprof()
539 int towrite;
540 ssize_t written, written_total = 0;
542 /* write profiling totals */
543 written = write(outfile_fd, &sprof_info, sizeof(sprof_info));
544 if (written != sizeof(sprof_info)) goto error;
545 written_total += written;
547 /* write raw samples */
548 towrite = mem_used == -1 ? mem_size : mem_used;
549 written = write(outfile_fd, mem_ptr, towrite);
550 if (written != towrite) goto error;
551 written_total += written;
553 return written_total;
555 error:
556 fprintf(stderr, "Error writing to outfile %s.\n", outfile);
557 return -1;