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 77465dadb5SJonathan T. Looney typedef uint64_t WIDEHISTCOUNTER; 78465dadb5SJonathan T. Looney 79465dadb5SJonathan T. Looney #define WIDEHISTCOUNTER_MAX UINT64_MAX 80465dadb5SJonathan T. Looney #define HISTCOUNTER_MAX USHRT_MAX 81465dadb5SJonathan T. Looney #define WIDEHISTCOUNTER_GMONTYPE ((int) 64) 82465dadb5SJonathan T. Looney #define HISTCOUNTER_GMONTYPE ((int) 0) 83465dadb5SJonathan T. Looney static int hc_sz=0; 84465dadb5SJonathan 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; 137465dadb5SJonathan T. Looney gm.ncnt = (pgf->pgf_nbuckets * hc_sz) + sizeof(struct gmonhdr); 1380b86b1bbSFabien Thomas gm.version = GMONVERSION; 1390b86b1bbSFabien Thomas gm.profrate = 0; /* use ticks */ 140465dadb5SJonathan T. Looney if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) 141465dadb5SJonathan T. Looney gm.histcounter_type = WIDEHISTCOUNTER_GMONTYPE; 142465dadb5SJonathan T. Looney else 143465dadb5SJonathan 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 { 313*d93c9128SEd Schouten const char *execpath; 3140b86b1bbSFabien Thomas int count, nlen; 315*d93c9128SEd Schouten char *sn, *snbuf; 3160b86b1bbSFabien Thomas char name[NAME_MAX]; 3170b86b1bbSFabien Thomas 3180b86b1bbSFabien Thomas /* 3190b86b1bbSFabien Thomas * Look for a suitable name for the sample files associated 3200b86b1bbSFabien Thomas * with this image: if `basename(path)`+".gmon" is available, 3210b86b1bbSFabien Thomas * we use that, otherwise we try iterating through 3220b86b1bbSFabien Thomas * `basename(path)`+ "~" + NNN + ".gmon" till we get a free 3230b86b1bbSFabien Thomas * entry. 3240b86b1bbSFabien Thomas */ 325*d93c9128SEd Schouten execpath = pmcstat_string_unintern(pi->pi_execpath); 326*d93c9128SEd Schouten if ((snbuf = strdup(execpath)) == NULL) 327*d93c9128SEd Schouten err(EX_OSERR, "ERROR: Cannot copy \"%s\"", execpath); 328*d93c9128SEd Schouten if ((sn = basename(snbuf)) == NULL) 329*d93c9128SEd Schouten err(EX_OSERR, "ERROR: Cannot process \"%s\"", execpath); 3300b86b1bbSFabien Thomas 3310b86b1bbSFabien Thomas nlen = strlen(sn); 3320b86b1bbSFabien Thomas nlen = min(nlen, (int) (sizeof(name) - sizeof(".gmon"))); 3330b86b1bbSFabien Thomas 3340b86b1bbSFabien Thomas snprintf(name, sizeof(name), "%.*s.gmon", nlen, sn); 3350b86b1bbSFabien Thomas 3360b86b1bbSFabien Thomas /* try use the unabridged name first */ 3370b86b1bbSFabien Thomas if (pmcstat_string_lookup(name) == NULL) 3380b86b1bbSFabien Thomas pi->pi_samplename = pmcstat_string_intern(name); 3390b86b1bbSFabien Thomas else { 3400b86b1bbSFabien Thomas /* 3410b86b1bbSFabien Thomas * Otherwise use a prefix from the original name and 3420b86b1bbSFabien Thomas * up to 3 digits. 3430b86b1bbSFabien Thomas */ 3440b86b1bbSFabien Thomas nlen = strlen(sn); 3450b86b1bbSFabien Thomas nlen = min(nlen, (int) (sizeof(name)-sizeof("~NNN.gmon"))); 3460b86b1bbSFabien Thomas count = 0; 3470b86b1bbSFabien Thomas do { 3480b86b1bbSFabien Thomas if (++count > 999) 34937d6f8a9SDavid E. O'Brien errx(EX_CANTCREAT, 35037d6f8a9SDavid E. O'Brien "ERROR: cannot create a gmon file for" 35137d6f8a9SDavid E. O'Brien " \"%s\"", name); 3520b86b1bbSFabien Thomas snprintf(name, sizeof(name), "%.*s~%3.3d.gmon", 3530b86b1bbSFabien Thomas nlen, sn, count); 3540b86b1bbSFabien Thomas if (pmcstat_string_lookup(name) == NULL) { 3550b86b1bbSFabien Thomas pi->pi_samplename = 3560b86b1bbSFabien Thomas pmcstat_string_intern(name); 3570b86b1bbSFabien Thomas count = 0; 3580b86b1bbSFabien Thomas } 3590b86b1bbSFabien Thomas } while (count > 0); 3600b86b1bbSFabien Thomas } 361*d93c9128SEd Schouten free(snbuf); 3620b86b1bbSFabien Thomas 3630b86b1bbSFabien Thomas LIST_INIT(&pi->pi_gmlist); 3640b86b1bbSFabien Thomas } 3650b86b1bbSFabien Thomas 3660b86b1bbSFabien Thomas void 3670b86b1bbSFabien Thomas pmcpl_gmon_shutdownimage(struct pmcstat_image *pi) 3680b86b1bbSFabien Thomas { 3690b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf, *pgftmp; 3700b86b1bbSFabien Thomas 3710b86b1bbSFabien Thomas LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next, pgftmp) { 3720b86b1bbSFabien Thomas if (pgf->pgf_file) 3730b86b1bbSFabien Thomas (void) fclose(pgf->pgf_file); 3740b86b1bbSFabien Thomas LIST_REMOVE(pgf, pgf_next); 3750b86b1bbSFabien Thomas free(pgf); 3760b86b1bbSFabien Thomas } 3770b86b1bbSFabien Thomas } 3780b86b1bbSFabien Thomas 3790b86b1bbSFabien Thomas void 3800b86b1bbSFabien Thomas pmcpl_gmon_newpmc(pmcstat_interned_string ps, struct pmcstat_pmcrecord *pr) 3810b86b1bbSFabien Thomas { 3820b86b1bbSFabien Thomas struct stat st; 3830b86b1bbSFabien Thomas char fullpath[PATH_MAX]; 3840b86b1bbSFabien Thomas 3850b86b1bbSFabien Thomas (void) pr; 3860b86b1bbSFabien Thomas 3870b86b1bbSFabien Thomas /* 3880b86b1bbSFabien Thomas * Create the appropriate directory to hold gmon.out files. 3890b86b1bbSFabien Thomas */ 3900b86b1bbSFabien Thomas 3910b86b1bbSFabien Thomas (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", args.pa_samplesdir, 3920b86b1bbSFabien Thomas pmcstat_string_unintern(ps)); 3930b86b1bbSFabien Thomas 3940b86b1bbSFabien Thomas /* If the path name exists, it should be a directory */ 3950b86b1bbSFabien Thomas if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) 3960b86b1bbSFabien Thomas return; 3970b86b1bbSFabien Thomas 3980b86b1bbSFabien Thomas if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) 3990b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: Cannot create directory \"%s\"", 4000b86b1bbSFabien Thomas fullpath); 4010b86b1bbSFabien Thomas } 4020b86b1bbSFabien Thomas 4030b86b1bbSFabien Thomas /* 4040b86b1bbSFabien Thomas * Increment the bucket in the gmon.out file corresponding to 'pmcid' 4050b86b1bbSFabien Thomas * and 'pc'. 4060b86b1bbSFabien Thomas */ 4070b86b1bbSFabien Thomas 4080b86b1bbSFabien Thomas void 4090b86b1bbSFabien Thomas pmcpl_gmon_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, 4100b86b1bbSFabien Thomas uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) 4110b86b1bbSFabien Thomas { 4120b86b1bbSFabien Thomas struct pmcstat_pcmap *map; 4130b86b1bbSFabien Thomas struct pmcstat_image *image; 4140b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf; 4150b86b1bbSFabien Thomas uintfptr_t bucket; 4160b86b1bbSFabien Thomas HISTCOUNTER *hc; 417465dadb5SJonathan T. Looney WIDEHISTCOUNTER *whc; 4180b86b1bbSFabien Thomas pmc_id_t pmcid; 4190b86b1bbSFabien Thomas 4200b86b1bbSFabien Thomas (void) nsamples; (void) usermode; (void) cpu; 4210b86b1bbSFabien Thomas 4220b86b1bbSFabien Thomas map = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, cc[0]); 4230b86b1bbSFabien Thomas if (map == NULL) { 4240b86b1bbSFabien Thomas /* Unknown offset. */ 4250b86b1bbSFabien Thomas pmcstat_stats.ps_samples_unknown_offset++; 4260b86b1bbSFabien Thomas return; 4270b86b1bbSFabien Thomas } 4280b86b1bbSFabien Thomas 4290b86b1bbSFabien Thomas assert(cc[0] >= map->ppm_lowpc && cc[0] < map->ppm_highpc); 4300b86b1bbSFabien Thomas 4310b86b1bbSFabien Thomas image = map->ppm_image; 4320b86b1bbSFabien Thomas pmcid = pmcr->pr_pmcid; 4330b86b1bbSFabien Thomas 4340b86b1bbSFabien Thomas /* 4350b86b1bbSFabien Thomas * If this is the first time we are seeing a sample for 4360b86b1bbSFabien Thomas * this executable image, try determine its parameters. 4370b86b1bbSFabien Thomas */ 4380b86b1bbSFabien Thomas if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 4390b86b1bbSFabien Thomas pmcstat_image_determine_type(image); 4400b86b1bbSFabien Thomas 4410b86b1bbSFabien Thomas assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); 4420b86b1bbSFabien Thomas 4430b86b1bbSFabien Thomas /* Ignore samples in images that we know nothing about. */ 4440b86b1bbSFabien Thomas if (image->pi_type == PMCSTAT_IMAGE_INDETERMINABLE) { 4450b86b1bbSFabien Thomas pmcstat_stats.ps_samples_indeterminable++; 4460b86b1bbSFabien Thomas return; 4470b86b1bbSFabien Thomas } 4480b86b1bbSFabien Thomas 4490b86b1bbSFabien Thomas /* 4500b86b1bbSFabien Thomas * Find the gmon file corresponding to 'pmcid', creating it if 4510b86b1bbSFabien Thomas * needed. 4520b86b1bbSFabien Thomas */ 4530b86b1bbSFabien Thomas pgf = pmcstat_image_find_gmonfile(image, pmcid); 4540b86b1bbSFabien Thomas if (pgf == NULL) { 455465dadb5SJonathan T. Looney if (hc_sz == 0) { 456465dadb5SJonathan T. Looney /* Determine the correct histcounter size. */ 457465dadb5SJonathan T. Looney if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) 458465dadb5SJonathan T. Looney hc_sz = sizeof(WIDEHISTCOUNTER); 459465dadb5SJonathan T. Looney else 460465dadb5SJonathan T. Looney hc_sz = sizeof(HISTCOUNTER); 461465dadb5SJonathan T. Looney } 462465dadb5SJonathan T. Looney 4630b86b1bbSFabien Thomas if ((pgf = calloc(1, sizeof(*pgf))) == NULL) 4640b86b1bbSFabien Thomas err(EX_OSERR, "ERROR:"); 4650b86b1bbSFabien Thomas 4660b86b1bbSFabien Thomas pgf->pgf_gmondata = NULL; /* mark as unmapped */ 4670b86b1bbSFabien Thomas pgf->pgf_name = pmcstat_gmon_create_name(args.pa_samplesdir, 4680b86b1bbSFabien Thomas image, pmcid); 4690b86b1bbSFabien Thomas pgf->pgf_pmcid = pmcid; 4700b86b1bbSFabien Thomas assert(image->pi_end > image->pi_start); 4710b86b1bbSFabien Thomas pgf->pgf_nbuckets = (image->pi_end - image->pi_start) / 4720b86b1bbSFabien Thomas FUNCTION_ALIGNMENT; /* see <machine/profile.h> */ 4730b86b1bbSFabien Thomas pgf->pgf_ndatabytes = sizeof(struct gmonhdr) + 474465dadb5SJonathan T. Looney pgf->pgf_nbuckets * hc_sz; 4750b86b1bbSFabien Thomas pgf->pgf_nsamples = 0; 4760b86b1bbSFabien Thomas pgf->pgf_file = NULL; 4770b86b1bbSFabien Thomas 4780b86b1bbSFabien Thomas pmcstat_gmon_create_file(pgf, image); 4790b86b1bbSFabien Thomas 4800b86b1bbSFabien Thomas LIST_INSERT_HEAD(&image->pi_gmlist, pgf, pgf_next); 4810b86b1bbSFabien Thomas } 4820b86b1bbSFabien Thomas 4830b86b1bbSFabien Thomas /* 4840b86b1bbSFabien Thomas * Map the gmon file in if needed. It may have been mapped 4850b86b1bbSFabien Thomas * out under memory pressure. 4860b86b1bbSFabien Thomas */ 4870b86b1bbSFabien Thomas if (pgf->pgf_gmondata == NULL) 4880b86b1bbSFabien Thomas pmcstat_gmon_map_file(pgf); 4890b86b1bbSFabien Thomas 4900b86b1bbSFabien Thomas assert(pgf->pgf_gmondata != NULL); 4910b86b1bbSFabien Thomas 4920b86b1bbSFabien Thomas /* 4930b86b1bbSFabien Thomas * 4940b86b1bbSFabien Thomas */ 4950b86b1bbSFabien Thomas 4960b86b1bbSFabien Thomas bucket = (cc[0] - map->ppm_lowpc) / FUNCTION_ALIGNMENT; 4970b86b1bbSFabien Thomas 4980b86b1bbSFabien Thomas assert(bucket < pgf->pgf_nbuckets); 4990b86b1bbSFabien Thomas 500465dadb5SJonathan T. Looney if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) { 501465dadb5SJonathan T. Looney whc = (WIDEHISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + 502465dadb5SJonathan T. Looney sizeof(struct gmonhdr)); 503465dadb5SJonathan T. Looney 504465dadb5SJonathan T. Looney /* saturating add */ 505465dadb5SJonathan T. Looney if (whc[bucket] < WIDEHISTCOUNTER_MAX) 506465dadb5SJonathan T. Looney whc[bucket]++; 507465dadb5SJonathan T. Looney else /* mark that an overflow occurred */ 508465dadb5SJonathan T. Looney pgf->pgf_overflow = 1; 509465dadb5SJonathan T. Looney } else { 5100b86b1bbSFabien Thomas hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + 5110b86b1bbSFabien Thomas sizeof(struct gmonhdr)); 5120b86b1bbSFabien Thomas 5130b86b1bbSFabien Thomas /* saturating add */ 514465dadb5SJonathan T. Looney if (hc[bucket] < HISTCOUNTER_MAX) 5150b86b1bbSFabien Thomas hc[bucket]++; 5160b86b1bbSFabien Thomas else /* mark that an overflow occurred */ 5170b86b1bbSFabien Thomas pgf->pgf_overflow = 1; 518465dadb5SJonathan T. Looney } 5190b86b1bbSFabien Thomas 5200b86b1bbSFabien Thomas pgf->pgf_nsamples++; 5210b86b1bbSFabien Thomas } 5220b86b1bbSFabien Thomas 5230b86b1bbSFabien Thomas /* 5240b86b1bbSFabien Thomas * Shutdown module. 5250b86b1bbSFabien Thomas */ 5260b86b1bbSFabien Thomas 5270b86b1bbSFabien Thomas void 5280b86b1bbSFabien Thomas pmcpl_gmon_shutdown(FILE *mf) 5290b86b1bbSFabien Thomas { 5300b86b1bbSFabien Thomas int i; 5310b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf; 5320b86b1bbSFabien Thomas struct pmcstat_image *pi; 5330b86b1bbSFabien Thomas 5340b86b1bbSFabien Thomas /* 5350b86b1bbSFabien Thomas * Sync back all gprof flat profile data. 5360b86b1bbSFabien Thomas */ 5370b86b1bbSFabien Thomas for (i = 0; i < PMCSTAT_NHASH; i++) { 5380b86b1bbSFabien Thomas LIST_FOREACH(pi, &pmcstat_image_hash[i], pi_next) { 5390b86b1bbSFabien Thomas if (mf) 5400b86b1bbSFabien Thomas (void) fprintf(mf, " \"%s\" => \"%s\"", 5410b86b1bbSFabien Thomas pmcstat_string_unintern(pi->pi_execpath), 5420b86b1bbSFabien Thomas pmcstat_string_unintern( 5430b86b1bbSFabien Thomas pi->pi_samplename)); 5440b86b1bbSFabien Thomas 5450b86b1bbSFabien Thomas /* flush gmon.out data to disk */ 5460b86b1bbSFabien Thomas LIST_FOREACH(pgf, &pi->pi_gmlist, pgf_next) { 5470b86b1bbSFabien Thomas pmcstat_gmon_unmap_file(pgf); 5480b86b1bbSFabien Thomas if (mf) 5490b86b1bbSFabien Thomas (void) fprintf(mf, " %s/%d", 5500b86b1bbSFabien Thomas pmcstat_pmcid_to_name( 5510b86b1bbSFabien Thomas pgf->pgf_pmcid), 5520b86b1bbSFabien Thomas pgf->pgf_nsamples); 5530b86b1bbSFabien Thomas if (pgf->pgf_overflow && args.pa_verbosity >= 1) 55437d6f8a9SDavid E. O'Brien warnx( 55537d6f8a9SDavid E. O'Brien "WARNING: profile \"%s\" overflowed.", 5560b86b1bbSFabien Thomas pmcstat_string_unintern( 5570b86b1bbSFabien Thomas pgf->pgf_name)); 5580b86b1bbSFabien Thomas } 5590b86b1bbSFabien Thomas 5600b86b1bbSFabien Thomas if (mf) 5610b86b1bbSFabien Thomas (void) fprintf(mf, "\n"); 5620b86b1bbSFabien Thomas } 5630b86b1bbSFabien Thomas } 5640b86b1bbSFabien Thomas 5650b86b1bbSFabien Thomas /* 5660b86b1bbSFabien Thomas * Compute arcs and add these to the gprof files. 5670b86b1bbSFabien Thomas */ 5680b86b1bbSFabien Thomas if (args.pa_flags & FLAG_DO_GPROF && args.pa_graphdepth > 1) 5690b86b1bbSFabien Thomas pmcstat_callgraph_do_gmon_arcs(); 5700b86b1bbSFabien Thomas } 571