1 /*- 2 * Copyright (c) 2003-2008 Joseph Koshy 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 #include <sys/types.h> 29 #include <sys/cpuset.h> 30 #include <sys/param.h> 31 #include <sys/socket.h> 32 #include <sys/stat.h> 33 #include <sys/pmc.h> 34 35 #include <assert.h> 36 #include <ctype.h> 37 #include <err.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <limits.h> 41 #include <netdb.h> 42 #include <pmc.h> 43 #include <pmclog.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <strings.h> 48 #include <sysexits.h> 49 #include <unistd.h> 50 51 #include "libpmcstat.h" 52 53 /* 54 * Get PMC record by id, apply merge policy. 55 */ 56 57 static struct pmcstat_pmcrecord * 58 pmcstat_lookup_pmcid(pmc_id_t pmcid, int pmcstat_mergepmc) 59 { 60 struct pmcstat_pmcrecord *pr; 61 62 LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) { 63 if (pr->pr_pmcid == pmcid) { 64 if (pmcstat_mergepmc) 65 return pr->pr_merge; 66 return pr; 67 } 68 } 69 70 return NULL; 71 } 72 73 /* 74 * Add a {pmcid,name} mapping. 75 */ 76 77 static void 78 pmcstat_pmcid_add(pmc_id_t pmcid, pmcstat_interned_string ps, 79 struct pmcstat_args *args, struct pmc_plugins *plugins, 80 int *pmcstat_npmcs) 81 { 82 struct pmcstat_pmcrecord *pr, *prm; 83 84 /* Replace an existing name for the PMC. */ 85 prm = NULL; 86 LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 87 if (pr->pr_pmcid == pmcid) { 88 pr->pr_pmcname = ps; 89 return; 90 } else if (pr->pr_pmcname == ps) 91 prm = pr; 92 93 /* 94 * Otherwise, allocate a new descriptor and call the 95 * plugins hook. 96 */ 97 if ((pr = malloc(sizeof(*pr))) == NULL) 98 err(EX_OSERR, "ERROR: Cannot allocate pmc record"); 99 100 pr->pr_pmcid = pmcid; 101 pr->pr_pmcname = ps; 102 pr->pr_pmcin = (*pmcstat_npmcs)++; 103 pr->pr_samples = 0; 104 pr->pr_dubious_frames = 0; 105 pr->pr_merge = prm == NULL ? pr : prm; 106 107 LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); 108 109 if (plugins[args->pa_pplugin].pl_newpmc != NULL) 110 plugins[args->pa_pplugin].pl_newpmc(ps, pr); 111 if (plugins[args->pa_plugin].pl_newpmc != NULL) 112 plugins[args->pa_plugin].pl_newpmc(ps, pr); 113 } 114 115 /* 116 * Unmap images in the range [start..end) associated with process 117 * 'pp'. 118 */ 119 120 static void 121 pmcstat_image_unmap(struct pmcstat_process *pp, uintfptr_t start, 122 uintfptr_t end) 123 { 124 struct pmcstat_pcmap *pcm, *pcmtmp, *pcmnew; 125 126 assert(pp != NULL); 127 assert(start < end); 128 129 /* 130 * Cases: 131 * - we could have the range completely in the middle of an 132 * existing pcmap; in this case we have to split the pcmap 133 * structure into two (i.e., generate a 'hole'). 134 * - we could have the range covering multiple pcmaps; these 135 * will have to be removed. 136 * - we could have either 'start' or 'end' falling in the 137 * middle of a pcmap; in this case shorten the entry. 138 */ 139 TAILQ_FOREACH_SAFE(pcm, &pp->pp_map, ppm_next, pcmtmp) { 140 assert(pcm->ppm_lowpc < pcm->ppm_highpc); 141 if (pcm->ppm_highpc <= start) 142 continue; 143 if (pcm->ppm_lowpc >= end) 144 return; 145 if (pcm->ppm_lowpc >= start && pcm->ppm_highpc <= end) { 146 /* 147 * The current pcmap is completely inside the 148 * unmapped range: remove it entirely. 149 */ 150 TAILQ_REMOVE(&pp->pp_map, pcm, ppm_next); 151 free(pcm); 152 } else if (pcm->ppm_lowpc < start && pcm->ppm_highpc > end) { 153 /* 154 * Split this pcmap into two; curtail the 155 * current map to end at [start-1], and start 156 * the new one at [end]. 157 */ 158 if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL) 159 err(EX_OSERR, 160 "ERROR: Cannot split a map entry"); 161 162 pcmnew->ppm_image = pcm->ppm_image; 163 164 pcmnew->ppm_lowpc = end; 165 pcmnew->ppm_highpc = pcm->ppm_highpc; 166 167 pcm->ppm_highpc = start; 168 169 TAILQ_INSERT_AFTER(&pp->pp_map, pcm, pcmnew, ppm_next); 170 171 return; 172 } else if (pcm->ppm_lowpc < start && pcm->ppm_highpc <= end) 173 pcm->ppm_highpc = start; 174 else if (pcm->ppm_lowpc >= start && pcm->ppm_highpc > end) 175 pcm->ppm_lowpc = end; 176 else 177 assert(0); 178 } 179 } 180 181 /* 182 * Convert a hwpmc(4) log to profile information. A system-wide 183 * callgraph is generated if FLAG_DO_CALLGRAPHS is set. gmon.out 184 * files usable by gprof(1) are created if FLAG_DO_GPROF is set. 185 */ 186 int 187 pmcstat_analyze_log(struct pmcstat_args *args, 188 struct pmc_plugins *plugins, 189 struct pmcstat_stats *pmcstat_stats, 190 struct pmcstat_process *pmcstat_kernproc, 191 int pmcstat_mergepmc, 192 int *pmcstat_npmcs, 193 int *ps_samples_period) 194 { 195 uint32_t cpu, cpuflags; 196 pid_t pid; 197 struct pmcstat_image *image; 198 struct pmcstat_process *pp, *ppnew; 199 struct pmcstat_pcmap *ppm, *ppmtmp; 200 struct pmclog_ev ev; 201 struct pmcstat_pmcrecord *pmcr; 202 pmcstat_interned_string image_path; 203 204 assert(args->pa_flags & FLAG_DO_ANALYSIS); 205 206 if (elf_version(EV_CURRENT) == EV_NONE) 207 err(EX_UNAVAILABLE, "Elf library initialization failed"); 208 209 while (pmclog_read(args->pa_logparser, &ev) == 0) { 210 assert(ev.pl_state == PMCLOG_OK); 211 212 switch (ev.pl_type) { 213 case PMCLOG_TYPE_INITIALIZE: 214 if ((ev.pl_u.pl_i.pl_version & 0xFF000000) != 215 PMC_VERSION_MAJOR << 24 && args->pa_verbosity > 0) 216 warnx( 217 "WARNING: Log version 0x%x does not match compiled version 0x%x.", 218 ev.pl_u.pl_i.pl_version, PMC_VERSION_MAJOR); 219 break; 220 221 case PMCLOG_TYPE_MAP_IN: 222 /* 223 * Introduce an address range mapping for a 224 * userland process or the kernel (pid == -1). 225 * 226 * We always allocate a process descriptor so 227 * that subsequent samples seen for this 228 * address range are mapped to the current 229 * object being mapped in. 230 */ 231 pid = ev.pl_u.pl_mi.pl_pid; 232 if (pid == -1) 233 pp = pmcstat_kernproc; 234 else 235 pp = pmcstat_process_lookup(pid, 236 PMCSTAT_ALLOCATE); 237 238 assert(pp != NULL); 239 240 image_path = pmcstat_string_intern(ev.pl_u.pl_mi. 241 pl_pathname); 242 image = pmcstat_image_from_path(image_path, pid == -1, 243 args, plugins); 244 if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 245 pmcstat_image_determine_type(image, args); 246 if (image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE) 247 pmcstat_image_link(pp, image, 248 ev.pl_u.pl_mi.pl_start); 249 break; 250 251 case PMCLOG_TYPE_MAP_OUT: 252 /* 253 * Remove an address map. 254 */ 255 pid = ev.pl_u.pl_mo.pl_pid; 256 if (pid == -1) 257 pp = pmcstat_kernproc; 258 else 259 pp = pmcstat_process_lookup(pid, 0); 260 261 if (pp == NULL) /* unknown process */ 262 break; 263 264 pmcstat_image_unmap(pp, ev.pl_u.pl_mo.pl_start, 265 ev.pl_u.pl_mo.pl_end); 266 break; 267 268 case PMCLOG_TYPE_CALLCHAIN: 269 pmcstat_stats->ps_samples_total++; 270 *ps_samples_period += 1; 271 272 cpuflags = ev.pl_u.pl_cc.pl_cpuflags; 273 cpu = PMC_CALLCHAIN_CPUFLAGS_TO_CPU(cpuflags); 274 275 if ((args->pa_flags & FLAG_FILTER_THREAD_ID) && 276 args->pa_tid != ev.pl_u.pl_cc.pl_tid) { 277 pmcstat_stats->ps_samples_skipped++; 278 break; 279 } 280 /* Filter on the CPU id. */ 281 if (!CPU_ISSET(cpu, &(args->pa_cpumask))) { 282 pmcstat_stats->ps_samples_skipped++; 283 break; 284 } 285 286 pp = pmcstat_process_lookup(ev.pl_u.pl_cc.pl_pid, 287 PMCSTAT_ALLOCATE); 288 289 /* Get PMC record. */ 290 pmcr = pmcstat_lookup_pmcid(ev.pl_u.pl_cc.pl_pmcid, pmcstat_mergepmc); 291 assert(pmcr != NULL); 292 pmcr->pr_samples++; 293 294 /* 295 * Call the plugins processing 296 */ 297 298 if (plugins[args->pa_pplugin].pl_process != NULL) 299 plugins[args->pa_pplugin].pl_process( 300 pp, pmcr, 301 ev.pl_u.pl_cc.pl_npc, 302 ev.pl_u.pl_cc.pl_pc, 303 PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), 304 cpu); 305 plugins[args->pa_plugin].pl_process( 306 pp, pmcr, 307 ev.pl_u.pl_cc.pl_npc, 308 ev.pl_u.pl_cc.pl_pc, 309 PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), 310 cpu); 311 break; 312 313 case PMCLOG_TYPE_PMCALLOCATE: 314 /* 315 * Record the association pmc id between this 316 * PMC and its name. 317 */ 318 pmcstat_pmcid_add(ev.pl_u.pl_a.pl_pmcid, 319 pmcstat_string_intern(ev.pl_u.pl_a.pl_evname), 320 args, plugins, pmcstat_npmcs); 321 break; 322 323 case PMCLOG_TYPE_PMCALLOCATEDYN: 324 /* 325 * Record the association pmc id between this 326 * PMC and its name. 327 */ 328 pmcstat_pmcid_add(ev.pl_u.pl_ad.pl_pmcid, 329 pmcstat_string_intern(ev.pl_u.pl_ad.pl_evname), 330 args, plugins, pmcstat_npmcs); 331 break; 332 333 case PMCLOG_TYPE_PROCEXEC: 334 /* 335 * Change the executable image associated with 336 * a process. 337 */ 338 pp = pmcstat_process_lookup(ev.pl_u.pl_x.pl_pid, 339 PMCSTAT_ALLOCATE); 340 341 /* delete the current process map */ 342 TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) { 343 TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 344 free(ppm); 345 } 346 347 /* 348 * Associate this process image. 349 */ 350 image_path = pmcstat_string_intern( 351 ev.pl_u.pl_x.pl_pathname); 352 assert(image_path != NULL); 353 pmcstat_process_exec(pp, image_path, 354 ev.pl_u.pl_x.pl_baseaddr, ev.pl_u.pl_x.pl_dynaddr, 355 args, plugins, pmcstat_stats); 356 break; 357 358 case PMCLOG_TYPE_PROCEXIT: 359 360 /* 361 * Due to the way the log is generated, the 362 * last few samples corresponding to a process 363 * may appear in the log after the process 364 * exit event is recorded. Thus we keep the 365 * process' descriptor and associated data 366 * structures around, but mark the process as 367 * having exited. 368 */ 369 pp = pmcstat_process_lookup(ev.pl_u.pl_e.pl_pid, 0); 370 if (pp == NULL) 371 break; 372 pp->pp_isactive = 0; /* mark as a zombie */ 373 break; 374 375 case PMCLOG_TYPE_SYSEXIT: 376 pp = pmcstat_process_lookup(ev.pl_u.pl_se.pl_pid, 0); 377 if (pp == NULL) 378 break; 379 pp->pp_isactive = 0; /* make a zombie */ 380 break; 381 382 case PMCLOG_TYPE_PROCFORK: 383 384 /* 385 * Allocate a process descriptor for the new 386 * (child) process. 387 */ 388 ppnew = 389 pmcstat_process_lookup(ev.pl_u.pl_f.pl_newpid, 390 PMCSTAT_ALLOCATE); 391 392 /* 393 * If we had been tracking the parent, clone 394 * its address maps. 395 */ 396 pp = pmcstat_process_lookup(ev.pl_u.pl_f.pl_oldpid, 0); 397 if (pp == NULL) 398 break; 399 TAILQ_FOREACH(ppm, &pp->pp_map, ppm_next) 400 pmcstat_image_link(ppnew, ppm->ppm_image, 401 ppm->ppm_lowpc); 402 break; 403 404 default: /* other types of entries are not relevant */ 405 break; 406 } 407 } 408 409 if (ev.pl_state == PMCLOG_EOF) 410 return (PMCSTAT_FINISHED); 411 else if (ev.pl_state == PMCLOG_REQUIRE_DATA) 412 return (PMCSTAT_RUNNING); 413 414 err(EX_DATAERR, 415 "ERROR: event parsing failed state: %d type: %d (record %jd, offset 0x%jx)", 416 ev.pl_state, ev.pl_type, (uintmax_t) ev.pl_count + 1, ev.pl_offset); 417 } 418 419 /* 420 * Open a log file, for reading or writing. 421 * 422 * The function returns the fd of a successfully opened log or -1 in 423 * case of failure. 424 */ 425 426 int 427 pmcstat_open_log(const char *path, int mode) 428 { 429 int error, fd, cfd; 430 size_t hlen; 431 const char *p, *errstr; 432 struct addrinfo hints, *res, *res0; 433 char hostname[MAXHOSTNAMELEN]; 434 435 errstr = NULL; 436 fd = -1; 437 438 /* 439 * If 'path' is "-" then open one of stdin or stdout depending 440 * on the value of 'mode'. 441 * 442 * If 'path' contains a ':' and does not start with a '/' or '.', 443 * and is being opened for writing, treat it as a "host:port" 444 * specification and open a network socket. 445 * 446 * Otherwise, treat 'path' as a file name and open that. 447 */ 448 if (path[0] == '-' && path[1] == '\0') 449 fd = (mode == PMCSTAT_OPEN_FOR_READ) ? 0 : 1; 450 else if (path[0] != '/' && 451 path[0] != '.' && strchr(path, ':') != NULL) { 452 453 p = strrchr(path, ':'); 454 hlen = p - path; 455 if (p == path || hlen >= sizeof(hostname)) { 456 errstr = strerror(EINVAL); 457 goto done; 458 } 459 460 assert(hlen < sizeof(hostname)); 461 (void) strncpy(hostname, path, hlen); 462 hostname[hlen] = '\0'; 463 464 (void) memset(&hints, 0, sizeof(hints)); 465 hints.ai_family = AF_UNSPEC; 466 hints.ai_socktype = SOCK_STREAM; 467 if ((error = getaddrinfo(hostname, p+1, &hints, &res0)) != 0) { 468 errstr = gai_strerror(error); 469 goto done; 470 } 471 472 fd = -1; 473 for (res = res0; res; res = res->ai_next) { 474 if ((fd = socket(res->ai_family, res->ai_socktype, 475 res->ai_protocol)) < 0) { 476 errstr = strerror(errno); 477 continue; 478 } 479 if (mode == PMCSTAT_OPEN_FOR_READ) { 480 if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) { 481 errstr = strerror(errno); 482 (void) close(fd); 483 fd = -1; 484 continue; 485 } 486 listen(fd, 1); 487 cfd = accept(fd, NULL, NULL); 488 (void) close(fd); 489 if (cfd < 0) { 490 errstr = strerror(errno); 491 fd = -1; 492 break; 493 } 494 fd = cfd; 495 } else { 496 if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { 497 errstr = strerror(errno); 498 (void) close(fd); 499 fd = -1; 500 continue; 501 } 502 } 503 errstr = NULL; 504 break; 505 } 506 freeaddrinfo(res0); 507 508 } else if ((fd = open(path, mode == PMCSTAT_OPEN_FOR_READ ? 509 O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC), 510 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) 511 errstr = strerror(errno); 512 513 done: 514 if (errstr) 515 errx(EX_OSERR, "ERROR: Cannot open \"%s\" for %s: %s.", path, 516 (mode == PMCSTAT_OPEN_FOR_READ ? "reading" : "writing"), 517 errstr); 518 519 return (fd); 520 } 521 522 /* 523 * Close a logfile, after first flushing all in-module queued data. 524 */ 525 526 int 527 pmcstat_close_log(struct pmcstat_args *args) 528 { 529 /* If a local logfile is configured ask the kernel to stop 530 * and flush data. Kernel will close the file when data is flushed 531 * so keep the status to EXITING. 532 */ 533 if (args->pa_logfd != -1) { 534 if (pmc_close_logfile() < 0) 535 err(EX_OSERR, "ERROR: logging failed"); 536 } 537 538 return (args->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING : 539 PMCSTAT_FINISHED); 540 } 541 542 /* 543 * Initialize module. 544 */ 545 546 void 547 pmcstat_initialize_logging(struct pmcstat_process **pmcstat_kernproc, 548 struct pmcstat_args *args, struct pmc_plugins *plugins, 549 int *pmcstat_npmcs, int *pmcstat_mergepmc) 550 { 551 struct pmcstat_process *pmcstat_kp; 552 int i; 553 554 /* use a convenient format for 'ldd' output */ 555 if (setenv("LD_TRACE_LOADED_OBJECTS_FMT1","%o \"%p\" %x\n",1) != 0) 556 err(EX_OSERR, "ERROR: Cannot setenv"); 557 558 /* Initialize hash tables */ 559 pmcstat_string_initialize(); 560 for (i = 0; i < PMCSTAT_NHASH; i++) { 561 LIST_INIT(&pmcstat_image_hash[i]); 562 LIST_INIT(&pmcstat_process_hash[i]); 563 } 564 565 /* 566 * Create a fake 'process' entry for the kernel with pid -1. 567 * hwpmc(4) will subsequently inform us about where the kernel 568 * and any loaded kernel modules are mapped. 569 */ 570 if ((pmcstat_kp = pmcstat_process_lookup((pid_t) -1, 571 PMCSTAT_ALLOCATE)) == NULL) 572 err(EX_OSERR, "ERROR: Cannot initialize logging"); 573 574 *pmcstat_kernproc = pmcstat_kp; 575 576 /* PMC count. */ 577 *pmcstat_npmcs = 0; 578 579 /* Merge PMC with same name. */ 580 *pmcstat_mergepmc = args->pa_mergepmc; 581 582 /* 583 * Initialize plugins 584 */ 585 586 if (plugins[args->pa_pplugin].pl_init != NULL) 587 plugins[args->pa_pplugin].pl_init(); 588 if (plugins[args->pa_plugin].pl_init != NULL) 589 plugins[args->pa_plugin].pl_init(); 590 } 591 592 /* 593 * Shutdown module. 594 */ 595 596 void 597 pmcstat_shutdown_logging(struct pmcstat_args *args, 598 struct pmc_plugins *plugins, 599 struct pmcstat_stats *pmcstat_stats) 600 { 601 struct pmcstat_image *pi, *pitmp; 602 struct pmcstat_process *pp, *pptmp; 603 struct pmcstat_pcmap *ppm, *ppmtmp; 604 FILE *mf; 605 int i; 606 607 /* determine where to send the map file */ 608 mf = NULL; 609 if (args->pa_mapfilename != NULL) 610 mf = (strcmp(args->pa_mapfilename, "-") == 0) ? 611 args->pa_printfile : fopen(args->pa_mapfilename, "w"); 612 613 if (mf == NULL && args->pa_flags & FLAG_DO_GPROF && 614 args->pa_verbosity >= 2) 615 mf = args->pa_printfile; 616 617 if (mf) 618 (void) fprintf(mf, "MAP:\n"); 619 620 /* 621 * Shutdown the plugins 622 */ 623 624 if (plugins[args->pa_plugin].pl_shutdown != NULL) 625 plugins[args->pa_plugin].pl_shutdown(mf); 626 if (plugins[args->pa_pplugin].pl_shutdown != NULL) 627 plugins[args->pa_pplugin].pl_shutdown(mf); 628 629 for (i = 0; i < PMCSTAT_NHASH; i++) { 630 LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next, 631 pitmp) { 632 if (plugins[args->pa_plugin].pl_shutdownimage != NULL) 633 plugins[args->pa_plugin].pl_shutdownimage(pi); 634 if (plugins[args->pa_pplugin].pl_shutdownimage != NULL) 635 plugins[args->pa_pplugin].pl_shutdownimage(pi); 636 637 free(pi->pi_symbols); 638 if (pi->pi_addr2line != NULL) 639 pclose(pi->pi_addr2line); 640 LIST_REMOVE(pi, pi_next); 641 free(pi); 642 } 643 644 LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[i], pp_next, 645 pptmp) { 646 TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) { 647 TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 648 free(ppm); 649 } 650 LIST_REMOVE(pp, pp_next); 651 free(pp); 652 } 653 } 654 655 pmcstat_string_shutdown(); 656 657 /* 658 * Print errors unless -q was specified. Print all statistics 659 * if verbosity > 1. 660 */ 661 #define PRINT(N,V) do { \ 662 if (pmcstat_stats->ps_##V || args->pa_verbosity >= 2) \ 663 (void) fprintf(args->pa_printfile, " %-40s %d\n",\ 664 N, pmcstat_stats->ps_##V); \ 665 } while (0) 666 667 if (args->pa_verbosity >= 1 && (args->pa_flags & FLAG_DO_ANALYSIS)) { 668 (void) fprintf(args->pa_printfile, "CONVERSION STATISTICS:\n"); 669 PRINT("#exec/a.out", exec_aout); 670 PRINT("#exec/elf", exec_elf); 671 PRINT("#exec/unknown", exec_indeterminable); 672 PRINT("#exec handling errors", exec_errors); 673 PRINT("#samples/total", samples_total); 674 PRINT("#samples/unclaimed", samples_unknown_offset); 675 PRINT("#samples/unknown-object", samples_indeterminable); 676 PRINT("#samples/unknown-function", samples_unknown_function); 677 PRINT("#callchain/dubious-frames", callchain_dubious_frames); 678 } 679 680 if (mf) 681 (void) fclose(mf); 682 } 683