Move to kernel style SPDX license identifiers
[lttng-ust.git] / liblttng-ust / lttng-ust-statedump.c
index b160e197376f70116ad6a34e304f399a92daeea0..4387cab4931ad8221b59d6d99df63eaff2904f7b 100644 (file)
@@ -1,51 +1,45 @@
 /*
- * Copyright (C) 2013  Paul Woegerer <paul_woegerer@mentor.com>
- * Copyright (C) 2015  Antoine Busque <abusque@efficios.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ * 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>
  */
 
 #define _LGPL_SOURCE
-#define _GNU_SOURCE
-
 #include <link.h>
 #include <limits.h>
 #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"
+#include "getenv.h"
+#include "compat.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 soinfo_data {
-       void *owner;
+struct bin_info_data {
        void *base_addr_ptr;
-       const char *resolved_path;
+       char resolved_path[PATH_MAX];
        char *dbg_file;
        uint8_t *build_id;
        uint64_t memsz;
@@ -53,16 +47,147 @@ struct soinfo_data {
        int vdso;
        uint32_t crc;
        uint8_t is_pic;
+       uint8_t has_build_id;
+       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;
@@ -75,37 +200,45 @@ int trace_statedump_event(tracepoint_cb tp_cb, void *owner, void *priv)
                        continue;
                tp_cb(session, priv);
        }
-       return 0;
 }
 
 static
-void trace_soinfo_cb(struct lttng_session *session, void *priv)
+void trace_bin_info_cb(struct lttng_session *session, void *priv)
 {
-       struct soinfo_data *so_data = (struct soinfo_data *) priv;
+       struct bin_info_data *bin_data = (struct bin_info_data *) priv;
 
-       tracepoint(lttng_ust_statedump, soinfo,
-               session, so_data->base_addr_ptr,
-               so_data->resolved_path, so_data->memsz, so_data->is_pic);
+       tracepoint(lttng_ust_statedump, bin_info,
+               session, bin_data->base_addr_ptr,
+               bin_data->resolved_path, bin_data->memsz,
+               bin_data->is_pic, bin_data->has_build_id,
+               bin_data->has_debug_link);
 }
 
 static
 void trace_build_id_cb(struct lttng_session *session, void *priv)
 {
-       struct soinfo_data *so_data = (struct soinfo_data *) priv;
+       struct bin_info_data *bin_data = (struct bin_info_data *) priv;
 
        tracepoint(lttng_ust_statedump, build_id,
-               session, so_data->base_addr_ptr,
-               so_data->build_id, so_data->build_id_len);
+               session, bin_data->base_addr_ptr,
+               bin_data->build_id, bin_data->build_id_len);
 }
 
 static
 void trace_debug_link_cb(struct lttng_session *session, void *priv)
 {
-       struct soinfo_data *so_data = (struct soinfo_data *) priv;
+       struct bin_info_data *bin_data = (struct bin_info_data *) priv;
 
        tracepoint(lttng_ust_statedump, debug_link,
-               session, so_data->base_addr_ptr,
-               so_data->dbg_file, so_data->crc);
+               session, bin_data->base_addr_ptr,
+               bin_data->dbg_file, bin_data->crc);
+}
+
+static
+void procname_cb(struct lttng_session *session, void *priv)
+{
+       char *procname = (char *) priv;
+       tracepoint(lttng_ust_statedump, procname, session, procname);
 }
 
 static
@@ -121,34 +254,40 @@ void trace_end_cb(struct lttng_session *session, void *priv)
 }
 
 static
-int get_elf_info(struct soinfo_data *so_data, int *has_build_id,
-               int *has_debug_link) {
+int get_elf_info(struct bin_info_data *bin_data)
+{
        struct lttng_ust_elf *elf;
-       int ret = 0;
+       int ret = 0, found;
 
-       elf = lttng_ust_elf_create(so_data->resolved_path);
+       elf = lttng_ust_elf_create(bin_data->resolved_path);
        if (!elf) {
                ret = -1;
                goto end;
        }
 
-       ret = lttng_ust_elf_get_memsz(elf, &so_data->memsz);
+       ret = lttng_ust_elf_get_memsz(elf, &bin_data->memsz);
        if (ret) {
                goto end;
        }
 
-       ret = lttng_ust_elf_get_build_id(elf, &so_data->build_id,
-                                       &so_data->build_id_len, has_build_id);
+       found = 0;
+       ret = lttng_ust_elf_get_build_id(elf, &bin_data->build_id,
+                                       &bin_data->build_id_len,
+                                       &found);
        if (ret) {
                goto end;
        }
-       ret = lttng_ust_elf_get_debug_link(elf, &so_data->dbg_file,
-                                       &so_data->crc, has_debug_link);
+       bin_data->has_build_id = !!found;
+       found = 0;
+       ret = lttng_ust_elf_get_debug_link(elf, &bin_data->dbg_file,
+                                       &bin_data->crc,
+                                       &found);
        if (ret) {
                goto end;
        }
+       bin_data->has_debug_link = !!found;
 
-       so_data->is_pic = lttng_ust_elf_is_pic(elf);
+       bin_data->is_pic = lttng_ust_elf_is_pic(elf);
 
 end:
        lttng_ust_elf_destroy(elf);
@@ -156,85 +295,177 @@ end:
 }
 
 static
-int trace_baddr(struct soinfo_data *so_data)
+void trace_baddr(struct bin_info_data *bin_data, void *owner)
 {
-       int ret = 0, has_build_id = 0, has_debug_link = 0;
+       trace_statedump_event(trace_bin_info_cb, owner, bin_data);
 
-       if (!so_data->vdso) {
-               ret = get_elf_info(so_data, &has_build_id, &has_debug_link);
-               if (ret) {
-                       goto end;
-               }
-       } else {
-               so_data->memsz = 0;
-       }
+       if (bin_data->has_build_id)
+               trace_statedump_event(trace_build_id_cb, owner, bin_data);
 
-       ret = trace_statedump_event(trace_soinfo_cb, so_data->owner, so_data);
-       if (ret) {
-               goto end;
-       }
+       if (bin_data->has_debug_link)
+               trace_statedump_event(trace_debug_link_cb, owner, bin_data);
+}
 
-       if (has_build_id) {
-               ret = trace_statedump_event(
-                       trace_build_id_cb, so_data->owner, so_data);
-               free(so_data->build_id);
-               if (ret) {
-                       goto end;
-               }
-       }
+static
+int extract_baddr(struct bin_info_data *bin_data)
+{
+       int ret = 0;
+       struct lttng_ust_dl_node *e;
 
-       if (has_debug_link) {
-               ret = trace_statedump_event(
-                       trace_debug_link_cb, so_data->owner, so_data);
-               free(so_data->dbg_file);
+       if (!bin_data->vdso) {
+               ret = get_elf_info(bin_data);
                if (ret) {
                        goto end;
                }
+       } else {
+               bin_data->memsz = 0;
+               bin_data->has_build_id = 0;
+               bin_data->has_debug_link = 0;
        }
 
+       e = find_or_create_dl_node(bin_data);
+       if (!e) {
+               ret = -1;
+               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_soinfo_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()) {
+               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;
+
+       if (data->cancel)
                goto end;
+       /*
+        * 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;
+                               }
+                               e->marked = false;
+                       } else {
+                               if (e->traced)
+                                       trace_lib_unload(&e->bin_data, ip);
+                               remove_dl_node(e);
+                               free_dl_node(e);
+                       }
+               }
+       }
+end:
+       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 soinfo_data so_data;
-               char resolved_path[PATH_MAX];
-               void *base_addr_ptr;
+               struct bin_info_data bin_data;
 
                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)) {
@@ -252,43 +483,96 @@ int extract_soinfo_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';
-                               so_data.vdso = 0;
+                               bin_data.resolved_path[path_len] = '\0';
+                               bin_data.vdso = 0;
                        } else {
-                               snprintf(resolved_path, PATH_MAX - 1, "[vdso]");
-                               so_data.vdso = 1;
+                               snprintf(bin_data.resolved_path,
+                                       PATH_MAX - 1, "[vdso]");
+                               bin_data.vdso = 1;
                        }
                } else {
                        /*
                         * For regular dl_phdr_info entries check if
-                        * the path to the SO really exists. If not,
+                        * 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);
-                               so_data.vdso = 1;
+                               bin_data.vdso = 1;
                        } else {
-                               so_data.vdso = 0;
+                               bin_data.vdso = 0;
                        }
                }
 
-               so_data.owner = data->owner;
-               so_data.base_addr_ptr = base_addr_ptr;
-               so_data.resolved_path = resolved_path;
-               ret = trace_baddr(&so_data);
+               ret = extract_baddr(&bin_data);
                break;
        }
 end:
-       ust_unlock();
        return ret;
 }
 
+static
+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 (lttng_getenv("LTTNG_UST_WITHOUT_BADDR_STATEDUMP"))
+               return;
+
+       /*
+        * Fixup lttng-ust TLS when called from dlopen/dlclose
+        * instrumentation.
+        */
+       lttng_ust_fixup_tls();
+
+       data.exec_found = 0;
+       data.first = true;
+       data.cancel = false;
+       /*
+        * Iterate through the list of currently loaded shared objects and
+        * 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
@@ -297,20 +581,20 @@ end:
 static
 int do_baddr_statedump(void *owner)
 {
-       struct dl_iterate_data data;
-
-       if (getenv("LTTNG_UST_WITHOUT_BADDR_STATEDUMP"))
+       if (lttng_getenv("LTTNG_UST_WITHOUT_BADDR_STATEDUMP"))
                return 0;
+       lttng_ust_dl_update(LTTNG_UST_CALLER_IP());
+       ust_dl_table_statedump(owner);
+       return 0;
+}
 
-       data.owner = owner;
-       data.exec_found = 0;
-       /*
-        * Iterate through the list of currently loaded shared objects and
-        * generate events for loadable segments using
-        * extract_soinfo_events.
-        */
-       dl_iterate_phdr(extract_soinfo_events, &data);
+static
+int do_procname_statedump(void *owner)
+{
+       if (lttng_getenv("LTTNG_UST_WITHOUT_PROCNAME_STATEDUMP"))
+               return 0;
 
+       trace_statedump_event(procname_cb, owner, lttng_ust_sockinfo_get_procname(owner));
        return 0;
 }
 
@@ -321,12 +605,23 @@ int do_baddr_statedump(void *owner)
  * session, statedumps from different processes may be
  * interleaved. The vpid context should be used to identify which
  * events belong to which process.
+ *
+ * Grab the ust_lock outside of the RCU read-side lock because we
+ * perform synchronize_rcu with the ust_lock held, which can trigger
+ * deadlocks otherwise.
  */
 int do_lttng_ust_statedump(void *owner)
 {
+       ust_lock_nocheck();
        trace_statedump_start(owner);
+       ust_unlock();
+
+       do_procname_statedump(owner);
        do_baddr_statedump(owner);
+
+       ust_lock_nocheck();
        trace_statedump_end(owner);
+       ust_unlock();
 
        return 0;
 }
@@ -336,6 +631,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)
@@ -343,4 +655,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.030843 seconds and 4 git commands to generate.