Deleted Added
full compact
2c2
< * Copyright (c) 2005, Joseph Koshy
---
> * Copyright (c) 2005-2006, Joseph Koshy
27,29d26
< #include <sys/cdefs.h>
< __FBSDID("$FreeBSD: head/usr.sbin/pmcstat/pmcstat_log.c 153710 2005-12-25 05:11:29Z jkoshy $");
<
31,45c28,29
< * Transform a hwpmc(4) log into human readable form and into gprof(1)
< * compatible profiles.
< *
< * Each executable object encountered in the log gets one 'gmon.out'
< * profile per PMC. We currently track:
< * - program executables
< * - shared libraries loaded by the runtime loader
< * - the runtime loader itself
< * - the kernel.
< * We do not track shared objects mapped in by dlopen() yet (this
< * needs additional support from hwpmc(4)).
< *
< * 'gmon.out' profiles generated for a given sampling PMC are
< * aggregates of all the samples for that particular executable
< * object.
---
> * Transform a hwpmc(4) log into human readable form, and into
> * gprof(1) compatible profiles.
47a32,34
> #include <sys/cdefs.h>
> __FBSDID("$FreeBSD: head/usr.sbin/pmcstat/pmcstat_log.c 157144 2006-03-26 12:20:54Z jkoshy $");
>
81,84c68,105
< * A simple implementation of interned strings. Each interned string
< * is assigned a unique address, so that subsequent string compares
< * can be done by a simple pointer comparision instead of with
< * strcmp().
---
> * PUBLIC INTERFACES
> *
> * pmcstat_initialize_logging() initialize this module, called first
> * pmcstat_shutdown_logging() orderly shutdown, called last
> * pmcstat_open_log() open an eventlog for processing
> * pmcstat_process_log() print/convert an event log
> * pmcstat_close_log() finish processing an event log
> *
> * IMPLEMENTATION OF GMON OUTPUT
> *
> * We correlate each 'sample' seen in the event log back to an
> * executable object in the system. Executable objects include:
> * - program executables,
> * - shared libraries loaded by the runtime loader,
> * - dlopen()'ed objects loaded by the program,
> * - the runtime loader itself,
> * - the kernel and kernel modules.
> *
> * Each such executable object gets one 'gmon.out' profile, per PMC in
> * use. Creation of 'gmon.out' profiles is done lazily. The
> * 'gmon.out' profiles generated for a given sampling PMC are
> * aggregates of all the samples for that particular executable
> * object.
> *
> * Each process that we know about is treated as a set of regions that
> * map to executable objects. Processes are described by
> * 'pmcstat_process' structures. Executable objects are tracked by
> * 'pmcstat_image' structures. The kernel and kernel modules are
> * common to all processes (they reside at the same virtual addresses
> * for all processes). Individual processes can have their text
> * segments and shared libraries loaded at process-specific locations.
> *
> * A given executable object can be in use by multiple processes
> * (e.g., libc.so) and loaded at a different address in each.
> * pmcstat_pcmap structures track per-image mappings.
> *
> * The sample log could have samples from multiple PMCs; we
> * generate one 'gmon.out' profile per PMC.
86,91d106
< struct pmcstat_string {
< LIST_ENTRY(pmcstat_string) ps_next; /* hash link */
< int ps_len;
< int ps_hash;
< const char *ps_string;
< };
93c108
< static LIST_HEAD(,pmcstat_string) pmcstat_string_hash[PMCSTAT_NHASH];
---
> typedef const void *pmcstat_interned_string;
102,103c117,118
< pmc_id_t pr_pmcid;
< const char *pr_pmcname;
---
> pmc_id_t pr_pmcid;
> pmcstat_interned_string pr_pmcname;
116a132
> int pgf_overflow; /* whether a count overflowed */
119c135
< const char *pgf_name; /* pathname of gmon.out file */
---
> pmcstat_interned_string pgf_name; /* pathname of gmon.out file */
124,126d139
< static TAILQ_HEAD(,pmcstat_gmonfile) pmcstat_gmonfiles =
< TAILQ_HEAD_INITIALIZER(pmcstat_gmonfiles);
<
129c142
< * disk. 'pi_internedpath' is a cookie representing the pathname of
---
> * disk. 'pi_execpath' is a cookie representing the pathname of
137,139c150,154
< PMCSTAT_IMAGE_UNKNOWN = 0,
< PMCSTAT_IMAGE_ELF,
< PMCSTAT_IMAGE_AOUT
---
> PMCSTAT_IMAGE_UNKNOWN = 0, /* never looked at the image */
> PMCSTAT_IMAGE_INDETERMINABLE, /* can't tell what the image is */
> PMCSTAT_IMAGE_ELF32, /* ELF 32 bit object */
> PMCSTAT_IMAGE_ELF64, /* ELF 64 bit object */
> PMCSTAT_IMAGE_AOUT /* AOUT object */
145,146c160,161
< const char *pi_internedpath; /* cookie */
< const char *pi_samplename; /* sample path name */
---
> pmcstat_interned_string pi_execpath;/* cookie */
> pmcstat_interned_string pi_samplename; /* sample path name */
148a164,168
>
> /*
> * Executables have pi_start and pi_end; these are zero
> * for shared libraries.
> */
152,153c172,176
< int pi_isdynamic; /* whether a dynamic object */
< const char *pi_dynlinkerpath; /* path in .interp section */
---
> uintfptr_t pi_vaddr; /* virtual address where loaded */
> int pi_isdynamic; /* whether a dynamic
> * object */
> int pi_iskernelmodule;
> pmcstat_interned_string pi_dynlinkerpath; /* path in .interp */
154a178,181
> /*
> * An image can be associated with one or more gmon.out files;
> * one per PMC.
> */
157a185,187
> /*
> * All image descriptors are kept in a hash table.
> */
161a192,195
> /*
> * A 'pmcstat_pcmap' structure maps a virtual address range to an
> * underlying 'pmcstat_image' descriptor.
> */
170c204,212
< * A 'pmcstat_process' structure tracks processes.
---
> * A 'pmcstat_process' structure models processes. Each process is
> * associated with a set of pmcstat_pcmap structures that map
> * addresses inside it to executable objects. This set is implemented
> * as a list, kept sorted in ascending order of mapped addresses.
> *
> * 'pp_pid' holds the pid of the process. When a process exits, the
> * 'pp_isactive' field is set to zero, but the process structure is
> * not immediately reclaimed because there may still be samples in the
> * log for this process.
180a223,227
> #define PMCSTAT_ALLOCATE 1
>
> /*
> * All process descriptors are kept in a hash table.
> */
184a232,242
> /* Misc. statistics */
> static struct pmcstat_stats {
> int ps_exec_aout; /* # a.out executables seen */
> int ps_exec_elf; /* # elf executables seen */
> int ps_exec_errors; /* # errors processing executables */
> int ps_exec_indeterminable; /* # unknown executables seen */
> int ps_samples_total; /* total number of samples processed */
> int ps_samples_unknown_offset; /* #samples not in any map */
> int ps_samples_indeterminable; /* #samples in indeterminable images */
> } pmcstat_stats;
>
191c249
< static const char *pmcstat_gmon_create_name(const char *_sd,
---
> static pmcstat_interned_string pmcstat_gmon_create_name(const char *_sd,
196,198c254,261
< static struct pmcstat_image *pmcstat_image_from_path(const char *_path);
< static enum pmcstat_image_type pmcstat_image_get_type(const char *_p);
< static void pmcstat_image_get_elf_params(struct pmcstat_image *_image);
---
> static void pmcstat_image_determine_type(struct pmcstat_image *_image,
> struct pmcstat_args *_a);
> static struct pmcstat_image *pmcstat_image_from_path(pmcstat_interned_string
> _path, int _iskernelmodule);
> static void pmcstat_image_get_aout_params(struct pmcstat_image *_image,
> struct pmcstat_args *_a);
> static void pmcstat_image_get_elf_params(struct pmcstat_image *_image,
> struct pmcstat_args *_a);
202c265
< struct pmcstat_image *_i, uintfptr_t _lpc, uintfptr_t _hpc);
---
> struct pmcstat_image *_i, uintfptr_t _lpc);
204,205c267,268
< static void pmcstat_pmcid_add(pmc_id_t _pmcid, const char *_name,
< struct pmcstat_args *_a);
---
> static void pmcstat_pmcid_add(pmc_id_t _pmcid,
> pmcstat_interned_string _name, struct pmcstat_args *_a);
208,209c271,276
< static void pmcstat_process_add_elf_image(struct pmcstat_process *_pp,
< const char *_path, uintfptr_t _entryaddr);
---
> static void pmcstat_process_aout_exec(struct pmcstat_process *_pp,
> struct pmcstat_image *_image, uintfptr_t _entryaddr,
> struct pmcstat_args *_a);
> static void pmcstat_process_elf_exec(struct pmcstat_process *_pp,
> struct pmcstat_image *_image, uintfptr_t _entryaddr,
> struct pmcstat_args *_a);
211,212c278,281
< const char *_path, uintfptr_t _entryaddr);
< static struct pmcstat_process *pmcstat_process_lookup(pid_t _pid, int _allocate);
---
> pmcstat_interned_string _path, uintfptr_t _entryaddr,
> struct pmcstat_args *_ao);
> static struct pmcstat_process *pmcstat_process_lookup(pid_t _pid,
> int _allocate);
217,218c286,291
< static const char *pmcstat_string_intern(const char *_s);
< static struct pmcstat_string *pmcstat_string_lookup(const char *_s);
---
> static void pmcstat_string_initialize(void);
> static pmcstat_interned_string pmcstat_string_intern(const char *_s);
> static pmcstat_interned_string pmcstat_string_lookup(const char *_s);
> static int pmcstat_string_lookup_hash(pmcstat_interned_string _is);
> static void pmcstat_string_shutdown(void);
> static const char *pmcstat_string_unintern(pmcstat_interned_string _is);
221a295,418
> * A simple implementation of interned strings. Each interned string
> * is assigned a unique address, so that subsequent string compares
> * can be done by a simple pointer comparision instead of using
> * strcmp(). This speeds up hash table lookups and saves memory if
> * duplicate strings are the norm.
> */
> struct pmcstat_string {
> LIST_ENTRY(pmcstat_string) ps_next; /* hash link */
> int ps_len;
> int ps_hash;
> char *ps_string;
> };
>
> static LIST_HEAD(,pmcstat_string) pmcstat_string_hash[PMCSTAT_NHASH];
>
> /*
> * Compute a 'hash' value for a string.
> */
>
> static int
> pmcstat_string_compute_hash(const char *s)
> {
> int hash;
>
> for (hash = 0; *s; s++)
> hash ^= *s;
>
> return (hash & PMCSTAT_HASH_MASK);
> }
>
> /*
> * Intern a copy of string 's', and return a pointer to the
> * interned structure.
> */
>
> static pmcstat_interned_string
> pmcstat_string_intern(const char *s)
> {
> struct pmcstat_string *ps;
> const struct pmcstat_string *cps;
> int hash, len;
>
> if ((cps = pmcstat_string_lookup(s)) != NULL)
> return (cps);
>
> hash = pmcstat_string_compute_hash(s);
> len = strlen(s);
>
> if ((ps = malloc(sizeof(*ps))) == NULL)
> err(EX_OSERR, "ERROR: Could not intern string");
> ps->ps_len = len;
> ps->ps_hash = hash;
> ps->ps_string = strdup(s);
> LIST_INSERT_HEAD(&pmcstat_string_hash[hash], ps, ps_next);
> return ((pmcstat_interned_string) ps);
> }
>
> static const char *
> pmcstat_string_unintern(pmcstat_interned_string str)
> {
> const char *s;
>
> s = ((const struct pmcstat_string *) str)->ps_string;
> return (s);
> }
>
> static pmcstat_interned_string
> pmcstat_string_lookup(const char *s)
> {
> struct pmcstat_string *ps;
> int hash, len;
>
> hash = pmcstat_string_compute_hash(s);
> len = strlen(s);
>
> LIST_FOREACH(ps, &pmcstat_string_hash[hash], ps_next)
> if (ps->ps_len == len && ps->ps_hash == hash &&
> strcmp(ps->ps_string, s) == 0)
> return (ps);
> return (NULL);
> }
>
> static int
> pmcstat_string_lookup_hash(pmcstat_interned_string s)
> {
> const struct pmcstat_string *ps;
>
> ps = (const struct pmcstat_string *) s;
> return (ps->ps_hash);
> }
>
> /*
> * Initialize the string interning facility.
> */
>
> static void
> pmcstat_string_initialize(void)
> {
> int i;
>
> for (i = 0; i < PMCSTAT_NHASH; i++)
> LIST_INIT(&pmcstat_string_hash[i]);
> }
>
> /*
> * Destroy the string table, free'ing up space.
> */
>
> static void
> pmcstat_string_shutdown(void)
> {
> int i;
> struct pmcstat_string *ps, *pstmp;
>
> for (i = 0; i < PMCSTAT_NHASH; i++)
> LIST_FOREACH_SAFE(ps, &pmcstat_string_hash[i], ps_next,
> pstmp) {
> LIST_REMOVE(ps, ps_next);
> free(ps->ps_string);
> free(ps);
> }
> }
>
> /*
231a429
> const char *pathname;
234c432,433
< if ((fd = open(pgf->pgf_name, O_RDWR|O_NOFOLLOW|O_CREAT,
---
> pathname = pmcstat_string_unintern(pgf->pgf_name);
> if ((fd = open(pathname, O_RDWR|O_NOFOLLOW|O_CREAT,
236c435
< err(EX_OSERR, "ERROR: Cannot open \"%s\"", pgf->pgf_name);
---
> err(EX_OSERR, "ERROR: Cannot open \"%s\"", pathname);
263a463,464
> /* TODO size the arc table */
>
269c470
< err(EX_OSERR, "ERROR: Cannot write \"%s\"", pgf->pgf_name);
---
> err(EX_OSERR, "ERROR: Cannot write \"%s\"", pathname);
272c473,478
< const char *
---
> /*
> * Determine the full pathname of a gmon.out file for a given
> * (image,pmcid) combination. Return the interned string.
> */
>
> pmcstat_interned_string
282c488,489
< "%s/%s/%s", samplesdir, pmcname, image->pi_samplename);
---
> "%s/%s/%s", samplesdir, pmcname,
> pmcstat_string_unintern(image->pi_samplename));
284c491
< return pmcstat_string_intern(fullpath);
---
> return (pmcstat_string_intern(fullpath));
287a495,498
> /*
> * Mmap in a gmon.out file for processing.
> */
>
291a503
> const char *pathname;
292a505,506
> pathname = pmcstat_string_unintern(pgf->pgf_name);
>
294,296c508,509
< if ((fd = open(pgf->pgf_name, O_RDWR | O_NOFOLLOW, 0)) < 0)
< err(EX_OSERR, "ERROR: cannot open \"%s\"",
< pgf->pgf_name);
---
> if ((fd = open(pathname, O_RDWR | O_NOFOLLOW, 0)) < 0)
> err(EX_OSERR, "ERROR: cannot open \"%s\"", pathname);
302,303c515
< /* XXX unmap a few files and try again? */
< err(EX_OSERR, "ERROR: cannot map \"%s\"", pgf->pgf_name);
---
> err(EX_OSERR, "ERROR: cannot map \"%s\"", pathname);
309c521
< * Unmap the data mapped from a gmon.out file.
---
> * Unmap a gmon.out file after sync'ing its data to disk.
320a533,538
> /*
> * Determine whether a given executable image is an A.OUT object, and
> * if so, fill in its parameters from the text file.
> * Sets image->pi_type.
> */
>
322c540,541
< pmcstat_image_get_elf_params(struct pmcstat_image *image)
---
> pmcstat_image_get_aout_params(struct pmcstat_image *image,
> struct pmcstat_args *a)
323a543,590
> int fd;
> ssize_t nbytes;
> struct exec ex;
> const char *path;
> char buffer[PATH_MAX];
>
> path = pmcstat_string_unintern(image->pi_execpath);
> assert(path != NULL);
>
> if (image->pi_iskernelmodule)
> errx(EX_SOFTWARE, "ERROR: a.out kernel modules are "
> "unsupported \"%s\"", path);
>
> (void) snprintf(buffer, sizeof(buffer), "%s%s",
> a->pa_fsroot, path);
>
> if ((fd = open(buffer, O_RDONLY, 0)) < 0 ||
> (nbytes = read(fd, &ex, sizeof(ex))) < 0) {
> warn("WARNING: Cannot determine type of \"%s\"", path);
> image->pi_type = PMCSTAT_IMAGE_INDETERMINABLE;
> if (fd != -1)
> (void) close(fd);
> return;
> }
>
> (void) close(fd);
>
> if ((unsigned) nbytes != sizeof(ex) ||
> N_BADMAG(ex))
> return;
>
> image->pi_type = PMCSTAT_IMAGE_AOUT;
>
> /* TODO: the rest of a.out processing */
>
> return;
> }
>
> /*
> * Examine an ELF file to determine the size of its text segment.
> * Sets image->pi_type if anything conclusive can be determined about
> * this image.
> */
>
> static void
> pmcstat_image_get_elf_params(struct pmcstat_image *image,
> struct pmcstat_args *a)
> {
325c592
< struct stat st;
---
> const char *path;
336c603,605
< const char *path;
---
> enum pmcstat_image_type image_type;
> struct stat st;
> char buffer[PATH_MAX];
342c611
< path = image->pi_internedpath;
---
> path = pmcstat_string_unintern(image->pi_execpath);
344,345c613
< if ((fd = open(path, O_RDONLY, 0)) < 0)
< err(EX_OSERR, "ERROR: Cannot open \"%s\"", path);
---
> assert(path != NULL);
347,348c615,624
< if (fstat(fd, &st) < 0)
< err(EX_OSERR, "ERROR: Cannot stat \"%s\"", path);
---
> /*
> * Look for kernel modules under FSROOT/KERNELPATH/NAME,
> * and user mode executable objects under FSROOT/PATHNAME.
> */
> if (image->pi_iskernelmodule)
> (void) snprintf(buffer, sizeof(buffer), "%s%s/%s",
> a->pa_fsroot, a->pa_kernel, path);
> else
> (void) snprintf(buffer, sizeof(buffer), "%s%s",
> a->pa_fsroot, path);
350,352c626,635
< if ((mapbase = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) ==
< MAP_FAILED)
< err(EX_OSERR, "ERROR: Cannot mmap \"%s\"", path);
---
> if ((fd = open(buffer, O_RDONLY, 0)) < 0 ||
> fstat(fd, &st) < 0 ||
> (mapbase = mmap(0, st.st_size, PROT_READ, MAP_SHARED,
> fd, 0)) == MAP_FAILED) {
> warn("WARNING: Cannot determine type of \"%s\"", buffer);
> image->pi_type = PMCSTAT_IMAGE_INDETERMINABLE;
> if (fd != -1)
> (void) close(fd);
> return;
> }
355a639
> /* Punt on non-ELF objects */
358c642
< err(EX_SOFTWARE, "ERROR: \"%s\" not an ELF file", path);
---
> return;
360,363c644,650
< /* we only handle executable objects */
< if (h->e_type != ET_EXEC && h->e_type != ET_DYN)
< err(EX_DATAERR, "ERROR: Unknown file type for \"%s\"",
< image->pi_internedpath);
---
> /*
> * We only handle executable ELF objects and kernel
> * modules.
> */
> if (h->e_type != ET_EXEC && h->e_type != ET_DYN &&
> !(image->pi_iskernelmodule && h->e_type == ET_REL))
> return;
364a652,655
> image->pi_isdynamic = 0;
> image->pi_dynlinkerpath = NULL;
> image->pi_vaddr = 0;
>
384c675
< (char *) mapbase + \
---
> (char *) mapbase + \
386a678,682
> case PT_LOAD: \
> if ((PH)[i].p_offset == 0) \
> image->pi_vaddr = \
> (PH)[i].p_vaddr; \
> break; \
391,394d686
< image->pi_type = PMCSTAT_IMAGE_ELF;
< image->pi_isdynamic = 0;
< image->pi_dynlinkerpath = NULL;
<
411a704
> image_type = PMCSTAT_IMAGE_ELF32;
425a719
> image_type = PMCSTAT_IMAGE_ELF64;
433c727,728
< image->pi_end = maxva;
---
> image->pi_end = maxva;
> image->pi_type = image_type;
436a732,733
> return;
> }
437a735,757
> /*
> * Given an image descriptor, determine whether it is an ELF, or AOUT.
> * If no handler claims the image, set its type to 'INDETERMINABLE'.
> */
>
> static void
> pmcstat_image_determine_type(struct pmcstat_image *image,
> struct pmcstat_args *a)
> {
> assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN);
>
> /* Try each kind of handler in turn */
> if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
> pmcstat_image_get_elf_params(image, a);
> if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
> pmcstat_image_get_aout_params(image, a);
>
> /*
> * Otherwise, remember that we tried to determine
> * the object's type and had failed.
> */
> if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
> image->pi_type = PMCSTAT_IMAGE_INDETERMINABLE;
443a764,767
> *
> * We defer filling in the file format specific parts of the image
> * structure till the time we actually see a sample that would fall
> * into this image.
447c771,772
< pmcstat_image_from_path(const char *internedpath)
---
> pmcstat_image_from_path(pmcstat_interned_string internedpath,
> int iskernelmodule)
454c779
< hash = pmcstat_string_compute_hash(internedpath);
---
> hash = pmcstat_string_lookup_hash(internedpath);
456c781
< /* Look for an existing entry. */
---
> /* First, look for an existing entry. */
458c783,784
< if (pi->pi_internedpath == internedpath) {
---
> if (pi->pi_execpath == internedpath &&
> pi->pi_iskernelmodule == iskernelmodule) {
462c788
< return pi;
---
> return (pi);
471c797
< return NULL;
---
> return (NULL);
474c800
< pi->pi_internedpath = internedpath;
---
> pi->pi_execpath = internedpath;
477a804
> pi->pi_iskernelmodule = iskernelmodule;
486,487c813,815
< if ((sn = basename(internedpath)) == NULL)
< err(EX_OSERR, "ERROR: Cannot process \"%s\"", internedpath);
---
> if ((sn = basename(pmcstat_string_unintern(internedpath))) == NULL)
> err(EX_OSERR, "ERROR: Cannot process \"%s\"",
> pmcstat_string_unintern(internedpath));
490c818
< nlen = min(nlen, (int) sizeof(name) - 6); /* ".gmon\0" */
---
> nlen = min(nlen, (int) (sizeof(name) - sizeof(".gmon")));
493a822
> /* try use the unabridged name first */
496a826,829
> /*
> * Otherwise use a prefix from the original name and
> * upto 3 digits.
> */
498c831
< nlen = min(nlen, (int) sizeof(name)-10); /* "~ddd.gmon\0" */
---
> nlen = min(nlen, (int) (sizeof(name)-sizeof("~NNN.gmon")));
501,502c834,837
< count++;
< snprintf(name, sizeof(name), "%.*s~%3.3d",
---
> if (++count > 999)
> errx(EX_CANTCREAT, "ERROR: cannot create a gmon "
> "file for \"%s\"", name);
> snprintf(name, sizeof(name), "%.*s~%3.3d.gmon",
510a846
>
516c852
< return pi;
---
> return (pi);
520,558d855
< * Given an open file, determine its file type.
< */
<
< static enum pmcstat_image_type
< pmcstat_image_get_type(const char *path)
< {
< int fd;
< Elf_Ehdr eh;
< struct exec ex;
< ssize_t nbytes;
< char buffer[DEFAULT_BUFFER_SIZE];
<
< if ((fd = open(path, O_RDONLY)) < 0)
< err(EX_OSERR, "ERROR: Cannot open \"%s\"", path);
<
< nbytes = max(sizeof(eh), sizeof(ex));
< if ((nbytes = pread(fd, buffer, nbytes, 0)) < 0)
< err(EX_OSERR, "ERROR: Cannot read \"%s\"", path);
<
< (void) close(fd);
<
< /* check if its an ELF file */
< if ((unsigned) nbytes >= sizeof(Elf_Ehdr)) {
< bcopy(buffer, &eh, sizeof(eh));
< if (IS_ELF(eh))
< return PMCSTAT_IMAGE_ELF;
< }
<
< /* Look for an A.OUT header */
< if ((unsigned) nbytes >= sizeof(struct exec)) {
< bcopy(buffer, &ex, sizeof(ex));
< if (!N_BADMAG(ex))
< return PMCSTAT_IMAGE_AOUT;
< }
<
< return PMCSTAT_IMAGE_UNKNOWN;
< }
<
< /*
573a871,872
> image = map->ppm_image;
>
575,576c874,875
< * Find the gmon file corresponding to 'pmcid', creating it if
< * needed.
---
> * If this is the first time we are seeing a sample for
> * this executable image, try determine its parameters.
577a877,878
> if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
> pmcstat_image_determine_type(image, a);
579c880
< image = map->ppm_image;
---
> assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN);
580a882,891
> /* Ignore samples in images that we know nothing about. */
> if (image->pi_type == PMCSTAT_IMAGE_INDETERMINABLE) {
> pmcstat_stats.ps_samples_indeterminable++;
> return;
> }
>
> /*
> * Find the gmon file corresponding to 'pmcid', creating it if
> * needed.
> */
611a923,928
> assert(pgf->pgf_gmondata != NULL);
>
> /*
> *
> */
>
620c937
< if (hc[bucket] < 0xFFFF)
---
> if (hc[bucket] < 0xFFFFU) /* XXX tie this to sizeof(HISTCOUNTER) */
622c939,940
<
---
> else /* mark that an overflow occurred */
> pgf->pgf_overflow = 1;
626c944
< * Record the fact that PC values from 'lowpc' to 'highpc' come from
---
> * Record the fact that PC values from 'start' to 'end' come from
632c950
< uintfptr_t lowpc, uintfptr_t highpc)
---
> uintfptr_t start)
634a953
> uintfptr_t offset;
635a955,957
> assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN &&
> image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE);
>
637c959
< err(EX_OSERR, "ERROR: ");
---
> err(EX_OSERR, "ERROR: Cannot create a map entry");
639,640c961,968
< pcmnew->ppm_lowpc = lowpc;
< pcmnew->ppm_highpc = highpc;
---
> /*
> * Adjust the map entry to only cover the text portion
> * of the object.
> */
>
> offset = start - image->pi_vaddr;
> pcmnew->ppm_lowpc = image->pi_start + offset;
> pcmnew->ppm_highpc = image->pi_end + offset;
642a971,973
> assert(pcmnew->ppm_lowpc < pcmnew->ppm_highpc);
>
> /* Overlapped mmap()'s are assumed to never occur. */
644c975
< if (pcm->ppm_lowpc < lowpc)
---
> if (pcm->ppm_lowpc >= pcmnew->ppm_highpc)
653a985,1051
> * Unmap images in the range [start..end) associated with process
> * 'pp'.
> */
>
> static void
> pmcstat_image_unmap(struct pmcstat_process *pp, uintfptr_t start,
> uintfptr_t end)
> {
> struct pmcstat_pcmap *pcm, *pcmtmp, *pcmnew;
>
> assert(pp != NULL);
> assert(start < end);
>
> /*
> * Cases:
> * - we could have the range completely in the middle of an
> * existing pcmap; in this case we have to split the pcmap
> * structure into two (i.e., generate a 'hole').
> * - we could have the range covering multiple pcmaps; these
> * will have to be removed.
> * - we could have either 'start' or 'end' falling in the
> * middle of a pcmap; in this case shorten the entry.
> */
>
> TAILQ_FOREACH_SAFE(pcm, &pp->pp_map, ppm_next, pcmtmp) {
> assert(pcm->ppm_lowpc < pcm->ppm_highpc);
> if (pcm->ppm_highpc <= start)
> continue;
> if (pcm->ppm_lowpc > end)
> return;
> if (pcm->ppm_lowpc >= start && pcm->ppm_highpc <= end) {
> /*
> * The current pcmap is completely inside the
> * unmapped range: remove it entirely.
> */
> TAILQ_REMOVE(&pp->pp_map, pcm, ppm_next);
> free(pcm);
> } else if (pcm->ppm_lowpc < start && pcm->ppm_highpc > end) {
> /*
> * Split this pcmap into two; curtail the
> * current map to end at [start-1], and start
> * the new one at [end].
> */
> if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL)
> err(EX_OSERR, "ERROR: Cannot split a map "
> "entry");
>
> pcmnew->ppm_image = pcm->ppm_image;
>
> pcmnew->ppm_lowpc = end;
> pcmnew->ppm_highpc = pcm->ppm_highpc;
>
> pcm->ppm_highpc = start;
>
> TAILQ_INSERT_AFTER(&pp->pp_map, pcm, pcmnew, ppm_next);
>
> return;
> } else if (pcm->ppm_lowpc < start)
> pcm->ppm_lowpc = start;
> else if (pcm->ppm_highpc > end)
> pcm->ppm_highpc = end;
> else
> assert(0);
> }
> }
>
> /*
658c1056,1057
< pmcstat_pmcid_add(pmc_id_t pmcid, const char *name, struct pmcstat_args *a)
---
> pmcstat_pmcid_add(pmc_id_t pmcid, pmcstat_interned_string ps,
> struct pmcstat_args *a)
666c1065
< pr->pr_pmcname = name;
---
> pr->pr_pmcname = ps;
674c1073
< pr->pr_pmcname = name;
---
> pr->pr_pmcname = ps;
678c1077
< name);
---
> pmcstat_string_unintern(ps));
690c1089
< * Given a pmcid in use, find its human-readable name, or a
---
> * Given a pmcid in use, find its human-readable name.
701c1100
< return pr->pr_pmcname;
---
> return (pmcstat_string_unintern(pr->pr_pmcname));
713c1112
< return pr->pr_pmcname;
---
> return (pmcstat_string_unintern(pr->pr_pmcname));
717,718c1116
< * Associate an ELF image with a process. Argument 'path' names the
< * executable while 'fd' is an already open descriptor to it.
---
> * Associate an AOUT image with a process.
722,723c1120,1122
< pmcstat_process_add_elf_image(struct pmcstat_process *pp, const char *path,
< uintfptr_t entryaddr)
---
> pmcstat_process_aout_exec(struct pmcstat_process *pp,
> struct pmcstat_image *image, uintfptr_t entryaddr,
> struct pmcstat_args *a)
725,731c1124,1129
< size_t linelen;
< FILE *rf;
< char *line;
< uintmax_t libstart;
< struct pmcstat_image *image, *rtldimage;
< char libname[PATH_MAX], libpath[PATH_MAX];
< char command[PATH_MAX + sizeof(PMCSTAT_LDD_COMMAND) + 1];
---
> (void) pp;
> (void) image;
> (void) entryaddr;
> (void) a;
> /* TODO Implement a.out handling */
> }
733,735c1131,1133
< /* Look up path in the cache. */
< if ((image = pmcstat_image_from_path(path)) == NULL)
< return;
---
> /*
> * Associate an ELF image with a process.
> */
737,738c1135,1141
< if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
< pmcstat_image_get_elf_params(image);
---
> static void
> pmcstat_process_elf_exec(struct pmcstat_process *pp,
> struct pmcstat_image *image, uintfptr_t entryaddr,
> struct pmcstat_args *a)
> {
> uintmax_t libstart;
> struct pmcstat_image *rtldimage;
739a1143,1145
> assert(image->pi_type == PMCSTAT_IMAGE_ELF32 ||
> image->pi_type == PMCSTAT_IMAGE_ELF64);
>
741c1147
< pmcstat_image_link(pp, image, image->pi_start, image->pi_end);
---
> pmcstat_image_link(pp, image, image->pi_vaddr);
749a1156
>
758a1166
>
769,772c1177,1185
< rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath);
< if (rtldimage == NULL)
< err(EX_OSERR, "ERROR: Cannot find image for "
< "\"%s\"", image->pi_dynlinkerpath);
---
> rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath,
> 0);
> if (rtldimage == NULL) {
> warnx("WARNING: Cannot find image for \"%s\".",
> pmcstat_string_unintern(image->pi_dynlinkerpath));
> pmcstat_stats.ps_exec_errors++;
> return;
> }
>
774c1187
< pmcstat_image_get_elf_params(rtldimage);
---
> pmcstat_image_get_elf_params(rtldimage, a);
776,817c1189,1193
< libstart = entryaddr - rtldimage->pi_entry;
< pmcstat_image_link(pp, rtldimage, libstart,
< libstart + rtldimage->pi_end - rtldimage->pi_start);
<
< /* Process all other objects loaded by this executable. */
< (void) snprintf(command, sizeof(command), "%s %s",
< PMCSTAT_LDD_COMMAND, path);
<
< if ((rf = popen(command, "r")) == NULL)
< err(EX_OSERR, "ERROR: Cannot create pipe");
<
< (void) fgetln(rf, &linelen);
<
< while (!feof(rf) && !ferror(rf)) {
<
< if ((line = fgetln(rf, &linelen)) == NULL)
< continue;
< line[linelen-1] = '\0';
<
< libstart = 0;
< libpath[0] = libname[0] = '\0';
< if (sscanf(line, "%s \"%[^\"]\" %jx",
< libname, libpath, &libstart) != 3)
< continue;
<
< if (libstart == 0) {
< warnx("WARNING: object \"%s\" was not found "
< "for program \"%s\".", libname, path);
< continue;
< }
<
< image = pmcstat_image_from_path(
< pmcstat_string_intern(libpath));
< if (image == NULL)
< err(EX_OSERR, "ERROR: Cannot process "
< "\"%s\"", libpath);
<
< if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
< pmcstat_image_get_elf_params(image);
<
< pmcstat_image_link(pp, image, libstart + image->pi_start,
< libstart + image->pi_end);
---
> if (rtldimage->pi_type != PMCSTAT_IMAGE_ELF32 &&
> rtldimage->pi_type != PMCSTAT_IMAGE_ELF64) {
> warnx("WARNING: rtld not an ELF object \"%s\".",
> pmcstat_string_unintern(image->pi_dynlinkerpath));
> return;
820,821c1196,1197
< (void) pclose(rf);
<
---
> libstart = entryaddr - rtldimage->pi_entry;
> pmcstat_image_link(pp, rtldimage, libstart);
846c1222
< if (allocate && !pp->pp_isactive) {
---
> if (allocate && pp->pp_isactive == 0) {
858c1234
< return pp;
---
> return (pp);
862c1238
< return NULL;
---
> return (NULL);
873c1249
< return pp;
---
> return (pp);
881,882c1257,1259
< pmcstat_process_exec(struct pmcstat_process *pp, const char *path,
< uintfptr_t entryaddr)
---
> pmcstat_process_exec(struct pmcstat_process *pp,
> pmcstat_interned_string path, uintfptr_t entryaddr,
> struct pmcstat_args *a)
884d1260
< enum pmcstat_image_type filetype;
887c1263,1264
< if ((image = pmcstat_image_from_path(path)) == NULL)
---
> if ((image = pmcstat_image_from_path(path, 0)) == NULL) {
> pmcstat_stats.ps_exec_errors++;
888a1266
> }
891,893c1269
< filetype = pmcstat_image_get_type(path);
< else
< filetype = image->pi_type;
---
> pmcstat_image_determine_type(image, a);
895,897c1271,1277
< switch (filetype) {
< case PMCSTAT_IMAGE_ELF:
< pmcstat_process_add_elf_image(pp, path, entryaddr);
---
> assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN);
>
> switch (image->pi_type) {
> case PMCSTAT_IMAGE_ELF32:
> case PMCSTAT_IMAGE_ELF64:
> pmcstat_stats.ps_exec_elf++;
> pmcstat_process_elf_exec(pp, image, entryaddr, a);
900a1281,1282
> pmcstat_stats.ps_exec_aout++;
> pmcstat_process_aout_exec(pp, image, entryaddr, a);
902a1285,1288
> case PMCSTAT_IMAGE_INDETERMINABLE:
> pmcstat_stats.ps_exec_indeterminable++;
> break;
>
905c1291
< "\"%s\"", path);
---
> "\"%s\"", pmcstat_string_unintern(path));
919,921c1305,1310
< TAILQ_FOREACH(ppm, &p->pp_map, ppm_next)
< if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc)
< return ppm;
---
> TAILQ_FOREACH(ppm, &p->pp_map, ppm_next) {
> if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc)
> return (ppm);
> if (pc < ppm->ppm_lowpc)
> return (NULL);
> }
923c1312
< return NULL;
---
> return (NULL);
927,929d1315
< /*
< * Compute a 'hash' value for a string.
< */
932,1003d1317
< pmcstat_string_compute_hash(const char *s)
< {
< int hash;
<
< for (hash = 0; *s; s++)
< hash ^= *s;
<
< return hash & PMCSTAT_HASH_MASK;
< }
<
< /*
< * Intern a copy of string 's', and return a pointer to it.
< */
<
< static const char *
< pmcstat_string_intern(const char *s)
< {
< struct pmcstat_string *ps;
< int hash, len;
<
< hash = pmcstat_string_compute_hash(s);
< len = strlen(s);
<
< if ((ps = pmcstat_string_lookup(s)) != NULL)
< return ps->ps_string;
<
< if ((ps = malloc(sizeof(*ps))) == NULL)
< err(EX_OSERR, "ERROR: Could not intern string");
< ps->ps_len = len;
< ps->ps_hash = hash;
< ps->ps_string = strdup(s);
< LIST_INSERT_HEAD(&pmcstat_string_hash[hash], ps, ps_next);
< return ps->ps_string;
< }
<
< static struct pmcstat_string *
< pmcstat_string_lookup(const char *s)
< {
< struct pmcstat_string *ps;
< int hash, len;
<
< hash = pmcstat_string_compute_hash(s);
< len = strlen(s);
<
< LIST_FOREACH(ps, &pmcstat_string_hash[hash], ps_next)
< if (ps->ps_len == len && ps->ps_hash == hash &&
< strcmp(ps->ps_string, s) == 0)
< return ps;
< return NULL;
< }
<
< /*
< * Public Interfaces.
< */
<
< /*
< * Close a logfile, after first flushing all in-module queued data.
< */
<
< int
< pmcstat_close_log(struct pmcstat_args *a)
< {
< if (pmc_flush_logfile() < 0 ||
< pmc_configure_logfile(-1) < 0)
< err(EX_OSERR, "ERROR: logging failed");
< a->pa_flags &= ~(FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE);
< return a->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING :
< PMCSTAT_FINISHED;
< }
<
<
< int
1006a1321,1322
> pid_t pid;
> struct pmcstat_image *image;
1010c1326
< const char *image_path;
---
> pmcstat_interned_string image_path;
1016c1332,1340
< case PMCLOG_TYPE_MAPPINGCHANGE:
---
> case PMCLOG_TYPE_INITIALIZE:
> if ((ev.pl_u.pl_i.pl_version & 0xFF000000) !=
> PMC_VERSION_MAJOR << 24 && a->pa_verbosity > 0)
> warnx("WARNING: Log version 0x%x does not "
> "match compiled version 0x%x.",
> ev.pl_u.pl_i.pl_version,
> PMC_VERSION_MAJOR);
> break;
> case PMCLOG_TYPE_MAP_IN:
1019c1343,1348
< * process.
---
> * userland process or the kernel (pid == -1).
> *
> * We always allocate a process descriptor so
> * that subsequent samples seen for this
> * address range are mapped to the current
> * object being mapped in.
1020a1350,1366
> pid = ev.pl_u.pl_mi.pl_pid;
> if (pid == -1)
> pp = pmcstat_kernproc;
> else
> pp = pmcstat_process_lookup(pid,
> PMCSTAT_ALLOCATE);
>
> assert(pp != NULL);
>
> image_path = pmcstat_string_intern(ev.pl_u.pl_mi.
> pl_pathname);
> image = pmcstat_image_from_path(image_path, pid == -1);
> if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
> pmcstat_image_determine_type(image, a);
> if (image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE)
> pmcstat_image_link(pp, image,
> ev.pl_u.pl_mi.pl_start);
1022a1369,1385
> case PMCLOG_TYPE_MAP_OUT:
> /*
> * Remove an address map.
> */
> pid = ev.pl_u.pl_mo.pl_pid;
> if (pid == -1)
> pp = pmcstat_kernproc;
> else
> pp = pmcstat_process_lookup(pid, 0);
>
> if (pp == NULL) /* unknown process */
> break;
>
> pmcstat_image_unmap(pp, ev.pl_u.pl_mo.pl_start,
> ev.pl_u.pl_mo.pl_end);
> break;
>
1030a1394,1395
> pmcstat_stats.ps_samples_total++;
>
1032c1397,1398
< pp = pmcstat_process_lookup(ev.pl_u.pl_s.pl_pid, 1);
---
> pp = pmcstat_process_lookup(ev.pl_u.pl_s.pl_pid,
> PMCSTAT_ALLOCATE);
1035,1036c1401,1404
< pc)) == NULL)
< break; /* unknown process,offset pair */
---
> pc)) == NULL) { /* unknown process,offset pair */
> pmcstat_stats.ps_samples_unknown_offset++;
> break;
> }
1058c1426,1427
< pp = pmcstat_process_lookup(ev.pl_u.pl_x.pl_pid, 1);
---
> pp = pmcstat_process_lookup(ev.pl_u.pl_x.pl_pid,
> PMCSTAT_ALLOCATE);
1066c1435
< /* locate the descriptor for the new 'base' image */
---
> /* associate this process image */
1069,1070c1438
<
< /* link to the new image */
---
> assert(image_path != NULL);
1072c1440
< ev.pl_u.pl_x.pl_entryaddr);
---
> ev.pl_u.pl_x.pl_entryaddr, a);
1089c1457
< pp->pp_isactive = 0; /* make a zombie */
---
> pp->pp_isactive = 0; /* mark as a zombie */
1102,1103c1470,1471
< * If we had been tracking 'oldpid', then clone
< * its pid descriptor.
---
> * Allocate a process descriptor for the new
> * (child) process.
1104a1473,1480
> ppnew =
> pmcstat_process_lookup(ev.pl_u.pl_f.pl_newpid,
> PMCSTAT_ALLOCATE);
>
> /*
> * If we had been tracking the parent, clone
> * its address maps.
> */
1108,1112d1483
<
< ppnew =
< pmcstat_process_lookup(ev.pl_u.pl_f.pl_newpid, 1);
<
< /* copy the old process' address maps */
1115c1486
< ppm->ppm_lowpc, ppm->ppm_highpc);
---
> ppm->ppm_lowpc);
1124c1495
< return PMCSTAT_FINISHED;
---
> return (PMCSTAT_FINISHED);
1126c1497
< return PMCSTAT_RUNNING;
---
> return (PMCSTAT_RUNNING);
1132d1502
<
1134,1160d1503
< * Open a log file, for reading or writing.
< *
< * The function returns the fd of a successfully opened log or -1 in
< * case of failure.
< */
<
< int
< pmcstat_open(const char *path, int mode)
< {
< int fd;
<
< /*
< * If 'path' is "-" then open one of stdin or stdout depending
< * on the value of 'mode'. Otherwise, treat 'path' as a file
< * name and open that.
< */
< if (path[0] == '-' && path[1] == '\0')
< fd = (mode == PMCSTAT_OPEN_FOR_READ) ? 0 : 1;
< else
< fd = open(path, mode == PMCSTAT_OPEN_FOR_READ ?
< O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC),
< S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
<
< return fd;
< }
<
< /*
1164c1507
< int
---
> static int
1183,1190c1526,1530
< case PMCLOG_TYPE_MAPPINGCHANGE:
< PMCSTAT_PRINT_ENTRY(a,"mapping","%s %d %p %p \"%s\"",
< ev.pl_u.pl_m.pl_type == PMCLOG_MAPPING_INSERT ?
< "insert" : "delete",
< ev.pl_u.pl_m.pl_pid,
< (void *) ev.pl_u.pl_m.pl_start,
< (void *) ev.pl_u.pl_m.pl_end,
< ev.pl_u.pl_m.pl_pathname);
---
> case PMCLOG_TYPE_MAP_IN:
> PMCSTAT_PRINT_ENTRY(a,"map-in","%d %p \"%s\"",
> ev.pl_u.pl_mi.pl_pid,
> (void *) ev.pl_u.pl_mi.pl_start,
> ev.pl_u.pl_mi.pl_pathname);
1191a1532,1537
> case PMCLOG_TYPE_MAP_OUT:
> PMCSTAT_PRINT_ENTRY(a,"map-out","%d %p %p",
> ev.pl_u.pl_mo.pl_pid,
> (void *) ev.pl_u.pl_mo.pl_start,
> (void *) ev.pl_u.pl_mo.pl_end);
> break;
1255c1601
< return PMCSTAT_FINISHED;
---
> return (PMCSTAT_FINISHED);
1257c1603
< return PMCSTAT_RUNNING;
---
> return (PMCSTAT_RUNNING);
1265a1612,1659
> * Public Interfaces.
> */
>
> /*
> * Close a logfile, after first flushing all in-module queued data.
> */
>
> int
> pmcstat_close_log(struct pmcstat_args *a)
> {
> if (pmc_flush_logfile() < 0 ||
> pmc_configure_logfile(-1) < 0)
> err(EX_OSERR, "ERROR: logging failed");
> a->pa_flags &= ~(FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE);
> return (a->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING :
> PMCSTAT_FINISHED);
> }
>
>
>
> /*
> * Open a log file, for reading or writing.
> *
> * The function returns the fd of a successfully opened log or -1 in
> * case of failure.
> */
>
> int
> pmcstat_open_log(const char *path, int mode)
> {
> int fd;
>
> /*
> * If 'path' is "-" then open one of stdin or stdout depending
> * on the value of 'mode'. Otherwise, treat 'path' as a file
> * name and open that.
> */
> if (path[0] == '-' && path[1] == '\0')
> fd = (mode == PMCSTAT_OPEN_FOR_READ) ? 0 : 1;
> else
> fd = open(path, mode == PMCSTAT_OPEN_FOR_READ ?
> O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC),
> S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
>
> return (fd);
> }
>
> /*
1278c1672
< return pmcstat_print_log(a);
---
> return (pmcstat_print_log(a));
1281c1675
< return pmcstat_convert_log(a);
---
> return (pmcstat_convert_log(a));
1283a1678,1681
> /*
> * Initialize module.
> */
>
1288,1289d1685
< const char *kernpath;
< struct pmcstat_image *img;
1290a1687,1688
> (void) a;
>
1293c1691
< goto error;
---
> err(EX_OSERR, "ERROR: Cannot setenv");
1295a1694
> pmcstat_string_initialize();
1299d1697
< LIST_INIT(&pmcstat_string_hash[i]);
1302,1317c1700,1707
< /* create a fake 'process' entry for the kernel with pid == -1 */
< if ((pmcstat_kernproc = pmcstat_process_lookup((pid_t) -1, 1)) == NULL)
< goto error;
<
< if ((kernpath = pmcstat_string_intern(a->pa_kernel)) == NULL)
< goto error;
<
< img = pmcstat_image_from_path(kernpath);
<
< pmcstat_image_get_elf_params(img);
< pmcstat_image_link(pmcstat_kernproc, img, img->pi_start, img->pi_end);
<
< return;
<
< error:
< err(EX_OSERR, "ERROR: Cannot initialize logging");
---
> /*
> * Create a fake 'process' entry for the kernel with pid -1.
> * hwpmc(4) will subsequently inform us about where the kernel
> * and any loaded kernel modules are mapped.
> */
> if ((pmcstat_kernproc = pmcstat_process_lookup((pid_t) -1,
> PMCSTAT_ALLOCATE)) == NULL)
> err(EX_OSERR, "ERROR: Cannot initialize logging");
1319a1710,1713
> /*
> * Shutdown module.
> */
>
1321c1715
< pmcstat_shutdown_logging(void)
---
> pmcstat_shutdown_logging(struct pmcstat_args *a)
1323a1718
> FILE *mf;
1327d1721
< struct pmcstat_string *ps, *pstmp;
1328a1723,1735
> /* determine where to send the map file */
> mf = NULL;
> if (a->pa_mapfilename != NULL)
> mf = (strcmp(a->pa_mapfilename, "-") == 0) ?
> a->pa_printfile : fopen(a->pa_mapfilename, "w");
>
> if (mf == NULL && a->pa_flags & FLAG_DO_GPROF &&
> a->pa_verbosity >= 2)
> mf = a->pa_printfile;
>
> if (mf)
> (void) fprintf(mf, "MAP:\n");
>
1334,1336c1741,1748
< pmcstat_gmon_unmap_file(pgf);
< LIST_REMOVE(pgf, pgf_next);
< free(pgf);
---
> pmcstat_gmon_unmap_file(pgf);
> LIST_REMOVE(pgf, pgf_next);
>
> if (pgf->pgf_overflow && a->pa_verbosity >= 1)
> warnx("WARNING: profile \"%s\" "
> "overflowed.",
> pmcstat_string_unintern(pgf->pgf_name));
> free(pgf);
1337a1750,1753
> if (mf)
> (void) fprintf(mf, " \"%s\" -> \"%s\"\n",
> pmcstat_string_unintern(pi->pi_execpath),
> pmcstat_string_unintern(pi->pi_samplename));
1347,1351d1762
< LIST_FOREACH_SAFE(ps, &pmcstat_string_hash[i], ps_next,
< pstmp) {
< LIST_REMOVE(ps, ps_next);
< free(ps);
< }
1352a1764,1789
>
> pmcstat_string_shutdown();
>
> /*
> * Print errors unless -q was specified. Print all statistics
> * if verbosity > 1.
> */
> #define PRINT(N,V,A) do { \
> if (pmcstat_stats.ps_##V || (A)->pa_verbosity >= 2) \
> (void) fprintf((A)->pa_printfile, " %-40s %d\n",\
> N, pmcstat_stats.ps_##V); \
> } while (0)
>
> if (a->pa_verbosity >= 1 && a->pa_flags & FLAG_DO_GPROF) {
> (void) fprintf(a->pa_printfile, "CONVERSION STATISTICS:\n");
> PRINT("#exec/a.out", exec_aout, a);
> PRINT("#exec/elf", exec_elf, a);
> PRINT("#exec/unknown", exec_indeterminable, a);
> PRINT("#exec handling errors", exec_errors, a);
> PRINT("#samples/total", samples_total, a);
> PRINT("#samples/unclaimed", samples_unknown_offset, a);
> PRINT("#samples/unknown-object", samples_indeterminable, a);
> }
>
> if (mf)
> (void) fclose(mf);