diff mbox

[RFC,lttng-ust,1/2] Add library load/unload tracking events

Message ID 1467672828-19184-1-git-send-email-mathieu.desnoyers@efficios.com
State Accepted, archived
Headers show

Commit Message

Mathieu Desnoyers July 4, 2016, 10:53 p.m. UTC
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(-)

Comments

Mathieu Desnoyers July 5, 2016, 8:01 p.m. UTC | #1
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
diff mbox

Patch

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();
 }