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); 25015139246SJoseph Koshy 25115139246SJoseph Koshy /* 25249874f6eSJoseph Koshy * A simple implementation of interned strings. Each interned string 25349874f6eSJoseph Koshy * is assigned a unique address, so that subsequent string compares 25449874f6eSJoseph Koshy * can be done by a simple pointer comparision instead of using 25549874f6eSJoseph Koshy * strcmp(). This speeds up hash table lookups and saves memory if 25649874f6eSJoseph Koshy * duplicate strings are the norm. 25749874f6eSJoseph Koshy */ 25849874f6eSJoseph Koshy struct pmcstat_string { 25949874f6eSJoseph Koshy LIST_ENTRY(pmcstat_string) ps_next; /* hash link */ 26049874f6eSJoseph Koshy int ps_len; 26149874f6eSJoseph Koshy int ps_hash; 26249874f6eSJoseph Koshy char *ps_string; 26349874f6eSJoseph Koshy }; 26449874f6eSJoseph Koshy 26549874f6eSJoseph Koshy static LIST_HEAD(,pmcstat_string) pmcstat_string_hash[PMCSTAT_NHASH]; 26649874f6eSJoseph Koshy 26749874f6eSJoseph Koshy /* 2680b86b1bbSFabien Thomas * PMC count. 2690b86b1bbSFabien Thomas */ 2700b86b1bbSFabien Thomas int pmcstat_npmcs; 2710b86b1bbSFabien Thomas 2720b86b1bbSFabien Thomas /* 2730b86b1bbSFabien Thomas * PMC Top mode pause state. 2740b86b1bbSFabien Thomas */ 2750b86b1bbSFabien Thomas int pmcstat_pause; 2760b86b1bbSFabien Thomas 2770b86b1bbSFabien Thomas /* 27849874f6eSJoseph Koshy * Compute a 'hash' value for a string. 27949874f6eSJoseph Koshy */ 28049874f6eSJoseph Koshy 28149874f6eSJoseph Koshy static int 28249874f6eSJoseph Koshy pmcstat_string_compute_hash(const char *s) 28349874f6eSJoseph Koshy { 28449874f6eSJoseph Koshy int hash; 28549874f6eSJoseph Koshy 28649874f6eSJoseph Koshy for (hash = 0; *s; s++) 28749874f6eSJoseph Koshy hash ^= *s; 28849874f6eSJoseph Koshy 28949874f6eSJoseph Koshy return (hash & PMCSTAT_HASH_MASK); 29049874f6eSJoseph Koshy } 29149874f6eSJoseph Koshy 29249874f6eSJoseph Koshy /* 29349874f6eSJoseph Koshy * Intern a copy of string 's', and return a pointer to the 29449874f6eSJoseph Koshy * interned structure. 29549874f6eSJoseph Koshy */ 29649874f6eSJoseph Koshy 2970b86b1bbSFabien Thomas pmcstat_interned_string 29849874f6eSJoseph Koshy pmcstat_string_intern(const char *s) 29949874f6eSJoseph Koshy { 30049874f6eSJoseph Koshy struct pmcstat_string *ps; 30149874f6eSJoseph Koshy const struct pmcstat_string *cps; 30249874f6eSJoseph Koshy int hash, len; 30349874f6eSJoseph Koshy 30449874f6eSJoseph Koshy if ((cps = pmcstat_string_lookup(s)) != NULL) 30549874f6eSJoseph Koshy return (cps); 30649874f6eSJoseph Koshy 30749874f6eSJoseph Koshy hash = pmcstat_string_compute_hash(s); 30849874f6eSJoseph Koshy len = strlen(s); 30949874f6eSJoseph Koshy 31049874f6eSJoseph Koshy if ((ps = malloc(sizeof(*ps))) == NULL) 31149874f6eSJoseph Koshy err(EX_OSERR, "ERROR: Could not intern string"); 31249874f6eSJoseph Koshy ps->ps_len = len; 31349874f6eSJoseph Koshy ps->ps_hash = hash; 31449874f6eSJoseph Koshy ps->ps_string = strdup(s); 31549874f6eSJoseph Koshy LIST_INSERT_HEAD(&pmcstat_string_hash[hash], ps, ps_next); 31649874f6eSJoseph Koshy return ((pmcstat_interned_string) ps); 31749874f6eSJoseph Koshy } 31849874f6eSJoseph Koshy 3190b86b1bbSFabien Thomas const char * 32049874f6eSJoseph Koshy pmcstat_string_unintern(pmcstat_interned_string str) 32149874f6eSJoseph Koshy { 32249874f6eSJoseph Koshy const char *s; 32349874f6eSJoseph Koshy 32449874f6eSJoseph Koshy s = ((const struct pmcstat_string *) str)->ps_string; 32549874f6eSJoseph Koshy return (s); 32649874f6eSJoseph Koshy } 32749874f6eSJoseph Koshy 3280b86b1bbSFabien Thomas pmcstat_interned_string 32949874f6eSJoseph Koshy pmcstat_string_lookup(const char *s) 33049874f6eSJoseph Koshy { 33149874f6eSJoseph Koshy struct pmcstat_string *ps; 33249874f6eSJoseph Koshy int hash, len; 33349874f6eSJoseph Koshy 33449874f6eSJoseph Koshy hash = pmcstat_string_compute_hash(s); 33549874f6eSJoseph Koshy len = strlen(s); 33649874f6eSJoseph Koshy 33749874f6eSJoseph Koshy LIST_FOREACH(ps, &pmcstat_string_hash[hash], ps_next) 33849874f6eSJoseph Koshy if (ps->ps_len == len && ps->ps_hash == hash && 33949874f6eSJoseph Koshy strcmp(ps->ps_string, s) == 0) 34049874f6eSJoseph Koshy return (ps); 34149874f6eSJoseph Koshy return (NULL); 34249874f6eSJoseph Koshy } 34349874f6eSJoseph Koshy 34449874f6eSJoseph Koshy static int 34549874f6eSJoseph Koshy pmcstat_string_lookup_hash(pmcstat_interned_string s) 34649874f6eSJoseph Koshy { 34749874f6eSJoseph Koshy const struct pmcstat_string *ps; 34849874f6eSJoseph Koshy 34949874f6eSJoseph Koshy ps = (const struct pmcstat_string *) s; 35049874f6eSJoseph Koshy return (ps->ps_hash); 35149874f6eSJoseph Koshy } 35249874f6eSJoseph Koshy 35349874f6eSJoseph Koshy /* 35449874f6eSJoseph Koshy * Initialize the string interning facility. 35549874f6eSJoseph Koshy */ 35649874f6eSJoseph Koshy 35749874f6eSJoseph Koshy static void 35849874f6eSJoseph Koshy pmcstat_string_initialize(void) 35949874f6eSJoseph Koshy { 36049874f6eSJoseph Koshy int i; 36149874f6eSJoseph Koshy 36249874f6eSJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) 36349874f6eSJoseph Koshy LIST_INIT(&pmcstat_string_hash[i]); 36449874f6eSJoseph Koshy } 36549874f6eSJoseph Koshy 36649874f6eSJoseph Koshy /* 36749874f6eSJoseph Koshy * Destroy the string table, free'ing up space. 36849874f6eSJoseph Koshy */ 36949874f6eSJoseph Koshy 37049874f6eSJoseph Koshy static void 37149874f6eSJoseph Koshy pmcstat_string_shutdown(void) 37249874f6eSJoseph Koshy { 37349874f6eSJoseph Koshy int i; 37449874f6eSJoseph Koshy struct pmcstat_string *ps, *pstmp; 37549874f6eSJoseph Koshy 37649874f6eSJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) 37749874f6eSJoseph Koshy LIST_FOREACH_SAFE(ps, &pmcstat_string_hash[i], ps_next, 37849874f6eSJoseph Koshy pstmp) { 37949874f6eSJoseph Koshy LIST_REMOVE(ps, ps_next); 38049874f6eSJoseph Koshy free(ps->ps_string); 38149874f6eSJoseph Koshy free(ps); 38249874f6eSJoseph Koshy } 38349874f6eSJoseph Koshy } 38449874f6eSJoseph Koshy 38549874f6eSJoseph Koshy /* 38649874f6eSJoseph Koshy * Determine whether a given executable image is an A.OUT object, and 38749874f6eSJoseph Koshy * if so, fill in its parameters from the text file. 38849874f6eSJoseph Koshy * Sets image->pi_type. 38949874f6eSJoseph Koshy */ 39049874f6eSJoseph Koshy 39115139246SJoseph Koshy static void 3920b86b1bbSFabien Thomas pmcstat_image_get_aout_params(struct pmcstat_image *image) 39349874f6eSJoseph Koshy { 39449874f6eSJoseph Koshy int fd; 39549874f6eSJoseph Koshy ssize_t nbytes; 39649874f6eSJoseph Koshy struct exec ex; 39749874f6eSJoseph Koshy const char *path; 39849874f6eSJoseph Koshy char buffer[PATH_MAX]; 39949874f6eSJoseph Koshy 40049874f6eSJoseph Koshy path = pmcstat_string_unintern(image->pi_execpath); 40149874f6eSJoseph Koshy assert(path != NULL); 40249874f6eSJoseph Koshy 40349874f6eSJoseph Koshy if (image->pi_iskernelmodule) 40449874f6eSJoseph Koshy errx(EX_SOFTWARE, "ERROR: a.out kernel modules are " 40549874f6eSJoseph Koshy "unsupported \"%s\"", path); 40649874f6eSJoseph Koshy 40749874f6eSJoseph Koshy (void) snprintf(buffer, sizeof(buffer), "%s%s", 4080b86b1bbSFabien Thomas args.pa_fsroot, path); 40949874f6eSJoseph Koshy 41049874f6eSJoseph Koshy if ((fd = open(buffer, O_RDONLY, 0)) < 0 || 41149874f6eSJoseph Koshy (nbytes = read(fd, &ex, sizeof(ex))) < 0) { 41249874f6eSJoseph Koshy warn("WARNING: Cannot determine type of \"%s\"", path); 41349874f6eSJoseph Koshy image->pi_type = PMCSTAT_IMAGE_INDETERMINABLE; 41449874f6eSJoseph Koshy if (fd != -1) 41549874f6eSJoseph Koshy (void) close(fd); 41649874f6eSJoseph Koshy return; 41749874f6eSJoseph Koshy } 41849874f6eSJoseph Koshy 41949874f6eSJoseph Koshy (void) close(fd); 42049874f6eSJoseph Koshy 42149874f6eSJoseph Koshy if ((unsigned) nbytes != sizeof(ex) || 42249874f6eSJoseph Koshy N_BADMAG(ex)) 42349874f6eSJoseph Koshy return; 42449874f6eSJoseph Koshy 42549874f6eSJoseph Koshy image->pi_type = PMCSTAT_IMAGE_AOUT; 42649874f6eSJoseph Koshy 42749874f6eSJoseph Koshy /* TODO: the rest of a.out processing */ 42849874f6eSJoseph Koshy 42949874f6eSJoseph Koshy return; 43049874f6eSJoseph Koshy } 43149874f6eSJoseph Koshy 43249874f6eSJoseph Koshy /* 433b6010f9eSJoseph Koshy * Helper function. 434b6010f9eSJoseph Koshy */ 435b6010f9eSJoseph Koshy 436b6010f9eSJoseph Koshy static int 437b6010f9eSJoseph Koshy pmcstat_symbol_compare(const void *a, const void *b) 438b6010f9eSJoseph Koshy { 439b6010f9eSJoseph Koshy const struct pmcstat_symbol *sym1, *sym2; 440b6010f9eSJoseph Koshy 441b6010f9eSJoseph Koshy sym1 = (const struct pmcstat_symbol *) a; 442b6010f9eSJoseph Koshy sym2 = (const struct pmcstat_symbol *) b; 443b6010f9eSJoseph Koshy 444b6010f9eSJoseph Koshy if (sym1->ps_end <= sym2->ps_start) 445b6010f9eSJoseph Koshy return (-1); 446b6010f9eSJoseph Koshy if (sym1->ps_start >= sym2->ps_end) 447b6010f9eSJoseph Koshy return (1); 448b6010f9eSJoseph Koshy return (0); 449b6010f9eSJoseph Koshy } 450b6010f9eSJoseph Koshy 451b6010f9eSJoseph Koshy /* 452b6010f9eSJoseph Koshy * Map an address to a symbol in an image. 453b6010f9eSJoseph Koshy */ 454b6010f9eSJoseph Koshy 4550b86b1bbSFabien Thomas struct pmcstat_symbol * 456b6010f9eSJoseph Koshy pmcstat_symbol_search(struct pmcstat_image *image, uintfptr_t addr) 457b6010f9eSJoseph Koshy { 458b6010f9eSJoseph Koshy struct pmcstat_symbol sym; 459b6010f9eSJoseph Koshy 460b6010f9eSJoseph Koshy if (image->pi_symbols == NULL) 461b6010f9eSJoseph Koshy return (NULL); 462b6010f9eSJoseph Koshy 463b6010f9eSJoseph Koshy sym.ps_name = NULL; 464b6010f9eSJoseph Koshy sym.ps_start = addr; 465b6010f9eSJoseph Koshy sym.ps_end = addr + 1; 466b6010f9eSJoseph Koshy 467b6010f9eSJoseph Koshy return (bsearch((void *) &sym, image->pi_symbols, 468b6010f9eSJoseph Koshy image->pi_symcount, sizeof(struct pmcstat_symbol), 469b6010f9eSJoseph Koshy pmcstat_symbol_compare)); 470b6010f9eSJoseph Koshy } 471b6010f9eSJoseph Koshy 472b6010f9eSJoseph Koshy /* 473b6010f9eSJoseph Koshy * Add the list of symbols in the given section to the list associated 474b6010f9eSJoseph Koshy * with the object. 475b6010f9eSJoseph Koshy */ 476b6010f9eSJoseph Koshy static void 477b6010f9eSJoseph Koshy pmcstat_image_add_symbols(struct pmcstat_image *image, Elf *e, 478b6010f9eSJoseph Koshy Elf_Scn *scn, GElf_Shdr *sh) 479b6010f9eSJoseph Koshy { 480b6010f9eSJoseph Koshy int firsttime; 481b6010f9eSJoseph Koshy size_t n, newsyms, nshsyms, nfuncsyms; 482b6010f9eSJoseph Koshy struct pmcstat_symbol *symptr; 483b6010f9eSJoseph Koshy char *fnname; 484b6010f9eSJoseph Koshy GElf_Sym sym; 485b6010f9eSJoseph Koshy Elf_Data *data; 486b6010f9eSJoseph Koshy 487b6010f9eSJoseph Koshy if ((data = elf_getdata(scn, NULL)) == NULL) 488b6010f9eSJoseph Koshy return; 489b6010f9eSJoseph Koshy 490b6010f9eSJoseph Koshy /* 491b6010f9eSJoseph Koshy * Determine the number of functions named in this 492b6010f9eSJoseph Koshy * section. 493b6010f9eSJoseph Koshy */ 494b6010f9eSJoseph Koshy 495b6010f9eSJoseph Koshy nshsyms = sh->sh_size / sh->sh_entsize; 496b6010f9eSJoseph Koshy for (n = nfuncsyms = 0; n < nshsyms; n++) { 497b6010f9eSJoseph Koshy if (gelf_getsym(data, (int) n, &sym) != &sym) 498b6010f9eSJoseph Koshy return; 499b6010f9eSJoseph Koshy if (GELF_ST_TYPE(sym.st_info) == STT_FUNC) 500b6010f9eSJoseph Koshy nfuncsyms++; 501b6010f9eSJoseph Koshy } 502b6010f9eSJoseph Koshy 503b6010f9eSJoseph Koshy if (nfuncsyms == 0) 504b6010f9eSJoseph Koshy return; 505b6010f9eSJoseph Koshy 506b6010f9eSJoseph Koshy /* 507b6010f9eSJoseph Koshy * Allocate space for the new entries. 508b6010f9eSJoseph Koshy */ 509b6010f9eSJoseph Koshy firsttime = image->pi_symbols == NULL; 510b6010f9eSJoseph Koshy symptr = realloc(image->pi_symbols, 511b6010f9eSJoseph Koshy sizeof(*symptr) * (image->pi_symcount + nfuncsyms)); 512b6010f9eSJoseph Koshy if (symptr == image->pi_symbols) /* realloc() failed. */ 513b6010f9eSJoseph Koshy return; 514b6010f9eSJoseph Koshy image->pi_symbols = symptr; 515b6010f9eSJoseph Koshy 516b6010f9eSJoseph Koshy /* 517b6010f9eSJoseph Koshy * Append new symbols to the end of the current table. 518b6010f9eSJoseph Koshy */ 519b6010f9eSJoseph Koshy symptr += image->pi_symcount; 520b6010f9eSJoseph Koshy 521b6010f9eSJoseph Koshy for (n = newsyms = 0; n < nshsyms; n++) { 522b6010f9eSJoseph Koshy if (gelf_getsym(data, (int) n, &sym) != &sym) 523b6010f9eSJoseph Koshy return; 524b6010f9eSJoseph Koshy if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) 525b6010f9eSJoseph Koshy continue; 526b6010f9eSJoseph Koshy 527b6010f9eSJoseph Koshy if (!firsttime && pmcstat_symbol_search(image, sym.st_value)) 528b6010f9eSJoseph Koshy continue; /* We've seen this symbol already. */ 529b6010f9eSJoseph Koshy 530b6010f9eSJoseph Koshy if ((fnname = elf_strptr(e, sh->sh_link, sym.st_name)) 531b6010f9eSJoseph Koshy == NULL) 532b6010f9eSJoseph Koshy continue; 533b6010f9eSJoseph Koshy 534b6010f9eSJoseph Koshy symptr->ps_name = pmcstat_string_intern(fnname); 535b6010f9eSJoseph Koshy symptr->ps_start = sym.st_value - image->pi_vaddr; 536b6010f9eSJoseph Koshy symptr->ps_end = symptr->ps_start + sym.st_size; 537b6010f9eSJoseph Koshy symptr++; 538b6010f9eSJoseph Koshy 539b6010f9eSJoseph Koshy newsyms++; 540b6010f9eSJoseph Koshy } 541b6010f9eSJoseph Koshy 542b6010f9eSJoseph Koshy image->pi_symcount += newsyms; 543b6010f9eSJoseph Koshy 544b6010f9eSJoseph Koshy assert(newsyms <= nfuncsyms); 545b6010f9eSJoseph Koshy 546b6010f9eSJoseph Koshy /* 547b6010f9eSJoseph Koshy * Return space to the system if there were duplicates. 548b6010f9eSJoseph Koshy */ 549b6010f9eSJoseph Koshy if (newsyms < nfuncsyms) 550b6010f9eSJoseph Koshy image->pi_symbols = realloc(image->pi_symbols, 551b6010f9eSJoseph Koshy sizeof(*symptr) * image->pi_symcount); 552b6010f9eSJoseph Koshy 553b6010f9eSJoseph Koshy /* 554b6010f9eSJoseph Koshy * Keep the list of symbols sorted. 555b6010f9eSJoseph Koshy */ 556b6010f9eSJoseph Koshy qsort(image->pi_symbols, image->pi_symcount, sizeof(*symptr), 557b6010f9eSJoseph Koshy pmcstat_symbol_compare); 558b6010f9eSJoseph Koshy 559b6010f9eSJoseph Koshy /* 560b6010f9eSJoseph Koshy * Deal with function symbols that have a size of 'zero' by 561b6010f9eSJoseph Koshy * making them extend to the next higher address. These 562b6010f9eSJoseph Koshy * symbols are usually defined in assembly code. 563b6010f9eSJoseph Koshy */ 564b6010f9eSJoseph Koshy for (symptr = image->pi_symbols; 565b6010f9eSJoseph Koshy symptr < image->pi_symbols + (image->pi_symcount - 1); 566b6010f9eSJoseph Koshy symptr++) 567b6010f9eSJoseph Koshy if (symptr->ps_start == symptr->ps_end) 568b6010f9eSJoseph Koshy symptr->ps_end = (symptr+1)->ps_start; 569b6010f9eSJoseph Koshy } 570b6010f9eSJoseph Koshy 571b6010f9eSJoseph Koshy /* 57249874f6eSJoseph Koshy * Examine an ELF file to determine the size of its text segment. 57349874f6eSJoseph Koshy * Sets image->pi_type if anything conclusive can be determined about 57449874f6eSJoseph Koshy * this image. 57549874f6eSJoseph Koshy */ 57649874f6eSJoseph Koshy 57749874f6eSJoseph Koshy static void 5780b86b1bbSFabien Thomas pmcstat_image_get_elf_params(struct pmcstat_image *image) 57915139246SJoseph Koshy { 580b6010f9eSJoseph Koshy int fd; 581b6010f9eSJoseph Koshy size_t i, nph, nsh; 582b6010f9eSJoseph Koshy const char *path, *elfbase; 5830b86b1bbSFabien Thomas char *p, *endp; 58415139246SJoseph Koshy uintfptr_t minva, maxva; 585b6010f9eSJoseph Koshy Elf *e; 586b6010f9eSJoseph Koshy Elf_Scn *scn; 587b6010f9eSJoseph Koshy GElf_Ehdr eh; 588b6010f9eSJoseph Koshy GElf_Phdr ph; 589b6010f9eSJoseph Koshy GElf_Shdr sh; 59049874f6eSJoseph Koshy enum pmcstat_image_type image_type; 59149874f6eSJoseph Koshy char buffer[PATH_MAX]; 59215139246SJoseph Koshy 59360f918efSJoseph Koshy assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN); 59460f918efSJoseph Koshy 595b6010f9eSJoseph Koshy image->pi_start = minva = ~(uintfptr_t) 0; 596b6010f9eSJoseph Koshy image->pi_end = maxva = (uintfptr_t) 0; 597b6010f9eSJoseph Koshy image->pi_type = image_type = PMCSTAT_IMAGE_INDETERMINABLE; 598b6010f9eSJoseph Koshy image->pi_isdynamic = 0; 599b6010f9eSJoseph Koshy image->pi_dynlinkerpath = NULL; 600b6010f9eSJoseph Koshy image->pi_vaddr = 0; 60115139246SJoseph Koshy 602b6010f9eSJoseph Koshy path = pmcstat_string_unintern(image->pi_execpath); 60349874f6eSJoseph Koshy assert(path != NULL); 60415139246SJoseph Koshy 60549874f6eSJoseph Koshy /* 60649874f6eSJoseph Koshy * Look for kernel modules under FSROOT/KERNELPATH/NAME, 60749874f6eSJoseph Koshy * and user mode executable objects under FSROOT/PATHNAME. 60849874f6eSJoseph Koshy */ 60949874f6eSJoseph Koshy if (image->pi_iskernelmodule) 61049874f6eSJoseph Koshy (void) snprintf(buffer, sizeof(buffer), "%s%s/%s", 6110b86b1bbSFabien Thomas args.pa_fsroot, args.pa_kernel, path); 61249874f6eSJoseph Koshy else 61349874f6eSJoseph Koshy (void) snprintf(buffer, sizeof(buffer), "%s%s", 6140b86b1bbSFabien Thomas args.pa_fsroot, path); 61515139246SJoseph Koshy 616b6010f9eSJoseph Koshy e = NULL; 61749874f6eSJoseph Koshy if ((fd = open(buffer, O_RDONLY, 0)) < 0 || 618b6010f9eSJoseph Koshy (e = elf_begin(fd, ELF_C_READ, NULL)) == NULL || 619b6010f9eSJoseph Koshy (elf_kind(e) != ELF_K_ELF)) { 620b6010f9eSJoseph Koshy warnx("WARNING: Cannot determine the type of \"%s\".", 621b6010f9eSJoseph Koshy buffer); 622b6010f9eSJoseph Koshy goto done; 62349874f6eSJoseph Koshy } 62415139246SJoseph Koshy 625b6010f9eSJoseph Koshy if (gelf_getehdr(e, &eh) != &eh) { 626b6010f9eSJoseph Koshy warnx("WARNING: Cannot retrieve the ELF Header for " 627b6010f9eSJoseph Koshy "\"%s\": %s.", buffer, elf_errmsg(-1)); 628b6010f9eSJoseph Koshy goto done; 629b6010f9eSJoseph Koshy } 63015139246SJoseph Koshy 631b6010f9eSJoseph Koshy if (eh.e_type != ET_EXEC && eh.e_type != ET_DYN && 632b6010f9eSJoseph Koshy !(image->pi_iskernelmodule && eh.e_type == ET_REL)) { 633b6010f9eSJoseph Koshy warnx("WARNING: \"%s\" is of an unsupported ELF type.", 634b6010f9eSJoseph Koshy buffer); 635b6010f9eSJoseph Koshy goto done; 636b6010f9eSJoseph Koshy } 637b6010f9eSJoseph Koshy 638b6010f9eSJoseph Koshy image_type = eh.e_ident[EI_CLASS] == ELFCLASS32 ? 639b6010f9eSJoseph Koshy PMCSTAT_IMAGE_ELF32 : PMCSTAT_IMAGE_ELF64; 64015139246SJoseph Koshy 64149874f6eSJoseph Koshy /* 642b6010f9eSJoseph Koshy * Determine the virtual address where an executable would be 643b6010f9eSJoseph Koshy * loaded. Additionally, for dynamically linked executables, 644b6010f9eSJoseph Koshy * save the pathname to the runtime linker. 64549874f6eSJoseph Koshy */ 646b6010f9eSJoseph Koshy if (eh.e_type == ET_EXEC) { 647b6010f9eSJoseph Koshy if (elf_getphnum(e, &nph) == 0) { 648b6010f9eSJoseph Koshy warnx("WARNING: Could not determine the number of " 649b6010f9eSJoseph Koshy "program headers in \"%s\": %s.", buffer, 650b6010f9eSJoseph Koshy elf_errmsg(-1)); 651b6010f9eSJoseph Koshy goto done; 652887a8d04SJoseph Koshy } 653b6010f9eSJoseph Koshy for (i = 0; i < eh.e_phnum; i++) { 654b6010f9eSJoseph Koshy if (gelf_getphdr(e, i, &ph) != &ph) { 655b6010f9eSJoseph Koshy warnx("WARNING: Retrieval of PHDR entry #%ju " 656b6010f9eSJoseph Koshy "in \"%s\" failed: %s.", (uintmax_t) i, 657b6010f9eSJoseph Koshy buffer, elf_errmsg(-1)); 658b6010f9eSJoseph Koshy goto done; 659b6010f9eSJoseph Koshy } 660b6010f9eSJoseph Koshy switch (ph.p_type) { 661b6010f9eSJoseph Koshy case PT_DYNAMIC: 662b6010f9eSJoseph Koshy image->pi_isdynamic = 1; 663887a8d04SJoseph Koshy break; 664b6010f9eSJoseph Koshy case PT_INTERP: 665b6010f9eSJoseph Koshy if ((elfbase = elf_rawfile(e, NULL)) == NULL) { 666b6010f9eSJoseph Koshy warnx("WARNING: Cannot retrieve the " 667b6010f9eSJoseph Koshy "interpreter for \"%s\": %s.", 668b6010f9eSJoseph Koshy buffer, elf_errmsg(-1)); 669b6010f9eSJoseph Koshy goto done; 670887a8d04SJoseph Koshy } 671b6010f9eSJoseph Koshy image->pi_dynlinkerpath = 672b6010f9eSJoseph Koshy pmcstat_string_intern(elfbase + 673b6010f9eSJoseph Koshy ph.p_offset); 674b6010f9eSJoseph Koshy break; 675b6010f9eSJoseph Koshy case PT_LOAD: 676b6010f9eSJoseph Koshy if (ph.p_offset == 0) 677b6010f9eSJoseph Koshy image->pi_vaddr = ph.p_vaddr; 67815139246SJoseph Koshy break; 67915139246SJoseph Koshy } 680b6010f9eSJoseph Koshy } 681b6010f9eSJoseph Koshy } 682887a8d04SJoseph Koshy 683b6010f9eSJoseph Koshy /* 684b6010f9eSJoseph Koshy * Get the min and max VA associated with this ELF object. 685b6010f9eSJoseph Koshy */ 686b6010f9eSJoseph Koshy if (elf_getshnum(e, &nsh) == 0) { 687b6010f9eSJoseph Koshy warnx("WARNING: Could not determine the number of sections " 688b6010f9eSJoseph Koshy "for \"%s\": %s.", buffer, elf_errmsg(-1)); 689b6010f9eSJoseph Koshy goto done; 690b6010f9eSJoseph Koshy } 691b6010f9eSJoseph Koshy 692b6010f9eSJoseph Koshy for (i = 0; i < nsh; i++) { 693b6010f9eSJoseph Koshy if ((scn = elf_getscn(e, i)) == NULL || 694b6010f9eSJoseph Koshy gelf_getshdr(scn, &sh) != &sh) { 695b6010f9eSJoseph Koshy warnx("WARNING: Could not retrieve section header " 696b6010f9eSJoseph Koshy "#%ju in \"%s\": %s.", (uintmax_t) i, buffer, 697b6010f9eSJoseph Koshy elf_errmsg(-1)); 698b6010f9eSJoseph Koshy goto done; 699b6010f9eSJoseph Koshy } 700b6010f9eSJoseph Koshy if (sh.sh_flags & SHF_EXECINSTR) { 701b6010f9eSJoseph Koshy minva = min(minva, sh.sh_addr); 702b6010f9eSJoseph Koshy maxva = max(maxva, sh.sh_addr + sh.sh_size); 703b6010f9eSJoseph Koshy } 704b6010f9eSJoseph Koshy if (sh.sh_type == SHT_SYMTAB || sh.sh_type == SHT_DYNSYM) 705b6010f9eSJoseph Koshy pmcstat_image_add_symbols(image, e, scn, &sh); 706b6010f9eSJoseph Koshy } 707887a8d04SJoseph Koshy 708887a8d04SJoseph Koshy image->pi_start = minva; 709887a8d04SJoseph Koshy image->pi_end = maxva; 71049874f6eSJoseph Koshy image->pi_type = image_type; 711b6010f9eSJoseph Koshy image->pi_fullpath = pmcstat_string_intern(buffer); 71215139246SJoseph Koshy 7130b86b1bbSFabien Thomas /* Build display name 7140b86b1bbSFabien Thomas */ 7150b86b1bbSFabien Thomas endp = buffer; 7160b86b1bbSFabien Thomas for (p = buffer; *p; p++) 7170b86b1bbSFabien Thomas if (*p == '/') 7180b86b1bbSFabien Thomas endp = p+1; 7190b86b1bbSFabien Thomas image->pi_name = pmcstat_string_intern(endp); 7200b86b1bbSFabien Thomas 721b6010f9eSJoseph Koshy done: 722b6010f9eSJoseph Koshy (void) elf_end(e); 723b6010f9eSJoseph Koshy if (fd >= 0) 724b6010f9eSJoseph Koshy (void) close(fd); 72549874f6eSJoseph Koshy return; 72649874f6eSJoseph Koshy } 72715139246SJoseph Koshy 72849874f6eSJoseph Koshy /* 72949874f6eSJoseph Koshy * Given an image descriptor, determine whether it is an ELF, or AOUT. 73049874f6eSJoseph Koshy * If no handler claims the image, set its type to 'INDETERMINABLE'. 73149874f6eSJoseph Koshy */ 73249874f6eSJoseph Koshy 7330b86b1bbSFabien Thomas void 7340b86b1bbSFabien Thomas pmcstat_image_determine_type(struct pmcstat_image *image) 73549874f6eSJoseph Koshy { 73649874f6eSJoseph Koshy assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN); 73749874f6eSJoseph Koshy 73849874f6eSJoseph Koshy /* Try each kind of handler in turn */ 73949874f6eSJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 7400b86b1bbSFabien Thomas pmcstat_image_get_elf_params(image); 74149874f6eSJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 7420b86b1bbSFabien Thomas pmcstat_image_get_aout_params(image); 74349874f6eSJoseph Koshy 74449874f6eSJoseph Koshy /* 74549874f6eSJoseph Koshy * Otherwise, remember that we tried to determine 74649874f6eSJoseph Koshy * the object's type and had failed. 74749874f6eSJoseph Koshy */ 74849874f6eSJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 74949874f6eSJoseph Koshy image->pi_type = PMCSTAT_IMAGE_INDETERMINABLE; 75015139246SJoseph Koshy } 75115139246SJoseph Koshy 75215139246SJoseph Koshy /* 75360f918efSJoseph Koshy * Locate an image descriptor given an interned path, adding a fresh 75460f918efSJoseph Koshy * descriptor to the cache if necessary. This function also finds a 75560f918efSJoseph Koshy * suitable name for this image's sample file. 75649874f6eSJoseph Koshy * 75749874f6eSJoseph Koshy * We defer filling in the file format specific parts of the image 75849874f6eSJoseph Koshy * structure till the time we actually see a sample that would fall 75949874f6eSJoseph Koshy * into this image. 76015139246SJoseph Koshy */ 76115139246SJoseph Koshy 76215139246SJoseph Koshy static struct pmcstat_image * 76349874f6eSJoseph Koshy pmcstat_image_from_path(pmcstat_interned_string internedpath, 76449874f6eSJoseph Koshy int iskernelmodule) 76515139246SJoseph Koshy { 7660b86b1bbSFabien Thomas int hash; 76715139246SJoseph Koshy struct pmcstat_image *pi; 76815139246SJoseph Koshy 76949874f6eSJoseph Koshy hash = pmcstat_string_lookup_hash(internedpath); 77015139246SJoseph Koshy 77149874f6eSJoseph Koshy /* First, look for an existing entry. */ 77215139246SJoseph Koshy LIST_FOREACH(pi, &pmcstat_image_hash[hash], pi_next) 77349874f6eSJoseph Koshy if (pi->pi_execpath == internedpath && 774b6010f9eSJoseph Koshy pi->pi_iskernelmodule == iskernelmodule) 77549874f6eSJoseph Koshy return (pi); 77615139246SJoseph Koshy 77715139246SJoseph Koshy /* 778b6010f9eSJoseph Koshy * Allocate a new entry and place it at the head of the hash 779b6010f9eSJoseph Koshy * and LRU lists. 78015139246SJoseph Koshy */ 78115139246SJoseph Koshy pi = malloc(sizeof(*pi)); 78215139246SJoseph Koshy if (pi == NULL) 78349874f6eSJoseph Koshy return (NULL); 78415139246SJoseph Koshy 78515139246SJoseph Koshy pi->pi_type = PMCSTAT_IMAGE_UNKNOWN; 78649874f6eSJoseph Koshy pi->pi_execpath = internedpath; 78715139246SJoseph Koshy pi->pi_start = ~0; 78815139246SJoseph Koshy pi->pi_end = 0; 789b6010f9eSJoseph Koshy pi->pi_entry = 0; 790b6010f9eSJoseph Koshy pi->pi_vaddr = 0; 791b6010f9eSJoseph Koshy pi->pi_isdynamic = 0; 79249874f6eSJoseph Koshy pi->pi_iskernelmodule = iskernelmodule; 793b6010f9eSJoseph Koshy pi->pi_dynlinkerpath = NULL; 794b6010f9eSJoseph Koshy pi->pi_symbols = NULL; 795b6010f9eSJoseph Koshy pi->pi_symcount = 0; 7960b86b1bbSFabien Thomas pi->pi_addr2line = NULL; 79715139246SJoseph Koshy 7980b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_initimage != NULL) 7990b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_initimage(pi); 8000b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_initimage != NULL) 8010b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_initimage(pi); 80215139246SJoseph Koshy 80315139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_image_hash[hash], pi, pi_next); 80415139246SJoseph Koshy 80549874f6eSJoseph Koshy return (pi); 80615139246SJoseph Koshy } 80715139246SJoseph Koshy 80815139246SJoseph Koshy /* 80949874f6eSJoseph Koshy * Record the fact that PC values from 'start' to 'end' come from 81015139246SJoseph Koshy * image 'image'. 81115139246SJoseph Koshy */ 81215139246SJoseph Koshy 81315139246SJoseph Koshy static void 81415139246SJoseph Koshy pmcstat_image_link(struct pmcstat_process *pp, struct pmcstat_image *image, 81549874f6eSJoseph Koshy uintfptr_t start) 81615139246SJoseph Koshy { 81715139246SJoseph Koshy struct pmcstat_pcmap *pcm, *pcmnew; 81849874f6eSJoseph Koshy uintfptr_t offset; 81949874f6eSJoseph Koshy 82049874f6eSJoseph Koshy assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN && 82149874f6eSJoseph Koshy image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE); 82215139246SJoseph Koshy 82315139246SJoseph Koshy if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL) 82449874f6eSJoseph Koshy err(EX_OSERR, "ERROR: Cannot create a map entry"); 82515139246SJoseph Koshy 82649874f6eSJoseph Koshy /* 82749874f6eSJoseph Koshy * Adjust the map entry to only cover the text portion 82849874f6eSJoseph Koshy * of the object. 82949874f6eSJoseph Koshy */ 83049874f6eSJoseph Koshy 83149874f6eSJoseph Koshy offset = start - image->pi_vaddr; 83249874f6eSJoseph Koshy pcmnew->ppm_lowpc = image->pi_start + offset; 83349874f6eSJoseph Koshy pcmnew->ppm_highpc = image->pi_end + offset; 83415139246SJoseph Koshy pcmnew->ppm_image = image; 83515139246SJoseph Koshy 83649874f6eSJoseph Koshy assert(pcmnew->ppm_lowpc < pcmnew->ppm_highpc); 83749874f6eSJoseph Koshy 83849874f6eSJoseph Koshy /* Overlapped mmap()'s are assumed to never occur. */ 83915139246SJoseph Koshy TAILQ_FOREACH(pcm, &pp->pp_map, ppm_next) 84049874f6eSJoseph Koshy if (pcm->ppm_lowpc >= pcmnew->ppm_highpc) 84115139246SJoseph Koshy break; 84215139246SJoseph Koshy 84315139246SJoseph Koshy if (pcm == NULL) 84415139246SJoseph Koshy TAILQ_INSERT_TAIL(&pp->pp_map, pcmnew, ppm_next); 84515139246SJoseph Koshy else 84615139246SJoseph Koshy TAILQ_INSERT_BEFORE(pcm, pcmnew, ppm_next); 84715139246SJoseph Koshy } 84815139246SJoseph Koshy 84915139246SJoseph Koshy /* 85049874f6eSJoseph Koshy * Unmap images in the range [start..end) associated with process 85149874f6eSJoseph Koshy * 'pp'. 85249874f6eSJoseph Koshy */ 85349874f6eSJoseph Koshy 85449874f6eSJoseph Koshy static void 85549874f6eSJoseph Koshy pmcstat_image_unmap(struct pmcstat_process *pp, uintfptr_t start, 85649874f6eSJoseph Koshy uintfptr_t end) 85749874f6eSJoseph Koshy { 85849874f6eSJoseph Koshy struct pmcstat_pcmap *pcm, *pcmtmp, *pcmnew; 85949874f6eSJoseph Koshy 86049874f6eSJoseph Koshy assert(pp != NULL); 86149874f6eSJoseph Koshy assert(start < end); 86249874f6eSJoseph Koshy 86349874f6eSJoseph Koshy /* 86449874f6eSJoseph Koshy * Cases: 86549874f6eSJoseph Koshy * - we could have the range completely in the middle of an 86649874f6eSJoseph Koshy * existing pcmap; in this case we have to split the pcmap 86749874f6eSJoseph Koshy * structure into two (i.e., generate a 'hole'). 86849874f6eSJoseph Koshy * - we could have the range covering multiple pcmaps; these 86949874f6eSJoseph Koshy * will have to be removed. 87049874f6eSJoseph Koshy * - we could have either 'start' or 'end' falling in the 87149874f6eSJoseph Koshy * middle of a pcmap; in this case shorten the entry. 87249874f6eSJoseph Koshy */ 87349874f6eSJoseph Koshy TAILQ_FOREACH_SAFE(pcm, &pp->pp_map, ppm_next, pcmtmp) { 87449874f6eSJoseph Koshy assert(pcm->ppm_lowpc < pcm->ppm_highpc); 87549874f6eSJoseph Koshy if (pcm->ppm_highpc <= start) 87649874f6eSJoseph Koshy continue; 877b6010f9eSJoseph Koshy if (pcm->ppm_lowpc >= end) 87849874f6eSJoseph Koshy return; 87949874f6eSJoseph Koshy if (pcm->ppm_lowpc >= start && pcm->ppm_highpc <= end) { 88049874f6eSJoseph Koshy /* 88149874f6eSJoseph Koshy * The current pcmap is completely inside the 88249874f6eSJoseph Koshy * unmapped range: remove it entirely. 88349874f6eSJoseph Koshy */ 88449874f6eSJoseph Koshy TAILQ_REMOVE(&pp->pp_map, pcm, ppm_next); 88549874f6eSJoseph Koshy free(pcm); 88649874f6eSJoseph Koshy } else if (pcm->ppm_lowpc < start && pcm->ppm_highpc > end) { 88749874f6eSJoseph Koshy /* 88849874f6eSJoseph Koshy * Split this pcmap into two; curtail the 88949874f6eSJoseph Koshy * current map to end at [start-1], and start 89049874f6eSJoseph Koshy * the new one at [end]. 89149874f6eSJoseph Koshy */ 89249874f6eSJoseph Koshy if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL) 89349874f6eSJoseph Koshy err(EX_OSERR, "ERROR: Cannot split a map " 89449874f6eSJoseph Koshy "entry"); 89549874f6eSJoseph Koshy 89649874f6eSJoseph Koshy pcmnew->ppm_image = pcm->ppm_image; 89749874f6eSJoseph Koshy 89849874f6eSJoseph Koshy pcmnew->ppm_lowpc = end; 89949874f6eSJoseph Koshy pcmnew->ppm_highpc = pcm->ppm_highpc; 90049874f6eSJoseph Koshy 90149874f6eSJoseph Koshy pcm->ppm_highpc = start; 90249874f6eSJoseph Koshy 90349874f6eSJoseph Koshy TAILQ_INSERT_AFTER(&pp->pp_map, pcm, pcmnew, ppm_next); 90449874f6eSJoseph Koshy 90549874f6eSJoseph Koshy return; 906b6010f9eSJoseph Koshy } else if (pcm->ppm_lowpc < start && pcm->ppm_highpc <= end) 907b6010f9eSJoseph Koshy pcm->ppm_highpc = start; 908b6010f9eSJoseph Koshy else if (pcm->ppm_lowpc >= start && pcm->ppm_highpc > end) 909b6010f9eSJoseph Koshy pcm->ppm_lowpc = end; 91049874f6eSJoseph Koshy else 91149874f6eSJoseph Koshy assert(0); 91249874f6eSJoseph Koshy } 91349874f6eSJoseph Koshy } 91449874f6eSJoseph Koshy 91549874f6eSJoseph Koshy /* 9160b86b1bbSFabien Thomas * Resolve file name and line number for the given address. 9170b86b1bbSFabien Thomas */ 9180b86b1bbSFabien Thomas int 9190b86b1bbSFabien Thomas pmcstat_image_addr2line(struct pmcstat_image *image, uintfptr_t addr, 9200b86b1bbSFabien Thomas char *sourcefile, size_t sourcefile_len, unsigned *sourceline, 9210b86b1bbSFabien Thomas char *funcname, size_t funcname_len) 9220b86b1bbSFabien Thomas { 9230b86b1bbSFabien Thomas static int addr2line_warn = 0; 9240b86b1bbSFabien Thomas 9250b86b1bbSFabien Thomas char *sep, cmdline[PATH_MAX], imagepath[PATH_MAX]; 9260b86b1bbSFabien Thomas int fd; 9270b86b1bbSFabien Thomas 9280b86b1bbSFabien Thomas if (image->pi_addr2line == NULL) { 9290b86b1bbSFabien Thomas snprintf(imagepath, sizeof(imagepath), "%s.symbols", 9300b86b1bbSFabien Thomas pmcstat_string_unintern(image->pi_fullpath)); 9310b86b1bbSFabien Thomas fd = open(imagepath, O_RDONLY); 9320b86b1bbSFabien Thomas if (fd < 0) { 9330b86b1bbSFabien Thomas snprintf(imagepath, sizeof(imagepath), "%s", 9340b86b1bbSFabien Thomas pmcstat_string_unintern(image->pi_fullpath)); 9350b86b1bbSFabien Thomas } else 9360b86b1bbSFabien Thomas close(fd); 9370b86b1bbSFabien Thomas snprintf(cmdline, sizeof(cmdline), "addr2line -Cfe \"%s\"", 9380b86b1bbSFabien Thomas imagepath); 9390b86b1bbSFabien Thomas image->pi_addr2line = popen(cmdline, "r+"); 9400b86b1bbSFabien Thomas if (image->pi_addr2line == NULL) { 9410b86b1bbSFabien Thomas if (!addr2line_warn) { 9420b86b1bbSFabien Thomas addr2line_warn = 1; 9430b86b1bbSFabien Thomas warnx("WARNING: addr2line is needed" 9440b86b1bbSFabien Thomas "for source code information."); 9450b86b1bbSFabien Thomas } 9460b86b1bbSFabien Thomas return (0); 9470b86b1bbSFabien Thomas } 9480b86b1bbSFabien Thomas } 9490b86b1bbSFabien Thomas 9500b86b1bbSFabien Thomas if (feof(image->pi_addr2line) || ferror(image->pi_addr2line)) { 9510b86b1bbSFabien Thomas warnx("WARNING: addr2line pipe error"); 9520b86b1bbSFabien Thomas pclose(image->pi_addr2line); 9530b86b1bbSFabien Thomas image->pi_addr2line = NULL; 9540b86b1bbSFabien Thomas return (0); 9550b86b1bbSFabien Thomas } 9560b86b1bbSFabien Thomas 9570b86b1bbSFabien Thomas fprintf(image->pi_addr2line, "%p\n", (void *)addr); 9580b86b1bbSFabien Thomas 9590b86b1bbSFabien Thomas if (fgets(funcname, funcname_len, image->pi_addr2line) == NULL) { 9600b86b1bbSFabien Thomas warnx("WARNING: addr2line function name read error"); 9610b86b1bbSFabien Thomas return (0); 9620b86b1bbSFabien Thomas } 9630b86b1bbSFabien Thomas sep = strchr(funcname, '\n'); 9640b86b1bbSFabien Thomas if (sep != NULL) 9650b86b1bbSFabien Thomas *sep = '\0'; 9660b86b1bbSFabien Thomas 9670b86b1bbSFabien Thomas if (fgets(sourcefile, sourcefile_len, image->pi_addr2line) == NULL) { 9680b86b1bbSFabien Thomas warnx("WARNING: addr2line source file read error"); 9690b86b1bbSFabien Thomas return (0); 9700b86b1bbSFabien Thomas } 9710b86b1bbSFabien Thomas sep = strchr(sourcefile, ':'); 9720b86b1bbSFabien Thomas if (sep == NULL) { 9730b86b1bbSFabien Thomas warnx("WARNING: addr2line source line separator missing"); 9740b86b1bbSFabien Thomas return (0); 9750b86b1bbSFabien Thomas } 9760b86b1bbSFabien Thomas *sep = '\0'; 9770b86b1bbSFabien Thomas *sourceline = atoi(sep+1); 9780b86b1bbSFabien Thomas if (*sourceline == 0) 9790b86b1bbSFabien Thomas return (0); 9800b86b1bbSFabien Thomas 9810b86b1bbSFabien Thomas return (1); 9820b86b1bbSFabien Thomas } 9830b86b1bbSFabien Thomas 9840b86b1bbSFabien Thomas /* 98515139246SJoseph Koshy * Add a {pmcid,name} mapping. 98615139246SJoseph Koshy */ 98715139246SJoseph Koshy 98815139246SJoseph Koshy static void 9890b86b1bbSFabien Thomas pmcstat_pmcid_add(pmc_id_t pmcid, pmcstat_interned_string ps) 99015139246SJoseph Koshy { 9910b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pr, *prm; 99215139246SJoseph Koshy 993b6010f9eSJoseph Koshy /* Replace an existing name for the PMC. */ 9940b86b1bbSFabien Thomas prm = NULL; 99515139246SJoseph Koshy LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 99615139246SJoseph Koshy if (pr->pr_pmcid == pmcid) { 99749874f6eSJoseph Koshy pr->pr_pmcname = ps; 99815139246SJoseph Koshy return; 9990b86b1bbSFabien Thomas } else if (pr->pr_pmcname == ps) 10000b86b1bbSFabien Thomas prm = pr; 100115139246SJoseph Koshy 1002b6010f9eSJoseph Koshy /* 10030b86b1bbSFabien Thomas * Otherwise, allocate a new descriptor and call the 10040b86b1bbSFabien Thomas * plugins hook. 1005b6010f9eSJoseph Koshy */ 100615139246SJoseph Koshy if ((pr = malloc(sizeof(*pr))) == NULL) 100715139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate pmc record"); 100815139246SJoseph Koshy 100915139246SJoseph Koshy pr->pr_pmcid = pmcid; 101049874f6eSJoseph Koshy pr->pr_pmcname = ps; 10110b86b1bbSFabien Thomas pr->pr_pmcin = pmcstat_npmcs++; 10120b86b1bbSFabien Thomas pr->pr_merge = prm == NULL ? pr : prm; 10130b86b1bbSFabien Thomas 101415139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); 101515139246SJoseph Koshy 10160b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_newpmc != NULL) 10170b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_newpmc(ps, pr); 10180b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_newpmc != NULL) 10190b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_newpmc(ps, pr); 102015139246SJoseph Koshy } 102115139246SJoseph Koshy 102215139246SJoseph Koshy /* 102349874f6eSJoseph Koshy * Given a pmcid in use, find its human-readable name. 102415139246SJoseph Koshy */ 102515139246SJoseph Koshy 10260b86b1bbSFabien Thomas const char * 102715139246SJoseph Koshy pmcstat_pmcid_to_name(pmc_id_t pmcid) 102815139246SJoseph Koshy { 102915139246SJoseph Koshy struct pmcstat_pmcrecord *pr; 103015139246SJoseph Koshy 103115139246SJoseph Koshy LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 103215139246SJoseph Koshy if (pr->pr_pmcid == pmcid) 103349874f6eSJoseph Koshy return (pmcstat_string_unintern(pr->pr_pmcname)); 103415139246SJoseph Koshy 10350b86b1bbSFabien Thomas err(EX_SOFTWARE, "ERROR: cannot find pmcid"); 10360b86b1bbSFabien Thomas return NULL; 10370b86b1bbSFabien Thomas } 103815139246SJoseph Koshy 10390b86b1bbSFabien Thomas /* 10400b86b1bbSFabien Thomas * Convert PMC index to name. 10410b86b1bbSFabien Thomas */ 104215139246SJoseph Koshy 10430b86b1bbSFabien Thomas const char * 10440b86b1bbSFabien Thomas pmcstat_pmcindex_to_name(int pmcin) 10450b86b1bbSFabien Thomas { 10460b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pr; 104715139246SJoseph Koshy 10480b86b1bbSFabien Thomas LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 10490b86b1bbSFabien Thomas if (pr->pr_pmcin == pmcin) 10500b86b1bbSFabien Thomas return pmcstat_string_unintern(pr->pr_pmcname); 10510b86b1bbSFabien Thomas 10520b86b1bbSFabien Thomas return NULL; 10530b86b1bbSFabien Thomas } 10540b86b1bbSFabien Thomas 10550b86b1bbSFabien Thomas /* 10560b86b1bbSFabien Thomas * Return PMC record with given index. 10570b86b1bbSFabien Thomas */ 10580b86b1bbSFabien Thomas 10590b86b1bbSFabien Thomas struct pmcstat_pmcrecord * 10600b86b1bbSFabien Thomas pmcstat_pmcindex_to_pmcr(int pmcin) 10610b86b1bbSFabien Thomas { 10620b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pr; 10630b86b1bbSFabien Thomas 10640b86b1bbSFabien Thomas LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 10650b86b1bbSFabien Thomas if (pr->pr_pmcin == pmcin) 10660b86b1bbSFabien Thomas return pr; 10670b86b1bbSFabien Thomas 10680b86b1bbSFabien Thomas err(EX_SOFTWARE, "ERROR: invalid pmcindex"); 10690b86b1bbSFabien Thomas return NULL; 10700b86b1bbSFabien Thomas } 10710b86b1bbSFabien Thomas 10720b86b1bbSFabien Thomas /* 10730b86b1bbSFabien Thomas * Get PMC record by id, apply merge policy. 10740b86b1bbSFabien Thomas */ 10750b86b1bbSFabien Thomas 10760b86b1bbSFabien Thomas static struct pmcstat_pmcrecord * 10770b86b1bbSFabien Thomas pmcstat_lookup_pmcid(pmc_id_t pmcid) 10780b86b1bbSFabien Thomas { 10790b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pr; 10800b86b1bbSFabien Thomas 10810b86b1bbSFabien Thomas LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) { 10820b86b1bbSFabien Thomas if (pr->pr_pmcid == pmcid) { 10830b86b1bbSFabien Thomas if (pmcstat_mergepmc) 10840b86b1bbSFabien Thomas return pr->pr_merge; 10850b86b1bbSFabien Thomas return pr; 10860b86b1bbSFabien Thomas } 10870b86b1bbSFabien Thomas } 10880b86b1bbSFabien Thomas 10890b86b1bbSFabien Thomas return NULL; 109015139246SJoseph Koshy } 109115139246SJoseph Koshy 109215139246SJoseph Koshy /* 109349874f6eSJoseph Koshy * Associate an AOUT image with a process. 109415139246SJoseph Koshy */ 109515139246SJoseph Koshy 109615139246SJoseph Koshy static void 109749874f6eSJoseph Koshy pmcstat_process_aout_exec(struct pmcstat_process *pp, 10980b86b1bbSFabien Thomas struct pmcstat_image *image, uintfptr_t entryaddr) 109915139246SJoseph Koshy { 110049874f6eSJoseph Koshy (void) pp; 110149874f6eSJoseph Koshy (void) image; 110249874f6eSJoseph Koshy (void) entryaddr; 110349874f6eSJoseph Koshy /* TODO Implement a.out handling */ 110449874f6eSJoseph Koshy } 110549874f6eSJoseph Koshy 110649874f6eSJoseph Koshy /* 110749874f6eSJoseph Koshy * Associate an ELF image with a process. 110849874f6eSJoseph Koshy */ 110949874f6eSJoseph Koshy 111049874f6eSJoseph Koshy static void 111149874f6eSJoseph Koshy pmcstat_process_elf_exec(struct pmcstat_process *pp, 11120b86b1bbSFabien Thomas struct pmcstat_image *image, uintfptr_t entryaddr) 111349874f6eSJoseph Koshy { 111415139246SJoseph Koshy uintmax_t libstart; 111549874f6eSJoseph Koshy struct pmcstat_image *rtldimage; 111615139246SJoseph Koshy 111749874f6eSJoseph Koshy assert(image->pi_type == PMCSTAT_IMAGE_ELF32 || 111849874f6eSJoseph Koshy image->pi_type == PMCSTAT_IMAGE_ELF64); 111915139246SJoseph Koshy 112060f918efSJoseph Koshy /* Create a map entry for the base executable. */ 112149874f6eSJoseph Koshy pmcstat_image_link(pp, image, image->pi_vaddr); 112215139246SJoseph Koshy 112360f918efSJoseph Koshy /* 1124b6010f9eSJoseph Koshy * For dynamically linked executables we need to determine 1125b6010f9eSJoseph Koshy * where the dynamic linker was mapped to for this process, 1126b6010f9eSJoseph Koshy * Subsequent executable objects that are mapped in by the 1127b6010f9eSJoseph Koshy * dynamic linker will be tracked by log events of type 1128b6010f9eSJoseph Koshy * PMCLOG_TYPE_MAP_IN. 112960f918efSJoseph Koshy */ 113049874f6eSJoseph Koshy 113115139246SJoseph Koshy if (image->pi_isdynamic) { 113215139246SJoseph Koshy 113360f918efSJoseph Koshy /* 113460f918efSJoseph Koshy * The runtime loader gets loaded just after the maximum 113560f918efSJoseph Koshy * possible heap address. Like so: 113660f918efSJoseph Koshy * 113760f918efSJoseph Koshy * [ TEXT DATA BSS HEAP -->*RTLD SHLIBS <--STACK] 113860f918efSJoseph Koshy * ^ ^ 113960f918efSJoseph Koshy * 0 VM_MAXUSER_ADDRESS 114049874f6eSJoseph Koshy 114160f918efSJoseph Koshy * 114260f918efSJoseph Koshy * The exact address where the loader gets mapped in 114360f918efSJoseph Koshy * will vary according to the size of the executable 114460f918efSJoseph Koshy * and the limits on the size of the process'es data 114560f918efSJoseph Koshy * segment at the time of exec(). The entry address 114660f918efSJoseph Koshy * recorded at process exec time corresponds to the 114760f918efSJoseph Koshy * 'start' address inside the dynamic linker. From 114860f918efSJoseph Koshy * this we can figure out the address where the 114960f918efSJoseph Koshy * runtime loader's file object had been mapped to. 115060f918efSJoseph Koshy */ 11510b86b1bbSFabien Thomas rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath, 0); 115249874f6eSJoseph Koshy if (rtldimage == NULL) { 115349874f6eSJoseph Koshy warnx("WARNING: Cannot find image for \"%s\".", 115449874f6eSJoseph Koshy pmcstat_string_unintern(image->pi_dynlinkerpath)); 115549874f6eSJoseph Koshy pmcstat_stats.ps_exec_errors++; 115649874f6eSJoseph Koshy return; 115749874f6eSJoseph Koshy } 115849874f6eSJoseph Koshy 115960f918efSJoseph Koshy if (rtldimage->pi_type == PMCSTAT_IMAGE_UNKNOWN) 11600b86b1bbSFabien Thomas pmcstat_image_get_elf_params(rtldimage); 116149874f6eSJoseph Koshy 116249874f6eSJoseph Koshy if (rtldimage->pi_type != PMCSTAT_IMAGE_ELF32 && 116349874f6eSJoseph Koshy rtldimage->pi_type != PMCSTAT_IMAGE_ELF64) { 116449874f6eSJoseph Koshy warnx("WARNING: rtld not an ELF object \"%s\".", 116549874f6eSJoseph Koshy pmcstat_string_unintern(image->pi_dynlinkerpath)); 116649874f6eSJoseph Koshy return; 116749874f6eSJoseph Koshy } 116860f918efSJoseph Koshy 116960f918efSJoseph Koshy libstart = entryaddr - rtldimage->pi_entry; 117049874f6eSJoseph Koshy pmcstat_image_link(pp, rtldimage, libstart); 117115139246SJoseph Koshy } 117215139246SJoseph Koshy } 117315139246SJoseph Koshy 117415139246SJoseph Koshy /* 117515139246SJoseph Koshy * Find the process descriptor corresponding to a PID. If 'allocate' 117615139246SJoseph Koshy * is zero, we return a NULL if a pid descriptor could not be found or 117715139246SJoseph Koshy * a process descriptor process. If 'allocate' is non-zero, then we 117815139246SJoseph Koshy * will attempt to allocate a fresh process descriptor. Zombie 117915139246SJoseph Koshy * process descriptors are only removed if a fresh allocation for the 118015139246SJoseph Koshy * same PID is requested. 118115139246SJoseph Koshy */ 118215139246SJoseph Koshy 118315139246SJoseph Koshy static struct pmcstat_process * 118415139246SJoseph Koshy pmcstat_process_lookup(pid_t pid, int allocate) 118515139246SJoseph Koshy { 118615139246SJoseph Koshy uint32_t hash; 118715139246SJoseph Koshy struct pmcstat_pcmap *ppm, *ppmtmp; 118815139246SJoseph Koshy struct pmcstat_process *pp, *pptmp; 118915139246SJoseph Koshy 119015139246SJoseph Koshy hash = (uint32_t) pid & PMCSTAT_HASH_MASK; /* simplicity wins */ 119115139246SJoseph Koshy 119215139246SJoseph Koshy LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[hash], pp_next, pptmp) 119315139246SJoseph Koshy if (pp->pp_pid == pid) { 119415139246SJoseph Koshy /* Found a descriptor, check and process zombies */ 119549874f6eSJoseph Koshy if (allocate && pp->pp_isactive == 0) { 119615139246SJoseph Koshy /* remove maps */ 119715139246SJoseph Koshy TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, 119815139246SJoseph Koshy ppmtmp) { 119915139246SJoseph Koshy TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 120015139246SJoseph Koshy free(ppm); 120115139246SJoseph Koshy } 120215139246SJoseph Koshy /* remove process entry */ 120315139246SJoseph Koshy LIST_REMOVE(pp, pp_next); 120415139246SJoseph Koshy free(pp); 120515139246SJoseph Koshy break; 120615139246SJoseph Koshy } 120749874f6eSJoseph Koshy return (pp); 120815139246SJoseph Koshy } 120915139246SJoseph Koshy 121015139246SJoseph Koshy if (!allocate) 121149874f6eSJoseph Koshy return (NULL); 121215139246SJoseph Koshy 121315139246SJoseph Koshy if ((pp = malloc(sizeof(*pp))) == NULL) 121415139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate pid descriptor"); 121515139246SJoseph Koshy 121615139246SJoseph Koshy pp->pp_pid = pid; 121715139246SJoseph Koshy pp->pp_isactive = 1; 121815139246SJoseph Koshy 121915139246SJoseph Koshy TAILQ_INIT(&pp->pp_map); 122015139246SJoseph Koshy 122115139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_process_hash[hash], pp, pp_next); 122249874f6eSJoseph Koshy return (pp); 122315139246SJoseph Koshy } 122415139246SJoseph Koshy 122515139246SJoseph Koshy /* 122615139246SJoseph Koshy * Associate an image and a process. 122715139246SJoseph Koshy */ 122815139246SJoseph Koshy 122915139246SJoseph Koshy static void 123049874f6eSJoseph Koshy pmcstat_process_exec(struct pmcstat_process *pp, 12310b86b1bbSFabien Thomas pmcstat_interned_string path, uintfptr_t entryaddr) 123215139246SJoseph Koshy { 123315139246SJoseph Koshy struct pmcstat_image *image; 123415139246SJoseph Koshy 123549874f6eSJoseph Koshy if ((image = pmcstat_image_from_path(path, 0)) == NULL) { 123649874f6eSJoseph Koshy pmcstat_stats.ps_exec_errors++; 123715139246SJoseph Koshy return; 123849874f6eSJoseph Koshy } 123915139246SJoseph Koshy 124015139246SJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 12410b86b1bbSFabien Thomas pmcstat_image_determine_type(image); 124215139246SJoseph Koshy 124349874f6eSJoseph Koshy assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); 124449874f6eSJoseph Koshy 124549874f6eSJoseph Koshy switch (image->pi_type) { 124649874f6eSJoseph Koshy case PMCSTAT_IMAGE_ELF32: 124749874f6eSJoseph Koshy case PMCSTAT_IMAGE_ELF64: 124849874f6eSJoseph Koshy pmcstat_stats.ps_exec_elf++; 12490b86b1bbSFabien Thomas pmcstat_process_elf_exec(pp, image, entryaddr); 125015139246SJoseph Koshy break; 125115139246SJoseph Koshy 125215139246SJoseph Koshy case PMCSTAT_IMAGE_AOUT: 125349874f6eSJoseph Koshy pmcstat_stats.ps_exec_aout++; 12540b86b1bbSFabien Thomas pmcstat_process_aout_exec(pp, image, entryaddr); 125549874f6eSJoseph Koshy break; 125649874f6eSJoseph Koshy 125749874f6eSJoseph Koshy case PMCSTAT_IMAGE_INDETERMINABLE: 125849874f6eSJoseph Koshy pmcstat_stats.ps_exec_indeterminable++; 125915139246SJoseph Koshy break; 126015139246SJoseph Koshy 126115139246SJoseph Koshy default: 126260f918efSJoseph Koshy err(EX_SOFTWARE, "ERROR: Unsupported executable type for " 126349874f6eSJoseph Koshy "\"%s\"", pmcstat_string_unintern(path)); 126415139246SJoseph Koshy } 126515139246SJoseph Koshy } 126615139246SJoseph Koshy 126715139246SJoseph Koshy 126860f918efSJoseph Koshy /* 126960f918efSJoseph Koshy * Find the map entry associated with process 'p' at PC value 'pc'. 127060f918efSJoseph Koshy */ 127160f918efSJoseph Koshy 12720b86b1bbSFabien Thomas struct pmcstat_pcmap * 127360f918efSJoseph Koshy pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc) 127460f918efSJoseph Koshy { 127560f918efSJoseph Koshy struct pmcstat_pcmap *ppm; 127660f918efSJoseph Koshy 127749874f6eSJoseph Koshy TAILQ_FOREACH(ppm, &p->pp_map, ppm_next) { 127860f918efSJoseph Koshy if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc) 127949874f6eSJoseph Koshy return (ppm); 128049874f6eSJoseph Koshy if (pc < ppm->ppm_lowpc) 128149874f6eSJoseph Koshy return (NULL); 128249874f6eSJoseph Koshy } 128360f918efSJoseph Koshy 128449874f6eSJoseph Koshy return (NULL); 128560f918efSJoseph Koshy } 128660f918efSJoseph Koshy 1287b6010f9eSJoseph Koshy /* 1288b6010f9eSJoseph Koshy * Convert a hwpmc(4) log to profile information. A system-wide 1289b6010f9eSJoseph Koshy * callgraph is generated if FLAG_DO_CALLGRAPHS is set. gmon.out 1290b6010f9eSJoseph Koshy * files usable by gprof(1) are created if FLAG_DO_GPROF is set. 1291b6010f9eSJoseph Koshy */ 1292b6010f9eSJoseph Koshy static int 12930b86b1bbSFabien Thomas pmcstat_analyze_log(void) 1294b6010f9eSJoseph Koshy { 1295b6010f9eSJoseph Koshy uint32_t cpu, cpuflags; 12960b86b1bbSFabien Thomas uintfptr_t pc; 129749874f6eSJoseph Koshy pid_t pid; 129849874f6eSJoseph Koshy struct pmcstat_image *image; 129915139246SJoseph Koshy struct pmcstat_process *pp, *ppnew; 130015139246SJoseph Koshy struct pmcstat_pcmap *ppm, *ppmtmp; 130115139246SJoseph Koshy struct pmclog_ev ev; 13020b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pmcr; 130349874f6eSJoseph Koshy pmcstat_interned_string image_path; 130415139246SJoseph Koshy 13050b86b1bbSFabien Thomas assert(args.pa_flags & FLAG_DO_ANALYSIS); 1306b6010f9eSJoseph Koshy 1307b6010f9eSJoseph Koshy if (elf_version(EV_CURRENT) == EV_NONE) 1308b6010f9eSJoseph Koshy err(EX_UNAVAILABLE, "Elf library intialization failed"); 1309b6010f9eSJoseph Koshy 13100b86b1bbSFabien Thomas while (pmclog_read(args.pa_logparser, &ev) == 0) { 131115139246SJoseph Koshy assert(ev.pl_state == PMCLOG_OK); 131215139246SJoseph Koshy 131315139246SJoseph Koshy switch (ev.pl_type) { 131449874f6eSJoseph Koshy case PMCLOG_TYPE_INITIALIZE: 131549874f6eSJoseph Koshy if ((ev.pl_u.pl_i.pl_version & 0xFF000000) != 13160b86b1bbSFabien Thomas PMC_VERSION_MAJOR << 24 && args.pa_verbosity > 0) 131749874f6eSJoseph Koshy warnx("WARNING: Log version 0x%x does not " 131849874f6eSJoseph Koshy "match compiled version 0x%x.", 131949874f6eSJoseph Koshy ev.pl_u.pl_i.pl_version, 132049874f6eSJoseph Koshy PMC_VERSION_MAJOR); 132149874f6eSJoseph Koshy break; 1322b6010f9eSJoseph Koshy 132349874f6eSJoseph Koshy case PMCLOG_TYPE_MAP_IN: 132415139246SJoseph Koshy /* 132515139246SJoseph Koshy * Introduce an address range mapping for a 132649874f6eSJoseph Koshy * userland process or the kernel (pid == -1). 132749874f6eSJoseph Koshy * 132849874f6eSJoseph Koshy * We always allocate a process descriptor so 132949874f6eSJoseph Koshy * that subsequent samples seen for this 133049874f6eSJoseph Koshy * address range are mapped to the current 133149874f6eSJoseph Koshy * object being mapped in. 133215139246SJoseph Koshy */ 133349874f6eSJoseph Koshy pid = ev.pl_u.pl_mi.pl_pid; 133449874f6eSJoseph Koshy if (pid == -1) 133549874f6eSJoseph Koshy pp = pmcstat_kernproc; 133649874f6eSJoseph Koshy else 133749874f6eSJoseph Koshy pp = pmcstat_process_lookup(pid, 133849874f6eSJoseph Koshy PMCSTAT_ALLOCATE); 133949874f6eSJoseph Koshy 134049874f6eSJoseph Koshy assert(pp != NULL); 134149874f6eSJoseph Koshy 134249874f6eSJoseph Koshy image_path = pmcstat_string_intern(ev.pl_u.pl_mi. 134349874f6eSJoseph Koshy pl_pathname); 134449874f6eSJoseph Koshy image = pmcstat_image_from_path(image_path, pid == -1); 134549874f6eSJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 13460b86b1bbSFabien Thomas pmcstat_image_determine_type(image); 134749874f6eSJoseph Koshy if (image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE) 134849874f6eSJoseph Koshy pmcstat_image_link(pp, image, 134949874f6eSJoseph Koshy ev.pl_u.pl_mi.pl_start); 135049874f6eSJoseph Koshy break; 135149874f6eSJoseph Koshy 135249874f6eSJoseph Koshy case PMCLOG_TYPE_MAP_OUT: 135349874f6eSJoseph Koshy /* 135449874f6eSJoseph Koshy * Remove an address map. 135549874f6eSJoseph Koshy */ 135649874f6eSJoseph Koshy pid = ev.pl_u.pl_mo.pl_pid; 135749874f6eSJoseph Koshy if (pid == -1) 135849874f6eSJoseph Koshy pp = pmcstat_kernproc; 135949874f6eSJoseph Koshy else 136049874f6eSJoseph Koshy pp = pmcstat_process_lookup(pid, 0); 136149874f6eSJoseph Koshy 136249874f6eSJoseph Koshy if (pp == NULL) /* unknown process */ 136349874f6eSJoseph Koshy break; 136449874f6eSJoseph Koshy 136549874f6eSJoseph Koshy pmcstat_image_unmap(pp, ev.pl_u.pl_mo.pl_start, 136649874f6eSJoseph Koshy ev.pl_u.pl_mo.pl_end); 136715139246SJoseph Koshy break; 136815139246SJoseph Koshy 136915139246SJoseph Koshy case PMCLOG_TYPE_PCSAMPLE: 1370b6010f9eSJoseph Koshy /* 1371b6010f9eSJoseph Koshy * Note: the `PCSAMPLE' log entry is not 1372b6010f9eSJoseph Koshy * generated by hpwmc(4) after version 2. 1373b6010f9eSJoseph Koshy */ 137415139246SJoseph Koshy 137515139246SJoseph Koshy /* 137615139246SJoseph Koshy * We bring in the gmon file for the image 137715139246SJoseph Koshy * currently associated with the PMC & pid 137815139246SJoseph Koshy * pair and increment the appropriate entry 137915139246SJoseph Koshy * bin inside this. 138015139246SJoseph Koshy */ 138149874f6eSJoseph Koshy pmcstat_stats.ps_samples_total++; 138249874f6eSJoseph Koshy 138315139246SJoseph Koshy pc = ev.pl_u.pl_s.pl_pc; 138449874f6eSJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_s.pl_pid, 138549874f6eSJoseph Koshy PMCSTAT_ALLOCATE); 138615139246SJoseph Koshy 13870b86b1bbSFabien Thomas /* Get PMC record. */ 13880b86b1bbSFabien Thomas pmcr = pmcstat_lookup_pmcid(ev.pl_u.pl_s.pl_pmcid); 13890b86b1bbSFabien Thomas assert(pmcr != NULL); 139015139246SJoseph Koshy 13910b86b1bbSFabien Thomas /* 13920b86b1bbSFabien Thomas * Call the plugins processing 13930b86b1bbSFabien Thomas * TODO: move pmcstat_process_find_map inside plugins 13940b86b1bbSFabien Thomas */ 13950b86b1bbSFabien Thomas 13960b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_process != NULL) 13970b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_process( 13980b86b1bbSFabien Thomas pp, pmcr, 1, &pc, 13990b86b1bbSFabien Thomas pmcstat_process_find_map(pp, pc) != NULL, 0); 14000b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_process( 14010b86b1bbSFabien Thomas pp, pmcr, 1, &pc, 14020b86b1bbSFabien Thomas pmcstat_process_find_map(pp, pc) != NULL, 0); 140315139246SJoseph Koshy break; 140415139246SJoseph Koshy 1405b6010f9eSJoseph Koshy case PMCLOG_TYPE_CALLCHAIN: 1406b6010f9eSJoseph Koshy pmcstat_stats.ps_samples_total++; 1407b6010f9eSJoseph Koshy 1408b6010f9eSJoseph Koshy cpuflags = ev.pl_u.pl_cc.pl_cpuflags; 1409b6010f9eSJoseph Koshy cpu = PMC_CALLCHAIN_CPUFLAGS_TO_CPU(cpuflags); 1410b6010f9eSJoseph Koshy 1411b6010f9eSJoseph Koshy /* Filter on the CPU id. */ 14120b86b1bbSFabien Thomas if ((args.pa_cpumask & (1 << cpu)) == 0) { 1413b6010f9eSJoseph Koshy pmcstat_stats.ps_samples_skipped++; 1414b6010f9eSJoseph Koshy break; 1415b6010f9eSJoseph Koshy } 1416b6010f9eSJoseph Koshy 1417b6010f9eSJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_cc.pl_pid, 1418b6010f9eSJoseph Koshy PMCSTAT_ALLOCATE); 1419b6010f9eSJoseph Koshy 14200b86b1bbSFabien Thomas /* Get PMC record. */ 14210b86b1bbSFabien Thomas pmcr = pmcstat_lookup_pmcid(ev.pl_u.pl_cc.pl_pmcid); 14220b86b1bbSFabien Thomas assert(pmcr != NULL); 1423b6010f9eSJoseph Koshy 14240b86b1bbSFabien Thomas /* 14250b86b1bbSFabien Thomas * Call the plugins processing 14260b86b1bbSFabien Thomas */ 1427b6010f9eSJoseph Koshy 14280b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_process != NULL) 14290b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_process( 14300b86b1bbSFabien Thomas pp, pmcr, 14310b86b1bbSFabien Thomas ev.pl_u.pl_cc.pl_npc, 14320b86b1bbSFabien Thomas ev.pl_u.pl_cc.pl_pc, 14330b86b1bbSFabien Thomas PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), 14340b86b1bbSFabien Thomas cpu); 14350b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_process( 14360b86b1bbSFabien Thomas pp, pmcr, 14370b86b1bbSFabien Thomas ev.pl_u.pl_cc.pl_npc, 14380b86b1bbSFabien Thomas ev.pl_u.pl_cc.pl_pc, 14390b86b1bbSFabien Thomas PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), 14400b86b1bbSFabien Thomas cpu); 1441b6010f9eSJoseph Koshy break; 1442b6010f9eSJoseph Koshy 144315139246SJoseph Koshy case PMCLOG_TYPE_PMCALLOCATE: 144415139246SJoseph Koshy /* 144515139246SJoseph Koshy * Record the association pmc id between this 144615139246SJoseph Koshy * PMC and its name. 144715139246SJoseph Koshy */ 144815139246SJoseph Koshy pmcstat_pmcid_add(ev.pl_u.pl_a.pl_pmcid, 14490b86b1bbSFabien Thomas pmcstat_string_intern(ev.pl_u.pl_a.pl_evname)); 145015139246SJoseph Koshy break; 145115139246SJoseph Koshy 145215139246SJoseph Koshy case PMCLOG_TYPE_PROCEXEC: 145315139246SJoseph Koshy 145415139246SJoseph Koshy /* 145515139246SJoseph Koshy * Change the executable image associated with 145615139246SJoseph Koshy * a process. 145715139246SJoseph Koshy */ 145849874f6eSJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_x.pl_pid, 145949874f6eSJoseph Koshy PMCSTAT_ALLOCATE); 146015139246SJoseph Koshy 146115139246SJoseph Koshy /* delete the current process map */ 146215139246SJoseph Koshy TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) { 146315139246SJoseph Koshy TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 146415139246SJoseph Koshy free(ppm); 146515139246SJoseph Koshy } 146615139246SJoseph Koshy 146749874f6eSJoseph Koshy /* associate this process image */ 146815139246SJoseph Koshy image_path = pmcstat_string_intern( 146915139246SJoseph Koshy ev.pl_u.pl_x.pl_pathname); 147049874f6eSJoseph Koshy assert(image_path != NULL); 147160f918efSJoseph Koshy pmcstat_process_exec(pp, image_path, 14720b86b1bbSFabien Thomas ev.pl_u.pl_x.pl_entryaddr); 147315139246SJoseph Koshy break; 147415139246SJoseph Koshy 147515139246SJoseph Koshy case PMCLOG_TYPE_PROCEXIT: 147615139246SJoseph Koshy 147715139246SJoseph Koshy /* 147815139246SJoseph Koshy * Due to the way the log is generated, the 147915139246SJoseph Koshy * last few samples corresponding to a process 148015139246SJoseph Koshy * may appear in the log after the process 148115139246SJoseph Koshy * exit event is recorded. Thus we keep the 148215139246SJoseph Koshy * process' descriptor and associated data 148315139246SJoseph Koshy * structures around, but mark the process as 148415139246SJoseph Koshy * having exited. 148515139246SJoseph Koshy */ 148615139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_e.pl_pid, 0); 148715139246SJoseph Koshy if (pp == NULL) 148815139246SJoseph Koshy break; 148949874f6eSJoseph Koshy pp->pp_isactive = 0; /* mark as a zombie */ 149015139246SJoseph Koshy break; 149115139246SJoseph Koshy 149215139246SJoseph Koshy case PMCLOG_TYPE_SYSEXIT: 149315139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_se.pl_pid, 0); 149415139246SJoseph Koshy if (pp == NULL) 149515139246SJoseph Koshy break; 149615139246SJoseph Koshy pp->pp_isactive = 0; /* make a zombie */ 149715139246SJoseph Koshy break; 149815139246SJoseph Koshy 149915139246SJoseph Koshy case PMCLOG_TYPE_PROCFORK: 150015139246SJoseph Koshy 150115139246SJoseph Koshy /* 150249874f6eSJoseph Koshy * Allocate a process descriptor for the new 150349874f6eSJoseph Koshy * (child) process. 150449874f6eSJoseph Koshy */ 150549874f6eSJoseph Koshy ppnew = 150649874f6eSJoseph Koshy pmcstat_process_lookup(ev.pl_u.pl_f.pl_newpid, 150749874f6eSJoseph Koshy PMCSTAT_ALLOCATE); 150849874f6eSJoseph Koshy 150949874f6eSJoseph Koshy /* 151049874f6eSJoseph Koshy * If we had been tracking the parent, clone 151149874f6eSJoseph Koshy * its address maps. 151215139246SJoseph Koshy */ 151315139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_f.pl_oldpid, 0); 151415139246SJoseph Koshy if (pp == NULL) 151515139246SJoseph Koshy break; 151615139246SJoseph Koshy TAILQ_FOREACH(ppm, &pp->pp_map, ppm_next) 151715139246SJoseph Koshy pmcstat_image_link(ppnew, ppm->ppm_image, 151849874f6eSJoseph Koshy ppm->ppm_lowpc); 151915139246SJoseph Koshy break; 152015139246SJoseph Koshy 152115139246SJoseph Koshy default: /* other types of entries are not relevant */ 152215139246SJoseph Koshy break; 152315139246SJoseph Koshy } 152415139246SJoseph Koshy } 152515139246SJoseph Koshy 152615139246SJoseph Koshy if (ev.pl_state == PMCLOG_EOF) 152749874f6eSJoseph Koshy return (PMCSTAT_FINISHED); 152815139246SJoseph Koshy else if (ev.pl_state == PMCLOG_REQUIRE_DATA) 152949874f6eSJoseph Koshy return (PMCSTAT_RUNNING); 153015139246SJoseph Koshy 153115139246SJoseph Koshy err(EX_DATAERR, "ERROR: event parsing failed (record %jd, " 153215139246SJoseph Koshy "offset 0x%jx)", (uintmax_t) ev.pl_count + 1, ev.pl_offset); 153315139246SJoseph Koshy } 153415139246SJoseph Koshy 153515139246SJoseph Koshy /* 153615139246SJoseph Koshy * Print log entries as text. 153715139246SJoseph Koshy */ 153815139246SJoseph Koshy 153949874f6eSJoseph Koshy static int 15400b86b1bbSFabien Thomas pmcstat_print_log(void) 154115139246SJoseph Koshy { 154215139246SJoseph Koshy struct pmclog_ev ev; 1543b6010f9eSJoseph Koshy uint32_t npc; 154415139246SJoseph Koshy 15450b86b1bbSFabien Thomas while (pmclog_read(args.pa_logparser, &ev) == 0) { 154615139246SJoseph Koshy assert(ev.pl_state == PMCLOG_OK); 154715139246SJoseph Koshy switch (ev.pl_type) { 1548b6010f9eSJoseph Koshy case PMCLOG_TYPE_CALLCHAIN: 15490b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("callchain", 1550b6010f9eSJoseph Koshy "%d 0x%x %d %d %c", ev.pl_u.pl_cc.pl_pid, 1551b6010f9eSJoseph Koshy ev.pl_u.pl_cc.pl_pmcid, 1552b6010f9eSJoseph Koshy PMC_CALLCHAIN_CPUFLAGS_TO_CPU(ev.pl_u.pl_cc. \ 1553b6010f9eSJoseph Koshy pl_cpuflags), ev.pl_u.pl_cc.pl_npc, 1554b6010f9eSJoseph Koshy PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(ev.pl_u.pl_cc.\ 1555b6010f9eSJoseph Koshy pl_cpuflags) ? 'u' : 's'); 1556b6010f9eSJoseph Koshy for (npc = 0; npc < ev.pl_u.pl_cc.pl_npc; npc++) 15570b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("...", "%p", 1558b6010f9eSJoseph Koshy (void *) ev.pl_u.pl_cc.pl_pc[npc]); 1559b6010f9eSJoseph Koshy break; 156015139246SJoseph Koshy case PMCLOG_TYPE_CLOSELOG: 15610b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("closelog",); 156215139246SJoseph Koshy break; 156315139246SJoseph Koshy case PMCLOG_TYPE_DROPNOTIFY: 15640b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("drop",); 156515139246SJoseph Koshy break; 156615139246SJoseph Koshy case PMCLOG_TYPE_INITIALIZE: 15670b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("initlog","0x%x \"%s\"", 156815139246SJoseph Koshy ev.pl_u.pl_i.pl_version, 156915139246SJoseph Koshy pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch)); 15703ab065b6SJoseph Koshy if ((ev.pl_u.pl_i.pl_version & 0xFF000000) != 15710b86b1bbSFabien Thomas PMC_VERSION_MAJOR << 24 && args.pa_verbosity > 0) 15723ab065b6SJoseph Koshy warnx("WARNING: Log version 0x%x != expected " 15733ab065b6SJoseph Koshy "version 0x%x.", ev.pl_u.pl_i.pl_version, 15743ab065b6SJoseph Koshy PMC_VERSION); 157515139246SJoseph Koshy break; 157649874f6eSJoseph Koshy case PMCLOG_TYPE_MAP_IN: 15770b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("map-in","%d %p \"%s\"", 157849874f6eSJoseph Koshy ev.pl_u.pl_mi.pl_pid, 157949874f6eSJoseph Koshy (void *) ev.pl_u.pl_mi.pl_start, 158049874f6eSJoseph Koshy ev.pl_u.pl_mi.pl_pathname); 158149874f6eSJoseph Koshy break; 158249874f6eSJoseph Koshy case PMCLOG_TYPE_MAP_OUT: 15830b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("map-out","%d %p %p", 158449874f6eSJoseph Koshy ev.pl_u.pl_mo.pl_pid, 158549874f6eSJoseph Koshy (void *) ev.pl_u.pl_mo.pl_start, 158649874f6eSJoseph Koshy (void *) ev.pl_u.pl_mo.pl_end); 158715139246SJoseph Koshy break; 158815139246SJoseph Koshy case PMCLOG_TYPE_PCSAMPLE: 15890b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("sample","0x%x %d %p %c", 159015139246SJoseph Koshy ev.pl_u.pl_s.pl_pmcid, 159115139246SJoseph Koshy ev.pl_u.pl_s.pl_pid, 159215139246SJoseph Koshy (void *) ev.pl_u.pl_s.pl_pc, 159315139246SJoseph Koshy ev.pl_u.pl_s.pl_usermode ? 'u' : 's'); 159415139246SJoseph Koshy break; 159515139246SJoseph Koshy case PMCLOG_TYPE_PMCALLOCATE: 15960b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("allocate","0x%x \"%s\" 0x%x", 159715139246SJoseph Koshy ev.pl_u.pl_a.pl_pmcid, 159815139246SJoseph Koshy ev.pl_u.pl_a.pl_evname, 159915139246SJoseph Koshy ev.pl_u.pl_a.pl_flags); 160015139246SJoseph Koshy break; 160115139246SJoseph Koshy case PMCLOG_TYPE_PMCATTACH: 16020b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("attach","0x%x %d \"%s\"", 160315139246SJoseph Koshy ev.pl_u.pl_t.pl_pmcid, 160415139246SJoseph Koshy ev.pl_u.pl_t.pl_pid, 160515139246SJoseph Koshy ev.pl_u.pl_t.pl_pathname); 160615139246SJoseph Koshy break; 160715139246SJoseph Koshy case PMCLOG_TYPE_PMCDETACH: 16080b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("detach","0x%x %d", 160915139246SJoseph Koshy ev.pl_u.pl_d.pl_pmcid, 161015139246SJoseph Koshy ev.pl_u.pl_d.pl_pid); 161115139246SJoseph Koshy break; 161215139246SJoseph Koshy case PMCLOG_TYPE_PROCCSW: 16130b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("cswval","0x%x %d %jd", 161415139246SJoseph Koshy ev.pl_u.pl_c.pl_pmcid, 161515139246SJoseph Koshy ev.pl_u.pl_c.pl_pid, 161615139246SJoseph Koshy ev.pl_u.pl_c.pl_value); 161715139246SJoseph Koshy break; 161815139246SJoseph Koshy case PMCLOG_TYPE_PROCEXEC: 16190b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("exec","0x%x %d %p \"%s\"", 162015139246SJoseph Koshy ev.pl_u.pl_x.pl_pmcid, 162115139246SJoseph Koshy ev.pl_u.pl_x.pl_pid, 162215139246SJoseph Koshy (void *) ev.pl_u.pl_x.pl_entryaddr, 162315139246SJoseph Koshy ev.pl_u.pl_x.pl_pathname); 162415139246SJoseph Koshy break; 162515139246SJoseph Koshy case PMCLOG_TYPE_PROCEXIT: 16260b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("exitval","0x%x %d %jd", 162715139246SJoseph Koshy ev.pl_u.pl_e.pl_pmcid, 162815139246SJoseph Koshy ev.pl_u.pl_e.pl_pid, 162915139246SJoseph Koshy ev.pl_u.pl_e.pl_value); 163015139246SJoseph Koshy break; 163115139246SJoseph Koshy case PMCLOG_TYPE_PROCFORK: 16320b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("fork","%d %d", 163315139246SJoseph Koshy ev.pl_u.pl_f.pl_oldpid, 163415139246SJoseph Koshy ev.pl_u.pl_f.pl_newpid); 163515139246SJoseph Koshy break; 163615139246SJoseph Koshy case PMCLOG_TYPE_USERDATA: 16370b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("userdata","0x%x", 163815139246SJoseph Koshy ev.pl_u.pl_u.pl_userdata); 163915139246SJoseph Koshy break; 164015139246SJoseph Koshy case PMCLOG_TYPE_SYSEXIT: 16410b86b1bbSFabien Thomas PMCSTAT_PRINT_ENTRY("exit","%d", 164215139246SJoseph Koshy ev.pl_u.pl_se.pl_pid); 164315139246SJoseph Koshy break; 164415139246SJoseph Koshy default: 16450b86b1bbSFabien Thomas fprintf(args.pa_printfile, "unknown event (type %d).\n", 164615139246SJoseph Koshy ev.pl_type); 164715139246SJoseph Koshy } 164815139246SJoseph Koshy } 164915139246SJoseph Koshy 165015139246SJoseph Koshy if (ev.pl_state == PMCLOG_EOF) 165149874f6eSJoseph Koshy return (PMCSTAT_FINISHED); 165215139246SJoseph Koshy else if (ev.pl_state == PMCLOG_REQUIRE_DATA) 165349874f6eSJoseph Koshy return (PMCSTAT_RUNNING); 165415139246SJoseph Koshy 16553ab065b6SJoseph Koshy errx(EX_DATAERR, "ERROR: event parsing failed " 16563ab065b6SJoseph Koshy "(record %jd, offset 0x%jx).", 165715139246SJoseph Koshy (uintmax_t) ev.pl_count + 1, ev.pl_offset); 165815139246SJoseph Koshy /*NOTREACHED*/ 165915139246SJoseph Koshy } 166015139246SJoseph Koshy 166115139246SJoseph Koshy /* 166249874f6eSJoseph Koshy * Public Interfaces. 166349874f6eSJoseph Koshy */ 166449874f6eSJoseph Koshy 166549874f6eSJoseph Koshy /* 166649874f6eSJoseph Koshy * Close a logfile, after first flushing all in-module queued data. 166749874f6eSJoseph Koshy */ 166849874f6eSJoseph Koshy 166949874f6eSJoseph Koshy int 16700b86b1bbSFabien Thomas pmcstat_close_log(void) 167149874f6eSJoseph Koshy { 1672b44906e5SFabien Thomas if (pmc_flush_logfile() < 0) 167349874f6eSJoseph Koshy err(EX_OSERR, "ERROR: logging failed"); 16740b86b1bbSFabien Thomas return (args.pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING : 167549874f6eSJoseph Koshy PMCSTAT_FINISHED); 167649874f6eSJoseph Koshy } 167749874f6eSJoseph Koshy 167849874f6eSJoseph Koshy 167949874f6eSJoseph Koshy 168049874f6eSJoseph Koshy /* 168149874f6eSJoseph Koshy * Open a log file, for reading or writing. 168249874f6eSJoseph Koshy * 168349874f6eSJoseph Koshy * The function returns the fd of a successfully opened log or -1 in 168449874f6eSJoseph Koshy * case of failure. 168549874f6eSJoseph Koshy */ 168649874f6eSJoseph Koshy 168749874f6eSJoseph Koshy int 168849874f6eSJoseph Koshy pmcstat_open_log(const char *path, int mode) 168949874f6eSJoseph Koshy { 1690302cbb90SJoseph Koshy int error, fd; 1691302cbb90SJoseph Koshy size_t hlen; 1692302cbb90SJoseph Koshy const char *p, *errstr; 1693302cbb90SJoseph Koshy struct addrinfo hints, *res, *res0; 1694302cbb90SJoseph Koshy char hostname[MAXHOSTNAMELEN]; 1695302cbb90SJoseph Koshy 1696302cbb90SJoseph Koshy errstr = NULL; 1697302cbb90SJoseph Koshy fd = -1; 169849874f6eSJoseph Koshy 169949874f6eSJoseph Koshy /* 170049874f6eSJoseph Koshy * If 'path' is "-" then open one of stdin or stdout depending 1701302cbb90SJoseph Koshy * on the value of 'mode'. 1702302cbb90SJoseph Koshy * 1703302cbb90SJoseph Koshy * If 'path' contains a ':' and does not start with a '/' or '.', 1704302cbb90SJoseph Koshy * and is being opened for writing, treat it as a "host:port" 1705302cbb90SJoseph Koshy * specification and open a network socket. 1706302cbb90SJoseph Koshy * 1707302cbb90SJoseph Koshy * Otherwise, treat 'path' as a file name and open that. 170849874f6eSJoseph Koshy */ 170949874f6eSJoseph Koshy if (path[0] == '-' && path[1] == '\0') 171049874f6eSJoseph Koshy fd = (mode == PMCSTAT_OPEN_FOR_READ) ? 0 : 1; 1711302cbb90SJoseph Koshy else if (mode == PMCSTAT_OPEN_FOR_WRITE && path[0] != '/' && 1712302cbb90SJoseph Koshy path[0] != '.' && strchr(path, ':') != NULL) { 1713302cbb90SJoseph Koshy 1714302cbb90SJoseph Koshy p = strrchr(path, ':'); 1715302cbb90SJoseph Koshy hlen = p - path; 1716302cbb90SJoseph Koshy if (p == path || hlen >= sizeof(hostname)) { 1717302cbb90SJoseph Koshy errstr = strerror(EINVAL); 1718302cbb90SJoseph Koshy goto done; 1719302cbb90SJoseph Koshy } 1720302cbb90SJoseph Koshy 1721302cbb90SJoseph Koshy assert(hlen < sizeof(hostname)); 1722302cbb90SJoseph Koshy (void) strncpy(hostname, path, hlen); 1723302cbb90SJoseph Koshy hostname[hlen] = '\0'; 1724302cbb90SJoseph Koshy 1725302cbb90SJoseph Koshy (void) memset(&hints, 0, sizeof(hints)); 1726302cbb90SJoseph Koshy hints.ai_family = AF_UNSPEC; 1727302cbb90SJoseph Koshy hints.ai_socktype = SOCK_STREAM; 1728302cbb90SJoseph Koshy if ((error = getaddrinfo(hostname, p+1, &hints, &res0)) != 0) { 1729302cbb90SJoseph Koshy errstr = gai_strerror(error); 1730302cbb90SJoseph Koshy goto done; 1731302cbb90SJoseph Koshy } 1732302cbb90SJoseph Koshy 1733302cbb90SJoseph Koshy fd = -1; 1734302cbb90SJoseph Koshy for (res = res0; res; res = res->ai_next) { 1735302cbb90SJoseph Koshy if ((fd = socket(res->ai_family, res->ai_socktype, 1736302cbb90SJoseph Koshy res->ai_protocol)) < 0) { 1737302cbb90SJoseph Koshy errstr = strerror(errno); 1738302cbb90SJoseph Koshy continue; 1739302cbb90SJoseph Koshy } 1740302cbb90SJoseph Koshy if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { 1741302cbb90SJoseph Koshy errstr = strerror(errno); 1742302cbb90SJoseph Koshy (void) close(fd); 1743302cbb90SJoseph Koshy fd = -1; 1744302cbb90SJoseph Koshy continue; 1745302cbb90SJoseph Koshy } 1746302cbb90SJoseph Koshy errstr = NULL; 1747302cbb90SJoseph Koshy break; 1748302cbb90SJoseph Koshy } 1749302cbb90SJoseph Koshy freeaddrinfo(res0); 1750302cbb90SJoseph Koshy 1751302cbb90SJoseph Koshy } else if ((fd = open(path, mode == PMCSTAT_OPEN_FOR_READ ? 175249874f6eSJoseph Koshy O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC), 1753302cbb90SJoseph Koshy S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) 1754302cbb90SJoseph Koshy errstr = strerror(errno); 1755302cbb90SJoseph Koshy 1756302cbb90SJoseph Koshy done: 1757302cbb90SJoseph Koshy if (errstr) 1758302cbb90SJoseph Koshy errx(EX_OSERR, "ERROR: Cannot open \"%s\" for %s: %s.", path, 1759302cbb90SJoseph Koshy (mode == PMCSTAT_OPEN_FOR_READ ? "reading" : "writing"), 1760302cbb90SJoseph Koshy errstr); 176149874f6eSJoseph Koshy 176249874f6eSJoseph Koshy return (fd); 176349874f6eSJoseph Koshy } 176449874f6eSJoseph Koshy 176549874f6eSJoseph Koshy /* 176615139246SJoseph Koshy * Process a log file in offline analysis mode. 176715139246SJoseph Koshy */ 176815139246SJoseph Koshy 1769dc1d9d2eSJoseph Koshy int 17700b86b1bbSFabien Thomas pmcstat_process_log(void) 177115139246SJoseph Koshy { 177215139246SJoseph Koshy 177315139246SJoseph Koshy /* 1774b6010f9eSJoseph Koshy * If analysis has not been asked for, just print the log to 1775b6010f9eSJoseph Koshy * the current output file. 177615139246SJoseph Koshy */ 17770b86b1bbSFabien Thomas if (args.pa_flags & FLAG_DO_PRINT) 17780b86b1bbSFabien Thomas return (pmcstat_print_log()); 177915139246SJoseph Koshy else 17800b86b1bbSFabien Thomas return (pmcstat_analyze_log()); 17810b86b1bbSFabien Thomas } 17820b86b1bbSFabien Thomas 17830b86b1bbSFabien Thomas /* 17840b86b1bbSFabien Thomas * Refresh top display. 17850b86b1bbSFabien Thomas */ 17860b86b1bbSFabien Thomas 17870b86b1bbSFabien Thomas static void 17880b86b1bbSFabien Thomas pmcstat_refresh_top(void) 17890b86b1bbSFabien Thomas { 17900b86b1bbSFabien Thomas char pmcname[40]; 17919ba84a88SFabien Thomas const char *s; 17920b86b1bbSFabien Thomas 17930b86b1bbSFabien Thomas /* If in pause mode do not refresh display. */ 17940b86b1bbSFabien Thomas if (pmcstat_pause) 17950b86b1bbSFabien Thomas return; 17960b86b1bbSFabien Thomas 17979ba84a88SFabien Thomas /* Wait until PMC pop in the log. */ 17989ba84a88SFabien Thomas s = pmcstat_pmcindex_to_name(pmcstat_pmcinfilter); 17999ba84a88SFabien Thomas if (s == NULL) 18009ba84a88SFabien Thomas return; 18019ba84a88SFabien Thomas 18020b86b1bbSFabien Thomas /* Format PMC name. */ 18030b86b1bbSFabien Thomas if (pmcstat_mergepmc) 18049ba84a88SFabien Thomas snprintf(pmcname, sizeof(pmcname), "[%s]", s); 18050b86b1bbSFabien Thomas else 18060b86b1bbSFabien Thomas snprintf(pmcname, sizeof(pmcname), "%s.%d", 18079ba84a88SFabien Thomas s, pmcstat_pmcinfilter); 18080b86b1bbSFabien Thomas 18090b86b1bbSFabien Thomas PMCSTAT_PRINTBEGIN(); 18100b86b1bbSFabien Thomas PMCSTAT_PRINTW("PMC: %s Samples: %u processed, %u invalid\n\n", 18110b86b1bbSFabien Thomas pmcname, 18120b86b1bbSFabien Thomas pmcstat_stats.ps_samples_total, 18130b86b1bbSFabien Thomas pmcstat_stats.ps_samples_unknown_offset + 18140b86b1bbSFabien Thomas pmcstat_stats.ps_samples_indeterminable + 18150b86b1bbSFabien Thomas pmcstat_stats.ps_callchain_dubious_frames); 18160b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_topdisplay != NULL) 18170b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_topdisplay(); 18180b86b1bbSFabien Thomas PMCSTAT_PRINTEND(); 18190b86b1bbSFabien Thomas } 18200b86b1bbSFabien Thomas 18210b86b1bbSFabien Thomas /* 18220b86b1bbSFabien Thomas * Find the next pmc index to display. 18230b86b1bbSFabien Thomas */ 18240b86b1bbSFabien Thomas 18250b86b1bbSFabien Thomas static void 18260b86b1bbSFabien Thomas pmcstat_changefilter(void) 18270b86b1bbSFabien Thomas { 18280b86b1bbSFabien Thomas int pmcin; 18290b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pmcr; 18300b86b1bbSFabien Thomas 18310b86b1bbSFabien Thomas /* 18320b86b1bbSFabien Thomas * Find the next merge target. 18330b86b1bbSFabien Thomas */ 18340b86b1bbSFabien Thomas if (pmcstat_mergepmc) { 18350b86b1bbSFabien Thomas pmcin = pmcstat_pmcinfilter; 18360b86b1bbSFabien Thomas 18370b86b1bbSFabien Thomas do { 18380b86b1bbSFabien Thomas pmcr = pmcstat_pmcindex_to_pmcr(pmcstat_pmcinfilter); 18390b86b1bbSFabien Thomas if (pmcr == pmcr->pr_merge) 18400b86b1bbSFabien Thomas break; 18410b86b1bbSFabien Thomas 18420b86b1bbSFabien Thomas pmcstat_pmcinfilter++; 18430b86b1bbSFabien Thomas if (pmcstat_pmcinfilter >= pmcstat_npmcs) 18440b86b1bbSFabien Thomas pmcstat_pmcinfilter = 0; 18450b86b1bbSFabien Thomas 18460b86b1bbSFabien Thomas } while (pmcstat_pmcinfilter != pmcin); 18470b86b1bbSFabien Thomas } 18480b86b1bbSFabien Thomas } 18490b86b1bbSFabien Thomas 18500b86b1bbSFabien Thomas /* 18510b86b1bbSFabien Thomas * Top mode keypress. 18520b86b1bbSFabien Thomas */ 18530b86b1bbSFabien Thomas 18540b86b1bbSFabien Thomas int 18550b86b1bbSFabien Thomas pmcstat_keypress_log(void) 18560b86b1bbSFabien Thomas { 18570b86b1bbSFabien Thomas int c, ret = 0; 18580b86b1bbSFabien Thomas WINDOW *w; 18590b86b1bbSFabien Thomas 18600b86b1bbSFabien Thomas w = newwin(1, 0, 1, 0); 18610b86b1bbSFabien Thomas c = wgetch(w); 18620b86b1bbSFabien Thomas wprintw(w, "Key: %c => ", c); 18630b86b1bbSFabien Thomas switch (c) { 18640b86b1bbSFabien Thomas case 'c': 18650b86b1bbSFabien Thomas wprintw(w, "enter mode 'd' or 'a' => "); 18660b86b1bbSFabien Thomas c = wgetch(w); 18670b86b1bbSFabien Thomas if (c == 'd') { 18680b86b1bbSFabien Thomas args.pa_topmode = PMCSTAT_TOP_DELTA; 18690b86b1bbSFabien Thomas wprintw(w, "switching to delta mode"); 18700b86b1bbSFabien Thomas } else { 18710b86b1bbSFabien Thomas args.pa_topmode = PMCSTAT_TOP_ACCUM; 18720b86b1bbSFabien Thomas wprintw(w, "switching to accumulation mode"); 18730b86b1bbSFabien Thomas } 18740b86b1bbSFabien Thomas break; 18750b86b1bbSFabien Thomas case 'm': 18760b86b1bbSFabien Thomas pmcstat_mergepmc = !pmcstat_mergepmc; 18770b86b1bbSFabien Thomas /* 18780b86b1bbSFabien Thomas * Changing merge state require data reset. 18790b86b1bbSFabien Thomas */ 18800b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_shutdown != NULL) 18810b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_shutdown(NULL); 18820b86b1bbSFabien Thomas bzero(&pmcstat_stats, sizeof(struct pmcstat_stats)); 18830b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_init != NULL) 18840b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_init(); 18850b86b1bbSFabien Thomas 18860b86b1bbSFabien Thomas /* Update filter to be on a merge target. */ 18870b86b1bbSFabien Thomas pmcstat_changefilter(); 18880b86b1bbSFabien Thomas wprintw(w, "merge PMC %s", pmcstat_mergepmc ? "on" : "off"); 18890b86b1bbSFabien Thomas break; 18900b86b1bbSFabien Thomas case 'n': 18910b86b1bbSFabien Thomas /* Close current plugin. */ 18920b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_shutdown != NULL) 18930b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_shutdown(NULL); 18940b86b1bbSFabien Thomas 18950b86b1bbSFabien Thomas /* Find next top display available. */ 18960b86b1bbSFabien Thomas do { 18970b86b1bbSFabien Thomas args.pa_plugin++; 18980b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_name == NULL) 18990b86b1bbSFabien Thomas args.pa_plugin = 0; 19000b86b1bbSFabien Thomas } while (plugins[args.pa_plugin].pl_topdisplay == NULL); 19010b86b1bbSFabien Thomas 19020b86b1bbSFabien Thomas /* Open new plugin. */ 19030b86b1bbSFabien Thomas bzero(&pmcstat_stats, sizeof(struct pmcstat_stats)); 19040b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_init != NULL) 19050b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_init(); 19060b86b1bbSFabien Thomas wprintw(w, "switching to plugin %s", 19070b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_name); 19080b86b1bbSFabien Thomas break; 19090b86b1bbSFabien Thomas case 'p': 19100b86b1bbSFabien Thomas pmcstat_pmcinfilter++; 19110b86b1bbSFabien Thomas if (pmcstat_pmcinfilter >= pmcstat_npmcs) 19120b86b1bbSFabien Thomas pmcstat_pmcinfilter = 0; 19130b86b1bbSFabien Thomas pmcstat_changefilter(); 19140b86b1bbSFabien Thomas wprintw(w, "switching to PMC %s.%d", 19150b86b1bbSFabien Thomas pmcstat_pmcindex_to_name(pmcstat_pmcinfilter), 19160b86b1bbSFabien Thomas pmcstat_pmcinfilter); 19170b86b1bbSFabien Thomas break; 19180b86b1bbSFabien Thomas case ' ': 19190b86b1bbSFabien Thomas pmcstat_pause = !pmcstat_pause; 19200b86b1bbSFabien Thomas if (pmcstat_pause) 19210b86b1bbSFabien Thomas wprintw(w, "pause => press space again to continue"); 19220b86b1bbSFabien Thomas break; 19230b86b1bbSFabien Thomas case 'q': 19240b86b1bbSFabien Thomas wprintw(w, "exiting..."); 19250b86b1bbSFabien Thomas ret = 1; 19260b86b1bbSFabien Thomas default: 19270b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_topkeypress != NULL) 19280b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_topkeypress(c, w)) 19290b86b1bbSFabien Thomas ret = 1; 19300b86b1bbSFabien Thomas } 19310b86b1bbSFabien Thomas 19320b86b1bbSFabien Thomas wrefresh(w); 19330b86b1bbSFabien Thomas delwin(w); 19340b86b1bbSFabien Thomas return ret; 19350b86b1bbSFabien Thomas } 19360b86b1bbSFabien Thomas 19370b86b1bbSFabien Thomas 19380b86b1bbSFabien Thomas /* 19390b86b1bbSFabien Thomas * Top mode display. 19400b86b1bbSFabien Thomas */ 19410b86b1bbSFabien Thomas 19420b86b1bbSFabien Thomas void 19430b86b1bbSFabien Thomas pmcstat_display_log(void) 19440b86b1bbSFabien Thomas { 19450b86b1bbSFabien Thomas 19460b86b1bbSFabien Thomas pmcstat_refresh_top(); 19470b86b1bbSFabien Thomas 19480b86b1bbSFabien Thomas /* Reset everythings if delta mode. */ 19490b86b1bbSFabien Thomas if (args.pa_topmode == PMCSTAT_TOP_DELTA) { 19500b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_shutdown != NULL) 19510b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_shutdown(NULL); 19520b86b1bbSFabien Thomas bzero(&pmcstat_stats, sizeof(struct pmcstat_stats)); 19530b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_init != NULL) 19540b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_init(); 19550b86b1bbSFabien Thomas } 19560b86b1bbSFabien Thomas 19570b86b1bbSFabien Thomas } 19580b86b1bbSFabien Thomas 19590b86b1bbSFabien Thomas /* 19600b86b1bbSFabien Thomas * Configure a plugins. 19610b86b1bbSFabien Thomas */ 19620b86b1bbSFabien Thomas 19630b86b1bbSFabien Thomas void 19640b86b1bbSFabien Thomas pmcstat_pluginconfigure_log(char *opt) 19650b86b1bbSFabien Thomas { 19660b86b1bbSFabien Thomas 19670b86b1bbSFabien Thomas if (strncmp(opt, "threshold=", 10) == 0) { 19680b86b1bbSFabien Thomas pmcstat_threshold = atof(opt+10); 19690b86b1bbSFabien Thomas } else { 19700b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_configure != NULL) { 19710b86b1bbSFabien Thomas if (!plugins[args.pa_plugin].pl_configure(opt)) 19720b86b1bbSFabien Thomas err(EX_USAGE, 19730b86b1bbSFabien Thomas "ERROR: unknown option <%s>.", opt); 19740b86b1bbSFabien Thomas } 19750b86b1bbSFabien Thomas } 197615139246SJoseph Koshy } 197715139246SJoseph Koshy 197849874f6eSJoseph Koshy /* 197949874f6eSJoseph Koshy * Initialize module. 198049874f6eSJoseph Koshy */ 198149874f6eSJoseph Koshy 198215139246SJoseph Koshy void 19830b86b1bbSFabien Thomas pmcstat_initialize_logging(void) 198415139246SJoseph Koshy { 198560f918efSJoseph Koshy int i; 198649874f6eSJoseph Koshy 198715139246SJoseph Koshy /* use a convenient format for 'ldd' output */ 1988f3c8200eSJoseph Koshy if (setenv("LD_TRACE_LOADED_OBJECTS_FMT1","%o \"%p\" %x\n",1) != 0) 198949874f6eSJoseph Koshy err(EX_OSERR, "ERROR: Cannot setenv"); 199015139246SJoseph Koshy 199115139246SJoseph Koshy /* Initialize hash tables */ 199249874f6eSJoseph Koshy pmcstat_string_initialize(); 199315139246SJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) { 199415139246SJoseph Koshy LIST_INIT(&pmcstat_image_hash[i]); 199515139246SJoseph Koshy LIST_INIT(&pmcstat_process_hash[i]); 199615139246SJoseph Koshy } 199715139246SJoseph Koshy 199849874f6eSJoseph Koshy /* 199949874f6eSJoseph Koshy * Create a fake 'process' entry for the kernel with pid -1. 200049874f6eSJoseph Koshy * hwpmc(4) will subsequently inform us about where the kernel 200149874f6eSJoseph Koshy * and any loaded kernel modules are mapped. 200249874f6eSJoseph Koshy */ 200349874f6eSJoseph Koshy if ((pmcstat_kernproc = pmcstat_process_lookup((pid_t) -1, 200449874f6eSJoseph Koshy PMCSTAT_ALLOCATE)) == NULL) 200515139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot initialize logging"); 20060b86b1bbSFabien Thomas 20070b86b1bbSFabien Thomas /* PMC count. */ 20080b86b1bbSFabien Thomas pmcstat_npmcs = 0; 20090b86b1bbSFabien Thomas 20100b86b1bbSFabien Thomas /* Merge PMC with same name. */ 20110b86b1bbSFabien Thomas pmcstat_mergepmc = args.pa_mergepmc; 20120b86b1bbSFabien Thomas 20130b86b1bbSFabien Thomas /* 20140b86b1bbSFabien Thomas * Initialize plugins 20150b86b1bbSFabien Thomas */ 20160b86b1bbSFabien Thomas 20170b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_init != NULL) 20180b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_init(); 20190b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_init != NULL) 20200b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_init(); 202115139246SJoseph Koshy } 202215139246SJoseph Koshy 202349874f6eSJoseph Koshy /* 202449874f6eSJoseph Koshy * Shutdown module. 202549874f6eSJoseph Koshy */ 202649874f6eSJoseph Koshy 202715139246SJoseph Koshy void 20280b86b1bbSFabien Thomas pmcstat_shutdown_logging(void) 202915139246SJoseph Koshy { 203015139246SJoseph Koshy int i; 203149874f6eSJoseph Koshy FILE *mf; 203215139246SJoseph Koshy struct pmcstat_image *pi, *pitmp; 203315139246SJoseph Koshy struct pmcstat_process *pp, *pptmp; 20340b86b1bbSFabien Thomas struct pmcstat_pcmap *ppm, *ppmtmp; 203549874f6eSJoseph Koshy 203649874f6eSJoseph Koshy /* determine where to send the map file */ 203749874f6eSJoseph Koshy mf = NULL; 20380b86b1bbSFabien Thomas if (args.pa_mapfilename != NULL) 20390b86b1bbSFabien Thomas mf = (strcmp(args.pa_mapfilename, "-") == 0) ? 20400b86b1bbSFabien Thomas args.pa_printfile : fopen(args.pa_mapfilename, "w"); 204149874f6eSJoseph Koshy 20420b86b1bbSFabien Thomas if (mf == NULL && args.pa_flags & FLAG_DO_GPROF && 20430b86b1bbSFabien Thomas args.pa_verbosity >= 2) 20440b86b1bbSFabien Thomas mf = args.pa_printfile; 204549874f6eSJoseph Koshy 204649874f6eSJoseph Koshy if (mf) 204749874f6eSJoseph Koshy (void) fprintf(mf, "MAP:\n"); 204815139246SJoseph Koshy 2049b6010f9eSJoseph Koshy /* 20500b86b1bbSFabien Thomas * Shutdown the plugins 2051b6010f9eSJoseph Koshy */ 20522a6d2e9cSJoseph Koshy 20530b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_shutdown != NULL) 20540b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_shutdown(mf); 20550b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_shutdown != NULL) 20560b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_shutdown(mf); 2057b6010f9eSJoseph Koshy 2058b6010f9eSJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) { 20590b86b1bbSFabien Thomas LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next, 20600b86b1bbSFabien Thomas pitmp) { 20610b86b1bbSFabien Thomas if (plugins[args.pa_plugin].pl_shutdownimage != NULL) 20620b86b1bbSFabien Thomas plugins[args.pa_plugin].pl_shutdownimage(pi); 20630b86b1bbSFabien Thomas if (plugins[args.pa_pplugin].pl_shutdownimage != NULL) 20640b86b1bbSFabien Thomas plugins[args.pa_pplugin].pl_shutdownimage(pi); 20650b86b1bbSFabien Thomas 2066b6010f9eSJoseph Koshy free(pi->pi_symbols); 20670b86b1bbSFabien Thomas if (pi->pi_addr2line != NULL) 20680b86b1bbSFabien Thomas pclose(pi->pi_addr2line); 206915139246SJoseph Koshy LIST_REMOVE(pi, pi_next); 207015139246SJoseph Koshy free(pi); 207115139246SJoseph Koshy } 2072b6010f9eSJoseph Koshy 207315139246SJoseph Koshy LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[i], pp_next, 207415139246SJoseph Koshy pptmp) { 20750b86b1bbSFabien Thomas TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) { 20760b86b1bbSFabien Thomas TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 20770b86b1bbSFabien Thomas free(ppm); 20780b86b1bbSFabien Thomas } 207915139246SJoseph Koshy LIST_REMOVE(pp, pp_next); 208015139246SJoseph Koshy free(pp); 208115139246SJoseph Koshy } 208215139246SJoseph Koshy } 208349874f6eSJoseph Koshy 208449874f6eSJoseph Koshy pmcstat_string_shutdown(); 208549874f6eSJoseph Koshy 208649874f6eSJoseph Koshy /* 208749874f6eSJoseph Koshy * Print errors unless -q was specified. Print all statistics 208849874f6eSJoseph Koshy * if verbosity > 1. 208949874f6eSJoseph Koshy */ 20900b86b1bbSFabien Thomas #define PRINT(N,V) do { \ 20910b86b1bbSFabien Thomas if (pmcstat_stats.ps_##V || args.pa_verbosity >= 2) \ 20920b86b1bbSFabien Thomas (void) fprintf(args.pa_printfile, " %-40s %d\n",\ 209349874f6eSJoseph Koshy N, pmcstat_stats.ps_##V); \ 209449874f6eSJoseph Koshy } while (0) 209549874f6eSJoseph Koshy 20960b86b1bbSFabien Thomas if (args.pa_verbosity >= 1 && (args.pa_flags & FLAG_DO_ANALYSIS) && 20970b86b1bbSFabien Thomas (args.pa_flags & FLAG_DO_TOP) == 0) { 20980b86b1bbSFabien Thomas (void) fprintf(args.pa_printfile, "CONVERSION STATISTICS:\n"); 20990b86b1bbSFabien Thomas PRINT("#exec/a.out", exec_aout); 21000b86b1bbSFabien Thomas PRINT("#exec/elf", exec_elf); 21010b86b1bbSFabien Thomas PRINT("#exec/unknown", exec_indeterminable); 21020b86b1bbSFabien Thomas PRINT("#exec handling errors", exec_errors); 21030b86b1bbSFabien Thomas PRINT("#samples/total", samples_total); 21040b86b1bbSFabien Thomas PRINT("#samples/unclaimed", samples_unknown_offset); 21050b86b1bbSFabien Thomas PRINT("#samples/unknown-object", samples_indeterminable); 21060b86b1bbSFabien Thomas PRINT("#callchain/dubious-frames", callchain_dubious_frames); 210715139246SJoseph Koshy } 210849874f6eSJoseph Koshy 210949874f6eSJoseph Koshy if (mf) 211049874f6eSJoseph Koshy (void) fclose(mf); 211115139246SJoseph Koshy } 2112