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
)
93 BT_CPPLOGD_SPEC(config
->logger
,
94 "Creating CTF metadata decoder: "
95 "clock-class-offset-s={}, "
96 "clock-class-offset-ns={}",
97 config
->clkClsCfg
.offsetSec
, config
->clkClsCfg
.offsetNanoSec
);
99 ctf_metadata_decoder
*mdec
= new ctf_metadata_decoder
{config
->logger
};
100 mdec
->scanner
= ctf_scanner_alloc(mdec
->logger
);
101 if (!mdec
->scanner
) {
102 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
103 "Cannot allocate a metadata lexical scanner: "
109 mdec
->text
= g_string_new(NULL
);
111 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
112 "Failed to allocate one GString: "
119 mdec
->config
= *config
;
120 mdec
->visitor
= ctf_visitor_generate_ir_create(config
);
121 if (!mdec
->visitor
) {
122 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
123 "Failed to create a CTF IR metadata AST visitor: "
129 BT_CPPLOGD_SPEC(mdec
->logger
,
130 "Creating CTF metadata decoder: "
131 "clock-class-offset-s={}, "
132 "clock-class-offset-ns={}, addr={}",
133 config
->clkClsCfg
.offsetSec
, config
->clkClsCfg
.offsetNanoSec
, fmt::ptr(mdec
));
137 ctf_metadata_decoder_destroy(mdec
);
141 return ctf_metadata_decoder_up
{mdec
};
144 void ctf_metadata_decoder_destroy(struct ctf_metadata_decoder
*mdec
)
151 ctf_scanner_free(mdec
->scanner
);
155 g_string_free(mdec
->text
, TRUE
);
158 BT_CPPLOGD_SPEC(mdec
->logger
, "Destroying CTF metadata decoder: addr={}", fmt::ptr(mdec
));
163 void ctf_metadata_decoder_deleter::operator()(ctf_metadata_decoder
*decoder
)
165 ctf_metadata_decoder_destroy(decoder
);
168 enum ctf_metadata_decoder_status
169 ctf_metadata_decoder_append_content(struct ctf_metadata_decoder
*mdec
, FILE *fp
)
171 enum ctf_metadata_decoder_status status
= CTF_METADATA_DECODER_STATUS_OK
;
174 bool close_fp
= false;
179 ret
= ctf_metadata_decoder_is_packetized(fp
, &is_packetized
, &mdec
->bo
, mdec
->logger
);
181 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
186 BT_CPPLOGI_SPEC(mdec
->logger
, "Metadata stream is packetized: mdec-addr={}",
188 ret
= ctf_metadata_decoder_packetized_file_stream_to_buf(
189 fp
, &buf
, mdec
->bo
, &mdec
->is_uuid_set
, mdec
->uuid
, mdec
->logger
);
191 BT_CPPLOGE_APPEND_CAUSE_SPEC(
193 "Cannot decode packetized metadata packets to metadata text: "
194 "mdec-addr={}, ret={}",
195 fmt::ptr(mdec
), ret
);
196 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
200 if (strlen(buf
) == 0) {
201 /* An empty metadata packet is OK. */
205 /* Convert the real file pointer to a memory file pointer */
206 fp
= bt_fmemopen(buf
, strlen(buf
), "rb");
209 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
210 "Cannot memory-open metadata buffer: {}: "
212 strerror(errno
), fmt::ptr(mdec
));
213 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
216 } else if (!mdec
->has_checked_plaintext_signature
) {
217 unsigned int major
, minor
;
219 const long init_pos
= ftell(fp
);
221 BT_CPPLOGI_SPEC(mdec
->logger
, "Metadata stream is plain text: mdec-addr={}",
225 BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(mdec
->logger
, "Failed to get current file position",
227 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
231 /* Check text-only metadata header and version */
232 nr_items
= fscanf(fp
, "/* CTF %10u.%10u", &major
, &minor
);
236 "Missing \"/* CTF major.minor\" signature in plain text metadata file stream: "
241 BT_CPPLOGI_SPEC(mdec
->logger
, "Found metadata stream version in signature: version={}.{}",
244 if (!ctf_metadata_decoder_is_packet_version_valid(major
, minor
)) {
245 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
246 "Invalid metadata version found in plain text signature: "
247 "version={}.{}, mdec-addr={}",
248 major
, minor
, fmt::ptr(mdec
));
249 status
= CTF_METADATA_DECODER_STATUS_INVAL_VERSION
;
253 if (fseek(fp
, init_pos
, SEEK_SET
)) {
254 BT_CPPLOGE_APPEND_CAUSE_SPEC(
256 "Cannot seek metadata file stream to initial position: {}: "
258 strerror(errno
), fmt::ptr(mdec
));
259 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
263 mdec
->has_checked_plaintext_signature
= true;
267 if (mdec
->logger
.wouldLogT()) {
272 /* Save the file's position: we'll seek back to append the plain text */
275 if (mdec
->config
.keep_plain_text
) {
276 start_pos
= ftell(fp
);
279 /* Append the metadata text content */
280 ret
= ctf_scanner_append_ast(mdec
->scanner
, fp
);
282 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
283 "Cannot create the metadata AST out of the metadata text: "
286 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
290 /* We know it's complete: append plain text */
291 if (mdec
->config
.keep_plain_text
) {
292 BT_ASSERT(start_pos
!= -1);
293 ret
= fseek(fp
, start_pos
, SEEK_SET
);
295 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
, "Failed to seek file: ret={}, mdec-addr={}",
296 ret
, fmt::ptr(mdec
));
297 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
301 ret
= bt_common_append_file_content_to_g_string(mdec
->text
, fp
);
303 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
304 "Failed to append to current plain text: "
305 "ret={}, mdec-addr={}",
306 ret
, fmt::ptr(mdec
));
307 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
312 ret
= ctf_visitor_semantic_check(0, &mdec
->scanner
->ast
->root
, mdec
->logger
);
314 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
315 "Validation of the metadata semantics failed: "
318 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
322 if (mdec
->config
.create_trace_class
) {
323 ret
= ctf_visitor_generate_ir_visit_node(mdec
->visitor
.get(), &mdec
->scanner
->ast
->root
);
329 BT_CPPLOGD_SPEC(mdec
->logger
,
330 "While visiting metadata AST: incomplete data: "
333 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
336 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
,
337 "Failed to visit AST node to create CTF IR objects: "
338 "mdec-addr={}, ret={}",
339 fmt::ptr(mdec
), ret
);
340 status
= CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR
;
350 if (fp
&& close_fp
) {
352 BT_CPPLOGE_SPEC(mdec
->logger
,
353 "Cannot close metadata file stream: "
364 bt_trace_class
*ctf_metadata_decoder_get_ir_trace_class(struct ctf_metadata_decoder
*mdec
)
367 BT_ASSERT_DBG(mdec
->config
.create_trace_class
);
368 return ctf_visitor_generate_ir_get_ir_trace_class(mdec
->visitor
.get());
371 struct ctf_trace_class
*
372 ctf_metadata_decoder_borrow_ctf_trace_class(struct ctf_metadata_decoder
*mdec
)
375 BT_ASSERT_DBG(mdec
->config
.create_trace_class
);
376 return ctf_visitor_generate_ir_borrow_ctf_trace_class(mdec
->visitor
.get());
379 const char *ctf_metadata_decoder_get_text(struct ctf_metadata_decoder
*mdec
)
382 BT_ASSERT_DBG(mdec
->config
.keep_plain_text
);
383 return mdec
->text
->str
;
386 int ctf_metadata_decoder_get_byte_order(struct ctf_metadata_decoder
*mdec
)
392 int ctf_metadata_decoder_get_uuid(struct ctf_metadata_decoder
*mdec
, bt_uuid_t uuid
)
398 if (!mdec
->is_uuid_set
) {
403 bt_uuid_copy(uuid
, mdec
->uuid
);
409 static enum ctf_metadata_decoder_status
find_uuid_in_trace_decl(struct ctf_metadata_decoder
*mdec
,
410 struct ctf_node
*trace_node
,
413 enum ctf_metadata_decoder_status status
= CTF_METADATA_DECODER_STATUS_OK
;
414 struct ctf_node
*entry_node
;
415 struct bt_list_head
*decl_list
= &trace_node
->u
.trace
.declaration_list
;
418 bt_list_for_each_entry (entry_node
, decl_list
, siblings
) {
419 if (entry_node
->type
== NODE_CTF_EXPRESSION
) {
422 left
= ctf_ast_concatenate_unary_strings(&entry_node
->u
.ctf_expression
.left
);
424 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
, "Cannot concatenate unary strings.");
425 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
429 if (strcmp(left
, "uuid") == 0) {
431 ctf_ast_get_unary_uuid(&entry_node
->u
.ctf_expression
.right
, uuid
, mdec
->logger
);
433 BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec
->logger
, "Invalid trace's `uuid` attribute.");
434 status
= CTF_METADATA_DECODER_STATUS_ERROR
;
446 status
= CTF_METADATA_DECODER_STATUS_NONE
;
453 enum ctf_metadata_decoder_status
454 ctf_metadata_decoder_get_trace_class_uuid(struct ctf_metadata_decoder
*mdec
, bt_uuid_t uuid
)
456 enum ctf_metadata_decoder_status status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
457 struct ctf_node
*root_node
= &mdec
->scanner
->ast
->root
;
458 struct ctf_node
*trace_node
;
461 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
465 trace_node
= bt_list_entry(root_node
->u
.root
.trace
.next
, struct ctf_node
, siblings
);
467 status
= CTF_METADATA_DECODER_STATUS_INCOMPLETE
;
471 status
= find_uuid_in_trace_decl(mdec
, trace_node
, uuid
);