tests/ust-elf/Makefile
tests/benchmark/Makefile
tests/utils/Makefile
+ tests/test-app-ctx/Makefile
lttng-ust.pc
])
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
* 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 */
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];
};
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;
ctx->handle = handle;
ctx->padding1 = 0;
ctx->ip = 0;
+ ctx->priv2 = priv2;
memset(ctx->padding2, 0, LTTNG_UST_RING_BUFFER_CTX_PADDING);
}
--- /dev/null
+#ifndef _LTTNG_UST_CONTEXT_PROVIDER_H
+#define _LTTNG_UST_CONTEXT_PROVIDER_H
+
+/*
+ * Copyright 2016 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <lttng/ust-events.h>
+#include <urcu/hlist.h>
+
+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 */
ustctl_atype_sequence,
ustctl_atype_string,
ustctl_atype_float,
+ ustctl_atype_variant,
+ ustctl_atype_struct,
NR_USTCTL_ABSTRACT_TYPES,
};
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;
--- /dev/null
+#ifndef _LTTNG_UST_DYNAMIC_TYPE_H
+#define _LTTNG_UST_DYNAMIC_TYPE_H
+
+/*
+ * Copyright 2016 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <lttng/ust-events.h>
+
+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 */
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
atype_sequence,
atype_string,
atype_float,
+ atype_dynamic,
+ atype_struct,
NR_ABSTRACT_TYPES,
};
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;
};
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;
#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
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;
};
/*
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)
/* 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 {
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);
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);
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 { \
__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) \
* 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,
#include <helper.h>
#include <lttng/ust-error.h>
#include <lttng/ust-events.h>
+#include <lttng/ust-dynamic-type.h>
#include <usterr-signal-safe.h>
#include "../liblttng-ust/compat.h"
#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.
*/
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)
}
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;
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,
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:
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;
}
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;
* 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,
/* 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;
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
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");
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 \
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 \
#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;
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)
#include <lttng/ringbuffer-config.h>
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;
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;
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 */
}
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;
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)
--- /dev/null
+/*
+ * lttng-context-provider.c
+ *
+ * LTTng UST application context provider.
+ *
+ * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <sys/types.h>
+#include <unistd.h>
+#include <lttng/ust-context-provider.h>
+#include "lttng-tracer-core.h"
+#include "jhash.h"
+#include <helper.h>
+
+#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;
+}
#include <lttng/ringbuffer-config.h>
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;
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)
#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;
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)
}
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;
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)
#include <lttng/ust-events.h>
#include <lttng/ust-tracer.h>
+#include <lttng/ust-context-provider.h>
+#include <urcu-pointer.h>
#include <usterr-signal-safe.h>
#include <helper.h>
#include <string.h>
* 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.
*/
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.
}
case atype_string:
break;
-
+ case atype_dynamic:
+ break;
case atype_enum:
default:
WARN_ON_ONCE(1);
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));
}
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;
}
#include <helper.h>
#include <lttng/ust-ctl.h>
#include <ust-comm.h>
+#include <lttng/ust-dynamic-type.h>
+#include <lttng/ust-context-provider.h>
#include "error.h"
#include "compat.h"
#include "lttng-ust-uuid.h"
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);
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;
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,
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.
*/
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;
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();
+ }
+ }
+}
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <urcu-pointer.h>
#include "lttng-filter.h"
/*
(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
/*
#define END_OP
+#define JUMP_TO(name) \
+ goto LABEL_##name
+
#endif
/*
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] = {
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 */
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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):
}
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;
}
{
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",
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;
{
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",
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;
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)) {
}
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;
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;
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;
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;
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;
}
{
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);
{
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;
/* 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;
}
}
/* 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;
}
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;
{
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;
{
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;
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)) {
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)) {
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)) {
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)) {
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)) {
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);
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);
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);
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);
/* 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:
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;
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;
/*
* 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:
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;
}
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;
}
/*
}
/* 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:
/*
* Return value:
- * 0: success
+ * >=0: success
* <0: error
*/
static
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;
}
break;
case REG_DOUBLE:
break;
+ case REG_UNKNOWN:
+ break;
}
break;
}
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;
}
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) {
/* 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:
{
/* 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 */
/* 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)) {
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)) {
/* 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:
/*
* 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);
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) {
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;
}
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);
#include <stdio.h>
#include <helper.h>
#include <lttng/ust-events.h>
+#include <lttng/ust-context-provider.h>
#include <stdint.h>
#include <assert.h>
#include <errno.h>
REG_S64,
REG_DOUBLE,
REG_STRING,
- REG_TYPE_UNKNOWN,
+ REG_UNKNOWN,
};
/* Validation stack */
/* Execution stack */
struct estack_entry {
+ enum entry_type type; /* For dynamic typing. */
union {
int64_t v;
double d;
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); \
&(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)
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;
}
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;
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;
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 */
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);
}
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.
*/
*/
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();
--- /dev/null
+/*
+ * lttng-ust-dynamic-type.c
+ *
+ * UST dynamic type implementation.
+ *
+ * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <inttypes.h>
+
+#include <helper.h>
+#include <lttng/ust-dynamic-type.h>
+
+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;
+}
SUBDIRS = utils hello same_line_tracepoint snprintf benchmark ust-elf \
- ctf-types
+ ctf-types test-app-ctx
if CXX_WORKS
SUBDIRS += hello.cxx
return 0;
}
+void test_inc_count(void);
+
int main(int argc, char **argv)
{
int i, netint;
--- /dev/null
+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
--- /dev/null
+/*
+ * Copyright (C) 2009 Pierre-Marc Fournier
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+/*
+ * Work-around inet.h missing struct mmsghdr forward declaration, with
+ * triggers a warning when system files warnings are enabled.
+ */
+struct mmsghdr;
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#define TRACEPOINT_DEFINE
+#include "ust_tests_hello.h"
+
+/* Internal header. */
+#include <lttng/ust-events.h>
+#include <lttng/ringbuffer-config.h>
+#include <lttng/ust-context-provider.h>
+
+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;
+}
--- /dev/null
+/*
+ * tp.c
+ *
+ * Copyright (c) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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"
--- /dev/null
+#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 <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <lttng/tracepoint.h>
+#include <stdbool.h>
+
+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 <lttng/tracepoint-event.h>
--- /dev/null
+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
--- /dev/null
+/*
+ * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Internal UST API: ust-variant.h */
+#include <lttng/ust-variant.h>
+#include <lttng/ust-events.h>
+#include <helper.h>
+
+#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;
+}