1203790Sfabient/*- 2203790Sfabient * Copyright (c) 2005-2007, Joseph Koshy 3203790Sfabient * Copyright (c) 2007 The FreeBSD Foundation 4203790Sfabient * Copyright (c) 2009, Fabien Thomas 5203790Sfabient * All rights reserved. 6203790Sfabient * 7203790Sfabient * Portions of this software were developed by A. Joseph Koshy under 8203790Sfabient * sponsorship from the FreeBSD Foundation and Google, Inc. 9203790Sfabient * 10203790Sfabient * Redistribution and use in source and binary forms, with or without 11203790Sfabient * modification, are permitted provided that the following conditions 12203790Sfabient * are met: 13203790Sfabient * 1. Redistributions of source code must retain the above copyright 14203790Sfabient * notice, this list of conditions and the following disclaimer. 15203790Sfabient * 2. Redistributions in binary form must reproduce the above copyright 16203790Sfabient * notice, this list of conditions and the following disclaimer in the 17203790Sfabient * documentation and/or other materials provided with the distribution. 18203790Sfabient * 19203790Sfabient * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20203790Sfabient * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21203790Sfabient * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22203790Sfabient * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23203790Sfabient * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24203790Sfabient * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25203790Sfabient * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26203790Sfabient * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27203790Sfabient * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28203790Sfabient * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29203790Sfabient * SUCH DAMAGE. 30203790Sfabient */ 31203790Sfabient 32203790Sfabient/* 33203790Sfabient * Transform a hwpmc(4) log into human readable form, and into 34203790Sfabient * gprof(1) compatible profiles. 35203790Sfabient */ 36203790Sfabient 37203790Sfabient#include <sys/cdefs.h> 38203790Sfabient__FBSDID("$FreeBSD$"); 39203790Sfabient 40203790Sfabient#include <sys/param.h> 41203790Sfabient#include <sys/endian.h> 42203790Sfabient#include <sys/gmon.h> 43203790Sfabient#include <sys/imgact_aout.h> 44203790Sfabient#include <sys/imgact_elf.h> 45203790Sfabient#include <sys/mman.h> 46203790Sfabient#include <sys/pmc.h> 47203790Sfabient#include <sys/queue.h> 48203790Sfabient#include <sys/socket.h> 49203790Sfabient#include <sys/stat.h> 50203790Sfabient#include <sys/wait.h> 51203790Sfabient 52203790Sfabient#include <netinet/in.h> 53203790Sfabient 54203790Sfabient#include <assert.h> 55203790Sfabient#include <curses.h> 56203790Sfabient#include <err.h> 57203790Sfabient#include <errno.h> 58203790Sfabient#include <fcntl.h> 59203790Sfabient#include <gelf.h> 60203790Sfabient#include <libgen.h> 61203790Sfabient#include <limits.h> 62203790Sfabient#include <netdb.h> 63203790Sfabient#include <pmc.h> 64203790Sfabient#include <pmclog.h> 65203790Sfabient#include <sysexits.h> 66203790Sfabient#include <stdint.h> 67203790Sfabient#include <stdio.h> 68203790Sfabient#include <stdlib.h> 69203790Sfabient#include <string.h> 70203790Sfabient#include <unistd.h> 71203790Sfabient 72203790Sfabient#include "pmcstat.h" 73203790Sfabient#include "pmcstat_log.h" 74203790Sfabient#include "pmcpl_callgraph.h" 75203790Sfabient#include "pmcpl_gprof.h" 76203790Sfabient 77203790Sfabient/* 78203790Sfabient * struct pmcstat_gmonfile tracks a given 'gmon.out' file. These 79203790Sfabient * files are mmap()'ed in as needed. 80203790Sfabient */ 81203790Sfabient 82203790Sfabientstruct pmcstat_gmonfile { 83203790Sfabient LIST_ENTRY(pmcstat_gmonfile) pgf_next; /* list of entries */ 84203790Sfabient int pgf_overflow; /* whether a count overflowed */ 85203790Sfabient pmc_id_t pgf_pmcid; /* id of the associated pmc */ 86203790Sfabient size_t pgf_nbuckets; /* #buckets in this gmon.out */ 87203790Sfabient unsigned int pgf_nsamples; /* #samples in this gmon.out */ 88203790Sfabient pmcstat_interned_string pgf_name; /* pathname of gmon.out file */ 89203790Sfabient size_t pgf_ndatabytes; /* number of bytes mapped */ 90203790Sfabient void *pgf_gmondata; /* pointer to mmap'ed data */ 91203790Sfabient FILE *pgf_file; /* used when writing gmon arcs */ 92203790Sfabient}; 93203790Sfabient 94203790Sfabient/* 95203790Sfabient * Prototypes 96203790Sfabient */ 97203790Sfabient 98203790Sfabientstatic void pmcstat_gmon_create_file(struct pmcstat_gmonfile *_pgf, 99203790Sfabient struct pmcstat_image *_image); 100203790Sfabientstatic pmcstat_interned_string pmcstat_gmon_create_name(const char *_sd, 101203790Sfabient struct pmcstat_image *_img, pmc_id_t _pmcid); 102203790Sfabientstatic void pmcstat_gmon_map_file(struct pmcstat_gmonfile *_pgf); 103203790Sfabientstatic void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf); 104203790Sfabient 105203790Sfabientstatic struct pmcstat_gmonfile *pmcstat_image_find_gmonfile(struct 106203790Sfabient pmcstat_image *_i, pmc_id_t _id); 107203790Sfabient 108203790Sfabient/* 109203790Sfabient * Create a gmon.out file and size it. 110203790Sfabient */ 111203790Sfabient 112203790Sfabientstatic void 113203790Sfabientpmcstat_gmon_create_file(struct pmcstat_gmonfile *pgf, 114203790Sfabient struct pmcstat_image *image) 115203790Sfabient{ 116203790Sfabient int fd; 117203790Sfabient size_t count; 118203790Sfabient struct gmonhdr gm; 119203790Sfabient const char *pathname; 120203790Sfabient char buffer[DEFAULT_BUFFER_SIZE]; 121203790Sfabient 122203790Sfabient pathname = pmcstat_string_unintern(pgf->pgf_name); 123203790Sfabient if ((fd = open(pathname, O_RDWR|O_NOFOLLOW|O_CREAT, 124203790Sfabient S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) 125203790Sfabient err(EX_OSERR, "ERROR: Cannot open \"%s\"", pathname); 126203790Sfabient 127203790Sfabient gm.lpc = image->pi_start; 128203790Sfabient gm.hpc = image->pi_end; 129203790Sfabient gm.ncnt = (pgf->pgf_nbuckets * sizeof(HISTCOUNTER)) + 130203790Sfabient sizeof(struct gmonhdr); 131203790Sfabient gm.version = GMONVERSION; 132203790Sfabient gm.profrate = 0; /* use ticks */ 133203790Sfabient gm.histcounter_type = 0; /* compatibility with moncontrol() */ 134203790Sfabient gm.spare[0] = gm.spare[1] = 0; 135203790Sfabient 136203790Sfabient /* Write out the gmon header */ 137203790Sfabient if (write(fd, &gm, sizeof(gm)) < 0) 138203790Sfabient goto error; 139203790Sfabient 140203790Sfabient /* Zero fill the samples[] array */ 141203790Sfabient (void) memset(buffer, 0, sizeof(buffer)); 142203790Sfabient 143203790Sfabient count = pgf->pgf_ndatabytes - sizeof(struct gmonhdr); 144203790Sfabient while (count > sizeof(buffer)) { 145203790Sfabient if (write(fd, &buffer, sizeof(buffer)) < 0) 146203790Sfabient goto error; 147203790Sfabient count -= sizeof(buffer); 148203790Sfabient } 149203790Sfabient 150203790Sfabient if (write(fd, &buffer, count) < 0) 151203790Sfabient goto error; 152203790Sfabient 153203790Sfabient (void) close(fd); 154203790Sfabient 155203790Sfabient return; 156203790Sfabient 157203790Sfabient error: 158203790Sfabient err(EX_OSERR, "ERROR: Cannot write \"%s\"", pathname); 159203790Sfabient} 160203790Sfabient 161203790Sfabient/* 162203790Sfabient * Determine the full pathname of a gmon.out file for a given 163203790Sfabient * (image,pmcid) combination. Return the interned string. 164203790Sfabient */ 165203790Sfabient 166203790Sfabientpmcstat_interned_string 167203790Sfabientpmcstat_gmon_create_name(const char *samplesdir, struct pmcstat_image *image, 168203790Sfabient pmc_id_t pmcid) 169203790Sfabient{ 170203790Sfabient const char *pmcname; 171203790Sfabient char fullpath[PATH_MAX]; 172203790Sfabient 173203790Sfabient pmcname = pmcstat_pmcid_to_name(pmcid); 174206635Sfabient if (!pmcname) 175206635Sfabient err(EX_SOFTWARE, "ERROR: cannot find pmcid"); 176203790Sfabient 177203790Sfabient (void) snprintf(fullpath, sizeof(fullpath), 178203790Sfabient "%s/%s/%s", samplesdir, pmcname, 179203790Sfabient pmcstat_string_unintern(image->pi_samplename)); 180203790Sfabient 181203790Sfabient return (pmcstat_string_intern(fullpath)); 182203790Sfabient} 183203790Sfabient 184203790Sfabient 185203790Sfabient/* 186203790Sfabient * Mmap in a gmon.out file for processing. 187203790Sfabient */ 188203790Sfabient 189203790Sfabientstatic void 190203790Sfabientpmcstat_gmon_map_file(struct pmcstat_gmonfile *pgf) 191203790Sfabient{ 192203790Sfabient int fd; 193203790Sfabient const char *pathname; 194203790Sfabient 195203790Sfabient pathname = pmcstat_string_unintern(pgf->pgf_name); 196203790Sfabient 197203790Sfabient /* the gmon.out file must already exist */ 198203790Sfabient if ((fd = open(pathname, O_RDWR | O_NOFOLLOW, 0)) < 0) 199203790Sfabient err(EX_OSERR, "ERROR: cannot open \"%s\"", pathname); 200203790Sfabient 201203790Sfabient pgf->pgf_gmondata = mmap(NULL, pgf->pgf_ndatabytes, 202203790Sfabient PROT_READ|PROT_WRITE, MAP_NOSYNC|MAP_SHARED, fd, 0); 203203790Sfabient 204203790Sfabient if (pgf->pgf_gmondata == MAP_FAILED) 205203790Sfabient err(EX_OSERR, "ERROR: cannot map \"%s\"", pathname); 206203790Sfabient 207203790Sfabient (void) close(fd); 208203790Sfabient} 209203790Sfabient 210203790Sfabient/* 211203790Sfabient * Unmap a gmon.out file after sync'ing its data to disk. 212203790Sfabient */ 213203790Sfabient 214203790Sfabientstatic void 215203790Sfabientpmcstat_gmon_unmap_file(struct pmcstat_gmonfile *pgf) 216203790Sfabient{ 217203790Sfabient (void) msync(pgf->pgf_gmondata, pgf->pgf_ndatabytes, 218203790Sfabient MS_SYNC); 219203790Sfabient (void) munmap(pgf->pgf_gmondata, pgf->pgf_ndatabytes); 220203790Sfabient pgf->pgf_gmondata = NULL; 221203790Sfabient} 222203790Sfabient 223203790Sfabientstatic void 224203790Sfabientpmcstat_gmon_append_arc(struct pmcstat_image *image, pmc_id_t pmcid, 225203790Sfabient uintptr_t rawfrom, uintptr_t rawto, uint32_t count) 226203790Sfabient{ 227203790Sfabient struct rawarc arc; /* from <sys/gmon.h> */ 228203790Sfabient const char *pathname; 229203790Sfabient struct pmcstat_gmonfile *pgf; 230203790Sfabient 231203790Sfabient if ((pgf = pmcstat_image_find_gmonfile(image, pmcid)) == NULL) 232203790Sfabient return; 233203790Sfabient 234203790Sfabient if (pgf->pgf_file == NULL) { 235203790Sfabient pathname = pmcstat_string_unintern(pgf->pgf_name); 236203790Sfabient if ((pgf->pgf_file = fopen(pathname, "a")) == NULL) 237203790Sfabient return; 238203790Sfabient } 239203790Sfabient 240203790Sfabient arc.raw_frompc = rawfrom + image->pi_vaddr; 241203790Sfabient arc.raw_selfpc = rawto + image->pi_vaddr; 242203790Sfabient arc.raw_count = count; 243203790Sfabient 244203790Sfabient (void) fwrite(&arc, sizeof(arc), 1, pgf->pgf_file); 245203790Sfabient 246203790Sfabient} 247203790Sfabient 248203790Sfabientstatic struct pmcstat_gmonfile * 249203790Sfabientpmcstat_image_find_gmonfile(struct pmcstat_image *image, pmc_id_t pmcid) 250203790Sfabient{ 251203790Sfabient struct pmcstat_gmonfile *pgf; 252203790Sfabient LIST_FOREACH(pgf, &image->pi_gmlist, pgf_next) 253203790Sfabient if (pgf->pgf_pmcid == pmcid) 254203790Sfabient return (pgf); 255203790Sfabient return (NULL); 256203790Sfabient} 257203790Sfabient 258203790Sfabientstatic void 259203790Sfabientpmcstat_cgnode_do_gmon_arcs(struct pmcstat_cgnode *cg, pmc_id_t pmcid) 260203790Sfabient{ 261203790Sfabient struct pmcstat_cgnode *cgc; 262203790Sfabient 263203790Sfabient /* 264203790Sfabient * Look for child nodes that belong to the same image. 265203790Sfabient */ 266203790Sfabient 267203790Sfabient LIST_FOREACH(cgc, &cg->pcg_children, pcg_sibling) { 268203790Sfabient if (cgc->pcg_image == cg->pcg_image) 269203790Sfabient pmcstat_gmon_append_arc(cg->pcg_image, pmcid, 270203790Sfabient cgc->pcg_func, cg->pcg_func, cgc->pcg_count); 271203790Sfabient if (cgc->pcg_nchildren > 0) 272203790Sfabient pmcstat_cgnode_do_gmon_arcs(cgc, pmcid); 273203790Sfabient } 274203790Sfabient} 275203790Sfabient 276203790Sfabientstatic void 277203790Sfabientpmcstat_callgraph_do_gmon_arcs_for_pmcid(pmc_id_t pmcid) 278203790Sfabient{ 279203790Sfabient int n; 280203790Sfabient struct pmcstat_cgnode_hash *pch; 281203790Sfabient 282203790Sfabient for (n = 0; n < PMCSTAT_NHASH; n++) 283203790Sfabient LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) 284203790Sfabient if (pch->pch_pmcid == pmcid && 285203790Sfabient pch->pch_cgnode->pcg_nchildren > 1) 286203790Sfabient pmcstat_cgnode_do_gmon_arcs(pch->pch_cgnode, 287203790Sfabient pmcid); 288203790Sfabient} 289203790Sfabient 290203790Sfabient 291203790Sfabientstatic void 292203790Sfabientpmcstat_callgraph_do_gmon_arcs(void) 293203790Sfabient{ 294203790Sfabient struct pmcstat_pmcrecord *pmcr; 295203790Sfabient 296203790Sfabient LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) 297203790Sfabient pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmcr->pr_pmcid); 298203790Sfabient} 299203790Sfabient 300203790Sfabientvoid 301203790Sfabientpmcpl_gmon_initimage(struct pmcstat_image *pi) 302203790Sfabient{ 303203790Sfabient int count, nlen; 304203790Sfabient char *sn; 305203790Sfabient char name[NAME_MAX]; 306203790Sfabient 307203790Sfabient /* 308203790Sfabient * Look for a suitable name for the sample files associated 309203790Sfabient * with this image: if `basename(path)`+".gmon" is available, 310203790Sfabient * we use that, otherwise we try iterating through 311203790Sfabient * `basename(path)`+ "~" + NNN + ".gmon" till we get a free 312203790Sfabient * entry. 313203790Sfabient */ 314203790Sfabient if ((sn = basename(pmcstat_string_unintern(pi->pi_execpath))) == NULL) 315203790Sfabient err(EX_OSERR, "ERROR: Cannot process \"%s\"", 316203790Sfabient pmcstat_string_unintern(pi->pi_execpath)); 317203790Sfabient 318203790Sfabient nlen = strlen(sn); 319203790Sfabient nlen = min(nlen, (int) (sizeof(name) - sizeof(".gmon"))); 320203790Sfabient 321203790Sfabient snprintf(name, sizeof(name), "%.*s.gmon", nlen, sn); 322203790Sfabient 323203790Sfabient /* try use the unabridged name first */ 324203790Sfabient if (pmcstat_string_lookup(name) == NULL) 325203790Sfabient pi->pi_samplename = pmcstat_string_intern(name); 326203790Sfabient else { 327203790Sfabient /* 328203790Sfabient * Otherwise use a prefix from the original name and 329228990Suqs * up to 3 digits. 330203790Sfabient */ 331203790Sfabient nlen = strlen(sn); 332203790Sfabient nlen = min(nlen, (int) (sizeof(name)-sizeof("~NNN.gmon"))); 333203790Sfabient count = 0; 334203790Sfabient do { 335203790Sfabient if (++count > 999) 336227524Sobrien errx(EX_CANTCREAT, 337227524Sobrien "ERROR: cannot create a gmon file for" 338227524Sobrien " \"%s\"", name); 339203790Sfabient snprintf(name, sizeof(name), "%.*s~%3.3d.gmon", 340203790Sfabient nlen, sn, count); 341203790Sfabient if (pmcstat_string_lookup(name) == NULL) { 342203790Sfabient pi->pi_samplename = 343203790Sfabient pmcstat_string_intern(name); 344203790Sfabient count = 0; 345203790Sfabient } 346203790Sfabient } while (count > 0); 347203790Sfabient } 348203790Sfabient 349203790Sfabient LIST_INIT(&pi->pi_gmlist); 350203790Sfabient} 351203790Sfabient 352203790Sfabientvoid 353203790Sfabientpmcpl_gmon_shutdownimage(struct pmcstat_image *pi) 354203790Sfabient{ 355203790Sfabient struct pmcstat_gmonfile *pgf, *pgftmp; 356203790Sfabient 357203790Sfabient LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next, pgftmp) { 358203790Sfabient if (pgf->pgf_file) 359203790Sfabient (void) fclose(pgf->pgf_file); 360203790Sfabient LIST_REMOVE(pgf, pgf_next); 361203790Sfabient free(pgf); 362203790Sfabient } 363203790Sfabient} 364203790Sfabient 365203790Sfabientvoid 366203790Sfabientpmcpl_gmon_newpmc(pmcstat_interned_string ps, struct pmcstat_pmcrecord *pr) 367203790Sfabient{ 368203790Sfabient struct stat st; 369203790Sfabient char fullpath[PATH_MAX]; 370203790Sfabient 371203790Sfabient (void) pr; 372203790Sfabient 373203790Sfabient /* 374203790Sfabient * Create the appropriate directory to hold gmon.out files. 375203790Sfabient */ 376203790Sfabient 377203790Sfabient (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", args.pa_samplesdir, 378203790Sfabient pmcstat_string_unintern(ps)); 379203790Sfabient 380203790Sfabient /* If the path name exists, it should be a directory */ 381203790Sfabient if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) 382203790Sfabient return; 383203790Sfabient 384203790Sfabient if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) 385203790Sfabient err(EX_OSERR, "ERROR: Cannot create directory \"%s\"", 386203790Sfabient fullpath); 387203790Sfabient} 388203790Sfabient 389203790Sfabient/* 390203790Sfabient * Increment the bucket in the gmon.out file corresponding to 'pmcid' 391203790Sfabient * and 'pc'. 392203790Sfabient */ 393203790Sfabient 394203790Sfabientvoid 395203790Sfabientpmcpl_gmon_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, 396203790Sfabient uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) 397203790Sfabient{ 398203790Sfabient struct pmcstat_pcmap *map; 399203790Sfabient struct pmcstat_image *image; 400203790Sfabient struct pmcstat_gmonfile *pgf; 401203790Sfabient uintfptr_t bucket; 402203790Sfabient HISTCOUNTER *hc; 403203790Sfabient pmc_id_t pmcid; 404203790Sfabient 405203790Sfabient (void) nsamples; (void) usermode; (void) cpu; 406203790Sfabient 407203790Sfabient map = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, cc[0]); 408203790Sfabient if (map == NULL) { 409203790Sfabient /* Unknown offset. */ 410203790Sfabient pmcstat_stats.ps_samples_unknown_offset++; 411203790Sfabient return; 412203790Sfabient } 413203790Sfabient 414203790Sfabient assert(cc[0] >= map->ppm_lowpc && cc[0] < map->ppm_highpc); 415203790Sfabient 416203790Sfabient image = map->ppm_image; 417203790Sfabient pmcid = pmcr->pr_pmcid; 418203790Sfabient 419203790Sfabient /* 420203790Sfabient * If this is the first time we are seeing a sample for 421203790Sfabient * this executable image, try determine its parameters. 422203790Sfabient */ 423203790Sfabient if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 424203790Sfabient pmcstat_image_determine_type(image); 425203790Sfabient 426203790Sfabient assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); 427203790Sfabient 428203790Sfabient /* Ignore samples in images that we know nothing about. */ 429203790Sfabient if (image->pi_type == PMCSTAT_IMAGE_INDETERMINABLE) { 430203790Sfabient pmcstat_stats.ps_samples_indeterminable++; 431203790Sfabient return; 432203790Sfabient } 433203790Sfabient 434203790Sfabient /* 435203790Sfabient * Find the gmon file corresponding to 'pmcid', creating it if 436203790Sfabient * needed. 437203790Sfabient */ 438203790Sfabient pgf = pmcstat_image_find_gmonfile(image, pmcid); 439203790Sfabient if (pgf == NULL) { 440203790Sfabient if ((pgf = calloc(1, sizeof(*pgf))) == NULL) 441203790Sfabient err(EX_OSERR, "ERROR:"); 442203790Sfabient 443203790Sfabient pgf->pgf_gmondata = NULL; /* mark as unmapped */ 444203790Sfabient pgf->pgf_name = pmcstat_gmon_create_name(args.pa_samplesdir, 445203790Sfabient image, pmcid); 446203790Sfabient pgf->pgf_pmcid = pmcid; 447203790Sfabient assert(image->pi_end > image->pi_start); 448203790Sfabient pgf->pgf_nbuckets = (image->pi_end - image->pi_start) / 449203790Sfabient FUNCTION_ALIGNMENT; /* see <machine/profile.h> */ 450203790Sfabient pgf->pgf_ndatabytes = sizeof(struct gmonhdr) + 451203790Sfabient pgf->pgf_nbuckets * sizeof(HISTCOUNTER); 452203790Sfabient pgf->pgf_nsamples = 0; 453203790Sfabient pgf->pgf_file = NULL; 454203790Sfabient 455203790Sfabient pmcstat_gmon_create_file(pgf, image); 456203790Sfabient 457203790Sfabient LIST_INSERT_HEAD(&image->pi_gmlist, pgf, pgf_next); 458203790Sfabient } 459203790Sfabient 460203790Sfabient /* 461203790Sfabient * Map the gmon file in if needed. It may have been mapped 462203790Sfabient * out under memory pressure. 463203790Sfabient */ 464203790Sfabient if (pgf->pgf_gmondata == NULL) 465203790Sfabient pmcstat_gmon_map_file(pgf); 466203790Sfabient 467203790Sfabient assert(pgf->pgf_gmondata != NULL); 468203790Sfabient 469203790Sfabient /* 470203790Sfabient * 471203790Sfabient */ 472203790Sfabient 473203790Sfabient bucket = (cc[0] - map->ppm_lowpc) / FUNCTION_ALIGNMENT; 474203790Sfabient 475203790Sfabient assert(bucket < pgf->pgf_nbuckets); 476203790Sfabient 477203790Sfabient hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + 478203790Sfabient sizeof(struct gmonhdr)); 479203790Sfabient 480203790Sfabient /* saturating add */ 481203790Sfabient if (hc[bucket] < 0xFFFFU) /* XXX tie this to sizeof(HISTCOUNTER) */ 482203790Sfabient hc[bucket]++; 483203790Sfabient else /* mark that an overflow occurred */ 484203790Sfabient pgf->pgf_overflow = 1; 485203790Sfabient 486203790Sfabient pgf->pgf_nsamples++; 487203790Sfabient} 488203790Sfabient 489203790Sfabient/* 490203790Sfabient * Shutdown module. 491203790Sfabient */ 492203790Sfabient 493203790Sfabientvoid 494203790Sfabientpmcpl_gmon_shutdown(FILE *mf) 495203790Sfabient{ 496203790Sfabient int i; 497203790Sfabient struct pmcstat_gmonfile *pgf; 498203790Sfabient struct pmcstat_image *pi; 499203790Sfabient 500203790Sfabient /* 501203790Sfabient * Sync back all gprof flat profile data. 502203790Sfabient */ 503203790Sfabient for (i = 0; i < PMCSTAT_NHASH; i++) { 504203790Sfabient LIST_FOREACH(pi, &pmcstat_image_hash[i], pi_next) { 505203790Sfabient if (mf) 506203790Sfabient (void) fprintf(mf, " \"%s\" => \"%s\"", 507203790Sfabient pmcstat_string_unintern(pi->pi_execpath), 508203790Sfabient pmcstat_string_unintern( 509203790Sfabient pi->pi_samplename)); 510203790Sfabient 511203790Sfabient /* flush gmon.out data to disk */ 512203790Sfabient LIST_FOREACH(pgf, &pi->pi_gmlist, pgf_next) { 513203790Sfabient pmcstat_gmon_unmap_file(pgf); 514203790Sfabient if (mf) 515203790Sfabient (void) fprintf(mf, " %s/%d", 516203790Sfabient pmcstat_pmcid_to_name( 517203790Sfabient pgf->pgf_pmcid), 518203790Sfabient pgf->pgf_nsamples); 519203790Sfabient if (pgf->pgf_overflow && args.pa_verbosity >= 1) 520227524Sobrien warnx( 521227524Sobrien"WARNING: profile \"%s\" overflowed.", 522203790Sfabient pmcstat_string_unintern( 523203790Sfabient pgf->pgf_name)); 524203790Sfabient } 525203790Sfabient 526203790Sfabient if (mf) 527203790Sfabient (void) fprintf(mf, "\n"); 528203790Sfabient } 529203790Sfabient } 530203790Sfabient 531203790Sfabient /* 532203790Sfabient * Compute arcs and add these to the gprof files. 533203790Sfabient */ 534203790Sfabient if (args.pa_flags & FLAG_DO_GPROF && args.pa_graphdepth > 1) 535203790Sfabient pmcstat_callgraph_do_gmon_arcs(); 536203790Sfabient} 537