summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog1
-rw-r--r--main.cxx17
-rw-r--r--runtime/probes.c152
-rw-r--r--runtime/staprun/common.c30
-rw-r--r--runtime/staprun/mainloop.c25
-rw-r--r--runtime/staprun/staprun.c184
-rw-r--r--runtime/staprun/staprun.h1
-rw-r--r--runtime/sym.c141
-rw-r--r--runtime/sym.h55
-rw-r--r--runtime/transport/ChangeLog3
-rw-r--r--runtime/transport/control.c10
-rw-r--r--runtime/transport/procfs.c2
-rw-r--r--runtime/transport/symbols.c566
-rw-r--r--runtime/transport/transport.c17
-rw-r--r--runtime/transport/transport_msgs.h17
-rw-r--r--runtime/unwind.c17
-rw-r--r--session.h3
-rw-r--r--tapsets.cxx8
-rw-r--r--translate.cxx301
19 files changed, 574 insertions, 976 deletions
diff --git a/ChangeLog b/ChangeLog
index 14c1ef14..960f8232 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -386,6 +386,7 @@
PR 6429.
* Makefile.am: Don't link stapio with -ldw.
+ * Makefile.in: Regenerated.
2008-05-29 Mark Wielaard <mwielaard@redhat.com>
diff --git a/main.cxx b/main.cxx
index eea2c7e7..edb72768 100644
--- a/main.cxx
+++ b/main.cxx
@@ -106,6 +106,17 @@ usage (systemtap_session& s, int exitcode)
<< " -c CMD start the probes, run CMD, and exit when it finishes"
<< endl
<< " -x PID sets target() to PID" << endl
+ << " -d OBJECT add unwind/symbol data for OBJECT file";
+ if (s.unwindsym_modules.size() == 0)
+ clog << endl;
+ else
+ clog << ", in addition to" << endl;
+ {
+ vector<string> syms (s.unwindsym_modules.begin(), s.unwindsym_modules.end());
+ for (unsigned i=0; i<syms.size(); i++)
+ clog << " " << syms[i] << endl;
+ }
+ clog
<< " -t collect probe timing information" << endl
#ifdef HAVE_LIBSQLITE3
<< " -q generate information on tapset coverage" << endl
@@ -393,7 +404,7 @@ main (int argc, char * const argv [])
{ "ignore-dwarf", 0, &long_opt, LONG_OPT_IGNORE_DWARF },
{ NULL, 0, NULL, 0 }
};
- int grc = getopt_long (argc, argv, "hVMvtp:I:e:o:R:r:m:kgPc:x:D:bs:uqwl:",
+ int grc = getopt_long (argc, argv, "hVMvtp:I:e:o:R:r:m:kgPc:x:D:bs:uqwl:d:",
long_options, NULL);
if (grc < 0)
break;
@@ -437,6 +448,10 @@ main (int argc, char * const argv [])
s.include_path.push_back (string (optarg));
break;
+ case 'd':
+ s.unwindsym_modules.insert (string (optarg));
+ break;
+
case 'e':
if (have_script)
{
diff --git a/runtime/probes.c b/runtime/probes.c
deleted file mode 100644
index 6fe844fb..00000000
--- a/runtime/probes.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/* -*- linux-c -*-
- * Functions for Registering and Unregistering Probes
- * Copyright (C) 2005 Red Hat Inc.
- *
- * This file is part of systemtap, and is free software. You can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License (GPL); either version 2, or (at your option) any
- * later version.
- */
-
-#ifndef _PROBES_C_
-#define _PROBES_C
-
-/** @file probes.c
- * @brief Functions to assist loading and unloading groups of probes.
- */
-
-/** Unregister a group of jprobes.
- * @param probes Pointer to an array of struct jprobe.
- * @param num_probes Number of probes in the array.
- */
-
-void _stp_unregister_jprobes (struct jprobe *probes, int num_probes)
-{
- int i;
- for (i = 0; i < num_probes; i++)
- unregister_jprobe(&probes[i]);
- // dbug("All jprobes removed\n");
-}
-
-/** Register a group of jprobes.
- * @param probes Pointer to an array of struct jprobe.
- * @param num_probes Number of probes in the array.
- * @return 0 on success.
- */
-
-int _stp_register_jprobes (struct jprobe *probes, int num_probes)
-{
- int i, ret ;
- unsigned long addr;
-
- for (i = 0; i < num_probes; i++) {
- addr =kallsyms_lookup_name((char *)probes[i].kp.addr);
- if (addr == 0) {
- _stp_warn("function %s not found!\n", (char *)probes[i].kp.addr);
- ret = -1; /* FIXME */
- goto out;
- }
- // dbug("inserting jprobe at %s (%p)\n", probes[i].kp.addr, addr);
- probes[i].kp.addr = (kprobe_opcode_t *)addr;
- ret = register_jprobe(&probes[i]);
- if (ret)
- goto out;
- }
- return 0;
-out:
- _stp_warn("probe module initialization failed. Exiting...\n");
- _stp_unregister_jprobes(probes, i);
- return ret;
-}
-
-/** Unregister a group of kprobes.
- * @param probes Pointer to an array of struct kprobe.
- * @param num_probes Number of probes in the array.
- */
-
-void _stp_unregister_kprobes (struct kprobe *probes, int num_probes)
-{
- int i;
- for (i = 0; i < num_probes; i++)
- unregister_kprobe(&probes[i]);
- // dbug("All kprobes removed\n");
-}
-
-
-#ifdef USE_RET_PROBES
-/** Unregister a group of return probes.
- * @param probes Pointer to an array of struct kretprobe.
- * @param num_probes Number of probes in the array.
- */
-void _stp_unregister_kretprobes (struct kretprobe *probes, int num_probes)
-{
- int i;
- for (i = 0; i < num_probes; i++)
- unregister_kretprobe(&probes[i]);
- // dbug("All return probes removed\n");
-}
-#endif
-
-/** Register a group of kprobes.
- * @param probes Pointer to an array of struct kprobe.
- * @param num_probes Number of probes in the array.
- * @return 0 on success.
- */
-int _stp_register_kprobes (struct kprobe *probes, int num_probes)
-{
- int i, ret ;
- unsigned long addr;
-
- for (i = 0; i < num_probes; i++) {
- addr = kallsyms_lookup_name((char *)probes[i].addr);
- if (addr == 0) {
- _stp_warn("function %s not found!\n", (char *)probes[i].addr);
- ret = -1;
- goto out;
- }
- // dbug("inserting kprobe at %s (%p)\n", probes[i].addr, addr);
- probes[i].addr = (kprobe_opcode_t *)addr;
- ret = register_kprobe(&probes[i]);
- if (ret)
- goto out;
- }
- return 0;
-out:
- _stp_warn("probe module initialization failed. Exiting...\n");
- _stp_unregister_kprobes(probes, i);
- return ret;
-}
-
-#ifdef USE_RET_PROBES
-/** Register a group of return probes.
- * @param probes Pointer to an array of struct kretprobe.
- * @param num_probes Number of probes in the array.
- * @return 0 on success.
- */
-int _stp_register_kretprobes (struct kretprobe *probes, int num_probes)
-{
- int i, ret ;
- unsigned long addr;
-
- for (i = 0; i < num_probes; i++) {
- addr = kallsyms_lookup_name((char *)probes[i].kp.addr);
- if (addr == 0) {
- _stp_warn("function %s not found!\n",
- (char *)probes[i].kp.addr);
- ret = -1; /* FIXME */
- goto out;
- }
- // dbug("inserting kretprobe at %s (%p)\n", probes[i].kp.addr, addr);
- probes[i].kp.addr = (kprobe_opcode_t *)addr;
- ret = register_kretprobe(&probes[i]);
- if (ret)
- goto out;
- }
- return 0;
-out:
- _stp_warn("probe module initialization failed. Exiting...\n");
- _stp_unregister_kretprobes(probes, i);
- return ret;
-}
-#endif
-#endif /* _PROBES_C */
diff --git a/runtime/staprun/common.c b/runtime/staprun/common.c
index 93da51d8..fd16b4b8 100644
--- a/runtime/staprun/common.c
+++ b/runtime/staprun/common.c
@@ -14,6 +14,8 @@
#include <sys/types.h>
#include <unistd.h>
#include <sys/utsname.h>
+#include <assert.h>
+
/* variables needed by parse_args() */
int verbose;
@@ -314,3 +316,31 @@ err:
close(fd);
return -1;
}
+
+
+/**
+ * send_request - send request to kernel over control channel
+ * @type: the relay-app command id
+ * @data: pointer to the data to be sent
+ * @len: length of the data to be sent
+ *
+ * Returns 0 on success, negative otherwise.
+ */
+int send_request(int type, void *data, int len)
+{
+ char buf[1024];
+ int rc = 0;
+
+ /* Before doing memcpy, make sure 'buf' is big enough. */
+ if ((len + 4) > (int)sizeof(buf)) {
+ _err("exceeded maximum send_request size.\n");
+ return -1;
+ }
+ memcpy(buf, &type, 4);
+ memcpy(&buf[4], data, len);
+
+ assert (control_channel >= 0);
+ rc = write (control_channel, buf, len + 4);
+ if (rc < 0) return rc;
+ return (rc != len+4);
+}
diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c
index 2bbadbc9..a7b919cb 100644
--- a/runtime/staprun/mainloop.c
+++ b/runtime/staprun/mainloop.c
@@ -1,6 +1,6 @@
/* -*- linux-c -*-
*
- * mainloop - staprun main loop
+ * mainloop - stapio main loop
*
* This file is part of systemtap, and is free software. You can
* redistribute it and/or modify it under the terms of the GNU General
@@ -19,27 +19,6 @@ static int use_old_transport = 0;
//enum _stp_sig_type { sig_none, sig_done, sig_detach };
//static enum _stp_sig_type got_signal = sig_none;
-/**
- * send_request - send request to kernel over control channel
- * @type: the relay-app command id
- * @data: pointer to the data to be sent
- * @len: length of the data to be sent
- *
- * Returns 0 on success, negative otherwise.
- */
-int send_request(int type, void *data, int len)
-{
- char buf[1024];
-
- /* Before doing memcpy, make sure 'buf' is big enough. */
- if ((len + 4) > (int)sizeof(buf)) {
- _err("exceeded maximum send_request size.\n");
- return -1;
- }
- memcpy(buf, &type, 4);
- memcpy(&buf[4], data, len);
- return write(control_channel, buf, len + 4);
-}
static void *signal_thread(void *arg)
{
@@ -308,8 +287,8 @@ int stp_main_loop(void)
setvbuf(ofp, (char *)NULL, _IOLBF, 0);
setup_main_signals();
-
dbug(2, "in main loop\n");
+
send_request(STP_READY, NULL, 0);
/* handle messages from control channel */
diff --git a/runtime/staprun/staprun.c b/runtime/staprun/staprun.c
index 0291d01f..664b75ee 100644
--- a/runtime/staprun/staprun.c
+++ b/runtime/staprun/staprun.c
@@ -21,12 +21,19 @@
*/
#include "staprun.h"
+#include <sys/uio.h>
+#include <glob.h>
+
+
/* used in dbug, _err and _perr */
char *__name__ = "staprun";
extern long delete_module(const char *, unsigned int);
+int send_relocations ();
+
+
static int run_as(uid_t uid, gid_t gid, const char *path, char *const argv[])
{
pid_t pid;
@@ -206,6 +213,8 @@ int init_staprun(void)
return -1;
if (insert_stap_module() < 0)
return -1;
+ if (send_relocations() < 0)
+ return -1;
}
return 0;
}
@@ -288,3 +297,178 @@ err:
remove_module(modname, 1);
return 1;
}
+
+
+
+/* Send a variety of relocation-related data to the kernel: for the
+ kernel proper, just the "_stext" symbol address; for all loaded
+ modules, a variety of symbol base addresses.
+
+ We do this under protest. The kernel ought expose this data to
+ modules such as ourselves, but instead the upstream community
+ continually shrinks its module-facing interfaces, including this
+ stuff, even when users exist.
+*/
+
+
+void send_a_relocation (const char* module, const char* reloc, unsigned long long address)
+{
+ struct _stp_msg_relocation msg;
+
+ if (strlen(module) >= STP_MODULE_NAME_LEN)
+ { _perr ("module name too long: %s", module); return; }
+ strcpy (msg.module, module);
+
+ if (strlen(reloc) >= STP_SYMBOL_NAME_LEN)
+ { _perr ("reloc name too long: %s", reloc); return; }
+ strcpy (msg.reloc, reloc);
+
+ msg.address = address;
+
+ send_request (STP_RELOCATION, & msg, sizeof (msg));
+ /* XXX: verify send_request RC */
+}
+
+
+int send_relocation_kernel ()
+{
+ int srkrc = 0;
+
+ FILE* kallsyms = fopen ("/proc/kallsyms", "r");
+ if (kallsyms == NULL)
+ {
+ perror("cannot open /proc/kallsyms");
+ // ... and the kernel module will almost certainly fail to initialize.
+ }
+ else
+ {
+ int done_with_kallsyms = 0;
+ while (! feof(kallsyms) && !done_with_kallsyms)
+ {
+ char *line = NULL;
+ size_t linesz = 0;
+ ssize_t linesize = getline (& line, & linesz, kallsyms);
+ if (linesize < 0)
+ break;
+ else
+ {
+ unsigned long long address;
+ char type;
+ char* symbol = NULL;
+ int rc = sscanf (line, "%llx %c %as", &address, &type, &symbol);
+ free (line); line=NULL;
+ if (symbol == NULL) continue; /* OOM? */
+
+#ifdef __powerpc__
+#define KERNEL_RELOC_SYMBOL ".__start"
+#else
+#define KERNEL_RELOC_SYMBOL "_stext"
+#endif
+ if ((rc == 3) && (0 == strcmp(symbol,KERNEL_RELOC_SYMBOL)))
+ {
+ /* NB: even on ppc, we use the _stext relocation name. */
+ send_a_relocation ("kernel", "_stext", address);
+
+ /* We need nothing more from the kernel. */
+ done_with_kallsyms=1;
+ }
+
+ free (symbol);
+ }
+ }
+ fclose (kallsyms);
+ if (!done_with_kallsyms) srkrc = -1;
+ }
+
+ return srkrc;
+}
+
+
+void send_relocation_modules ()
+{
+ unsigned i;
+ glob_t globbuf;
+ int r = glob("/sys/module/*/sections/*", GLOB_PERIOD, NULL, &globbuf);
+
+ if (r == GLOB_NOSPACE || r == GLOB_ABORTED)
+ return;
+
+ for (i=0; i<globbuf.gl_pathc; i++)
+ {
+ char *module_section_file;
+ char *section_name;
+ char *module_name;
+ char *module_name_end;
+ FILE* secfile;
+ unsigned long long section_address;
+
+ module_section_file = globbuf.gl_pathv[i];
+
+ /* Tokenize the file name.
+ Sample gl_pathv[]: /sys/modules/zlib_deflate/sections/.text
+ Pieces: ^^^^^^^^^^^^ ^^^^^
+ */
+ section_name = rindex (module_section_file, '/');
+ if (! section_name) continue;
+ section_name ++;
+
+ if (!strcmp (section_name, ".")) continue;
+ if (!strcmp (section_name, "..")) continue;
+
+ module_name = index (module_section_file, '/');
+ if (! module_name) continue;
+ module_name ++;
+ module_name = index (module_name, '/');
+ if (! module_name) continue;
+ module_name ++;
+ module_name = index (module_name, '/');
+ if (! module_name) continue;
+ module_name ++;
+
+ module_name_end = index (module_name, '/');
+ if (! module_name_end) continue;
+
+ secfile = fopen (module_section_file, "r");
+ if (! secfile) continue;
+
+ if (1 == fscanf (secfile, "0x%llx", &section_address))
+ {
+ /* Now we destructively modify the string, but by now the file
+ is open so we won't need the full name again. */
+ *module_name_end = '\0';
+
+ send_a_relocation (module_name, section_name, section_address);
+ }
+
+ if (strcmp (section_name, ".gnu.linkonce.this_module"))
+ fclose (secfile);
+ else
+ {
+ set_clexec (fileno (secfile));
+ /* NB: don't fclose this arbitrarily-chosen section file.
+ This forces the kernel to keep a nonzero reference count
+ on the subject module, until staprun exits, by which time
+ the kernel module will have inserted its separate claws
+ into the probeworthy modules. This prevents a race
+ condition where a probe may be just starting up at the
+ same time that a probeworthy module is being unloaded. */
+ }
+ }
+
+ globfree (& globbuf);
+}
+
+
+
+int send_relocations ()
+{
+ int rc;
+ rc = init_ctl_channel (modname, 0);
+ if (rc < 0) goto out;
+ rc = send_relocation_kernel ();
+ send_relocation_modules ();
+ close_ctl_channel ();
+ out:
+ return rc;
+}
+
diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h
index 60bab391..0a35fee6 100644
--- a/runtime/staprun/staprun.h
+++ b/runtime/staprun/staprun.h
@@ -116,7 +116,6 @@ int init_stapio(void);
int stp_main_loop(void);
int send_request(int type, void *data, int len);
void cleanup_and_exit (int);
-void send_unwind_data(const char *name);
int init_ctl_channel(const char *name, int verb);
void close_ctl_channel(void);
int init_relayfs(void);
diff --git a/runtime/sym.c b/runtime/sym.c
index 7163bf92..178c6219 100644
--- a/runtime/sym.c
+++ b/runtime/sym.c
@@ -25,7 +25,7 @@ unsigned long _stp_module_relocate(const char *module, const char *section, unsi
static struct _stp_module *last = NULL;
static struct _stp_symbol *last_sec;
unsigned long flags;
- int i, j;
+ unsigned i, j;
/* if module is -1, we invalidate last. _stp_del_module calls this when modules are deleted. */
if ((long)module == -1) {
@@ -35,10 +35,8 @@ unsigned long _stp_module_relocate(const char *module, const char *section, unsi
dbug_sym(1, "%s, %s, %lx\n", module, section, offset);
- STP_RLOCK_MODULES;
if (!module || !strcmp(section, "") /* absolute, unrelocated address */
||_stp_num_modules == 0) {
- STP_RUNLOCK_MODULES;
return offset;
}
@@ -46,119 +44,80 @@ unsigned long _stp_module_relocate(const char *module, const char *section, unsi
if (last) {
if (!strcmp(module, last->name) && !strcmp(section, last_sec->symbol)) {
offset += last_sec->addr;
- STP_RUNLOCK_MODULES;
- dbug_sym(1, "offset = %lx\n", offset);
+ dbug_sym(1, "cached address=%lx\n", offset);
return offset;
}
}
- /* not cached. need to scan all modules */
- if (!strcmp(module, "kernel")) {
- STP_RUNLOCK_MODULES;
-
- /* See also transport/symbols.c (_stp_do_symbols). */
- if (strcmp(section, "_stext"))
- return 0;
- else
- return offset + _stp_modules[0]->text;
- } else {
- /* relocatable module */
- for (i = 1; i < _stp_num_modules; i++) { /* skip over [0]=kernel */
- last = _stp_modules[i];
- if (strcmp(module, last->name))
- continue;
- for (j = 0; j < (int)last->num_sections; j++) {
- last_sec = &last->sections[j];
- if (!strcmp(section, last_sec->symbol)) {
- offset += last_sec->addr;
- STP_RUNLOCK_MODULES;
- dbug_sym(1, "offset = %lx\n", offset);
- return offset;
- }
- }
- }
+ for (i = 0; i < _stp_num_modules; i++) {
+ last = _stp_modules[i];
+ if (strcmp(module, last->name))
+ continue;
+ for (j = 0; j < last->num_sections; j++) {
+ last_sec = &last->sections[j];
+ if (!strcmp(section, last_sec->symbol)) {
+
+ if (last_sec->addr == 0) /* module/section not in memory */
+ continue;
+
+ offset += last_sec->addr;
+ dbug_sym(1, "address=%lx\n", offset);
+ return offset;
+ }
+ }
}
- STP_RUNLOCK_MODULES;
+
last = NULL;
return 0;
}
-/* Lookup the kernel address for this symbol. Returns 0 if not found. */
-static unsigned long _stp_kallsyms_lookup_name(const char *name)
-{
- struct _stp_symbol *s = _stp_modules[0]->symbols;
- unsigned num = _stp_modules[0]->num_symbols;
-
- while (num--) {
- if (strcmp(name, s->symbol) == 0)
- return s->addr;
- s++;
- }
- return 0;
-}
+/* Return the module that likely contains the given address. */
+/* XXX: This query only makes sense with respect to a particular
+ address space. A more general interface would have to identify
+ the address space, and also pass back the section. */
static struct _stp_module *_stp_find_module_by_addr(unsigned long addr)
{
- unsigned begin = 0;
- unsigned end = _stp_num_modules;
-
- if (unlikely(addr < _stp_modules_by_addr[0]->text))
- return NULL;
-
- if (_stp_num_modules > 1 && addr > _stp_modules_by_addr[0]->data) {
- /* binary search on index [begin,end) */
- do {
- unsigned mid = (begin + end) / 2;
- if (addr < _stp_modules_by_addr[mid]->text)
- end = mid;
- else
- begin = mid;
- } while (begin + 1 < end);
- /* result index in $begin, guaranteed between [0,_stp_num_modules) */
- }
- /* check if addr is past the last module */
- if (unlikely(begin == _stp_num_modules - 1
- && (addr > _stp_modules_by_addr[begin]->text + _stp_modules_by_addr[begin]->text_size)))
- return NULL;
-
- return _stp_modules_by_addr[begin];
+ unsigned i;
+ struct _stp_module *closest_module = NULL;
+ unsigned long closest_module_offset = ~0; /* minimum[addr - module->.text] */
+
+ for (i=0; i<_stp_num_modules; i++)
+ {
+ unsigned long module_text_addr, this_module_offset;
+
+ if (_stp_modules[i]->num_sections < 1) continue;
+ module_text_addr = _stp_modules[i]->sections[0].addr; /* XXX: assume section[0]=>text */
+ if (addr < module_text_addr) continue;
+ this_module_offset = module_text_addr - addr;
+
+ if (this_module_offset < closest_module_offset)
+ {
+ closest_module = _stp_modules[i];
+ closest_module_offset = this_module_offset;
+ }
+ }
+
+ return closest_module;
}
-static struct _stp_module *_stp_get_unwind_info(unsigned long addr)
-{
- struct _stp_module *m;
- struct _stp_symbol *s;
- unsigned long flags;
- STP_RLOCK_MODULES;
- m = _stp_find_module_by_addr(addr);
- if (unlikely(m == NULL)) {
- STP_RUNLOCK_MODULES;
- return NULL;
- }
- /* Lock the module struct so it doesn't go away while being used. */
- /* Probably could never happen, but lock it to be sure for now. */
- read_lock(&m->lock);
- STP_RUNLOCK_MODULES;
- return m;
-}
-
-static const char *_stp_kallsyms_lookup(unsigned long addr,
- unsigned long *symbolsize, unsigned long *offset, char **modname, char *namebuf)
+static const char *_stp_kallsyms_lookup(unsigned long addr, unsigned long *symbolsize,
+ unsigned long *offset, char **modname, char *namebuf)
{
struct _stp_module *m;
struct _stp_symbol *s;
unsigned long flags;
unsigned end, begin = 0;
- STP_RLOCK_MODULES;
m = _stp_find_module_by_addr(addr);
if (unlikely(m == NULL)) {
- STP_RUNLOCK_MODULES;
return NULL;
}
+ /* NB: relativize the address to the (XXX) presumed text section. */
+ addr -= m->sections[0].addr;
end = m->num_symbols;
/* binary search for symbols within the module */
@@ -187,17 +146,15 @@ static const char *_stp_kallsyms_lookup(unsigned long addr,
}
if (namebuf) {
strlcpy(namebuf, s->symbol, KSYM_NAME_LEN + 1);
- STP_RUNLOCK_MODULES;
return namebuf;
} else {
- STP_RUNLOCK_MODULES;
return s->symbol;
}
}
- STP_RUNLOCK_MODULES;
return NULL;
}
+
/** Print an address symbolically.
* @param address The address to lookup.
* @note Symbolic lookups should not normally be done within
diff --git a/runtime/sym.h b/runtime/sym.h
index 0bb64c13..c7caacae 100644
--- a/runtime/sym.h
+++ b/runtime/sym.h
@@ -17,12 +17,6 @@ struct _stp_symbol {
const char *symbol;
};
-DEFINE_RWLOCK(_stp_module_lock);
-#define STP_RLOCK_MODULES read_lock_irqsave(&_stp_module_lock, flags)
-#define STP_WLOCK_MODULES write_lock_irqsave(&_stp_module_lock, flags)
-#define STP_RUNLOCK_MODULES read_unlock_irqrestore(&_stp_module_lock, flags)
-#define STP_WUNLOCK_MODULES write_unlock_irqrestore(&_stp_module_lock, flags)
-
struct _stp_module {
/* the module name, or "" for kernel */
char name[STP_MODULE_NAME_LEN];
@@ -31,59 +25,34 @@ struct _stp_module {
/* trust this because as of 2.6.19, there are not yet */
/* any notifier hooks that will tell us when a module */
/* is unloading. */
- unsigned long module;
-
- /* the start of the module's text and data sections */
- unsigned long text;
- unsigned long data;
-
- uint32_t text_size;
-
- /* how many symbols this module has that we are interested in */
- uint32_t num_symbols;
-
- /* how many sections this module has */
- uint32_t num_sections;
-
- /* how the data below was allocated */
- /* 0 = kmalloc, 1 = vmalloc */
- struct {
- unsigned symbols :1;
- unsigned symbol_data :1;
- unsigned unwind_data :1;
- unsigned unwind_hdr :1;
- } allocated;
+ unsigned long module; /* XXX: why not struct module * ? */
struct _stp_symbol *sections;
-
- /* an array of num_symbols _stp_symbol structs */
- struct _stp_symbol *symbols; /* ordered by address */
-
- /* where we stash our copy of the strtab */
- void *symbol_data;
-
+ unsigned num_sections;
+ struct _stp_symbol *symbols; /* ordered by address */
+ unsigned num_symbols;
+
/* the stack unwind data for this module */
void *unwind_data;
void *unwind_hdr;
uint32_t unwind_data_len;
uint32_t unwind_hdr_len;
uint32_t unwind_is_ehframe; /* unwind data comes from .eh_frame */
- rwlock_t lock; /* lock while unwinding is happening */
-
};
-#ifndef STP_MAX_MODULES
-#define STP_MAX_MODULES 256
-#endif
-/* the alphabetical array of modules */
-struct _stp_module *_stp_modules[STP_MAX_MODULES];
+/* Defined by translator-generated stap-symbols.h. */
+struct _stp_module *_stp_modules [];
+int _stp_num_modules;
+
+#if 0
/* the array of modules ordered by addresses */
struct _stp_module *_stp_modules_by_addr[STP_MAX_MODULES];
+#endif
/* the number of modules in the arrays */
-int _stp_num_modules = 0;
+
static unsigned long _stp_kretprobe_trampoline = 0;
unsigned long _stp_module_relocate (const char *module, const char *section, unsigned long offset);
diff --git a/runtime/transport/ChangeLog b/runtime/transport/ChangeLog
index 0a157390..4bb0868b 100644
--- a/runtime/transport/ChangeLog
+++ b/runtime/transport/ChangeLog
@@ -13,7 +13,8 @@
* symbols.c (_stp_validate_addr): Add validating address in runtime.
2008-06-13 Wenji Huang <wenji.huang@oracle.com>
- * control.c (_stp_ctl_write_dbug): Remove STP_UNWIND support.
+
+ * control.c (_stp_ctl_write_dbug): Remove STP_UNWIND support.
2008-06-03 Frank Ch. Eigler <fche@elastic.org>
diff --git a/runtime/transport/control.c b/runtime/transport/control.c
index 9319b9ca..09506bb1 100644
--- a/runtime/transport/control.c
+++ b/runtime/transport/control.c
@@ -30,7 +30,9 @@ static ssize_t _stp_ctl_write_cmd(struct file *file, const char __user *buf, siz
count -= sizeof(u32);
buf += sizeof(u32);
+
#ifdef DEBUG_TRANS
+ printk (KERN_INFO " control write_cmd: Got %s. len=%d\n", _stp_command_name[type], (int)count);
if (type < STP_MAX_CMD)
_dbug("Got %s. len=%d\n", _stp_command_name[type], (int)count);
#endif
@@ -56,9 +58,11 @@ static ssize_t _stp_ctl_write_cmd(struct file *file, const char __user *buf, siz
#else
return -1;
#endif
+ case STP_RELOCATION:
+ _stp_do_relocation (buf, count);
+ break;
+
case STP_READY:
- /* request symbolic information */
- /* _stp_ask_for_symbols(); */
break;
default:
@@ -66,7 +70,7 @@ static ssize_t _stp_ctl_write_cmd(struct file *file, const char __user *buf, siz
return -EINVAL;
}
- return count;
+ return count; /* Pretend that we absorbed the entire message. */
}
struct _stp_buffer {
diff --git a/runtime/transport/procfs.c b/runtime/transport/procfs.c
index 750e1994..64b48e4d 100644
--- a/runtime/transport/procfs.c
+++ b/runtime/transport/procfs.c
@@ -122,6 +122,8 @@ static ssize_t _stp_ctl_write_cmd(struct file *file, const char __user *buf, siz
if (get_user(type, (int __user *)buf))
return -EFAULT;
+ printk (KERN_INFO " procfs write_cmd: Got %s. len=%d\n", _stp_command_name[type], (int)count);
+
#if DEBUG_TRANSPORT > 0
if (type < STP_MAX_CMD)
_dbug("Got %s. len=%d\n", _stp_command_name[type], (int)count);
diff --git a/runtime/transport/symbols.c b/runtime/transport/symbols.c
index 9299fc67..c868c5fd 100644
--- a/runtime/transport/symbols.c
+++ b/runtime/transport/symbols.c
@@ -16,293 +16,52 @@
#define _STP_SYMBOLS_C_
#include "../sym.h"
-static char *_stp_symbol_data = NULL;
-static int _stp_symbol_state = 0;
-static char *_stp_module_data = NULL;
-static int _stp_module_state = 0;
-/* these are all the symbol types we are interested in */
-static int _stp_sym_type_ok(int type)
-{
- /* we only care about function symbols, which are in the text section */
- if (type == 'T' || type == 't')
- return 1;
- return 0;
-}
-
-/* From a module struct, scan the symtab and figure out how much space */
-/* we need to store all the parts we are interested in */
-static unsigned _stp_get_sym_sizes(struct module *m, unsigned *dsize)
-{
- unsigned int i;
- unsigned num = 0, datasize = 0;
- for (i = 0; i < m->num_symtab; i++) {
- char *str = (char *)(m->strtab + m->symtab[i].st_name);
- if (*str != '\0' && _stp_sym_type_ok(m->symtab[i].st_info)) {
- datasize += strlen(str) + 1;
- num++;
- }
- }
- *dsize = datasize;
- return num;
-}
-
-/* allocate space for a module, sections, and symbols */
-static struct _stp_module *_stp_alloc_module(unsigned sectsize, unsigned num, unsigned datasize)
-{
- struct _stp_module *mod = (struct _stp_module *)_stp_kzalloc(sizeof(struct _stp_module));
- if (mod == NULL)
- goto bad;
-
- mod->sections = (struct _stp_symbol *)_stp_kmalloc(sectsize);
- if (mod->sections == NULL)
- goto bad;
-
- mod->symbols = (struct _stp_symbol *)_stp_kmalloc(num * sizeof(struct _stp_symbol));
- if (mod->symbols == NULL) {
- mod->symbols = (struct _stp_symbol *)_stp_vmalloc(num * sizeof(struct _stp_symbol));
- if (mod->symbols == NULL)
- goto bad;
- mod->allocated.symbols = 1;
- }
-
- mod->symbol_data = _stp_kmalloc(datasize);
- if (mod->symbol_data == NULL) {
- mod->symbol_data = _stp_vmalloc(datasize);
- if (mod->symbol_data == NULL)
- goto bad;
- mod->allocated.symbol_data = 1;
- }
-
- mod->num_symbols = num;
- return mod;
-
-bad:
- if (mod) {
- if (mod->sections)
- _stp_kfree(mod->sections);
- if (mod->symbols) {
- if (mod->allocated.symbols)
- _stp_vfree(mod->symbols);
- else
- _stp_kfree(mod->symbols);
- }
- _stp_kfree(mod);
- }
- return NULL;
-}
-
-static void _stp_free_module(struct _stp_module *mod)
-{
- /* The module write lock is held. Any prior readers of this */
- /* module's data will have read locks and need to finish before */
- /* the memory is freed. */
- write_lock(&mod->lock);
- write_unlock(&mod->lock); /* there will be no more readers */
-
- /* Free symbol memory */
- /* If symbol_data wasn't allocated, then symbols weren't either. */
- if (mod->symbol_data) {
- if (mod->symbols) {
- if (mod->allocated.symbols)
- _stp_vfree(mod->symbols);
- else
- _stp_kfree(mod->symbols);
- }
- if (mod->allocated.symbol_data)
- _stp_vfree(mod->symbol_data);
- else
- _stp_kfree(mod->symbol_data);
- }
- if (mod->unwind_data) {
- if (mod->allocated.unwind_data)
- _stp_vfree(mod->unwind_data);
- else
- _stp_kfree(mod->unwind_data);
- }
- if (mod->unwind_hdr) {
- if (mod->allocated.unwind_hdr)
- _stp_vfree(mod->unwind_hdr);
- else
- _stp_kfree(mod->unwind_hdr);
- }
- if (mod->sections)
- _stp_kfree(mod->sections);
-
- /* free module memory */
- _stp_kfree(mod);
-}
-
-/* Delete a module and free its memory. */
-/* The module lock should already be held before calling this. */
-static void _stp_del_module(struct _stp_module *mod)
-{
- int i, num;
-
- dbug_sym(1, "deleting module %s\n", mod->name);
- /* signal relocation code to clear its cache */
- _stp_module_relocate((char *)-1, NULL, 0);
-
- /* remove module from the arrays */
- for (num = 0; num < _stp_num_modules; num++) {
- if (_stp_modules[num] == mod)
- break;
- }
- if (num >= _stp_num_modules)
- return;
-
- for (i = num; i < _stp_num_modules - 1; i++)
- _stp_modules[i] = _stp_modules[i + 1];
-
- for (num = 0; num < _stp_num_modules; num++) {
- if (_stp_modules_by_addr[num] == mod)
- break;
- }
- for (i = num; i < _stp_num_modules - 1; i++)
- _stp_modules_by_addr[i] = _stp_modules_by_addr[i + 1];
-
- _stp_num_modules--;
-
- _stp_free_module(mod);
-}
-
-static void _stp_free_modules(void)
-{
- int i;
- /* This only happens when the systemtap module unloads */
- /* so there is no need for locks. */
- for (i = _stp_num_modules - 1; i >= 0; i--)
- _stp_del_module(_stp_modules[i]);
-}
-
-static unsigned long _stp_kallsyms_lookup_name(const char *name);
static void _stp_create_unwind_hdr(struct _stp_module *m);
-extern unsigned _stp_num_kernel_symbols;
-extern struct _stp_symbol _stp_kernel_symbols[];
-/* initialize the kernel symbols */
-static int _stp_init_kernel_symbols(void)
+static void _stp_do_relocation(const char __user *buf, size_t count)
{
- _stp_modules[0] = (struct _stp_module *)_stp_kzalloc(sizeof(struct _stp_module));
- if (_stp_modules[0] == NULL) {
- _dbug("cannot allocate memory\n");
- return -1;
- }
- _stp_modules[0]->symbols = _stp_kernel_symbols;
- _stp_modules[0]->num_symbols = _stp_num_kernel_symbols;
- rwlock_init(&_stp_modules[0]->lock);
- _stp_num_modules = 1;
+ struct _stp_msg_relocation msg;
+ unsigned mi, si;
- /* Note: this mapping is used by kernel/_stext pseudo-relocations. */
- #ifdef __powerpc__
- _stp_modules[0]->text = _stp_kallsyms_lookup_name(".__start");
- #else
- _stp_modules[0]->text = _stp_kallsyms_lookup_name("_stext");
- #endif
- if (_stp_modules[0]->text == 0) {
- _dbug("Lookup of _stext failed. Exiting.\n");
- return -1;
- }
+ if (sizeof(msg) != count)
+ {
+ errk ("STP_RELOCATE message size mismatch (%lu vs %lu)\n",
+ (long unsigned) sizeof(msg), (long unsigned) count);
+ return;
+ }
- _stp_modules[0]->data = _stp_kallsyms_lookup_name("_etext");
- if (_stp_modules[0]->data == 0) {
- _dbug("Lookup of _etext failed. Exiting.\n");
- return -1;
- }
+ if (unlikely(copy_from_user (& msg, buf, count)))
+ return;
- _stp_modules[0]->text_size = _stp_modules[0]->data - _stp_modules[0]->text;
- _stp_modules_by_addr[0] = _stp_modules[0];
-
- _stp_kretprobe_trampoline = _stp_kallsyms_lookup_name("kretprobe_trampoline");
- /* Lookup failure is not fatal */
+ dbug_sym(2, "relocate (%s %s 0x%lx)\n", msg.module, msg.reloc, (unsigned long) msg.address);
- return 0;
-}
-
-static void _stp_do_unwind_data(const char __user *buf, size_t count)
-{
- u32 unwind_len;
- unsigned long flags;
- char name[STP_MODULE_NAME_LEN];
- int i;
- struct _stp_module *m;
- dbug_unwind(1, "got unwind data, count=%d\n", count);
+ /* Save the relocation value. XXX: While keeping the data here is
+ fine for the kernel address space ("kernel" and "*.ko" modules),
+ it is NOT fine for user-space apps. They need a separate
+ relocation values for each address space, since the same shared
+ libraries/executables can be mapped in at different
+ addresses. */
- if (count < STP_MODULE_NAME_LEN + sizeof(unwind_len)) {
- dbug_unwind(1, "unwind message too short\n");
- return;
- }
- if (strncpy_from_user(name, buf, STP_MODULE_NAME_LEN) < 0) {
- errk("userspace copy failed\n");
- return;
- }
- dbug_unwind(1, "name=%s\n", name);
- if (!strcmp(name,"*")) {
- /* OK, all initial unwind data received. Ready to go. */
- _stp_ctl_send(STP_TRANSPORT, NULL, 0);
- return;
- }
- count -= STP_MODULE_NAME_LEN;
- buf += STP_MODULE_NAME_LEN;
+ for (mi=0; mi<_stp_num_modules; mi++)
+ {
+ if (strcmp (_stp_modules[mi]->name, msg.module))
+ continue;
- if (get_user(unwind_len, (u32 __user *)buf)) {
- errk("userspace copy failed\n");
- return;
- }
- count -= sizeof(unwind_len);
- buf += sizeof(unwind_len);
- if (count != unwind_len) {
- dbug_unwind(1, "count=%d unwind_len=%d\n", (int)count, (int)unwind_len);
- return;
- }
+ for (si=0; si<_stp_modules[mi]->num_sections; si++)
+ {
+ if (strcmp (_stp_modules[mi]->sections[si].symbol, msg.reloc))
+ continue;
- STP_RLOCK_MODULES;
- for (i = 0; i < _stp_num_modules; i++) {
- if (strcmp(name, _stp_modules[i]->name) == 0)
- break;
- }
- if (unlikely(i == _stp_num_modules)) {
- dbug_unwind(1, "module %s not found!\n", name);
- STP_RUNLOCK_MODULES;
- return;
- }
- m = _stp_modules[i];
- write_lock(&m->lock);
- STP_RUNLOCK_MODULES;
-
- /* allocate space for unwind data */
- m->unwind_data = _stp_kmalloc(count);
- if (unlikely(m->unwind_data == NULL)) {
- m->unwind_data = _stp_vmalloc(count);
- if (m->unwind_data == NULL) {
- errk("kmalloc failed\n");
- goto done;
- }
- m->allocated.unwind_data = 1;
- }
-
- if (unlikely(copy_from_user(m->unwind_data, buf, count))) {
- errk("userspace copy failed\n");
- if (m->unwind_data) {
- if (m->allocated.unwind_data)
- _stp_vfree(m->unwind_data);
- else
- _stp_kfree(m->unwind_data);
- m->unwind_data = NULL;
- }
- goto done;
- }
- m->unwind_data_len = count;
-#ifdef STP_USE_DWARF_UNWINDER
- _stp_create_unwind_hdr(m);
-#endif
-done:
- write_unlock(&m->lock);
+ _stp_modules[mi]->sections[si].addr = msg.address;
+ } /* loop over sections */
+ } /* loop over modules */
}
+
static int _stp_compare_addr(const void *p1, const void *p2)
{
struct _stp_symbol *s1 = (struct _stp_symbol *)p1;
@@ -405,270 +164,5 @@ static int _stp_section_is_interesting(const char *name)
return ret;
}
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)
-struct module_sect_attr
-{
- struct module_attribute mattr;
- char *name;
- unsigned long address;
-};
-
-struct module_sect_attrs
-{
- struct attribute_group grp;
- unsigned int nsections;
- struct module_sect_attr attrs[0];
-};
-#endif
-
-/* Create a new _stp_module and load the symbols */
-static struct _stp_module *_stp_load_module_symbols(struct module *mod)
-{
- int i, num, overflow = 0;
- struct module_sect_attrs *sa = mod->sect_attrs;
- struct attribute_group *sag = & sa->grp;
- unsigned sect_size = 0, sect_num = 0, sym_size, sym_num;
- struct _stp_module *sm;
- char *dataptr, *endptr;
- unsigned nsections = 0;
-
-#ifdef STAPCONF_MODULE_NSECTIONS
- nsections = sa->nsections;
-#else
- /* count section attributes on older kernel */
- struct attribute** gattr;
- for (gattr = sag->attrs; *gattr; gattr++)
- nsections++;
- dbug_sym(2, "\tcount %d\n", nsections);
-#endif
-
- /* calculate how much space to allocate for section strings */
- for (i = 0; i < nsections; i++) {
- if (_stp_section_is_interesting(sa->attrs[i].name)) {
- sect_num++;
- sect_size += strlen(sa->attrs[i].name) + 1;
- dbug_sym(2, "\t%s\t%lx\n", sa->attrs[i].name, sa->attrs[i].address);
- }
- }
- sect_size += sect_num * sizeof(struct _stp_symbol);
-
- /* and how much space for symbols */
- sym_num = _stp_get_sym_sizes(mod, &sym_size);
-
- sm = _stp_alloc_module(sect_size, sym_num, sym_size);
- if (!sm) {
- errk("failed to allocate memory for module.\n");
- return NULL;
- }
-
- strlcpy(sm->name, mod->name, STP_MODULE_NAME_LEN);
- sm->module = (unsigned long)mod;
- sm->text = (unsigned long)mod->module_core;
- sm->text_size = mod->core_text_size;
- sm->data = 0; /* fixme */
- sm->num_sections = sect_num;
- rwlock_init(&sm->lock);
-
- /* copy in section data */
- dataptr = (char *)((long)sm->sections + sect_num * sizeof(struct _stp_symbol));
- endptr = (char *)((long)sm->sections + sect_size);
- num = 0;
- for (i = 0; i < nsections; i++) {
- size_t len, maxlen;
- if (_stp_section_is_interesting(sa->attrs[i].name)) {
- sm->sections[num].addr = sa->attrs[i].address;
- sm->sections[num].symbol = dataptr;
- maxlen = (size_t) (endptr - dataptr);
- len = strlcpy(dataptr, sa->attrs[i].name, maxlen);
- if (unlikely(len >= maxlen)) {
- _dbug("dataptr=%lx endptr=%lx len=%d maxlen=%d\n", dataptr, endptr, len, maxlen);
- overflow = 1;
- }
- dataptr += len + 1;
- num++;
- }
- }
- if (unlikely(overflow)) {
- errk("Section names truncated!!! Should never happen!!\n");
- *endptr = 0;
- overflow = 0;
- }
-
- /* now copy all the symbols we are interested in */
- dataptr = sm->symbol_data;
- endptr = dataptr + sym_size - 1;
- num = 0;
- for (i = 0; i < mod->num_symtab; i++) {
- char *str = (char *)(mod->strtab + mod->symtab[i].st_name);
- if (*str != '\0' && _stp_sym_type_ok(mod->symtab[i].st_info)) {
- sm->symbols[num].symbol = dataptr;
- sm->symbols[num].addr = mod->symtab[i].st_value;
- while (*str && (dataptr < endptr))
- *dataptr++ = *str++;
- if (unlikely(*str))
- overflow = 1;
- *dataptr++ = 0;
- num++;
- }
- }
- if (unlikely(overflow))
- errk("Symbol names truncated!!! Should never happen!!\n");
-
- /* sort symbols by address */
- _stp_sort(sm->symbols, num, sizeof(struct _stp_symbol), _stp_compare_addr, _stp_swap_symbol);
-
- return sm;
-}
-
-/* Remove any old module info from our database. */
-static void _stp_module_exists_delete(struct _stp_module *mod)
-{
- int i, num;
- /* remove any old modules with the same name */
- for (num = 1; num < _stp_num_modules; num++) {
- if (strcmp(_stp_modules[num]->name, mod->name) == 0) {
- dbug_sym(1, "found existing module with name %s. Deleting.\n", mod->name);
- _stp_del_module(_stp_modules[num]);
- break;
- }
- }
-
- /* remove modules with overlapping addresses */
- for (num = 1; num < _stp_num_modules; num++) {
- if (mod->text + mod->text_size < _stp_modules_by_addr[num]->text)
- continue;
- if (mod->text < _stp_modules_by_addr[num]->text + _stp_modules_by_addr[num]->text_size) {
- dbug_sym(1, "New module %s overlaps with old module %s. Deleting old.\n",
- mod->name, _stp_modules_by_addr[num]->name);
- _stp_del_module(_stp_modules_by_addr[num]);
- }
- }
-
-}
-
-static void _stp_ins_module(struct module *mod)
-{
- int i, num, res;
- unsigned long flags;
- struct _stp_module *m;
- dbug_sym(1, "insert %s\n", mod->name);
- m = _stp_load_module_symbols(mod);
- if (m == NULL)
- return;
-
- STP_WLOCK_MODULES;
- _stp_module_exists_delete(m);
- /* check for overflow */
- if (_stp_num_modules == STP_MAX_MODULES) {
- errk("Exceeded the limit of %d modules\n", STP_MAX_MODULES);
- goto done;
- }
-
- /* insert alphabetically in _stp_modules[] */
- for (num = 1; num < _stp_num_modules; num++)
- if (strcmp(_stp_modules[num]->name, m->name) > 0)
- break;
- for (i = _stp_num_modules; i > num; i--)
- _stp_modules[i] = _stp_modules[i - 1];
- _stp_modules[num] = m;
- /* insert by text address in _stp_modules_by_addr[] */
- for (num = 1; num < _stp_num_modules; num++)
- if (m->text < _stp_modules_by_addr[num]->text)
- break;
- for (i = _stp_num_modules; i > num; i--)
- _stp_modules_by_addr[i] = _stp_modules_by_addr[i - 1];
- _stp_modules_by_addr[num] = m;
- _stp_num_modules++;
-done:
- STP_WUNLOCK_MODULES;
- return;
-}
-
-static int _stp_module_load_notify(struct notifier_block *self, unsigned long val, void *data)
-{
- struct module *mod = (struct module *)data;
- struct _stp_module rmod;
- switch (val) {
- case MODULE_STATE_COMING:
- dbug_sym(1, "module %s load notify\n", mod->name);
- _stp_ins_module(mod);
- break;
- default:
- errk("module loaded? val=%ld\n", val);
- }
- return 0;
-}
-
-static struct notifier_block _stp_module_load_nb = {
- .notifier_call = _stp_module_load_notify,
-};
-
-#include <linux/seq_file.h>
-
-static int _stp_init_modules(void)
-{
- loff_t pos = 0;
- void *res;
- struct module *mod;
- const struct seq_operations *modules_op = (const struct seq_operations *)_stp_kallsyms_lookup_name("modules_op");
-
- if (modules_op == NULL) {
- _dbug("Lookup of modules_op failed.\n");
- return -1;
- }
-
- /* Use the seq_file interface to safely get a list of installed modules */
- res = modules_op->start(NULL, &pos);
- while (res) {
- mod = list_entry(res, struct module, list);
- _stp_ins_module(mod);
- res = modules_op->next(NULL, res, &pos);
- }
-
- if (register_module_notifier(&_stp_module_load_nb))
- errk("failed to load module notifier\n");
-
- /* unlocks the list */
- modules_op->stop(NULL, NULL);
-
-#if 0 /* def STP_USE_DWARF_UNWINDER */
- /* now that we have all the modules, ask for their unwind info */
- {
- unsigned long flags;
- int i, left = STP_CTL_BUFFER_SIZE;
- char buf[STP_CTL_BUFFER_SIZE];
- char *ptr = buf;
- *ptr = 0;
-
- STP_RLOCK_MODULES;
- /* Loop through modules, sending module names packed into */
- /* messages of size STP_CTL_BUFFER. */
- for (i = 0; i < _stp_num_modules; i++) {
- char *name = _stp_modules[i]->name;
- int len = strlen(name);
- if (len >= left) {
- _stp_ctl_send(STP_UNWIND, buf, sizeof(buf) - left);
- ptr = buf;
- left = STP_CTL_BUFFER_SIZE;
- }
- strlcpy(ptr, name, left);
- ptr += len + 1;
- left -= len + 1;
- }
- STP_RUNLOCK_MODULES;
-
- /* Send terminator. When we get this back from stapio */
- /* that means all the unwind info has been sent. */
- strlcpy(ptr, "*", left);
- left -= 2;
- _stp_ctl_send(STP_UNWIND, buf, sizeof(buf) - left);
- }
-#else
- /* done with modules, now go */
- _stp_ctl_send(STP_TRANSPORT, NULL, 0);
-#endif /* STP_USE_DWARF_UNWINDER */
-
- return 0;
-}
#endif /* _STP_SYMBOLS_C_ */
diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c
index 8fc06f37..ed9718ae 100644
--- a/runtime/transport/transport.c
+++ b/runtime/transport/transport.c
@@ -84,7 +84,6 @@ static void _stp_cleanup_and_exit(int send_exit)
int failures;
_stp_exit_flag = 1;
- unregister_module_notifier(&_stp_module_load_nb);
/* we only want to do this stuff once */
_stp_exit_called = 1;
@@ -180,8 +179,7 @@ void _stp_transport_close()
_stp_unregister_ctl_channel();
if (_stp_utt)
utt_trace_remove(_stp_utt);
- _stp_free_modules();
- _stp_kill_time();
+ _stp_kill_time(); /* Go to a beach. Drink a beer. */
_stp_print_cleanup(); /* free print buffers */
_stp_mem_debug_done();
dbug_trans(1, "---- CLOSED ----\n");
@@ -263,20 +261,11 @@ int _stp_transport_init(void)
_stp_transport_state = 1;
- dbug_trans(1, "calling init_kernel_symbols\n");
- if (_stp_init_kernel_symbols() < 0)
- goto err4;
-
- dbug_trans(1, "calling init_modules\n");
- if (_stp_init_modules() < 0)
- goto err4;
+ /* Signal stapio to send us STP_START back (XXX: ?!?!?!). */
+ _stp_ctl_send(STP_TRANSPORT, NULL, 0);
return 0;
-err4:
- errk("failed to initialize modules\n");
- _stp_free_modules();
- destroy_workqueue(_stp_wq);
err3:
_stp_print_cleanup();
err2:
diff --git a/runtime/transport/transport_msgs.h b/runtime/transport/transport_msgs.h
index 27476e76..596f4925 100644
--- a/runtime/transport/transport_msgs.h
+++ b/runtime/transport/transport_msgs.h
@@ -10,6 +10,7 @@
*/
#define STP_MODULE_NAME_LEN 64
+#define STP_SYMBOL_NAME_LEN 64
struct _stp_trace {
uint32_t sequence; /* event number */
@@ -28,12 +29,11 @@ enum
STP_DISCONNECT,
STP_BULK,
STP_READY,
-#ifdef STP_OLD_TRANSPORT
- /** deprecated **/
+ STP_RELOCATION,
+ /** deprecated STP_OLD_TRANSPORT **/
STP_BUF_INFO,
STP_SUBBUFS_CONSUMED,
STP_REALTIME_DATA,
-#endif
STP_MAX_CMD
};
@@ -48,11 +48,10 @@ static const char *_stp_command_name[] = {
"STP_DISCONNECT",
"STP_BULK",
"STP_READY",
-#ifdef STP_OLD_TRANSPORT
+ "STP_RELOCATION",
"STP_BUF_INFO",
"STP_SUBBUFS_CONSUMED",
"STP_REALTIME_DATA",
-#endif
};
#endif /* DEBUG_TRANS */
@@ -97,3 +96,11 @@ struct _stp_consumed_info
uint32_t consumed;
};
#endif
+
+/* Unwind data. stapio->module */
+struct _stp_msg_relocation
+{
+ char module[STP_MODULE_NAME_LEN];
+ char reloc[STP_SYMBOL_NAME_LEN];
+ uint64_t address;
+};
diff --git a/runtime/unwind.c b/runtime/unwind.c
index aa270cad..21ea4559 100644
--- a/runtime/unwind.c
+++ b/runtime/unwind.c
@@ -108,7 +108,6 @@ static void _stp_create_unwind_hdr(struct _stp_module *m)
header = _stp_vmalloc(hdrSize);
if (header == NULL)
return;
- m->allocated.unwind_hdr = 1;
}
header->version = 1;
@@ -156,17 +155,10 @@ static void _stp_create_unwind_hdr(struct _stp_module *m)
bad:
dbug_unwind(1, "unwind data for %s is unacceptable. Freeing.", m->name);
if (header) {
- if (m->allocated.unwind_hdr) {
- m->allocated.unwind_hdr = 0;
- _stp_vfree(header);
- } else
- _stp_kfree(header);
+ _stp_vfree(header);
}
if (m->unwind_data) {
- if (m->allocated.unwind_data)
- _stp_vfree(m->unwind_data);
- else
- _stp_kfree(m->unwind_data);
+ _stp_vfree(m->unwind_data);
m->unwind_data = NULL;
m->unwind_data_len = 0;
}
@@ -691,7 +683,7 @@ int unwind(struct unwind_frame_info *frame)
if (UNW_PC(frame) == 0)
return -EINVAL;
- m = _stp_get_unwind_info(pc);
+ m = NULL /*_stp_get_unwind_info(pc) */;
if (unlikely(m == NULL)) {
dbug_unwind(1, "No module found for pc=%lx", pc);
return -EINVAL;
@@ -940,21 +932,18 @@ int unwind(struct unwind_frame_info *frame)
break;
}
}
- read_unlock(&m->lock);
dbug_unwind(1, "returning 0 (%lx)\n", UNW_PC(frame));
return 0;
copy_failed:
dbug_unwind(1, "_stp_read_address failed to access memory\n");
err:
- read_unlock(&m->lock);
return -EIO;
done:
/* PC was in a range convered by a module but no unwind info */
/* found for the specific PC. This seems to happen only for kretprobe */
/* trampolines and at the end of interrupt backtraces. */
- read_unlock(&m->lock);
return 1;
#undef CASES
#undef FRAME_REG
diff --git a/session.h b/session.h
index 58bc7ac5..734c8d7d 100644
--- a/session.h
+++ b/session.h
@@ -164,7 +164,10 @@ struct systemtap_session
Dwarf_Addr sym_kprobes_text_end;
Dwarf_Addr sym_stext;
+ // List of libdwfl module names to extract symbol/unwind data for.
+ std::set<std::string> unwindsym_modules;
struct module_cache* module_cache;
+
std::set<std::string> seen_errors;
std::set<std::string> seen_warnings;
unsigned num_errors () { return seen_errors.size(); }
diff --git a/tapsets.cxx b/tapsets.cxx
index cdc14d61..7714715e 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -3255,6 +3255,7 @@ dwarf_query::add_probe_point(const string& funcname,
if (! bad)
{
+ sess.unwindsym_modules.insert (module);
probe = new dwarf_derived_probe(funcname, filename, line,
module, reloc_section, addr, reloc_addr, *this, scope_die);
results.push_back(probe);
@@ -3301,7 +3302,6 @@ dwarf_query::assess_dbinfo_reqt()
}
-
// The critical determining factor when interpreting a pattern
// string is, perhaps surprisingly: "presence of a lineno". The
// presence of a lineno changes the search strategy completely.
@@ -4844,6 +4844,7 @@ dwarf_builder::build(systemtap_session & sess,
q.statement_num_val, q.statement_num_val,
q, 0);
finished_results.push_back (p);
+ sess.unwindsym_modules.insert ("kernel");
return;
}
@@ -5472,6 +5473,9 @@ struct utrace_builder: public derived_probe_builder
string::size_type start_pos, end_pos;
string component;
+ // XXX: these checks should be done in terms of filesystem
+ // operations.
+
// Make sure it starts with '/'.
if (path[0] != '/')
throw semantic_error ("process path must start with a '/'",
@@ -5498,6 +5502,8 @@ struct utrace_builder: public derived_probe_builder
// Make sure it isn't relative.
else if (component == "." || component == "..")
throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok);
+
+ sess.unwindsym_modules.insert (path);
}
finished_results.push_back(new utrace_derived_probe(sess, base, location,
diff --git a/translate.cxx b/translate.cxx
index 557c2f12..0e10f0fc 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -14,6 +14,7 @@
#include "session.h"
#include "tapsets.h"
#include "util.h"
+#include "dwarf_wrappers.h"
#include <cstdlib>
#include <iostream>
@@ -21,6 +22,7 @@
#include <sstream>
#include <string>
#include <cassert>
+#include <cstring>
extern "C" {
#include <elfutils/libdwfl.h>
@@ -4323,51 +4325,175 @@ c_unparser::visit_hist_op (hist_op*)
}
-static map< Dwarf_Addr, string> addrmap;
-#include <string.h>
-
-static int
-kernel_filter (const char *module, const char *file __attribute__((unused)))
+struct unwindsym_dump_context
{
- return !strcmp(module,"kernel");
-}
+ systemtap_session& session;
+ ostream& output;
+ unsigned stp_module_index;
+};
+
static int
-get_symbols (Dwfl_Module *m,
- void **userdata __attribute__ ((unused)),
- const char *name __attribute__ ((unused)),
- Dwarf_Addr base __attribute__ ((unused)),
- void *arg __attribute__ ((unused)))
+dump_unwindsyms (Dwfl_Module *m,
+ void **userdata __attribute__ ((unused)),
+ const char *name,
+ Dwarf_Addr base,
+ void *arg)
{
+ unwindsym_dump_context* c = (unwindsym_dump_context*) arg;
+ assert (c);
+ unsigned real_stpmodules_index = c->stp_module_index;
+
+ string modname = name;
+
+ // skip modules/files we're not actually interested in
+ if (c->session.unwindsym_modules.find(modname) == c->session.unwindsym_modules.end())
+ return DWARF_CB_OK;
+
+ c->stp_module_index ++;
+
+ if (c->session.verbose > 1)
+ clog << "dump_unwindsyms " << name
+ << " index=" << real_stpmodules_index
+ << " base=0x" << hex << base << dec << endl;
+
+ // We want to extract several bits of information:
+ // - parts of the program-header that map the file's physical offsets to the text section
+ // - section table: just a list of section (relocation) base addresses
+ // - symbol table of the text section, with all addresses relativized to .text base
+ // - the contents of .debug_frame section, for unwinding purposes
+ // In the future, we'll also care about data symbols.
+
+ c->output << "struct _stp_symbol _stp_module_" << real_stpmodules_index<< "_sections[] = {" << endl;
+ if (modname != "kernel")
+ c->output << " { 0, \".text\" }, " << endl; // XXX
+ else
+ c->output << " { 0, \"_stext\" }, " << endl; // XXX
+ c->output << "};" << endl;
+
int syments = dwfl_module_getsymtab(m);
assert(syments);
+
+ // Look up the relocation basis for symbols
+ int n = dwfl_module_relocations (m);
+ dwfl_assert ("dwfl_module_relocations", n >= 0);
+
+ // XXX: unfortunate duplication with tapsets.cxx:emit_address()
+
+ typedef map<Dwarf_Addr,const char*> addrmap_t; // NB: plain map, sorted by address
+ addrmap_t addrmap; // NB: plain map, sorted by address
+
+ Dwarf_Addr extra_offset = 0;
+
for (int i = 1; i < syments; ++i)
{
GElf_Sym sym;
const char *name = dwfl_module_getsym(m, i, &sym, NULL);
- if (name) {
- if (GELF_ST_TYPE (sym.st_info) == STT_FUNC ||
- strcmp(name, "_etext") == 0 ||
- strcmp(name, "_stext") == 0 ||
- strcmp(name, "modules_op") == 0)
- addrmap[sym.st_value] = name;
- }
+ if (name)
+ {
+ // NB: Yey, we found the kernel's _stext value.
+ // Sess.sym_stext may be unset (0) at this point, since
+ // there may have been no kernel probes set. We could
+ // use tapsets.cxx:lookup_symbol_address(), but then
+ // we're already iterating over the same data here...
+ if (modname == "kernel" && !strcmp(name, "_stext"))
+ {
+ extra_offset = sym.st_value;
+ if (c->session.verbose > 2)
+ clog << "Found kernel _stext 0x" << hex << extra_offset << dec << endl;
+ }
+
+ if (GELF_ST_TYPE (sym.st_info) == STT_FUNC)
+ {
+ Dwarf_Addr sym_addr = sym.st_value;
+
+ int i = dwfl_module_relocate_address (m, &sym_addr);
+ dwfl_assert ("dwfl_module_relocate_address", i >= 0);
+ const char *secname = dwfl_module_relocation_info (m, i, NULL);
+
+ if (n == 0 || (n==1 && secname == NULL))
+ {
+ if (c->session.verbose > 2)
+ clog << "Skipped absolute symbol " << name << endl;
+ continue;
+ }
+
+ if (n == 1 && modname == "kernel" && secname[0] == '\0')
+ {
+ // This is a symbol within a relocatable kernel image.
+ secname = "_stext"; // not actually used
+ // NB: don't subtract session.sym_stext, which could be inconveniently NULL.
+ }
+ else if (strcmp (secname, ".text")) /* XXX: only care about .text-related relocations for now. */
+ {
+ if (c->session.verbose > 2)
+ clog << "Skipped symbol " << name << ", due to non-.text relocation section " << secname << endl;
+ continue;
+ }
+ else
+ {
+ // sym_addr has already been relocate relative to .text
+ }
+
+ addrmap[sym_addr] = name;
+ }
+ }
}
+
+ // We write out a *sorted* symbol table, so the runtime doesn't have to sort them later.
+ c->output << "struct _stp_symbol _stp_module_" << real_stpmodules_index<< "_symbols[] = {" << endl;
+ for (addrmap_t::iterator it = addrmap.begin(); it != addrmap.end(); it++)
+ {
+ if (it->first < extra_offset)
+ continue; // skip symbols that occur before our chosen base address
+
+ c->output << " { 0x" << hex << it->first-extra_offset << dec
+ << ", " << lex_cast_qstring (it->second) << " }," << endl;
+ }
+ c->output << "};" << endl;
+
+ c->output << "struct _stp_module _stp_module_" << real_stpmodules_index << " = {" << endl;
+ c->output << ".name = " << lex_cast_qstring (modname) << ", " << endl;
+
+ c->output << ".sections = _stp_module_" << real_stpmodules_index << "_sections" << ", " << endl;
+ c->output << ".num_sections = sizeof(_stp_module_" << real_stpmodules_index << "_sections)/"
+ << "sizeof(struct _stp_symbol), " << endl;
+
+ c->output << ".symbols = _stp_module_" << real_stpmodules_index << "_symbols" << ", " << endl;
+ c->output << ".num_symbols = sizeof(_stp_module_" << real_stpmodules_index << "_symbols)/"
+ << "sizeof(struct _stp_symbol), " << endl;
+
+ c->output << "};" << endl << endl;
+
return DWARF_CB_OK;
}
-int
-emit_symbol_data_from_debuginfo(systemtap_session& s, ofstream& kallsyms_out)
+
+// Emit symbol table & unwind data, plus any calls needed to register
+// them with the runtime.
+
+void
+emit_symbol_data (systemtap_session& s)
{
+ string symfile = "stap-symbols.h";
+
+ s.op->newline() << "#include " << lex_cast_qstring (symfile);
+
+ ofstream kallsyms_out ((s.tmpdir + "/" + symfile).c_str());
+
+ unwindsym_dump_context ctx = { s, kallsyms_out, 0 };
+
+ // XXX: copied from tapsets.cxx, sadly
static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug:build";
static char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH");
static char *debuginfo_path = (debuginfo_env_arr ?
- debuginfo_env_arr : debuginfo_path_arr);
+ debuginfo_env_arr : debuginfo_path_arr);
static const char *debug_path = (debuginfo_env_arr ?
- debuginfo_env_arr : s.kernel_release.c_str());
-
+ debuginfo_env_arr : s.kernel_release.c_str());
+
+ // ---- step 1: process any kernel modules listed
static const Dwfl_Callbacks kernel_callbacks =
{
dwfl_linux_kernel_find_elf,
@@ -4376,75 +4502,70 @@ emit_symbol_data_from_debuginfo(systemtap_session& s, ofstream& kallsyms_out)
& debuginfo_path
};
- Dwfl *dwfl = dwfl_begin (&kernel_callbacks);
+ Dwfl *dwfl = dwfl_begin (&kernel_callbacks);
+ if (!dwfl)
+ throw semantic_error ("cannot open dwfl");
+ dwfl_report_begin (dwfl);
+ int rc = dwfl_linux_kernel_report_offline (dwfl, debug_path, NULL /* XXX: filtering callback */);
+ dwfl_report_end (dwfl, NULL, NULL);
+ dwfl_assert ("dwfl_linux_kernel_report_offline", rc);
+ ptrdiff_t off = 0;
+ do
+ {
+ if (pending_interrupts) return;
+ off = dwfl_getmodules (dwfl, &dump_unwindsyms, (void *) &ctx, 0);
+ }
+ while (off > 0);
+ dwfl_assert("dwfl_getmodules", off == 0);
+ dwfl_end(dwfl);
+
+
+ // ---- step 2: process any user modules (files) listed
+ static const Dwfl_Callbacks user_callbacks =
+ {
+ NULL, /* dwfl_linux_kernel_find_elf, */
+ dwfl_standard_find_debuginfo,
+ dwfl_offline_section_address,
+ & debuginfo_path
+ };
+
+ for (std::set<std::string>::iterator it = s.unwindsym_modules.begin();
+ it != s.unwindsym_modules.end();
+ it++)
+ {
+ string modname = *it;
+ assert (modname.length() != 0);
+ if (modname[0] != '/') continue; // user-space files must be full paths
+ Dwfl *dwfl = dwfl_begin (&user_callbacks);
if (!dwfl)
- throw semantic_error ("cannot open dwfl");
+ throw semantic_error ("cannot create dwfl for " + modname);
+
dwfl_report_begin (dwfl);
-
- int rc = dwfl_linux_kernel_report_offline (dwfl,
- debug_path,
- kernel_filter);
+ Dwfl_Module* mod = dwfl_report_offline (dwfl, modname.c_str(), modname.c_str(), -1);
dwfl_report_end (dwfl, NULL, NULL);
- if (rc < 0)
- return rc;
-
- dwfl_getmodules (dwfl, &get_symbols, NULL, 0);
+ dwfl_assert ("dwfl_report_offline", mod);
+ ptrdiff_t off = 0;
+ do
+ {
+ if (pending_interrupts) return;
+ off = dwfl_getmodules (dwfl, &dump_unwindsyms, (void *) &ctx, 0);
+ }
+ while (off > 0);
+ dwfl_assert("dwfl_getmodules", off == 0);
dwfl_end(dwfl);
+ }
- int i = 0;
- map< Dwarf_Addr, string>::iterator pos;
- kallsyms_out << "struct _stp_symbol _stp_kernel_symbols [] = {";
- for (pos = addrmap.begin(); pos != addrmap.end(); pos++) {
- kallsyms_out << " { 0x" << hex << pos->first << ", " << "\"" << pos->second << "\" },\n";
- i++;
- }
- kallsyms_out << "};\n";
- kallsyms_out << "unsigned _stp_num_kernel_symbols = " << dec << i << ";\n";
- return i == 0;
-}
+ // Print out a definition of the runtime's _stp_modules[] globals.
-int
-emit_symbol_data (systemtap_session& s)
-{
- unsigned i=0;
- char kallsyms_outbuf [4096];
- ofstream kallsyms_out ((s.tmpdir + "/stap-symbols.h").c_str());
- kallsyms_out.rdbuf()->pubsetbuf (kallsyms_outbuf,
- sizeof(kallsyms_outbuf));
- s.op->newline() << "\n\n#include \"stap-symbols.h\"";
-
- // FIXME for non-debuginfo use.
- if (true) {
- return emit_symbol_data_from_debuginfo(s, kallsyms_out);
- } else {
- // For symbol-table only operation, we don't have debuginfo,
- // so parse /proc/kallsyms.
-
- ifstream kallsyms("/proc/kallsyms");
- string lastaddr;
-
- kallsyms_out << "struct _stp_symbol _stp_kernel_symbols [] = {";
- while (! kallsyms.eof())
- {
- string addr, type, sym;
- kallsyms >> addr >> type >> sym >> ws;
-
- if (kallsyms.peek() == '[')
- break;
-
- // NB: kallsyms includes some duplicate addresses
- if ((type == "t" || type == "T" || type == "A" || sym == "modules_op") && lastaddr != addr)
- {
- kallsyms_out << " { 0x" << addr << ", " << "\"" << sym << "\" },\n";
- lastaddr = addr;
- i ++;
- }
- }
- kallsyms_out << "};\n";
- kallsyms_out << "unsigned _stp_num_kernel_symbols = " << i << ";\n";
- }
- return (i == 0);
+ kallsyms_out << endl;
+ kallsyms_out << "struct _stp_module *_stp_modules [] = {" << endl;
+ for (unsigned i=0; i<ctx.stp_module_index; i++)
+ {
+ kallsyms_out << "& _stp_module_" << i << "," << endl;
+ }
+ kallsyms_out << "};" << endl;
+ kallsyms_out << "int _stp_num_modules = " << ctx.stp_module_index << ";" << endl;
}
@@ -4610,16 +4731,16 @@ translate_pass (systemtap_session& s)
s.up->emit_global_param (s.globals[i]);
}
- s.op->newline() << "MODULE_DESCRIPTION(\"systemtap probe\");";
- s.op->newline() << "MODULE_LICENSE(\"GPL\");"; // XXX
+ emit_symbol_data (s);
+
+ s.op->newline() << "MODULE_DESCRIPTION(\"systemtap-generated probe\");";
+ s.op->newline() << "MODULE_LICENSE(\"GPL\");";
}
catch (const semantic_error& e)
{
s.print_error (e);
}
- rc |= emit_symbol_data (s);
-
s.op->line() << "\n";
delete s.op;