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 struct ctf_visitor_generate_ir
*visitor
= nullptr;
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 struct ctf_metadata_decoder
*
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
);
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
));
160 ctf_visitor_generate_ir_destroy(mdec
->visitor
);
164 enum ctf_metadata_decoder_status
165 ctf_metadata_decoder_append_content(struct ctf_metadata_decoder
*mdec
, FILE *fp
)
167 enum ctf_metadata_decoder_status status
= CTF_METADATA_DECODER_STATUS_OK
;
170 bool close_fp
= false;
175 ret
= ctf_metadata_decoder_is_packetized(fp
, &is_packetized
, &mdec
->bo
, mdec
->logger
);
177 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
182 BT_CPPLOGI_SPEC(mdec
->logger
, "Metadata stream is packetized: mdec-addr={}",
184 ret
= ctf_metadata_decoder_packetized_file_stream_to_buf(
185 fp
, &buf
, mdec
->bo
, &mdec
->is_uuid_set
, mdec
->uuid
, mdec
->logger
);
187 BT_CPPLOGE_APPEND_CAUSE_SPEC(
189 "Cannot decode packetized metadata packets to metadata text: "
190 "mdec-addr={}, ret={}",
191 fmt::ptr(mdec
), ret
);
192 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
196 if (strlen(buf
) == 0) {
197 /* An empty metadata packet is OK. */
201 /* Convert the real file pointer to a memory file pointer */
202 fp
= bt_fmemopen(buf
, strlen(buf
), "rb");
205 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
206 "Cannot memory-open metadata buffer: {}: "
208 strerror(errno
), fmt::ptr(mdec
));
209 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
212 } else if (!mdec
->has_checked_plaintext_signature
) {
213 unsigned int major
, minor
;
215 const long init_pos
= ftell(fp
);
217 BT_CPPLOGI_SPEC(mdec
->logger
, "Metadata stream is plain text: mdec-addr={}",
221 BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(mdec
->logger
, "Failed to get current file position",
223 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
227 /* Check text-only metadata header and version */
228 nr_items
= fscanf(fp
, "/* CTF %10u.%10u", &major
, &minor
);
232 "Missing \"/* CTF major.minor\" signature in plain text metadata file stream: "
237 BT_CPPLOGI_SPEC(mdec
->logger
, "Found metadata stream version in signature: version={}.{}",
240 if (!ctf_metadata_decoder_is_packet_version_valid(major
, minor
)) {
241 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
242 "Invalid metadata version found in plain text signature: "
243 "version={}.{}, mdec-addr={}",
244 major
, minor
, fmt::ptr(mdec
));
245 status
= CTF_METADATA_DECODER_STATUS_INVAL_VERSION
;
249 if (fseek(fp
, init_pos
, SEEK_SET
)) {
250 BT_CPPLOGE_APPEND_CAUSE_SPEC(
252 "Cannot seek metadata file stream to initial position: {}: "
254 strerror(errno
), fmt::ptr(mdec
));
255 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
259 mdec
->has_checked_plaintext_signature
= true;
263 if (mdec
->logger
.wouldLogT()) {
268 /* Save the file's position: we'll seek back to append the plain text */
271 if (mdec
->config
.keep_plain_text
) {
272 start_pos
= ftell(fp
);
275 /* Append the metadata text content */
276 ret
= ctf_scanner_append_ast(mdec
->scanner
, fp
);
278 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
279 "Cannot create the metadata AST out of the metadata text: "
282 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
286 /* We know it's complete: append plain text */
287 if (mdec
->config
.keep_plain_text
) {
288 BT_ASSERT(start_pos
!= -1);
289 ret
= fseek(fp
, start_pos
, SEEK_SET
);
291 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
, "Failed to seek file: ret={}, mdec-addr={}",
292 ret
, fmt::ptr(mdec
));
293 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
297 ret
= bt_common_append_file_content_to_g_string(mdec
->text
, fp
);
299 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
300 "Failed to append to current plain text: "
301 "ret={}, mdec-addr={}",
302 ret
, fmt::ptr(mdec
));
303 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
308 ret
= ctf_visitor_semantic_check(0, &mdec
->scanner
->ast
->root
, mdec
->logger
);
310 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
311 "Validation of the metadata semantics failed: "
314 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
318 if (mdec
->config
.create_trace_class
) {
319 ret
= ctf_visitor_generate_ir_visit_node(mdec
->visitor
, &mdec
->scanner
->ast
->root
);
325 BT_CPPLOGD_SPEC(mdec
->logger
,
326 "While visiting metadata AST: incomplete data: "
329 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
332 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
333 "Failed to visit AST node to create CTF IR objects: "
334 "mdec-addr={}, ret={}",
335 fmt::ptr(mdec
), ret
);
336 status
= CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR
;
346 if (fp
&& close_fp
) {
348 BT_CPPLOGE_SPEC(mdec
->logger
,
349 "Cannot close metadata file stream: "
360 bt_trace_class
*ctf_metadata_decoder_get_ir_trace_class(struct ctf_metadata_decoder
*mdec
)
363 BT_ASSERT_DBG(mdec
->config
.create_trace_class
);
364 return ctf_visitor_generate_ir_get_ir_trace_class(mdec
->visitor
);
367 struct ctf_trace_class
*
368 ctf_metadata_decoder_borrow_ctf_trace_class(struct ctf_metadata_decoder
*mdec
)
371 BT_ASSERT_DBG(mdec
->config
.create_trace_class
);
372 return ctf_visitor_generate_ir_borrow_ctf_trace_class(mdec
->visitor
);
375 const char *ctf_metadata_decoder_get_text(struct ctf_metadata_decoder
*mdec
)
378 BT_ASSERT_DBG(mdec
->config
.keep_plain_text
);
379 return mdec
->text
->str
;
382 int ctf_metadata_decoder_get_byte_order(struct ctf_metadata_decoder
*mdec
)
388 int ctf_metadata_decoder_get_uuid(struct ctf_metadata_decoder
*mdec
, bt_uuid_t uuid
)
394 if (!mdec
->is_uuid_set
) {
399 bt_uuid_copy(uuid
, mdec
->uuid
);
405 static enum ctf_metadata_decoder_status
find_uuid_in_trace_decl(struct ctf_metadata_decoder
*mdec
,
406 struct ctf_node
*trace_node
,
409 enum ctf_metadata_decoder_status status
= CTF_METADATA_DECODER_STATUS_OK
;
410 struct ctf_node
*entry_node
;
411 struct bt_list_head
*decl_list
= &trace_node
->u
.trace
.declaration_list
;
414 bt_list_for_each_entry (entry_node
, decl_list
, siblings
) {
415 if (entry_node
->type
== NODE_CTF_EXPRESSION
) {
418 left
= ctf_ast_concatenate_unary_strings(&entry_node
->u
.ctf_expression
.left
);
420 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
, "Cannot concatenate unary strings.");
421 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
425 if (strcmp(left
, "uuid") == 0) {
427 ctf_ast_get_unary_uuid(&entry_node
->u
.ctf_expression
.right
, uuid
, mdec
->logger
);
429 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
, "Invalid trace's `uuid` attribute.");
430 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
442 status
= CTF_METADATA_DECODER_STATUS_NONE
;
449 enum ctf_metadata_decoder_status
450 ctf_metadata_decoder_get_trace_class_uuid(struct ctf_metadata_decoder
*mdec
, bt_uuid_t uuid
)
452 enum ctf_metadata_decoder_status status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
453 struct ctf_node
*root_node
= &mdec
->scanner
->ast
->root
;
454 struct ctf_node
*trace_node
;
457 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
461 trace_node
= bt_list_entry(root_node
->u
.root
.trace
.next
, struct ctf_node
, siblings
);
463 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
467 status
= find_uuid_in_trace_decl(mdec
, trace_node
, uuid
);