* fine too).
* Each tuple is also separated by a comma.
*/
-#define _TP_COMBINE_TOKENS1(_tokena, _tokenb) _tokena##_tokenb
-#define _TP_COMBINE_TOKENS(_tokena, _tokenb) _TP_COMBINE_TOKENS1(_tokena, _tokenb)
+#define __TP_COMBINE_TOKENS(_tokena, _tokenb) \
+ _tokena##_tokenb
+#define _TP_COMBINE_TOKENS(_tokena, _tokenb) \
+ __TP_COMBINE_TOKENS(_tokena, _tokenb)
+#define __TP_COMBINE_TOKENS3(_tokena, _tokenb, _tokenc) \
+ _tokena##_tokenb##_tokenc
+#define _TP_COMBINE_TOKENS3(_tokena, _tokenb, _tokenc) \
+ __TP_COMBINE_TOKENS3(_tokena, _tokenb, _tokenc)
+#define __TP_COMBINE_TOKENS4(_tokena, _tokenb, _tokenc, _tokend) \
+ _tokena##_tokenb##_tokenc##_tokend
+#define _TP_COMBINE_TOKENS4(_tokena, _tokenb, _tokenc, _tokend) \
+ __TP_COMBINE_TOKENS4(_tokena, _tokenb, _tokenc, _tokend)
/* _TP_EXVAR* extract the var names. */
#define _TP_EXVAR0()
#define LTTNG_UST_COMM_VERSION_MINOR 1
enum lttng_ust_instrumentation {
- LTTNG_UST_TRACEPOINT = 0,
- LTTNG_UST_PROBE = 1,
- LTTNG_UST_FUNCTION = 2,
+ LTTNG_UST_TRACEPOINT = 0,
+ LTTNG_UST_PROBE = 1,
+ LTTNG_UST_FUNCTION = 2,
+ LTTNG_UST_TRACEPOINT_LOGLEVEL = 3,
};
enum lttng_ust_output {
enum lttng_ust_output output; /* splice, mmap */
};
+struct lttng_ust_loglevel {
+ char provider[LTTNG_UST_SYM_NAME_LEN];
+ char loglevel[LTTNG_UST_SYM_NAME_LEN];
+ int64_t value;
+};
+
struct lttng_ust_object_data {
int handle;
int shm_fd;
#define LTTNG_UST_TRACEPOINT_LIST _UST_CMD(0x42)
#define LTTNG_UST_WAIT_QUIESCENT _UST_CMD(0x43)
#define LTTNG_UST_REGISTER_DONE _UST_CMD(0x44)
+#define LTTNG_UST_LOGLEVEL_LIST _UST_CMD(0x45)
/* Session FD commands */
#define LTTNG_UST_METADATA \
/* Tracepoint list commands */
#define LTTNG_UST_TRACEPOINT_LIST_GET _UST_CMD(0x90)
+/* Loglevel list commands */
+#define LTTNG_UST_LOGLEVEL_LIST_GET _UST_CMD(0xA0)
+
#define LTTNG_UST_ROOT_HANDLE 0
struct lttng_ust_obj;
unsigned int allocated_fields;
};
+struct tracepoint_loglevel_entry {
+ const char *identifier;
+ long value;
+};
+
struct lttng_event_desc {
const char *name;
void *probe_callback;
const struct lttng_event_ctx *ctx; /* context */
const struct lttng_event_field *fields; /* event payload */
unsigned int nr_fields;
+ const struct tracepoint_loglevel_entry **loglevel;
};
struct lttng_probe_desc {
- const struct lttng_event_desc *event_desc;
+ const char *provider;
+ const struct lttng_event_desc **event_desc;
unsigned int nr_events;
+ const struct tracepoint_loglevel_entry **loglevels;
+ unsigned int nr_loglevels;
struct cds_list_head head; /* chain registered probes */
- struct tracepoint_loglevel *loglevels;
+};
+
+struct tp_loglevel_iter {
+ struct lttng_probe_desc *desc;
+ const struct tracepoint_loglevel_entry *loglevel;
};
struct ust_pending_probe;
struct ltt_channel_ops ops;
};
-struct tracepoint_loglevel_enum_entry {
- const char *identifier;
- long value;
-};
-
-/* mapping between tracepoint and loglevel */
-struct tracepoint_loglevel {
- const char *name;
- const struct tracepoint_loglevel_enum_entry *loglevel;
-};
-
struct ltt_session *ltt_session_create(void);
int ltt_session_enable(struct ltt_session *session);
int ltt_session_disable(struct ltt_session *session);
/*
* Stage 3 of tracepoint event generation.
*
- * Create an array of events.
- */
-
-/* Reset all macros within TRACEPOINT_EVENT */
-#include <lttng/ust-tracepoint-event-reset.h>
-
-#undef TRACEPOINT_EVENT_INSTANCE
-#define TRACEPOINT_EVENT_INSTANCE(_provider, _template, _name, _args) \
- { \
- .fields = __event_fields___##_provider##___##_template,\
- .name = #_provider ":" #_name, \
- .probe_callback = (void *) &__event_probe__##_provider##___##_template,\
- .nr_fields = _TP_ARRAY_SIZE(__event_fields___##_provider##___##_template), \
- },
-
-static const struct lttng_event_desc _TP_COMBINE_TOKENS(__event_desc___, TRACEPOINT_PROVIDER)[] = {
-#include TRACEPOINT_INCLUDE
-};
-
-
-/*
- * Stage 4 of tracepoint event generation.
- *
* Create static inline function that calculates event size.
*/
#include TRACEPOINT_INCLUDE
/*
- * Stage 5 of tracepoint event generation.
+ * Stage 4 of tracepoint event generation.
*
* Create static inline function that calculates event payload alignment.
*/
/*
- * Stage 6 of tracepoint event generation.
+ * Stage 5 of tracepoint event generation.
*
* Create the probe function. This function calls event size calculation
* and writes event data into the buffer.
#undef __get_dynamic_len
/*
- * Stage 7.1 of tracepoint event generation.
+ * Stage 6.1 of tracepoint event generation.
*
* Tracepoint loglevel enumeration definition generation.
*/
#undef tp_loglevel
#define tp_loglevel(_identifier, _value) \
-static const struct tracepoint_loglevel_enum_entry \
- _TP_COMBINE_TOKENS(_TP_COMBINE_TOKENS(TRACEPOINT_PROVIDER, __tp_loglevel_enum_entry__), _identifier) = \
- { \
+static const struct tracepoint_loglevel_entry \
+ _TP_COMBINE_TOKENS4(__tp_loglevel_entry__, TRACEPOINT_PROVIDER, ___, _identifier) = { \
.identifier = #_identifier, \
.value = (_value), \
};
#include TRACEPOINT_INCLUDE
/*
- * Stage 7.2 of tracepoint event generation.
+ * Stage 6.2 of tracepoint event generation.
*
* Tracepoint loglevel enumeration array generation.
*/
#undef tp_loglevel
#define tp_loglevel(_identifier, _value) \
- &_TP_COMBINE_TOKENS(_TP_COMBINE_TOKENS(TRACEPOINT_PROVIDER, __tp_loglevel_enum_entry__), _identifier),
+ &_TP_COMBINE_TOKENS4(__tp_loglevel_entry__, TRACEPOINT_PROVIDER, ___, _identifier), \
-static const struct tracepoint_loglevel_enum_entry *_TP_COMBINE_TOKENS(__tracepoint_loglevel_enum__, TRACEPOINT_PROVIDER)[] __attribute__((unused)) =
-{
+static const struct tracepoint_loglevel_entry *_TP_COMBINE_TOKENS(__tracepoint_loglevel_enum__, TRACEPOINT_PROVIDER)[] = {
#include TRACEPOINT_INCLUDE
};
/*
- * Stage 8 of tracepoint event generation.
+ * Stage 7 of tracepoint event generation.
*
- * Tracepoint loglevel definition generation.
+ * Tracepoint loglevel mapping definition generation. We generate a
+ * symbol for each mapping for a provider/event to ensure at most a 1 to
+ * 1 mapping between events and loglevels. If the symbol is repeated,
+ * the compiler will complain.
*/
/* Reset all macros within TRACEPOINT_EVENT */
#include <lttng/ust-tracepoint-event-reset.h>
#undef TRACEPOINT_LOGLEVEL
-#define TRACEPOINT_LOGLEVEL(__provider, __name, __loglevel) \
-{ \
- .name = #__provider ":" #__name, \
- .loglevel = &_TP_COMBINE_TOKENS(_TP_COMBINE_TOKENS(TRACEPOINT_PROVIDER, __tp_loglevel_enum_entry__), __loglevel), \
-},
+#define TRACEPOINT_LOGLEVEL(__provider, __name, __loglevel) \
+static const struct tracepoint_loglevel_entry * \
+ _loglevel_mapping___##__provider##___##__name = \
+ &__tp_loglevel_entry__##__provider##___##__loglevel;
+
+#include TRACEPOINT_INCLUDE
+
+/*
+ * Stage 8.1 of tracepoint event generation.
+ *
+ * Create events description structures. We use a weakref because
+ * loglevels are optional. If not declared, the event will point to the
+ * a loglevel that contains NULL.
+ */
+
+/* Reset all macros within TRACEPOINT_EVENT */
+#include <lttng/ust-tracepoint-event-reset.h>
+
+#undef TRACEPOINT_EVENT_INSTANCE
+#define TRACEPOINT_EVENT_INSTANCE(_provider, _template, _name, _args) \
+static const struct tracepoint_loglevel_entry * \
+ __ref_loglevel_mapping___##_provider##___##_name \
+ __attribute__((weakref ("_loglevel_mapping___" #_provider "___" #_name))); \
+const struct lttng_event_desc __event_desc___##_provider##_##_name = { \
+ .fields = __event_fields___##_provider##___##_template, \
+ .name = #_provider ":" #_name, \
+ .probe_callback = (void *) &__event_probe__##_provider##___##_template,\
+ .nr_fields = _TP_ARRAY_SIZE(__event_fields___##_provider##___##_template), \
+ .loglevel = &__ref_loglevel_mapping___##_provider##___##_name, \
+};
+
+#include TRACEPOINT_INCLUDE
-static struct tracepoint_loglevel _TP_COMBINE_TOKENS(__tracepoint_loglevels__, TRACEPOINT_PROVIDER)[] = {
+/*
+ * Stage 8.2 of tracepoint event generation.
+ *
+ * Create array of events.
+ */
+
+/* Reset all macros within TRACEPOINT_EVENT */
+#include <lttng/ust-tracepoint-event-reset.h>
+
+#undef TRACEPOINT_EVENT_INSTANCE
+#define TRACEPOINT_EVENT_INSTANCE(_provider, _template, _name, _args) \
+ &__event_desc___##_provider##_##_name,
+
+static const struct lttng_event_desc *_TP_COMBINE_TOKENS(__event_desc___, TRACEPOINT_PROVIDER)[] = {
#include TRACEPOINT_INCLUDE
};
+
/*
* Stage 9 of tracepoint event generation.
*
/* non-const because list head will be modified when registered. */
static struct lttng_probe_desc _TP_COMBINE_TOKENS(__probe_desc___, TRACEPOINT_PROVIDER) = {
+ .provider = __tp_stringify(TRACEPOINT_PROVIDER),
.event_desc = _TP_COMBINE_TOKENS(__event_desc___, TRACEPOINT_PROVIDER),
.nr_events = _TP_ARRAY_SIZE(_TP_COMBINE_TOKENS(__event_desc___, TRACEPOINT_PROVIDER)),
- .loglevels = _TP_COMBINE_TOKENS(__tracepoint_loglevels__, TRACEPOINT_PROVIDER),
+ .loglevels = _TP_COMBINE_TOKENS(__tracepoint_loglevel_enum__, TRACEPOINT_PROVIDER),
+ .nr_loglevels = _TP_ARRAY_SIZE(_TP_COMBINE_TOKENS(__tracepoint_loglevel_enum__, TRACEPOINT_PROVIDER)),
};
/*
struct lttng_ust_context context;
struct lttng_ust_tracer_version version;
char tracepoint_list_entry[LTTNG_UST_SYM_NAME_LEN];
+ struct lttng_ust_loglevel loglevel_list_entry;
} u;
};
} stream;
struct lttng_ust_tracer_version version;
char tracepoint_list_entry[LTTNG_UST_SYM_NAME_LEN];
+ struct lttng_ust_loglevel loglevel_list_entry;
} u;
};
goto add_pending_error;
}
break;
+ case LTTNG_UST_TRACEPOINT_LOGLEVEL:
+ /*
+ * TODO: add tracepoint loglevel to hash table, with
+ * event info. Enable all events corresponding to
+ * loglevel.
+ */
+ break;
default:
WARN_ON_ONCE(1);
}
*/
static CDS_LIST_HEAD(probe_list);
+static
+const struct lttng_probe_desc *find_provider(const char *provider)
+{
+ struct lttng_probe_desc *iter;
+
+ cds_list_for_each_entry(iter, &probe_list, head) {
+ if (!strcmp(iter->provider, provider))
+ return iter;
+ }
+ return NULL;
+}
+
static
const struct lttng_event_desc *find_event(const char *name)
{
cds_list_for_each_entry(probe_desc, &probe_list, head) {
for (i = 0; i < probe_desc->nr_events; i++) {
- if (!strcmp(probe_desc->event_desc[i].name, name))
- return &probe_desc->event_desc[i];
+ if (!strcmp(probe_desc->event_desc[i]->name, name))
+ return probe_desc->event_desc[i];
}
}
return NULL;
int ltt_probe_register(struct lttng_probe_desc *desc)
{
+ struct lttng_probe_desc *iter;
int ret = 0;
int i;
ust_lock();
+ if (find_provider(desc->provider)) {
+ ret = -EEXIST;
+ goto end;
+ }
/*
* TODO: This is O(N^2). Turn into a hash table when probe registration
* overhead becomes an issue.
*/
for (i = 0; i < desc->nr_events; i++) {
- if (find_event(desc->event_desc[i].name)) {
+ if (find_event(desc->event_desc[i]->name)) {
ret = -EEXIST;
goto end;
}
}
+
+ /*
+ * We sort the providers by struct lttng_probe_desc pointer
+ * address.
+ */
+ cds_list_for_each_entry_reverse(iter, &probe_list, head) {
+ BUG_ON(iter == desc); /* Should never be in the list twice */
+ if (iter < desc) {
+ /* We belong to the location right after iter. */
+ cds_list_add(&desc->head, &iter->head);
+ goto desc_added;
+ }
+ }
+ /* We should be added at the head of the list */
cds_list_add(&desc->head, &probe_list);
+desc_added:
/*
* fix the events awaiting probe load.
*/
for (i = 0; i < desc->nr_events; i++) {
- ret = pending_probe_fix_events(&desc->event_desc[i]);
+ ret = pending_probe_fix_events(desc->event_desc[i]);
assert(!ret);
}
end:
void ltt_event_put(const struct lttng_event_desc *event)
{
}
-
-#if 0
-static
-void *tp_list_start(struct seq_file *m, loff_t *pos)
-{
- struct lttng_probe_desc *probe_desc;
- int iter = 0, i;
-
- pthread_mutex_lock(&probe_mutex);
- cds_list_for_each_entry(probe_desc, &probe_list, head) {
- for (i = 0; i < probe_desc->nr_events; i++) {
- if (iter++ >= *pos)
- return (void *) &probe_desc->event_desc[i];
- }
- }
- /* End of list */
- return NULL;
-}
-
-static
-void *tp_list_next(struct seq_file *m, void *p, loff_t *ppos)
-{
- struct lttng_probe_desc *probe_desc;
- int iter = 0, i;
-
- (*ppos)++;
- cds_list_for_each_entry(probe_desc, &probe_list, head) {
- for (i = 0; i < probe_desc->nr_events; i++) {
- if (iter++ >= *ppos)
- return (void *) &probe_desc->event_desc[i];
- }
- }
- /* End of list */
- return NULL;
-}
-
-static
-void tp_list_stop(struct seq_file *m, void *p)
-{
- pthread_mutex_unlock(&probe_mutex);
-}
-
-static
-int tp_list_show(struct seq_file *m, void *p)
-{
- const struct lttng_event_desc *probe_desc = p;
-
- /*
- * Don't export lttng internal events (metadata).
- */
- if (!strncmp(probe_desc->name, "lttng_", sizeof("lttng_") - 1))
- return 0;
- seq_printf(m, "event { name = %s; };\n",
- probe_desc->name);
- return 0;
-}
-
-static
-const struct seq_operations lttng_tracepoint_list_seq_ops = {
- .start = tp_list_start,
- .next = tp_list_next,
- .stop = tp_list_stop,
- .show = tp_list_show,
-};
-
-static
-int lttng_tracepoint_list_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, <tng_tracepoint_list_seq_ops);
-}
-
-const struct file_operations lttng_tracepoint_list_fops = {
- .open = lttng_tracepoint_list_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-#endif //0
int got_first;
};
+struct ltt_loglevel_list {
+ struct loglevel_iter *iter;
+ int got_first;
+};
+
static int lttng_ust_abi_close_in_progress;
static
int lttng_abi_tracepoint_list(void);
+static
+int lttng_abi_loglevel_list(void);
+
/*
* Object descriptor table. Should be protected from concurrent access
* by the caller.
static const struct lttng_ust_objd_ops lttng_event_ops;
static const struct lttng_ust_objd_ops lib_ring_buffer_objd_ops;
static const struct lttng_ust_objd_ops lttng_tracepoint_list_ops;
+static const struct lttng_ust_objd_ops lttng_loglevel_list_ops;
enum channel_type {
PER_CPU_CHANNEL,
* Returns a file descriptor listing available tracepoints
* LTTNG_UST_WAIT_QUIESCENT
* Returns after all previously running probes have completed
+ * LTTNG_UST_LOGLEVEL_LIST
+ * Returns a file descriptor listing available loglevels
*
* The returned session will be deleted when its file descriptor is closed.
*/
case LTTNG_UST_WAIT_QUIESCENT:
synchronize_trace();
return 0;
+ case LTTNG_UST_LOGLEVEL_LIST:
+ return lttng_abi_loglevel_list();
default:
return -EINVAL;
}
.cmd = lttng_tracepoint_list_cmd,
};
+/*
+ * beware: we don't keep the mutex over the send, but we must walk the
+ * whole list each time we are called again. So sending one loglevel
+ * entry at a time means this is O(n^2). TODO: do as in the kernel and
+ * send multiple tracepoints for each call to amortize this cost.
+ */
+static
+void ltt_loglevel_list_get(struct ltt_loglevel_list *list,
+ const char *loglevel_provider,
+ const char *loglevel,
+ long *value)
+{
+#if 0
+next:
+ if (!list->got_first) {
+ //tp_loglevel_iter_start(&list->iter);
+ list->got_first = 1;
+ goto copy;
+ }
+ //tp_loglevel_iter_next(&list->iter);
+copy:
+ if (!list->iter->desc.provider) {
+ loglevel_provider[0] = '\0'; /* end of list */
+ } else {
+ memcpy(loglevel_provider, list->iter->desc.provider,
+ LTTNG_UST_SYM_NAME_LEN);
+ memcpy(loglevel, list->iter.loglevel,
+ LTTNG_UST_SYM_NAME_LEN);
+ *value = list->iter.value;
+ }
+#endif
+}
+
+static
+long lttng_loglevel_list_cmd(int objd, unsigned int cmd, unsigned long arg)
+{
+ struct ltt_loglevel_list *list = objd_private(objd);
+ struct lttng_ust_loglevel *loglevel_list_entry =
+ (struct lttng_ust_loglevel *) arg;
+
+ switch (cmd) {
+ case LTTNG_UST_LOGLEVEL_LIST_GET:
+/*
+ ltt_tracepoint_list_get(list,
+ loglevel_list_entry->provider,
+ loglevel_list_entry->loglevel,
+ &loglevel_list_entry->value);
+ if (loglevel_list_entry->provider[0] == '\0')
+ return -ENOENT;
+*/
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static
+int lttng_abi_loglevel_list(void)
+{
+ int list_objd, ret;
+ struct ltt_loglevel_list *list;
+
+ list_objd = objd_alloc(NULL, <tng_loglevel_list_ops);
+ if (list_objd < 0) {
+ ret = list_objd;
+ goto objd_error;
+ }
+ list = zmalloc(sizeof(*list));
+ if (!list) {
+ ret = -ENOMEM;
+ goto alloc_error;
+ }
+ objd_set_private(list_objd, list);
+
+ return list_objd;
+
+alloc_error:
+ {
+ int err;
+
+ err = lttng_ust_objd_unref(list_objd);
+ assert(!err);
+ }
+objd_error:
+ return ret;
+}
+
+static
+int lttng_release_loglevel_list(int objd)
+{
+ struct ltt_loglevel_list *list = objd_private(objd);
+
+ if (list) {
+ //tp_loglevel_iter_stop(&list->iter);
+ free(list);
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+}
+
+static const struct lttng_ust_objd_ops lttng_loglevel_list_ops = {
+ .release = lttng_release_loglevel_list,
+ .cmd = lttng_loglevel_list_cmd,
+};
+
struct stream_priv_data {
struct lttng_ust_lib_ring_buffer *buf;
struct ltt_channel *ltt_chan;
ctf_integer(int, value, value)
)
)
-TRACEPOINT_LOGLEVEL(ust_tests_demo, done, test1)
+TRACEPOINT_LOGLEVEL(ust_tests_demo3, done, test1)
#endif /* _TRACEPOINT_UST_TESTS_DEMO3_H */