cpp-common/bt2c/fmt.hpp: use `wise_enum::string_type` in `EnableIfIsWiseEnum` definition
[babeltrace.git] / src / plugins / text / details / write.c
1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
5 */
6
7 #include <babeltrace2/babeltrace.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <string.h>
11
12 #include "common/assert.h"
13 #include "common/common.h"
14 #include "common/uuid.h"
15 #include "details.h"
16 #include "write.h"
17 #include "obj-lifetime-mgmt.h"
18 #include "colors.h"
19
20 static inline
21 const char *plural(uint64_t value)
22 {
23 return value == 1 ? "" : "s";
24 }
25
26 static inline
27 void incr_indent_by(struct details_write_ctx *ctx, unsigned int value)
28 {
29 BT_ASSERT_DBG(ctx);
30 ctx->indent_level += value;
31 }
32
33 static inline
34 void incr_indent(struct details_write_ctx *ctx)
35 {
36 incr_indent_by(ctx, 2);
37 }
38
39 static inline
40 void decr_indent_by(struct details_write_ctx *ctx, unsigned int value)
41 {
42 BT_ASSERT_DBG(ctx);
43 BT_ASSERT_DBG(ctx->indent_level >= value);
44 ctx->indent_level -= value;
45 }
46
47 static inline
48 void decr_indent(struct details_write_ctx *ctx)
49 {
50 decr_indent_by(ctx, 2);
51 }
52
53 static inline
54 void format_uint(char *buf, uint64_t value, unsigned int base)
55 {
56 const char *spec = "%" PRIu64;
57 char *buf_start = buf;
58 unsigned int digits_per_group = 3;
59 char sep = ',';
60 bool sep_digits = true;
61
62 switch (base) {
63 case 2:
64 case 16:
65 /* TODO: Support binary format */
66 spec = "%" PRIx64;
67 strcpy(buf, "0x");
68 buf_start = buf + 2;
69 digits_per_group = 4;
70 sep = ':';
71 break;
72 case 8:
73 spec = "%" PRIo64;
74 strcpy(buf, "0");
75 buf_start = buf + 1;
76 sep = ':';
77 break;
78 case 10:
79 if (value <= 9999) {
80 /*
81 * Do not insert digit separators for numbers
82 * under 10,000 as it looks weird.
83 */
84 sep_digits = false;
85 }
86
87 break;
88 default:
89 bt_common_abort();
90 }
91
92 #pragma GCC diagnostic push
93 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
94 sprintf(buf_start, spec, value);
95 #pragma GCC diagnostic pop
96
97 if (sep_digits) {
98 bt_common_sep_digits(buf_start, digits_per_group, sep);
99 }
100 }
101
102 static inline
103 void format_int(char *buf, int64_t value, unsigned int base)
104 {
105 const char *spec = "%" PRIu64;
106 char *buf_start = buf;
107 unsigned int digits_per_group = 3;
108 char sep = ',';
109 bool sep_digits = true;
110 uint64_t abs_value = value < 0 ? (uint64_t) -value : (uint64_t) value;
111
112 if (value < 0) {
113 buf[0] = '-';
114 buf_start++;
115 }
116
117 switch (base) {
118 case 2:
119 case 16:
120 /* TODO: Support binary format */
121 spec = "%" PRIx64;
122 strcpy(buf_start, "0x");
123 buf_start += 2;
124 digits_per_group = 4;
125 sep = ':';
126 break;
127 case 8:
128 spec = "%" PRIo64;
129 strcpy(buf_start, "0");
130 buf_start++;
131 sep = ':';
132 break;
133 case 10:
134 if (value >= -9999 && value <= 9999) {
135 /*
136 * Do not insert digit separators for numbers
137 * over -10,000 and under 10,000 as it looks
138 * weird.
139 */
140 sep_digits = false;
141 }
142
143 break;
144 default:
145 bt_common_abort();
146 }
147
148 #pragma GCC diagnostic push
149 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
150 sprintf(buf_start, spec, abs_value);
151 #pragma GCC diagnostic pop
152
153 if (sep_digits) {
154 bt_common_sep_digits(buf_start, digits_per_group, sep);
155 }
156 }
157
158 static inline
159 void write_nl(struct details_write_ctx *ctx)
160 {
161 BT_ASSERT_DBG(ctx);
162 g_string_append_c(ctx->str, '\n');
163 }
164
165 static inline
166 void write_sp(struct details_write_ctx *ctx)
167 {
168 BT_ASSERT_DBG(ctx);
169 g_string_append_c(ctx->str, ' ');
170 }
171
172 static inline
173 void write_indent(struct details_write_ctx *ctx)
174 {
175 uint64_t i;
176
177 BT_ASSERT_DBG(ctx);
178
179 for (i = 0; i < ctx->indent_level; i++) {
180 write_sp(ctx);
181 }
182 }
183
184 static inline
185 void write_compound_member_name(struct details_write_ctx *ctx, const char *name)
186 {
187 write_indent(ctx);
188 g_string_append_printf(ctx->str, "%s%s%s:",
189 color_fg_cyan(ctx), name, color_reset(ctx));
190 }
191
192 static inline
193 void write_array_index(struct details_write_ctx *ctx, uint64_t index,
194 const char *color)
195 {
196 char buf[32];
197
198 write_indent(ctx);
199 format_uint(buf, index, 10);
200 g_string_append_printf(ctx->str, "%s[%s]%s:",
201 color, buf, color_reset(ctx));
202 }
203
204 static inline
205 void write_obj_type_name(struct details_write_ctx *ctx, const char *name)
206 {
207 g_string_append_printf(ctx->str, "%s%s%s%s",
208 color_bold(ctx), color_fg_bright_yellow(ctx), name,
209 color_reset(ctx));
210 }
211
212 static inline
213 void write_prop_name(struct details_write_ctx *ctx, const char *prop_name)
214 {
215 g_string_append_printf(ctx->str, "%s%s%s",
216 color_fg_magenta(ctx), prop_name, color_reset(ctx));
217 }
218
219 static inline
220 void write_prop_name_line(struct details_write_ctx *ctx, const char *prop_name)
221 {
222 write_indent(ctx);
223 g_string_append_printf(ctx->str, "%s%s%s:",
224 color_fg_magenta(ctx), prop_name, color_reset(ctx));
225 }
226
227 static inline
228 void write_str_prop_value(struct details_write_ctx *ctx, const char *value)
229 {
230 g_string_append_printf(ctx->str, "%s%s%s",
231 color_bold(ctx), value, color_reset(ctx));
232 }
233
234 static inline
235 void write_none_prop_value(struct details_write_ctx *ctx, const char *value)
236 {
237 g_string_append_printf(ctx->str, "%s%s%s%s",
238 color_bold(ctx), color_fg_bright_magenta(ctx),
239 value, color_reset(ctx));
240 }
241
242 static inline
243 void write_uint_str_prop_value(struct details_write_ctx *ctx, const char *value)
244 {
245 write_str_prop_value(ctx, value);
246 }
247
248 static inline
249 void write_uint_prop_value(struct details_write_ctx *ctx, uint64_t value)
250 {
251 char buf[32];
252
253 format_uint(buf, value, 10);
254 write_uint_str_prop_value(ctx, buf);
255 }
256
257 static inline
258 void write_int_prop_value(struct details_write_ctx *ctx, int64_t value)
259 {
260 char buf[32];
261
262 format_int(buf, value, 10);
263 write_uint_str_prop_value(ctx, buf);
264 }
265
266 static inline
267 void write_float_prop_value(struct details_write_ctx *ctx, double value)
268 {
269 g_string_append_printf(ctx->str, "%s%f%s",
270 color_bold(ctx), value, color_reset(ctx));
271 }
272
273 static inline
274 void write_str_prop_line(struct details_write_ctx *ctx, const char *prop_name,
275 const char *prop_value)
276 {
277 BT_ASSERT_DBG(prop_value);
278 write_indent(ctx);
279 write_prop_name(ctx, prop_name);
280 g_string_append(ctx->str, ": ");
281 write_str_prop_value(ctx, prop_value);
282 write_nl(ctx);
283 }
284
285 static inline
286 void write_uint_prop_line(struct details_write_ctx *ctx, const char *prop_name,
287 uint64_t prop_value)
288 {
289 write_indent(ctx);
290 write_prop_name(ctx, prop_name);
291 g_string_append(ctx->str, ": ");
292 write_uint_prop_value(ctx, prop_value);
293 write_nl(ctx);
294 }
295
296 static inline
297 void write_int_prop_line(struct details_write_ctx *ctx, const char *prop_name,
298 int64_t prop_value)
299 {
300 write_indent(ctx);
301 write_prop_name(ctx, prop_name);
302 g_string_append(ctx->str, ": ");
303 write_int_prop_value(ctx, prop_value);
304 write_nl(ctx);
305 }
306
307 static inline
308 void write_int_str_prop_value(struct details_write_ctx *ctx, const char *value)
309 {
310 write_str_prop_value(ctx, value);
311 }
312
313 static inline
314 void write_bool_prop_value(struct details_write_ctx *ctx, bt_bool prop_value)
315 {
316 const char *str;
317
318 g_string_append(ctx->str, color_bold(ctx));
319
320 if (prop_value) {
321 g_string_append(ctx->str, color_fg_bright_green(ctx));
322 str = "Yes";
323 } else {
324 g_string_append(ctx->str, color_fg_bright_red(ctx));
325 str = "No";
326 }
327
328 g_string_append_printf(ctx->str, "%s%s", str, color_reset(ctx));
329 }
330
331 static inline
332 void write_bool_prop_line(struct details_write_ctx *ctx, const char *prop_name,
333 bt_bool prop_value)
334 {
335 write_indent(ctx);
336 write_prop_name(ctx, prop_name);
337 g_string_append(ctx->str, ": ");
338 write_bool_prop_value(ctx, prop_value);
339 write_nl(ctx);
340 }
341
342 static inline
343 void write_uuid_prop_line(struct details_write_ctx *ctx, const char *prop_name,
344 bt_uuid uuid)
345 {
346 BT_ASSERT_DBG(uuid);
347 write_indent(ctx);
348 write_prop_name(ctx, prop_name);
349 g_string_append_printf(ctx->str,
350 ": %s" BT_UUID_FMT "%s\n",
351 color_bold(ctx),
352 BT_UUID_FMT_VALUES(uuid),
353 color_reset(ctx));
354 }
355
356 static
357 gint compare_strings(const char **a, const char **b)
358 {
359 return strcmp(*a, *b);
360 }
361
362 static
363 bt_value_map_foreach_entry_const_func_status map_value_foreach_add_key_to_array(
364 const char *key,
365 const bt_value *object __attribute__((unused)),
366 void *data)
367 {
368 GPtrArray *keys = data;
369
370 BT_ASSERT_DBG(keys);
371 g_ptr_array_add(keys, (void *) key);
372 return BT_VALUE_MAP_FOREACH_ENTRY_CONST_FUNC_STATUS_OK;
373 }
374
375 static
376 void write_value(struct details_write_ctx *ctx, const bt_value *value,
377 const char *name)
378 {
379 uint64_t i;
380 bt_value_type value_type = bt_value_get_type(value);
381 GPtrArray *keys = g_ptr_array_new();
382 char buf[64];
383
384 BT_ASSERT_DBG(keys);
385
386 /* Write field's name */
387 if (name) {
388 write_prop_name_line(ctx, name);
389 }
390
391 /* Write field's value */
392 switch (value_type) {
393 case BT_VALUE_TYPE_NULL:
394 write_sp(ctx);
395 write_none_prop_value(ctx, "Null");
396 break;
397 case BT_VALUE_TYPE_BOOL:
398 write_sp(ctx);
399 write_bool_prop_value(ctx, bt_value_bool_get(value));
400 break;
401 case BT_VALUE_TYPE_UNSIGNED_INTEGER:
402 format_uint(buf, bt_value_integer_unsigned_get(value), 10);
403 write_sp(ctx);
404 write_uint_str_prop_value(ctx, buf);
405 break;
406 case BT_VALUE_TYPE_SIGNED_INTEGER:
407 format_int(buf, bt_value_integer_signed_get(value), 10);
408 write_sp(ctx);
409 write_int_str_prop_value(ctx, buf);
410 break;
411 case BT_VALUE_TYPE_REAL:
412 write_sp(ctx);
413 write_float_prop_value(ctx, bt_value_real_get(value));
414 break;
415 case BT_VALUE_TYPE_STRING:
416 if (strlen(bt_value_string_get(value)) > 0) {
417 write_sp(ctx);
418 write_str_prop_value(ctx, bt_value_string_get(value));
419 }
420
421 break;
422 case BT_VALUE_TYPE_ARRAY:
423 {
424 uint64_t length = bt_value_array_get_length(value);
425
426 if (length == 0) {
427 write_sp(ctx);
428 write_none_prop_value(ctx, "Empty");
429 } else {
430 g_string_append(ctx->str, " Length ");
431 write_uint_prop_value(ctx, length);
432 g_string_append_c(ctx->str, ':');
433 }
434
435 incr_indent(ctx);
436
437 for (i = 0; i < length; i++) {
438 const bt_value *elem_value =
439 bt_value_array_borrow_element_by_index_const(
440 value, i);
441
442 write_nl(ctx);
443 write_array_index(ctx, i, color_fg_magenta(ctx));
444 write_value(ctx, elem_value, NULL);
445 }
446
447 decr_indent(ctx);
448 break;
449 }
450 case BT_VALUE_TYPE_MAP:
451 {
452 bt_value_map_foreach_entry_const_status foreach_status =
453 bt_value_map_foreach_entry_const(value,
454 map_value_foreach_add_key_to_array, keys);
455
456 BT_ASSERT_DBG(foreach_status ==
457 BT_VALUE_MAP_FOREACH_ENTRY_CONST_STATUS_OK);
458 g_ptr_array_sort(keys, (GCompareFunc) compare_strings);
459
460 if (keys->len > 0) {
461 incr_indent(ctx);
462
463 for (i = 0; i < keys->len; i++) {
464 const char *key = keys->pdata[i];
465 const bt_value *entry_value =
466 bt_value_map_borrow_entry_value_const(
467 value, key);
468
469 write_nl(ctx);
470 write_value(ctx, entry_value, key);
471 }
472
473 decr_indent(ctx);
474 } else {
475 write_sp(ctx);
476 write_none_prop_value(ctx, "Empty");
477 }
478
479 break;
480 }
481 default:
482 bt_common_abort();
483 }
484
485 g_ptr_array_free(keys, TRUE);
486 }
487
488 static
489 void write_user_attributes(struct details_write_ctx *ctx,
490 const bt_value *user_attrs, bool write_newline, bool *written)
491 {
492 BT_ASSERT_DBG(user_attrs);
493
494 if (!bt_value_map_is_empty(user_attrs)) {
495 write_value(ctx, user_attrs, "User attributes");
496
497 if (write_newline) {
498 write_nl(ctx);
499 }
500
501 if (written) {
502 *written = true;
503 }
504 }
505 }
506
507 static
508 void write_int_field_class_props(struct details_write_ctx *ctx,
509 const bt_field_class *fc, bool close)
510 {
511 g_string_append_printf(ctx->str, "(%s%" PRIu64 "-bit%s, Base ",
512 color_bold(ctx),
513 bt_field_class_integer_get_field_value_range(fc),
514 color_reset(ctx));
515
516 switch (bt_field_class_integer_get_preferred_display_base(fc)) {
517 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY:
518 write_uint_prop_value(ctx, 2);
519 break;
520 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL:
521 write_uint_prop_value(ctx, 8);
522 break;
523 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL:
524 write_uint_prop_value(ctx, 10);
525 break;
526 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL:
527 write_uint_prop_value(ctx, 16);
528 break;
529 default:
530 bt_common_abort();
531 }
532
533 if (close) {
534 g_string_append(ctx->str, ")");
535 }
536 }
537
538 struct int_range {
539 union {
540 uint64_t u;
541 int64_t i;
542 } lower;
543
544 union {
545 uint64_t u;
546 int64_t i;
547 } upper;
548 };
549
550 struct enum_field_class_mapping {
551 /* Weak */
552 const char *label;
553
554 /* Array of `struct int_range` */
555 GArray *ranges;
556 };
557
558 static
559 gint compare_enum_field_class_mappings(struct enum_field_class_mapping **a,
560 struct enum_field_class_mapping **b)
561 {
562 return strcmp((*a)->label, (*b)->label);
563 }
564
565 static
566 gint compare_int_ranges_signed(struct int_range *a, struct int_range *b)
567 {
568
569 if (a->lower.i < b->lower.i) {
570 return -1;
571 } else if (a->lower.i > b->lower.i) {
572 return 1;
573 } else {
574 if (a->upper.i < b->upper.i) {
575 return -1;
576 } else if (a->upper.i > b->upper.i) {
577 return 1;
578 } else {
579 return 0;
580 }
581 }
582 }
583
584 static
585 gint compare_int_ranges_unsigned(struct int_range *a, struct int_range *b)
586 {
587 if (a->lower.u < b->lower.u) {
588 return -1;
589 } else if (a->lower.u > b->lower.u) {
590 return 1;
591 } else {
592 if (a->upper.u < b->upper.u) {
593 return -1;
594 } else if (a->upper.u > b->upper.u) {
595 return 1;
596 } else {
597 return 0;
598 }
599 }
600 }
601
602 static
603 GArray *range_set_to_int_ranges(const void *spec_range_set, bool is_signed)
604 {
605 uint64_t i;
606 const bt_integer_range_set *range_set;
607 GArray *ranges = g_array_new(FALSE, TRUE, sizeof(struct int_range));
608
609 if (!ranges) {
610 goto end;
611 }
612
613 if (is_signed) {
614 range_set = bt_integer_range_set_signed_as_range_set_const(
615 spec_range_set);
616 } else {
617 range_set = bt_integer_range_set_unsigned_as_range_set_const(
618 spec_range_set);
619 }
620
621 for (i = 0; i < bt_integer_range_set_get_range_count(range_set); i++) {
622 struct int_range range;
623
624 if (is_signed) {
625 const bt_integer_range_signed *orig_range =
626 bt_integer_range_set_signed_borrow_range_by_index_const(
627 spec_range_set, i);
628
629 range.lower.i = bt_integer_range_signed_get_lower(orig_range);
630 range.upper.i = bt_integer_range_signed_get_upper(orig_range);
631 } else {
632 const bt_integer_range_unsigned *orig_range =
633 bt_integer_range_set_unsigned_borrow_range_by_index_const(
634 spec_range_set, i);
635
636 range.lower.u = bt_integer_range_unsigned_get_lower(orig_range);
637 range.upper.u = bt_integer_range_unsigned_get_upper(orig_range);
638 }
639
640 g_array_append_val(ranges, range);
641 }
642
643 if (is_signed) {
644 g_array_sort(ranges, (GCompareFunc) compare_int_ranges_signed);
645 } else {
646 g_array_sort(ranges,
647 (GCompareFunc) compare_int_ranges_unsigned);
648 }
649
650 end:
651 return ranges;
652 }
653
654 static
655 void destroy_enum_field_class_mapping(struct enum_field_class_mapping *mapping)
656 {
657 if (mapping->ranges) {
658 g_array_free(mapping->ranges, TRUE);
659 mapping->ranges = NULL;
660 }
661
662 g_free(mapping);
663 }
664
665 static
666 struct int_range *int_range_at(GArray *ranges, uint64_t index)
667 {
668 return &bt_g_array_index(ranges, struct int_range, index);
669 }
670
671 static
672 void write_int_range(struct details_write_ctx *ctx,
673 struct int_range *range, bool is_signed)
674 {
675 g_string_append(ctx->str, "[");
676
677 if (is_signed) {
678 write_int_prop_value(ctx, range->lower.i);
679 } else {
680 write_uint_prop_value(ctx, range->lower.u);
681 }
682
683 if (range->lower.u != range->upper.u) {
684 g_string_append(ctx->str, ", ");
685
686 if (is_signed) {
687 write_int_prop_value(ctx, range->upper.i);
688 } else {
689 write_uint_prop_value(ctx, range->upper.u);
690 }
691 }
692
693 g_string_append(ctx->str, "]");
694 }
695
696 static
697 void write_enum_field_class_mappings(struct details_write_ctx *ctx,
698 const bt_field_class *fc)
699 {
700 GPtrArray *mappings;
701 uint64_t i;
702 uint64_t range_i;
703 bool is_signed = bt_field_class_get_type(fc) ==
704 BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION;
705
706 mappings = g_ptr_array_new_with_free_func(
707 (GDestroyNotify) destroy_enum_field_class_mapping);
708 BT_ASSERT_DBG(mappings);
709
710 /*
711 * Copy field class's mappings to our own arrays and structures
712 * to sort them.
713 */
714 for (i = 0; i < bt_field_class_enumeration_get_mapping_count(fc); i++) {
715 const void *fc_mapping;
716 const void *fc_range_set;
717 struct enum_field_class_mapping *mapping = g_new0(
718 struct enum_field_class_mapping, 1);
719
720 BT_ASSERT_DBG(mapping);
721
722 if (is_signed) {
723 fc_mapping = bt_field_class_enumeration_signed_borrow_mapping_by_index_const(
724 fc, i);
725 fc_range_set = bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
726 fc_mapping);
727 } else {
728 fc_mapping = bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
729 fc, i);
730 fc_range_set = bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
731 fc_mapping);
732 }
733
734 mapping->label = bt_field_class_enumeration_mapping_get_label(
735 bt_field_class_enumeration_signed_mapping_as_mapping_const(
736 fc_mapping));
737 mapping->ranges = range_set_to_int_ranges(fc_range_set,
738 is_signed);
739 BT_ASSERT_DBG(mapping->ranges);
740 g_ptr_array_add(mappings, mapping);
741 }
742
743 /* Sort mappings (ranges are already sorted within mappings) */
744 g_ptr_array_sort(mappings,
745 (GCompareFunc) compare_enum_field_class_mappings);
746
747 /* Write mappings */
748 for (i = 0; i < mappings->len; i++) {
749 struct enum_field_class_mapping *mapping = mappings->pdata[i];
750
751 write_nl(ctx);
752 write_prop_name_line(ctx, mapping->label);
753
754 for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
755 write_sp(ctx);
756 write_int_range(ctx,
757 int_range_at(mapping->ranges, range_i),
758 is_signed);
759 }
760 }
761
762 g_ptr_array_free(mappings, TRUE);
763 }
764
765 static
766 void write_field_path(struct details_write_ctx *ctx,
767 const bt_field_path *field_path)
768 {
769 uint64_t i;
770
771 g_string_append_c(ctx->str, '[');
772
773 switch (bt_field_path_get_root_scope(field_path)) {
774 case BT_FIELD_PATH_SCOPE_PACKET_CONTEXT:
775 write_str_prop_value(ctx, "Packet context");
776 break;
777 case BT_FIELD_PATH_SCOPE_EVENT_COMMON_CONTEXT:
778 write_str_prop_value(ctx, "Event common context");
779 break;
780 case BT_FIELD_PATH_SCOPE_EVENT_SPECIFIC_CONTEXT:
781 write_str_prop_value(ctx, "Event specific context");
782 break;
783 case BT_FIELD_PATH_SCOPE_EVENT_PAYLOAD:
784 write_str_prop_value(ctx, "Event payload");
785 break;
786 default:
787 bt_common_abort();
788 }
789
790 g_string_append(ctx->str, ": ");
791
792 for (i = 0; i < bt_field_path_get_item_count(field_path); i++) {
793 const bt_field_path_item *fp_item =
794 bt_field_path_borrow_item_by_index_const(field_path, i);
795
796 if (i != 0) {
797 g_string_append(ctx->str, ", ");
798 }
799
800 switch (bt_field_path_item_get_type(fp_item)) {
801 case BT_FIELD_PATH_ITEM_TYPE_INDEX:
802 write_uint_prop_value(ctx,
803 bt_field_path_item_index_get_index(fp_item));
804 break;
805 case BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT:
806 write_str_prop_value(ctx, "<current>");
807 break;
808 default:
809 bt_common_abort();
810 }
811 }
812
813 g_string_append_c(ctx->str, ']');
814 }
815
816 static
817 void write_field_class(struct details_write_ctx *ctx, const bt_field_class *fc);
818
819 static
820 void write_variant_field_class_option(struct details_write_ctx *ctx,
821 const bt_field_class *fc, uint64_t index)
822 {
823 bt_field_class_type fc_type = bt_field_class_get_type(fc);
824 const bt_field_class_variant_option *option =
825 bt_field_class_variant_borrow_option_by_index_const(
826 fc, index);
827 const void *orig_ranges = NULL;
828 GArray *int_ranges = NULL;
829 bool is_signed;
830 const bt_value *user_attrs =
831 bt_field_class_variant_option_borrow_user_attributes_const(
832 option);
833 const bt_field_class *option_fc =
834 bt_field_class_variant_option_borrow_field_class_const(option);
835
836 write_nl(ctx);
837 write_compound_member_name(ctx,
838 bt_field_class_variant_option_get_name(option));
839
840 if (fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD) {
841 const bt_field_class_variant_with_selector_field_integer_unsigned_option *spec_opt =
842 bt_field_class_variant_with_selector_field_integer_unsigned_borrow_option_by_index_const(
843 fc, index);
844
845 orig_ranges =
846 bt_field_class_variant_with_selector_field_integer_unsigned_option_borrow_ranges_const(
847 spec_opt);
848 is_signed = false;
849 } else if (fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_INTEGER_SELECTOR_FIELD) {
850 const bt_field_class_variant_with_selector_field_integer_signed_option *spec_opt =
851 bt_field_class_variant_with_selector_field_integer_signed_borrow_option_by_index_const(
852 fc, index);
853
854 orig_ranges =
855 bt_field_class_variant_with_selector_field_integer_signed_option_borrow_ranges_const(
856 spec_opt);
857 is_signed = true;
858 }
859
860 if (orig_ranges) {
861 uint64_t i;
862
863 int_ranges = range_set_to_int_ranges(orig_ranges, is_signed);
864 BT_ASSERT_DBG(int_ranges);
865
866 for (i = 0; i < int_ranges->len; i++) {
867 struct int_range *range = int_range_at(int_ranges, i);
868
869 write_sp(ctx);
870 write_int_range(ctx, range, is_signed);
871 }
872
873 g_string_append(ctx->str, ": ");
874 } else {
875 write_sp(ctx);
876 }
877
878 if (bt_value_map_is_empty(user_attrs)) {
879 write_field_class(ctx, option_fc);
880 } else {
881 write_nl(ctx);
882 incr_indent(ctx);
883
884 /* Field class */
885 write_prop_name_line(ctx, "Field class");
886 write_sp(ctx);
887 write_field_class(ctx, option_fc);
888 write_nl(ctx);
889
890 /* User attributes */
891 write_user_attributes(ctx, user_attrs,
892 false, NULL);
893
894 decr_indent(ctx);
895 }
896
897 if (int_ranges) {
898 g_array_free(int_ranges, TRUE);
899 }
900 }
901
902 static
903 void write_field_class(struct details_write_ctx *ctx, const bt_field_class *fc)
904 {
905 uint64_t i;
906 const char *type;
907 bt_field_class_type fc_type = bt_field_class_get_type(fc);
908 const bt_value *user_attrs;
909 bool wrote_user_attrs = false;
910
911 /* Write field class's type */
912 switch (fc_type) {
913 case BT_FIELD_CLASS_TYPE_BOOL:
914 type = "Boolean";
915 break;
916 case BT_FIELD_CLASS_TYPE_BIT_ARRAY:
917 type = "Bit array";
918 break;
919 case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER:
920 type = "Unsigned integer";
921 break;
922 case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER:
923 type = "Signed integer";
924 break;
925 case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION:
926 type = "Unsigned enumeration";
927 break;
928 case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION:
929 type = "Signed enumeration";
930 break;
931 case BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL:
932 type = "Single-precision real";
933 break;
934 case BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL:
935 type = "Double-precision real";
936 break;
937 case BT_FIELD_CLASS_TYPE_STRING:
938 type = "String";
939 break;
940 case BT_FIELD_CLASS_TYPE_STRUCTURE:
941 type = "Structure";
942 break;
943 case BT_FIELD_CLASS_TYPE_STATIC_ARRAY:
944 type = "Static array";
945 break;
946 case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITHOUT_LENGTH_FIELD:
947 type = "Dynamic array (no length field)";
948 break;
949 case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITH_LENGTH_FIELD:
950 type = "Dynamic array (with length field)";
951 break;
952 case BT_FIELD_CLASS_TYPE_OPTION_WITHOUT_SELECTOR_FIELD:
953 type = "Option (no selector)";
954 break;
955 case BT_FIELD_CLASS_TYPE_OPTION_WITH_BOOL_SELECTOR_FIELD:
956 type = "Option (boolean selector)";
957 break;
958 case BT_FIELD_CLASS_TYPE_OPTION_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD:
959 type = "Option (unsigned integer selector)";
960 break;
961 case BT_FIELD_CLASS_TYPE_OPTION_WITH_SIGNED_INTEGER_SELECTOR_FIELD:
962 type = "Option (signed integer selector)";
963 break;
964 case BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR_FIELD:
965 type = "Variant (no selector)";
966 break;
967 case BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD:
968 type = "Variant (unsigned integer selector)";
969 break;
970 case BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_INTEGER_SELECTOR_FIELD:
971 type = "Variant (signed integer selector)";
972 break;
973 default:
974 bt_common_abort();
975 }
976
977 g_string_append_printf(ctx->str, "%s%s%s",
978 color_fg_blue(ctx), type, color_reset(ctx));
979
980 /* Write field class's single-line properties */
981 if (bt_field_class_type_is(fc_type, BT_FIELD_CLASS_TYPE_ENUMERATION)) {
982 uint64_t mapping_count =
983 bt_field_class_enumeration_get_mapping_count(fc);
984
985 write_sp(ctx);
986 write_int_field_class_props(ctx, fc, false);
987 g_string_append(ctx->str, ", ");
988 write_uint_prop_value(ctx, mapping_count);
989 g_string_append_printf(ctx->str, " mapping%s)",
990 plural(mapping_count));
991 } else if (bt_field_class_type_is(fc_type,
992 BT_FIELD_CLASS_TYPE_INTEGER)) {
993 write_sp(ctx);
994 write_int_field_class_props(ctx, fc, true);
995 } else if (fc_type == BT_FIELD_CLASS_TYPE_STRUCTURE) {
996 uint64_t member_count =
997 bt_field_class_structure_get_member_count(fc);
998
999 g_string_append(ctx->str, " (");
1000 write_uint_prop_value(ctx, member_count);
1001 g_string_append_printf(ctx->str, " member%s)",
1002 plural(member_count));
1003 } else if (fc_type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY) {
1004 g_string_append(ctx->str, " (Length ");
1005 write_uint_prop_value(ctx,
1006 bt_field_class_array_static_get_length(fc));
1007 g_string_append_c(ctx->str, ')');
1008 } else if (fc_type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITH_LENGTH_FIELD) {
1009 const bt_field_path *length_field_path =
1010 bt_field_class_array_dynamic_with_length_field_borrow_length_field_path_const(
1011 fc);
1012
1013 g_string_append(ctx->str, " (Length field path ");
1014 write_field_path(ctx, length_field_path);
1015 g_string_append_c(ctx->str, ')');
1016 } else if (bt_field_class_type_is(fc_type,
1017 BT_FIELD_CLASS_TYPE_OPTION_WITH_SELECTOR_FIELD)) {
1018 const bt_field_path *selector_field_path =
1019 bt_field_class_option_with_selector_field_borrow_selector_field_path_const(
1020 fc);
1021
1022 g_string_append(ctx->str, " (Selector field path ");
1023 write_field_path(ctx, selector_field_path);
1024 g_string_append_c(ctx->str, ')');
1025 } else if (bt_field_class_type_is(fc_type,
1026 BT_FIELD_CLASS_TYPE_VARIANT)) {
1027 uint64_t option_count =
1028 bt_field_class_variant_get_option_count(fc);
1029 const bt_field_path *sel_field_path = NULL;
1030
1031 if (bt_field_class_type_is(fc_type,
1032 BT_FIELD_CLASS_TYPE_VARIANT_WITH_SELECTOR_FIELD)) {
1033 sel_field_path =
1034 bt_field_class_variant_with_selector_field_borrow_selector_field_path_const(
1035 fc);
1036 BT_ASSERT_DBG(sel_field_path);
1037 }
1038
1039 g_string_append(ctx->str, " (");
1040 write_uint_prop_value(ctx, option_count);
1041 g_string_append_printf(ctx->str, " option%s",
1042 plural(option_count));
1043
1044 if (sel_field_path) {
1045 g_string_append(ctx->str, ", Selector field path ");
1046 write_field_path(ctx, sel_field_path);
1047 }
1048
1049 g_string_append_c(ctx->str, ')');
1050 }
1051
1052 incr_indent(ctx);
1053 user_attrs = bt_field_class_borrow_user_attributes_const(fc);
1054 if (!bt_value_map_is_empty(user_attrs)) {
1055 g_string_append(ctx->str, ":\n");
1056 write_user_attributes(ctx, user_attrs, false, NULL);
1057 wrote_user_attrs = true;
1058 }
1059
1060 /* Write field class's complex properties */
1061 if (bt_field_class_type_is(fc_type, BT_FIELD_CLASS_TYPE_ENUMERATION)) {
1062 uint64_t mapping_count =
1063 bt_field_class_enumeration_get_mapping_count(fc);
1064
1065 if (mapping_count > 0) {
1066 if (wrote_user_attrs) {
1067 write_nl(ctx);
1068 write_indent(ctx);
1069 write_prop_name(ctx, "Mappings");
1070 g_string_append_c(ctx->str, ':');
1071 incr_indent(ctx);
1072 } else {
1073 /* Each mapping starts with its own newline */
1074 g_string_append_c(ctx->str, ':');
1075 }
1076
1077 write_enum_field_class_mappings(ctx, fc);
1078
1079 if (wrote_user_attrs) {
1080 decr_indent(ctx);
1081 }
1082 }
1083 } else if (fc_type == BT_FIELD_CLASS_TYPE_STRUCTURE) {
1084 uint64_t member_count =
1085 bt_field_class_structure_get_member_count(fc);
1086
1087 if (member_count > 0) {
1088 if (wrote_user_attrs) {
1089 write_nl(ctx);
1090 write_indent(ctx);
1091 write_prop_name(ctx, "Members");
1092 g_string_append_c(ctx->str, ':');
1093 incr_indent(ctx);
1094 } else {
1095 /* Each member starts with its own newline */
1096 g_string_append_c(ctx->str, ':');
1097 }
1098
1099 for (i = 0; i < member_count; i++) {
1100 const bt_field_class_structure_member *member =
1101 bt_field_class_structure_borrow_member_by_index_const(
1102 fc, i);
1103 const bt_value *member_user_attrs;
1104 const bt_field_class *member_fc =
1105 bt_field_class_structure_member_borrow_field_class_const(member);
1106
1107 write_nl(ctx);
1108 write_compound_member_name(ctx,
1109 bt_field_class_structure_member_get_name(member));
1110 member_user_attrs = bt_field_class_structure_member_borrow_user_attributes_const(
1111 member);
1112
1113 if (bt_value_map_is_empty(member_user_attrs)) {
1114 write_sp(ctx);
1115 write_field_class(ctx, member_fc);
1116 } else {
1117 write_nl(ctx);
1118 incr_indent(ctx);
1119
1120 /* Field class */
1121 write_prop_name_line(ctx, "Field class");
1122 write_sp(ctx);
1123 write_field_class(ctx, member_fc);
1124 write_nl(ctx);
1125
1126 /* User attributes */
1127 write_user_attributes(ctx, member_user_attrs,
1128 false, NULL);
1129
1130 decr_indent(ctx);
1131 }
1132 }
1133
1134 if (wrote_user_attrs) {
1135 decr_indent(ctx);
1136 }
1137 }
1138 } else if (bt_field_class_type_is(fc_type, BT_FIELD_CLASS_TYPE_ARRAY)) {
1139 if (wrote_user_attrs) {
1140 write_nl(ctx);
1141 } else {
1142 g_string_append(ctx->str, ":\n");
1143 }
1144
1145 write_prop_name_line(ctx, "Element");
1146 write_sp(ctx);
1147 write_field_class(ctx,
1148 bt_field_class_array_borrow_element_field_class_const(fc));
1149 } else if (bt_field_class_type_is(fc_type,
1150 BT_FIELD_CLASS_TYPE_OPTION)) {
1151 const void *ranges = NULL;
1152 bool selector_is_signed = false;
1153
1154 if (wrote_user_attrs) {
1155 write_nl(ctx);
1156 } else {
1157 g_string_append(ctx->str, ":\n");
1158 }
1159
1160 if (fc_type == BT_FIELD_CLASS_TYPE_OPTION_WITH_BOOL_SELECTOR_FIELD) {
1161 write_bool_prop_line(ctx, "Selector is reversed",
1162 bt_field_class_option_with_selector_field_bool_selector_is_reversed(fc));
1163 } else if (fc_type == BT_FIELD_CLASS_TYPE_OPTION_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD) {
1164 ranges = bt_field_class_option_with_selector_field_integer_unsigned_borrow_selector_ranges_const(fc);
1165 } else if (fc_type == BT_FIELD_CLASS_TYPE_OPTION_WITH_SIGNED_INTEGER_SELECTOR_FIELD) {
1166 ranges = bt_field_class_option_with_selector_field_integer_signed_borrow_selector_ranges_const(fc);
1167 selector_is_signed = true;
1168 }
1169
1170 if (ranges) {
1171 GArray *sorted_ranges = range_set_to_int_ranges(
1172 ranges, selector_is_signed);
1173
1174 BT_ASSERT_DBG(sorted_ranges);
1175 BT_ASSERT_DBG(sorted_ranges->len > 0);
1176 write_prop_name_line(ctx, "Selector ranges");
1177
1178 for (i = 0; i < sorted_ranges->len; i++) {
1179 write_sp(ctx);
1180 write_int_range(ctx,
1181 int_range_at(sorted_ranges, i),
1182 selector_is_signed);
1183 }
1184
1185 write_nl(ctx);
1186 g_array_free(sorted_ranges, TRUE);
1187 }
1188
1189 write_prop_name_line(ctx, "Content");
1190 write_sp(ctx);
1191 write_field_class(ctx,
1192 bt_field_class_option_borrow_field_class_const(fc));
1193 } else if (bt_field_class_type_is(fc_type,
1194 BT_FIELD_CLASS_TYPE_VARIANT)) {
1195 uint64_t option_count =
1196 bt_field_class_variant_get_option_count(fc);
1197
1198 if (option_count > 0) {
1199 if (wrote_user_attrs) {
1200 write_nl(ctx);
1201 write_indent(ctx);
1202 write_prop_name(ctx, "Options");
1203 g_string_append_c(ctx->str, ':');
1204 incr_indent(ctx);
1205 } else {
1206 /* Each option starts with its own newline */
1207 g_string_append_c(ctx->str, ':');
1208 }
1209
1210 for (i = 0; i < option_count; i++) {
1211 write_variant_field_class_option(ctx, fc, i);
1212 }
1213
1214 if (wrote_user_attrs) {
1215 decr_indent(ctx);
1216 }
1217 }
1218 }
1219
1220 decr_indent(ctx);
1221 }
1222
1223 static
1224 void write_root_field_class(struct details_write_ctx *ctx, const char *name,
1225 const bt_field_class *fc)
1226 {
1227 BT_ASSERT_DBG(name);
1228 BT_ASSERT_DBG(fc);
1229 write_indent(ctx);
1230 write_prop_name(ctx, name);
1231 g_string_append(ctx->str, ": ");
1232 write_field_class(ctx, fc);
1233 write_nl(ctx);
1234 }
1235
1236 static
1237 void write_event_class(struct details_write_ctx *ctx, const bt_event_class *ec)
1238 {
1239 const char *name = bt_event_class_get_name(ec);
1240 const char *emf_uri;
1241 const bt_field_class *fc;
1242 bt_event_class_log_level log_level;
1243
1244 write_indent(ctx);
1245 write_obj_type_name(ctx, "Event class");
1246
1247 /* Write name and ID */
1248 if (name) {
1249 g_string_append_printf(ctx->str, " `%s%s%s`",
1250 color_fg_green(ctx), name, color_reset(ctx));
1251 }
1252
1253 g_string_append(ctx->str, " (ID ");
1254 write_uint_prop_value(ctx, bt_event_class_get_id(ec));
1255 g_string_append(ctx->str, "):\n");
1256
1257 /* Write properties */
1258 incr_indent(ctx);
1259
1260 /* Write user attributes */
1261 write_user_attributes(ctx,
1262 bt_event_class_borrow_user_attributes_const(ec), true, NULL);
1263
1264 /* Write log level */
1265 if (bt_event_class_get_log_level(ec, &log_level) ==
1266 BT_PROPERTY_AVAILABILITY_AVAILABLE) {
1267 const char *ll_str = NULL;
1268
1269 switch (log_level) {
1270 case BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY:
1271 ll_str = "Emergency";
1272 break;
1273 case BT_EVENT_CLASS_LOG_LEVEL_ALERT:
1274 ll_str = "Alert";
1275 break;
1276 case BT_EVENT_CLASS_LOG_LEVEL_CRITICAL:
1277 ll_str = "Critical";
1278 break;
1279 case BT_EVENT_CLASS_LOG_LEVEL_ERROR:
1280 ll_str = "Error";
1281 break;
1282 case BT_EVENT_CLASS_LOG_LEVEL_WARNING:
1283 ll_str = "Warning";
1284 break;
1285 case BT_EVENT_CLASS_LOG_LEVEL_NOTICE:
1286 ll_str = "Notice";
1287 break;
1288 case BT_EVENT_CLASS_LOG_LEVEL_INFO:
1289 ll_str = "Info";
1290 break;
1291 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM:
1292 ll_str = "Debug (system)";
1293 break;
1294 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM:
1295 ll_str = "Debug (program)";
1296 break;
1297 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS:
1298 ll_str = "Debug (process)";
1299 break;
1300 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE:
1301 ll_str = "Debug (module)";
1302 break;
1303 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT:
1304 ll_str = "Debug (unit)";
1305 break;
1306 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION:
1307 ll_str = "Debug (function)";
1308 break;
1309 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE:
1310 ll_str = "Debug (line)";
1311 break;
1312 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG:
1313 ll_str = "Debug";
1314 break;
1315 default:
1316 bt_common_abort();
1317 }
1318
1319 write_str_prop_line(ctx, "Log level", ll_str);
1320 }
1321
1322 /* Write EMF URI */
1323 emf_uri = bt_event_class_get_emf_uri(ec);
1324 if (emf_uri) {
1325 write_str_prop_line(ctx, "EMF URI", emf_uri);
1326 }
1327
1328 /* Write specific context field class */
1329 fc = bt_event_class_borrow_specific_context_field_class_const(ec);
1330 if (fc) {
1331 write_root_field_class(ctx, "Specific context field class", fc);
1332 }
1333
1334 /* Write payload field class */
1335 fc = bt_event_class_borrow_payload_field_class_const(ec);
1336 if (fc) {
1337 write_root_field_class(ctx, "Payload field class", fc);
1338 }
1339
1340 decr_indent(ctx);
1341 }
1342
1343 static
1344 void write_clock_class_prop_lines(struct details_write_ctx *ctx,
1345 const bt_clock_class *cc)
1346 {
1347 int64_t offset_seconds;
1348 uint64_t offset_cycles;
1349 const char *str;
1350
1351 str = bt_clock_class_get_name(cc);
1352 if (str) {
1353 write_str_prop_line(ctx, "Name", str);
1354 }
1355
1356 write_user_attributes(ctx,
1357 bt_clock_class_borrow_user_attributes_const(cc), true, NULL);
1358 str = bt_clock_class_get_description(cc);
1359 if (str) {
1360 write_str_prop_line(ctx, "Description", str);
1361 }
1362
1363 write_uint_prop_line(ctx, "Frequency (Hz)",
1364 bt_clock_class_get_frequency(cc));
1365 write_uint_prop_line(ctx, "Precision (cycles)",
1366 bt_clock_class_get_precision(cc));
1367 bt_clock_class_get_offset(cc, &offset_seconds, &offset_cycles);
1368 write_int_prop_line(ctx, "Offset (s)", offset_seconds);
1369 write_uint_prop_line(ctx, "Offset (cycles)", offset_cycles);
1370 write_bool_prop_line(ctx, "Origin is Unix epoch",
1371 bt_clock_class_origin_is_unix_epoch(cc));
1372
1373 if (ctx->details_comp->cfg.with_uuid) {
1374 bt_uuid uuid = bt_clock_class_get_uuid(cc);
1375
1376 if (uuid) {
1377 write_uuid_prop_line(ctx, "UUID", uuid);
1378 }
1379 }
1380 }
1381
1382 static
1383 gint compare_event_classes(const bt_event_class **a, const bt_event_class **b)
1384 {
1385 uint64_t id_a = bt_event_class_get_id(*a);
1386 uint64_t id_b = bt_event_class_get_id(*b);
1387
1388 if (id_a < id_b) {
1389 return -1;
1390 } else if (id_a > id_b) {
1391 return 1;
1392 } else {
1393 return 0;
1394 }
1395 }
1396
1397 static
1398 void write_stream_class(struct details_write_ctx *ctx,
1399 const bt_stream_class *sc)
1400 {
1401 const bt_field_class *fc;
1402 GPtrArray *event_classes = g_ptr_array_new();
1403 uint64_t i;
1404
1405 write_indent(ctx);
1406 write_obj_type_name(ctx, "Stream class");
1407
1408 /* Write name and ID */
1409 if (ctx->details_comp->cfg.with_stream_class_name) {
1410 const char *name = bt_stream_class_get_name(sc);
1411
1412 if (name) {
1413 g_string_append(ctx->str, " `");
1414 write_str_prop_value(ctx, name);
1415 g_string_append(ctx->str, "`");
1416 }
1417 }
1418
1419 g_string_append(ctx->str, " (ID ");
1420 write_uint_prop_value(ctx, bt_stream_class_get_id(sc));
1421 g_string_append(ctx->str, "):\n");
1422
1423 /* Write properties */
1424 incr_indent(ctx);
1425
1426 /* Write user attributes */
1427 write_user_attributes(ctx,
1428 bt_stream_class_borrow_user_attributes_const(sc), true, NULL);
1429
1430 /* Write configuration */
1431 write_bool_prop_line(ctx,
1432 "Supports packets", bt_stream_class_supports_packets(sc));
1433
1434 if (bt_stream_class_supports_packets(sc)) {
1435 write_bool_prop_line(ctx,
1436 "Packets have beginning default clock snapshot",
1437 bt_stream_class_packets_have_beginning_default_clock_snapshot(sc));
1438 write_bool_prop_line(ctx,
1439 "Packets have end default clock snapshot",
1440 bt_stream_class_packets_have_end_default_clock_snapshot(sc));
1441 }
1442
1443 write_bool_prop_line(ctx,
1444 "Supports discarded events",
1445 bt_stream_class_supports_discarded_events(sc));
1446
1447 if (bt_stream_class_supports_discarded_events(sc)) {
1448 write_bool_prop_line(ctx,
1449 "Discarded events have default clock snapshots",
1450 bt_stream_class_discarded_events_have_default_clock_snapshots(sc));
1451 }
1452
1453 write_bool_prop_line(ctx,
1454 "Supports discarded packets",
1455 bt_stream_class_supports_discarded_packets(sc));
1456
1457 if (bt_stream_class_supports_discarded_packets(sc)) {
1458 write_bool_prop_line(ctx,
1459 "Discarded packets have default clock snapshots",
1460 bt_stream_class_discarded_packets_have_default_clock_snapshots(sc));
1461 }
1462
1463 /* Write default clock class */
1464 if (bt_stream_class_borrow_default_clock_class_const(sc)) {
1465 write_indent(ctx);
1466 write_prop_name(ctx, "Default clock class");
1467 g_string_append_c(ctx->str, ':');
1468 write_nl(ctx);
1469 incr_indent(ctx);
1470 write_clock_class_prop_lines(ctx,
1471 bt_stream_class_borrow_default_clock_class_const(sc));
1472 decr_indent(ctx);
1473 }
1474
1475 fc = bt_stream_class_borrow_packet_context_field_class_const(sc);
1476 if (fc) {
1477 write_root_field_class(ctx, "Packet context field class", fc);
1478 }
1479
1480 fc = bt_stream_class_borrow_event_common_context_field_class_const(sc);
1481 if (fc) {
1482 write_root_field_class(ctx, "Event common context field class",
1483 fc);
1484 }
1485
1486 for (i = 0; i < bt_stream_class_get_event_class_count(sc); i++) {
1487 g_ptr_array_add(event_classes,
1488 (gpointer) bt_stream_class_borrow_event_class_by_index_const(
1489 sc, i));
1490 }
1491
1492 g_ptr_array_sort(event_classes, (GCompareFunc) compare_event_classes);
1493
1494 for (i = 0; i < event_classes->len; i++) {
1495 write_event_class(ctx, event_classes->pdata[i]);
1496 }
1497
1498 decr_indent(ctx);
1499 g_ptr_array_free(event_classes, TRUE);
1500 }
1501
1502 static
1503 gint compare_stream_classes(const bt_stream_class **a, const bt_stream_class **b)
1504 {
1505 uint64_t id_a = bt_stream_class_get_id(*a);
1506 uint64_t id_b = bt_stream_class_get_id(*b);
1507
1508 if (id_a < id_b) {
1509 return -1;
1510 } else if (id_a > id_b) {
1511 return 1;
1512 } else {
1513 return 0;
1514 }
1515 }
1516
1517 static
1518 void write_trace_class(struct details_write_ctx *ctx, const bt_trace_class *tc)
1519 {
1520 GPtrArray *stream_classes = g_ptr_array_new();
1521 uint64_t i;
1522 bool printed_prop = false;
1523
1524 write_indent(ctx);
1525 write_obj_type_name(ctx, "Trace class");
1526
1527
1528 for (i = 0; i < bt_trace_class_get_stream_class_count(tc); i++) {
1529 g_ptr_array_add(stream_classes,
1530 (gpointer) bt_trace_class_borrow_stream_class_by_index_const(
1531 tc, i));
1532 }
1533
1534 g_ptr_array_sort(stream_classes, (GCompareFunc) compare_stream_classes);
1535
1536 if (stream_classes->len > 0) {
1537 if (!printed_prop) {
1538 g_string_append(ctx->str, ":\n");
1539 printed_prop = true;
1540 }
1541 }
1542
1543 incr_indent(ctx);
1544
1545 /* Write user attributes */
1546 write_user_attributes(ctx,
1547 bt_trace_class_borrow_user_attributes_const(tc), true,
1548 &printed_prop);
1549
1550 /* Write stream classes */
1551 for (i = 0; i < stream_classes->len; i++) {
1552 write_stream_class(ctx, stream_classes->pdata[i]);
1553 }
1554
1555 if (!printed_prop) {
1556 write_nl(ctx);
1557 }
1558
1559 decr_indent(ctx);
1560 g_ptr_array_free(stream_classes, TRUE);
1561 }
1562
1563 static
1564 int try_write_meta(struct details_write_ctx *ctx, const bt_trace_class *tc,
1565 const bt_stream_class *sc, const bt_event_class *ec)
1566 {
1567 int ret = 0;
1568
1569 BT_ASSERT_DBG(tc);
1570
1571 if (details_need_to_write_trace_class(ctx, tc)) {
1572 uint64_t sc_i;
1573
1574 if (ctx->details_comp->cfg.compact &&
1575 ctx->details_comp->printed_something) {
1576 /*
1577 * There are no empty line between messages in
1578 * compact mode, so write one here to decouple
1579 * the trace class from the next message.
1580 */
1581 write_nl(ctx);
1582 }
1583
1584 /*
1585 * write_trace_class() also writes all its stream
1586 * classes their event classes, so we don't need to
1587 * rewrite `sc`.
1588 */
1589 write_trace_class(ctx, tc);
1590
1591 /*
1592 * Mark this trace class as written, as well as all
1593 * its stream classes and their event classes.
1594 */
1595 ret = details_did_write_trace_class(ctx, tc);
1596 if (ret) {
1597 goto end;
1598 }
1599
1600 for (sc_i = 0; sc_i < bt_trace_class_get_stream_class_count(tc);
1601 sc_i++) {
1602 uint64_t ec_i;
1603 const bt_stream_class *tc_sc =
1604 bt_trace_class_borrow_stream_class_by_index_const(
1605 tc, sc_i);
1606
1607 details_did_write_meta_object(ctx, tc, tc_sc);
1608
1609 for (ec_i = 0; ec_i <
1610 bt_stream_class_get_event_class_count(tc_sc);
1611 ec_i++) {
1612 details_did_write_meta_object(ctx, tc,
1613 bt_stream_class_borrow_event_class_by_index_const(
1614 tc_sc, ec_i));
1615 }
1616 }
1617
1618 goto end;
1619 }
1620
1621 if (sc && details_need_to_write_meta_object(ctx, tc, sc)) {
1622 uint64_t ec_i;
1623
1624 BT_ASSERT_DBG(tc);
1625
1626 if (ctx->details_comp->cfg.compact &&
1627 ctx->details_comp->printed_something) {
1628 /*
1629 * There are no empty line between messages in
1630 * compact mode, so write one here to decouple
1631 * the stream class from the next message.
1632 */
1633 write_nl(ctx);
1634 }
1635
1636 /*
1637 * write_stream_class() also writes all its event
1638 * classes, so we don't need to rewrite `ec`.
1639 */
1640 write_stream_class(ctx, sc);
1641
1642 /*
1643 * Mark this stream class as written, as well as all its
1644 * event classes.
1645 */
1646 details_did_write_meta_object(ctx, tc, sc);
1647
1648 for (ec_i = 0; ec_i <
1649 bt_stream_class_get_event_class_count(sc);
1650 ec_i++) {
1651 details_did_write_meta_object(ctx, tc,
1652 bt_stream_class_borrow_event_class_by_index_const(
1653 sc, ec_i));
1654 }
1655
1656 goto end;
1657 }
1658
1659 if (ec && details_need_to_write_meta_object(ctx, tc, ec)) {
1660 BT_ASSERT_DBG(sc);
1661
1662 if (ctx->details_comp->cfg.compact &&
1663 ctx->details_comp->printed_something) {
1664 /*
1665 * There are no empty line between messages in
1666 * compact mode, so write one here to decouple
1667 * the event class from the next message.
1668 */
1669 write_nl(ctx);
1670 }
1671
1672 write_event_class(ctx, ec);
1673 details_did_write_meta_object(ctx, tc, ec);
1674 goto end;
1675 }
1676
1677 end:
1678 return ret;
1679 }
1680
1681 static
1682 void write_time_str(struct details_write_ctx *ctx, const char *str)
1683 {
1684 if (!ctx->details_comp->cfg.with_time) {
1685 goto end;
1686 }
1687
1688 g_string_append_printf(ctx->str, "[%s%s%s%s]",
1689 color_bold(ctx), color_fg_bright_blue(ctx), str,
1690 color_reset(ctx));
1691
1692 if (ctx->details_comp->cfg.compact) {
1693 write_sp(ctx);
1694 } else {
1695 write_nl(ctx);
1696 }
1697
1698 end:
1699 return;
1700 }
1701
1702 static
1703 void write_time(struct details_write_ctx *ctx, const bt_clock_snapshot *cs)
1704 {
1705 bt_clock_snapshot_get_ns_from_origin_status cs_status;
1706 int64_t ns_from_origin;
1707 char buf[32];
1708
1709 if (!ctx->details_comp->cfg.with_time) {
1710 goto end;
1711 }
1712
1713 format_uint(buf, bt_clock_snapshot_get_value(cs), 10);
1714 g_string_append_printf(ctx->str, "[%s%s%s%s%s",
1715 color_bold(ctx), color_fg_bright_blue(ctx), buf,
1716 color_reset(ctx),
1717 ctx->details_comp->cfg.compact ? "" : " cycles");
1718 cs_status = bt_clock_snapshot_get_ns_from_origin(cs, &ns_from_origin);
1719 if (cs_status == BT_CLOCK_SNAPSHOT_GET_NS_FROM_ORIGIN_STATUS_OK) {
1720 format_int(buf, ns_from_origin, 10);
1721 g_string_append_printf(ctx->str, "%s %s%s%s%s%s",
1722 ctx->details_comp->cfg.compact ? "" : ",",
1723 color_bold(ctx), color_fg_bright_blue(ctx), buf,
1724 color_reset(ctx),
1725 ctx->details_comp->cfg.compact ? "" : " ns from origin");
1726 }
1727
1728 g_string_append(ctx->str, "]");
1729
1730 if (ctx->details_comp->cfg.compact) {
1731 write_sp(ctx);
1732 } else {
1733 write_nl(ctx);
1734 }
1735
1736 end:
1737 return;
1738 }
1739
1740 static
1741 int write_message_follow_tag(struct details_write_ctx *ctx,
1742 const bt_stream *stream)
1743 {
1744 int ret;
1745 uint64_t unique_trace_id;
1746 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
1747 const bt_trace *trace = bt_stream_borrow_trace_const(stream);
1748
1749 ret = details_trace_unique_id(ctx, trace, &unique_trace_id);
1750 if (ret) {
1751 goto end;
1752 }
1753
1754 if (ctx->details_comp->cfg.compact) {
1755 g_string_append_printf(ctx->str,
1756 "%s{%s%s%" PRIu64 " %" PRIu64 " %" PRIu64 "%s%s}%s ",
1757 color_fg_cyan(ctx), color_bold(ctx),
1758 color_fg_bright_cyan(ctx),
1759 unique_trace_id, bt_stream_class_get_id(sc),
1760 bt_stream_get_id(stream),
1761 color_reset(ctx), color_fg_cyan(ctx), color_reset(ctx));
1762 } else {
1763 g_string_append_printf(ctx->str,
1764 "%s{Trace %s%s%" PRIu64 "%s%s, Stream class ID %s%s%" PRIu64 "%s%s, Stream ID %s%s%" PRIu64 "%s%s}%s\n",
1765 color_fg_cyan(ctx),
1766 color_bold(ctx), color_fg_bright_cyan(ctx),
1767 unique_trace_id, color_reset(ctx),
1768 color_fg_cyan(ctx),
1769 color_bold(ctx), color_fg_bright_cyan(ctx),
1770 bt_stream_class_get_id(sc), color_reset(ctx),
1771 color_fg_cyan(ctx),
1772 color_bold(ctx), color_fg_bright_cyan(ctx),
1773 bt_stream_get_id(stream), color_reset(ctx),
1774 color_fg_cyan(ctx), color_reset(ctx));
1775 }
1776
1777 end:
1778 return ret;
1779 }
1780
1781 static
1782 void write_field(struct details_write_ctx *ctx, const bt_field *field,
1783 const char *name)
1784 {
1785 uint64_t i;
1786 bt_field_class_type fc_type = bt_field_get_class_type(field);
1787 const bt_field_class *fc;
1788 char buf[64];
1789
1790 /* Write field's name */
1791 if (name) {
1792 write_compound_member_name(ctx, name);
1793 }
1794
1795 /* Write field's value */
1796 if (fc_type == BT_FIELD_CLASS_TYPE_BOOL) {
1797 write_sp(ctx);
1798 write_bool_prop_value(ctx, bt_field_bool_get_value(field));
1799 } else if (fc_type == BT_FIELD_CLASS_TYPE_BIT_ARRAY) {
1800 format_uint(buf, bt_field_bit_array_get_value_as_integer(field),
1801 16);
1802 write_sp(ctx);
1803 write_uint_str_prop_value(ctx, buf);
1804 } else if (bt_field_class_type_is(fc_type,
1805 BT_FIELD_CLASS_TYPE_INTEGER)) {
1806 unsigned int fmt_base;
1807 bt_field_class_integer_preferred_display_base base;
1808
1809 fc = bt_field_borrow_class_const(field);
1810 base = bt_field_class_integer_get_preferred_display_base(fc);
1811
1812 switch (base) {
1813 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL:
1814 fmt_base = 10;
1815 break;
1816 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL:
1817 fmt_base = 8;
1818 break;
1819 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY:
1820 fmt_base = 2;
1821 break;
1822 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL:
1823 fmt_base = 16;
1824 break;
1825 default:
1826 bt_common_abort();
1827 }
1828
1829 if (bt_field_class_type_is(fc_type,
1830 BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER)) {
1831 format_uint(buf,
1832 bt_field_integer_unsigned_get_value(field),
1833 fmt_base);
1834 write_sp(ctx);
1835 write_uint_str_prop_value(ctx, buf);
1836 } else {
1837 format_int(buf,
1838 bt_field_integer_signed_get_value(field),
1839 fmt_base);
1840 write_sp(ctx);
1841 write_int_str_prop_value(ctx, buf);
1842 }
1843 } else if (fc_type == BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL) {
1844 write_sp(ctx);
1845 write_float_prop_value(ctx, bt_field_real_single_precision_get_value(field));
1846 } else if (fc_type == BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL) {
1847 write_sp(ctx);
1848 write_float_prop_value(ctx, bt_field_real_double_precision_get_value(field));
1849 } else if (fc_type == BT_FIELD_CLASS_TYPE_STRING) {
1850 write_sp(ctx);
1851 write_str_prop_value(ctx, bt_field_string_get_value(field));
1852 } else if (fc_type == BT_FIELD_CLASS_TYPE_STRUCTURE) {
1853 uint64_t member_count;
1854
1855 fc = bt_field_borrow_class_const(field);
1856 member_count = bt_field_class_structure_get_member_count(fc);
1857
1858 if (member_count > 0) {
1859 incr_indent(ctx);
1860
1861 for (i = 0; i < member_count; i++) {
1862 const bt_field_class_structure_member *member =
1863 bt_field_class_structure_borrow_member_by_index_const(
1864 fc, i);
1865 const bt_field *member_field =
1866 bt_field_structure_borrow_member_field_by_index_const(
1867 field, i);
1868
1869 write_nl(ctx);
1870 write_field(ctx, member_field,
1871 bt_field_class_structure_member_get_name(member));
1872 }
1873
1874 decr_indent(ctx);
1875 } else {
1876 write_sp(ctx);
1877 write_none_prop_value(ctx, "Empty");
1878 }
1879 } else if (bt_field_class_type_is(fc_type, BT_FIELD_CLASS_TYPE_ARRAY)) {
1880 uint64_t length = bt_field_array_get_length(field);
1881
1882 if (length == 0) {
1883 write_sp(ctx);
1884 write_none_prop_value(ctx, "Empty");
1885 } else {
1886 g_string_append(ctx->str, " Length ");
1887 write_uint_prop_value(ctx, length);
1888 g_string_append_c(ctx->str, ':');
1889 }
1890
1891 incr_indent(ctx);
1892
1893 for (i = 0; i < length; i++) {
1894 const bt_field *elem_field =
1895 bt_field_array_borrow_element_field_by_index_const(
1896 field, i);
1897
1898 write_nl(ctx);
1899 write_array_index(ctx, i, color_fg_cyan(ctx));
1900 write_field(ctx, elem_field, NULL);
1901 }
1902
1903 decr_indent(ctx);
1904 } else if (bt_field_class_type_is(fc_type,
1905 BT_FIELD_CLASS_TYPE_OPTION)) {
1906 const bt_field *content_field =
1907 bt_field_option_borrow_field_const(field);
1908
1909 if (!content_field) {
1910 write_sp(ctx);
1911 write_none_prop_value(ctx, "None");
1912 } else {
1913 write_field(ctx, content_field, NULL);
1914 }
1915 } else if (bt_field_class_type_is(fc_type,
1916 BT_FIELD_CLASS_TYPE_VARIANT)) {
1917 write_field(ctx,
1918 bt_field_variant_borrow_selected_option_field_const(
1919 field), NULL);
1920 } else {
1921 bt_common_abort();
1922 }
1923 }
1924
1925 static
1926 void write_root_field(struct details_write_ctx *ctx, const char *name,
1927 const bt_field *field)
1928 {
1929 BT_ASSERT_DBG(name);
1930 BT_ASSERT_DBG(field);
1931 write_indent(ctx);
1932 write_prop_name(ctx, name);
1933 g_string_append(ctx->str, ":");
1934 write_field(ctx, field, NULL);
1935 write_nl(ctx);
1936 }
1937
1938 static
1939 int write_event_message(struct details_write_ctx *ctx,
1940 const bt_message *msg)
1941 {
1942 int ret = 0;
1943 const bt_event *event = bt_message_event_borrow_event_const(msg);
1944 const bt_stream *stream = bt_event_borrow_stream_const(event);
1945 const bt_event_class *ec = bt_event_borrow_class_const(event);
1946 const bt_stream_class *sc = bt_event_class_borrow_stream_class_const(ec);
1947 const bt_trace_class *tc = bt_stream_class_borrow_trace_class_const(sc);
1948 const char *ec_name;
1949 const bt_field *field;
1950
1951 ret = try_write_meta(ctx, tc, sc, ec);
1952 if (ret) {
1953 goto end;
1954 }
1955
1956 if (!ctx->details_comp->cfg.with_data) {
1957 goto end;
1958 }
1959
1960 if (ctx->str->len > 0) {
1961 /*
1962 * Output buffer contains metadata: separate blocks with
1963 * newline.
1964 */
1965 write_nl(ctx);
1966 }
1967
1968 /* Write time */
1969 if (bt_stream_class_borrow_default_clock_class_const(sc)) {
1970 write_time(ctx,
1971 bt_message_event_borrow_default_clock_snapshot_const(
1972 msg));
1973 }
1974
1975 /* Write follow tag for message */
1976 ret = write_message_follow_tag(ctx, stream);
1977 if (ret) {
1978 goto end;
1979 }
1980
1981 /* Write object's basic properties */
1982 write_obj_type_name(ctx, "Event");
1983 ec_name = bt_event_class_get_name(ec);
1984 if (ec_name) {
1985 g_string_append_printf(ctx->str, " `%s%s%s`",
1986 color_fg_green(ctx), ec_name, color_reset(ctx));
1987 }
1988
1989 g_string_append(ctx->str, " (");
1990
1991 if (!ctx->details_comp->cfg.compact) {
1992 g_string_append(ctx->str, "Class ID ");
1993 }
1994
1995 write_uint_prop_value(ctx, bt_event_class_get_id(ec));
1996 g_string_append(ctx->str, ")");
1997
1998 if (ctx->details_comp->cfg.compact) {
1999 write_nl(ctx);
2000 goto end;
2001 }
2002
2003 /* Write fields */
2004 g_string_append(ctx->str, ":\n");
2005 incr_indent(ctx);
2006 field = bt_event_borrow_common_context_field_const(event);
2007 if (field) {
2008 write_root_field(ctx, "Common context", field);
2009 }
2010
2011 field = bt_event_borrow_specific_context_field_const(event);
2012 if (field) {
2013 write_root_field(ctx, "Specific context", field);
2014 }
2015
2016 field = bt_event_borrow_payload_field_const(event);
2017 if (field) {
2018 write_root_field(ctx, "Payload", field);
2019 }
2020
2021 decr_indent(ctx);
2022
2023 end:
2024 return ret;
2025 }
2026
2027 static
2028 gint compare_streams(const bt_stream **a, const bt_stream **b)
2029 {
2030 uint64_t id_a = bt_stream_get_id(*a);
2031 uint64_t id_b = bt_stream_get_id(*b);
2032
2033 if (id_a < id_b) {
2034 return -1;
2035 } else if (id_a > id_b) {
2036 return 1;
2037 } else {
2038 const bt_stream_class *a_sc = bt_stream_borrow_class_const(*a);
2039 const bt_stream_class *b_sc = bt_stream_borrow_class_const(*b);
2040 uint64_t a_sc_id = bt_stream_class_get_id(a_sc);
2041 uint64_t b_sc_id = bt_stream_class_get_id(b_sc);
2042
2043 if (a_sc_id < b_sc_id) {
2044 return -1;
2045 } else if (a_sc_id > b_sc_id) {
2046 return 1;
2047 } else {
2048 return 0;
2049 }
2050 }
2051 }
2052
2053 static
2054 void write_trace(struct details_write_ctx *ctx, const bt_trace *trace)
2055 {
2056 GPtrArray *streams = g_ptr_array_new();
2057 uint64_t i;
2058 bool printed_prop = false;
2059 GPtrArray *env_names = g_ptr_array_new();
2060 uint64_t env_count;
2061
2062 write_indent(ctx);
2063 write_obj_type_name(ctx, "Trace");
2064
2065 /* Write name */
2066 if (ctx->details_comp->cfg.with_trace_name) {
2067 const char *name = bt_trace_get_name(trace);
2068 if (name) {
2069 g_string_append(ctx->str, " `");
2070 write_str_prop_value(ctx, name);
2071 g_string_append(ctx->str, "`");
2072 }
2073 }
2074
2075 /* Write properties */
2076 incr_indent(ctx);
2077
2078 /* Write UUID */
2079 if (ctx->details_comp->cfg.with_uuid) {
2080 bt_uuid uuid = bt_trace_get_uuid(trace);
2081
2082 if (uuid) {
2083 if (!printed_prop) {
2084 g_string_append(ctx->str, ":\n");
2085 printed_prop = true;
2086 }
2087
2088 write_uuid_prop_line(ctx, "UUID", uuid);
2089 }
2090 }
2091
2092 /* Write environment */
2093 env_count = bt_trace_get_environment_entry_count(trace);
2094 if (env_count > 0) {
2095 if (!printed_prop) {
2096 g_string_append(ctx->str, ":\n");
2097 printed_prop = true;
2098 }
2099
2100 write_indent(ctx);
2101 write_prop_name(ctx, "Environment");
2102 g_string_append(ctx->str, " (");
2103 write_uint_prop_value(ctx, env_count);
2104 g_string_append_printf(ctx->str, " entr%s):",
2105 env_count == 1 ? "y" : "ies");
2106 write_nl(ctx);
2107 incr_indent(ctx);
2108
2109 for (i = 0; i < env_count; i++) {
2110 const char *name;
2111 const bt_value *value;
2112
2113 bt_trace_borrow_environment_entry_by_index_const(
2114 trace, i, &name, &value);
2115 g_ptr_array_add(env_names, (gpointer) name);
2116 }
2117
2118 g_ptr_array_sort(env_names, (GCompareFunc) compare_strings);
2119
2120 for (i = 0; i < env_names->len; i++) {
2121 const char *name = env_names->pdata[i];
2122 const bt_value *value =
2123 bt_trace_borrow_environment_entry_value_by_name_const(
2124 trace, name);
2125
2126 BT_ASSERT_DBG(value);
2127 write_compound_member_name(ctx, name);
2128
2129 if (bt_value_get_type(value) ==
2130 BT_VALUE_TYPE_SIGNED_INTEGER) {
2131 write_sp(ctx);
2132 write_int_prop_value(ctx,
2133 bt_value_integer_signed_get(value));
2134 } else if (bt_value_get_type(value) ==
2135 BT_VALUE_TYPE_STRING) {
2136 if (strlen(bt_value_string_get(value)) > 0) {
2137 write_sp(ctx);
2138 write_str_prop_value(ctx,
2139 bt_value_string_get(value));
2140 }
2141 } else {
2142 bt_common_abort();
2143 }
2144
2145 write_nl(ctx);
2146 }
2147
2148 decr_indent(ctx);
2149 }
2150
2151 for (i = 0; i < bt_trace_get_stream_count(trace); i++) {
2152 g_ptr_array_add(streams,
2153 (gpointer) bt_trace_borrow_stream_by_index_const(
2154 trace, i));
2155 }
2156
2157 g_ptr_array_sort(streams, (GCompareFunc) compare_streams);
2158
2159 if (streams->len > 0 && !printed_prop) {
2160 g_string_append(ctx->str, ":\n");
2161 printed_prop = true;
2162 }
2163
2164 for (i = 0; i < streams->len; i++) {
2165 const bt_stream *stream = streams->pdata[i];
2166
2167 write_indent(ctx);
2168 write_obj_type_name(ctx, "Stream");
2169 g_string_append(ctx->str, " (ID ");
2170 write_uint_prop_value(ctx, bt_stream_get_id(stream));
2171 g_string_append(ctx->str, ", Class ID ");
2172 write_uint_prop_value(ctx, bt_stream_class_get_id(
2173 bt_stream_borrow_class_const(stream)));
2174 g_string_append(ctx->str, ")");
2175 write_nl(ctx);
2176 }
2177
2178 decr_indent(ctx);
2179
2180 if (!printed_prop) {
2181 write_nl(ctx);
2182 }
2183
2184 g_ptr_array_free(streams, TRUE);
2185 g_ptr_array_free(env_names, TRUE);
2186 }
2187
2188 static
2189 int write_stream_beginning_message(struct details_write_ctx *ctx,
2190 const bt_message *msg)
2191 {
2192 int ret = 0;
2193 const bt_stream *stream =
2194 bt_message_stream_beginning_borrow_stream_const(msg);
2195 const bt_trace *trace = bt_stream_borrow_trace_const(stream);
2196 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
2197 const bt_clock_class *cc = bt_stream_class_borrow_default_clock_class_const(sc);
2198 const bt_trace_class *tc = bt_stream_class_borrow_trace_class_const(sc);
2199 const char *name;
2200
2201 ret = try_write_meta(ctx, tc, sc, NULL);
2202 if (ret) {
2203 goto end;
2204 }
2205
2206 if (!ctx->details_comp->cfg.with_data) {
2207 goto end;
2208 }
2209
2210 if (ctx->str->len > 0) {
2211 /*
2212 * Output buffer contains metadata: separate blocks with
2213 * newline.
2214 */
2215 write_nl(ctx);
2216 }
2217
2218 /* Write time */
2219 if (cc) {
2220 const bt_clock_snapshot *cs;
2221 bt_message_stream_clock_snapshot_state cs_state =
2222 bt_message_stream_beginning_borrow_default_clock_snapshot_const(msg, &cs);
2223
2224 if (cs_state == BT_MESSAGE_STREAM_CLOCK_SNAPSHOT_STATE_KNOWN) {
2225 write_time(ctx, cs);
2226 } else {
2227 write_time_str(ctx, "Unknown");
2228 }
2229 }
2230
2231 /* Write follow tag for message */
2232 ret = write_message_follow_tag(ctx, stream);
2233 if (ret) {
2234 goto end;
2235 }
2236
2237 /* Write stream properties */
2238 write_obj_type_name(ctx, "Stream beginning");
2239
2240 if (ctx->details_comp->cfg.compact) {
2241 write_nl(ctx);
2242 goto end;
2243 }
2244
2245 g_string_append(ctx->str, ":\n");
2246 incr_indent(ctx);
2247
2248 if (ctx->details_comp->cfg.with_stream_name) {
2249 name = bt_stream_get_name(stream);
2250 if (name) {
2251 write_str_prop_line(ctx, "Name", name);
2252 }
2253 }
2254
2255 if (ctx->details_comp->cfg.with_stream_class_name) {
2256 name = bt_stream_class_get_name(sc);
2257 if (name) {
2258 write_str_prop_line(ctx, "Class name", name);
2259 }
2260 }
2261
2262 write_trace(ctx, trace);
2263 decr_indent(ctx);
2264
2265 end:
2266 return ret;
2267 }
2268
2269 static
2270 int write_stream_end_message(struct details_write_ctx *ctx,
2271 const bt_message *msg)
2272 {
2273 int ret = 0;
2274 const bt_stream *stream =
2275 bt_message_stream_end_borrow_stream_const(msg);
2276 const bt_stream_class *sc =
2277 bt_stream_borrow_class_const(stream);
2278 const bt_clock_class *cc =
2279 bt_stream_class_borrow_default_clock_class_const(sc);
2280
2281 if (!ctx->details_comp->cfg.with_data) {
2282 goto end;
2283 }
2284
2285 /* Write time */
2286 if (cc) {
2287 const bt_clock_snapshot *cs;
2288 bt_message_stream_clock_snapshot_state cs_state =
2289 bt_message_stream_end_borrow_default_clock_snapshot_const(msg, &cs);
2290
2291 if (cs_state == BT_MESSAGE_STREAM_CLOCK_SNAPSHOT_STATE_KNOWN) {
2292 write_time(ctx, cs);
2293 } else {
2294 write_time_str(ctx, "Unknown");
2295 }
2296 }
2297
2298 /* Write follow tag for message */
2299 ret = write_message_follow_tag(ctx, stream);
2300 if (ret) {
2301 goto end;
2302 }
2303
2304 /* Write stream properties */
2305 write_obj_type_name(ctx, "Stream end\n");
2306
2307 end:
2308 return ret;
2309 }
2310
2311 static
2312 int write_packet_beginning_message(struct details_write_ctx *ctx,
2313 const bt_message *msg)
2314 {
2315 int ret = 0;
2316 const bt_packet *packet =
2317 bt_message_packet_beginning_borrow_packet_const(msg);
2318 const bt_stream *stream = bt_packet_borrow_stream_const(packet);
2319 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
2320 const bt_field *field;
2321
2322 if (!ctx->details_comp->cfg.with_data) {
2323 goto end;
2324 }
2325
2326 /* Write time */
2327 if (bt_stream_class_packets_have_beginning_default_clock_snapshot(sc)) {
2328 write_time(ctx,
2329 bt_message_packet_beginning_borrow_default_clock_snapshot_const(
2330 msg));
2331 }
2332
2333 /* Write follow tag for message */
2334 ret = write_message_follow_tag(ctx, stream);
2335 if (ret) {
2336 goto end;
2337 }
2338
2339 write_obj_type_name(ctx, "Packet beginning");
2340
2341 if (ctx->details_comp->cfg.compact) {
2342 write_nl(ctx);
2343 goto end;
2344 }
2345
2346 /* Write field */
2347 field = bt_packet_borrow_context_field_const(packet);
2348 if (field) {
2349 g_string_append(ctx->str, ":\n");
2350 incr_indent(ctx);
2351 write_root_field(ctx, "Context", field);
2352 decr_indent(ctx);
2353 } else {
2354 write_nl(ctx);
2355 }
2356
2357 end:
2358 return ret;
2359 }
2360
2361 static
2362 int write_discarded_items_message(struct details_write_ctx *ctx,
2363 const char *name, const bt_stream *stream,
2364 const bt_clock_snapshot *beginning_cs,
2365 const bt_clock_snapshot *end_cs, uint64_t count)
2366 {
2367 int ret = 0;
2368
2369 /* Write times */
2370 if (beginning_cs) {
2371 write_time(ctx, beginning_cs);
2372 BT_ASSERT_DBG(end_cs);
2373 write_time(ctx, end_cs);
2374 }
2375
2376 /* Write follow tag for message */
2377 ret = write_message_follow_tag(ctx, stream);
2378 if (ret) {
2379 goto end;
2380 }
2381
2382 write_obj_type_name(ctx, "Discarded ");
2383 write_obj_type_name(ctx, name);
2384
2385 /* Write count */
2386 if (count == UINT64_C(-1)) {
2387 write_nl(ctx);
2388 goto end;
2389 }
2390
2391 g_string_append(ctx->str, " (");
2392 write_uint_prop_value(ctx, count);
2393 g_string_append_printf(ctx->str, " %s)\n", name);
2394
2395 end:
2396 return ret;
2397 }
2398
2399 static
2400 int write_discarded_events_message(struct details_write_ctx *ctx,
2401 const bt_message *msg)
2402 {
2403 int ret = 0;
2404 const bt_stream *stream = bt_message_discarded_events_borrow_stream_const(
2405 msg);
2406 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
2407 const bt_clock_snapshot *beginning_cs = NULL;
2408 const bt_clock_snapshot *end_cs = NULL;
2409 uint64_t count;
2410
2411 if (!ctx->details_comp->cfg.with_data) {
2412 goto end;
2413 }
2414
2415 if (bt_stream_class_discarded_events_have_default_clock_snapshots(sc)) {
2416 beginning_cs =
2417 bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const(
2418 msg);
2419 end_cs =
2420 bt_message_discarded_events_borrow_end_default_clock_snapshot_const(
2421 msg);
2422 }
2423
2424 if (bt_message_discarded_events_get_count(msg, &count) !=
2425 BT_PROPERTY_AVAILABILITY_AVAILABLE) {
2426 count = UINT64_C(-1);
2427 }
2428
2429 ret = write_discarded_items_message(ctx, "events", stream,
2430 beginning_cs, end_cs, count);
2431
2432 end:
2433 return ret;
2434 }
2435
2436 static
2437 int write_discarded_packets_message(struct details_write_ctx *ctx,
2438 const bt_message *msg)
2439 {
2440 int ret = 0;
2441 const bt_stream *stream = bt_message_discarded_packets_borrow_stream_const(
2442 msg);
2443 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
2444 const bt_clock_snapshot *beginning_cs = NULL;
2445 const bt_clock_snapshot *end_cs = NULL;
2446 uint64_t count;
2447
2448 if (!ctx->details_comp->cfg.with_data) {
2449 goto end;
2450 }
2451
2452 if (bt_stream_class_discarded_packets_have_default_clock_snapshots(sc)) {
2453 beginning_cs =
2454 bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const(
2455 msg);
2456 end_cs =
2457 bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(
2458 msg);
2459 }
2460
2461 if (bt_message_discarded_packets_get_count(msg, &count) !=
2462 BT_PROPERTY_AVAILABILITY_AVAILABLE) {
2463 count = UINT64_C(-1);
2464 }
2465
2466 ret = write_discarded_items_message(ctx, "packets", stream,
2467 beginning_cs, end_cs, count);
2468
2469 end:
2470 return ret;
2471 }
2472
2473 static
2474 int write_packet_end_message(struct details_write_ctx *ctx,
2475 const bt_message *msg)
2476 {
2477 int ret = 0;
2478 const bt_packet *packet =
2479 bt_message_packet_end_borrow_packet_const(msg);
2480 const bt_stream *stream = bt_packet_borrow_stream_const(packet);
2481 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
2482
2483 if (!ctx->details_comp->cfg.with_data) {
2484 goto end;
2485 }
2486
2487 /* Write time */
2488 if (bt_stream_class_packets_have_end_default_clock_snapshot(sc)) {
2489 write_time(ctx,
2490 bt_message_packet_end_borrow_default_clock_snapshot_const(
2491 msg));
2492 }
2493
2494 /* Write follow tag for message */
2495 ret = write_message_follow_tag(ctx, stream);
2496 if (ret) {
2497 goto end;
2498 }
2499
2500 write_obj_type_name(ctx, "Packet end");
2501 write_nl(ctx);
2502
2503 end:
2504 return ret;
2505 }
2506
2507 static
2508 int write_message_iterator_inactivity_message(struct details_write_ctx *ctx,
2509 const bt_message *msg)
2510 {
2511 int ret = 0;
2512 const bt_clock_snapshot *cs =
2513 bt_message_message_iterator_inactivity_borrow_clock_snapshot_const(
2514 msg);
2515
2516 /* Write time */
2517 write_time(ctx, cs);
2518 write_obj_type_name(ctx, "Message iterator inactivity");
2519
2520 if (ctx->details_comp->cfg.compact) {
2521 write_nl(ctx);
2522 goto end;
2523 }
2524
2525 /* Write clock class properties */
2526 g_string_append(ctx->str, ":\n");
2527 incr_indent(ctx);
2528 write_indent(ctx);
2529 write_prop_name(ctx, "Clock class");
2530 g_string_append_c(ctx->str, ':');
2531 write_nl(ctx);
2532 incr_indent(ctx);
2533 write_clock_class_prop_lines(ctx,
2534 bt_clock_snapshot_borrow_clock_class_const(cs));
2535 decr_indent(ctx);
2536
2537 end:
2538 return ret;
2539 }
2540
2541 int details_write_message(struct details_comp *details_comp,
2542 const bt_message *msg)
2543 {
2544 int ret = 0;
2545 struct details_write_ctx ctx = {
2546 .details_comp = details_comp,
2547 .str = details_comp->str,
2548 .indent_level = 0,
2549 };
2550
2551 /* Reset output buffer */
2552 g_string_assign(details_comp->str, "");
2553
2554 switch (bt_message_get_type(msg)) {
2555 case BT_MESSAGE_TYPE_EVENT:
2556 ret = write_event_message(&ctx, msg);
2557 break;
2558 case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY:
2559 ret = write_message_iterator_inactivity_message(&ctx, msg);
2560 break;
2561 case BT_MESSAGE_TYPE_STREAM_BEGINNING:
2562 ret = write_stream_beginning_message(&ctx, msg);
2563 break;
2564 case BT_MESSAGE_TYPE_STREAM_END:
2565 ret = write_stream_end_message(&ctx, msg);
2566 break;
2567 case BT_MESSAGE_TYPE_PACKET_BEGINNING:
2568 ret = write_packet_beginning_message(&ctx, msg);
2569 break;
2570 case BT_MESSAGE_TYPE_PACKET_END:
2571 ret = write_packet_end_message(&ctx, msg);
2572 break;
2573 case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
2574 ret = write_discarded_events_message(&ctx, msg);
2575 break;
2576 case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
2577 ret = write_discarded_packets_message(&ctx, msg);
2578 break;
2579 default:
2580 bt_common_abort();
2581 }
2582
2583 /*
2584 * If this component printed at least one character so far, and
2585 * we're not in compact mode, and there's something in the
2586 * output buffer for this message, then prepend a newline to the
2587 * output buffer to visually separate message blocks.
2588 */
2589 if (details_comp->printed_something && !details_comp->cfg.compact &&
2590 details_comp->str->len > 0) {
2591 /* TODO: Optimize this */
2592 g_string_prepend_c(details_comp->str, '\n');
2593 }
2594
2595 return ret;
2596 }
This page took 0.161287 seconds and 5 git commands to generate.