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