summaryrefslogtreecommitdiffstats
path: root/runtime/transport
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/transport')
-rw-r--r--runtime/transport/ChangeLog15
-rw-r--r--runtime/transport/procfs.c12
-rw-r--r--runtime/transport/symbols.c420
-rw-r--r--runtime/transport/transport.c45
-rw-r--r--runtime/transport/transport_msgs.h4
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];
};
+
+