#include <stdio.h>
#include <unistd.h>
#include <string.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
/* Offset (ns) to apply to clock classes on creation */
uint64_t clock_class_offset_ns;
+ /* Eventual name suffix of the trace to set */
+ char *trace_name_suffix;
+
/* Trace attributes */
enum bt_ctf_byte_order trace_bo;
uint64_t trace_major;
name, decl);
}
+/**
+ * Destroys a visitor context.
+ *
+ * @param ctx Visitor context to destroy
+ */
+static
+void ctx_destroy(struct ctx *ctx)
+{
+ struct ctx_decl_scope *scope;
+ /*
+ * Destroy all scopes, from current one to the root scope.
+ */
+
+ if (!ctx) {
+ goto end;
+ }
+
+ scope = ctx->current_scope;
+
+ while (scope) {
+ struct ctx_decl_scope *parent_scope = scope->parent_scope;
+
+ ctx_decl_scope_destroy(scope);
+ scope = parent_scope;
+ }
+
+ bt_put(ctx->trace);
+
+ if (ctx->stream_classes) {
+ g_hash_table_destroy(ctx->stream_classes);
+ }
+
+ free(ctx->trace_name_suffix);
+ g_free(ctx);
+
+end:
+ return;
+}
+
/**
* Creates a new visitor context.
*
*/
static
struct ctx *ctx_create(struct bt_ctf_trace *trace, FILE *efd,
- uint64_t clock_class_offset_ns)
+ uint64_t clock_class_offset_ns, const char *trace_name_suffix)
{
struct ctx *ctx = NULL;
struct ctx_decl_scope *scope = NULL;
goto error;
}
+ if (trace_name_suffix) {
+ ctx->trace_name_suffix = strdup(trace_name_suffix);
+ if (!ctx->trace_name_suffix) {
+ goto error;
+ }
+ }
+
ctx->trace = trace;
ctx->efd = efd;
ctx->current_scope = scope;
+ scope = NULL;
ctx->trace_bo = BT_CTF_BYTE_ORDER_NATIVE;
ctx->clock_class_offset_ns = clock_class_offset_ns;
return ctx;
error:
- g_free(ctx);
+ ctx_destroy(ctx);
ctx_decl_scope_destroy(scope);
return NULL;
}
-/**
- * Destroys a visitor context.
- *
- * @param ctx Visitor context to destroy
- */
-static
-void ctx_destroy(struct ctx *ctx)
-{
- struct ctx_decl_scope *scope;
- /*
- * Destroy all scopes, from current one to the root scope.
- */
-
- if (!ctx) {
- goto end;
- }
-
- scope = ctx->current_scope;
-
- while (scope) {
- struct ctx_decl_scope *parent_scope = scope->parent_scope;
-
- ctx_decl_scope_destroy(scope);
- scope = parent_scope;
- }
-
- bt_put(ctx->trace);
- g_hash_table_destroy(ctx->stream_classes);
- g_free(ctx);
-
-end:
- return;
-}
-
/**
* Pushes a new declaration scope on top of a visitor context's
* declaration scope stack.
int ret = 0;
struct ctf_node *node;
+ if (bt_list_empty(head)) {
+ ret = -1;
+ goto end;
+ }
+
bt_list_for_each_entry(node, head, siblings) {
int uexpr_type = node->u.unary_expression.type;
int uexpr_link = node->u.unary_expression.link;
int get_type_specifier_list_name(struct ctx *ctx,
struct ctf_node *type_specifier_list, GString *str)
{
- int ret;
+ int ret = 0;
struct ctf_node *iter;
int alias_item_nr = 0;
struct bt_list_head *head =
ret = get_unary_unsigned(&node->u.ctf_expression.right,
(uint64_t *) &id);
- if (ret || id < 0) {
+ /* Only read "id" if get_unary_unsigned() succeeded. */
+ if (ret || (!ret && id < 0)) {
_PERROR("%s", "unexpected unary expression for event declaration's \"id\" attribute");
ret = -EINVAL;
goto error;
ret = get_unary_unsigned(&node->u.ctf_expression.right,
(uint64_t *) stream_id);
- if (ret || *stream_id < 0) {
+ /*
+ * Only read "stream_id" if get_unary_unsigned()
+ * succeeded.
+ */
+ if (ret || (!ret && *stream_id < 0)) {
_PERROR("%s", "unexpected unary expression for event declaration's \"stream_id\" attribute");
ret = -EINVAL;
goto error;
event_id);
if (eevent_class) {
BT_PUT(eevent_class);
- _PERROR("%s", "duplicate event with ID %" PRId64 " in same stream");
+ _PERROR("duplicate event with ID %" PRId64 " in same stream", event_id);
ret = -EEXIST;
goto error;
}
ret = get_unary_unsigned(&node->u.ctf_expression.right,
(uint64_t *) &id);
- if (ret || id < 0) {
+ /* Only read "id" if get_unary_unsigned() succeeded. */
+ if (ret || (!ret && id < 0)) {
_PERROR("%s", "unexpected unary expression for stream declaration's \"id\" attribute");
ret = -EINVAL;
goto error;
return ret;
}
+static
+int set_trace_name(struct ctx *ctx)
+{
+ GString *name;
+ int ret = 0;
+ struct bt_value *value = NULL;
+
+ assert(bt_ctf_trace_get_stream_class_count(ctx->trace) == 0);
+ name = g_string_new(NULL);
+ if (!name) {
+ ret = -1;
+ goto end;
+ }
+
+ /*
+ * Check if we have a trace environment string value named `hostname`.
+ * If so, use it as the trace name's prefix.
+ */
+ value = bt_ctf_trace_get_environment_field_value_by_name(ctx->trace,
+ "hostname");
+ if (bt_value_is_string(value)) {
+ const char *hostname;
+
+ ret = bt_value_string_get(value, &hostname);
+ assert(ret == 0);
+ g_string_append(name, hostname);
+
+ if (ctx->trace_name_suffix) {
+ g_string_append_c(name, G_DIR_SEPARATOR);
+ }
+ }
+
+ if (ctx->trace_name_suffix) {
+ g_string_append(name, ctx->trace_name_suffix);
+ }
+
+ ret = bt_ctf_trace_set_name(ctx->trace, name->str);
+ if (ret) {
+ goto error;
+ }
+
+ goto end;
+
+error:
+ ret = -1;
+
+end:
+ bt_put(value);
+
+ if (name) {
+ g_string_free(name, TRUE);
+ }
+
+ return ret;
+}
+
static
int move_ctx_stream_classes_to_trace(struct ctx *ctx)
{
- int ret;
+ int ret = 0;
GHashTableIter iter;
gpointer key, stream_class;
+ if (g_hash_table_size(ctx->stream_classes) > 0 &&
+ bt_ctf_trace_get_stream_class_count(ctx->trace) == 0) {
+ /*
+ * We're about to add the first stream class to the
+ * trace. This will freeze the trace, and after this
+ * we cannot set the name anymore. At this point,
+ * set the trace name.
+ */
+ ret = set_trace_name(ctx);
+ if (ret) {
+ goto end;
+ }
+ }
+
g_hash_table_iter_init(&iter, ctx->stream_classes);
while (g_hash_table_iter_next(&iter, &key, &stream_class)) {
BT_HIDDEN
struct ctf_visitor_generate_ir *ctf_visitor_generate_ir_create(FILE *efd,
- uint64_t clock_class_offset_ns)
+ uint64_t clock_class_offset_ns, const char *name)
{
int ret;
struct ctx *ctx = NULL;
}
/* Create visitor's context */
- ctx = ctx_create(trace, efd, clock_class_offset_ns);
+ ctx = ctx_create(trace, efd, clock_class_offset_ns, name);
if (!ctx) {
_FPERROR(efd, "%s", "cannot create visitor context");
goto error;