Add library load/unload tracking events
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Mon, 4 Jul 2016 19:39:01 +0000 (15:39 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Tue, 5 Jul 2016 20:01:25 +0000 (16:01 -0400)
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@efficios.com>
include/lttng/ust-events.h
liblttng-ust-dl/lttng-ust-dl.c
liblttng-ust/Makefile.am
liblttng-ust/lttng-ust-elf.c
liblttng-ust/lttng-ust-statedump.c

index 039b2ccba1b5db86726ec0a4512e8c52e10c6139..5167ddee50a9de5f526c80545221dab32fbb3246 100644 (file)
@@ -723,6 +723,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);
index e457e7a3cbdfa69697c3794465a82a66681a0ec3..81b5bec470ad91dda754776220b9f5bb9435cf3d 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2013  Paul Woegerer <paul.woegerer@mentor.com>
  * Copyright (C) 2015  Antoine Busque <abusque@efficios.com>
+ * Copyright (C) 2016  Mathieu Desnoyers <mathieu.desnoyers@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;
 }
index 05929be71f61640bba27db03a53ac59d68ae2064..a2a32b4efcedfcd68d45491e1c1c5af939c30f05 100644 (file)
@@ -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 \
index 5f27920e48fa041f1d971677b56c643993064de9..e5b8a08f44141d0fd26ea0c346b43bfcb571c620 100644 (file)
@@ -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
index bcb168be45948fc242c82db913ed1dfe052975fc..c8e508cdf9e9bd477f333ce79046e42625d64922 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2013  Paul Woegerer <paul_woegerer@mentor.com>
  * Copyright (C) 2015  Antoine Busque <abusque@efficios.com>
+ * Copyright (C) 2016  Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #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();
 }
This page took 0.033243 seconds and 4 git commands to generate.