4 * Babeltrace CTF file system Reader Component queries
6 * Copyright 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 #define BT_LOG_OUTPUT_LEVEL log_level
28 #define BT_LOG_TAG "PLUGIN/SRC.CTF.FS/QUERY"
29 #include "logging/log.h"
33 #include "common/assert.h"
35 #include "../common/metadata/decoder.h"
36 #include "common/common.h"
37 #include "common/macros.h"
38 #include <babeltrace2/babeltrace.h>
41 #define METADATA_TEXT_SIG "/* CTF 1.8"
50 bt_component_class_query_method_status
metadata_info_query(
51 bt_self_component_class_source
*comp_class
,
52 const bt_value
*params
, bt_logging_level log_level
,
53 const bt_value
**user_result
)
55 bt_component_class_query_method_status status
=
56 BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK
;
57 bt_value
*result
= NULL
;
58 const bt_value
*path_value
= NULL
;
59 char *metadata_text
= NULL
;
60 FILE *metadata_fp
= NULL
;
61 GString
*g_metadata_text
= NULL
;
67 result
= bt_value_map_create();
69 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR
;
75 if (!bt_value_is_map(params
)) {
76 BT_LOGE_STR("Query parameters is not a map value object.");
77 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_INVALID_PARAMS
;
81 path_value
= bt_value_map_borrow_entry_value_const(params
, "path");
83 BT_LOGE_STR("Mandatory `path` parameter missing");
84 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_INVALID_PARAMS
;
88 if (!bt_value_is_string(path_value
)) {
89 BT_LOGE_STR("`path` parameter is required to be a string value");
90 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_INVALID_PARAMS
;
94 path
= bt_value_string_get(path_value
);
97 metadata_fp
= ctf_fs_metadata_open_file(path
);
99 BT_LOGE("Cannot open trace metadata: path=\"%s\".", path
);
103 is_packetized
= ctf_metadata_decoder_is_packetized(metadata_fp
,
104 &bo
, log_level
, NULL
);
107 ret
= ctf_metadata_decoder_packetized_file_stream_to_buf(
108 metadata_fp
, &metadata_text
, bo
, NULL
, NULL
,
111 BT_LOGE("Cannot decode packetized metadata file: path=\"%s\"",
118 ret
= fseek(metadata_fp
, 0, SEEK_END
);
120 BT_LOGE_ERRNO("Failed to seek to the end of the metadata file",
121 ": path=\"%s\"", path
);
124 filesize
= ftell(metadata_fp
);
126 BT_LOGE_ERRNO("Failed to get the current position in the metadata file",
127 ": path=\"%s\"", path
);
131 metadata_text
= malloc(filesize
+ 1);
132 if (!metadata_text
) {
133 BT_LOGE_STR("Cannot allocate buffer for metadata text.");
137 if (fread(metadata_text
, filesize
, 1, metadata_fp
) != 1) {
138 BT_LOGE_ERRNO("Cannot read metadata file", ": path=\"%s\"",
143 metadata_text
[filesize
] = '\0';
146 g_metadata_text
= g_string_new(NULL
);
147 if (!g_metadata_text
) {
151 if (strncmp(metadata_text
, METADATA_TEXT_SIG
,
152 sizeof(METADATA_TEXT_SIG
) - 1) != 0) {
153 g_string_assign(g_metadata_text
, METADATA_TEXT_SIG
);
154 g_string_append(g_metadata_text
, " */\n\n");
157 g_string_append(g_metadata_text
, metadata_text
);
159 ret
= bt_value_map_insert_string_entry(result
, "text",
160 g_metadata_text
->str
);
162 BT_LOGE_STR("Cannot insert metadata text into query result.");
166 ret
= bt_value_map_insert_bool_entry(result
, "is-packetized",
169 BT_LOGE_STR("Cannot insert \"is-packetized\" attribute into query result.");
176 BT_VALUE_PUT_REF_AND_RESET(result
);
180 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR
;
186 if (g_metadata_text
) {
187 g_string_free(g_metadata_text
, TRUE
);
194 *user_result
= result
;
199 int add_range(bt_value
*info
, struct range
*range
,
200 const char *range_name
)
203 bt_value_map_insert_entry_status status
;
204 bt_value
*range_map
= NULL
;
211 range_map
= bt_value_map_create();
217 status
= bt_value_map_insert_signed_integer_entry(range_map
, "begin",
219 if (status
!= BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
224 status
= bt_value_map_insert_signed_integer_entry(range_map
, "end",
226 if (status
!= BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
231 status
= bt_value_map_insert_entry(info
, range_name
,
233 if (status
!= BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
239 bt_value_put_ref(range_map
);
244 int add_stream_ids(bt_value
*info
, struct ctf_fs_ds_file_group
*ds_file_group
)
247 bt_value_map_insert_entry_status status
;
249 if (ds_file_group
->stream_id
!= UINT64_C(-1)) {
250 status
= bt_value_map_insert_unsigned_integer_entry(info
, "id",
251 ds_file_group
->stream_id
);
252 if (status
!= BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
258 status
= bt_value_map_insert_unsigned_integer_entry(info
, "class-id",
259 ds_file_group
->sc
->id
);
260 if (status
!= BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
270 int populate_stream_info(struct ctf_fs_ds_file_group
*group
,
271 bt_value
*group_info
, struct range
*stream_range
)
275 bt_value_map_insert_entry_status insert_status
;
276 bt_value_array_append_element_status append_status
;
277 bt_value
*file_paths
;
278 struct ctf_fs_ds_index_entry
*first_ds_index_entry
, *last_ds_index_entry
;
279 gchar
*port_name
= NULL
;
281 file_paths
= bt_value_array_create();
287 for (file_idx
= 0; file_idx
< group
->ds_file_infos
->len
; file_idx
++) {
288 struct ctf_fs_ds_file_info
*info
=
289 g_ptr_array_index(group
->ds_file_infos
,
292 append_status
= bt_value_array_append_string_element(file_paths
,
294 if (append_status
!= BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK
) {
301 * Since each `struct ctf_fs_ds_file_group` has a sorted array of
302 * `struct ctf_fs_ds_index_entry`, we can compute the stream range from
303 * the timestamp_begin of the first index entry and the timestamp_end
304 * of the last index entry.
306 BT_ASSERT(group
->index
);
307 BT_ASSERT(group
->index
->entries
);
308 BT_ASSERT(group
->index
->entries
->len
> 0);
311 first_ds_index_entry
= (struct ctf_fs_ds_index_entry
*) g_ptr_array_index(
312 group
->index
->entries
, 0);
315 last_ds_index_entry
= (struct ctf_fs_ds_index_entry
*) g_ptr_array_index(
316 group
->index
->entries
, group
->index
->entries
->len
- 1);
318 stream_range
->begin_ns
= first_ds_index_entry
->timestamp_begin_ns
;
319 stream_range
->end_ns
= last_ds_index_entry
->timestamp_end_ns
;
322 * If any of the begin and end timestamps is not set it means that
323 * packets don't include `timestamp_begin` _and_ `timestamp_end` fields
324 * in their packet context so we can't set the range.
326 stream_range
->set
= stream_range
->begin_ns
!= UINT64_C(-1) &&
327 stream_range
->end_ns
!= UINT64_C(-1);
329 ret
= add_range(group_info
, stream_range
, "range-ns");
334 insert_status
= bt_value_map_insert_entry(group_info
, "paths",
336 if (insert_status
!= BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
341 ret
= add_stream_ids(group_info
, group
);
346 port_name
= ctf_fs_make_port_name(group
);
352 insert_status
= bt_value_map_insert_string_entry(group_info
,
353 "port-name", port_name
);
354 if (insert_status
!= BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
361 bt_value_put_ref(file_paths
);
366 int populate_trace_info(const struct ctf_fs_trace
*trace
, bt_value
*trace_info
)
370 bt_value_map_insert_entry_status insert_status
;
371 bt_value_array_append_element_status append_status
;
372 bt_value
*file_groups
= NULL
;
373 struct range trace_range
= {
374 .begin_ns
= INT64_MAX
,
378 struct range trace_intersection
= {
384 BT_ASSERT(trace
->ds_file_groups
);
385 /* Add trace range info only if it contains streams. */
386 if (trace
->ds_file_groups
->len
== 0) {
391 file_groups
= bt_value_array_create();
396 insert_status
= bt_value_map_insert_string_entry(trace_info
, "name",
398 if (insert_status
!= BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
402 insert_status
= bt_value_map_insert_string_entry(trace_info
, "path",
404 if (insert_status
!= BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
409 /* Find range of all stream groups, and of the trace. */
410 for (group_idx
= 0; group_idx
< trace
->ds_file_groups
->len
;
412 bt_value
*group_info
;
413 struct range group_range
= { .set
= false };
414 struct ctf_fs_ds_file_group
*group
= g_ptr_array_index(
415 trace
->ds_file_groups
, group_idx
);
417 group_info
= bt_value_map_create();
423 ret
= populate_stream_info(group
, group_info
, &group_range
);
425 bt_value_put_ref(group_info
);
429 append_status
= bt_value_array_append_element(file_groups
,
431 bt_value_put_ref(group_info
);
432 if (append_status
!= BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK
) {
436 if (group_range
.set
) {
437 trace_range
.begin_ns
= MIN(trace_range
.begin_ns
,
438 group_range
.begin_ns
);
439 trace_range
.end_ns
= MAX(trace_range
.end_ns
,
441 trace_range
.set
= true;
443 trace_intersection
.begin_ns
= MAX(trace_intersection
.begin_ns
,
444 group_range
.begin_ns
);
445 trace_intersection
.end_ns
= MIN(trace_intersection
.end_ns
,
447 trace_intersection
.set
= true;
451 ret
= add_range(trace_info
, &trace_range
, "range-ns");
456 if (trace_intersection
.begin_ns
< trace_intersection
.end_ns
) {
457 ret
= add_range(trace_info
, &trace_intersection
,
458 "intersection-range-ns");
464 insert_status
= bt_value_map_insert_entry(trace_info
, "streams",
466 BT_VALUE_PUT_REF_AND_RESET(file_groups
);
467 if (insert_status
!= BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
473 bt_value_put_ref(file_groups
);
478 bt_component_class_query_method_status
trace_info_query(
479 bt_self_component_class_source
*comp_class
,
480 const bt_value
*params
, bt_logging_level log_level
,
481 const bt_value
**user_result
)
483 struct ctf_fs_component
*ctf_fs
= NULL
;
484 bt_component_class_query_method_status status
=
485 BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK
;
486 bt_value
*result
= NULL
;
487 const bt_value
*inputs_value
= NULL
;
493 if (!bt_value_is_map(params
)) {
494 BT_LOGE("Query parameters is not a map value object.");
495 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_INVALID_PARAMS
;
499 ctf_fs
= ctf_fs_component_create(log_level
, NULL
);
504 if (!read_src_fs_parameters(params
, &inputs_value
, ctf_fs
)) {
505 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_INVALID_PARAMS
;
509 if (ctf_fs_component_create_ctf_fs_traces(NULL
, ctf_fs
, inputs_value
)) {
513 result
= bt_value_array_create();
515 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR
;
519 for (i
= 0; i
< ctf_fs
->traces
->len
; i
++) {
520 struct ctf_fs_trace
*trace
;
521 bt_value
*trace_info
;
522 bt_value_array_append_element_status append_status
;
524 trace
= g_ptr_array_index(ctf_fs
->traces
, i
);
527 trace_info
= bt_value_map_create();
529 BT_LOGE("Failed to create trace info map.");
533 ret
= populate_trace_info(trace
, trace_info
);
535 bt_value_put_ref(trace_info
);
539 append_status
= bt_value_array_append_element(result
,
541 bt_value_put_ref(trace_info
);
542 if (append_status
!= BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK
) {
550 BT_VALUE_PUT_REF_AND_RESET(result
);
554 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR
;
559 ctf_fs_destroy(ctf_fs
);
563 *user_result
= result
;
568 bt_component_class_query_method_status
support_info_query(
569 bt_self_component_class_source
*comp_class
,
570 const bt_value
*params
, bt_logging_level log_level
,
571 const bt_value
**user_result
)
573 const bt_value
*input_type_value
;
574 const char *input_type
;
575 bt_component_class_query_method_status status
;
577 gchar
*metadata_path
= NULL
;
578 bt_value
*result
= NULL
;
580 input_type_value
= bt_value_map_borrow_entry_value_const(params
, "type");
581 BT_ASSERT(input_type_value
);
582 BT_ASSERT(bt_value_get_type(input_type_value
) == BT_VALUE_TYPE_STRING
);
583 input_type
= bt_value_string_get(input_type_value
);
585 result
= bt_value_map_create();
587 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR
;
591 if (strcmp(input_type
, "directory") == 0) {
592 const bt_value
*input_value
;
595 input_value
= bt_value_map_borrow_entry_value_const(params
, "input");
596 BT_ASSERT(input_value
);
597 BT_ASSERT(bt_value_get_type(input_value
) == BT_VALUE_TYPE_STRING
);
598 path
= bt_value_string_get(input_value
);
600 metadata_path
= g_build_filename(path
, CTF_FS_METADATA_FILENAME
, NULL
);
601 if (!metadata_path
) {
602 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR
;
607 * If the metadata file exists in this directory, consider it to
610 if (g_file_test(metadata_path
, G_FILE_TEST_EXISTS
)) {
615 if (bt_value_map_insert_real_entry(result
, "weight", weight
) != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
616 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR
;
621 * Use the arbitrary constant string "ctf" as the group, such that all
622 * found ctf traces are passed to the same instance of src.ctf.fs.
624 if (bt_value_map_insert_string_entry(result
, "group", "ctf") != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
625 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR
;
629 *user_result
= result
;
631 status
= BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK
;
634 g_free(metadata_path
);
635 bt_value_put_ref(result
);