re-establish kernel assert()s.
[minix.git] / commands / profile / profile.c
blobc987a3e66db7d8e9e81eaf261c240058dc267990
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 #define SPROFILE 1
11 #define CPROFILE 1
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <sys/stat.h>
20 #include <minix/profile.h>
22 #define EHELP 1
23 #define ESYNTAX 2
24 #define EMEM 3
25 #define EOUTFILE 4
26 #define EFREQ 5
27 #define EACTION 6
29 #define START 1
30 #define STOP 2
31 #define GET 3
32 #define RESET 4
34 #define SPROF (action==START||action==STOP)
35 #define CPROF (!SPROF)
36 #define DEF_OUTFILE_S "profile.stat.out"
37 #define DEF_OUTFILE_C "profile.call.out"
38 #define DEF_OUTFILE (SPROF?DEF_OUTFILE_S:DEF_OUTFILE_C)
39 #define NPIPE "/tmp/profile.npipe"
40 #define MIN_MEMSIZE 1
41 #define DEF_MEMSIZE 64
42 #define MIN_FREQ 3
43 #define MAX_FREQ 15
44 #define DEF_FREQ 6
45 #define BUFSIZE 1024
46 #define MB (1024*1024)
47 #define SYNCING "SYNC"
48 #define DEV_LOG "/dev/log"
50 int action = 0;
51 int mem_size = 0;
52 int mem_used = 0;
53 int freq = 0;
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 _PROTOTYPE(int handle_args, (int argc, char *argv[]));
61 _PROTOTYPE(int start, (void));
62 _PROTOTYPE(int stop, (void));
63 _PROTOTYPE(int get, (void));
64 _PROTOTYPE(int reset, (void));
65 _PROTOTYPE(int create_named_pipe, (void));
66 _PROTOTYPE(int alloc_mem, (void));
67 _PROTOTYPE(int init_outfile, (void));
68 _PROTOTYPE(int write_outfile, (void));
69 _PROTOTYPE(int detach, (void));
71 int main(int argc, char *argv[])
73 int res;
75 if (res = handle_args(argc, argv)) {
76 switch(res) {
77 case ESYNTAX:
78 printf("Error in parameters.\n");
79 return 1;
80 break;
81 case EACTION:
82 printf("Specify one of start|stop|get|reset.\n");
83 return 1;
84 break;
85 case EMEM:
86 printf("Incorrect memory size.\n");
87 return 1;
88 break;
89 case EFREQ:
90 printf("Incorrect frequency.\n");
91 return 1;
92 break;
93 case EOUTFILE:
94 printf("Output filename missing.\n");
95 return 1;
96 break;
97 default:
98 break;
101 printf("Statistical Profiling:\n");
102 printf(" profile start [-m memsize] [-o outfile] [-f frequency]\n");
103 printf(" profile stop\n\n");
104 printf("Call Profiling:\n");
105 printf(" profile get [-m memsize] [-o outfile]\n");
106 printf(" profile reset\n\n");
107 printf(" - memsize in MB, default: %u\n", DEF_MEMSIZE);
108 printf(" - default output file: profile.{stat|call}.out\n");
109 printf( " - sample frequencies (default: %u):\n", DEF_FREQ);
110 printf(" 3 8192 Hz 10 64 Hz\n");
111 printf(" 4 4096 Hz 11 32 Hz\n");
112 printf(" 5 2048 Hz 12 16 Hz\n");
113 printf(" 6 1024 Hz 13 8 Hz\n");
114 printf(" 7 512 Hz 14 4 Hz\n");
115 printf(" 8 256 Hz 15 2 Hz\n");
116 printf(" 9 128 Hz\n\n");
117 printf("Use [sc]profalyze.pl to analyze output file.\n");
118 return 1;
121 switch(action) {
122 case START:
123 if (start()) return 1;
124 break;
125 case STOP:
126 if (stop()) return 1;
127 break;
128 case GET:
129 if (get()) return 1;
130 break;
131 case RESET:
132 if (reset()) return 1;
133 break;
134 default:
135 break;
137 return 0;
141 int handle_args(int argc, char *argv[])
143 while (--argc) {
144 ++argv;
146 if (strcmp(*argv, "-h") == 0 || strcmp(*argv, "help") == 0 ||
147 strcmp(*argv, "--help") == 0) {
148 return EHELP;
149 } else
150 if (strcmp(*argv, "-m") == 0) {
151 if (--argc == 0) return ESYNTAX;
152 if (sscanf(*++argv, "%u", &mem_size) != 1 ||
153 mem_size < MIN_MEMSIZE ) return EMEM;
154 } else
155 if (strcmp(*argv, "-f") == 0) {
156 if (--argc == 0) return ESYNTAX;
157 if (sscanf(*++argv, "%u", &freq) != 1 ||
158 freq < MIN_FREQ || freq > MAX_FREQ) return EFREQ;
159 } else
160 if (strcmp(*argv, "-o") == 0) {
161 if (--argc == 0) return ESYNTAX;
162 outfile = *++argv;
163 } else
164 if (strcmp(*argv, "start") == 0) {
165 if (action) return EACTION;
166 action = START;
167 } else
168 if (strcmp(*argv, "stop") == 0) {
169 if (action) return EACTION;
170 action = STOP;
171 } else
172 if (strcmp(*argv, "get") == 0) {
173 if (action) return EACTION;
174 action = GET;
175 } else
176 if (strcmp(*argv, "reset") == 0) {
177 if (action) return EACTION;
178 action = RESET;
182 /* No action specified. */
183 if (!action) return EHELP;
185 /* Init unspecified parameters. */
186 if (action == START || action == GET) {
187 if (strcmp(outfile, "") == 0) outfile = DEF_OUTFILE;
188 if (mem_size == 0) mem_size = DEF_MEMSIZE;
189 mem_size *= MB; /* mem_size in bytes */
191 if (action == START) {
192 mem_size -= mem_size % sizeof(sprof_sample); /* align to sample size */
193 if (freq == 0) freq = DEF_FREQ; /* default frequency */
195 return 0;
199 int start()
201 /* This is the "starter process" for statistical profiling.
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.
211 int log_fd;
213 if (init_outfile() || create_named_pipe()) return 1;
215 printf("Starting statistical profiling.\n");
217 if (fork() != 0) exit(0);
219 if (alloc_mem()) return 1;
221 if (sprofile(PROF_START, mem_size, freq, &sprof_info, mem_ptr)) {
222 perror("sprofile");
223 printf("Error starting profiling.\n");
224 return 1;
227 detach();
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);
234 if ((npipe_fd = open(NPIPE, O_WRONLY)) < 0) {
235 printf("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));
241 /* Now redirect to named pipe. */
242 dup2(npipe_fd, 1);
243 dup2(npipe_fd, 2);
245 mem_used = sprof_info.mem_used;
247 if (mem_used == -1) {
248 printf("WARNING: Profiling was stopped prematurely due to ");
249 printf("insufficient memory.\n");
250 printf("Try increasing available memory using the -m switch.\n");
253 if (write_outfile()) return 1;
255 close(log_fd);
256 close(npipe_fd);
257 unlink(NPIPE);
258 close(outfile_fd);
259 free(mem_ptr);
261 return 0;
265 int stop()
267 /* This is the "stopper" process for statistical profiling.
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.
274 int n;
275 char buf[BUFSIZE];
277 if (sprofile(PROF_STOP, 0, 0, 0, 0)) {
278 perror("sprofile");
279 printf("Error stopping profiling.\n");
280 return 1;
281 } else printf("Statistical profiling stopped.\n");
283 if ((npipe_fd = open(NPIPE, O_RDONLY)) < 0) {
284 printf("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));
290 while ((n = read(npipe_fd, buf, BUFSIZE)) > 0)
291 write(1, buf, n);
293 close(npipe_fd);
295 return 0;
299 int get()
301 /* Get function for call profiling.
303 * Create output file. Allocate memory. Perform system call to get
304 * profiling table and write it to file. Clean up.
306 if (init_outfile()) return 1;
308 printf("Getting call profiling data.\n");
310 if (alloc_mem()) return 1;
312 if (cprofile(PROF_GET, mem_size, &cprof_info, mem_ptr)) {
313 perror("cprofile");
314 printf("Error getting data.\n");
315 return 1;
318 mem_used = cprof_info.mem_used;
320 if (mem_used == -1) {
321 printf("ERROR: unable to get data due to insufficient memory.\n");
322 printf("Try increasing available memory using the -m switch.\n");
323 } else
324 if (cprof_info.err) {
325 printf("ERROR: the following error(s) happened during profiling:\n");
326 if (cprof_info.err & CPROF_CPATH_OVERRUN)
327 printf(" call path overrun\n");
328 if (cprof_info.err & CPROF_STACK_OVERRUN)
329 printf(" call stack overrun\n");
330 if (cprof_info.err & CPROF_TABLE_OVERRUN)
331 printf(" hash table overrun\n");
332 printf("Try changing values in /usr/src/include/minix/profile.h ");
333 printf("and then rebuild the system.\n");
334 } else
335 if (write_outfile()) return 1;
337 close(outfile_fd);
338 free(mem_ptr);
340 return 0;
344 int reset()
346 /* Reset function for call profiling.
348 * Perform system call to reset profiling table.
350 printf("Resetting call profiling data.\n");
352 if (cprofile(PROF_RESET, 0, 0, 0)) {
353 perror("cprofile");
354 printf("Error resetting data.\n");
355 return 1;
358 return 0;
362 int alloc_mem()
364 if ((mem_ptr = malloc(mem_size)) == 0) {
365 printf("Unable to allocate memory.\n");
366 printf("Used chmem to increase available proces memory?\n");
367 return 1;
368 } else memset(mem_ptr, '\0', mem_size);
370 return 0;
374 int init_outfile()
376 if ((outfile_fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY)) <= 0) {
377 printf("Unable to create outfile %s.\n", outfile);
378 return 1;
379 } else chmod(outfile, S_IRUSR | S_IWUSR);
381 return 0;
385 int create_named_pipe()
387 if ((mkfifo(NPIPE, S_IRUSR | S_IWUSR) == -1) && (errno != EEXIST)) {
388 printf("Unable to create named pipe %s.\n", NPIPE);
389 return 1;
390 } else
391 return 0;
395 int detach()
397 setsid();
398 (void) chdir("/");
399 close(0);
400 close(1);
401 close(2);
405 int write_outfile()
407 int n, towrite, written = 0;
408 char *buf = mem_ptr;
409 char header[80];
411 printf("Writing to %s ...", outfile);
413 /* Write header. */
414 if (SPROF)
415 sprintf(header, "stat\n%d %d %d %d\n", sprof_info.total_samples,
416 sprof_info.idle_samples,
417 sprof_info.system_samples,
418 sprof_info.user_samples);
419 else
420 sprintf(header, "call\n%u %u\n",
421 CPROF_CPATH_MAX_LEN, CPROF_PROCNAME_LEN);
423 n = write(outfile_fd, header, strlen(header));
425 if (n < 0) { printf("Error writing to outfile %s.\n", outfile); return 1; }
427 /* Write data. */
428 towrite = mem_used == -1 ? mem_size : mem_used;
430 while (towrite > 0) {
432 n = write(outfile_fd, buf, towrite);
434 if (n < 0)
435 { printf("Error writing to outfile %s.\n", outfile); return 1; }
437 towrite -= n;
438 buf += n;
439 written += n;
442 printf(" header %d bytes, data %d bytes.\n", strlen(header), written);
444 return 0;