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