1 /* profile - profile operating system 2 * 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. 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 #define GET 3 31 #define RESET 4 32 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" 39 #define MIN_MEMSIZE 1 40 #define DEF_MEMSIZE 64 41 #define MIN_FREQ 3 42 #define MAX_FREQ 15 43 #define DEF_FREQ 6 44 #define BUFSIZE 1024 45 #define MB (1024*1024) 46 #define SYNCING "SYNC" 47 #define DEV_LOG "/dev/log" 48 49 int action = 0; 50 int mem_size = 0; 51 int mem_used = 0; 52 int freq = 0; 53 int intr_type = PROF_RTC; 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; 59 60 #define HASH_MOD 128 61 struct sproc { 62 endpoint_t ep; 63 char name[8]; 64 struct sproc * next; 65 }; 66 67 static struct sproc * proc_hash[HASH_MOD]; 68 69 int handle_args(int argc, char *argv[]); 70 int start(void); 71 int stop(void); 72 int get(void); 73 int reset(void); 74 int create_named_pipe(void); 75 int alloc_mem(void); 76 int init_outfile(void); 77 int write_outfile(void); 78 int write_outfile_cprof(void); 79 int write_outfile_sprof(void); 80 void detach(void); 81 82 int main(int argc, char *argv[]) 83 { 84 int res; 85 86 if ((res = handle_args(argc, argv))) { 87 switch(res) { 88 case ESYNTAX: 89 printf("Error in parameters.\n"); 90 return 1; 91 break; 92 case EACTION: 93 printf("Specify one of start|stop|get|reset.\n"); 94 return 1; 95 break; 96 case EMEM: 97 printf("Incorrect memory size.\n"); 98 return 1; 99 break; 100 case EFREQ: 101 printf("Incorrect frequency.\n"); 102 return 1; 103 break; 104 case EOUTFILE: 105 printf("Output filename missing.\n"); 106 return 1; 107 break; 108 default: 109 break; 110 } 111 112 /* 113 * Check the frequency when we know the intr type. Only selected values 114 * are correct for RTC 115 */ 116 if (action == START && intr_type == PROF_RTC && 117 (freq < MIN_FREQ || freq > MAX_FREQ)) { 118 printf("Incorrect frequency.\n"); 119 return 1; 120 } 121 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"); 141 return 1; 142 } 143 144 switch(action) { 145 case START: 146 if (start()) return 1; 147 break; 148 case STOP: 149 if (stop()) return 1; 150 break; 151 case GET: 152 if (get()) return 1; 153 break; 154 case RESET: 155 if (reset()) return 1; 156 break; 157 default: 158 break; 159 } 160 return 0; 161 } 162 163 164 int handle_args(int argc, char *argv[]) 165 { 166 while (--argc) { 167 ++argv; 168 169 if (strcmp(*argv, "-h") == 0 || strcmp(*argv, "help") == 0 || 170 strcmp(*argv, "--help") == 0) { 171 return EHELP; 172 } else 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; 177 } else 178 if (strcmp(*argv, "-f") == 0) { 179 if (--argc == 0) return ESYNTAX; 180 if (sscanf(*++argv, "%u", &freq) != 1) 181 return EFREQ; 182 } else 183 if (strcmp(*argv, "-o") == 0) { 184 if (--argc == 0) return ESYNTAX; 185 outfile = *++argv; 186 } else 187 if (strcmp(*argv, "--rtc") == 0) { 188 intr_type = PROF_RTC; 189 } else 190 if (strcmp(*argv, "--nmi") == 0) { 191 intr_type = PROF_NMI; 192 } else 193 if (strcmp(*argv, "start") == 0) { 194 if (action) return EACTION; 195 action = START; 196 } else 197 if (strcmp(*argv, "stop") == 0) { 198 if (action) return EACTION; 199 action = STOP; 200 } else 201 if (strcmp(*argv, "get") == 0) { 202 if (action) return EACTION; 203 action = GET; 204 } else 205 if (strcmp(*argv, "reset") == 0) { 206 if (action) return EACTION; 207 action = RESET; 208 } 209 } 210 211 /* No action specified. */ 212 if (!action) return EHELP; 213 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 */ 219 } 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 */ 223 } 224 return 0; 225 } 226 227 228 int start() 229 { 230 /* This is the "starter process" for statistical profiling. 231 * 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. 238 * Clean up. 239 */ 240 int log_fd; 241 242 if (init_outfile() || create_named_pipe()) return 1; 243 244 printf("Starting statistical profiling.\n"); 245 246 if (fork() != 0) exit(0); 247 248 if (alloc_mem()) return 1; 249 250 if (sprofile(PROF_START, mem_size, freq, intr_type, &sprof_info, mem_ptr)) { 251 perror("sprofile"); 252 fprintf(stderr, "Error starting profiling.\n"); 253 return 1; 254 } 255 256 detach(); 257 258 /* Temporarily redirect to system log to catch errors. */ 259 log_fd = open(DEV_LOG, O_WRONLY); 260 dup2(log_fd, 1); 261 dup2(log_fd, 2); 262 263 if ((npipe_fd = open(NPIPE, O_WRONLY)) < 0) { 264 fprintf(stderr, "Unable to open named pipe %s.\n", NPIPE); 265 return 1; 266 } else 267 /* Synchronize with stopper process. */ 268 write(npipe_fd, SYNCING, strlen(SYNCING)); 269 270 /* Now redirect to named pipe. */ 271 dup2(npipe_fd, 1); 272 dup2(npipe_fd, 2); 273 274 mem_used = sprof_info.mem_used; 275 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"); 280 } 281 282 if (write_outfile()) return 1; 283 284 close(log_fd); 285 close(npipe_fd); 286 unlink(NPIPE); 287 close(outfile_fd); 288 free(mem_ptr); 289 290 return 0; 291 } 292 293 294 int stop() 295 { 296 /* This is the "stopper" process for statistical profiling. 297 * 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 301 * to be printed. 302 */ 303 int n; 304 char buf[BUFSIZE]; 305 306 if (sprofile(PROF_STOP, 0, 0, 0, 0, 0)) { 307 perror("sprofile"); 308 fprintf(stderr, "Error stopping profiling.\n"); 309 return 1; 310 } else printf("Statistical profiling stopped.\n"); 311 312 if ((npipe_fd = open(NPIPE, O_RDONLY)) < 0) { 313 fprintf(stderr, "Unable to open named pipe %s.\n", NPIPE); 314 return 1; 315 } else 316 /* Synchronize with starter process. */ 317 read(npipe_fd, buf, strlen(SYNCING)); 318 319 while ((n = read(npipe_fd, buf, BUFSIZE)) > 0) 320 write(1, buf, n); 321 322 close(npipe_fd); 323 324 return 0; 325 } 326 327 328 int get() 329 { 330 /* Get function for call profiling. 331 * 332 * Create output file. Allocate memory. Perform system call to get 333 * profiling table and write it to file. Clean up. 334 */ 335 if (init_outfile()) return 1; 336 337 printf("Getting call profiling data.\n"); 338 339 if (alloc_mem()) return 1; 340 341 if (cprofile(PROF_GET, mem_size, &cprof_info, mem_ptr)) { 342 perror("cprofile"); 343 fprintf(stderr, "Error getting data.\n"); 344 return 1; 345 } 346 347 mem_used = cprof_info.mem_used; 348 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"); 352 } else 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"); 363 } else 364 if (write_outfile()) return 1; 365 366 close(outfile_fd); 367 free(mem_ptr); 368 369 return 0; 370 } 371 372 373 int reset() 374 { 375 /* Reset function for call profiling. 376 * 377 * Perform system call to reset profiling table. 378 */ 379 printf("Resetting call profiling data.\n"); 380 381 if (cprofile(PROF_RESET, 0, 0, 0)) { 382 perror("cprofile"); 383 fprintf(stderr, "Error resetting data.\n"); 384 return 1; 385 } 386 387 return 0; 388 } 389 390 391 int alloc_mem() 392 { 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"); 396 return 1; 397 } else memset(mem_ptr, '\0', mem_size); 398 399 return 0; 400 } 401 402 403 int init_outfile() 404 { 405 if ((outfile_fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY)) <= 0) { 406 fprintf(stderr, "Unable to create outfile %s.\n", outfile); 407 return 1; 408 } else chmod(outfile, S_IRUSR | S_IWUSR); 409 410 return 0; 411 } 412 413 414 int create_named_pipe() 415 { 416 if ((mkfifo(NPIPE, S_IRUSR | S_IWUSR) == -1) && (errno != EEXIST)) { 417 fprintf(stderr, "Unable to create named pipe %s.\n", NPIPE); 418 return 1; 419 } else 420 return 0; 421 } 422 423 424 void detach() 425 { 426 setsid(); 427 (void) chdir("/"); 428 close(0); 429 close(1); 430 close(2); 431 } 432 433 static void add_proc(struct sprof_proc * p) 434 { 435 struct sproc * n; 436 int slot = ((unsigned)(p->proc)) % HASH_MOD; 437 438 n = malloc(sizeof(struct sproc)); 439 if (!n) 440 abort(); 441 n->ep = p->proc; 442 memcpy(n->name, p->name, 8); 443 n->next = proc_hash[slot]; 444 proc_hash[slot] = n; 445 } 446 447 static char * get_proc_name(endpoint_t ep) 448 { 449 struct sproc * p; 450 451 for (p = proc_hash[((unsigned)ep) % HASH_MOD]; p; p = p->next) { 452 if (p->ep == ep) 453 return p->name; 454 } 455 456 return NULL; 457 } 458 459 int write_outfile() 460 { 461 ssize_t n; 462 int written; 463 char header[80]; 464 465 printf("Writing to %s ...", outfile); 466 467 /* Write header. */ 468 if (SPROF) 469 sprintf(header, "stat\n%u %u %u\n", sizeof(struct sprof_info_s), 470 sizeof(struct sprof_sample), 471 sizeof(struct sprof_proc)); 472 else 473 sprintf(header, "call\n%u %u\n", 474 CPROF_CPATH_MAX_LEN, CPROF_PROCNAME_LEN); 475 476 n = write(outfile_fd, header, strlen(header)); 477 478 if (n < 0) { 479 fprintf(stderr, "Error writing to outfile %s.\n", outfile); 480 return 1; 481 } 482 483 /* for sprofile, raw data will do; cprofile is handled by a script that needs 484 * some preprocessing to be done by us 485 */ 486 if (SPROF) { 487 written = write_outfile_sprof(); 488 } else { 489 written = write_outfile_cprof(); 490 } 491 if (written < 0) return -1; 492 493 printf(" header %d bytes, data %d bytes.\n", strlen(header), written); 494 return 0; 495 } 496 497 int write_outfile_cprof() 498 { 499 int towrite, written = 0; 500 struct sprof_sample *sample; 501 502 /* Write data. */ 503 towrite = mem_used == -1 ? mem_size : mem_used; 504 505 sample = (struct sprof_sample *) mem_ptr; 506 while (towrite > 0) { 507 unsigned bytes; 508 char entry[12]; 509 char * name; 510 511 name = get_proc_name(sample->proc); 512 if (!name) { 513 add_proc((struct sprof_proc *)sample); 514 bytes = sizeof(struct sprof_proc); 515 towrite -= bytes; 516 sample = (struct sprof_sample *)(((char *) sample) + bytes); 517 continue; 518 } 519 520 memset(entry, 0, 12); 521 memcpy(entry, name, strlen(name)); 522 memcpy(entry + 8, &sample->pc, 4); 523 524 if (write(outfile_fd, entry, 12) != 12) { 525 fprintf(stderr, "Error writing to outfile %s.\n", outfile); 526 return -1; 527 } 528 towrite -= sizeof(struct sprof_sample); 529 sample++; 530 531 written += 12; 532 } 533 534 return written; 535 } 536 537 int write_outfile_sprof() 538 { 539 int towrite; 540 ssize_t written, written_total = 0; 541 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; 546 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; 552 553 return written_total; 554 555 error: 556 fprintf(stderr, "Error writing to outfile %s.\n", outfile); 557 return -1; 558 } 559