summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Olsa <Jiri Olsa jolsa@redhat.com>2011-01-31 20:27:59 +0100
committerJiri Olsa <Jiri Olsa jolsa@redhat.com>2011-01-31 20:27:59 +0100
commitc167c2a3a404f5166ffd70a15a09c97cd1883ee8 (patch)
tree12d2916bd2a58a20c9672a8d4c78974c5e9eb13d
parentab67022bd5b60c839cc5d8360cd0c0a7f8c21f83 (diff)
downloadlatrace-c167c2a3a404f5166ffd70a15a09c97cd1883ee8.tar.gz
latrace-c167c2a3a404f5166ffd70a15a09c97cd1883ee8.tar.xz
latrace-c167c2a3a404f5166ffd70a15a09c97cd1883ee8.zip
adding support for global symbol config
one global tree to rule them all - only one tree is searched during the plt entry/exit - symbols are added during the bind audit callback
-rw-r--r--ChangeLog6
-rw-r--r--src/Makefile3
-rw-r--r--src/args.c54
-rw-r--r--src/args.h10
-rw-r--r--src/audit-init.c4
-rw-r--r--src/audit.c47
-rw-r--r--src/config.h17
-rw-r--r--src/symbol.c154
8 files changed, 251 insertions, 44 deletions
diff --git a/ChangeLog b/ChangeLog
index 27022f2..1bae726 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2010-01-31 Jiri Olsa <olsajiri@gmail.com>
+ * adding support for global symbol config
+ one global symbol tree to rule them all
+ - only one tree is searched during the plt entry/exit
+ - symbols are added during the bind audit callback
+
2010-10-17 Jiri Olsa <olsajiri@gmail.com>
* Artur Skawina <art.08.09@gmail.com>
- enhancing names check with *-logic for
diff --git a/src/Makefile b/src/Makefile
index 796028a..eb16064 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -28,7 +28,8 @@ AUDIT_OBJS=\
src/fifo.o \
src/output.o \
src/objsearch.o \
- src/stack.o
+ src/stack.o \
+ src/symbol.o
ifeq ($(CONFIG_ARCH_HAVE_ARGS),y)
AUDIT_OBJS+=\
diff --git a/src/args.c b/src/args.c
index 8672156..a7b745e 100644
--- a/src/args.c
+++ b/src/args.c
@@ -749,25 +749,6 @@ int lt_args_init(struct lt_config_shared *cfg)
return ret;
}
-static struct lt_args_sym* getsym(struct lt_config_shared *cfg, char *sym)
-{
- struct lt_args_sym *a;
- ENTRY e, *ep;
-
- PRINT_VERBOSE(cfg, 1, "request for <%s>\n", sym);
-
- e.key = sym;
- hsearch_r(e, FIND, &ep, &cfg->args_tab);
-
- if (!ep)
- return NULL;
-
- a = (struct lt_args_sym*) ep->data;
-
- PRINT_VERBOSE(cfg, 1, "found %p <%s>\n", a, a->name);
- return a;
-}
-
static int getstr_addenum(struct lt_config_shared *cfg, struct lt_arg *arg,
char *argbuf, int alen, long val)
{
@@ -1096,12 +1077,32 @@ struct lt_args_include* lt_args_buf_get(void)
return inc;
}
-int lt_args_sym_entry(struct lt_config_shared *cfg, char *sym, La_regs *regs,
- char **argbuf, char **argdbuf)
+struct lt_args_sym* lt_args_sym_get(struct lt_config_shared *cfg,
+ const char *sym)
+{
+ struct lt_args_sym *a;
+ ENTRY e, *ep;
+
+ PRINT_VERBOSE(cfg, 1, "request for <%s>\n", sym);
+
+ e.key = (char*) sym;
+ hsearch_r(e, FIND, &ep, &cfg->args_tab);
+
+ if (!ep)
+ return NULL;
+
+ a = (struct lt_args_sym*) ep->data;
+
+ PRINT_VERBOSE(cfg, 1, "found %p <%s>\n", a, a->name);
+ return a;
+}
+
+int lt_args_sym_entry(struct lt_config_shared *cfg, struct lt_symbol *sym,
+ La_regs *regs, char **argbuf, char **argdbuf)
{
- struct lt_args_sym *asym;
+ struct lt_args_sym *asym = sym ? sym->args : NULL;
- if (NULL == (asym = getsym(cfg, sym)))
+ if (!asym)
return -1;
return getargs(cfg, asym, regs, argbuf, argdbuf);
@@ -1146,12 +1147,13 @@ static int getargs_ret(struct lt_config_shared *cfg, struct lt_args_sym *asym,
return lt_stack_process_ret(cfg, asym, regs, &data);
}
-int lt_args_sym_exit(struct lt_config_shared *cfg, char *sym, La_regs *inregs, La_retval *outregs,
+int lt_args_sym_exit(struct lt_config_shared *cfg, struct lt_symbol *sym,
+ La_regs *inregs, La_retval *outregs,
char **argbuf, char **argdbuf)
{
- struct lt_args_sym *asym;
+ struct lt_args_sym *asym = sym ? sym->args : NULL;
- if (NULL == (asym = getsym(cfg, sym)))
+ if (!asym)
return -1;
return getargs_ret(cfg, asym, outregs, argbuf, argdbuf);
diff --git a/src/args.h b/src/args.h
index b7fa427..e0f4846 100644
--- a/src/args.h
+++ b/src/args.h
@@ -23,6 +23,7 @@
#define ARGS_H
struct lt_config_shared;
+struct lt_symbol;
enum {
LT_ARGS_DTYPE_POD = 1,
@@ -126,10 +127,13 @@ struct lt_args_data {
/* arguments */
int lt_args_init(struct lt_config_shared *cfg);
-int lt_args_sym_entry(struct lt_config_shared *cfg, char *sym, La_regs *regs,
+struct lt_args_sym* lt_args_sym_get(struct lt_config_shared *cfg,
+ const char *sym);
+int lt_args_sym_entry(struct lt_config_shared *cfg, struct lt_symbol *sym,
+ La_regs *regs, char **argbuf, char **argdbuf);
+int lt_args_sym_exit(struct lt_config_shared *cfg, struct lt_symbol *sym,
+ La_regs *inregs, La_retval *outregs,
char **argbuf, char **argdbuf);
-int lt_args_sym_exit(struct lt_config_shared *cfg, char *sym, La_regs *inregs,
- La_retval *outregs, char **argbuf, char **argdbuf);
int lt_args_add_enum(struct lt_config_shared *cfg, char *name,
struct lt_list_head *h);
struct lt_enum_elem* lt_args_get_enum(struct lt_config_shared *cfg, char *name, char *val);
diff --git a/src/audit-init.c b/src/audit-init.c
index 686c56d..fad89ca 100644
--- a/src/audit-init.c
+++ b/src/audit-init.c
@@ -242,6 +242,10 @@ int audit_init(int argc, char **argv, char **env)
if (lt_sh(&cfg, not_follow_fork))
lt_sh(&cfg, pid) = getpid();
+ /* enable global symbols if needed */
+ lt_sh(&cfg, global_symbols) = lt_sh(&cfg, args_enabled);
+ PRINT_VERBOSE(&cfg, 1, "global_symbols %d\n", lt_sh(&cfg, global_symbols));
+
cfg.init_ok = 1;
return 0;
}
diff --git a/src/audit.c b/src/audit.c
index 2313c7c..d26a539 100644
--- a/src/audit.c
+++ b/src/audit.c
@@ -70,12 +70,13 @@ static int check_flow_below(const char *symname, int in)
return ret;
}
-static int sym_entry(const char *symname, char *lib_from, char *lib_to,
- La_regs *regs)
+static int sym_entry(const char *symname, void *ptr,
+ char *lib_from, char *lib_to, La_regs *regs)
{
int argret = -1;
char *argbuf = "", *argdbuf = "";
struct timeval tv;
+ struct lt_symbol *sym = NULL;
PRINT_VERBOSE(&cfg, 2, "%s@%s\n", symname, lib_to);
@@ -85,9 +86,12 @@ static int sym_entry(const char *symname, char *lib_from, char *lib_to,
if (lt_sh(&cfg, timestamp) || lt_sh(&cfg, counts))
gettimeofday(&tv, NULL);
+ if (lt_sh(&cfg, global_symbols))
+ sym = lt_symbol_get(cfg.sh, ptr, symname);
+
#ifdef CONFIG_ARCH_HAVE_ARGS
argret = lt_sh(&cfg, args_enabled) ?
- lt_args_sym_entry(cfg.sh, (char*) symname, regs, &argbuf, &argdbuf) : -1;
+ lt_args_sym_entry(cfg.sh, sym, regs, &argbuf, &argdbuf) : -1;
#endif
if (lt_sh(&cfg, pipe)) {
@@ -119,12 +123,14 @@ static int sym_entry(const char *symname, char *lib_from, char *lib_to,
return 0;
}
-static int sym_exit(const char *symname, char *lib_from, char *lib_to,
- const La_regs *inregs, La_retval *outregs)
+static int sym_exit(const char *symname, void *ptr,
+ char *lib_from, char *lib_to,
+ const La_regs *inregs, La_retval *outregs)
{
int argret = -1;
char *argbuf = "", *argdbuf = "";
struct timeval tv;
+ struct lt_symbol *sym = NULL;
PRINT_VERBOSE(&cfg, 2, "%s@%s\n", symname, lib_to);
@@ -134,9 +140,12 @@ static int sym_exit(const char *symname, char *lib_from, char *lib_to,
if (lt_sh(&cfg, timestamp) || lt_sh(&cfg, counts))
gettimeofday(&tv, NULL);
+ if (lt_sh(&cfg, global_symbols))
+ sym = lt_symbol_get(cfg.sh, ptr, symname);
+
#ifdef CONFIG_ARCH_HAVE_ARGS
argret = lt_sh(&cfg, args_enabled) ?
- lt_args_sym_exit(cfg.sh, (char*) symname,
+ lt_args_sym_exit(cfg.sh, sym,
(La_regs*) inregs, outregs, &argbuf, &argdbuf) : -1;
#endif
@@ -225,7 +234,7 @@ unsigned int la_objopen(struct link_map *l, Lmid_t a, uintptr_t *cookie)
return 0;
}
-static unsigned int la_symbind(const char *symname)
+static unsigned int la_symbind(ElfW(Sym) *sym, const char *symname)
{
unsigned int flags = 0;
@@ -240,6 +249,10 @@ static unsigned int la_symbind(const char *symname)
flags = LA_SYMB_NOPLTENTER|LA_SYMB_NOPLTEXIT;
}
+ /* we are interested in this symbol */
+ if (!(flags & LA_SYMB_NOPLTENTER))
+ lt_symbol_bind(cfg.sh, (void*) sym->st_value, symname);
+
return flags;
}
@@ -267,19 +280,21 @@ unsigned int la_objclose(uintptr_t *__cookie)
return 0;
}
+#if __ELF_NATIVE_CLASS == 32
uintptr_t la_symbind32(Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
uintptr_t *defcook, unsigned int *flags, const char *symname)
{
- *flags = la_symbind(symname);
+ *flags = la_symbind(sym, symname);
return sym->st_value;
}
-
+#elif __ELF_NATIVE_CLASS == 64
uintptr_t la_symbind64(Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
uintptr_t *defcook, unsigned int *flags, const char *symname)
{
- *flags = la_symbind(symname);
+ *flags = la_symbind(sym, symname);
return sym->st_value;
}
+#endif
ElfW(Addr)
pltenter(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
@@ -295,8 +310,10 @@ pltenter(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
CHECK_PID(sym->st_value);
- sym_entry(symname, lr ? lr->l_name : NULL,
- ld ? ld->l_name : NULL, regs);
+ sym_entry(symname, (void*) sym->st_value,
+ lr ? lr->l_name : NULL,
+ ld ? ld->l_name : NULL,
+ regs);
} while(0);
@@ -318,8 +335,10 @@ unsigned int pltexit(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
CHECK_PID(0);
- sym_exit(symname, lr ? lr->l_name : NULL,
- ld ? ld->l_name : NULL, inregs, outregs);
+ sym_exit(symname, (void*) sym->st_value,
+ lr ? lr->l_name : NULL,
+ ld ? ld->l_name : NULL,
+ inregs, outregs);
} while(0);
diff --git a/src/config.h b/src/config.h
index 29c9c4f..c7f88ee 100644
--- a/src/config.h
+++ b/src/config.h
@@ -118,6 +118,7 @@ struct lt_config_shared {
int not_follow_fork;
int framesize_check;
unsigned int framesize;
+ int global_symbols;
/* for 'not_follow_fork' */
pid_t pid;
@@ -260,6 +261,15 @@ struct lt_thread {
struct lt_thread *next;
};
+struct lt_symbol {
+ struct lt_args_sym *args;
+
+ /* symbol name */
+ const char *name;
+ /* symbol address */
+ void *ptr;
+};
+
/* ctl */
int main_ctl(int argc, char **argv);
@@ -307,6 +317,12 @@ char* lt_objsearch(struct lt_config_audit *cfg, const char *name,
/* stack */
int lt_stack_framesize(struct lt_config_audit *cfg, La_regs *regs);
+/* symbol */
+struct lt_symbol* lt_symbol_bind(struct lt_config_shared *cfg,
+ void *ptr, const char *name);
+struct lt_symbol* lt_symbol_get(struct lt_config_shared *cfg,
+ void *ptr, const char *name);
+
#define PRINT(fmt, args...) \
do { \
char lpbuf[1024]; \
@@ -316,6 +332,7 @@ do { \
__LINE__, \
fmt); \
printf(lpbuf, ## args); \
+ fflush(NULL); \
} while(0)
#define PRINT_VERBOSE(cfg, cond, fmt, args...) \
diff --git a/src/symbol.c b/src/symbol.c
new file mode 100644
index 0000000..c4e25b9
--- /dev/null
+++ b/src/symbol.c
@@ -0,0 +1,154 @@
+/*
+ Copyright (C) 2011 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <strings.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+
+static void *root = NULL;
+extern struct lt_config_audit cfg;
+
+static int compare(const void *a, const void *b)
+{
+ const struct lt_symbol *sym_a = a;
+ const struct lt_symbol *sym_b = b;
+
+ PRINT_VERBOSE(&cfg, 1, "a %p, b %p\n", sym_a, sym_b);
+ PRINT_VERBOSE(&cfg, 1, "a ptr %p, b ptr %p\n",
+ sym_a ? sym_a->ptr : NULL,
+ sym_b ? sym_b->ptr : NULL);
+ PRINT_VERBOSE(&cfg, 1, "a name %s, b name %s\n",
+ sym_a ? sym_a->name : "",
+ sym_b ? sym_b->name: "");
+
+ if (!sym_a || !sym_b)
+ return 1;
+
+/* XXX There's a glibc bug/feature causing the symbol to
+ * have different value in plt entry/exit.. so using name
+ * check instead.. so far.. ;) */
+
+#define SEARCH_NAME
+#undef SEARCH_PTR
+
+#ifdef SEARCH_NAME
+ return strcmp(sym_a->name, sym_b->name);
+#endif
+
+#if SEARCH_PTR
+ PRINT_VERBOSE(&cfg, 1, "%s(%p) %s(%p)\n",
+ sym_a->name, sym_a->ptr,
+ sym_b->name, sym_b->ptr);
+
+ if (sym_a->ptr > sym_b->ptr)
+ return 1;
+
+ if (sym_a->ptr < sym_b->ptr)
+ return -1;
+
+ return 0;
+#endif
+}
+
+static int symbol_init(struct lt_config_shared *cfg,
+ struct lt_symbol *sym, const char *name)
+{
+ struct lt_args_sym* a;
+
+ if (lt_sh(cfg, args_enabled)) {
+ a = lt_args_sym_get(cfg, name);
+ if (!a)
+ return -1;
+ }
+
+ sym->args = a;
+
+ PRINT_VERBOSE(cfg, 1, "ok name %s, ptr %p, sym %p\n",
+ name, sym->ptr, sym);
+ return 0;
+}
+
+struct lt_symbol* lt_symbol_bind(struct lt_config_shared *cfg,
+ void *ptr, const char *name)
+{
+ static struct lt_symbol *sym = NULL;
+ struct lt_symbol *s = NULL;
+ void *val;
+
+ if (!sym) {
+ sym = malloc(sizeof(*sym));
+ if (!sym)
+ return NULL;
+ }
+
+ bzero(sym, sizeof(*sym));
+ sym->ptr = ptr;
+ sym->name = name;
+
+ PRINT_VERBOSE(cfg, 1, "checking %s(%p)\n", name, ptr);
+
+ val = tsearch((void *) sym, &root, compare);
+ if (!val)
+ return NULL;
+
+ s = (*(void**) val);
+
+ /* symbol already in */
+ if (s != sym) {
+ PRINT_VERBOSE(cfg, 1, "found %s, ptr %p, sym %p\n",
+ name, sym->ptr, sym);
+ return s;
+ }
+
+ PRINT_VERBOSE(cfg, 1, "added %s, ptr %p, sym %p\n",
+ name, sym->ptr, sym);
+
+ /* not interesting symbol */
+ if (symbol_init(cfg, sym, name))
+ return NULL;
+
+ /* symbol properly added */
+ sym = NULL;
+ return s;
+}
+
+struct lt_symbol* lt_symbol_get(struct lt_config_shared *cfg,
+ void *ptr, const char *name)
+{
+ void *val;
+ struct lt_symbol *s;
+ struct lt_symbol sym = {
+ .ptr = ptr,
+ .name = name,
+ };
+
+ val = tfind(&sym, &root, compare);
+
+ if (!val)
+ s = NULL;
+ else
+ s = *(struct lt_symbol**) val;
+
+ PRINT_VERBOSE(cfg, 1, "found %p '%s'\n", s, s ? s->name : "");
+ return s;
+}