From 9a89449aef8f314e6466fb64033d52dce00ba5e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Genevi=C3=A8ve=20Bastien?= Date: Wed, 19 Jun 2019 09:53:48 -0400 Subject: [PATCH] Add kernel symbol tracepoints when modules come/go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tracepoints to enumerate the new symbols brought when a new kernel module is loaded. The symbol enumeration is done using the struct module's kallsyms field. Part of the code for the enumeration is inspired by or taken from the linux kernel's module.c file. The overhead when tracepoints are enabled depends on the number of symbols in the module, but is in tens or hundreds or microseconds. With tracepoints disabled, overhead is below 10us in the cases tested. When the module is unloaded, there is only one event to notify of the module unload. The lttng_kallsyms_module_coming function is greatly inspired by and a few lines are copy-pasted from linux's module.c file. Signed-off-by: Geneviève Bastien --- .../events/lttng-module/lttng-kallsyms.h | 24 +++++ lttng-kallsyms.c | 101 +++++++++++++++++- 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/instrumentation/events/lttng-module/lttng-kallsyms.h b/instrumentation/events/lttng-module/lttng-kallsyms.h index ceedb811..e3eea53f 100644 --- a/instrumentation/events/lttng-module/lttng-kallsyms.h +++ b/instrumentation/events/lttng-module/lttng-kallsyms.h @@ -34,6 +34,30 @@ LTTNG_TRACEPOINT_EVENT(lttng_kallsyms_symbol_core, ) ) +LTTNG_TRACEPOINT_EVENT(lttng_kallsyms_new_module_symbol, + + TP_PROTO(unsigned long addr, const char *symbol, const char *module), + + TP_ARGS(addr, symbol, module), + + TP_FIELDS( + ctf_integer_hex(unsigned long, addr, addr) + ctf_string(symbol, symbol) + ctf_string(module, module) + ) +) + +LTTNG_TRACEPOINT_EVENT(lttng_kallsyms_module_unloaded, + + TP_PROTO(const char *module), + + TP_ARGS(module), + + TP_FIELDS( + ctf_string(module, module) + ) +) + #endif /* LTTNG_TRACE_LTTNG_KALLSYMS_H */ /* This part must be outside protection */ diff --git a/lttng-kallsyms.c b/lttng-kallsyms.c index e38a8d5f..d5213cfb 100644 --- a/lttng-kallsyms.c +++ b/lttng-kallsyms.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: (GPL-2.0 or LGPL-2.1) +/* SPDX-License-Identifier: (GPL-2.0) * * lttng-kallsyms.c * @@ -19,6 +19,8 @@ DEFINE_TRACE(lttng_kallsyms_symbol_module); DEFINE_TRACE(lttng_kallsyms_symbol_core); +DEFINE_TRACE(lttng_kallsyms_new_module_symbol); +DEFINE_TRACE(lttng_kallsyms_module_unloaded); /* * Trace the kernel symbols from a given module @@ -57,6 +59,103 @@ int lttng_enumerate_kernel_symbols(struct lttng_session *session) } EXPORT_SYMBOL_GPL(lttng_enumerate_kernel_symbols); +#ifdef CONFIG_MODULES + +/* + * This function is taken from the linux kernel's module.c file + */ +static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms, unsigned int symnum) +{ + return kallsyms->strtab + kallsyms->symtab[symnum].st_name; +} + +static +int lttng_kallsyms_module_coming(struct module *mod) +{ + int ret = 0, i; + struct mod_kallsyms *kallsyms; + + /* Inspired by and partly taken from linux kernel's + * module.c file, module_kallsyms_on_each_symbol function */ + kallsyms = mod->kallsyms; + for (i = 0; i < kallsyms->num_symtab; i++) { + const Elf_Sym *sym = &kallsyms->symtab[i]; + + if (sym->st_shndx == SHN_UNDEF) + continue; + + trace_lttng_kallsyms_new_module_symbol(kallsyms_symbol_value(sym), + kallsyms_symbol_name(kallsyms, i), mod->name); + } + + return ret; +} + +static +int lttng_kallsyms_module_going(struct module *mod) +{ + trace_lttng_kallsyms_module_unloaded(mod->name); + return 0; +} + +static +int lttng_kallsyms_module_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + struct module *mod = data; + int ret = 0; + + switch (val) { + case MODULE_STATE_COMING: + ret = lttng_kallsyms_module_coming(mod); + break; + case MODULE_STATE_GOING: + ret = lttng_kallsyms_module_going(mod); + break; + default: + break; + } + + return ret; +} + +static +struct notifier_block lttng_kallsyms_module_notifier = { + .notifier_call = lttng_kallsyms_module_notify, + .priority = 0, +}; + +static +int lttng_kallsyms_module_init(void) +{ + return register_module_notifier(<tng_kallsyms_module_notifier); +} + +static +void lttng_kallsyms_module_exit(void) +{ + WARN_ON(unregister_module_notifier(<tng_kallsyms_module_notifier)); +} + +#else /* #ifdef CONFIG_MODULES */ + +static +int lttng_kallsyms_module_init(void) +{ + return 0; +} + +static +void lttng_kallsyms_module_exit(void) +{ +} + +#endif /* #else #ifdef CONFIG_MODULES */ + +module_init(lttng_kallsyms_module_init); + +module_exit(lttng_kallsyms_module_exit); + MODULE_LICENSE("GPL and additional rights"); MODULE_AUTHOR("Geneviève Bastien