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 <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1339
Tested-by: jenkins
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
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
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;
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) {
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.");
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
{
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);
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;
}
end:
if (port_name) {
- g_string_free(port_name, TRUE);
+ g_free(port_name);
}
port_data_destroy(port_data);
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
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 */
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) {
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;
import unittest
import bt2
import os
+import re
test_ctf_traces_path = os.environ['TEST_CTF_TRACES_PATH']
'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()