[lttng-dev] [RFC PATCH lttng-ust 1/2] Add library load/unload tracking events
Mathieu Desnoyers
mathieu.desnoyers at efficios.com
Tue Jul 5 20:01:59 UTC 2016
Patches 1&2 merged into lttng-ust master.
----- On Jul 4, 2016, at 6:53 PM, Mathieu Desnoyers mathieu.desnoyers at efficios.com wrote:
> Track library load and unload, with the following new events:
>
> lttng_ust_lib:load
> lttng_ust_lib:build_id
> lttng_ust_lib:debug_link
> lttng_ust_lib:unload
>
> This takes care of correctly tracing the mapping of direct dependencies
> of dlopen'd libraries, which was not appropriately done by tracing just
> dlopen events.
>
> Fixes: #1035
>
> Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers at efficios.com>
> ---
> include/lttng/ust-events.h | 2 +
> liblttng-ust-dl/lttng-ust-dl.c | 15 +-
> liblttng-ust/Makefile.am | 2 +
> liblttng-ust/lttng-ust-elf.c | 1 +
> liblttng-ust/lttng-ust-statedump.c | 395 +++++++++++++++++++++++++++++++------
> 5 files changed, 350 insertions(+), 65 deletions(-)
>
> diff --git a/include/lttng/ust-events.h b/include/lttng/ust-events.h
> index 9f6aa06..309206d 100644
> --- a/include/lttng/ust-events.h
> +++ b/include/lttng/ust-events.h
> @@ -722,6 +722,8 @@ struct cds_list_head *_lttng_get_sessions(void);
> struct lttng_enum *lttng_ust_enum_get(struct lttng_session *session,
> const char *enum_name);
>
> +void lttng_ust_dl_update(void *ip);
> +
> /* For backward compatibility. Leave those exported symbols in place. */
> extern struct lttng_ctx *lttng_static_ctx;
> void lttng_context_init(void);
> diff --git a/liblttng-ust-dl/lttng-ust-dl.c b/liblttng-ust-dl/lttng-ust-dl.c
> index e457e7a..81b5bec 100644
> --- a/liblttng-ust-dl/lttng-ust-dl.c
> +++ b/liblttng-ust-dl/lttng-ust-dl.c
> @@ -1,6 +1,7 @@
> /*
> * Copyright (C) 2013 Paul Woegerer <paul.woegerer at mentor.com>
> * Copyright (C) 2015 Antoine Busque <abusque at efficios.com>
> + * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers at efficios.com>
> *
> * This library is free software; you can redistribute it and/or
> * modify it under the terms of the GNU Lesser General Public
> @@ -26,6 +27,7 @@
>
> #include <lttng/ust-dlfcn.h>
> #include <lttng/ust-elf.h>
> +#include <lttng/ust-events.h>
> #include <helper.h>
> #include "usterr-signal-safe.h"
>
> @@ -135,19 +137,21 @@ void *dlopen(const char *filename, int flag)
>
> ret = dlinfo(handle, RTLD_DI_LINKMAP, &p);
> if (ret != -1 && p != NULL && p->l_addr != 0) {
> - lttng_ust_dl_dlopen((void *) p->l_addr, p->l_name,
> + lttng_ust_dl_dlopen((void *) p->l_addr,
> + p->l_name,
> LTTNG_UST_CALLER_IP());
> }
> }
> -
> + lttng_ust_dl_update(LTTNG_UST_CALLER_IP());
> return handle;
> }
>
> int dlclose(void *handle)
> {
> + int ret;
> +
> if (__tracepoint_ptrs_registered) {
> struct link_map *p = NULL;
> - int ret;
>
> ret = dlinfo(handle, RTLD_DI_LINKMAP, &p);
> if (ret != -1 && p != NULL && p->l_addr != 0) {
> @@ -156,6 +160,7 @@ int dlclose(void *handle)
> (void *) p->l_addr);
> }
> }
> -
> - return _lttng_ust_dl_libc_dlclose(handle);
> + ret = _lttng_ust_dl_libc_dlclose(handle);
> + lttng_ust_dl_update(LTTNG_UST_CALLER_IP());
> + return ret;
> }
> diff --git a/liblttng-ust/Makefile.am b/liblttng-ust/Makefile.am
> index 05929be..a2a32b4 100644
> --- a/liblttng-ust/Makefile.am
> +++ b/liblttng-ust/Makefile.am
> @@ -43,6 +43,8 @@ liblttng_ust_runtime_la_SOURCES = \
> lttng-ust-statedump.c \
> lttng-ust-statedump.h \
> lttng-ust-statedump-provider.h \
> + ust_lib.c \
> + ust_lib.h \
> tracepoint-internal.h \
> clock.h \
> compat.h \
> diff --git a/liblttng-ust/lttng-ust-elf.c b/liblttng-ust/lttng-ust-elf.c
> index 5f27920..e5b8a08 100644
> --- a/liblttng-ust/lttng-ust-elf.c
> +++ b/liblttng-ust/lttng-ust-elf.c
> @@ -25,6 +25,7 @@
> #include <sys/stat.h>
> #include <fcntl.h>
> #include <unistd.h>
> +#include <stdbool.h>
> #include "lttng-tracer-core.h"
>
> #define BUF_LEN 4096
> diff --git a/liblttng-ust/lttng-ust-statedump.c
> b/liblttng-ust/lttng-ust-statedump.c
> index bcb168b..c8e508c 100644
> --- a/liblttng-ust/lttng-ust-statedump.c
> +++ b/liblttng-ust/lttng-ust-statedump.c
> @@ -1,6 +1,7 @@
> /*
> * Copyright (C) 2013 Paul Woegerer <paul_woegerer at mentor.com>
> * Copyright (C) 2015 Antoine Busque <abusque at efficios.com>
> + * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers at efficios.com>
> *
> * This library is free software; you can redistribute it and/or
> * modify it under the terms of the GNU Lesser General Public
> @@ -24,27 +25,32 @@
> #include <stdio.h>
> #include <stdint.h>
> #include <stdlib.h>
> +#include <stdbool.h>
> #include <sys/types.h>
> #include <unistd.h>
>
> #include <lttng/ust-elf.h>
> +#include <helper.h>
> #include "lttng-tracer-core.h"
> #include "lttng-ust-statedump.h"
> +#include "jhash.h"
>
> #define TRACEPOINT_DEFINE
> +#include "ust_lib.h" /* Only define. */
> +
> #define TRACEPOINT_CREATE_PROBES
> #define TP_SESSION_CHECK
> -#include "lttng-ust-statedump-provider.h"
> +#include "lttng-ust-statedump-provider.h" /* Define and create probes. */
>
> struct dl_iterate_data {
> - void *owner;
> int exec_found;
> + bool first;
> + bool cancel;
> };
>
> struct bin_info_data {
> - void *owner;
> void *base_addr_ptr;
> - const char *resolved_path;
> + char resolved_path[PATH_MAX];
> char *dbg_file;
> uint8_t *build_id;
> uint64_t memsz;
> @@ -56,14 +62,143 @@ struct bin_info_data {
> uint8_t has_debug_link;
> };
>
> +struct lttng_ust_dl_node {
> + struct bin_info_data bin_data;
> + struct cds_hlist_node node;
> + bool traced;
> + bool marked;
> +};
> +
> +#define UST_DL_STATE_HASH_BITS 8
> +#define UST_DL_STATE_TABLE_SIZE (1 << UST_DL_STATE_HASH_BITS)
> +struct cds_hlist_head dl_state_table[UST_DL_STATE_TABLE_SIZE];
> +
> typedef void (*tracepoint_cb)(struct lttng_session *session, void *priv);
>
> +static
> +struct lttng_ust_dl_node *alloc_dl_node(const struct bin_info_data *bin_data)
> +{
> + struct lttng_ust_dl_node *e;
> +
> + e = zmalloc(sizeof(struct lttng_ust_dl_node));
> + if (!e)
> + return NULL;
> + if (bin_data->dbg_file) {
> + e->bin_data.dbg_file = strdup(bin_data->dbg_file);
> + if (!e->bin_data.dbg_file)
> + goto error;
> + }
> + if (bin_data->build_id) {
> + e->bin_data.build_id = zmalloc(bin_data->build_id_len);
> + if (!e->bin_data.build_id)
> + goto error;
> + memcpy(e->bin_data.build_id, bin_data->build_id,
> + bin_data->build_id_len);
> + }
> + e->bin_data.base_addr_ptr = bin_data->base_addr_ptr;
> + memcpy(e->bin_data.resolved_path, bin_data->resolved_path, PATH_MAX);
> + e->bin_data.memsz = bin_data->memsz;
> + e->bin_data.build_id_len = bin_data->build_id_len;
> + e->bin_data.vdso = bin_data->vdso;
> + e->bin_data.crc = bin_data->crc;
> + e->bin_data.is_pic = bin_data->is_pic;
> + e->bin_data.has_build_id = bin_data->has_build_id;
> + e->bin_data.has_debug_link = bin_data->has_debug_link;
> + return e;
> +
> +error:
> + free(e->bin_data.build_id);
> + free(e->bin_data.dbg_file);
> + free(e);
> + return NULL;
> +}
> +
> +static
> +void free_dl_node(struct lttng_ust_dl_node *e)
> +{
> + free(e->bin_data.build_id);
> + free(e->bin_data.dbg_file);
> + free(e);
> +}
> +
> +/* Return 0 if same, nonzero if not. */
> +static
> +int compare_bin_data(const struct bin_info_data *a,
> + const struct bin_info_data *b)
> +{
> + if (a->base_addr_ptr != b->base_addr_ptr)
> + return -1;
> + if (strcmp(a->resolved_path, b->resolved_path) != 0)
> + return -1;
> + if (a->dbg_file && !b->dbg_file)
> + return -1;
> + if (!a->dbg_file && b->dbg_file)
> + return -1;
> + if (a->dbg_file && strcmp(a->dbg_file, b->dbg_file) != 0)
> + return -1;
> + if (a->build_id && !b->build_id)
> + return -1;
> + if (!a->build_id && b->build_id)
> + return -1;
> + if (a->build_id_len != b->build_id_len)
> + return -1;
> + if (a->build_id &&
> + memcmp(a->build_id, b->build_id, a->build_id_len) != 0)
> + return -1;
> + if (a->memsz != b->memsz)
> + return -1;
> + if (a->vdso != b->vdso)
> + return -1;
> + if (a->crc != b->crc)
> + return -1;
> + if (a->is_pic != b->is_pic)
> + return -1;
> + if (a->has_build_id != b->has_build_id)
> + return -1;
> + if (a->has_debug_link != b->has_debug_link)
> + return -1;
> + return 0;
> +}
> +
> +static
> +struct lttng_ust_dl_node *find_or_create_dl_node(struct bin_info_data
> *bin_data)
> +{
> + struct cds_hlist_head *head;
> + struct lttng_ust_dl_node *e;
> + unsigned int hash;
> + bool found = false;
> +
> + hash = jhash(&bin_data->base_addr_ptr,
> + sizeof(bin_data->base_addr_ptr), 0);
> + head = &dl_state_table[hash & (UST_DL_STATE_TABLE_SIZE - 1)];
> + cds_hlist_for_each_entry_2(e, head, node) {
> + if (compare_bin_data(&e->bin_data, bin_data) != 0)
> + continue;
> + found = true;
> + break;
> + }
> + if (!found) {
> + /* Create */
> + e = alloc_dl_node(bin_data);
> + if (!e)
> + return NULL;
> + cds_hlist_add_head(&e->node, head);
> + }
> + return e;
> +}
> +
> +static
> +void remove_dl_node(struct lttng_ust_dl_node *e)
> +{
> + cds_hlist_del(&e->node);
> +}
> +
> /*
> * Trace statedump event into all sessions owned by the caller thread
> * for which statedump is pending.
> */
> static
> -int trace_statedump_event(tracepoint_cb tp_cb, void *owner, void *priv)
> +void trace_statedump_event(tracepoint_cb tp_cb, void *owner, void *priv)
> {
> struct cds_list_head *sessionsp;
> struct lttng_session *session;
> @@ -76,7 +211,6 @@ int trace_statedump_event(tracepoint_cb tp_cb, void *owner,
> void *priv)
> continue;
> tp_cb(session, priv);
> }
> - return 0;
> }
>
> static
> @@ -165,9 +299,22 @@ end:
> }
>
> static
> -int trace_baddr(struct bin_info_data *bin_data)
> +void trace_baddr(struct bin_info_data *bin_data, void *owner)
> +{
> + trace_statedump_event(trace_bin_info_cb, owner, bin_data);
> +
> + if (bin_data->has_build_id)
> + trace_statedump_event(trace_build_id_cb, owner, bin_data);
> +
> + if (bin_data->has_debug_link)
> + trace_statedump_event(trace_debug_link_cb, owner, bin_data);
> +}
> +
> +static
> +int extract_baddr(struct bin_info_data *bin_data)
> {
> int ret = 0;
> + struct lttng_ust_dl_node *e;
>
> if (!bin_data->vdso) {
> ret = get_elf_info(bin_data);
> @@ -180,73 +327,146 @@ int trace_baddr(struct bin_info_data *bin_data)
> bin_data->has_debug_link = 0;
> }
>
> - ret = trace_statedump_event(trace_bin_info_cb, bin_data->owner,
> - bin_data);
> - if (ret) {
> + e = find_or_create_dl_node(bin_data);
> + if (!e) {
> + ret = -1;
> goto end;
> }
> -
> - if (bin_data->has_build_id) {
> - ret = trace_statedump_event(
> - trace_build_id_cb, bin_data->owner, bin_data);
> - free(bin_data->build_id);
> - if (ret) {
> - goto end;
> - }
> - }
> -
> - if (bin_data->has_debug_link) {
> - ret = trace_statedump_event(
> - trace_debug_link_cb, bin_data->owner, bin_data);
> - free(bin_data->dbg_file);
> - if (ret) {
> - goto end;
> - }
> - }
> -
> + e->marked = true;
> end:
> + free(bin_data->build_id);
> + bin_data->build_id = NULL;
> + free(bin_data->dbg_file);
> + bin_data->dbg_file = NULL;
> return ret;
> }
>
> static
> -int trace_statedump_start(void *owner)
> +void trace_statedump_start(void *owner)
> {
> - return trace_statedump_event(trace_start_cb, owner, NULL);
> + trace_statedump_event(trace_start_cb, owner, NULL);
> }
>
> static
> -int trace_statedump_end(void *owner)
> +void trace_statedump_end(void *owner)
> {
> - return trace_statedump_event(trace_end_cb, owner, NULL);
> + trace_statedump_event(trace_end_cb, owner, NULL);
> }
>
> static
> -int extract_bin_info_events(struct dl_phdr_info *info, size_t size, void
> *_data)
> +void iter_begin(struct dl_iterate_data *data)
> {
> - int j, ret = 0;
> - struct dl_iterate_data *data = _data;
> + unsigned int i;
>
> /*
> * UST lock nests within dynamic loader lock.
> *
> - * Hold this lock across handling of the entire module to
> + * Hold this lock across handling of the module listing to
> * protect memory allocation at early process start, due to
> * interactions with libc-wrapper lttng malloc instrumentation.
> */
> if (ust_lock()) {
> - goto end;
> + data->cancel = true;
> + return;
> }
>
> + /* Ensure all entries are unmarked. */
> + for (i = 0; i < UST_DL_STATE_TABLE_SIZE; i++) {
> + struct cds_hlist_head *head;
> + struct lttng_ust_dl_node *e;
> +
> + head = &dl_state_table[i];
> + cds_hlist_for_each_entry_2(e, head, node)
> + assert(!e->marked);
> + }
> +}
> +
> +static
> +void trace_lib_load(const struct bin_info_data *bin_data, void *ip)
> +{
> + tracepoint(lttng_ust_lib, load,
> + ip, bin_data->base_addr_ptr, bin_data->resolved_path,
> + bin_data->memsz, bin_data->has_build_id,
> + bin_data->has_debug_link);
> +
> + if (bin_data->has_build_id) {
> + tracepoint(lttng_ust_lib, build_id,
> + ip, bin_data->base_addr_ptr, bin_data->build_id,
> + bin_data->build_id_len);
> + }
> +
> + if (bin_data->has_debug_link) {
> + tracepoint(lttng_ust_lib, debug_link,
> + ip, bin_data->base_addr_ptr, bin_data->dbg_file,
> + bin_data->crc);
> + }
> +}
> +
> +static
> +void trace_lib_unload(const struct bin_info_data *bin_data, void *ip)
> +{
> + tracepoint(lttng_ust_lib, unload, ip, bin_data->base_addr_ptr);
> +}
> +
> +static
> +void iter_end(struct dl_iterate_data *data, void *ip)
> +{
> + unsigned int i;
> +
> + /*
> + * Iterate on hash table.
> + * For each marked, traced, do nothing.
> + * For each marked, not traced, trace lib open event. traced = true.
> + * For each unmarked, traced, trace lib close event. remove node.
> + * For each unmarked, not traced, remove node.
> + */
> + for (i = 0; i < UST_DL_STATE_TABLE_SIZE; i++) {
> + struct cds_hlist_head *head;
> + struct lttng_ust_dl_node *e;
> +
> + head = &dl_state_table[i];
> + cds_hlist_for_each_entry_2(e, head, node) {
> + if (e->marked) {
> + if (!e->traced) {
> + trace_lib_load(&e->bin_data, ip);
> + e->traced = true;
> + }
> + } else {
> + if (e->traced)
> + trace_lib_unload(&e->bin_data, ip);
> + remove_dl_node(e);
> + free_dl_node(e);
> + }
> + e->marked = false;
> + }
> + }
> + ust_unlock();
> +}
> +
> +static
> +int extract_bin_info_events(struct dl_phdr_info *info, size_t size, void
> *_data)
> +{
> + int j, ret = 0;
> + struct dl_iterate_data *data = _data;
> +
> + if (data->first) {
> + iter_begin(data);
> + data->first = false;
> + }
> +
> + if (data->cancel)
> + goto end;
> +
> for (j = 0; j < info->dlpi_phnum; j++) {
> struct bin_info_data bin_data;
> - char resolved_path[PATH_MAX];
> - void *base_addr_ptr;
>
> if (info->dlpi_phdr[j].p_type != PT_LOAD)
> continue;
>
> + memset(&bin_data, 0, sizeof(bin_data));
> +
> /* Calculate virtual memory address of the loadable segment */
> - base_addr_ptr = (void *) info->dlpi_addr +
> + bin_data.base_addr_ptr = (void *) info->dlpi_addr +
> info->dlpi_phdr[j].p_vaddr;
>
> if ((info->dlpi_name == NULL || info->dlpi_name[0] == 0)) {
> @@ -264,15 +484,16 @@ int extract_bin_info_events(struct dl_phdr_info *info,
> size_t size, void *_data)
> * executable's full path.
> */
> path_len = readlink("/proc/self/exe",
> - resolved_path,
> + bin_data.resolved_path,
> PATH_MAX - 1);
> if (path_len <= 0)
> break;
>
> - resolved_path[path_len] = '\0';
> + bin_data.resolved_path[path_len] = '\0';
> bin_data.vdso = 0;
> } else {
> - snprintf(resolved_path, PATH_MAX - 1, "[vdso]");
> + snprintf(bin_data.resolved_path,
> + PATH_MAX - 1, "[vdso]");
> bin_data.vdso = 1;
> }
> } else {
> @@ -281,8 +502,10 @@ int extract_bin_info_events(struct dl_phdr_info *info,
> size_t size, void *_data)
> * the path to the binary really exists. If not,
> * treat as vdso and use dlpi_name as 'path'.
> */
> - if (!realpath(info->dlpi_name, resolved_path)) {
> - snprintf(resolved_path, PATH_MAX - 1, "[%s]",
> + if (!realpath(info->dlpi_name,
> + bin_data.resolved_path)) {
> + snprintf(bin_data.resolved_path,
> + PATH_MAX - 1, "[%s]",
> info->dlpi_name);
> bin_data.vdso = 1;
> } else {
> @@ -290,39 +513,73 @@ int extract_bin_info_events(struct dl_phdr_info *info,
> size_t size, void *_data)
> }
> }
>
> - bin_data.owner = data->owner;
> - bin_data.base_addr_ptr = base_addr_ptr;
> - bin_data.resolved_path = resolved_path;
> - ret = trace_baddr(&bin_data);
> + ret = extract_baddr(&bin_data);
> break;
> }
> end:
> - ust_unlock();
> return ret;
> }
>
> -/*
> - * Generate a statedump of base addresses of all shared objects loaded
> - * by the traced application, as well as for the application's
> - * executable itself.
> - */
> static
> -int do_baddr_statedump(void *owner)
> +void ust_dl_table_statedump(void *owner)
> +{
> + unsigned int i;
> +
> + if (ust_lock())
> + goto end;
> +
> + /* Statedump each traced table entry into session for owner. */
> + for (i = 0; i < UST_DL_STATE_TABLE_SIZE; i++) {
> + struct cds_hlist_head *head;
> + struct lttng_ust_dl_node *e;
> +
> + head = &dl_state_table[i];
> + cds_hlist_for_each_entry_2(e, head, node) {
> + if (e->traced)
> + trace_baddr(&e->bin_data, owner);
> + }
> + }
> +
> +end:
> + ust_unlock();
> +}
> +
> +void lttng_ust_dl_update(void *ip)
> {
> struct dl_iterate_data data;
>
> if (getenv("LTTNG_UST_WITHOUT_BADDR_STATEDUMP"))
> - return 0;
> + return;
>
> - data.owner = owner;
> data.exec_found = 0;
> + data.first = true;
> + data.cancel = false;
> /*
> * Iterate through the list of currently loaded shared objects and
> - * generate events for loadable segments using
> + * generate tables entries for loadable segments using
> * extract_bin_info_events.
> + * Removed libraries are detected by mark-and-sweep: marking is
> + * done in the iteration over libraries, and sweeping is
> + * performed by iter_end().
> */
> dl_iterate_phdr(extract_bin_info_events, &data);
> + if (data.first)
> + iter_begin(&data);
> + iter_end(&data, ip);
> +}
>
> +/*
> + * Generate a statedump of base addresses of all shared objects loaded
> + * by the traced application, as well as for the application's
> + * executable itself.
> + */
> +static
> +int do_baddr_statedump(void *owner)
> +{
> + if (getenv("LTTNG_UST_WITHOUT_BADDR_STATEDUMP"))
> + return 0;
> + lttng_ust_dl_update(LTTNG_UST_CALLER_IP());
> + ust_dl_table_statedump(owner);
> return 0;
> }
>
> @@ -348,6 +605,23 @@ void lttng_ust_statedump_init(void)
> __tracepoints__init();
> __tracepoints__ptrs_init();
> __lttng_events_init__lttng_ust_statedump();
> + lttng_ust_dl_update(LTTNG_UST_CALLER_IP());
> +}
> +
> +static
> +void ust_dl_state_destroy(void)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < UST_DL_STATE_TABLE_SIZE; i++) {
> + struct cds_hlist_head *head;
> + struct lttng_ust_dl_node *e, *tmp;
> +
> + head = &dl_state_table[i];
> + cds_hlist_for_each_entry_safe_2(e, tmp, head, node)
> + free_dl_node(e);
> + CDS_INIT_HLIST_HEAD(head);
> + }
> }
>
> void lttng_ust_statedump_destroy(void)
> @@ -355,4 +629,5 @@ void lttng_ust_statedump_destroy(void)
> __lttng_events_exit__lttng_ust_statedump();
> __tracepoints__ptrs_destroy();
> __tracepoints__destroy();
> + ust_dl_state_destroy();
> }
> --
> 2.1.4
--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com
More information about the lttng-dev
mailing list