*/
#define _GNU_SOURCE
+#define _LGPL_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "buffer-registry.h"
#include "trace-ust.h"
#include "utils.h"
+#include "ust-app.h"
+#include "agent.h"
/*
* Match function for the events hash table lookup.
return NULL;
}
+/*
+ * Lookup an agent in the session agents hash table by domain type and return
+ * the object if found else NULL.
+ *
+ * RCU read side lock must be acquired before calling and only released
+ * once the agent is no longer in scope or being used.
+ */
+struct agent *trace_ust_find_agent(struct ltt_ust_session *session,
+ enum lttng_domain_type domain_type)
+{
+ struct agent *agt = NULL;
+ struct lttng_ht_node_u64 *node;
+ struct lttng_ht_iter iter;
+ uint64_t key;
+
+ assert(session);
+
+ DBG3("Trace ust agent lookup for domain %d", domain_type);
+
+ key = domain_type;
+
+ lttng_ht_lookup(session->agents, &key, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
+ if (!node) {
+ goto end;
+ }
+ agt = caa_container_of(node, struct agent, node);
+
+end:
+ return agt;
+}
+
/*
* Allocate and initialize a ust session data structure.
*
*/
struct ltt_ust_session *trace_ust_create_session(uint64_t session_id)
{
- int ret;
struct ltt_ust_session *lus;
/* Allocate a new ltt ust session */
/* Alloc UST global domain channels' HT */
lus->domain_global.channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
- ret = agent_init(&lus->agent);
- if (ret < 0) {
- goto error_consumer;
- }
+ /* Alloc agent hash table. */
+ lus->agents = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
lus->consumer = consumer_create_output(CONSUMER_DST_LOCAL);
if (lus->consumer == NULL) {
error_consumer:
ht_cleanup_push(lus->domain_global.channels);
- agent_destroy(&lus->agent);
+ ht_cleanup_push(lus->agents);
free(lus);
error:
return NULL;
/*
* Allocate and initialize a ust event. Set name and event type.
+ * We own filter_expression, filter, and exclusion.
*
* Return pointer to structure or NULL.
*/
struct ltt_ust_event *trace_ust_create_event(struct lttng_event *ev,
char *filter_expression,
struct lttng_filter_bytecode *filter,
- struct lttng_event_exclusion *exclusion)
+ struct lttng_event_exclusion *exclusion,
+ bool internal_event)
{
struct ltt_ust_event *lue;
goto error;
}
+ lue->internal = internal_event;
+
switch (ev->type) {
case LTTNG_EVENT_PROBE:
lue->attr.instrumentation = LTTNG_UST_PROBE;
error_free_event:
free(lue);
error:
+ free(filter_expression);
+ free(filter);
+ free(exclusion);
return NULL;
}
utype = LTTNG_UST_CONTEXT_IP;
break;
case LTTNG_EVENT_CONTEXT_PERF_THREAD_COUNTER:
- utype = LTTNG_UST_CONTEXT_PERF_THREAD_COUNTER;
+ if (!ustctl_has_perf_counters()) {
+ utype = -1;
+ WARN("Perf counters not implemented in UST");
+ } else {
+ utype = LTTNG_UST_CONTEXT_PERF_THREAD_COUNTER;
+ }
break;
default:
ERR("Invalid UST context");
return NULL;
}
+static
+void destroy_pid_tracker_node_rcu(struct rcu_head *head)
+{
+ struct ust_pid_tracker_node *tracker_node =
+ caa_container_of(head, struct ust_pid_tracker_node, node.head);
+ free(tracker_node);
+}
+
+static
+void destroy_pid_tracker_node(struct ust_pid_tracker_node *tracker_node)
+{
+
+ call_rcu(&tracker_node->node.head, destroy_pid_tracker_node_rcu);
+}
+
+static
+int init_pid_tracker(struct ust_pid_tracker *pid_tracker)
+{
+ int ret = 0;
+
+ pid_tracker->ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ if (!pid_tracker->ht) {
+ ret = -1;
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+/*
+ * Teardown pid tracker content, but don't free pid_tracker object.
+ */
+static
+void fini_pid_tracker(struct ust_pid_tracker *pid_tracker)
+{
+ struct ust_pid_tracker_node *tracker_node;
+ struct lttng_ht_iter iter;
+
+ if (!pid_tracker->ht) {
+ return;
+ }
+ rcu_read_lock();
+ cds_lfht_for_each_entry(pid_tracker->ht->ht,
+ &iter.iter, tracker_node, node.node) {
+ int ret = lttng_ht_del(pid_tracker->ht, &iter);
+
+ assert(!ret);
+ destroy_pid_tracker_node(tracker_node);
+ }
+ rcu_read_unlock();
+ ht_cleanup_push(pid_tracker->ht);
+ pid_tracker->ht = NULL;
+}
+
+static
+struct ust_pid_tracker_node *pid_tracker_lookup(
+ struct ust_pid_tracker *pid_tracker, int pid,
+ struct lttng_ht_iter *iter)
+{
+ unsigned long _pid = (unsigned long) pid;
+ struct lttng_ht_node_ulong *node;
+
+ lttng_ht_lookup(pid_tracker->ht, (void *) _pid, iter);
+ node = lttng_ht_iter_get_node_ulong(iter);
+ if (node) {
+ return caa_container_of(node, struct ust_pid_tracker_node,
+ node);
+ } else {
+ return NULL;
+ }
+}
+
+static
+int pid_tracker_add_pid(struct ust_pid_tracker *pid_tracker, int pid)
+{
+ int retval = LTTNG_OK;
+ struct ust_pid_tracker_node *tracker_node;
+ struct lttng_ht_iter iter;
+
+ if (pid < 0) {
+ retval = LTTNG_ERR_INVALID;
+ goto end;
+ }
+ tracker_node = pid_tracker_lookup(pid_tracker, pid, &iter);
+ if (tracker_node) {
+ /* Already exists. */
+ retval = LTTNG_ERR_PID_TRACKED;
+ goto end;
+ }
+ tracker_node = zmalloc(sizeof(*tracker_node));
+ if (!tracker_node) {
+ retval = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ lttng_ht_node_init_ulong(&tracker_node->node, (unsigned long) pid);
+ lttng_ht_add_unique_ulong(pid_tracker->ht, &tracker_node->node);
+end:
+ return retval;
+}
+
+static
+int pid_tracker_del_pid(struct ust_pid_tracker *pid_tracker, int pid)
+{
+ int retval = LTTNG_OK, ret;
+ struct ust_pid_tracker_node *tracker_node;
+ struct lttng_ht_iter iter;
+
+ if (pid < 0) {
+ retval = LTTNG_ERR_INVALID;
+ goto end;
+ }
+ tracker_node = pid_tracker_lookup(pid_tracker, pid, &iter);
+ if (!tracker_node) {
+ /* Not found */
+ retval = LTTNG_ERR_PID_NOT_TRACKED;
+ goto end;
+ }
+ ret = lttng_ht_del(pid_tracker->ht, &iter);
+ assert(!ret);
+
+ destroy_pid_tracker_node(tracker_node);
+end:
+ return retval;
+}
+
+/*
+ * The session lock is held when calling this function.
+ */
+int trace_ust_pid_tracker_lookup(struct ltt_ust_session *session, int pid)
+{
+ struct lttng_ht_iter iter;
+
+ if (!session->pid_tracker.ht) {
+ return 1;
+ }
+ if (pid_tracker_lookup(&session->pid_tracker, pid, &iter)) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Called with the session lock held.
+ */
+int trace_ust_track_pid(struct ltt_ust_session *session, int pid)
+{
+ int retval = LTTNG_OK;
+
+ if (pid == -1) {
+ /* Track all pids: destroy tracker if exists. */
+ if (session->pid_tracker.ht) {
+ fini_pid_tracker(&session->pid_tracker);
+ /* Ensure all apps have session. */
+ ust_app_global_update_all(session);
+ }
+ } else {
+ int ret;
+
+ if (!session->pid_tracker.ht) {
+ /* Create tracker. */
+ if (init_pid_tracker(&session->pid_tracker)) {
+ ERR("Error initializing PID tracker");
+ retval = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ ret = pid_tracker_add_pid(&session->pid_tracker, pid);
+ if (ret != LTTNG_OK) {
+ retval = ret;
+ fini_pid_tracker(&session->pid_tracker);
+ goto end;
+ }
+ /* Remove all apps from session except pid. */
+ ust_app_global_update_all(session);
+ } else {
+ struct ust_app *app;
+
+ ret = pid_tracker_add_pid(&session->pid_tracker, pid);
+ if (ret != LTTNG_OK) {
+ retval = ret;
+ goto end;
+ }
+ /* Add session to application */
+ app = ust_app_find_by_pid(pid);
+ if (app) {
+ ust_app_global_update(session, app);
+ }
+ }
+ }
+end:
+ return retval;
+}
+
+/*
+ * Called with the session lock held.
+ */
+int trace_ust_untrack_pid(struct ltt_ust_session *session, int pid)
+{
+ int retval = LTTNG_OK;
+
+ if (pid == -1) {
+ /* Create empty tracker, replace old tracker. */
+ struct ust_pid_tracker tmp_tracker;
+
+ tmp_tracker = session->pid_tracker;
+ if (init_pid_tracker(&session->pid_tracker)) {
+ ERR("Error initializing PID tracker");
+ retval = LTTNG_ERR_NOMEM;
+ /* Rollback operation. */
+ session->pid_tracker = tmp_tracker;
+ goto end;
+ }
+ fini_pid_tracker(&tmp_tracker);
+
+ /* Remove session from all applications */
+ ust_app_global_update_all(session);
+ } else {
+ int ret;
+ struct ust_app *app;
+
+ if (!session->pid_tracker.ht) {
+ retval = LTTNG_ERR_INVALID;
+ goto end;
+ }
+ /* Remove PID from tracker */
+ ret = pid_tracker_del_pid(&session->pid_tracker, pid);
+ if (ret != LTTNG_OK) {
+ retval = ret;
+ goto end;
+ }
+ /* Remove session from application. */
+ app = ust_app_find_by_pid(pid);
+ if (app) {
+ ust_app_global_update(session, app);
+ }
+ }
+end:
+ return retval;
+}
+
+/*
+ * Called with session lock held.
+ */
+ssize_t trace_ust_list_tracker_pids(struct ltt_ust_session *session,
+ int32_t **_pids)
+{
+ struct ust_pid_tracker_node *tracker_node;
+ struct lttng_ht_iter iter;
+ unsigned long count, i = 0;
+ long approx[2];
+ int32_t *pids;
+ int ret = 0;
+
+ if (!session->pid_tracker.ht) {
+ /* Tracker disabled. Set first entry to -1. */
+ pids = zmalloc(sizeof(*pids));
+ if (!pids) {
+ ret = -1;
+ goto end;
+ }
+ pids[0] = -1;
+ *_pids = pids;
+ return 1;
+ }
+
+ rcu_read_lock();
+ cds_lfht_count_nodes(session->pid_tracker.ht->ht,
+ &approx[0], &count, &approx[1]);
+ pids = zmalloc(sizeof(*pids) * count);
+ if (!pids) {
+ ret = -1;
+ goto end;
+ }
+ cds_lfht_for_each_entry(session->pid_tracker.ht->ht,
+ &iter.iter, tracker_node, node.node) {
+ pids[i++] = tracker_node->node.key;
+ }
+ *_pids = pids;
+ ret = count;
+end:
+ rcu_read_unlock();
+ return ret;
+}
+
/*
* RCU safe free context structure.
*/
*/
void trace_ust_destroy_session(struct ltt_ust_session *session)
{
+ struct agent *agt;
struct buffer_reg_uid *reg, *sreg;
+ struct lttng_ht_iter iter;
assert(session);
/* Cleaning up UST domain */
destroy_domain_global(&session->domain_global);
- agent_destroy(&session->agent);
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(session->agents->ht, &iter.iter, agt, node.node) {
+ int ret = lttng_ht_del(session->agents, &iter);
+
+ assert(!ret);
+ agent_destroy(agt);
+ }
+ rcu_read_unlock();
+
+ ht_cleanup_push(session->agents);
/* Cleanup UID buffer registry object(s). */
cds_list_for_each_entry_safe(reg, sreg, &session->buffer_reg_uid_list,
consumer_destroy_output(session->consumer);
consumer_destroy_output(session->tmp_consumer);
+ fini_pid_tracker(&session->pid_tracker);
+
free(session);
}