xref: /minix/minix/commands/profile/profile.c (revision e3b78ef1)
1 /* profile - profile operating system
2  *
3  * The profile command is used to control statistical 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 
31 #define DEF_OUTFILE		"profile.stat.out"
32 #define NPIPE			"/tmp/profile.npipe"
33 #define MIN_MEMSIZE		1
34 #define DEF_MEMSIZE		64
35 #define MIN_FREQ		3
36 #define MAX_FREQ		15
37 #define DEF_FREQ		6
38 #define BUFSIZE			1024
39 #define MB			(1024*1024)
40 #define SYNCING			"SYNC"
41 #define DEV_LOG			"/dev/log"
42 
43 int action = 0;
44 int mem_size = 0;
45 int mem_used = 0;
46 int freq = 0;
47 int intr_type = PROF_RTC;
48 char *outfile = "";
49 char *mem_ptr;
50 int outfile_fd, npipe_fd;
51 struct sprof_info_s sprof_info;
52 
53 #define HASH_MOD	128
54 struct sproc {
55 	endpoint_t	ep;
56 	char		name[8];
57 	struct sproc *	next;
58 };
59 
60 static struct sproc * proc_hash[HASH_MOD];
61 
62 int handle_args(int argc, char *argv[]);
63 int start(void);
64 int stop(void);
65 int create_named_pipe(void);
66 int alloc_mem(void);
67 int init_outfile(void);
68 int write_outfile(void);
69 int write_outfile_sprof(void);
70 void detach(void);
71 
72 int main(int argc, char *argv[])
73 {
74   int res;
75 
76   if ((res = handle_args(argc, argv))) {
77 	switch(res) {
78 		case ESYNTAX:
79 			printf("Error in parameters.\n");
80 			return 1;
81 			break;
82 		case EACTION:
83 			printf("Specify one of start|stop|get|reset.\n");
84 			return 1;
85 			break;
86 		case EMEM:
87 			printf("Incorrect memory size.\n");
88 			return 1;
89 			break;
90 		case EFREQ:
91 			printf("Incorrect frequency.\n");
92 			return 1;
93 			break;
94 		case EOUTFILE:
95 			printf("Output filename missing.\n");
96 			return 1;
97 			break;
98 		default:
99 			break;
100 	}
101 
102 	/*
103 	 * Check the frequency when we know the intr type. Only selected values
104 	 * are correct for RTC
105 	 */
106 	if (action == START && intr_type == PROF_RTC &&
107 			(freq < MIN_FREQ || freq > MAX_FREQ)) {
108 		printf("Incorrect frequency.\n");
109 		return 1;
110 	}
111 
112         printf("Usage:\n");
113 	printf("  profile start [--rtc | --nmi] "
114 			"[-m memsize] [-o outfile] [-f frequency]\n");
115         printf("  profile stop\n\n");
116 	printf("   - --rtc is default, --nmi allows kernel profiling\n");
117 	printf("   - memsize in MB, default: %u\n", DEF_MEMSIZE);
118 	printf("   - default output file: profile.stat.out\n");
119 	printf("   - sample frequencies for --rtc (default: %u):\n", DEF_FREQ);
120 	printf("      3    8192 Hz          10     64 Hz\n");
121 	printf("      4    4096 Hz          11     32 Hz\n");
122 	printf("      5    2048 Hz          12     16 Hz\n");
123 	printf("      6    1024 Hz          13      8 Hz\n");
124 	printf("      7     512 Hz          14      4 Hz\n");
125 	printf("      8     256 Hz          15      2 Hz\n");
126 	printf("      9     128 Hz\n\n");
127 	printf("Use sprofalyze to analyze output file.\n");
128 	return 1;
129   }
130 
131   switch(action) {
132 	  case START:
133 	  	if (start()) return 1;
134 		break;
135 	  case STOP:
136 	  	if (stop()) return 1;
137 		break;
138 	  default:
139 		break;
140   }
141   return 0;
142 }
143 
144 
145 int handle_args(int argc, char *argv[])
146 {
147   while (--argc) {
148 	++argv;
149 
150 	if (strcmp(*argv, "-h") == 0 || strcmp(*argv, "help") == 0 ||
151 		strcmp(*argv, "--help") == 0) {
152 		return EHELP;
153 	} else
154 	if (strcmp(*argv, "-m") == 0) {
155 		if (--argc == 0) return ESYNTAX;
156 		if (sscanf(*++argv, "%u", &mem_size) != 1 ||
157 			mem_size < MIN_MEMSIZE ) return EMEM;
158 	} else
159 	if (strcmp(*argv, "-f") == 0) {
160 		if (--argc == 0) return ESYNTAX;
161 		if (sscanf(*++argv, "%u", &freq) != 1)
162 			return EFREQ;
163 	} else
164 	if (strcmp(*argv, "-o") == 0) {
165 		if (--argc == 0) return ESYNTAX;
166 		outfile = *++argv;
167 	} else
168 	if (strcmp(*argv, "--rtc") == 0) {
169 		intr_type = PROF_RTC;
170 	} else
171 	if (strcmp(*argv, "--nmi") == 0) {
172 		intr_type = PROF_NMI;
173 	} else
174 	if (strcmp(*argv, "start") == 0) {
175 		if (action) return EACTION;
176 	       	action = START;
177 	} else
178 	if (strcmp(*argv, "stop") == 0) {
179 		if (action) return EACTION;
180 	       	action = STOP;
181 	}
182   }
183 
184   /* No action specified. */
185   if (!action) return EHELP;
186 
187   /* Init unspecified parameters. */
188   if (action == START) {
189 	if (strcmp(outfile, "") == 0) outfile = DEF_OUTFILE;
190 	if (mem_size == 0) mem_size = DEF_MEMSIZE;
191 	mem_size *= MB;				   /* mem_size in bytes */
192 	mem_size -= mem_size % sizeof(struct sprof_sample); /* align to sample size */
193 	if (freq == 0) freq = DEF_FREQ;		   /* default frequency */
194   }
195   return 0;
196 }
197 
198 
199 int start()
200 {
201  /* This is the "starter process" for statistical profiling.
202   *
203   * Create output file for profiling data.  Create named pipe to
204   * synchronize with stopper process.  Fork so the parent can exit.
205   * Allocate memory for profiling data.  Start profiling in kernel.
206   * Complete detachment from terminal.  Write known string to named
207   * pipe, which blocks until read by stopper process.  Redirect
208   * stdout/stderr to the named pipe.  Write profiling data to file.
209   * Clean up.
210   */
211   int log_fd;
212 
213   if (init_outfile() || create_named_pipe()) return 1;
214 
215   printf("Starting statistical profiling.\n");
216 
217   if (fork() != 0) exit(0);
218 
219   if (alloc_mem()) return 1;
220 
221   if (sprofile(PROF_START, mem_size, freq, intr_type, &sprof_info, mem_ptr)) {
222 	perror("sprofile");
223 	fprintf(stderr, "Error starting profiling.\n");
224 	return 1;
225   }
226 
227   detach();
228 
229   /* Temporarily redirect to system log to catch errors. */
230   log_fd = open(DEV_LOG, O_WRONLY);
231   dup2(log_fd, 1);
232   dup2(log_fd, 2);
233 
234   if ((npipe_fd = open(NPIPE, O_WRONLY)) < 0) {
235 	fprintf(stderr, "Unable to open named pipe %s.\n", NPIPE);
236 	return 1;
237   } else
238 	/* Synchronize with stopper process. */
239 	write(npipe_fd, SYNCING, strlen(SYNCING));
240 
241   /* Now redirect to named pipe. */
242   dup2(npipe_fd, 1);
243   dup2(npipe_fd, 2);
244 
245   mem_used = sprof_info.mem_used;
246 
247   if (mem_used == -1) {
248 	  fprintf(stderr, "WARNING: Profiling was stopped prematurely due to ");
249 	  fprintf(stderr, "insufficient memory.\n");
250 	  fprintf(stderr, "Try increasing available memory using the -m switch.\n");
251   }
252 
253   if (write_outfile()) return 1;
254 
255   close(log_fd);
256   close(npipe_fd);
257   unlink(NPIPE);
258   close(outfile_fd);
259   free(mem_ptr);
260 
261   return 0;
262 }
263 
264 
265 int stop()
266 {
267   /* This is the "stopper" process for statistical profiling.
268    *
269    * Stop profiling in kernel.  Read known string from named pipe
270    * to synchronize with starter proces.  Read named pipe until EOF
271    * and write to stdout, this allows feedback from started process
272    * to be printed.
273    */
274   int n;
275   char buf[BUFSIZE];
276 
277   if (sprofile(PROF_STOP, 0, 0, 0, 0, 0)) {
278 	perror("sprofile");
279 	fprintf(stderr, "Error stopping profiling.\n");
280   	return 1;
281   } else printf("Statistical profiling stopped.\n");
282 
283   if ((npipe_fd = open(NPIPE, O_RDONLY)) < 0) {
284 	fprintf(stderr, "Unable to open named pipe %s.\n", NPIPE);
285 	return 1;
286   } else
287 	/* Synchronize with starter process. */
288 	read(npipe_fd, buf, strlen(SYNCING));
289 
290   while ((n = read(npipe_fd, buf, BUFSIZE)) > 0)
291 	write(1, buf, n);
292 
293   close(npipe_fd);
294 
295   return 0;
296 }
297 
298 
299 int alloc_mem()
300 {
301   if ((mem_ptr = malloc(mem_size)) == 0) {
302 	fprintf(stderr, "Unable to allocate memory.\n");
303 	fprintf(stderr, "Used chmem to increase available proces memory?\n");
304 	return 1;
305   } else memset(mem_ptr, '\0', mem_size);
306 
307   return 0;
308 }
309 
310 
311 int init_outfile()
312 {
313   if ((outfile_fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY)) <= 0) {
314 	fprintf(stderr, "Unable to create outfile %s.\n", outfile);
315 	return 1;
316   } else chmod(outfile, S_IRUSR | S_IWUSR);
317 
318   return 0;
319 }
320 
321 
322 int create_named_pipe()
323 {
324   if ((mkfifo(NPIPE, S_IRUSR | S_IWUSR) == -1) && (errno != EEXIST)) {
325 	fprintf(stderr, "Unable to create named pipe %s.\n", NPIPE);
326 	return 1;
327   } else
328 	return 0;
329 }
330 
331 
332 void detach()
333 {
334   setsid();
335   (void) chdir("/");
336   close(0);
337   close(1);
338   close(2);
339 }
340 
341 static void add_proc(struct sprof_proc * p)
342 {
343 	struct sproc * n;
344 	int slot = ((unsigned)(p->proc)) % HASH_MOD;
345 
346 	n = malloc(sizeof(struct sproc));
347 	if (!n)
348 		abort();
349 	n->ep = p->proc;
350 	memcpy(n->name, p->name, 8);
351 	n->next = proc_hash[slot];
352 	proc_hash[slot] = n;
353 }
354 
355 static char * get_proc_name(endpoint_t ep)
356 {
357 	struct sproc * p;
358 
359 	for (p = proc_hash[((unsigned)ep) % HASH_MOD]; p; p = p->next) {
360 		if (p->ep == ep)
361 			return p->name;
362 	}
363 
364 	return NULL;
365 }
366 
367 int write_outfile()
368 {
369   ssize_t n;
370   int written;
371   char header[80];
372 
373   printf("Writing to %s ...", outfile);
374 
375   /* Write header. */
376   sprintf(header, "stat\n%u %u %u\n",	sizeof(struct sprof_info_s),
377 					sizeof(struct sprof_sample),
378 					sizeof(struct sprof_proc));
379 
380   n = write(outfile_fd, header, strlen(header));
381 
382   if (n < 0) {
383 	fprintf(stderr, "Error writing to outfile %s.\n", outfile);
384 	return 1;
385   }
386 
387   written = write_outfile_sprof();
388   if (written < 0) return -1;
389 
390   printf(" header %d bytes, data %d bytes.\n", strlen(header), written);
391   return 0;
392 }
393 
394 int write_outfile_sprof()
395 {
396   int towrite;
397   ssize_t written, written_total = 0;
398 
399   /* write profiling totals */
400   written = write(outfile_fd, &sprof_info, sizeof(sprof_info));
401   if (written != sizeof(sprof_info)) goto error;
402   written_total += written;
403 
404   /* write raw samples */
405   towrite = mem_used == -1 ? mem_size : mem_used;
406   written = write(outfile_fd, mem_ptr, towrite);
407   if (written != towrite) goto error;
408   written_total += written;
409 
410   return written_total;
411 
412 error:
413   fprintf(stderr, "Error writing to outfile %s.\n", outfile);
414   return -1;
415 }
416