115139246SJoseph Koshy /*- 215139246SJoseph Koshy * Copyright (c) 2005, Joseph Koshy 315139246SJoseph Koshy * All rights reserved. 415139246SJoseph Koshy * 515139246SJoseph Koshy * Redistribution and use in source and binary forms, with or without 615139246SJoseph Koshy * modification, are permitted provided that the following conditions 715139246SJoseph Koshy * are met: 815139246SJoseph Koshy * 1. Redistributions of source code must retain the above copyright 915139246SJoseph Koshy * notice, this list of conditions and the following disclaimer. 1015139246SJoseph Koshy * 2. Redistributions in binary form must reproduce the above copyright 1115139246SJoseph Koshy * notice, this list of conditions and the following disclaimer in the 1215139246SJoseph Koshy * documentation and/or other materials provided with the distribution. 1315139246SJoseph Koshy * 1415139246SJoseph Koshy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1515139246SJoseph Koshy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1615139246SJoseph Koshy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1715139246SJoseph Koshy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1815139246SJoseph Koshy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1915139246SJoseph Koshy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2015139246SJoseph Koshy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2115139246SJoseph Koshy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2215139246SJoseph Koshy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2315139246SJoseph Koshy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2415139246SJoseph Koshy * SUCH DAMAGE. 2515139246SJoseph Koshy */ 2615139246SJoseph Koshy 2715139246SJoseph Koshy #include <sys/cdefs.h> 2815139246SJoseph Koshy __FBSDID("$FreeBSD$"); 2915139246SJoseph Koshy 3015139246SJoseph Koshy /* 3115139246SJoseph Koshy * Transform a hwpmc(4) log into human readable form and into gprof(1) 3215139246SJoseph Koshy * compatible profiles. 3315139246SJoseph Koshy */ 3415139246SJoseph Koshy 3515139246SJoseph Koshy #include <sys/param.h> 3615139246SJoseph Koshy #include <sys/endian.h> 3715139246SJoseph Koshy #include <sys/gmon.h> 3815139246SJoseph Koshy #include <sys/imgact_aout.h> 3915139246SJoseph Koshy #include <sys/imgact_elf.h> 4015139246SJoseph Koshy #include <sys/mman.h> 4115139246SJoseph Koshy #include <sys/pmc.h> 4215139246SJoseph Koshy #include <sys/queue.h> 4315139246SJoseph Koshy #include <sys/stat.h> 4415139246SJoseph Koshy #include <sys/wait.h> 4515139246SJoseph Koshy 4615139246SJoseph Koshy #include <netinet/in.h> 4715139246SJoseph Koshy 4815139246SJoseph Koshy #include <assert.h> 4915139246SJoseph Koshy #include <err.h> 5015139246SJoseph Koshy #include <fcntl.h> 5115139246SJoseph Koshy #include <libgen.h> 5215139246SJoseph Koshy #include <limits.h> 5315139246SJoseph Koshy #include <pmc.h> 5415139246SJoseph Koshy #include <pmclog.h> 5515139246SJoseph Koshy #include <sysexits.h> 5615139246SJoseph Koshy #include <stdint.h> 5715139246SJoseph Koshy #include <stdio.h> 5815139246SJoseph Koshy #include <stdlib.h> 5915139246SJoseph Koshy #include <string.h> 6015139246SJoseph Koshy #include <unistd.h> 6115139246SJoseph Koshy 6215139246SJoseph Koshy #include "pmcstat.h" 6315139246SJoseph Koshy 6415139246SJoseph Koshy #define min(A,B) ((A) < (B) ? (A) : (B)) 6515139246SJoseph Koshy #define max(A,B) ((A) > (B) ? (A) : (B)) 6615139246SJoseph Koshy 6715139246SJoseph Koshy /* 6815139246SJoseph Koshy * A simple implementation to intern strings. Each interned string is 6915139246SJoseph Koshy * assigned a unique address, so that subsequent string compares can 7015139246SJoseph Koshy * be done by a simple pointer comparision. 7115139246SJoseph Koshy */ 7215139246SJoseph Koshy 7315139246SJoseph Koshy struct pmcstat_string { 7415139246SJoseph Koshy LIST_ENTRY(pmcstat_string) ps_next; /* hash link */ 7515139246SJoseph Koshy int ps_len; 7615139246SJoseph Koshy int ps_hash; 7715139246SJoseph Koshy const char *ps_string; 7815139246SJoseph Koshy }; 7915139246SJoseph Koshy 8015139246SJoseph Koshy static LIST_HEAD(,pmcstat_string) pmcstat_string_hash[PMCSTAT_NHASH]; 8115139246SJoseph Koshy 8215139246SJoseph Koshy /* 8315139246SJoseph Koshy * 'pmcstat_pmcs' is a mapping for PMC ids to their human-readable 8415139246SJoseph Koshy * names. 8515139246SJoseph Koshy */ 8615139246SJoseph Koshy 8715139246SJoseph Koshy struct pmcstat_pmcrecord { 8815139246SJoseph Koshy LIST_ENTRY(pmcstat_pmcrecord) pr_next; 8915139246SJoseph Koshy pmc_id_t pr_pmcid; 9015139246SJoseph Koshy const char *pr_pmcname; 9115139246SJoseph Koshy }; 9215139246SJoseph Koshy 9315139246SJoseph Koshy static LIST_HEAD(,pmcstat_pmcrecord) pmcstat_pmcs = 9415139246SJoseph Koshy LIST_HEAD_INITIALIZER(&pmcstat_pmcs); 9515139246SJoseph Koshy 9615139246SJoseph Koshy struct pmcstat_gmonfile { 9715139246SJoseph Koshy LIST_ENTRY(pmcstat_gmonfile) pgf_next; /* list of entries */ 9815139246SJoseph Koshy pmc_id_t pgf_pmcid; /* id of the associated pmc */ 9915139246SJoseph Koshy size_t pgf_nsamples; /* number of samples in this gmon.out */ 10015139246SJoseph Koshy const char *pgf_name; /* name of gmon.out file */ 10115139246SJoseph Koshy size_t pgf_ndatabytes; /* number of bytes mapped */ 10215139246SJoseph Koshy void *pgf_gmondata; /* pointer to mmap'ed data */ 10315139246SJoseph Koshy }; 10415139246SJoseph Koshy 10515139246SJoseph Koshy static TAILQ_HEAD(,pmcstat_gmonfile) pmcstat_gmonfiles = 10615139246SJoseph Koshy TAILQ_HEAD_INITIALIZER(pmcstat_gmonfiles); 10715139246SJoseph Koshy 10815139246SJoseph Koshy #define GM_TO_BUCKETS(GM) ((uint16_t *) ((char *) (GM) + sizeof(*(GM)))) 10915139246SJoseph Koshy 11015139246SJoseph Koshy /* 11115139246SJoseph Koshy * A 'pmcstat_image' structure describes an executable program on 11215139246SJoseph Koshy * disk. 'pi_internedpath' is a cookie representing the pathname of 11315139246SJoseph Koshy * the executable. 'pi_start' and 'pi_end' are the least and greatest 11415139246SJoseph Koshy * virtual addresses for the text segments in the executable. 11515139246SJoseph Koshy * 'pi_gmonlist' contains a linked list of gmon.out files associated 11615139246SJoseph Koshy * with this image. 11715139246SJoseph Koshy */ 11815139246SJoseph Koshy 11915139246SJoseph Koshy enum pmcstat_image_type { 12015139246SJoseph Koshy PMCSTAT_IMAGE_UNKNOWN = 0, 12115139246SJoseph Koshy PMCSTAT_IMAGE_ELF, 12215139246SJoseph Koshy PMCSTAT_IMAGE_AOUT 12315139246SJoseph Koshy }; 12415139246SJoseph Koshy 12515139246SJoseph Koshy struct pmcstat_image { 12615139246SJoseph Koshy LIST_ENTRY(pmcstat_image) pi_next; /* hash link */ 12715139246SJoseph Koshy TAILQ_ENTRY(pmcstat_image) pi_lru; /* LRU list */ 12815139246SJoseph Koshy const char *pi_internedpath; /* cookie */ 12915139246SJoseph Koshy const char *pi_samplename; /* sample file name */ 13015139246SJoseph Koshy 13115139246SJoseph Koshy enum pmcstat_image_type pi_type; /* executable type */ 13215139246SJoseph Koshy uintfptr_t pi_start; /* start address (inclusive) */ 13315139246SJoseph Koshy uintfptr_t pi_end; /* end address (exclusive) */ 13415139246SJoseph Koshy int pi_isdynamic; /* whether a dynamic object */ 13515139246SJoseph Koshy 13615139246SJoseph Koshy LIST_HEAD(,pmcstat_gmonfile) pi_gmlist; 13715139246SJoseph Koshy }; 13815139246SJoseph Koshy 13915139246SJoseph Koshy static LIST_HEAD(,pmcstat_image) pmcstat_image_hash[PMCSTAT_NHASH]; 14015139246SJoseph Koshy static TAILQ_HEAD(,pmcstat_image) pmcstat_image_lru = 14115139246SJoseph Koshy TAILQ_HEAD_INITIALIZER(pmcstat_image_lru); 14215139246SJoseph Koshy 14315139246SJoseph Koshy struct pmcstat_pcmap { 14415139246SJoseph Koshy TAILQ_ENTRY(pmcstat_pcmap) ppm_next; 14515139246SJoseph Koshy uintfptr_t ppm_lowpc; 14615139246SJoseph Koshy uintfptr_t ppm_highpc; 14715139246SJoseph Koshy struct pmcstat_image *ppm_image; 14815139246SJoseph Koshy }; 14915139246SJoseph Koshy 15015139246SJoseph Koshy /* 15115139246SJoseph Koshy * A 'pmcstat_process' structure tracks processes. 15215139246SJoseph Koshy */ 15315139246SJoseph Koshy 15415139246SJoseph Koshy struct pmcstat_process { 15515139246SJoseph Koshy LIST_ENTRY(pmcstat_process) pp_next; /* hash-next */ 15615139246SJoseph Koshy pid_t pp_pid; /* associated pid */ 15715139246SJoseph Koshy int pp_isactive; /* whether active */ 15815139246SJoseph Koshy TAILQ_HEAD(,pmcstat_pcmap) pp_map; /* address range map */ 15915139246SJoseph Koshy }; 16015139246SJoseph Koshy 16115139246SJoseph Koshy static LIST_HEAD(,pmcstat_process) pmcstat_process_hash[PMCSTAT_NHASH]; 16215139246SJoseph Koshy 16315139246SJoseph Koshy static struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */ 16415139246SJoseph Koshy 16515139246SJoseph Koshy /* 16615139246SJoseph Koshy * Prototypes 16715139246SJoseph Koshy */ 16815139246SJoseph Koshy 16915139246SJoseph Koshy static void pmcstat_gmon_create_file(struct pmcstat_gmonfile *_pgf, 17015139246SJoseph Koshy struct pmcstat_image *_image); 17115139246SJoseph Koshy static const char *pmcstat_gmon_create_name(const char *_sd, 17215139246SJoseph Koshy struct pmcstat_image *_img, pmc_id_t _pmcid); 17315139246SJoseph Koshy static void pmcstat_gmon_map_file(struct pmcstat_gmonfile *_pgf); 17415139246SJoseph Koshy static void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf); 17515139246SJoseph Koshy 17615139246SJoseph Koshy static struct pmcstat_image *pmcstat_image_from_path(const char *_path); 17715139246SJoseph Koshy static enum pmcstat_image_type pmcstat_image_get_type(const char *_p); 17815139246SJoseph Koshy static void pmcstat_image_get_elf_params(struct pmcstat_image *_image, 17915139246SJoseph Koshy uintfptr_t *_minp, uintfptr_t *_maxp, int *_isdyn); 18015139246SJoseph Koshy static void pmcstat_image_increment_bucket(struct pmcstat_pcmap *_pcm, 18115139246SJoseph Koshy uintfptr_t _pc, pmc_id_t _pmcid, struct pmcstat_args *_a); 18215139246SJoseph Koshy static void pmcstat_image_link(struct pmcstat_process *_pp, 18315139246SJoseph Koshy struct pmcstat_image *_i, uintfptr_t _lpc, uintfptr_t _hpc); 18415139246SJoseph Koshy 18515139246SJoseph Koshy static void pmcstat_pmcid_add(pmc_id_t _pmcid, const char *_name, 18615139246SJoseph Koshy struct pmcstat_args *_a); 18715139246SJoseph Koshy static const char *pmcstat_pmcid_to_name(pmc_id_t _pmcid); 18815139246SJoseph Koshy 18915139246SJoseph Koshy static void pmcstat_process_add_elf_image(struct pmcstat_process *_pp, 19015139246SJoseph Koshy const char *_path); 19115139246SJoseph Koshy static struct pmcstat_process *pmcstat_process_lookup(pid_t _pid, int _allocate); 19215139246SJoseph Koshy static struct pmcstat_pcmap *pmcstat_process_find_map( 19315139246SJoseph Koshy struct pmcstat_process *_p, uintfptr_t _pc); 19415139246SJoseph Koshy static void pmcstat_process_new_image(struct pmcstat_process *_pp, 19515139246SJoseph Koshy const char *_path); 19615139246SJoseph Koshy 19715139246SJoseph Koshy static int pmcstat_string_compute_hash(const char *_string); 19815139246SJoseph Koshy static const char *pmcstat_string_intern(const char *_s); 19915139246SJoseph Koshy static struct pmcstat_string *pmcstat_string_lookup(const char *_s); 20015139246SJoseph Koshy 20115139246SJoseph Koshy 20215139246SJoseph Koshy /* 20315139246SJoseph Koshy * Create a gmon.out file and size it. 20415139246SJoseph Koshy */ 20515139246SJoseph Koshy 20615139246SJoseph Koshy static void 20715139246SJoseph Koshy pmcstat_gmon_create_file(struct pmcstat_gmonfile *pgf, 20815139246SJoseph Koshy struct pmcstat_image *image) 20915139246SJoseph Koshy { 21015139246SJoseph Koshy int fd; 21115139246SJoseph Koshy size_t count; 21215139246SJoseph Koshy struct gmonhdr gm; 21315139246SJoseph Koshy char buffer[DEFAULT_BUFFER_SIZE]; 21415139246SJoseph Koshy 21515139246SJoseph Koshy if ((fd = open(pgf->pgf_name, O_RDWR|O_NOFOLLOW|O_CREAT, 21615139246SJoseph Koshy S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) 21715139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot open \"%s\"", pgf->pgf_name); 21815139246SJoseph Koshy 21915139246SJoseph Koshy gm.lpc = image->pi_start; 22015139246SJoseph Koshy gm.hpc = image->pi_end; 22115139246SJoseph Koshy gm.ncnt = pgf->pgf_nsamples; 22215139246SJoseph Koshy gm.version = GMONVERSION; 22315139246SJoseph Koshy gm.profrate = 0; /* use ticks */ 22415139246SJoseph Koshy gm.histcounter_type = 0; /* compatibility with moncontrol() */ 22515139246SJoseph Koshy gm.spare[0] = gm.spare[1] = 0; 22615139246SJoseph Koshy 22715139246SJoseph Koshy /* Write out the gmon header */ 22815139246SJoseph Koshy if (write(fd, &gm, sizeof(gm)) < 0) 22915139246SJoseph Koshy goto error; 23015139246SJoseph Koshy 23115139246SJoseph Koshy /* Zero fill the samples[] array */ 23215139246SJoseph Koshy (void) memset(buffer, 0, sizeof(buffer)); 23315139246SJoseph Koshy 23415139246SJoseph Koshy count = pgf->pgf_ndatabytes - sizeof(struct gmonhdr); 23515139246SJoseph Koshy while (count > sizeof(buffer)) { 23615139246SJoseph Koshy if (write(fd, &buffer, sizeof(buffer)) < 0) 23715139246SJoseph Koshy goto error; 23815139246SJoseph Koshy count -= sizeof(buffer); 23915139246SJoseph Koshy } 24015139246SJoseph Koshy 24115139246SJoseph Koshy if (write(fd, &buffer, count) < 0) 24215139246SJoseph Koshy goto error; 24315139246SJoseph Koshy 24415139246SJoseph Koshy (void) close(fd); 24515139246SJoseph Koshy 24615139246SJoseph Koshy return; 24715139246SJoseph Koshy 24815139246SJoseph Koshy error: 24915139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot write \"%s\"", pgf->pgf_name); 25015139246SJoseph Koshy } 25115139246SJoseph Koshy 25215139246SJoseph Koshy const char * 25315139246SJoseph Koshy pmcstat_gmon_create_name(const char *samplesdir, struct pmcstat_image *image, 25415139246SJoseph Koshy pmc_id_t pmcid) 25515139246SJoseph Koshy { 25615139246SJoseph Koshy const char *pmcname; 25715139246SJoseph Koshy char fullpath[PATH_MAX]; 25815139246SJoseph Koshy 25915139246SJoseph Koshy pmcname = pmcstat_pmcid_to_name(pmcid); 26015139246SJoseph Koshy 26115139246SJoseph Koshy (void) snprintf(fullpath, sizeof(fullpath), 26215139246SJoseph Koshy "%s/%s/%s", samplesdir, pmcname, image->pi_samplename); 26315139246SJoseph Koshy 26415139246SJoseph Koshy return pmcstat_string_intern(fullpath); 26515139246SJoseph Koshy } 26615139246SJoseph Koshy 26715139246SJoseph Koshy 26815139246SJoseph Koshy static void 26915139246SJoseph Koshy pmcstat_gmon_map_file(struct pmcstat_gmonfile *pgf) 27015139246SJoseph Koshy { 27115139246SJoseph Koshy int fd; 27215139246SJoseph Koshy 27315139246SJoseph Koshy /* the gmon.out file must already exist */ 27415139246SJoseph Koshy if ((fd = open(pgf->pgf_name, O_RDWR | O_NOFOLLOW, 0)) < 0) 27515139246SJoseph Koshy err(EX_OSERR, "ERROR: cannot open \"%s\"", 27615139246SJoseph Koshy pgf->pgf_name); 27715139246SJoseph Koshy 27815139246SJoseph Koshy pgf->pgf_gmondata = mmap(NULL, pgf->pgf_ndatabytes, 27915139246SJoseph Koshy PROT_READ|PROT_WRITE, MAP_NOSYNC|MAP_SHARED, fd, 0); 28015139246SJoseph Koshy 28115139246SJoseph Koshy if (pgf->pgf_gmondata == MAP_FAILED) 28215139246SJoseph Koshy /* XXX unmap a few files and try again? */ 28315139246SJoseph Koshy err(EX_OSERR, "ERROR: cannot map \"%s\"", pgf->pgf_name); 28415139246SJoseph Koshy 28515139246SJoseph Koshy (void) close(fd); 28615139246SJoseph Koshy } 28715139246SJoseph Koshy 28815139246SJoseph Koshy /* 28915139246SJoseph Koshy * Unmap the data mapped from a gmon.out file. 29015139246SJoseph Koshy */ 29115139246SJoseph Koshy 29215139246SJoseph Koshy static void 29315139246SJoseph Koshy pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *pgf) 29415139246SJoseph Koshy { 29515139246SJoseph Koshy (void) msync(pgf->pgf_gmondata, pgf->pgf_ndatabytes, 29615139246SJoseph Koshy MS_SYNC); 29715139246SJoseph Koshy (void) munmap(pgf->pgf_gmondata, pgf->pgf_ndatabytes); 29815139246SJoseph Koshy pgf->pgf_gmondata = NULL; 29915139246SJoseph Koshy } 30015139246SJoseph Koshy 30115139246SJoseph Koshy static void 30215139246SJoseph Koshy pmcstat_image_get_elf_params(struct pmcstat_image *image, uintfptr_t *minp, 30315139246SJoseph Koshy uintfptr_t *maxp, int *is_dynamic) 30415139246SJoseph Koshy { 30515139246SJoseph Koshy int fd, i; 30615139246SJoseph Koshy struct stat st; 30715139246SJoseph Koshy void *mapbase; 30815139246SJoseph Koshy uintfptr_t minva, maxva; 30915139246SJoseph Koshy const Elf_Ehdr *h; 31015139246SJoseph Koshy const Elf_Phdr *ph; 31115139246SJoseph Koshy const Elf_Shdr *sh; 31215139246SJoseph Koshy const char *path; 31315139246SJoseph Koshy 31415139246SJoseph Koshy minva = ~(uintfptr_t) 0; 31515139246SJoseph Koshy maxva = (uintfptr_t) 0; 31615139246SJoseph Koshy path = image->pi_internedpath; 31715139246SJoseph Koshy 31815139246SJoseph Koshy if ((fd = open(path, O_RDONLY, 0)) < 0) 31915139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot open \"%s\"", path); 32015139246SJoseph Koshy 32115139246SJoseph Koshy if (fstat(fd, &st) < 0) 32215139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot stat \"%s\"", path); 32315139246SJoseph Koshy 32415139246SJoseph Koshy if ((mapbase = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == 32515139246SJoseph Koshy MAP_FAILED) 32615139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot mmap \"%s\"", path); 32715139246SJoseph Koshy 32815139246SJoseph Koshy (void) close(fd); 32915139246SJoseph Koshy 33015139246SJoseph Koshy h = (const Elf_Ehdr *) mapbase; 33115139246SJoseph Koshy if (!IS_ELF(*h)) 33215139246SJoseph Koshy err(EX_SOFTWARE, "ERROR: \"%s\" not an ELF file", path); 33315139246SJoseph Koshy 33415139246SJoseph Koshy sh = (const Elf_Shdr *)((const char *) mapbase + h->e_shoff); 33515139246SJoseph Koshy 33615139246SJoseph Koshy if (h->e_type == ET_EXEC || h->e_type == ET_DYN) { 33715139246SJoseph Koshy /* 33815139246SJoseph Koshy * Some kind of shared object: find the min,max va for 33915139246SJoseph Koshy * its executable sections. 34015139246SJoseph Koshy */ 34115139246SJoseph Koshy for (i = 0; i < h->e_shnum; i++) 34215139246SJoseph Koshy if (sh[i].sh_flags & SHF_EXECINSTR) { /* code */ 34315139246SJoseph Koshy minva = min(minva, sh[i].sh_addr); 34415139246SJoseph Koshy maxva = max(maxva, sh[i].sh_addr + 34515139246SJoseph Koshy sh[i].sh_size); 34615139246SJoseph Koshy } 34715139246SJoseph Koshy } else 34815139246SJoseph Koshy err(EX_DATAERR, "ERROR: Unknown file type for \"%s\"", 34915139246SJoseph Koshy image->pi_internedpath); 35015139246SJoseph Koshy 35115139246SJoseph Koshy *is_dynamic = 0; 35215139246SJoseph Koshy if (h->e_type == ET_EXEC) { 35315139246SJoseph Koshy ph = (const Elf_Phdr *)((const char *) mapbase + h->e_phoff); 35415139246SJoseph Koshy for (i = 0; i < h->e_phnum; i++) { 35515139246SJoseph Koshy switch (ph[i].p_type) { 35615139246SJoseph Koshy case PT_DYNAMIC: 35715139246SJoseph Koshy *is_dynamic = 1; 35815139246SJoseph Koshy break; 35915139246SJoseph Koshy } 36015139246SJoseph Koshy } 36115139246SJoseph Koshy } 36215139246SJoseph Koshy 36315139246SJoseph Koshy if (munmap(mapbase, st.st_size) < 0) 36415139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot unmap \"%s\"", path); 36515139246SJoseph Koshy 36615139246SJoseph Koshy *minp = minva; 36715139246SJoseph Koshy *maxp = maxva; 36815139246SJoseph Koshy 36915139246SJoseph Koshy } 37015139246SJoseph Koshy 37115139246SJoseph Koshy /* 37215139246SJoseph Koshy * Locate an image descriptor given an interned path. 37315139246SJoseph Koshy */ 37415139246SJoseph Koshy 37515139246SJoseph Koshy static struct pmcstat_image * 37615139246SJoseph Koshy pmcstat_image_from_path(const char *internedpath) 37715139246SJoseph Koshy { 37815139246SJoseph Koshy int count, hash, nlen; 37915139246SJoseph Koshy struct pmcstat_image *pi; 38015139246SJoseph Koshy char *sn; 38115139246SJoseph Koshy char name[NAME_MAX]; 38215139246SJoseph Koshy 38315139246SJoseph Koshy hash = pmcstat_string_compute_hash(internedpath); 38415139246SJoseph Koshy 38515139246SJoseph Koshy /* look for an existing entry */ 38615139246SJoseph Koshy LIST_FOREACH(pi, &pmcstat_image_hash[hash], pi_next) 38715139246SJoseph Koshy if (pi->pi_internedpath == internedpath) { 38815139246SJoseph Koshy /* move descriptor to the head of the lru list */ 38915139246SJoseph Koshy TAILQ_REMOVE(&pmcstat_image_lru, pi, pi_lru); 39015139246SJoseph Koshy TAILQ_INSERT_HEAD(&pmcstat_image_lru, pi, pi_lru); 39115139246SJoseph Koshy return pi; 39215139246SJoseph Koshy } 39315139246SJoseph Koshy 39415139246SJoseph Koshy /* 39515139246SJoseph Koshy * allocate a new entry and place at the head of the hash and 39615139246SJoseph Koshy * LRU lists 39715139246SJoseph Koshy */ 39815139246SJoseph Koshy pi = malloc(sizeof(*pi)); 39915139246SJoseph Koshy if (pi == NULL) 40015139246SJoseph Koshy return NULL; 40115139246SJoseph Koshy 40215139246SJoseph Koshy pi->pi_type = PMCSTAT_IMAGE_UNKNOWN; 40315139246SJoseph Koshy pi->pi_internedpath = internedpath; 40415139246SJoseph Koshy pi->pi_start = ~0; 40515139246SJoseph Koshy pi->pi_end = 0; 40615139246SJoseph Koshy 40715139246SJoseph Koshy /* look for a suitable name for the sample files */ 40815139246SJoseph Koshy if ((sn = basename(internedpath)) == NULL) 40915139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot process \"%s\"", internedpath); 41015139246SJoseph Koshy 41115139246SJoseph Koshy nlen = strlen(sn); 41215139246SJoseph Koshy nlen = min(nlen, (int) sizeof(name) - 6); /* ".gmon\0" */ 41315139246SJoseph Koshy 41415139246SJoseph Koshy snprintf(name, sizeof(name), "%.*s.gmon", 41515139246SJoseph Koshy nlen, sn); 41615139246SJoseph Koshy 41715139246SJoseph Koshy if (pmcstat_string_lookup(name) == NULL) 41815139246SJoseph Koshy pi->pi_samplename = pmcstat_string_intern(name); 41915139246SJoseph Koshy else { 42015139246SJoseph Koshy nlen = strlen(sn); 42115139246SJoseph Koshy nlen = min(nlen, (int) sizeof(name)-10); /* "~ddd.gmon\0" */ 42215139246SJoseph Koshy count = 0; 42315139246SJoseph Koshy do { 42415139246SJoseph Koshy count++; 42515139246SJoseph Koshy snprintf(name, sizeof(name), "%.*s~%3.3d", 42615139246SJoseph Koshy nlen, sn, count); 42715139246SJoseph Koshy if (pmcstat_string_lookup(name) == NULL) { 42815139246SJoseph Koshy pi->pi_samplename = pmcstat_string_intern(name); 42915139246SJoseph Koshy count = 0; 43015139246SJoseph Koshy } 43115139246SJoseph Koshy } while (count > 0); 43215139246SJoseph Koshy } 43315139246SJoseph Koshy 43415139246SJoseph Koshy LIST_INIT(&pi->pi_gmlist); 43515139246SJoseph Koshy 43615139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_image_hash[hash], pi, pi_next); 43715139246SJoseph Koshy TAILQ_INSERT_HEAD(&pmcstat_image_lru, pi, pi_lru); 43815139246SJoseph Koshy 43915139246SJoseph Koshy return pi; 44015139246SJoseph Koshy } 44115139246SJoseph Koshy 44215139246SJoseph Koshy /* 44315139246SJoseph Koshy * Given an open file, determine its file type. 44415139246SJoseph Koshy */ 44515139246SJoseph Koshy 44615139246SJoseph Koshy static enum pmcstat_image_type 44715139246SJoseph Koshy pmcstat_image_get_type(const char *path) 44815139246SJoseph Koshy { 44915139246SJoseph Koshy int fd; 45015139246SJoseph Koshy Elf_Ehdr *eh; 45115139246SJoseph Koshy struct exec *ex; 45215139246SJoseph Koshy ssize_t nbytes; 45315139246SJoseph Koshy char buffer[DEFAULT_BUFFER_SIZE]; 45415139246SJoseph Koshy 45515139246SJoseph Koshy if ((fd = open(path, O_RDONLY)) < 0) 45615139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot open \"%s\"", path); 45715139246SJoseph Koshy 45815139246SJoseph Koshy if ((nbytes = pread(fd, buffer, sizeof(buffer), 0)) < 0) 45915139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot read \"%s\"", path); 46015139246SJoseph Koshy 46115139246SJoseph Koshy (void) close(fd); 46215139246SJoseph Koshy 46315139246SJoseph Koshy /* check if its an ELF file */ 46415139246SJoseph Koshy if ((unsigned) nbytes >= sizeof(Elf_Ehdr)) { 46515139246SJoseph Koshy eh = (Elf_Ehdr *) buffer; 46615139246SJoseph Koshy if (IS_ELF(*eh)) 46715139246SJoseph Koshy return PMCSTAT_IMAGE_ELF; 46815139246SJoseph Koshy } 46915139246SJoseph Koshy 47015139246SJoseph Koshy /* Look for an A.OUT header */ 47115139246SJoseph Koshy if ((unsigned) nbytes >= sizeof(struct exec)) { 47215139246SJoseph Koshy ex = (struct exec *) buffer; 47315139246SJoseph Koshy if (!N_BADMAG(*ex)) 47415139246SJoseph Koshy return PMCSTAT_IMAGE_AOUT; 47515139246SJoseph Koshy } 47615139246SJoseph Koshy 47715139246SJoseph Koshy return PMCSTAT_IMAGE_UNKNOWN; 47815139246SJoseph Koshy } 47915139246SJoseph Koshy 48015139246SJoseph Koshy /* 48115139246SJoseph Koshy * Increment the bucket in the gmon.out file corresponding to 'pmcid' 48215139246SJoseph Koshy * and 'pc'. 48315139246SJoseph Koshy */ 48415139246SJoseph Koshy 48515139246SJoseph Koshy static void 48615139246SJoseph Koshy pmcstat_image_increment_bucket(struct pmcstat_pcmap *map, uintfptr_t pc, 48715139246SJoseph Koshy pmc_id_t pmcid, struct pmcstat_args *a) 48815139246SJoseph Koshy { 48915139246SJoseph Koshy struct pmcstat_image *image; 49015139246SJoseph Koshy struct pmcstat_gmonfile *pgf; 49115139246SJoseph Koshy uintfptr_t bucket; 49215139246SJoseph Koshy HISTCOUNTER *hc; 49315139246SJoseph Koshy 49415139246SJoseph Koshy assert(pc >= map->ppm_lowpc && pc < map->ppm_highpc); 49515139246SJoseph Koshy 49615139246SJoseph Koshy /* 49715139246SJoseph Koshy * Find the gmon file corresponding to 'pmcid', creating it if 49815139246SJoseph Koshy * needed. 49915139246SJoseph Koshy */ 50015139246SJoseph Koshy 50115139246SJoseph Koshy image = map->ppm_image; 50215139246SJoseph Koshy 50315139246SJoseph Koshy LIST_FOREACH(pgf, &image->pi_gmlist, pgf_next) 50415139246SJoseph Koshy if (pgf->pgf_pmcid == pmcid) 50515139246SJoseph Koshy break; 50615139246SJoseph Koshy 50715139246SJoseph Koshy /* If we don't have a gmon.out file for this PMCid, create one */ 50815139246SJoseph Koshy if (pgf == NULL) { 50915139246SJoseph Koshy if ((pgf = calloc(1, sizeof(*pgf))) == NULL) 51015139246SJoseph Koshy err(EX_OSERR, "ERROR:"); 51115139246SJoseph Koshy 51215139246SJoseph Koshy pgf->pgf_gmondata = NULL; /* mark as unmapped */ 51315139246SJoseph Koshy pgf->pgf_name = pmcstat_gmon_create_name(a->pa_samplesdir, 51415139246SJoseph Koshy image, pmcid); 51515139246SJoseph Koshy pgf->pgf_pmcid = pmcid; 51615139246SJoseph Koshy pgf->pgf_nsamples = (image->pi_end - image->pi_start) / 51715139246SJoseph Koshy FUNCTION_ALIGNMENT; /* see <machine/profile.h> */ 51815139246SJoseph Koshy pgf->pgf_ndatabytes = sizeof(struct gmonhdr) + 51915139246SJoseph Koshy pgf->pgf_nsamples * sizeof(HISTCOUNTER); 52015139246SJoseph Koshy 52115139246SJoseph Koshy pmcstat_gmon_create_file(pgf, image); 52215139246SJoseph Koshy 52315139246SJoseph Koshy LIST_INSERT_HEAD(&image->pi_gmlist, pgf, pgf_next); 52415139246SJoseph Koshy } 52515139246SJoseph Koshy 52615139246SJoseph Koshy /* 52715139246SJoseph Koshy * Map the gmon file in if needed. It may have been mapped 52815139246SJoseph Koshy * out under memory pressure. 52915139246SJoseph Koshy */ 53015139246SJoseph Koshy if (pgf->pgf_gmondata == NULL) 53115139246SJoseph Koshy pmcstat_gmon_map_file(pgf); 53215139246SJoseph Koshy 53315139246SJoseph Koshy bucket = (pc - map->ppm_lowpc) / FUNCTION_ALIGNMENT; 53415139246SJoseph Koshy 53515139246SJoseph Koshy assert(bucket < pgf->pgf_nsamples); 53615139246SJoseph Koshy 53715139246SJoseph Koshy hc = (HISTCOUNTER *) ((char *) pgf->pgf_gmondata + 53815139246SJoseph Koshy sizeof(struct gmonhdr)); 53915139246SJoseph Koshy hc[bucket]++; 54015139246SJoseph Koshy 54115139246SJoseph Koshy } 54215139246SJoseph Koshy 54315139246SJoseph Koshy /* 54415139246SJoseph Koshy * Record the fact that PC values from 'lowpc' to 'highpc' come from 54515139246SJoseph Koshy * image 'image'. 54615139246SJoseph Koshy */ 54715139246SJoseph Koshy 54815139246SJoseph Koshy static void 54915139246SJoseph Koshy pmcstat_image_link(struct pmcstat_process *pp, struct pmcstat_image *image, 55015139246SJoseph Koshy uintfptr_t lowpc, uintfptr_t highpc) 55115139246SJoseph Koshy { 55215139246SJoseph Koshy struct pmcstat_pcmap *pcm, *pcmnew; 55315139246SJoseph Koshy 55415139246SJoseph Koshy if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL) 55515139246SJoseph Koshy err(EX_OSERR, "ERROR: "); 55615139246SJoseph Koshy 55715139246SJoseph Koshy pcmnew->ppm_lowpc = lowpc; 55815139246SJoseph Koshy pcmnew->ppm_highpc = highpc; 55915139246SJoseph Koshy pcmnew->ppm_image = image; 56015139246SJoseph Koshy 56115139246SJoseph Koshy TAILQ_FOREACH(pcm, &pp->pp_map, ppm_next) 56215139246SJoseph Koshy if (pcm->ppm_lowpc < lowpc) 56315139246SJoseph Koshy break; 56415139246SJoseph Koshy 56515139246SJoseph Koshy if (pcm == NULL) 56615139246SJoseph Koshy TAILQ_INSERT_TAIL(&pp->pp_map, pcmnew, ppm_next); 56715139246SJoseph Koshy else 56815139246SJoseph Koshy TAILQ_INSERT_BEFORE(pcm, pcmnew, ppm_next); 56915139246SJoseph Koshy } 57015139246SJoseph Koshy 57115139246SJoseph Koshy /* 57215139246SJoseph Koshy * Add a {pmcid,name} mapping. 57315139246SJoseph Koshy */ 57415139246SJoseph Koshy 57515139246SJoseph Koshy static void 57615139246SJoseph Koshy pmcstat_pmcid_add(pmc_id_t pmcid, const char *name, struct pmcstat_args *a) 57715139246SJoseph Koshy { 57815139246SJoseph Koshy struct pmcstat_pmcrecord *pr; 57915139246SJoseph Koshy struct stat st; 58015139246SJoseph Koshy char fullpath[PATH_MAX]; 58115139246SJoseph Koshy 58215139246SJoseph Koshy LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 58315139246SJoseph Koshy if (pr->pr_pmcid == pmcid) { 58415139246SJoseph Koshy pr->pr_pmcname = name; 58515139246SJoseph Koshy return; 58615139246SJoseph Koshy } 58715139246SJoseph Koshy 58815139246SJoseph Koshy if ((pr = malloc(sizeof(*pr))) == NULL) 58915139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate pmc record"); 59015139246SJoseph Koshy 59115139246SJoseph Koshy pr->pr_pmcid = pmcid; 59215139246SJoseph Koshy pr->pr_pmcname = name; 59315139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); 59415139246SJoseph Koshy 59515139246SJoseph Koshy (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", a->pa_samplesdir, 59615139246SJoseph Koshy name); 59715139246SJoseph Koshy 59815139246SJoseph Koshy /* If the path name exists, it should be a directory */ 59915139246SJoseph Koshy if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) 60015139246SJoseph Koshy return; 60115139246SJoseph Koshy 60215139246SJoseph Koshy if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) 60315139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot create directory \"%s\"", 60415139246SJoseph Koshy fullpath); 60515139246SJoseph Koshy } 60615139246SJoseph Koshy 60715139246SJoseph Koshy /* 60815139246SJoseph Koshy * Given a pmcid in use, find its human-readable name, or a 60915139246SJoseph Koshy */ 61015139246SJoseph Koshy 61115139246SJoseph Koshy static const char * 61215139246SJoseph Koshy pmcstat_pmcid_to_name(pmc_id_t pmcid) 61315139246SJoseph Koshy { 61415139246SJoseph Koshy struct pmcstat_pmcrecord *pr; 61515139246SJoseph Koshy char fullpath[PATH_MAX]; 61615139246SJoseph Koshy 61715139246SJoseph Koshy LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 61815139246SJoseph Koshy if (pr->pr_pmcid == pmcid) 61915139246SJoseph Koshy return pr->pr_pmcname; 62015139246SJoseph Koshy 62115139246SJoseph Koshy /* create a default name and add this entry */ 62215139246SJoseph Koshy if ((pr = malloc(sizeof(*pr))) == NULL) 62315139246SJoseph Koshy err(EX_OSERR, "ERROR: "); 62415139246SJoseph Koshy pr->pr_pmcid = pmcid; 62515139246SJoseph Koshy 62615139246SJoseph Koshy (void) snprintf(fullpath, sizeof(fullpath), "%X", (unsigned int) pmcid); 62715139246SJoseph Koshy pr->pr_pmcname = pmcstat_string_intern(fullpath); 62815139246SJoseph Koshy 62915139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); 63015139246SJoseph Koshy 63115139246SJoseph Koshy return pr->pr_pmcname; 63215139246SJoseph Koshy } 63315139246SJoseph Koshy 63415139246SJoseph Koshy /* 63515139246SJoseph Koshy * Associate an ELF image with a process. Argument 'path' names the 63615139246SJoseph Koshy * executable while 'fd' is an already open descriptor to it. 63715139246SJoseph Koshy */ 63815139246SJoseph Koshy 63915139246SJoseph Koshy static void 64015139246SJoseph Koshy pmcstat_process_add_elf_image(struct pmcstat_process *pp, const char *path) 64115139246SJoseph Koshy { 64215139246SJoseph Koshy int isdynamic; 64315139246SJoseph Koshy size_t linelen; 64415139246SJoseph Koshy FILE *rf; 64515139246SJoseph Koshy char *line; 64615139246SJoseph Koshy uintfptr_t minva, maxva; 64715139246SJoseph Koshy uintmax_t libstart; 64815139246SJoseph Koshy struct pmcstat_image *image; 64915139246SJoseph Koshy char libpath[PATH_MAX]; 65015139246SJoseph Koshy char command[PATH_MAX + sizeof(PMCSTAT_LDD_COMMAND) + 1]; 65115139246SJoseph Koshy 65215139246SJoseph Koshy minva = ~ (uintfptr_t) 0; 65315139246SJoseph Koshy maxva = (uintfptr_t) 0; 65415139246SJoseph Koshy isdynamic = 0; 65515139246SJoseph Koshy 65615139246SJoseph Koshy if ((image = pmcstat_image_from_path(path)) == NULL) 65715139246SJoseph Koshy return; 65815139246SJoseph Koshy 65915139246SJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) { 66015139246SJoseph Koshy 66115139246SJoseph Koshy pmcstat_image_get_elf_params(image, &minva, &maxva, 66215139246SJoseph Koshy &isdynamic); 66315139246SJoseph Koshy 66415139246SJoseph Koshy image->pi_type = PMCSTAT_IMAGE_ELF; 66515139246SJoseph Koshy image->pi_start = minva; 66615139246SJoseph Koshy image->pi_end = maxva; 66715139246SJoseph Koshy image->pi_isdynamic = isdynamic; 66815139246SJoseph Koshy } 66915139246SJoseph Koshy 67015139246SJoseph Koshy /* create a map entry for the base executable */ 67115139246SJoseph Koshy pmcstat_image_link(pp, image, minva, maxva); 67215139246SJoseph Koshy 67315139246SJoseph Koshy if (image->pi_isdynamic) { 67415139246SJoseph Koshy 67515139246SJoseph Koshy (void) snprintf(command, sizeof(command), "%s %s", 67615139246SJoseph Koshy PMCSTAT_LDD_COMMAND, path); 67715139246SJoseph Koshy 67815139246SJoseph Koshy if ((rf = popen(command, "r")) == NULL) 67915139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot create pipe"); 68015139246SJoseph Koshy 68115139246SJoseph Koshy (void) fgetln(rf, &linelen); 68215139246SJoseph Koshy 68315139246SJoseph Koshy while (!feof(rf) && !ferror(rf)) { 68415139246SJoseph Koshy 68515139246SJoseph Koshy if ((line = fgetln(rf, &linelen)) == NULL) 68615139246SJoseph Koshy continue; 68715139246SJoseph Koshy line[linelen-1] = '\0'; 68815139246SJoseph Koshy 68915139246SJoseph Koshy if (sscanf(line, "%s %jx", 69015139246SJoseph Koshy libpath, &libstart) != 2) 69115139246SJoseph Koshy continue; 69215139246SJoseph Koshy 69315139246SJoseph Koshy image = pmcstat_image_from_path( 69415139246SJoseph Koshy pmcstat_string_intern(libpath)); 69515139246SJoseph Koshy if (image == NULL) 69615139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot process " 69715139246SJoseph Koshy "\"%s\"", libpath); 69815139246SJoseph Koshy 69915139246SJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) { 70015139246SJoseph Koshy 70115139246SJoseph Koshy pmcstat_image_get_elf_params(image, 70215139246SJoseph Koshy &minva, &maxva, &isdynamic); 70315139246SJoseph Koshy 70415139246SJoseph Koshy image->pi_type = PMCSTAT_IMAGE_ELF; 70515139246SJoseph Koshy image->pi_start = minva; 70615139246SJoseph Koshy image->pi_end = maxva; 70715139246SJoseph Koshy image->pi_isdynamic = isdynamic; 70815139246SJoseph Koshy } 70915139246SJoseph Koshy 71015139246SJoseph Koshy pmcstat_image_link(pp, image, libstart + image->pi_start, 71115139246SJoseph Koshy libstart + image->pi_end); 71215139246SJoseph Koshy } 71315139246SJoseph Koshy 71415139246SJoseph Koshy (void) pclose(rf); 71515139246SJoseph Koshy 71615139246SJoseph Koshy } 71715139246SJoseph Koshy } 71815139246SJoseph Koshy 71915139246SJoseph Koshy /* 72015139246SJoseph Koshy * Find the process descriptor corresponding to a PID. If 'allocate' 72115139246SJoseph Koshy * is zero, we return a NULL if a pid descriptor could not be found or 72215139246SJoseph Koshy * a process descriptor process. If 'allocate' is non-zero, then we 72315139246SJoseph Koshy * will attempt to allocate a fresh process descriptor. Zombie 72415139246SJoseph Koshy * process descriptors are only removed if a fresh allocation for the 72515139246SJoseph Koshy * same PID is requested. 72615139246SJoseph Koshy */ 72715139246SJoseph Koshy 72815139246SJoseph Koshy static struct pmcstat_process * 72915139246SJoseph Koshy pmcstat_process_lookup(pid_t pid, int allocate) 73015139246SJoseph Koshy { 73115139246SJoseph Koshy uint32_t hash; 73215139246SJoseph Koshy struct pmcstat_pcmap *ppm, *ppmtmp; 73315139246SJoseph Koshy struct pmcstat_process *pp, *pptmp; 73415139246SJoseph Koshy 73515139246SJoseph Koshy hash = (uint32_t) pid & PMCSTAT_HASH_MASK; /* simplicity wins */ 73615139246SJoseph Koshy 73715139246SJoseph Koshy LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[hash], pp_next, pptmp) 73815139246SJoseph Koshy if (pp->pp_pid == pid) { 73915139246SJoseph Koshy /* Found a descriptor, check and process zombies */ 74015139246SJoseph Koshy if (allocate && !pp->pp_isactive) { 74115139246SJoseph Koshy /* remove maps */ 74215139246SJoseph Koshy TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, 74315139246SJoseph Koshy ppmtmp) { 74415139246SJoseph Koshy TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 74515139246SJoseph Koshy free(ppm); 74615139246SJoseph Koshy } 74715139246SJoseph Koshy /* remove process entry */ 74815139246SJoseph Koshy LIST_REMOVE(pp, pp_next); 74915139246SJoseph Koshy free(pp); 75015139246SJoseph Koshy break; 75115139246SJoseph Koshy } 75215139246SJoseph Koshy return pp; 75315139246SJoseph Koshy } 75415139246SJoseph Koshy 75515139246SJoseph Koshy if (!allocate) 75615139246SJoseph Koshy return NULL; 75715139246SJoseph Koshy 75815139246SJoseph Koshy if ((pp = malloc(sizeof(*pp))) == NULL) 75915139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate pid descriptor"); 76015139246SJoseph Koshy 76115139246SJoseph Koshy pp->pp_pid = pid; 76215139246SJoseph Koshy pp->pp_isactive = 1; 76315139246SJoseph Koshy 76415139246SJoseph Koshy TAILQ_INIT(&pp->pp_map); 76515139246SJoseph Koshy 76615139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_process_hash[hash], pp, pp_next); 76715139246SJoseph Koshy return pp; 76815139246SJoseph Koshy } 76915139246SJoseph Koshy 77015139246SJoseph Koshy /* 77115139246SJoseph Koshy * Find the map entry associated with process 'p' at PC value 'pc'. 77215139246SJoseph Koshy */ 77315139246SJoseph Koshy 77415139246SJoseph Koshy static struct pmcstat_pcmap * 77515139246SJoseph Koshy pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc) 77615139246SJoseph Koshy { 77715139246SJoseph Koshy struct pmcstat_pcmap *ppm; 77815139246SJoseph Koshy 77915139246SJoseph Koshy TAILQ_FOREACH(ppm, &p->pp_map, ppm_next) 78015139246SJoseph Koshy if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc) 78115139246SJoseph Koshy return ppm; 78215139246SJoseph Koshy 78315139246SJoseph Koshy return NULL; 78415139246SJoseph Koshy } 78515139246SJoseph Koshy 78615139246SJoseph Koshy /* 78715139246SJoseph Koshy * Associate an image and a process. 78815139246SJoseph Koshy */ 78915139246SJoseph Koshy 79015139246SJoseph Koshy static void 79115139246SJoseph Koshy pmcstat_process_new_image(struct pmcstat_process *pp, const char *path) 79215139246SJoseph Koshy { 79315139246SJoseph Koshy enum pmcstat_image_type filetype; 79415139246SJoseph Koshy struct pmcstat_image *image; 79515139246SJoseph Koshy 79615139246SJoseph Koshy if ((image = pmcstat_image_from_path(path)) == NULL) 79715139246SJoseph Koshy return; 79815139246SJoseph Koshy 79915139246SJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 80015139246SJoseph Koshy filetype = pmcstat_image_get_type(path); 80115139246SJoseph Koshy else 80215139246SJoseph Koshy filetype = image->pi_type; 80315139246SJoseph Koshy 80415139246SJoseph Koshy switch (filetype) { 80515139246SJoseph Koshy case PMCSTAT_IMAGE_ELF: 80615139246SJoseph Koshy pmcstat_process_add_elf_image(pp, path); 80715139246SJoseph Koshy break; 80815139246SJoseph Koshy 80915139246SJoseph Koshy case PMCSTAT_IMAGE_AOUT: 81015139246SJoseph Koshy break; 81115139246SJoseph Koshy 81215139246SJoseph Koshy default: 81315139246SJoseph Koshy err(EX_SOFTWARE, "ERROR: Unsupported executable type \"%s\"", 81415139246SJoseph Koshy path); 81515139246SJoseph Koshy } 81615139246SJoseph Koshy } 81715139246SJoseph Koshy 81815139246SJoseph Koshy 81915139246SJoseph Koshy 82015139246SJoseph Koshy /* 82115139246SJoseph Koshy * Compute a 'hash' value for a string. 82215139246SJoseph Koshy */ 82315139246SJoseph Koshy 82415139246SJoseph Koshy static int 82515139246SJoseph Koshy pmcstat_string_compute_hash(const char *s) 82615139246SJoseph Koshy { 82715139246SJoseph Koshy int hash; 82815139246SJoseph Koshy 82915139246SJoseph Koshy for (hash = 0; *s; s++) 83015139246SJoseph Koshy hash ^= *s; 83115139246SJoseph Koshy 83215139246SJoseph Koshy return hash & PMCSTAT_HASH_MASK; 83315139246SJoseph Koshy } 83415139246SJoseph Koshy 83515139246SJoseph Koshy /* 83615139246SJoseph Koshy * Intern a copy of string 's', and return a pointer to it. 83715139246SJoseph Koshy */ 83815139246SJoseph Koshy 83915139246SJoseph Koshy static const char * 84015139246SJoseph Koshy pmcstat_string_intern(const char *s) 84115139246SJoseph Koshy { 84215139246SJoseph Koshy struct pmcstat_string *ps; 84315139246SJoseph Koshy int hash, len; 84415139246SJoseph Koshy 84515139246SJoseph Koshy hash = pmcstat_string_compute_hash(s); 84615139246SJoseph Koshy len = strlen(s); 84715139246SJoseph Koshy 84815139246SJoseph Koshy if ((ps = pmcstat_string_lookup(s)) != NULL) 84915139246SJoseph Koshy return ps->ps_string; 85015139246SJoseph Koshy 85115139246SJoseph Koshy if ((ps = malloc(sizeof(*ps))) == NULL) 85215139246SJoseph Koshy err(EX_OSERR, "ERROR: Could not intern string"); 85315139246SJoseph Koshy ps->ps_len = len; 85415139246SJoseph Koshy ps->ps_hash = hash; 85515139246SJoseph Koshy ps->ps_string = strdup(s); 85615139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_string_hash[hash], ps, ps_next); 85715139246SJoseph Koshy return ps->ps_string; 85815139246SJoseph Koshy } 85915139246SJoseph Koshy 86015139246SJoseph Koshy static struct pmcstat_string * 86115139246SJoseph Koshy pmcstat_string_lookup(const char *s) 86215139246SJoseph Koshy { 86315139246SJoseph Koshy struct pmcstat_string *ps; 86415139246SJoseph Koshy int hash, len; 86515139246SJoseph Koshy 86615139246SJoseph Koshy hash = pmcstat_string_compute_hash(s); 86715139246SJoseph Koshy len = strlen(s); 86815139246SJoseph Koshy 86915139246SJoseph Koshy LIST_FOREACH(ps, &pmcstat_string_hash[hash], ps_next) 87015139246SJoseph Koshy if (ps->ps_len == len && ps->ps_hash == hash && 87115139246SJoseph Koshy strcmp(ps->ps_string, s) == 0) 87215139246SJoseph Koshy return ps; 87315139246SJoseph Koshy return NULL; 87415139246SJoseph Koshy } 87515139246SJoseph Koshy 87615139246SJoseph Koshy /* 87715139246SJoseph Koshy * Public Interfaces. 87815139246SJoseph Koshy */ 87915139246SJoseph Koshy 88015139246SJoseph Koshy /* 88115139246SJoseph Koshy * Close a logfile, after first flushing all in-module queued data. 88215139246SJoseph Koshy */ 88315139246SJoseph Koshy 88415139246SJoseph Koshy int 88515139246SJoseph Koshy pmcstat_close_log(struct pmcstat_args *a) 88615139246SJoseph Koshy { 88715139246SJoseph Koshy if (pmc_flush_logfile() < 0 || 88815139246SJoseph Koshy pmc_configure_logfile(-1) < 0) 88915139246SJoseph Koshy err(EX_OSERR, "ERROR: logging failed"); 89015139246SJoseph Koshy a->pa_flags &= ~(FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE); 89115139246SJoseph Koshy return a->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING : 89215139246SJoseph Koshy PMCSTAT_FINISHED; 89315139246SJoseph Koshy } 89415139246SJoseph Koshy 89515139246SJoseph Koshy 89615139246SJoseph Koshy int 89715139246SJoseph Koshy pmcstat_convert_log(struct pmcstat_args *a) 89815139246SJoseph Koshy { 89915139246SJoseph Koshy uintfptr_t pc; 90015139246SJoseph Koshy struct pmcstat_process *pp, *ppnew; 90115139246SJoseph Koshy struct pmcstat_pcmap *ppm, *ppmtmp; 90215139246SJoseph Koshy struct pmclog_ev ev; 90315139246SJoseph Koshy const char *image_path; 90415139246SJoseph Koshy 90515139246SJoseph Koshy while (pmclog_read(a->pa_logparser, &ev) == 0) { 90615139246SJoseph Koshy assert(ev.pl_state == PMCLOG_OK); 90715139246SJoseph Koshy 90815139246SJoseph Koshy switch (ev.pl_type) { 90915139246SJoseph Koshy case PMCLOG_TYPE_MAPPINGCHANGE: 91015139246SJoseph Koshy /* 91115139246SJoseph Koshy * Introduce an address range mapping for a 91215139246SJoseph Koshy * process. 91315139246SJoseph Koshy */ 91415139246SJoseph Koshy break; 91515139246SJoseph Koshy 91615139246SJoseph Koshy case PMCLOG_TYPE_PCSAMPLE: 91715139246SJoseph Koshy 91815139246SJoseph Koshy /* 91915139246SJoseph Koshy * We bring in the gmon file for the image 92015139246SJoseph Koshy * currently associated with the PMC & pid 92115139246SJoseph Koshy * pair and increment the appropriate entry 92215139246SJoseph Koshy * bin inside this. 92315139246SJoseph Koshy */ 92415139246SJoseph Koshy pc = ev.pl_u.pl_s.pl_pc; 92515139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_s.pl_pid, 1); 92615139246SJoseph Koshy if ((ppm = pmcstat_process_find_map(pp, pc)) == NULL && 92715139246SJoseph Koshy (ppm = pmcstat_process_find_map(pmcstat_kernproc, 92815139246SJoseph Koshy pc)) == NULL) { 92915139246SJoseph Koshy printf("!%d unknown %jx\n", pp->pp_pid, 93015139246SJoseph Koshy (uintmax_t) pc); 93115139246SJoseph Koshy break; /* unknown process,offset pair */ 93215139246SJoseph Koshy } 93315139246SJoseph Koshy 93415139246SJoseph Koshy pmcstat_image_increment_bucket(ppm, pc, 93515139246SJoseph Koshy ev.pl_u.pl_s.pl_pmcid, a); 93615139246SJoseph Koshy 93715139246SJoseph Koshy break; 93815139246SJoseph Koshy 93915139246SJoseph Koshy case PMCLOG_TYPE_PMCALLOCATE: 94015139246SJoseph Koshy /* 94115139246SJoseph Koshy * Record the association pmc id between this 94215139246SJoseph Koshy * PMC and its name. 94315139246SJoseph Koshy */ 94415139246SJoseph Koshy pmcstat_pmcid_add(ev.pl_u.pl_a.pl_pmcid, 94515139246SJoseph Koshy pmcstat_string_intern(ev.pl_u.pl_a.pl_evname), a); 94615139246SJoseph Koshy break; 94715139246SJoseph Koshy 94815139246SJoseph Koshy case PMCLOG_TYPE_PROCEXEC: 94915139246SJoseph Koshy 95015139246SJoseph Koshy /* 95115139246SJoseph Koshy * Change the executable image associated with 95215139246SJoseph Koshy * a process. 95315139246SJoseph Koshy */ 95415139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_x.pl_pid, 1); 95515139246SJoseph Koshy 95615139246SJoseph Koshy /* delete the current process map */ 95715139246SJoseph Koshy TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) { 95815139246SJoseph Koshy TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 95915139246SJoseph Koshy free(ppm); 96015139246SJoseph Koshy } 96115139246SJoseph Koshy 96215139246SJoseph Koshy /* locate the descriptor for the new 'base' image */ 96315139246SJoseph Koshy image_path = pmcstat_string_intern( 96415139246SJoseph Koshy ev.pl_u.pl_x.pl_pathname); 96515139246SJoseph Koshy 96615139246SJoseph Koshy /* link to the new image */ 96715139246SJoseph Koshy pmcstat_process_new_image(pp, image_path); 96815139246SJoseph Koshy break; 96915139246SJoseph Koshy 97015139246SJoseph Koshy case PMCLOG_TYPE_PROCEXIT: 97115139246SJoseph Koshy 97215139246SJoseph Koshy /* 97315139246SJoseph Koshy * Due to the way the log is generated, the 97415139246SJoseph Koshy * last few samples corresponding to a process 97515139246SJoseph Koshy * may appear in the log after the process 97615139246SJoseph Koshy * exit event is recorded. Thus we keep the 97715139246SJoseph Koshy * process' descriptor and associated data 97815139246SJoseph Koshy * structures around, but mark the process as 97915139246SJoseph Koshy * having exited. 98015139246SJoseph Koshy */ 98115139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_e.pl_pid, 0); 98215139246SJoseph Koshy if (pp == NULL) 98315139246SJoseph Koshy break; 98415139246SJoseph Koshy pp->pp_isactive = 0; /* make a zombie */ 98515139246SJoseph Koshy break; 98615139246SJoseph Koshy 98715139246SJoseph Koshy case PMCLOG_TYPE_SYSEXIT: 98815139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_se.pl_pid, 0); 98915139246SJoseph Koshy if (pp == NULL) 99015139246SJoseph Koshy break; 99115139246SJoseph Koshy pp->pp_isactive = 0; /* make a zombie */ 99215139246SJoseph Koshy break; 99315139246SJoseph Koshy 99415139246SJoseph Koshy case PMCLOG_TYPE_PROCFORK: 99515139246SJoseph Koshy 99615139246SJoseph Koshy /* 99715139246SJoseph Koshy * If we had been tracking 'oldpid', then clone 99815139246SJoseph Koshy * its pid descriptor. 99915139246SJoseph Koshy */ 100015139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_f.pl_oldpid, 0); 100115139246SJoseph Koshy if (pp == NULL) 100215139246SJoseph Koshy break; 100315139246SJoseph Koshy 100415139246SJoseph Koshy ppnew = 100515139246SJoseph Koshy pmcstat_process_lookup(ev.pl_u.pl_f.pl_newpid, 1); 100615139246SJoseph Koshy 100715139246SJoseph Koshy /* copy the old process' address maps */ 100815139246SJoseph Koshy TAILQ_FOREACH(ppm, &pp->pp_map, ppm_next) 100915139246SJoseph Koshy pmcstat_image_link(ppnew, ppm->ppm_image, 101015139246SJoseph Koshy ppm->ppm_lowpc, ppm->ppm_highpc); 101115139246SJoseph Koshy break; 101215139246SJoseph Koshy 101315139246SJoseph Koshy default: /* other types of entries are not relevant */ 101415139246SJoseph Koshy break; 101515139246SJoseph Koshy } 101615139246SJoseph Koshy } 101715139246SJoseph Koshy 101815139246SJoseph Koshy if (ev.pl_state == PMCLOG_EOF) 101915139246SJoseph Koshy return PMCSTAT_FINISHED; 102015139246SJoseph Koshy else if (ev.pl_state == PMCLOG_REQUIRE_DATA) 102115139246SJoseph Koshy return PMCSTAT_RUNNING; 102215139246SJoseph Koshy 102315139246SJoseph Koshy err(EX_DATAERR, "ERROR: event parsing failed (record %jd, " 102415139246SJoseph Koshy "offset 0x%jx)", (uintmax_t) ev.pl_count + 1, ev.pl_offset); 102515139246SJoseph Koshy } 102615139246SJoseph Koshy 102715139246SJoseph Koshy 102815139246SJoseph Koshy /* 102915139246SJoseph Koshy * Open a log file, for reading or writing. 103015139246SJoseph Koshy * 103115139246SJoseph Koshy * The function returns the fd of a successfully opened log or -1 in 103215139246SJoseph Koshy * case of failure. 103315139246SJoseph Koshy */ 103415139246SJoseph Koshy 103515139246SJoseph Koshy int 103615139246SJoseph Koshy pmcstat_open(const char *path, int mode) 103715139246SJoseph Koshy { 103815139246SJoseph Koshy int fd; 103915139246SJoseph Koshy 104015139246SJoseph Koshy /* 104115139246SJoseph Koshy * If 'path' is "-" then open one of stdin or stdout depending 104215139246SJoseph Koshy * on the value of 'mode'. Otherwise, treat 'path' as a file 104315139246SJoseph Koshy * name and open that. 104415139246SJoseph Koshy */ 104515139246SJoseph Koshy if (path[0] == '-' && path[1] == '\0') 104615139246SJoseph Koshy fd = (mode == PMCSTAT_OPEN_FOR_READ) ? 0 : 1; 104715139246SJoseph Koshy else 104815139246SJoseph Koshy fd = open(path, mode == PMCSTAT_OPEN_FOR_READ ? 104915139246SJoseph Koshy O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC), 105015139246SJoseph Koshy S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); 105115139246SJoseph Koshy 105215139246SJoseph Koshy return fd; 105315139246SJoseph Koshy } 105415139246SJoseph Koshy 105515139246SJoseph Koshy /* 105615139246SJoseph Koshy * Print log entries as text. 105715139246SJoseph Koshy */ 105815139246SJoseph Koshy 105915139246SJoseph Koshy int 106015139246SJoseph Koshy pmcstat_print_log(struct pmcstat_args *a) 106115139246SJoseph Koshy { 106215139246SJoseph Koshy struct pmclog_ev ev; 106315139246SJoseph Koshy 106415139246SJoseph Koshy while (pmclog_read(a->pa_logparser, &ev) == 0) { 106515139246SJoseph Koshy assert(ev.pl_state == PMCLOG_OK); 106615139246SJoseph Koshy switch (ev.pl_type) { 106715139246SJoseph Koshy case PMCLOG_TYPE_CLOSELOG: 106815139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"closelog",); 106915139246SJoseph Koshy break; 107015139246SJoseph Koshy case PMCLOG_TYPE_DROPNOTIFY: 107115139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"drop",); 107215139246SJoseph Koshy break; 107315139246SJoseph Koshy case PMCLOG_TYPE_INITIALIZE: 107415139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"initlog","0x%x \"%s\"", 107515139246SJoseph Koshy ev.pl_u.pl_i.pl_version, 107615139246SJoseph Koshy pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch)); 107715139246SJoseph Koshy break; 107815139246SJoseph Koshy case PMCLOG_TYPE_MAPPINGCHANGE: 107915139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"mapping","%s %d %p %p \"%s\"", 108015139246SJoseph Koshy ev.pl_u.pl_m.pl_type == PMCLOG_MAPPING_INSERT ? 108115139246SJoseph Koshy "insert" : "delete", 108215139246SJoseph Koshy ev.pl_u.pl_m.pl_pid, 108315139246SJoseph Koshy (void *) ev.pl_u.pl_m.pl_start, 108415139246SJoseph Koshy (void *) ev.pl_u.pl_m.pl_end, 108515139246SJoseph Koshy ev.pl_u.pl_m.pl_pathname); 108615139246SJoseph Koshy break; 108715139246SJoseph Koshy case PMCLOG_TYPE_PCSAMPLE: 108815139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"sample","0x%x %d %p %c", 108915139246SJoseph Koshy ev.pl_u.pl_s.pl_pmcid, 109015139246SJoseph Koshy ev.pl_u.pl_s.pl_pid, 109115139246SJoseph Koshy (void *) ev.pl_u.pl_s.pl_pc, 109215139246SJoseph Koshy ev.pl_u.pl_s.pl_usermode ? 'u' : 's'); 109315139246SJoseph Koshy break; 109415139246SJoseph Koshy case PMCLOG_TYPE_PMCALLOCATE: 109515139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"allocate","0x%x \"%s\" 0x%x", 109615139246SJoseph Koshy ev.pl_u.pl_a.pl_pmcid, 109715139246SJoseph Koshy ev.pl_u.pl_a.pl_evname, 109815139246SJoseph Koshy ev.pl_u.pl_a.pl_flags); 109915139246SJoseph Koshy break; 110015139246SJoseph Koshy case PMCLOG_TYPE_PMCATTACH: 110115139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"attach","0x%x %d \"%s\"", 110215139246SJoseph Koshy ev.pl_u.pl_t.pl_pmcid, 110315139246SJoseph Koshy ev.pl_u.pl_t.pl_pid, 110415139246SJoseph Koshy ev.pl_u.pl_t.pl_pathname); 110515139246SJoseph Koshy break; 110615139246SJoseph Koshy case PMCLOG_TYPE_PMCDETACH: 110715139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"detach","0x%x %d", 110815139246SJoseph Koshy ev.pl_u.pl_d.pl_pmcid, 110915139246SJoseph Koshy ev.pl_u.pl_d.pl_pid); 111015139246SJoseph Koshy break; 111115139246SJoseph Koshy case PMCLOG_TYPE_PROCCSW: 111215139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"cswval","0x%x %d %jd", 111315139246SJoseph Koshy ev.pl_u.pl_c.pl_pmcid, 111415139246SJoseph Koshy ev.pl_u.pl_c.pl_pid, 111515139246SJoseph Koshy ev.pl_u.pl_c.pl_value); 111615139246SJoseph Koshy break; 111715139246SJoseph Koshy case PMCLOG_TYPE_PROCEXEC: 111815139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"exec","0x%x %d %p \"%s\"", 111915139246SJoseph Koshy ev.pl_u.pl_x.pl_pmcid, 112015139246SJoseph Koshy ev.pl_u.pl_x.pl_pid, 112115139246SJoseph Koshy (void *) ev.pl_u.pl_x.pl_entryaddr, 112215139246SJoseph Koshy ev.pl_u.pl_x.pl_pathname); 112315139246SJoseph Koshy break; 112415139246SJoseph Koshy case PMCLOG_TYPE_PROCEXIT: 112515139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"exitval","0x%x %d %jd", 112615139246SJoseph Koshy ev.pl_u.pl_e.pl_pmcid, 112715139246SJoseph Koshy ev.pl_u.pl_e.pl_pid, 112815139246SJoseph Koshy ev.pl_u.pl_e.pl_value); 112915139246SJoseph Koshy break; 113015139246SJoseph Koshy case PMCLOG_TYPE_PROCFORK: 113115139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"fork","%d %d", 113215139246SJoseph Koshy ev.pl_u.pl_f.pl_oldpid, 113315139246SJoseph Koshy ev.pl_u.pl_f.pl_newpid); 113415139246SJoseph Koshy break; 113515139246SJoseph Koshy case PMCLOG_TYPE_USERDATA: 113615139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"userdata","0x%x", 113715139246SJoseph Koshy ev.pl_u.pl_u.pl_userdata); 113815139246SJoseph Koshy break; 113915139246SJoseph Koshy case PMCLOG_TYPE_SYSEXIT: 114015139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"exit","%d", 114115139246SJoseph Koshy ev.pl_u.pl_se.pl_pid); 114215139246SJoseph Koshy break; 114315139246SJoseph Koshy default: 114415139246SJoseph Koshy fprintf(a->pa_printfile, "unknown %d", 114515139246SJoseph Koshy ev.pl_type); 114615139246SJoseph Koshy } 114715139246SJoseph Koshy } 114815139246SJoseph Koshy 114915139246SJoseph Koshy if (ev.pl_state == PMCLOG_EOF) 115015139246SJoseph Koshy return PMCSTAT_FINISHED; 115115139246SJoseph Koshy else if (ev.pl_state == PMCLOG_REQUIRE_DATA) 115215139246SJoseph Koshy return PMCSTAT_RUNNING; 115315139246SJoseph Koshy 115415139246SJoseph Koshy err(EX_DATAERR, "ERROR: event parsing failed " 115515139246SJoseph Koshy "(record %jd, offset 0x%jx)", 115615139246SJoseph Koshy (uintmax_t) ev.pl_count + 1, ev.pl_offset); 115715139246SJoseph Koshy /*NOTREACHED*/ 115815139246SJoseph Koshy } 115915139246SJoseph Koshy 116015139246SJoseph Koshy /* 116115139246SJoseph Koshy * Process a log file in offline analysis mode. 116215139246SJoseph Koshy */ 116315139246SJoseph Koshy 116415139246SJoseph Koshy void 116515139246SJoseph Koshy pmcstat_process_log(struct pmcstat_args *a) 116615139246SJoseph Koshy { 116715139246SJoseph Koshy 116815139246SJoseph Koshy /* 116915139246SJoseph Koshy * If gprof style profiles haven't been asked for, just print the 117015139246SJoseph Koshy * log to the current output file. 117115139246SJoseph Koshy */ 117215139246SJoseph Koshy if (a->pa_flags & FLAG_DO_PRINT) 117315139246SJoseph Koshy pmcstat_print_log(a); 117415139246SJoseph Koshy else 117515139246SJoseph Koshy /* convert the log to gprof compatible profiles */ 117615139246SJoseph Koshy pmcstat_convert_log(a); 117715139246SJoseph Koshy 117815139246SJoseph Koshy return; 117915139246SJoseph Koshy } 118015139246SJoseph Koshy 118115139246SJoseph Koshy void 118215139246SJoseph Koshy pmcstat_initialize_logging(struct pmcstat_args *a) 118315139246SJoseph Koshy { 118415139246SJoseph Koshy int i, isdynamic; 118515139246SJoseph Koshy const char *kernpath; 118615139246SJoseph Koshy struct pmcstat_image *img; 118715139246SJoseph Koshy uintfptr_t minva, maxva; 118815139246SJoseph Koshy 118915139246SJoseph Koshy /* use a convenient format for 'ldd' output */ 119015139246SJoseph Koshy if (setenv("LD_TRACE_LOADED_OBJECTS_FMT1","%p %x\n",1) != 0) 119115139246SJoseph Koshy goto error; 119215139246SJoseph Koshy 119315139246SJoseph Koshy /* Initialize hash tables */ 119415139246SJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) { 119515139246SJoseph Koshy LIST_INIT(&pmcstat_image_hash[i]); 119615139246SJoseph Koshy LIST_INIT(&pmcstat_process_hash[i]); 119715139246SJoseph Koshy LIST_INIT(&pmcstat_string_hash[i]); 119815139246SJoseph Koshy } 119915139246SJoseph Koshy 120015139246SJoseph Koshy /* create a fake 'process' entry for the kernel with pid == -1 */ 120115139246SJoseph Koshy if ((pmcstat_kernproc = pmcstat_process_lookup((pid_t) -1, 1)) == NULL) 120215139246SJoseph Koshy goto error; 120315139246SJoseph Koshy 120415139246SJoseph Koshy if ((kernpath = pmcstat_string_intern(a->pa_kernel)) == NULL) 120515139246SJoseph Koshy goto error; 120615139246SJoseph Koshy 120715139246SJoseph Koshy img = pmcstat_image_from_path(kernpath); 120815139246SJoseph Koshy 120915139246SJoseph Koshy pmcstat_image_get_elf_params(img, &minva, &maxva, &isdynamic); 121015139246SJoseph Koshy img->pi_type = PMCSTAT_IMAGE_ELF; 121115139246SJoseph Koshy img->pi_start = minva; 121215139246SJoseph Koshy img->pi_end = maxva; 121315139246SJoseph Koshy 121415139246SJoseph Koshy pmcstat_image_link(pmcstat_kernproc, img, minva, maxva); 121515139246SJoseph Koshy 121615139246SJoseph Koshy return; 121715139246SJoseph Koshy 121815139246SJoseph Koshy error: 121915139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot initialize logging"); 122015139246SJoseph Koshy } 122115139246SJoseph Koshy 122215139246SJoseph Koshy void 122315139246SJoseph Koshy pmcstat_shutdown_logging(void) 122415139246SJoseph Koshy { 122515139246SJoseph Koshy int i; 122615139246SJoseph Koshy struct pmcstat_gmonfile *pgf, *pgftmp; 122715139246SJoseph Koshy struct pmcstat_image *pi, *pitmp; 122815139246SJoseph Koshy struct pmcstat_process *pp, *pptmp; 122915139246SJoseph Koshy struct pmcstat_string *ps, *pstmp; 123015139246SJoseph Koshy 123115139246SJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) { 123215139246SJoseph Koshy LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next, pitmp) { 123315139246SJoseph Koshy /* flush gmon.out data to disk */ 123415139246SJoseph Koshy LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next, 123515139246SJoseph Koshy pgftmp) { 123615139246SJoseph Koshy pmcstat_gmon_unmap_file(pgf); 123715139246SJoseph Koshy LIST_REMOVE(pgf, pgf_next); 123815139246SJoseph Koshy free(pgf); 123915139246SJoseph Koshy } 124015139246SJoseph Koshy 124115139246SJoseph Koshy LIST_REMOVE(pi, pi_next); 124215139246SJoseph Koshy free(pi); 124315139246SJoseph Koshy } 124415139246SJoseph Koshy LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[i], pp_next, 124515139246SJoseph Koshy pptmp) { 124615139246SJoseph Koshy LIST_REMOVE(pp, pp_next); 124715139246SJoseph Koshy free(pp); 124815139246SJoseph Koshy } 124915139246SJoseph Koshy LIST_FOREACH_SAFE(ps, &pmcstat_string_hash[i], ps_next, 125015139246SJoseph Koshy pstmp) { 125115139246SJoseph Koshy LIST_REMOVE(ps, ps_next); 125215139246SJoseph Koshy free(ps); 125315139246SJoseph Koshy } 125415139246SJoseph Koshy } 125515139246SJoseph Koshy } 1256