Commit | Line | Data |
---|---|---|
04c0ba87 | 1 | /* |
0235b0db | 2 | * SPDX-License-Identifier: MIT |
04c0ba87 JG |
3 | * |
4 | * Copyright 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
5 | * | |
0235b0db | 6 | * Babeltrace CTF file system Reader Component queries |
04c0ba87 JG |
7 | */ |
8 | ||
27a14e13 SM |
9 | #define BT_CLOG_CFG logCfg |
10 | #define BT_LOG_TAG "PLUGIN/SRC.CTF.FS/QUERY" | |
98903a3e | 11 | |
087cd0f5 | 12 | #include "query.hpp" |
04c0ba87 | 13 | #include <stdbool.h> |
493917ba SM |
14 | #include <glib.h> |
15 | #include <glib/gstdio.h> | |
16 | #include <fcntl.h> | |
17 | #include <sys/types.h> | |
18 | #include <sys/stat.h> | |
578e048b | 19 | #include "common/assert.h" |
087cd0f5 | 20 | #include "metadata.hpp" |
71332bf4 SM |
21 | #include "../common/src/metadata/tsdl/metadata-stream-decoder.hpp" |
22 | #include "../common/src/metadata/metadata-stream-parser-utils.hpp" | |
578e048b | 23 | #include "common/common.h" |
91d81473 | 24 | #include "common/macros.h" |
6aa47d30 | 25 | #include "plugins/common/param-validation/param-validation.h" |
3fadfbc0 | 26 | #include <babeltrace2/babeltrace.h> |
087cd0f5 | 27 | #include "fs.hpp" |
27a14e13 | 28 | #include "cpp-common/cfg-logging-error-reporting.hpp" |
396a8f9f | 29 | #include "cpp-common/cfg-logging-error-reporting-throw.hpp" |
e1871ef7 | 30 | #include "cpp-common/libc-up.hpp" |
396a8f9f | 31 | #include "cpp-common/exc.hpp" |
71332bf4 | 32 | #include "cpp-common/file-utils.hpp" |
f1946ada | 33 | #include "cpp-common/make-unique.hpp" |
55314f2a | 34 | |
4164020e | 35 | #define METADATA_TEXT_SIG "/* CTF 1.8" |
04c0ba87 | 36 | |
4164020e SM |
37 | struct range |
38 | { | |
39 | int64_t begin_ns = 0; | |
40 | int64_t end_ns = 0; | |
41 | bool set = false; | |
97ade20b JG |
42 | }; |
43 | ||
6aa47d30 SM |
44 | static struct bt_param_validation_map_value_entry_descr metadataInfoQueryParamsDesc[] = { |
45 | {"path", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, | |
46 | bt_param_validation_value_descr::makeString()}, | |
47 | BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END}; | |
48 | ||
04c0ba87 | 49 | BT_HIDDEN |
396a8f9f | 50 | bt2::Value::Shared metadata_info_query(bt2::ConstMapValue params, const bt2_common::LogCfg& logCfg) |
04c0ba87 | 51 | { |
6aa47d30 SM |
52 | gchar *validateError = NULL; |
53 | auto validationStatus = bt_param_validation_validate( | |
54 | params.libObjPtr(), metadataInfoQueryParamsDesc, &validateError); | |
4164020e | 55 | |
6aa47d30 SM |
56 | if (validationStatus == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) { |
57 | throw bt2_common::MemoryError {}; | |
58 | } else if (validationStatus == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) { | |
59 | const std::string error {validateError}; | |
60 | ||
61 | g_free(validateError); | |
62 | BT_CLOGE_APPEND_CAUSE_AND_THROW(bt2_common::Error, "%s", error.data()); | |
4164020e SM |
63 | } |
64 | ||
71332bf4 | 65 | const auto path = params["path"]->asString().value(); |
6aa47d30 | 66 | |
71332bf4 SM |
67 | try { |
68 | const auto buffer = | |
69 | bt2_common::dataFromFile(std::string {path.to_string() + "/metadata"}.c_str()); | |
4164020e | 70 | |
71332bf4 | 71 | ctf::src::MetadataStreamDecoder decoder {logCfg}; |
4164020e | 72 | |
71332bf4 SM |
73 | auto plainText = |
74 | decoder.decode(buffer.data(), bt2_common::DataLen::fromBytes(buffer.size())); | |
4164020e | 75 | |
71332bf4 SM |
76 | auto result = bt2::MapValue::create(); |
77 | /* | |
78 | * If the metadata does not already start with the plaintext metadata | |
79 | * signature, prepend it. | |
80 | */ | |
81 | if (plainText.rfind(METADATA_TEXT_SIG, 0) != 0) { | |
82 | plainText.insert(0, std::string {METADATA_TEXT_SIG} + " */\n\n"); | |
83 | } | |
4164020e | 84 | |
71332bf4 | 85 | result->insert("text", plainText.data()); |
c7eee084 | 86 | |
71332bf4 SM |
87 | result->insert("is-packetized", decoder.pktInfo().has_value()); |
88 | return result; | |
89 | } catch (const bt2_common::Error&) { | |
90 | BT_CLOGE_APPEND_CAUSE_AND_RETHROW("Error getting plaintext metadata section from file"); | |
91 | } | |
97ade20b | 92 | } |
9ec238a8 | 93 | |
396a8f9f | 94 | static void add_range(bt2::MapValue info, struct range *range, const char *range_name) |
97ade20b | 95 | { |
4164020e SM |
96 | if (!range->set) { |
97 | /* Not an error. */ | |
396a8f9f | 98 | return; |
4164020e SM |
99 | } |
100 | ||
396a8f9f SM |
101 | bt2::MapValue rangeMap = info.insertEmptyMap(range_name); |
102 | rangeMap.insert("begin", range->begin_ns); | |
103 | rangeMap.insert("end", range->end_ns); | |
97ade20b JG |
104 | } |
105 | ||
396a8f9f SM |
106 | static void populate_stream_info(struct ctf_fs_ds_file_group *group, bt2::MapValue groupInfo, |
107 | struct range *stream_range, const bt2_common::LogCfg& logCfg) | |
97ade20b | 108 | { |
4164020e SM |
109 | /* |
110 | * Since each `struct ctf_fs_ds_file_group` has a sorted array of | |
111 | * `struct ctf_fs_ds_index_entry`, we can compute the stream range from | |
112 | * the timestamp_begin of the first index entry and the timestamp_end | |
113 | * of the last index entry. | |
114 | */ | |
115 | BT_ASSERT(group->index); | |
5ce40cf7 | 116 | BT_ASSERT(!group->index->entries.empty()); |
4164020e SM |
117 | |
118 | /* First entry. */ | |
3505f709 | 119 | const ctf_fs_ds_index_entry& first_ds_index_entry = group->index->entries.front(); |
4164020e SM |
120 | |
121 | /* Last entry. */ | |
3505f709 | 122 | const ctf_fs_ds_index_entry& last_ds_index_entry = group->index->entries.back(); |
4164020e | 123 | |
3505f709 SM |
124 | stream_range->begin_ns = first_ds_index_entry.timestamp_begin_ns; |
125 | stream_range->end_ns = last_ds_index_entry.timestamp_end_ns; | |
4164020e SM |
126 | |
127 | /* | |
128 | * If any of the begin and end timestamps is not set it means that | |
129 | * packets don't include `timestamp_begin` _and_ `timestamp_end` fields | |
130 | * in their packet context so we can't set the range. | |
131 | */ | |
132 | stream_range->set = | |
133 | stream_range->begin_ns != UINT64_C(-1) && stream_range->end_ns != UINT64_C(-1); | |
134 | ||
396a8f9f | 135 | add_range(groupInfo, stream_range, "range-ns"); |
4164020e | 136 | |
a373ad9a SM |
137 | std::string portName = ctf_fs_make_port_name(group); |
138 | groupInfo.insert("port-name", portName.c_str()); | |
97ade20b JG |
139 | } |
140 | ||
396a8f9f SM |
141 | static void populate_trace_info(const struct ctf_fs_trace *trace, bt2::MapValue traceInfo, |
142 | const bt2_common::LogCfg& logCfg) | |
97ade20b | 143 | { |
4164020e | 144 | /* Add trace range info only if it contains streams. */ |
1d732493 | 145 | if (trace->ds_file_groups.empty()) { |
396a8f9f | 146 | BT_CLOGE_APPEND_CAUSE_AND_THROW(bt2_common::Error, "Trace has no streams: trace-path=%s", |
94b0446f | 147 | trace->path.c_str()); |
4164020e SM |
148 | } |
149 | ||
396a8f9f | 150 | bt2::ArrayValue fileGroups = traceInfo.insertEmptyArray("stream-infos"); |
4164020e SM |
151 | |
152 | /* Find range of all stream groups, and of the trace. */ | |
1d732493 | 153 | for (const ctf_fs_ds_file_group::UP& group : trace->ds_file_groups) { |
4164020e | 154 | range group_range; |
396a8f9f | 155 | bt2::MapValue groupInfo = fileGroups.appendEmptyMap(); |
1d732493 | 156 | populate_stream_info(group.get(), groupInfo, &group_range, logCfg); |
4164020e | 157 | } |
97ade20b JG |
158 | } |
159 | ||
160 | BT_HIDDEN | |
396a8f9f | 161 | bt2::Value::Shared trace_infos_query(bt2::ConstMapValue params, const bt2_common::LogCfg& logCfg) |
97ade20b | 162 | { |
f1946ada | 163 | ctf_fs_component::UP ctf_fs = bt2_common::makeUnique<ctf_fs_component>(logCfg); |
4164020e | 164 | |
396a8f9f SM |
165 | const bt_value *inputs_value = NULL; |
166 | const bt_value *trace_name_value; | |
167 | ||
168 | if (!read_src_fs_parameters(params.libObjPtr(), &inputs_value, &trace_name_value, | |
169 | ctf_fs.get())) { | |
170 | BT_CLOGE_APPEND_CAUSE_AND_THROW(bt2_common::Error, "Failed to read parameters"); | |
4164020e SM |
171 | } |
172 | ||
6bcdb2d6 SM |
173 | if (ctf_fs_component_create_ctf_fs_trace(ctf_fs.get(), inputs_value, trace_name_value, |
174 | nullptr)) { | |
396a8f9f | 175 | BT_CLOGE_APPEND_CAUSE_AND_THROW(bt2_common::Error, "Failed to create trace"); |
4164020e SM |
176 | } |
177 | ||
396a8f9f SM |
178 | bt2::ArrayValue::Shared result = bt2::ArrayValue::create(); |
179 | bt2::MapValue traceInfo = result->appendEmptyMap(); | |
86285034 | 180 | populate_trace_info(ctf_fs->trace.get(), traceInfo, logCfg); |
c7eee084 | 181 | |
396a8f9f | 182 | return result; |
04c0ba87 | 183 | } |
73760435 | 184 | |
6aa47d30 SM |
185 | static struct bt_param_validation_map_value_entry_descr supportInfoQueryParamsDesc[] = { |
186 | {"type", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, | |
187 | bt_param_validation_value_descr::makeString()}, | |
188 | {"input", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, | |
189 | bt_param_validation_value_descr::makeString()}, | |
190 | BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END}; | |
191 | ||
73760435 | 192 | BT_HIDDEN |
396a8f9f | 193 | bt2::Value::Shared support_info_query(bt2::ConstMapValue params, const bt2_common::LogCfg& logCfg) |
73760435 | 194 | { |
6aa47d30 SM |
195 | gchar *validateError = NULL; |
196 | auto validationStatus = bt_param_validation_validate( | |
197 | params.libObjPtr(), supportInfoQueryParamsDesc, &validateError); | |
198 | ||
199 | if (validationStatus == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) { | |
200 | throw bt2_common::MemoryError {}; | |
201 | } else if (validationStatus == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) { | |
202 | const std::string error {validateError}; | |
203 | ||
204 | g_free(validateError); | |
205 | BT_CLOGE_APPEND_CAUSE_AND_THROW(bt2_common::Error, "%s", error.data()); | |
206 | } | |
207 | ||
208 | bpstd::string_view type = params["type"]->asString().value(); | |
4164020e | 209 | |
396a8f9f | 210 | if (type != "directory") { |
6aa47d30 SM |
211 | /* |
212 | * The input type is not a directory so we are 100% sure it's not a CTF | |
213 | * 1.8 trace as it would need a directory with at least 1 metadata file | |
214 | * and 1 data stream file. | |
215 | */ | |
396a8f9f SM |
216 | bt2::MapValue::Shared result = bt2::MapValue::create(); |
217 | result->insert("weight", 0.0f); | |
218 | return result; | |
4164020e SM |
219 | } |
220 | ||
6aa47d30 | 221 | bpstd::string_view input = params["input"]->asString().value(); |
4164020e | 222 | |
71332bf4 SM |
223 | auto result = bt2::MapValue::create(); |
224 | try { | |
225 | const auto buffer = | |
226 | bt2_common::dataFromFile(std::string {input.to_string() + "/metadata"}.c_str()); | |
227 | ctf::src::MetadataStreamParser::ParseRet parseRet = ctf::src::parseMetadataStream( | |
228 | {}, nullptr, buffer.data(), buffer.data() + buffer.size(), logCfg); | |
229 | ctf::src::TraceCls *ctfTraceCls = parseRet.first.get(); | |
230 | BT_ASSERT(ctfTraceCls); | |
4164020e SM |
231 | |
232 | /* | |
71332bf4 SM |
233 | * We were able to parse the metadata file, so we are confident it's a |
234 | * CTF trace. | |
4164020e | 235 | */ |
71332bf4 SM |
236 | result->insert("weight", 0.75); |
237 | if (ctfTraceCls->uuid()) { | |
238 | result->insert("group", ctfTraceCls->uuid()->str()); | |
4164020e | 239 | } |
71332bf4 SM |
240 | } catch (const bt2_common::NoSuchFileOrDirectoryError&) { |
241 | /* | |
242 | * Failing to find the metadata file is not an error, it simply | |
243 | * indicates that the directory is not a trace. Report appropriate | |
244 | * weight of zero. | |
245 | */ | |
246 | bt_current_thread_clear_error(); | |
247 | result->insert("weight", 0.0); | |
4164020e | 248 | } |
396a8f9f | 249 | return result; |
73760435 | 250 | } |