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 79*d27927f7SRuslan Bukin #define min(A,B) ((A) < (B) ? (A) : (B)) 80*d27927f7SRuslan Bukin #define max(A,B) ((A) > (B) ? (A) : (B)) 81*d27927f7SRuslan Bukin 82465dadb5SJonathan T. Looney #define WIDEHISTCOUNTER_MAX UINT64_MAX 83465dadb5SJonathan T. Looney #define HISTCOUNTER_MAX USHRT_MAX 84465dadb5SJonathan T. Looney #define WIDEHISTCOUNTER_GMONTYPE ((int) 64) 85465dadb5SJonathan T. Looney #define HISTCOUNTER_GMONTYPE ((int) 0) 86465dadb5SJonathan T. Looney static int hc_sz=0; 87465dadb5SJonathan T. Looney 880b86b1bbSFabien Thomas /* 890b86b1bbSFabien Thomas * struct pmcstat_gmonfile tracks a given 'gmon.out' file. These 900b86b1bbSFabien Thomas * files are mmap()'ed in as needed. 910b86b1bbSFabien Thomas */ 920b86b1bbSFabien Thomas 930b86b1bbSFabien Thomas struct pmcstat_gmonfile { 940b86b1bbSFabien Thomas LIST_ENTRY(pmcstat_gmonfile) pgf_next; /* list of entries */ 950b86b1bbSFabien Thomas int pgf_overflow; /* whether a count overflowed */ 960b86b1bbSFabien Thomas pmc_id_t pgf_pmcid; /* id of the associated pmc */ 970b86b1bbSFabien Thomas size_t pgf_nbuckets; /* #buckets in this gmon.out */ 980b86b1bbSFabien Thomas unsigned int pgf_nsamples; /* #samples in this gmon.out */ 990b86b1bbSFabien Thomas pmcstat_interned_string pgf_name; /* pathname of gmon.out file */ 1000b86b1bbSFabien Thomas size_t pgf_ndatabytes; /* number of bytes mapped */ 1010b86b1bbSFabien Thomas void *pgf_gmondata; /* pointer to mmap'ed data */ 1020b86b1bbSFabien Thomas FILE *pgf_file; /* used when writing gmon arcs */ 1030b86b1bbSFabien Thomas }; 1040b86b1bbSFabien Thomas 1050b86b1bbSFabien Thomas /* 1060b86b1bbSFabien Thomas * Prototypes 1070b86b1bbSFabien Thomas */ 1080b86b1bbSFabien Thomas 1090b86b1bbSFabien Thomas static void pmcstat_gmon_create_file(struct pmcstat_gmonfile *_pgf, 1100b86b1bbSFabien Thomas struct pmcstat_image *_image); 1110b86b1bbSFabien Thomas static pmcstat_interned_string pmcstat_gmon_create_name(const char *_sd, 1120b86b1bbSFabien Thomas struct pmcstat_image *_img, pmc_id_t _pmcid); 1130b86b1bbSFabien Thomas static void pmcstat_gmon_map_file(struct pmcstat_gmonfile *_pgf); 1140b86b1bbSFabien Thomas static void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf); 1150b86b1bbSFabien Thomas 1160b86b1bbSFabien Thomas static struct pmcstat_gmonfile *pmcstat_image_find_gmonfile(struct 1170b86b1bbSFabien Thomas pmcstat_image *_i, pmc_id_t _id); 1180b86b1bbSFabien Thomas 1190b86b1bbSFabien Thomas /* 1200b86b1bbSFabien Thomas * Create a gmon.out file and size it. 1210b86b1bbSFabien Thomas */ 1220b86b1bbSFabien Thomas 1230b86b1bbSFabien Thomas static void 1240b86b1bbSFabien Thomas pmcstat_gmon_create_file(struct pmcstat_gmonfile *pgf, 1250b86b1bbSFabien Thomas struct pmcstat_image *image) 1260b86b1bbSFabien Thomas { 1270b86b1bbSFabien Thomas int fd; 1280b86b1bbSFabien Thomas size_t count; 1290b86b1bbSFabien Thomas struct gmonhdr gm; 1300b86b1bbSFabien Thomas const char *pathname; 1310b86b1bbSFabien Thomas char buffer[DEFAULT_BUFFER_SIZE]; 1320b86b1bbSFabien Thomas 1330b86b1bbSFabien Thomas pathname = pmcstat_string_unintern(pgf->pgf_name); 1340b86b1bbSFabien Thomas if ((fd = open(pathname, O_RDWR|O_NOFOLLOW|O_CREAT, 1350b86b1bbSFabien Thomas S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) 1360b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: Cannot open \"%s\"", pathname); 1370b86b1bbSFabien Thomas 1380b86b1bbSFabien Thomas gm.lpc = image->pi_start; 1390b86b1bbSFabien Thomas gm.hpc = image->pi_end; 140465dadb5SJonathan T. Looney gm.ncnt = (pgf->pgf_nbuckets * hc_sz) + sizeof(struct gmonhdr); 1410b86b1bbSFabien Thomas gm.version = GMONVERSION; 1420b86b1bbSFabien Thomas gm.profrate = 0; /* use ticks */ 143465dadb5SJonathan T. Looney if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) 144465dadb5SJonathan T. Looney gm.histcounter_type = WIDEHISTCOUNTER_GMONTYPE; 145465dadb5SJonathan T. Looney else 146465dadb5SJonathan T. Looney gm.histcounter_type = HISTCOUNTER_GMONTYPE; 1470b86b1bbSFabien Thomas gm.spare[0] = gm.spare[1] = 0; 1480b86b1bbSFabien Thomas 1490b86b1bbSFabien Thomas /* Write out the gmon header */ 1500b86b1bbSFabien Thomas if (write(fd, &gm, sizeof(gm)) < 0) 1510b86b1bbSFabien Thomas goto error; 1520b86b1bbSFabien Thomas 1530b86b1bbSFabien Thomas /* Zero fill the samples[] array */ 1540b86b1bbSFabien Thomas (void) memset(buffer, 0, sizeof(buffer)); 1550b86b1bbSFabien Thomas 1560b86b1bbSFabien Thomas count = pgf->pgf_ndatabytes - sizeof(struct gmonhdr); 1570b86b1bbSFabien Thomas while (count > sizeof(buffer)) { 1580b86b1bbSFabien Thomas if (write(fd, &buffer, sizeof(buffer)) < 0) 1590b86b1bbSFabien Thomas goto error; 1600b86b1bbSFabien Thomas count -= sizeof(buffer); 1610b86b1bbSFabien Thomas } 1620b86b1bbSFabien Thomas 1630b86b1bbSFabien Thomas if (write(fd, &buffer, count) < 0) 1640b86b1bbSFabien Thomas goto error; 1650b86b1bbSFabien Thomas 1660b86b1bbSFabien Thomas (void) close(fd); 1670b86b1bbSFabien Thomas 1680b86b1bbSFabien Thomas return; 1690b86b1bbSFabien Thomas 1700b86b1bbSFabien Thomas error: 1710b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: Cannot write \"%s\"", pathname); 1720b86b1bbSFabien Thomas } 1730b86b1bbSFabien Thomas 1740b86b1bbSFabien Thomas /* 1750b86b1bbSFabien Thomas * Determine the full pathname of a gmon.out file for a given 1760b86b1bbSFabien Thomas * (image,pmcid) combination. Return the interned string. 1770b86b1bbSFabien Thomas */ 1780b86b1bbSFabien Thomas 1790b86b1bbSFabien Thomas pmcstat_interned_string 1800b86b1bbSFabien Thomas pmcstat_gmon_create_name(const char *samplesdir, struct pmcstat_image *image, 1810b86b1bbSFabien Thomas pmc_id_t pmcid) 1820b86b1bbSFabien Thomas { 1830b86b1bbSFabien Thomas const char *pmcname; 1840b86b1bbSFabien Thomas char fullpath[PATH_MAX]; 1850b86b1bbSFabien Thomas 1860b86b1bbSFabien Thomas pmcname = pmcstat_pmcid_to_name(pmcid); 187410c7662SFabien Thomas if (!pmcname) 188410c7662SFabien Thomas err(EX_SOFTWARE, "ERROR: cannot find pmcid"); 1890b86b1bbSFabien Thomas 1900b86b1bbSFabien Thomas (void) snprintf(fullpath, sizeof(fullpath), 1910b86b1bbSFabien Thomas "%s/%s/%s", samplesdir, pmcname, 1920b86b1bbSFabien Thomas pmcstat_string_unintern(image->pi_samplename)); 1930b86b1bbSFabien Thomas 1940b86b1bbSFabien Thomas return (pmcstat_string_intern(fullpath)); 1950b86b1bbSFabien Thomas } 1960b86b1bbSFabien Thomas 1970b86b1bbSFabien Thomas 1980b86b1bbSFabien Thomas /* 1990b86b1bbSFabien Thomas * Mmap in a gmon.out file for processing. 2000b86b1bbSFabien Thomas */ 2010b86b1bbSFabien Thomas 2020b86b1bbSFabien Thomas static void 2030b86b1bbSFabien Thomas pmcstat_gmon_map_file(struct pmcstat_gmonfile *pgf) 2040b86b1bbSFabien Thomas { 2050b86b1bbSFabien Thomas int fd; 2060b86b1bbSFabien Thomas const char *pathname; 2070b86b1bbSFabien Thomas 2080b86b1bbSFabien Thomas pathname = pmcstat_string_unintern(pgf->pgf_name); 2090b86b1bbSFabien Thomas 2100b86b1bbSFabien Thomas /* the gmon.out file must already exist */ 2110b86b1bbSFabien Thomas if ((fd = open(pathname, O_RDWR | O_NOFOLLOW, 0)) < 0) 2120b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: cannot open \"%s\"", pathname); 2130b86b1bbSFabien Thomas 2140b86b1bbSFabien Thomas pgf->pgf_gmondata = mmap(NULL, pgf->pgf_ndatabytes, 2150b86b1bbSFabien Thomas PROT_READ|PROT_WRITE, MAP_NOSYNC|MAP_SHARED, fd, 0); 2160b86b1bbSFabien Thomas 2170b86b1bbSFabien Thomas if (pgf->pgf_gmondata == MAP_FAILED) 2180b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: cannot map \"%s\"", pathname); 2190b86b1bbSFabien Thomas 2200b86b1bbSFabien Thomas (void) close(fd); 2210b86b1bbSFabien Thomas } 2220b86b1bbSFabien Thomas 2230b86b1bbSFabien Thomas /* 2240b86b1bbSFabien Thomas * Unmap a gmon.out file after sync'ing its data to disk. 2250b86b1bbSFabien Thomas */ 2260b86b1bbSFabien Thomas 2270b86b1bbSFabien Thomas static void 2280b86b1bbSFabien Thomas pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *pgf) 2290b86b1bbSFabien Thomas { 2300b86b1bbSFabien Thomas (void) msync(pgf->pgf_gmondata, pgf->pgf_ndatabytes, 2310b86b1bbSFabien Thomas MS_SYNC); 2320b86b1bbSFabien Thomas (void) munmap(pgf->pgf_gmondata, pgf->pgf_ndatabytes); 2330b86b1bbSFabien Thomas pgf->pgf_gmondata = NULL; 2340b86b1bbSFabien Thomas } 2350b86b1bbSFabien Thomas 2360b86b1bbSFabien Thomas static void 2370b86b1bbSFabien Thomas pmcstat_gmon_append_arc(struct pmcstat_image *image, pmc_id_t pmcid, 2380b86b1bbSFabien Thomas uintptr_t rawfrom, uintptr_t rawto, uint32_t count) 2390b86b1bbSFabien Thomas { 2400b86b1bbSFabien Thomas struct rawarc arc; /* from <sys/gmon.h> */ 2410b86b1bbSFabien Thomas const char *pathname; 2420b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf; 2430b86b1bbSFabien Thomas 2440b86b1bbSFabien Thomas if ((pgf = pmcstat_image_find_gmonfile(image, pmcid)) == NULL) 2450b86b1bbSFabien Thomas return; 2460b86b1bbSFabien Thomas 2470b86b1bbSFabien Thomas if (pgf->pgf_file == NULL) { 2480b86b1bbSFabien Thomas pathname = pmcstat_string_unintern(pgf->pgf_name); 2490b86b1bbSFabien Thomas if ((pgf->pgf_file = fopen(pathname, "a")) == NULL) 2500b86b1bbSFabien Thomas return; 2510b86b1bbSFabien Thomas } 2520b86b1bbSFabien Thomas 2530b86b1bbSFabien Thomas arc.raw_frompc = rawfrom + image->pi_vaddr; 2540b86b1bbSFabien Thomas arc.raw_selfpc = rawto + image->pi_vaddr; 2550b86b1bbSFabien Thomas arc.raw_count = count; 2560b86b1bbSFabien Thomas 2570b86b1bbSFabien Thomas (void) fwrite(&arc, sizeof(arc), 1, pgf->pgf_file); 2580b86b1bbSFabien Thomas 2590b86b1bbSFabien Thomas } 2600b86b1bbSFabien Thomas 2610b86b1bbSFabien Thomas static struct pmcstat_gmonfile * 2620b86b1bbSFabien Thomas pmcstat_image_find_gmonfile(struct pmcstat_image *image, pmc_id_t pmcid) 2630b86b1bbSFabien Thomas { 2640b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf; 2650b86b1bbSFabien Thomas LIST_FOREACH(pgf, &image->pi_gmlist, pgf_next) 2660b86b1bbSFabien Thomas if (pgf->pgf_pmcid == pmcid) 2670b86b1bbSFabien Thomas return (pgf); 2680b86b1bbSFabien Thomas return (NULL); 2690b86b1bbSFabien Thomas } 2700b86b1bbSFabien Thomas 2710b86b1bbSFabien Thomas static void 2720b86b1bbSFabien Thomas pmcstat_cgnode_do_gmon_arcs(struct pmcstat_cgnode *cg, pmc_id_t pmcid) 2730b86b1bbSFabien Thomas { 2740b86b1bbSFabien Thomas struct pmcstat_cgnode *cgc; 2750b86b1bbSFabien Thomas 2760b86b1bbSFabien Thomas /* 2770b86b1bbSFabien Thomas * Look for child nodes that belong to the same image. 2780b86b1bbSFabien Thomas */ 2790b86b1bbSFabien Thomas 2800b86b1bbSFabien Thomas LIST_FOREACH(cgc, &cg->pcg_children, pcg_sibling) { 2810b86b1bbSFabien Thomas if (cgc->pcg_image == cg->pcg_image) 2820b86b1bbSFabien Thomas pmcstat_gmon_append_arc(cg->pcg_image, pmcid, 2830b86b1bbSFabien Thomas cgc->pcg_func, cg->pcg_func, cgc->pcg_count); 2840b86b1bbSFabien Thomas if (cgc->pcg_nchildren > 0) 2850b86b1bbSFabien Thomas pmcstat_cgnode_do_gmon_arcs(cgc, pmcid); 2860b86b1bbSFabien Thomas } 2870b86b1bbSFabien Thomas } 2880b86b1bbSFabien Thomas 2890b86b1bbSFabien Thomas static void 2900b86b1bbSFabien Thomas pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmc_id_t pmcid) 2910b86b1bbSFabien Thomas { 2920b86b1bbSFabien Thomas int n; 2930b86b1bbSFabien Thomas struct pmcstat_cgnode_hash *pch; 2940b86b1bbSFabien Thomas 2950b86b1bbSFabien Thomas for (n = 0; n < PMCSTAT_NHASH; n++) 2960b86b1bbSFabien Thomas LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) 2970b86b1bbSFabien Thomas if (pch->pch_pmcid == pmcid && 2980b86b1bbSFabien Thomas pch->pch_cgnode->pcg_nchildren > 1) 2990b86b1bbSFabien Thomas pmcstat_cgnode_do_gmon_arcs(pch->pch_cgnode, 3000b86b1bbSFabien Thomas pmcid); 3010b86b1bbSFabien Thomas } 3020b86b1bbSFabien Thomas 3030b86b1bbSFabien Thomas 3040b86b1bbSFabien Thomas static void 3050b86b1bbSFabien Thomas pmcstat_callgraph_do_gmon_arcs(void) 3060b86b1bbSFabien Thomas { 3070b86b1bbSFabien Thomas struct pmcstat_pmcrecord *pmcr; 3080b86b1bbSFabien Thomas 3090b86b1bbSFabien Thomas LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) 3100b86b1bbSFabien Thomas pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmcr->pr_pmcid); 3110b86b1bbSFabien Thomas } 3120b86b1bbSFabien Thomas 3130b86b1bbSFabien Thomas void 3140b86b1bbSFabien Thomas pmcpl_gmon_initimage(struct pmcstat_image *pi) 3150b86b1bbSFabien Thomas { 316d93c9128SEd Schouten const char *execpath; 3170b86b1bbSFabien Thomas int count, nlen; 318d93c9128SEd Schouten char *sn, *snbuf; 3190b86b1bbSFabien Thomas char name[NAME_MAX]; 3200b86b1bbSFabien Thomas 3210b86b1bbSFabien Thomas /* 3220b86b1bbSFabien Thomas * Look for a suitable name for the sample files associated 3230b86b1bbSFabien Thomas * with this image: if `basename(path)`+".gmon" is available, 3240b86b1bbSFabien Thomas * we use that, otherwise we try iterating through 3250b86b1bbSFabien Thomas * `basename(path)`+ "~" + NNN + ".gmon" till we get a free 3260b86b1bbSFabien Thomas * entry. 3270b86b1bbSFabien Thomas */ 328d93c9128SEd Schouten execpath = pmcstat_string_unintern(pi->pi_execpath); 329d93c9128SEd Schouten if ((snbuf = strdup(execpath)) == NULL) 330d93c9128SEd Schouten err(EX_OSERR, "ERROR: Cannot copy \"%s\"", execpath); 331d93c9128SEd Schouten if ((sn = basename(snbuf)) == NULL) 332d93c9128SEd Schouten err(EX_OSERR, "ERROR: Cannot process \"%s\"", execpath); 3330b86b1bbSFabien Thomas 3340b86b1bbSFabien Thomas nlen = strlen(sn); 3350b86b1bbSFabien Thomas nlen = min(nlen, (int) (sizeof(name) - sizeof(".gmon"))); 3360b86b1bbSFabien Thomas 3370b86b1bbSFabien Thomas snprintf(name, sizeof(name), "%.*s.gmon", nlen, sn); 3380b86b1bbSFabien Thomas 3390b86b1bbSFabien Thomas /* try use the unabridged name first */ 3400b86b1bbSFabien Thomas if (pmcstat_string_lookup(name) == NULL) 3410b86b1bbSFabien Thomas pi->pi_samplename = pmcstat_string_intern(name); 3420b86b1bbSFabien Thomas else { 3430b86b1bbSFabien Thomas /* 3440b86b1bbSFabien Thomas * Otherwise use a prefix from the original name and 3450b86b1bbSFabien Thomas * up to 3 digits. 3460b86b1bbSFabien Thomas */ 3470b86b1bbSFabien Thomas nlen = strlen(sn); 3480b86b1bbSFabien Thomas nlen = min(nlen, (int) (sizeof(name)-sizeof("~NNN.gmon"))); 3490b86b1bbSFabien Thomas count = 0; 3500b86b1bbSFabien Thomas do { 3510b86b1bbSFabien Thomas if (++count > 999) 35237d6f8a9SDavid E. O'Brien errx(EX_CANTCREAT, 35337d6f8a9SDavid E. O'Brien "ERROR: cannot create a gmon file for" 35437d6f8a9SDavid E. O'Brien " \"%s\"", name); 3550b86b1bbSFabien Thomas snprintf(name, sizeof(name), "%.*s~%3.3d.gmon", 3560b86b1bbSFabien Thomas nlen, sn, count); 3570b86b1bbSFabien Thomas if (pmcstat_string_lookup(name) == NULL) { 3580b86b1bbSFabien Thomas pi->pi_samplename = 3590b86b1bbSFabien Thomas pmcstat_string_intern(name); 3600b86b1bbSFabien Thomas count = 0; 3610b86b1bbSFabien Thomas } 3620b86b1bbSFabien Thomas } while (count > 0); 3630b86b1bbSFabien Thomas } 364d93c9128SEd Schouten free(snbuf); 3650b86b1bbSFabien Thomas 3660b86b1bbSFabien Thomas LIST_INIT(&pi->pi_gmlist); 3670b86b1bbSFabien Thomas } 3680b86b1bbSFabien Thomas 3690b86b1bbSFabien Thomas void 3700b86b1bbSFabien Thomas pmcpl_gmon_shutdownimage(struct pmcstat_image *pi) 3710b86b1bbSFabien Thomas { 3720b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf, *pgftmp; 3730b86b1bbSFabien Thomas 3740b86b1bbSFabien Thomas LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next, pgftmp) { 3750b86b1bbSFabien Thomas if (pgf->pgf_file) 3760b86b1bbSFabien Thomas (void) fclose(pgf->pgf_file); 3770b86b1bbSFabien Thomas LIST_REMOVE(pgf, pgf_next); 3780b86b1bbSFabien Thomas free(pgf); 3790b86b1bbSFabien Thomas } 3800b86b1bbSFabien Thomas } 3810b86b1bbSFabien Thomas 3820b86b1bbSFabien Thomas void 3830b86b1bbSFabien Thomas pmcpl_gmon_newpmc(pmcstat_interned_string ps, struct pmcstat_pmcrecord *pr) 3840b86b1bbSFabien Thomas { 3850b86b1bbSFabien Thomas struct stat st; 3860b86b1bbSFabien Thomas char fullpath[PATH_MAX]; 3870b86b1bbSFabien Thomas 3880b86b1bbSFabien Thomas (void) pr; 3890b86b1bbSFabien Thomas 3900b86b1bbSFabien Thomas /* 3910b86b1bbSFabien Thomas * Create the appropriate directory to hold gmon.out files. 3920b86b1bbSFabien Thomas */ 3930b86b1bbSFabien Thomas 3940b86b1bbSFabien Thomas (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", args.pa_samplesdir, 3950b86b1bbSFabien Thomas pmcstat_string_unintern(ps)); 3960b86b1bbSFabien Thomas 3970b86b1bbSFabien Thomas /* If the path name exists, it should be a directory */ 3980b86b1bbSFabien Thomas if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) 3990b86b1bbSFabien Thomas return; 4000b86b1bbSFabien Thomas 4010b86b1bbSFabien Thomas if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) 4020b86b1bbSFabien Thomas err(EX_OSERR, "ERROR: Cannot create directory \"%s\"", 4030b86b1bbSFabien Thomas fullpath); 4040b86b1bbSFabien Thomas } 4050b86b1bbSFabien Thomas 4060b86b1bbSFabien Thomas /* 4070b86b1bbSFabien Thomas * Increment the bucket in the gmon.out file corresponding to 'pmcid' 4080b86b1bbSFabien Thomas * and 'pc'. 4090b86b1bbSFabien Thomas */ 4100b86b1bbSFabien Thomas 4110b86b1bbSFabien Thomas void 4120b86b1bbSFabien Thomas pmcpl_gmon_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, 4130b86b1bbSFabien Thomas uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) 4140b86b1bbSFabien Thomas { 4150b86b1bbSFabien Thomas struct pmcstat_pcmap *map; 4160b86b1bbSFabien Thomas struct pmcstat_image *image; 4170b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf; 4180b86b1bbSFabien Thomas uintfptr_t bucket; 4190b86b1bbSFabien Thomas HISTCOUNTER *hc; 420465dadb5SJonathan T. Looney WIDEHISTCOUNTER *whc; 4210b86b1bbSFabien Thomas pmc_id_t pmcid; 4220b86b1bbSFabien Thomas 4230b86b1bbSFabien Thomas (void) nsamples; (void) usermode; (void) cpu; 4240b86b1bbSFabien Thomas 4250b86b1bbSFabien Thomas map = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, cc[0]); 4260b86b1bbSFabien Thomas if (map == NULL) { 4270b86b1bbSFabien Thomas /* Unknown offset. */ 4280b86b1bbSFabien Thomas pmcstat_stats.ps_samples_unknown_offset++; 4290b86b1bbSFabien Thomas return; 4300b86b1bbSFabien Thomas } 4310b86b1bbSFabien Thomas 4320b86b1bbSFabien Thomas assert(cc[0] >= map->ppm_lowpc && cc[0] < map->ppm_highpc); 4330b86b1bbSFabien Thomas 4340b86b1bbSFabien Thomas image = map->ppm_image; 4350b86b1bbSFabien Thomas pmcid = pmcr->pr_pmcid; 4360b86b1bbSFabien Thomas 4370b86b1bbSFabien Thomas /* 4380b86b1bbSFabien Thomas * If this is the first time we are seeing a sample for 4390b86b1bbSFabien Thomas * this executable image, try determine its parameters. 4400b86b1bbSFabien Thomas */ 4410b86b1bbSFabien Thomas if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 442*d27927f7SRuslan Bukin pmcstat_image_determine_type(image, &args); 4430b86b1bbSFabien Thomas 4440b86b1bbSFabien Thomas assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); 4450b86b1bbSFabien Thomas 4460b86b1bbSFabien Thomas /* Ignore samples in images that we know nothing about. */ 4470b86b1bbSFabien Thomas if (image->pi_type == PMCSTAT_IMAGE_INDETERMINABLE) { 4480b86b1bbSFabien Thomas pmcstat_stats.ps_samples_indeterminable++; 4490b86b1bbSFabien Thomas return; 4500b86b1bbSFabien Thomas } 4510b86b1bbSFabien Thomas 4520b86b1bbSFabien Thomas /* 4530b86b1bbSFabien Thomas * Find the gmon file corresponding to 'pmcid', creating it if 4540b86b1bbSFabien Thomas * needed. 4550b86b1bbSFabien Thomas */ 4560b86b1bbSFabien Thomas pgf = pmcstat_image_find_gmonfile(image, pmcid); 4570b86b1bbSFabien Thomas if (pgf == NULL) { 458465dadb5SJonathan T. Looney if (hc_sz == 0) { 459465dadb5SJonathan T. Looney /* Determine the correct histcounter size. */ 460465dadb5SJonathan T. Looney if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) 461465dadb5SJonathan T. Looney hc_sz = sizeof(WIDEHISTCOUNTER); 462465dadb5SJonathan T. Looney else 463465dadb5SJonathan T. Looney hc_sz = sizeof(HISTCOUNTER); 464465dadb5SJonathan T. Looney } 465465dadb5SJonathan T. Looney 4660b86b1bbSFabien Thomas if ((pgf = calloc(1, sizeof(*pgf))) == NULL) 4670b86b1bbSFabien Thomas err(EX_OSERR, "ERROR:"); 4680b86b1bbSFabien Thomas 4690b86b1bbSFabien Thomas pgf->pgf_gmondata = NULL; /* mark as unmapped */ 4700b86b1bbSFabien Thomas pgf->pgf_name = pmcstat_gmon_create_name(args.pa_samplesdir, 4710b86b1bbSFabien Thomas image, pmcid); 4720b86b1bbSFabien Thomas pgf->pgf_pmcid = pmcid; 4730b86b1bbSFabien Thomas assert(image->pi_end > image->pi_start); 474e28f262aSKonstantin Belousov pgf->pgf_nbuckets = howmany(image->pi_end - image->pi_start, 475e28f262aSKonstantin Belousov FUNCTION_ALIGNMENT); /* see <machine/profile.h> */ 4760b86b1bbSFabien Thomas pgf->pgf_ndatabytes = sizeof(struct gmonhdr) + 477465dadb5SJonathan T. Looney pgf->pgf_nbuckets * hc_sz; 4780b86b1bbSFabien Thomas pgf->pgf_nsamples = 0; 4790b86b1bbSFabien Thomas pgf->pgf_file = NULL; 4800b86b1bbSFabien Thomas 4810b86b1bbSFabien Thomas pmcstat_gmon_create_file(pgf, image); 4820b86b1bbSFabien Thomas 4830b86b1bbSFabien Thomas LIST_INSERT_HEAD(&image->pi_gmlist, pgf, pgf_next); 4840b86b1bbSFabien Thomas } 4850b86b1bbSFabien Thomas 4860b86b1bbSFabien Thomas /* 4870b86b1bbSFabien Thomas * Map the gmon file in if needed. It may have been mapped 4880b86b1bbSFabien Thomas * out under memory pressure. 4890b86b1bbSFabien Thomas */ 4900b86b1bbSFabien Thomas if (pgf->pgf_gmondata == NULL) 4910b86b1bbSFabien Thomas pmcstat_gmon_map_file(pgf); 4920b86b1bbSFabien Thomas 4930b86b1bbSFabien Thomas assert(pgf->pgf_gmondata != NULL); 4940b86b1bbSFabien Thomas 4950b86b1bbSFabien Thomas /* 4960b86b1bbSFabien Thomas * 4970b86b1bbSFabien Thomas */ 4980b86b1bbSFabien Thomas 4990b86b1bbSFabien Thomas bucket = (cc[0] - map->ppm_lowpc) / FUNCTION_ALIGNMENT; 5000b86b1bbSFabien Thomas 5010b86b1bbSFabien Thomas assert(bucket < pgf->pgf_nbuckets); 5020b86b1bbSFabien Thomas 503465dadb5SJonathan T. Looney if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) { 504465dadb5SJonathan T. Looney whc = (WIDEHISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + 505465dadb5SJonathan T. Looney sizeof(struct gmonhdr)); 506465dadb5SJonathan T. Looney 507465dadb5SJonathan T. Looney /* saturating add */ 508465dadb5SJonathan T. Looney if (whc[bucket] < WIDEHISTCOUNTER_MAX) 509465dadb5SJonathan T. Looney whc[bucket]++; 510465dadb5SJonathan T. Looney else /* mark that an overflow occurred */ 511465dadb5SJonathan T. Looney pgf->pgf_overflow = 1; 512465dadb5SJonathan T. Looney } else { 5130b86b1bbSFabien Thomas hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + 5140b86b1bbSFabien Thomas sizeof(struct gmonhdr)); 5150b86b1bbSFabien Thomas 5160b86b1bbSFabien Thomas /* saturating add */ 517465dadb5SJonathan T. Looney if (hc[bucket] < HISTCOUNTER_MAX) 5180b86b1bbSFabien Thomas hc[bucket]++; 5190b86b1bbSFabien Thomas else /* mark that an overflow occurred */ 5200b86b1bbSFabien Thomas pgf->pgf_overflow = 1; 521465dadb5SJonathan T. Looney } 5220b86b1bbSFabien Thomas 5230b86b1bbSFabien Thomas pgf->pgf_nsamples++; 5240b86b1bbSFabien Thomas } 5250b86b1bbSFabien Thomas 5260b86b1bbSFabien Thomas /* 5270b86b1bbSFabien Thomas * Shutdown module. 5280b86b1bbSFabien Thomas */ 5290b86b1bbSFabien Thomas 5300b86b1bbSFabien Thomas void 5310b86b1bbSFabien Thomas pmcpl_gmon_shutdown(FILE *mf) 5320b86b1bbSFabien Thomas { 5330b86b1bbSFabien Thomas int i; 5340b86b1bbSFabien Thomas struct pmcstat_gmonfile *pgf; 5350b86b1bbSFabien Thomas struct pmcstat_image *pi; 5360b86b1bbSFabien Thomas 5370b86b1bbSFabien Thomas /* 5380b86b1bbSFabien Thomas * Sync back all gprof flat profile data. 5390b86b1bbSFabien Thomas */ 5400b86b1bbSFabien Thomas for (i = 0; i < PMCSTAT_NHASH; i++) { 5410b86b1bbSFabien Thomas LIST_FOREACH(pi, &pmcstat_image_hash[i], pi_next) { 5420b86b1bbSFabien Thomas if (mf) 5430b86b1bbSFabien Thomas (void) fprintf(mf, " \"%s\" => \"%s\"", 5440b86b1bbSFabien Thomas pmcstat_string_unintern(pi->pi_execpath), 5450b86b1bbSFabien Thomas pmcstat_string_unintern( 5460b86b1bbSFabien Thomas pi->pi_samplename)); 5470b86b1bbSFabien Thomas 5480b86b1bbSFabien Thomas /* flush gmon.out data to disk */ 5490b86b1bbSFabien Thomas LIST_FOREACH(pgf, &pi->pi_gmlist, pgf_next) { 5500b86b1bbSFabien Thomas pmcstat_gmon_unmap_file(pgf); 5510b86b1bbSFabien Thomas if (mf) 5520b86b1bbSFabien Thomas (void) fprintf(mf, " %s/%d", 5530b86b1bbSFabien Thomas pmcstat_pmcid_to_name( 5540b86b1bbSFabien Thomas pgf->pgf_pmcid), 5550b86b1bbSFabien Thomas pgf->pgf_nsamples); 5560b86b1bbSFabien Thomas if (pgf->pgf_overflow && args.pa_verbosity >= 1) 55737d6f8a9SDavid E. O'Brien warnx( 55837d6f8a9SDavid E. O'Brien "WARNING: profile \"%s\" overflowed.", 5590b86b1bbSFabien Thomas pmcstat_string_unintern( 5600b86b1bbSFabien Thomas pgf->pgf_name)); 5610b86b1bbSFabien Thomas } 5620b86b1bbSFabien Thomas 5630b86b1bbSFabien Thomas if (mf) 5640b86b1bbSFabien Thomas (void) fprintf(mf, "\n"); 5650b86b1bbSFabien Thomas } 5660b86b1bbSFabien Thomas } 5670b86b1bbSFabien Thomas 5680b86b1bbSFabien Thomas /* 5690b86b1bbSFabien Thomas * Compute arcs and add these to the gprof files. 5700b86b1bbSFabien Thomas */ 5710b86b1bbSFabien Thomas if (args.pa_flags & FLAG_DO_GPROF && args.pa_graphdepth > 1) 5720b86b1bbSFabien Thomas pmcstat_callgraph_do_gmon_arcs(); 5730b86b1bbSFabien Thomas } 574