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.
7 * 14 Aug, 2006 Created (Rogier Meurs)
19 #include <minix/profile.h>
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"
40 #define DEF_MEMSIZE 64
45 #define MB (1024*1024)
46 #define SYNCING "SYNC"
47 #define DEV_LOG "/dev/log"
53 int intr_type
= PROF_RTC
;
56 int outfile_fd
, npipe_fd
;
57 struct sprof_info_s sprof_info
;
58 struct cprof_info_s cprof_info
;
67 static struct sproc
* proc_hash
[HASH_MOD
];
69 int handle_args(int argc
, char *argv
[]);
74 int create_named_pipe(void);
76 int init_outfile(void);
77 int write_outfile(void);
78 int write_outfile_cprof(void);
79 int write_outfile_sprof(void);
82 int main(int argc
, char *argv
[])
86 if ((res
= handle_args(argc
, argv
))) {
89 printf("Error in parameters.\n");
93 printf("Specify one of start|stop|get|reset.\n");
97 printf("Incorrect memory size.\n");
101 printf("Incorrect frequency.\n");
105 printf("Output filename missing.\n");
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");
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");
146 if (start()) return 1;
149 if (stop()) return 1;
155 if (reset()) return 1;
164 int handle_args(int argc
, char *argv
[])
169 if (strcmp(*argv
, "-h") == 0 || strcmp(*argv
, "help") == 0 ||
170 strcmp(*argv
, "--help") == 0) {
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
;
178 if (strcmp(*argv
, "-f") == 0) {
179 if (--argc
== 0) return ESYNTAX
;
180 if (sscanf(*++argv
, "%u", &freq
) != 1)
183 if (strcmp(*argv
, "-o") == 0) {
184 if (--argc
== 0) return ESYNTAX
;
187 if (strcmp(*argv
, "--rtc") == 0) {
188 intr_type
= PROF_RTC
;
190 if (strcmp(*argv
, "--nmi") == 0) {
191 intr_type
= PROF_NMI
;
193 if (strcmp(*argv
, "start") == 0) {
194 if (action
) return EACTION
;
197 if (strcmp(*argv
, "stop") == 0) {
198 if (action
) return EACTION
;
201 if (strcmp(*argv
, "get") == 0) {
202 if (action
) return EACTION
;
205 if (strcmp(*argv
, "reset") == 0) {
206 if (action
) return EACTION
;
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 */
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.
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
)) {
252 fprintf(stderr
, "Error starting profiling.\n");
258 /* Temporarily redirect to system log to catch errors. */
259 log_fd
= open(DEV_LOG
, O_WRONLY
);
263 if ((npipe_fd
= open(NPIPE
, O_WRONLY
)) < 0) {
264 fprintf(stderr
, "Unable to open named pipe %s.\n", NPIPE
);
267 /* Synchronize with stopper process. */
268 write(npipe_fd
, SYNCING
, strlen(SYNCING
));
270 /* Now redirect to named pipe. */
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;
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
306 if (sprofile(PROF_STOP
, 0, 0, 0, 0, 0)) {
308 fprintf(stderr
, "Error stopping profiling.\n");
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
);
316 /* Synchronize with starter process. */
317 read(npipe_fd
, buf
, strlen(SYNCING
));
319 while ((n
= read(npipe_fd
, buf
, BUFSIZE
)) > 0)
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
)) {
343 fprintf(stderr
, "Error getting data.\n");
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");
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");
364 if (write_outfile()) return 1;
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)) {
383 fprintf(stderr
, "Error resetting data.\n");
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");
397 } else memset(mem_ptr
, '\0', mem_size
);
405 if ((outfile_fd
= open(outfile
, O_CREAT
| O_TRUNC
| O_WRONLY
)) <= 0) {
406 fprintf(stderr
, "Unable to create outfile %s.\n", outfile
);
408 } else chmod(outfile
, S_IRUSR
| S_IWUSR
);
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
);
433 static void add_proc(struct sprof_proc
* p
)
436 int slot
= ((unsigned)(p
->proc
)) % HASH_MOD
;
438 n
= malloc(sizeof(struct sproc
));
442 memcpy(n
->name
, p
->name
, 8);
443 n
->next
= proc_hash
[slot
];
447 static char * get_proc_name(endpoint_t ep
)
451 for (p
= proc_hash
[((unsigned)ep
) % HASH_MOD
]; p
; p
= p
->next
) {
465 printf("Writing to %s ...", outfile
);
469 sprintf(header
, "stat\n%u %u %u\n", sizeof(struct sprof_info_s
),
470 sizeof(struct sprof_sample
),
471 sizeof(struct sprof_proc
));
473 sprintf(header
, "call\n%u %u\n",
474 CPROF_CPATH_MAX_LEN
, CPROF_PROCNAME_LEN
);
476 n
= write(outfile_fd
, header
, strlen(header
));
479 fprintf(stderr
, "Error writing to outfile %s.\n", outfile
);
483 /* for sprofile, raw data will do; cprofile is handled by a script that needs
484 * some preprocessing to be done by us
487 written
= write_outfile_sprof();
489 written
= write_outfile_cprof();
491 if (written
< 0) return -1;
493 printf(" header %d bytes, data %d bytes.\n", strlen(header
), written
);
497 int write_outfile_cprof()
499 int towrite
, written
= 0;
500 struct sprof_sample
*sample
;
503 towrite
= mem_used
== -1 ? mem_size
: mem_used
;
505 sample
= (struct sprof_sample
*) mem_ptr
;
506 while (towrite
> 0) {
511 name
= get_proc_name(sample
->proc
);
513 add_proc((struct sprof_proc
*)sample
);
514 bytes
= sizeof(struct sprof_proc
);
516 sample
= (struct sprof_sample
*)(((char *) sample
) + bytes
);
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
);
528 towrite
-= sizeof(struct sprof_sample
);
537 int write_outfile_sprof()
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
;
556 fprintf(stderr
, "Error writing to outfile %s.\n", outfile
);