[PATCH 2/2] libtraceevent: Add tep_load_modules() API
Steven Rostedt <[email protected]> Tue, 3 Feb 2026 18:12:26 -0500
| Newsgroups | org.kernel.vger.linux-trace-devel |
|---|---|
| Message-ID | <[email protected]> |
From: "Steven Rostedt (Google)" <[email protected]> When the last_boot_info is loaded by tep_parse_last_boot_info(), the offset between the last boot core functions can be calculated by the _text address of kallsyms of the current boot. But the module functions need to be calculated by where the modules are loaded. This can be found by the /proc/modules file. Add tep_load_modules() to read the /proc/modules file and this will be used to calculate the offsets from a buffer from a previous boot and from the module functions in kallsyms of the current boot. Signed-off-by: Steven Rostedt (Google) <[email protected]> --- Documentation/libtraceevent-parse-files.txt | 65 ++++++-- Documentation/libtraceevent.txt | 1 + include/traceevent/event-parse.h | 1 + src/event-parse-local.h | 3 + src/event-parse.c | 158 +++++++++++++++++++- 5 files changed, 216 insertions(+), 12 deletions(-) diff --git a/Documentation/libtraceevent-parse-files.txt b/Documentation/libtraceevent-parse-files.txt index 65270b2be9ec..b1dd36f68bc2 100644 --- a/Documentation/libtraceevent-parse-files.txt +++ b/Documentation/libtraceevent-parse-files.txt @@ -4,7 +4,7 @@ libtraceevent(3) NAME ---- tep_parse_saved_cmdlines, tep_parse_printk_formats, tep_parse_kallsyms, -tep_parse_last_boot_info - Parsing functions to load mappings +tep_parse_last_boot_info, tep_load_modules - Parsing functions to load mappings SYNOPSIS -------- @@ -16,6 +16,7 @@ int *tep_parse_saved_cmdlines*(struct tep_handle pass:[*]_tep_, const char pass: int *tep_parse_printk_formats*(struct tep_handle pass:[*]_tep_, const char pass:[*]_buf_); int *tep_parse_kallsyms*(struct tep_handle pass:[*]_tep_, const char pass:[*]_buf_); int *tep_parse_last_boot_info*(struct tep_handle pass:[*]_tep_, const char pass:[*]_lbi_); +int *tep_load_modules*(struct tep_handle pass:[*]_tep_, char pass:[*]_modules_, size_t _size_); -- DESCRIPTION @@ -50,6 +51,14 @@ the function offsets in the kallsyms (loaded by *tep_parse_kallsyms()*) into the _tep_ handler. The _lbi_ is a nul terminated string that contains the content of the last_boot_info file from the persistent ring buffer instance. +*tep_load_modules()* loads the contents of the file /proc/modules into the _tep_ +handler. This will be used to match the modules found in the last_boot_info (loaded +by *tep_parse_last_boot_info()*) to match the offsets of the binary data in +in a persistent ring buffer that matches the address of a module from the previous +boot to a module address found in _modules_. The _modules_ parameter does not need +to be nul terminated, but _size_ is used to denote the size of the _modules_ string. +This function is useless if *tep_parse_last_boot_info()* isnt used. + RETURN VALUE ------------ The *tep_parse_saved_cmdlines*() function returns 0 in case of success, or -1 @@ -64,6 +73,9 @@ in case of an error. The *tep_parse_last_boot_info*() function retuns 0 in case of success, or -1 in case of error. +The *tep_load_modules*() function returns 0 in case of success, or -1 +in case of error. + EXAMPLE ------- [source,c] @@ -99,16 +111,16 @@ int load_print_strings(struct tep_handle *tep) return r; } -int load_kallsyms(struct tep_handle *tep) +static char *read_file(const char *file, size_t *size) { - char *line = NULL; - char *buf = NULL; - size_t sz = 0; FILE *fp; - int len = 0; - int r; + char *line = NULL; + char *buf = NULL; + size_t sz = 0; + size_t len = 0; + int r; - fp = fopen("/proc/kallsyms", "r"); + fp = fopen(file, "r"); while ((r = getline(&line, &sz, fp)) >= 0) { buf = realloc(buf, len + r + 1); memcpy(buf + len, line, r); @@ -117,9 +129,44 @@ int load_kallsyms(struct tep_handle *tep) free(line); fclose(fp); if (!buf) - return -1; + return NULL; buf[len] = 0; + *size = len; + return buf; +} + +int load_kallsyms(struct tep_handle *tep) +{ + char *buf; + size_t sz; + int r; + + buf = read_file("/proc/kallsyms", &sz); + if (!buf) + return -1; + r = tep_parse_kallsyms(tep, buf); + free(buf); + return r; +} + +int load_last_boot(struct tep_handle *tep, struct tracefs_instance *instance) +{ + char *buf; + size_t sz; + int r; + + buf = tracefs_instance_file_read(instance, "last_boot_info", NULL); + if (!buf) + return 0; + + r = tep_parse_last_boot_info(tep, buf); + free(buf); + if (r < 0) + return r; + buf = read_file("/proc/modules", &sz); + if (!buf) + return -1; r = tep_parse_kallsyms(tep, buf); free(buf); return r; diff --git a/Documentation/libtraceevent.txt b/Documentation/libtraceevent.txt index bc40b2309e28..fba027e55467 100644 --- a/Documentation/libtraceevent.txt +++ b/Documentation/libtraceevent.txt @@ -56,6 +56,7 @@ Meta data parsing: int *tep_parse_printk_formats*(struct tep_handle pass:[*]_tep_, const char pass:[*]_buf_); int *tep_parse_kallsyms*(struct tep_handle pass:[*]_tep_, const char pass:[*]_buf_); int *tep_parse_last_boot_info*(struct tep_handle pass:[*]_tep_, const char pass:[*]_lbi_); + int *tep_load_modules*(struct tep_handle pass:[*]_tep_, char pass:[*]_modules_, size_t _size_); Plugins management: struct tep_plugin_list pass:[*]*tep_load_plugins*(struct tep_handle pass:[*]_tep_); diff --git a/include/traceevent/event-parse.h b/include/traceevent/event-parse.h index ea90986b21b2..3ec151ab634c 100644 --- a/include/traceevent/event-parse.h +++ b/include/traceevent/event-parse.h @@ -429,6 +429,7 @@ int tep_override_comm(struct tep_handle *tep, const char *comm, int pid); int tep_parse_saved_cmdlines(struct tep_handle *tep, const char *buf); int tep_parse_kallsyms(struct tep_handle *tep, const char *kallsyms); int tep_parse_last_boot_info(struct tep_handle *tep, const char *lbi); +int tep_load_modules(struct tep_handle *tep, char *modules, size_t size); int tep_register_function(struct tep_handle *tep, char *name, unsigned long long addr, char *mod); int tep_parse_printk_formats(struct tep_handle *tep, const char *buf); diff --git a/src/event-parse-local.h b/src/event-parse-local.h index 5df83e1d13f7..d3d6ef402476 100644 --- a/src/event-parse-local.h +++ b/src/event-parse-local.h @@ -48,9 +48,12 @@ struct tep_handle { struct func_list *funclist; unsigned int func_count; unsigned long long func_offset; + unsigned long long mod_addr; unsigned long long _text_addr; struct tep_mod_addr *mod_addrs; + struct tep_mod_addr *proc_mods; int nr_mod_addrs; + int nr_proc_mods; struct printk_map *printk_map; diff --git a/src/event-parse.c b/src/event-parse.c index 168379257364..fee0f91c554e 100644 --- a/src/event-parse.c +++ b/src/event-parse.c @@ -534,13 +534,51 @@ static int func_map_init(struct tep_handle *tep) return 0; } +static int cmp_mod_addrs(const void *A, const void *B) +{ + const struct tep_mod_addr *a = A; + const struct tep_mod_addr *b = B; + + if (a->addr < b->addr) + return -1; + + return b[1].addr && a->addr >= b[1].addr; +} + +static int cmp_mods(const void *A, const void *B) +{ + const struct tep_mod_addr *a = A; + const struct tep_mod_addr *b = B; + + return strcmp(a->mod, b->mod); +} + +static unsigned long long mod_addr_offset(struct tep_handle *tep, unsigned long long addr) +{ + struct tep_mod_addr key = { .addr = addr }; + struct tep_mod_addr *mod; + + if (!tep->mod_addr || addr < tep->mod_addr) + return tep->func_offset; + + mod = bsearch(&key, tep->mod_addrs, tep->nr_mod_addrs, + sizeof(key), cmp_mod_addrs); + if (!mod) + return 0; + + mod = bsearch(mod, tep->proc_mods, tep->nr_proc_mods, + sizeof(key), cmp_mods); + + return mod ? mod->addr : 0; +} + static struct func_map * __find_func(struct tep_handle *tep, unsigned long long addr) { struct func_map *func; struct func_map key; - addr += tep->func_offset; + addr += mod_addr_offset(tep, addr); if (!tep->func_map) func_map_init(tep); @@ -623,7 +661,7 @@ static unsigned long long addr_offset(struct tep_handle *tep, struct func_map *m if (!tep->mod_addrs || map->mod) return map->addr; - return map->addr - tep->func_offset; + return map->addr - mod_addr_offset(tep, map->addr); } /** @@ -782,6 +820,35 @@ static int cmp_addrs(const void *A, const void *B) return a->addr > b->addr; } +static void update_mod(struct tep_handle *tep, const struct tep_mod_addr *key) +{ + struct tep_mod_addr *addr; + + addr = bsearch(key, tep->proc_mods, tep->nr_proc_mods, sizeof(*addr), cmp_mods); + if (!addr) + return; + + addr->addr -= key->addr; +} + +static void set_func_mods(struct tep_handle *tep) +{ + int a; + + if (!tep->proc_mods || !tep->mod_addrs) + return; + + for (a = 0; a < tep->nr_mod_addrs; a++) { + if (strncmp(tep->mod_addrs[a].mod, "[kernel]", 8) == 0) + continue; + + if (!tep->mod_addr) + tep->mod_addr = tep->mod_addrs[a].addr; + + update_mod(tep, &tep->mod_addrs[a]); + } +} + /** * tep_parse_last_boot_info - read the last_boot_info file * @tep: a handle to the trace event parser @@ -823,7 +890,7 @@ int tep_parse_last_boot_info(struct tep_handle *tep, const char *lbi) for (p = copy, lines = 0; p; p = strchr(p + 1, '\n'), lines++) ; - addrs = calloc(lines, sizeof(*addrs)); + addrs = calloc(lines + 1, sizeof(*addrs)); if (!addrs) goto out; @@ -860,12 +927,96 @@ int tep_parse_last_boot_info(struct tep_handle *tep, const char *lbi) if (tep->_text_addr) set_func_offset(tep); + if (tep->proc_mods) + set_func_mods(tep); + out: free(copy); free(addrs); return ret; } +/** + * tep_load_modules - Load module information into a tep handle + * @tep: The tep handle to load the module info into + * @modules: A string containing the content of /proc/modules + * @size: The size of the modules string + * + * Saves the locations of where the modules are loaded. This is useful + * with tep_parse_last_boot_info() as it will be used to calculate the + * offsets between the current module locations and the one from the + * previous boot. + * + * Returns: 0 on success and -1 on failure. + */ +int tep_load_modules(struct tep_handle *tep, char *modules, size_t size) +{ + struct tep_mod_addr *mods = NULL; + unsigned long long addr; + char *copy; + char *line; + char *next = NULL; + char *mod; + int lines; + char *p; + int a; + int ret = -1; + + if (!modules) + return -1; + + copy = malloc(size + 1); + if (!copy) + return -1; + strncpy(copy, modules, size); + copy[size] = '\0'; + + for (p = copy, lines = 0; p; p = strchr(p + 1, '\n'), lines++) + ; + + mods = calloc(lines, sizeof(*mods)); + if (!mods) + goto out; + + line = strtok_r(copy, "\n", &next); + for (a = 0; line; a++) { + int n; + + mod = NULL; + errno = 0; + n = sscanf(line, "%ms %*s %*s %*s %*s %18llx", &mod, &addr); + if (errno) + goto out; + + if (n != 2) { + tep_warning("Failed to parse /proc/modules"); + goto out; + } + + mods[a].mod = mod; + mods[a].addr = addr; + + line = strtok_r(NULL, "\n", &next); + } + ret = 0; + + qsort(mods, a, sizeof(*mods), cmp_mods); + + tep->nr_proc_mods = a; + tep->proc_mods = mods; + + /* Allow to free on error handling too */ + mods = NULL; + + if (tep->mod_addrs) + set_func_mods(tep); + + out: + free(copy); + free(mods); + return ret; +} + /** * tep_parse_kallsyms - load functions from a read of /proc/kallsyms * @tep: a handle to the trace event parser @@ -8902,6 +9053,7 @@ void tep_free(struct tep_handle *tep) } free(tep->mod_addrs); + free(tep->proc_mods); while (tep->func_handlers) { func_handler = tep->func_handlers; -- 2.51.0