From: Mathieu Desnoyers Date: Sat, 27 Sep 2014 20:20:31 +0000 (-0400) Subject: Implement PID tracking X-Git-Tag: v2.7.0-rc1~80 X-Git-Url: http://git.lttng.org/?p=lttng-modules.git;a=commitdiff_plain;h=e0130fabb0cbec05e19fcc7fe955fe9d8d8bc91e Implement PID tracking Signed-off-by: Mathieu Desnoyers --- diff --git a/Makefile b/Makefile index eeffdfe9..23049ca3 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,8 @@ lttng-tracer-objs := lttng-events.o lttng-abi.o \ lttng-context-vtid.o lttng-context-ppid.o \ lttng-context-vppid.o lttng-calibrate.o \ lttng-context-hostname.o wrapper/random.o \ - probes/lttng.o wrapper/trace-clock.o + probes/lttng.o wrapper/trace-clock.o \ + lttng-tracker-pid.o obj-m += lttng-statedump.o lttng-statedump-objs := lttng-statedump-impl.o wrapper/irqdesc.o \ diff --git a/lttng-abi.c b/lttng-abi.c index 5823a1db..f3d98fc1 100644 --- a/lttng-abi.c +++ b/lttng-abi.c @@ -423,6 +423,10 @@ fd_error: * Disables tracing for a session (strong disable) * LTTNG_KERNEL_METADATA * Returns a LTTng metadata file descriptor + * LTTNG_KERNEL_SESSION_TRACK_PID + * Add PID to session tracker + * LTTNG_KERNEL_SESSION_UNTRACK_PID + * Remove PID from session tracker * * The returned channel will be deleted when its file descriptor is closed. */ @@ -502,6 +506,10 @@ long lttng_session_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return lttng_abi_create_channel(file, &chan_param, METADATA_CHANNEL); } + case LTTNG_KERNEL_SESSION_TRACK_PID: + return lttng_session_track_pid(session, (int) arg); + case LTTNG_KERNEL_SESSION_UNTRACK_PID: + return lttng_session_untrack_pid(session, (int) arg); default: return -ENOIOCTLCMD; } diff --git a/lttng-abi.h b/lttng-abi.h index b63ead8d..bcea06e3 100644 --- a/lttng-abi.h +++ b/lttng-abi.h @@ -185,6 +185,10 @@ struct lttng_kernel_context { _IOW(0xF6, 0x55, struct lttng_kernel_channel) #define LTTNG_KERNEL_SESSION_START _IO(0xF6, 0x56) #define LTTNG_KERNEL_SESSION_STOP _IO(0xF6, 0x57) +#define LTTNG_KERNEL_SESSION_TRACK_PID \ + _IOR(0xF6, 0x58, int32_t) +#define LTTNG_KERNEL_SESSION_UNTRACK_PID \ + _IOR(0xF6, 0x59, int32_t) /* Channel FD ioctl */ #define LTTNG_KERNEL_STREAM _IO(0xF6, 0x62) diff --git a/lttng-events.c b/lttng-events.c index 135c8c52..8a48c6b5 100644 --- a/lttng-events.c +++ b/lttng-events.c @@ -147,6 +147,8 @@ void lttng_session_destroy(struct lttng_session *session) } list_for_each_entry(metadata_stream, &session->metadata_cache->metadata_stream, list) _lttng_metadata_channel_hangup(metadata_stream); + if (session->pid_tracker) + lttng_pid_tracker_destroy(session->pid_tracker); kref_put(&session->metadata_cache->refcount, metadata_cache_destroy); list_del(&session->list); mutex_unlock(&sessions_mutex); @@ -586,6 +588,78 @@ void _lttng_event_destroy(struct lttng_event *event) kmem_cache_free(event_cache, event); } +int lttng_session_track_pid(struct lttng_session *session, int pid) +{ + int ret; + + if (pid < -1) + return -EINVAL; + mutex_lock(&sessions_mutex); + if (pid == -1) { + /* track all pids: destroy tracker. */ + if (session->pid_tracker) { + struct lttng_pid_tracker *lpf; + + lpf = session->pid_tracker; + rcu_assign_pointer(session->pid_tracker, NULL); + synchronize_trace(); + lttng_pid_tracker_destroy(lpf); + } + ret = 0; + } else { + if (!session->pid_tracker) { + struct lttng_pid_tracker *lpf; + + lpf = lttng_pid_tracker_create(); + if (!lpf) { + ret = -ENOMEM; + goto unlock; + } + ret = lttng_pid_tracker_add(lpf, pid); + rcu_assign_pointer(session->pid_tracker, lpf); + } else { + ret = lttng_pid_tracker_add(session->pid_tracker, pid); + } + } +unlock: + mutex_unlock(&sessions_mutex); + return ret; +} + +int lttng_session_untrack_pid(struct lttng_session *session, int pid) +{ + int ret; + + if (pid < -1) + return -EINVAL; + mutex_lock(&sessions_mutex); + if (pid == -1) { + /* untrack all pids: replace by empty tracker. */ + struct lttng_pid_tracker *old_lpf = session->pid_tracker; + struct lttng_pid_tracker *lpf; + + lpf = lttng_pid_tracker_create(); + if (!lpf) { + ret = -ENOMEM; + goto unlock; + } + rcu_assign_pointer(session->pid_tracker, lpf); + synchronize_trace(); + if (old_lpf) + lttng_pid_tracker_destroy(old_lpf); + ret = 0; + } else { + if (!session->pid_tracker) { + ret = -ENOENT; + goto unlock; + } + ret = lttng_pid_tracker_del(session->pid_tracker, pid); + } +unlock: + mutex_unlock(&sessions_mutex); + return ret; +} + /* * Serialize at most one packet worth of metadata into a metadata * channel. diff --git a/lttng-events.h b/lttng-events.h index 118ea3b0..7d331459 100644 --- a/lttng-events.h +++ b/lttng-events.h @@ -324,6 +324,18 @@ struct lttng_metadata_stream { struct mutex lock; }; + +/* + * struct lttng_pid_tracker declared in header due to deferencing of *v + * in RCU_INITIALIZER(v). + */ +#define LTTNG_PID_HASH_BITS 6 +#define LTTNG_PID_TABLE_SIZE (1 << LTTNG_PID_HASH_BITS) + +struct lttng_pid_tracker { + struct hlist_head pid_hash[LTTNG_PID_TABLE_SIZE]; +}; + struct lttng_session { int active; /* Is trace session active ? */ int been_active; /* Has trace session been active ? */ @@ -334,6 +346,7 @@ struct lttng_session { unsigned int free_chan_id; /* Next chan ID to allocate */ uuid_le uuid; /* Trace session unique ID */ struct lttng_metadata_cache *metadata_cache; + struct lttng_pid_tracker *pid_tracker; unsigned int metadata_dumped:1; }; @@ -399,6 +412,15 @@ void lttng_probes_exit(void); int lttng_metadata_output_channel(struct lttng_metadata_stream *stream, struct channel *chan); +struct lttng_pid_tracker *lttng_pid_tracker_create(void); +void lttng_pid_tracker_destroy(struct lttng_pid_tracker *lpf); +bool lttng_pid_tracker_lookup(struct lttng_pid_tracker *lpf, int pid); +int lttng_pid_tracker_add(struct lttng_pid_tracker *lpf, int pid); +int lttng_pid_tracker_del(struct lttng_pid_tracker *lpf, int pid); + +int lttng_session_track_pid(struct lttng_session *session, int pid); +int lttng_session_untrack_pid(struct lttng_session *session, int pid); + #if defined(CONFIG_HAVE_SYSCALL_TRACEPOINTS) int lttng_syscalls_register(struct lttng_channel *chan, void *filter); int lttng_syscalls_unregister(struct lttng_channel *chan); diff --git a/lttng-tracker-pid.c b/lttng-tracker-pid.c new file mode 100644 index 00000000..f8f0a51d --- /dev/null +++ b/lttng-tracker-pid.c @@ -0,0 +1,157 @@ +/* + * lttng-tracker-pid.c + * + * LTTng Process ID trackering. + * + * Copyright (C) 2014 Mathieu Desnoyers + * + * 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; only + * version 2.1 of the License. + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wrapper/tracepoint.h" +#include "lttng-events.h" + +/* + * Hash table is allocated and freed when there are no possible + * concurrent lookups (ensured by the alloc/free caller). However, + * there can be concurrent RCU lookups vs add/del operations. + * + * Concurrent updates of the PID hash table are forbidden: the caller + * must ensure mutual exclusion. This is currently done by holding the + * sessions_mutex across calls to create, destroy, add, and del + * functions of this API. + */ +struct lttng_pid_hash_node { + struct hlist_node hlist; + int pid; +}; + +/* + * Lookup performed from RCU read-side critical section (RCU sched), + * protected by preemption off at the tracepoint call site. + * Return 1 if found, 0 if not found. + */ +bool lttng_pid_tracker_lookup(struct lttng_pid_tracker *lpf, int pid) +{ + struct hlist_head *head; + struct lttng_pid_hash_node *e; + uint32_t hash = hash_32(pid, 32); + + head = &lpf->pid_hash[hash & (LTTNG_PID_TABLE_SIZE - 1)]; + hlist_for_each_entry_rcu_notrace(e, head, hlist) { + if (pid == e->pid) + return 1; /* Found */ + } + return 0; +} +EXPORT_SYMBOL_GPL(lttng_pid_tracker_lookup); + +/* + * Tracker add and del operations support concurrent RCU lookups. + */ +int lttng_pid_tracker_add(struct lttng_pid_tracker *lpf, int pid) +{ + struct hlist_head *head; + struct lttng_pid_hash_node *e; + uint32_t hash = hash_32(pid, 32); + + head = &lpf->pid_hash[hash & (LTTNG_PID_TABLE_SIZE - 1)]; + hlist_for_each_entry(e, head, hlist) { + if (pid == e->pid) + return -EEXIST; + } + e = kmalloc(sizeof(struct lttng_pid_hash_node), GFP_KERNEL); + if (!e) + return -ENOMEM; + e->pid = pid; + hlist_add_head_rcu(&e->hlist, head); + return 0; +} + +static +void pid_tracker_del_node_rcu(struct lttng_pid_hash_node *e) +{ + hlist_del_rcu(&e->hlist); + /* + * We choose to use a heavyweight synchronize on removal here, + * since removal of a PID from the tracker mask is a rare + * operation, and we don't want to use more cache lines than + * what we really need when doing the PID lookups, so we don't + * want to afford adding a rcu_head field to those pid hash + * node. + */ + synchronize_trace(); + kfree(e); +} + +/* + * This removal is only used on destroy, so it does not need to support + * concurrent RCU lookups. + */ +static +void pid_tracker_del_node(struct lttng_pid_hash_node *e) +{ + hlist_del(&e->hlist); + kfree(e); +} + +int lttng_pid_tracker_del(struct lttng_pid_tracker *lpf, int pid) +{ + struct hlist_head *head; + struct lttng_pid_hash_node *e; + uint32_t hash = hash_32(pid, 32); + + head = &lpf->pid_hash[hash & (LTTNG_PID_TABLE_SIZE - 1)]; + /* + * No need of _safe iteration, because we stop traversal as soon + * as we remove the entry. + */ + hlist_for_each_entry(e, head, hlist) { + if (pid == e->pid) { + pid_tracker_del_node_rcu(e); + return 0; + } + } + return -ENOENT; /* Not found */ +} + +struct lttng_pid_tracker *lttng_pid_tracker_create(void) +{ + return kzalloc(sizeof(struct lttng_pid_tracker), GFP_KERNEL); +} + +void lttng_pid_tracker_destroy(struct lttng_pid_tracker *lpf) +{ + int i; + + for (i = 0; i < LTTNG_PID_TABLE_SIZE; i++) { + struct hlist_head *head = &lpf->pid_hash[i]; + struct lttng_pid_hash_node *e; + struct hlist_node *tmp; + + hlist_for_each_entry_safe(e, tmp, head, hlist) + pid_tracker_del_node(e); + } + kfree(lpf); +} diff --git a/probes/lttng-events.h b/probes/lttng-events.h index 22cabbaa..b885fa18 100644 --- a/probes/lttng-events.h +++ b/probes/lttng-events.h @@ -837,6 +837,7 @@ static void __event_probe__##_name(void *__data, _proto) \ struct probe_local_vars { _locvar }; \ struct lttng_event *__event = __data; \ struct lttng_channel *__chan = __event->chan; \ + struct lttng_session *__session = __chan->session; \ struct lib_ring_buffer_ctx __ctx; \ size_t __event_len, __event_align; \ size_t __dynamic_len_idx __attribute__((unused)) = 0; \ @@ -846,15 +847,19 @@ static void __event_probe__##_name(void *__data, _proto) \ struct probe_local_vars __tp_locvar; \ struct probe_local_vars *tp_locvar __attribute__((unused)) = \ &__tp_locvar; \ + struct lttng_pid_tracker *__lpf; \ \ - if (!_TP_SESSION_CHECK(session, __chan->session)) \ + if (!_TP_SESSION_CHECK(session, __session)) \ return; \ - if (unlikely(!ACCESS_ONCE(__chan->session->active))) \ + if (unlikely(!ACCESS_ONCE(__session->active))) \ return; \ if (unlikely(!ACCESS_ONCE(__chan->enabled))) \ return; \ if (unlikely(!ACCESS_ONCE(__event->enabled))) \ return; \ + __lpf = rcu_dereference(__session->pid_tracker); \ + if (__lpf && likely(!lttng_pid_tracker_lookup(__lpf, current->pid))) \ + return; \ _code \ __event_len = __event_get_size__##_name(__dynamic_len, tp_locvar, \ _args); \ @@ -879,6 +884,7 @@ static void __event_probe__##_name(void *__data) \ struct probe_local_vars { _locvar }; \ struct lttng_event *__event = __data; \ struct lttng_channel *__chan = __event->chan; \ + struct lttng_session *__session = __chan->session; \ struct lib_ring_buffer_ctx __ctx; \ size_t __event_len, __event_align; \ size_t __dynamic_len_idx __attribute__((unused)) = 0; \ @@ -888,15 +894,19 @@ static void __event_probe__##_name(void *__data) \ struct probe_local_vars __tp_locvar; \ struct probe_local_vars *tp_locvar __attribute__((unused)) = \ &__tp_locvar; \ + struct lttng_pid_tracker *__lpf; \ \ - if (!_TP_SESSION_CHECK(session, __chan->session)) \ + if (!_TP_SESSION_CHECK(session, __session)) \ return; \ - if (unlikely(!ACCESS_ONCE(__chan->session->active))) \ + if (unlikely(!ACCESS_ONCE(__session->active))) \ return; \ if (unlikely(!ACCESS_ONCE(__chan->enabled))) \ return; \ if (unlikely(!ACCESS_ONCE(__event->enabled))) \ return; \ + __lpf = rcu_dereference(__session->pid_tracker); \ + if (__lpf && likely(!lttng_pid_tracker_lookup(__lpf, current->pid))) \ + return; \ _code \ __event_len = __event_get_size__##_name(__dynamic_len, tp_locvar); \ __event_align = __event_get_align__##_name(tp_locvar); \