Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 18 Sep 2009 21:39:59 +0000 (UTC)
From:      Fabien Thomas <fabient@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-user@freebsd.org
Subject:   svn commit: r197319 - user/fabient/svctest/usr.sbin/pmcstat
Message-ID:  <200909182139.n8ILdxHi001887@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200909182139.n8ILdxHi001887>