diff options
Diffstat (limited to 'usr.sbin/pmcstat/pmcstat_log.c')
-rw-r--r-- | usr.sbin/pmcstat/pmcstat_log.c | 1702 |
1 files changed, 589 insertions, 1113 deletions
diff --git a/usr.sbin/pmcstat/pmcstat_log.c b/usr.sbin/pmcstat/pmcstat_log.c index a403852..5811af3 100644 --- a/usr.sbin/pmcstat/pmcstat_log.c +++ b/usr.sbin/pmcstat/pmcstat_log.c @@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include <netinet/in.h> #include <assert.h> +#include <curses.h> #include <err.h> #include <errno.h> #include <fcntl.h> @@ -68,9 +69,8 @@ __FBSDID("$FreeBSD$"); #include <unistd.h> #include "pmcstat.h" - -#define min(A,B) ((A) < (B) ? (A) : (B)) -#define max(A,B) ((A) > (B) ? (A) : (B)) +#include "pmcstat_log.h" +#include "pmcstat_top.h" #define PMCSTAT_ALLOCATE 1 @@ -81,6 +81,7 @@ __FBSDID("$FreeBSD$"); * pmcstat_shutdown_logging() orderly shutdown, called last * pmcstat_open_log() open an eventlog for processing * pmcstat_process_log() print/convert an event log + * pmcstat_display_log() top mode display for the log * pmcstat_close_log() finish processing an event log * * IMPLEMENTATION NOTES @@ -127,236 +128,125 @@ __FBSDID("$FreeBSD$"); * also given a 'rank' that reflects its depth in the call stack. */ -typedef const void *pmcstat_interned_string; - -/* - * 'pmcstat_pmcrecord' is a mapping from PMC ids to human-readable - * names. - */ - -struct pmcstat_pmcrecord { - LIST_ENTRY(pmcstat_pmcrecord) pr_next; - pmc_id_t pr_pmcid; - pmcstat_interned_string pr_pmcname; -}; - -static LIST_HEAD(,pmcstat_pmcrecord) pmcstat_pmcs = - LIST_HEAD_INITIALIZER(pmcstat_pmcs); - - -/* - * struct pmcstat_gmonfile tracks a given 'gmon.out' file. These - * files are mmap()'ed in as needed. - */ - -struct pmcstat_gmonfile { - LIST_ENTRY(pmcstat_gmonfile) pgf_next; /* list of entries */ - int pgf_overflow; /* whether a count overflowed */ - pmc_id_t pgf_pmcid; /* id of the associated pmc */ - size_t pgf_nbuckets; /* #buckets in this gmon.out */ - unsigned int pgf_nsamples; /* #samples in this gmon.out */ - pmcstat_interned_string pgf_name; /* pathname of gmon.out file */ - size_t pgf_ndatabytes; /* number of bytes mapped */ - void *pgf_gmondata; /* pointer to mmap'ed data */ - FILE *pgf_file; /* used when writing gmon arcs */ -}; - -/* - * A 'pmcstat_image' structure describes an executable program on - * disk. 'pi_execpath' is a cookie representing the pathname of - * the executable. 'pi_start' and 'pi_end' are the least and greatest - * virtual addresses for the text segments in the executable. - * 'pi_gmonlist' contains a linked list of gmon.out files associated - * with this image. - */ - -enum pmcstat_image_type { - PMCSTAT_IMAGE_UNKNOWN = 0, /* never looked at the image */ - PMCSTAT_IMAGE_INDETERMINABLE, /* can't tell what the image is */ - PMCSTAT_IMAGE_ELF32, /* ELF 32 bit object */ - PMCSTAT_IMAGE_ELF64, /* ELF 64 bit object */ - PMCSTAT_IMAGE_AOUT /* AOUT object */ -}; - -struct pmcstat_image { - LIST_ENTRY(pmcstat_image) pi_next; /* hash link */ - TAILQ_ENTRY(pmcstat_image) pi_lru; /* LRU list */ - pmcstat_interned_string pi_execpath; /* cookie */ - pmcstat_interned_string pi_samplename; /* sample path name */ - pmcstat_interned_string pi_fullpath; /* path to FS object */ - - enum pmcstat_image_type pi_type; /* executable type */ - - /* - * Executables have pi_start and pi_end; these are zero - * for shared libraries. - */ - uintfptr_t pi_start; /* start address (inclusive) */ - uintfptr_t pi_end; /* end address (exclusive) */ - uintfptr_t pi_entry; /* entry address */ - uintfptr_t pi_vaddr; /* virtual address where loaded */ - int pi_isdynamic; /* whether a dynamic object */ - int pi_iskernelmodule; - pmcstat_interned_string pi_dynlinkerpath; /* path in .interp */ - - /* All symbols associated with this object. */ - struct pmcstat_symbol *pi_symbols; - size_t pi_symcount; - - /* - * An image can be associated with one or more gmon.out files; - * one per PMC. - */ - LIST_HEAD(,pmcstat_gmonfile) pi_gmlist; -}; +struct pmcstat_pmcs pmcstat_pmcs = LIST_HEAD_INITIALIZER(pmcstat_pmcs); /* * All image descriptors are kept in a hash table. */ -static LIST_HEAD(,pmcstat_image) pmcstat_image_hash[PMCSTAT_NHASH]; - -/* - * A 'pmcstat_pcmap' structure maps a virtual address range to an - * underlying 'pmcstat_image' descriptor. - */ -struct pmcstat_pcmap { - TAILQ_ENTRY(pmcstat_pcmap) ppm_next; - uintfptr_t ppm_lowpc; - uintfptr_t ppm_highpc; - struct pmcstat_image *ppm_image; -}; +struct pmcstat_image_hash_list pmcstat_image_hash[PMCSTAT_NHASH]; /* - * A 'pmcstat_process' structure models processes. Each process is - * associated with a set of pmcstat_pcmap structures that map - * addresses inside it to executable objects. This set is implemented - * as a list, kept sorted in ascending order of mapped addresses. - * - * 'pp_pid' holds the pid of the process. When a process exits, the - * 'pp_isactive' field is set to zero, but the process structure is - * not immediately reclaimed because there may still be samples in the - * log for this process. + * All process descriptors are kept in a hash table. */ +struct pmcstat_process_hash_list pmcstat_process_hash[PMCSTAT_NHASH]; -struct pmcstat_process { - LIST_ENTRY(pmcstat_process) pp_next; /* hash-next */ - pid_t pp_pid; /* associated pid */ - int pp_isactive; /* whether active */ - uintfptr_t pp_entryaddr; /* entry address */ - TAILQ_HEAD(,pmcstat_pcmap) pp_map; /* address range map */ -}; +struct pmcstat_stats pmcstat_stats; /* statistics */ -/* - * All process descriptors are kept in a hash table. - */ -static LIST_HEAD(,pmcstat_process) pmcstat_process_hash[PMCSTAT_NHASH]; +struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */ -static struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */ +#include "pmcpl_gprof.h" +#include "pmcpl_callgraph.h" +#include "pmcpl_annotate.h" +#include "pmcpl_calltree.h" -/* - * Each function symbol tracked by pmcstat(8). - */ +struct pmc_plugins { + const char *pl_name; /* name */ -struct pmcstat_symbol { - pmcstat_interned_string ps_name; - uint64_t ps_start; - uint64_t ps_end; -}; + /* configure */ + int (*pl_configure)(char *opt); -/* - * Each call graph node is tracked by a pmcstat_cgnode struct. - */ + /* init and shutdown */ + int (*pl_init)(void); + void (*pl_shutdown)(FILE *mf); -struct pmcstat_cgnode { - struct pmcstat_image *pcg_image; - uintfptr_t pcg_func; - uint32_t pcg_count; - uint32_t pcg_nchildren; - LIST_ENTRY(pmcstat_cgnode) pcg_sibling; - LIST_HEAD(,pmcstat_cgnode) pcg_children; -}; + /* sample processing */ + void (*pl_process)(struct pmcstat_process *pp, + struct pmcstat_pmcrecord *pmcr, uint32_t nsamples, + uintfptr_t *cc, int usermode, uint32_t cpu); -struct pmcstat_cgnode_hash { - struct pmcstat_cgnode *pch_cgnode; - uint32_t pch_pmcid; - LIST_ENTRY(pmcstat_cgnode_hash) pch_next; -}; + /* image */ + void (*pl_initimage)(struct pmcstat_image *pi); + void (*pl_shutdownimage)(struct pmcstat_image *pi); -static int pmcstat_cgnode_hash_count; -static pmcstat_interned_string pmcstat_previous_filename_printed; + /* pmc */ + void (*pl_newpmc)(pmcstat_interned_string ps, + struct pmcstat_pmcrecord *pr); + + /* top display */ + void (*pl_topdisplay)(void); -/* - * The toplevel CG nodes (i.e., with rank == 0) are placed in a hash table. - */ + /* top keypress */ + int (*pl_topkeypress)(int c, WINDOW *w); -static LIST_HEAD(,pmcstat_cgnode_hash) pmcstat_cgnode_hash[PMCSTAT_NHASH]; +} plugins[] = { + { + .pl_name = "none", + }, + { + .pl_name = "callgraph", + .pl_init = pmcpl_cg_init, + .pl_shutdown = pmcpl_cg_shutdown, + .pl_process = pmcpl_cg_process, + .pl_topkeypress = pmcpl_cg_topkeypress, + .pl_topdisplay = pmcpl_cg_topdisplay + }, + { + .pl_name = "gprof", + .pl_shutdown = pmcpl_gmon_shutdown, + .pl_process = pmcpl_gmon_process, + .pl_initimage = pmcpl_gmon_initimage, + .pl_shutdownimage = pmcpl_gmon_shutdownimage, + .pl_newpmc = pmcpl_gmon_newpmc + }, + { + .pl_name = "annotate", + .pl_process = pmcpl_annotate_process + }, + { + .pl_name = "calltree", + .pl_configure = pmcpl_ct_configure, + .pl_init = pmcpl_ct_init, + .pl_shutdown = pmcpl_ct_shutdown, + .pl_process = pmcpl_ct_process, + .pl_topkeypress = pmcpl_ct_topkeypress, + .pl_topdisplay = pmcpl_ct_topdisplay + }, + { + .pl_name = NULL + } +}; -/* Misc. statistics */ -static struct pmcstat_stats { - int ps_exec_aout; /* # a.out executables seen */ - int ps_exec_elf; /* # elf executables seen */ - int ps_exec_errors; /* # errors processing executables */ - int ps_exec_indeterminable; /* # unknown executables seen */ - int ps_samples_total; /* total number of samples processed */ - int ps_samples_skipped; /* #samples filtered out for any reason */ - int ps_samples_unknown_offset; /* #samples of rank 0 not in a map */ - int ps_samples_indeterminable; /* #samples in indeterminable images */ - int ps_callchain_dubious_frames;/* #dubious frame pointers seen */ -} pmcstat_stats; +int pmcstat_mergepmc; +int pmcstat_pmcinfilter = 0; /* PMC filter for top mode. */ +float pmcstat_threshold = 0.5; /* Cost filter for top mode. */ /* * Prototypes */ -static void pmcstat_gmon_create_file(struct pmcstat_gmonfile *_pgf, - struct pmcstat_image *_image); -static pmcstat_interned_string pmcstat_gmon_create_name(const char *_sd, - struct pmcstat_image *_img, pmc_id_t _pmcid); -static void pmcstat_gmon_map_file(struct pmcstat_gmonfile *_pgf); -static void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf); - -static void pmcstat_image_determine_type(struct pmcstat_image *_image, - struct pmcstat_args *_a); -static struct pmcstat_gmonfile *pmcstat_image_find_gmonfile(struct - pmcstat_image *_i, pmc_id_t _id); static struct pmcstat_image *pmcstat_image_from_path(pmcstat_interned_string _path, int _iskernelmodule); -static void pmcstat_image_get_aout_params(struct pmcstat_image *_image, - struct pmcstat_args *_a); -static void pmcstat_image_get_elf_params(struct pmcstat_image *_image, - struct pmcstat_args *_a); -static void pmcstat_image_increment_bucket(struct pmcstat_pcmap *_pcm, - uintfptr_t _pc, pmc_id_t _pmcid, struct pmcstat_args *_a); +static void pmcstat_image_get_aout_params(struct pmcstat_image *_image); +static void pmcstat_image_get_elf_params(struct pmcstat_image *_image); static void pmcstat_image_link(struct pmcstat_process *_pp, struct pmcstat_image *_i, uintfptr_t _lpc); static void pmcstat_pmcid_add(pmc_id_t _pmcid, - pmcstat_interned_string _name, struct pmcstat_args *_a); -static const char *pmcstat_pmcid_to_name(pmc_id_t _pmcid); + pmcstat_interned_string _name); static void pmcstat_process_aout_exec(struct pmcstat_process *_pp, - struct pmcstat_image *_image, uintfptr_t _entryaddr, - struct pmcstat_args *_a); + struct pmcstat_image *_image, uintfptr_t _entryaddr); static void pmcstat_process_elf_exec(struct pmcstat_process *_pp, - struct pmcstat_image *_image, uintfptr_t _entryaddr, - struct pmcstat_args *_a); + struct pmcstat_image *_image, uintfptr_t _entryaddr); static void pmcstat_process_exec(struct pmcstat_process *_pp, - pmcstat_interned_string _path, uintfptr_t _entryaddr, - struct pmcstat_args *_ao); + pmcstat_interned_string _path, uintfptr_t _entryaddr); static struct pmcstat_process *pmcstat_process_lookup(pid_t _pid, int _allocate); -static struct pmcstat_pcmap *pmcstat_process_find_map( - struct pmcstat_process *_p, uintfptr_t _pc); - static int pmcstat_string_compute_hash(const char *_string); static void pmcstat_string_initialize(void); -static pmcstat_interned_string pmcstat_string_intern(const char *_s); -static pmcstat_interned_string pmcstat_string_lookup(const char *_s); static int pmcstat_string_lookup_hash(pmcstat_interned_string _is); static void pmcstat_string_shutdown(void); -static const char *pmcstat_string_unintern(pmcstat_interned_string _is); - /* * A simple implementation of interned strings. Each interned string @@ -375,6 +265,16 @@ struct pmcstat_string { static LIST_HEAD(,pmcstat_string) pmcstat_string_hash[PMCSTAT_NHASH]; /* + * PMC count. + */ +int pmcstat_npmcs; + +/* + * PMC Top mode pause state. + */ +int pmcstat_pause; + +/* * Compute a 'hash' value for a string. */ @@ -394,7 +294,7 @@ pmcstat_string_compute_hash(const char *s) * interned structure. */ -static pmcstat_interned_string +pmcstat_interned_string pmcstat_string_intern(const char *s) { struct pmcstat_string *ps; @@ -416,7 +316,7 @@ pmcstat_string_intern(const char *s) return ((pmcstat_interned_string) ps); } -static const char * +const char * pmcstat_string_unintern(pmcstat_interned_string str) { const char *s; @@ -425,7 +325,7 @@ pmcstat_string_unintern(pmcstat_interned_string str) return (s); } -static pmcstat_interned_string +pmcstat_interned_string pmcstat_string_lookup(const char *s) { struct pmcstat_string *ps; @@ -483,163 +383,13 @@ pmcstat_string_shutdown(void) } /* - * Create a gmon.out file and size it. - */ - -static void -pmcstat_gmon_create_file(struct pmcstat_gmonfile *pgf, - struct pmcstat_image *image) -{ - int fd; - size_t count; - struct gmonhdr gm; - const char *pathname; - char buffer[DEFAULT_BUFFER_SIZE]; - - pathname = pmcstat_string_unintern(pgf->pgf_name); - if ((fd = open(pathname, O_RDWR|O_NOFOLLOW|O_CREAT, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) - err(EX_OSERR, "ERROR: Cannot open \"%s\"", pathname); - - gm.lpc = image->pi_start; - gm.hpc = image->pi_end; - gm.ncnt = (pgf->pgf_nbuckets * sizeof(HISTCOUNTER)) + - sizeof(struct gmonhdr); - gm.version = GMONVERSION; - gm.profrate = 0; /* use ticks */ - gm.histcounter_type = 0; /* compatibility with moncontrol() */ - gm.spare[0] = gm.spare[1] = 0; - - /* Write out the gmon header */ - if (write(fd, &gm, sizeof(gm)) < 0) - goto error; - - /* Zero fill the samples[] array */ - (void) memset(buffer, 0, sizeof(buffer)); - - count = pgf->pgf_ndatabytes - sizeof(struct gmonhdr); - while (count > sizeof(buffer)) { - if (write(fd, &buffer, sizeof(buffer)) < 0) - goto error; - count -= sizeof(buffer); - } - - if (write(fd, &buffer, count) < 0) - goto error; - - (void) close(fd); - - return; - - error: - err(EX_OSERR, "ERROR: Cannot write \"%s\"", pathname); -} - -/* - * Determine the full pathname of a gmon.out file for a given - * (image,pmcid) combination. Return the interned string. - */ - -pmcstat_interned_string -pmcstat_gmon_create_name(const char *samplesdir, struct pmcstat_image *image, - pmc_id_t pmcid) -{ - const char *pmcname; - char fullpath[PATH_MAX]; - - pmcname = pmcstat_pmcid_to_name(pmcid); - - (void) snprintf(fullpath, sizeof(fullpath), - "%s/%s/%s", samplesdir, pmcname, - pmcstat_string_unintern(image->pi_samplename)); - - return (pmcstat_string_intern(fullpath)); -} - - -/* - * Mmap in a gmon.out file for processing. - */ - -static void -pmcstat_gmon_map_file(struct pmcstat_gmonfile *pgf) -{ - int fd; - const char *pathname; - - pathname = pmcstat_string_unintern(pgf->pgf_name); - - /* the gmon.out file must already exist */ - if ((fd = open(pathname, O_RDWR | O_NOFOLLOW, 0)) < 0) - err(EX_OSERR, "ERROR: cannot open \"%s\"", pathname); - - pgf->pgf_gmondata = mmap(NULL, pgf->pgf_ndatabytes, - PROT_READ|PROT_WRITE, MAP_NOSYNC|MAP_SHARED, fd, 0); - - if (pgf->pgf_gmondata == MAP_FAILED) - err(EX_OSERR, "ERROR: cannot map \"%s\"", pathname); - - (void) close(fd); -} - -/* - * Unmap a gmon.out file after sync'ing its data to disk. - */ - -static void -pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *pgf) -{ - (void) msync(pgf->pgf_gmondata, pgf->pgf_ndatabytes, - MS_SYNC); - (void) munmap(pgf->pgf_gmondata, pgf->pgf_ndatabytes); - pgf->pgf_gmondata = NULL; -} - -static void -pmcstat_gmon_append_arc(struct pmcstat_image *image, pmc_id_t pmcid, - uintptr_t rawfrom, uintptr_t rawto, uint32_t count) -{ - struct rawarc arc; /* from <sys/gmon.h> */ - const char *pathname; - struct pmcstat_gmonfile *pgf; - - if ((pgf = pmcstat_image_find_gmonfile(image, pmcid)) == NULL) - return; - - if (pgf->pgf_file == NULL) { - pathname = pmcstat_string_unintern(pgf->pgf_name); - if ((pgf->pgf_file = fopen(pathname, "a")) == NULL) - return; - } - - arc.raw_frompc = rawfrom + image->pi_vaddr; - arc.raw_selfpc = rawto + image->pi_vaddr; - arc.raw_count = count; - - (void) fwrite(&arc, sizeof(arc), 1, pgf->pgf_file); - -} - -static struct pmcstat_gmonfile * -pmcstat_image_find_gmonfile(struct pmcstat_image *image, pmc_id_t pmcid) -{ - struct pmcstat_gmonfile *pgf; - LIST_FOREACH(pgf, &image->pi_gmlist, pgf_next) - if (pgf->pgf_pmcid == pmcid) - return (pgf); - return (NULL); -} - - -/* * Determine whether a given executable image is an A.OUT object, and * if so, fill in its parameters from the text file. * Sets image->pi_type. */ static void -pmcstat_image_get_aout_params(struct pmcstat_image *image, - struct pmcstat_args *a) +pmcstat_image_get_aout_params(struct pmcstat_image *image) { int fd; ssize_t nbytes; @@ -655,7 +405,7 @@ pmcstat_image_get_aout_params(struct pmcstat_image *image, "unsupported \"%s\"", path); (void) snprintf(buffer, sizeof(buffer), "%s%s", - a->pa_fsroot, path); + args.pa_fsroot, path); if ((fd = open(buffer, O_RDONLY, 0)) < 0 || (nbytes = read(fd, &ex, sizeof(ex))) < 0) { @@ -702,7 +452,7 @@ pmcstat_symbol_compare(const void *a, const void *b) * Map an address to a symbol in an image. */ -static struct pmcstat_symbol * +struct pmcstat_symbol * pmcstat_symbol_search(struct pmcstat_image *image, uintfptr_t addr) { struct pmcstat_symbol sym; @@ -825,12 +575,12 @@ pmcstat_image_add_symbols(struct pmcstat_image *image, Elf *e, */ static void -pmcstat_image_get_elf_params(struct pmcstat_image *image, - struct pmcstat_args *a) +pmcstat_image_get_elf_params(struct pmcstat_image *image) { int fd; size_t i, nph, nsh; const char *path, *elfbase; + char *p, *endp; uintfptr_t minva, maxva; Elf *e; Elf_Scn *scn; @@ -858,10 +608,10 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image, */ if (image->pi_iskernelmodule) (void) snprintf(buffer, sizeof(buffer), "%s%s/%s", - a->pa_fsroot, a->pa_kernel, path); + args.pa_fsroot, args.pa_kernel, path); else (void) snprintf(buffer, sizeof(buffer), "%s%s", - a->pa_fsroot, path); + args.pa_fsroot, path); e = NULL; if ((fd = open(buffer, O_RDONLY, 0)) < 0 || @@ -960,6 +710,14 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image, image->pi_type = image_type; image->pi_fullpath = pmcstat_string_intern(buffer); + /* Build display name + */ + endp = buffer; + for (p = buffer; *p; p++) + if (*p == '/') + endp = p+1; + image->pi_name = pmcstat_string_intern(endp); + done: (void) elf_end(e); if (fd >= 0) @@ -972,17 +730,16 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image, * If no handler claims the image, set its type to 'INDETERMINABLE'. */ -static void -pmcstat_image_determine_type(struct pmcstat_image *image, - struct pmcstat_args *a) +void +pmcstat_image_determine_type(struct pmcstat_image *image) { assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN); /* Try each kind of handler in turn */ if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_get_elf_params(image, a); + pmcstat_image_get_elf_params(image); if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_get_aout_params(image, a); + pmcstat_image_get_aout_params(image); /* * Otherwise, remember that we tried to determine @@ -1006,10 +763,8 @@ static struct pmcstat_image * pmcstat_image_from_path(pmcstat_interned_string internedpath, int iskernelmodule) { - int count, hash, nlen; + int hash; struct pmcstat_image *pi; - char *sn; - char name[NAME_MAX]; hash = pmcstat_string_lookup_hash(internedpath); @@ -1038,50 +793,12 @@ pmcstat_image_from_path(pmcstat_interned_string internedpath, pi->pi_dynlinkerpath = NULL; pi->pi_symbols = NULL; pi->pi_symcount = 0; + pi->pi_addr2line = NULL; - /* - * Look for a suitable name for the sample files associated - * with this image: if `basename(path)`+".gmon" is available, - * we use that, otherwise we try iterating through - * `basename(path)`+ "~" + NNN + ".gmon" till we get a free - * entry. - */ - if ((sn = basename(pmcstat_string_unintern(internedpath))) == NULL) - err(EX_OSERR, "ERROR: Cannot process \"%s\"", - pmcstat_string_unintern(internedpath)); - - nlen = strlen(sn); - nlen = min(nlen, (int) (sizeof(name) - sizeof(".gmon"))); - - snprintf(name, sizeof(name), "%.*s.gmon", nlen, sn); - - /* try use the unabridged name first */ - if (pmcstat_string_lookup(name) == NULL) - pi->pi_samplename = pmcstat_string_intern(name); - else { - /* - * Otherwise use a prefix from the original name and - * upto 3 digits. - */ - nlen = strlen(sn); - nlen = min(nlen, (int) (sizeof(name)-sizeof("~NNN.gmon"))); - count = 0; - do { - if (++count > 999) - errx(EX_CANTCREAT, "ERROR: cannot create a " - "gmon file for \"%s\"", name); - snprintf(name, sizeof(name), "%.*s~%3.3d.gmon", - nlen, sn, count); - if (pmcstat_string_lookup(name) == NULL) { - pi->pi_samplename = - pmcstat_string_intern(name); - count = 0; - } - } while (count > 0); - } - - - LIST_INIT(&pi->pi_gmlist); + if (plugins[args.pa_pplugin].pl_initimage != NULL) + plugins[args.pa_pplugin].pl_initimage(pi); + if (plugins[args.pa_plugin].pl_initimage != NULL) + plugins[args.pa_plugin].pl_initimage(pi); LIST_INSERT_HEAD(&pmcstat_image_hash[hash], pi, pi_next); @@ -1089,94 +806,6 @@ pmcstat_image_from_path(pmcstat_interned_string internedpath, } /* - * Increment the bucket in the gmon.out file corresponding to 'pmcid' - * and 'pc'. - */ - -static void -pmcstat_image_increment_bucket(struct pmcstat_pcmap *map, uintfptr_t pc, - pmc_id_t pmcid, struct pmcstat_args *a) -{ - struct pmcstat_image *image; - struct pmcstat_gmonfile *pgf; - uintfptr_t bucket; - HISTCOUNTER *hc; - - assert(pc >= map->ppm_lowpc && pc < map->ppm_highpc); - - image = map->ppm_image; - - /* - * If this is the first time we are seeing a sample for - * this executable image, try determine its parameters. - */ - if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_determine_type(image, a); - - assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); - - /* Ignore samples in images that we know nothing about. */ - if (image->pi_type == PMCSTAT_IMAGE_INDETERMINABLE) { - pmcstat_stats.ps_samples_indeterminable++; - return; - } - - /* - * Find the gmon file corresponding to 'pmcid', creating it if - * needed. - */ - pgf = pmcstat_image_find_gmonfile(image, pmcid); - if (pgf == NULL) { - if ((pgf = calloc(1, sizeof(*pgf))) == NULL) - err(EX_OSERR, "ERROR:"); - - pgf->pgf_gmondata = NULL; /* mark as unmapped */ - pgf->pgf_name = pmcstat_gmon_create_name(a->pa_samplesdir, - image, pmcid); - pgf->pgf_pmcid = pmcid; - assert(image->pi_end > image->pi_start); - pgf->pgf_nbuckets = (image->pi_end - image->pi_start) / - FUNCTION_ALIGNMENT; /* see <machine/profile.h> */ - pgf->pgf_ndatabytes = sizeof(struct gmonhdr) + - pgf->pgf_nbuckets * sizeof(HISTCOUNTER); - pgf->pgf_nsamples = 0; - pgf->pgf_file = NULL; - - pmcstat_gmon_create_file(pgf, image); - - LIST_INSERT_HEAD(&image->pi_gmlist, pgf, pgf_next); - } - - /* - * Map the gmon file in if needed. It may have been mapped - * out under memory pressure. - */ - if (pgf->pgf_gmondata == NULL) - pmcstat_gmon_map_file(pgf); - - assert(pgf->pgf_gmondata != NULL); - - /* - * - */ - - bucket = (pc - map->ppm_lowpc) / FUNCTION_ALIGNMENT; - - assert(bucket < pgf->pgf_nbuckets); - - hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + - sizeof(struct gmonhdr)); - - /* saturating add */ - if (hc[bucket] < 0xFFFFU) /* XXX tie this to sizeof(HISTCOUNTER) */ - hc[bucket]++; - else /* mark that an overflow occurred */ - pgf->pgf_overflow = 1; - - pgf->pgf_nsamples++; -} - -/* * Record the fact that PC values from 'start' to 'end' come from * image 'image'. */ @@ -1284,72 +913,181 @@ pmcstat_image_unmap(struct pmcstat_process *pp, uintfptr_t start, } /* + * Resolve file name and line number for the given address. + */ +int +pmcstat_image_addr2line(struct pmcstat_image *image, uintfptr_t addr, + char *sourcefile, size_t sourcefile_len, unsigned *sourceline, + char *funcname, size_t funcname_len) +{ + static int addr2line_warn = 0; + + char *sep, cmdline[PATH_MAX], imagepath[PATH_MAX]; + int fd; + + if (image->pi_addr2line == NULL) { + snprintf(imagepath, sizeof(imagepath), "%s.symbols", + pmcstat_string_unintern(image->pi_fullpath)); + fd = open(imagepath, O_RDONLY); + if (fd < 0) { + snprintf(imagepath, sizeof(imagepath), "%s", + pmcstat_string_unintern(image->pi_fullpath)); + } else + close(fd); + snprintf(cmdline, sizeof(cmdline), "addr2line -Cfe \"%s\"", + imagepath); + image->pi_addr2line = popen(cmdline, "r+"); + if (image->pi_addr2line == NULL) { + if (!addr2line_warn) { + addr2line_warn = 1; + warnx("WARNING: addr2line is needed" + "for source code information."); + } + return (0); + } + } + + if (feof(image->pi_addr2line) || ferror(image->pi_addr2line)) { + warnx("WARNING: addr2line pipe error"); + pclose(image->pi_addr2line); + image->pi_addr2line = NULL; + return (0); + } + + fprintf(image->pi_addr2line, "%p\n", (void *)addr); + + if (fgets(funcname, funcname_len, image->pi_addr2line) == NULL) { + warnx("WARNING: addr2line function name read error"); + return (0); + } + sep = strchr(funcname, '\n'); + if (sep != NULL) + *sep = '\0'; + + if (fgets(sourcefile, sourcefile_len, image->pi_addr2line) == NULL) { + warnx("WARNING: addr2line source file read error"); + return (0); + } + sep = strchr(sourcefile, ':'); + if (sep == NULL) { + warnx("WARNING: addr2line source line separator missing"); + return (0); + } + *sep = '\0'; + *sourceline = atoi(sep+1); + if (*sourceline == 0) + return (0); + + return (1); +} + +/* * Add a {pmcid,name} mapping. */ static void -pmcstat_pmcid_add(pmc_id_t pmcid, pmcstat_interned_string ps, - struct pmcstat_args *a) +pmcstat_pmcid_add(pmc_id_t pmcid, pmcstat_interned_string ps) { - struct pmcstat_pmcrecord *pr; - struct stat st; - char fullpath[PATH_MAX]; + struct pmcstat_pmcrecord *pr, *prm; /* Replace an existing name for the PMC. */ + prm = NULL; LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) - if (pr->pr_pmcid == pmcid) { - pr->pr_pmcname = ps; - return; - } + if (pr->pr_pmcid == pmcid) { + pr->pr_pmcname = ps; + return; + } else if (pr->pr_pmcname == ps) + prm = pr; /* - * Otherwise, allocate a new descriptor and create the - * appropriate directory to hold gmon.out files. + * Otherwise, allocate a new descriptor and call the + * plugins hook. */ if ((pr = malloc(sizeof(*pr))) == NULL) err(EX_OSERR, "ERROR: Cannot allocate pmc record"); pr->pr_pmcid = pmcid; pr->pr_pmcname = ps; - LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); + pr->pr_pmcin = pmcstat_npmcs++; + pr->pr_merge = prm == NULL ? pr : prm; - (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", a->pa_samplesdir, - pmcstat_string_unintern(ps)); - - /* If the path name exists, it should be a directory */ - if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) - return; + LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); - if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) - err(EX_OSERR, "ERROR: Cannot create directory \"%s\"", - fullpath); + if (plugins[args.pa_pplugin].pl_newpmc != NULL) + plugins[args.pa_pplugin].pl_newpmc(ps, pr); + if (plugins[args.pa_plugin].pl_newpmc != NULL) + plugins[args.pa_plugin].pl_newpmc(ps, pr); } /* * Given a pmcid in use, find its human-readable name. */ -static const char * +const char * pmcstat_pmcid_to_name(pmc_id_t pmcid) { struct pmcstat_pmcrecord *pr; - char fullpath[PATH_MAX]; LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) if (pr->pr_pmcid == pmcid) return (pmcstat_string_unintern(pr->pr_pmcname)); - /* create a default name and add this entry */ - if ((pr = malloc(sizeof(*pr))) == NULL) - err(EX_OSERR, "ERROR: "); - pr->pr_pmcid = pmcid; + err(EX_SOFTWARE, "ERROR: cannot find pmcid"); + return NULL; +} - (void) snprintf(fullpath, sizeof(fullpath), "%X", (unsigned int) pmcid); - pr->pr_pmcname = pmcstat_string_intern(fullpath); +/* + * Convert PMC index to name. + */ - LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); +const char * +pmcstat_pmcindex_to_name(int pmcin) +{ + struct pmcstat_pmcrecord *pr; + + LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) + if (pr->pr_pmcin == pmcin) + return pmcstat_string_unintern(pr->pr_pmcname); + + err(EX_SOFTWARE, "ERROR: cannot find pmcid name"); + return NULL; +} + +/* + * Return PMC record with given index. + */ + +struct pmcstat_pmcrecord * +pmcstat_pmcindex_to_pmcr(int pmcin) +{ + struct pmcstat_pmcrecord *pr; + + LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) + if (pr->pr_pmcin == pmcin) + return pr; - return (pmcstat_string_unintern(pr->pr_pmcname)); + err(EX_SOFTWARE, "ERROR: invalid pmcindex"); + return NULL; +} + +/* + * Get PMC record by id, apply merge policy. + */ + +static struct pmcstat_pmcrecord * +pmcstat_lookup_pmcid(pmc_id_t pmcid) +{ + struct pmcstat_pmcrecord *pr; + + LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) { + if (pr->pr_pmcid == pmcid) { + if (pmcstat_mergepmc) + return pr->pr_merge; + return pr; + } + } + + return NULL; } /* @@ -1358,13 +1096,11 @@ pmcstat_pmcid_to_name(pmc_id_t pmcid) static void pmcstat_process_aout_exec(struct pmcstat_process *pp, - struct pmcstat_image *image, uintfptr_t entryaddr, - struct pmcstat_args *a) + struct pmcstat_image *image, uintfptr_t entryaddr) { (void) pp; (void) image; (void) entryaddr; - (void) a; /* TODO Implement a.out handling */ } @@ -1374,8 +1110,7 @@ pmcstat_process_aout_exec(struct pmcstat_process *pp, static void pmcstat_process_elf_exec(struct pmcstat_process *pp, - struct pmcstat_image *image, uintfptr_t entryaddr, - struct pmcstat_args *a) + struct pmcstat_image *image, uintfptr_t entryaddr) { uintmax_t libstart; struct pmcstat_image *rtldimage; @@ -1414,8 +1149,7 @@ pmcstat_process_elf_exec(struct pmcstat_process *pp, * this we can figure out the address where the * runtime loader's file object had been mapped to. */ - rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath, - 0); + rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath, 0); if (rtldimage == NULL) { warnx("WARNING: Cannot find image for \"%s\".", pmcstat_string_unintern(image->pi_dynlinkerpath)); @@ -1424,7 +1158,7 @@ pmcstat_process_elf_exec(struct pmcstat_process *pp, } if (rtldimage->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_get_elf_params(rtldimage, a); + pmcstat_image_get_elf_params(rtldimage); if (rtldimage->pi_type != PMCSTAT_IMAGE_ELF32 && rtldimage->pi_type != PMCSTAT_IMAGE_ELF64) { @@ -1495,8 +1229,7 @@ pmcstat_process_lookup(pid_t pid, int allocate) static void pmcstat_process_exec(struct pmcstat_process *pp, - pmcstat_interned_string path, uintfptr_t entryaddr, - struct pmcstat_args *a) + pmcstat_interned_string path, uintfptr_t entryaddr) { struct pmcstat_image *image; @@ -1506,7 +1239,7 @@ pmcstat_process_exec(struct pmcstat_process *pp, } if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_determine_type(image, a); + pmcstat_image_determine_type(image); assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); @@ -1514,12 +1247,12 @@ pmcstat_process_exec(struct pmcstat_process *pp, case PMCSTAT_IMAGE_ELF32: case PMCSTAT_IMAGE_ELF64: pmcstat_stats.ps_exec_elf++; - pmcstat_process_elf_exec(pp, image, entryaddr, a); + pmcstat_process_elf_exec(pp, image, entryaddr); break; case PMCSTAT_IMAGE_AOUT: pmcstat_stats.ps_exec_aout++; - pmcstat_process_aout_exec(pp, image, entryaddr, a); + pmcstat_process_aout_exec(pp, image, entryaddr); break; case PMCSTAT_IMAGE_INDETERMINABLE: @@ -1537,7 +1270,7 @@ pmcstat_process_exec(struct pmcstat_process *pp, * Find the map entry associated with process 'p' at PC value 'pc'. */ -static struct pmcstat_pcmap * +struct pmcstat_pcmap * pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc) { struct pmcstat_pcmap *ppm; @@ -1552,444 +1285,36 @@ pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc) return (NULL); } -static struct pmcstat_cgnode * -pmcstat_cgnode_allocate(struct pmcstat_image *image, uintfptr_t pc) -{ - struct pmcstat_cgnode *cg; - - if ((cg = malloc(sizeof(*cg))) == NULL) - err(EX_OSERR, "ERROR: Cannot allocate callgraph node"); - - cg->pcg_image = image; - cg->pcg_func = pc; - - cg->pcg_count = 0; - cg->pcg_nchildren = 0; - LIST_INIT(&cg->pcg_children); - - return (cg); -} - -/* - * Free a node and its children. - */ -static void -pmcstat_cgnode_free(struct pmcstat_cgnode *cg) -{ - struct pmcstat_cgnode *cgc, *cgtmp; - - LIST_FOREACH_SAFE(cgc, &cg->pcg_children, pcg_sibling, cgtmp) - pmcstat_cgnode_free(cgc); - free(cg); -} - -/* - * Look for a callgraph node associated with pmc `pmcid' in the global - * hash table that corresponds to the given `pc' value in the process - * `pp'. - */ -static struct pmcstat_cgnode * -pmcstat_cgnode_hash_lookup_pc(struct pmcstat_process *pp, uint32_t pmcid, - uintfptr_t pc, int usermode) -{ - struct pmcstat_pcmap *ppm; - struct pmcstat_symbol *sym; - struct pmcstat_image *image; - struct pmcstat_cgnode *cg; - struct pmcstat_cgnode_hash *h; - uintfptr_t loadaddress; - unsigned int i, hash; - - ppm = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, pc); - if (ppm == NULL) - return (NULL); - - image = ppm->ppm_image; - - loadaddress = ppm->ppm_lowpc + image->pi_vaddr - image->pi_start; - pc -= loadaddress; /* Convert to an offset in the image. */ - - /* - * Try determine the function at this offset. If we can't - * find a function round leave the `pc' value alone. - */ - if ((sym = pmcstat_symbol_search(image, pc)) != NULL) - pc = sym->ps_start; - - for (hash = i = 0; i < sizeof(uintfptr_t); i++) - hash += (pc >> i) & 0xFF; - - hash &= PMCSTAT_HASH_MASK; - - cg = NULL; - LIST_FOREACH(h, &pmcstat_cgnode_hash[hash], pch_next) - { - if (h->pch_pmcid != pmcid) - continue; - - cg = h->pch_cgnode; - - assert(cg != NULL); - - if (cg->pcg_image == image && cg->pcg_func == pc) - return (cg); - } - - /* - * We haven't seen this (pmcid, pc) tuple yet, so allocate a - * new callgraph node and a new hash table entry for it. - */ - cg = pmcstat_cgnode_allocate(image, pc); - if ((h = malloc(sizeof(*h))) == NULL) - err(EX_OSERR, "ERROR: Could not allocate callgraph node"); - - h->pch_pmcid = pmcid; - h->pch_cgnode = cg; - LIST_INSERT_HEAD(&pmcstat_cgnode_hash[hash], h, pch_next); - - pmcstat_cgnode_hash_count++; - - return (cg); -} - -/* - * Compare two callgraph nodes for sorting. - */ -static int -pmcstat_cgnode_compare(const void *a, const void *b) -{ - const struct pmcstat_cgnode *const *pcg1, *const *pcg2, *cg1, *cg2; - - pcg1 = (const struct pmcstat_cgnode *const *) a; - cg1 = *pcg1; - pcg2 = (const struct pmcstat_cgnode *const *) b; - cg2 = *pcg2; - - /* Sort in reverse order */ - if (cg1->pcg_count < cg2->pcg_count) - return (1); - if (cg1->pcg_count > cg2->pcg_count) - return (-1); - return (0); -} - -/* - * Find (allocating if a needed) a callgraph node in the given - * parent with the same (image, pcoffset) pair. - */ - -static struct pmcstat_cgnode * -pmcstat_cgnode_find(struct pmcstat_cgnode *parent, struct pmcstat_image *image, - uintfptr_t pcoffset) -{ - struct pmcstat_cgnode *child; - - LIST_FOREACH(child, &parent->pcg_children, pcg_sibling) { - if (child->pcg_image == image && - child->pcg_func == pcoffset) - return (child); - } - - /* - * Allocate a new structure. - */ - - child = pmcstat_cgnode_allocate(image, pcoffset); - - /* - * Link it into the parent. - */ - LIST_INSERT_HEAD(&parent->pcg_children, child, pcg_sibling); - parent->pcg_nchildren++; - - return (child); -} - -/* - * Print one callgraph node. The output format is: - * - * indentation %(parent's samples) #nsamples function@object - */ -static void -pmcstat_cgnode_print(struct pmcstat_args *a, struct pmcstat_cgnode *cg, - int depth, uint32_t total) -{ - uint32_t n; - const char *space; - struct pmcstat_symbol *sym; - struct pmcstat_cgnode **sortbuffer, **cgn, *pcg; - - space = " "; - - if (depth > 0) - (void) fprintf(a->pa_graphfile, "%*s", depth, space); - - if (cg->pcg_count == total) - (void) fprintf(a->pa_graphfile, "100.0%% "); - else - (void) fprintf(a->pa_graphfile, "%05.2f%% ", - 100.0 * cg->pcg_count / total); - - n = fprintf(a->pa_graphfile, " [%u] ", cg->pcg_count); - - /* #samples is a 12 character wide field. */ - if (n < 12) - (void) fprintf(a->pa_graphfile, "%*s", 12 - n, space); - - if (depth > 0) - (void) fprintf(a->pa_graphfile, "%*s", depth, space); - - sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func); - if (sym) - (void) fprintf(a->pa_graphfile, "%s", - pmcstat_string_unintern(sym->ps_name)); - else - (void) fprintf(a->pa_graphfile, "%p", - (void *) (cg->pcg_image->pi_vaddr + cg->pcg_func)); - - if (pmcstat_previous_filename_printed != - cg->pcg_image->pi_fullpath) { - pmcstat_previous_filename_printed = cg->pcg_image->pi_fullpath; - (void) fprintf(a->pa_graphfile, " @ %s\n", - pmcstat_string_unintern( - pmcstat_previous_filename_printed)); - } else - (void) fprintf(a->pa_graphfile, "\n"); - - if (cg->pcg_nchildren == 0) - return; - - if ((sortbuffer = (struct pmcstat_cgnode **) - malloc(sizeof(struct pmcstat_cgnode *) * - cg->pcg_nchildren)) == NULL) - err(EX_OSERR, "ERROR: Cannot print callgraph"); - cgn = sortbuffer; - - LIST_FOREACH(pcg, &cg->pcg_children, pcg_sibling) - *cgn++ = pcg; - - assert(cgn - sortbuffer == (int) cg->pcg_nchildren); - - qsort(sortbuffer, cg->pcg_nchildren, sizeof(struct pmcstat_cgnode *), - pmcstat_cgnode_compare); - - for (cgn = sortbuffer, n = 0; n < cg->pcg_nchildren; n++, cgn++) - pmcstat_cgnode_print(a, *cgn, depth+1, cg->pcg_count); - - free(sortbuffer); -} - -/* - * Record a callchain. - */ - -static void -pmcstat_record_callchain(struct pmcstat_process *pp, uint32_t pmcid, - uint32_t nsamples, uintfptr_t *cc, int usermode, struct pmcstat_args *a) -{ - uintfptr_t pc, loadaddress; - uint32_t n; - struct pmcstat_image *image; - struct pmcstat_pcmap *ppm; - struct pmcstat_symbol *sym; - struct pmcstat_cgnode *parent, *child; - - /* - * Find the callgraph node recorded in the global hash table - * for this (pmcid, pc). - */ - - pc = cc[0]; - parent = pmcstat_cgnode_hash_lookup_pc(pp, pmcid, pc, usermode); - if (parent == NULL) { - pmcstat_stats.ps_callchain_dubious_frames++; - return; - } - - parent->pcg_count++; - - /* - * For each return address in the call chain record, subject - * to the maximum depth desired. - * - Find the image associated with the sample. Stop if there - * there is no valid image at that address. - * - Find the function that overlaps the return address. - * - If found: use the start address of the function. - * If not found (say an object's symbol table is not present or - * is incomplete), round down to th gprof bucket granularity. - * - Convert return virtual address to an offset in the image. - * - Look for a child with the same {offset,image} tuple, - * inserting one if needed. - * - Increment the count of occurrences of the child. - */ - - for (n = 1; n < (uint32_t) a->pa_graphdepth && n < nsamples; n++, - parent = child) { - pc = cc[n]; - - ppm = pmcstat_process_find_map(usermode ? pp : - pmcstat_kernproc, pc); - if (ppm == NULL) - return; - - image = ppm->ppm_image; - loadaddress = ppm->ppm_lowpc + image->pi_vaddr - - image->pi_start; - pc -= loadaddress; - - if ((sym = pmcstat_symbol_search(image, pc)) != NULL) - pc = sym->ps_start; - - child = pmcstat_cgnode_find(parent, image, pc); - child->pcg_count++; - } -} - -/* - * Printing a callgraph for a PMC. - */ -static void -pmcstat_callgraph_print_for_pmcid(struct pmcstat_args *a, - struct pmcstat_pmcrecord *pmcr) -{ - int n, nentries; - uint32_t nsamples, pmcid; - struct pmcstat_cgnode **sortbuffer, **cgn; - struct pmcstat_cgnode_hash *pch; - - /* - * We pull out all callgraph nodes in the top-level hash table - * with a matching PMC id. We then sort these based on the - * frequency of occurrence. Each callgraph node is then - * printed. - */ - - nsamples = 0; - pmcid = pmcr->pr_pmcid; - if ((sortbuffer = (struct pmcstat_cgnode **) - malloc(sizeof(struct pmcstat_cgnode *) * - pmcstat_cgnode_hash_count)) == NULL) - err(EX_OSERR, "ERROR: Cannot sort callgraph"); - cgn = sortbuffer; - - memset(sortbuffer, 0xFF, pmcstat_cgnode_hash_count * - sizeof(struct pmcstat_cgnode **)); - - for (n = 0; n < PMCSTAT_NHASH; n++) - LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) - if (pch->pch_pmcid == pmcid) { - nsamples += pch->pch_cgnode->pcg_count; - *cgn++ = pch->pch_cgnode; - } - - nentries = cgn - sortbuffer; - assert(nentries <= pmcstat_cgnode_hash_count); - - if (nentries == 0) - return; - - qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *), - pmcstat_cgnode_compare); - - (void) fprintf(a->pa_graphfile, - "@ %s [%u samples]\n\n", - pmcstat_string_unintern(pmcr->pr_pmcname), - nsamples); - - for (cgn = sortbuffer, n = 0; n < nentries; n++, cgn++) { - pmcstat_previous_filename_printed = NULL; - pmcstat_cgnode_print(a, *cgn, 0, nsamples); - (void) fprintf(a->pa_graphfile, "\n"); - } - - free(sortbuffer); -} - -/* - * Print out callgraphs. - */ - -static void -pmcstat_callgraph_print(struct pmcstat_args *a) -{ - struct pmcstat_pmcrecord *pmcr; - - LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) - pmcstat_callgraph_print_for_pmcid(a, pmcr); -} - -static void -pmcstat_cgnode_do_gmon_arcs(struct pmcstat_cgnode *cg, pmc_id_t pmcid) -{ - struct pmcstat_cgnode *cgc; - - /* - * Look for child nodes that belong to the same image. - */ - - LIST_FOREACH(cgc, &cg->pcg_children, pcg_sibling) { - if (cgc->pcg_image == cg->pcg_image) - pmcstat_gmon_append_arc(cg->pcg_image, pmcid, - cgc->pcg_func, cg->pcg_func, cgc->pcg_count); - if (cgc->pcg_nchildren > 0) - pmcstat_cgnode_do_gmon_arcs(cgc, pmcid); - } -} - -static void -pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmc_id_t pmcid) -{ - int n; - struct pmcstat_cgnode_hash *pch; - - for (n = 0; n < PMCSTAT_NHASH; n++) - LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) - if (pch->pch_pmcid == pmcid && - pch->pch_cgnode->pcg_nchildren > 1) - pmcstat_cgnode_do_gmon_arcs(pch->pch_cgnode, - pmcid); -} - - -static void -pmcstat_callgraph_do_gmon_arcs(void) -{ - struct pmcstat_pmcrecord *pmcr; - - LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) - pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmcr->pr_pmcid); -} - /* * Convert a hwpmc(4) log to profile information. A system-wide * callgraph is generated if FLAG_DO_CALLGRAPHS is set. gmon.out * files usable by gprof(1) are created if FLAG_DO_GPROF is set. */ static int -pmcstat_analyze_log(struct pmcstat_args *a) +pmcstat_analyze_log(void) { uint32_t cpu, cpuflags; - uintfptr_t pc, newpc; + uintfptr_t pc; pid_t pid; struct pmcstat_image *image; - struct pmcstat_symbol *sym; struct pmcstat_process *pp, *ppnew; struct pmcstat_pcmap *ppm, *ppmtmp; struct pmclog_ev ev; + struct pmcstat_pmcrecord *pmcr; pmcstat_interned_string image_path; - assert(a->pa_flags & FLAG_DO_ANALYSIS); + assert(args.pa_flags & FLAG_DO_ANALYSIS); if (elf_version(EV_CURRENT) == EV_NONE) err(EX_UNAVAILABLE, "Elf library intialization failed"); - while (pmclog_read(a->pa_logparser, &ev) == 0) { + while (pmclog_read(args.pa_logparser, &ev) == 0) { assert(ev.pl_state == PMCLOG_OK); switch (ev.pl_type) { case PMCLOG_TYPE_INITIALIZE: if ((ev.pl_u.pl_i.pl_version & 0xFF000000) != - PMC_VERSION_MAJOR << 24 && a->pa_verbosity > 0) + PMC_VERSION_MAJOR << 24 && args.pa_verbosity > 0) warnx("WARNING: Log version 0x%x does not " "match compiled version 0x%x.", ev.pl_u.pl_i.pl_version, @@ -2019,7 +1344,7 @@ pmcstat_analyze_log(struct pmcstat_args *a) pl_pathname); image = pmcstat_image_from_path(image_path, pid == -1); if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_determine_type(image, a); + pmcstat_image_determine_type(image); if (image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE) pmcstat_image_link(pp, image, ev.pl_u.pl_mi.pl_start); @@ -2059,16 +1384,23 @@ pmcstat_analyze_log(struct pmcstat_args *a) pc = ev.pl_u.pl_s.pl_pc; pp = pmcstat_process_lookup(ev.pl_u.pl_s.pl_pid, PMCSTAT_ALLOCATE); - if ((ppm = pmcstat_process_find_map(pp, pc)) == NULL && - (ppm = pmcstat_process_find_map(pmcstat_kernproc, - pc)) == NULL) { /* unknown process,offset pair */ - pmcstat_stats.ps_samples_unknown_offset++; - break; - } - pmcstat_image_increment_bucket(ppm, pc, - ev.pl_u.pl_s.pl_pmcid, a); + /* Get PMC record. */ + pmcr = pmcstat_lookup_pmcid(ev.pl_u.pl_s.pl_pmcid); + assert(pmcr != NULL); + /* + * Call the plugins processing + * TODO: move pmcstat_process_find_map inside plugins + */ + + if (plugins[args.pa_pplugin].pl_process != NULL) + plugins[args.pa_pplugin].pl_process( + pp, pmcr, 1, &pc, + pmcstat_process_find_map(pp, pc) != NULL, 0); + plugins[args.pa_plugin].pl_process( + pp, pmcr, 1, &pc, + pmcstat_process_find_map(pp, pc) != NULL, 0); break; case PMCLOG_TYPE_CALLCHAIN: @@ -2078,7 +1410,7 @@ pmcstat_analyze_log(struct pmcstat_args *a) cpu = PMC_CALLCHAIN_CPUFLAGS_TO_CPU(cpuflags); /* Filter on the CPU id. */ - if ((a->pa_cpumask & (1 << cpu)) == 0) { + if ((args.pa_cpumask & (1 << cpu)) == 0) { pmcstat_stats.ps_samples_skipped++; break; } @@ -2086,45 +1418,27 @@ pmcstat_analyze_log(struct pmcstat_args *a) pp = pmcstat_process_lookup(ev.pl_u.pl_cc.pl_pid, PMCSTAT_ALLOCATE); - if ((a->pa_flags & FLAG_WANTS_MAPPINGS) == 0) - pmcstat_record_callchain(pp, - ev.pl_u.pl_cc.pl_pmcid, - ev.pl_u.pl_cc.pl_npc, ev.pl_u.pl_cc.pl_pc, - PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), a); - - if ((a->pa_flags & - (FLAG_DO_GPROF | FLAG_WANTS_MAPPINGS)) == 0) - break; - - pc = ev.pl_u.pl_cc.pl_pc[0]; - if (PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags) == 0) - pp = pmcstat_kernproc; - ppm = pmcstat_process_find_map(pp, pc); - if (ppm == NULL) { - - /* Unknown offset. */ - pmcstat_stats.ps_samples_unknown_offset++; - break; - } - if (a->pa_flags & FLAG_WANTS_MAPPINGS) { - image = ppm->ppm_image; - newpc = pc - (ppm->ppm_lowpc + - (image->pi_vaddr - image->pi_start)); - sym = pmcstat_symbol_search(image, newpc); - if (sym == NULL) - break; - fprintf(a->pa_graphfile, "%p %s 0x%jx 0x%jx\n", - (void *)pc, - pmcstat_string_unintern(sym->ps_name), - (uintmax_t)(sym->ps_start + - image->pi_vaddr), (uintmax_t)(sym->ps_end + - image->pi_vaddr)); - break; - } + /* Get PMC record. */ + pmcr = pmcstat_lookup_pmcid(ev.pl_u.pl_cc.pl_pmcid); + assert(pmcr != NULL); - pmcstat_image_increment_bucket(ppm, pc, - ev.pl_u.pl_cc.pl_pmcid, a); + /* + * Call the plugins processing + */ + if (plugins[args.pa_pplugin].pl_process != NULL) + plugins[args.pa_pplugin].pl_process( + pp, pmcr, + ev.pl_u.pl_cc.pl_npc, + ev.pl_u.pl_cc.pl_pc, + PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), + cpu); + plugins[args.pa_plugin].pl_process( + pp, pmcr, + ev.pl_u.pl_cc.pl_npc, + ev.pl_u.pl_cc.pl_pc, + PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), + cpu); break; case PMCLOG_TYPE_PMCALLOCATE: @@ -2133,7 +1447,7 @@ pmcstat_analyze_log(struct pmcstat_args *a) * PMC and its name. */ pmcstat_pmcid_add(ev.pl_u.pl_a.pl_pmcid, - pmcstat_string_intern(ev.pl_u.pl_a.pl_evname), a); + pmcstat_string_intern(ev.pl_u.pl_a.pl_evname)); break; case PMCLOG_TYPE_PROCEXEC: @@ -2156,7 +1470,7 @@ pmcstat_analyze_log(struct pmcstat_args *a) ev.pl_u.pl_x.pl_pathname); assert(image_path != NULL); pmcstat_process_exec(pp, image_path, - ev.pl_u.pl_x.pl_entryaddr, a); + ev.pl_u.pl_x.pl_entryaddr); break; case PMCLOG_TYPE_PROCEXIT: @@ -2224,16 +1538,16 @@ pmcstat_analyze_log(struct pmcstat_args *a) */ static int -pmcstat_print_log(struct pmcstat_args *a) +pmcstat_print_log(void) { struct pmclog_ev ev; uint32_t npc; - while (pmclog_read(a->pa_logparser, &ev) == 0) { + while (pmclog_read(args.pa_logparser, &ev) == 0) { assert(ev.pl_state == PMCLOG_OK); switch (ev.pl_type) { case PMCLOG_TYPE_CALLCHAIN: - PMCSTAT_PRINT_ENTRY(a, "callchain", + PMCSTAT_PRINT_ENTRY("callchain", "%d 0x%x %d %d %c", ev.pl_u.pl_cc.pl_pid, ev.pl_u.pl_cc.pl_pmcid, PMC_CALLCHAIN_CPUFLAGS_TO_CPU(ev.pl_u.pl_cc. \ @@ -2241,95 +1555,95 @@ pmcstat_print_log(struct pmcstat_args *a) PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(ev.pl_u.pl_cc.\ pl_cpuflags) ? 'u' : 's'); for (npc = 0; npc < ev.pl_u.pl_cc.pl_npc; npc++) - PMCSTAT_PRINT_ENTRY(a, "...", "%p", + PMCSTAT_PRINT_ENTRY("...", "%p", (void *) ev.pl_u.pl_cc.pl_pc[npc]); break; case PMCLOG_TYPE_CLOSELOG: - PMCSTAT_PRINT_ENTRY(a,"closelog",); + PMCSTAT_PRINT_ENTRY("closelog",); break; case PMCLOG_TYPE_DROPNOTIFY: - PMCSTAT_PRINT_ENTRY(a,"drop",); + PMCSTAT_PRINT_ENTRY("drop",); break; case PMCLOG_TYPE_INITIALIZE: - PMCSTAT_PRINT_ENTRY(a,"initlog","0x%x \"%s\"", + PMCSTAT_PRINT_ENTRY("initlog","0x%x \"%s\"", ev.pl_u.pl_i.pl_version, pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch)); if ((ev.pl_u.pl_i.pl_version & 0xFF000000) != - PMC_VERSION_MAJOR << 24 && a->pa_verbosity > 0) + PMC_VERSION_MAJOR << 24 && args.pa_verbosity > 0) warnx("WARNING: Log version 0x%x != expected " "version 0x%x.", ev.pl_u.pl_i.pl_version, PMC_VERSION); break; case PMCLOG_TYPE_MAP_IN: - PMCSTAT_PRINT_ENTRY(a,"map-in","%d %p \"%s\"", + PMCSTAT_PRINT_ENTRY("map-in","%d %p \"%s\"", ev.pl_u.pl_mi.pl_pid, (void *) ev.pl_u.pl_mi.pl_start, ev.pl_u.pl_mi.pl_pathname); break; case PMCLOG_TYPE_MAP_OUT: - PMCSTAT_PRINT_ENTRY(a,"map-out","%d %p %p", + PMCSTAT_PRINT_ENTRY("map-out","%d %p %p", ev.pl_u.pl_mo.pl_pid, (void *) ev.pl_u.pl_mo.pl_start, (void *) ev.pl_u.pl_mo.pl_end); break; case PMCLOG_TYPE_PCSAMPLE: - PMCSTAT_PRINT_ENTRY(a,"sample","0x%x %d %p %c", + PMCSTAT_PRINT_ENTRY("sample","0x%x %d %p %c", ev.pl_u.pl_s.pl_pmcid, ev.pl_u.pl_s.pl_pid, (void *) ev.pl_u.pl_s.pl_pc, ev.pl_u.pl_s.pl_usermode ? 'u' : 's'); break; case PMCLOG_TYPE_PMCALLOCATE: - PMCSTAT_PRINT_ENTRY(a,"allocate","0x%x \"%s\" 0x%x", + PMCSTAT_PRINT_ENTRY("allocate","0x%x \"%s\" 0x%x", ev.pl_u.pl_a.pl_pmcid, ev.pl_u.pl_a.pl_evname, ev.pl_u.pl_a.pl_flags); break; case PMCLOG_TYPE_PMCATTACH: - PMCSTAT_PRINT_ENTRY(a,"attach","0x%x %d \"%s\"", + PMCSTAT_PRINT_ENTRY("attach","0x%x %d \"%s\"", ev.pl_u.pl_t.pl_pmcid, ev.pl_u.pl_t.pl_pid, ev.pl_u.pl_t.pl_pathname); break; case PMCLOG_TYPE_PMCDETACH: - PMCSTAT_PRINT_ENTRY(a,"detach","0x%x %d", + PMCSTAT_PRINT_ENTRY("detach","0x%x %d", ev.pl_u.pl_d.pl_pmcid, ev.pl_u.pl_d.pl_pid); break; case PMCLOG_TYPE_PROCCSW: - PMCSTAT_PRINT_ENTRY(a,"cswval","0x%x %d %jd", + PMCSTAT_PRINT_ENTRY("cswval","0x%x %d %jd", ev.pl_u.pl_c.pl_pmcid, ev.pl_u.pl_c.pl_pid, ev.pl_u.pl_c.pl_value); break; case PMCLOG_TYPE_PROCEXEC: - PMCSTAT_PRINT_ENTRY(a,"exec","0x%x %d %p \"%s\"", + PMCSTAT_PRINT_ENTRY("exec","0x%x %d %p \"%s\"", ev.pl_u.pl_x.pl_pmcid, ev.pl_u.pl_x.pl_pid, (void *) ev.pl_u.pl_x.pl_entryaddr, ev.pl_u.pl_x.pl_pathname); break; case PMCLOG_TYPE_PROCEXIT: - PMCSTAT_PRINT_ENTRY(a,"exitval","0x%x %d %jd", + PMCSTAT_PRINT_ENTRY("exitval","0x%x %d %jd", ev.pl_u.pl_e.pl_pmcid, ev.pl_u.pl_e.pl_pid, ev.pl_u.pl_e.pl_value); break; case PMCLOG_TYPE_PROCFORK: - PMCSTAT_PRINT_ENTRY(a,"fork","%d %d", + PMCSTAT_PRINT_ENTRY("fork","%d %d", ev.pl_u.pl_f.pl_oldpid, ev.pl_u.pl_f.pl_newpid); break; case PMCLOG_TYPE_USERDATA: - PMCSTAT_PRINT_ENTRY(a,"userdata","0x%x", + PMCSTAT_PRINT_ENTRY("userdata","0x%x", ev.pl_u.pl_u.pl_userdata); break; case PMCLOG_TYPE_SYSEXIT: - PMCSTAT_PRINT_ENTRY(a,"exit","%d", + PMCSTAT_PRINT_ENTRY("exit","%d", ev.pl_u.pl_se.pl_pid); break; default: - fprintf(a->pa_printfile, "unknown event (type %d).\n", + fprintf(args.pa_printfile, "unknown event (type %d).\n", ev.pl_type); } } @@ -2354,13 +1668,13 @@ pmcstat_print_log(struct pmcstat_args *a) */ int -pmcstat_close_log(struct pmcstat_args *a) +pmcstat_close_log(void) { if (pmc_flush_logfile() < 0 || pmc_configure_logfile(-1) < 0) err(EX_OSERR, "ERROR: logging failed"); - a->pa_flags &= ~(FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE); - return (a->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING : + args.pa_flags &= ~(FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE); + return (args.pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING : PMCSTAT_FINISHED); } @@ -2456,17 +1770,208 @@ pmcstat_open_log(const char *path, int mode) */ int -pmcstat_process_log(struct pmcstat_args *a) +pmcstat_process_log(void) { /* * If analysis has not been asked for, just print the log to * the current output file. */ - if (a->pa_flags & FLAG_DO_PRINT) - return (pmcstat_print_log(a)); + if (args.pa_flags & FLAG_DO_PRINT) + return (pmcstat_print_log()); else - return (pmcstat_analyze_log(a)); + return (pmcstat_analyze_log()); +} + +/* + * Refresh top display. + */ + +static void +pmcstat_refresh_top(void) +{ + char pmcname[40]; + + /* If in pause mode do not refresh display. */ + if (pmcstat_pause) + return; + + /* Format PMC name. */ + if (pmcstat_mergepmc) + snprintf(pmcname, sizeof(pmcname), "[%s]", + pmcstat_pmcindex_to_name(pmcstat_pmcinfilter)); + else + snprintf(pmcname, sizeof(pmcname), "%s.%d", + pmcstat_pmcindex_to_name(pmcstat_pmcinfilter), + pmcstat_pmcinfilter); + + PMCSTAT_PRINTBEGIN(); + PMCSTAT_PRINTW("PMC: %s Samples: %u processed, %u invalid\n\n", + pmcname, + pmcstat_stats.ps_samples_total, + pmcstat_stats.ps_samples_unknown_offset + + pmcstat_stats.ps_samples_indeterminable + + pmcstat_stats.ps_callchain_dubious_frames); + if (plugins[args.pa_plugin].pl_topdisplay != NULL) + plugins[args.pa_plugin].pl_topdisplay(); + PMCSTAT_PRINTEND(); +} + +/* + * Find the next pmc index to display. + */ + +static void +pmcstat_changefilter(void) +{ + int pmcin; + struct pmcstat_pmcrecord *pmcr; + + /* + * Find the next merge target. + */ + if (pmcstat_mergepmc) { + pmcin = pmcstat_pmcinfilter; + + do { + pmcr = pmcstat_pmcindex_to_pmcr(pmcstat_pmcinfilter); + if (pmcr == pmcr->pr_merge) + break; + + pmcstat_pmcinfilter++; + if (pmcstat_pmcinfilter >= pmcstat_npmcs) + pmcstat_pmcinfilter = 0; + + } while (pmcstat_pmcinfilter != pmcin); + } +} + +/* + * Top mode keypress. + */ + +int +pmcstat_keypress_log(void) +{ + int c, ret = 0; + WINDOW *w; + + w = newwin(1, 0, 1, 0); + c = wgetch(w); + wprintw(w, "Key: %c => ", c); + switch (c) { + case 'c': + wprintw(w, "enter mode 'd' or 'a' => "); + c = wgetch(w); + if (c == 'd') { + args.pa_topmode = PMCSTAT_TOP_DELTA; + wprintw(w, "switching to delta mode"); + } else { + args.pa_topmode = PMCSTAT_TOP_ACCUM; + wprintw(w, "switching to accumulation mode"); + } + break; + case 'm': + pmcstat_mergepmc = !pmcstat_mergepmc; + /* + * Changing merge state require data reset. + */ + if (plugins[args.pa_plugin].pl_shutdown != NULL) + plugins[args.pa_plugin].pl_shutdown(NULL); + bzero(&pmcstat_stats, sizeof(struct pmcstat_stats)); + if (plugins[args.pa_plugin].pl_init != NULL) + plugins[args.pa_plugin].pl_init(); + + /* Update filter to be on a merge target. */ + pmcstat_changefilter(); + wprintw(w, "merge PMC %s", pmcstat_mergepmc ? "on" : "off"); + break; + case 'n': + /* Close current plugin. */ + if (plugins[args.pa_plugin].pl_shutdown != NULL) + plugins[args.pa_plugin].pl_shutdown(NULL); + + /* Find next top display available. */ + do { + args.pa_plugin++; + if (plugins[args.pa_plugin].pl_name == NULL) + args.pa_plugin = 0; + } while (plugins[args.pa_plugin].pl_topdisplay == NULL); + + /* Open new plugin. */ + bzero(&pmcstat_stats, sizeof(struct pmcstat_stats)); + if (plugins[args.pa_plugin].pl_init != NULL) + plugins[args.pa_plugin].pl_init(); + wprintw(w, "switching to plugin %s", + plugins[args.pa_plugin].pl_name); + break; + case 'p': + pmcstat_pmcinfilter++; + if (pmcstat_pmcinfilter >= pmcstat_npmcs) + pmcstat_pmcinfilter = 0; + pmcstat_changefilter(); + wprintw(w, "switching to PMC %s.%d", + pmcstat_pmcindex_to_name(pmcstat_pmcinfilter), + pmcstat_pmcinfilter); + break; + case ' ': + pmcstat_pause = !pmcstat_pause; + if (pmcstat_pause) + wprintw(w, "pause => press space again to continue"); + break; + case 'q': + wprintw(w, "exiting..."); + ret = 1; + default: + if (plugins[args.pa_plugin].pl_topkeypress != NULL) + if (plugins[args.pa_plugin].pl_topkeypress(c, w)) + ret = 1; + } + + wrefresh(w); + delwin(w); + return ret; +} + + +/* + * Top mode display. + */ + +void +pmcstat_display_log(void) +{ + + pmcstat_refresh_top(); + + /* Reset everythings if delta mode. */ + if (args.pa_topmode == PMCSTAT_TOP_DELTA) { + if (plugins[args.pa_plugin].pl_shutdown != NULL) + plugins[args.pa_plugin].pl_shutdown(NULL); + bzero(&pmcstat_stats, sizeof(struct pmcstat_stats)); + if (plugins[args.pa_plugin].pl_init != NULL) + plugins[args.pa_plugin].pl_init(); + } + +} + +/* + * Configure a plugins. + */ + +void +pmcstat_pluginconfigure_log(char *opt) +{ + + if (strncmp(opt, "threshold=", 10) == 0) { + pmcstat_threshold = atof(opt+10); + } else { + if (plugins[args.pa_plugin].pl_configure != NULL) { + if (!plugins[args.pa_plugin].pl_configure(opt)) + err(EX_USAGE, + "ERROR: unknown option <%s>.", opt); + } + } } /* @@ -2474,12 +1979,10 @@ pmcstat_process_log(struct pmcstat_args *a) */ void -pmcstat_initialize_logging(struct pmcstat_args *a) +pmcstat_initialize_logging(void) { int i; - (void) a; - /* use a convenient format for 'ldd' output */ if (setenv("LD_TRACE_LOADED_OBJECTS_FMT1","%o \"%p\" %x\n",1) != 0) err(EX_OSERR, "ERROR: Cannot setenv"); @@ -2499,6 +2002,21 @@ pmcstat_initialize_logging(struct pmcstat_args *a) if ((pmcstat_kernproc = pmcstat_process_lookup((pid_t) -1, PMCSTAT_ALLOCATE)) == NULL) err(EX_OSERR, "ERROR: Cannot initialize logging"); + + /* PMC count. */ + pmcstat_npmcs = 0; + + /* Merge PMC with same name. */ + pmcstat_mergepmc = args.pa_mergepmc; + + /* + * Initialize plugins + */ + + if (plugins[args.pa_pplugin].pl_init != NULL) + plugins[args.pa_pplugin].pl_init(); + if (plugins[args.pa_plugin].pl_init != NULL) + plugins[args.pa_plugin].pl_init(); } /* @@ -2506,99 +2024,57 @@ pmcstat_initialize_logging(struct pmcstat_args *a) */ void -pmcstat_shutdown_logging(struct pmcstat_args *a) +pmcstat_shutdown_logging(void) { int i; FILE *mf; - struct pmcstat_gmonfile *pgf, *pgftmp; struct pmcstat_image *pi, *pitmp; struct pmcstat_process *pp, *pptmp; - struct pmcstat_cgnode_hash *pch, *pchtmp; + struct pmcstat_pcmap *ppm, *ppmtmp; /* determine where to send the map file */ mf = NULL; - if (a->pa_mapfilename != NULL) - mf = (strcmp(a->pa_mapfilename, "-") == 0) ? - a->pa_printfile : fopen(a->pa_mapfilename, "w"); + if (args.pa_mapfilename != NULL) + mf = (strcmp(args.pa_mapfilename, "-") == 0) ? + args.pa_printfile : fopen(args.pa_mapfilename, "w"); - if (mf == NULL && a->pa_flags & FLAG_DO_GPROF && - a->pa_verbosity >= 2) - mf = a->pa_printfile; + if (mf == NULL && args.pa_flags & FLAG_DO_GPROF && + args.pa_verbosity >= 2) + mf = args.pa_printfile; if (mf) (void) fprintf(mf, "MAP:\n"); - - if (a->pa_flags & FLAG_DO_CALLGRAPHS) - pmcstat_callgraph_print(a); - - /* - * Sync back all gprof flat profile data. - */ - for (i = 0; i < PMCSTAT_NHASH; i++) { - LIST_FOREACH(pi, &pmcstat_image_hash[i], pi_next) { - if (mf) - (void) fprintf(mf, " \"%s\" => \"%s\"", - pmcstat_string_unintern(pi->pi_execpath), - pmcstat_string_unintern( - pi->pi_samplename)); - - /* flush gmon.out data to disk */ - LIST_FOREACH(pgf, &pi->pi_gmlist, pgf_next) { - pmcstat_gmon_unmap_file(pgf); - if (mf) - (void) fprintf(mf, " %s/%d", - pmcstat_pmcid_to_name( - pgf->pgf_pmcid), - pgf->pgf_nsamples); - if (pgf->pgf_overflow && a->pa_verbosity >= 1) - warnx("WARNING: profile \"%s\" " - "overflowed.", - pmcstat_string_unintern( - pgf->pgf_name)); - } - - if (mf) - (void) fprintf(mf, "\n"); - } - } - /* - * Compute arcs and add these to the gprof files. + * Shutdown the plugins */ - if (a->pa_flags & FLAG_DO_GPROF && a->pa_graphdepth > 1) - pmcstat_callgraph_do_gmon_arcs(); - /* - * Free memory. - */ - for (i = 0; i < PMCSTAT_NHASH; i++) { - LIST_FOREACH_SAFE(pch, &pmcstat_cgnode_hash[i], pch_next, - pchtmp) { - pmcstat_cgnode_free(pch->pch_cgnode); - free(pch); - } - } + if (plugins[args.pa_plugin].pl_shutdown != NULL) + plugins[args.pa_plugin].pl_shutdown(mf); + if (plugins[args.pa_pplugin].pl_shutdown != NULL) + plugins[args.pa_pplugin].pl_shutdown(mf); for (i = 0; i < PMCSTAT_NHASH; i++) { - LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next, pitmp) - { - LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next, - pgftmp) { - if (pgf->pgf_file) - (void) fclose(pgf->pgf_file); - LIST_REMOVE(pgf, pgf_next); - free(pgf); - } - if (pi->pi_symbols) - free(pi->pi_symbols); - + LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next, + pitmp) { + if (plugins[args.pa_plugin].pl_shutdownimage != NULL) + plugins[args.pa_plugin].pl_shutdownimage(pi); + if (plugins[args.pa_pplugin].pl_shutdownimage != NULL) + plugins[args.pa_pplugin].pl_shutdownimage(pi); + + free(pi->pi_symbols); + if (pi->pi_addr2line != NULL) + pclose(pi->pi_addr2line); LIST_REMOVE(pi, pi_next); free(pi); } LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[i], pp_next, pptmp) { + TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) { + TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); + free(ppm); + } LIST_REMOVE(pp, pp_next); free(pp); } @@ -2610,23 +2086,23 @@ pmcstat_shutdown_logging(struct pmcstat_args *a) * Print errors unless -q was specified. Print all statistics * if verbosity > 1. */ -#define PRINT(N,V,A) do { \ - if (pmcstat_stats.ps_##V || (A)->pa_verbosity >= 2) \ - (void) fprintf((A)->pa_printfile, " %-40s %d\n",\ +#define PRINT(N,V) do { \ + if (pmcstat_stats.ps_##V || args.pa_verbosity >= 2) \ + (void) fprintf(args.pa_printfile, " %-40s %d\n",\ N, pmcstat_stats.ps_##V); \ } while (0) - if (a->pa_verbosity >= 1 && a->pa_flags & FLAG_DO_GPROF) { - (void) fprintf(a->pa_printfile, "CONVERSION STATISTICS:\n"); - PRINT("#exec/a.out", exec_aout, a); - PRINT("#exec/elf", exec_elf, a); - PRINT("#exec/unknown", exec_indeterminable, a); - PRINT("#exec handling errors", exec_errors, a); - PRINT("#samples/total", samples_total, a); - PRINT("#samples/unclaimed", samples_unknown_offset, a); - PRINT("#samples/unknown-object", samples_indeterminable, a); - PRINT("#callchain/dubious-frames", callchain_dubious_frames, - a); + if (args.pa_verbosity >= 1 && (args.pa_flags & FLAG_DO_ANALYSIS) && + (args.pa_flags & FLAG_DO_TOP) == 0) { + (void) fprintf(args.pa_printfile, "CONVERSION STATISTICS:\n"); + PRINT("#exec/a.out", exec_aout); + PRINT("#exec/elf", exec_elf); + PRINT("#exec/unknown", exec_indeterminable); + PRINT("#exec handling errors", exec_errors); + PRINT("#samples/total", samples_total); + PRINT("#samples/unclaimed", samples_unknown_offset); + PRINT("#samples/unknown-object", samples_indeterminable); + PRINT("#callchain/dubious-frames", callchain_dubious_frames); } if (mf) |