From: Simon Marchi Date: Tue, 28 May 2019 16:01:36 +0000 (-0400) Subject: src.ctf.fs: add stream port name to trace-info query, use it for stream intersection X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=commitdiff_plain;h=a38d765099a8eb46b2b319381054db90a75029a9 src.ctf.fs: add stream port name to trace-info query, use it for stream intersection For creating stream intersection trimmers, we currently rely on the fact that the CLI and the Python bindings know that src.ctf.fs component port names are the path to the data stream files (or the first data stream file for the corresponding stream, if there are multiple files). Using this information, they can deduce which trace (from the trace-info query) this port is from, by checking for a trace whose path is a prefix of the port name. This patch changes how this works to avoid the CLI and Python bindings having internal knowledge of how the src.ctf.fs component class names the ports. The trace-info query now include a "port-name" property for each stream. This property contains the name that a src.ctf.fs component would give the port associated to this stream. This allows the CLI and the Python bindings to match a port with a trace (and therefore an intersection range) just by comparing the port name in the actual component instance with the port name in the trace-info query result. Here's an excerpt (just two streams) from a trace-info result with this patch applied: - range-ns: begin: 1556911621056878212 end: 1556911631077588247 class-id: 0 paths: - /home/smarchi/src/lttng-envs/augmented-metadata/home/lttng-traces/perpid-20190503-152658/archives/20190503T152700-0400-20190503T152703-0400-1/ust/pid/hello-ust-2335-20190503-152701/chan_2 - /home/smarchi/src/lttng-envs/augmented-metadata/home/lttng-traces/perpid-20190503-152658/20190503T152705-0400-3/ust/pid/hello-ust-2335-20190503-152701/chan_2 port-name: 1838b922-0089-497c-96e7-2c7ad63e901e | 0 | 2 id: 2 - range-ns: begin: 1556911621053751789 end: 1556911631077569864 class-id: 0 paths: - /home/smarchi/src/lttng-envs/augmented-metadata/home/lttng-traces/perpid-20190503-152658/archives/20190503T152700-0400-20190503T152703-0400-1/ust/pid/hello-ust-2335-20190503-152701/chan_1 - /home/smarchi/src/lttng-envs/augmented-metadata/home/lttng-traces/perpid-20190503-152658/20190503T152705-0400-3/ust/pid/hello-ust-2335-20190503-152701/chan_1 port-name: 1838b922-0089-497c-96e7-2c7ad63e901e | 0 | 1 id: 1 I added some simple tests, although they don't cover all the cases. We currently don't have a trace without uuid, or a trace with stream_instance_id fields, to test the remaining possibilities. I plan on adding some soon, when adding tests for the trace merging feature, at which point we can also enhance test_query_trace_info.QueryTraceInfoPortNameTestCase. Change-Id: Ib3afc19f2c0e09a7235475b53f8023c8a2f40ebf Signed-off-by: Simon Marchi Reviewed-on: https://review.lttng.org/c/babeltrace/+/1339 Tested-by: jenkins Reviewed-by: Philippe Proulx --- diff --git a/bindings/python/bt2/bt2/trace_collection_message_iterator.py b/bindings/python/bt2/bt2/trace_collection_message_iterator.py index 269e7962..2185fcd5 100644 --- a/bindings/python/bt2/bt2/trace_collection_message_iterator.py +++ b/bindings/python/bt2/bt2/trace_collection_message_iterator.py @@ -140,14 +140,15 @@ class TraceCollectionMessageIterator(bt2.message_iterator._MessageIterator): begin = None end = None - # find the trace info for this port's trace by name's prefix + # find the trace info for this port's trace try: for trace_info in trace_info_res: - if port.name.startswith(str(trace_info['path'])): - range_ns = trace_info['intersection-range-ns'] - begin = range_ns['begin'] - end = range_ns['end'] - break + for stream in trace_info['streams']: + if stream['port-name'] == port.name: + range_ns = trace_info['intersection-range-ns'] + begin = range_ns['begin'] + end = range_ns['end'] + break except Exception: pass diff --git a/cli/babeltrace.c b/cli/babeltrace.c index 814ad0f3..913a7421 100644 --- a/cli/babeltrace.c +++ b/cli/babeltrace.c @@ -2169,8 +2169,6 @@ int set_stream_intersections(struct cmd_run_ctx *ctx, const bt_value *intersection_range = NULL; const bt_value *intersection_begin = NULL; const bt_value *intersection_end = NULL; - const bt_value *stream_path_value = NULL; - const bt_value *stream_paths = NULL; const bt_value *stream_infos = NULL; const bt_value *stream_info = NULL; struct port_id *port_id = NULL; @@ -2269,20 +2267,8 @@ int set_stream_intersections(struct cmd_run_ctx *ctx, goto error; } - /* - * FIXME - * - * The first path of a stream's "paths" is currently used to - * associate streams/ports to a given trace intersection. - * - * This is a fragile hack as it relies on the port names - * being set to the various streams path. - * - * A stream name should be introduced as part of the trace-info - * query result. - */ for (stream_idx = 0; stream_idx < stream_count; stream_idx++) { - const char *stream_path; + const bt_value *port_name; port_id = g_new0(struct port_id, 1); if (!port_id) { @@ -2314,26 +2300,14 @@ int set_stream_intersections(struct cmd_run_ctx *ctx, goto error; } - stream_paths = bt_value_map_borrow_entry_value_const(stream_info, - "paths"); - if (!stream_paths || !bt_value_is_array(stream_paths)) { - ret = -1; - BT_LOGD_STR("Cannot retrieve stream paths from trace in query result."); - goto error; - } - - stream_path_value = - bt_value_array_borrow_element_by_index_const( - stream_paths, 0); - if (!stream_path_value || - !bt_value_is_string(stream_path_value)) { + port_name = bt_value_map_borrow_entry_value_const(stream_info, "port-name"); + if (!port_name || !bt_value_is_string(port_name)) { ret = -1; - BT_LOGD_STR("Cannot retrieve stream path value from trace in query result."); + BT_LOGD_STR("Cannot retrieve port name in query result."); goto error; } - stream_path = bt_value_string_get(stream_path_value); - port_id->port_name = strdup(stream_path); + port_id->port_name = g_strdup(bt_value_string_get(port_name)); if (!port_id->port_name) { ret = -1; BT_LOGE_STR("Cannot allocate memory for port_id port_name."); diff --git a/plugins/ctf/fs-src/fs.c b/plugins/ctf/fs-src/fs.c index 6f02c8de..f1583ae4 100644 --- a/plugins/ctf/fs-src/fs.c +++ b/plugins/ctf/fs-src/fs.c @@ -399,29 +399,48 @@ void ctf_fs_finalize(bt_self_component_source *component) bt_self_component_source_as_self_component(component))); } -static -GString *get_stream_instance_unique_name( - struct ctf_fs_ds_file_group *ds_file_group) +gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group) { - GString *name; - struct ctf_fs_ds_file_info *ds_file_info; + GString *name = g_string_new(NULL); - name = g_string_new(NULL); - if (!name) { - goto end; + /* + * The unique port name is generated by concatenating unique identifiers + * for: + * + * - the trace + * - the stream class + * - the stream + */ + + /* For the trace, use the uuid if present, else the path. */ + if (ds_file_group->ctf_fs_trace->metadata->tc->is_uuid_set) { + char uuid_str[BABELTRACE_UUID_STR_LEN]; + + bt_uuid_unparse(ds_file_group->ctf_fs_trace->metadata->tc->uuid, uuid_str); + g_string_assign(name, uuid_str); + } else { + g_string_assign(name, ds_file_group->ctf_fs_trace->path->str); } /* - * If there's more than one stream file in the stream file - * group, the first (earliest) stream file's path is used as - * the stream's unique name. + * For the stream class, use the id if present. We can omit this field + * otherwise, as there will only be a single stream class. */ - BT_ASSERT(ds_file_group->ds_file_infos->len > 0); - ds_file_info = g_ptr_array_index(ds_file_group->ds_file_infos, 0); - g_string_assign(name, ds_file_info->path->str); + if (ds_file_group->sc->id != UINT64_C(-1)) { + g_string_append_printf(name, " | %" PRIu64, ds_file_group->sc->id); + } -end: - return name; + /* For the stream, use the id if present, else, use the path. */ + if (ds_file_group->stream_id != UINT64_C(-1)) { + g_string_append_printf(name, " | %" PRIu64, ds_file_group->stream_id); + } else { + BT_ASSERT(ds_file_group->ds_file_infos->len == 1); + struct ctf_fs_ds_file_info *ds_file_info = + g_ptr_array_index(ds_file_group->ds_file_infos, 0); + g_string_append_printf(name, " | %s", ds_file_info->path->str); + } + + return g_string_free(name, FALSE); } static @@ -431,14 +450,14 @@ int create_one_port_for_trace(struct ctf_fs_component *ctf_fs, { int ret = 0; struct ctf_fs_port_data *port_data = NULL; - GString *port_name = NULL; + gchar *port_name; - port_name = get_stream_instance_unique_name(ds_file_group); + port_name = ctf_fs_make_port_name(ds_file_group); if (!port_name) { goto error; } - BT_LOGD("Creating one port named `%s`", port_name->str); + BT_LOGD("Creating one port named `%s`", port_name); /* Create output port for this file */ port_data = g_new0(struct ctf_fs_port_data, 1); @@ -449,7 +468,7 @@ int create_one_port_for_trace(struct ctf_fs_component *ctf_fs, port_data->ctf_fs = ctf_fs; port_data->ds_file_group = ds_file_group; ret = bt_self_component_source_add_output_port( - ctf_fs->self_comp, port_name->str, port_data, NULL); + ctf_fs->self_comp, port_name, port_data, NULL); if (ret) { goto error; } @@ -463,7 +482,7 @@ error: end: if (port_name) { - g_string_free(port_name, TRUE); + g_free(port_name); } port_data_destroy(port_data); @@ -1587,6 +1606,31 @@ end: return ret; } +static +GString *get_stream_instance_unique_name( + struct ctf_fs_ds_file_group *ds_file_group) +{ + GString *name; + struct ctf_fs_ds_file_info *ds_file_info; + + name = g_string_new(NULL); + if (!name) { + goto end; + } + + /* + * If there's more than one stream file in the stream file + * group, the first (earliest) stream file's path is used as + * the stream's unique name. + */ + BT_ASSERT(ds_file_group->ds_file_infos->len > 0); + ds_file_info = g_ptr_array_index(ds_file_group->ds_file_infos, 0); + g_string_assign(name, ds_file_info->path->str); + +end: + return name; +} + /* Create the IR stream objects for ctf_fs_trace. */ static diff --git a/plugins/ctf/fs-src/fs.h b/plugins/ctf/fs-src/fs.h index 6594b611..ed65aa3c 100644 --- a/plugins/ctf/fs-src/fs.h +++ b/plugins/ctf/fs-src/fs.h @@ -218,4 +218,13 @@ BT_HIDDEN bool read_src_fs_parameters(const bt_value *params, const bt_value **paths, struct ctf_fs_component *ctf_fs); +/* + * Generate the port name to be used for a given data stream file group. + * + * The result must be freed using g_free by the caller. + */ + +BT_HIDDEN +gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group); + #endif /* BABELTRACE_PLUGIN_CTF_FS_H */ diff --git a/plugins/ctf/fs-src/query.c b/plugins/ctf/fs-src/query.c index 31d71081..d666e1c4 100644 --- a/plugins/ctf/fs-src/query.c +++ b/plugins/ctf/fs-src/query.c @@ -273,6 +273,7 @@ int populate_stream_info(struct ctf_fs_ds_file_group *group, bt_value *file_paths; struct ctf_fs_ds_file_info *first_file_info, *last_file_info; struct ctf_fs_ds_index_entry *first_ds_index_entry, *last_ds_index_entry; + gchar *port_name = NULL; file_paths = bt_value_array_create(); if (!file_paths) { @@ -344,6 +345,20 @@ int populate_stream_info(struct ctf_fs_ds_file_group *group, if (ret) { goto end; } + + port_name = ctf_fs_make_port_name(group); + if (!port_name) { + ret = -1; + goto end; + } + + status = bt_value_map_insert_string_entry(group_info, "port-name", + port_name); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + end: bt_value_put_ref(file_paths); return ret; diff --git a/tests/plugins/ctf/test_query_trace_info.py b/tests/plugins/ctf/test_query_trace_info.py index c8b5923c..053c0e1e 100644 --- a/tests/plugins/ctf/test_query_trace_info.py +++ b/tests/plugins/ctf/test_query_trace_info.py @@ -17,6 +17,7 @@ import unittest import bt2 import os +import re test_ctf_traces_path = os.environ['TEST_CTF_TRACES_PATH'] @@ -121,5 +122,54 @@ class QueryTraceInfoClockOffsetTestCase(unittest.TestCase): 'clock-class-offset-ns': None, }) + +class QueryTraceInfoPortNameTestCase(unittest.TestCase): + def setUp(self): + ctf = bt2.find_plugin("ctf") + self._fs = ctf.source_component_classes["fs"] + + self._executor = bt2.QueryExecutor() + + def test_trace_uuid_stream_class_id_no_stream_id(self): + res = self._executor.query( + self._fs, + "trace-info", + { + "paths": [ + os.path.join( + test_ctf_traces_path, "intersection", "3eventsintersect" + ) + ] + }, + ) + self.assertEqual(len(res), 1) + trace = res[0] + streams = sorted(trace["streams"], key=sort_by_begin) + self.assertEqual(len(streams), 2) + self.assertRegexpMatches( + str(streams[0]["port-name"]), + r"^7afe8fbe-79b8-4f6a-bbc7-d0c782e7ddaf \| 0 \| .*/tests/ctf-traces/intersection/3eventsintersect/test_stream_0$", + ) + self.assertRegexpMatches( + str(streams[1]["port-name"]), + r"^7afe8fbe-79b8-4f6a-bbc7-d0c782e7ddaf \| 0 \| .*/tests/ctf-traces/intersection/3eventsintersect/test_stream_1$", + ) + + def test_trace_uuid_no_stream_class_id_no_stream_id(self): + res = self._executor.query( + self._fs, + "trace-info", + {"paths": [os.path.join(test_ctf_traces_path, "succeed", "succeed1")]}, + ) + self.assertEqual(len(res), 1) + trace = res[0] + streams = sorted(trace["streams"], key=sort_by_begin) + self.assertEqual(len(streams), 1) + self.assertRegexpMatches( + str(streams[0]["port-name"]), + r"^2a6422d0-6cee-11e0-8c08-cb07d7b3a564 \| .*/tests/ctf-traces/succeed/succeed1/dummystream$", + ) + + if __name__ == '__main__': unittest.main()