summaryrefslogtreecommitdiffstats
path: root/usr.sbin/pmcstat/pmcstat_log.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/pmcstat/pmcstat_log.c')
-rw-r--r--usr.sbin/pmcstat/pmcstat_log.c1702
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)
OpenPOWER on IntegriCloud