1 /* profile - profile operating system 2 * 3 * The profile command is used to control statistical 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 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" 42 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; 52 53 #define HASH_MOD 128 54 struct sproc { 55 endpoint_t ep; 56 char name[8]; 57 struct sproc * next; 58 }; 59 60 static struct sproc * proc_hash[HASH_MOD]; 61 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); 71 72 int main(int argc, char *argv[]) 73 { 74 int res; 75 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; 100 } 101 102 /* 103 * Check the frequency when we know the intr type. Only selected values 104 * are correct for RTC 105 */ 106 if (action == START && intr_type == PROF_RTC && 107 (freq < MIN_FREQ || freq > MAX_FREQ)) { 108 printf("Incorrect frequency.\n"); 109 return 1; 110 } 111 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; 129 } 130 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; 140 } 141 return 0; 142 } 143 144 145 int handle_args(int argc, char *argv[]) 146 { 147 while (--argc) { 148 ++argv; 149 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; 181 } 182 } 183 184 /* No action specified. */ 185 if (!action) return EHELP; 186 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 */ 194 } 195 return 0; 196 } 197 198 199 int start() 200 { 201 /* This is the "starter process" for statistical profiling. 202 * 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. 210 */ 211 int log_fd; 212 213 if (init_outfile() || create_named_pipe()) return 1; 214 215 printf("Starting statistical profiling.\n"); 216 217 if (fork() != 0) exit(0); 218 219 if (alloc_mem()) return 1; 220 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; 225 } 226 227 detach(); 228 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); 233 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)); 240 241 /* Now redirect to named pipe. */ 242 dup2(npipe_fd, 1); 243 dup2(npipe_fd, 2); 244 245 mem_used = sprof_info.mem_used; 246 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"); 251 } 252 253 if (write_outfile()) return 1; 254 255 close(log_fd); 256 close(npipe_fd); 257 unlink(NPIPE); 258 close(outfile_fd); 259 free(mem_ptr); 260 261 return 0; 262 } 263 264 265 int stop() 266 { 267 /* This is the "stopper" process for statistical profiling. 268 * 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. 273 */ 274 int n; 275 char buf[BUFSIZE]; 276 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"); 282 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)); 289 290 while ((n = read(npipe_fd, buf, BUFSIZE)) > 0) 291 write(1, buf, n); 292 293 close(npipe_fd); 294 295 return 0; 296 } 297 298 299 int alloc_mem() 300 { 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); 306 307 return 0; 308 } 309 310 311 int init_outfile() 312 { 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); 317 318 return 0; 319 } 320 321 322 int create_named_pipe() 323 { 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; 329 } 330 331 332 void detach() 333 { 334 setsid(); 335 (void) chdir("/"); 336 close(0); 337 close(1); 338 close(2); 339 } 340 341 static void add_proc(struct sprof_proc * p) 342 { 343 struct sproc * n; 344 int slot = ((unsigned)(p->proc)) % HASH_MOD; 345 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; 353 } 354 355 static char * get_proc_name(endpoint_t ep) 356 { 357 struct sproc * p; 358 359 for (p = proc_hash[((unsigned)ep) % HASH_MOD]; p; p = p->next) { 360 if (p->ep == ep) 361 return p->name; 362 } 363 364 return NULL; 365 } 366 367 int write_outfile() 368 { 369 ssize_t n; 370 int written; 371 char header[80]; 372 373 printf("Writing to %s ...", outfile); 374 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)); 379 380 n = write(outfile_fd, header, strlen(header)); 381 382 if (n < 0) { 383 fprintf(stderr, "Error writing to outfile %s.\n", outfile); 384 return 1; 385 } 386 387 written = write_outfile_sprof(); 388 if (written < 0) return -1; 389 390 printf(" header %d bytes, data %d bytes.\n", strlen(header), written); 391 return 0; 392 } 393 394 int write_outfile_sprof() 395 { 396 int towrite; 397 ssize_t written, written_total = 0; 398 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; 403 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; 409 410 return written_total; 411 412 error: 413 fprintf(stderr, "Error writing to outfile %s.\n", outfile); 414 return -1; 415 } 416