2 * SPDX-License-Identifier: MIT
4 * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
11 #include "common/assert.h"
12 #include "common/uuid.h"
13 #include "compat/memstream.h"
14 #include "cpp-common/vendor/fmt/format.h"
17 #include "decoder-packetized-file-stream-to-buf.hpp"
18 #include "decoder.hpp"
19 #include "parser-wrap.hpp"
20 #include "scanner.hpp"
22 #define TSDL_MAGIC 0x75d11d57
24 struct ctf_metadata_decoder
26 explicit ctf_metadata_decoder(const bt2c::Logger
& parentLogger
) :
27 logger
{parentLogger
, "PLUGIN/CTF/META/DECODER"}, config
{logger
}
32 struct ctf_scanner
*scanner
= nullptr;
33 GString
*text
= nullptr;
34 ctf_visitor_generate_ir::UP visitor
;
36 bool is_uuid_set
= false;
38 struct ctf_metadata_decoder_config config
;
39 bool has_checked_plaintext_signature
= false;
47 uint32_t content_size
;
49 uint8_t compression_scheme
;
50 uint8_t encryption_scheme
;
51 uint8_t checksum_scheme
;
54 } __attribute__((__packed__
));
56 int ctf_metadata_decoder_is_packetized(FILE *fp
, bool *is_packetized
, int *byte_order
,
57 const bt2c::Logger
& logger
)
63 *is_packetized
= false;
64 len
= fread(&magic
, sizeof(magic
), 1, fp
);
68 "Cannot read first metadata packet header: assuming the stream is not packetized.");
74 if (magic
== TSDL_MAGIC
) {
75 *is_packetized
= true;
76 *byte_order
= BYTE_ORDER
;
77 } else if (magic
== GUINT32_SWAP_LE_BE(TSDL_MAGIC
)) {
78 *is_packetized
= true;
79 *byte_order
= BYTE_ORDER
== BIG_ENDIAN
? LITTLE_ENDIAN
: BIG_ENDIAN
;
89 ctf_metadata_decoder_up
90 ctf_metadata_decoder_create(const struct ctf_metadata_decoder_config
*config
)
94 BT_CPPLOGD_SPEC(config
->logger
,
95 "Creating CTF metadata decoder: "
96 "clock-class-offset-s={}, "
97 "clock-class-offset-ns={}",
98 config
->clock_class_offset_s
, config
->clock_class_offset_ns
);
100 ctf_metadata_decoder
*mdec
= new ctf_metadata_decoder
{config
->logger
};
101 mdec
->scanner
= ctf_scanner_alloc(mdec
->logger
);
102 if (!mdec
->scanner
) {
103 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
104 "Cannot allocate a metadata lexical scanner: "
110 mdec
->text
= g_string_new(NULL
);
112 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
113 "Failed to allocate one GString: "
120 mdec
->config
= *config
;
121 mdec
->visitor
= ctf_visitor_generate_ir_create(config
);
122 if (!mdec
->visitor
) {
123 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
124 "Failed to create a CTF IR metadata AST visitor: "
130 BT_CPPLOGD_SPEC(mdec
->logger
,
131 "Creating CTF metadata decoder: "
132 "clock-class-offset-s={}, "
133 "clock-class-offset-ns={}, addr={}",
134 config
->clock_class_offset_s
, config
->clock_class_offset_ns
, fmt::ptr(mdec
));
138 ctf_metadata_decoder_destroy(mdec
);
142 return ctf_metadata_decoder_up
{mdec
};
145 void ctf_metadata_decoder_destroy(struct ctf_metadata_decoder
*mdec
)
152 ctf_scanner_free(mdec
->scanner
);
156 g_string_free(mdec
->text
, TRUE
);
159 BT_CPPLOGD_SPEC(mdec
->logger
, "Destroying CTF metadata decoder: addr={}", fmt::ptr(mdec
));
164 void ctf_metadata_decoder_deleter::operator()(ctf_metadata_decoder
*decoder
)
166 ctf_metadata_decoder_destroy(decoder
);
169 enum ctf_metadata_decoder_status
170 ctf_metadata_decoder_append_content(struct ctf_metadata_decoder
*mdec
, FILE *fp
)
172 enum ctf_metadata_decoder_status status
= CTF_METADATA_DECODER_STATUS_OK
;
175 bool close_fp
= false;
180 ret
= ctf_metadata_decoder_is_packetized(fp
, &is_packetized
, &mdec
->bo
, mdec
->logger
);
182 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
187 BT_CPPLOGI_SPEC(mdec
->logger
, "Metadata stream is packetized: mdec-addr={}",
189 ret
= ctf_metadata_decoder_packetized_file_stream_to_buf(
190 fp
, &buf
, mdec
->bo
, &mdec
->is_uuid_set
, mdec
->uuid
, mdec
->logger
);
192 BT_CPPLOGE_APPEND_CAUSE_SPEC(
194 "Cannot decode packetized metadata packets to metadata text: "
195 "mdec-addr={}, ret={}",
196 fmt::ptr(mdec
), ret
);
197 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
201 if (strlen(buf
) == 0) {
202 /* An empty metadata packet is OK. */
206 /* Convert the real file pointer to a memory file pointer */
207 fp
= bt_fmemopen(buf
, strlen(buf
), "rb");
210 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
211 "Cannot memory-open metadata buffer: {}: "
213 strerror(errno
), fmt::ptr(mdec
));
214 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
217 } else if (!mdec
->has_checked_plaintext_signature
) {
218 unsigned int major
, minor
;
220 const long init_pos
= ftell(fp
);
222 BT_CPPLOGI_SPEC(mdec
->logger
, "Metadata stream is plain text: mdec-addr={}",
226 BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(mdec
->logger
, "Failed to get current file position",
228 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
232 /* Check text-only metadata header and version */
233 nr_items
= fscanf(fp
, "/* CTF %10u.%10u", &major
, &minor
);
237 "Missing \"/* CTF major.minor\" signature in plain text metadata file stream: "
242 BT_CPPLOGI_SPEC(mdec
->logger
, "Found metadata stream version in signature: version={}.{}",
245 if (!ctf_metadata_decoder_is_packet_version_valid(major
, minor
)) {
246 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
247 "Invalid metadata version found in plain text signature: "
248 "version={}.{}, mdec-addr={}",
249 major
, minor
, fmt::ptr(mdec
));
250 status
= CTF_METADATA_DECODER_STATUS_INVAL_VERSION
;
254 if (fseek(fp
, init_pos
, SEEK_SET
)) {
255 BT_CPPLOGE_APPEND_CAUSE_SPEC(
257 "Cannot seek metadata file stream to initial position: {}: "
259 strerror(errno
), fmt::ptr(mdec
));
260 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
264 mdec
->has_checked_plaintext_signature
= true;
268 if (mdec
->logger
.wouldLogT()) {
273 /* Save the file's position: we'll seek back to append the plain text */
276 if (mdec
->config
.keep_plain_text
) {
277 start_pos
= ftell(fp
);
280 /* Append the metadata text content */
281 ret
= ctf_scanner_append_ast(mdec
->scanner
, fp
);
283 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
284 "Cannot create the metadata AST out of the metadata text: "
287 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
291 /* We know it's complete: append plain text */
292 if (mdec
->config
.keep_plain_text
) {
293 BT_ASSERT(start_pos
!= -1);
294 ret
= fseek(fp
, start_pos
, SEEK_SET
);
296 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
, "Failed to seek file: ret={}, mdec-addr={}",
297 ret
, fmt::ptr(mdec
));
298 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
302 ret
= bt_common_append_file_content_to_g_string(mdec
->text
, fp
);
304 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
305 "Failed to append to current plain text: "
306 "ret={}, mdec-addr={}",
307 ret
, fmt::ptr(mdec
));
308 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
313 ret
= ctf_visitor_semantic_check(0, &mdec
->scanner
->ast
->root
, mdec
->logger
);
315 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
316 "Validation of the metadata semantics failed: "
319 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
323 if (mdec
->config
.create_trace_class
) {
324 ret
= ctf_visitor_generate_ir_visit_node(mdec
->visitor
.get(), &mdec
->scanner
->ast
->root
);
330 BT_CPPLOGD_SPEC(mdec
->logger
,
331 "While visiting metadata AST: incomplete data: "
334 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
337 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
338 "Failed to visit AST node to create CTF IR objects: "
339 "mdec-addr={}, ret={}",
340 fmt::ptr(mdec
), ret
);
341 status
= CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR
;
351 if (fp
&& close_fp
) {
353 BT_CPPLOGE_SPEC(mdec
->logger
,
354 "Cannot close metadata file stream: "
365 bt_trace_class
*ctf_metadata_decoder_get_ir_trace_class(struct ctf_metadata_decoder
*mdec
)
368 BT_ASSERT_DBG(mdec
->config
.create_trace_class
);
369 return ctf_visitor_generate_ir_get_ir_trace_class(mdec
->visitor
.get());
372 struct ctf_trace_class
*
373 ctf_metadata_decoder_borrow_ctf_trace_class(struct ctf_metadata_decoder
*mdec
)
376 BT_ASSERT_DBG(mdec
->config
.create_trace_class
);
377 return ctf_visitor_generate_ir_borrow_ctf_trace_class(mdec
->visitor
.get());
380 const char *ctf_metadata_decoder_get_text(struct ctf_metadata_decoder
*mdec
)
383 BT_ASSERT_DBG(mdec
->config
.keep_plain_text
);
384 return mdec
->text
->str
;
387 int ctf_metadata_decoder_get_byte_order(struct ctf_metadata_decoder
*mdec
)
393 int ctf_metadata_decoder_get_uuid(struct ctf_metadata_decoder
*mdec
, bt_uuid_t uuid
)
399 if (!mdec
->is_uuid_set
) {
404 bt_uuid_copy(uuid
, mdec
->uuid
);
410 static enum ctf_metadata_decoder_status
find_uuid_in_trace_decl(struct ctf_metadata_decoder
*mdec
,
411 struct ctf_node
*trace_node
,
414 enum ctf_metadata_decoder_status status
= CTF_METADATA_DECODER_STATUS_OK
;
415 struct ctf_node
*entry_node
;
416 struct bt_list_head
*decl_list
= &trace_node
->u
.trace
.declaration_list
;
419 bt_list_for_each_entry (entry_node
, decl_list
, siblings
) {
420 if (entry_node
->type
== NODE_CTF_EXPRESSION
) {
423 left
= ctf_ast_concatenate_unary_strings(&entry_node
->u
.ctf_expression
.left
);
425 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
, "Cannot concatenate unary strings.");
426 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
430 if (strcmp(left
, "uuid") == 0) {
432 ctf_ast_get_unary_uuid(&entry_node
->u
.ctf_expression
.right
, uuid
, mdec
->logger
);
434 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
, "Invalid trace's `uuid` attribute.");
435 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
447 status
= CTF_METADATA_DECODER_STATUS_NONE
;
454 enum ctf_metadata_decoder_status
455 ctf_metadata_decoder_get_trace_class_uuid(struct ctf_metadata_decoder
*mdec
, bt_uuid_t uuid
)
457 enum ctf_metadata_decoder_status status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
458 struct ctf_node
*root_node
= &mdec
->scanner
->ast
->root
;
459 struct ctf_node
*trace_node
;
462 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
466 trace_node
= bt_list_entry(root_node
->u
.root
.trace
.next
, struct ctf_node
, siblings
);
468 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
472 status
= find_uuid_in_trace_decl(mdec
, trace_node
, uuid
);