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 334bf840f4aSJoseph Koshy sh = (const Elf_Shdr *)((uintptr_t) 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) { 353bf840f4aSJoseph Koshy ph = (const Elf_Phdr *)((uintptr_t) 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; 450bf840f4aSJoseph Koshy Elf_Ehdr eh; 451bf840f4aSJoseph 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 458bf840f4aSJoseph Koshy nbytes = max(sizeof(eh), sizeof(ex)); 459bf840f4aSJoseph Koshy if ((nbytes = pread(fd, buffer, nbytes, 0)) < 0) 46015139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot read \"%s\"", path); 46115139246SJoseph Koshy 46215139246SJoseph Koshy (void) close(fd); 46315139246SJoseph Koshy 46415139246SJoseph Koshy /* check if its an ELF file */ 46515139246SJoseph Koshy if ((unsigned) nbytes >= sizeof(Elf_Ehdr)) { 466bf840f4aSJoseph Koshy bcopy(buffer, &eh, sizeof(eh)); 467bf840f4aSJoseph Koshy if (IS_ELF(eh)) 46815139246SJoseph Koshy return PMCSTAT_IMAGE_ELF; 46915139246SJoseph Koshy } 47015139246SJoseph Koshy 47115139246SJoseph Koshy /* Look for an A.OUT header */ 47215139246SJoseph Koshy if ((unsigned) nbytes >= sizeof(struct exec)) { 473bf840f4aSJoseph Koshy bcopy(buffer, &ex, sizeof(ex)); 474bf840f4aSJoseph Koshy if (!N_BADMAG(ex)) 47515139246SJoseph Koshy return PMCSTAT_IMAGE_AOUT; 47615139246SJoseph Koshy } 47715139246SJoseph Koshy 47815139246SJoseph Koshy return PMCSTAT_IMAGE_UNKNOWN; 47915139246SJoseph Koshy } 48015139246SJoseph Koshy 48115139246SJoseph Koshy /* 48215139246SJoseph Koshy * Increment the bucket in the gmon.out file corresponding to 'pmcid' 48315139246SJoseph Koshy * and 'pc'. 48415139246SJoseph Koshy */ 48515139246SJoseph Koshy 48615139246SJoseph Koshy static void 48715139246SJoseph Koshy pmcstat_image_increment_bucket(struct pmcstat_pcmap *map, uintfptr_t pc, 48815139246SJoseph Koshy pmc_id_t pmcid, struct pmcstat_args *a) 48915139246SJoseph Koshy { 49015139246SJoseph Koshy struct pmcstat_image *image; 49115139246SJoseph Koshy struct pmcstat_gmonfile *pgf; 49215139246SJoseph Koshy uintfptr_t bucket; 49315139246SJoseph Koshy HISTCOUNTER *hc; 49415139246SJoseph Koshy 49515139246SJoseph Koshy assert(pc >= map->ppm_lowpc && pc < map->ppm_highpc); 49615139246SJoseph Koshy 49715139246SJoseph Koshy /* 49815139246SJoseph Koshy * Find the gmon file corresponding to 'pmcid', creating it if 49915139246SJoseph Koshy * needed. 50015139246SJoseph Koshy */ 50115139246SJoseph Koshy 50215139246SJoseph Koshy image = map->ppm_image; 50315139246SJoseph Koshy 50415139246SJoseph Koshy LIST_FOREACH(pgf, &image->pi_gmlist, pgf_next) 50515139246SJoseph Koshy if (pgf->pgf_pmcid == pmcid) 50615139246SJoseph Koshy break; 50715139246SJoseph Koshy 50815139246SJoseph Koshy /* If we don't have a gmon.out file for this PMCid, create one */ 50915139246SJoseph Koshy if (pgf == NULL) { 51015139246SJoseph Koshy if ((pgf = calloc(1, sizeof(*pgf))) == NULL) 51115139246SJoseph Koshy err(EX_OSERR, "ERROR:"); 51215139246SJoseph Koshy 51315139246SJoseph Koshy pgf->pgf_gmondata = NULL; /* mark as unmapped */ 51415139246SJoseph Koshy pgf->pgf_name = pmcstat_gmon_create_name(a->pa_samplesdir, 51515139246SJoseph Koshy image, pmcid); 51615139246SJoseph Koshy pgf->pgf_pmcid = pmcid; 51715139246SJoseph Koshy pgf->pgf_nsamples = (image->pi_end - image->pi_start) / 51815139246SJoseph Koshy FUNCTION_ALIGNMENT; /* see <machine/profile.h> */ 51915139246SJoseph Koshy pgf->pgf_ndatabytes = sizeof(struct gmonhdr) + 52015139246SJoseph Koshy pgf->pgf_nsamples * sizeof(HISTCOUNTER); 52115139246SJoseph Koshy 52215139246SJoseph Koshy pmcstat_gmon_create_file(pgf, image); 52315139246SJoseph Koshy 52415139246SJoseph Koshy LIST_INSERT_HEAD(&image->pi_gmlist, pgf, pgf_next); 52515139246SJoseph Koshy } 52615139246SJoseph Koshy 52715139246SJoseph Koshy /* 52815139246SJoseph Koshy * Map the gmon file in if needed. It may have been mapped 52915139246SJoseph Koshy * out under memory pressure. 53015139246SJoseph Koshy */ 53115139246SJoseph Koshy if (pgf->pgf_gmondata == NULL) 53215139246SJoseph Koshy pmcstat_gmon_map_file(pgf); 53315139246SJoseph Koshy 53415139246SJoseph Koshy bucket = (pc - map->ppm_lowpc) / FUNCTION_ALIGNMENT; 53515139246SJoseph Koshy 53615139246SJoseph Koshy assert(bucket < pgf->pgf_nsamples); 53715139246SJoseph Koshy 538bf840f4aSJoseph Koshy hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + 53915139246SJoseph Koshy sizeof(struct gmonhdr)); 54015139246SJoseph Koshy hc[bucket]++; 54115139246SJoseph Koshy 54215139246SJoseph Koshy } 54315139246SJoseph Koshy 54415139246SJoseph Koshy /* 54515139246SJoseph Koshy * Record the fact that PC values from 'lowpc' to 'highpc' come from 54615139246SJoseph Koshy * image 'image'. 54715139246SJoseph Koshy */ 54815139246SJoseph Koshy 54915139246SJoseph Koshy static void 55015139246SJoseph Koshy pmcstat_image_link(struct pmcstat_process *pp, struct pmcstat_image *image, 55115139246SJoseph Koshy uintfptr_t lowpc, uintfptr_t highpc) 55215139246SJoseph Koshy { 55315139246SJoseph Koshy struct pmcstat_pcmap *pcm, *pcmnew; 55415139246SJoseph Koshy 55515139246SJoseph Koshy if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL) 55615139246SJoseph Koshy err(EX_OSERR, "ERROR: "); 55715139246SJoseph Koshy 55815139246SJoseph Koshy pcmnew->ppm_lowpc = lowpc; 55915139246SJoseph Koshy pcmnew->ppm_highpc = highpc; 56015139246SJoseph Koshy pcmnew->ppm_image = image; 56115139246SJoseph Koshy 56215139246SJoseph Koshy TAILQ_FOREACH(pcm, &pp->pp_map, ppm_next) 56315139246SJoseph Koshy if (pcm->ppm_lowpc < lowpc) 56415139246SJoseph Koshy break; 56515139246SJoseph Koshy 56615139246SJoseph Koshy if (pcm == NULL) 56715139246SJoseph Koshy TAILQ_INSERT_TAIL(&pp->pp_map, pcmnew, ppm_next); 56815139246SJoseph Koshy else 56915139246SJoseph Koshy TAILQ_INSERT_BEFORE(pcm, pcmnew, ppm_next); 57015139246SJoseph Koshy } 57115139246SJoseph Koshy 57215139246SJoseph Koshy /* 57315139246SJoseph Koshy * Add a {pmcid,name} mapping. 57415139246SJoseph Koshy */ 57515139246SJoseph Koshy 57615139246SJoseph Koshy static void 57715139246SJoseph Koshy pmcstat_pmcid_add(pmc_id_t pmcid, const char *name, struct pmcstat_args *a) 57815139246SJoseph Koshy { 57915139246SJoseph Koshy struct pmcstat_pmcrecord *pr; 58015139246SJoseph Koshy struct stat st; 58115139246SJoseph Koshy char fullpath[PATH_MAX]; 58215139246SJoseph Koshy 58315139246SJoseph Koshy LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 58415139246SJoseph Koshy if (pr->pr_pmcid == pmcid) { 58515139246SJoseph Koshy pr->pr_pmcname = name; 58615139246SJoseph Koshy return; 58715139246SJoseph Koshy } 58815139246SJoseph Koshy 58915139246SJoseph Koshy if ((pr = malloc(sizeof(*pr))) == NULL) 59015139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate pmc record"); 59115139246SJoseph Koshy 59215139246SJoseph Koshy pr->pr_pmcid = pmcid; 59315139246SJoseph Koshy pr->pr_pmcname = name; 59415139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); 59515139246SJoseph Koshy 59615139246SJoseph Koshy (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", a->pa_samplesdir, 59715139246SJoseph Koshy name); 59815139246SJoseph Koshy 59915139246SJoseph Koshy /* If the path name exists, it should be a directory */ 60015139246SJoseph Koshy if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) 60115139246SJoseph Koshy return; 60215139246SJoseph Koshy 60315139246SJoseph Koshy if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) 60415139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot create directory \"%s\"", 60515139246SJoseph Koshy fullpath); 60615139246SJoseph Koshy } 60715139246SJoseph Koshy 60815139246SJoseph Koshy /* 60915139246SJoseph Koshy * Given a pmcid in use, find its human-readable name, or a 61015139246SJoseph Koshy */ 61115139246SJoseph Koshy 61215139246SJoseph Koshy static const char * 61315139246SJoseph Koshy pmcstat_pmcid_to_name(pmc_id_t pmcid) 61415139246SJoseph Koshy { 61515139246SJoseph Koshy struct pmcstat_pmcrecord *pr; 61615139246SJoseph Koshy char fullpath[PATH_MAX]; 61715139246SJoseph Koshy 61815139246SJoseph Koshy LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) 61915139246SJoseph Koshy if (pr->pr_pmcid == pmcid) 62015139246SJoseph Koshy return pr->pr_pmcname; 62115139246SJoseph Koshy 62215139246SJoseph Koshy /* create a default name and add this entry */ 62315139246SJoseph Koshy if ((pr = malloc(sizeof(*pr))) == NULL) 62415139246SJoseph Koshy err(EX_OSERR, "ERROR: "); 62515139246SJoseph Koshy pr->pr_pmcid = pmcid; 62615139246SJoseph Koshy 62715139246SJoseph Koshy (void) snprintf(fullpath, sizeof(fullpath), "%X", (unsigned int) pmcid); 62815139246SJoseph Koshy pr->pr_pmcname = pmcstat_string_intern(fullpath); 62915139246SJoseph Koshy 63015139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); 63115139246SJoseph Koshy 63215139246SJoseph Koshy return pr->pr_pmcname; 63315139246SJoseph Koshy } 63415139246SJoseph Koshy 63515139246SJoseph Koshy /* 63615139246SJoseph Koshy * Associate an ELF image with a process. Argument 'path' names the 63715139246SJoseph Koshy * executable while 'fd' is an already open descriptor to it. 63815139246SJoseph Koshy */ 63915139246SJoseph Koshy 64015139246SJoseph Koshy static void 64115139246SJoseph Koshy pmcstat_process_add_elf_image(struct pmcstat_process *pp, const char *path) 64215139246SJoseph Koshy { 64315139246SJoseph Koshy int isdynamic; 64415139246SJoseph Koshy size_t linelen; 64515139246SJoseph Koshy FILE *rf; 64615139246SJoseph Koshy char *line; 64715139246SJoseph Koshy uintfptr_t minva, maxva; 64815139246SJoseph Koshy uintmax_t libstart; 64915139246SJoseph Koshy struct pmcstat_image *image; 65015139246SJoseph Koshy char libpath[PATH_MAX]; 65115139246SJoseph Koshy char command[PATH_MAX + sizeof(PMCSTAT_LDD_COMMAND) + 1]; 65215139246SJoseph Koshy 65315139246SJoseph Koshy minva = ~ (uintfptr_t) 0; 65415139246SJoseph Koshy maxva = (uintfptr_t) 0; 65515139246SJoseph Koshy isdynamic = 0; 65615139246SJoseph Koshy 65715139246SJoseph Koshy if ((image = pmcstat_image_from_path(path)) == NULL) 65815139246SJoseph Koshy return; 65915139246SJoseph Koshy 66015139246SJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) { 66115139246SJoseph Koshy 66215139246SJoseph Koshy pmcstat_image_get_elf_params(image, &minva, &maxva, 66315139246SJoseph Koshy &isdynamic); 66415139246SJoseph Koshy 66515139246SJoseph Koshy image->pi_type = PMCSTAT_IMAGE_ELF; 66615139246SJoseph Koshy image->pi_start = minva; 66715139246SJoseph Koshy image->pi_end = maxva; 66815139246SJoseph Koshy image->pi_isdynamic = isdynamic; 66915139246SJoseph Koshy } 67015139246SJoseph Koshy 67115139246SJoseph Koshy /* create a map entry for the base executable */ 67215139246SJoseph Koshy pmcstat_image_link(pp, image, minva, maxva); 67315139246SJoseph Koshy 67415139246SJoseph Koshy if (image->pi_isdynamic) { 67515139246SJoseph Koshy 67615139246SJoseph Koshy (void) snprintf(command, sizeof(command), "%s %s", 67715139246SJoseph Koshy PMCSTAT_LDD_COMMAND, path); 67815139246SJoseph Koshy 67915139246SJoseph Koshy if ((rf = popen(command, "r")) == NULL) 68015139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot create pipe"); 68115139246SJoseph Koshy 68215139246SJoseph Koshy (void) fgetln(rf, &linelen); 68315139246SJoseph Koshy 68415139246SJoseph Koshy while (!feof(rf) && !ferror(rf)) { 68515139246SJoseph Koshy 68615139246SJoseph Koshy if ((line = fgetln(rf, &linelen)) == NULL) 68715139246SJoseph Koshy continue; 68815139246SJoseph Koshy line[linelen-1] = '\0'; 68915139246SJoseph Koshy 69015139246SJoseph Koshy if (sscanf(line, "%s %jx", 69115139246SJoseph Koshy libpath, &libstart) != 2) 69215139246SJoseph Koshy continue; 69315139246SJoseph Koshy 69415139246SJoseph Koshy image = pmcstat_image_from_path( 69515139246SJoseph Koshy pmcstat_string_intern(libpath)); 69615139246SJoseph Koshy if (image == NULL) 69715139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot process " 69815139246SJoseph Koshy "\"%s\"", libpath); 69915139246SJoseph Koshy 70015139246SJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) { 70115139246SJoseph Koshy 70215139246SJoseph Koshy pmcstat_image_get_elf_params(image, 70315139246SJoseph Koshy &minva, &maxva, &isdynamic); 70415139246SJoseph Koshy 70515139246SJoseph Koshy image->pi_type = PMCSTAT_IMAGE_ELF; 70615139246SJoseph Koshy image->pi_start = minva; 70715139246SJoseph Koshy image->pi_end = maxva; 70815139246SJoseph Koshy image->pi_isdynamic = isdynamic; 70915139246SJoseph Koshy } 71015139246SJoseph Koshy 71115139246SJoseph Koshy pmcstat_image_link(pp, image, libstart + image->pi_start, 71215139246SJoseph Koshy libstart + image->pi_end); 71315139246SJoseph Koshy } 71415139246SJoseph Koshy 71515139246SJoseph Koshy (void) pclose(rf); 71615139246SJoseph Koshy 71715139246SJoseph Koshy } 71815139246SJoseph Koshy } 71915139246SJoseph Koshy 72015139246SJoseph Koshy /* 72115139246SJoseph Koshy * Find the process descriptor corresponding to a PID. If 'allocate' 72215139246SJoseph Koshy * is zero, we return a NULL if a pid descriptor could not be found or 72315139246SJoseph Koshy * a process descriptor process. If 'allocate' is non-zero, then we 72415139246SJoseph Koshy * will attempt to allocate a fresh process descriptor. Zombie 72515139246SJoseph Koshy * process descriptors are only removed if a fresh allocation for the 72615139246SJoseph Koshy * same PID is requested. 72715139246SJoseph Koshy */ 72815139246SJoseph Koshy 72915139246SJoseph Koshy static struct pmcstat_process * 73015139246SJoseph Koshy pmcstat_process_lookup(pid_t pid, int allocate) 73115139246SJoseph Koshy { 73215139246SJoseph Koshy uint32_t hash; 73315139246SJoseph Koshy struct pmcstat_pcmap *ppm, *ppmtmp; 73415139246SJoseph Koshy struct pmcstat_process *pp, *pptmp; 73515139246SJoseph Koshy 73615139246SJoseph Koshy hash = (uint32_t) pid & PMCSTAT_HASH_MASK; /* simplicity wins */ 73715139246SJoseph Koshy 73815139246SJoseph Koshy LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[hash], pp_next, pptmp) 73915139246SJoseph Koshy if (pp->pp_pid == pid) { 74015139246SJoseph Koshy /* Found a descriptor, check and process zombies */ 74115139246SJoseph Koshy if (allocate && !pp->pp_isactive) { 74215139246SJoseph Koshy /* remove maps */ 74315139246SJoseph Koshy TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, 74415139246SJoseph Koshy ppmtmp) { 74515139246SJoseph Koshy TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 74615139246SJoseph Koshy free(ppm); 74715139246SJoseph Koshy } 74815139246SJoseph Koshy /* remove process entry */ 74915139246SJoseph Koshy LIST_REMOVE(pp, pp_next); 75015139246SJoseph Koshy free(pp); 75115139246SJoseph Koshy break; 75215139246SJoseph Koshy } 75315139246SJoseph Koshy return pp; 75415139246SJoseph Koshy } 75515139246SJoseph Koshy 75615139246SJoseph Koshy if (!allocate) 75715139246SJoseph Koshy return NULL; 75815139246SJoseph Koshy 75915139246SJoseph Koshy if ((pp = malloc(sizeof(*pp))) == NULL) 76015139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate pid descriptor"); 76115139246SJoseph Koshy 76215139246SJoseph Koshy pp->pp_pid = pid; 76315139246SJoseph Koshy pp->pp_isactive = 1; 76415139246SJoseph Koshy 76515139246SJoseph Koshy TAILQ_INIT(&pp->pp_map); 76615139246SJoseph Koshy 76715139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_process_hash[hash], pp, pp_next); 76815139246SJoseph Koshy return pp; 76915139246SJoseph Koshy } 77015139246SJoseph Koshy 77115139246SJoseph Koshy /* 77215139246SJoseph Koshy * Find the map entry associated with process 'p' at PC value 'pc'. 77315139246SJoseph Koshy */ 77415139246SJoseph Koshy 77515139246SJoseph Koshy static struct pmcstat_pcmap * 77615139246SJoseph Koshy pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc) 77715139246SJoseph Koshy { 77815139246SJoseph Koshy struct pmcstat_pcmap *ppm; 77915139246SJoseph Koshy 78015139246SJoseph Koshy TAILQ_FOREACH(ppm, &p->pp_map, ppm_next) 78115139246SJoseph Koshy if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc) 78215139246SJoseph Koshy return ppm; 78315139246SJoseph Koshy 78415139246SJoseph Koshy return NULL; 78515139246SJoseph Koshy } 78615139246SJoseph Koshy 78715139246SJoseph Koshy /* 78815139246SJoseph Koshy * Associate an image and a process. 78915139246SJoseph Koshy */ 79015139246SJoseph Koshy 79115139246SJoseph Koshy static void 79215139246SJoseph Koshy pmcstat_process_new_image(struct pmcstat_process *pp, const char *path) 79315139246SJoseph Koshy { 79415139246SJoseph Koshy enum pmcstat_image_type filetype; 79515139246SJoseph Koshy struct pmcstat_image *image; 79615139246SJoseph Koshy 79715139246SJoseph Koshy if ((image = pmcstat_image_from_path(path)) == NULL) 79815139246SJoseph Koshy return; 79915139246SJoseph Koshy 80015139246SJoseph Koshy if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 80115139246SJoseph Koshy filetype = pmcstat_image_get_type(path); 80215139246SJoseph Koshy else 80315139246SJoseph Koshy filetype = image->pi_type; 80415139246SJoseph Koshy 80515139246SJoseph Koshy switch (filetype) { 80615139246SJoseph Koshy case PMCSTAT_IMAGE_ELF: 80715139246SJoseph Koshy pmcstat_process_add_elf_image(pp, path); 80815139246SJoseph Koshy break; 80915139246SJoseph Koshy 81015139246SJoseph Koshy case PMCSTAT_IMAGE_AOUT: 81115139246SJoseph Koshy break; 81215139246SJoseph Koshy 81315139246SJoseph Koshy default: 81415139246SJoseph Koshy err(EX_SOFTWARE, "ERROR: Unsupported executable type \"%s\"", 81515139246SJoseph Koshy path); 81615139246SJoseph Koshy } 81715139246SJoseph Koshy } 81815139246SJoseph Koshy 81915139246SJoseph Koshy 82015139246SJoseph Koshy 82115139246SJoseph Koshy /* 82215139246SJoseph Koshy * Compute a 'hash' value for a string. 82315139246SJoseph Koshy */ 82415139246SJoseph Koshy 82515139246SJoseph Koshy static int 82615139246SJoseph Koshy pmcstat_string_compute_hash(const char *s) 82715139246SJoseph Koshy { 82815139246SJoseph Koshy int hash; 82915139246SJoseph Koshy 83015139246SJoseph Koshy for (hash = 0; *s; s++) 83115139246SJoseph Koshy hash ^= *s; 83215139246SJoseph Koshy 83315139246SJoseph Koshy return hash & PMCSTAT_HASH_MASK; 83415139246SJoseph Koshy } 83515139246SJoseph Koshy 83615139246SJoseph Koshy /* 83715139246SJoseph Koshy * Intern a copy of string 's', and return a pointer to it. 83815139246SJoseph Koshy */ 83915139246SJoseph Koshy 84015139246SJoseph Koshy static const char * 84115139246SJoseph Koshy pmcstat_string_intern(const char *s) 84215139246SJoseph Koshy { 84315139246SJoseph Koshy struct pmcstat_string *ps; 84415139246SJoseph Koshy int hash, len; 84515139246SJoseph Koshy 84615139246SJoseph Koshy hash = pmcstat_string_compute_hash(s); 84715139246SJoseph Koshy len = strlen(s); 84815139246SJoseph Koshy 84915139246SJoseph Koshy if ((ps = pmcstat_string_lookup(s)) != NULL) 85015139246SJoseph Koshy return ps->ps_string; 85115139246SJoseph Koshy 85215139246SJoseph Koshy if ((ps = malloc(sizeof(*ps))) == NULL) 85315139246SJoseph Koshy err(EX_OSERR, "ERROR: Could not intern string"); 85415139246SJoseph Koshy ps->ps_len = len; 85515139246SJoseph Koshy ps->ps_hash = hash; 85615139246SJoseph Koshy ps->ps_string = strdup(s); 85715139246SJoseph Koshy LIST_INSERT_HEAD(&pmcstat_string_hash[hash], ps, ps_next); 85815139246SJoseph Koshy return ps->ps_string; 85915139246SJoseph Koshy } 86015139246SJoseph Koshy 86115139246SJoseph Koshy static struct pmcstat_string * 86215139246SJoseph Koshy pmcstat_string_lookup(const char *s) 86315139246SJoseph Koshy { 86415139246SJoseph Koshy struct pmcstat_string *ps; 86515139246SJoseph Koshy int hash, len; 86615139246SJoseph Koshy 86715139246SJoseph Koshy hash = pmcstat_string_compute_hash(s); 86815139246SJoseph Koshy len = strlen(s); 86915139246SJoseph Koshy 87015139246SJoseph Koshy LIST_FOREACH(ps, &pmcstat_string_hash[hash], ps_next) 87115139246SJoseph Koshy if (ps->ps_len == len && ps->ps_hash == hash && 87215139246SJoseph Koshy strcmp(ps->ps_string, s) == 0) 87315139246SJoseph Koshy return ps; 87415139246SJoseph Koshy return NULL; 87515139246SJoseph Koshy } 87615139246SJoseph Koshy 87715139246SJoseph Koshy /* 87815139246SJoseph Koshy * Public Interfaces. 87915139246SJoseph Koshy */ 88015139246SJoseph Koshy 88115139246SJoseph Koshy /* 88215139246SJoseph Koshy * Close a logfile, after first flushing all in-module queued data. 88315139246SJoseph Koshy */ 88415139246SJoseph Koshy 88515139246SJoseph Koshy int 88615139246SJoseph Koshy pmcstat_close_log(struct pmcstat_args *a) 88715139246SJoseph Koshy { 88815139246SJoseph Koshy if (pmc_flush_logfile() < 0 || 88915139246SJoseph Koshy pmc_configure_logfile(-1) < 0) 89015139246SJoseph Koshy err(EX_OSERR, "ERROR: logging failed"); 89115139246SJoseph Koshy a->pa_flags &= ~(FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE); 89215139246SJoseph Koshy return a->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING : 89315139246SJoseph Koshy PMCSTAT_FINISHED; 89415139246SJoseph Koshy } 89515139246SJoseph Koshy 89615139246SJoseph Koshy 89715139246SJoseph Koshy int 89815139246SJoseph Koshy pmcstat_convert_log(struct pmcstat_args *a) 89915139246SJoseph Koshy { 90015139246SJoseph Koshy uintfptr_t pc; 90115139246SJoseph Koshy struct pmcstat_process *pp, *ppnew; 90215139246SJoseph Koshy struct pmcstat_pcmap *ppm, *ppmtmp; 90315139246SJoseph Koshy struct pmclog_ev ev; 90415139246SJoseph Koshy const char *image_path; 90515139246SJoseph Koshy 90615139246SJoseph Koshy while (pmclog_read(a->pa_logparser, &ev) == 0) { 90715139246SJoseph Koshy assert(ev.pl_state == PMCLOG_OK); 90815139246SJoseph Koshy 90915139246SJoseph Koshy switch (ev.pl_type) { 91015139246SJoseph Koshy case PMCLOG_TYPE_MAPPINGCHANGE: 91115139246SJoseph Koshy /* 91215139246SJoseph Koshy * Introduce an address range mapping for a 91315139246SJoseph Koshy * process. 91415139246SJoseph Koshy */ 91515139246SJoseph Koshy break; 91615139246SJoseph Koshy 91715139246SJoseph Koshy case PMCLOG_TYPE_PCSAMPLE: 91815139246SJoseph Koshy 91915139246SJoseph Koshy /* 92015139246SJoseph Koshy * We bring in the gmon file for the image 92115139246SJoseph Koshy * currently associated with the PMC & pid 92215139246SJoseph Koshy * pair and increment the appropriate entry 92315139246SJoseph Koshy * bin inside this. 92415139246SJoseph Koshy */ 92515139246SJoseph Koshy pc = ev.pl_u.pl_s.pl_pc; 92615139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_s.pl_pid, 1); 92715139246SJoseph Koshy if ((ppm = pmcstat_process_find_map(pp, pc)) == NULL && 92815139246SJoseph Koshy (ppm = pmcstat_process_find_map(pmcstat_kernproc, 929bf840f4aSJoseph Koshy pc)) == NULL) 93015139246SJoseph Koshy break; /* unknown process,offset pair */ 93115139246SJoseph Koshy 93215139246SJoseph Koshy pmcstat_image_increment_bucket(ppm, pc, 93315139246SJoseph Koshy ev.pl_u.pl_s.pl_pmcid, a); 93415139246SJoseph Koshy 93515139246SJoseph Koshy break; 93615139246SJoseph Koshy 93715139246SJoseph Koshy case PMCLOG_TYPE_PMCALLOCATE: 93815139246SJoseph Koshy /* 93915139246SJoseph Koshy * Record the association pmc id between this 94015139246SJoseph Koshy * PMC and its name. 94115139246SJoseph Koshy */ 94215139246SJoseph Koshy pmcstat_pmcid_add(ev.pl_u.pl_a.pl_pmcid, 94315139246SJoseph Koshy pmcstat_string_intern(ev.pl_u.pl_a.pl_evname), a); 94415139246SJoseph Koshy break; 94515139246SJoseph Koshy 94615139246SJoseph Koshy case PMCLOG_TYPE_PROCEXEC: 94715139246SJoseph Koshy 94815139246SJoseph Koshy /* 94915139246SJoseph Koshy * Change the executable image associated with 95015139246SJoseph Koshy * a process. 95115139246SJoseph Koshy */ 95215139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_x.pl_pid, 1); 95315139246SJoseph Koshy 95415139246SJoseph Koshy /* delete the current process map */ 95515139246SJoseph Koshy TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) { 95615139246SJoseph Koshy TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); 95715139246SJoseph Koshy free(ppm); 95815139246SJoseph Koshy } 95915139246SJoseph Koshy 96015139246SJoseph Koshy /* locate the descriptor for the new 'base' image */ 96115139246SJoseph Koshy image_path = pmcstat_string_intern( 96215139246SJoseph Koshy ev.pl_u.pl_x.pl_pathname); 96315139246SJoseph Koshy 96415139246SJoseph Koshy /* link to the new image */ 96515139246SJoseph Koshy pmcstat_process_new_image(pp, image_path); 96615139246SJoseph Koshy break; 96715139246SJoseph Koshy 96815139246SJoseph Koshy case PMCLOG_TYPE_PROCEXIT: 96915139246SJoseph Koshy 97015139246SJoseph Koshy /* 97115139246SJoseph Koshy * Due to the way the log is generated, the 97215139246SJoseph Koshy * last few samples corresponding to a process 97315139246SJoseph Koshy * may appear in the log after the process 97415139246SJoseph Koshy * exit event is recorded. Thus we keep the 97515139246SJoseph Koshy * process' descriptor and associated data 97615139246SJoseph Koshy * structures around, but mark the process as 97715139246SJoseph Koshy * having exited. 97815139246SJoseph Koshy */ 97915139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_e.pl_pid, 0); 98015139246SJoseph Koshy if (pp == NULL) 98115139246SJoseph Koshy break; 98215139246SJoseph Koshy pp->pp_isactive = 0; /* make a zombie */ 98315139246SJoseph Koshy break; 98415139246SJoseph Koshy 98515139246SJoseph Koshy case PMCLOG_TYPE_SYSEXIT: 98615139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_se.pl_pid, 0); 98715139246SJoseph Koshy if (pp == NULL) 98815139246SJoseph Koshy break; 98915139246SJoseph Koshy pp->pp_isactive = 0; /* make a zombie */ 99015139246SJoseph Koshy break; 99115139246SJoseph Koshy 99215139246SJoseph Koshy case PMCLOG_TYPE_PROCFORK: 99315139246SJoseph Koshy 99415139246SJoseph Koshy /* 99515139246SJoseph Koshy * If we had been tracking 'oldpid', then clone 99615139246SJoseph Koshy * its pid descriptor. 99715139246SJoseph Koshy */ 99815139246SJoseph Koshy pp = pmcstat_process_lookup(ev.pl_u.pl_f.pl_oldpid, 0); 99915139246SJoseph Koshy if (pp == NULL) 100015139246SJoseph Koshy break; 100115139246SJoseph Koshy 100215139246SJoseph Koshy ppnew = 100315139246SJoseph Koshy pmcstat_process_lookup(ev.pl_u.pl_f.pl_newpid, 1); 100415139246SJoseph Koshy 100515139246SJoseph Koshy /* copy the old process' address maps */ 100615139246SJoseph Koshy TAILQ_FOREACH(ppm, &pp->pp_map, ppm_next) 100715139246SJoseph Koshy pmcstat_image_link(ppnew, ppm->ppm_image, 100815139246SJoseph Koshy ppm->ppm_lowpc, ppm->ppm_highpc); 100915139246SJoseph Koshy break; 101015139246SJoseph Koshy 101115139246SJoseph Koshy default: /* other types of entries are not relevant */ 101215139246SJoseph Koshy break; 101315139246SJoseph Koshy } 101415139246SJoseph Koshy } 101515139246SJoseph Koshy 101615139246SJoseph Koshy if (ev.pl_state == PMCLOG_EOF) 101715139246SJoseph Koshy return PMCSTAT_FINISHED; 101815139246SJoseph Koshy else if (ev.pl_state == PMCLOG_REQUIRE_DATA) 101915139246SJoseph Koshy return PMCSTAT_RUNNING; 102015139246SJoseph Koshy 102115139246SJoseph Koshy err(EX_DATAERR, "ERROR: event parsing failed (record %jd, " 102215139246SJoseph Koshy "offset 0x%jx)", (uintmax_t) ev.pl_count + 1, ev.pl_offset); 102315139246SJoseph Koshy } 102415139246SJoseph Koshy 102515139246SJoseph Koshy 102615139246SJoseph Koshy /* 102715139246SJoseph Koshy * Open a log file, for reading or writing. 102815139246SJoseph Koshy * 102915139246SJoseph Koshy * The function returns the fd of a successfully opened log or -1 in 103015139246SJoseph Koshy * case of failure. 103115139246SJoseph Koshy */ 103215139246SJoseph Koshy 103315139246SJoseph Koshy int 103415139246SJoseph Koshy pmcstat_open(const char *path, int mode) 103515139246SJoseph Koshy { 103615139246SJoseph Koshy int fd; 103715139246SJoseph Koshy 103815139246SJoseph Koshy /* 103915139246SJoseph Koshy * If 'path' is "-" then open one of stdin or stdout depending 104015139246SJoseph Koshy * on the value of 'mode'. Otherwise, treat 'path' as a file 104115139246SJoseph Koshy * name and open that. 104215139246SJoseph Koshy */ 104315139246SJoseph Koshy if (path[0] == '-' && path[1] == '\0') 104415139246SJoseph Koshy fd = (mode == PMCSTAT_OPEN_FOR_READ) ? 0 : 1; 104515139246SJoseph Koshy else 104615139246SJoseph Koshy fd = open(path, mode == PMCSTAT_OPEN_FOR_READ ? 104715139246SJoseph Koshy O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC), 104815139246SJoseph Koshy S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); 104915139246SJoseph Koshy 105015139246SJoseph Koshy return fd; 105115139246SJoseph Koshy } 105215139246SJoseph Koshy 105315139246SJoseph Koshy /* 105415139246SJoseph Koshy * Print log entries as text. 105515139246SJoseph Koshy */ 105615139246SJoseph Koshy 105715139246SJoseph Koshy int 105815139246SJoseph Koshy pmcstat_print_log(struct pmcstat_args *a) 105915139246SJoseph Koshy { 106015139246SJoseph Koshy struct pmclog_ev ev; 106115139246SJoseph Koshy 106215139246SJoseph Koshy while (pmclog_read(a->pa_logparser, &ev) == 0) { 106315139246SJoseph Koshy assert(ev.pl_state == PMCLOG_OK); 106415139246SJoseph Koshy switch (ev.pl_type) { 106515139246SJoseph Koshy case PMCLOG_TYPE_CLOSELOG: 106615139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"closelog",); 106715139246SJoseph Koshy break; 106815139246SJoseph Koshy case PMCLOG_TYPE_DROPNOTIFY: 106915139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"drop",); 107015139246SJoseph Koshy break; 107115139246SJoseph Koshy case PMCLOG_TYPE_INITIALIZE: 107215139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"initlog","0x%x \"%s\"", 107315139246SJoseph Koshy ev.pl_u.pl_i.pl_version, 107415139246SJoseph Koshy pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch)); 107515139246SJoseph Koshy break; 107615139246SJoseph Koshy case PMCLOG_TYPE_MAPPINGCHANGE: 107715139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"mapping","%s %d %p %p \"%s\"", 107815139246SJoseph Koshy ev.pl_u.pl_m.pl_type == PMCLOG_MAPPING_INSERT ? 107915139246SJoseph Koshy "insert" : "delete", 108015139246SJoseph Koshy ev.pl_u.pl_m.pl_pid, 108115139246SJoseph Koshy (void *) ev.pl_u.pl_m.pl_start, 108215139246SJoseph Koshy (void *) ev.pl_u.pl_m.pl_end, 108315139246SJoseph Koshy ev.pl_u.pl_m.pl_pathname); 108415139246SJoseph Koshy break; 108515139246SJoseph Koshy case PMCLOG_TYPE_PCSAMPLE: 108615139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"sample","0x%x %d %p %c", 108715139246SJoseph Koshy ev.pl_u.pl_s.pl_pmcid, 108815139246SJoseph Koshy ev.pl_u.pl_s.pl_pid, 108915139246SJoseph Koshy (void *) ev.pl_u.pl_s.pl_pc, 109015139246SJoseph Koshy ev.pl_u.pl_s.pl_usermode ? 'u' : 's'); 109115139246SJoseph Koshy break; 109215139246SJoseph Koshy case PMCLOG_TYPE_PMCALLOCATE: 109315139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"allocate","0x%x \"%s\" 0x%x", 109415139246SJoseph Koshy ev.pl_u.pl_a.pl_pmcid, 109515139246SJoseph Koshy ev.pl_u.pl_a.pl_evname, 109615139246SJoseph Koshy ev.pl_u.pl_a.pl_flags); 109715139246SJoseph Koshy break; 109815139246SJoseph Koshy case PMCLOG_TYPE_PMCATTACH: 109915139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"attach","0x%x %d \"%s\"", 110015139246SJoseph Koshy ev.pl_u.pl_t.pl_pmcid, 110115139246SJoseph Koshy ev.pl_u.pl_t.pl_pid, 110215139246SJoseph Koshy ev.pl_u.pl_t.pl_pathname); 110315139246SJoseph Koshy break; 110415139246SJoseph Koshy case PMCLOG_TYPE_PMCDETACH: 110515139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"detach","0x%x %d", 110615139246SJoseph Koshy ev.pl_u.pl_d.pl_pmcid, 110715139246SJoseph Koshy ev.pl_u.pl_d.pl_pid); 110815139246SJoseph Koshy break; 110915139246SJoseph Koshy case PMCLOG_TYPE_PROCCSW: 111015139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"cswval","0x%x %d %jd", 111115139246SJoseph Koshy ev.pl_u.pl_c.pl_pmcid, 111215139246SJoseph Koshy ev.pl_u.pl_c.pl_pid, 111315139246SJoseph Koshy ev.pl_u.pl_c.pl_value); 111415139246SJoseph Koshy break; 111515139246SJoseph Koshy case PMCLOG_TYPE_PROCEXEC: 111615139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"exec","0x%x %d %p \"%s\"", 111715139246SJoseph Koshy ev.pl_u.pl_x.pl_pmcid, 111815139246SJoseph Koshy ev.pl_u.pl_x.pl_pid, 111915139246SJoseph Koshy (void *) ev.pl_u.pl_x.pl_entryaddr, 112015139246SJoseph Koshy ev.pl_u.pl_x.pl_pathname); 112115139246SJoseph Koshy break; 112215139246SJoseph Koshy case PMCLOG_TYPE_PROCEXIT: 112315139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"exitval","0x%x %d %jd", 112415139246SJoseph Koshy ev.pl_u.pl_e.pl_pmcid, 112515139246SJoseph Koshy ev.pl_u.pl_e.pl_pid, 112615139246SJoseph Koshy ev.pl_u.pl_e.pl_value); 112715139246SJoseph Koshy break; 112815139246SJoseph Koshy case PMCLOG_TYPE_PROCFORK: 112915139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"fork","%d %d", 113015139246SJoseph Koshy ev.pl_u.pl_f.pl_oldpid, 113115139246SJoseph Koshy ev.pl_u.pl_f.pl_newpid); 113215139246SJoseph Koshy break; 113315139246SJoseph Koshy case PMCLOG_TYPE_USERDATA: 113415139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"userdata","0x%x", 113515139246SJoseph Koshy ev.pl_u.pl_u.pl_userdata); 113615139246SJoseph Koshy break; 113715139246SJoseph Koshy case PMCLOG_TYPE_SYSEXIT: 113815139246SJoseph Koshy PMCSTAT_PRINT_ENTRY(a,"exit","%d", 113915139246SJoseph Koshy ev.pl_u.pl_se.pl_pid); 114015139246SJoseph Koshy break; 114115139246SJoseph Koshy default: 114215139246SJoseph Koshy fprintf(a->pa_printfile, "unknown %d", 114315139246SJoseph Koshy ev.pl_type); 114415139246SJoseph Koshy } 114515139246SJoseph Koshy } 114615139246SJoseph Koshy 114715139246SJoseph Koshy if (ev.pl_state == PMCLOG_EOF) 114815139246SJoseph Koshy return PMCSTAT_FINISHED; 114915139246SJoseph Koshy else if (ev.pl_state == PMCLOG_REQUIRE_DATA) 115015139246SJoseph Koshy return PMCSTAT_RUNNING; 115115139246SJoseph Koshy 115215139246SJoseph Koshy err(EX_DATAERR, "ERROR: event parsing failed " 115315139246SJoseph Koshy "(record %jd, offset 0x%jx)", 115415139246SJoseph Koshy (uintmax_t) ev.pl_count + 1, ev.pl_offset); 115515139246SJoseph Koshy /*NOTREACHED*/ 115615139246SJoseph Koshy } 115715139246SJoseph Koshy 115815139246SJoseph Koshy /* 115915139246SJoseph Koshy * Process a log file in offline analysis mode. 116015139246SJoseph Koshy */ 116115139246SJoseph Koshy 1162dc1d9d2eSJoseph Koshy int 116315139246SJoseph Koshy pmcstat_process_log(struct pmcstat_args *a) 116415139246SJoseph Koshy { 116515139246SJoseph Koshy 116615139246SJoseph Koshy /* 116715139246SJoseph Koshy * If gprof style profiles haven't been asked for, just print the 116815139246SJoseph Koshy * log to the current output file. 116915139246SJoseph Koshy */ 117015139246SJoseph Koshy if (a->pa_flags & FLAG_DO_PRINT) 1171dc1d9d2eSJoseph Koshy return pmcstat_print_log(a); 117215139246SJoseph Koshy else 117315139246SJoseph Koshy /* convert the log to gprof compatible profiles */ 1174dc1d9d2eSJoseph Koshy return pmcstat_convert_log(a); 117515139246SJoseph Koshy } 117615139246SJoseph Koshy 117715139246SJoseph Koshy void 117815139246SJoseph Koshy pmcstat_initialize_logging(struct pmcstat_args *a) 117915139246SJoseph Koshy { 118015139246SJoseph Koshy int i, isdynamic; 118115139246SJoseph Koshy const char *kernpath; 118215139246SJoseph Koshy struct pmcstat_image *img; 118315139246SJoseph Koshy uintfptr_t minva, maxva; 118415139246SJoseph Koshy 118515139246SJoseph Koshy /* use a convenient format for 'ldd' output */ 118615139246SJoseph Koshy if (setenv("LD_TRACE_LOADED_OBJECTS_FMT1","%p %x\n",1) != 0) 118715139246SJoseph Koshy goto error; 118815139246SJoseph Koshy 118915139246SJoseph Koshy /* Initialize hash tables */ 119015139246SJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) { 119115139246SJoseph Koshy LIST_INIT(&pmcstat_image_hash[i]); 119215139246SJoseph Koshy LIST_INIT(&pmcstat_process_hash[i]); 119315139246SJoseph Koshy LIST_INIT(&pmcstat_string_hash[i]); 119415139246SJoseph Koshy } 119515139246SJoseph Koshy 119615139246SJoseph Koshy /* create a fake 'process' entry for the kernel with pid == -1 */ 119715139246SJoseph Koshy if ((pmcstat_kernproc = pmcstat_process_lookup((pid_t) -1, 1)) == NULL) 119815139246SJoseph Koshy goto error; 119915139246SJoseph Koshy 120015139246SJoseph Koshy if ((kernpath = pmcstat_string_intern(a->pa_kernel)) == NULL) 120115139246SJoseph Koshy goto error; 120215139246SJoseph Koshy 120315139246SJoseph Koshy img = pmcstat_image_from_path(kernpath); 120415139246SJoseph Koshy 120515139246SJoseph Koshy pmcstat_image_get_elf_params(img, &minva, &maxva, &isdynamic); 120615139246SJoseph Koshy img->pi_type = PMCSTAT_IMAGE_ELF; 120715139246SJoseph Koshy img->pi_start = minva; 120815139246SJoseph Koshy img->pi_end = maxva; 120915139246SJoseph Koshy 121015139246SJoseph Koshy pmcstat_image_link(pmcstat_kernproc, img, minva, maxva); 121115139246SJoseph Koshy 121215139246SJoseph Koshy return; 121315139246SJoseph Koshy 121415139246SJoseph Koshy error: 121515139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot initialize logging"); 121615139246SJoseph Koshy } 121715139246SJoseph Koshy 121815139246SJoseph Koshy void 121915139246SJoseph Koshy pmcstat_shutdown_logging(void) 122015139246SJoseph Koshy { 122115139246SJoseph Koshy int i; 122215139246SJoseph Koshy struct pmcstat_gmonfile *pgf, *pgftmp; 122315139246SJoseph Koshy struct pmcstat_image *pi, *pitmp; 122415139246SJoseph Koshy struct pmcstat_process *pp, *pptmp; 122515139246SJoseph Koshy struct pmcstat_string *ps, *pstmp; 122615139246SJoseph Koshy 122715139246SJoseph Koshy for (i = 0; i < PMCSTAT_NHASH; i++) { 122815139246SJoseph Koshy LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next, pitmp) { 122915139246SJoseph Koshy /* flush gmon.out data to disk */ 123015139246SJoseph Koshy LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next, 123115139246SJoseph Koshy pgftmp) { 123215139246SJoseph Koshy pmcstat_gmon_unmap_file(pgf); 123315139246SJoseph Koshy LIST_REMOVE(pgf, pgf_next); 123415139246SJoseph Koshy free(pgf); 123515139246SJoseph Koshy } 123615139246SJoseph Koshy 123715139246SJoseph Koshy LIST_REMOVE(pi, pi_next); 123815139246SJoseph Koshy free(pi); 123915139246SJoseph Koshy } 124015139246SJoseph Koshy LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[i], pp_next, 124115139246SJoseph Koshy pptmp) { 124215139246SJoseph Koshy LIST_REMOVE(pp, pp_next); 124315139246SJoseph Koshy free(pp); 124415139246SJoseph Koshy } 124515139246SJoseph Koshy LIST_FOREACH_SAFE(ps, &pmcstat_string_hash[i], ps_next, 124615139246SJoseph Koshy pstmp) { 124715139246SJoseph Koshy LIST_REMOVE(ps, ps_next); 124815139246SJoseph Koshy free(ps); 124915139246SJoseph Koshy } 125015139246SJoseph Koshy } 125115139246SJoseph Koshy } 1252