115139246SJoseph Koshy /*- 2b6010f9eSJoseph Koshy * Copyright (c) 2005-2007, Joseph Koshy 3b6010f9eSJoseph Koshy * Copyright (c) 2007 The FreeBSD Foundation 415139246SJoseph Koshy * All rights reserved. 515139246SJoseph Koshy * 6b6010f9eSJoseph Koshy * Portions of this software were developed by A. Joseph Koshy under 7b6010f9eSJoseph Koshy * sponsorship from the FreeBSD Foundation and Google, Inc. 8b6010f9eSJoseph Koshy * 915139246SJoseph Koshy * Redistribution and use in source and binary forms, with or without 1015139246SJoseph Koshy * modification, are permitted provided that the following conditions 1115139246SJoseph Koshy * are met: 1215139246SJoseph Koshy * 1. Redistributions of source code must retain the above copyright 1315139246SJoseph Koshy * notice, this list of conditions and the following disclaimer. 1415139246SJoseph Koshy * 2. Redistributions in binary form must reproduce the above copyright 1515139246SJoseph Koshy * notice, this list of conditions and the following disclaimer in the 1615139246SJoseph Koshy * documentation and/or other materials provided with the distribution. 1715139246SJoseph Koshy * 1815139246SJoseph Koshy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1915139246SJoseph Koshy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2015139246SJoseph Koshy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2115139246SJoseph Koshy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2215139246SJoseph Koshy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2315139246SJoseph Koshy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2415139246SJoseph Koshy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2515139246SJoseph Koshy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2615139246SJoseph Koshy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2715139246SJoseph Koshy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2815139246SJoseph Koshy * SUCH DAMAGE. 2915139246SJoseph Koshy */ 3015139246SJoseph Koshy 3149874f6eSJoseph Koshy /* 3249874f6eSJoseph Koshy * Transform a hwpmc(4) log into human readable form, and into 3349874f6eSJoseph Koshy * gprof(1) compatible profiles. 3449874f6eSJoseph Koshy */ 3549874f6eSJoseph Koshy 3615139246SJoseph Koshy #include <sys/cdefs.h> 3715139246SJoseph Koshy __FBSDID("$FreeBSD$"); 3815139246SJoseph Koshy 3915139246SJoseph Koshy #include <sys/param.h> 4015139246SJoseph Koshy #include <sys/endian.h> 4115139246SJoseph Koshy #include <sys/gmon.h> 4215139246SJoseph Koshy #include <sys/imgact_aout.h> 4315139246SJoseph Koshy #include <sys/imgact_elf.h> 4415139246SJoseph Koshy #include <sys/mman.h> 4515139246SJoseph Koshy #include <sys/pmc.h> 4615139246SJoseph Koshy #include <sys/queue.h> 47302cbb90SJoseph Koshy #include <sys/socket.h> 4815139246SJoseph Koshy #include <sys/stat.h> 4915139246SJoseph Koshy #include <sys/wait.h> 5015139246SJoseph Koshy 5115139246SJoseph Koshy #include <netinet/in.h> 5215139246SJoseph Koshy 5315139246SJoseph Koshy #include <assert.h> 540b86b1bbSFabien Thomas #include <curses.h> 5515139246SJoseph Koshy #include <err.h> 56302cbb90SJoseph Koshy #include <errno.h> 5715139246SJoseph Koshy #include <fcntl.h> 58b6010f9eSJoseph Koshy #include <gelf.h> 5915139246SJoseph Koshy #include <libgen.h> 6015139246SJoseph Koshy #include <limits.h> 61302cbb90SJoseph Koshy #include <netdb.h> 6215139246SJoseph Koshy #include <pmc.h> 6315139246SJoseph Koshy #include <pmclog.h> 6415139246SJoseph Koshy #include <sysexits.h> 6515139246SJoseph Koshy #include <stdint.h> 6615139246SJoseph Koshy #include <stdio.h> 6715139246SJoseph Koshy #include <stdlib.h> 6815139246SJoseph Koshy #include <string.h> 6915139246SJoseph Koshy #include <unistd.h> 7015139246SJoseph Koshy 7115139246SJoseph Koshy #include "pmcstat.h" 720b86b1bbSFabien Thomas #include "pmcstat_log.h" 730b86b1bbSFabien Thomas #include "pmcstat_top.h" 7415139246SJoseph Koshy 75b6010f9eSJoseph Koshy #define PMCSTAT_ALLOCATE 1 76b6010f9eSJoseph Koshy 7715139246SJoseph Koshy /* 7849874f6eSJoseph Koshy * PUBLIC INTERFACES 7949874f6eSJoseph Koshy * 8049874f6eSJoseph Koshy * pmcstat_initialize_logging() initialize this module, called first 8149874f6eSJoseph Koshy * pmcstat_shutdown_logging() orderly shutdown, called last 8249874f6eSJoseph Koshy * pmcstat_open_log() open an eventlog for processing 8349874f6eSJoseph Koshy * pmcstat_process_log() print/convert an event log 840b86b1bbSFabien Thomas * pmcstat_display_log() top mode display for the log 8549874f6eSJoseph Koshy * pmcstat_close_log() finish processing an event log 8649874f6eSJoseph Koshy * 87b6010f9eSJoseph Koshy * IMPLEMENTATION NOTES 8849874f6eSJoseph Koshy * 89b6010f9eSJoseph Koshy * We correlate each 'callchain' or 'sample' entry seen in the event 90b6010f9eSJoseph Koshy * log back to an executable object in the system. Executable objects 91b6010f9eSJoseph Koshy * include: 9249874f6eSJoseph Koshy * - program executables, 9349874f6eSJoseph Koshy * - shared libraries loaded by the runtime loader, 9449874f6eSJoseph Koshy * - dlopen()'ed objects loaded by the program, 9549874f6eSJoseph Koshy * - the runtime loader itself, 9649874f6eSJoseph Koshy * - the kernel and kernel modules. 9749874f6eSJoseph Koshy * 9849874f6eSJoseph Koshy * Each process that we know about is treated as a set of regions that 9949874f6eSJoseph Koshy * map to executable objects. Processes are described by 10049874f6eSJoseph Koshy * 'pmcstat_process' structures. Executable objects are tracked by 10149874f6eSJoseph Koshy * 'pmcstat_image' structures. The kernel and kernel modules are 10249874f6eSJoseph Koshy * common to all processes (they reside at the same virtual addresses 10349874f6eSJoseph Koshy * for all processes). Individual processes can have their text 10449874f6eSJoseph Koshy * segments and shared libraries loaded at process-specific locations. 10549874f6eSJoseph Koshy * 10649874f6eSJoseph Koshy * A given executable object can be in use by multiple processes 10749874f6eSJoseph Koshy * (e.g., libc.so) and loaded at a different address in each. 10849874f6eSJoseph Koshy * pmcstat_pcmap structures track per-image mappings. 10949874f6eSJoseph Koshy * 11049874f6eSJoseph Koshy * The sample log could have samples from multiple PMCs; we 11149874f6eSJoseph Koshy * generate one 'gmon.out' profile per PMC. 112b6010f9eSJoseph Koshy * 113b6010f9eSJoseph Koshy * IMPLEMENTATION OF GMON OUTPUT 114b6010f9eSJoseph Koshy * 115b6010f9eSJoseph Koshy * Each executable object gets one 'gmon.out' profile, per PMC in 116b6010f9eSJoseph Koshy * use. Creation of 'gmon.out' profiles is done lazily. The 117b6010f9eSJoseph Koshy * 'gmon.out' profiles generated for a given sampling PMC are 118b6010f9eSJoseph Koshy * aggregates of all the samples for that particular executable 119b6010f9eSJoseph Koshy * object. 120b6010f9eSJoseph Koshy * 121b6010f9eSJoseph Koshy * IMPLEMENTATION OF SYSTEM-WIDE CALLGRAPH OUTPUT 122b6010f9eSJoseph Koshy * 123b6010f9eSJoseph Koshy * Each active pmcid has its own callgraph structure, described by a 124b6010f9eSJoseph Koshy * 'struct pmcstat_callgraph'. Given a process id and a list of pc 125b6010f9eSJoseph Koshy * values, we map each pc value to a tuple (image, symbol), where 126b6010f9eSJoseph Koshy * 'image' denotes an executable object and 'symbol' is the closest 127b6010f9eSJoseph Koshy * symbol that precedes the pc value. Each pc value in the list is 128b6010f9eSJoseph Koshy * also given a 'rank' that reflects its depth in the call stack. 12915139246SJoseph Koshy */ 13015139246SJoseph Koshy 1310b86b1bbSFabien Thomas struct pmcstat_pmcs pmcstat_pmcs = LIST_HEAD_INITIALIZER(pmcstat_pmcs); 13215139246SJoseph Koshy 13349874f6eSJoseph Koshy /* 13449874f6eSJoseph Koshy * All image descriptors are kept in a hash table. 13549874f6eSJoseph Koshy */ 1360b86b1bbSFabien Thomas struct pmcstat_image_hash_list pmcstat_image_hash[PMCSTAT_NHASH]; 13715139246SJoseph Koshy 13849874f6eSJoseph Koshy /* 13949874f6eSJoseph Koshy * All process descriptors are kept in a hash table. 14049874f6eSJoseph Koshy */ 1410b86b1bbSFabien Thomas struct pmcstat_process_hash_list pmcstat_process_hash[PMCSTAT_NHASH]; 14215139246SJoseph Koshy 1430b86b1bbSFabien Thomas struct pmcstat_stats pmcstat_stats; /* statistics */ 14415139246SJoseph Koshy 1450b86b1bbSFabien Thomas struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */ 146b6010f9eSJoseph Koshy 1470b86b1bbSFabien Thomas #include "pmcpl_gprof.h" 1480b86b1bbSFabien Thomas #include "pmcpl_callgraph.h" 1490b86b1bbSFabien Thomas #include "pmcpl_annotate.h" 1500b86b1bbSFabien Thomas #include "pmcpl_calltree.h" 1510b86b1bbSFabien Thomas 1520b86b1bbSFabien Thomas struct pmc_plugins { 1530b86b1bbSFabien Thomas const char *pl_name; /* name */ 1540b86b1bbSFabien Thomas 1550b86b1bbSFabien Thomas /* configure */ 1560b86b1bbSFabien Thomas int (*pl_configure)(char *opt); 1570b86b1bbSFabien Thomas 1580b86b1bbSFabien Thomas /* init and shutdown */ 1590b86b1bbSFabien Thomas int (*pl_init)(void); 1600b86b1bbSFabien Thomas void (*pl_shutdown)(FILE *mf); 1610b86b1bbSFabien Thomas 1620b86b1bbSFabien Thomas /* sample processing */ 1630b86b1bbSFabien Thomas void (*pl_process)(struct pmcstat_process *pp, 1640b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pmcr, uint32_t nsamples, 1650b86b1bbSFabien Thomas uintfptr_t *cc, int usermode, uint32_t cpu); 1660b86b1bbSFabien Thomas 1670b86b1bbSFabien Thomas /* image */ 1680b86b1bbSFabien Thomas void (*pl_initimage)(struct pmcstat_image *pi); 1690b86b1bbSFabien Thomas void (*pl_shutdownimage)(struct pmcstat_image *pi); 1700b86b1bbSFabien Thomas 1710b86b1bbSFabien Thomas /* pmc */ 1720b86b1bbSFabien Thomas void (*pl_newpmc)(pmcstat_interned_string ps, 1730b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pr); 1740b86b1bbSFabien Thomas 1750b86b1bbSFabien Thomas /* top display */ 1760b86b1bbSFabien Thomas void (*pl_topdisplay)(void); 1770b86b1bbSFabien Thomas 1780b86b1bbSFabien Thomas /* top keypress */ 1790b86b1bbSFabien Thomas int (*pl_topkeypress)(int c, WINDOW *w); 1800b86b1bbSFabien Thomas 1810b86b1bbSFabien Thomas } plugins[] = { 1820b86b1bbSFabien Thomas { 1830b86b1bbSFabien Thomas .pl_name = "none", 1840b86b1bbSFabien Thomas }, 1850b86b1bbSFabien Thomas { 1860b86b1bbSFabien Thomas .pl_name = "callgraph", 1870b86b1bbSFabien Thomas .pl_init = pmcpl_cg_init, 1880b86b1bbSFabien Thomas .pl_shutdown = pmcpl_cg_shutdown, 1890b86b1bbSFabien Thomas .pl_process = pmcpl_cg_process, 1900b86b1bbSFabien Thomas .pl_topkeypress = pmcpl_cg_topkeypress, 1910b86b1bbSFabien Thomas .pl_topdisplay = pmcpl_cg_topdisplay 1920b86b1bbSFabien Thomas }, 1930b86b1bbSFabien Thomas { 1940b86b1bbSFabien Thomas .pl_name = "gprof", 1950b86b1bbSFabien Thomas .pl_shutdown = pmcpl_gmon_shutdown, 1960b86b1bbSFabien Thomas .pl_process = pmcpl_gmon_process, 1970b86b1bbSFabien Thomas .pl_initimage = pmcpl_gmon_initimage, 1980b86b1bbSFabien Thomas .pl_shutdownimage = pmcpl_gmon_shutdownimage, 1990b86b1bbSFabien Thomas .pl_newpmc = pmcpl_gmon_newpmc 2000b86b1bbSFabien Thomas }, 2010b86b1bbSFabien Thomas { 2020b86b1bbSFabien Thomas .pl_name = "annotate", 2030b86b1bbSFabien Thomas .pl_process = pmcpl_annotate_process 2040b86b1bbSFabien Thomas }, 2050b86b1bbSFabien Thomas { 2060b86b1bbSFabien Thomas .pl_name = "calltree", 2070b86b1bbSFabien Thomas .pl_configure = pmcpl_ct_configure, 2080b86b1bbSFabien Thomas .pl_init = pmcpl_ct_init, 2090b86b1bbSFabien Thomas .pl_shutdown = pmcpl_ct_shutdown, 2100b86b1bbSFabien Thomas .pl_process = pmcpl_ct_process, 2110b86b1bbSFabien Thomas .pl_topkeypress = pmcpl_ct_topkeypress, 2120b86b1bbSFabien Thomas .pl_topdisplay = pmcpl_ct_topdisplay 2130b86b1bbSFabien Thomas }, 2140b86b1bbSFabien Thomas { 2150b86b1bbSFabien Thomas .pl_name = NULL 2160b86b1bbSFabien Thomas } 217b6010f9eSJoseph Koshy }; 218b6010f9eSJoseph Koshy 2190b86b1bbSFabien Thomas int pmcstat_mergepmc; 220b6010f9eSJoseph Koshy 2210b86b1bbSFabien Thomas int pmcstat_pmcinfilter = 0; /* PMC filter for top mode. */ 2220b86b1bbSFabien Thomas float pmcstat_threshold = 0.5; /* Cost filter for top mode. */ 223b6010f9eSJoseph Koshy 22415139246SJoseph Koshy /* 22515139246SJoseph Koshy * Prototypes 22615139246SJoseph Koshy */ 22715139246SJoseph Koshy 22849874f6eSJoseph Koshy static struct pmcstat_image *pmcstat_image_from_path(pmcstat_interned_string 22949874f6eSJoseph Koshy _path, int _iskernelmodule); 2300b86b1bbSFabien Thomas static void pmcstat_image_get_aout_params(struct pmcstat_image *_image); 2310b86b1bbSFabien Thomas static void pmcstat_image_get_elf_params(struct pmcstat_image *_image); 23215139246SJoseph Koshy static void pmcstat_image_link(struct pmcstat_process *_pp, 23349874f6eSJoseph Koshy struct pmcstat_image *_i, uintfptr_t _lpc); 23415139246SJoseph Koshy 23549874f6eSJoseph Koshy static void pmcstat_pmcid_add(pmc_id_t _pmcid, 2360b86b1bbSFabien Thomas pmcstat_interned_string _name); 23715139246SJoseph Koshy 23849874f6eSJoseph Koshy static void pmcstat_process_aout_exec(struct pmcstat_process *_pp, 2390b86b1bbSFabien Thomas struct pmcstat_image *_image, uintfptr_t _entryaddr); 24049874f6eSJoseph Koshy static void pmcstat_process_elf_exec(struct pmcstat_process *_pp, 2410b86b1bbSFabien Thomas struct pmcstat_image *_image, uintfptr_t _entryaddr); 24260f918efSJoseph Koshy static void pmcstat_process_exec(struct pmcstat_process *_pp, 2430b86b1bbSFabien Thomas pmcstat_interned_string _path, uintfptr_t _entryaddr); 24449874f6eSJoseph Koshy static struct pmcstat_process *pmcstat_process_lookup(pid_t _pid, 24549874f6eSJoseph Koshy int _allocate); 24615139246SJoseph Koshy static int pmcstat_string_compute_hash(const char *_string); 24749874f6eSJoseph Koshy static void pmcstat_string_initialize(void); 24849874f6eSJoseph Koshy static int pmcstat_string_lookup_hash(pmcstat_interned_string _is); 24949874f6eSJoseph Koshy static void pmcstat_string_shutdown(void); 250c86819ecSFabien Thomas static void pmcstat_stats_reset(void); 25115139246SJoseph Koshy 25215139246SJoseph Koshy /* 25349874f6eSJoseph Koshy * A simple implementation of interned strings. Each interned string 25449874f6eSJoseph Koshy * is assigned a unique address, so that subsequent string compares 25549874f6eSJoseph Koshy * can be done by a simple pointer comparision instead of using 25649874f6eSJoseph Koshy * strcmp(). This speeds up hash table lookups and saves memory if 25749874f6eSJoseph Koshy * duplicate strings are the norm. 25849874f6eSJoseph Koshy */ 25949874f6eSJoseph Koshy struct pmcstat_string { 26049874f6eSJoseph Koshy LIST_ENTRY(pmcstat_string) ps_next; /* hash link */ 26149874f6eSJoseph Koshy int ps_len; 26249874f6eSJoseph Koshy int ps_hash; 26349874f6eSJoseph Koshy char *ps_string; 26449874f6eSJoseph Koshy }; 26549874f6eSJoseph Koshy 26649874f6eSJoseph Koshy static LIST_HEAD(,pmcstat_string) pmcstat_string_hash[PMCSTAT_NHASH]; 26749874f6eSJoseph Koshy 26849874f6eSJoseph Koshy /* 2690b86b1bbSFabien Thomas * PMC count. 2700b86b1bbSFabien Thomas */ 2710b86b1bbSFabien Thomas int pmcstat_npmcs; 2720b86b1bbSFabien Thomas 2730b86b1bbSFabien Thomas /* 2740b86b1bbSFabien Thomas * PMC Top mode pause state. 2750b86b1bbSFabien Thomas */ 2760b86b1bbSFabien Thomas int pmcstat_pause; 2770b86b1bbSFabien Thomas 278c86819ecSFabien Thomas static void 279c86819ecSFabien Thomas pmcstat_stats_reset(void) 280c86819ecSFabien Thomas { 281c86819ecSFabien Thomas struct pmcstat_pmcrecord *pr; 282c86819ecSFabien Thomas 283c86819ecSFabien Thomas /* Flush PMCs stats. */ 284c86819ecSFabien Thomas LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) { 285c86819ecSFabien Thomas pr->pr_samples = 0; 286c86819ecSFabien Thomas pr->pr_dubious_frames = 0; 287c86819ecSFabien Thomas } 288c86819ecSFabien Thomas 289c86819ecSFabien Thomas /* Flush global stats. */ 290c86819ecSFabien Thomas bzero(&pmcstat_stats, sizeof(struct pmcstat_stats)); 291c86819ecSFabien Thomas } 292c86819ecSFabien Thomas 2930b86b1bbSFabien Thomas /* 29449874f6eSJoseph Koshy * Compute a 'hash' value for a string. 29549874f6eSJoseph Koshy */ 29649874f6eSJoseph Koshy 29749874f6eSJoseph Koshy static int 29849874f6eSJoseph Koshy pmcstat_string_compute_hash(const char *s) 29949874f6eSJoseph Koshy { 30049874f6eSJoseph Koshy int hash; 30149874f6eSJoseph Koshy 30249874f6eSJoseph Koshy for (hash = 0; *s; s++) 30349874f6eSJoseph Koshy hash ^= *s; 30449874f6eSJoseph Koshy 30549874f6eSJoseph Koshy return (hash & PMCSTAT_HASH_MASK); 30649874f6eSJoseph Koshy } 30749874f6eSJoseph Koshy 30849874f6eSJoseph Koshy /* 30949874f6eSJoseph Koshy * Intern a copy of string 's', and return a pointer to the 31049874f6eSJoseph Koshy * interned structure. 31149874f6eSJoseph Koshy */ 31249874f6eSJoseph Koshy 3130b86b1bbSFabien Thomas pmcstat_interned_string 31449874f6eSJoseph Koshy pmcstat_string_intern(const char *s) 31549874f6eSJoseph Koshy { 31649874f6eSJoseph Koshy struct pmcstat_string *ps; 31749874f6eSJoseph Koshy const struct pmcstat_string *cps; 31849874f6eSJoseph Koshy int hash, len; 31949874f6eSJoseph Koshy 32049874f6eSJoseph Koshy if ((cps = pmcstat_string_lookup(s)) != NULL) 32149874f6eSJoseph Koshy return (cps); 32249874f6eSJoseph Koshy 32349874f6eSJoseph Koshy hash = pmcstat_string_compute_hash(s); 32449874f6eSJoseph Koshy len = strlen(s); 32549874f6eSJoseph Koshy 32649874f6eSJoseph Koshy if ((ps = malloc(sizeof(*ps))) == NULL) 32749874f6eSJoseph Koshy err(EX_OSERR, "ERROR: Could not intern string"); 32849874f6eSJoseph Koshy ps->ps_len = len; 32949874f6eSJoseph Koshy ps->ps_hash = hash; 33049874f6eSJoseph Koshy ps->ps_string = strdup(s); 33149874f6eSJoseph Koshy LIST_INSERT_HEAD(&pmcstat_string_hash[hash], ps, ps_next); 33249874f6eSJoseph Koshy return ((pmcstat_interned_string) ps); 33349874f6eSJoseph Koshy } 33449874f6eSJoseph Koshy 3350b86b1bbSFabien Thomas const char * 33649874f6eSJoseph Koshy pmcstat_string_unintern(pmcstat_interned_string str) 33749874f6eSJoseph Koshy { 33849874f6eSJoseph Koshy const char *s; 33949874f6eSJoseph Koshy 34049874f6eSJoseph Koshy s = ((const struct pmcstat_string *) str)->ps_string; 34149874f6eSJoseph Koshy return (s); 34249874f6eSJoseph Koshy } 34349874f6eSJoseph Koshy 3440b86b1bbSFabien Thomas pmcstat_interned_string 34549874f6eSJoseph Koshy pmcstat_string_lookup(const char *s) 34649874f6eSJoseph Koshy { 34749874f6eSJoseph Koshy struct pmcstat_string *ps; 34849874f6eSJoseph Koshy int hash, len; 34949874f6eSJoseph Koshy 35049874f6eSJoseph Koshy hash = pmcstat_string_compute_hash(s); 35149874f6eSJoseph Koshy len = strlen(s); 35249874f6eSJoseph Koshy 35349874f6eSJoseph Koshy LIST_FOREACH(ps, &pmcstat_string_hash[hash], ps_next) 35449874f6eSJoseph Koshy if (ps->ps_len == len && ps->ps_hash == hash && 35549874f6eSJoseph Koshy strcmp(ps->ps_string, s) == 0) 35649874f6eSJoseph Koshy return (ps); 35749874f6eSJoseph Koshy return (NULL); 35849874f6eSJoseph Koshy } 35949874f6eSJoseph Koshy 36049874f6eSJoseph Koshy static int 36149874f6eSJoseph Koshy pmcstat_string_lookup_hash(pmcstat_interned_string s) 36249874f6eSJoseph Koshy { 36349874f6eSJoseph Koshy const struct pmcstat_string *ps; 36449874f6eSJoseph Koshy 36549874f6eSJoseph Koshy ps = (const struct pmcstat_string *) s; 36649874f6eSJoseph Koshy return (ps->ps_hash); 36749874f6eSJoseph Koshy } 36849874f6eSJoseph Koshy 36949874f6eSJoseph Koshy /* 37049874f6eSJoseph Koshy * Initialize the string interning facility. 37149874f6eSJoseph Koshy */ 37249874f6eSJoseph Koshy 37349874f6eSJoseph Koshy static void 37449874f6eSJoseph Koshy pmcstat_string_initialize(void) 37549874f6eSJoseph Koshy { 37649874f6eSJoseph Koshy int i; 37749874f6eSJoseph Koshy 37849874f6eSJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) 37949874f6eSJoseph Koshy LIST_INIT(&pmcstat_string_hash[i]); 38049874f6eSJoseph Koshy } 38149874f6eSJoseph Koshy 38249874f6eSJoseph Koshy /* 38349874f6eSJoseph Koshy * Destroy the string table, free'ing up space. 38449874f6eSJoseph Koshy */ 38549874f6eSJoseph Koshy 38649874f6eSJoseph Koshy static void 38749874f6eSJoseph Koshy pmcstat_string_shutdown(void) 38849874f6eSJoseph Koshy { 38949874f6eSJoseph Koshy int i; 39049874f6eSJoseph Koshy struct pmcstat_string *ps, *pstmp; 39149874f6eSJoseph Koshy 39249874f6eSJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) 39349874f6eSJoseph Koshy LIST_FOREACH_SAFE(ps, &pmcstat_string_hash[i], ps_next, 39449874f6eSJoseph Koshy pstmp) { 39549874f6eSJoseph Koshy LIST_REMOVE(ps, ps_next); 39649874f6eSJoseph Koshy free(ps->ps_string); 39749874f6eSJoseph Koshy free(ps); 39849874f6eSJoseph Koshy } 39949874f6eSJoseph Koshy } 40049874f6eSJoseph Koshy 40149874f6eSJoseph Koshy /* 40249874f6eSJoseph Koshy * Determine whether a given executable image is an A.OUT object, and 40349874f6eSJoseph Koshy * if so, fill in its parameters from the text file. 40449874f6eSJoseph Koshy * Sets image->pi_type. 40549874f6eSJoseph Koshy */ 40649874f6eSJoseph Koshy 40715139246SJoseph Koshy static void 4080b86b1bbSFabien Thomas pmcstat_image_get_aout_params(struct pmcstat_image *image) 40949874f6eSJoseph Koshy { 41049874f6eSJoseph Koshy int fd; 41149874f6eSJoseph Koshy ssize_t nbytes; 41249874f6eSJoseph Koshy struct exec ex; 41349874f6eSJoseph Koshy const char *path; 41449874f6eSJoseph Koshy char buffer[PATH_MAX]; 41549874f6eSJoseph Koshy 41649874f6eSJoseph Koshy path = pmcstat_string_unintern(image->pi_execpath); 41749874f6eSJoseph Koshy assert(path != NULL); 41849874f6eSJoseph Koshy 41949874f6eSJoseph Koshy if (image->pi_iskernelmodule) 42049874f6eSJoseph Koshy errx(EX_SOFTWARE, "ERROR: a.out kernel modules are " 42149874f6eSJoseph Koshy "unsupported \"%s\"", path); 42249874f6eSJoseph Koshy 42349874f6eSJoseph Koshy (void) snprintf(buffer, sizeof(buffer), "%s%s", 4240b86b1bbSFabien Thomas args.pa_fsroot, path); 42549874f6eSJoseph Koshy 42649874f6eSJoseph Koshy if ((fd = open(buffer, O_RDONLY, 0)) < 0 || 42749874f6eSJoseph Koshy (nbytes = read(fd, &ex, sizeof(ex))) < 0) { 42849874f6eSJoseph Koshy warn("WARNING: Cannot determine type of \"%s\"", path); 42949874f6eSJoseph Koshy image->pi_type = PMCSTAT_IMAGE_INDETERMINABLE; 43049874f6eSJoseph Koshy if (fd != -1) 43149874f6eSJoseph Koshy (void) close(fd); 43249874f6eSJoseph Koshy return; 43349874f6eSJoseph Koshy } 43449874f6eSJoseph Koshy 43549874f6eSJoseph Koshy (void) close(fd); 43649874f6eSJoseph Koshy 43749874f6eSJoseph Koshy if ((unsigned) nbytes != sizeof(ex) || 43849874f6eSJoseph Koshy N_BADMAG(ex)) 43949874f6eSJoseph Koshy return; 44049874f6eSJoseph Koshy 44149874f6eSJoseph Koshy image->pi_type = PMCSTAT_IMAGE_AOUT; 44249874f6eSJoseph Koshy 44349874f6eSJoseph Koshy /* TODO: the rest of a.out processing */ 44449874f6eSJoseph Koshy 44549874f6eSJoseph Koshy return; 44649874f6eSJoseph Koshy } 44749874f6eSJoseph Koshy 44849874f6eSJoseph Koshy /* 449b6010f9eSJoseph Koshy * Helper function. 450b6010f9eSJoseph Koshy */ 451b6010f9eSJoseph Koshy 452b6010f9eSJoseph Koshy static int 453b6010f9eSJoseph Koshy pmcstat_symbol_compare(const void *a, const void *b) 454b6010f9eSJoseph Koshy { 455b6010f9eSJoseph Koshy const struct pmcstat_symbol *sym1, *sym2; 456b6010f9eSJoseph Koshy 457b6010f9eSJoseph Koshy sym1 = (const struct pmcstat_symbol *) a; 458b6010f9eSJoseph Koshy sym2 = (const struct pmcstat_symbol *) b; 459b6010f9eSJoseph Koshy 460b6010f9eSJoseph Koshy if (sym1->ps_end <= sym2->ps_start) 461b6010f9eSJoseph Koshy return (-1); 462b6010f9eSJoseph Koshy if (sym1->ps_start >= sym2->ps_end) 463b6010f9eSJoseph Koshy return (1); 464b6010f9eSJoseph Koshy return (0); 465b6010f9eSJoseph Koshy } 466b6010f9eSJoseph Koshy 467b6010f9eSJoseph Koshy /* 468b6010f9eSJoseph Koshy * Map an address to a symbol in an image. 469b6010f9eSJoseph Koshy */ 470b6010f9eSJoseph Koshy 4710b86b1bbSFabien Thomas struct pmcstat_symbol * 472b6010f9eSJoseph Koshy pmcstat_symbol_search(struct pmcstat_image *image, uintfptr_t addr) 473b6010f9eSJoseph Koshy { 474b6010f9eSJoseph Koshy struct pmcstat_symbol sym; 475b6010f9eSJoseph Koshy 476b6010f9eSJoseph Koshy if (image->pi_symbols == NULL) 477b6010f9eSJoseph Koshy return (NULL); 478b6010f9eSJoseph Koshy 479b6010f9eSJoseph Koshy sym.ps_name = NULL; 480b6010f9eSJoseph Koshy sym.ps_start = addr; 481b6010f9eSJoseph Koshy sym.ps_end = addr + 1; 482b6010f9eSJoseph Koshy 483b6010f9eSJoseph Koshy return (bsearch((void *) &sym, image->pi_symbols, 484b6010f9eSJoseph Koshy image->pi_symcount, sizeof(struct pmcstat_symbol), 485b6010f9eSJoseph Koshy pmcstat_symbol_compare)); 486b6010f9eSJoseph Koshy } 487b6010f9eSJoseph Koshy 488b6010f9eSJoseph Koshy /* 489b6010f9eSJoseph Koshy * Add the list of symbols in the given section to the list associated 490b6010f9eSJoseph Koshy * with the object. 491b6010f9eSJoseph Koshy */ 492b6010f9eSJoseph Koshy static void 493b6010f9eSJoseph Koshy pmcstat_image_add_symbols(struct pmcstat_image *image, Elf *e, 494b6010f9eSJoseph Koshy Elf_Scn *scn, GElf_Shdr *sh) 495b6010f9eSJoseph Koshy { 496b6010f9eSJoseph Koshy int firsttime; 497b6010f9eSJoseph Koshy size_t n, newsyms, nshsyms, nfuncsyms; 498b6010f9eSJoseph Koshy struct pmcstat_symbol *symptr; 499b6010f9eSJoseph Koshy char *fnname; 500b6010f9eSJoseph Koshy GElf_Sym sym; 501b6010f9eSJoseph Koshy Elf_Data *data; 502b6010f9eSJoseph Koshy 503b6010f9eSJoseph Koshy if ((data = elf_getdata(scn, NULL)) == NULL) 504b6010f9eSJoseph Koshy return; 505b6010f9eSJoseph Koshy 506b6010f9eSJoseph Koshy /* 507b6010f9eSJoseph Koshy * Determine the number of functions named in this 508b6010f9eSJoseph Koshy * section. 509b6010f9eSJoseph Koshy */ 510b6010f9eSJoseph Koshy 511b6010f9eSJoseph Koshy nshsyms = sh->sh_size / sh->sh_entsize; 512b6010f9eSJoseph Koshy for (n = nfuncsyms = 0; n < nshsyms; n++) { 513b6010f9eSJoseph Koshy if (gelf_getsym(data, (int) n, &sym) != &sym) 514b6010f9eSJoseph Koshy return; 515b6010f9eSJoseph Koshy if (GELF_ST_TYPE(sym.st_info) == STT_FUNC) 516b6010f9eSJoseph Koshy nfuncsyms++; 517b6010f9eSJoseph Koshy } 518b6010f9eSJoseph Koshy 519b6010f9eSJoseph Koshy if (nfuncsyms == 0) 520b6010f9eSJoseph Koshy return; 521b6010f9eSJoseph Koshy 522b6010f9eSJoseph Koshy /* 523b6010f9eSJoseph Koshy * Allocate space for the new entries. 524b6010f9eSJoseph Koshy */ 525b6010f9eSJoseph Koshy firsttime = image->pi_symbols == NULL; 526b6010f9eSJoseph Koshy symptr = realloc(image->pi_symbols, 527b6010f9eSJoseph Koshy sizeof(*symptr) * (image->pi_symcount + nfuncsyms)); 528b6010f9eSJoseph Koshy if (symptr == image->pi_symbols) /* realloc() failed. */ 529b6010f9eSJoseph Koshy return; 530b6010f9eSJoseph Koshy image->pi_symbols = symptr; 531b6010f9eSJoseph Koshy 532b6010f9eSJoseph Koshy /* 533b6010f9eSJoseph Koshy * Append new symbols to the end of the current table. 534b6010f9eSJoseph Koshy */ 535b6010f9eSJoseph Koshy symptr += image->pi_symcount; 536b6010f9eSJoseph Koshy 537b6010f9eSJoseph Koshy for (n = newsyms = 0; n < nshsyms; n++) { 538b6010f9eSJoseph Koshy if (gelf_getsym(data, (int) n, &sym) != &sym) 539b6010f9eSJoseph Koshy return; 540b6010f9eSJoseph Koshy if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) 541b6010f9eSJoseph Koshy continue; 542b6010f9eSJoseph Koshy 543b6010f9eSJoseph Koshy if (!firsttime && pmcstat_symbol_search(image, sym.st_value)) 544b6010f9eSJoseph Koshy continue; /* We've seen this symbol already. */ 545b6010f9eSJoseph Koshy 546b6010f9eSJoseph Koshy if ((fnname = elf_strptr(e, sh->sh_link, sym.st_name)) 547b6010f9eSJoseph Koshy == NULL) 548b6010f9eSJoseph Koshy continue; 549b6010f9eSJoseph Koshy 550b6010f9eSJoseph Koshy symptr->ps_name = pmcstat_string_intern(fnname); 551b6010f9eSJoseph Koshy symptr->ps_start = sym.st_value - image->pi_vaddr; 552b6010f9eSJoseph Koshy symptr->ps_end = symptr->ps_start + sym.st_size; 553b6010f9eSJoseph Koshy symptr++; 554b6010f9eSJoseph Koshy 555b6010f9eSJoseph Koshy newsyms++; 556b6010f9eSJoseph Koshy } 557b6010f9eSJoseph Koshy 558b6010f9eSJoseph Koshy image->pi_symcount += newsyms; 559b6010f9eSJoseph Koshy 560b6010f9eSJoseph Koshy assert(newsyms <= nfuncsyms); 561b6010f9eSJoseph Koshy 562b6010f9eSJoseph Koshy /* 563b6010f9eSJoseph Koshy * Return space to the system if there were duplicates. 564b6010f9eSJoseph Koshy */ 565b6010f9eSJoseph Koshy if (newsyms < nfuncsyms) 566b6010f9eSJoseph Koshy image->pi_symbols = realloc(image->pi_symbols, 567b6010f9eSJoseph Koshy sizeof(*symptr) * image->pi_symcount); 568b6010f9eSJoseph Koshy 569b6010f9eSJoseph Koshy /* 570b6010f9eSJoseph Koshy * Keep the list of symbols sorted. 571b6010f9eSJoseph Koshy */ 572b6010f9eSJoseph Koshy qsort(image->pi_symbols, image->pi_symcount, sizeof(*symptr), 573b6010f9eSJoseph Koshy pmcstat_symbol_compare); 574b6010f9eSJoseph Koshy 575b6010f9eSJoseph Koshy /* 576b6010f9eSJoseph Koshy * Deal with function symbols that have a size of 'zero' by 577b6010f9eSJoseph Koshy * making them extend to the next higher address. These 578b6010f9eSJoseph Koshy * symbols are usually defined in assembly code. 579b6010f9eSJoseph Koshy */ 580b6010f9eSJoseph Koshy for (symptr = image->pi_symbols; 581b6010f9eSJoseph Koshy symptr < image->pi_symbols + (image->pi_symcount - 1); 582b6010f9eSJoseph Koshy symptr++) 583b6010f9eSJoseph Koshy if (symptr->ps_start == symptr->ps_end) 584b6010f9eSJoseph Koshy symptr->ps_end = (symptr+1)->ps_start; 585b6010f9eSJoseph Koshy } 586b6010f9eSJoseph Koshy 587b6010f9eSJoseph Koshy /* 58849874f6eSJoseph Koshy * Examine an ELF file to determine the size of its text segment. 58949874f6eSJoseph Koshy * Sets image->pi_type if anything conclusive can be determined about 59049874f6eSJoseph Koshy * this image. 59149874f6eSJoseph Koshy */ 59249874f6eSJoseph Koshy 59349874f6eSJoseph Koshy static void 5940b86b1bbSFabien Thomas pmcstat_image_get_elf_params(struct pmcstat_image *image) 59515139246SJoseph Koshy { 596b6010f9eSJoseph Koshy int fd; 597b6010f9eSJoseph Koshy size_t i, nph, nsh; 598b6010f9eSJoseph Koshy const char *path, *elfbase; 5990b86b1bbSFabien Thomas char *p, *endp; 60015139246SJoseph Koshy uintfptr_t minva, maxva; 601b6010f9eSJoseph Koshy Elf *e; 602b6010f9eSJoseph Koshy Elf_Scn *scn; 603b6010f9eSJoseph Koshy GElf_Ehdr eh; 604b6010f9eSJoseph Koshy GElf_Phdr ph; 605b6010f9eSJoseph Koshy GElf_Shdr sh; 60649874f6eSJoseph Koshy enum pmcstat_image_type image_type; 60749874f6eSJoseph Koshy char buffer[PATH_MAX]; 60815139246SJoseph Koshy 60960f918efSJoseph Koshy assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN); 61060f918efSJoseph Koshy 611b6010f9eSJoseph Koshy image->pi_start = minva = ~(uintfptr_t) 0; 612b6010f9eSJoseph Koshy image->pi_end = maxva = (uintfptr_t) 0; 613b6010f9eSJoseph Koshy image->pi_type = image_type = PMCSTAT_IMAGE_INDETERMINABLE; 614b6010f9eSJoseph Koshy image->pi_isdynamic = 0; 615b6010f9eSJoseph Koshy image->pi_dynlinkerpath = NULL; 616b6010f9eSJoseph Koshy image->pi_vaddr = 0; 61715139246SJoseph Koshy 618b6010f9eSJoseph Koshy path = pmcstat_string_unintern(image->pi_execpath); 61949874f6eSJoseph Koshy assert(path != NULL); 62015139246SJoseph Koshy 62149874f6eSJoseph Koshy /* 62249874f6eSJoseph Koshy * Look for kernel modules under FSROOT/KERNELPATH/NAME, 62349874f6eSJoseph Koshy * and user mode executable objects under FSROOT/PATHNAME. 62449874f6eSJoseph Koshy */ 62549874f6eSJoseph Koshy if (image->pi_iskernelmodule) 62649874f6eSJoseph Koshy (void) snprintf(buffer, sizeof(buffer), "%s%s/%s", 6270b86b1bbSFabien Thomas args.pa_fsroot, args.pa_kernel, path); 62849874f6eSJoseph Koshy else 62949874f6eSJoseph Koshy (void) snprintf(buffer, sizeof(buffer), "%s%s", 6300b86b1bbSFabien Thomas args.pa_fsroot, path); 63115139246SJoseph Koshy 632b6010f9eSJoseph Koshy e = NULL; 63349874f6eSJoseph Koshy if ((fd = open(buffer, O_RDONLY, 0)) < 0 || 634b6010f9eSJoseph Koshy (e = elf_begin(fd, ELF_C_READ, NULL)) == NULL || 635b6010f9eSJoseph Koshy (elf_kind(e) != ELF_K_ELF)) { 636b6010f9eSJoseph Koshy warnx("WARNING: Cannot determine the type of \"%s\".", 637b6010f9eSJoseph Koshy buffer); 638b6010f9eSJoseph Koshy goto done; 63949874f6eSJoseph Koshy } 64015139246SJoseph Koshy 641b6010f9eSJoseph Koshy if (gelf_getehdr(e, &eh) != &eh) { 642b6010f9eSJoseph Koshy warnx("WARNING: Cannot retrieve the ELF Header for " 643b6010f9eSJoseph Koshy "\"%s\": %s.", buffer, elf_errmsg(-1)); 644b6010f9eSJoseph Koshy goto done; 645b6010f9eSJoseph Koshy } 64615139246SJoseph Koshy 647b6010f9eSJoseph Koshy if (eh.e_type != ET_EXEC && eh.e_type != ET_DYN && 648b6010f9eSJoseph Koshy !(image->pi_iskernelmodule && eh.e_type == ET_REL)) { 649b6010f9eSJoseph Koshy warnx("WARNING: \"%s\" is of an unsupported ELF type.", 650b6010f9eSJoseph Koshy buffer); 651b6010f9eSJoseph Koshy goto done; 652b6010f9eSJoseph Koshy } 653b6010f9eSJoseph Koshy 654b6010f9eSJoseph Koshy image_type = eh.e_ident[EI_CLASS] == ELFCLASS32 ? 655b6010f9eSJoseph Koshy PMCSTAT_IMAGE_ELF32 : PMCSTAT_IMAGE_ELF64; 65615139246SJoseph Koshy 65749874f6eSJoseph Koshy /* 658b6010f9eSJoseph Koshy * Determine the virtual address where an executable would be 659b6010f9eSJoseph Koshy * loaded. Additionally, for dynamically linked executables, 660b6010f9eSJoseph Koshy * save the pathname to the runtime linker. 66149874f6eSJoseph Koshy */ 662b6010f9eSJoseph Koshy if (eh.e_type == ET_EXEC) { 663b6010f9eSJoseph Koshy if (elf_getphnum(e, &nph) == 0) { 664b6010f9eSJoseph Koshy warnx("WARNING: Could not determine the number of " 665b6010f9eSJoseph Koshy "program headers in \"%s\": %s.", buffer, 666b6010f9eSJoseph Koshy elf_errmsg(-1)); 667b6010f9eSJoseph Koshy goto done; 668887a8d04SJoseph Koshy } 669b6010f9eSJoseph Koshy for (i = 0; i < eh.e_phnum; i++) { 670b6010f9eSJoseph Koshy if (gelf_getphdr(e, i, &ph) != &ph) { 671b6010f9eSJoseph Koshy warnx("WARNING: Retrieval of PHDR entry #%ju " 672b6010f9eSJoseph Koshy "in \"%s\" failed: %s.", (uintmax_t) i, 673b6010f9eSJoseph Koshy buffer, elf_errmsg(-1)); 674b6010f9eSJoseph Koshy goto done; 675b6010f9eSJoseph Koshy } 676b6010f9eSJoseph Koshy switch (ph.p_type) { 677b6010f9eSJoseph Koshy case PT_DYNAMIC: 678b6010f9eSJoseph Koshy image->pi_isdynamic = 1; 679887a8d04SJoseph Koshy break; 680b6010f9eSJoseph Koshy case PT_INTERP: 681b6010f9eSJoseph Koshy if ((elfbase = elf_rawfile(e, NULL)) == NULL) { 682b6010f9eSJoseph Koshy warnx("WARNING: Cannot retrieve the " 683b6010f9eSJoseph Koshy "interpreter for \"%s\": %s.", 684b6010f9eSJoseph Koshy buffer, elf_errmsg(-1)); 685b6010f9eSJoseph Koshy goto done; 686887a8d04SJoseph Koshy } 687b6010f9eSJoseph Koshy image->pi_dynlinkerpath = 688b6010f9eSJoseph Koshy pmcstat_string_intern(elfbase + 689b6010f9eSJoseph Koshy ph.p_offset); 690b6010f9eSJoseph Koshy break; 691b6010f9eSJoseph Koshy case PT_LOAD: 692b6010f9eSJoseph Koshy if (ph.p_offset == 0) 693b6010f9eSJoseph Koshy image->pi_vaddr = ph.p_vaddr; 69415139246SJoseph Koshy break; 69515139246SJoseph Koshy } 696b6010f9eSJoseph Koshy } 697b6010f9eSJoseph Koshy } 698887a8d04SJoseph Koshy 699b6010f9eSJoseph Koshy /* 700b6010f9eSJoseph Koshy * Get the min and max VA associated with this ELF object. 701b6010f9eSJoseph Koshy */ 702b6010f9eSJoseph Koshy if (elf_getshnum(e, &nsh) == 0) { 703b6010f9eSJoseph Koshy warnx("WARNING: Could not determine the number of sections " 704b6010f9eSJoseph Koshy "for \"%s\": %s.", buffer, elf_errmsg(-1)); 705b6010f9eSJoseph Koshy goto done; 706b6010f9eSJoseph Koshy } 707b6010f9eSJoseph Koshy 708b6010f9eSJoseph Koshy for (i = 0; i < nsh; i++) { 709b6010f9eSJoseph Koshy if ((scn = elf_getscn(e, i)) == NULL || 710b6010f9eSJoseph Koshy gelf_getshdr(scn, &sh) != &sh) { 711b6010f9eSJoseph Koshy warnx("WARNING: Could not retrieve section header " 712b6010f9eSJoseph Koshy "#%ju in \"%s\": %s.", (uintmax_t) i, buffer, 713b6010f9eSJoseph Koshy elf_errmsg(-1)); 714b6010f9eSJoseph Koshy goto done; 715b6010f9eSJoseph Koshy } 716b6010f9eSJoseph Koshy if (sh.sh_flags & SHF_EXECINSTR) { 717b6010f9eSJoseph Koshy minva = min(minva, sh.sh_addr); 718b6010f9eSJoseph Koshy maxva = max(maxva, sh.sh_addr + sh.sh_size); 719b6010f9eSJoseph Koshy } 720b6010f9eSJoseph Koshy if (sh.sh_type == SHT_SYMTAB || sh.sh_type == SHT_DYNSYM) 721b6010f9eSJoseph Koshy pmcstat_image_add_symbols(image, e, scn, &sh); 722b6010f9eSJoseph Koshy } 723887a8d04SJoseph Koshy 724887a8d04SJoseph Koshy image->pi_start = minva; 725887a8d04SJoseph Koshy image->pi_end = maxva; 72649874f6eSJoseph Koshy image->pi_type = image_type; 727b6010f9eSJoseph Koshy image->pi_fullpath = pmcstat_string_intern(buffer); 72815139246SJoseph Koshy 7290b86b1bbSFabien Thomas /* Build display name 7300b86b1bbSFabien Thomas */ 7310b86b1bbSFabien Thomas endp = buffer; 7320b86b1bbSFabien Thomas for (p = buffer; *p; p++) 7330b86b1bbSFabien Thomas if (*p == '/') 7340b86b1bbSFabien Thomas endp = p+1; 7350b86b1bbSFabien Thomas image->pi_name = pmcstat_string_intern(endp); 7360b86b1bbSFabien Thomas 737b6010f9eSJoseph Koshy done: 738b6010f9eSJoseph Koshy (void) elf_end(e); 739b6010f9eSJoseph Koshy if (fd >= 0) 740b6010f9eSJoseph Koshy (void) close(fd); 74149874f6eSJoseph Koshy return; 74249874f6eSJoseph Koshy } 74315139246SJoseph Koshy 74449874f6eSJoseph Koshy /* 74549874f6eSJoseph Koshy * Given an image descriptor, determine whether it is an ELF, or AOUT. 74649874f6eSJoseph Koshy * If no handler claims the image, set its type to 'INDETERMINABLE'. 74749874f6eSJoseph Koshy */ 74849874f6eSJoseph Koshy 7490b86b1bbSFabien Thomas void 7500b86b1bbSFabien Thomas pmcstat_image_determine_type(struct pmcstat_image *image) 75149874f6eSJoseph Koshy { 75249874f6eSJoseph Koshy assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN); 75349874f6eSJoseph Koshy 75449874f6eSJoseph Koshy /* Try each kind of handler in turn */ 75549874f6eSJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 7560b86b1bbSFabien Thomas pmcstat_image_get_elf_params(image); 75749874f6eSJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 7580b86b1bbSFabien Thomas pmcstat_image_get_aout_params(image); 75949874f6eSJoseph Koshy 76049874f6eSJoseph Koshy /* 76149874f6eSJoseph Koshy * Otherwise, remember that we tried to determine 76249874f6eSJoseph Koshy * the object's type and had failed. 76349874f6eSJoseph Koshy */ 76449874f6eSJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 76549874f6eSJoseph Koshy image->pi_type = PMCSTAT_IMAGE_INDETERMINABLE; 76615139246SJoseph Koshy } 76715139246SJoseph Koshy 76815139246SJoseph Koshy /* 76960f918efSJoseph Koshy * Locate an image descriptor given an interned path, adding a fresh 77060f918efSJoseph Koshy * descriptor to the cache if necessary. This function also finds a 77160f918efSJoseph Koshy * suitable name for this image's sample file. 77249874f6eSJoseph Koshy * 77349874f6eSJoseph Koshy * We defer filling in the file format specific parts of the image 77449874f6eSJoseph Koshy * structure till the time we actually see a sample that would fall 77549874f6eSJoseph Koshy * into this image. 77615139246SJoseph Koshy */ 77715139246SJoseph Koshy 77815139246SJoseph Koshy static struct pmcstat_image * 77949874f6eSJoseph Koshy pmcstat_image_from_path(pmcstat_interned_string internedpath, 78049874f6eSJoseph Koshy int iskernelmodule) 78115139246SJoseph Koshy { 7820b86b1bbSFabien Thomas int hash; 78315139246SJoseph Koshy struct pmcstat_image *pi; 78415139246SJoseph Koshy 78549874f6eSJoseph Koshy hash = pmcstat_string_lookup_hash(internedpath); 78615139246SJoseph Koshy 78749874f6eSJoseph Koshy /* First, look for an existing entry. */ 78815139246SJoseph Koshy LIST_FOREACH(pi, &pmcstat_image_hash[hash], pi_next) 78949874f6eSJoseph Koshy if (pi->pi_execpath == internedpath && 790b6010f9eSJoseph Koshy pi->pi_iskernelmodule == iskernelmodule) 79149874f6eSJoseph Koshy return (pi); 79215139246SJoseph Koshy 79315139246SJoseph Koshy /* 794b6010f9eSJoseph Koshy * Allocate a new entry and place it at the head of the hash 795b6010f9eSJoseph Koshy * and LRU lists. 79615139246SJoseph Koshy */ 79715139246SJoseph Koshy pi = malloc(sizeof(*pi)); 79815139246SJoseph Koshy if (pi == NULL) 79949874f6eSJoseph Koshy return (NULL); 80015139246SJoseph Koshy 80115139246SJoseph Koshy pi->pi_type = PMCSTAT_IMAGE_UNKNOWN; 80249874f6eSJoseph Koshy pi->pi_execpath = internedpath; 80315139246SJoseph Koshy pi->pi_start = ~0; 80415139246SJoseph Koshy pi->pi_end = 0; 805b6010f9eSJoseph Koshy pi->pi_entry = 0; 806b6010f9eSJoseph Koshy pi->pi_vaddr = 0; 807b6010f9eSJoseph Koshy pi->pi_isdynamic = 0; 80849874f6eSJoseph Koshy pi->pi_iskernelmodule = iskernelmodule; 809b6010f9eSJoseph Koshy pi->pi_dynlinkerpath = NULL; 810b6010f9eSJoseph Koshy pi->pi_symbols = NULL; 811b6010f9eSJoseph Koshy pi->pi_symcount = 0; 8120b86b1bbSFabien Thomas pi->pi_addr2line = NULL; 81315139246SJoseph Koshy 8140b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_initimage != NULL) 8150b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_initimage(pi); 8160b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_initimage != NULL) 8170b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_initimage(pi); 81815139246SJoseph Koshy 81915139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_image_hash[hash], pi, pi_next); 82015139246SJoseph Koshy 82149874f6eSJoseph Koshy return (pi); 82215139246SJoseph Koshy } 82315139246SJoseph Koshy 82415139246SJoseph Koshy /* 82549874f6eSJoseph Koshy * Record the fact that PC values from 'start' to 'end' come from 82615139246SJoseph Koshy * image 'image'. 82715139246SJoseph Koshy */ 82815139246SJoseph Koshy 82915139246SJoseph Koshy static void 83015139246SJoseph Koshy pmcstat_image_link(struct pmcstat_process *pp, struct pmcstat_image *image, 83149874f6eSJoseph Koshy uintfptr_t start) 83215139246SJoseph Koshy { 83315139246SJoseph Koshy struct pmcstat_pcmap *pcm, *pcmnew; 83449874f6eSJoseph Koshy uintfptr_t offset; 83549874f6eSJoseph Koshy 83649874f6eSJoseph Koshy assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN && 83749874f6eSJoseph Koshy image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE); 83815139246SJoseph Koshy 83915139246SJoseph Koshy if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL) 84049874f6eSJoseph Koshy err(EX_OSERR, "ERROR: Cannot create a map entry"); 84115139246SJoseph Koshy 84249874f6eSJoseph Koshy /* 84349874f6eSJoseph Koshy * Adjust the map entry to only cover the text portion 84449874f6eSJoseph Koshy * of the object. 84549874f6eSJoseph Koshy */ 84649874f6eSJoseph Koshy 84749874f6eSJoseph Koshy offset = start - image->pi_vaddr; 84849874f6eSJoseph Koshy pcmnew->ppm_lowpc = image->pi_start + offset; 84949874f6eSJoseph Koshy pcmnew->ppm_highpc = image->pi_end + offset; 85015139246SJoseph Koshy pcmnew->ppm_image = image; 85115139246SJoseph Koshy 85249874f6eSJoseph Koshy assert(pcmnew->ppm_lowpc < pcmnew->ppm_highpc); 85349874f6eSJoseph Koshy 85449874f6eSJoseph Koshy /* Overlapped mmap()'s are assumed to never occur. */ 85515139246SJoseph Koshy TAILQ_FOREACH(pcm, &pp->pp_map, ppm_next) 85649874f6eSJoseph Koshy if (pcm->ppm_lowpc >= pcmnew->ppm_highpc) 85715139246SJoseph Koshy break; 85815139246SJoseph Koshy 85915139246SJoseph Koshy if (pcm == NULL) 86015139246SJoseph Koshy TAILQ_INSERT_TAIL(&pp->pp_map, pcmnew, ppm_next); 86115139246SJoseph Koshy else 86215139246SJoseph Koshy TAILQ_INSERT_BEFORE(pcm, pcmnew, ppm_next); 86315139246SJoseph Koshy } 86415139246SJoseph Koshy 86515139246SJoseph Koshy /* 86649874f6eSJoseph Koshy * Unmap images in the range [start..end) associated with process 86749874f6eSJoseph Koshy * 'pp'. 86849874f6eSJoseph Koshy */ 86949874f6eSJoseph Koshy 87049874f6eSJoseph Koshy static void 87149874f6eSJoseph Koshy pmcstat_image_unmap(struct pmcstat_process *pp, uintfptr_t start, 87249874f6eSJoseph Koshy uintfptr_t end) 87349874f6eSJoseph Koshy { 87449874f6eSJoseph Koshy struct pmcstat_pcmap *pcm, *pcmtmp, *pcmnew; 87549874f6eSJoseph Koshy 87649874f6eSJoseph Koshy assert(pp != NULL); 87749874f6eSJoseph Koshy assert(start < end); 87849874f6eSJoseph Koshy 87949874f6eSJoseph Koshy /* 88049874f6eSJoseph Koshy * Cases: 88149874f6eSJoseph Koshy * - we could have the range completely in the middle of an 88249874f6eSJoseph Koshy * existing pcmap; in this case we have to split the pcmap 88349874f6eSJoseph Koshy * structure into two (i.e., generate a 'hole'). 88449874f6eSJoseph Koshy * - we could have the range covering multiple pcmaps; these 88549874f6eSJoseph Koshy * will have to be removed. 88649874f6eSJoseph Koshy * - we could have either 'start' or 'end' falling in the 88749874f6eSJoseph Koshy * middle of a pcmap; in this case shorten the entry. 88849874f6eSJoseph Koshy */ 88949874f6eSJoseph Koshy TAILQ_FOREACH_SAFE(pcm, &pp->pp_map, ppm_next, pcmtmp) { 89049874f6eSJoseph Koshy assert(pcm->ppm_lowpc < pcm->ppm_highpc); 89149874f6eSJoseph Koshy if (pcm->ppm_highpc <= start) 89249874f6eSJoseph Koshy continue; 893b6010f9eSJoseph Koshy if (pcm->ppm_lowpc >= end) 89449874f6eSJoseph Koshy return; 89549874f6eSJoseph Koshy if (pcm->ppm_lowpc >= start && pcm->ppm_highpc <= end) { 89649874f6eSJoseph Koshy /* 89749874f6eSJoseph Koshy * The current pcmap is completely inside the 89849874f6eSJoseph Koshy * unmapped range: remove it entirely. 89949874f6eSJoseph Koshy */ 90049874f6eSJoseph Koshy TAILQ_REMOVE(&pp->pp_map, pcm, ppm_next); 90149874f6eSJoseph Koshy free(pcm); 90249874f6eSJoseph Koshy } else if (pcm->ppm_lowpc < start && pcm->ppm_highpc > end) { 90349874f6eSJoseph Koshy /* 90449874f6eSJoseph Koshy * Split this pcmap into two; curtail the 90549874f6eSJoseph Koshy * current map to end at [start-1], and start 90649874f6eSJoseph Koshy * the new one at [end]. 90749874f6eSJoseph Koshy */ 90849874f6eSJoseph Koshy if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL) 90949874f6eSJoseph Koshy err(EX_OSERR, "ERROR: Cannot split a map " 91049874f6eSJoseph Koshy "entry"); 91149874f6eSJoseph Koshy 91249874f6eSJoseph Koshy pcmnew->ppm_image = pcm->ppm_image; 91349874f6eSJoseph Koshy 91449874f6eSJoseph Koshy pcmnew->ppm_lowpc = end; 91549874f6eSJoseph Koshy pcmnew->ppm_highpc = pcm->ppm_highpc; 91649874f6eSJoseph Koshy 91749874f6eSJoseph Koshy pcm->ppm_highpc = start; 91849874f6eSJoseph Koshy 91949874f6eSJoseph Koshy TAILQ_INSERT_AFTER(&pp->pp_map, pcm, pcmnew, ppm_next); 92049874f6eSJoseph Koshy 92149874f6eSJoseph Koshy return; 922b6010f9eSJoseph Koshy } else if (pcm->ppm_lowpc < start && pcm->ppm_highpc <= end) 923b6010f9eSJoseph Koshy pcm->ppm_highpc = start; 924b6010f9eSJoseph Koshy else if (pcm->ppm_lowpc >= start && pcm->ppm_highpc > end) 925b6010f9eSJoseph Koshy pcm->ppm_lowpc = end; 92649874f6eSJoseph Koshy else 92749874f6eSJoseph Koshy assert(0); 92849874f6eSJoseph Koshy } 92949874f6eSJoseph Koshy } 93049874f6eSJoseph Koshy 93149874f6eSJoseph Koshy /* 9320b86b1bbSFabien Thomas * Resolve file name and line number for the given address. 9330b86b1bbSFabien Thomas */ 9340b86b1bbSFabien Thomas int 9350b86b1bbSFabien Thomas pmcstat_image_addr2line(struct pmcstat_image *image, uintfptr_t addr, 9360b86b1bbSFabien Thomas char *sourcefile, size_t sourcefile_len, unsigned *sourceline, 9370b86b1bbSFabien Thomas char *funcname, size_t funcname_len) 9380b86b1bbSFabien Thomas { 9390b86b1bbSFabien Thomas static int addr2line_warn = 0; 9400b86b1bbSFabien Thomas 9410b86b1bbSFabien Thomas char *sep, cmdline[PATH_MAX], imagepath[PATH_MAX]; 9420b86b1bbSFabien Thomas int fd; 9430b86b1bbSFabien Thomas 9440b86b1bbSFabien Thomas if (image->pi_addr2line == NULL) { 9450b86b1bbSFabien Thomas snprintf(imagepath, sizeof(imagepath), "%s.symbols", 9460b86b1bbSFabien Thomas pmcstat_string_unintern(image->pi_fullpath)); 9470b86b1bbSFabien Thomas fd = open(imagepath, O_RDONLY); 9480b86b1bbSFabien Thomas if (fd < 0) { 9490b86b1bbSFabien Thomas snprintf(imagepath, sizeof(imagepath), "%s", 9500b86b1bbSFabien Thomas pmcstat_string_unintern(image->pi_fullpath)); 9510b86b1bbSFabien Thomas } else 9520b86b1bbSFabien Thomas close(fd); 9530b86b1bbSFabien Thomas snprintf(cmdline, sizeof(cmdline), "addr2line -Cfe \"%s\"", 9540b86b1bbSFabien Thomas imagepath); 9550b86b1bbSFabien Thomas image->pi_addr2line = popen(cmdline, "r+"); 9560b86b1bbSFabien Thomas if (image->pi_addr2line == NULL) { 9570b86b1bbSFabien Thomas if (!addr2line_warn) { 9580b86b1bbSFabien Thomas addr2line_warn = 1; 9590b86b1bbSFabien Thomas warnx("WARNING: addr2line is needed" 9600b86b1bbSFabien Thomas "for source code information."); 9610b86b1bbSFabien Thomas } 9620b86b1bbSFabien Thomas return (0); 9630b86b1bbSFabien Thomas } 9640b86b1bbSFabien Thomas } 9650b86b1bbSFabien Thomas 9660b86b1bbSFabien Thomas if (feof(image->pi_addr2line) || ferror(image->pi_addr2line)) { 9670b86b1bbSFabien Thomas warnx("WARNING: addr2line pipe error"); 9680b86b1bbSFabien Thomas pclose(image->pi_addr2line); 9690b86b1bbSFabien Thomas image->pi_addr2line = NULL; 9700b86b1bbSFabien Thomas return (0); 9710b86b1bbSFabien Thomas } 9720b86b1bbSFabien Thomas 9730b86b1bbSFabien Thomas fprintf(image->pi_addr2line, "%p\n", (void *)addr); 9740b86b1bbSFabien Thomas 9750b86b1bbSFabien Thomas if (fgets(funcname, funcname_len, image->pi_addr2line) == NULL) { 9760b86b1bbSFabien Thomas warnx("WARNING: addr2line function name read error"); 9770b86b1bbSFabien Thomas return (0); 9780b86b1bbSFabien Thomas } 9790b86b1bbSFabien Thomas sep = strchr(funcname, '\n'); 9800b86b1bbSFabien Thomas if (sep != NULL) 9810b86b1bbSFabien Thomas *sep = '\0'; 9820b86b1bbSFabien Thomas 9830b86b1bbSFabien Thomas if (fgets(sourcefile, sourcefile_len, image->pi_addr2line) == NULL) { 9840b86b1bbSFabien Thomas warnx("WARNING: addr2line source file read error"); 9850b86b1bbSFabien Thomas return (0); 9860b86b1bbSFabien Thomas } 9870b86b1bbSFabien Thomas sep = strchr(sourcefile, ':'); 9880b86b1bbSFabien Thomas if (sep == NULL) { 9890b86b1bbSFabien Thomas warnx("WARNING: addr2line source line separator missing"); 9900b86b1bbSFabien Thomas return (0); 9910b86b1bbSFabien Thomas } 9920b86b1bbSFabien Thomas *sep = '\0'; 9930b86b1bbSFabien Thomas *sourceline = atoi(sep+1); 9940b86b1bbSFabien Thomas if (*sourceline == 0) 9950b86b1bbSFabien Thomas return (0); 9960b86b1bbSFabien Thomas 9970b86b1bbSFabien Thomas return (1); 9980b86b1bbSFabien Thomas } 9990b86b1bbSFabien Thomas 10000b86b1bbSFabien Thomas /* 100115139246SJoseph Koshy * Add a {pmcid,name} mapping. 100215139246SJoseph Koshy */ 100315139246SJoseph Koshy 100415139246SJoseph Koshy static void 10050b86b1bbSFabien Thomas pmcstat_pmcid_add(pmc_id_t pmcid, pmcstat_interned_string ps) 100615139246SJoseph Koshy { 10070b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pr, *prm; 100815139246SJoseph Koshy 1009b6010f9eSJoseph Koshy /* Replace an existing name for the PMC. */ 10100b86b1bbSFabien Thomas prm = NULL; 101115139246SJoseph Koshy LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 101215139246SJoseph Koshy if (pr->pr_pmcid == pmcid) { 101349874f6eSJoseph Koshy pr->pr_pmcname = ps; 101415139246SJoseph Koshy return; 10150b86b1bbSFabien Thomas } else if (pr->pr_pmcname == ps) 10160b86b1bbSFabien Thomas prm = pr; 101715139246SJoseph Koshy 1018b6010f9eSJoseph Koshy /* 10190b86b1bbSFabien Thomas * Otherwise, allocate a new descriptor and call the 10200b86b1bbSFabien Thomas * plugins hook. 1021b6010f9eSJoseph Koshy */ 102215139246SJoseph Koshy if ((pr = malloc(sizeof(*pr))) == NULL) 102315139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate pmc record"); 102415139246SJoseph Koshy 102515139246SJoseph Koshy pr->pr_pmcid = pmcid; 102649874f6eSJoseph Koshy pr->pr_pmcname = ps; 10270b86b1bbSFabien Thomas pr->pr_pmcin = pmcstat_npmcs++; 1028c86819ecSFabien Thomas pr->pr_samples = 0; 1029c86819ecSFabien Thomas pr->pr_dubious_frames = 0; 10300b86b1bbSFabien Thomas pr->pr_merge = prm == NULL ? pr : prm; 10310b86b1bbSFabien Thomas 103215139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); 103315139246SJoseph Koshy 10340b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_newpmc != NULL) 10350b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_newpmc(ps, pr); 10360b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_newpmc != NULL) 10370b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_newpmc(ps, pr); 103815139246SJoseph Koshy } 103915139246SJoseph Koshy 104015139246SJoseph Koshy /* 104149874f6eSJoseph Koshy * Given a pmcid in use, find its human-readable name. 104215139246SJoseph Koshy */ 104315139246SJoseph Koshy 10440b86b1bbSFabien Thomas const char * 104515139246SJoseph Koshy pmcstat_pmcid_to_name(pmc_id_t pmcid) 104615139246SJoseph Koshy { 104715139246SJoseph Koshy struct pmcstat_pmcrecord *pr; 104815139246SJoseph Koshy 104915139246SJoseph Koshy LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 105015139246SJoseph Koshy if (pr->pr_pmcid == pmcid) 105149874f6eSJoseph Koshy return (pmcstat_string_unintern(pr->pr_pmcname)); 105215139246SJoseph Koshy 10530b86b1bbSFabien Thomas err(EX_SOFTWARE, "ERROR: cannot find pmcid"); 10540b86b1bbSFabien Thomas return NULL; 10550b86b1bbSFabien Thomas } 105615139246SJoseph Koshy 10570b86b1bbSFabien Thomas /* 10580b86b1bbSFabien Thomas * Convert PMC index to name. 10590b86b1bbSFabien Thomas */ 106015139246SJoseph Koshy 10610b86b1bbSFabien Thomas const char * 10620b86b1bbSFabien Thomas pmcstat_pmcindex_to_name(int pmcin) 10630b86b1bbSFabien Thomas { 10640b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pr; 106515139246SJoseph Koshy 10660b86b1bbSFabien Thomas LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 10670b86b1bbSFabien Thomas if (pr->pr_pmcin == pmcin) 10680b86b1bbSFabien Thomas return pmcstat_string_unintern(pr->pr_pmcname); 10690b86b1bbSFabien Thomas 10700b86b1bbSFabien Thomas return NULL; 10710b86b1bbSFabien Thomas } 10720b86b1bbSFabien Thomas 10730b86b1bbSFabien Thomas /* 10740b86b1bbSFabien Thomas * Return PMC record with given index. 10750b86b1bbSFabien Thomas */ 10760b86b1bbSFabien Thomas 10770b86b1bbSFabien Thomas struct pmcstat_pmcrecord * 10780b86b1bbSFabien Thomas pmcstat_pmcindex_to_pmcr(int pmcin) 10790b86b1bbSFabien Thomas { 10800b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pr; 10810b86b1bbSFabien Thomas 10820b86b1bbSFabien Thomas LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 10830b86b1bbSFabien Thomas if (pr->pr_pmcin == pmcin) 10840b86b1bbSFabien Thomas return pr; 10850b86b1bbSFabien Thomas 10860b86b1bbSFabien Thomas err(EX_SOFTWARE, "ERROR: invalid pmcindex"); 10870b86b1bbSFabien Thomas return NULL; 10880b86b1bbSFabien Thomas } 10890b86b1bbSFabien Thomas 10900b86b1bbSFabien Thomas /* 10910b86b1bbSFabien Thomas * Get PMC record by id, apply merge policy. 10920b86b1bbSFabien Thomas */ 10930b86b1bbSFabien Thomas 10940b86b1bbSFabien Thomas static struct pmcstat_pmcrecord * 10950b86b1bbSFabien Thomas pmcstat_lookup_pmcid(pmc_id_t pmcid) 10960b86b1bbSFabien Thomas { 10970b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pr; 10980b86b1bbSFabien Thomas 10990b86b1bbSFabien Thomas LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) { 11000b86b1bbSFabien Thomas if (pr->pr_pmcid == pmcid) { 11010b86b1bbSFabien Thomas if (pmcstat_mergepmc) 11020b86b1bbSFabien Thomas return pr->pr_merge; 11030b86b1bbSFabien Thomas return pr; 11040b86b1bbSFabien Thomas } 11050b86b1bbSFabien Thomas } 11060b86b1bbSFabien Thomas 11070b86b1bbSFabien Thomas return NULL; 110815139246SJoseph Koshy } 110915139246SJoseph Koshy 111015139246SJoseph Koshy /* 111149874f6eSJoseph Koshy * Associate an AOUT image with a process. 111215139246SJoseph Koshy */ 111315139246SJoseph Koshy 111415139246SJoseph Koshy static void 111549874f6eSJoseph Koshy pmcstat_process_aout_exec(struct pmcstat_process *pp, 11160b86b1bbSFabien Thomas struct pmcstat_image *image, uintfptr_t entryaddr) 111715139246SJoseph Koshy { 111849874f6eSJoseph Koshy (void) pp; 111949874f6eSJoseph Koshy (void) image; 112049874f6eSJoseph Koshy (void) entryaddr; 112149874f6eSJoseph Koshy /* TODO Implement a.out handling */ 112249874f6eSJoseph Koshy } 112349874f6eSJoseph Koshy 112449874f6eSJoseph Koshy /* 112549874f6eSJoseph Koshy * Associate an ELF image with a process. 112649874f6eSJoseph Koshy */ 112749874f6eSJoseph Koshy 112849874f6eSJoseph Koshy static void 112949874f6eSJoseph Koshy pmcstat_process_elf_exec(struct pmcstat_process *pp, 11300b86b1bbSFabien Thomas struct pmcstat_image *image, uintfptr_t entryaddr) 113149874f6eSJoseph Koshy { 113215139246SJoseph Koshy uintmax_t libstart; 113349874f6eSJoseph Koshy struct pmcstat_image *rtldimage; 113415139246SJoseph Koshy 113549874f6eSJoseph Koshy assert(image->pi_type == PMCSTAT_IMAGE_ELF32 || 113649874f6eSJoseph Koshy image->pi_type == PMCSTAT_IMAGE_ELF64); 113715139246SJoseph Koshy 113860f918efSJoseph Koshy /* Create a map entry for the base executable. */ 113949874f6eSJoseph Koshy pmcstat_image_link(pp, image, image->pi_vaddr); 114015139246SJoseph Koshy 114160f918efSJoseph Koshy /* 1142b6010f9eSJoseph Koshy * For dynamically linked executables we need to determine 1143b6010f9eSJoseph Koshy * where the dynamic linker was mapped to for this process, 1144b6010f9eSJoseph Koshy * Subsequent executable objects that are mapped in by the 1145b6010f9eSJoseph Koshy * dynamic linker will be tracked by log events of type 1146b6010f9eSJoseph Koshy * PMCLOG_TYPE_MAP_IN. 114760f918efSJoseph Koshy */ 114849874f6eSJoseph Koshy 114915139246SJoseph Koshy if (image->pi_isdynamic) { 115015139246SJoseph Koshy 115160f918efSJoseph Koshy /* 115260f918efSJoseph Koshy * The runtime loader gets loaded just after the maximum 115360f918efSJoseph Koshy * possible heap address. Like so: 115460f918efSJoseph Koshy * 115560f918efSJoseph Koshy * [ TEXT DATA BSS HEAP -->*RTLD SHLIBS <--STACK] 115660f918efSJoseph Koshy * ^ ^ 115760f918efSJoseph Koshy * 0 VM_MAXUSER_ADDRESS 115849874f6eSJoseph Koshy 115960f918efSJoseph Koshy * 116060f918efSJoseph Koshy * The exact address where the loader gets mapped in 116160f918efSJoseph Koshy * will vary according to the size of the executable 116260f918efSJoseph Koshy * and the limits on the size of the process'es data 116360f918efSJoseph Koshy * segment at the time of exec(). The entry address 116460f918efSJoseph Koshy * recorded at process exec time corresponds to the 116560f918efSJoseph Koshy * 'start' address inside the dynamic linker. From 116660f918efSJoseph Koshy * this we can figure out the address where the 116760f918efSJoseph Koshy * runtime loader's file object had been mapped to. 116860f918efSJoseph Koshy */ 11690b86b1bbSFabien Thomas rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath, 0); 117049874f6eSJoseph Koshy if (rtldimage == NULL) { 117149874f6eSJoseph Koshy warnx("WARNING: Cannot find image for \"%s\".", 117249874f6eSJoseph Koshy pmcstat_string_unintern(image->pi_dynlinkerpath)); 117349874f6eSJoseph Koshy pmcstat_stats.ps_exec_errors++; 117449874f6eSJoseph Koshy return; 117549874f6eSJoseph Koshy } 117649874f6eSJoseph Koshy 117760f918efSJoseph Koshy if (rtldimage->pi_type == PMCSTAT_IMAGE_UNKNOWN) 11780b86b1bbSFabien Thomas pmcstat_image_get_elf_params(rtldimage); 117949874f6eSJoseph Koshy 118049874f6eSJoseph Koshy if (rtldimage->pi_type != PMCSTAT_IMAGE_ELF32 && 118149874f6eSJoseph Koshy rtldimage->pi_type != PMCSTAT_IMAGE_ELF64) { 118249874f6eSJoseph Koshy warnx("WARNING: rtld not an ELF object \"%s\".", 118349874f6eSJoseph Koshy pmcstat_string_unintern(image->pi_dynlinkerpath)); 118449874f6eSJoseph Koshy return; 118549874f6eSJoseph Koshy } 118660f918efSJoseph Koshy 118760f918efSJoseph Koshy libstart = entryaddr - rtldimage->pi_entry; 118849874f6eSJoseph Koshy pmcstat_image_link(pp, rtldimage, libstart); 118915139246SJoseph Koshy } 119015139246SJoseph Koshy } 119115139246SJoseph Koshy 119215139246SJoseph Koshy /* 119315139246SJoseph Koshy * Find the process descriptor corresponding to a PID. If 'allocate' 119415139246SJoseph Koshy * is zero, we return a NULL if a pid descriptor could not be found or 119515139246SJoseph Koshy * a process descriptor process. If 'allocate' is non-zero, then we 119615139246SJoseph Koshy * will attempt to allocate a fresh process descriptor. Zombie 119715139246SJoseph Koshy * process descriptors are only removed if a fresh allocation for the 119815139246SJoseph Koshy * same PID is requested. 119915139246SJoseph Koshy */ 120015139246SJoseph Koshy 120115139246SJoseph Koshy static struct pmcstat_process * 120215139246SJoseph Koshy pmcstat_process_lookup(pid_t pid, int allocate) 120315139246SJoseph Koshy { 120415139246SJoseph Koshy uint32_t hash; 120515139246SJoseph Koshy struct pmcstat_pcmap *ppm, *ppmtmp; 120615139246SJoseph Koshy struct pmcstat_process *pp, *pptmp; 120715139246SJoseph Koshy 120815139246SJoseph Koshy hash = (uint32_t) pid & PMCSTAT_HASH_MASK; /* simplicity wins */ 120915139246SJoseph Koshy 121015139246SJoseph Koshy LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[hash], pp_next, pptmp) 121115139246SJoseph Koshy if (pp->pp_pid == pid) { 121215139246SJoseph Koshy /* Found a descriptor, check and process zombies */ 121349874f6eSJoseph Koshy if (allocate && pp->pp_isactive == 0) { 121415139246SJoseph Koshy /* remove maps */ 121515139246SJoseph Koshy TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, 121615139246SJoseph Koshy ppmtmp) { 121715139246SJoseph Koshy TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 121815139246SJoseph Koshy free(ppm); 121915139246SJoseph Koshy } 122015139246SJoseph Koshy /* remove process entry */ 122115139246SJoseph Koshy LIST_REMOVE(pp, pp_next); 122215139246SJoseph Koshy free(pp); 122315139246SJoseph Koshy break; 122415139246SJoseph Koshy } 122549874f6eSJoseph Koshy return (pp); 122615139246SJoseph Koshy } 122715139246SJoseph Koshy 122815139246SJoseph Koshy if (!allocate) 122949874f6eSJoseph Koshy return (NULL); 123015139246SJoseph Koshy 123115139246SJoseph Koshy if ((pp = malloc(sizeof(*pp))) == NULL) 123215139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate pid descriptor"); 123315139246SJoseph Koshy 123415139246SJoseph Koshy pp->pp_pid = pid; 123515139246SJoseph Koshy pp->pp_isactive = 1; 123615139246SJoseph Koshy 123715139246SJoseph Koshy TAILQ_INIT(&pp->pp_map); 123815139246SJoseph Koshy 123915139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_process_hash[hash], pp, pp_next); 124049874f6eSJoseph Koshy return (pp); 124115139246SJoseph Koshy } 124215139246SJoseph Koshy 124315139246SJoseph Koshy /* 124415139246SJoseph Koshy * Associate an image and a process. 124515139246SJoseph Koshy */ 124615139246SJoseph Koshy 124715139246SJoseph Koshy static void 124849874f6eSJoseph Koshy pmcstat_process_exec(struct pmcstat_process *pp, 12490b86b1bbSFabien Thomas pmcstat_interned_string path, uintfptr_t entryaddr) 125015139246SJoseph Koshy { 125115139246SJoseph Koshy struct pmcstat_image *image; 125215139246SJoseph Koshy 125349874f6eSJoseph Koshy if ((image = pmcstat_image_from_path(path, 0)) == NULL) { 125449874f6eSJoseph Koshy pmcstat_stats.ps_exec_errors++; 125515139246SJoseph Koshy return; 125649874f6eSJoseph Koshy } 125715139246SJoseph Koshy 125815139246SJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 12590b86b1bbSFabien Thomas pmcstat_image_determine_type(image); 126015139246SJoseph Koshy 126149874f6eSJoseph Koshy assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); 126249874f6eSJoseph Koshy 126349874f6eSJoseph Koshy switch (image->pi_type) { 126449874f6eSJoseph Koshy case PMCSTAT_IMAGE_ELF32: 126549874f6eSJoseph Koshy case PMCSTAT_IMAGE_ELF64: 126649874f6eSJoseph Koshy pmcstat_stats.ps_exec_elf++; 12670b86b1bbSFabien Thomas pmcstat_process_elf_exec(pp, image, entryaddr); 126815139246SJoseph Koshy break; 126915139246SJoseph Koshy 127015139246SJoseph Koshy case PMCSTAT_IMAGE_AOUT: 127149874f6eSJoseph Koshy pmcstat_stats.ps_exec_aout++; 12720b86b1bbSFabien Thomas pmcstat_process_aout_exec(pp, image, entryaddr); 127349874f6eSJoseph Koshy break; 127449874f6eSJoseph Koshy 127549874f6eSJoseph Koshy case PMCSTAT_IMAGE_INDETERMINABLE: 127649874f6eSJoseph Koshy pmcstat_stats.ps_exec_indeterminable++; 127715139246SJoseph Koshy break; 127815139246SJoseph Koshy 127915139246SJoseph Koshy default: 128060f918efSJoseph Koshy err(EX_SOFTWARE, "ERROR: Unsupported executable type for " 128149874f6eSJoseph Koshy "\"%s\"", pmcstat_string_unintern(path)); 128215139246SJoseph Koshy } 128315139246SJoseph Koshy } 128415139246SJoseph Koshy 128515139246SJoseph Koshy 128660f918efSJoseph Koshy /* 128760f918efSJoseph Koshy * Find the map entry associated with process 'p' at PC value 'pc'. 128860f918efSJoseph Koshy */ 128960f918efSJoseph Koshy 12900b86b1bbSFabien Thomas struct pmcstat_pcmap * 129160f918efSJoseph Koshy pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc) 129260f918efSJoseph Koshy { 129360f918efSJoseph Koshy struct pmcstat_pcmap *ppm; 129460f918efSJoseph Koshy 129549874f6eSJoseph Koshy TAILQ_FOREACH(ppm, &p->pp_map, ppm_next) { 129660f918efSJoseph Koshy if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc) 129749874f6eSJoseph Koshy return (ppm); 129849874f6eSJoseph Koshy if (pc < ppm->ppm_lowpc) 129949874f6eSJoseph Koshy return (NULL); 130049874f6eSJoseph Koshy } 130160f918efSJoseph Koshy 130249874f6eSJoseph Koshy return (NULL); 130360f918efSJoseph Koshy } 130460f918efSJoseph Koshy 1305b6010f9eSJoseph Koshy /* 1306b6010f9eSJoseph Koshy * Convert a hwpmc(4) log to profile information. A system-wide 1307b6010f9eSJoseph Koshy * callgraph is generated if FLAG_DO_CALLGRAPHS is set. gmon.out 1308b6010f9eSJoseph Koshy * files usable by gprof(1) are created if FLAG_DO_GPROF is set. 1309b6010f9eSJoseph Koshy */ 1310b6010f9eSJoseph Koshy static int 13110b86b1bbSFabien Thomas pmcstat_analyze_log(void) 1312b6010f9eSJoseph Koshy { 1313b6010f9eSJoseph Koshy uint32_t cpu, cpuflags; 13140b86b1bbSFabien Thomas uintfptr_t pc; 131549874f6eSJoseph Koshy pid_t pid; 131649874f6eSJoseph Koshy struct pmcstat_image *image; 131715139246SJoseph Koshy struct pmcstat_process *pp, *ppnew; 131815139246SJoseph Koshy struct pmcstat_pcmap *ppm, *ppmtmp; 131915139246SJoseph Koshy struct pmclog_ev ev; 13200b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pmcr; 132149874f6eSJoseph Koshy pmcstat_interned_string image_path; 132215139246SJoseph Koshy 13230b86b1bbSFabien Thomas assert(args.pa_flags & FLAG_DO_ANALYSIS); 1324b6010f9eSJoseph Koshy 1325b6010f9eSJoseph Koshy if (elf_version(EV_CURRENT) == EV_NONE) 1326b6010f9eSJoseph Koshy err(EX_UNAVAILABLE, "Elf library intialization failed"); 1327b6010f9eSJoseph Koshy 13280b86b1bbSFabien Thomas while (pmclog_read(args.pa_logparser, &ev) == 0) { 132915139246SJoseph Koshy assert(ev.pl_state == PMCLOG_OK); 133015139246SJoseph Koshy 133115139246SJoseph Koshy switch (ev.pl_type) { 133249874f6eSJoseph Koshy case PMCLOG_TYPE_INITIALIZE: 133349874f6eSJoseph Koshy if ((ev.pl_u.pl_i.pl_version & 0xFF000000) != 13340b86b1bbSFabien Thomas PMC_VERSION_MAJOR << 24 && args.pa_verbosity > 0) 133549874f6eSJoseph Koshy warnx("WARNING: Log version 0x%x does not " 133649874f6eSJoseph Koshy "match compiled version 0x%x.", 133749874f6eSJoseph Koshy ev.pl_u.pl_i.pl_version, 133849874f6eSJoseph Koshy PMC_VERSION_MAJOR); 133949874f6eSJoseph Koshy break; 1340b6010f9eSJoseph Koshy 134149874f6eSJoseph Koshy case PMCLOG_TYPE_MAP_IN: 134215139246SJoseph Koshy /* 134315139246SJoseph Koshy * Introduce an address range mapping for a 134449874f6eSJoseph Koshy * userland process or the kernel (pid == -1). 134549874f6eSJoseph Koshy * 134649874f6eSJoseph Koshy * We always allocate a process descriptor so 134749874f6eSJoseph Koshy * that subsequent samples seen for this 134849874f6eSJoseph Koshy * address range are mapped to the current 134949874f6eSJoseph Koshy * object being mapped in. 135015139246SJoseph Koshy */ 135149874f6eSJoseph Koshy pid = ev.pl_u.pl_mi.pl_pid; 135249874f6eSJoseph Koshy if (pid == -1) 135349874f6eSJoseph Koshy pp = pmcstat_kernproc; 135449874f6eSJoseph Koshy else 135549874f6eSJoseph Koshy pp = pmcstat_process_lookup(pid, 135649874f6eSJoseph Koshy PMCSTAT_ALLOCATE); 135749874f6eSJoseph Koshy 135849874f6eSJoseph Koshy assert(pp != NULL); 135949874f6eSJoseph Koshy 136049874f6eSJoseph Koshy image_path = pmcstat_string_intern(ev.pl_u.pl_mi. 136149874f6eSJoseph Koshy pl_pathname); 136249874f6eSJoseph Koshy image = pmcstat_image_from_path(image_path, pid == -1); 136349874f6eSJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 13640b86b1bbSFabien Thomas pmcstat_image_determine_type(image); 136549874f6eSJoseph Koshy if (image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE) 136649874f6eSJoseph Koshy pmcstat_image_link(pp, image, 136749874f6eSJoseph Koshy ev.pl_u.pl_mi.pl_start); 136849874f6eSJoseph Koshy break; 136949874f6eSJoseph Koshy 137049874f6eSJoseph Koshy case PMCLOG_TYPE_MAP_OUT: 137149874f6eSJoseph Koshy /* 137249874f6eSJoseph Koshy * Remove an address map. 137349874f6eSJoseph Koshy */ 137449874f6eSJoseph Koshy pid = ev.pl_u.pl_mo.pl_pid; 137549874f6eSJoseph Koshy if (pid == -1) 137649874f6eSJoseph Koshy pp = pmcstat_kernproc; 137749874f6eSJoseph Koshy else 137849874f6eSJoseph Koshy pp = pmcstat_process_lookup(pid, 0); 137949874f6eSJoseph Koshy 138049874f6eSJoseph Koshy if (pp == NULL) /* unknown process */ 138149874f6eSJoseph Koshy break; 138249874f6eSJoseph Koshy 138349874f6eSJoseph Koshy pmcstat_image_unmap(pp, ev.pl_u.pl_mo.pl_start, 138449874f6eSJoseph Koshy ev.pl_u.pl_mo.pl_end); 138515139246SJoseph Koshy break; 138615139246SJoseph Koshy 138715139246SJoseph Koshy case PMCLOG_TYPE_PCSAMPLE: 1388b6010f9eSJoseph Koshy /* 1389b6010f9eSJoseph Koshy * Note: the `PCSAMPLE' log entry is not 1390b6010f9eSJoseph Koshy * generated by hpwmc(4) after version 2. 1391b6010f9eSJoseph Koshy */ 139215139246SJoseph Koshy 139315139246SJoseph Koshy /* 139415139246SJoseph Koshy * We bring in the gmon file for the image 139515139246SJoseph Koshy * currently associated with the PMC & pid 139615139246SJoseph Koshy * pair and increment the appropriate entry 139715139246SJoseph Koshy * bin inside this. 139815139246SJoseph Koshy */ 139949874f6eSJoseph Koshy pmcstat_stats.ps_samples_total++; 140049874f6eSJoseph Koshy 140115139246SJoseph Koshy pc = ev.pl_u.pl_s.pl_pc; 140249874f6eSJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_s.pl_pid, 140349874f6eSJoseph Koshy PMCSTAT_ALLOCATE); 140415139246SJoseph Koshy 14050b86b1bbSFabien Thomas /* Get PMC record. */ 14060b86b1bbSFabien Thomas pmcr = pmcstat_lookup_pmcid(ev.pl_u.pl_s.pl_pmcid); 14070b86b1bbSFabien Thomas assert(pmcr != NULL); 1408c86819ecSFabien Thomas pmcr->pr_samples++; 140915139246SJoseph Koshy 14100b86b1bbSFabien Thomas /* 14110b86b1bbSFabien Thomas * Call the plugins processing 14120b86b1bbSFabien Thomas * TODO: move pmcstat_process_find_map inside plugins 14130b86b1bbSFabien Thomas */ 14140b86b1bbSFabien Thomas 14150b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_process != NULL) 14160b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_process( 14170b86b1bbSFabien Thomas pp, pmcr, 1, &pc, 14180b86b1bbSFabien Thomas pmcstat_process_find_map(pp, pc) != NULL, 0); 14190b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_process( 14200b86b1bbSFabien Thomas pp, pmcr, 1, &pc, 14210b86b1bbSFabien Thomas pmcstat_process_find_map(pp, pc) != NULL, 0); 142215139246SJoseph Koshy break; 142315139246SJoseph Koshy 1424b6010f9eSJoseph Koshy case PMCLOG_TYPE_CALLCHAIN: 1425b6010f9eSJoseph Koshy pmcstat_stats.ps_samples_total++; 1426b6010f9eSJoseph Koshy 1427b6010f9eSJoseph Koshy cpuflags = ev.pl_u.pl_cc.pl_cpuflags; 1428b6010f9eSJoseph Koshy cpu = PMC_CALLCHAIN_CPUFLAGS_TO_CPU(cpuflags); 1429b6010f9eSJoseph Koshy 1430b6010f9eSJoseph Koshy /* Filter on the CPU id. */ 14310b86b1bbSFabien Thomas if ((args.pa_cpumask & (1 << cpu)) == 0) { 1432b6010f9eSJoseph Koshy pmcstat_stats.ps_samples_skipped++; 1433b6010f9eSJoseph Koshy break; 1434b6010f9eSJoseph Koshy } 1435b6010f9eSJoseph Koshy 1436b6010f9eSJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_cc.pl_pid, 1437b6010f9eSJoseph Koshy PMCSTAT_ALLOCATE); 1438b6010f9eSJoseph Koshy 14390b86b1bbSFabien Thomas /* Get PMC record. */ 14400b86b1bbSFabien Thomas pmcr = pmcstat_lookup_pmcid(ev.pl_u.pl_cc.pl_pmcid); 14410b86b1bbSFabien Thomas assert(pmcr != NULL); 1442c86819ecSFabien Thomas pmcr->pr_samples++; 1443b6010f9eSJoseph Koshy 14440b86b1bbSFabien Thomas /* 14450b86b1bbSFabien Thomas * Call the plugins processing 14460b86b1bbSFabien Thomas */ 1447b6010f9eSJoseph Koshy 14480b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_process != NULL) 14490b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_process( 14500b86b1bbSFabien Thomas pp, pmcr, 14510b86b1bbSFabien Thomas ev.pl_u.pl_cc.pl_npc, 14520b86b1bbSFabien Thomas ev.pl_u.pl_cc.pl_pc, 14530b86b1bbSFabien Thomas PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), 14540b86b1bbSFabien Thomas cpu); 14550b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_process( 14560b86b1bbSFabien Thomas pp, pmcr, 14570b86b1bbSFabien Thomas ev.pl_u.pl_cc.pl_npc, 14580b86b1bbSFabien Thomas ev.pl_u.pl_cc.pl_pc, 14590b86b1bbSFabien Thomas PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), 14600b86b1bbSFabien Thomas cpu); 1461b6010f9eSJoseph Koshy break; 1462b6010f9eSJoseph Koshy 146315139246SJoseph Koshy case PMCLOG_TYPE_PMCALLOCATE: 146415139246SJoseph Koshy /* 146515139246SJoseph Koshy * Record the association pmc id between this 146615139246SJoseph Koshy * PMC and its name. 146715139246SJoseph Koshy */ 146815139246SJoseph Koshy pmcstat_pmcid_add(ev.pl_u.pl_a.pl_pmcid, 14690b86b1bbSFabien Thomas pmcstat_string_intern(ev.pl_u.pl_a.pl_evname)); 147015139246SJoseph Koshy break; 147115139246SJoseph Koshy 147215139246SJoseph Koshy case PMCLOG_TYPE_PROCEXEC: 147315139246SJoseph Koshy 147415139246SJoseph Koshy /* 147515139246SJoseph Koshy * Change the executable image associated with 147615139246SJoseph Koshy * a process. 147715139246SJoseph Koshy */ 147849874f6eSJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_x.pl_pid, 147949874f6eSJoseph Koshy PMCSTAT_ALLOCATE); 148015139246SJoseph Koshy 148115139246SJoseph Koshy /* delete the current process map */ 148215139246SJoseph Koshy TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) { 148315139246SJoseph Koshy TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 148415139246SJoseph Koshy free(ppm); 148515139246SJoseph Koshy } 148615139246SJoseph Koshy 148749874f6eSJoseph Koshy /* associate this process image */ 148815139246SJoseph Koshy image_path = pmcstat_string_intern( 148915139246SJoseph Koshy ev.pl_u.pl_x.pl_pathname); 149049874f6eSJoseph Koshy assert(image_path != NULL); 149160f918efSJoseph Koshy pmcstat_process_exec(pp, image_path, 14920b86b1bbSFabien Thomas ev.pl_u.pl_x.pl_entryaddr); 149315139246SJoseph Koshy break; 149415139246SJoseph Koshy 149515139246SJoseph Koshy case PMCLOG_TYPE_PROCEXIT: 149615139246SJoseph Koshy 149715139246SJoseph Koshy /* 149815139246SJoseph Koshy * Due to the way the log is generated, the 149915139246SJoseph Koshy * last few samples corresponding to a process 150015139246SJoseph Koshy * may appear in the log after the process 150115139246SJoseph Koshy * exit event is recorded. Thus we keep the 150215139246SJoseph Koshy * process' descriptor and associated data 150315139246SJoseph Koshy * structures around, but mark the process as 150415139246SJoseph Koshy * having exited. 150515139246SJoseph Koshy */ 150615139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_e.pl_pid, 0); 150715139246SJoseph Koshy if (pp == NULL) 150815139246SJoseph Koshy break; 150949874f6eSJoseph Koshy pp->pp_isactive = 0; /* mark as a zombie */ 151015139246SJoseph Koshy break; 151115139246SJoseph Koshy 151215139246SJoseph Koshy case PMCLOG_TYPE_SYSEXIT: 151315139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_se.pl_pid, 0); 151415139246SJoseph Koshy if (pp == NULL) 151515139246SJoseph Koshy break; 151615139246SJoseph Koshy pp->pp_isactive = 0; /* make a zombie */ 151715139246SJoseph Koshy break; 151815139246SJoseph Koshy 151915139246SJoseph Koshy case PMCLOG_TYPE_PROCFORK: 152015139246SJoseph Koshy 152115139246SJoseph Koshy /* 152249874f6eSJoseph Koshy * Allocate a process descriptor for the new 152349874f6eSJoseph Koshy * (child) process. 152449874f6eSJoseph Koshy */ 152549874f6eSJoseph Koshy ppnew = 152649874f6eSJoseph Koshy pmcstat_process_lookup(ev.pl_u.pl_f.pl_newpid, 152749874f6eSJoseph Koshy PMCSTAT_ALLOCATE); 152849874f6eSJoseph Koshy 152949874f6eSJoseph Koshy /* 153049874f6eSJoseph Koshy * If we had been tracking the parent, clone 153149874f6eSJoseph Koshy * its address maps. 153215139246SJoseph Koshy */ 153315139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_f.pl_oldpid, 0); 153415139246SJoseph Koshy if (pp == NULL) 153515139246SJoseph Koshy break; 153615139246SJoseph Koshy TAILQ_FOREACH(ppm, &pp->pp_map, ppm_next) 153715139246SJoseph Koshy pmcstat_image_link(ppnew, ppm->ppm_image, 153849874f6eSJoseph Koshy ppm->ppm_lowpc); 153915139246SJoseph Koshy break; 154015139246SJoseph Koshy 154115139246SJoseph Koshy default: /* other types of entries are not relevant */ 154215139246SJoseph Koshy break; 154315139246SJoseph Koshy } 154415139246SJoseph Koshy } 154515139246SJoseph Koshy 154615139246SJoseph Koshy if (ev.pl_state == PMCLOG_EOF) 154749874f6eSJoseph Koshy return (PMCSTAT_FINISHED); 154815139246SJoseph Koshy else if (ev.pl_state == PMCLOG_REQUIRE_DATA) 154949874f6eSJoseph Koshy return (PMCSTAT_RUNNING); 155015139246SJoseph Koshy 155115139246SJoseph Koshy err(EX_DATAERR, "ERROR: event parsing failed (record %jd, " 155215139246SJoseph Koshy "offset 0x%jx)", (uintmax_t) ev.pl_count + 1, ev.pl_offset); 155315139246SJoseph Koshy } 155415139246SJoseph Koshy 155515139246SJoseph Koshy /* 155615139246SJoseph Koshy * Print log entries as text. 155715139246SJoseph Koshy */ 155815139246SJoseph Koshy 155949874f6eSJoseph Koshy static int 15600b86b1bbSFabien Thomas pmcstat_print_log(void) 156115139246SJoseph Koshy { 156215139246SJoseph Koshy struct pmclog_ev ev; 1563b6010f9eSJoseph Koshy uint32_t npc; 156415139246SJoseph Koshy 15650b86b1bbSFabien Thomas while (pmclog_read(args.pa_logparser, &ev) == 0) { 156615139246SJoseph Koshy assert(ev.pl_state == PMCLOG_OK); 156715139246SJoseph Koshy switch (ev.pl_type) { 1568b6010f9eSJoseph Koshy case PMCLOG_TYPE_CALLCHAIN: 15690b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("callchain", 1570b6010f9eSJoseph Koshy "%d 0x%x %d %d %c", ev.pl_u.pl_cc.pl_pid, 1571b6010f9eSJoseph Koshy ev.pl_u.pl_cc.pl_pmcid, 1572b6010f9eSJoseph Koshy PMC_CALLCHAIN_CPUFLAGS_TO_CPU(ev.pl_u.pl_cc. \ 1573b6010f9eSJoseph Koshy pl_cpuflags), ev.pl_u.pl_cc.pl_npc, 1574b6010f9eSJoseph Koshy PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(ev.pl_u.pl_cc.\ 1575b6010f9eSJoseph Koshy pl_cpuflags) ? 'u' : 's'); 1576b6010f9eSJoseph Koshy for (npc = 0; npc < ev.pl_u.pl_cc.pl_npc; npc++) 15770b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("...", "%p", 1578b6010f9eSJoseph Koshy (void *) ev.pl_u.pl_cc.pl_pc[npc]); 1579b6010f9eSJoseph Koshy break; 158015139246SJoseph Koshy case PMCLOG_TYPE_CLOSELOG: 15810b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("closelog",); 158215139246SJoseph Koshy break; 158315139246SJoseph Koshy case PMCLOG_TYPE_DROPNOTIFY: 15840b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("drop",); 158515139246SJoseph Koshy break; 158615139246SJoseph Koshy case PMCLOG_TYPE_INITIALIZE: 15870b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("initlog","0x%x \"%s\"", 158815139246SJoseph Koshy ev.pl_u.pl_i.pl_version, 158915139246SJoseph Koshy pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch)); 15903ab065b6SJoseph Koshy if ((ev.pl_u.pl_i.pl_version & 0xFF000000) != 15910b86b1bbSFabien Thomas PMC_VERSION_MAJOR << 24 && args.pa_verbosity > 0) 15923ab065b6SJoseph Koshy warnx("WARNING: Log version 0x%x != expected " 15933ab065b6SJoseph Koshy "version 0x%x.", ev.pl_u.pl_i.pl_version, 15943ab065b6SJoseph Koshy PMC_VERSION); 159515139246SJoseph Koshy break; 159649874f6eSJoseph Koshy case PMCLOG_TYPE_MAP_IN: 15970b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("map-in","%d %p \"%s\"", 159849874f6eSJoseph Koshy ev.pl_u.pl_mi.pl_pid, 159949874f6eSJoseph Koshy (void *) ev.pl_u.pl_mi.pl_start, 160049874f6eSJoseph Koshy ev.pl_u.pl_mi.pl_pathname); 160149874f6eSJoseph Koshy break; 160249874f6eSJoseph Koshy case PMCLOG_TYPE_MAP_OUT: 16030b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("map-out","%d %p %p", 160449874f6eSJoseph Koshy ev.pl_u.pl_mo.pl_pid, 160549874f6eSJoseph Koshy (void *) ev.pl_u.pl_mo.pl_start, 160649874f6eSJoseph Koshy (void *) ev.pl_u.pl_mo.pl_end); 160715139246SJoseph Koshy break; 160815139246SJoseph Koshy case PMCLOG_TYPE_PCSAMPLE: 16090b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("sample","0x%x %d %p %c", 161015139246SJoseph Koshy ev.pl_u.pl_s.pl_pmcid, 161115139246SJoseph Koshy ev.pl_u.pl_s.pl_pid, 161215139246SJoseph Koshy (void *) ev.pl_u.pl_s.pl_pc, 161315139246SJoseph Koshy ev.pl_u.pl_s.pl_usermode ? 'u' : 's'); 161415139246SJoseph Koshy break; 161515139246SJoseph Koshy case PMCLOG_TYPE_PMCALLOCATE: 16160b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("allocate","0x%x \"%s\" 0x%x", 161715139246SJoseph Koshy ev.pl_u.pl_a.pl_pmcid, 161815139246SJoseph Koshy ev.pl_u.pl_a.pl_evname, 161915139246SJoseph Koshy ev.pl_u.pl_a.pl_flags); 162015139246SJoseph Koshy break; 162115139246SJoseph Koshy case PMCLOG_TYPE_PMCATTACH: 16220b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("attach","0x%x %d \"%s\"", 162315139246SJoseph Koshy ev.pl_u.pl_t.pl_pmcid, 162415139246SJoseph Koshy ev.pl_u.pl_t.pl_pid, 162515139246SJoseph Koshy ev.pl_u.pl_t.pl_pathname); 162615139246SJoseph Koshy break; 162715139246SJoseph Koshy case PMCLOG_TYPE_PMCDETACH: 16280b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("detach","0x%x %d", 162915139246SJoseph Koshy ev.pl_u.pl_d.pl_pmcid, 163015139246SJoseph Koshy ev.pl_u.pl_d.pl_pid); 163115139246SJoseph Koshy break; 163215139246SJoseph Koshy case PMCLOG_TYPE_PROCCSW: 16330b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("cswval","0x%x %d %jd", 163415139246SJoseph Koshy ev.pl_u.pl_c.pl_pmcid, 163515139246SJoseph Koshy ev.pl_u.pl_c.pl_pid, 163615139246SJoseph Koshy ev.pl_u.pl_c.pl_value); 163715139246SJoseph Koshy break; 163815139246SJoseph Koshy case PMCLOG_TYPE_PROCEXEC: 16390b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("exec","0x%x %d %p \"%s\"", 164015139246SJoseph Koshy ev.pl_u.pl_x.pl_pmcid, 164115139246SJoseph Koshy ev.pl_u.pl_x.pl_pid, 164215139246SJoseph Koshy (void *) ev.pl_u.pl_x.pl_entryaddr, 164315139246SJoseph Koshy ev.pl_u.pl_x.pl_pathname); 164415139246SJoseph Koshy break; 164515139246SJoseph Koshy case PMCLOG_TYPE_PROCEXIT: 16460b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("exitval","0x%x %d %jd", 164715139246SJoseph Koshy ev.pl_u.pl_e.pl_pmcid, 164815139246SJoseph Koshy ev.pl_u.pl_e.pl_pid, 164915139246SJoseph Koshy ev.pl_u.pl_e.pl_value); 165015139246SJoseph Koshy break; 165115139246SJoseph Koshy case PMCLOG_TYPE_PROCFORK: 16520b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("fork","%d %d", 165315139246SJoseph Koshy ev.pl_u.pl_f.pl_oldpid, 165415139246SJoseph Koshy ev.pl_u.pl_f.pl_newpid); 165515139246SJoseph Koshy break; 165615139246SJoseph Koshy case PMCLOG_TYPE_USERDATA: 16570b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("userdata","0x%x", 165815139246SJoseph Koshy ev.pl_u.pl_u.pl_userdata); 165915139246SJoseph Koshy break; 166015139246SJoseph Koshy case PMCLOG_TYPE_SYSEXIT: 16610b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("exit","%d", 166215139246SJoseph Koshy ev.pl_u.pl_se.pl_pid); 166315139246SJoseph Koshy break; 166415139246SJoseph Koshy default: 16650b86b1bbSFabien Thomas fprintf(args.pa_printfile, "unknown event (type %d).\n", 166615139246SJoseph Koshy ev.pl_type); 166715139246SJoseph Koshy } 166815139246SJoseph Koshy } 166915139246SJoseph Koshy 167015139246SJoseph Koshy if (ev.pl_state == PMCLOG_EOF) 167149874f6eSJoseph Koshy return (PMCSTAT_FINISHED); 167215139246SJoseph Koshy else if (ev.pl_state == PMCLOG_REQUIRE_DATA) 167349874f6eSJoseph Koshy return (PMCSTAT_RUNNING); 167415139246SJoseph Koshy 16753ab065b6SJoseph Koshy errx(EX_DATAERR, "ERROR: event parsing failed " 16763ab065b6SJoseph Koshy "(record %jd, offset 0x%jx).", 167715139246SJoseph Koshy (uintmax_t) ev.pl_count + 1, ev.pl_offset); 167815139246SJoseph Koshy /*NOTREACHED*/ 167915139246SJoseph Koshy } 168015139246SJoseph Koshy 168115139246SJoseph Koshy /* 168249874f6eSJoseph Koshy * Public Interfaces. 168349874f6eSJoseph Koshy */ 168449874f6eSJoseph Koshy 168549874f6eSJoseph Koshy /* 168649874f6eSJoseph Koshy * Close a logfile, after first flushing all in-module queued data. 168749874f6eSJoseph Koshy */ 168849874f6eSJoseph Koshy 168949874f6eSJoseph Koshy int 16900b86b1bbSFabien Thomas pmcstat_close_log(void) 169149874f6eSJoseph Koshy { 1692b44906e5SFabien Thomas if (pmc_flush_logfile() < 0) 169349874f6eSJoseph Koshy err(EX_OSERR, "ERROR: logging failed"); 16940b86b1bbSFabien Thomas return (args.pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING : 169549874f6eSJoseph Koshy PMCSTAT_FINISHED); 169649874f6eSJoseph Koshy } 169749874f6eSJoseph Koshy 169849874f6eSJoseph Koshy 169949874f6eSJoseph Koshy 170049874f6eSJoseph Koshy /* 170149874f6eSJoseph Koshy * Open a log file, for reading or writing. 170249874f6eSJoseph Koshy * 170349874f6eSJoseph Koshy * The function returns the fd of a successfully opened log or -1 in 170449874f6eSJoseph Koshy * case of failure. 170549874f6eSJoseph Koshy */ 170649874f6eSJoseph Koshy 170749874f6eSJoseph Koshy int 170849874f6eSJoseph Koshy pmcstat_open_log(const char *path, int mode) 170949874f6eSJoseph Koshy { 1710302cbb90SJoseph Koshy int error, fd; 1711302cbb90SJoseph Koshy size_t hlen; 1712302cbb90SJoseph Koshy const char *p, *errstr; 1713302cbb90SJoseph Koshy struct addrinfo hints, *res, *res0; 1714302cbb90SJoseph Koshy char hostname[MAXHOSTNAMELEN]; 1715302cbb90SJoseph Koshy 1716302cbb90SJoseph Koshy errstr = NULL; 1717302cbb90SJoseph Koshy fd = -1; 171849874f6eSJoseph Koshy 171949874f6eSJoseph Koshy /* 172049874f6eSJoseph Koshy * If 'path' is "-" then open one of stdin or stdout depending 1721302cbb90SJoseph Koshy * on the value of 'mode'. 1722302cbb90SJoseph Koshy * 1723302cbb90SJoseph Koshy * If 'path' contains a ':' and does not start with a '/' or '.', 1724302cbb90SJoseph Koshy * and is being opened for writing, treat it as a "host:port" 1725302cbb90SJoseph Koshy * specification and open a network socket. 1726302cbb90SJoseph Koshy * 1727302cbb90SJoseph Koshy * Otherwise, treat 'path' as a file name and open that. 172849874f6eSJoseph Koshy */ 172949874f6eSJoseph Koshy if (path[0] == '-' && path[1] == '\0') 173049874f6eSJoseph Koshy fd = (mode == PMCSTAT_OPEN_FOR_READ) ? 0 : 1; 1731302cbb90SJoseph Koshy else if (mode == PMCSTAT_OPEN_FOR_WRITE && path[0] != '/' && 1732302cbb90SJoseph Koshy path[0] != '.' && strchr(path, ':') != NULL) { 1733302cbb90SJoseph Koshy 1734302cbb90SJoseph Koshy p = strrchr(path, ':'); 1735302cbb90SJoseph Koshy hlen = p - path; 1736302cbb90SJoseph Koshy if (p == path || hlen >= sizeof(hostname)) { 1737302cbb90SJoseph Koshy errstr = strerror(EINVAL); 1738302cbb90SJoseph Koshy goto done; 1739302cbb90SJoseph Koshy } 1740302cbb90SJoseph Koshy 1741302cbb90SJoseph Koshy assert(hlen < sizeof(hostname)); 1742302cbb90SJoseph Koshy (void) strncpy(hostname, path, hlen); 1743302cbb90SJoseph Koshy hostname[hlen] = '\0'; 1744302cbb90SJoseph Koshy 1745302cbb90SJoseph Koshy (void) memset(&hints, 0, sizeof(hints)); 1746302cbb90SJoseph Koshy hints.ai_family = AF_UNSPEC; 1747302cbb90SJoseph Koshy hints.ai_socktype = SOCK_STREAM; 1748302cbb90SJoseph Koshy if ((error = getaddrinfo(hostname, p+1, &hints, &res0)) != 0) { 1749302cbb90SJoseph Koshy errstr = gai_strerror(error); 1750302cbb90SJoseph Koshy goto done; 1751302cbb90SJoseph Koshy } 1752302cbb90SJoseph Koshy 1753302cbb90SJoseph Koshy fd = -1; 1754302cbb90SJoseph Koshy for (res = res0; res; res = res->ai_next) { 1755302cbb90SJoseph Koshy if ((fd = socket(res->ai_family, res->ai_socktype, 1756302cbb90SJoseph Koshy res->ai_protocol)) < 0) { 1757302cbb90SJoseph Koshy errstr = strerror(errno); 1758302cbb90SJoseph Koshy continue; 1759302cbb90SJoseph Koshy } 1760302cbb90SJoseph Koshy if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { 1761302cbb90SJoseph Koshy errstr = strerror(errno); 1762302cbb90SJoseph Koshy (void) close(fd); 1763302cbb90SJoseph Koshy fd = -1; 1764302cbb90SJoseph Koshy continue; 1765302cbb90SJoseph Koshy } 1766302cbb90SJoseph Koshy errstr = NULL; 1767302cbb90SJoseph Koshy break; 1768302cbb90SJoseph Koshy } 1769302cbb90SJoseph Koshy freeaddrinfo(res0); 1770302cbb90SJoseph Koshy 1771302cbb90SJoseph Koshy } else if ((fd = open(path, mode == PMCSTAT_OPEN_FOR_READ ? 177249874f6eSJoseph Koshy O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC), 1773302cbb90SJoseph Koshy S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) 1774302cbb90SJoseph Koshy errstr = strerror(errno); 1775302cbb90SJoseph Koshy 1776302cbb90SJoseph Koshy done: 1777302cbb90SJoseph Koshy if (errstr) 1778302cbb90SJoseph Koshy errx(EX_OSERR, "ERROR: Cannot open \"%s\" for %s: %s.", path, 1779302cbb90SJoseph Koshy (mode == PMCSTAT_OPEN_FOR_READ ? "reading" : "writing"), 1780302cbb90SJoseph Koshy errstr); 178149874f6eSJoseph Koshy 178249874f6eSJoseph Koshy return (fd); 178349874f6eSJoseph Koshy } 178449874f6eSJoseph Koshy 178549874f6eSJoseph Koshy /* 178615139246SJoseph Koshy * Process a log file in offline analysis mode. 178715139246SJoseph Koshy */ 178815139246SJoseph Koshy 1789dc1d9d2eSJoseph Koshy int 17900b86b1bbSFabien Thomas pmcstat_process_log(void) 179115139246SJoseph Koshy { 179215139246SJoseph Koshy 179315139246SJoseph Koshy /* 1794b6010f9eSJoseph Koshy * If analysis has not been asked for, just print the log to 1795b6010f9eSJoseph Koshy * the current output file. 179615139246SJoseph Koshy */ 17970b86b1bbSFabien Thomas if (args.pa_flags & FLAG_DO_PRINT) 17980b86b1bbSFabien Thomas return (pmcstat_print_log()); 179915139246SJoseph Koshy else 18000b86b1bbSFabien Thomas return (pmcstat_analyze_log()); 18010b86b1bbSFabien Thomas } 18020b86b1bbSFabien Thomas 18030b86b1bbSFabien Thomas /* 18040b86b1bbSFabien Thomas * Refresh top display. 18050b86b1bbSFabien Thomas */ 18060b86b1bbSFabien Thomas 18070b86b1bbSFabien Thomas static void 18080b86b1bbSFabien Thomas pmcstat_refresh_top(void) 18090b86b1bbSFabien Thomas { 1810c86819ecSFabien Thomas int v_attrs; 1811c86819ecSFabien Thomas float v; 18120b86b1bbSFabien Thomas char pmcname[40]; 1813c86819ecSFabien Thomas struct pmcstat_pmcrecord *pmcpr; 18140b86b1bbSFabien Thomas 18150b86b1bbSFabien Thomas /* If in pause mode do not refresh display. */ 18160b86b1bbSFabien Thomas if (pmcstat_pause) 18170b86b1bbSFabien Thomas return; 18180b86b1bbSFabien Thomas 18199ba84a88SFabien Thomas /* Wait until PMC pop in the log. */ 1820c86819ecSFabien Thomas pmcpr = pmcstat_pmcindex_to_pmcr(pmcstat_pmcinfilter); 1821c86819ecSFabien Thomas if (pmcpr == NULL) 18229ba84a88SFabien Thomas return; 18239ba84a88SFabien Thomas 18240b86b1bbSFabien Thomas /* Format PMC name. */ 18250b86b1bbSFabien Thomas if (pmcstat_mergepmc) 1826c86819ecSFabien Thomas snprintf(pmcname, sizeof(pmcname), "[%s]", 1827c86819ecSFabien Thomas pmcstat_string_unintern(pmcpr->pr_pmcname)); 18280b86b1bbSFabien Thomas else 18290b86b1bbSFabien Thomas snprintf(pmcname, sizeof(pmcname), "%s.%d", 1830c86819ecSFabien Thomas pmcstat_string_unintern(pmcpr->pr_pmcname), 1831c86819ecSFabien Thomas pmcstat_pmcinfilter); 1832c86819ecSFabien Thomas 1833c86819ecSFabien Thomas /* Format samples count. */ 1834c86819ecSFabien Thomas if (pmcstat_stats.ps_samples_total > 0) 1835c86819ecSFabien Thomas v = (pmcpr->pr_samples * 100.0) / 1836c86819ecSFabien Thomas pmcstat_stats.ps_samples_total; 1837c86819ecSFabien Thomas else 1838c86819ecSFabien Thomas v = 0.; 1839c86819ecSFabien Thomas v_attrs = PMCSTAT_ATTRPERCENT(v); 18400b86b1bbSFabien Thomas 18410b86b1bbSFabien Thomas PMCSTAT_PRINTBEGIN(); 1842c86819ecSFabien Thomas PMCSTAT_PRINTW("PMC: %s Samples: %u ", 18430b86b1bbSFabien Thomas pmcname, 1844c86819ecSFabien Thomas pmcpr->pr_samples); 1845c86819ecSFabien Thomas PMCSTAT_ATTRON(v_attrs); 1846c86819ecSFabien Thomas PMCSTAT_PRINTW("(%.1f%%) ", v); 1847c86819ecSFabien Thomas PMCSTAT_ATTROFF(v_attrs); 1848c86819ecSFabien Thomas PMCSTAT_PRINTW(", %u unresolved\n\n", 1849c86819ecSFabien Thomas pmcpr->pr_dubious_frames); 18500b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_topdisplay != NULL) 18510b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_topdisplay(); 18520b86b1bbSFabien Thomas PMCSTAT_PRINTEND(); 18530b86b1bbSFabien Thomas } 18540b86b1bbSFabien Thomas 18550b86b1bbSFabien Thomas /* 18560b86b1bbSFabien Thomas * Find the next pmc index to display. 18570b86b1bbSFabien Thomas */ 18580b86b1bbSFabien Thomas 18590b86b1bbSFabien Thomas static void 18600b86b1bbSFabien Thomas pmcstat_changefilter(void) 18610b86b1bbSFabien Thomas { 18620b86b1bbSFabien Thomas int pmcin; 18630b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pmcr; 18640b86b1bbSFabien Thomas 18650b86b1bbSFabien Thomas /* 18660b86b1bbSFabien Thomas * Find the next merge target. 18670b86b1bbSFabien Thomas */ 18680b86b1bbSFabien Thomas if (pmcstat_mergepmc) { 18690b86b1bbSFabien Thomas pmcin = pmcstat_pmcinfilter; 18700b86b1bbSFabien Thomas 18710b86b1bbSFabien Thomas do { 18720b86b1bbSFabien Thomas pmcr = pmcstat_pmcindex_to_pmcr(pmcstat_pmcinfilter); 18730b86b1bbSFabien Thomas if (pmcr == pmcr->pr_merge) 18740b86b1bbSFabien Thomas break; 18750b86b1bbSFabien Thomas 18760b86b1bbSFabien Thomas pmcstat_pmcinfilter++; 18770b86b1bbSFabien Thomas if (pmcstat_pmcinfilter >= pmcstat_npmcs) 18780b86b1bbSFabien Thomas pmcstat_pmcinfilter = 0; 18790b86b1bbSFabien Thomas 18800b86b1bbSFabien Thomas } while (pmcstat_pmcinfilter != pmcin); 18810b86b1bbSFabien Thomas } 18820b86b1bbSFabien Thomas } 18830b86b1bbSFabien Thomas 18840b86b1bbSFabien Thomas /* 18850b86b1bbSFabien Thomas * Top mode keypress. 18860b86b1bbSFabien Thomas */ 18870b86b1bbSFabien Thomas 18880b86b1bbSFabien Thomas int 18890b86b1bbSFabien Thomas pmcstat_keypress_log(void) 18900b86b1bbSFabien Thomas { 18910b86b1bbSFabien Thomas int c, ret = 0; 18920b86b1bbSFabien Thomas WINDOW *w; 18930b86b1bbSFabien Thomas 18940b86b1bbSFabien Thomas w = newwin(1, 0, 1, 0); 18950b86b1bbSFabien Thomas c = wgetch(w); 18960b86b1bbSFabien Thomas wprintw(w, "Key: %c => ", c); 18970b86b1bbSFabien Thomas switch (c) { 18980b86b1bbSFabien Thomas case 'c': 18990b86b1bbSFabien Thomas wprintw(w, "enter mode 'd' or 'a' => "); 19000b86b1bbSFabien Thomas c = wgetch(w); 19010b86b1bbSFabien Thomas if (c == 'd') { 19020b86b1bbSFabien Thomas args.pa_topmode = PMCSTAT_TOP_DELTA; 19030b86b1bbSFabien Thomas wprintw(w, "switching to delta mode"); 19040b86b1bbSFabien Thomas } else { 19050b86b1bbSFabien Thomas args.pa_topmode = PMCSTAT_TOP_ACCUM; 19060b86b1bbSFabien Thomas wprintw(w, "switching to accumulation mode"); 19070b86b1bbSFabien Thomas } 19080b86b1bbSFabien Thomas break; 19090b86b1bbSFabien Thomas case 'm': 19100b86b1bbSFabien Thomas pmcstat_mergepmc = !pmcstat_mergepmc; 19110b86b1bbSFabien Thomas /* 19120b86b1bbSFabien Thomas * Changing merge state require data reset. 19130b86b1bbSFabien Thomas */ 19140b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_shutdown != NULL) 19150b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_shutdown(NULL); 1916c86819ecSFabien Thomas pmcstat_stats_reset(); 19170b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_init != NULL) 19180b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_init(); 19190b86b1bbSFabien Thomas 19200b86b1bbSFabien Thomas /* Update filter to be on a merge target. */ 19210b86b1bbSFabien Thomas pmcstat_changefilter(); 19220b86b1bbSFabien Thomas wprintw(w, "merge PMC %s", pmcstat_mergepmc ? "on" : "off"); 19230b86b1bbSFabien Thomas break; 19240b86b1bbSFabien Thomas case 'n': 19250b86b1bbSFabien Thomas /* Close current plugin. */ 19260b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_shutdown != NULL) 19270b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_shutdown(NULL); 19280b86b1bbSFabien Thomas 19290b86b1bbSFabien Thomas /* Find next top display available. */ 19300b86b1bbSFabien Thomas do { 19310b86b1bbSFabien Thomas args.pa_plugin++; 19320b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_name == NULL) 19330b86b1bbSFabien Thomas args.pa_plugin = 0; 19340b86b1bbSFabien Thomas } while (plugins[args.pa_plugin].pl_topdisplay == NULL); 19350b86b1bbSFabien Thomas 19360b86b1bbSFabien Thomas /* Open new plugin. */ 1937c86819ecSFabien Thomas pmcstat_stats_reset(); 19380b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_init != NULL) 19390b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_init(); 19400b86b1bbSFabien Thomas wprintw(w, "switching to plugin %s", 19410b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_name); 19420b86b1bbSFabien Thomas break; 19430b86b1bbSFabien Thomas case 'p': 19440b86b1bbSFabien Thomas pmcstat_pmcinfilter++; 19450b86b1bbSFabien Thomas if (pmcstat_pmcinfilter >= pmcstat_npmcs) 19460b86b1bbSFabien Thomas pmcstat_pmcinfilter = 0; 19470b86b1bbSFabien Thomas pmcstat_changefilter(); 19480b86b1bbSFabien Thomas wprintw(w, "switching to PMC %s.%d", 19490b86b1bbSFabien Thomas pmcstat_pmcindex_to_name(pmcstat_pmcinfilter), 19500b86b1bbSFabien Thomas pmcstat_pmcinfilter); 19510b86b1bbSFabien Thomas break; 19520b86b1bbSFabien Thomas case ' ': 19530b86b1bbSFabien Thomas pmcstat_pause = !pmcstat_pause; 19540b86b1bbSFabien Thomas if (pmcstat_pause) 19550b86b1bbSFabien Thomas wprintw(w, "pause => press space again to continue"); 19560b86b1bbSFabien Thomas break; 19570b86b1bbSFabien Thomas case 'q': 19580b86b1bbSFabien Thomas wprintw(w, "exiting..."); 19590b86b1bbSFabien Thomas ret = 1; 19600b86b1bbSFabien Thomas default: 19610b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_topkeypress != NULL) 19620b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_topkeypress(c, w)) 19630b86b1bbSFabien Thomas ret = 1; 19640b86b1bbSFabien Thomas } 19650b86b1bbSFabien Thomas 19660b86b1bbSFabien Thomas wrefresh(w); 19670b86b1bbSFabien Thomas delwin(w); 19680b86b1bbSFabien Thomas return ret; 19690b86b1bbSFabien Thomas } 19700b86b1bbSFabien Thomas 19710b86b1bbSFabien Thomas 19720b86b1bbSFabien Thomas /* 19730b86b1bbSFabien Thomas * Top mode display. 19740b86b1bbSFabien Thomas */ 19750b86b1bbSFabien Thomas 19760b86b1bbSFabien Thomas void 19770b86b1bbSFabien Thomas pmcstat_display_log(void) 19780b86b1bbSFabien Thomas { 19790b86b1bbSFabien Thomas 19800b86b1bbSFabien Thomas pmcstat_refresh_top(); 19810b86b1bbSFabien Thomas 19820b86b1bbSFabien Thomas /* Reset everythings if delta mode. */ 19830b86b1bbSFabien Thomas if (args.pa_topmode == PMCSTAT_TOP_DELTA) { 19840b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_shutdown != NULL) 19850b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_shutdown(NULL); 1986c86819ecSFabien Thomas pmcstat_stats_reset(); 19870b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_init != NULL) 19880b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_init(); 19890b86b1bbSFabien Thomas } 19900b86b1bbSFabien Thomas 19910b86b1bbSFabien Thomas } 19920b86b1bbSFabien Thomas 19930b86b1bbSFabien Thomas /* 19940b86b1bbSFabien Thomas * Configure a plugins. 19950b86b1bbSFabien Thomas */ 19960b86b1bbSFabien Thomas 19970b86b1bbSFabien Thomas void 19980b86b1bbSFabien Thomas pmcstat_pluginconfigure_log(char *opt) 19990b86b1bbSFabien Thomas { 20000b86b1bbSFabien Thomas 20010b86b1bbSFabien Thomas if (strncmp(opt, "threshold=", 10) == 0) { 20020b86b1bbSFabien Thomas pmcstat_threshold = atof(opt+10); 20030b86b1bbSFabien Thomas } else { 20040b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_configure != NULL) { 20050b86b1bbSFabien Thomas if (!plugins[args.pa_plugin].pl_configure(opt)) 20060b86b1bbSFabien Thomas err(EX_USAGE, 20070b86b1bbSFabien Thomas "ERROR: unknown option <%s>.", opt); 20080b86b1bbSFabien Thomas } 20090b86b1bbSFabien Thomas } 201015139246SJoseph Koshy } 201115139246SJoseph Koshy 201249874f6eSJoseph Koshy /* 201349874f6eSJoseph Koshy * Initialize module. 201449874f6eSJoseph Koshy */ 201549874f6eSJoseph Koshy 201615139246SJoseph Koshy void 20170b86b1bbSFabien Thomas pmcstat_initialize_logging(void) 201815139246SJoseph Koshy { 201960f918efSJoseph Koshy int i; 202049874f6eSJoseph Koshy 202115139246SJoseph Koshy /* use a convenient format for 'ldd' output */ 2022f3c8200eSJoseph Koshy if (setenv("LD_TRACE_LOADED_OBJECTS_FMT1","%o \"%p\" %x\n",1) != 0) 202349874f6eSJoseph Koshy err(EX_OSERR, "ERROR: Cannot setenv"); 202415139246SJoseph Koshy 202515139246SJoseph Koshy /* Initialize hash tables */ 202649874f6eSJoseph Koshy pmcstat_string_initialize(); 202715139246SJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) { 202815139246SJoseph Koshy LIST_INIT(&pmcstat_image_hash[i]); 202915139246SJoseph Koshy LIST_INIT(&pmcstat_process_hash[i]); 203015139246SJoseph Koshy } 203115139246SJoseph Koshy 203249874f6eSJoseph Koshy /* 203349874f6eSJoseph Koshy * Create a fake 'process' entry for the kernel with pid -1. 203449874f6eSJoseph Koshy * hwpmc(4) will subsequently inform us about where the kernel 203549874f6eSJoseph Koshy * and any loaded kernel modules are mapped. 203649874f6eSJoseph Koshy */ 203749874f6eSJoseph Koshy if ((pmcstat_kernproc = pmcstat_process_lookup((pid_t) -1, 203849874f6eSJoseph Koshy PMCSTAT_ALLOCATE)) == NULL) 203915139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot initialize logging"); 20400b86b1bbSFabien Thomas 20410b86b1bbSFabien Thomas /* PMC count. */ 20420b86b1bbSFabien Thomas pmcstat_npmcs = 0; 20430b86b1bbSFabien Thomas 20440b86b1bbSFabien Thomas /* Merge PMC with same name. */ 20450b86b1bbSFabien Thomas pmcstat_mergepmc = args.pa_mergepmc; 20460b86b1bbSFabien Thomas 20470b86b1bbSFabien Thomas /* 20480b86b1bbSFabien Thomas * Initialize plugins 20490b86b1bbSFabien Thomas */ 20500b86b1bbSFabien Thomas 20510b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_init != NULL) 20520b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_init(); 20530b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_init != NULL) 20540b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_init(); 205515139246SJoseph Koshy } 205615139246SJoseph Koshy 205749874f6eSJoseph Koshy /* 205849874f6eSJoseph Koshy * Shutdown module. 205949874f6eSJoseph Koshy */ 206049874f6eSJoseph Koshy 206115139246SJoseph Koshy void 20620b86b1bbSFabien Thomas pmcstat_shutdown_logging(void) 206315139246SJoseph Koshy { 206415139246SJoseph Koshy int i; 206549874f6eSJoseph Koshy FILE *mf; 206615139246SJoseph Koshy struct pmcstat_image *pi, *pitmp; 206715139246SJoseph Koshy struct pmcstat_process *pp, *pptmp; 20680b86b1bbSFabien Thomas struct pmcstat_pcmap *ppm, *ppmtmp; 206949874f6eSJoseph Koshy 207049874f6eSJoseph Koshy /* determine where to send the map file */ 207149874f6eSJoseph Koshy mf = NULL; 20720b86b1bbSFabien Thomas if (args.pa_mapfilename != NULL) 20730b86b1bbSFabien Thomas mf = (strcmp(args.pa_mapfilename, "-") == 0) ? 20740b86b1bbSFabien Thomas args.pa_printfile : fopen(args.pa_mapfilename, "w"); 207549874f6eSJoseph Koshy 20760b86b1bbSFabien Thomas if (mf == NULL && args.pa_flags & FLAG_DO_GPROF && 20770b86b1bbSFabien Thomas args.pa_verbosity >= 2) 20780b86b1bbSFabien Thomas mf = args.pa_printfile; 207949874f6eSJoseph Koshy 208049874f6eSJoseph Koshy if (mf) 208149874f6eSJoseph Koshy (void) fprintf(mf, "MAP:\n"); 208215139246SJoseph Koshy 2083b6010f9eSJoseph Koshy /* 20840b86b1bbSFabien Thomas * Shutdown the plugins 2085b6010f9eSJoseph Koshy */ 20862a6d2e9cSJoseph Koshy 20870b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_shutdown != NULL) 20880b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_shutdown(mf); 20890b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_shutdown != NULL) 20900b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_shutdown(mf); 2091b6010f9eSJoseph Koshy 2092b6010f9eSJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) { 20930b86b1bbSFabien Thomas LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next, 20940b86b1bbSFabien Thomas pitmp) { 20950b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_shutdownimage != NULL) 20960b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_shutdownimage(pi); 20970b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_shutdownimage != NULL) 20980b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_shutdownimage(pi); 20990b86b1bbSFabien Thomas 2100b6010f9eSJoseph Koshy free(pi->pi_symbols); 21010b86b1bbSFabien Thomas if (pi->pi_addr2line != NULL) 21020b86b1bbSFabien Thomas pclose(pi->pi_addr2line); 210315139246SJoseph Koshy LIST_REMOVE(pi, pi_next); 210415139246SJoseph Koshy free(pi); 210515139246SJoseph Koshy } 2106b6010f9eSJoseph Koshy 210715139246SJoseph Koshy LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[i], pp_next, 210815139246SJoseph Koshy pptmp) { 21090b86b1bbSFabien Thomas TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) { 21100b86b1bbSFabien Thomas TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 21110b86b1bbSFabien Thomas free(ppm); 21120b86b1bbSFabien Thomas } 211315139246SJoseph Koshy LIST_REMOVE(pp, pp_next); 211415139246SJoseph Koshy free(pp); 211515139246SJoseph Koshy } 211615139246SJoseph Koshy } 211749874f6eSJoseph Koshy 211849874f6eSJoseph Koshy pmcstat_string_shutdown(); 211949874f6eSJoseph Koshy 212049874f6eSJoseph Koshy /* 212149874f6eSJoseph Koshy * Print errors unless -q was specified. Print all statistics 212249874f6eSJoseph Koshy * if verbosity > 1. 212349874f6eSJoseph Koshy */ 21240b86b1bbSFabien Thomas #define PRINT(N,V) do { \ 21250b86b1bbSFabien Thomas if (pmcstat_stats.ps_##V || args.pa_verbosity >= 2) \ 21260b86b1bbSFabien Thomas (void) fprintf(args.pa_printfile, " %-40s %d\n",\ 212749874f6eSJoseph Koshy N, pmcstat_stats.ps_##V); \ 212849874f6eSJoseph Koshy } while (0) 212949874f6eSJoseph Koshy 21300b86b1bbSFabien Thomas if (args.pa_verbosity >= 1 && (args.pa_flags & FLAG_DO_ANALYSIS) && 21310b86b1bbSFabien Thomas (args.pa_flags & FLAG_DO_TOP) == 0) { 21320b86b1bbSFabien Thomas (void) fprintf(args.pa_printfile, "CONVERSION STATISTICS:\n"); 21330b86b1bbSFabien Thomas PRINT("#exec/a.out", exec_aout); 21340b86b1bbSFabien Thomas PRINT("#exec/elf", exec_elf); 21350b86b1bbSFabien Thomas PRINT("#exec/unknown", exec_indeterminable); 21360b86b1bbSFabien Thomas PRINT("#exec handling errors", exec_errors); 21370b86b1bbSFabien Thomas PRINT("#samples/total", samples_total); 21380b86b1bbSFabien Thomas PRINT("#samples/unclaimed", samples_unknown_offset); 21390b86b1bbSFabien Thomas PRINT("#samples/unknown-object", samples_indeterminable); 21400b86b1bbSFabien Thomas PRINT("#callchain/dubious-frames", callchain_dubious_frames); 214115139246SJoseph Koshy } 214249874f6eSJoseph Koshy 214349874f6eSJoseph Koshy if (mf) 214449874f6eSJoseph Koshy (void) fclose(mf); 214515139246SJoseph Koshy } 2146