summaryrefslogtreecommitdiffstats
path: root/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c
diff options
context:
space:
mode:
authorrpaulo <rpaulo@FreeBSD.org>2010-08-21 11:50:53 +0000
committerrpaulo <rpaulo@FreeBSD.org>2010-08-21 11:50:53 +0000
commitb325e69c9282072ea3b203319017abc400db4771 (patch)
treeae5b6066f0143ecb034e8de394e160841cd4f84f /cddl/contrib/opensolaris/lib/libdtrace/common/drti.c
parent232561a60dcdeeffd316d29086bb31f2e12d7591 (diff)
downloadFreeBSD-src-b325e69c9282072ea3b203319017abc400db4771.zip
FreeBSD-src-b325e69c9282072ea3b203319017abc400db4771.tar.gz
Add libdtrace support for tracing userland programs.
Summary of changes: * Implement a compatibility shim between Solaris libproc and our libproc and remove several ifdefs because of this. * Port the drti to FreeBSD. * Implement the missing DOODAD sections * Link with libproc and librtld_db * Support for ustack, jstack and uregs (by sson@) * Misc bugfixing When writing the SUWN_dof section, we had to resort to building the ELF file layout by "hand". This is the job of libelf, but our libelf doesn't support this yet. When libelf is fixed, we can remove the code under #ifdef BROKEN_LIBELF. Sponsored by: The FreeBSD Foundation
Diffstat (limited to 'cddl/contrib/opensolaris/lib/libdtrace/common/drti.c')
-rw-r--r--cddl/contrib/opensolaris/lib/libdtrace/common/drti.c168
1 files changed, 161 insertions, 7 deletions
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c b/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c
index 1ae8283..bce8038 100644
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c
@@ -34,6 +34,8 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <libelf.h>
+#include <gelf.h>
/*
* In Solaris 10 GA, the only mechanism for communicating helper information
@@ -53,12 +55,16 @@
*/
static const char *devnamep = "/dev/dtrace/helper";
+#if defined(sun)
static const char *olddevname = "/devices/pseudo/dtrace@0:helper";
+#endif
static const char *modname; /* Name of this load object */
static int gen; /* DOF helper generation */
+#if defined(sun)
extern dof_hdr_t __SUNW_dof; /* DOF defined in the .SUNW_dof section */
-static boolean_t dof_init_debug = B_FALSE; /* From DTRACE_DOF_INIT_DEBUG */
+#endif
+static boolean_t dof_init_debug = B_TRUE; /* From DTRACE_DOF_INIT_DEBUG */
static void
dprintf(int debug, const char *fmt, ...)
@@ -83,6 +89,36 @@ dprintf(int debug, const char *fmt, ...)
va_end(ap);
}
+#if !defined(sun)
+static void
+fixsymbol(Elf *e, Elf_Data *data, size_t idx, int nprobes, char *buf,
+ dof_sec_t *sec, int *fixedprobes, char *dofstrtab)
+{
+ GElf_Sym sym;
+ char *s;
+ unsigned char *funcname;
+ dof_probe_t *prb;
+ int j = 0;
+ int ndx;
+
+ while (gelf_getsym(data, j++, &sym) != NULL) {
+ prb = (dof_probe_t *)(buf + sec->dofs_offset);
+
+ for (ndx = nprobes; ndx; ndx--, prb += 1) {
+ funcname = dofstrtab + prb->dofpr_func;
+ s = elf_strptr(e, idx, sym.st_name);
+ if (strcmp(s, funcname) == 0) {
+ dprintf(1, "fixing %s() symbol\n", s);
+ prb->dofpr_addr = sym.st_value;
+ (*fixedprobes)++;
+ }
+ }
+ if (*fixedprobes == nprobes)
+ break;
+ }
+}
+#endif
+
#if defined(sun)
#pragma init(dtrace_dof_init)
#else
@@ -92,22 +128,39 @@ static void dtrace_dof_init(void) __attribute__ ((constructor));
static void
dtrace_dof_init(void)
{
+#if defined(sun)
dof_hdr_t *dof = &__SUNW_dof;
+#else
+ dof_hdr_t *dof = NULL;
+#endif
#ifdef _LP64
Elf64_Ehdr *elf;
#else
Elf32_Ehdr *elf;
#endif
dof_helper_t dh;
-#if defined(sun)
Link_map *lmp;
+#if defined(sun)
Lmid_t lmid;
#else
- struct link_map *lmp;
u_long lmid = 0;
+ dof_sec_t *sec;
+ size_t i;
#endif
int fd;
const char *p;
+#if !defined(sun)
+ Elf *e;
+ Elf_Scn *scn = NULL;
+ Elf_Data *symtabdata = NULL, *dynsymdata = NULL;
+ GElf_Shdr shdr;
+ int efd, nprobes;
+ char *s;
+ size_t shstridx, symtabidx = 0, dynsymidx = 0;
+ unsigned char *dofstrtab = NULL;
+ unsigned char *buf;
+ int fixedprobes = 0;
+#endif
if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL)
return;
@@ -127,10 +180,46 @@ dtrace_dof_init(void)
}
#endif
+
if ((modname = strrchr(lmp->l_name, '/')) == NULL)
modname = lmp->l_name;
else
modname++;
+#if !defined(sun)
+ elf_version(EV_CURRENT);
+ if ((efd = open(lmp->l_name, O_RDONLY, 0)) < 0) {
+ dprintf(1, "couldn't open file for reading\n");
+ return;
+ }
+ if ((e = elf_begin(efd, ELF_C_READ, NULL)) == NULL) {
+ dprintf(1, "elf_begin failed\n");
+ close(efd);
+ return;
+ }
+ elf_getshdrstrndx(e, &shstridx);
+ dof = NULL;
+ while ((scn = elf_nextscn(e, scn)) != NULL) {
+ gelf_getshdr(scn, &shdr);
+ if (shdr.sh_type == SHT_SYMTAB) {
+ symtabidx = shdr.sh_link;
+ symtabdata = elf_getdata(scn, NULL);
+ } else if (shdr.sh_type == SHT_DYNSYM) {
+ dynsymidx = shdr.sh_link;
+ dynsymdata = elf_getdata(scn, NULL);
+ } else if (shdr.sh_type == SHT_PROGBITS) {
+ s = elf_strptr(e, shstridx, shdr.sh_name);
+ if (s && strcmp(s, ".SUNW_dof") == 0) {
+ dof = elf_getdata(scn, NULL)->d_buf;
+ }
+ }
+ }
+ if (dof == NULL) {
+ dprintf(1, "SUNW_dof section not found\n");
+ elf_end(e);
+ close(efd);
+ return;
+ }
+#endif
if (dof->dofh_ident[DOF_ID_MAG0] != DOF_MAG_MAG0 ||
dof->dofh_ident[DOF_ID_MAG1] != DOF_MAG_MAG1 ||
@@ -158,7 +247,7 @@ dtrace_dof_init(void)
if ((fd = open64(devnamep, O_RDWR)) < 0) {
dprintf(1, "failed to open helper device %s", devnamep);
-
+#if defined(sun)
/*
* If the device path wasn't explicitly set, try again with
* the old device path.
@@ -172,14 +261,79 @@ dtrace_dof_init(void)
dprintf(1, "failed to open helper device %s", devnamep);
return;
}
+#else
+ return;
+#endif
}
-
+#if !defined(sun)
+ /*
+ * We need to fix the base address of each probe since this wasn't
+ * done by ld(1). (ld(1) needs to grow support for parsing the
+ * SUNW_dof section).
+ *
+ * The complexity of this is not that great. The first for loop
+ * iterates over the sections inside the DOF file. There are usually
+ * 10 sections here. We asume the STRTAB section comes first and the
+ * PROBES section comes after. Since we are only interested in fixing
+ * data inside the PROBES section we quit the for loop after processing
+ * the PROBES section. It's usually the case that the first section
+ * is the STRTAB section and the second section is the PROBES section,
+ * so this for loop is not meaningful when doing complexity analysis.
+ *
+ * After finding the probes section, we iterate over the symbols
+ * in the symtab section. When we find a symbol name that matches
+ * the probe function name, we fix it. If we have fixed all the
+ * probes, we exit all the loops and we are done.
+ * The number of probes is given by the variable 'nprobes' and this
+ * depends entirely on the user, but some optimizations were done.
+ *
+ * We are assuming the number of probes is less than the number of
+ * symbols (libc can have 4k symbols, for example).
+ */
+ sec = (dof_sec_t *)(dof + 1);
+ buf = (char *)dof;
+ for (i = 0; i < dof->dofh_secnum; i++, sec++) {
+ if (sec->dofs_type == DOF_SECT_STRTAB)
+ dofstrtab = (unsigned char *)(buf + sec->dofs_offset);
+ else if (sec->dofs_type == DOF_SECT_PROBES && dofstrtab)
+ break;
+
+ }
+ nprobes = sec->dofs_size / sec->dofs_entsize;
+ fixsymbol(e, symtabdata, symtabidx, nprobes, buf, sec, &fixedprobes,
+ dofstrtab);
+ if (fixedprobes != nprobes) {
+ /*
+ * If we haven't fixed all the probes using the
+ * symtab section, look inside the dynsym
+ * section.
+ */
+ fixsymbol(e, dynsymdata, dynsymidx, nprobes, buf, sec,
+ &fixedprobes, dofstrtab);
+ }
+ if (fixedprobes != nprobes) {
+ fprintf(stderr, "WARNING: number of probes "
+ "fixed does not match the number of "
+ "defined probes (%d != %d, "
+ "respectively)\n", fixedprobes, nprobes);
+ fprintf(stderr, "WARNING: some probes might "
+ "not fire or your program might crash\n");
+ }
+#endif
if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1)
dprintf(1, "DTrace ioctl failed for DOF at %p", dof);
- else
+ else {
dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof);
+#if !defined(sun)
+ gen = dh.gen;
+#endif
+ }
(void) close(fd);
+#if !defined(sun)
+ elf_end(e);
+ (void) close(efd);
+#endif
}
#if defined(sun)
@@ -198,7 +352,7 @@ dtrace_dof_fini(void)
return;
}
- if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, gen)) == -1)
+ if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, &gen)) == -1)
dprintf(1, "DTrace ioctl failed to remove DOF (%d)\n", gen);
else
dprintf(1, "DTrace ioctl removed DOF (%d)\n", gen);
OpenPOWER on IntegriCloud