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