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