sink.text.details: write discarded events/packets CS props when supported
[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 {
204 char buf[32];
205
206 write_indent(ctx);
207 format_uint(buf, index, 10);
208 g_string_append_printf(ctx->str, "%s[%s]%s:",
209 color_fg_cyan(ctx), buf, color_reset(ctx));
210 }
211
212 static inline
213 void write_obj_type_name(struct details_write_ctx *ctx, const char *name)
214 {
215 g_string_append_printf(ctx->str, "%s%s%s%s",
216 color_fg_yellow(ctx), color_bold(ctx), name, color_reset(ctx));
217 }
218
219 static inline
220 void write_prop_name(struct details_write_ctx *ctx, const char *prop_name)
221 {
222 g_string_append_printf(ctx->str, "%s%s%s",
223 color_fg_magenta(ctx), prop_name, color_reset(ctx));
224 }
225
226 static inline
227 void write_str_prop_value(struct details_write_ctx *ctx, const char *value)
228 {
229 g_string_append_printf(ctx->str, "%s%s%s",
230 color_bold(ctx), value, color_reset(ctx));
231 }
232
233 static inline
234 void write_uint_str_prop_value(struct details_write_ctx *ctx, const char *value)
235 {
236 write_str_prop_value(ctx, value);
237 }
238
239 static inline
240 void write_uint_prop_value(struct details_write_ctx *ctx, uint64_t value)
241 {
242 char buf[32];
243
244 format_uint(buf, value, 10);
245 write_uint_str_prop_value(ctx, buf);
246 }
247
248 static inline
249 void write_int_prop_value(struct details_write_ctx *ctx, int64_t value)
250 {
251 char buf[32];
252
253 format_int(buf, value, 10);
254 write_uint_str_prop_value(ctx, buf);
255 }
256
257 static inline
258 void write_float_prop_value(struct details_write_ctx *ctx, double value)
259 {
260 g_string_append_printf(ctx->str, "%s%f%s",
261 color_bold(ctx), value, color_reset(ctx));
262 }
263
264 static inline
265 void write_str_prop_line(struct details_write_ctx *ctx, const char *prop_name,
266 const char *prop_value)
267 {
268 BT_ASSERT(prop_value);
269 write_indent(ctx);
270 write_prop_name(ctx, prop_name);
271 g_string_append(ctx->str, ": ");
272 write_str_prop_value(ctx, prop_value);
273 write_nl(ctx);
274 }
275
276 static inline
277 void write_uint_prop_line(struct details_write_ctx *ctx, const char *prop_name,
278 uint64_t prop_value)
279 {
280 write_indent(ctx);
281 write_prop_name(ctx, prop_name);
282 g_string_append(ctx->str, ": ");
283 write_uint_prop_value(ctx, prop_value);
284 write_nl(ctx);
285 }
286
287 static inline
288 void write_int_prop_line(struct details_write_ctx *ctx, const char *prop_name,
289 int64_t prop_value)
290 {
291 write_indent(ctx);
292 write_prop_name(ctx, prop_name);
293 g_string_append(ctx->str, ": ");
294 write_int_prop_value(ctx, prop_value);
295 write_nl(ctx);
296 }
297
298 static inline
299 void write_int_str_prop_value(struct details_write_ctx *ctx, const char *value)
300 {
301 write_str_prop_value(ctx, value);
302 }
303
304 static inline
305 void write_bool_prop_line(struct details_write_ctx *ctx, const char *prop_name,
306 bt_bool prop_value)
307 {
308 const char *str;
309
310 write_indent(ctx);
311 write_prop_name(ctx, prop_name);
312 g_string_append_printf(ctx->str, ": %s", color_bold(ctx));
313
314 if (prop_value) {
315 g_string_append(ctx->str, color_fg_green(ctx));
316 str = "Yes";
317 } else {
318 g_string_append(ctx->str, color_fg_red(ctx));
319 str = "No";
320 }
321
322 g_string_append_printf(ctx->str, "%s%s\n", str, color_reset(ctx));
323 }
324
325 static inline
326 void write_uuid_prop_line(struct details_write_ctx *ctx, const char *prop_name,
327 bt_uuid uuid)
328 {
329 BT_ASSERT(uuid);
330 write_indent(ctx);
331 write_prop_name(ctx, prop_name);
332 g_string_append_printf(ctx->str,
333 ": %s" BT_UUID_FMT "%s\n",
334 color_bold(ctx),
335 BT_UUID_FMT_VALUES(uuid),
336 color_reset(ctx));
337 }
338
339 static
340 void write_int_field_class_props(struct details_write_ctx *ctx,
341 const bt_field_class *fc, bool close)
342 {
343 g_string_append_printf(ctx->str, "(%s%" PRIu64 "-bit%s, Base ",
344 color_bold(ctx),
345 bt_field_class_integer_get_field_value_range(fc),
346 color_reset(ctx));
347
348 switch (bt_field_class_integer_get_preferred_display_base(fc)) {
349 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY:
350 write_uint_prop_value(ctx, 2);
351 break;
352 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL:
353 write_uint_prop_value(ctx, 8);
354 break;
355 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL:
356 write_uint_prop_value(ctx, 10);
357 break;
358 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL:
359 write_uint_prop_value(ctx, 16);
360 break;
361 default:
362 abort();
363 }
364
365 if (close) {
366 g_string_append(ctx->str, ")");
367 }
368 }
369
370 struct enum_field_class_mapping_range {
371 union {
372 uint64_t u;
373 int64_t i;
374 } lower;
375
376 union {
377 uint64_t u;
378 int64_t i;
379 } upper;
380 };
381
382 struct enum_field_class_mapping {
383 /* Weak */
384 const char *label;
385
386 /* Array of `struct enum_field_class_mapping_range` */
387 GArray *ranges;
388 };
389
390 static
391 gint compare_enum_field_class_mappings(struct enum_field_class_mapping **a,
392 struct enum_field_class_mapping **b)
393 {
394 return strcmp((*a)->label, (*b)->label);
395 }
396
397 static
398 gint compare_enum_field_class_mapping_ranges_signed(
399 struct enum_field_class_mapping_range *a,
400 struct enum_field_class_mapping_range *b)
401 {
402
403 if (a->lower.i < b->lower.i) {
404 return -1;
405 } else if (a->lower.i > b->lower.i) {
406 return 1;
407 } else {
408 if (a->upper.i < b->upper.i) {
409 return -1;
410 } else if (a->upper.i > b->upper.i) {
411 return 1;
412 } else {
413 return 0;
414 }
415 }
416 }
417
418 static
419 gint compare_enum_field_class_mapping_ranges_unsigned(
420 struct enum_field_class_mapping_range *a,
421 struct enum_field_class_mapping_range *b)
422 {
423 if (a->lower.u < b->lower.u) {
424 return -1;
425 } else if (a->lower.u > b->lower.u) {
426 return 1;
427 } else {
428 if (a->upper.u < b->upper.u) {
429 return -1;
430 } else if (a->upper.u > b->upper.u) {
431 return 1;
432 } else {
433 return 0;
434 }
435 }
436 }
437
438 static
439 void destroy_enum_field_class_mapping(struct enum_field_class_mapping *mapping)
440 {
441 if (mapping->ranges) {
442 g_array_free(mapping->ranges, TRUE);
443 mapping->ranges = NULL;
444 }
445
446 g_free(mapping);
447 }
448
449 static
450 void write_enum_field_class_mapping_range(struct details_write_ctx *ctx,
451 struct enum_field_class_mapping_range *range, bool is_signed)
452 {
453 g_string_append(ctx->str, "[");
454
455 if (is_signed) {
456 write_int_prop_value(ctx, range->lower.i);
457 } else {
458 write_int_prop_value(ctx, range->lower.u);
459 }
460
461 g_string_append(ctx->str, ", ");
462
463 if (is_signed) {
464 write_int_prop_value(ctx, range->upper.i);
465 } else {
466 write_int_prop_value(ctx, range->upper.u);
467 }
468
469 g_string_append(ctx->str, "]");
470 }
471
472 static
473 void write_enum_field_class_mappings(struct details_write_ctx *ctx,
474 const bt_field_class *fc)
475 {
476 GPtrArray *mappings;
477 uint64_t i;
478 uint64_t range_i;
479 bool is_signed = bt_field_class_get_type(fc) ==
480 BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION;
481
482 mappings = g_ptr_array_new_with_free_func(
483 (GDestroyNotify) destroy_enum_field_class_mapping);
484 BT_ASSERT(mappings);
485
486 /*
487 * Copy field class's mappings to our own arrays and structures
488 * to sort them.
489 */
490 for (i = 0; i < bt_field_class_enumeration_get_mapping_count(fc); i++) {
491 const void *fc_mapping;
492 struct enum_field_class_mapping *mapping = g_new0(
493 struct enum_field_class_mapping, 1);
494
495 BT_ASSERT(mapping);
496 mapping->ranges = g_array_new(FALSE, TRUE,
497 sizeof(struct enum_field_class_mapping_range));
498 BT_ASSERT(mapping->ranges);
499
500 if (is_signed) {
501 fc_mapping = bt_field_class_signed_enumeration_borrow_mapping_by_index_const(
502 fc, i);
503 } else {
504 fc_mapping = bt_field_class_unsigned_enumeration_borrow_mapping_by_index_const(
505 fc, i);
506 }
507
508 mapping->label = bt_field_class_enumeration_mapping_get_label(
509 bt_field_class_signed_enumeration_mapping_as_mapping_const(
510 fc_mapping));
511
512 for (range_i = 0;
513 range_i < bt_field_class_enumeration_mapping_get_range_count(
514 bt_field_class_signed_enumeration_mapping_as_mapping_const(fc_mapping));
515 range_i++) {
516 struct enum_field_class_mapping_range range;
517
518 if (is_signed) {
519 bt_field_class_signed_enumeration_mapping_get_range_by_index(
520 fc_mapping, range_i,
521 &range.lower.i, &range.upper.i);
522 } else {
523 bt_field_class_unsigned_enumeration_mapping_get_range_by_index(
524 fc_mapping, range_i,
525 &range.lower.u, &range.upper.u);
526 }
527
528 g_array_append_val(mapping->ranges, range);
529 }
530
531 g_ptr_array_add(mappings, mapping);
532 }
533
534 /* Sort mappings, and for each mapping, sort ranges */
535 g_ptr_array_sort(mappings,
536 (GCompareFunc) compare_enum_field_class_mappings);
537
538 for (i = 0; i < mappings->len; i++) {
539 struct enum_field_class_mapping *mapping = mappings->pdata[i];
540
541 if (is_signed) {
542 g_array_sort(mapping->ranges,
543 (GCompareFunc)
544 compare_enum_field_class_mapping_ranges_signed);
545 } else {
546 g_array_sort(mapping->ranges,
547 (GCompareFunc)
548 compare_enum_field_class_mapping_ranges_unsigned);
549 }
550 }
551
552 /* Write mappings */
553 for (i = 0; i < mappings->len; i++) {
554 struct enum_field_class_mapping *mapping = mappings->pdata[i];
555
556 write_nl(ctx);
557 write_compound_member_name(ctx, mapping->label);
558
559 if (mapping->ranges->len == 1) {
560 /* Single one: write on same line */
561 write_sp(ctx);
562 write_enum_field_class_mapping_range(ctx,
563 &g_array_index(mapping->ranges,
564 struct enum_field_class_mapping_range,
565 0), is_signed);
566 continue;
567 }
568
569 incr_indent(ctx);
570
571 for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
572 write_nl(ctx);
573 write_indent(ctx);
574 write_enum_field_class_mapping_range(ctx,
575 &g_array_index(mapping->ranges,
576 struct enum_field_class_mapping_range,
577 range_i), is_signed);
578 }
579
580 decr_indent(ctx);
581 }
582
583 g_ptr_array_free(mappings, TRUE);
584 }
585
586 static
587 void write_field_path(struct details_write_ctx *ctx,
588 const bt_field_path *field_path)
589 {
590 uint64_t i;
591
592 g_string_append_c(ctx->str, '[');
593
594 switch (bt_field_path_get_root_scope(field_path)) {
595 case BT_SCOPE_PACKET_CONTEXT:
596 write_str_prop_value(ctx, "Packet context");
597 break;
598 case BT_SCOPE_EVENT_COMMON_CONTEXT:
599 write_str_prop_value(ctx, "Event common context");
600 break;
601 case BT_SCOPE_EVENT_SPECIFIC_CONTEXT:
602 write_str_prop_value(ctx, "Event specific context");
603 break;
604 case BT_SCOPE_EVENT_PAYLOAD:
605 write_str_prop_value(ctx, "Event payload");
606 break;
607 default:
608 abort();
609 }
610
611 g_string_append(ctx->str, ": ");
612
613 for (i = 0; i < bt_field_path_get_item_count(field_path); i++) {
614 const bt_field_path_item *fp_item =
615 bt_field_path_borrow_item_by_index_const(field_path, i);
616
617 if (i != 0) {
618 g_string_append(ctx->str, ", ");
619 }
620
621 switch (bt_field_path_item_get_type(fp_item)) {
622 case BT_FIELD_PATH_ITEM_TYPE_INDEX:
623 write_uint_prop_value(ctx,
624 bt_field_path_item_index_get_index(fp_item));
625 break;
626 case BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT:
627 write_str_prop_value(ctx, "<current>");
628 break;
629 default:
630 abort();
631 }
632 }
633
634 g_string_append_c(ctx->str, ']');
635 }
636
637 static
638 void write_field_class(struct details_write_ctx *ctx, const bt_field_class *fc,
639 const char *name)
640 {
641 uint64_t i;
642 const char *type;
643 bt_field_class_type fc_type = bt_field_class_get_type(fc);
644
645 /* Write field class's name */
646 if (name) {
647 write_compound_member_name(ctx, name);
648 write_sp(ctx);
649 }
650
651 /* Write field class's type */
652 switch (fc_type) {
653 case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER:
654 type = "Unsigned integer";
655 break;
656 case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER:
657 type = "Signed integer";
658 break;
659 case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION:
660 type = "Unsigned enumeration";
661 break;
662 case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION:
663 type = "Signed enumeration";
664 break;
665 case BT_FIELD_CLASS_TYPE_REAL:
666 type = "Real";
667 break;
668 case BT_FIELD_CLASS_TYPE_STRING:
669 type = "String";
670 break;
671 case BT_FIELD_CLASS_TYPE_STRUCTURE:
672 type = "Structure";
673 break;
674 case BT_FIELD_CLASS_TYPE_STATIC_ARRAY:
675 type = "Static array";
676 break;
677 case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY:
678 type = "Dynamic array";
679 break;
680 case BT_FIELD_CLASS_TYPE_VARIANT:
681 type = "Variant";
682 break;
683 default:
684 abort();
685 }
686
687 g_string_append_printf(ctx->str, "%s%s%s",
688 color_fg_blue(ctx), type, color_reset(ctx));
689
690 /* Write field class's properties */
691 switch (fc_type) {
692 case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER:
693 case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER:
694 write_sp(ctx);
695 write_int_field_class_props(ctx, fc, true);
696 break;
697 case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION:
698 case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION:
699 {
700 uint64_t mapping_count =
701 bt_field_class_enumeration_get_mapping_count(fc);
702
703 write_sp(ctx);
704 write_int_field_class_props(ctx, fc, false);
705 g_string_append(ctx->str, ", ");
706 write_uint_prop_value(ctx, mapping_count);
707 g_string_append_printf(ctx->str, " mapping%s)",
708 plural(mapping_count));
709
710 if (mapping_count > 0) {
711 g_string_append_c(ctx->str, ':');
712 incr_indent(ctx);
713 write_enum_field_class_mappings(ctx, fc);
714 decr_indent(ctx);
715 }
716
717 break;
718 }
719 case BT_FIELD_CLASS_TYPE_REAL:
720 if (bt_field_class_real_is_single_precision(fc)) {
721 g_string_append(ctx->str, " (Single precision)");
722 } else {
723 g_string_append(ctx->str, " (Double precision)");
724 }
725
726 break;
727 case BT_FIELD_CLASS_TYPE_STRUCTURE:
728 {
729 uint64_t member_count =
730 bt_field_class_structure_get_member_count(fc);
731
732 g_string_append(ctx->str, " (");
733 write_uint_prop_value(ctx, member_count);
734 g_string_append_printf(ctx->str, " member%s)",
735 plural(member_count));
736
737 if (member_count > 0) {
738 g_string_append_c(ctx->str, ':');
739 incr_indent(ctx);
740
741 for (i = 0; i < member_count; i++) {
742 const bt_field_class_structure_member *member =
743 bt_field_class_structure_borrow_member_by_index_const(
744 fc, i);
745
746 write_nl(ctx);
747 write_field_class(ctx,
748 bt_field_class_structure_member_borrow_field_class_const(member),
749 bt_field_class_structure_member_get_name(member));
750 }
751
752 decr_indent(ctx);
753 }
754
755 break;
756 }
757 case BT_FIELD_CLASS_TYPE_STATIC_ARRAY:
758 case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY:
759 if (fc_type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY) {
760 g_string_append(ctx->str, " (Length ");
761 write_uint_prop_value(ctx,
762 bt_field_class_static_array_get_length(fc));
763 g_string_append_c(ctx->str, ')');
764 } else {
765 const bt_field_path *length_field_path =
766 bt_field_class_dynamic_array_borrow_length_field_path_const(
767 fc);
768
769 if (length_field_path) {
770 g_string_append(ctx->str, " (Length field path ");
771 write_field_path(ctx, length_field_path);
772 g_string_append_c(ctx->str, ')');
773 }
774 }
775
776 g_string_append_c(ctx->str, ':');
777 write_nl(ctx);
778 incr_indent(ctx);
779 write_field_class(ctx,
780 bt_field_class_array_borrow_element_field_class_const(fc),
781 "Element");
782 decr_indent(ctx);
783 break;
784 case BT_FIELD_CLASS_TYPE_VARIANT:
785 {
786 uint64_t option_count =
787 bt_field_class_variant_get_option_count(fc);
788 const bt_field_path *sel_field_path =
789 bt_field_class_variant_borrow_selector_field_path_const(
790 fc);
791
792 g_string_append(ctx->str, " (");
793 write_uint_prop_value(ctx, option_count);
794 g_string_append_printf(ctx->str, " option%s, ",
795 plural(option_count));
796
797 if (sel_field_path) {
798 g_string_append(ctx->str, "Selector field path ");
799 write_field_path(ctx, sel_field_path);
800 }
801
802 g_string_append_c(ctx->str, ')');
803
804 if (option_count > 0) {
805 g_string_append_c(ctx->str, ':');
806 incr_indent(ctx);
807
808 for (i = 0; i < option_count; i++) {
809 const bt_field_class_variant_option *option =
810 bt_field_class_variant_borrow_option_by_index_const(
811 fc, i);
812
813 write_nl(ctx);
814 write_field_class(ctx,
815 bt_field_class_variant_option_borrow_field_class_const(option),
816 bt_field_class_variant_option_get_name(option));
817 }
818
819 decr_indent(ctx);
820 }
821
822 break;
823 }
824 default:
825 break;
826 }
827 }
828
829 static
830 void write_root_field_class(struct details_write_ctx *ctx, const char *name,
831 const bt_field_class *fc)
832 {
833 BT_ASSERT(name);
834 BT_ASSERT(fc);
835 write_indent(ctx);
836 write_prop_name(ctx, name);
837 g_string_append(ctx->str, ": ");
838 write_field_class(ctx, fc, NULL);
839 write_nl(ctx);
840 }
841
842 static
843 void write_event_class(struct details_write_ctx *ctx, const bt_event_class *ec)
844 {
845 const char *name = bt_event_class_get_name(ec);
846 const char *emf_uri;
847 const bt_field_class *fc;
848 bt_event_class_log_level log_level;
849
850 write_indent(ctx);
851 write_obj_type_name(ctx, "Event class");
852
853 /* Write name and ID */
854 if (name) {
855 g_string_append_printf(ctx->str, " `%s%s%s`",
856 color_fg_green(ctx), name, color_reset(ctx));
857 }
858
859 g_string_append(ctx->str, " (ID ");
860 write_uint_prop_value(ctx, bt_event_class_get_id(ec));
861 g_string_append(ctx->str, "):\n");
862
863 /* Write properties */
864 incr_indent(ctx);
865
866 /* Write log level */
867 if (bt_event_class_get_log_level(ec, &log_level) ==
868 BT_PROPERTY_AVAILABILITY_AVAILABLE) {
869 const char *ll_str = NULL;
870
871 switch (log_level) {
872 case BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY:
873 ll_str = "Emergency";
874 break;
875 case BT_EVENT_CLASS_LOG_LEVEL_ALERT:
876 ll_str = "Alert";
877 break;
878 case BT_EVENT_CLASS_LOG_LEVEL_CRITICAL:
879 ll_str = "Critical";
880 break;
881 case BT_EVENT_CLASS_LOG_LEVEL_ERROR:
882 ll_str = "Error";
883 break;
884 case BT_EVENT_CLASS_LOG_LEVEL_WARNING:
885 ll_str = "Warning";
886 break;
887 case BT_EVENT_CLASS_LOG_LEVEL_NOTICE:
888 ll_str = "Notice";
889 break;
890 case BT_EVENT_CLASS_LOG_LEVEL_INFO:
891 ll_str = "Info";
892 break;
893 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM:
894 ll_str = "Debug (system)";
895 break;
896 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM:
897 ll_str = "Debug (program)";
898 break;
899 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS:
900 ll_str = "Debug (process)";
901 break;
902 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE:
903 ll_str = "Debug (module)";
904 break;
905 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT:
906 ll_str = "Debug (unit)";
907 break;
908 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION:
909 ll_str = "Debug (function)";
910 break;
911 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE:
912 ll_str = "Debug (line)";
913 break;
914 case BT_EVENT_CLASS_LOG_LEVEL_DEBUG:
915 ll_str = "Debug";
916 break;
917 default:
918 abort();
919 }
920
921 write_str_prop_line(ctx, "Log level", ll_str);
922 }
923
924 /* Write EMF URI */
925 emf_uri = bt_event_class_get_emf_uri(ec);
926 if (emf_uri) {
927 write_str_prop_line(ctx, "EMF URI", emf_uri);
928 }
929
930 /* Write specific context field class */
931 fc = bt_event_class_borrow_specific_context_field_class_const(ec);
932 if (fc) {
933 write_root_field_class(ctx, "Specific context field class", fc);
934 }
935
936 /* Write payload field class */
937 fc = bt_event_class_borrow_payload_field_class_const(ec);
938 if (fc) {
939 write_root_field_class(ctx, "Payload field class", fc);
940 }
941
942 decr_indent(ctx);
943 }
944
945 static
946 void write_clock_class_prop_lines(struct details_write_ctx *ctx,
947 const bt_clock_class *cc)
948 {
949 int64_t offset_seconds;
950 uint64_t offset_cycles;
951 const char *str;
952
953 str = bt_clock_class_get_name(cc);
954 if (str) {
955 write_str_prop_line(ctx, "Name", str);
956 }
957
958 str = bt_clock_class_get_description(cc);
959 if (str) {
960 write_str_prop_line(ctx, "Description", str);
961 }
962
963 write_uint_prop_line(ctx, "Frequency (Hz)",
964 bt_clock_class_get_frequency(cc));
965 write_uint_prop_line(ctx, "Precision (cycles)",
966 bt_clock_class_get_precision(cc));
967 bt_clock_class_get_offset(cc, &offset_seconds, &offset_cycles);
968 write_int_prop_line(ctx, "Offset (s)", offset_seconds);
969 write_uint_prop_line(ctx, "Offset (cycles)", offset_cycles);
970 write_bool_prop_line(ctx, "Origin is Unix epoch",
971 bt_clock_class_origin_is_unix_epoch(cc));
972
973 if (ctx->details_comp->cfg.with_uuid) {
974 bt_uuid uuid = bt_clock_class_get_uuid(cc);
975
976 if (uuid) {
977 write_uuid_prop_line(ctx, "UUID", uuid);
978 }
979 }
980 }
981
982 static
983 gint compare_event_classes(const bt_event_class **a, const bt_event_class **b)
984 {
985 uint64_t id_a = bt_event_class_get_id(*a);
986 uint64_t id_b = bt_event_class_get_id(*b);
987
988 if (id_a < id_b) {
989 return -1;
990 } else if (id_a > id_b) {
991 return 1;
992 } else {
993 return 0;
994 }
995 }
996
997 static
998 void write_stream_class(struct details_write_ctx *ctx,
999 const bt_stream_class *sc)
1000 {
1001 const bt_field_class *fc;
1002 GPtrArray *event_classes = g_ptr_array_new();
1003 uint64_t i;
1004
1005 write_indent(ctx);
1006 write_obj_type_name(ctx, "Stream class");
1007
1008 /* Write name and ID */
1009 if (ctx->details_comp->cfg.with_stream_class_name) {
1010 const char *name = bt_stream_class_get_name(sc);
1011
1012 if (name) {
1013 g_string_append(ctx->str, " `");
1014 write_str_prop_value(ctx, name);
1015 g_string_append(ctx->str, "`");
1016 }
1017 }
1018
1019 g_string_append(ctx->str, " (ID ");
1020 write_uint_prop_value(ctx, bt_stream_class_get_id(sc));
1021 g_string_append(ctx->str, "):\n");
1022
1023 /* Write properties */
1024 incr_indent(ctx);
1025
1026 /* Write configuration */
1027 write_bool_prop_line(ctx,
1028 "Packets have beginning default clock snapshot",
1029 bt_stream_class_packets_have_beginning_default_clock_snapshot(sc));
1030 write_bool_prop_line(ctx,
1031 "Packets have end default clock snapshot",
1032 bt_stream_class_packets_have_end_default_clock_snapshot(sc));
1033 write_bool_prop_line(ctx,
1034 "Supports discarded events",
1035 bt_stream_class_supports_discarded_events(sc));
1036
1037 if (bt_stream_class_supports_discarded_events(sc)) {
1038 write_bool_prop_line(ctx,
1039 "Discarded events have default clock snapshots",
1040 bt_stream_class_discarded_events_have_default_clock_snapshots(sc));
1041 }
1042
1043 write_bool_prop_line(ctx,
1044 "Supports discarded packets",
1045 bt_stream_class_supports_discarded_packets(sc));
1046
1047 if (bt_stream_class_supports_discarded_packets(sc)) {
1048 write_bool_prop_line(ctx,
1049 "Discarded packets have default clock snapshots",
1050 bt_stream_class_discarded_packets_have_default_clock_snapshots(sc));
1051 }
1052
1053 /* Write default clock class */
1054 if (bt_stream_class_borrow_default_clock_class_const(sc)) {
1055 write_indent(ctx);
1056 write_prop_name(ctx, "Default clock class");
1057 g_string_append_c(ctx->str, ':');
1058 write_nl(ctx);
1059 incr_indent(ctx);
1060 write_clock_class_prop_lines(ctx,
1061 bt_stream_class_borrow_default_clock_class_const(sc));
1062 decr_indent(ctx);
1063 }
1064
1065 fc = bt_stream_class_borrow_packet_context_field_class_const(sc);
1066 if (fc) {
1067 write_root_field_class(ctx, "Packet context field class", fc);
1068 }
1069
1070 fc = bt_stream_class_borrow_event_common_context_field_class_const(sc);
1071 if (fc) {
1072 write_root_field_class(ctx, "Event common context field class",
1073 fc);
1074 }
1075
1076 for (i = 0; i < bt_stream_class_get_event_class_count(sc); i++) {
1077 g_ptr_array_add(event_classes,
1078 (gpointer) bt_stream_class_borrow_event_class_by_index_const(
1079 sc, i));
1080 }
1081
1082 g_ptr_array_sort(event_classes, (GCompareFunc) compare_event_classes);
1083
1084 for (i = 0; i < event_classes->len; i++) {
1085 write_event_class(ctx, event_classes->pdata[i]);
1086 }
1087
1088 decr_indent(ctx);
1089 g_ptr_array_free(event_classes, TRUE);
1090 }
1091
1092 static
1093 gint compare_stream_classes(const bt_stream_class **a, const bt_stream_class **b)
1094 {
1095 uint64_t id_a = bt_stream_class_get_id(*a);
1096 uint64_t id_b = bt_stream_class_get_id(*b);
1097
1098 if (id_a < id_b) {
1099 return -1;
1100 } else if (id_a > id_b) {
1101 return 1;
1102 } else {
1103 return 0;
1104 }
1105 }
1106
1107 static
1108 gint compare_strings(const char **a, const char **b)
1109 {
1110 return strcmp(*a, *b);
1111 }
1112
1113 static
1114 void write_trace_class(struct details_write_ctx *ctx, const bt_trace_class *tc)
1115 {
1116 GPtrArray *stream_classes = g_ptr_array_new();
1117 uint64_t i;
1118 bool printed_prop = false;
1119
1120 write_indent(ctx);
1121 write_obj_type_name(ctx, "Trace class");
1122
1123 for (i = 0; i < bt_trace_class_get_stream_class_count(tc); i++) {
1124 g_ptr_array_add(stream_classes,
1125 (gpointer) bt_trace_class_borrow_stream_class_by_index_const(
1126 tc, i));
1127 }
1128
1129 g_ptr_array_sort(stream_classes, (GCompareFunc) compare_stream_classes);
1130
1131 if (stream_classes->len > 0) {
1132 if (!printed_prop) {
1133 g_string_append(ctx->str, ":\n");
1134 printed_prop = true;
1135 }
1136 }
1137
1138 incr_indent(ctx);
1139
1140 for (i = 0; i < stream_classes->len; i++) {
1141 write_stream_class(ctx, stream_classes->pdata[i]);
1142 }
1143
1144 if (!printed_prop) {
1145 write_nl(ctx);
1146 }
1147
1148 decr_indent(ctx);
1149 g_ptr_array_free(stream_classes, TRUE);
1150 }
1151
1152 static
1153 int try_write_meta(struct details_write_ctx *ctx, const bt_trace_class *tc,
1154 const bt_stream_class *sc, const bt_event_class *ec)
1155 {
1156 int ret = 0;
1157
1158 BT_ASSERT(tc);
1159
1160 if (details_need_to_write_trace_class(ctx, tc)) {
1161 uint64_t sc_i;
1162
1163 if (ctx->details_comp->cfg.compact &&
1164 ctx->details_comp->printed_something) {
1165 /*
1166 * There are no empty line between messages in
1167 * compact mode, so write one here to decouple
1168 * the trace class from the next message.
1169 */
1170 write_nl(ctx);
1171 }
1172
1173 /*
1174 * write_trace_class() also writes all its stream
1175 * classes their event classes, so we don't need to
1176 * rewrite `sc`.
1177 */
1178 write_trace_class(ctx, tc);
1179 write_nl(ctx);
1180
1181 /*
1182 * Mark this trace class as written, as well as all
1183 * its stream classes and their event classes.
1184 */
1185 ret = details_did_write_trace_class(ctx, tc);
1186 if (ret) {
1187 goto end;
1188 }
1189
1190 for (sc_i = 0; sc_i < bt_trace_class_get_stream_class_count(tc);
1191 sc_i++) {
1192 uint64_t ec_i;
1193 const bt_stream_class *tc_sc =
1194 bt_trace_class_borrow_stream_class_by_index_const(
1195 tc, sc_i);
1196
1197 details_did_write_meta_object(ctx, tc, tc_sc);
1198
1199 for (ec_i = 0; ec_i <
1200 bt_stream_class_get_event_class_count(tc_sc);
1201 ec_i++) {
1202 details_did_write_meta_object(ctx, tc,
1203 bt_stream_class_borrow_event_class_by_index_const(
1204 tc_sc, ec_i));
1205 }
1206 }
1207
1208 goto end;
1209 }
1210
1211 if (sc && details_need_to_write_meta_object(ctx, tc, sc)) {
1212 uint64_t ec_i;
1213
1214 BT_ASSERT(tc);
1215
1216 if (ctx->details_comp->cfg.compact &&
1217 ctx->details_comp->printed_something) {
1218 /*
1219 * There are no empty line between messages in
1220 * compact mode, so write one here to decouple
1221 * the stream class from the next message.
1222 */
1223 write_nl(ctx);
1224 }
1225
1226 /*
1227 * write_stream_class() also writes all its event
1228 * classes, so we don't need to rewrite `ec`.
1229 */
1230 write_stream_class(ctx, sc);
1231 write_nl(ctx);
1232
1233 /*
1234 * Mark this stream class as written, as well as all its
1235 * event classes.
1236 */
1237 details_did_write_meta_object(ctx, tc, sc);
1238
1239 for (ec_i = 0; ec_i <
1240 bt_stream_class_get_event_class_count(sc);
1241 ec_i++) {
1242 details_did_write_meta_object(ctx, tc,
1243 bt_stream_class_borrow_event_class_by_index_const(
1244 sc, ec_i));
1245 }
1246
1247 goto end;
1248 }
1249
1250 if (ec && details_need_to_write_meta_object(ctx, tc, ec)) {
1251 BT_ASSERT(sc);
1252
1253 if (ctx->details_comp->cfg.compact &&
1254 ctx->details_comp->printed_something) {
1255 /*
1256 * There are no empty line between messages in
1257 * compact mode, so write one here to decouple
1258 * the event class from the next message.
1259 */
1260 write_nl(ctx);
1261 }
1262
1263 write_event_class(ctx, ec);
1264 write_nl(ctx);
1265 details_did_write_meta_object(ctx, tc, ec);
1266 goto end;
1267 }
1268
1269 end:
1270 return ret;
1271 }
1272
1273 static
1274 void write_time_str(struct details_write_ctx *ctx, const char *str)
1275 {
1276 if (!ctx->details_comp->cfg.with_time) {
1277 goto end;
1278 }
1279
1280 g_string_append_printf(ctx->str, "[%s%s%s%s]",
1281 color_bold(ctx), color_fg_blue(ctx), str, color_reset(ctx));
1282
1283 if (ctx->details_comp->cfg.compact) {
1284 write_sp(ctx);
1285 } else {
1286 write_nl(ctx);
1287 }
1288
1289 end:
1290 return;
1291 }
1292
1293 static
1294 void write_time(struct details_write_ctx *ctx, const bt_clock_snapshot *cs)
1295 {
1296 bt_clock_snapshot_get_ns_from_origin_status cs_status;
1297 int64_t ns_from_origin;
1298 char buf[32];
1299
1300 if (!ctx->details_comp->cfg.with_time) {
1301 goto end;
1302 }
1303
1304 format_uint(buf, bt_clock_snapshot_get_value(cs), 10);
1305 g_string_append_printf(ctx->str, "[%s%s%s%s%s",
1306 color_bold(ctx), color_fg_blue(ctx), buf,
1307 color_reset(ctx),
1308 ctx->details_comp->cfg.compact ? "" : " cycles");
1309 cs_status = bt_clock_snapshot_get_ns_from_origin(cs, &ns_from_origin);
1310 if (cs_status == BT_CLOCK_SNAPSHOT_GET_NS_FROM_ORIGIN_STATUS_OK) {
1311 format_int(buf, ns_from_origin, 10);
1312 g_string_append_printf(ctx->str, "%s %s%s%s%s%s",
1313 ctx->details_comp->cfg.compact ? "" : ",",
1314 color_bold(ctx), color_fg_blue(ctx), buf,
1315 color_reset(ctx),
1316 ctx->details_comp->cfg.compact ? "" : " ns from origin");
1317 }
1318
1319 g_string_append(ctx->str, "]");
1320
1321 if (ctx->details_comp->cfg.compact) {
1322 write_sp(ctx);
1323 } else {
1324 write_nl(ctx);
1325 }
1326
1327 end:
1328 return;
1329 }
1330
1331 static
1332 int write_message_follow_tag(struct details_write_ctx *ctx,
1333 const bt_stream *stream)
1334 {
1335 int ret;
1336 uint64_t unique_trace_id;
1337 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
1338 const bt_trace *trace = bt_stream_borrow_trace_const(stream);
1339
1340 ret = details_trace_unique_id(ctx, trace, &unique_trace_id);
1341 if (ret) {
1342 goto end;
1343 }
1344
1345 if (ctx->details_comp->cfg.compact) {
1346 g_string_append_printf(ctx->str,
1347 "%s{%s%" PRIu64 " %" PRIu64 " %" PRIu64 "%s%s}%s ",
1348 color_fg_cyan(ctx), color_bold(ctx),
1349 unique_trace_id, bt_stream_class_get_id(sc),
1350 bt_stream_get_id(stream),
1351 color_reset(ctx), color_fg_cyan(ctx), color_reset(ctx));
1352 } else {
1353 g_string_append_printf(ctx->str,
1354 "%s{Trace %s%" PRIu64 "%s%s, Stream class ID %s%" PRIu64 "%s%s, Stream ID %s%" PRIu64 "%s%s}%s\n",
1355 color_fg_cyan(ctx),
1356 color_bold(ctx), unique_trace_id,
1357 color_reset(ctx), color_fg_cyan(ctx),
1358 color_bold(ctx), bt_stream_class_get_id(sc),
1359 color_reset(ctx), color_fg_cyan(ctx),
1360 color_bold(ctx), bt_stream_get_id(stream),
1361 color_reset(ctx), color_fg_cyan(ctx),
1362 color_reset(ctx));
1363 }
1364
1365 end:
1366 return ret;
1367 }
1368
1369 static
1370 void write_field(struct details_write_ctx *ctx, const bt_field *field,
1371 const char *name)
1372 {
1373 uint64_t i;
1374 bt_field_class_type fc_type = bt_field_get_class_type(field);
1375 const bt_field_class *fc;
1376 char buf[64];
1377
1378 /* Write field's name */
1379 if (name) {
1380 write_compound_member_name(ctx, name);
1381 }
1382
1383 /* Write field's value */
1384 switch (fc_type) {
1385 case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER:
1386 case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION:
1387 case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER:
1388 case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION:
1389 {
1390 unsigned int fmt_base;
1391 bt_field_class_integer_preferred_display_base base;
1392
1393 fc = bt_field_borrow_class_const(field);
1394 base = bt_field_class_integer_get_preferred_display_base(fc);
1395
1396 switch (base) {
1397 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL:
1398 fmt_base = 10;
1399 break;
1400 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL:
1401 fmt_base = 8;
1402 break;
1403 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY:
1404 fmt_base = 2;
1405 break;
1406 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL:
1407 fmt_base = 16;
1408 break;
1409 default:
1410 abort();
1411 }
1412
1413 if (fc_type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER ||
1414 fc_type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION) {
1415 format_uint(buf,
1416 bt_field_unsigned_integer_get_value(field),
1417 fmt_base);
1418 write_sp(ctx);
1419 write_uint_str_prop_value(ctx, buf);
1420 } else {
1421 format_int(buf,
1422 bt_field_signed_integer_get_value(field),
1423 fmt_base);
1424 write_sp(ctx);
1425 write_int_str_prop_value(ctx, buf);
1426 }
1427
1428 break;
1429 }
1430 case BT_FIELD_CLASS_TYPE_REAL:
1431 write_sp(ctx);
1432 write_float_prop_value(ctx, bt_field_real_get_value(field));
1433 break;
1434 case BT_FIELD_CLASS_TYPE_STRING:
1435 write_sp(ctx);
1436 write_str_prop_value(ctx, bt_field_string_get_value(field));
1437 break;
1438 case BT_FIELD_CLASS_TYPE_STRUCTURE:
1439 {
1440 uint64_t member_count;
1441
1442 fc = bt_field_borrow_class_const(field);
1443 member_count = bt_field_class_structure_get_member_count(fc);
1444
1445 if (member_count > 0) {
1446 incr_indent(ctx);
1447
1448 for (i = 0; i < member_count; i++) {
1449 const bt_field_class_structure_member *member =
1450 bt_field_class_structure_borrow_member_by_index_const(
1451 fc, i);
1452 const bt_field *member_field =
1453 bt_field_structure_borrow_member_field_by_index_const(
1454 field, i);
1455
1456 write_nl(ctx);
1457 write_field(ctx, member_field,
1458 bt_field_class_structure_member_get_name(member));
1459 }
1460
1461 decr_indent(ctx);
1462 } else {
1463 g_string_append(ctx->str, " Empty");
1464 }
1465
1466 break;
1467 }
1468 case BT_FIELD_CLASS_TYPE_STATIC_ARRAY:
1469 case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY:
1470 {
1471 uint64_t length = bt_field_array_get_length(field);
1472
1473 if (length == 0) {
1474 g_string_append(ctx->str, " Empty");
1475 } else {
1476 g_string_append(ctx->str, " Length ");
1477 write_uint_prop_value(ctx, length);
1478 g_string_append_c(ctx->str, ':');
1479 }
1480
1481 incr_indent(ctx);
1482
1483 for (i = 0; i < length; i++) {
1484 const bt_field *elem_field =
1485 bt_field_array_borrow_element_field_by_index_const(
1486 field, i);
1487
1488 write_nl(ctx);
1489 write_array_index(ctx, i);
1490 write_field(ctx, elem_field, NULL);
1491 }
1492
1493 decr_indent(ctx);
1494 break;
1495 }
1496 case BT_FIELD_CLASS_TYPE_VARIANT:
1497 write_field(ctx,
1498 bt_field_variant_borrow_selected_option_field_const(
1499 field), NULL);
1500 break;
1501 default:
1502 abort();
1503 }
1504 }
1505
1506 static
1507 void write_root_field(struct details_write_ctx *ctx, const char *name,
1508 const bt_field *field)
1509 {
1510 BT_ASSERT(name);
1511 BT_ASSERT(field);
1512 write_indent(ctx);
1513 write_prop_name(ctx, name);
1514 g_string_append(ctx->str, ":");
1515 write_field(ctx, field, NULL);
1516 write_nl(ctx);
1517 }
1518
1519 static
1520 int write_event_message(struct details_write_ctx *ctx,
1521 const bt_message *msg)
1522 {
1523 int ret = 0;
1524 const bt_event *event = bt_message_event_borrow_event_const(msg);
1525 const bt_stream *stream = bt_event_borrow_stream_const(event);
1526 const bt_event_class *ec = bt_event_borrow_class_const(event);
1527 const bt_stream_class *sc = bt_event_class_borrow_stream_class_const(ec);
1528 const bt_trace_class *tc = bt_stream_class_borrow_trace_class_const(sc);
1529 const char *ec_name;
1530 const bt_field *field;
1531
1532 ret = try_write_meta(ctx, tc, sc, ec);
1533 if (ret) {
1534 goto end;
1535 }
1536
1537 /* Write time */
1538 if (bt_stream_class_borrow_default_clock_class_const(sc)) {
1539 write_time(ctx,
1540 bt_message_event_borrow_default_clock_snapshot_const(
1541 msg));
1542 }
1543
1544 /* Write follow tag for message */
1545 ret = write_message_follow_tag(ctx, stream);
1546 if (ret) {
1547 goto end;
1548 }
1549
1550 /* Write object's basic properties */
1551 write_obj_type_name(ctx, "Event");
1552 ec_name = bt_event_class_get_name(ec);
1553 if (ec_name) {
1554 g_string_append_printf(ctx->str, " `%s%s%s`",
1555 color_fg_green(ctx), ec_name, color_reset(ctx));
1556 }
1557
1558 g_string_append(ctx->str, " (");
1559
1560 if (!ctx->details_comp->cfg.compact) {
1561 g_string_append(ctx->str, "Class ID ");
1562 }
1563
1564 write_uint_prop_value(ctx, bt_event_class_get_id(ec));
1565 g_string_append(ctx->str, ")");
1566
1567 if (ctx->details_comp->cfg.compact) {
1568 write_nl(ctx);
1569 goto end;
1570 }
1571
1572 /* Write fields */
1573 g_string_append(ctx->str, ":\n");
1574 incr_indent(ctx);
1575 field = bt_event_borrow_common_context_field_const(event);
1576 if (field) {
1577 write_root_field(ctx, "Common context", field);
1578 }
1579
1580 field = bt_event_borrow_specific_context_field_const(event);
1581 if (field) {
1582 write_root_field(ctx, "Specific context", field);
1583 }
1584
1585 field = bt_event_borrow_payload_field_const(event);
1586 if (field) {
1587 write_root_field(ctx, "Payload", field);
1588 }
1589
1590 decr_indent(ctx);
1591
1592 end:
1593
1594 return ret;
1595 }
1596
1597 static
1598 gint compare_streams(const bt_stream **a, const bt_stream **b)
1599 {
1600 uint64_t id_a = bt_stream_get_id(*a);
1601 uint64_t id_b = bt_stream_get_id(*b);
1602
1603 if (id_a < id_b) {
1604 return -1;
1605 } else if (id_a > id_b) {
1606 return 1;
1607 } else {
1608 const bt_stream_class *a_sc = bt_stream_borrow_class_const(*a);
1609 const bt_stream_class *b_sc = bt_stream_borrow_class_const(*b);
1610 uint64_t a_sc_id = bt_stream_class_get_id(a_sc);
1611 uint64_t b_sc_id = bt_stream_class_get_id(b_sc);
1612
1613 if (a_sc_id < b_sc_id) {
1614 return -1;
1615 } else if (a_sc_id > b_sc_id) {
1616 return 1;
1617 } else {
1618 return 0;
1619 }
1620 }
1621 }
1622
1623 static
1624 void write_trace(struct details_write_ctx *ctx, const bt_trace *trace)
1625 {
1626 const char *name;
1627 GPtrArray *streams = g_ptr_array_new();
1628 uint64_t i;
1629 bool printed_prop = false;
1630 GPtrArray *env_names = g_ptr_array_new();
1631 uint64_t env_count;
1632
1633 write_indent(ctx);
1634 write_obj_type_name(ctx, "Trace");
1635
1636 /* Write name */
1637 if (ctx->details_comp->cfg.with_trace_name) {
1638 name = bt_trace_get_name(trace);
1639 if (name) {
1640 g_string_append(ctx->str, " `");
1641 write_str_prop_value(ctx, name);
1642 g_string_append(ctx->str, "`");
1643 }
1644 }
1645
1646 /* Write properties */
1647 incr_indent(ctx);
1648
1649 /* Write UUID */
1650 if (ctx->details_comp->cfg.with_uuid) {
1651 bt_uuid uuid = bt_trace_get_uuid(trace);
1652
1653 if (uuid) {
1654 if (!printed_prop) {
1655 g_string_append(ctx->str, ":\n");
1656 printed_prop = true;
1657 }
1658
1659 write_uuid_prop_line(ctx, "UUID", uuid);
1660 }
1661 }
1662
1663 /* Write environment */
1664 env_count = bt_trace_get_environment_entry_count(trace);
1665 if (env_count > 0) {
1666 if (!printed_prop) {
1667 g_string_append(ctx->str, ":\n");
1668 printed_prop = true;
1669 }
1670
1671 write_indent(ctx);
1672 write_prop_name(ctx, "Environment");
1673 g_string_append(ctx->str, " (");
1674 write_uint_prop_value(ctx, env_count);
1675 g_string_append_printf(ctx->str, " entr%s):",
1676 env_count == 1 ? "y" : "ies");
1677 write_nl(ctx);
1678 incr_indent(ctx);
1679
1680 for (i = 0; i < env_count; i++) {
1681 const char *name;
1682 const bt_value *value;
1683
1684 bt_trace_borrow_environment_entry_by_index_const(
1685 trace, i, &name, &value);
1686 g_ptr_array_add(env_names, (gpointer) name);
1687 }
1688
1689 g_ptr_array_sort(env_names, (GCompareFunc) compare_strings);
1690
1691 for (i = 0; i < env_names->len; i++) {
1692 const char *name = env_names->pdata[i];
1693 const bt_value *value =
1694 bt_trace_borrow_environment_entry_value_by_name_const(
1695 trace, name);
1696
1697 BT_ASSERT(value);
1698 write_compound_member_name(ctx, name);
1699 write_sp(ctx);
1700
1701 if (bt_value_get_type(value) ==
1702 BT_VALUE_TYPE_SIGNED_INTEGER) {
1703 write_int_prop_value(ctx,
1704 bt_value_signed_integer_get(value));
1705 } else if (bt_value_get_type(value) ==
1706 BT_VALUE_TYPE_STRING) {
1707 write_str_prop_value(ctx,
1708 bt_value_string_get(value));
1709 } else {
1710 abort();
1711 }
1712
1713 write_nl(ctx);
1714 }
1715
1716 decr_indent(ctx);
1717 }
1718
1719 for (i = 0; i < bt_trace_get_stream_count(trace); i++) {
1720 g_ptr_array_add(streams,
1721 (gpointer) bt_trace_borrow_stream_by_index_const(
1722 trace, i));
1723 }
1724
1725 g_ptr_array_sort(streams, (GCompareFunc) compare_streams);
1726
1727 if (streams->len > 0 && !printed_prop) {
1728 g_string_append(ctx->str, ":\n");
1729 printed_prop = true;
1730 }
1731
1732 for (i = 0; i < streams->len; i++) {
1733 const bt_stream *stream = streams->pdata[i];
1734
1735 write_indent(ctx);
1736 write_obj_type_name(ctx, "Stream");
1737 g_string_append(ctx->str, " (ID ");
1738 write_uint_prop_value(ctx, bt_stream_get_id(stream));
1739 g_string_append(ctx->str, ", Class ID ");
1740 write_uint_prop_value(ctx, bt_stream_class_get_id(
1741 bt_stream_borrow_class_const(stream)));
1742 g_string_append(ctx->str, ")");
1743 write_nl(ctx);
1744 }
1745
1746 decr_indent(ctx);
1747
1748 if (!printed_prop) {
1749 write_nl(ctx);
1750 }
1751
1752 g_ptr_array_free(streams, TRUE);
1753 g_ptr_array_free(env_names, TRUE);
1754 }
1755
1756 static
1757 int write_stream_beginning_message(struct details_write_ctx *ctx,
1758 const bt_message *msg)
1759 {
1760 int ret = 0;
1761 const bt_stream *stream =
1762 bt_message_stream_beginning_borrow_stream_const(msg);
1763 const bt_trace *trace = bt_stream_borrow_trace_const(stream);
1764 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
1765 const bt_clock_class *cc = bt_stream_class_borrow_default_clock_class_const(sc);
1766 const bt_trace_class *tc = bt_stream_class_borrow_trace_class_const(sc);
1767 const char *name;
1768
1769 ret = try_write_meta(ctx, tc, sc, NULL);
1770 if (ret) {
1771 goto end;
1772 }
1773
1774 /* Write time */
1775 if (cc) {
1776 const bt_clock_snapshot *cs;
1777 bt_message_stream_clock_snapshot_state cs_state =
1778 bt_message_stream_beginning_borrow_default_clock_snapshot_const(msg, &cs);
1779
1780 if (cs_state == BT_MESSAGE_STREAM_CLOCK_SNAPSHOT_STATE_KNOWN) {
1781 write_time(ctx, cs);
1782 } else {
1783 write_time_str(ctx, "Unknown");
1784 }
1785 }
1786
1787 /* Write follow tag for message */
1788 ret = write_message_follow_tag(ctx, stream);
1789 if (ret) {
1790 goto end;
1791 }
1792
1793 /* Write stream properties */
1794 write_obj_type_name(ctx, "Stream beginning");
1795
1796 if (ctx->details_comp->cfg.compact) {
1797 write_nl(ctx);
1798 goto end;
1799 }
1800
1801 g_string_append(ctx->str, ":\n");
1802 incr_indent(ctx);
1803
1804 if (ctx->details_comp->cfg.with_stream_name) {
1805 name = bt_stream_get_name(stream);
1806 if (name) {
1807 write_str_prop_line(ctx, "Name", name);
1808 }
1809 }
1810
1811 if (ctx->details_comp->cfg.with_stream_class_name) {
1812 name = bt_stream_class_get_name(sc);
1813 if (name) {
1814 write_str_prop_line(ctx, "Class name", name);
1815 }
1816 }
1817
1818 write_trace(ctx, trace);
1819 decr_indent(ctx);
1820
1821 end:
1822 return ret;
1823 }
1824
1825 static
1826 int write_stream_end_message(struct details_write_ctx *ctx,
1827 const bt_message *msg)
1828 {
1829 int ret = 0;
1830 const bt_stream *stream =
1831 bt_message_stream_end_borrow_stream_const(msg);
1832 const bt_stream_class *sc =
1833 bt_stream_borrow_class_const(stream);
1834 const bt_clock_class *cc =
1835 bt_stream_class_borrow_default_clock_class_const(sc);
1836
1837 /* Write time */
1838 if (cc) {
1839 const bt_clock_snapshot *cs;
1840 bt_message_stream_clock_snapshot_state cs_state =
1841 bt_message_stream_end_borrow_default_clock_snapshot_const(msg, &cs);
1842
1843 if (cs_state == BT_MESSAGE_STREAM_CLOCK_SNAPSHOT_STATE_KNOWN) {
1844 write_time(ctx, cs);
1845 } else {
1846 write_time_str(ctx, "Unknown");
1847 }
1848 }
1849
1850 /* Write follow tag for message */
1851 ret = write_message_follow_tag(ctx, stream);
1852 if (ret) {
1853 goto end;
1854 }
1855
1856 /* Write stream properties */
1857 write_obj_type_name(ctx, "Stream end\n");
1858
1859 end:
1860 return ret;
1861 }
1862
1863 static
1864 int write_packet_beginning_message(struct details_write_ctx *ctx,
1865 const bt_message *msg)
1866 {
1867 int ret = 0;
1868 const bt_packet *packet =
1869 bt_message_packet_beginning_borrow_packet_const(msg);
1870 const bt_stream *stream = bt_packet_borrow_stream_const(packet);
1871 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
1872 const bt_field *field;
1873
1874 /* Write time */
1875 if (bt_stream_class_packets_have_beginning_default_clock_snapshot(sc)) {
1876 write_time(ctx,
1877 bt_message_packet_beginning_borrow_default_clock_snapshot_const(
1878 msg));
1879 }
1880
1881 /* Write follow tag for message */
1882 ret = write_message_follow_tag(ctx, stream);
1883 if (ret) {
1884 goto end;
1885 }
1886
1887 write_obj_type_name(ctx, "Packet beginning");
1888
1889 if (ctx->details_comp->cfg.compact) {
1890 write_nl(ctx);
1891 goto end;
1892 }
1893
1894 /* Write field */
1895 g_string_append(ctx->str, ":\n");
1896 incr_indent(ctx);
1897 field = bt_packet_borrow_context_field_const(packet);
1898 if (field) {
1899 write_root_field(ctx, "Context", field);
1900 }
1901
1902 decr_indent(ctx);
1903
1904 end:
1905 return ret;
1906 }
1907
1908 static
1909 int write_discarded_items_message(struct details_write_ctx *ctx,
1910 const char *name, const bt_stream *stream,
1911 const bt_clock_snapshot *beginning_cs,
1912 const bt_clock_snapshot *end_cs, uint64_t count)
1913 {
1914 int ret = 0;
1915
1916 /* Write times */
1917 if (beginning_cs) {
1918 write_time(ctx, beginning_cs);
1919 BT_ASSERT(end_cs);
1920 write_time(ctx, end_cs);
1921 }
1922
1923 /* Write follow tag for message */
1924 ret = write_message_follow_tag(ctx, stream);
1925 if (ret) {
1926 goto end;
1927 }
1928
1929 write_obj_type_name(ctx, "Discarded ");
1930 write_obj_type_name(ctx, name);
1931
1932 /* Write count */
1933 if (count == UINT64_C(-1)) {
1934 write_nl(ctx);
1935 goto end;
1936 }
1937
1938 g_string_append(ctx->str, " (");
1939 write_uint_prop_value(ctx, count);
1940 g_string_append_printf(ctx->str, " %s)\n", name);
1941
1942 end:
1943 return ret;
1944 }
1945
1946 static
1947 int write_discarded_events_message(struct details_write_ctx *ctx,
1948 const bt_message *msg)
1949 {
1950 const bt_stream *stream = bt_message_discarded_events_borrow_stream_const(
1951 msg);
1952 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
1953 const bt_clock_snapshot *beginning_cs = NULL;
1954 const bt_clock_snapshot *end_cs = NULL;
1955 uint64_t count;
1956
1957 if (bt_stream_class_discarded_events_have_default_clock_snapshots(sc)) {
1958 beginning_cs =
1959 bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const(
1960 msg);
1961 end_cs =
1962 bt_message_discarded_events_borrow_end_default_clock_snapshot_const(
1963 msg);
1964 }
1965
1966 if (bt_message_discarded_events_get_count(msg, &count) !=
1967 BT_PROPERTY_AVAILABILITY_AVAILABLE) {
1968 count = UINT64_C(-1);
1969 }
1970
1971 return write_discarded_items_message(ctx, "events", stream,
1972 beginning_cs, end_cs, count);
1973 }
1974
1975 static
1976 int write_discarded_packets_message(struct details_write_ctx *ctx,
1977 const bt_message *msg)
1978 {
1979 const bt_stream *stream = bt_message_discarded_packets_borrow_stream_const(
1980 msg);
1981 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
1982 const bt_clock_snapshot *beginning_cs = NULL;
1983 const bt_clock_snapshot *end_cs = NULL;
1984 uint64_t count;
1985
1986 if (bt_stream_class_discarded_packets_have_default_clock_snapshots(sc)) {
1987 beginning_cs =
1988 bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const(
1989 msg);
1990 end_cs =
1991 bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(
1992 msg);
1993 }
1994
1995 if (bt_message_discarded_packets_get_count(msg, &count) !=
1996 BT_PROPERTY_AVAILABILITY_AVAILABLE) {
1997 count = UINT64_C(-1);
1998 }
1999
2000 return write_discarded_items_message(ctx, "packets", stream,
2001 beginning_cs, end_cs, count);
2002 }
2003
2004 static
2005 int write_packet_end_message(struct details_write_ctx *ctx,
2006 const bt_message *msg)
2007 {
2008 int ret = 0;
2009 const bt_packet *packet =
2010 bt_message_packet_end_borrow_packet_const(msg);
2011 const bt_stream *stream = bt_packet_borrow_stream_const(packet);
2012 const bt_stream_class *sc = bt_stream_borrow_class_const(stream);
2013
2014 /* Write time */
2015 if (bt_stream_class_packets_have_end_default_clock_snapshot(sc)) {
2016 write_time(ctx,
2017 bt_message_packet_end_borrow_default_clock_snapshot_const(
2018 msg));
2019 }
2020
2021 /* Write follow tag for message */
2022 ret = write_message_follow_tag(ctx, stream);
2023 if (ret) {
2024 goto end;
2025 }
2026
2027 write_obj_type_name(ctx, "Packet end");
2028 write_nl(ctx);
2029
2030 end:
2031 return ret;
2032 }
2033
2034 static
2035 int write_message_iterator_inactivity_message(struct details_write_ctx *ctx,
2036 const bt_message *msg)
2037 {
2038 int ret = 0;
2039 const bt_clock_snapshot *cs =
2040 bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const(
2041 msg);
2042
2043 /* Write time */
2044 write_time(ctx, cs);
2045 write_obj_type_name(ctx, "Message iterator inactivity");
2046
2047 if (ctx->details_comp->cfg.compact) {
2048 write_nl(ctx);
2049 goto end;
2050 }
2051
2052 /* Write clock class properties */
2053 g_string_append(ctx->str, ":\n");
2054 incr_indent(ctx);
2055 write_indent(ctx);
2056 write_prop_name(ctx, "Clock class");
2057 g_string_append_c(ctx->str, ':');
2058 write_nl(ctx);
2059 incr_indent(ctx);
2060 write_clock_class_prop_lines(ctx,
2061 bt_clock_snapshot_borrow_clock_class_const(cs));
2062 decr_indent(ctx);
2063
2064 end:
2065 return ret;
2066 }
2067
2068 BT_HIDDEN
2069 int details_write_message(struct details_comp *details_comp,
2070 const bt_message *msg)
2071 {
2072 int ret = 0;
2073 struct details_write_ctx ctx = {
2074 .details_comp = details_comp,
2075 .str = details_comp->str,
2076 .indent_level = 0,
2077 };
2078
2079 /* Reset output buffer */
2080 g_string_assign(details_comp->str, "");
2081
2082 if (details_comp->printed_something && !details_comp->cfg.compact) {
2083 write_nl(&ctx);
2084 }
2085
2086 switch (bt_message_get_type(msg)) {
2087 case BT_MESSAGE_TYPE_EVENT:
2088 ret = write_event_message(&ctx, msg);
2089 break;
2090 case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY:
2091 ret = write_message_iterator_inactivity_message(&ctx, msg);
2092 break;
2093 case BT_MESSAGE_TYPE_STREAM_BEGINNING:
2094 ret = write_stream_beginning_message(&ctx, msg);
2095 break;
2096 case BT_MESSAGE_TYPE_STREAM_END:
2097 ret = write_stream_end_message(&ctx, msg);
2098 break;
2099 case BT_MESSAGE_TYPE_PACKET_BEGINNING:
2100 ret = write_packet_beginning_message(&ctx, msg);
2101 break;
2102 case BT_MESSAGE_TYPE_PACKET_END:
2103 ret = write_packet_end_message(&ctx, msg);
2104 break;
2105 case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
2106 ret = write_discarded_events_message(&ctx, msg);
2107 break;
2108 case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
2109 ret = write_discarded_packets_message(&ctx, msg);
2110 break;
2111 default:
2112 abort();
2113 }
2114
2115 return ret;
2116 }
This page took 0.106272 seconds and 5 git commands to generate.