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