.request_bytes = medop_request_bytes,
.get_stream = medop_get_stream,
};
+static
+struct ctf_fs_ds_index *ctf_fs_ds_index_create(size_t length)
+{
+ struct ctf_fs_ds_index *index = g_new0(struct ctf_fs_ds_index, 1);
+
+ if (!index) {
+ BT_LOGE_STR("Failed to allocate index");
+ goto error;
+ }
+
+ index->entries = g_array_sized_new(FALSE, TRUE,
+ sizeof(struct ctf_fs_ds_index_entry), length);
+ if (!index->entries) {
+ BT_LOGE("Failed to allocate %zu index entries.", length);
+ goto error;
+ }
+ g_array_set_size(index->entries, length);
+end:
+ return index;
+error:
+ ctf_fs_ds_index_destroy(index);
+ goto end;
+}
static
-int build_index_from_idx_file(struct ctf_fs_ds_file *ds_file)
+struct bt_ctf_clock_class *get_field_mapped_clock_class(
+ struct bt_ctf_field *field)
+{
+ struct bt_ctf_field_type *field_type;
+ struct bt_ctf_clock_class *clock_class = NULL;
+
+ field_type = bt_ctf_field_get_type(field);
+ if (!field_type) {
+ goto end;
+ }
+
+ clock_class = bt_ctf_field_type_integer_get_mapped_clock_class(
+ field_type);
+ if (!clock_class) {
+ goto end;
+ }
+end:
+ bt_put(field_type);
+ return clock_class;
+}
+
+static
+int get_ds_file_packet_bounds_clock_classes(struct ctf_fs_ds_file *ds_file,
+ struct bt_ctf_clock_class **_timestamp_begin_cc,
+ struct bt_ctf_clock_class **_timestamp_end_cc)
+{
+ int ret;
+ struct bt_ctf_field *timestamp_field;
+ struct bt_ctf_field *packet_context_field = NULL;
+ struct bt_ctf_clock_class *timestamp_begin_cc = NULL;
+ struct bt_ctf_clock_class *timestamp_end_cc = NULL;
+
+ ret = ctf_fs_ds_file_get_packet_header_context_fields(ds_file,
+ NULL, &packet_context_field);
+ if (ret || !packet_context_field) {
+ BT_LOGD("Cannot retrieve packet context field of stream \'%s\'",
+ ds_file->file->path->str);
+ ret = -1;
+ goto end;
+ }
+
+ timestamp_field = bt_ctf_field_structure_get_field_by_name(
+ packet_context_field, "timestamp_begin");
+ if (!timestamp_field) {
+ BT_LOGD("Cannot retrieve timestamp_begin field in packet context of stream \'%s\'",
+ ds_file->file->path->str);
+ ret = -1;
+ goto end;
+ }
+
+ timestamp_begin_cc = get_field_mapped_clock_class(timestamp_field);
+ if (!timestamp_begin_cc) {
+ BT_LOGD("Cannot retrieve the clock mapped to timestamp_begin of stream \'%s\'",
+ ds_file->file->path->str);
+ }
+ bt_put(timestamp_field);
+
+ timestamp_field = bt_ctf_field_structure_get_field_by_name(
+ packet_context_field, "timestamp_end");
+ if (!timestamp_field) {
+ BT_LOGD("Cannot retrieve timestamp_end field in packet context of stream \'%s\'",
+ ds_file->file->path->str);
+ ret = -1;
+ goto end;
+ }
+
+ timestamp_end_cc = get_field_mapped_clock_class(timestamp_field);
+ if (!timestamp_end_cc) {
+ BT_LOGD("Cannot retrieve the clock mapped to timestamp_end in stream \'%s\'",
+ ds_file->file->path->str);
+ }
+
+ if (_timestamp_begin_cc) {
+ *_timestamp_begin_cc = bt_get(timestamp_begin_cc);
+ }
+ if (_timestamp_end_cc) {
+ *_timestamp_end_cc = bt_get(timestamp_end_cc);
+ }
+end:
+ bt_put(packet_context_field);
+ bt_put(timestamp_field);
+ bt_put(timestamp_begin_cc);
+ bt_put(timestamp_end_cc);
+ return ret;
+}
+
+static
+int convert_cycles_to_ns(struct bt_ctf_clock_class *clock_class,
+ uint64_t cycles, int64_t *ns)
{
int ret = 0;
+ struct bt_ctf_clock_value *clock_value;
+
+ assert(ns);
+ clock_value = bt_ctf_clock_value_create(clock_class, cycles);
+ if (!clock_value) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = bt_ctf_clock_value_get_value_ns_from_epoch(clock_value, ns);
+ if (ret) {
+ goto end;
+ }
+end:
+ bt_put(clock_value);
+ return ret;
+}
+
+static
+struct ctf_fs_ds_index *build_index_from_idx_file(
+ struct ctf_fs_ds_file *ds_file)
+{
+ int ret;
gchar *directory = NULL;
gchar *basename = NULL;
GString *index_basename = NULL;
gchar *index_file_path = NULL;
GMappedFile *mapped_file = NULL;
gsize filesize;
- const struct ctf_packet_index_file_hdr *header;
- const char *mmap_begin, *file_pos;
- struct index_entry *index;
+ const char *mmap_begin = NULL, *file_pos = NULL;
+ const struct ctf_packet_index_file_hdr *header = NULL;
+ struct ctf_fs_ds_index *index = NULL;
+ struct ctf_fs_ds_index_entry *index_entry = NULL;
uint64_t total_packets_size = 0;
size_t file_index_entry_size;
size_t file_entry_count;
size_t i;
+ struct bt_ctf_clock_class *timestamp_begin_cc = NULL;
+ struct bt_ctf_clock_class *timestamp_end_cc = NULL;
+
+ BT_LOGD("Building index from .idx file of stream file %s",
+ ds_file->file->path->str);
+
+ ret = get_ds_file_packet_bounds_clock_classes(ds_file,
+ ×tamp_begin_cc, ×tamp_end_cc);
+ if (ret) {
+ BT_LOGD("Cannot get clock classes of \"timestamp_begin\" and \"timestamp_end\" fields");
+ goto error;
+ }
/* Look for index file in relative path index/name.idx. */
basename = g_path_get_basename(ds_file->file->path->str);
if (!basename) {
- BT_LOGE("Failed to get the basename of datastream file %s",
+ BT_LOGE("Cannot get the basename of datastream file %s",
ds_file->file->path->str);
- ret = -1;
- goto end;
+ goto error;
}
directory = g_path_get_dirname(ds_file->file->path->str);
if (!directory) {
- BT_LOGE("Failed to get dirname of datastream file %s",
+ BT_LOGE("Cannot get dirname of datastream file %s",
ds_file->file->path->str);
- ret = -1;
- goto end;
+ goto error;
}
index_basename = g_string_new(basename);
if (!index_basename) {
- BT_LOGE("Failed to allocate index file basename string");
- ret = -1;
- goto end;
+ BT_LOGE("Cannot allocate index file basename string");
+ goto error;
}
g_string_append(index_basename, ".idx");
index_basename->str, NULL);
mapped_file = g_mapped_file_new(index_file_path, FALSE, NULL);
if (!mapped_file) {
- BT_LOGD("Failed to create new mapped file %s",
+ BT_LOGD("Cannot create new mapped file %s",
index_file_path);
- ret = -1;
- goto end;
+ goto error;
}
filesize = g_mapped_file_get_length(mapped_file);
if (filesize < sizeof(*header)) {
BT_LOGW("Invalid LTTng trace index file: file size < header size");
- ret = -1;
- goto end;
+ goto error;
}
mmap_begin = g_mapped_file_get_contents(mapped_file);
file_pos = g_mapped_file_get_contents(mapped_file) + sizeof(*header);
if (be32toh(header->magic) != CTF_INDEX_MAGIC) {
BT_LOGW("Invalid LTTng trace index: \"magic\" validation failed");
- ret = -1;
- goto end;
+ goto error;
}
file_index_entry_size = be32toh(header->packet_index_len);
file_entry_count = (filesize - sizeof(*header)) / file_index_entry_size;
- if ((filesize - sizeof(*header)) % (file_entry_count * file_index_entry_size)) {
+ if ((filesize - sizeof(*header)) % file_index_entry_size) {
BT_LOGW("Invalid index file size; not a multiple of index entry size");
- ret = -1;
- goto end;
+ goto error;
}
- ds_file->index.entries = g_array_sized_new(FALSE, TRUE,
- sizeof(struct index_entry), file_entry_count);
- if (!ds_file->index.entries) {
- ret = -1;
- goto end;
+ index = ctf_fs_ds_index_create(file_entry_count);
+ if (!index) {
+ goto error;
}
- index = (struct index_entry *) ds_file->index.entries->data;
+
+ index_entry = (struct ctf_fs_ds_index_entry *) &g_array_index(
+ index->entries, struct ctf_fs_ds_index_entry, 0);
for (i = 0; i < file_entry_count; i++) {
struct ctf_packet_index *file_index =
(struct ctf_packet_index *) file_pos;
uint64_t packet_size = be64toh(file_index->packet_size);
if (packet_size % CHAR_BIT) {
- ret = -1;
BT_LOGW("Invalid packet size encountered in index file");
- goto invalid_index;
+ goto error;
}
/* Convert size in bits to bytes. */
packet_size /= CHAR_BIT;
- index->packet_size = packet_size;
+ index_entry->packet_size = packet_size;
- index->offset = be64toh(file_index->offset);
- if (i != 0 && index->offset < (index - 1)->offset) {
+ index_entry->offset = be64toh(file_index->offset);
+ if (i != 0 && index_entry->offset < (index_entry - 1)->offset) {
BT_LOGW("Invalid, non-monotonic, packet offset encountered in index file");
- ret = -1;
- goto invalid_index;
+ goto error;
}
- index->timestamp_begin = be64toh(file_index->timestamp_begin);
- index->timestamp_end = be64toh(file_index->timestamp_end);
- if (index->timestamp_end < index->timestamp_begin) {
+ index_entry->timestamp_begin = be64toh(file_index->timestamp_begin);
+ index_entry->timestamp_end = be64toh(file_index->timestamp_end);
+ if (index_entry->timestamp_end < index_entry->timestamp_begin) {
BT_LOGW("Invalid packet time bounds encountered in index file");
- ret = -1;
- goto invalid_index;
+ goto error;
+ }
+
+ /* Convert the packet's bound to nanoseconds since Epoch. */
+ ret = convert_cycles_to_ns(timestamp_begin_cc,
+ index_entry->timestamp_begin,
+ &index_entry->timestamp_begin_ns);
+ if (ret) {
+ goto error;
+ }
+ ret = convert_cycles_to_ns(timestamp_end_cc,
+ index_entry->timestamp_end,
+ &index_entry->timestamp_end_ns);
+ if (ret) {
+ goto error;
}
total_packets_size += packet_size;
file_pos += file_index_entry_size;
- index++;
+ index_entry++;
}
/* Validate that the index addresses the complete stream. */
if (ds_file->file->size != total_packets_size) {
BT_LOGW("Invalid index; indexed size != stream file size");
- ret = -1;
- goto invalid_index;
+ goto error;
}
end:
g_free(directory);
if (mapped_file) {
g_mapped_file_unref(mapped_file);
}
- return ret;
-invalid_index:
- g_array_free(ds_file->index.entries, TRUE);
+ bt_put(timestamp_begin_cc);
+ bt_put(timestamp_end_cc);
+ return index;
+error:
+ ctf_fs_ds_index_destroy(index);
+ index = NULL;
goto end;
}
-static
-int build_index_from_data_stream_file(struct ctf_fs_ds_file *stream)
-{
- return 0;
-}
-
-static
-int init_stream_index(struct ctf_fs_ds_file *ds_file)
-{
- int ret;
-
- ret = build_index_from_idx_file(ds_file);
- if (!ret) {
- goto end;
- }
-
- ret = build_index_from_data_stream_file(ds_file);
-end:
- return ret;
-}
-
BT_HIDDEN
struct ctf_fs_ds_file *ctf_fs_ds_file_create(
struct ctf_fs_trace *ctf_fs_trace,
- struct bt_ctf_stream *stream, const char *path,
- bool build_index)
+ struct bt_ctf_stream *stream, const char *path)
{
int ret;
const size_t page_size = bt_common_get_page_size();
ds_file->mmap_max_len = page_size * 2048;
- if (build_index) {
- ret = init_stream_index(ds_file);
- if (ret) {
- goto error;
- }
- }
-
goto end;
error:
return ds_file;
}
+BT_HIDDEN
+struct ctf_fs_ds_index *ctf_fs_ds_file_build_index(
+ struct ctf_fs_ds_file *ds_file)
+{
+ return build_index_from_idx_file(ds_file);
+}
+
BT_HIDDEN
void ctf_fs_ds_file_destroy(struct ctf_fs_ds_file *ds_file)
{
bt_ctf_notif_iter_destroy(ds_file->notif_iter);
}
- if (ds_file->index.entries) {
- g_array_free(ds_file->index.entries, TRUE);
- }
-
g_free(ds_file);
}
BT_HIDDEN
int ctf_fs_ds_file_get_packet_header_context_fields(
- struct ctf_fs_trace *ctf_fs_trace, const char *path,
+ struct ctf_fs_ds_file *ds_file,
struct bt_ctf_field **packet_header_field,
struct bt_ctf_field **packet_context_field)
{
enum bt_ctf_notif_iter_status notif_iter_status;
- struct ctf_fs_ds_file *ds_file;
int ret = 0;
- ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL, path, false);
- if (!ds_file) {
- goto error;
- }
-
+ assert(ds_file);
notif_iter_status = bt_ctf_notif_iter_get_packet_header_context_fields(
ds_file->notif_iter, packet_header_field, packet_context_field);
switch (notif_iter_status) {
}
end:
- ctf_fs_ds_file_destroy(ds_file);
return ret;
}
+
+BT_HIDDEN
+void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index)
+{
+ if (!index) {
+ return;
+ }
+
+ if (index->entries) {
+ g_array_free(index->entries, TRUE);
+ }
+ g_free(index);
+}
struct ctf_fs_trace;
struct ctf_fs_ds_file;
-struct index_entry {
- uint64_t offset; /* in bytes. */
- uint64_t packet_size; /* in bytes. */
- /* relative to the packet context field's mapped clock. */
+struct ctf_fs_ds_index_entry {
+ /* Position, in bytes, of the packet from the beginning of the file. */
+ uint64_t offset;
+
+ /* Size of the packet, in bytes. */
+ uint64_t packet_size;
+
+ /*
+ * Extracted from the packet context, relative to the respective fields'
+ * mapped clock classes (in cycles).
+ */
uint64_t timestamp_begin, timestamp_end;
+
+ /*
+ * Converted from the packet context, relative to the trace's EPOCH
+ * (in ns since EPOCH).
+ */
+ int64_t timestamp_begin_ns, timestamp_end_ns;
};
-struct index {
- GArray *entries; /* Array of struct index_entry. */
+struct ctf_fs_ds_index {
+ /* Array of struct ctf_fs_fd_index_entry. */
+ GArray *entries;
+};
+
+struct ctf_fs_ds_file_info {
+ /*
+ * Owned by this. May be NULL.
+ *
+ * A stream cannot be assumed to be indexed as the indexing might have
+ * been skipped. Moreover, the index's fields may not all be available
+ * depending on the producer (e.g. timestamp_begin/end are not
+ * mandatory).
+ *
+ * FIXME In such cases (missing fields), the indexing is aborted as
+ * no the index entries don't have a concept of fields being present
+ * or not.
+ */
+ struct ctf_fs_ds_index *index;
+
+ /* Owned by this. */
+ GString *path;
+
+ /* Guaranteed to be set, as opposed to the index. */
+ uint64_t begin_ns;
+};
+
+struct ctf_fs_ds_file {
+ /* Owned by this */
+ struct ctf_fs_file *file;
+
+ /* Owned by this */
+ struct bt_ctf_stream *stream;
+
+ /* Owned by this */
+ struct bt_clock_class_priority_map *cc_prio_map;
+
+ /* Owned by this */
+ struct bt_ctf_notif_iter *notif_iter;
+
+ void *mmap_addr;
+
+ /* Max length of chunk to mmap() when updating the current mapping. */
+ size_t mmap_max_len;
+
+ /* Length of the current mapping. */
+ size_t mmap_len;
+
+ /* Length of the current mapping which *exists* in the backing file. */
+ size_t mmap_valid_len;
+
+ /* Offset in the file where the current mapping starts. */
+ off_t mmap_offset;
+
+ /*
+ * Offset, in the current mapping, of the address to return on the next
+ * request.
+ */
+ off_t request_offset;
+
+ bool end_reached;
};
BT_HIDDEN
struct ctf_fs_ds_file *ctf_fs_ds_file_create(
struct ctf_fs_trace *ctf_fs_trace,
- struct bt_ctf_stream *stream, const char *path,
- bool build_index);
+ struct bt_ctf_stream *stream, const char *path);
BT_HIDDEN
int ctf_fs_ds_file_get_packet_header_context_fields(
- struct ctf_fs_trace *ctf_fs_trace, const char *path,
+ struct ctf_fs_ds_file *ds_file,
struct bt_ctf_field **packet_header_field,
struct bt_ctf_field **packet_context_field);
struct bt_notification_iterator_next_return ctf_fs_ds_file_next(
struct ctf_fs_ds_file *stream);
+BT_HIDDEN
+struct ctf_fs_ds_index *ctf_fs_ds_file_build_index(
+ struct ctf_fs_ds_file *ds_file);
+
+BT_HIDDEN
+void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index);
+
#endif /* CTF_FS_DS_FILE_H */
notif_iter_data->ds_file = ctf_fs_ds_file_create(
notif_iter_data->ds_file_group->ctf_fs_trace,
notif_iter_data->ds_file_group->stream,
- ds_file_info->path->str, true);
+ ds_file_info->path->str);
if (!notif_iter_data->ds_file) {
ret = -1;
}
g_free(ctf_fs);
}
-static
-void ctf_fs_trace_destroy(void *data)
+BT_HIDDEN
+void ctf_fs_trace_destroy(struct ctf_fs_trace *ctf_fs_trace)
{
- struct ctf_fs_trace *ctf_fs_trace = data;
-
if (!ctf_fs_trace) {
return;
}
g_free(ctf_fs_trace);
}
+static
+void ctf_fs_trace_destroy_notifier(void *data)
+{
+ struct ctf_fs_trace *trace = data;
+ ctf_fs_trace_destroy(trace);
+}
+
void ctf_fs_finalize(struct bt_private_component *component)
{
void *data = bt_private_component_get_user_data(component);
g_string_free(ds_file_info->path, TRUE);
}
+ ctf_fs_ds_index_destroy(ds_file_info->index);
g_free(ds_file_info);
}
static
struct ctf_fs_ds_file_info *ctf_fs_ds_file_info_create(const char *path,
- uint64_t begin_ns)
+ uint64_t begin_ns, struct ctf_fs_ds_index *index)
{
struct ctf_fs_ds_file_info *ds_file_info;
}
ds_file_info->begin_ns = begin_ns;
+ ds_file_info->index = index;
+ index = NULL;
end:
+ ctf_fs_ds_index_destroy(index);
return ds_file_info;
}
static
int ctf_fs_ds_file_group_add_ds_file_info(
struct ctf_fs_ds_file_group *ds_file_group,
- const char *path, uint64_t begin_ns)
+ const char *path, uint64_t begin_ns,
+ struct ctf_fs_ds_index *index)
{
struct ctf_fs_ds_file_info *ds_file_info;
gint i = 0;
int ret = 0;
- ds_file_info = ctf_fs_ds_file_info_create(path, begin_ns);
+ /* Onwership of index is transferred. */
+ ds_file_info = ctf_fs_ds_file_info_create(path, begin_ns, index);
+ index = NULL;
if (!ds_file_info) {
goto error;
}
error:
ctf_fs_ds_file_info_destroy(ds_file_info);
+ ctf_fs_ds_index_destroy(index);
ret = -1;
-
end:
return ret;
}
bool add_group = false;
int ret;
size_t i;
+ struct ctf_fs_ds_file *ds_file;
+ struct ctf_fs_ds_index *index = NULL;
- ret = ctf_fs_ds_file_get_packet_header_context_fields(
- ctf_fs_trace, path, &packet_header_field,
- &packet_context_field);
+ ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL, path);
+ if (!ds_file) {
+ goto error;
+ }
+
+ ret = ctf_fs_ds_file_get_packet_header_context_fields(ds_file,
+ &packet_header_field, &packet_context_field);
if (ret) {
BT_LOGE("Cannot get stream file's first packet's header and context fields (`%s`).",
path);
goto error;
}
+ index = ctf_fs_ds_file_build_index(ds_file);
+ if (!index) {
+ BT_LOGW("Failed to index CTF stream file \'%s\'",
+ ds_file->file->path->str);
+ }
+
if (begin_ns == -1ULL) {
/*
* No beggining timestamp to sort the stream files
}
ret = ctf_fs_ds_file_group_add_ds_file_info(ds_file_group,
- path, begin_ns);
+ path, begin_ns, index);
+ /* Ownership of index is transferred. */
+ index = NULL;
if (ret) {
goto error;
}
}
ret = ctf_fs_ds_file_group_add_ds_file_info(ds_file_group,
- path, begin_ns);
+ path, begin_ns, index);
+ index = NULL;
if (ret) {
goto error;
}
if (add_group && ds_file_group) {
g_ptr_array_add(ctf_fs_trace->ds_file_groups, ds_file_group);
}
-
+ ctf_fs_ds_file_destroy(ds_file);
+ ctf_fs_ds_index_destroy(index);
bt_put(packet_header_field);
bt_put(packet_context_field);
bt_put(stream_class);
return ret;
}
-static
+BT_HIDDEN
struct ctf_fs_trace *ctf_fs_trace_create(const char *path, const char *name,
struct metadata_overrides *overrides)
{
goto error;
}
- ctf_fs->traces = g_ptr_array_new_with_free_func(ctf_fs_trace_destroy);
+ ctf_fs->traces = g_ptr_array_new_with_free_func(
+ ctf_fs_trace_destroy_notifier);
if (!ctf_fs->traces) {
goto error;
}
if (!strcmp(object, "metadata-info")) {
result = metadata_info_query(comp_class, params);
+ } else if (!strcmp(object, "trace-info")) {
+ result = trace_info_query(comp_class, params);
} else {
BT_LOGE("Unknown query object `%s`", object);
goto end;
#include <babeltrace/graph/component.h>
#include <babeltrace/graph/clock-class-priority-map.h>
#include "data-stream-file.h"
+#include "metadata.h"
BT_HIDDEN
extern bool ctf_fs_debug;
int bo;
};
-struct ctf_fs_ds_file_info {
- GString *path;
- uint64_t begin_ns;
-};
-
-struct ctf_fs_ds_file {
- /* Owned by this */
- struct ctf_fs_file *file;
-
- /* Owned by this */
- struct bt_ctf_stream *stream;
-
- /* Owned by this */
- struct bt_clock_class_priority_map *cc_prio_map;
-
- /* Owned by this */
- struct bt_ctf_notif_iter *notif_iter;
-
- /* A stream is assumed to be indexed. */
- struct index index;
- void *mmap_addr;
- /* Max length of chunk to mmap() when updating the current mapping. */
- size_t mmap_max_len;
- /* Length of the current mapping. */
- size_t mmap_len;
- /* Length of the current mapping which *exists* in the backing file. */
- size_t mmap_valid_len;
- /* Offset in the file where the current mapping starts. */
- off_t mmap_offset;
- /*
- * Offset, in the current mapping, of the address to return on the next
- * request.
- */
- off_t request_offset;
- bool end_reached;
-};
-
struct ctf_fs_component_options {
int64_t clock_offset;
int64_t clock_offset_ns;
BT_HIDDEN
void ctf_fs_finalize(struct bt_private_component *component);
-BT_HIDDEN
-enum bt_notification_iterator_status ctf_fs_iterator_init(
- struct bt_private_notification_iterator *it,
- struct bt_private_port *port);
-
BT_HIDDEN
struct bt_value *ctf_fs_query(struct bt_component_class *comp_class,
const char *object, struct bt_value *params);
+BT_HIDDEN
+struct ctf_fs_trace *ctf_fs_trace_create(const char *path, const char *name,
+ struct metadata_overrides *overrides);
+
+BT_HIDDEN
+void ctf_fs_trace_destroy(struct ctf_fs_trace *trace);
+
BT_HIDDEN
int ctf_fs_find_traces(GList **trace_paths, const char *start_path);
BT_HIDDEN
GList *ctf_fs_create_trace_names(GList *trace_paths, const char *base_path);
+BT_HIDDEN
+enum bt_notification_iterator_status ctf_fs_iterator_init(
+ struct bt_private_notification_iterator *it,
+ struct bt_private_port *port);
+BT_HIDDEN
void ctf_fs_iterator_finalize(struct bt_private_notification_iterator *it);
+BT_HIDDEN
struct bt_notification_iterator_next_return ctf_fs_iterator_next(
struct bt_private_notification_iterator *iterator);
#include <glib.h>
#include <babeltrace/babeltrace-internal.h>
#include <babeltrace/ctf-ir/trace.h>
-#include "fs.h"
#define CTF_FS_METADATA_FILENAME "metadata"
+struct ctf_fs_trace;
+struct ctf_fs_metadata;
+
struct metadata_overrides {
int64_t clock_offset_s;
int64_t clock_offset_ns;
BT_HIDDEN
bool ctf_metadata_is_packetized(FILE *fp, int *byte_order);
-BT_HIDDEN
-int ctf_metadata_packetized_file_to_buf(struct ctf_fs_component *ctf_fs,
- FILE *fp, uint8_t **buf, int byte_order);
-
#endif /* CTF_FS_METADATA_H */
#include "metadata.h"
#include "../common/metadata/decoder.h"
#include <babeltrace/common-internal.h>
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/ctf-ir/stream.h>
+#include "fs.h"
#define BT_LOG_TAG "PLUGIN-CTF-FS-QUERY-SRC"
#include "logging.h"
#define METADATA_TEXT_SIG "/* CTF 1.8"
+struct range {
+ int64_t begin_ns;
+ int64_t end_ns;
+ bool set;
+};
+
BT_HIDDEN
struct bt_value *metadata_info_query(struct bt_component_class *comp_class,
struct bt_value *params)
{
- struct bt_value *results = NULL;
+ struct bt_value *result = NULL;
struct bt_value *path_value = NULL;
char *metadata_text = NULL;
FILE *metadata_fp = NULL;
const char *path;
bool is_packetized;
- results = bt_value_map_create();
- if (!results) {
+ result = bt_value_map_create();
+ if (!result) {
goto error;
}
g_string_append(g_metadata_text, metadata_text);
- ret = bt_value_map_insert_string(results, "text",
+ ret = bt_value_map_insert_string(result, "text",
g_metadata_text->str);
if (ret) {
- fprintf(stderr, "Cannot insert metadata text into results\n");
+ fprintf(stderr, "Cannot insert metadata text into result\n");
goto error;
}
- ret = bt_value_map_insert_bool(results, "is-packetized",
+ ret = bt_value_map_insert_bool(result, "is-packetized",
is_packetized);
if (ret) {
- fprintf(stderr, "Cannot insert is packetized into results\n");
+ fprintf(stderr, "Cannot insert is packetized into result\n");
goto error;
}
goto end;
error:
- BT_PUT(results);
+ BT_PUT(result);
end:
bt_put(path_value);
if (metadata_fp) {
fclose(metadata_fp);
}
- return results;
+ return result;
+}
+static
+int add_range(struct bt_value *info, struct range *range,
+ const char *range_name)
+{
+ int ret = 0;
+ enum bt_value_status status;
+ struct bt_value *range_map = NULL;
+
+ if (!range->set) {
+ /* Not an error. */
+ goto end;
+ }
+
+ range_map = bt_value_map_create();
+ if (!range_map) {
+ ret = -1;
+ goto end;
+ }
+
+ status = bt_value_map_insert_integer(range_map, "begin",
+ range->begin_ns);
+ if (status != BT_VALUE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ status = bt_value_map_insert_integer(range_map, "end",
+ range->end_ns);
+ if (status != BT_VALUE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ status = bt_value_map_insert(info, range_name, range_map);
+ if (status != BT_VALUE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+end:
+ bt_put(range_map);
+ return ret;
+}
+
+static
+int add_stream_ids(struct bt_value *info, struct bt_ctf_stream *stream)
+{
+ int ret = 0;
+ int64_t stream_class_id, stream_instance_id;
+ enum bt_value_status status;
+ struct bt_ctf_stream_class *stream_class;
+
+ stream_instance_id = bt_ctf_stream_get_id(stream);
+ if (stream_instance_id != -1) {
+ status = bt_value_map_insert_integer(info, "id",
+ stream_instance_id);
+ if (status != BT_VALUE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+ }
+
+ stream_class = bt_ctf_stream_get_class(stream);
+ if (!stream_class) {
+ ret = -1;
+ goto end;
+ }
+
+ stream_class_id = bt_ctf_stream_class_get_id(stream_class);
+ if (stream_class_id == -1) {
+ ret = -1;
+ goto end;
+ }
+
+ status = bt_value_map_insert_integer(info, "class-id", stream_class_id);
+ if (status != BT_VALUE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+end:
+ bt_put(stream_class);
+ return ret;
+}
+
+static
+int populate_stream_info(struct ctf_fs_ds_file_group *group,
+ struct bt_value *group_info,
+ struct range *stream_range)
+{
+ int ret = 0;
+ size_t file_idx;
+ enum bt_value_status status;
+ struct bt_value *file_paths;
+
+ stream_range->begin_ns = INT64_MAX;
+ stream_range->end_ns = 0;
+
+ file_paths = bt_value_array_create();
+ if (!file_paths) {
+ ret = -1;
+ goto end;
+ }
+
+ for (file_idx = 0; file_idx < group->ds_file_infos->len; file_idx++) {
+ int64_t file_begin_epoch, file_end_epoch;
+ struct ctf_fs_ds_file_info *info =
+ g_ptr_array_index(group->ds_file_infos,
+ file_idx);
+
+ if (!info->index || info->index->entries->len == 0) {
+ BT_LOGW("Cannot determine range of unindexed stream file \'%s\'",
+ info->path->str);
+ ret = -1;
+ goto end;
+ }
+
+ status = bt_value_array_append_string(file_paths,
+ info->path->str);
+ if (status != BT_VALUE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ /*
+ * file range is from timestamp_begin of the first entry to the
+ * timestamp_end of the last entry.
+ */
+ file_begin_epoch = ((struct ctf_fs_ds_index_entry *) &g_array_index(info->index->entries,
+ struct ctf_fs_ds_index_entry, 0))->timestamp_begin_ns;
+ file_end_epoch = ((struct ctf_fs_ds_index_entry *) &g_array_index(info->index->entries,
+ struct ctf_fs_ds_index_entry, info->index->entries->len - 1))->timestamp_end_ns;
+
+ stream_range->begin_ns = min(stream_range->begin_ns, file_begin_epoch);
+ stream_range->end_ns = max(stream_range->end_ns, file_end_epoch);
+ stream_range->set = true;
+ }
+
+ if (stream_range->set) {
+ ret = add_range(group_info, stream_range, "range-ns");
+ if (ret) {
+ goto end;
+ }
+ }
+
+ status = bt_value_map_insert(group_info, "paths", file_paths);
+ if (status != BT_VALUE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = add_stream_ids(group_info, group->stream);
+ if (ret) {
+ goto end;
+ }
+end:
+ bt_put(file_paths);
+ return ret;
+}
+
+static
+int populate_trace_info(const char *trace_path, const char *trace_name,
+ struct bt_value *trace_info)
+{
+ int ret = 0;
+ size_t group_idx;
+ struct ctf_fs_trace *trace = NULL;
+ enum bt_value_status status;
+ struct bt_value *file_groups;
+ struct range trace_range = {
+ .begin_ns = INT64_MAX,
+ .end_ns = 0,
+ .set = false,
+ };
+ struct range trace_intersection = {
+ .begin_ns = 0,
+ .end_ns = INT64_MAX,
+ .set = false,
+ };
+
+ file_groups = bt_value_array_create();
+ if (!file_groups) {
+ goto end;
+ }
+
+ status = bt_value_map_insert_string(trace_info, "name",
+ trace_name);
+ if (status != BT_VALUE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+ status = bt_value_map_insert_string(trace_info, "path",
+ trace_path);
+ if (status != BT_VALUE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ trace = ctf_fs_trace_create(trace_path, trace_name, NULL);
+ if (!trace) {
+ BT_LOGE("Failed to create fs trace at \'%s\'", trace_path);
+ ret = -1;
+ goto end;
+ }
+
+ assert(trace->ds_file_groups);
+ /* Add trace range info only if it contains streams. */
+ if (trace->ds_file_groups->len == 0) {
+ ret = -1;
+ goto end;
+ }
+
+ /* Find range of all stream groups, and of the trace. */
+ for (group_idx = 0; group_idx < trace->ds_file_groups->len;
+ group_idx++) {
+ struct bt_value *group_info;
+ struct range group_range = { .set = false };
+ struct ctf_fs_ds_file_group *group = g_ptr_array_index(
+ trace->ds_file_groups, group_idx);
+
+ group_info = bt_value_map_create();
+ if (!group_info) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = populate_stream_info(group, group_info, &group_range);
+ if (ret) {
+ bt_put(group_info);
+ goto end;
+ }
+
+ if (group_range.set) {
+ trace_range.begin_ns = min(trace_range.begin_ns,
+ group_range.begin_ns);
+ trace_range.end_ns = max(trace_range.end_ns,
+ group_range.end_ns);
+ trace_range.set = true;
+
+ trace_intersection.begin_ns = max(trace_intersection.begin_ns,
+ group_range.begin_ns);
+ trace_intersection.end_ns = min(trace_intersection.end_ns,
+ group_range.end_ns);
+ trace_intersection.set = true;
+ status = bt_value_array_append(file_groups, group_info);
+ bt_put(group_info);
+ if (status != BT_VALUE_STATUS_OK) {
+ goto end;
+ }
+ }
+ }
+
+ ret = add_range(trace_info, &trace_range, "range-ns");
+ if (ret) {
+ goto end;
+ }
+ ret = add_range(trace_info, &trace_intersection,
+ "intersection-range-ns");
+ if (ret) {
+ goto end;
+ }
+
+ status = bt_value_map_insert(trace_info, "streams", file_groups);
+ BT_PUT(file_groups);
+ if (status != BT_VALUE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+end:
+ bt_put(file_groups);
+ ctf_fs_trace_destroy(trace);
+ return ret;
+}
+
+BT_HIDDEN
+struct bt_value *trace_info_query(struct bt_component_class *comp_class,
+ struct bt_value *params)
+{
+ struct bt_value *trace_infos = NULL;
+ struct bt_value *path_value = NULL;
+ int ret = 0;
+ const char *path = NULL;
+ GList *trace_paths = NULL;
+ GList *trace_names = NULL;
+ GList *tp_node = NULL;
+ GList *tn_node = NULL;
+ GString *normalized_path = NULL;
+
+ if (!bt_value_is_map(params)) {
+ BT_LOGE("Query parameters is not a map value object.");
+ goto error;
+ }
+
+ path_value = bt_value_map_get(params, "path");
+ ret = bt_value_string_get(path_value, &path);
+ if (ret) {
+ BT_LOGE("Cannot get `path` string parameter.");
+ goto error;
+ }
+
+ normalized_path = bt_common_normalize_path(path, NULL);
+ if (!normalized_path) {
+ BT_LOGE("Failed to normalize path: `%s`.", path);
+ goto error;
+ }
+ assert(path);
+
+ ret = ctf_fs_find_traces(&trace_paths, normalized_path->str);
+ if (ret) {
+ goto error;
+ }
+
+ trace_names = ctf_fs_create_trace_names(trace_paths,
+ normalized_path->str);
+ if (!trace_names) {
+ BT_LOGE("Cannot create trace names from trace paths.");
+ goto error;
+ }
+
+ trace_infos = bt_value_array_create();
+ if (!trace_infos) {
+ goto error;
+ }
+
+ /* Iterates over both trace paths and names simultaneously. */
+ for (tp_node = trace_paths, tn_node = trace_names; tp_node;
+ tp_node = g_list_next(tp_node),
+ tn_node = g_list_next(tn_node)) {
+ GString *trace_path = tp_node->data;
+ GString *trace_name = tn_node->data;
+ enum bt_value_status status;
+ struct bt_value *trace_info;
+
+ trace_info = bt_value_map_create();
+ if (!trace_info) {
+ BT_LOGE("Failed to create trace info map.");
+ goto error;
+ }
+
+ ret = populate_trace_info(trace_path->str, trace_name->str,
+ trace_info);
+ if (ret) {
+ bt_put(trace_info);
+ goto error;
+ }
+
+ status = bt_value_array_append(trace_infos, trace_info);
+ bt_put(trace_info);
+ if (status != BT_VALUE_STATUS_OK) {
+ goto error;
+ }
+ }
+
+ goto end;
+
+error:
+ BT_PUT(trace_infos);
+end:
+ if (normalized_path) {
+ g_string_free(normalized_path, TRUE);
+ }
+ if (trace_paths) {
+ for (tp_node = trace_paths; tp_node; tp_node = g_list_next(tp_node)) {
+ if (tp_node->data) {
+ g_string_free(tp_node->data, TRUE);
+ }
+ }
+ g_list_free(trace_paths);
+ }
+ if (trace_names) {
+ for (tn_node = trace_names; tn_node; tn_node = g_list_next(tn_node)) {
+ if (tn_node->data) {
+ g_string_free(tn_node->data, TRUE);
+ }
+ }
+ g_list_free(trace_names);
+ }
+ /* "path" becomes invalid with the release of path_value. */
+ bt_put(path_value);
+ return trace_infos;
}
struct bt_value *metadata_info_query(struct bt_component_class *comp_class,
struct bt_value *params);
+BT_HIDDEN
+struct bt_value *trace_info_query(struct bt_component_class *comp_class,
+ struct bt_value *params);
+
#endif /* BABELTRACE_PLUGIN_CTF_FS_QUERY_H */
BT_PLUGIN(utils);
BT_PLUGIN_DESCRIPTION("Graph utilities");
-BT_PLUGIN_AUTHOR("Julien Desfossez, Philippe Proulx");
+BT_PLUGIN_AUTHOR("Julien Desfossez, Jérémie Galarneau, Philippe Proulx");
BT_PLUGIN_LICENSE("MIT");
/* dummy sink */