diff options
Diffstat (limited to 'runtime/transport')
-rw-r--r-- | runtime/transport/ChangeLog | 15 | ||||
-rw-r--r-- | runtime/transport/procfs.c | 12 | ||||
-rw-r--r-- | runtime/transport/symbols.c | 420 | ||||
-rw-r--r-- | runtime/transport/transport.c | 45 | ||||
-rw-r--r-- | runtime/transport/transport_msgs.h | 4 |
5 files changed, 486 insertions, 10 deletions
diff --git a/runtime/transport/ChangeLog b/runtime/transport/ChangeLog index 6b63669c..54a3c282 100644 --- a/runtime/transport/ChangeLog +++ b/runtime/transport/ChangeLog @@ -1,3 +1,18 @@ +2006-11-02 Martin Hunt <hunt@redhat.com> + + * symbols.c: New file. Get the STP_SYMBOLS and STP_MODULE + messages, allocate memory and store the data. + + * procfs.c (_stp_proc_write_cmd): When STP_SYMBOLS or STP_MODULE + request is received, call the appropriate functions. + + * transport.c (_stp_handle_start): If necessary, ask staprun for + symbols and modules. + (_stp_cleanup_and_exit): Unregister module notifier. + (_stp_transport_close): Unregister module notifier and free module + memory. + * transport_msgs.h (enum): Add STP_MODULE and STP_SYMBOLS. + 2006-09-26 David Smith <dsmith@redhat.com> * transport.c: Changed 'stpd' references to 'staprun'. diff --git a/runtime/transport/procfs.c b/runtime/transport/procfs.c index b0b03a73..2605e8f1 100644 --- a/runtime/transport/procfs.c +++ b/runtime/transport/procfs.c @@ -82,6 +82,7 @@ static ssize_t _stp_proc_write_cmd (struct file *file, const char __user *buf, //printk ("_stp_proc_write_cmd. count:%d type:%d\n", count, type); count -= sizeof(int); + buf += sizeof(int); switch (type) { case STP_START: @@ -94,16 +95,23 @@ static ssize_t _stp_proc_write_cmd (struct file *file, const char __user *buf, _stp_handle_start (&st); break; } + + case STP_SYMBOLS: + count = _stp_do_symbols(buf, count); + break; + case STP_MODULE: + count = _stp_do_module(buf, count); + break; case STP_EXIT: _stp_exit_flag = 1; break; case STP_TRANSPORT_INFO: { struct transport_info ti; - //printk("STP_TRANSPORT_INFO %d %d\n", count, sizeof(struct transport_info)); + kbug("STP_TRANSPORT_INFO %d %d\n", (int)count, (int)sizeof(struct transport_info)); if (count < sizeof(struct transport_info)) return 0; - if (copy_from_user (&ti, &buf[4], sizeof(struct transport_info))) + if (copy_from_user (&ti, buf, sizeof(struct transport_info))) return -EFAULT; if (_stp_transport_open (&ti) < 0) return -1; diff --git a/runtime/transport/symbols.c b/runtime/transport/symbols.c new file mode 100644 index 00000000..e583103f --- /dev/null +++ b/runtime/transport/symbols.c @@ -0,0 +1,420 @@ +/* -*- linux-c -*- + * symbols.c - stp symbol and module functions + * + * Copyright (C) Red Hat Inc, 2006 + * + * 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 _SYMBOLS_C_ +#define _SYMBOLS_C_ +#include "../sym.h" +#include <linux/sort.h> + +spinlock_t _stp_module_lock = SPIN_LOCK_UNLOCKED; +#define STP_TRYLOCK_MODULES ({ \ + int numtrylock = 0; \ + while (!spin_trylock_irqsave (&_stp_module_lock, flags) && (++numtrylock < MAXTRYLOCK)) \ + ndelay (TRYLOCKDELAY); \ + (numtrylock >= MAXTRYLOCK); \ + }) +#define STP_LOCK_MODULES spin_lock_irqsave(&_stp_module_lock, flags) +#define STP_UNLOCK_MODULES spin_unlock_irqrestore(&_stp_module_lock, flags) + +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) +{ + switch (type) { + case 'T': + case 't': + return 1; + default: + return 0; + } + 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) +{ + 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 and symbols */ +static struct _stp_module * _stp_alloc_module(unsigned num, unsigned datasize) +{ + struct _stp_module *mod = (struct _stp_module *)kmalloc(sizeof(struct _stp_module), GFP_KERNEL); + if (mod == NULL) + goto bad; + + memset(mod, 0, sizeof(struct _stp_module)); + mod->symbols = (struct _stp_symbol *)kmalloc(num * sizeof(struct _stp_symbol), GFP_KERNEL); + if (mod->symbols == NULL) { + mod->symbols = (struct _stp_symbol *)vmalloc(num * sizeof(struct _stp_symbol)); + if (mod->symbols == NULL) + goto bad; + mod->allocated = 1; + } + + mod->symbol_data = kmalloc(datasize, GFP_KERNEL); + if (mod->symbol_data == NULL) { + mod->symbol_data = vmalloc(datasize); + if (mod->symbol_data == NULL) + goto bad; + mod->allocated |= 2; + } + mod->num_symbols = num; + return mod; + +bad: + if (mod) + kfree(mod); + if (mod->allocated && mod->symbols) + vfree(mod->symbols); + else + kfree(mod->symbols); + return NULL; +} + +static struct _stp_module * _stp_alloc_module_from_module (struct module *m) +{ + unsigned datasize, num = _stp_get_sym_sizes(m, &datasize); + return _stp_alloc_module(num, datasize); +} + +/* Delete a module and free its memory. */ +/* The lock should already be held before calling this. */ +static void _stp_del_module(struct _stp_module *mod) +{ + int i, num; + + kbug("deleting %s\n", mod->name); + + /* remove module from the arrays */ + for (num = 1; 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 = 1; 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--; + + /* free symbol memory */ + if (mod->num_symbols) { + if (mod->allocated & 1) + vfree(mod->symbols); + else + kfree(mod->symbols); + if (mod->allocated & 2) + vfree(mod->symbol_data); + else + kfree(mod->symbol_data); + } + if (mod->sections) + kfree(mod->sections); + + /* free module memory */ + kfree(mod); +} + +static void _stp_free_modules(void) +{ + int i; + unsigned long flags; + + STP_LOCK_MODULES; + for (i = _stp_num_modules - 1; i >= 0; i--) + _stp_del_module(_stp_modules[i]); + STP_UNLOCK_MODULES; +} + +static unsigned long _stp_kallsyms_lookup_name(const char *name); + +/* process the KERNEL symbols */ +static int _stp_do_symbols(const char __user *buf, int count) +{ + unsigned datasize, num; + int i; + struct _stp_symbol *s; + + switch (_stp_symbol_state) { + case 0: + if (count != 8) { + printk("unexpected systemtap error: _stp_do_symbols: count=%d\n", count); + return -EFAULT; + } + if (get_user(num, (unsigned __user *)buf)) + return -EFAULT; + if (get_user(datasize, (unsigned __user *)(buf+4))) + return -EFAULT; + kbug("num=%d datasize=%d\n", num, datasize); + + _stp_modules[0] = _stp_alloc_module(num, datasize); + if (_stp_modules[0] == NULL) { + printk("unexpected systemtap error: cannot allocate memory\n"); + return -EFAULT; + } + _stp_symbol_state = 1; + break; + case 1: + if (copy_from_user ((char *)_stp_modules[0]->symbols, buf, count)) + return -EFAULT; + kbug("got stap_symbols, count=%d\n", count); + _stp_symbol_state = 2; + break; + case 2: + if (copy_from_user (_stp_modules[0]->symbol_data, buf, count)) + return -EFAULT; + kbug("got symbol data, count=%d\n", count); + _stp_num_modules = 1; + + + s = _stp_modules[0]->symbols; + for (i = 0; i < _stp_modules[0]->num_symbols; i++) + s[i].symbol += (long)_stp_modules[0]->symbol_data; + _stp_symbol_state = 3; + _stp_modules[0]->text = _stp_kallsyms_lookup_name("_stext"); + _stp_modules_by_addr[0] = _stp_modules[0]; + break; + default: + printk("systemtap error: unexpected symbol data of size %d.\n", count); + } + return count; +} + +static int _stp_compare_addr(const void *p1, const void *p2) +{ + struct _stp_symbol *s1 = (struct _stp_symbol *)p1; + struct _stp_symbol *s2 = (struct _stp_symbol *)p2; + if (s1->addr == s2->addr) return 0; + if (s1->addr < s2->addr) return -1; + return 1; +} + +static void _stp_swap_symbol(void *x, void *y, int size) +{ + struct _stp_symbol *a = (struct _stp_symbol *)x; + struct _stp_symbol *b = (struct _stp_symbol *)y; + unsigned long addr = a->addr; + const char *symbol = a->symbol; + a->addr = b->addr; + a->symbol = b->symbol; + b->addr = addr; + b->symbol = symbol; +} + + +/* Create a new _stp_module and load the symbols */ +static struct _stp_module *_stp_load_module_symbols (struct _stp_module *imod) +{ + int i, num=0; + struct module *m = (struct module *)imod->module; + struct _stp_module *mod = NULL; + char *dataptr; + + if (m == NULL) { + kbug("imod->module is NULL\n"); + return NULL; + } + if (try_module_get(m)) { + + mod = _stp_alloc_module_from_module(m); + if (mod == NULL) { + module_put(m); + printk("Systemtap failed to allocate memory for module.\n"); + return NULL; + } + + strlcpy(mod->name, imod->name, STP_MODULE_NAME_LEN); + mod->module = imod->module; + mod->text = imod->text; + mod->data = imod->data; + mod->num_sections = imod->num_sections; + mod->sections = imod->sections; + + /* now copy all the symbols we are interested in */ + dataptr = mod->symbol_data; + 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)) { + mod->symbols[num].symbol = dataptr; + mod->symbols[num].addr = m->symtab[i].st_value; + while (*str) *dataptr++ = *str++; + *dataptr++ = 0; + num++; + } + } + module_put(m); + + /* sort symbols by address */ + sort (mod->symbols, num, sizeof(struct _stp_symbol), _stp_compare_addr, _stp_swap_symbol); + } + return mod; +} + +/* Do we already have this module? */ +static int _stp_module_exists(struct _stp_module *mod) +{ + int i, res; + unsigned long flags; + kbug("exists? %s\n", mod->name); + STP_LOCK_MODULES; + for (i = 1; i < _stp_num_modules; i++) { + res = strcmp(_stp_modules[i]->name, mod->name); + if (res > 0) + break; + if (res == 0 && _stp_modules[i]->module == mod->module) { + STP_UNLOCK_MODULES; + return 1; + } + } + STP_UNLOCK_MODULES; + return 0; +} + +static void _stp_ins_module(struct _stp_module *mod) +{ + int i, num, res; + unsigned long flags; + + kbug("insert %s\n", mod->name); + + STP_LOCK_MODULES; + + /* insert alphabetically in _stp_modules[] */ + for (num = 1; num < _stp_num_modules; num++) { + res = strcmp(_stp_modules[num]->name, mod->name); + if (res < 0) + continue; + if (res > 0) + break; + _stp_del_module(_stp_modules[num]); + break; + } + for (i = _stp_num_modules; i > num; i--) + _stp_modules[i] = _stp_modules[i-1]; + _stp_modules[num] = mod; + + /* insert by text address in _stp_modules_by_addr[] */ + for (num = 1; num < _stp_num_modules; num++) { + if (_stp_modules_by_addr[num]->text > mod->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] = mod; + + _stp_num_modules++; + + STP_UNLOCK_MODULES; +} + + +/* Called from procfs.c when a STP_MODULE msg is received */ +static int _stp_do_module(const char __user *buf, int count) +{ + struct _stp_module tmpmod, *mod; + int i; + + if (count < sizeof(tmpmod)) { + printk("_stp_do_modules: expected %ld and got %d\n", sizeof(tmpmod), count); + return -EFAULT; + } + if (copy_from_user ((char *)&tmpmod, buf, sizeof(tmpmod))) + return -EFAULT; + + kbug("Got module %s, count=%d(0x%x)\n", tmpmod.name, count,count); + + if (_stp_module_exists(&tmpmod)) + return count; + + /* copy in section data */ + tmpmod.sections = kmalloc(count - sizeof(tmpmod), GFP_KERNEL); + if (tmpmod.sections == NULL) { + printk("_stp_do_module: unable to allocate memory.\n"); + return -EFAULT; + } + if (copy_from_user ((char *)tmpmod.sections, buf+sizeof(tmpmod), count-sizeof(tmpmod))) { + kfree(tmpmod.sections); + return -EFAULT; + } + for (i = 0; i < tmpmod.num_sections; i++) { + tmpmod.sections[i].symbol = + (char *)((long)tmpmod.sections[i].symbol + + (long)((long)tmpmod.sections + tmpmod.num_sections * sizeof(struct _stp_symbol))); + } + + #ifdef DEBUG + for (i = 0; i < tmpmod.num_sections; i++) + printk("section %d (stored at %p): %s %lx\n", i, &tmpmod.sections[i], tmpmod.sections[i].symbol, tmpmod.sections[i].addr); + #endif + + /* load symbols from tmpmod.module to mod */ + mod = _stp_load_module_symbols(&tmpmod); + if (mod == NULL) { + kfree(tmpmod.sections); + return -EFAULT; + } + + _stp_ins_module(mod); + + return count; +} + +static int _stp_transport_send (int type, void *data, int len); + +static int _stp_module_load_notify(struct notifier_block * self, unsigned long val, void * data) +{ +#ifdef CONFIG_MODULES + struct module *mod = (struct module *)data; + struct _stp_module rmod; + + switch (val) { + case MODULE_STATE_COMING: + dbug("module %s loaded\n", mod->name); + strlcpy(rmod.name, mod->name, STP_MODULE_NAME_LEN); + _stp_transport_send(STP_MODULE, &rmod, sizeof(struct _stp_module)); + break; + default: + printk("module loaded? val=%ld\n", val); + } +#endif + return 0; +} + +static struct notifier_block _stp_module_load_nb = { + .notifier_call = _stp_module_load_notify, +}; + +#endif /* _SYMBOLS_C_ */ diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c index 70c0f157..6e9c9ae1 100644 --- a/runtime/transport/transport.c +++ b/runtime/transport/transport.c @@ -17,6 +17,7 @@ #include <linux/delay.h> #include "transport.h" #include "time.c" +#include "symbols.c" #ifdef STP_RELAYFS #include "relayfs.c" @@ -49,7 +50,7 @@ int _stp_transport_open(struct transport_info *info); #include "procfs.c" /* send commands with timeout and retry */ -int _stp_transport_send (int type, void *data, int len) +static int _stp_transport_send (int type, void *data, int len) { int err, trylimit = 50; while ((err = _stp_write(type, data, len)) < 0 && trylimit--) @@ -58,7 +59,7 @@ int _stp_transport_send (int type, void *data, int len) } #ifndef STP_RELAYFS -int _stp_transport_write (void *data, int len) +static int _stp_transport_write (void *data, int len) { /* when _stp_exit_called is set, we are in probe_exit() and we can sleep */ if (_stp_exit_called) @@ -91,10 +92,36 @@ static void _stp_handle_buf_info(int *cpuptr) /* * _stp_handle_start - handle STP_START */ + void _stp_handle_start (struct transport_start *st) { +#ifdef CONFIG_MODULES + static int got_modules=0; +#endif + kbug ("stp_handle_start pid=%d\n", st->pid); + /* we've got a start request, but first, grab kernel symbols if we need them */ + if (_stp_num_modules == 0) { + char tmp = 0; + _stp_transport_send(STP_SYMBOLS, &tmp, 1); + return; + } + +#ifdef CONFIG_MODULES + /* grab current module addresses if we haven't already */ + if (got_modules == 0) { + struct _stp_module mod; + strcpy(mod.name, ""); + got_modules = 1; + _stp_transport_send(STP_MODULE, &mod, sizeof(struct _stp_module)); + return; + } + + if (register_module_notifier(&_stp_module_load_nb)) + printk("Systemtap error: failed to load module notifier\n"); +#endif + /* note: st->pid is actually the return code for the reply packet */ st->pid = probe_start(); atomic_set(&_stp_start_finished,1); @@ -128,6 +155,9 @@ static void _stp_cleanup_and_exit (int dont_rmmod) if (!_stp_exit_called) { int failures; +#ifdef CONFIG_MODULES + unregister_module_notifier(&_stp_module_load_nb); +#endif /* we only want to do this stuff once */ _stp_exit_called = 1; @@ -197,13 +227,13 @@ void _stp_transport_close() if (_stp_transport_mode == STP_TRANSPORT_RELAYFS) _stp_relayfs_close(_stp_chan, _stp_dir); #endif +#ifdef CONFIG_MODULES + unregister_module_notifier(&_stp_module_load_nb); +#endif _stp_unregister_procfs(); - + _stp_free_modules(); _stp_kill_time(); - - /* free print buffers */ - _stp_print_cleanup(); - + _stp_print_cleanup(); /* free print buffers */ kbug("---- CLOSED ----\n"); } @@ -317,5 +347,4 @@ static int _stp_relay_write (const void *data, unsigned length) return length; } #endif - #endif /* _TRANSPORT_C_ */ diff --git a/runtime/transport/transport_msgs.h b/runtime/transport/transport_msgs.h index 6d0b2c34..282762ab 100644 --- a/runtime/transport/transport_msgs.h +++ b/runtime/transport/transport_msgs.h @@ -16,6 +16,8 @@ enum STP_EXIT, STP_OOB_DATA, STP_SYSTEM, + STP_SYMBOLS, + STP_MODULE, }; /* control channel command structs */ @@ -56,3 +58,5 @@ struct cmd_info char cmd[128]; }; + + |