From 7753db0d6dc99750df8e5c940076c7bfa826fb6b Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Wed, 14 Aug 2019 13:34:10 -0400 Subject: [PATCH] sink.text.details: print user attributes This patch makes a `sink.text.details` component print user attributes. The component only prints user attributes when the map value is not empty. The output looks like this: Event class (ID 0): User attributes: domain: Python ip: Length 4: [0]: 192 [1]: 168 [2]: 0 [3]: 100 weight: 17.231600 with-net: Yes ... The new write_value() function textually and recursively serializes a value object. When writing a map value object, the keys are sorted first. The change brought by this patch is not invasive in that all the current test expectation files do not need any change because the sources never set any user attribute. The goal of this patch is not to add any textual noise. When an enumeration, a structure, or a variant field class has user attributes, the component prints the mappings, members, and options indented within a dedicated section, for example: Payload field class: Structure (3 members): User attributes: a: 23 b: 'log' Members: hello: String bbb: Boolean opt: Option: Content: Unsigned integer (64-bit, Base 10) The same strategy applies to structure field class members and variant field class options: Payload field class: Structure (3 members): hello: Field class: String User attributes: factor: 17.150000 bbb: Boolean opt: Option: Content: Unsigned integer (64-bit, Base 10) Signed-off-by: Philippe Proulx Change-Id: Icf3060dbf23bab607ebb7af6e839aa5b28bd2658 Reviewed-on: https://review.lttng.org/c/babeltrace/+/1926 Tested-by: jenkins Reviewed-by: Francis Deslauriers --- src/plugins/text/details/write.c | 481 ++++++++++++++++++++++++------- 1 file changed, 382 insertions(+), 99 deletions(-) diff --git a/src/plugins/text/details/write.c b/src/plugins/text/details/write.c index 6f3d69d7..7c05d145 100644 --- a/src/plugins/text/details/write.c +++ b/src/plugins/text/details/write.c @@ -199,14 +199,15 @@ void write_compound_member_name(struct details_write_ctx *ctx, const char *name) } static inline -void write_array_index(struct details_write_ctx *ctx, uint64_t index) +void write_array_index(struct details_write_ctx *ctx, uint64_t index, + const char *color) { char buf[32]; write_indent(ctx); format_uint(buf, index, 10); g_string_append_printf(ctx->str, "%s[%s]%s:", - color_fg_cyan(ctx), buf, color_reset(ctx)); + color, buf, color_reset(ctx)); } static inline @@ -223,6 +224,14 @@ void write_prop_name(struct details_write_ctx *ctx, const char *prop_name) color_fg_magenta(ctx), prop_name, color_reset(ctx)); } +static inline +void write_prop_name_line(struct details_write_ctx *ctx, const char *prop_name) +{ + write_indent(ctx); + g_string_append_printf(ctx->str, "%s%s%s:", + color_fg_magenta(ctx), prop_name, color_reset(ctx)); +} + static inline void write_str_prop_value(struct details_write_ctx *ctx, const char *value) { @@ -352,6 +361,152 @@ void write_uuid_prop_line(struct details_write_ctx *ctx, const char *prop_name, color_reset(ctx)); } +static +gint compare_strings(const char **a, const char **b) +{ + return strcmp(*a, *b); +} + +static +bt_bool map_value_foreach_add_key_to_array(const char *key, + const bt_value *object, void *data) +{ + GPtrArray *keys = data; + + BT_ASSERT(keys); + g_ptr_array_add(keys, (void *) key); + return BT_TRUE; +} + +static +void write_value(struct details_write_ctx *ctx, const bt_value *value, + const char *name) +{ + uint64_t i; + bt_value_type value_type = bt_value_get_type(value); + GPtrArray *keys = g_ptr_array_new(); + char buf[64]; + + BT_ASSERT(keys); + + /* Write field's name */ + if (name) { + write_prop_name_line(ctx, name); + } + + /* Write field's value */ + switch (value_type) { + case BT_VALUE_TYPE_NULL: + write_sp(ctx); + write_none_prop_value(ctx, "Null"); + break; + case BT_VALUE_TYPE_BOOL: + write_sp(ctx); + write_bool_prop_value(ctx, bt_value_bool_get(value)); + break; + case BT_VALUE_TYPE_UNSIGNED_INTEGER: + format_uint(buf, bt_value_integer_unsigned_get(value), 10); + write_sp(ctx); + write_uint_str_prop_value(ctx, buf); + break; + case BT_VALUE_TYPE_SIGNED_INTEGER: + format_int(buf, bt_value_integer_signed_get(value), 10); + write_sp(ctx); + write_int_str_prop_value(ctx, buf); + break; + case BT_VALUE_TYPE_REAL: + write_sp(ctx); + write_float_prop_value(ctx, bt_value_real_get(value)); + break; + case BT_VALUE_TYPE_STRING: + write_sp(ctx); + write_str_prop_value(ctx, bt_value_string_get(value)); + break; + case BT_VALUE_TYPE_ARRAY: + { + uint64_t length = bt_value_array_get_size(value); + + if (length == 0) { + write_sp(ctx); + write_none_prop_value(ctx, "Empty"); + } else { + g_string_append(ctx->str, " Length "); + write_uint_prop_value(ctx, length); + g_string_append_c(ctx->str, ':'); + } + + incr_indent(ctx); + + for (i = 0; i < length; i++) { + const bt_value *elem_value = + bt_value_array_borrow_element_by_index_const( + value, i); + + write_nl(ctx); + write_array_index(ctx, i, color_fg_magenta(ctx)); + write_value(ctx, elem_value, NULL); + } + + decr_indent(ctx); + break; + } + case BT_VALUE_TYPE_MAP: + { + bt_value_map_foreach_entry_const_status foreach_status = + bt_value_map_foreach_entry_const(value, + map_value_foreach_add_key_to_array, keys); + + BT_ASSERT(foreach_status == + BT_VALUE_MAP_FOREACH_ENTRY_CONST_STATUS_OK); + g_ptr_array_sort(keys, (GCompareFunc) compare_strings); + + if (keys->len > 0) { + incr_indent(ctx); + + for (i = 0; i < keys->len; i++) { + const char *key = keys->pdata[i]; + const bt_value *entry_value = + bt_value_map_borrow_entry_value_const( + value, key); + + write_nl(ctx); + write_value(ctx, entry_value, key); + } + + decr_indent(ctx); + } else { + write_sp(ctx); + write_none_prop_value(ctx, "Empty"); + } + + break; + } + default: + abort(); + } + + g_ptr_array_free(keys, TRUE); +} + +static +void write_user_attributes(struct details_write_ctx *ctx, + const bt_value *user_attrs, bool write_newline, bool *written) +{ + BT_ASSERT(user_attrs); + + if (!bt_value_map_is_empty(user_attrs)) { + write_value(ctx, user_attrs, "User attributes"); + + if (write_newline) { + write_nl(ctx); + } + + if (written) { + *written = true; + } + } +} + static void write_int_field_class_props(struct details_write_ctx *ctx, const bt_field_class *fc, bool close) @@ -597,7 +752,7 @@ void write_enum_field_class_mappings(struct details_write_ctx *ctx, struct enum_field_class_mapping *mapping = mappings->pdata[i]; write_nl(ctx); - write_compound_member_name(ctx, mapping->label); + write_prop_name_line(ctx, mapping->label); for (range_i = 0; range_i < mapping->ranges->len; range_i++) { write_sp(ctx); @@ -672,9 +827,14 @@ void write_variant_field_class_option(struct details_write_ctx *ctx, const bt_field_class_variant_option *option = bt_field_class_variant_borrow_option_by_index_const( fc, index); - const void *orig_ranges; + const void *orig_ranges = NULL; GArray *int_ranges = NULL; bool is_signed; + const bt_value *user_attrs = + bt_field_class_variant_option_borrow_user_attributes_const( + option); + const bt_field_class *option_fc = + bt_field_class_variant_option_borrow_field_class_const(option); write_nl(ctx); write_compound_member_name(ctx, @@ -689,7 +849,7 @@ void write_variant_field_class_option(struct details_write_ctx *ctx, bt_field_class_variant_with_selector_unsigned_option_borrow_ranges_const( spec_opt); is_signed = false; - } else { + } else if (fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_SELECTOR) { const bt_field_class_variant_with_selector_signed_option *spec_opt = bt_field_class_variant_with_selector_signed_borrow_option_by_index_const( fc, index); @@ -718,53 +878,27 @@ void write_variant_field_class_option(struct details_write_ctx *ctx, write_sp(ctx); } - write_field_class(ctx, - bt_field_class_variant_option_borrow_field_class_const(option)); - - if (int_ranges) { - g_array_free(int_ranges, TRUE); - } -} - -static -void write_variant_field_class(struct details_write_ctx *ctx, - const bt_field_class *fc) -{ - bt_field_class_type fc_type = bt_field_class_get_type(fc); - uint64_t option_count = - bt_field_class_variant_get_option_count(fc); - const bt_field_path *sel_field_path = NULL; + if (bt_value_map_is_empty(user_attrs)) { + write_field_class(ctx, option_fc); + } else { + write_nl(ctx); + incr_indent(ctx); - if (fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_SELECTOR) { - sel_field_path = - bt_field_class_variant_with_selector_borrow_selector_field_path_const( - fc); - BT_ASSERT(sel_field_path); - } + /* Field class */ + write_prop_name_line(ctx, "Field class"); + write_sp(ctx); + write_field_class(ctx, option_fc); + write_nl(ctx); - g_string_append(ctx->str, " ("); - write_uint_prop_value(ctx, option_count); - g_string_append_printf(ctx->str, " option%s, ", - plural(option_count)); + /* User attributes */ + write_user_attributes(ctx, user_attrs, + false, NULL); - if (sel_field_path) { - g_string_append(ctx->str, "Selector field path "); - write_field_path(ctx, sel_field_path); + decr_indent(ctx); } - g_string_append_c(ctx->str, ')'); - - if (option_count > 0) { - uint64_t i; - - g_string_append_c(ctx->str, ':'); - incr_indent(ctx); - - for (i = 0; i < option_count; i++) { - write_variant_field_class_option(ctx, fc, i); - } - - decr_indent(ctx); + if (int_ranges) { + g_array_free(int_ranges, TRUE); } } @@ -774,6 +908,8 @@ void write_field_class(struct details_write_ctx *ctx, const bt_field_class *fc) uint64_t i; const char *type; bt_field_class_type fc_type = bt_field_class_get_type(fc); + const bt_value *user_attrs; + bool wrote_user_attrs = false; /* Write field class's type */ switch (fc_type) { @@ -829,7 +965,7 @@ void write_field_class(struct details_write_ctx *ctx, const bt_field_class *fc) g_string_append_printf(ctx->str, "%s%s%s", color_fg_blue(ctx), type, color_reset(ctx)); - /* Write field class's properties */ + /* Write field class's single-line properties */ switch (fc_type) { case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: @@ -848,14 +984,6 @@ void write_field_class(struct details_write_ctx *ctx, const bt_field_class *fc) write_uint_prop_value(ctx, mapping_count); g_string_append_printf(ctx->str, " mapping%s)", plural(mapping_count)); - - if (mapping_count > 0) { - g_string_append_c(ctx->str, ':'); - incr_indent(ctx); - write_enum_field_class_mappings(ctx, fc); - decr_indent(ctx); - } - break; } case BT_FIELD_CLASS_TYPE_REAL: @@ -875,27 +1003,6 @@ void write_field_class(struct details_write_ctx *ctx, const bt_field_class *fc) write_uint_prop_value(ctx, member_count); g_string_append_printf(ctx->str, " member%s)", plural(member_count)); - - if (member_count > 0) { - g_string_append_c(ctx->str, ':'); - incr_indent(ctx); - - for (i = 0; i < member_count; i++) { - const bt_field_class_structure_member *member = - bt_field_class_structure_borrow_member_by_index_const( - fc, i); - - write_nl(ctx); - write_compound_member_name(ctx, - bt_field_class_structure_member_get_name(member)); - write_sp(ctx); - write_field_class(ctx, - bt_field_class_structure_member_borrow_field_class_const(member)); - } - - decr_indent(ctx); - } - break; } case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: @@ -917,14 +1024,6 @@ void write_field_class(struct details_write_ctx *ctx, const bt_field_class *fc) } } - g_string_append_c(ctx->str, ':'); - write_nl(ctx); - incr_indent(ctx); - write_compound_member_name(ctx, "Element"); - write_sp(ctx); - write_field_class(ctx, - bt_field_class_array_borrow_element_field_class_const(fc)); - decr_indent(ctx); break; case BT_FIELD_CLASS_TYPE_OPTION: { @@ -937,24 +1036,197 @@ void write_field_class(struct details_write_ctx *ctx, const bt_field_class *fc) g_string_append_c(ctx->str, ')'); } - g_string_append_c(ctx->str, ':'); - write_nl(ctx); - incr_indent(ctx); - write_compound_member_name(ctx, "Content"); + break; + } + case BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR: + case BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_SELECTOR: + case BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_SELECTOR: + { + uint64_t option_count = + bt_field_class_variant_get_option_count(fc); + const bt_field_path *sel_field_path = NULL; + + if (fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_SELECTOR || + fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_SELECTOR) { + sel_field_path = + bt_field_class_variant_with_selector_borrow_selector_field_path_const( + fc); + BT_ASSERT(sel_field_path); + } + + g_string_append(ctx->str, " ("); + write_uint_prop_value(ctx, option_count); + g_string_append_printf(ctx->str, " option%s", + plural(option_count)); + + if (sel_field_path) { + g_string_append(ctx->str, ", Selector field path "); + write_field_path(ctx, sel_field_path); + } + + g_string_append_c(ctx->str, ')'); + break; + } + default: + break; + } + + incr_indent(ctx); + user_attrs = bt_field_class_borrow_user_attributes_const(fc); + if (!bt_value_map_is_empty(user_attrs)) { + g_string_append(ctx->str, ":\n"); + write_user_attributes(ctx, user_attrs, false, NULL); + wrote_user_attrs = true; + } + + /* Write field class's complex properties */ + switch (fc_type) { + case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: + case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: + { + uint64_t mapping_count = + bt_field_class_enumeration_get_mapping_count(fc); + + if (mapping_count > 0) { + if (wrote_user_attrs) { + write_nl(ctx); + write_indent(ctx); + write_prop_name(ctx, "Mappings"); + g_string_append_c(ctx->str, ':'); + incr_indent(ctx); + } else { + /* Each mapping starts with its own newline */ + g_string_append_c(ctx->str, ':'); + } + + write_enum_field_class_mappings(ctx, fc); + + if (wrote_user_attrs) { + decr_indent(ctx); + } + } + + break; + } + case BT_FIELD_CLASS_TYPE_STRUCTURE: + { + uint64_t member_count = + bt_field_class_structure_get_member_count(fc); + + if (member_count > 0) { + if (wrote_user_attrs) { + write_nl(ctx); + write_indent(ctx); + write_prop_name(ctx, "Members"); + g_string_append_c(ctx->str, ':'); + incr_indent(ctx); + } else { + /* Each member starts with its own newline */ + g_string_append_c(ctx->str, ':'); + } + + for (i = 0; i < member_count; i++) { + const bt_field_class_structure_member *member = + bt_field_class_structure_borrow_member_by_index_const( + fc, i); + const bt_value *user_attrs; + const bt_field_class *member_fc = + bt_field_class_structure_member_borrow_field_class_const(member); + + write_nl(ctx); + write_compound_member_name(ctx, + bt_field_class_structure_member_get_name(member)); + user_attrs = bt_field_class_structure_member_borrow_user_attributes_const( + member); + + if (bt_value_map_is_empty(user_attrs)) { + write_sp(ctx); + write_field_class(ctx, member_fc); + } else { + write_nl(ctx); + incr_indent(ctx); + + /* Field class */ + write_prop_name_line(ctx, "Field class"); + write_sp(ctx); + write_field_class(ctx, member_fc); + write_nl(ctx); + + /* User attributes */ + write_user_attributes(ctx, user_attrs, + false, NULL); + + decr_indent(ctx); + } + } + + if (wrote_user_attrs) { + decr_indent(ctx); + } + } + + break; + } + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + if (wrote_user_attrs) { + write_nl(ctx); + } else { + g_string_append(ctx->str, ":\n"); + } + + write_prop_name_line(ctx, "Element"); + write_sp(ctx); + write_field_class(ctx, + bt_field_class_array_borrow_element_field_class_const(fc)); + break; + case BT_FIELD_CLASS_TYPE_OPTION: + if (wrote_user_attrs) { + write_nl(ctx); + } else { + g_string_append(ctx->str, ":\n"); + } + + write_prop_name_line(ctx, "Content"); write_sp(ctx); write_field_class(ctx, bt_field_class_option_borrow_field_class_const(fc)); - decr_indent(ctx); break; - } case BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR: case BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_SELECTOR: case BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_SELECTOR: - write_variant_field_class(ctx, fc); + { + uint64_t option_count = + bt_field_class_variant_get_option_count(fc); + + if (option_count > 0) { + if (wrote_user_attrs) { + write_nl(ctx); + write_indent(ctx); + write_prop_name(ctx, "Options"); + g_string_append_c(ctx->str, ':'); + incr_indent(ctx); + } else { + /* Each option starts with its own newline */ + g_string_append_c(ctx->str, ':'); + } + + for (i = 0; i < option_count; i++) { + write_variant_field_class_option(ctx, fc, i); + } + + if (wrote_user_attrs) { + decr_indent(ctx); + } + } + break; + } default: break; } + + decr_indent(ctx); } static @@ -994,6 +1266,10 @@ void write_event_class(struct details_write_ctx *ctx, const bt_event_class *ec) /* Write properties */ incr_indent(ctx); + /* Write user attributes */ + write_user_attributes(ctx, + bt_event_class_borrow_user_attributes_const(ec), true, NULL); + /* Write log level */ if (bt_event_class_get_log_level(ec, &log_level) == BT_PROPERTY_AVAILABILITY_AVAILABLE) { @@ -1086,6 +1362,8 @@ void write_clock_class_prop_lines(struct details_write_ctx *ctx, write_str_prop_line(ctx, "Name", str); } + write_user_attributes(ctx, + bt_clock_class_borrow_user_attributes_const(cc), true, NULL); str = bt_clock_class_get_description(cc); if (str) { write_str_prop_line(ctx, "Description", str); @@ -1154,6 +1432,10 @@ void write_stream_class(struct details_write_ctx *ctx, /* Write properties */ incr_indent(ctx); + /* Write user attributes */ + write_user_attributes(ctx, + bt_stream_class_borrow_user_attributes_const(sc), true, NULL); + /* Write configuration */ write_bool_prop_line(ctx, "Supports packets", bt_stream_class_supports_packets(sc)); @@ -1241,12 +1523,6 @@ gint compare_stream_classes(const bt_stream_class **a, const bt_stream_class **b } } -static -gint compare_strings(const char **a, const char **b) -{ - return strcmp(*a, *b); -} - static void write_trace_class(struct details_write_ctx *ctx, const bt_trace_class *tc) { @@ -1257,6 +1533,7 @@ void write_trace_class(struct details_write_ctx *ctx, const bt_trace_class *tc) write_indent(ctx); write_obj_type_name(ctx, "Trace class"); + for (i = 0; i < bt_trace_class_get_stream_class_count(tc); i++) { g_ptr_array_add(stream_classes, (gpointer) bt_trace_class_borrow_stream_class_by_index_const( @@ -1274,6 +1551,12 @@ void write_trace_class(struct details_write_ctx *ctx, const bt_trace_class *tc) incr_indent(ctx); + /* Write user attributes */ + write_user_attributes(ctx, + bt_trace_class_borrow_user_attributes_const(tc), true, + &printed_prop); + + /* Write stream classes */ for (i = 0; i < stream_classes->len; i++) { write_stream_class(ctx, stream_classes->pdata[i]); } @@ -1635,7 +1918,7 @@ void write_field(struct details_write_ctx *ctx, const bt_field *field, field, i); write_nl(ctx); - write_array_index(ctx, i); + write_array_index(ctx, i, color_fg_cyan(ctx)); write_field(ctx, elem_field, NULL); } -- 2.34.1