From 97ade20be9c0184f651dc247a19e68a11afdeab3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Wed, 7 Jun 2017 16:21:18 -0400 Subject: [PATCH] source.ctf.fs: implement the trace-info query MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérémie Galarneau --- plugins/ctf/fs-src/data-stream-file.c | 312 ++++++++++++++------ plugins/ctf/fs-src/data-stream-file.h | 96 +++++- plugins/ctf/fs-src/fs.c | 65 ++-- plugins/ctf/fs-src/fs.h | 56 +--- plugins/ctf/fs-src/metadata.h | 8 +- plugins/ctf/fs-src/query.c | 407 +++++++++++++++++++++++++- plugins/ctf/fs-src/query.h | 4 + plugins/utils/plugin.c | 2 +- 8 files changed, 778 insertions(+), 172 deletions(-) diff --git a/plugins/ctf/fs-src/data-stream-file.c b/plugins/ctf/fs-src/data-stream-file.c index 16977d9d..51908e0a 100644 --- a/plugins/ctf/fs-src/data-stream-file.c +++ b/plugins/ctf/fs-src/data-stream-file.c @@ -203,47 +203,191 @@ static struct bt_ctf_notif_iter_medium_ops medops = { .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"); @@ -251,16 +395,14 @@ int build_index_from_idx_file(struct ctf_fs_ds_file *ds_file) 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); @@ -269,65 +411,73 @@ int build_index_from_idx_file(struct ctf_fs_ds_file *ds_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); @@ -339,38 +489,19 @@ end: 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(); @@ -401,13 +532,6 @@ struct ctf_fs_ds_file *ctf_fs_ds_file_create( ds_file->mmap_max_len = page_size * 2048; - if (build_index) { - ret = init_stream_index(ds_file); - if (ret) { - goto error; - } - } - goto end; error: @@ -419,6 +543,13 @@ end: 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) { @@ -438,10 +569,6 @@ 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); } @@ -484,19 +611,14 @@ struct bt_notification_iterator_next_return ctf_fs_ds_file_next( 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) { @@ -526,6 +648,18 @@ error: } 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); +} diff --git a/plugins/ctf/fs-src/data-stream-file.h b/plugins/ctf/fs-src/data-stream-file.h index e5bc7c76..99efd36f 100644 --- a/plugins/ctf/fs-src/data-stream-file.h +++ b/plugins/ctf/fs-src/data-stream-file.h @@ -37,26 +37,97 @@ struct ctf_fs_file; 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); @@ -67,4 +138,11 @@ BT_HIDDEN 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 */ diff --git a/plugins/ctf/fs-src/fs.c b/plugins/ctf/fs-src/fs.c index 47f780a9..8738f233 100644 --- a/plugins/ctf/fs-src/fs.c +++ b/plugins/ctf/fs-src/fs.c @@ -71,7 +71,7 @@ int notif_iter_data_set_current_ds_file(struct ctf_fs_notif_iter_data *notif_ite 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; } @@ -216,11 +216,9 @@ void ctf_fs_destroy(struct ctf_fs_component *ctf_fs) 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; } @@ -246,6 +244,13 @@ void ctf_fs_trace_destroy(void *data) 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); @@ -487,12 +492,13 @@ void ctf_fs_ds_file_info_destroy(struct ctf_fs_ds_file_info *ds_file_info) 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; @@ -509,8 +515,11 @@ struct ctf_fs_ds_file_info *ctf_fs_ds_file_info_create(const char *path, } 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; } @@ -576,13 +585,16 @@ end: 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; } @@ -608,8 +620,8 @@ int ctf_fs_ds_file_group_add_ds_file_info( error: ctf_fs_ds_file_info_destroy(ds_file_info); + ctf_fs_ds_index_destroy(index); ret = -1; - end: return ret; } @@ -627,10 +639,16 @@ int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace, 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); @@ -647,6 +665,12 @@ int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace, 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 @@ -672,7 +696,9 @@ int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace, } 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; } @@ -717,7 +743,8 @@ int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace, } 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; } @@ -732,7 +759,8 @@ end: 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); @@ -871,7 +899,7 @@ end: return ret; } -static +BT_HIDDEN struct ctf_fs_trace *ctf_fs_trace_create(const char *path, const char *name, struct metadata_overrides *overrides) { @@ -1274,7 +1302,8 @@ struct ctf_fs_component *ctf_fs_create(struct bt_private_component *priv_comp, 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; } @@ -1321,6 +1350,8 @@ struct bt_value *ctf_fs_query(struct bt_component_class *comp_class, 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; diff --git a/plugins/ctf/fs-src/fs.h b/plugins/ctf/fs-src/fs.h index a4b04f37..c5c12fb4 100644 --- a/plugins/ctf/fs-src/fs.h +++ b/plugins/ctf/fs-src/fs.h @@ -33,6 +33,7 @@ #include #include #include "data-stream-file.h" +#include "metadata.h" BT_HIDDEN extern bool ctf_fs_debug; @@ -59,43 +60,6 @@ struct ctf_fs_metadata { 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; @@ -173,23 +137,31 @@ enum bt_component_status ctf_fs_init(struct bt_private_component *source, 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); diff --git a/plugins/ctf/fs-src/metadata.h b/plugins/ctf/fs-src/metadata.h index 4976d62d..112c681f 100644 --- a/plugins/ctf/fs-src/metadata.h +++ b/plugins/ctf/fs-src/metadata.h @@ -27,10 +27,12 @@ #include #include #include -#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; @@ -52,8 +54,4 @@ FILE *ctf_fs_metadata_open_file(const char *trace_path); 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 */ diff --git a/plugins/ctf/fs-src/query.c b/plugins/ctf/fs-src/query.c index 8fa9b282..6fa7446f 100644 --- a/plugins/ctf/fs-src/query.c +++ b/plugins/ctf/fs-src/query.c @@ -30,17 +30,26 @@ #include "metadata.h" #include "../common/metadata/decoder.h" #include +#include +#include +#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; @@ -50,8 +59,8 @@ struct bt_value *metadata_info_query(struct bt_component_class *comp_class, const char *path; bool is_packetized; - results = bt_value_map_create(); - if (!results) { + result = bt_value_map_create(); + if (!result) { goto error; } @@ -123,24 +132,24 @@ struct bt_value *metadata_info_query(struct bt_component_class *comp_class, 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); @@ -153,5 +162,385 @@ end: 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; } diff --git a/plugins/ctf/fs-src/query.h b/plugins/ctf/fs-src/query.h index 01b67c2d..996a0f1a 100644 --- a/plugins/ctf/fs-src/query.h +++ b/plugins/ctf/fs-src/query.h @@ -33,4 +33,8 @@ BT_HIDDEN 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 */ diff --git a/plugins/utils/plugin.c b/plugins/utils/plugin.c index c155d771..872402f5 100644 --- a/plugins/utils/plugin.c +++ b/plugins/utils/plugin.c @@ -28,7 +28,7 @@ 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 */ -- 2.34.1