svn commit: r324959 - in head: lib lib/libpmcstat share/mk usr.sbin/pmcstat

Ruslan Bukin br at FreeBSD.org
Tue Oct 24 16:28:02 UTC 2017


Author: br
Date: Tue Oct 24 16:28:00 2017
New Revision: 324959
URL: https://svnweb.freebsd.org/changeset/base/324959

Log:
  Extract a set of pmcstat functions and interfaces to the new internal
  library -- libpmcstat.
  
  This includes PMC logging module, symbols lookup functions,
  ELF parsing, process management, PMC attachment, etc.
  
  This allows to reuse code while building new hwpmc(4)-based applications.
  
  Also add pmcstat_symbol_search_by_name() function that allows to find
  mapped IP range for a given function name.
  
  Reviewed by:	kib
  Sponsored by:	DARPA, AFRL
  Differential Revision:	https://reviews.freebsd.org/D12718

Added:
  head/lib/libpmcstat/
  head/lib/libpmcstat/Makefile   (contents, props changed)
  head/lib/libpmcstat/libpmcstat.h   (contents, props changed)
  head/lib/libpmcstat/libpmcstat_event.c   (contents, props changed)
  head/lib/libpmcstat/libpmcstat_image.c   (contents, props changed)
  head/lib/libpmcstat/libpmcstat_logging.c   (contents, props changed)
  head/lib/libpmcstat/libpmcstat_process.c   (contents, props changed)
  head/lib/libpmcstat/libpmcstat_string.c   (contents, props changed)
  head/lib/libpmcstat/libpmcstat_symbol.c   (contents, props changed)
Modified:
  head/lib/Makefile
  head/share/mk/src.libnames.mk
  head/usr.sbin/pmcstat/Makefile
  head/usr.sbin/pmcstat/pmcpl_callgraph.c
  head/usr.sbin/pmcstat/pmcpl_callgraph.h
  head/usr.sbin/pmcstat/pmcpl_calltree.c
  head/usr.sbin/pmcstat/pmcpl_calltree.h
  head/usr.sbin/pmcstat/pmcpl_gprof.c
  head/usr.sbin/pmcstat/pmcstat.c
  head/usr.sbin/pmcstat/pmcstat.h
  head/usr.sbin/pmcstat/pmcstat_log.c
  head/usr.sbin/pmcstat/pmcstat_log.h

Modified: head/lib/Makefile
==============================================================================
--- head/lib/Makefile	Tue Oct 24 16:24:12 2017	(r324958)
+++ head/lib/Makefile	Tue Oct 24 16:28:00 2017	(r324959)
@@ -186,7 +186,7 @@ _libdl=		libdl
 .endif
 
 SUBDIR.${MK_OPENSSL}+=	libmp
-SUBDIR.${MK_PMC}+=	libpmc
+SUBDIR.${MK_PMC}+=	libpmc libpmcstat
 SUBDIR.${MK_RADIUS_SUPPORT}+=	libradius
 SUBDIR.${MK_SENDMAIL}+=	libmilter libsm libsmdb libsmutil
 SUBDIR.${MK_TELNET}+=	libtelnet

Added: head/lib/libpmcstat/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libpmcstat/Makefile	Tue Oct 24 16:28:00 2017	(r324959)
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+PACKAGE=lib${LIB}
+LIB=	pmcstat
+INTERNALLIB=
+
+SRCS=	\
+	libpmcstat_event.c	\
+	libpmcstat_image.c	\
+	libpmcstat_logging.c	\
+	libpmcstat_process.c	\
+	libpmcstat_string.c	\
+	libpmcstat_symbol.c
+INCS=	libpmcstat.h
+
+.include <bsd.lib.mk>

Added: head/lib/libpmcstat/libpmcstat.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libpmcstat/libpmcstat.h	Tue Oct 24 16:28:00 2017	(r324959)
@@ -0,0 +1,382 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	_LIBPMCSTAT_H_
+#define	_LIBPMCSTAT_H_
+
+#include <sys/_cpuset.h>
+#include <sys/queue.h>
+
+#include <stdio.h>
+#include <gelf.h>
+
+#define	PMCSTAT_ALLOCATE		1
+
+#define	NSOCKPAIRFD			2
+#define	PARENTSOCKET			0
+#define	CHILDSOCKET			1
+
+#define	PMCSTAT_OPEN_FOR_READ		0
+#define	PMCSTAT_OPEN_FOR_WRITE		1
+#define	READPIPEFD			0
+#define	WRITEPIPEFD			1
+#define	NPIPEFD				2
+
+#define	PMCSTAT_NHASH			256
+#define	PMCSTAT_HASH_MASK		0xFF
+
+typedef const void *pmcstat_interned_string;
+struct pmc_plugins;
+
+enum pmcstat_state {
+	PMCSTAT_FINISHED = 0,
+	PMCSTAT_EXITING  = 1,
+	PMCSTAT_RUNNING  = 2
+};
+
+struct pmcstat_ev {
+	STAILQ_ENTRY(pmcstat_ev) ev_next;
+	int		ev_count; /* associated count if in sampling mode */
+	uint32_t	ev_cpu;	  /* cpus for this event */
+	int		ev_cumulative;  /* show cumulative counts */
+	int		ev_flags; /* PMC_F_* */
+	int		ev_fieldskip;   /* #leading spaces */
+	int		ev_fieldwidth;  /* print width */
+	enum pmc_mode	ev_mode;  /* desired mode */
+	char	       *ev_name;  /* (derived) event name */
+	pmc_id_t	ev_pmcid; /* allocated ID */
+	pmc_value_t	ev_saved; /* for incremental counts */
+	char	       *ev_spec;  /* event specification */
+};
+
+struct pmcstat_target {
+	SLIST_ENTRY(pmcstat_target) pt_next;
+	pid_t		pt_pid;
+};
+
+struct pmcstat_args {
+	int	pa_flags;		/* argument flags */
+#define	FLAG_HAS_TARGET			0x00000001	/* process target */
+#define	FLAG_HAS_WAIT_INTERVAL		0x00000002	/* -w secs */
+#define	FLAG_HAS_OUTPUT_LOGFILE		0x00000004	/* -O file or pipe */
+#define	FLAG_HAS_COMMANDLINE		0x00000008	/* command */
+#define	FLAG_HAS_SAMPLING_PMCS		0x00000010	/* -S or -P */
+#define	FLAG_HAS_COUNTING_PMCS		0x00000020	/* -s or -p */
+#define	FLAG_HAS_PROCESS_PMCS		0x00000040	/* -P or -p */
+#define	FLAG_HAS_SYSTEM_PMCS		0x00000080	/* -S or -s */
+#define	FLAG_HAS_PIPE			0x00000100	/* implicit log */
+#define	FLAG_READ_LOGFILE		0x00000200	/* -R file */
+#define	FLAG_DO_GPROF			0x00000400	/* -g */
+#define	FLAG_HAS_SAMPLESDIR		0x00000800	/* -D dir */
+#define	FLAG_HAS_KERNELPATH		0x00001000	/* -k kernel */
+#define	FLAG_DO_PRINT			0x00002000	/* -o */
+#define	FLAG_DO_CALLGRAPHS		0x00004000	/* -G or -F */
+#define	FLAG_DO_ANNOTATE		0x00008000	/* -m */
+#define	FLAG_DO_TOP			0x00010000	/* -T */
+#define	FLAG_DO_ANALYSIS		0x00020000	/* -g or -G or -m or -T */
+#define	FLAGS_HAS_CPUMASK		0x00040000	/* -c */
+#define	FLAG_HAS_DURATION		0x00080000	/* -l secs */
+#define	FLAG_DO_WIDE_GPROF_HC		0x00100000	/* -e */
+
+	int	pa_required;		/* required features */
+	int	pa_pplugin;		/* pre-processing plugin */
+	int	pa_plugin;		/* analysis plugin */
+	int	pa_verbosity;		/* verbosity level */
+	FILE	*pa_printfile;		/* where to send printed output */
+	int	pa_logfd;		/* output log file */
+	char	*pa_inputpath;		/* path to input log */
+	char	*pa_outputpath;		/* path to output log */
+	void	*pa_logparser;		/* log file parser */
+	const char	*pa_fsroot;	/* FS root where executables reside */
+	char	*pa_kernel;		/* pathname of the kernel */
+	const char	*pa_samplesdir;	/* directory for profile files */
+	const char	*pa_mapfilename;/* mapfile name */
+	FILE	*pa_graphfile;		/* where to send the callgraph */
+	int	pa_graphdepth;		/* print depth for callgraphs */
+	double	pa_interval;		/* printing interval in seconds */
+	cpuset_t	pa_cpumask;	/* filter for CPUs analysed */
+	int	pa_ctdumpinstr;		/* dump instructions with calltree */
+	int	pa_topmode;		/* delta or accumulative */
+	int	pa_toptty;		/* output to tty or file */
+	int	pa_topcolor;		/* terminal support color */
+	int	pa_mergepmc;		/* merge PMC with same name */
+	double	pa_duration;		/* time duration */
+	int	pa_argc;
+	char	**pa_argv;
+	STAILQ_HEAD(, pmcstat_ev) pa_events;
+	SLIST_HEAD(, pmcstat_target) pa_targets;
+};
+
+/*
+ * Each function symbol tracked by pmcstat(8).
+ */
+
+struct pmcstat_symbol {
+	pmcstat_interned_string ps_name;
+	uint64_t	ps_start;
+	uint64_t	ps_end;
+};
+
+/*
+ * 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 */
+	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;	/* display 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;
+
+	/* Handle to addr2line for this image. */
+	FILE *pi_addr2line;
+
+	/*
+	 * Plugins private data
+	 */
+
+	/* gprof:
+	 * An image can be associated with one or more gmon.out files;
+	 * one per PMC.
+	 */
+	LIST_HEAD(,pmcstat_gmonfile) pi_gmlist;
+};
+
+extern LIST_HEAD(pmcstat_image_hash_list, pmcstat_image) pmcstat_image_hash[PMCSTAT_NHASH];
+
+/*
+ * 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 comparison 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;
+};
+
+/*
+ * 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 */
+};
+extern LIST_HEAD(pmcstat_process_hash_list, pmcstat_process) pmcstat_process_hash[PMCSTAT_NHASH];
+
+/*
+ * '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;
+	int				pr_pmcin;
+	pmcstat_interned_string		pr_pmcname;
+	int				pr_samples;
+	int				pr_dubious_frames;
+	struct pmcstat_pmcrecord	*pr_merge;
+};
+extern LIST_HEAD(pmcstat_pmcs, pmcstat_pmcrecord) pmcstat_pmcs; /* PMC list */
+
+struct pmc_plugins {
+	const char *pl_name;
+
+	/* configure */
+	int (*pl_configure)(char *opt);
+
+	/* init and shutdown */
+	int (*pl_init)(void);
+	void (*pl_shutdown)(FILE *mf);
+
+	/* sample processing */
+	void (*pl_process)(struct pmcstat_process *pp,
+	    struct pmcstat_pmcrecord *pmcr, uint32_t nsamples,
+	    uintfptr_t *cc, int usermode, uint32_t cpu);
+
+	/* image */
+	void (*pl_initimage)(struct pmcstat_image *pi);
+	void (*pl_shutdownimage)(struct pmcstat_image *pi);
+
+	/* pmc */
+	void (*pl_newpmc)(pmcstat_interned_string ps,
+		struct pmcstat_pmcrecord *pr);
+	
+	/* top display */
+	void (*pl_topdisplay)(void);
+
+	/* top keypress */
+	int (*pl_topkeypress)(int c, void *w);
+};
+
+/*
+ * Misc. statistics
+ */
+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_samples_unknown_function;/* #samples with unknown function at offset */
+	int ps_callchain_dubious_frames;/* #dubious frame pointers seen */
+};
+
+__BEGIN_DECLS
+int pmcstat_symbol_compare(const void *a, const void *b);
+struct pmcstat_symbol *pmcstat_symbol_search(struct pmcstat_image *image,
+    uintfptr_t addr);
+void pmcstat_image_add_symbols(struct pmcstat_image *image, Elf *e,
+    Elf_Scn *scn, GElf_Shdr *sh);
+
+const char *pmcstat_string_unintern(pmcstat_interned_string _is);
+pmcstat_interned_string pmcstat_string_intern(const char *_s);
+int pmcstat_string_compute_hash(const char *s);
+pmcstat_interned_string pmcstat_string_lookup(const char *_s);
+void pmcstat_image_get_elf_params(struct pmcstat_image *image, struct pmcstat_args *args);
+
+struct pmcstat_image *
+    pmcstat_image_from_path(pmcstat_interned_string internedpath,
+    int iskernelmodule, struct pmcstat_args *args,
+    struct pmc_plugins *plugins);
+int pmcstat_string_lookup_hash(pmcstat_interned_string _is);
+
+void pmcstat_process_elf_exec(struct pmcstat_process *_pp,
+    struct pmcstat_image *_image, uintfptr_t _entryaddr,
+    struct pmcstat_args *args, struct pmc_plugins *plugins,
+    struct pmcstat_stats *pmcstat_stats);
+
+void pmcstat_image_link(struct pmcstat_process *_pp,
+    struct pmcstat_image *_i, uintfptr_t _lpc);
+
+void pmcstat_process_aout_exec(struct pmcstat_process *_pp,
+    struct pmcstat_image *_image, uintfptr_t _entryaddr);
+void pmcstat_process_exec(struct pmcstat_process *_pp,
+    pmcstat_interned_string _path, uintfptr_t _entryaddr,
+    struct pmcstat_args *args, struct pmc_plugins *plugins,
+    struct pmcstat_stats *pmcstat_stats);
+void pmcstat_image_determine_type(struct pmcstat_image *_image, struct pmcstat_args *args);
+void pmcstat_image_get_aout_params(struct pmcstat_image *_image, struct pmcstat_args *args);
+struct pmcstat_pcmap *pmcstat_process_find_map(struct pmcstat_process *_p,
+    uintfptr_t _pc);
+void pmcstat_initialize_logging(struct pmcstat_process **pmcstat_kernproc,
+    struct pmcstat_args *args, struct pmc_plugins *plugins,
+    int *pmcstat_npmcs, int *pmcstat_mergepmc);
+void pmcstat_shutdown_logging(struct pmcstat_args *args,
+    struct pmc_plugins *plugins,
+    struct pmcstat_stats *pmcstat_stats);
+struct pmcstat_process *pmcstat_process_lookup(pid_t _pid, int _allocate);
+void pmcstat_clone_event_descriptor(struct pmcstat_ev *ev, const cpuset_t *cpumask, struct pmcstat_args *args);
+
+void pmcstat_create_process(int *pmcstat_sockpair, struct pmcstat_args *args, int pmcstat_kq);
+void pmcstat_start_process(int *pmcstat_sockpair);
+
+void pmcstat_attach_pmcs(struct pmcstat_args *args);
+struct pmcstat_symbol *pmcstat_symbol_search_by_name(struct pmcstat_process *pp, const char *pi_name, const char *name, uintptr_t *, uintptr_t *);
+
+void pmcstat_string_initialize(void);
+void pmcstat_string_shutdown(void);
+
+int pmcstat_analyze_log(struct pmcstat_args *args,
+    struct pmc_plugins *plugins,
+    struct pmcstat_stats *pmcstat_stats,
+    struct pmcstat_process *pmcstat_kernproc,
+    int pmcstat_mergepmc,
+    int *pmcstat_npmcs,
+    int *ps_samples_period);
+
+int pmcstat_open_log(const char *_p, int _mode);
+int pmcstat_close_log(struct pmcstat_args *args);
+__END_DECLS
+
+#endif /* !_LIBPMCSTAT_H_ */

Added: head/lib/libpmcstat/libpmcstat_event.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libpmcstat/libpmcstat_event.c	Tue Oct 24 16:28:00 2017	(r324959)
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2003-2008 Joseph Koshy
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/cpuset.h>
+#include <sys/pmc.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "libpmcstat.h"
+
+void
+pmcstat_clone_event_descriptor(struct pmcstat_ev *ev, const cpuset_t *cpumask,
+    struct pmcstat_args *args)
+{
+	int cpu;
+	struct pmcstat_ev *ev_clone;
+
+	for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
+		if (!CPU_ISSET(cpu, cpumask))
+			continue;
+
+		if ((ev_clone = malloc(sizeof(*ev_clone))) == NULL)
+			errx(EX_SOFTWARE, "ERROR: Out of memory");
+		(void) memset(ev_clone, 0, sizeof(*ev_clone));
+
+		ev_clone->ev_count = ev->ev_count;
+		ev_clone->ev_cpu   = cpu;
+		ev_clone->ev_cumulative = ev->ev_cumulative;
+		ev_clone->ev_flags = ev->ev_flags;
+		ev_clone->ev_mode  = ev->ev_mode;
+		ev_clone->ev_name  = strdup(ev->ev_name);
+		if (ev_clone->ev_name == NULL)
+			errx(EX_SOFTWARE, "ERROR: Out of memory");
+		ev_clone->ev_pmcid = ev->ev_pmcid;
+		ev_clone->ev_saved = ev->ev_saved;
+		ev_clone->ev_spec  = strdup(ev->ev_spec);
+		if (ev_clone->ev_spec == NULL)
+			errx(EX_SOFTWARE, "ERROR: Out of memory");
+
+		STAILQ_INSERT_TAIL(&args->pa_events, ev_clone, ev_next);
+	}
+}

Added: head/lib/libpmcstat/libpmcstat_image.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libpmcstat/libpmcstat_image.c	Tue Oct 24 16:28:00 2017	(r324959)
@@ -0,0 +1,517 @@
+/*-
+ * Copyright (c) 2003-2008 Joseph Koshy
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/cpuset.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/pmc.h>
+#include <sys/imgact_aout.h>
+#include <sys/imgact_elf.h>
+
+#include <netinet/in.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <pmc.h>
+#include <pmclog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "libpmcstat.h"
+
+#define	min(A,B)		((A) < (B) ? (A) : (B))
+#define	max(A,B)		((A) > (B) ? (A) : (B))
+
+/*
+ * Add the list of symbols in the given section to the list associated
+ * with the object.
+ */
+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 = reallocarray(image->pi_symbols,
+	    image->pi_symcount + nfuncsyms, sizeof(*symptr));
+	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 (sym.st_shndx == STN_UNDEF)
+			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;
+#ifdef __arm__
+		/* Remove spurious ARM function name. */
+		if (fnname[0] == '$' &&
+		    (fnname[1] == 'a' || fnname[1] == 't' ||
+		    fnname[1] == 'd') &&
+		    fnname[2] == '\0')
+			continue;
+#endif
+
+		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;
+	if (image->pi_symcount == 0)
+		return;
+
+	assert(newsyms <= nfuncsyms);
+
+	/*
+	 * Return space to the system if there were duplicates.
+	 */
+	if (newsyms < nfuncsyms)
+		image->pi_symbols = reallocarray(image->pi_symbols,
+		    image->pi_symcount, sizeof(*symptr));
+
+	/*
+	 * 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;
+}
+
+/*
+ * Record the fact that PC values from 'start' to 'end' come from
+ * image 'image'.
+ */
+
+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.
+	 */
+
+	offset = start - image->pi_vaddr;
+	pcmnew->ppm_lowpc  = image->pi_start + offset;
+	pcmnew->ppm_highpc = image->pi_end + offset;
+	pcmnew->ppm_image  = image;
+
+	assert(pcmnew->ppm_lowpc < pcmnew->ppm_highpc);
+
+	/* Overlapped mmap()'s are assumed to never occur. */
+	TAILQ_FOREACH(pcm, &pp->pp_map, ppm_next)
+	    if (pcm->ppm_lowpc >= pcmnew->ppm_highpc)
+		    break;
+
+	if (pcm == NULL)
+		TAILQ_INSERT_TAIL(&pp->pp_map, pcmnew, ppm_next);
+	else
+		TAILQ_INSERT_BEFORE(pcm, pcmnew, ppm_next);
+}
+
+/*
+ * 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.
+ */
+
+void
+pmcstat_image_get_aout_params(struct pmcstat_image *image,
+    struct pmcstat_args *args)
+{
+	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",
+	    args->pa_fsroot, path);
+
+	if ((fd = open(buffer, O_RDONLY, 0)) < 0 ||
+	    (nbytes = read(fd, &ex, sizeof(ex))) < 0) {
+		if (args->pa_verbosity >= 2)
+			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.
+ */
+
+void
+pmcstat_image_get_elf_params(struct pmcstat_image *image,
+    struct pmcstat_args *args)
+{
+	int fd;
+	size_t i, nph, nsh;
+	const char *path, *elfbase;
+	char *p, *endp;
+	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];
+
+	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",
+		    args->pa_fsroot, args->pa_kernel, path);
+	else
+		(void) snprintf(buffer, sizeof(buffer), "%s%s",
+		    args->pa_fsroot, path);
+
+	e = NULL;
+	if ((fd = open(buffer, O_RDONLY, 0)) < 0) {
+		warnx("WARNING: Cannot open \"%s\".",
+		    buffer);
+		goto done;
+	}
+
+	if (elf_version(EV_CURRENT) == EV_NONE) {
+		warnx("WARNING: failed to init elf\n");
+		goto done;
+	}
+
+	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
+		warnx("WARNING: Cannot read \"%s\".",
+		    buffer);
+		goto done;
+	}
+
+	if (elf_kind(e) != ELF_K_ELF) {
+		if (args->pa_verbosity >= 2)
+			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_flags & PF_X) != 0 &&
+				    (ph.p_offset & (-ph.p_align)) == 0)
+					image->pi_vaddr = ph.p_vaddr & (-ph.p_align);
+				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);
+
+	/* Build display name
+	 */
+	endp = buffer;
+	for (p = buffer; *p; p++)
+		if (*p == '/')
+			endp = p+1;
+	image->pi_name = pmcstat_string_intern(endp);
+
+ 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'.
+ */
+
+void
+pmcstat_image_determine_type(struct pmcstat_image *image,
+    struct pmcstat_args *args)
+{
+	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, args);
+	if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
+		pmcstat_image_get_aout_params(image, args);
+
+	/*
+	 * 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.
+ */
+
+struct pmcstat_image *
+pmcstat_image_from_path(pmcstat_interned_string internedpath,
+    int iskernelmodule, struct pmcstat_args *args,
+    struct pmc_plugins *plugins)
+{
+	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;
+	pi->pi_addr2line = NULL;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list