Remove building with NOCRYPTO option
[minix.git] / minix / commands / profile / profile.c
blob43ec4554775eae1621fbc67d86308e9c27159eb5
1 /* profile - profile operating system
3 * The profile command is used to control statistical 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
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"
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;
53 #define HASH_MOD 128
54 struct sproc {
55 endpoint_t ep;
56 char name[8];
57 struct sproc * next;
60 static struct sproc * proc_hash[HASH_MOD];
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);
72 int main(int argc, char *argv[])
74 int res;
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;
103 * Check the frequency when we know the intr type. Only selected values
104 * are correct for RTC
106 if (action == START && intr_type == PROF_RTC &&
107 (freq < MIN_FREQ || freq > MAX_FREQ)) {
108 printf("Incorrect frequency.\n");
109 return 1;
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;
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;
141 return 0;
145 int handle_args(int argc, char *argv[])
147 while (--argc) {
148 ++argv;
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;
184 /* No action specified. */
185 if (!action) return EHELP;
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 */
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, intr_type, &sprof_info, mem_ptr)) {
222 perror("sprofile");
223 fprintf(stderr, "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 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));
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 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");
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, 0)) {
278 perror("sprofile");
279 fprintf(stderr, "Error stopping profiling.\n");
280 return 1;
281 } else printf("Statistical profiling stopped.\n");
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));
290 while ((n = read(npipe_fd, buf, BUFSIZE)) > 0)
291 write(1, buf, n);
293 close(npipe_fd);
295 return 0;
299 int alloc_mem()
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);
307 return 0;
311 int init_outfile()
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);
318 return 0;
322 int create_named_pipe()
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;
332 void detach()
334 setsid();
335 (void) chdir("/");
336 close(0);
337 close(1);
338 close(2);
341 static void add_proc(struct sprof_proc * p)
343 struct sproc * n;
344 int slot = ((unsigned)(p->proc)) % HASH_MOD;
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;
355 static char * get_proc_name(endpoint_t ep)
357 struct sproc * p;
359 for (p = proc_hash[((unsigned)ep) % HASH_MOD]; p; p = p->next) {
360 if (p->ep == ep)
361 return p->name;
364 return NULL;
367 int write_outfile()
369 ssize_t n;
370 int written;
371 char header[80];
373 printf("Writing to %s ...", outfile);
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));
380 n = write(outfile_fd, header, strlen(header));
382 if (n < 0) {
383 fprintf(stderr, "Error writing to outfile %s.\n", outfile);
384 return 1;
387 written = write_outfile_sprof();
388 if (written < 0) return -1;
390 printf(" header %d bytes, data %d bytes.\n", strlen(header), written);
391 return 0;
394 int write_outfile_sprof()
396 int towrite;
397 ssize_t written, written_total = 0;
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;
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;
410 return written_total;
412 error:
413 fprintf(stderr, "Error writing to outfile %s.\n", outfile);
414 return -1;