From cf6db82d0bd4644120a3c864d35976e32d1652c5 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Wed, 13 Jan 2016 16:52:26 -0500 Subject: [PATCH] Implement dynamic types, and application context provider support Signed-off-by: Mathieu Desnoyers --- configure.ac | 1 + include/Makefile.am | 2 + include/lttng/ringbuffer-config.h | 10 +- include/lttng/ust-context-provider.h | 65 +++ include/lttng/ust-ctl.h | 11 + include/lttng/ust-dynamic-type.h | 33 ++ include/lttng/ust-events.h | 55 +- include/lttng/ust-tracepoint-event.h | 9 +- include/ust-comm.h | 1 + liblttng-ust-comm/lttng-ust-comm.c | 263 +++++++-- liblttng-ust-ctl/ustctl.c | 4 +- liblttng-ust/Makefile.am | 2 + liblttng-ust/lttng-context-cpu-id.c | 6 +- liblttng-ust/lttng-context-ip.c | 2 +- liblttng-ust/lttng-context-perf-counters.c | 6 +- liblttng-ust/lttng-context-procname.c | 6 +- liblttng-ust/lttng-context-provider.c | 181 ++++++ liblttng-ust/lttng-context-pthread-id.c | 6 +- liblttng-ust/lttng-context-vpid.c | 6 +- liblttng-ust/lttng-context-vtid.c | 6 +- liblttng-ust/lttng-context.c | 178 +++++- liblttng-ust/lttng-events.c | 292 ++++++--- liblttng-ust/lttng-filter-interpreter.c | 650 ++++++++++++++++++--- liblttng-ust/lttng-filter-specialize.c | 66 ++- liblttng-ust/lttng-filter-validator.c | 100 +++- liblttng-ust/lttng-filter.c | 28 +- liblttng-ust/lttng-filter.h | 24 +- liblttng-ust/lttng-ring-buffer-client.h | 14 +- liblttng-ust/lttng-ust-comm.c | 2 - liblttng-ust/lttng-ust-dynamic-type.c | 202 +++++++ tests/Makefile.am | 2 +- tests/hello/hello.c | 2 + tests/test-app-ctx/Makefile.am | 13 + tests/test-app-ctx/hello.c | 341 +++++++++++ tests/test-app-ctx/tp.c | 26 + tests/test-app-ctx/ust_tests_hello.h | 68 +++ tests/ust-variant/Makefile.am | 13 + tests/ust-variant/ust-variant.c | 195 +++++++ 38 files changed, 2582 insertions(+), 309 deletions(-) create mode 100644 include/lttng/ust-context-provider.h create mode 100644 include/lttng/ust-dynamic-type.h create mode 100644 liblttng-ust/lttng-context-provider.c create mode 100644 liblttng-ust/lttng-ust-dynamic-type.c create mode 100644 tests/test-app-ctx/Makefile.am create mode 100644 tests/test-app-ctx/hello.c create mode 100644 tests/test-app-ctx/tp.c create mode 100644 tests/test-app-ctx/ust_tests_hello.h create mode 100644 tests/ust-variant/Makefile.am create mode 100644 tests/ust-variant/ust-variant.c diff --git a/configure.ac b/configure.ac index c92840bc..9d972ccf 100644 --- a/configure.ac +++ b/configure.ac @@ -407,6 +407,7 @@ AC_CONFIG_FILES([ tests/ust-elf/Makefile tests/benchmark/Makefile tests/utils/Makefile + tests/test-app-ctx/Makefile lttng-ust.pc ]) diff --git a/include/Makefile.am b/include/Makefile.am index 3c9cf248..47c715b1 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -36,6 +36,8 @@ noinst_HEADERS = \ lttng/ust-tid.h \ lttng/bitfield.h \ lttng/ust-dlfcn.h \ + lttng/ust-dynamic-type.h \ + lttng/ust-context-provider.h \ helper.h \ share.h diff --git a/include/lttng/ringbuffer-config.h b/include/lttng/ringbuffer-config.h index 87344c94..5ae0d8a6 100644 --- a/include/lttng/ringbuffer-config.h +++ b/include/lttng/ringbuffer-config.h @@ -228,7 +228,7 @@ struct lttng_ust_lib_ring_buffer_config { * removed. */ #define LTTNG_UST_RING_BUFFER_CTX_PADDING \ - (24 - sizeof(int) - sizeof(void *)) + (24 - sizeof(int) - sizeof(void *) - sizeof(void *)) struct lttng_ust_lib_ring_buffer_ctx { /* input received by lib_ring_buffer_reserve(), saved here. */ struct channel *chan; /* channel */ @@ -258,6 +258,7 @@ struct lttng_ust_lib_ring_buffer_ctx { unsigned int rflags; /* reservation flags */ unsigned int padding1; /* padding to realign on pointer */ void *ip; /* caller ip address */ + void *priv2; /* 2nd priv data */ char padding2[LTTNG_UST_RING_BUFFER_CTX_PADDING]; }; @@ -274,12 +275,14 @@ static inline lttng_ust_notrace void lib_ring_buffer_ctx_init(struct lttng_ust_lib_ring_buffer_ctx *ctx, struct channel *chan, void *priv, size_t data_size, int largest_align, - int cpu, struct lttng_ust_shm_handle *handle); + int cpu, struct lttng_ust_shm_handle *handle, + void *priv2); static inline void lib_ring_buffer_ctx_init(struct lttng_ust_lib_ring_buffer_ctx *ctx, struct channel *chan, void *priv, size_t data_size, int largest_align, - int cpu, struct lttng_ust_shm_handle *handle) + int cpu, struct lttng_ust_shm_handle *handle, + void *priv2) { ctx->chan = chan; ctx->priv = priv; @@ -290,6 +293,7 @@ void lib_ring_buffer_ctx_init(struct lttng_ust_lib_ring_buffer_ctx *ctx, ctx->handle = handle; ctx->padding1 = 0; ctx->ip = 0; + ctx->priv2 = priv2; memset(ctx->padding2, 0, LTTNG_UST_RING_BUFFER_CTX_PADDING); } diff --git a/include/lttng/ust-context-provider.h b/include/lttng/ust-context-provider.h new file mode 100644 index 00000000..5b266fa8 --- /dev/null +++ b/include/lttng/ust-context-provider.h @@ -0,0 +1,65 @@ +#ifndef _LTTNG_UST_CONTEXT_PROVIDER_H +#define _LTTNG_UST_CONTEXT_PROVIDER_H + +/* + * Copyright 2016 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +struct lttng_ust_context_provider { + char *name; + size_t (*get_size)(struct lttng_ctx_field *field, size_t offset); + void (*record)(struct lttng_ctx_field *field, + struct lttng_ust_lib_ring_buffer_ctx *ctx, + struct lttng_channel *chan); + void (*get_value)(struct lttng_ctx_field *field, + struct lttng_ctx_value *value); + struct cds_hlist_node node; +}; + +int lttng_ust_context_provider_register(struct lttng_ust_context_provider *provider); +void lttng_ust_context_provider_unregister(struct lttng_ust_context_provider *provider); + +int lttng_context_is_app(const char *name); + +void lttng_ust_context_set_session_provider(const char *name, + size_t (*get_size)(struct lttng_ctx_field *field, size_t offset), + void (*record)(struct lttng_ctx_field *field, + struct lttng_ust_lib_ring_buffer_ctx *ctx, + struct lttng_channel *chan), + void (*get_value)(struct lttng_ctx_field *field, + struct lttng_ctx_value *value)); + +int lttng_ust_add_app_context_to_ctx_rcu(const char *name, struct lttng_ctx **ctx); +int lttng_ust_context_set_provider_rcu(struct lttng_ctx **_ctx, + const char *name, + size_t (*get_size)(struct lttng_ctx_field *field, size_t offset), + void (*record)(struct lttng_ctx_field *field, + struct lttng_ust_lib_ring_buffer_ctx *ctx, + struct lttng_channel *chan), + void (*get_value)(struct lttng_ctx_field *field, + struct lttng_ctx_value *value)); +int lttng_context_add_rcu(struct lttng_ctx **ctx_p, + const struct lttng_ctx_field *f); + +#endif /* _LTTNG_UST_CONTEXT_PROVIDER_H */ diff --git a/include/lttng/ust-ctl.h b/include/lttng/ust-ctl.h index 7a5f969e..916eb280 100644 --- a/include/lttng/ust-ctl.h +++ b/include/lttng/ust-ctl.h @@ -273,6 +273,8 @@ enum ustctl_abstract_types { ustctl_atype_sequence, ustctl_atype_string, ustctl_atype_float, + ustctl_atype_variant, + ustctl_atype_struct, NR_USTCTL_ABSTRACT_TYPES, }; @@ -345,6 +347,15 @@ struct ustctl_type { struct ustctl_basic_type length_type; struct ustctl_basic_type elem_type; } sequence; + struct { + uint32_t nr_choices; + char tag_name[LTTNG_UST_SYM_NAME_LEN]; + /* Followed by nr_choices struct ustctl_field. */ + } variant; + struct { + uint32_t nr_fields; + /* Followed by nr_fields struct ustctl_field. */ + } _struct; char padding[USTCTL_UST_TYPE_PADDING]; } u; } LTTNG_PACKED; diff --git a/include/lttng/ust-dynamic-type.h b/include/lttng/ust-dynamic-type.h new file mode 100644 index 00000000..d87dc256 --- /dev/null +++ b/include/lttng/ust-dynamic-type.h @@ -0,0 +1,33 @@ +#ifndef _LTTNG_UST_DYNAMIC_TYPE_H +#define _LTTNG_UST_DYNAMIC_TYPE_H + +/* + * Copyright 2016 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +int lttng_ust_dynamic_type_choices(size_t *nr_choices, + const struct lttng_event_field **choices); +const struct lttng_event_field *lttng_ust_dynamic_type_field(int64_t value); +const struct lttng_event_field *lttng_ust_dynamic_type_tag_field(void); + +#endif /* _LTTNG_UST_DYNAMIC_TYPE_H */ diff --git a/include/lttng/ust-events.h b/include/lttng/ust-events.h index f7cbc1a6..f3ade45c 100644 --- a/include/lttng/ust-events.h +++ b/include/lttng/ust-events.h @@ -57,6 +57,8 @@ extern "C" { struct lttng_channel; struct lttng_session; struct lttng_ust_lib_ring_buffer_ctx; +struct lttng_ust_context_app; +struct lttng_event_field; /* * Data structures used by tracepoint event declarations, and by the @@ -86,6 +88,8 @@ enum lttng_abstract_types { atype_sequence, atype_string, atype_float, + atype_dynamic, + atype_struct, NR_ABSTRACT_TYPES, }; @@ -206,6 +210,10 @@ struct lttng_type { struct lttng_basic_type length_type; struct lttng_basic_type elem_type; } sequence; + struct { + uint32_t nr_fields; + struct lttng_event_field *fields; /* Array of fields. */ + } _struct; char padding[LTTNG_UST_TYPE_PADDING]; } u; }; @@ -234,10 +242,29 @@ struct lttng_event_field { char padding[LTTNG_UST_EVENT_FIELD_PADDING]; }; -union lttng_ctx_value { - int64_t s64; - const char *str; - double d; +enum lttng_ust_dynamic_type { + LTTNG_UST_DYNAMIC_TYPE_NONE, + LTTNG_UST_DYNAMIC_TYPE_S8, + LTTNG_UST_DYNAMIC_TYPE_S16, + LTTNG_UST_DYNAMIC_TYPE_S32, + LTTNG_UST_DYNAMIC_TYPE_S64, + LTTNG_UST_DYNAMIC_TYPE_U8, + LTTNG_UST_DYNAMIC_TYPE_U16, + LTTNG_UST_DYNAMIC_TYPE_U32, + LTTNG_UST_DYNAMIC_TYPE_U64, + LTTNG_UST_DYNAMIC_TYPE_FLOAT, + LTTNG_UST_DYNAMIC_TYPE_DOUBLE, + LTTNG_UST_DYNAMIC_TYPE_STRING, + _NR_LTTNG_UST_DYNAMIC_TYPES, +}; + +struct lttng_ctx_value { + enum lttng_ust_dynamic_type sel; + union { + int64_t s64; + const char *str; + double d; + } u; }; struct lttng_perf_counter_field; @@ -245,17 +272,18 @@ struct lttng_perf_counter_field; #define LTTNG_UST_CTX_FIELD_PADDING 40 struct lttng_ctx_field { struct lttng_event_field event_field; - size_t (*get_size)(size_t offset); + size_t (*get_size)(struct lttng_ctx_field *field, size_t offset); void (*record)(struct lttng_ctx_field *field, struct lttng_ust_lib_ring_buffer_ctx *ctx, struct lttng_channel *chan); void (*get_value)(struct lttng_ctx_field *field, - union lttng_ctx_value *value); + struct lttng_ctx_value *value); union { struct lttng_perf_counter_field *perf_counter; char padding[LTTNG_UST_CTX_FIELD_PADDING]; } u; void (*destroy)(struct lttng_ctx_field *field); + char *field_name; /* Has ownership, dynamically allocated. */ }; #define LTTNG_UST_CTX_PADDING 20 @@ -380,6 +408,7 @@ struct lttng_bytecode_runtime { uint64_t (*filter)(void *filter_data, const char *filter_stack_data); int link_failed; struct cds_list_head node; /* list of bytecode runtime in event */ + struct lttng_session *session; }; /* @@ -507,6 +536,14 @@ struct lttng_channel { int tstate:1; /* Transient enable state */ }; +#define LTTNG_UST_STACK_CTX_PADDING 32 +struct lttng_stack_ctx { + struct lttng_event *event; + struct lttng_ctx *chan_ctx; /* RCU dereferenced. */ + struct lttng_ctx *event_ctx; /* RCU dereferenced. */ + char padding[LTTNG_UST_STACK_CTX_PADDING]; +}; + #define LTTNG_UST_EVENT_HT_BITS 12 #define LTTNG_UST_EVENT_HT_SIZE (1U << LTTNG_UST_EVENT_HT_BITS) @@ -551,6 +588,7 @@ struct lttng_session { /* New UST 2.8 */ struct lttng_ust_enum_ht enums_ht; /* ht of enumerations */ struct cds_list_head enums_head; + struct lttng_ctx *ctx; /* contexts for filters. */ }; struct lttng_transport { @@ -592,9 +630,7 @@ int lttng_enabler_attach_exclusion(struct lttng_enabler *enabler, int lttng_attach_context(struct lttng_ust_context *context_param, struct lttng_ctx **ctx, struct lttng_session *session); -void lttng_context_init(void); -void lttng_context_exit(void); -extern struct lttng_ctx *lttng_static_ctx; /* Used by filtering */ +int lttng_session_context_init(struct lttng_ctx **ctx); void lttng_transport_register(struct lttng_transport *transport); void lttng_transport_unregister(struct lttng_transport *transport); @@ -619,6 +655,7 @@ int lttng_add_pthread_id_to_ctx(struct lttng_ctx **ctx); int lttng_add_procname_to_ctx(struct lttng_ctx **ctx); int lttng_add_ip_to_ctx(struct lttng_ctx **ctx); int lttng_add_cpu_id_to_ctx(struct lttng_ctx **ctx); +int lttng_add_dyntest_to_ctx(struct lttng_ctx **ctx); void lttng_context_vtid_reset(void); void lttng_context_vpid_reset(void); diff --git a/include/lttng/ust-tracepoint-event.h b/include/lttng/ust-tracepoint-event.h index aa8b50f8..f96239e0 100644 --- a/include/lttng/ust-tracepoint-event.h +++ b/include/lttng/ust-tracepoint-event.h @@ -663,9 +663,10 @@ void __event_probe__##_provider##___##_name(_TP_ARGS_DATA_PROTO(_args)); \ static \ void __event_probe__##_provider##___##_name(_TP_ARGS_DATA_PROTO(_args)) \ { \ - struct lttng_event *__event = (struct lttng_event *) __tp_data; \ + struct lttng_event *__event = (struct lttng_event *) __tp_data; \ struct lttng_channel *__chan = __event->chan; \ struct lttng_ust_lib_ring_buffer_ctx __ctx; \ + struct lttng_stack_ctx __lttng_ctx; \ size_t __event_len, __event_align; \ size_t __dynamic_len_idx = 0; \ union { \ @@ -703,8 +704,12 @@ void __event_probe__##_provider##___##_name(_TP_ARGS_DATA_PROTO(_args)) \ __event_len = __event_get_size__##_provider##___##_name(__stackvar.__dynamic_len, \ _TP_ARGS_DATA_VAR(_args)); \ __event_align = __event_get_align__##_provider##___##_name(_TP_ARGS_VAR(_args)); \ + memset(&__lttng_ctx, 0, sizeof(__lttng_ctx)); \ + __lttng_ctx.event = __event; \ + __lttng_ctx.chan_ctx = tp_rcu_dereference_bp(__chan->ctx); \ + __lttng_ctx.event_ctx = tp_rcu_dereference_bp(__event->ctx); \ lib_ring_buffer_ctx_init(&__ctx, __chan->chan, __event, __event_len, \ - __event_align, -1, __chan->handle); \ + __event_align, -1, __chan->handle, &__lttng_ctx); \ __ctx.ip = _TP_IP_PARAM(TP_IP_PARAM); \ __ret = __chan->ops->event_reserve(&__ctx, __event->id); \ if (__ret < 0) \ diff --git a/include/ust-comm.h b/include/ust-comm.h index efebbb2a..7f363d40 100644 --- a/include/ust-comm.h +++ b/include/ust-comm.h @@ -260,6 +260,7 @@ int ustcomm_register_enum(int sock, * Returns -EPIPE or -ECONNRESET if other end has hung up. */ int ustcomm_register_channel(int sock, + struct lttng_session *session, int session_objd, /* session descriptor */ int channel_objd, /* channel descriptor */ size_t nr_ctx_fields, diff --git a/liblttng-ust-comm/lttng-ust-comm.c b/liblttng-ust-comm/lttng-ust-comm.c index 1a4419f5..2c54a443 100644 --- a/liblttng-ust-comm/lttng-ust-comm.c +++ b/liblttng-ust-comm/lttng-ust-comm.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include "../liblttng-ust/compat.h" @@ -45,6 +46,14 @@ #define USTCOMM_MAX_SEND_FDS 4 +static +ssize_t count_fields_recursive(size_t nr_fields, + const struct lttng_event_field *lttng_fields); +static +int serialize_one_field(struct lttng_session *session, + struct ustctl_field *fields, size_t *iter_output, + const struct lttng_event_field *lf); + /* * Human readable error message. */ @@ -676,6 +685,86 @@ int ustcomm_send_reg_msg(int sock, return 0; } +static +ssize_t count_one_type(const struct lttng_type *lt) +{ + switch (lt->atype) { + case atype_integer: + case atype_float: + case atype_string: + case atype_enum: + case atype_array: + case atype_sequence: + return 1; + case atype_struct: + //TODO: implement non-empty struct. + return 1; + case atype_dynamic: + { + const struct lttng_event_field *choices; + size_t nr_choices; + int ret; + + ret = lttng_ust_dynamic_type_choices(&nr_choices, + &choices); + if (ret) + return ret; + /* + * One field for enum, one field for variant, and + * one field per choice. + */ + return count_fields_recursive(nr_choices, choices) + 2; + } + default: + return -EINVAL; + } + return 0; +} + +static +ssize_t count_fields_recursive(size_t nr_fields, + const struct lttng_event_field *lttng_fields) +{ + int i; + ssize_t ret, count = 0; + + for (i = 0; i < nr_fields; i++) { + const struct lttng_event_field *lf; + + lf = <tng_fields[i]; + /* skip 'nowrite' fields */ + if (lf->nowrite) + continue; + ret = count_one_type(&lf->type); + if (ret < 0) + return ret; /* error */ + count += ret; + } + return count; +} + +static +ssize_t count_ctx_fields_recursive(size_t nr_fields, + const struct lttng_ctx_field *lttng_fields) +{ + int i; + ssize_t ret, count = 0; + + for (i = 0; i < nr_fields; i++) { + const struct lttng_event_field *lf; + + lf = <tng_fields[i].event_field; + /* skip 'nowrite' fields */ + if (lf->nowrite) + continue; + ret = count_one_type(&lf->type); + if (ret < 0) + return ret; /* error */ + count += ret; + } + return count; +} + static int serialize_string_encoding(enum ustctl_string_encodings *ue, enum lttng_string_encodings le) @@ -778,27 +867,103 @@ int serialize_basic_type(struct lttng_session *session, } static -int serialize_one_type(struct lttng_session *session, - struct ustctl_type *ut, const struct lttng_type *lt) +int serialize_dynamic_type(struct lttng_session *session, + struct ustctl_field *fields, size_t *iter_output, + const struct lttng_event_field *lf) { + const struct lttng_event_field *choices; + char tag_field_name[LTTNG_UST_SYM_NAME_LEN]; + const struct lttng_type *tag_type; + const struct lttng_event_field *tag_field_generic; + struct lttng_event_field tag_field = { + .name = tag_field_name, + .nowrite = 0, + }; + struct ustctl_field *uf; + size_t nr_choices, i; int ret; + tag_field_generic = lttng_ust_dynamic_type_tag_field(); + tag_type = &tag_field_generic->type; + + /* Serialize enum field. */ + strncpy(tag_field_name, lf->name, LTTNG_UST_SYM_NAME_LEN); + tag_field_name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; + strncat(tag_field_name, + "_tag", + LTTNG_UST_SYM_NAME_LEN - strlen(tag_field_name) - 1); + tag_field.type = *tag_type; + ret = serialize_one_field(session, fields, iter_output, + &tag_field); + if (ret) + return ret; + + /* Serialize variant field. */ + uf = &fields[*iter_output]; + ret = lttng_ust_dynamic_type_choices(&nr_choices, &choices); + if (ret) + return ret; + + strncpy(uf->name, lf->name, LTTNG_UST_SYM_NAME_LEN); + uf->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; + uf->type.atype = ustctl_atype_variant; + uf->type.u.variant.nr_choices = nr_choices; + strncpy(uf->type.u.variant.tag_name, + tag_field_name, + LTTNG_UST_SYM_NAME_LEN); + uf->type.u.variant.tag_name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; + (*iter_output)++; + + /* Serialize choice fields after variant. */ + for (i = 0; i < nr_choices; i++) { + ret = serialize_one_field(session, fields, + iter_output, &choices[i]); + if (ret) + return ret; + } + return 0; +} + +static +int serialize_one_field(struct lttng_session *session, + struct ustctl_field *fields, size_t *iter_output, + const struct lttng_event_field *lf) +{ + const struct lttng_type *lt = &lf->type; + int ret; + + /* skip 'nowrite' fields */ + if (lf->nowrite) + return 0; + switch (lt->atype) { case atype_integer: case atype_float: case atype_string: case atype_enum: + { + struct ustctl_field *uf = &fields[*iter_output]; + struct ustctl_type *ut = &uf->type; + + strncpy(uf->name, lf->name, LTTNG_UST_SYM_NAME_LEN); + uf->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; ret = serialize_basic_type(session, &ut->atype, lt->atype, &ut->u.basic, <->u.basic); if (ret) return ret; + (*iter_output)++; break; + } case atype_array: { + struct ustctl_field *uf = &fields[*iter_output]; + struct ustctl_type *ut = &uf->type; struct ustctl_basic_type *ubt; const struct lttng_basic_type *lbt; - int ret; + strncpy(uf->name, lf->name, LTTNG_UST_SYM_NAME_LEN); + uf->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; + uf->type.atype = ustctl_atype_array; ubt = &ut->u.array.elem_type; lbt = <->u.array.elem_type; ut->u.array.length = lt->u.array.length; @@ -807,14 +972,20 @@ int serialize_one_type(struct lttng_session *session, if (ret) return -EINVAL; ut->atype = ustctl_atype_array; + (*iter_output)++; break; } case atype_sequence: { + struct ustctl_field *uf = &fields[*iter_output]; + struct ustctl_type *ut = &uf->type; struct ustctl_basic_type *ubt; const struct lttng_basic_type *lbt; int ret; + strncpy(uf->name, lf->name, LTTNG_UST_SYM_NAME_LEN); + uf->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; + uf->type.atype = ustctl_atype_sequence; ubt = &ut->u.sequence.length_type; lbt = <->u.sequence.length_type; ret = serialize_basic_type(session, &ubt->atype, lbt->atype, @@ -828,6 +999,31 @@ int serialize_one_type(struct lttng_session *session, if (ret) return -EINVAL; ut->atype = ustctl_atype_sequence; + (*iter_output)++; + break; + } + case atype_dynamic: + { + ret = serialize_dynamic_type(session, fields, iter_output, lf); + if (ret) + return -EINVAL; + break; + } + case atype_struct: + { + struct ustctl_field *uf = &fields[*iter_output]; + + /* + * TODO: add support for non-empty struct. + */ + if (lf->type.u._struct.nr_fields != 0) { + return -EINVAL; + } + strncpy(uf->name, lf->name, LTTNG_UST_SYM_NAME_LEN); + uf->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; + uf->type.atype = ustctl_atype_struct; + uf->type.u._struct.nr_fields = 0; + (*iter_output)++; break; } default: @@ -844,29 +1040,24 @@ int serialize_fields(struct lttng_session *session, const struct lttng_event_field *lttng_fields) { struct ustctl_field *fields; - int i, ret; - size_t nr_write_fields = 0; + int ret; + size_t i, iter_output = 0; + ssize_t nr_write_fields; + + nr_write_fields = count_fields_recursive(nr_fields, lttng_fields); + if (nr_write_fields < 0) { + return (int) nr_write_fields; + } - fields = zmalloc(nr_fields * sizeof(*fields)); + fields = zmalloc(nr_write_fields * sizeof(*fields)); if (!fields) return -ENOMEM; for (i = 0; i < nr_fields; i++) { - struct ustctl_field *f; - const struct lttng_event_field *lf; - - f = &fields[nr_write_fields]; - lf = <tng_fields[i]; - - /* skip 'nowrite' fields */ - if (lf->nowrite) - continue; - strncpy(f->name, lf->name, LTTNG_UST_SYM_NAME_LEN); - f->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; - ret = serialize_one_type(session, &f->type, &lf->type); + ret = serialize_one_field(session, fields, &iter_output, + <tng_fields[i]); if (ret) goto error_type; - nr_write_fields++; } *_nr_write_fields = nr_write_fields; @@ -907,35 +1098,32 @@ int serialize_entries(struct ustctl_enum_entry **_entries, } static -int serialize_ctx_fields(size_t *_nr_write_fields, +int serialize_ctx_fields(struct lttng_session *session, + size_t *_nr_write_fields, struct ustctl_field **ustctl_fields, size_t nr_fields, const struct lttng_ctx_field *lttng_fields) { struct ustctl_field *fields; - int i, ret; - size_t nr_write_fields = 0; + int ret; + size_t i, iter_output = 0; + ssize_t nr_write_fields; - fields = zmalloc(nr_fields * sizeof(*fields)); + nr_write_fields = count_ctx_fields_recursive(nr_fields, + lttng_fields); + if (nr_write_fields < 0) { + return (int) nr_write_fields; + } + + fields = zmalloc(nr_write_fields * sizeof(*fields)); if (!fields) return -ENOMEM; for (i = 0; i < nr_fields; i++) { - struct ustctl_field *f; - const struct lttng_event_field *lf; - - f = &fields[nr_write_fields]; - lf = <tng_fields[i].event_field; - - /* skip 'nowrite' fields */ - if (lf->nowrite) - continue; - strncpy(f->name, lf->name, LTTNG_UST_SYM_NAME_LEN); - f->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; - ret = serialize_one_type(NULL, &f->type, &lf->type); + ret = serialize_one_field(session, fields, &iter_output, + <tng_fields[i].event_field); if (ret) goto error_type; - nr_write_fields++; } *_nr_write_fields = nr_write_fields; @@ -1198,6 +1386,7 @@ error_entries: * Returns -EPIPE or -ECONNRESET if other end has hung up. */ int ustcomm_register_channel(int sock, + struct lttng_session *session, int session_objd, /* session descriptor */ int channel_objd, /* channel descriptor */ size_t nr_ctx_fields, @@ -1226,7 +1415,7 @@ int ustcomm_register_channel(int sock, /* Calculate fields len, serialize fields. */ if (nr_ctx_fields > 0) { - ret = serialize_ctx_fields(&nr_write_fields, &fields, + ret = serialize_ctx_fields(session, &nr_write_fields, &fields, nr_ctx_fields, ctx_fields); if (ret) return ret; diff --git a/liblttng-ust-ctl/ustctl.c b/liblttng-ust-ctl/ustctl.c index 75b32fe5..bbf47381 100644 --- a/liblttng-ust-ctl/ustctl.c +++ b/liblttng-ust-ctl/ustctl.c @@ -1071,7 +1071,7 @@ int ustctl_write_metadata_to_channel( chan->ops->packet_avail_size(chan->chan, chan->handle), len - pos); lib_ring_buffer_ctx_init(&ctx, chan->chan, NULL, reserve_len, - sizeof(char), -1, chan->handle); + sizeof(char), -1, chan->handle, NULL); /* * We don't care about metadata buffer's records lost * count, because we always retry here. Report error if @@ -1118,7 +1118,7 @@ ssize_t ustctl_write_one_packet_to_channel( chan->ops->packet_avail_size(chan->chan, chan->handle), len); lib_ring_buffer_ctx_init(&ctx, chan->chan, NULL, reserve_len, - sizeof(char), -1, chan->handle); + sizeof(char), -1, chan->handle, NULL); ret = chan->ops->event_reserve(&ctx, 0); if (ret != 0) { DBG("LTTng: event reservation failed"); diff --git a/liblttng-ust/Makefile.am b/liblttng-ust/Makefile.am index 698b4805..876e9b51 100644 --- a/liblttng-ust/Makefile.am +++ b/liblttng-ust/Makefile.am @@ -21,6 +21,7 @@ liblttng_ust_runtime_la_SOURCES = \ lttng-ust-comm.c \ lttng-ust-abi.c \ lttng-probes.c \ + lttng-context-provider.c \ lttng-context-vtid.c \ lttng-context-vpid.c \ lttng-context-pthread-id.c \ @@ -62,6 +63,7 @@ liblttng_ust_support_la_SOURCES = \ lttng-tracer.h \ lttng-tracer-core.h \ ust-core.c \ + lttng-ust-dynamic-type.c \ lttng-rb-clients.h \ lttng-ring-buffer-client.h \ lttng-ring-buffer-client-discard.c \ diff --git a/liblttng-ust/lttng-context-cpu-id.c b/liblttng-ust/lttng-context-cpu-id.c index fb6e407c..840cafe7 100644 --- a/liblttng-ust/lttng-context-cpu-id.c +++ b/liblttng-ust/lttng-context-cpu-id.c @@ -35,7 +35,7 @@ #include "../libringbuffer/getcpu.h" static -size_t cpu_id_get_size(size_t offset) +size_t cpu_id_get_size(struct lttng_ctx_field *field, size_t offset) { size_t size = 0; @@ -58,12 +58,12 @@ void cpu_id_record(struct lttng_ctx_field *field, static void cpu_id_get_value(struct lttng_ctx_field *field, - union lttng_ctx_value *value) + struct lttng_ctx_value *value) { int cpu; cpu = lttng_ust_get_cpu(); - value->s64 = cpu; + value->u.s64 = cpu; } int lttng_add_cpu_id_to_ctx(struct lttng_ctx **ctx) diff --git a/liblttng-ust/lttng-context-ip.c b/liblttng-ust/lttng-context-ip.c index 31283f17..84c73499 100644 --- a/liblttng-ust/lttng-context-ip.c +++ b/liblttng-ust/lttng-context-ip.c @@ -27,7 +27,7 @@ #include static -size_t ip_get_size(size_t offset) +size_t ip_get_size(struct lttng_ctx_field *field, size_t offset) { size_t size = 0; diff --git a/liblttng-ust/lttng-context-perf-counters.c b/liblttng-ust/lttng-context-perf-counters.c index 83b371c5..97ddf977 100644 --- a/liblttng-ust/lttng-context-perf-counters.c +++ b/liblttng-ust/lttng-context-perf-counters.c @@ -69,7 +69,7 @@ struct lttng_perf_counter_field { static pthread_key_t perf_counter_key; static -size_t perf_counter_get_size(size_t offset) +size_t perf_counter_get_size(struct lttng_ctx_field *field, size_t offset) { size_t size = 0; @@ -280,12 +280,12 @@ void perf_counter_record(struct lttng_ctx_field *field, static void perf_counter_get_value(struct lttng_ctx_field *field, - union lttng_ctx_value *value) + struct lttng_ctx_value *value) { uint64_t v; v = wrapper_perf_counter_read(field); - value->s64 = v; + value->u.s64 = v; } /* Called with UST lock held */ diff --git a/liblttng-ust/lttng-context-procname.c b/liblttng-ust/lttng-context-procname.c index 4d41593e..13461a68 100644 --- a/liblttng-ust/lttng-context-procname.c +++ b/liblttng-ust/lttng-context-procname.c @@ -55,7 +55,7 @@ void lttng_context_procname_reset(void) } static -size_t procname_get_size(size_t offset) +size_t procname_get_size(struct lttng_ctx_field *field, size_t offset) { size_t size = 0; @@ -76,12 +76,12 @@ void procname_record(struct lttng_ctx_field *field, static void procname_get_value(struct lttng_ctx_field *field, - union lttng_ctx_value *value) + struct lttng_ctx_value *value) { char *procname; procname = wrapper_getprocname(); - value->str = procname; + value->u.str = procname; } int lttng_add_procname_to_ctx(struct lttng_ctx **ctx) diff --git a/liblttng-ust/lttng-context-provider.c b/liblttng-ust/lttng-context-provider.c new file mode 100644 index 00000000..e7462cda --- /dev/null +++ b/liblttng-ust/lttng-context-provider.c @@ -0,0 +1,181 @@ +/* + * lttng-context-provider.c + * + * LTTng UST application context provider. + * + * Copyright (C) 2016 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 "lttng-tracer-core.h" +#include "jhash.h" +#include + +#define CONTEXT_PROVIDER_HT_BITS 12 +#define CONTEXT_PROVIDER_HT_SIZE (1U << CONTEXT_PROVIDER_HT_BITS) +struct context_provider_ht { + struct cds_hlist_head table[CONTEXT_PROVIDER_HT_SIZE]; +}; + +static struct context_provider_ht context_provider_ht; + +static struct lttng_ust_context_provider * + lookup_provider_by_name(const char *name) +{ + struct cds_hlist_head *head; + struct cds_hlist_node *node; + struct lttng_ust_context_provider *provider; + uint32_t hash; + const char *end; + size_t len; + + /* Lookup using everything before first ':' as key. */ + end = strchr(name, ':'); + if (end) + len = end - name; + else + len = strlen(name); + hash = jhash(name, len, 0); + head = &context_provider_ht.table[hash & (CONTEXT_PROVIDER_HT_SIZE - 1)]; + cds_hlist_for_each_entry(provider, node, head, node) { + if (!strncmp(provider->name, name, len)) + return provider; + } + return NULL; +} + +int lttng_ust_context_provider_register(struct lttng_ust_context_provider *provider) +{ + struct cds_hlist_head *head; + size_t name_len = strlen(provider->name); + uint32_t hash; + int ret = 0; + + /* Provider name starts with "$app.". */ + if (strncmp("$app.", provider->name, strlen("$app.") != 0)) + return -EINVAL; + /* Provider name cannot contain a column character. */ + if (strchr(provider->name, ':')) + return -EINVAL; + if (ust_lock()) { + ret = -EBUSY; + goto end; + } + if (lookup_provider_by_name(provider->name)) { + ret = -EBUSY; + goto end; + } + hash = jhash(provider->name, name_len, 0); + head = &context_provider_ht.table[hash & (CONTEXT_PROVIDER_HT_SIZE - 1)]; + cds_hlist_add_head(&provider->node, head); + lttng_ust_context_set_session_provider(provider->name, + provider->get_size, provider->record, + provider->get_value); +end: + ust_unlock(); + return ret; +} + +static +size_t dummy_get_size(struct lttng_ctx_field *field, size_t offset) +{ + size_t size = 0; + + size += lib_ring_buffer_align(offset, lttng_alignof(char)); + size += sizeof(char); /* tag */ + return size; +} + +static +void dummy_record(struct lttng_ctx_field *field, + struct lttng_ust_lib_ring_buffer_ctx *ctx, + struct lttng_channel *chan) +{ + char sel_char = (char) LTTNG_UST_DYNAMIC_TYPE_NONE; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(sel_char)); + chan->ops->event_write(ctx, &sel_char, sizeof(sel_char)); +} + +static +void dummy_get_value(struct lttng_ctx_field *field, + struct lttng_ctx_value *value) +{ + value->sel = LTTNG_UST_DYNAMIC_TYPE_NONE; +} + +void lttng_ust_context_provider_unregister(struct lttng_ust_context_provider *provider) +{ + if (ust_lock()) + goto end; + lttng_ust_context_set_session_provider(provider->name, + dummy_get_size, dummy_record, dummy_get_value); + cds_hlist_del(&provider->node); +end: + ust_unlock(); +} + +/* + * Called with ust mutex held. + * Add application context to array of context, even if the application + * context is not currently loaded by application. It will then use the + * dummy callbacks in that case. + * Always performed before tracing is started, since it modifies + * metadata describing the context. + */ +int lttng_ust_add_app_context_to_ctx_rcu(const char *name, + struct lttng_ctx **ctx) +{ + struct lttng_ust_context_provider *provider; + struct lttng_ctx_field new_field; + int ret; + + if (*ctx && lttng_find_context(*ctx, name)) + return -EEXIST; + /* + * For application context, add it by expanding + * ctx array. + */ + memset(&new_field, 0, sizeof(new_field)); + new_field.field_name = strdup(name); + if (!new_field.field_name) + return -ENOMEM; + new_field.event_field.name = new_field.field_name; + new_field.event_field.type.atype = atype_dynamic; + /* + * If provider is not found, we add the context anyway, but + * it will provide a dummy context. + */ + provider = lookup_provider_by_name(name); + if (provider) { + new_field.get_size = provider->get_size; + new_field.record = provider->record; + new_field.get_value = provider->get_value; + } else { + new_field.get_size = dummy_get_size; + new_field.record = dummy_record; + new_field.get_value = dummy_get_value; + } + ret = lttng_context_add_rcu(ctx, &new_field); + if (ret) { + free(new_field.field_name); + return ret; + } + return 0; +} diff --git a/liblttng-ust/lttng-context-pthread-id.c b/liblttng-ust/lttng-context-pthread-id.c index 2b90e7bc..c2c21157 100644 --- a/liblttng-ust/lttng-context-pthread-id.c +++ b/liblttng-ust/lttng-context-pthread-id.c @@ -26,7 +26,7 @@ #include static -size_t pthread_id_get_size(size_t offset) +size_t pthread_id_get_size(struct lttng_ctx_field *field, size_t offset) { size_t size = 0; @@ -49,12 +49,12 @@ void pthread_id_record(struct lttng_ctx_field *field, static void pthread_id_get_value(struct lttng_ctx_field *field, - union lttng_ctx_value *value) + struct lttng_ctx_value *value) { unsigned long pthread_id; pthread_id = (unsigned long) pthread_self(); - value->s64 = pthread_id; + value->u.s64 = pthread_id; } int lttng_add_pthread_id_to_ctx(struct lttng_ctx **ctx) diff --git a/liblttng-ust/lttng-context-vpid.c b/liblttng-ust/lttng-context-vpid.c index b54ada1d..7d8091be 100644 --- a/liblttng-ust/lttng-context-vpid.c +++ b/liblttng-ust/lttng-context-vpid.c @@ -62,7 +62,7 @@ void lttng_context_vpid_reset(void) #endif static -size_t vpid_get_size(size_t offset) +size_t vpid_get_size(struct lttng_ctx_field *field, size_t offset) { size_t size = 0; @@ -85,12 +85,12 @@ void vpid_record(struct lttng_ctx_field *field, static void vpid_get_value(struct lttng_ctx_field *field, - union lttng_ctx_value *value) + struct lttng_ctx_value *value) { pid_t pid; pid = wrapper_getpid(); - value->s64 = pid; + value->u.s64 = pid; } int lttng_add_vpid_to_ctx(struct lttng_ctx **ctx) diff --git a/liblttng-ust/lttng-context-vtid.c b/liblttng-ust/lttng-context-vtid.c index f9abadbb..d1c73d8d 100644 --- a/liblttng-ust/lttng-context-vtid.c +++ b/liblttng-ust/lttng-context-vtid.c @@ -46,7 +46,7 @@ void lttng_context_vtid_reset(void) } static -size_t vtid_get_size(size_t offset) +size_t vtid_get_size(struct lttng_ctx_field *field, size_t offset) { size_t size = 0; @@ -69,11 +69,11 @@ void vtid_record(struct lttng_ctx_field *field, static void vtid_get_value(struct lttng_ctx_field *field, - union lttng_ctx_value *value) + struct lttng_ctx_value *value) { if (caa_unlikely(!URCU_TLS(cached_vtid))) URCU_TLS(cached_vtid) = gettid(); - value->s64 = URCU_TLS(cached_vtid); + value->u.s64 = URCU_TLS(cached_vtid); } int lttng_add_vtid_to_ctx(struct lttng_ctx **ctx) diff --git a/liblttng-ust/lttng-context.c b/liblttng-ust/lttng-context.c index 199ec5d5..e2032162 100644 --- a/liblttng-ust/lttng-context.c +++ b/liblttng-ust/lttng-context.c @@ -23,6 +23,8 @@ #include #include +#include +#include #include #include #include @@ -33,41 +35,71 @@ * same context performed by the same thread return the same result. */ -/* - * Static array of contexts, for $ctx filters. - */ -struct lttng_ctx *lttng_static_ctx; - int lttng_find_context(struct lttng_ctx *ctx, const char *name) { unsigned int i; + const char *subname; + if (strncmp(name, "$ctx.", strlen("$ctx.")) == 0) { + subname = name + strlen("$ctx."); + } else { + subname = name; + } for (i = 0; i < ctx->nr_fields; i++) { /* Skip allocated (but non-initialized) contexts */ if (!ctx->fields[i].event_field.name) continue; - if (!strcmp(ctx->fields[i].event_field.name, name)) + if (!strcmp(ctx->fields[i].event_field.name, subname)) return 1; } return 0; } +int lttng_context_is_app(const char *name) +{ + if (strncmp(name, "$app.", strlen("$app.")) != 0) { + return 0; + } + return 1; +} + int lttng_get_context_index(struct lttng_ctx *ctx, const char *name) { unsigned int i; + const char *subname; if (!ctx) return -1; + if (strncmp(name, "$ctx.", strlen("$ctx.")) == 0) { + subname = name + strlen("$ctx."); + } else { + subname = name; + } for (i = 0; i < ctx->nr_fields; i++) { /* Skip allocated (but non-initialized) contexts */ if (!ctx->fields[i].event_field.name) continue; - if (!strcmp(ctx->fields[i].event_field.name, name)) + if (!strcmp(ctx->fields[i].event_field.name, subname)) return i; } return -1; } +static int lttng_find_context_provider(struct lttng_ctx *ctx, const char *name) +{ + unsigned int i; + + for (i = 0; i < ctx->nr_fields; i++) { + /* Skip allocated (but non-initialized) contexts */ + if (!ctx->fields[i].event_field.name) + continue; + if (!strncmp(ctx->fields[i].event_field.name, name, + strlen(name))) + return 1; + } + return 0; +} + /* * Note: as we append context information, the pointer location may change. */ @@ -100,6 +132,45 @@ struct lttng_ctx_field *lttng_append_context(struct lttng_ctx **ctx_p) return field; } +int lttng_context_add_rcu(struct lttng_ctx **ctx_p, + const struct lttng_ctx_field *f) +{ + struct lttng_ctx *old_ctx = *ctx_p, *new_ctx = NULL; + struct lttng_ctx_field *new_fields = NULL; + struct lttng_ctx_field *nf; + + if (old_ctx) { + new_ctx = zmalloc(sizeof(struct lttng_ctx)); + if (!new_ctx) + return -ENOMEM; + *new_ctx = *old_ctx; + new_fields = zmalloc(new_ctx->allocated_fields + * sizeof(struct lttng_ctx_field)); + if (!new_fields) { + free(new_ctx); + return -ENOMEM; + } + memcpy(new_fields, old_ctx->fields, + sizeof(*old_ctx->fields) * old_ctx->nr_fields); + new_ctx->fields = new_fields; + } + nf = lttng_append_context(&new_ctx); + if (!nf) { + free(new_fields); + free(new_ctx); + return -ENOMEM; + } + *nf = *f; + lttng_context_update(new_ctx); + rcu_assign_pointer(*ctx_p, new_ctx); + synchronize_trace(); + if (old_ctx) { + free(old_ctx->fields); + free(old_ctx); + } + return 0; +} + /* * lttng_context_update() should be called at least once between context * modification and trace start. @@ -177,7 +248,8 @@ void lttng_context_update(struct lttng_ctx *ctx) } case atype_string: break; - + case atype_dynamic: + break; case atype_enum: default: WARN_ON_ONCE(1); @@ -199,6 +271,7 @@ void lttng_remove_context_field(struct lttng_ctx **ctx_p, ctx = *ctx_p; ctx->nr_fields--; assert(&ctx->fields[ctx->nr_fields] == field); + assert(field->field_name == NULL); memset(&ctx->fields[ctx->nr_fields], 0, sizeof(struct lttng_ctx_field)); } @@ -211,39 +284,106 @@ void lttng_destroy_context(struct lttng_ctx *ctx) for (i = 0; i < ctx->nr_fields; i++) { if (ctx->fields[i].destroy) ctx->fields[i].destroy(&ctx->fields[i]); + free(ctx->fields[i].field_name); } free(ctx->fields); free(ctx); } -void lttng_context_init(void) +/* + * Can be safely performed concurrently with tracing using the struct + * lttng_ctx. Using RCU update. Needs to match RCU read-side handling of + * contexts. + * + * This does not allow adding, removing, or changing typing of the + * contexts, since this needs to stay invariant for metadata. However, + * it allows updating the handlers associated with all contexts matching + * a provider (by name) while tracing is using it, in a way that ensures + * a single RCU read-side critical section see either all old, or all + * new handlers. + */ +int lttng_ust_context_set_provider_rcu(struct lttng_ctx **_ctx, + const char *name, + size_t (*get_size)(struct lttng_ctx_field *field, size_t offset), + void (*record)(struct lttng_ctx_field *field, + struct lttng_ust_lib_ring_buffer_ctx *ctx, + struct lttng_channel *chan), + void (*get_value)(struct lttng_ctx_field *field, + struct lttng_ctx_value *value)) +{ + int i, ret; + struct lttng_ctx *ctx = *_ctx, *new_ctx; + struct lttng_ctx_field *new_fields; + + if (!ctx || !lttng_find_context_provider(ctx, name)) + return 0; + /* + * We have at least one instance of context for the provider. + */ + new_ctx = zmalloc(sizeof(*new_ctx)); + if (!new_ctx) + return -ENOMEM; + *new_ctx = *ctx; + new_fields = zmalloc(sizeof(*new_fields) * ctx->allocated_fields); + if (!new_fields) { + ret = -ENOMEM; + goto field_error; + } + memcpy(new_fields, ctx->fields, + sizeof(*new_fields) * ctx->allocated_fields); + for (i = 0; i < ctx->nr_fields; i++) { + if (strncmp(new_fields[i].event_field.name, + name, strlen(name)) != 0) + continue; + new_fields[i].get_size = get_size; + new_fields[i].record = record; + new_fields[i].get_value = get_value; + } + new_ctx->fields = new_fields; + rcu_assign_pointer(*_ctx, new_ctx); + synchronize_trace(); + free(ctx->fields); + free(ctx); + return 0; + +field_error: + free(new_ctx); + return ret; +} + +int lttng_session_context_init(struct lttng_ctx **ctx) { int ret; - ret = lttng_add_pthread_id_to_ctx(<tng_static_ctx); + ret = lttng_add_pthread_id_to_ctx(ctx); if (ret) { WARN("Cannot add context lttng_add_pthread_id_to_ctx"); + goto error; } - ret = lttng_add_vtid_to_ctx(<tng_static_ctx); + ret = lttng_add_vtid_to_ctx(ctx); if (ret) { WARN("Cannot add context lttng_add_vtid_to_ctx"); + goto error; } - ret = lttng_add_vpid_to_ctx(<tng_static_ctx); + ret = lttng_add_vpid_to_ctx(ctx); if (ret) { WARN("Cannot add context lttng_add_vpid_to_ctx"); + goto error; } - ret = lttng_add_procname_to_ctx(<tng_static_ctx); + ret = lttng_add_procname_to_ctx(ctx); if (ret) { WARN("Cannot add context lttng_add_procname_to_ctx"); + goto error; } - ret = lttng_add_cpu_id_to_ctx(<tng_static_ctx); + ret = lttng_add_cpu_id_to_ctx(ctx); if (ret) { WARN("Cannot add context lttng_add_cpu_id_to_ctx"); + goto error; } -} + lttng_context_update(*ctx); + return 0; -void lttng_context_exit(void) -{ - lttng_destroy_context(lttng_static_ctx); - lttng_static_ctx = NULL; +error: + lttng_destroy_context(*ctx); + return ret; } diff --git a/liblttng-ust/lttng-events.c b/liblttng-ust/lttng-events.c index eb85b50d..f6db6e8a 100644 --- a/liblttng-ust/lttng-events.c +++ b/liblttng-ust/lttng-events.c @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include "error.h" #include "compat.h" #include "lttng-ust-uuid.h" @@ -138,6 +140,10 @@ struct lttng_session *lttng_session_create(void) session = zmalloc(sizeof(struct lttng_session)); if (!session) return NULL; + if (lttng_session_context_init(&session->ctx)) { + free(session); + return NULL; + } CDS_INIT_LIST_HEAD(&session->chan_head); CDS_INIT_LIST_HEAD(&session->events_head); CDS_INIT_LIST_HEAD(&session->enums_head); @@ -236,9 +242,149 @@ void lttng_session_destroy(struct lttng_session *session) cds_list_for_each_entry_safe(chan, tmpchan, &session->chan_head, node) _lttng_channel_unmap(chan); cds_list_del(&session->node); + lttng_destroy_context(session->ctx); free(session); } +static +int lttng_enum_create(const struct lttng_enum_desc *desc, + struct lttng_session *session) +{ + const char *enum_name = desc->name; + struct lttng_enum *_enum; + struct cds_hlist_head *head; + struct cds_hlist_node *node; + int ret = 0; + size_t name_len = strlen(enum_name); + uint32_t hash; + int notify_socket; + + hash = jhash(enum_name, name_len, 0); + head = &session->enums_ht.table[hash & (LTTNG_UST_ENUM_HT_SIZE - 1)]; + cds_hlist_for_each_entry(_enum, node, head, hlist) { + assert(_enum->desc); + if (!strncmp(_enum->desc->name, desc->name, + LTTNG_UST_SYM_NAME_LEN - 1)) { + ret = -EEXIST; + goto exist; + } + } + + notify_socket = lttng_get_notify_socket(session->owner); + if (notify_socket < 0) { + ret = notify_socket; + goto socket_error; + } + + _enum = zmalloc(sizeof(*_enum)); + if (!_enum) { + ret = -ENOMEM; + goto cache_error; + } + _enum->session = session; + _enum->desc = desc; + + ret = ustcomm_register_enum(notify_socket, + session->objd, + enum_name, + desc->nr_entries, + desc->entries, + &_enum->id); + if (ret < 0) { + DBG("Error (%d) registering enumeration to sessiond", ret); + goto sessiond_register_error; + } + cds_list_add(&_enum->node, &session->enums_head); + cds_hlist_add_head(&_enum->hlist, head); + return 0; + +sessiond_register_error: + free(_enum); +cache_error: +socket_error: +exist: + return ret; +} + +static +int lttng_create_enum_check(const struct lttng_type *type, + struct lttng_session *session) +{ + switch (type->atype) { + case atype_enum: + { + const struct lttng_enum_desc *enum_desc; + int ret; + + enum_desc = type->u.basic.enumeration.desc; + ret = lttng_enum_create(enum_desc, session); + if (ret && ret != -EEXIST) { + DBG("Unable to create enum error: (%d)", ret); + return ret; + } + break; + } + case atype_dynamic: + { + const struct lttng_event_field *tag_field_generic; + const struct lttng_enum_desc *enum_desc; + int ret; + + tag_field_generic = lttng_ust_dynamic_type_tag_field(); + enum_desc = tag_field_generic->type.u.basic.enumeration.desc; + ret = lttng_enum_create(enum_desc, session); + if (ret && ret != -EEXIST) { + DBG("Unable to create enum error: (%d)", ret); + return ret; + } + break; + } + default: + /* TODO: nested types when they become supported. */ + break; + } + return 0; +} + +static +int lttng_create_all_event_enums(size_t nr_fields, + const struct lttng_event_field *event_fields, + struct lttng_session *session) +{ + size_t i; + int ret; + + /* For each field, ensure enum is part of the session. */ + for (i = 0; i < nr_fields; i++) { + const struct lttng_type *type = &event_fields[i].type; + + ret = lttng_create_enum_check(type, session); + if (ret) + return ret; + } + return 0; +} + +static +int lttng_create_all_ctx_enums(size_t nr_fields, + const struct lttng_ctx_field *ctx_fields, + struct lttng_session *session) +{ + size_t i; + int ret; + + /* For each field, ensure enum is part of the session. */ + for (i = 0; i < nr_fields; i++) { + const struct lttng_type *type = &ctx_fields[i].event_field.type; + + ret = lttng_create_enum_check(type, session); + if (ret) + return ret; + } + return 0; +} + + int lttng_session_enable(struct lttng_session *session) { int ret = 0; @@ -274,8 +420,15 @@ int lttng_session_enable(struct lttng_session *session) if (ctx) { nr_fields = ctx->nr_fields; fields = ctx->fields; + ret = lttng_create_all_ctx_enums(nr_fields, fields, + session); + if (ret < 0) { + DBG("Error (%d) adding enum to session", ret); + return ret; + } } ret = ustcomm_register_channel(notify_socket, + session, session->objd, chan->objd, nr_fields, @@ -358,101 +511,6 @@ end: return ret; } -static -int lttng_enum_create(const struct lttng_enum_desc *desc, - struct lttng_session *session) -{ - const char *enum_name = desc->name; - struct lttng_enum *_enum; - struct cds_hlist_head *head; - struct cds_hlist_node *node; - int ret = 0; - size_t name_len = strlen(enum_name); - uint32_t hash; - int notify_socket; - - hash = jhash(enum_name, name_len, 0); - head = &session->enums_ht.table[hash & (LTTNG_UST_ENUM_HT_SIZE - 1)]; - cds_hlist_for_each_entry(_enum, node, head, hlist) { - assert(_enum->desc); - if (!strncmp(_enum->desc->name, desc->name, - LTTNG_UST_SYM_NAME_LEN - 1)) { - ret = -EEXIST; - goto exist; - } - } - - notify_socket = lttng_get_notify_socket(session->owner); - if (notify_socket < 0) { - ret = notify_socket; - goto socket_error; - } - - _enum = zmalloc(sizeof(*_enum)); - if (!_enum) { - ret = -ENOMEM; - goto cache_error; - } - _enum->session = session; - _enum->desc = desc; - - ret = ustcomm_register_enum(notify_socket, - session->objd, - enum_name, - desc->nr_entries, - desc->entries, - &_enum->id); - if (ret < 0) { - DBG("Error (%d) registering enumeration to sessiond", ret); - goto sessiond_register_error; - } - cds_list_add(&_enum->node, &session->enums_head); - cds_hlist_add_head(&_enum->hlist, head); - return 0; - -sessiond_register_error: - free(_enum); -cache_error: -socket_error: -exist: - return ret; -} - -static -int lttng_event_create_all_enums(const struct lttng_event_desc *desc, - struct lttng_session *session) -{ - unsigned int nr_fields, i; - const struct lttng_event_field *fields; - - /* For each field, ensure enum is part of the session. */ - nr_fields = desc->nr_fields; - fields = desc->fields; - for (i = 0; i < nr_fields; i++) { - const struct lttng_type *type = &fields[i].type; - - switch (type->atype) { - case atype_enum: - { - const struct lttng_enum_desc *enum_desc; - int ret; - - enum_desc = type->u.basic.enumeration.desc; - ret = lttng_enum_create(enum_desc, session); - if (ret && ret != -EEXIST) { - DBG("Unable to create enum error: (%d)", ret); - return ret; - } - break; - } - default: - /* TODO: nested types when they become supported. */ - continue; - } - } - return 0; -} - /* * Supports event creation while tracing session is active. */ @@ -489,7 +547,8 @@ int lttng_event_create(const struct lttng_event_desc *desc, goto socket_error; } - ret = lttng_event_create_all_enums(desc, session); + ret = lttng_create_all_event_enums(desc->nr_fields, desc->fields, + session); if (ret < 0) { DBG("Error (%d) adding enum to session", ret); goto create_enum_error; @@ -1067,3 +1126,44 @@ void lttng_session_lazy_sync_enablers(struct lttng_session *session) return; lttng_session_sync_enablers(session); } + +/* + * Update all sessions with the given app context. + * Called with ust lock held. + * This is invoked when an application context gets loaded/unloaded. It + * ensures the context callbacks are in sync with the application + * context (either app context callbacks, or dummy callbacks). + */ +void lttng_ust_context_set_session_provider(const char *name, + size_t (*get_size)(struct lttng_ctx_field *field, size_t offset), + void (*record)(struct lttng_ctx_field *field, + struct lttng_ust_lib_ring_buffer_ctx *ctx, + struct lttng_channel *chan), + void (*get_value)(struct lttng_ctx_field *field, + struct lttng_ctx_value *value)) +{ + struct lttng_session *session; + + cds_list_for_each_entry(session, &sessions, node) { + struct lttng_channel *chan; + struct lttng_event *event; + int ret; + + ret = lttng_ust_context_set_provider_rcu(&session->ctx, + name, get_size, record, get_value); + if (ret) + abort(); + cds_list_for_each_entry(chan, &session->chan_head, node) { + ret = lttng_ust_context_set_provider_rcu(&chan->ctx, + name, get_size, record, get_value); + if (ret) + abort(); + } + cds_list_for_each_entry(event, &session->events_head, node) { + ret = lttng_ust_context_set_provider_rcu(&event->ctx, + name, get_size, record, get_value); + if (ret) + abort(); + } + } +} diff --git a/liblttng-ust/lttng-filter-interpreter.c b/liblttng-ust/lttng-filter-interpreter.c index df4add22..1f95dcf5 100644 --- a/liblttng-ust/lttng-filter-interpreter.c +++ b/liblttng-ust/lttng-filter-interpreter.c @@ -20,6 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include "lttng-filter.h" /* @@ -136,13 +137,17 @@ uint64_t lttng_filter_false(void *filter_data, (unsigned int) *(filter_opcode_t *) pc); \ switch (*(filter_opcode_t *) pc) { -#define OP(name) case name +#define OP(name) jump_target_##name: __attribute__((unused)); \ + case name #define PO break #define END_OP } \ } +#define JUMP_TO(name) \ + goto jump_target_##name + #else /* @@ -165,6 +170,9 @@ LABEL_##name #define END_OP +#define JUMP_TO(name) \ + goto LABEL_##name + #endif /* @@ -176,12 +184,14 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, const char *filter_stack_data) { struct bytecode_runtime *bytecode = filter_data; + struct lttng_session *session = bytecode->p.session; void *pc, *next_pc, *start_pc; int ret = -EINVAL; uint64_t retval = 0; struct estack _stack; struct estack *stack = &_stack; register int64_t ax = 0, bx = 0; + register enum entry_type ax_t = REG_UNKNOWN, bx_t = REG_UNKNOWN; register int top = FILTER_STACK_EMPTY; #ifndef INTERPRETER_USE_SWITCH static void *dispatch[NR_FILTER_OPS] = { @@ -292,7 +302,6 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, OP(FILTER_OP_UNKNOWN): OP(FILTER_OP_LOAD_FIELD_REF): - OP(FILTER_OP_GET_CONTEXT_REF): #ifdef INTERPRETER_USE_SWITCH default: #endif /* INTERPRETER_USE_SWITCH */ @@ -324,23 +333,362 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, goto end; OP(FILTER_OP_EQ): + { + /* Dynamic typing. */ + switch (estack_ax_t) { + case REG_S64: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_EQ_S64); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_EQ_DOUBLE_S64); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_DOUBLE: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_EQ_S64_DOUBLE); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_EQ_DOUBLE); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_STRING: + switch (estack_bx_t) { + case REG_S64: /* Fall-through */ + case REG_DOUBLE: + ret = -EINVAL; + goto end; + case REG_STRING: + JUMP_TO(FILTER_OP_EQ_STRING); + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + default: + ERR("Unknown filter register type (%d)", + (int) estack_ax_t); + ret = -EINVAL; + goto end; + } + } OP(FILTER_OP_NE): + { + /* Dynamic typing. */ + switch (estack_ax_t) { + case REG_S64: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_NE_S64); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_NE_DOUBLE_S64); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_DOUBLE: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_NE_S64_DOUBLE); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_NE_DOUBLE); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_STRING: + switch (estack_bx_t) { + case REG_S64: /* Fall-through */ + case REG_DOUBLE: + ret = -EINVAL; + goto end; + case REG_STRING: + JUMP_TO(FILTER_OP_NE_STRING); + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + default: + ERR("Unknown filter register type (%d)", + (int) estack_ax_t); + ret = -EINVAL; + goto end; + } + } OP(FILTER_OP_GT): + { + /* Dynamic typing. */ + switch (estack_ax_t) { + case REG_S64: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_GT_S64); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_GT_DOUBLE_S64); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_DOUBLE: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_GT_S64_DOUBLE); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_GT_DOUBLE); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_STRING: + switch (estack_bx_t) { + case REG_S64: /* Fall-through */ + case REG_DOUBLE: + ret = -EINVAL; + goto end; + case REG_STRING: + JUMP_TO(FILTER_OP_GT_STRING); + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + default: + ERR("Unknown filter register type (%d)", + (int) estack_ax_t); + ret = -EINVAL; + goto end; + } + } OP(FILTER_OP_LT): + { + /* Dynamic typing. */ + switch (estack_ax_t) { + case REG_S64: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_LT_S64); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_LT_DOUBLE_S64); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_DOUBLE: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_LT_S64_DOUBLE); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_LT_DOUBLE); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_STRING: + switch (estack_bx_t) { + case REG_S64: /* Fall-through */ + case REG_DOUBLE: + ret = -EINVAL; + goto end; + case REG_STRING: + JUMP_TO(FILTER_OP_LT_STRING); + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + default: + ERR("Unknown filter register type (%d)", + (int) estack_ax_t); + ret = -EINVAL; + goto end; + } + } OP(FILTER_OP_GE): + { + /* Dynamic typing. */ + switch (estack_ax_t) { + case REG_S64: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_GE_S64); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_GE_DOUBLE_S64); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_DOUBLE: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_GE_S64_DOUBLE); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_GE_DOUBLE); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_STRING: + switch (estack_bx_t) { + case REG_S64: /* Fall-through */ + case REG_DOUBLE: + ret = -EINVAL; + goto end; + case REG_STRING: + JUMP_TO(FILTER_OP_GE_STRING); + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + default: + ERR("Unknown filter register type (%d)", + (int) estack_ax_t); + ret = -EINVAL; + goto end; + } + } OP(FILTER_OP_LE): - ERR("unsupported non-specialized bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; + { + /* Dynamic typing. */ + switch (estack_ax_t) { + case REG_S64: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_LE_S64); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_LE_DOUBLE_S64); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_DOUBLE: + switch (estack_bx_t) { + case REG_S64: + JUMP_TO(FILTER_OP_LE_S64_DOUBLE); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_LE_DOUBLE); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + case REG_STRING: + switch (estack_bx_t) { + case REG_S64: /* Fall-through */ + case REG_DOUBLE: + ret = -EINVAL; + goto end; + case REG_STRING: + JUMP_TO(FILTER_OP_LE_STRING); + default: + ERR("Unknown filter register type (%d)", + (int) estack_bx_t); + ret = -EINVAL; + goto end; + } + break; + default: + ERR("Unknown filter register type (%d)", + (int) estack_ax_t); + ret = -EINVAL; + goto end; + } + } OP(FILTER_OP_EQ_STRING): { int res; res = (stack_strcmp(stack, top, "==") == 0); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -349,8 +697,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (stack_strcmp(stack, top, "!=") != 0); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -359,8 +708,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (stack_strcmp(stack, top, ">") > 0); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -369,8 +719,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (stack_strcmp(stack, top, "<") < 0); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -379,8 +730,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (stack_strcmp(stack, top, ">=") >= 0); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -389,8 +741,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (stack_strcmp(stack, top, "<=") <= 0); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -400,8 +753,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v == estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -410,8 +764,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v != estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -420,8 +775,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v > estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -430,8 +786,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v < estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -440,8 +797,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v >= estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -450,8 +808,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v <= estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -461,8 +820,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d == estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -471,8 +831,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d != estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -481,8 +842,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d > estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -491,8 +853,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d < estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -501,8 +864,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d >= estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -511,8 +875,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d <= estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -523,8 +888,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d == estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -533,8 +899,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d != estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -543,8 +910,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d > estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -553,8 +921,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d < estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -563,8 +932,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d >= estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -573,8 +943,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx(stack, top)->u.d <= estack_ax_v); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -584,8 +955,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v == estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -594,8 +966,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v != estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -604,8 +977,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v > estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -614,8 +988,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v < estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -624,8 +999,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v >= estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } @@ -634,21 +1010,70 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, int res; res = (estack_bx_v <= estack_ax(stack, top)->u.d); - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = res; + estack_ax_t = REG_S64; next_pc += sizeof(struct binary_op); PO; } /* unary */ OP(FILTER_OP_UNARY_PLUS): + { + /* Dynamic typing. */ + switch (estack_ax_t) { + case REG_S64: /* Fall-through. */ + JUMP_TO(FILTER_OP_UNARY_PLUS_S64); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_UNARY_PLUS_DOUBLE); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_ax_t); + ret = -EINVAL; + goto end; + } + } OP(FILTER_OP_UNARY_MINUS): + { + /* Dynamic typing. */ + switch (estack_ax_t) { + case REG_S64: + JUMP_TO(FILTER_OP_UNARY_MINUS_S64); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_UNARY_MINUS_DOUBLE); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_ax_t); + ret = -EINVAL; + goto end; + } + } OP(FILTER_OP_UNARY_NOT): - ERR("unsupported non-specialized bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; - + { + /* Dynamic typing. */ + switch (estack_ax_t) { + case REG_S64: + JUMP_TO(FILTER_OP_UNARY_NOT_S64); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_UNARY_NOT_DOUBLE); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_ax_t); + ret = -EINVAL; + goto end; + } + next_pc += sizeof(struct unary_op); + PO; + } OP(FILTER_OP_UNARY_PLUS_S64): OP(FILTER_OP_UNARY_PLUS_DOUBLE): @@ -676,7 +1101,8 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, } OP(FILTER_OP_UNARY_NOT_DOUBLE): { - estack_ax(stack, top)->u.d = !estack_ax(stack, top)->u.d; + estack_ax_v = !estack_ax(stack, top)->u.d; + estack_ax_t = REG_S64; next_pc += sizeof(struct unary_op); PO; } @@ -686,6 +1112,10 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, { struct logical_op *insn = (struct logical_op *) pc; + if (estack_ax_t != REG_S64) { + ret = -EINVAL; + goto end; + } /* If AX is 0, skip and evaluate to 0 */ if (unlikely(estack_ax_v == 0)) { dbg_printf("Jumping to bytecode offset %u\n", @@ -693,7 +1123,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, next_pc = start_pc + insn->skip_offset; } else { /* Pop 1 when jump not taken */ - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); next_pc += sizeof(struct logical_op); } PO; @@ -702,8 +1132,11 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, { struct logical_op *insn = (struct logical_op *) pc; + if (estack_ax_t != REG_S64) { + ret = -EINVAL; + goto end; + } /* If AX is nonzero, skip and evaluate to 1 */ - if (unlikely(estack_ax_v != 0)) { estack_ax_v = 1; dbg_printf("Jumping to bytecode offset %u\n", @@ -711,7 +1144,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, next_pc = start_pc + insn->skip_offset; } else { /* Pop 1 when jump not taken */ - estack_pop(stack, top, ax, bx); + estack_pop(stack, top, ax, bx, ax_t, bx_t); next_pc += sizeof(struct logical_op); } PO; @@ -726,7 +1159,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, dbg_printf("load field ref offset %u type string\n", ref->offset); - estack_push(stack, top, ax, bx); + estack_push(stack, top, ax, bx, ax_t, bx_t); estack_ax(stack, top)->u.s.str = *(const char * const *) &filter_stack_data[ref->offset]; if (unlikely(!estack_ax(stack, top)->u.s.str)) { @@ -736,6 +1169,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, } estack_ax(stack, top)->u.s.seq_len = UINT_MAX; estack_ax(stack, top)->u.s.literal = 0; + estack_ax_t = REG_STRING; dbg_printf("ref load string %s\n", estack_ax(stack, top)->u.s.str); next_pc += sizeof(struct load_op) + sizeof(struct field_ref); PO; @@ -748,12 +1182,13 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, dbg_printf("load field ref offset %u type sequence\n", ref->offset); - estack_push(stack, top, ax, bx); + estack_push(stack, top, ax, bx, ax_t, bx_t); estack_ax(stack, top)->u.s.seq_len = *(unsigned long *) &filter_stack_data[ref->offset]; estack_ax(stack, top)->u.s.str = *(const char **) (&filter_stack_data[ref->offset + sizeof(unsigned long)]); + estack_ax_t = REG_STRING; if (unlikely(!estack_ax(stack, top)->u.s.str)) { dbg_printf("Filter warning: loading a NULL sequence.\n"); ret = -EINVAL; @@ -771,9 +1206,10 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, dbg_printf("load field ref offset %u type s64\n", ref->offset); - estack_push(stack, top, ax, bx); + estack_push(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = ((struct literal_numeric *) &filter_stack_data[ref->offset])->v; + estack_ax_t = REG_S64; dbg_printf("ref load s64 %" PRIi64 "\n", estack_ax_v); next_pc += sizeof(struct load_op) + sizeof(struct field_ref); PO; @@ -786,9 +1222,10 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, dbg_printf("load field ref offset %u type double\n", ref->offset); - estack_push(stack, top, ax, bx); + estack_push(stack, top, ax, bx, ax_t, bx_t); memcpy(&estack_ax(stack, top)->u.d, &filter_stack_data[ref->offset], sizeof(struct literal_double)); + estack_ax_t = REG_DOUBLE; dbg_printf("ref load double %g\n", estack_ax(stack, top)->u.d); next_pc += sizeof(struct load_op) + sizeof(struct field_ref); PO; @@ -800,10 +1237,11 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, struct load_op *insn = (struct load_op *) pc; dbg_printf("load string %s\n", insn->data); - estack_push(stack, top, ax, bx); + estack_push(stack, top, ax, bx, ax_t, bx_t); estack_ax(stack, top)->u.s.str = insn->data; estack_ax(stack, top)->u.s.seq_len = UINT_MAX; estack_ax(stack, top)->u.s.literal = 1; + estack_ax_t = REG_STRING; next_pc += sizeof(struct load_op) + strlen(insn->data) + 1; PO; } @@ -812,8 +1250,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, { struct load_op *insn = (struct load_op *) pc; - estack_push(stack, top, ax, bx); + estack_push(stack, top, ax, bx, ax_t, bx_t); estack_ax_v = ((struct literal_numeric *) insn->data)->v; + estack_ax_t = REG_S64; dbg_printf("load s64 %" PRIi64 "\n", estack_ax_v); next_pc += sizeof(struct load_op) + sizeof(struct literal_numeric); @@ -824,10 +1263,11 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, { struct load_op *insn = (struct load_op *) pc; - estack_push(stack, top, ax, bx); + estack_push(stack, top, ax, bx, ax_t, bx_t); memcpy(&estack_ax(stack, top)->u.d, insn->data, sizeof(struct literal_double)); - dbg_printf("load s64 %g\n", estack_ax(stack, top)->u.d); + estack_ax_t = REG_DOUBLE; + dbg_printf("load double %g\n", estack_ax(stack, top)->u.d); next_pc += sizeof(struct load_op) + sizeof(struct literal_double); PO; @@ -835,14 +1275,28 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, /* cast */ OP(FILTER_OP_CAST_TO_S64): - ERR("unsupported non-specialized bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; + { + /* Dynamic typing. */ + switch (estack_ax_t) { + case REG_S64: + JUMP_TO(FILTER_OP_CAST_NOP); + case REG_DOUBLE: + JUMP_TO(FILTER_OP_CAST_DOUBLE_TO_S64); + case REG_STRING: + ret = -EINVAL; + goto end; + default: + ERR("Unknown filter register type (%d)", + (int) estack_ax_t); + ret = -EINVAL; + goto end; + } + } OP(FILTER_OP_CAST_DOUBLE_TO_S64): { estack_ax_v = (int64_t) estack_ax(stack, top)->u.d; + estack_ax_t = REG_S64; next_pc += sizeof(struct cast_op); PO; } @@ -854,19 +1308,70 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, } /* get context ref */ + OP(FILTER_OP_GET_CONTEXT_REF): + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + struct lttng_ctx *ctx; + struct lttng_ctx_field *ctx_field; + struct lttng_ctx_value v; + + dbg_printf("get context ref offset %u type dynamic\n", + ref->offset); + ctx = rcu_dereference(session->ctx); + ctx_field = &ctx->fields[ref->offset]; + ctx_field->get_value(ctx_field, &v); + estack_push(stack, top, ax, bx, ax_t, bx_t); + switch (v.sel) { + case LTTNG_UST_DYNAMIC_TYPE_NONE: + ret = -EINVAL; + goto end; + case LTTNG_UST_DYNAMIC_TYPE_S64: + estack_ax_v = v.u.s64; + estack_ax_t = REG_S64; + dbg_printf("ref get context dynamic s64 %" PRIi64 "\n", estack_ax_v); + break; + case LTTNG_UST_DYNAMIC_TYPE_DOUBLE: + estack_ax(stack, top)->u.d = v.u.d; + estack_ax_t = REG_DOUBLE; + dbg_printf("ref get context dynamic double %g\n", estack_ax(stack, top)->u.d); + break; + case LTTNG_UST_DYNAMIC_TYPE_STRING: + estack_ax(stack, top)->u.s.str = v.u.str; + if (unlikely(!estack_ax(stack, top)->u.s.str)) { + dbg_printf("Filter warning: loading a NULL string.\n"); + ret = -EINVAL; + goto end; + } + estack_ax(stack, top)->u.s.seq_len = UINT_MAX; + estack_ax(stack, top)->u.s.literal = 0; + dbg_printf("ref get context dynamic string %s\n", estack_ax(stack, top)->u.s.str); + estack_ax_t = REG_STRING; + break; + default: + dbg_printf("Filter warning: unknown dynamic type (%d).\n", (int) v.sel); + ret = -EINVAL; + goto end; + } + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + PO; + } + OP(FILTER_OP_GET_CONTEXT_REF_STRING): { struct load_op *insn = (struct load_op *) pc; struct field_ref *ref = (struct field_ref *) insn->data; + struct lttng_ctx *ctx; struct lttng_ctx_field *ctx_field; - union lttng_ctx_value v; + struct lttng_ctx_value v; dbg_printf("get context ref offset %u type string\n", ref->offset); - ctx_field = <tng_static_ctx->fields[ref->offset]; + ctx = rcu_dereference(session->ctx); + ctx_field = &ctx->fields[ref->offset]; ctx_field->get_value(ctx_field, &v); - estack_push(stack, top, ax, bx); - estack_ax(stack, top)->u.s.str = v.str; + estack_push(stack, top, ax, bx, ax_t, bx_t); + estack_ax(stack, top)->u.s.str = v.u.str; if (unlikely(!estack_ax(stack, top)->u.s.str)) { dbg_printf("Filter warning: loading a NULL string.\n"); ret = -EINVAL; @@ -874,6 +1379,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, } estack_ax(stack, top)->u.s.seq_len = UINT_MAX; estack_ax(stack, top)->u.s.literal = 0; + estack_ax_t = REG_STRING; dbg_printf("ref get context string %s\n", estack_ax(stack, top)->u.s.str); next_pc += sizeof(struct load_op) + sizeof(struct field_ref); PO; @@ -883,15 +1389,18 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, { struct load_op *insn = (struct load_op *) pc; struct field_ref *ref = (struct field_ref *) insn->data; + struct lttng_ctx *ctx; struct lttng_ctx_field *ctx_field; - union lttng_ctx_value v; + struct lttng_ctx_value v; dbg_printf("get context ref offset %u type s64\n", ref->offset); - ctx_field = <tng_static_ctx->fields[ref->offset]; + ctx = rcu_dereference(session->ctx); + ctx_field = &ctx->fields[ref->offset]; ctx_field->get_value(ctx_field, &v); - estack_push(stack, top, ax, bx); - estack_ax_v = v.s64; + estack_push(stack, top, ax, bx, ax_t, bx_t); + estack_ax_v = v.u.s64; + estack_ax_t = REG_S64; dbg_printf("ref get context s64 %" PRIi64 "\n", estack_ax_v); next_pc += sizeof(struct load_op) + sizeof(struct field_ref); PO; @@ -901,15 +1410,18 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, { struct load_op *insn = (struct load_op *) pc; struct field_ref *ref = (struct field_ref *) insn->data; + struct lttng_ctx *ctx; struct lttng_ctx_field *ctx_field; - union lttng_ctx_value v; + struct lttng_ctx_value v; dbg_printf("get context ref offset %u type double\n", ref->offset); - ctx_field = <tng_static_ctx->fields[ref->offset]; + ctx = rcu_dereference(session->ctx); + ctx_field = &ctx->fields[ref->offset]; ctx_field->get_value(ctx_field, &v); - estack_push(stack, top, ax, bx); - memcpy(&estack_ax(stack, top)->u.d, &v.d, sizeof(struct literal_double)); + estack_push(stack, top, ax, bx, ax_t, bx_t); + memcpy(&estack_ax(stack, top)->u.d, &v.u.d, sizeof(struct literal_double)); + estack_ax_t = REG_DOUBLE; dbg_printf("ref get context double %g\n", estack_ax(stack, top)->u.d); next_pc += sizeof(struct load_op) + sizeof(struct field_ref); PO; diff --git a/liblttng-ust/lttng-filter-specialize.c b/liblttng-ust/lttng-filter-specialize.c index a729e427..dddb448e 100644 --- a/liblttng-ust/lttng-filter-specialize.c +++ b/liblttng-ust/lttng-filter-specialize.c @@ -73,20 +73,28 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) goto end; case REG_STRING: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; insn->op = FILTER_OP_EQ_STRING; break; case REG_S64: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_EQ_S64; else insn->op = FILTER_OP_EQ_DOUBLE_S64; break; case REG_DOUBLE: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_EQ_S64_DOUBLE; else insn->op = FILTER_OP_EQ_DOUBLE; break; + case REG_UNKNOWN: + break; /* Dynamic typing. */ } /* Pop 2, push 1 */ if (vstack_pop(stack)) { @@ -109,20 +117,28 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) goto end; case REG_STRING: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; insn->op = FILTER_OP_NE_STRING; break; case REG_S64: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_NE_S64; else insn->op = FILTER_OP_NE_DOUBLE_S64; break; case REG_DOUBLE: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_NE_S64_DOUBLE; else insn->op = FILTER_OP_NE_DOUBLE; break; + case REG_UNKNOWN: + break; /* Dynamic typing. */ } /* Pop 2, push 1 */ if (vstack_pop(stack)) { @@ -145,20 +161,28 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) goto end; case REG_STRING: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; insn->op = FILTER_OP_GT_STRING; break; case REG_S64: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_GT_S64; else insn->op = FILTER_OP_GT_DOUBLE_S64; break; case REG_DOUBLE: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_GT_S64_DOUBLE; else insn->op = FILTER_OP_GT_DOUBLE; break; + case REG_UNKNOWN: + break; /* Dynamic typing. */ } /* Pop 2, push 1 */ if (vstack_pop(stack)) { @@ -181,20 +205,28 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) goto end; case REG_STRING: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; insn->op = FILTER_OP_LT_STRING; break; case REG_S64: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_LT_S64; else insn->op = FILTER_OP_LT_DOUBLE_S64; break; case REG_DOUBLE: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_LT_S64_DOUBLE; else insn->op = FILTER_OP_LT_DOUBLE; break; + case REG_UNKNOWN: + break; /* Dynamic typing. */ } /* Pop 2, push 1 */ if (vstack_pop(stack)) { @@ -217,20 +249,28 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) goto end; case REG_STRING: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; insn->op = FILTER_OP_GE_STRING; break; case REG_S64: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_GE_S64; else insn->op = FILTER_OP_GE_DOUBLE_S64; break; case REG_DOUBLE: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_GE_S64_DOUBLE; else insn->op = FILTER_OP_GE_DOUBLE; break; + case REG_UNKNOWN: + break; /* Dynamic typing. */ } /* Pop 2, push 1 */ if (vstack_pop(stack)) { @@ -252,20 +292,28 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) goto end; case REG_STRING: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; insn->op = FILTER_OP_LE_STRING; break; case REG_S64: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_LE_S64; else insn->op = FILTER_OP_LE_DOUBLE_S64; break; case REG_DOUBLE: + if (vstack_bx(stack)->type == REG_UNKNOWN) + break; if (vstack_bx(stack)->type == REG_S64) insn->op = FILTER_OP_LE_S64_DOUBLE; else insn->op = FILTER_OP_LE_DOUBLE; break; + case REG_UNKNOWN: + break; /* Dynamic typing. */ } vstack_ax(stack)->type = REG_S64; next_pc += sizeof(struct binary_op); @@ -330,6 +378,8 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) case REG_DOUBLE: insn->op = FILTER_OP_UNARY_PLUS_DOUBLE; break; + case REG_UNKNOWN: /* Dynamic typing. */ + break; } /* Pop 1, push 1 */ next_pc += sizeof(struct unary_op); @@ -352,6 +402,8 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) case REG_DOUBLE: insn->op = FILTER_OP_UNARY_MINUS_DOUBLE; break; + case REG_UNKNOWN: /* Dynamic typing. */ + break; } /* Pop 1, push 1 */ next_pc += sizeof(struct unary_op); @@ -374,6 +426,8 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) case REG_DOUBLE: insn->op = FILTER_OP_UNARY_NOT_DOUBLE; break; + case REG_UNKNOWN: /* Dynamic typing. */ + break; } /* Pop 1, push 1 */ next_pc += sizeof(struct unary_op); @@ -416,9 +470,13 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) /* get context ref */ case FILTER_OP_GET_CONTEXT_REF: { - ERR("Unknown get context ref type\n"); - ret = -EINVAL; - goto end; + if (vstack_push(stack)) { + ret = -EINVAL; + goto end; + } + vstack_ax(stack)->type = REG_UNKNOWN; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; } case FILTER_OP_LOAD_FIELD_REF_STRING: case FILTER_OP_LOAD_FIELD_REF_SEQUENCE: @@ -514,6 +572,8 @@ int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) case REG_DOUBLE: insn->op = FILTER_OP_CAST_DOUBLE_TO_S64; break; + case REG_UNKNOWN: + break; } /* Pop 1, push 1 */ vstack_ax(stack)->type = REG_S64; diff --git a/liblttng-ust/lttng-filter-validator.c b/liblttng-ust/lttng-filter-validator.c index 6cdfd8c1..dc50774d 100644 --- a/liblttng-ust/lttng-filter-validator.c +++ b/liblttng-ust/lttng-filter-validator.c @@ -74,7 +74,9 @@ int merge_points_compare(const struct vstack *stacka, len = stacka->top + 1; assert(len >= 0); for (i = 0; i < len; i++) { - if (stacka->e[i].type != stackb->e[i].type) + if (stacka->e[i].type != REG_UNKNOWN + && stackb->e[i].type != REG_UNKNOWN + && stacka->e[i].type != stackb->e[i].type) return 1; } return 0; @@ -118,22 +120,28 @@ int merge_point_add_check(struct cds_lfht *ht, unsigned long target_pc, /* * Binary comparators use top of stack and top of stack -1. + * Return 0 if typing is known to match, 1 if typing is dynamic + * (unknown), negative error value on error. */ static int bin_op_compare_check(struct vstack *stack, const char *str) { if (unlikely(!vstack_ax(stack) || !vstack_bx(stack))) - goto error_unknown; + goto error_empty; switch (vstack_ax(stack)->type) { default: - goto error_unknown; + goto error_type; + case REG_UNKNOWN: + goto unknown; case REG_STRING: switch (vstack_bx(stack)->type) { default: - goto error_unknown; + goto error_type; + case REG_UNKNOWN: + goto unknown; case REG_STRING: break; case REG_S64: @@ -145,11 +153,12 @@ int bin_op_compare_check(struct vstack *stack, const char *str) case REG_DOUBLE: switch (vstack_bx(stack)->type) { default: - goto error_unknown; + goto error_type; + case REG_UNKNOWN: + goto unknown; case REG_STRING: goto error_mismatch; - case REG_S64: case REG_DOUBLE: break; @@ -158,12 +167,20 @@ int bin_op_compare_check(struct vstack *stack, const char *str) } return 0; -error_unknown: - return -EINVAL; +unknown: + return 1; error_mismatch: ERR("type mismatch for '%s' binary operator\n", str); return -EINVAL; + +error_empty: + ERR("empty stack for '%s' binary operator\n", str); + return -EINVAL; + +error_type: + ERR("unknown type for '%s' binary operator\n", str); + return -EINVAL; } /* @@ -295,11 +312,6 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode, } /* get context ref */ case FILTER_OP_GET_CONTEXT_REF: - { - ERR("Unknown field ref type\n"); - ret = -EINVAL; - break; - } case FILTER_OP_LOAD_FIELD_REF_STRING: case FILTER_OP_LOAD_FIELD_REF_SEQUENCE: case FILTER_OP_LOAD_FIELD_REF_S64: @@ -391,7 +403,7 @@ unsigned long delete_all_nodes(struct cds_lfht *ht) /* * Return value: - * 0: success + * >=0: success * <0: error */ static @@ -438,42 +450,42 @@ int validate_instruction_context(struct bytecode_runtime *bytecode, case FILTER_OP_EQ: { ret = bin_op_compare_check(stack, "=="); - if (ret) + if (ret < 0) goto end; break; } case FILTER_OP_NE: { ret = bin_op_compare_check(stack, "!="); - if (ret) + if (ret < 0) goto end; break; } case FILTER_OP_GT: { ret = bin_op_compare_check(stack, ">"); - if (ret) + if (ret < 0) goto end; break; } case FILTER_OP_LT: { ret = bin_op_compare_check(stack, "<"); - if (ret) + if (ret < 0) goto end; break; } case FILTER_OP_GE: { ret = bin_op_compare_check(stack, ">="); - if (ret) + if (ret < 0) goto end; break; } case FILTER_OP_LE: { ret = bin_op_compare_check(stack, "<="); - if (ret) + if (ret < 0) goto end; break; } @@ -604,6 +616,8 @@ int validate_instruction_context(struct bytecode_runtime *bytecode, break; case REG_DOUBLE: break; + case REG_UNKNOWN: + break; } break; } @@ -653,8 +667,9 @@ int validate_instruction_context(struct bytecode_runtime *bytecode, ret = -EINVAL; goto end; } - if (vstack_ax(stack)->type != REG_S64) { - ERR("Logical comparator expects S64 register\n"); + if (vstack_ax(stack)->type != REG_S64 + && vstack_ax(stack)->type != REG_UNKNOWN) { + ERR("Logical comparator expects S64 or dynamic register\n"); ret = -EINVAL; goto end; } @@ -745,6 +760,8 @@ int validate_instruction_context(struct bytecode_runtime *bytecode, break; case REG_DOUBLE: break; + case REG_UNKNOWN: + break; } if (insn->op == FILTER_OP_CAST_DOUBLE_TO_S64) { if (vstack_ax(stack)->type != REG_DOUBLE) { @@ -763,9 +780,12 @@ int validate_instruction_context(struct bytecode_runtime *bytecode, /* get context ref */ case FILTER_OP_GET_CONTEXT_REF: { - ERR("Unknown get context ref type\n"); - ret = -EINVAL; - goto end; + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + dbg_printf("Validate get context ref offset %u type dynamic\n", + ref->offset); + break; } case FILTER_OP_GET_CONTEXT_REF_STRING: { @@ -821,7 +841,7 @@ int validate_instruction_all_contexts(struct bytecode_runtime *bytecode, /* Validate the context resulting from the previous instruction */ ret = validate_instruction_context(bytecode, stack, start_pc, pc); - if (ret) + if (ret < 0) return ret; /* Validate merge points */ @@ -959,10 +979,23 @@ int exec_insn(struct bytecode_runtime *bytecode, /* unary */ case FILTER_OP_UNARY_PLUS: case FILTER_OP_UNARY_MINUS: - case FILTER_OP_UNARY_NOT: + { + /* Pop 1, push 1 */ + if (!vstack_ax(stack)) { + ERR("Empty stack\n"); + ret = -EINVAL; + goto end; + } + vstack_ax(stack)->type = REG_UNKNOWN; + next_pc += sizeof(struct unary_op); + break; + } + case FILTER_OP_UNARY_PLUS_S64: case FILTER_OP_UNARY_MINUS_S64: + case FILTER_OP_UNARY_NOT: case FILTER_OP_UNARY_NOT_S64: + case FILTER_OP_UNARY_NOT_DOUBLE: { /* Pop 1, push 1 */ if (!vstack_ax(stack)) { @@ -977,7 +1010,6 @@ int exec_insn(struct bytecode_runtime *bytecode, case FILTER_OP_UNARY_PLUS_DOUBLE: case FILTER_OP_UNARY_MINUS_DOUBLE: - case FILTER_OP_UNARY_NOT_DOUBLE: { /* Pop 1, push 1 */ if (!vstack_ax(stack)) { @@ -1024,9 +1056,13 @@ int exec_insn(struct bytecode_runtime *bytecode, /* get context ref */ case FILTER_OP_GET_CONTEXT_REF: { - ERR("Unknown get context ref type\n"); - ret = -EINVAL; - goto end; + if (vstack_push(stack)) { + ret = -EINVAL; + goto end; + } + vstack_ax(stack)->type = REG_UNKNOWN; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; } case FILTER_OP_LOAD_FIELD_REF_STRING: case FILTER_OP_LOAD_FIELD_REF_SEQUENCE: @@ -1171,7 +1207,7 @@ int lttng_filter_validate_bytecode(struct bytecode_runtime *bytecode) /* * For each instruction, validate the current context * (traversal of entire execution flow), and validate - * all merge points targeting this instruction. + * all merge points targeting this instruction. */ ret = validate_instruction_all_contexts(bytecode, merge_points, &stack, start_pc, pc); diff --git a/liblttng-ust/lttng-filter.c b/liblttng-ust/lttng-filter.c index d71485d1..ee94a4cc 100644 --- a/liblttng-ust/lttng-filter.c +++ b/liblttng-ust/lttng-filter.c @@ -228,20 +228,34 @@ int apply_context_reloc(struct lttng_event *event, struct load_op *op; struct lttng_ctx_field *ctx_field; int idx; + struct lttng_session *session = runtime->p.session; dbg_printf("Apply context reloc: %u %s\n", reloc_offset, context_name); /* Get context index */ - idx = lttng_get_context_index(lttng_static_ctx, context_name); - if (idx < 0) - return -ENOENT; - + idx = lttng_get_context_index(session->ctx, context_name); + if (idx < 0) { + if (lttng_context_is_app(context_name)) { + int ret; + + ret = lttng_ust_add_app_context_to_ctx_rcu(context_name, + &session->ctx); + if (ret) + return ret; + idx = lttng_get_context_index(session->ctx, + context_name); + if (idx < 0) + return -ENOENT; + } else { + return -ENOENT; + } + } /* Check if idx is too large for 16-bit offset */ if (idx > FILTER_BYTECODE_MAX_LEN - 1) return -EINVAL; /* Get context return type */ - ctx_field = <tng_static_ctx->fields[idx]; + ctx_field = &session->ctx->fields[idx]; op = (struct load_op *) &runtime->data[reloc_offset]; field_ref = (struct field_ref *) op->data; switch (ctx_field->event_field.type.atype) { @@ -258,6 +272,9 @@ int apply_context_reloc(struct lttng_event *event, case atype_float: op->op = FILTER_OP_GET_CONTEXT_REF_DOUBLE; break; + case atype_dynamic: + op->op = FILTER_OP_GET_CONTEXT_REF; + break; default: return -EINVAL; } @@ -339,6 +356,7 @@ int _lttng_filter_event_link_bytecode(struct lttng_event *event, goto alloc_error; } runtime->p.bc = filter_bytecode; + runtime->p.session = event->chan->session; runtime->len = filter_bytecode->bc.reloc_offset; /* copy original bytecode */ memcpy(runtime->data, filter_bytecode->bc.data, runtime->len); diff --git a/liblttng-ust/lttng-filter.h b/liblttng-ust/lttng-filter.h index fdc8ac67..23ccff23 100644 --- a/liblttng-ust/lttng-filter.h +++ b/liblttng-ust/lttng-filter.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -78,7 +79,7 @@ enum entry_type { REG_S64, REG_DOUBLE, REG_STRING, - REG_TYPE_UNKNOWN, + REG_UNKNOWN, }; /* Validation stack */ @@ -137,6 +138,7 @@ int vstack_pop(struct vstack *stack) /* Execution stack */ struct estack_entry { + enum entry_type type; /* For dynamic typing. */ union { int64_t v; double d; @@ -154,9 +156,18 @@ struct estack { struct estack_entry e[FILTER_STACK_LEN]; }; +/* + * Always use aliased type for ax/bx (top of stack). + * When ax/bx are S64, use aliased value. + */ #define estack_ax_v ax #define estack_bx_v bx +#define estack_ax_t ax_t +#define estack_bx_t bx_t +/* + * ax and bx registers can hold either integer, double or string. + */ #define estack_ax(stack, top) \ ({ \ assert((top) > FILTER_STACK_EMPTY); \ @@ -169,19 +180,26 @@ struct estack { &(stack)->e[(top) - 1]; \ }) -#define estack_push(stack, top, ax, bx) \ +/* + * Currently, only integers (REG_S64) can be pushed into the stack. + */ +#define estack_push(stack, top, ax, bx, ax_t, bx_t) \ do { \ assert((top) < FILTER_STACK_LEN - 1); \ (stack)->e[(top) - 1].u.v = (bx); \ + (stack)->e[(top) - 1].type = (bx_t); \ (bx) = (ax); \ + (bx_t) = (ax_t); \ ++(top); \ } while (0) -#define estack_pop(stack, top, ax, bx) \ +#define estack_pop(stack, top, ax, bx, ax_t, bx_t) \ do { \ assert((top) > FILTER_STACK_EMPTY); \ (ax) = (bx); \ + (ax_t) = (bx_t); \ (bx) = (stack)->e[(top) - 2].u.v; \ + (bx_t) = (stack)->e[(top) - 2].type; \ (top)--; \ } while (0) diff --git a/liblttng-ust/lttng-ring-buffer-client.h b/liblttng-ust/lttng-ring-buffer-client.h index 3f1e2656..d219b79c 100644 --- a/liblttng-ust/lttng-ring-buffer-client.h +++ b/liblttng-ust/lttng-ring-buffer-client.h @@ -78,7 +78,7 @@ size_t ctx_get_size(size_t offset, struct lttng_ctx *ctx) return 0; offset += lib_ring_buffer_align(offset, ctx->largest_align); for (i = 0; i < ctx->nr_fields; i++) - offset += ctx->fields[i].get_size(offset); + offset += ctx->fields[i].get_size(&ctx->fields[i], offset); return offset - orig_offset; } @@ -185,7 +185,7 @@ void lttng_write_event_header(const struct lttng_ust_lib_ring_buffer_config *con uint32_t event_id) { struct lttng_channel *lttng_chan = channel_get_private(ctx->chan); - struct lttng_event *event = ctx->priv; + struct lttng_stack_ctx *lttng_ctx = ctx->priv2; if (caa_unlikely(ctx->rflags)) goto slow_path; @@ -220,8 +220,8 @@ void lttng_write_event_header(const struct lttng_ust_lib_ring_buffer_config *con WARN_ON_ONCE(1); } - ctx_record(ctx, lttng_chan, lttng_chan->ctx); - ctx_record(ctx, lttng_chan, event->ctx); + ctx_record(ctx, lttng_chan, lttng_ctx->chan_ctx); + ctx_record(ctx, lttng_chan, lttng_ctx->event_ctx); lib_ring_buffer_align_ctx(ctx, ctx->largest_align); return; @@ -236,7 +236,7 @@ void lttng_write_event_header_slow(const struct lttng_ust_lib_ring_buffer_config uint32_t event_id) { struct lttng_channel *lttng_chan = channel_get_private(ctx->chan); - struct lttng_event *event = ctx->priv; + struct lttng_stack_ctx *lttng_ctx = ctx->priv2; switch (lttng_chan->header_type) { case 1: /* compact */ @@ -293,8 +293,8 @@ void lttng_write_event_header_slow(const struct lttng_ust_lib_ring_buffer_config default: WARN_ON_ONCE(1); } - ctx_record(ctx, lttng_chan, lttng_chan->ctx); - ctx_record(ctx, lttng_chan, event->ctx); + ctx_record(ctx, lttng_chan, lttng_ctx->chan_ctx); + ctx_record(ctx, lttng_chan, lttng_ctx->event_ctx); lib_ring_buffer_align_ctx(ctx, ctx->largest_align); } diff --git a/liblttng-ust/lttng-ust-comm.c b/liblttng-ust/lttng-ust-comm.c index c619b544..1387aa82 100644 --- a/liblttng-ust/lttng-ust-comm.c +++ b/liblttng-ust/lttng-ust-comm.c @@ -1520,7 +1520,6 @@ void __attribute__((constructor)) lttng_ust_init(void) lttng_ring_buffer_client_discard_init(); lttng_ring_buffer_client_discard_rt_init(); lttng_perf_counter_init(); - lttng_context_init(); /* * Invoke ust malloc wrapper init before starting other threads. */ @@ -1625,7 +1624,6 @@ void lttng_ust_cleanup(int exiting) */ lttng_ust_abi_exit(); lttng_ust_events_exit(); - lttng_context_exit(); lttng_perf_counter_exit(); lttng_ring_buffer_client_discard_rt_exit(); lttng_ring_buffer_client_discard_exit(); diff --git a/liblttng-ust/lttng-ust-dynamic-type.c b/liblttng-ust/lttng-ust-dynamic-type.c new file mode 100644 index 00000000..c654f019 --- /dev/null +++ b/liblttng-ust/lttng-ust-dynamic-type.c @@ -0,0 +1,202 @@ +/* + * lttng-ust-dynamic-type.c + * + * UST dynamic type implementation. + * + * Copyright (C) 2016 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 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include +#include + +static const struct lttng_enum_entry dt_enum[_NR_LTTNG_UST_DYNAMIC_TYPES] = { + [LTTNG_UST_DYNAMIC_TYPE_NONE] = { + .start = 0, + .end = 0, + .string = "_none", + }, + [LTTNG_UST_DYNAMIC_TYPE_S8] = { + .start = 1, + .end = 1, + .string = "_int8", + }, + [LTTNG_UST_DYNAMIC_TYPE_S16] = { + .start = 2, + .end = 2, + .string = "_int16", + }, + [LTTNG_UST_DYNAMIC_TYPE_S32] = { + .start = 3, + .end = 3, + .string = "_int32", + }, + [LTTNG_UST_DYNAMIC_TYPE_S64] = { + .start = 4, + .end = 4, + .string = "_int64", + }, + [LTTNG_UST_DYNAMIC_TYPE_U8] = { + .start = 5, + .end = 5, + .string = "_uint8", + }, + [LTTNG_UST_DYNAMIC_TYPE_U16] = { + .start = 6, + .end = 6, + .string = "_uint16", + }, + [LTTNG_UST_DYNAMIC_TYPE_U32] = { + .start = 7, + .end = 7, + .string = "_uint32", + }, + [LTTNG_UST_DYNAMIC_TYPE_U64] = { + .start = 8, + .end = 8, + .string = "_uint64", + }, + [LTTNG_UST_DYNAMIC_TYPE_FLOAT] = { + .start = 9, + .end = 9, + .string = "_float", + }, + [LTTNG_UST_DYNAMIC_TYPE_DOUBLE] = { + .start = 10, + .end = 10, + .string = "_double", + }, + [LTTNG_UST_DYNAMIC_TYPE_STRING] = { + .start = 11, + .end = 11, + .string = "_string", + }, +}; + +static const struct lttng_enum_desc dt_enum_desc = { + .name = "dynamic_type_enum", + .entries = dt_enum, + .nr_entries = LTTNG_ARRAY_SIZE(dt_enum), +}; + +const struct lttng_event_field dt_var_fields[_NR_LTTNG_UST_DYNAMIC_TYPES] = { + [LTTNG_UST_DYNAMIC_TYPE_NONE] = { + .name = "none", + .type = { + .atype = atype_struct, + .u._struct.nr_fields = 0, /* empty struct. */ + }, + .nowrite = 0, + }, + [LTTNG_UST_DYNAMIC_TYPE_S8] = { + .name = "int8", + .type = __type_integer(int8_t, BYTE_ORDER, 10, none), + .nowrite = 0, + }, + [LTTNG_UST_DYNAMIC_TYPE_S16] = { + .name = "int16", + .type = __type_integer(int16_t, BYTE_ORDER, 10, none), + .nowrite = 0, + }, + [LTTNG_UST_DYNAMIC_TYPE_S32] = { + .name = "int32", + .type = __type_integer(int32_t, BYTE_ORDER, 10, none), + .nowrite = 0, + }, + [LTTNG_UST_DYNAMIC_TYPE_S64] = { + .name = "int64", + .type = __type_integer(int64_t, BYTE_ORDER, 10, none), + .nowrite = 0, + }, + [LTTNG_UST_DYNAMIC_TYPE_U8] = { + .name = "uint8", + .type = __type_integer(uint8_t, BYTE_ORDER, 10, none), + .nowrite = 0, + }, + [LTTNG_UST_DYNAMIC_TYPE_U16] = { + .name = "uint16", + .type = __type_integer(uint16_t, BYTE_ORDER, 10, none), + .nowrite = 0, + }, + [LTTNG_UST_DYNAMIC_TYPE_U32] = { + .name = "uint32", + .type = __type_integer(uint32_t, BYTE_ORDER, 10, none), + .nowrite = 0, + }, + [LTTNG_UST_DYNAMIC_TYPE_U64] = { + .name = "uint64", + .type = __type_integer(uint64_t, BYTE_ORDER, 10, none), + .nowrite = 0, + }, + [LTTNG_UST_DYNAMIC_TYPE_FLOAT] = { + .name = "float", + .type = __type_float(float), + .nowrite = 0, + }, + [LTTNG_UST_DYNAMIC_TYPE_DOUBLE] = { + .name = "double", + .type = __type_float(double), + .nowrite = 0, + }, + [LTTNG_UST_DYNAMIC_TYPE_STRING] = { + .name = "string", + .type = { + .atype = atype_string, + .u.basic.string.encoding = lttng_encode_UTF8, + }, + .nowrite = 0, + }, +}; + +static const struct lttng_event_field dt_enum_field = { + .name = NULL, + .type.atype = atype_enum, + .type.u.basic.enumeration.desc = &dt_enum_desc, + .type.u.basic.enumeration.container_type = { + .size = sizeof(char) * CHAR_BIT, + .alignment = lttng_alignof(char) * CHAR_BIT, + .signedness = lttng_is_signed_type(char), + .reverse_byte_order = 0, + .base = 10, + .encoding = lttng_encode_none, + }, + .nowrite = 0, +}; + +const struct lttng_event_field *lttng_ust_dynamic_type_field(int64_t value) +{ + if (value >= _NR_LTTNG_UST_DYNAMIC_TYPES || value < 0) + return NULL; + return &dt_var_fields[value]; +} + +int lttng_ust_dynamic_type_choices(size_t *nr_choices, const struct lttng_event_field **choices) +{ + *nr_choices = _NR_LTTNG_UST_DYNAMIC_TYPES; + *choices = dt_var_fields; + return 0; +} + +const struct lttng_event_field *lttng_ust_dynamic_type_tag_field(void) +{ + return &dt_enum_field; +} diff --git a/tests/Makefile.am b/tests/Makefile.am index be300c86..377acd67 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS = utils hello same_line_tracepoint snprintf benchmark ust-elf \ - ctf-types + ctf-types test-app-ctx if CXX_WORKS SUBDIRS += hello.cxx diff --git a/tests/hello/hello.c b/tests/hello/hello.c index 68c137ee..058f7fae 100644 --- a/tests/hello/hello.c +++ b/tests/hello/hello.c @@ -71,6 +71,8 @@ int init_int_handler(void) return 0; } +void test_inc_count(void); + int main(int argc, char **argv) { int i, netint; diff --git a/tests/test-app-ctx/Makefile.am b/tests/test-app-ctx/Makefile.am new file mode 100644 index 00000000..324c2cd9 --- /dev/null +++ b/tests/test-app-ctx/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -Wsystem-headers + +noinst_PROGRAMS = hello +hello_SOURCES = hello.c tp.c ust_tests_hello.h +hello_LDADD = $(top_builddir)/liblttng-ust/liblttng-ust.la +hello_CFLAGS = -Werror=old-style-definition + +if LTTNG_UST_BUILD_WITH_LIBDL +hello_LDADD += -ldl +endif +if LTTNG_UST_BUILD_WITH_LIBC_DL +hello_LDADD += -lc +endif diff --git a/tests/test-app-ctx/hello.c b/tests/test-app-ctx/hello.c new file mode 100644 index 00000000..197df21e --- /dev/null +++ b/tests/test-app-ctx/hello.c @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2009 Pierre-Marc Fournier + * Copyright (C) 2011 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; 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 +/* + * Work-around inet.h missing struct mmsghdr forward declaration, with + * triggers a warning when system files warnings are enabled. + */ +struct mmsghdr; +#include +#include +#include + +#define TRACEPOINT_DEFINE +#include "ust_tests_hello.h" + +/* Internal header. */ +#include +#include +#include + +static __thread unsigned int test_count; + +void test_inc_count(void) +{ + test_count++; +} + +static +size_t test_get_size(struct lttng_ctx_field *field, size_t offset) +{ + int sel = test_count % _NR_LTTNG_UST_DYNAMIC_TYPES; + size_t size = 0; + + size += lib_ring_buffer_align(offset, lttng_alignof(char)); + size += sizeof(char); /* tag */ + switch (sel) { + case LTTNG_UST_DYNAMIC_TYPE_NONE: + break; + case LTTNG_UST_DYNAMIC_TYPE_S8: + size += lib_ring_buffer_align(offset, lttng_alignof(int8_t)); + size += sizeof(int8_t); /* variant */ + break; + case LTTNG_UST_DYNAMIC_TYPE_S16: + size += lib_ring_buffer_align(offset, lttng_alignof(int16_t)); + size += sizeof(int16_t); /* variant */ + break; + case LTTNG_UST_DYNAMIC_TYPE_S32: + size += lib_ring_buffer_align(offset, lttng_alignof(int32_t)); + size += sizeof(int32_t); /* variant */ + break; + case LTTNG_UST_DYNAMIC_TYPE_S64: + size += lib_ring_buffer_align(offset, lttng_alignof(int64_t)); + size += sizeof(int64_t); /* variant */ + break; + case LTTNG_UST_DYNAMIC_TYPE_U8: + size += lib_ring_buffer_align(offset, lttng_alignof(uint8_t)); + size += sizeof(uint8_t); /* variant */ + break; + case LTTNG_UST_DYNAMIC_TYPE_U16: + size += lib_ring_buffer_align(offset, lttng_alignof(uint16_t)); + size += sizeof(uint16_t); /* variant */ + break; + case LTTNG_UST_DYNAMIC_TYPE_U32: + size += lib_ring_buffer_align(offset, lttng_alignof(uint32_t)); + size += sizeof(uint32_t); /* variant */ + break; + case LTTNG_UST_DYNAMIC_TYPE_U64: + size += lib_ring_buffer_align(offset, lttng_alignof(uint64_t)); + size += sizeof(uint64_t); /* variant */ + break; + case LTTNG_UST_DYNAMIC_TYPE_FLOAT: + size += lib_ring_buffer_align(offset, lttng_alignof(float)); + size += sizeof(float); /* variant */ + break; + case LTTNG_UST_DYNAMIC_TYPE_DOUBLE: + size += lib_ring_buffer_align(offset, lttng_alignof(double)); + size += sizeof(double); /* variant */ + break; + case LTTNG_UST_DYNAMIC_TYPE_STRING: + size += strlen("teststr") + 1; + break; + default: + abort(); + } + + return size; +} + +static +void test_record(struct lttng_ctx_field *field, + struct lttng_ust_lib_ring_buffer_ctx *ctx, + struct lttng_channel *chan) +{ + int sel = test_count % _NR_LTTNG_UST_DYNAMIC_TYPES; + char sel_char = (char) sel; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(char)); + chan->ops->event_write(ctx, &sel_char, sizeof(sel_char)); + switch (sel) { + case LTTNG_UST_DYNAMIC_TYPE_NONE: + break; + case LTTNG_UST_DYNAMIC_TYPE_S8: + { + int8_t v = -8; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); + chan->ops->event_write(ctx, &v, sizeof(v)); + break; + } + case LTTNG_UST_DYNAMIC_TYPE_S16: + { + int16_t v = -16; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); + chan->ops->event_write(ctx, &v, sizeof(v)); + break; + } + case LTTNG_UST_DYNAMIC_TYPE_S32: + { + int32_t v = -32; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); + chan->ops->event_write(ctx, &v, sizeof(v)); + break; + } + case LTTNG_UST_DYNAMIC_TYPE_S64: + { + int64_t v = -64; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); + chan->ops->event_write(ctx, &v, sizeof(v)); + break; + } + case LTTNG_UST_DYNAMIC_TYPE_U8: + { + uint8_t v = 8; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); + chan->ops->event_write(ctx, &v, sizeof(v)); + break; + } + case LTTNG_UST_DYNAMIC_TYPE_U16: + { + uint16_t v = 16; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); + chan->ops->event_write(ctx, &v, sizeof(v)); + break; + } + case LTTNG_UST_DYNAMIC_TYPE_U32: + { + uint32_t v = 32; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); + chan->ops->event_write(ctx, &v, sizeof(v)); + break; + } + case LTTNG_UST_DYNAMIC_TYPE_U64: + { + uint64_t v = 64; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); + chan->ops->event_write(ctx, &v, sizeof(v)); + break; + } + case LTTNG_UST_DYNAMIC_TYPE_FLOAT: + { + float f = 22322.0; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(f)); + chan->ops->event_write(ctx, &f, sizeof(f)); + break; + } + case LTTNG_UST_DYNAMIC_TYPE_DOUBLE: + { + double d = 2.0; + + lib_ring_buffer_align_ctx(ctx, lttng_alignof(d)); + chan->ops->event_write(ctx, &d, sizeof(d)); + break; + } + case LTTNG_UST_DYNAMIC_TYPE_STRING: + { + const char *str = "teststr"; + chan->ops->event_write(ctx, str, strlen(str) + 1); + break; + } + default: + abort(); + } +} + +static +void test_get_value(struct lttng_ctx_field *field, + struct lttng_ctx_value *value) +{ + int sel = test_count % _NR_LTTNG_UST_DYNAMIC_TYPES; + + value->sel = sel; + switch (sel) { + case LTTNG_UST_DYNAMIC_TYPE_NONE: + break; + case LTTNG_UST_DYNAMIC_TYPE_S8: + value->u.s64 = -8; + break; + case LTTNG_UST_DYNAMIC_TYPE_S16: + value->u.s64 = -16; + break; + case LTTNG_UST_DYNAMIC_TYPE_S32: + value->u.s64 = -32; + break; + case LTTNG_UST_DYNAMIC_TYPE_S64: + value->u.s64 = -64; + break; + case LTTNG_UST_DYNAMIC_TYPE_U8: + value->u.s64 = 8; + break; + case LTTNG_UST_DYNAMIC_TYPE_U16: + value->u.s64 = 16; + break; + case LTTNG_UST_DYNAMIC_TYPE_U32: + value->u.s64 = 32; + break; + case LTTNG_UST_DYNAMIC_TYPE_U64: + value->u.s64 = 64; + break; + case LTTNG_UST_DYNAMIC_TYPE_FLOAT: + value->u.d = 22322.0; + break; + case LTTNG_UST_DYNAMIC_TYPE_DOUBLE: + value->u.d = 2.0; + break; + case LTTNG_UST_DYNAMIC_TYPE_STRING: + value->u.str = "teststr"; + break; + default: + abort(); + } +} + +struct lttng_ust_context_provider myprovider = { + .name = "$app.myprovider", + .get_size = test_get_size, + .record = test_record, + .get_value = test_get_value, +}; + +void inthandler(int sig) +{ + printf("in SIGUSR1 handler\n"); + tracepoint(ust_tests_hello, tptest_sighandler); +} + +int init_int_handler(void) +{ + int result; + struct sigaction act; + + memset(&act, 0, sizeof(act)); + result = sigemptyset(&act.sa_mask); + if (result == -1) { + perror("sigemptyset"); + return -1; + } + + act.sa_handler = inthandler; + act.sa_flags = SA_RESTART; + + /* Only defer ourselves. Also, try to restart interrupted + * syscalls to disturb the traced program as little as possible. + */ + result = sigaction(SIGUSR1, &act, NULL); + if (result == -1) { + perror("sigaction"); + return -1; + } + + return 0; +} + +void test_inc_count(void); + +int main(int argc, char **argv) +{ + int i, netint; + long values[] = { 1, 2, 3 }; + char text[10] = "test"; + double dbl = 2.0; + float flt = 2222.0; + int delay = 0; + bool mybool = 123; /* should print "1" */ + + init_int_handler(); + + if (argc == 2) + delay = atoi(argv[1]); + + if (lttng_ust_context_provider_register(&myprovider)) + abort(); + + fprintf(stderr, "Hello, World!\n"); + + sleep(delay); + + fprintf(stderr, "Tracing... "); + for (i = 0; i < 1000000; i++) { + netint = htonl(i); + tracepoint(ust_tests_hello, tptest, i, netint, values, + text, strlen(text), dbl, flt, mybool); + test_inc_count(); + //usleep(100000); + } + lttng_ust_context_provider_unregister(&myprovider); + fprintf(stderr, " done.\n"); + return 0; +} diff --git a/tests/test-app-ctx/tp.c b/tests/test-app-ctx/tp.c new file mode 100644 index 00000000..4790965e --- /dev/null +++ b/tests/test-app-ctx/tp.c @@ -0,0 +1,26 @@ +/* + * tp.c + * + * Copyright (c) 2011 Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define TRACEPOINT_CREATE_PROBES +#include "ust_tests_hello.h" diff --git a/tests/test-app-ctx/ust_tests_hello.h b/tests/test-app-ctx/ust_tests_hello.h new file mode 100644 index 00000000..e518b0d4 --- /dev/null +++ b/tests/test-app-ctx/ust_tests_hello.h @@ -0,0 +1,68 @@ +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER ust_tests_hello + +#if !defined(_TRACEPOINT_UST_TESTS_HELLO_H) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define _TRACEPOINT_UST_TESTS_HELLO_H + +/* + * Copyright (C) 2011 Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +TRACEPOINT_EVENT(ust_tests_hello, tptest, + TP_ARGS(int, anint, int, netint, long *, values, + char *, text, size_t, textlen, + double, doublearg, float, floatarg, + bool, boolarg), + TP_FIELDS( + ctf_integer(int, intfield, anint) + ctf_integer_hex(int, intfield2, anint) + ctf_integer(long, longfield, anint) + ctf_integer_network(int, netintfield, netint) + ctf_integer_network_hex(int, netintfieldhex, netint) + ctf_array(long, arrfield1, values, 3) + ctf_array_text(char, arrfield2, text, 10) + ctf_sequence(char, seqfield1, text, + size_t, textlen) + ctf_sequence_text(char, seqfield2, text, + size_t, textlen) + ctf_string(stringfield, text) + ctf_float(float, floatfield, floatarg) + ctf_float(double, doublefield, doublearg) + ctf_integer(bool, boolfield, boolarg) + ctf_integer_nowrite(int, filterfield, anint) + ) +) + +TRACEPOINT_EVENT(ust_tests_hello, tptest_sighandler, + TP_ARGS(), + TP_FIELDS() +) + +#endif /* _TRACEPOINT_UST_TESTS_HELLO_H */ + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./ust_tests_hello.h" + +/* This part must be outside ifdef protection */ +#include diff --git a/tests/ust-variant/Makefile.am b/tests/ust-variant/Makefile.am new file mode 100644 index 00000000..3c3d61db --- /dev/null +++ b/tests/ust-variant/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -Wsystem-headers + +noinst_PROGRAMS = ust-variant +ust_variant_SOURCES = ust-variant.c +ust_variant_LDADD = $(top_builddir)/liblttng-ust/liblttng-ust.la +ust_variant_CFLAGS = -Werror=old-style-definition + +if LTTNG_UST_BUILD_WITH_LIBDL +ust_variant_LDADD += -ldl +endif +if LTTNG_UST_BUILD_WITH_LIBC_DL +ust_variant_LDADD += -lc +endif diff --git a/tests/ust-variant/ust-variant.c b/tests/ust-variant/ust-variant.c new file mode 100644 index 00000000..3d97c1a3 --- /dev/null +++ b/tests/ust-variant/ust-variant.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2016 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; 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 + +/* Internal UST API: ust-variant.h */ +#include +#include +#include + +#define NR_ENTRIES 5 + +static const struct lttng_enum_entry myentries[NR_ENTRIES] = { + [0] = { + .start = 0, + .end = 0, + .string = "_mystring", + }, + [1] = { + .start = 1, + .end = 1, + .string = "_myint32", + }, + [2] = { + .start = 2, + .end = 2, + .string = "_myuint16", + }, + [3] = { + .start = 3, + .end = 3, + .string = "_mychar", + }, + [4] = { + .start = 4, + .end = 4, + .string = "_mylonglong", + }, +}; + +static const struct lttng_enum_desc myenum_desc = { + .name = "myenum", + .entries = myentries, + .nr_entries = LTTNG_ARRAY_SIZE(myentries), +}; + +const struct lttng_event_field myvarfields[NR_ENTRIES] = { + [0] = { + .name = "mystring", + .type = { + .atype = atype_string, + .u.basic.string.encoding = lttng_encode_UTF8, + }, + .nowrite = 0, + }, + [1] = { + .name = "myint32", + .type = __type_integer(int32_t, BYTE_ORDER, 10, none), + .nowrite = 0, + }, + [2] = { + .name = "myuint16", + .type = __type_integer(uint16_t, BYTE_ORDER, 10, none), + .nowrite = 0, + }, + [3] = { + .name = "mychar", + .type = __type_integer(char, BYTE_ORDER, 10, none), + .nowrite = 0, + }, + [4] = { + .name = "mylonglong", + .type = __type_integer(long long, BYTE_ORDER, 10, none), + .nowrite = 0, + }, +}; + +static const struct lttng_event_field *get_field(const struct lttng_ust_type_variant *variant, + int64_t value) +{ + if (value >= NR_ENTRIES || value < 0) + return NULL; + return &myvarfields[value]; +} + +static int get_choices(const struct lttng_ust_type_variant *variant, + size_t *nr_choices, const struct lttng_event_field **choices) +{ + *nr_choices = NR_ENTRIES; + *choices = myvarfields; + return 0; +} + +static const struct lttng_event_field myfields[]; + +static const struct lttng_ust_type_variant myvariant = { + .tag = &myfields[0], + .get_field = get_field, + .get_choices = get_choices, + .free_priv = NULL, + .priv = NULL, +}; + +/* dummy event */ + +static void __event_probe__myprobe___myevent(void * __tp_data) +{ +} + +static const struct lttng_event_field myfields[] = { + [0] = { + .name = "mytag", + .type.atype = atype_enum, + .type.u.basic.enumeration.desc = &myenum_desc, + .type.u.basic.enumeration.container_type = { + .size = sizeof(char) * CHAR_BIT, + .alignment = lttng_alignof(char) * CHAR_BIT, + .signedness = lttng_is_signed_type(char), + .reverse_byte_order = 0, + .base = 10, + .encoding = lttng_encode_none, + }, + .nowrite = 0, + }, + [1] = { + .name = "myfield", + .type = { + .atype = atype_variant, + .u.variant = &myvariant, + }, + .nowrite = 0, + }, +}; + +static const struct lttng_event_desc myevent_desc = { + .name = "myprobe:myevent", + .probe_callback = (void (*)(void)) &__event_probe__myprobe___myevent, + .ctx = NULL, + .fields = myfields, + .nr_fields = LTTNG_ARRAY_SIZE(myfields), + .loglevel = NULL, + .signature = "mysig", + .u = { + .ext = { + .model_emf_uri = NULL, + }, + }, +}; + +static const struct lttng_event_desc *event_desc_array[] = { + [0] = &myevent_desc, +}; + +/* Dummy probe. */ + +static struct lttng_probe_desc __probe_desc___myprobe = { + .provider = "myprobe", + .event_desc = event_desc_array, + .nr_events = LTTNG_ARRAY_SIZE(event_desc_array), + .head = { NULL, NULL }, + .lazy_init_head = { NULL, NULL }, + .lazy = 0, + .major = LTTNG_UST_PROVIDER_MAJOR, + .minor = LTTNG_UST_PROVIDER_MINOR, +}; + +int main(int argc, char **argv) +{ + int ret; + + ret = lttng_probe_register(&__probe_desc___myprobe); + if (ret) + abort(); + sleep(5); + lttng_probe_unregister(&__probe_desc___myprobe); + + return 0; +} -- 2.34.1