10b86b1bbSFabien Thomas /*- 20b86b1bbSFabien Thomas * Copyright (c) 2005-2007, Joseph Koshy 30b86b1bbSFabien Thomas * Copyright (c) 2007 The FreeBSD Foundation 40b86b1bbSFabien Thomas * Copyright (c) 2009, Fabien Thomas 50b86b1bbSFabien Thomas * All rights reserved. 60b86b1bbSFabien Thomas * 70b86b1bbSFabien Thomas * Portions of this software were developed by A. Joseph Koshy under 80b86b1bbSFabien Thomas * sponsorship from the FreeBSD Foundation and Google, Inc. 90b86b1bbSFabien Thomas * 100b86b1bbSFabien Thomas * Redistribution and use in source and binary forms, with or without 110b86b1bbSFabien Thomas * modification, are permitted provided that the following conditions 120b86b1bbSFabien Thomas * are met: 130b86b1bbSFabien Thomas * 1. Redistributions of source code must retain the above copyright 140b86b1bbSFabien Thomas * notice, this list of conditions and the following disclaimer. 150b86b1bbSFabien Thomas * 2. Redistributions in binary form must reproduce the above copyright 160b86b1bbSFabien Thomas * notice, this list of conditions and the following disclaimer in the 170b86b1bbSFabien Thomas * documentation and/or other materials provided with the distribution. 180b86b1bbSFabien Thomas * 190b86b1bbSFabien Thomas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 200b86b1bbSFabien Thomas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 210b86b1bbSFabien Thomas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 220b86b1bbSFabien Thomas * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 230b86b1bbSFabien Thomas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 240b86b1bbSFabien Thomas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 250b86b1bbSFabien Thomas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 260b86b1bbSFabien Thomas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 270b86b1bbSFabien Thomas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 280b86b1bbSFabien Thomas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 290b86b1bbSFabien Thomas * SUCH DAMAGE. 300b86b1bbSFabien Thomas */ 310b86b1bbSFabien Thomas 320b86b1bbSFabien Thomas /* 330b86b1bbSFabien Thomas * Transform a hwpmc(4) log into human readable form, and into 340b86b1bbSFabien Thomas * gprof(1) compatible profiles. 350b86b1bbSFabien Thomas */ 360b86b1bbSFabien Thomas 370b86b1bbSFabien Thomas #include <sys/cdefs.h> 380b86b1bbSFabien Thomas __FBSDID("$FreeBSD$"); 390b86b1bbSFabien Thomas 400b86b1bbSFabien Thomas #include <sys/param.h> 410b86b1bbSFabien Thomas #include <sys/endian.h> 420b86b1bbSFabien Thomas #include <sys/gmon.h> 430b86b1bbSFabien Thomas #include <sys/imgact_aout.h> 440b86b1bbSFabien Thomas #include <sys/imgact_elf.h> 450b86b1bbSFabien Thomas #include <sys/mman.h> 460b86b1bbSFabien Thomas #include <sys/pmc.h> 470b86b1bbSFabien Thomas #include <sys/queue.h> 480b86b1bbSFabien Thomas #include <sys/socket.h> 490b86b1bbSFabien Thomas #include <sys/stat.h> 500b86b1bbSFabien Thomas #include <sys/wait.h> 510b86b1bbSFabien Thomas 520b86b1bbSFabien Thomas #include <netinet/in.h> 530b86b1bbSFabien Thomas 540b86b1bbSFabien Thomas #include <assert.h> 550b86b1bbSFabien Thomas #include <curses.h> 560b86b1bbSFabien Thomas #include <err.h> 570b86b1bbSFabien Thomas #include <errno.h> 580b86b1bbSFabien Thomas #include <fcntl.h> 590b86b1bbSFabien Thomas #include <gelf.h> 600b86b1bbSFabien Thomas #include <libgen.h> 610b86b1bbSFabien Thomas #include <limits.h> 620b86b1bbSFabien Thomas #include <netdb.h> 630b86b1bbSFabien Thomas #include <pmc.h> 640b86b1bbSFabien Thomas #include <pmclog.h> 650b86b1bbSFabien Thomas #include <sysexits.h> 660b86b1bbSFabien Thomas #include <stdint.h> 670b86b1bbSFabien Thomas #include <stdio.h> 680b86b1bbSFabien Thomas #include <stdlib.h> 690b86b1bbSFabien Thomas #include <string.h> 700b86b1bbSFabien Thomas #include <unistd.h> 710b86b1bbSFabien Thomas 720b86b1bbSFabien Thomas #include "pmcstat.h" 730b86b1bbSFabien Thomas #include "pmcstat_log.h" 740b86b1bbSFabien Thomas #include "pmcpl_callgraph.h" 750b86b1bbSFabien Thomas #include "pmcpl_gprof.h" 760b86b1bbSFabien Thomas 77*465dadb5SJonathan T. Looney typedef uint64_t WIDEHISTCOUNTER; 78*465dadb5SJonathan T. Looney 79*465dadb5SJonathan T. Looney #define WIDEHISTCOUNTER_MAX UINT64_MAX 80*465dadb5SJonathan T. Looney #define HISTCOUNTER_MAX USHRT_MAX 81*465dadb5SJonathan T. Looney #define WIDEHISTCOUNTER_GMONTYPE ((int) 64) 82*465dadb5SJonathan T. Looney #define HISTCOUNTER_GMONTYPE ((int) 0) 83*465dadb5SJonathan T. Looney static int hc_sz=0; 84*465dadb5SJonathan T. Looney 850b86b1bbSFabien Thomas /* 860b86b1bbSFabien Thomas * struct pmcstat_gmonfile tracks a given 'gmon.out' file. These 870b86b1bbSFabien Thomas * files are mmap()'ed in as needed. 880b86b1bbSFabien Thomas */ 890b86b1bbSFabien Thomas 900b86b1bbSFabien Thomas struct pmcstat_gmonfile { 910b86b1bbSFabien Thomas LIST_ENTRY(pmcstat_gmonfile) pgf_next; /* list of entries */ 920b86b1bbSFabien Thomas int pgf_overflow; /* whether a count overflowed */ 930b86b1bbSFabien Thomas pmc_id_t pgf_pmcid; /* id of the associated pmc */ 940b86b1bbSFabien Thomas size_t pgf_nbuckets; /* #buckets in this gmon.out */ 950b86b1bbSFabien Thomas unsigned int pgf_nsamples; /* #samples in this gmon.out */ 960b86b1bbSFabien Thomas pmcstat_interned_string pgf_name; /* pathname of gmon.out file */ 970b86b1bbSFabien Thomas size_t pgf_ndatabytes; /* number of bytes mapped */ 980b86b1bbSFabien Thomas void *pgf_gmondata; /* pointer to mmap'ed data */ 990b86b1bbSFabien Thomas FILE *pgf_file; /* used when writing gmon arcs */ 1000b86b1bbSFabien Thomas }; 1010b86b1bbSFabien Thomas 1020b86b1bbSFabien Thomas /* 1030b86b1bbSFabien Thomas * Prototypes 1040b86b1bbSFabien Thomas */ 1050b86b1bbSFabien Thomas 1060b86b1bbSFabien Thomas static void pmcstat_gmon_create_file(struct pmcstat_gmonfile *_pgf, 1070b86b1bbSFabien Thomas struct pmcstat_image *_image); 1080b86b1bbSFabien Thomas static pmcstat_interned_string pmcstat_gmon_create_name(const char *_sd, 1090b86b1bbSFabien Thomas struct pmcstat_image *_img, pmc_id_t _pmcid); 1100b86b1bbSFabien Thomas static void pmcstat_gmon_map_file(struct pmcstat_gmonfile *_pgf); 1110b86b1bbSFabien Thomas static void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf); 1120b86b1bbSFabien Thomas 1130b86b1bbSFabien Thomas static struct pmcstat_gmonfile *pmcstat_image_find_gmonfile(struct 1140b86b1bbSFabien Thomas pmcstat_image *_i, pmc_id_t _id); 1150b86b1bbSFabien Thomas 1160b86b1bbSFabien Thomas /* 1170b86b1bbSFabien Thomas * Create a gmon.out file and size it. 1180b86b1bbSFabien Thomas */ 1190b86b1bbSFabien Thomas 1200b86b1bbSFabien Thomas static void 1210b86b1bbSFabien Thomas pmcstat_gmon_create_file(struct pmcstat_gmonfile *pgf, 1220b86b1bbSFabien Thomas struct pmcstat_image *image) 1230b86b1bbSFabien Thomas { 1240b86b1bbSFabien Thomas int fd; 1250b86b1bbSFabien Thomas size_t count; 1260b86b1bbSFabien Thomas struct gmonhdr gm; 1270b86b1bbSFabien Thomas const char *pathname; 1280b86b1bbSFabien Thomas char buffer[DEFAULT_BUFFER_SIZE]; 1290b86b1bbSFabien Thomas 1300b86b1bbSFabien Thomas pathname = pmcstat_string_unintern(pgf->pgf_name); 1310b86b1bbSFabien Thomas if ((fd = open(pathname, O_RDWR|O_NOFOLLOW|O_CREAT, 1320b86b1bbSFabien Thomas S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) 1330b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: Cannot open \"%s\"", pathname); 1340b86b1bbSFabien Thomas 1350b86b1bbSFabien Thomas gm.lpc = image->pi_start; 1360b86b1bbSFabien Thomas gm.hpc = image->pi_end; 137*465dadb5SJonathan T. Looney gm.ncnt = (pgf->pgf_nbuckets * hc_sz) + sizeof(struct gmonhdr); 1380b86b1bbSFabien Thomas gm.version = GMONVERSION; 1390b86b1bbSFabien Thomas gm.profrate = 0; /* use ticks */ 140*465dadb5SJonathan T. Looney if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) 141*465dadb5SJonathan T. Looney gm.histcounter_type = WIDEHISTCOUNTER_GMONTYPE; 142*465dadb5SJonathan T. Looney else 143*465dadb5SJonathan T. Looney gm.histcounter_type = HISTCOUNTER_GMONTYPE; 1440b86b1bbSFabien Thomas gm.spare[0] = gm.spare[1] = 0; 1450b86b1bbSFabien Thomas 1460b86b1bbSFabien Thomas /* Write out the gmon header */ 1470b86b1bbSFabien Thomas if (write(fd, &gm, sizeof(gm)) < 0) 1480b86b1bbSFabien Thomas goto error; 1490b86b1bbSFabien Thomas 1500b86b1bbSFabien Thomas /* Zero fill the samples[] array */ 1510b86b1bbSFabien Thomas (void) memset(buffer, 0, sizeof(buffer)); 1520b86b1bbSFabien Thomas 1530b86b1bbSFabien Thomas count = pgf->pgf_ndatabytes - sizeof(struct gmonhdr); 1540b86b1bbSFabien Thomas while (count > sizeof(buffer)) { 1550b86b1bbSFabien Thomas if (write(fd, &buffer, sizeof(buffer)) < 0) 1560b86b1bbSFabien Thomas goto error; 1570b86b1bbSFabien Thomas count -= sizeof(buffer); 1580b86b1bbSFabien Thomas } 1590b86b1bbSFabien Thomas 1600b86b1bbSFabien Thomas if (write(fd, &buffer, count) < 0) 1610b86b1bbSFabien Thomas goto error; 1620b86b1bbSFabien Thomas 1630b86b1bbSFabien Thomas (void) close(fd); 1640b86b1bbSFabien Thomas 1650b86b1bbSFabien Thomas return; 1660b86b1bbSFabien Thomas 1670b86b1bbSFabien Thomas error: 1680b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: Cannot write \"%s\"", pathname); 1690b86b1bbSFabien Thomas } 1700b86b1bbSFabien Thomas 1710b86b1bbSFabien Thomas /* 1720b86b1bbSFabien Thomas * Determine the full pathname of a gmon.out file for a given 1730b86b1bbSFabien Thomas * (image,pmcid) combination. Return the interned string. 1740b86b1bbSFabien Thomas */ 1750b86b1bbSFabien Thomas 1760b86b1bbSFabien Thomas pmcstat_interned_string 1770b86b1bbSFabien Thomas pmcstat_gmon_create_name(const char *samplesdir, struct pmcstat_image *image, 1780b86b1bbSFabien Thomas pmc_id_t pmcid) 1790b86b1bbSFabien Thomas { 1800b86b1bbSFabien Thomas const char *pmcname; 1810b86b1bbSFabien Thomas char fullpath[PATH_MAX]; 1820b86b1bbSFabien Thomas 1830b86b1bbSFabien Thomas pmcname = pmcstat_pmcid_to_name(pmcid); 184410c7662SFabien Thomas if (!pmcname) 185410c7662SFabien Thomas err(EX_SOFTWARE, "ERROR: cannot find pmcid"); 1860b86b1bbSFabien Thomas 1870b86b1bbSFabien Thomas (void) snprintf(fullpath, sizeof(fullpath), 1880b86b1bbSFabien Thomas "%s/%s/%s", samplesdir, pmcname, 1890b86b1bbSFabien Thomas pmcstat_string_unintern(image->pi_samplename)); 1900b86b1bbSFabien Thomas 1910b86b1bbSFabien Thomas return (pmcstat_string_intern(fullpath)); 1920b86b1bbSFabien Thomas } 1930b86b1bbSFabien Thomas 1940b86b1bbSFabien Thomas 1950b86b1bbSFabien Thomas /* 1960b86b1bbSFabien Thomas * Mmap in a gmon.out file for processing. 1970b86b1bbSFabien Thomas */ 1980b86b1bbSFabien Thomas 1990b86b1bbSFabien Thomas static void 2000b86b1bbSFabien Thomas pmcstat_gmon_map_file(struct pmcstat_gmonfile *pgf) 2010b86b1bbSFabien Thomas { 2020b86b1bbSFabien Thomas int fd; 2030b86b1bbSFabien Thomas const char *pathname; 2040b86b1bbSFabien Thomas 2050b86b1bbSFabien Thomas pathname = pmcstat_string_unintern(pgf->pgf_name); 2060b86b1bbSFabien Thomas 2070b86b1bbSFabien Thomas /* the gmon.out file must already exist */ 2080b86b1bbSFabien Thomas if ((fd = open(pathname, O_RDWR | O_NOFOLLOW, 0)) < 0) 2090b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: cannot open \"%s\"", pathname); 2100b86b1bbSFabien Thomas 2110b86b1bbSFabien Thomas pgf->pgf_gmondata = mmap(NULL, pgf->pgf_ndatabytes, 2120b86b1bbSFabien Thomas PROT_READ|PROT_WRITE, MAP_NOSYNC|MAP_SHARED, fd, 0); 2130b86b1bbSFabien Thomas 2140b86b1bbSFabien Thomas if (pgf->pgf_gmondata == MAP_FAILED) 2150b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: cannot map \"%s\"", pathname); 2160b86b1bbSFabien Thomas 2170b86b1bbSFabien Thomas (void) close(fd); 2180b86b1bbSFabien Thomas } 2190b86b1bbSFabien Thomas 2200b86b1bbSFabien Thomas /* 2210b86b1bbSFabien Thomas * Unmap a gmon.out file after sync'ing its data to disk. 2220b86b1bbSFabien Thomas */ 2230b86b1bbSFabien Thomas 2240b86b1bbSFabien Thomas static void 2250b86b1bbSFabien Thomas pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *pgf) 2260b86b1bbSFabien Thomas { 2270b86b1bbSFabien Thomas (void) msync(pgf->pgf_gmondata, pgf->pgf_ndatabytes, 2280b86b1bbSFabien Thomas MS_SYNC); 2290b86b1bbSFabien Thomas (void) munmap(pgf->pgf_gmondata, pgf->pgf_ndatabytes); 2300b86b1bbSFabien Thomas pgf->pgf_gmondata = NULL; 2310b86b1bbSFabien Thomas } 2320b86b1bbSFabien Thomas 2330b86b1bbSFabien Thomas static void 2340b86b1bbSFabien Thomas pmcstat_gmon_append_arc(struct pmcstat_image *image, pmc_id_t pmcid, 2350b86b1bbSFabien Thomas uintptr_t rawfrom, uintptr_t rawto, uint32_t count) 2360b86b1bbSFabien Thomas { 2370b86b1bbSFabien Thomas struct rawarc arc; /* from <sys/gmon.h> */ 2380b86b1bbSFabien Thomas const char *pathname; 2390b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf; 2400b86b1bbSFabien Thomas 2410b86b1bbSFabien Thomas if ((pgf = pmcstat_image_find_gmonfile(image, pmcid)) == NULL) 2420b86b1bbSFabien Thomas return; 2430b86b1bbSFabien Thomas 2440b86b1bbSFabien Thomas if (pgf->pgf_file == NULL) { 2450b86b1bbSFabien Thomas pathname = pmcstat_string_unintern(pgf->pgf_name); 2460b86b1bbSFabien Thomas if ((pgf->pgf_file = fopen(pathname, "a")) == NULL) 2470b86b1bbSFabien Thomas return; 2480b86b1bbSFabien Thomas } 2490b86b1bbSFabien Thomas 2500b86b1bbSFabien Thomas arc.raw_frompc = rawfrom + image->pi_vaddr; 2510b86b1bbSFabien Thomas arc.raw_selfpc = rawto + image->pi_vaddr; 2520b86b1bbSFabien Thomas arc.raw_count = count; 2530b86b1bbSFabien Thomas 2540b86b1bbSFabien Thomas (void) fwrite(&arc, sizeof(arc), 1, pgf->pgf_file); 2550b86b1bbSFabien Thomas 2560b86b1bbSFabien Thomas } 2570b86b1bbSFabien Thomas 2580b86b1bbSFabien Thomas static struct pmcstat_gmonfile * 2590b86b1bbSFabien Thomas pmcstat_image_find_gmonfile(struct pmcstat_image *image, pmc_id_t pmcid) 2600b86b1bbSFabien Thomas { 2610b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf; 2620b86b1bbSFabien Thomas LIST_FOREACH(pgf, &image->pi_gmlist, pgf_next) 2630b86b1bbSFabien Thomas if (pgf->pgf_pmcid == pmcid) 2640b86b1bbSFabien Thomas return (pgf); 2650b86b1bbSFabien Thomas return (NULL); 2660b86b1bbSFabien Thomas } 2670b86b1bbSFabien Thomas 2680b86b1bbSFabien Thomas static void 2690b86b1bbSFabien Thomas pmcstat_cgnode_do_gmon_arcs(struct pmcstat_cgnode *cg, pmc_id_t pmcid) 2700b86b1bbSFabien Thomas { 2710b86b1bbSFabien Thomas struct pmcstat_cgnode *cgc; 2720b86b1bbSFabien Thomas 2730b86b1bbSFabien Thomas /* 2740b86b1bbSFabien Thomas * Look for child nodes that belong to the same image. 2750b86b1bbSFabien Thomas */ 2760b86b1bbSFabien Thomas 2770b86b1bbSFabien Thomas LIST_FOREACH(cgc, &cg->pcg_children, pcg_sibling) { 2780b86b1bbSFabien Thomas if (cgc->pcg_image == cg->pcg_image) 2790b86b1bbSFabien Thomas pmcstat_gmon_append_arc(cg->pcg_image, pmcid, 2800b86b1bbSFabien Thomas cgc->pcg_func, cg->pcg_func, cgc->pcg_count); 2810b86b1bbSFabien Thomas if (cgc->pcg_nchildren > 0) 2820b86b1bbSFabien Thomas pmcstat_cgnode_do_gmon_arcs(cgc, pmcid); 2830b86b1bbSFabien Thomas } 2840b86b1bbSFabien Thomas } 2850b86b1bbSFabien Thomas 2860b86b1bbSFabien Thomas static void 2870b86b1bbSFabien Thomas pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmc_id_t pmcid) 2880b86b1bbSFabien Thomas { 2890b86b1bbSFabien Thomas int n; 2900b86b1bbSFabien Thomas struct pmcstat_cgnode_hash *pch; 2910b86b1bbSFabien Thomas 2920b86b1bbSFabien Thomas for (n = 0; n < PMCSTAT_NHASH; n++) 2930b86b1bbSFabien Thomas LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) 2940b86b1bbSFabien Thomas if (pch->pch_pmcid == pmcid && 2950b86b1bbSFabien Thomas pch->pch_cgnode->pcg_nchildren > 1) 2960b86b1bbSFabien Thomas pmcstat_cgnode_do_gmon_arcs(pch->pch_cgnode, 2970b86b1bbSFabien Thomas pmcid); 2980b86b1bbSFabien Thomas } 2990b86b1bbSFabien Thomas 3000b86b1bbSFabien Thomas 3010b86b1bbSFabien Thomas static void 3020b86b1bbSFabien Thomas pmcstat_callgraph_do_gmon_arcs(void) 3030b86b1bbSFabien Thomas { 3040b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pmcr; 3050b86b1bbSFabien Thomas 3060b86b1bbSFabien Thomas LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) 3070b86b1bbSFabien Thomas pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmcr->pr_pmcid); 3080b86b1bbSFabien Thomas } 3090b86b1bbSFabien Thomas 3100b86b1bbSFabien Thomas void 3110b86b1bbSFabien Thomas pmcpl_gmon_initimage(struct pmcstat_image *pi) 3120b86b1bbSFabien Thomas { 3130b86b1bbSFabien Thomas int count, nlen; 3140b86b1bbSFabien Thomas char *sn; 3150b86b1bbSFabien Thomas char name[NAME_MAX]; 3160b86b1bbSFabien Thomas 3170b86b1bbSFabien Thomas /* 3180b86b1bbSFabien Thomas * Look for a suitable name for the sample files associated 3190b86b1bbSFabien Thomas * with this image: if `basename(path)`+".gmon" is available, 3200b86b1bbSFabien Thomas * we use that, otherwise we try iterating through 3210b86b1bbSFabien Thomas * `basename(path)`+ "~" + NNN + ".gmon" till we get a free 3220b86b1bbSFabien Thomas * entry. 3230b86b1bbSFabien Thomas */ 3240b86b1bbSFabien Thomas if ((sn = basename(pmcstat_string_unintern(pi->pi_execpath))) == NULL) 3250b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: Cannot process \"%s\"", 3260b86b1bbSFabien Thomas pmcstat_string_unintern(pi->pi_execpath)); 3270b86b1bbSFabien Thomas 3280b86b1bbSFabien Thomas nlen = strlen(sn); 3290b86b1bbSFabien Thomas nlen = min(nlen, (int) (sizeof(name) - sizeof(".gmon"))); 3300b86b1bbSFabien Thomas 3310b86b1bbSFabien Thomas snprintf(name, sizeof(name), "%.*s.gmon", nlen, sn); 3320b86b1bbSFabien Thomas 3330b86b1bbSFabien Thomas /* try use the unabridged name first */ 3340b86b1bbSFabien Thomas if (pmcstat_string_lookup(name) == NULL) 3350b86b1bbSFabien Thomas pi->pi_samplename = pmcstat_string_intern(name); 3360b86b1bbSFabien Thomas else { 3370b86b1bbSFabien Thomas /* 3380b86b1bbSFabien Thomas * Otherwise use a prefix from the original name and 3390b86b1bbSFabien Thomas * up to 3 digits. 3400b86b1bbSFabien Thomas */ 3410b86b1bbSFabien Thomas nlen = strlen(sn); 3420b86b1bbSFabien Thomas nlen = min(nlen, (int) (sizeof(name)-sizeof("~NNN.gmon"))); 3430b86b1bbSFabien Thomas count = 0; 3440b86b1bbSFabien Thomas do { 3450b86b1bbSFabien Thomas if (++count > 999) 34637d6f8a9SDavid E. O'Brien errx(EX_CANTCREAT, 34737d6f8a9SDavid E. O'Brien "ERROR: cannot create a gmon file for" 34837d6f8a9SDavid E. O'Brien " \"%s\"", name); 3490b86b1bbSFabien Thomas snprintf(name, sizeof(name), "%.*s~%3.3d.gmon", 3500b86b1bbSFabien Thomas nlen, sn, count); 3510b86b1bbSFabien Thomas if (pmcstat_string_lookup(name) == NULL) { 3520b86b1bbSFabien Thomas pi->pi_samplename = 3530b86b1bbSFabien Thomas pmcstat_string_intern(name); 3540b86b1bbSFabien Thomas count = 0; 3550b86b1bbSFabien Thomas } 3560b86b1bbSFabien Thomas } while (count > 0); 3570b86b1bbSFabien Thomas } 3580b86b1bbSFabien Thomas 3590b86b1bbSFabien Thomas LIST_INIT(&pi->pi_gmlist); 3600b86b1bbSFabien Thomas } 3610b86b1bbSFabien Thomas 3620b86b1bbSFabien Thomas void 3630b86b1bbSFabien Thomas pmcpl_gmon_shutdownimage(struct pmcstat_image *pi) 3640b86b1bbSFabien Thomas { 3650b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf, *pgftmp; 3660b86b1bbSFabien Thomas 3670b86b1bbSFabien Thomas LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next, pgftmp) { 3680b86b1bbSFabien Thomas if (pgf->pgf_file) 3690b86b1bbSFabien Thomas (void) fclose(pgf->pgf_file); 3700b86b1bbSFabien Thomas LIST_REMOVE(pgf, pgf_next); 3710b86b1bbSFabien Thomas free(pgf); 3720b86b1bbSFabien Thomas } 3730b86b1bbSFabien Thomas } 3740b86b1bbSFabien Thomas 3750b86b1bbSFabien Thomas void 3760b86b1bbSFabien Thomas pmcpl_gmon_newpmc(pmcstat_interned_string ps, struct pmcstat_pmcrecord *pr) 3770b86b1bbSFabien Thomas { 3780b86b1bbSFabien Thomas struct stat st; 3790b86b1bbSFabien Thomas char fullpath[PATH_MAX]; 3800b86b1bbSFabien Thomas 3810b86b1bbSFabien Thomas (void) pr; 3820b86b1bbSFabien Thomas 3830b86b1bbSFabien Thomas /* 3840b86b1bbSFabien Thomas * Create the appropriate directory to hold gmon.out files. 3850b86b1bbSFabien Thomas */ 3860b86b1bbSFabien Thomas 3870b86b1bbSFabien Thomas (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", args.pa_samplesdir, 3880b86b1bbSFabien Thomas pmcstat_string_unintern(ps)); 3890b86b1bbSFabien Thomas 3900b86b1bbSFabien Thomas /* If the path name exists, it should be a directory */ 3910b86b1bbSFabien Thomas if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) 3920b86b1bbSFabien Thomas return; 3930b86b1bbSFabien Thomas 3940b86b1bbSFabien Thomas if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) 3950b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: Cannot create directory \"%s\"", 3960b86b1bbSFabien Thomas fullpath); 3970b86b1bbSFabien Thomas } 3980b86b1bbSFabien Thomas 3990b86b1bbSFabien Thomas /* 4000b86b1bbSFabien Thomas * Increment the bucket in the gmon.out file corresponding to 'pmcid' 4010b86b1bbSFabien Thomas * and 'pc'. 4020b86b1bbSFabien Thomas */ 4030b86b1bbSFabien Thomas 4040b86b1bbSFabien Thomas void 4050b86b1bbSFabien Thomas pmcpl_gmon_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, 4060b86b1bbSFabien Thomas uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) 4070b86b1bbSFabien Thomas { 4080b86b1bbSFabien Thomas struct pmcstat_pcmap *map; 4090b86b1bbSFabien Thomas struct pmcstat_image *image; 4100b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf; 4110b86b1bbSFabien Thomas uintfptr_t bucket; 4120b86b1bbSFabien Thomas HISTCOUNTER *hc; 413*465dadb5SJonathan T. Looney WIDEHISTCOUNTER *whc; 4140b86b1bbSFabien Thomas pmc_id_t pmcid; 4150b86b1bbSFabien Thomas 4160b86b1bbSFabien Thomas (void) nsamples; (void) usermode; (void) cpu; 4170b86b1bbSFabien Thomas 4180b86b1bbSFabien Thomas map = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, cc[0]); 4190b86b1bbSFabien Thomas if (map == NULL) { 4200b86b1bbSFabien Thomas /* Unknown offset. */ 4210b86b1bbSFabien Thomas pmcstat_stats.ps_samples_unknown_offset++; 4220b86b1bbSFabien Thomas return; 4230b86b1bbSFabien Thomas } 4240b86b1bbSFabien Thomas 4250b86b1bbSFabien Thomas assert(cc[0] >= map->ppm_lowpc && cc[0] < map->ppm_highpc); 4260b86b1bbSFabien Thomas 4270b86b1bbSFabien Thomas image = map->ppm_image; 4280b86b1bbSFabien Thomas pmcid = pmcr->pr_pmcid; 4290b86b1bbSFabien Thomas 4300b86b1bbSFabien Thomas /* 4310b86b1bbSFabien Thomas * If this is the first time we are seeing a sample for 4320b86b1bbSFabien Thomas * this executable image, try determine its parameters. 4330b86b1bbSFabien Thomas */ 4340b86b1bbSFabien Thomas if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 4350b86b1bbSFabien Thomas pmcstat_image_determine_type(image); 4360b86b1bbSFabien Thomas 4370b86b1bbSFabien Thomas assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); 4380b86b1bbSFabien Thomas 4390b86b1bbSFabien Thomas /* Ignore samples in images that we know nothing about. */ 4400b86b1bbSFabien Thomas if (image->pi_type == PMCSTAT_IMAGE_INDETERMINABLE) { 4410b86b1bbSFabien Thomas pmcstat_stats.ps_samples_indeterminable++; 4420b86b1bbSFabien Thomas return; 4430b86b1bbSFabien Thomas } 4440b86b1bbSFabien Thomas 4450b86b1bbSFabien Thomas /* 4460b86b1bbSFabien Thomas * Find the gmon file corresponding to 'pmcid', creating it if 4470b86b1bbSFabien Thomas * needed. 4480b86b1bbSFabien Thomas */ 4490b86b1bbSFabien Thomas pgf = pmcstat_image_find_gmonfile(image, pmcid); 4500b86b1bbSFabien Thomas if (pgf == NULL) { 451*465dadb5SJonathan T. Looney if (hc_sz == 0) { 452*465dadb5SJonathan T. Looney /* Determine the correct histcounter size. */ 453*465dadb5SJonathan T. Looney if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) 454*465dadb5SJonathan T. Looney hc_sz = sizeof(WIDEHISTCOUNTER); 455*465dadb5SJonathan T. Looney else 456*465dadb5SJonathan T. Looney hc_sz = sizeof(HISTCOUNTER); 457*465dadb5SJonathan T. Looney } 458*465dadb5SJonathan T. Looney 4590b86b1bbSFabien Thomas if ((pgf = calloc(1, sizeof(*pgf))) == NULL) 4600b86b1bbSFabien Thomas err(EX_OSERR, "ERROR:"); 4610b86b1bbSFabien Thomas 4620b86b1bbSFabien Thomas pgf->pgf_gmondata = NULL; /* mark as unmapped */ 4630b86b1bbSFabien Thomas pgf->pgf_name = pmcstat_gmon_create_name(args.pa_samplesdir, 4640b86b1bbSFabien Thomas image, pmcid); 4650b86b1bbSFabien Thomas pgf->pgf_pmcid = pmcid; 4660b86b1bbSFabien Thomas assert(image->pi_end > image->pi_start); 4670b86b1bbSFabien Thomas pgf->pgf_nbuckets = (image->pi_end - image->pi_start) / 4680b86b1bbSFabien Thomas FUNCTION_ALIGNMENT; /* see <machine/profile.h> */ 4690b86b1bbSFabien Thomas pgf->pgf_ndatabytes = sizeof(struct gmonhdr) + 470*465dadb5SJonathan T. Looney pgf->pgf_nbuckets * hc_sz; 4710b86b1bbSFabien Thomas pgf->pgf_nsamples = 0; 4720b86b1bbSFabien Thomas pgf->pgf_file = NULL; 4730b86b1bbSFabien Thomas 4740b86b1bbSFabien Thomas pmcstat_gmon_create_file(pgf, image); 4750b86b1bbSFabien Thomas 4760b86b1bbSFabien Thomas LIST_INSERT_HEAD(&image->pi_gmlist, pgf, pgf_next); 4770b86b1bbSFabien Thomas } 4780b86b1bbSFabien Thomas 4790b86b1bbSFabien Thomas /* 4800b86b1bbSFabien Thomas * Map the gmon file in if needed. It may have been mapped 4810b86b1bbSFabien Thomas * out under memory pressure. 4820b86b1bbSFabien Thomas */ 4830b86b1bbSFabien Thomas if (pgf->pgf_gmondata == NULL) 4840b86b1bbSFabien Thomas pmcstat_gmon_map_file(pgf); 4850b86b1bbSFabien Thomas 4860b86b1bbSFabien Thomas assert(pgf->pgf_gmondata != NULL); 4870b86b1bbSFabien Thomas 4880b86b1bbSFabien Thomas /* 4890b86b1bbSFabien Thomas * 4900b86b1bbSFabien Thomas */ 4910b86b1bbSFabien Thomas 4920b86b1bbSFabien Thomas bucket = (cc[0] - map->ppm_lowpc) / FUNCTION_ALIGNMENT; 4930b86b1bbSFabien Thomas 4940b86b1bbSFabien Thomas assert(bucket < pgf->pgf_nbuckets); 4950b86b1bbSFabien Thomas 496*465dadb5SJonathan T. Looney if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) { 497*465dadb5SJonathan T. Looney whc = (WIDEHISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + 498*465dadb5SJonathan T. Looney sizeof(struct gmonhdr)); 499*465dadb5SJonathan T. Looney 500*465dadb5SJonathan T. Looney /* saturating add */ 501*465dadb5SJonathan T. Looney if (whc[bucket] < WIDEHISTCOUNTER_MAX) 502*465dadb5SJonathan T. Looney whc[bucket]++; 503*465dadb5SJonathan T. Looney else /* mark that an overflow occurred */ 504*465dadb5SJonathan T. Looney pgf->pgf_overflow = 1; 505*465dadb5SJonathan T. Looney } else { 5060b86b1bbSFabien Thomas hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + 5070b86b1bbSFabien Thomas sizeof(struct gmonhdr)); 5080b86b1bbSFabien Thomas 5090b86b1bbSFabien Thomas /* saturating add */ 510*465dadb5SJonathan T. Looney if (hc[bucket] < HISTCOUNTER_MAX) 5110b86b1bbSFabien Thomas hc[bucket]++; 5120b86b1bbSFabien Thomas else /* mark that an overflow occurred */ 5130b86b1bbSFabien Thomas pgf->pgf_overflow = 1; 514*465dadb5SJonathan T. Looney } 5150b86b1bbSFabien Thomas 5160b86b1bbSFabien Thomas pgf->pgf_nsamples++; 5170b86b1bbSFabien Thomas } 5180b86b1bbSFabien Thomas 5190b86b1bbSFabien Thomas /* 5200b86b1bbSFabien Thomas * Shutdown module. 5210b86b1bbSFabien Thomas */ 5220b86b1bbSFabien Thomas 5230b86b1bbSFabien Thomas void 5240b86b1bbSFabien Thomas pmcpl_gmon_shutdown(FILE *mf) 5250b86b1bbSFabien Thomas { 5260b86b1bbSFabien Thomas int i; 5270b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf; 5280b86b1bbSFabien Thomas struct pmcstat_image *pi; 5290b86b1bbSFabien Thomas 5300b86b1bbSFabien Thomas /* 5310b86b1bbSFabien Thomas * Sync back all gprof flat profile data. 5320b86b1bbSFabien Thomas */ 5330b86b1bbSFabien Thomas for (i = 0; i < PMCSTAT_NHASH; i++) { 5340b86b1bbSFabien Thomas LIST_FOREACH(pi, &pmcstat_image_hash[i], pi_next) { 5350b86b1bbSFabien Thomas if (mf) 5360b86b1bbSFabien Thomas (void) fprintf(mf, " \"%s\" => \"%s\"", 5370b86b1bbSFabien Thomas pmcstat_string_unintern(pi->pi_execpath), 5380b86b1bbSFabien Thomas pmcstat_string_unintern( 5390b86b1bbSFabien Thomas pi->pi_samplename)); 5400b86b1bbSFabien Thomas 5410b86b1bbSFabien Thomas /* flush gmon.out data to disk */ 5420b86b1bbSFabien Thomas LIST_FOREACH(pgf, &pi->pi_gmlist, pgf_next) { 5430b86b1bbSFabien Thomas pmcstat_gmon_unmap_file(pgf); 5440b86b1bbSFabien Thomas if (mf) 5450b86b1bbSFabien Thomas (void) fprintf(mf, " %s/%d", 5460b86b1bbSFabien Thomas pmcstat_pmcid_to_name( 5470b86b1bbSFabien Thomas pgf->pgf_pmcid), 5480b86b1bbSFabien Thomas pgf->pgf_nsamples); 5490b86b1bbSFabien Thomas if (pgf->pgf_overflow && args.pa_verbosity >= 1) 55037d6f8a9SDavid E. O'Brien warnx( 55137d6f8a9SDavid E. O'Brien "WARNING: profile \"%s\" overflowed.", 5520b86b1bbSFabien Thomas pmcstat_string_unintern( 5530b86b1bbSFabien Thomas pgf->pgf_name)); 5540b86b1bbSFabien Thomas } 5550b86b1bbSFabien Thomas 5560b86b1bbSFabien Thomas if (mf) 5570b86b1bbSFabien Thomas (void) fprintf(mf, "\n"); 5580b86b1bbSFabien Thomas } 5590b86b1bbSFabien Thomas } 5600b86b1bbSFabien Thomas 5610b86b1bbSFabien Thomas /* 5620b86b1bbSFabien Thomas * Compute arcs and add these to the gprof files. 5630b86b1bbSFabien Thomas */ 5640b86b1bbSFabien Thomas if (args.pa_flags & FLAG_DO_GPROF && args.pa_graphdepth > 1) 5650b86b1bbSFabien Thomas pmcstat_callgraph_do_gmon_arcs(); 5660b86b1bbSFabien Thomas } 567