svn commit: r197319 - user/fabient/svctest/usr.sbin/pmcstat
Fabien Thomas
fabient at FreeBSD.org
Fri Sep 18 21:40:00 UTC 2009
Author: fabient
Date: Fri Sep 18 21:39:59 2009
New Revision: 197319
URL: http://svn.freebsd.org/changeset/base/197319
Log:
git branch to svn
Added:
user/fabient/svctest/usr.sbin/pmcstat/pmcstat_logct.c
Modified:
user/fabient/svctest/usr.sbin/pmcstat/Makefile
user/fabient/svctest/usr.sbin/pmcstat/pmcstat.c
user/fabient/svctest/usr.sbin/pmcstat/pmcstat.h
Modified: user/fabient/svctest/usr.sbin/pmcstat/Makefile
==============================================================================
--- user/fabient/svctest/usr.sbin/pmcstat/Makefile Fri Sep 18 20:35:05 2009 (r197318)
+++ user/fabient/svctest/usr.sbin/pmcstat/Makefile Fri Sep 18 21:39:59 2009 (r197319)
@@ -10,6 +10,6 @@ LDADD= -lelf -lkvm -lpmc -lm
WARNS?= 6
-SRCS= pmcstat.c pmcstat.h pmcstat_log.c
+SRCS= pmcstat.c pmcstat.h pmcstat_log.c pmcstat_logct.c
.include <bsd.prog.mk>
Modified: user/fabient/svctest/usr.sbin/pmcstat/pmcstat.c
==============================================================================
--- user/fabient/svctest/usr.sbin/pmcstat/pmcstat.c Fri Sep 18 20:35:05 2009 (r197318)
+++ user/fabient/svctest/usr.sbin/pmcstat/pmcstat.c Fri Sep 18 21:39:59 2009 (r197319)
@@ -494,6 +494,7 @@ pmcstat_show_usage(void)
"\t -D path\t create profiles in directory \"path\"\n"
"\t -E\t\t (toggle) show counts at process exit\n"
"\t -G file\t write a system-wide callgraph to \"file\"\n"
+ "\t -f type\t set output format (calltree|callgraph) for system-wide callgraph\n"
"\t -M file\t print executable/gmon file map to \"file\"\n"
"\t -N\t\t (toggle) capture callchains\n"
"\t -O file\t send log output to \"file\"\n"
@@ -594,7 +595,7 @@ main(int argc, char **argv)
}
while ((option = getopt(argc, argv,
- "CD:EG:M:NO:P:R:S:Wc:dgk:m:n:o:p:qr:s:t:vw:z:")) != -1)
+ "CD:EG:M:NO:P:R:S:Wc:dgk:m:n:o:p:qr:s:t:vw:z:f:")) != -1)
switch (option) {
case 'C': /* cumulative values */
use_cumulative_counts = !use_cumulative_counts;
@@ -633,6 +634,11 @@ main(int argc, char **argv)
graphfilename = optarg;
break;
+ case 'f': /* output system-wide callgraph in calltree format (KCachegrind) */
+ if (strcasecmp(optarg, "calltree") == 0)
+ args.pa_flags |= FLAG_DO_CALLTREE;
+ break;
+
case 'g': /* produce gprof compatible profiles */
args.pa_flags |= FLAG_DO_GPROF;
break;
@@ -1012,6 +1018,17 @@ main(int argc, char **argv)
if ((args.pa_flags & FLAG_DO_ANALYSIS) == 0)
args.pa_flags |= FLAG_DO_PRINT;
+ if (args.pa_flags & FLAG_DO_CALLTREE) {
+ pmcstat_ct_initialize_logging(&args);
+ args.pa_logfd = pmcstat_ct_open_log(args.pa_inputpath,
+ PMCSTAT_OPEN_FOR_READ);
+ if ((args.pa_logparser = pmclog_open(args.pa_logfd)) == NULL)
+ err(EX_OSERR, "ERROR: Cannot create parser");
+ pmcstat_ct_process_log(&args);
+ pmcstat_ct_shutdown_logging(&args);
+ exit(EX_OK);
+ }
+
pmcstat_initialize_logging(&args);
args.pa_logfd = pmcstat_open_log(args.pa_inputpath,
PMCSTAT_OPEN_FOR_READ);
Modified: user/fabient/svctest/usr.sbin/pmcstat/pmcstat.h
==============================================================================
--- user/fabient/svctest/usr.sbin/pmcstat/pmcstat.h Fri Sep 18 20:35:05 2009 (r197318)
+++ user/fabient/svctest/usr.sbin/pmcstat/pmcstat.h Fri Sep 18 21:39:59 2009 (r197319)
@@ -50,6 +50,7 @@
#define FLAG_DO_CALLGRAPHS 0x00004000 /* -G */
#define FLAG_DO_ANALYSIS 0x00008000 /* -g or -G */
#define FLAG_WANTS_MAPPINGS 0x00010000 /* -m */
+#define FLAG_DO_CALLTREE 0x00020000 /* -f calltree */
#define DEFAULT_SAMPLE_COUNT 65536
#define DEFAULT_WAIT_INTERVAL 5.0
@@ -136,19 +137,24 @@ void pmcstat_cleanup(struct pmcstat_args
void pmcstat_clone_event_descriptor(struct pmcstat_args *_a,
struct pmcstat_ev *_ev, uint32_t _cpumask);
int pmcstat_close_log(struct pmcstat_args *_a);
+int pmcstat_ct_close_log(struct pmcstat_args *_a);
void pmcstat_create_process(struct pmcstat_args *_a);
void pmcstat_find_targets(struct pmcstat_args *_a, const char *_arg);
void pmcstat_initialize_logging(struct pmcstat_args *_a);
+void pmcstat_ct_initialize_logging(struct pmcstat_args *_a);
void pmcstat_kill_process(struct pmcstat_args *_a);
int pmcstat_open_log(const char *_p, int _mode);
+int pmcstat_ct_open_log(const char *_p, int _mode);
void pmcstat_print_counters(struct pmcstat_args *_a);
void pmcstat_print_headers(struct pmcstat_args *_a);
void pmcstat_print_pmcs(struct pmcstat_args *_a);
void pmcstat_show_usage(void);
void pmcstat_shutdown_logging(struct pmcstat_args *_a);
+void pmcstat_ct_shutdown_logging(struct pmcstat_args *_a);
void pmcstat_start_pmcs(struct pmcstat_args *_a);
void pmcstat_start_process(void);
int pmcstat_process_log(struct pmcstat_args *_a);
+int pmcstat_ct_process_log(struct pmcstat_args *_a);
uint32_t pmcstat_get_cpumask(const char *_a);
#endif /* _PMCSTAT_H_ */
Added: user/fabient/svctest/usr.sbin/pmcstat/pmcstat_logct.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ user/fabient/svctest/usr.sbin/pmcstat/pmcstat_logct.c Fri Sep 18 21:39:59 2009 (r197319)
@@ -0,0 +1,2022 @@
+/*-
+ * Copyright (c) 2005-2007, Joseph Koshy
+ * Copyright (c) 2007 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by A. Joseph Koshy under
+ * sponsorship from the FreeBSD Foundation and Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Transform a hwpmc(4) log into human readable form, and into
+ * gprof(1) compatible profiles.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/gmon.h>
+#include <sys/imgact_aout.h>
+#include <sys/imgact_elf.h>
+#include <sys/mman.h>
+#include <sys/pmc.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libgen.h>
+#include <limits.h>
+#include <netdb.h>
+#include <pmc.h>
+#include <pmclog.h>
+#include <sysexits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pmcstat.h"
+
+#define min(A,B) ((A) < (B) ? (A) : (B))
+#define max(A,B) ((A) > (B) ? (A) : (B))
+
+#define PMCSTAT_ALLOCATE 1
+
+#define PMCSTAT_PC_RESERVED 0
+
+/*
+ * PUBLIC INTERFACES
+ *
+ * pmcstat_ct_initialize_logging() initialize this module, called first
+ * pmcstat_ct_shutdown_logging() orderly shutdown, called last
+ * pmcstat_ct_open_log() open an eventlog for processing
+ * pmcstat_ct_process_log() print/convert an event log
+ * pmcstat_ct_close_log() finish processing an event log
+ *
+ * IMPLEMENTATION NOTES
+ *
+ * We correlate each 'callchain' or 'sample' entry 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 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.
+ *
+ */
+
+typedef const void *pmcstat_interned_string;
+
+/*
+ * 'pmcstat_pmcrecord' is a mapping from PMC ids to human-readable
+ * names.
+ */
+
+struct pmcstat_pmcrecord {
+ LIST_ENTRY(pmcstat_pmcrecord) pr_next;
+ pmc_id_t pr_pmcid;
+ pmcstat_interned_string pr_pmcname;
+ unsigned int pr_index;
+};
+
+static unsigned int pmcstat_npmcs = 0;
+static LIST_HEAD(,pmcstat_pmcrecord) pmcstat_pmcs = LIST_HEAD_INITIALIZER(&pmcstat_pmcs);
+static int pmcstat_mergepmc = 1;
+
+/*
+ * A 'pmcstat_image' structure describes an executable program on
+ * disk. 'pi_execpath' is a cookie representing the pathname of
+ * the executable. 'pi_start' and 'pi_end' are the least and greatest
+ * virtual addresses for the text segments in the executable.
+ * 'pi_gmonlist' contains a linked list of gmon.out files associated
+ * with this image.
+ */
+
+enum pmcstat_image_type {
+ 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 */
+};
+
+struct pmcstat_image {
+ LIST_ENTRY(pmcstat_image) pi_next; /* hash link */
+ TAILQ_ENTRY(pmcstat_image) pi_lru; /* LRU list */
+ pmcstat_interned_string pi_execpath; /* cookie */
+ pmcstat_interned_string pi_samplename; /* sample path name */
+ pmcstat_interned_string pi_fullpath; /* path to FS object */
+ pmcstat_interned_string pi_name; /* file name */
+ enum pmcstat_image_type pi_type; /* executable type */
+
+ /*
+ * Executables have pi_start and pi_end; these are zero
+ * for shared libraries.
+ */
+ uintfptr_t pi_start; /* start address (inclusive) */
+ uintfptr_t pi_end; /* end address (exclusive) */
+ uintfptr_t pi_entry; /* entry address */
+ 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 */
+
+ /* All symbols associated with this object. */
+ struct pmcstat_symbol *pi_symbols;
+ size_t pi_symcount;
+
+};
+
+/*
+ * All image descriptors are kept in a hash table.
+ */
+static LIST_HEAD(,pmcstat_image) pmcstat_image_hash[PMCSTAT_NHASH];
+
+/*
+ * A 'pmcstat_pcmap' structure maps a virtual address range to an
+ * underlying 'pmcstat_image' descriptor.
+ */
+struct pmcstat_pcmap {
+ TAILQ_ENTRY(pmcstat_pcmap) ppm_next;
+ uintfptr_t ppm_lowpc;
+ uintfptr_t ppm_highpc;
+ struct pmcstat_image *ppm_image;
+};
+
+/*
+ * 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.
+ */
+
+struct pmcstat_process {
+ LIST_ENTRY(pmcstat_process) pp_next; /* hash-next */
+ pid_t pp_pid; /* associated pid */
+ int pp_isactive; /* whether active */
+ uintfptr_t pp_entryaddr; /* entry address */
+ TAILQ_HEAD(,pmcstat_pcmap) pp_map; /* address range map */
+};
+
+/*
+ * All process descriptors are kept in a hash table.
+ */
+static LIST_HEAD(,pmcstat_process) pmcstat_process_hash[PMCSTAT_NHASH];
+
+static struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */
+
+/*
+ * Each function symbol tracked by pmcstat(8).
+ */
+
+struct pmcstat_symbol {
+ pmcstat_interned_string ps_name;
+ uint64_t ps_start;
+ uint64_t ps_end;
+};
+
+static pmcstat_interned_string pmcstat_previous_filename_printed;
+
+struct pmcstat_ctnode;
+
+struct pmcstat_ctarc {
+ uint32_t pcta_child_count_c;
+ uint32_t *pcta_child_count;
+ struct pmcstat_ctnode *pcta_child;
+};
+
+/*
+ * Each call tree node is tracked by a pmcstat_ctnode struct.
+ */
+struct pmcstat_ctnode {
+ struct pmcstat_image *pct_image;
+ uintfptr_t pct_func;
+ uint32_t pct_self_count_c;
+ uint32_t *pct_self_count;
+
+ uint32_t pct_narc;
+ uint32_t pct_arc_c;
+ struct pmcstat_ctarc *pct_arc;
+};
+
+struct pmcstat_ctnode_hash {
+ struct pmcstat_ctnode *pch_ctnode;
+ LIST_ENTRY(pmcstat_ctnode_hash) pch_next;
+};
+
+/*
+ * All nodes indexed by function/image name are placed in a hash table.
+ */
+static LIST_HEAD(,pmcstat_ctnode_hash) pmcstat_ctnode_hash[PMCSTAT_NHASH];
+
+/* 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_skipped; /* #samples filtered out for any reason */
+ int ps_samples_unknown_offset; /* #samples of rank 0 not in a map */
+ int ps_samples_indeterminable; /* #samples in indeterminable images */
+ int ps_callchain_dubious_frames;/* #dubious frame pointers seen */
+ int ps_callchain_single_frames; /* #single frame seen */
+ /* TODO: add stats for specific calltree error
+ */
+} pmcstat_stats;
+
+
+/*
+ * Prototypes
+ */
+
+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);
+static void pmcstat_image_link(struct pmcstat_process *_pp,
+ struct pmcstat_image *_i, uintfptr_t _lpc);
+
+static void pmcstat_pmcid_add(pmc_id_t _pmcid, pmcstat_interned_string _name);
+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);
+static void pmcstat_process_exec(struct pmcstat_process *_pp,
+ pmcstat_interned_string _path, uintfptr_t _entryaddr,
+ struct pmcstat_args *_ao);
+static struct pmcstat_process *pmcstat_process_lookup(pid_t _pid,
+ int _allocate);
+static struct pmcstat_pcmap *pmcstat_process_find_map(
+ struct pmcstat_process *_p, uintfptr_t _pc);
+
+static int pmcstat_string_compute_hash(const char *_string);
+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);
+
+
+/*
+ * 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];
+
+
+/*
+ * Block realloc items
+ */
+static void
+pmcstat_growit(uint32_t item, uint32_t *count, uint32_t size, void **items)
+{
+#define GROWIT_BLOCKSIZE 4
+ uint32_t new_count;
+
+ if (item < *count)
+ return;
+
+ new_count = *count + max(item + 1 - *count, GROWIT_BLOCKSIZE);
+ *items = realloc(*items, new_count * size);
+. if (*items == NULL)
+ errx(EX_SOFTWARE, "ERROR: out of memory");
+ bzero((char *)*items + *count * size, (new_count - *count) * size);
+ *count = new_count;
+}
+
+/*
+ * 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);
+ }
+}
+
+/*
+ * 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.
+ */
+
+static void
+pmcstat_image_get_aout_params(struct pmcstat_image *image,
+ struct pmcstat_args *a)
+{
+ 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;
+}
+
+/*
+ * Helper function.
+ */
+
+static int
+pmcstat_symbol_compare(const void *a, const void *b)
+{
+ const struct pmcstat_symbol *sym1, *sym2;
+
+ sym1 = (const struct pmcstat_symbol *) a;
+ sym2 = (const struct pmcstat_symbol *) b;
+
+ if (sym1->ps_end <= sym2->ps_start)
+ return (-1);
+ if (sym1->ps_start >= sym2->ps_end)
+ return (1);
+ return (0);
+}
+
+/*
+ * Map an address to a symbol in an image.
+ */
+
+static struct pmcstat_symbol *
+pmcstat_symbol_search(struct pmcstat_image *image, uintfptr_t addr)
+{
+ struct pmcstat_symbol sym;
+
+ if (image->pi_symbols == NULL)
+ return (NULL);
+
+ sym.ps_name = NULL;
+ sym.ps_start = addr;
+ sym.ps_end = addr + 1;
+
+ return (bsearch((void *) &sym, image->pi_symbols,
+ image->pi_symcount, sizeof(struct pmcstat_symbol),
+ pmcstat_symbol_compare));
+}
+
+/*
+ * Add the list of symbols in the given section to the list associated
+ * with the object.
+ */
+static void
+pmcstat_image_add_symbols(struct pmcstat_image *image, Elf *e,
+ Elf_Scn *scn, GElf_Shdr *sh)
+{
+ int firsttime;
+ size_t n, newsyms, nshsyms, nfuncsyms;
+ struct pmcstat_symbol *symptr;
+ char *fnname;
+ GElf_Sym sym;
+ Elf_Data *data;
+
+ if ((data = elf_getdata(scn, NULL)) == NULL)
+ return;
+
+ /*
+ * Determine the number of functions named in this
+ * section.
+ */
+
+ nshsyms = sh->sh_size / sh->sh_entsize;
+ for (n = nfuncsyms = 0; n < nshsyms; n++) {
+ if (gelf_getsym(data, (int) n, &sym) != &sym)
+ return;
+ if (GELF_ST_TYPE(sym.st_info) == STT_FUNC)
+ nfuncsyms++;
+ }
+
+ if (nfuncsyms == 0)
+ return;
+
+ /*
+ * Allocate space for the new entries.
+ */
+ firsttime = image->pi_symbols == NULL;
+ symptr = realloc(image->pi_symbols,
+ sizeof(*symptr) * (image->pi_symcount + nfuncsyms));
+ if (symptr == image->pi_symbols) /* realloc() failed. */
+ return;
+ image->pi_symbols = symptr;
+
+ /*
+ * Append new symbols to the end of the current table.
+ */
+ symptr += image->pi_symcount;
+
+ for (n = newsyms = 0; n < nshsyms; n++) {
+ if (gelf_getsym(data, (int) n, &sym) != &sym)
+ return;
+ if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
+ continue;
+
+ if (!firsttime && pmcstat_symbol_search(image, sym.st_value))
+ continue; /* We've seen this symbol already. */
+
+ if ((fnname = elf_strptr(e, sh->sh_link, sym.st_name))
+ == NULL)
+ continue;
+
+ symptr->ps_name = pmcstat_string_intern(fnname);
+ symptr->ps_start = sym.st_value - image->pi_vaddr;
+ symptr->ps_end = symptr->ps_start + sym.st_size;
+ symptr++;
+
+ newsyms++;
+ }
+
+ image->pi_symcount += newsyms;
+
+ assert(newsyms <= nfuncsyms);
+
+ /*
+ * Return space to the system if there were duplicates.
+ */
+ if (newsyms < nfuncsyms)
+ image->pi_symbols = realloc(image->pi_symbols,
+ sizeof(*symptr) * image->pi_symcount);
+
+ /*
+ * Keep the list of symbols sorted.
+ */
+ qsort(image->pi_symbols, image->pi_symcount, sizeof(*symptr),
+ pmcstat_symbol_compare);
+
+ /*
+ * Deal with function symbols that have a size of 'zero' by
+ * making them extend to the next higher address. These
+ * symbols are usually defined in assembly code.
+ */
+ for (symptr = image->pi_symbols;
+ symptr < image->pi_symbols + (image->pi_symcount - 1);
+ symptr++)
+ if (symptr->ps_start == symptr->ps_end)
+ symptr->ps_end = (symptr+1)->ps_start;
+}
+
+/*
+ * 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)
+{
+ int fd;
+ size_t i, nph, nsh;
+ const char *path, *elfbase;
+ uintfptr_t minva, maxva;
+ Elf *e;
+ Elf_Scn *scn;
+ GElf_Ehdr eh;
+ GElf_Phdr ph;
+ GElf_Shdr sh;
+ enum pmcstat_image_type image_type;
+ char buffer[PATH_MAX], *p, *q;
+
+ assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN);
+
+ image->pi_start = minva = ~(uintfptr_t) 0;
+ image->pi_end = maxva = (uintfptr_t) 0;
+ image->pi_type = image_type = PMCSTAT_IMAGE_INDETERMINABLE;
+ image->pi_isdynamic = 0;
+ image->pi_dynlinkerpath = NULL;
+ image->pi_vaddr = 0;
+
+ path = pmcstat_string_unintern(image->pi_execpath);
+ assert(path != NULL);
+
+ /*
+ * 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);
+
+ e = NULL;
+ if ((fd = open(buffer, O_RDONLY, 0)) < 0 ||
+ (e = elf_begin(fd, ELF_C_READ, NULL)) == NULL ||
+ (elf_kind(e) != ELF_K_ELF)) {
+ warnx("WARNING: Cannot determine the type of \"%s\".",
+ buffer);
+ goto done;
+ }
+
+ if (gelf_getehdr(e, &eh) != &eh) {
+ warnx("WARNING: Cannot retrieve the ELF Header for "
+ "\"%s\": %s.", buffer, elf_errmsg(-1));
+ goto done;
+ }
+
+ if (eh.e_type != ET_EXEC && eh.e_type != ET_DYN &&
+ !(image->pi_iskernelmodule && eh.e_type == ET_REL)) {
+ warnx("WARNING: \"%s\" is of an unsupported ELF type.",
+ buffer);
+ goto done;
+ }
+
+ image_type = eh.e_ident[EI_CLASS] == ELFCLASS32 ?
+ PMCSTAT_IMAGE_ELF32 : PMCSTAT_IMAGE_ELF64;
+
+ /*
+ * Determine the virtual address where an executable would be
+ * loaded. Additionally, for dynamically linked executables,
+ * save the pathname to the runtime linker.
+ */
+ if (eh.e_type == ET_EXEC) {
+ if (elf_getphnum(e, &nph) == 0) {
+ warnx("WARNING: Could not determine the number of "
+ "program headers in \"%s\": %s.", buffer,
+ elf_errmsg(-1));
+ goto done;
+ }
+ for (i = 0; i < eh.e_phnum; i++) {
+ if (gelf_getphdr(e, i, &ph) != &ph) {
+ warnx("WARNING: Retrieval of PHDR entry #%ju "
+ "in \"%s\" failed: %s.", (uintmax_t) i,
+ buffer, elf_errmsg(-1));
+ goto done;
+ }
+ switch (ph.p_type) {
+ case PT_DYNAMIC:
+ image->pi_isdynamic = 1;
+ break;
+ case PT_INTERP:
+ if ((elfbase = elf_rawfile(e, NULL)) == NULL) {
+ warnx("WARNING: Cannot retrieve the "
+ "interpreter for \"%s\": %s.",
+ buffer, elf_errmsg(-1));
+ goto done;
+ }
+ image->pi_dynlinkerpath =
+ pmcstat_string_intern(elfbase +
+ ph.p_offset);
+ break;
+ case PT_LOAD:
+ if (ph.p_offset == 0)
+ image->pi_vaddr = ph.p_vaddr;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Get the min and max VA associated with this ELF object.
+ */
+ if (elf_getshnum(e, &nsh) == 0) {
+ warnx("WARNING: Could not determine the number of sections "
+ "for \"%s\": %s.", buffer, elf_errmsg(-1));
+ goto done;
+ }
+
+ for (i = 0; i < nsh; i++) {
+ if ((scn = elf_getscn(e, i)) == NULL ||
+ gelf_getshdr(scn, &sh) != &sh) {
+ warnx("WARNING: Could not retrieve section header "
+ "#%ju in \"%s\": %s.", (uintmax_t) i, buffer,
+ elf_errmsg(-1));
+ goto done;
+ }
+ if (sh.sh_flags & SHF_EXECINSTR) {
+ minva = min(minva, sh.sh_addr);
+ maxva = max(maxva, sh.sh_addr + sh.sh_size);
+ }
+ if (sh.sh_type == SHT_SYMTAB || sh.sh_type == SHT_DYNSYM)
+ pmcstat_image_add_symbols(image, e, scn, &sh);
+ }
+
+ image->pi_start = minva;
+ image->pi_end = maxva;
+ image->pi_type = image_type;
+ image->pi_fullpath = pmcstat_string_intern(buffer);
+
+ for (p = q = buffer; *p ; p++) {
+ if ( *p == '\\' || *p == '/' )
+ q = p;
+ }
+ image->pi_name = pmcstat_string_intern(q);
+ done:
+ (void) elf_end(e);
+ if (fd >= 0)
+ (void) close(fd);
+ return;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Locate an image descriptor given an interned path, adding a fresh
+ * descriptor to the cache if necessary. This function also finds a
+ * suitable name for this image's sample file.
+ *
+ * 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.
+ */
+
+static struct pmcstat_image *
+pmcstat_image_from_path(pmcstat_interned_string internedpath,
+ int iskernelmodule)
+{
+ int hash;
+ struct pmcstat_image *pi;
+
+ hash = pmcstat_string_lookup_hash(internedpath);
+
+ /* First, look for an existing entry. */
+ LIST_FOREACH(pi, &pmcstat_image_hash[hash], pi_next)
+ if (pi->pi_execpath == internedpath &&
+ pi->pi_iskernelmodule == iskernelmodule)
+ return (pi);
+
+ /*
+ * Allocate a new entry and place it at the head of the hash
+ * and LRU lists.
+ */
+ pi = malloc(sizeof(*pi));
+ if (pi == NULL)
+ return (NULL);
+
+ pi->pi_type = PMCSTAT_IMAGE_UNKNOWN;
+ pi->pi_execpath = internedpath;
+ pi->pi_start = ~0;
+ pi->pi_end = 0;
+ pi->pi_entry = 0;
+ pi->pi_vaddr = 0;
+ pi->pi_isdynamic = 0;
+ pi->pi_iskernelmodule = iskernelmodule;
+ pi->pi_dynlinkerpath = NULL;
+ pi->pi_symbols = NULL;
+ pi->pi_symcount = 0;
+
+ LIST_INSERT_HEAD(&pmcstat_image_hash[hash], pi, pi_next);
+
+ return (pi);
+}
+
+/*
+ * Record the fact that PC values from 'start' to 'end' come from
+ * image 'image'.
+ */
+
+static void
+pmcstat_image_link(struct pmcstat_process *pp, struct pmcstat_image *image,
+ uintfptr_t start)
+{
+ struct pmcstat_pcmap *pcm, *pcmnew;
+ uintfptr_t offset;
+
+ assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN &&
+ image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE);
+
+ if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL)
+ err(EX_OSERR, "ERROR: Cannot create a map entry");
+
+ /*
+ * Adjust the map entry to only cover the text portion
+ * of the object.
+ */
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-user
mailing list