xref: /minix/minix/commands/profile/profile.c (revision 433d6423)
1 /* profile - profile operating system
2  *
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.
5  *
6  * Changes:
7  *   14 Aug, 2006   Created (Rogier Meurs)
8  */
9 
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>
20 
21 #define EHELP			1
22 #define ESYNTAX			2
23 #define EMEM			3
24 #define EOUTFILE		4
25 #define EFREQ			5
26 #define EACTION			6
27 
28 #define START			1
29 #define STOP			2
30 #define GET			3
31 #define RESET			4
32 
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"
48 
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;
59 
60 #define HASH_MOD	128
61 struct sproc {
62 	endpoint_t	ep;
63 	char		name[8];
64 	struct sproc *	next;
65 };
66 
67 static struct sproc * proc_hash[HASH_MOD];
68 
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);
81 
82 int main(int argc, char *argv[])
83 {
84   int res;
85 
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;
110 	}
111 
112 	/*
113 	 * Check the frequency when we know the intr type. Only selected values
114 	 * are correct for RTC
115 	 */
116 	if (action == START && intr_type == PROF_RTC &&
117 			(freq < MIN_FREQ || freq > MAX_FREQ)) {
118 		printf("Incorrect frequency.\n");
119 		return 1;
120 	}
121 
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;
142   }
143 
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;
159   }
160   return 0;
161 }
162 
163 
164 int handle_args(int argc, char *argv[])
165 {
166   while (--argc) {
167 	++argv;
168 
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;
208 	}
209   }
210 
211   /* No action specified. */
212   if (!action) return EHELP;
213 
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 */
219   }
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 */
223   }
224   return 0;
225 }
226 
227 
228 int start()
229 {
230  /* This is the "starter process" for statistical profiling.
231   *
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.
239   */
240   int log_fd;
241 
242   if (init_outfile() || create_named_pipe()) return 1;
243 
244   printf("Starting statistical profiling.\n");
245 
246   if (fork() != 0) exit(0);
247 
248   if (alloc_mem()) return 1;
249 
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;
254   }
255 
256   detach();
257 
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);
262 
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));
269 
270   /* Now redirect to named pipe. */
271   dup2(npipe_fd, 1);
272   dup2(npipe_fd, 2);
273 
274   mem_used = sprof_info.mem_used;
275 
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");
280   }
281 
282   if (write_outfile()) return 1;
283 
284   close(log_fd);
285   close(npipe_fd);
286   unlink(NPIPE);
287   close(outfile_fd);
288   free(mem_ptr);
289 
290   return 0;
291 }
292 
293 
294 int stop()
295 {
296   /* This is the "stopper" process for statistical profiling.
297    *
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.
302    */
303   int n;
304   char buf[BUFSIZE];
305 
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");
311 
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));
318 
319   while ((n = read(npipe_fd, buf, BUFSIZE)) > 0)
320 	write(1, buf, n);
321 
322   close(npipe_fd);
323 
324   return 0;
325 }
326 
327 
328 int get()
329 {
330  /* Get function for call profiling.
331   *
332   * Create output file.  Allocate memory.  Perform system call to get
333   * profiling table and write it to file.  Clean up.
334   */
335   if (init_outfile()) return 1;
336 
337   printf("Getting call profiling data.\n");
338 
339   if (alloc_mem()) return 1;
340 
341   if (cprofile(PROF_GET, mem_size, &cprof_info, mem_ptr)) {
342 	perror("cprofile");
343 	fprintf(stderr, "Error getting data.\n");
344 	return 1;
345   }
346 
347   mem_used = cprof_info.mem_used;
348 
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;
365 
366   close(outfile_fd);
367   free(mem_ptr);
368 
369   return 0;
370 }
371 
372 
373 int reset()
374 {
375  /* Reset function for call profiling.
376   *
377   * Perform system call to reset profiling table.
378   */
379   printf("Resetting call profiling data.\n");
380 
381   if (cprofile(PROF_RESET, 0, 0, 0)) {
382 	perror("cprofile");
383 	fprintf(stderr, "Error resetting data.\n");
384 	return 1;
385   }
386 
387   return 0;
388 }
389 
390 
391 int alloc_mem()
392 {
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);
398 
399   return 0;
400 }
401 
402 
403 int init_outfile()
404 {
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);
409 
410   return 0;
411 }
412 
413 
414 int create_named_pipe()
415 {
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;
421 }
422 
423 
424 void detach()
425 {
426   setsid();
427   (void) chdir("/");
428   close(0);
429   close(1);
430   close(2);
431 }
432 
433 static void add_proc(struct sprof_proc * p)
434 {
435 	struct sproc * n;
436 	int slot = ((unsigned)(p->proc)) % HASH_MOD;
437 
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;
445 }
446 
447 static char * get_proc_name(endpoint_t ep)
448 {
449 	struct sproc * p;
450 
451 	for (p = proc_hash[((unsigned)ep) % HASH_MOD]; p; p = p->next) {
452 		if (p->ep == ep)
453 			return p->name;
454 	}
455 
456 	return NULL;
457 }
458 
459 int write_outfile()
460 {
461   ssize_t n;
462   int written;
463   char header[80];
464 
465   printf("Writing to %s ...", outfile);
466 
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);
475 
476   n = write(outfile_fd, header, strlen(header));
477 
478   if (n < 0) {
479 	fprintf(stderr, "Error writing to outfile %s.\n", outfile);
480 	return 1;
481   }
482 
483   /* for sprofile, raw data will do; cprofile is handled by a script that needs
484    * some preprocessing to be done by us
485    */
486   if (SPROF) {
487 	written = write_outfile_sprof();
488   } else {
489 	written = write_outfile_cprof();
490   }
491   if (written < 0) return -1;
492 
493   printf(" header %d bytes, data %d bytes.\n", strlen(header), written);
494   return 0;
495 }
496 
497 int write_outfile_cprof()
498 {
499   int towrite, written = 0;
500   struct sprof_sample *sample;
501 
502   /* Write data. */
503   towrite = mem_used == -1 ? mem_size : mem_used;
504 
505   sample = (struct sprof_sample *) mem_ptr;
506   while (towrite > 0) {
507 	  unsigned bytes;
508 	  char	entry[12];
509 	  char * name;
510 
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;
518 	  }
519 
520 	  memset(entry, 0, 12);
521 	  memcpy(entry, name, strlen(name));
522 	  memcpy(entry + 8, &sample->pc, 4);
523 
524 	  if (write(outfile_fd, entry, 12) != 12) {
525 		  fprintf(stderr, "Error writing to outfile %s.\n", outfile);
526 		  return -1;
527 	  }
528 	  towrite -= sizeof(struct sprof_sample);
529 	  sample++;
530 
531 	  written += 12;
532   }
533 
534   return written;
535 }
536 
537 int write_outfile_sprof()
538 {
539   int towrite;
540   ssize_t written, written_total = 0;
541 
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;
546 
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;
552 
553   return written_total;
554 
555 error:
556   fprintf(stderr, "Error writing to outfile %s.\n", outfile);
557   return -1;
558 }
559