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)
20 #include <minix/profile.h>
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"
41 #define DEF_MEMSIZE 64
46 #define MB (1024*1024)
47 #define SYNCING "SYNC"
48 #define DEV_LOG "/dev/log"
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
[])
75 if (res
= handle_args(argc
, argv
)) {
78 printf("Error in parameters.\n");
82 printf("Specify one of start|stop|get|reset.\n");
86 printf("Incorrect memory size.\n");
90 printf("Incorrect frequency.\n");
94 printf("Output filename missing.\n");
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");
123 if (start()) return 1;
126 if (stop()) return 1;
132 if (reset()) return 1;
141 int handle_args(int argc
, char *argv
[])
146 if (strcmp(*argv
, "-h") == 0 || strcmp(*argv
, "help") == 0 ||
147 strcmp(*argv
, "--help") == 0) {
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
;
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
;
160 if (strcmp(*argv
, "-o") == 0) {
161 if (--argc
== 0) return ESYNTAX
;
164 if (strcmp(*argv
, "start") == 0) {
165 if (action
) return EACTION
;
168 if (strcmp(*argv
, "stop") == 0) {
169 if (action
) return EACTION
;
172 if (strcmp(*argv
, "get") == 0) {
173 if (action
) return EACTION
;
176 if (strcmp(*argv
, "reset") == 0) {
177 if (action
) return EACTION
;
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 */
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.
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
)) {
223 printf("Error starting profiling.\n");
229 /* Temporarily redirect to system log to catch errors. */
230 log_fd
= open(DEV_LOG
, O_WRONLY
);
234 if ((npipe_fd
= open(NPIPE
, O_WRONLY
)) < 0) {
235 printf("Unable to open named pipe %s.\n", NPIPE
);
238 /* Synchronize with stopper process. */
239 write(npipe_fd
, SYNCING
, strlen(SYNCING
));
241 /* Now redirect to named pipe. */
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;
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
277 if (sprofile(PROF_STOP
, 0, 0, 0, 0)) {
279 printf("Error stopping profiling.\n");
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
);
287 /* Synchronize with starter process. */
288 read(npipe_fd
, buf
, strlen(SYNCING
));
290 while ((n
= read(npipe_fd
, buf
, BUFSIZE
)) > 0)
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
)) {
314 printf("Error getting data.\n");
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");
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");
335 if (write_outfile()) return 1;
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)) {
354 printf("Error resetting data.\n");
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");
368 } else memset(mem_ptr
, '\0', mem_size
);
376 if ((outfile_fd
= open(outfile
, O_CREAT
| O_TRUNC
| O_WRONLY
)) <= 0) {
377 printf("Unable to create outfile %s.\n", outfile
);
379 } else chmod(outfile
, S_IRUSR
| S_IWUSR
);
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
);
407 int n
, towrite
, written
= 0;
411 printf("Writing to %s ...", outfile
);
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
);
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; }
428 towrite
= mem_used
== -1 ? mem_size
: mem_used
;
430 while (towrite
> 0) {
432 n
= write(outfile_fd
, buf
, towrite
);
435 { printf("Error writing to outfile %s.\n", outfile
); return 1; }
442 printf(" header %d bytes, data %d bytes.\n", strlen(header
), written
);