#include <urcu/list.h>
#include <urcu/hlist.h>
#include <pthread.h>
-#include <urcu-bp.h>
-#include <urcu/compiler.h>
-#include <urcu/uatomic.h>
#include <uuid/uuid.h>
-#include <lttng/tracepoint.h>
#include <errno.h>
#include <sys/shm.h>
#include <sys/ipc.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <inttypes.h>
+#include <time.h>
+#include "clock.h"
+
+#include <urcu-bp.h>
+#include <urcu/compiler.h>
+#include <urcu/uatomic.h>
+#include <urcu/arch.h>
+
+#include <lttng/tracepoint.h>
#include <lttng/ust-events.h>
-#include <lttng/usterr-signal-safe.h>
+
+#include <usterr-signal-safe.h>
+#include <helper.h>
+#include "error.h"
+
#include "ltt-tracer.h"
#include "ltt-tracer-core.h"
#include "wait.h"
#include "../libringbuffer/shm.h"
-#include <stdint.h>
-#include <stddef.h>
-#include <urcu/arch.h>
#include "jhash.h"
-#include <helper.h>
/*
* The sessions mutex is the centralized mutex across UST tracing
};
static void _ltt_event_destroy(struct ltt_event *event);
+static void _ltt_loglevel_destroy(struct session_loglevel *sl);
+static void _ltt_wildcard_destroy(struct session_wildcard *sw);
static void _ltt_channel_destroy(struct ltt_channel *chan);
static int _ltt_event_unregister(struct ltt_event *event);
static
{
struct cds_hlist_head *head;
struct ust_pending_probe *e;
- size_t name_len = strlen(name) + 1;
- uint32_t hash = jhash(name, name_len - 1, 0);
+ size_t name_len = strlen(name);
+ uint32_t hash;
+ if (name_len > LTTNG_UST_SYM_NAME_LEN - 1) {
+ WARN("Truncating tracepoint name %s which exceeds size limits of %u chars", name, LTTNG_UST_SYM_NAME_LEN - 1);
+ name_len = LTTNG_UST_SYM_NAME_LEN - 1;
+ }
+ hash = jhash(name, name_len, 0);
head = &pending_probe_table[hash & (PENDING_PROBE_HASH_SIZE - 1)];
e = zmalloc(sizeof(struct ust_pending_probe) + name_len);
if (!e)
return -ENOMEM;
- memcpy(&e->name[0], name, name_len);
+ memcpy(&e->name[0], name, name_len + 1);
+ e->name[name_len] = '\0';
cds_hlist_add_head(&e->node, head);
e->event = event;
event->pending_probe = e;
struct cds_hlist_node *node, *p;
struct ust_pending_probe *e;
const char *name = desc->name;
- size_t name_len = strlen(name) + 1;
- uint32_t hash = jhash(name, name_len - 1, 0);
int ret = 0;
+ struct lttng_ust_event event_param;
+ size_t name_len = strlen(name);
+ uint32_t hash;
+
+ /*
+ * For this event, we need to lookup the loglevel. If active (in
+ * the active loglevels hash table), we must create the event.
+ */
+ if (desc->loglevel) {
+ const struct tracepoint_loglevel_entry *ev_ll;
+ struct loglevel_entry *loglevel;
+
+ ev_ll = *desc->loglevel;
+ loglevel = get_loglevel(ev_ll->identifier);
+ if (!loglevel)
+ loglevel = get_loglevel_value(ev_ll->value);
+ if (loglevel) {
+ struct session_loglevel *sl;
+
+ cds_list_for_each_entry(sl, &loglevel->session_list,
+ session_list) {
+ struct ltt_event *ev;
+ int ret;
+
+ memcpy(&event_param, &sl->event_param,
+ sizeof(event_param));
+ memcpy(event_param.name,
+ desc->name,
+ sizeof(event_param.name));
+ /* create event */
+ ret = ltt_event_create(sl->chan,
+ &event_param, NULL,
+ &ev);
+ if (ret) {
+ DBG("Error creating event");
+ continue;
+ }
+ cds_list_add(&ev->loglevel_list,
+ &sl->events);
+ }
+ }
+ }
+ /* Wildcard */
+ {
+ struct wildcard_entry *wildcard;
+
+ wildcard = match_wildcard(desc->name);
+ if (strcmp(desc->name, "lttng_ust:metadata") && wildcard) {
+ struct session_wildcard *sw;
+
+ cds_list_for_each_entry(sw, &wildcard->session_list,
+ session_list) {
+ struct ltt_event *ev;
+ int ret;
+
+ memcpy(&event_param, &sw->event_param,
+ sizeof(event_param));
+ memcpy(event_param.name,
+ desc->name,
+ sizeof(event_param.name));
+ /* create event */
+ ret = ltt_event_create(sw->chan,
+ &event_param, NULL,
+ &ev);
+ if (ret) {
+ DBG("Error creating event");
+ continue;
+ }
+ cds_list_add(&ev->wildcard_list,
+ &sw->events);
+ }
+ }
+ }
+
+ if (name_len > LTTNG_UST_SYM_NAME_LEN - 1) {
+ WARN("Truncating tracepoint name %s which exceeds size limits of %u chars", name, LTTNG_UST_SYM_NAME_LEN - 1);
+ name_len = LTTNG_UST_SYM_NAME_LEN - 1;
+ }
+ hash = jhash(name, name_len, 0);
head = &pending_probe_table[hash & (PENDING_PROBE_HASH_SIZE - 1)];
cds_hlist_for_each_entry_safe(e, node, p, head, node) {
struct ltt_event *event;
struct ltt_channel *chan;
- if (strcmp(name, e->name))
+ if (strncmp(name, e->name, LTTNG_UST_SYM_NAME_LEN - 1))
continue;
event = e->event;
chan = event->chan;
return NULL;
CDS_INIT_LIST_HEAD(&session->chan);
CDS_INIT_LIST_HEAD(&session->events);
+ CDS_INIT_LIST_HEAD(&session->loglevels);
+ CDS_INIT_LIST_HEAD(&session->wildcards);
uuid_generate(session->uuid);
cds_list_add(&session->list, &sessions);
return session;
{
struct ltt_channel *chan, *tmpchan;
struct ltt_event *event, *tmpevent;
+ struct session_loglevel *loglevel, *tmploglevel;
+ struct session_wildcard *wildcard, *tmpwildcard;
int ret;
CMM_ACCESS_ONCE(session->active) = 0;
WARN_ON(ret);
}
synchronize_trace(); /* Wait for in-flight events to complete */
+ cds_list_for_each_entry_safe(loglevel, tmploglevel, &session->loglevels, list)
+ _ltt_loglevel_destroy(loglevel);
+ cds_list_for_each_entry_safe(wildcard, tmpwildcard, &session->wildcards, list)
+ _ltt_wildcard_destroy(wildcard);
cds_list_for_each_entry_safe(event, tmpevent, &session->events, list)
_ltt_event_destroy(event);
cds_list_for_each_entry_safe(chan, tmpchan, &session->chan, list)
size_t subbuf_size, size_t num_subbuf,
unsigned int switch_timer_interval,
unsigned int read_timer_interval,
- int *shm_fd, int *wait_fd,
- uint64_t *memory_map_size,
+ int **shm_fd, int **wait_fd,
+ uint64_t **memory_map_size,
struct ltt_channel *chan_priv_init)
{
struct ltt_channel *chan = NULL;
chan->ops->channel_destroy(chan);
}
+int ltt_loglevel_create(struct ltt_channel *chan,
+ struct lttng_ust_event *event_param,
+ struct session_loglevel **_sl)
+{
+ struct session_loglevel *sl;
+
+ sl = add_loglevel(event_param->name, chan, event_param);
+ if (!sl || IS_ERR(sl)) {
+ return PTR_ERR(sl);
+ }
+ *_sl = sl;
+ return 0;
+}
+
+static
+void _ltt_loglevel_destroy(struct session_loglevel *sl)
+{
+ _remove_loglevel(sl);
+}
+
+int ltt_wildcard_create(struct ltt_channel *chan,
+ struct lttng_ust_event *event_param,
+ struct session_wildcard **_sw)
+{
+ struct session_wildcard *sw;
+
+ sw = add_wildcard(event_param->name, chan, event_param);
+ if (!sw || IS_ERR(sw)) {
+ return PTR_ERR(sw);
+ }
+ *_sw = sw;
+ return 0;
+}
+
+static
+void _ltt_wildcard_destroy(struct session_wildcard *sw)
+{
+ _remove_wildcard(sw);
+}
+
/*
* Supports event creation while tracing session is active.
*/
-struct ltt_event *ltt_event_create(struct ltt_channel *chan,
- struct lttng_ust_event *event_param,
- void *filter)
+int ltt_event_create(struct ltt_channel *chan,
+ struct lttng_ust_event *event_param,
+ void *filter,
+ struct ltt_event **_event)
{
struct ltt_event *event;
- int ret;
+ int ret = 0;
- if (chan->used_event_id == -1UL)
+ if (chan->used_event_id == -1UL) {
+ ret = -ENOMEM;
goto full;
+ }
/*
* This is O(n^2) (for each event, the loop is called at event
* creation). Might require a hash if we have lots of events.
*/
- cds_list_for_each_entry(event, &chan->session->events, list)
- if (event->desc && !strcmp(event->desc->name, event_param->name))
+ cds_list_for_each_entry(event, &chan->session->events, list) {
+ if (event->desc && !strncmp(event->desc->name,
+ event_param->name,
+ LTTNG_UST_SYM_NAME_LEN - 1)) {
+ ret = -EEXIST;
goto exist;
+ }
+ }
event = zmalloc(sizeof(struct ltt_event));
- if (!event)
+ if (!event) {
+ ret = -ENOMEM;
goto cache_error;
+ }
event->chan = chan;
event->filter = filter;
/*
goto add_pending_error;
}
break;
+ case LTTNG_UST_TRACEPOINT_LOGLEVEL:
+ assert(0);
+ break;
default:
WARN_ON_ONCE(1);
}
goto statedump_error;
}
cds_list_add(&event->list, &chan->session->events);
- return event;
+ *_event = event;
+ return 0;
statedump_error:
if (event->desc) {
cache_error:
exist:
full:
- return NULL;
+ return ret;
}
/*
if (ret)
goto end;
+ if (event->desc->loglevel) {
+ const struct tracepoint_loglevel_entry *ll_entry;
+
+ ll_entry = *event->desc->loglevel;
+ ret = lttng_metadata_printf(session,
+ " loglevel.identifier = \"%s\";\n"
+ " loglevel.value = %lld;\n",
+ ll_entry->identifier,
+ (long long) ll_entry->value);
+ if (ret)
+ goto end;
+ }
+
if (event->ctx) {
ret = lttng_metadata_printf(session,
" context := struct {\n");
{
return lttng_metadata_printf(session,
"struct packet_context {\n"
- " uint64_t timestamp_begin;\n"
- " uint64_t timestamp_end;\n"
+ " uint64_clock_monotonic_t timestamp_begin;\n"
+ " uint64_clock_monotonic_t timestamp_end;\n"
" uint32_t events_discarded;\n"
" uint32_t content_size;\n"
" uint32_t packet_size;\n"
" enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n"
" variant <id> {\n"
" struct {\n"
- " uint27_t timestamp;\n"
+ " uint27_clock_monotonic_t timestamp;\n"
" } compact;\n"
" struct {\n"
" uint32_t id;\n"
- " uint64_t timestamp;\n"
+ " uint64_clock_monotonic_t timestamp;\n"
" } extended;\n"
" } v;\n"
"} align(%u);\n"
" enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n"
" variant <id> {\n"
" struct {\n"
- " uint32_t timestamp;\n"
+ " uint32_clock_monotonic_t timestamp;\n"
" } compact;\n"
" struct {\n"
" uint32_t id;\n"
- " uint64_t timestamp;\n"
+ " uint64_clock_monotonic_t timestamp;\n"
" } extended;\n"
" } v;\n"
"} align(%u);\n\n",
);
}
+/*
+ * Approximation of NTP time of day to clock monotonic correlation,
+ * taken at start of trace.
+ * Yes, this is only an approximation. Yes, we can (and will) do better
+ * in future versions.
+ */
+static
+uint64_t measure_clock_offset(void)
+{
+ uint64_t offset, monotonic[2], realtime;
+ struct timespec rts = { 0, 0 };
+ int ret;
+
+ monotonic[0] = trace_clock_read64();
+ ret = clock_gettime(CLOCK_REALTIME, &rts);
+ if (ret < 0)
+ return 0;
+ monotonic[1] = trace_clock_read64();
+ offset = (monotonic[0] + monotonic[1]) >> 1;
+ realtime = rts.tv_sec * 1000000000ULL;
+ realtime += rts.tv_nsec;
+ offset = realtime - offset;
+ return offset;
+}
+
/*
* Output metadata into this session's metadata buffers.
*/
int _ltt_session_metadata_statedump(struct ltt_session *session)
{
unsigned char *uuid_c = session->uuid;
- char uuid_s[37];
+ char uuid_s[37], clock_uuid_s[CLOCK_UUID_LEN];
struct ltt_channel *chan;
struct ltt_event *event;
int ret = 0;
if (ret)
goto end;
+ ret = lttng_metadata_printf(session,
+ "clock {\n"
+ " name = %s;\n",
+ "monotonic"
+ );
+ if (ret)
+ goto end;
+
+ if (!trace_clock_uuid(clock_uuid_s)) {
+ ret = lttng_metadata_printf(session,
+ " uuid = \"%s\";\n",
+ clock_uuid_s
+ );
+ if (ret)
+ goto end;
+ }
+
+ ret = lttng_metadata_printf(session,
+ " description = \"Monotonic Clock\";\n"
+ " freq = %" PRIu64 "; /* Frequency, in Hz */\n"
+ " /* clock value offset from Epoch is: offset * (1/freq) */\n"
+ " offset = %" PRIu64 ";\n"
+ "};\n\n",
+ trace_clock_freq(),
+ measure_clock_offset()
+ );
+ if (ret)
+ goto end;
+
+ ret = lttng_metadata_printf(session,
+ "typealias integer {\n"
+ " size = 27; align = 1; signed = false;\n"
+ " map = clock.monotonic.value;\n"
+ "} := uint27_clock_monotonic_t;\n"
+ "\n"
+ "typealias integer {\n"
+ " size = 32; align = %u; signed = false;\n"
+ " map = clock.monotonic.value;\n"
+ "} := uint32_clock_monotonic_t;\n"
+ "\n"
+ "typealias integer {\n"
+ " size = 64; align = %u; signed = false;\n"
+ " map = clock.monotonic.value;\n"
+ "} := uint64_clock_monotonic_t;\n\n",
+ lttng_alignof(uint32_t) * CHAR_BIT,
+ lttng_alignof(uint64_t) * CHAR_BIT
+ );
+ if (ret)
+ goto end;
+
ret = _ltt_stream_packet_context_declare(session);
if (ret)
goto end;