diff options
-rw-r--r-- | ChangeLog | 1 | ||||
-rw-r--r-- | main.cxx | 17 | ||||
-rw-r--r-- | runtime/probes.c | 152 | ||||
-rw-r--r-- | runtime/staprun/common.c | 30 | ||||
-rw-r--r-- | runtime/staprun/mainloop.c | 25 | ||||
-rw-r--r-- | runtime/staprun/staprun.c | 184 | ||||
-rw-r--r-- | runtime/staprun/staprun.h | 1 | ||||
-rw-r--r-- | runtime/sym.c | 141 | ||||
-rw-r--r-- | runtime/sym.h | 55 | ||||
-rw-r--r-- | runtime/transport/ChangeLog | 3 | ||||
-rw-r--r-- | runtime/transport/control.c | 10 | ||||
-rw-r--r-- | runtime/transport/procfs.c | 2 | ||||
-rw-r--r-- | runtime/transport/symbols.c | 566 | ||||
-rw-r--r-- | runtime/transport/transport.c | 17 | ||||
-rw-r--r-- | runtime/transport/transport_msgs.h | 17 | ||||
-rw-r--r-- | runtime/unwind.c | 17 | ||||
-rw-r--r-- | session.h | 3 | ||||
-rw-r--r-- | tapsets.cxx | 8 | ||||
-rw-r--r-- | translate.cxx | 301 |
19 files changed, 574 insertions, 976 deletions
@@ -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> @@ -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", §ion_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 @@ -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; |