From ad96d93646ede91f93d3630b7e6d655f0edf1b3b Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Fri, 10 Feb 2017 20:31:56 -0500 Subject: [PATCH] text plugin: add color support MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This patch adds terminal color support to the text plugin. The text component class accepts a new initialization parameter `color` which you can set to one of the following strings: never: Never output color codes. auto: Output color codes if the standard output is connected to a color-capable terminal and if the file stream to use is the standard output. always: Always output color codes. This is useful to print colors even if the standard output is not connected to a terminal, for example: babeltrace /path/to/trace -o text.text -p color=always | less -R The semantic of this parameter is the same as ls(1)'s and grep(1)'s --color option. The chosen color scheme is open to debate. In an ideal world, it should be configurable by the component parameters, or read $LS_COLORS. Signed-off-by: Philippe Proulx Signed-off-by: Jérémie Galarneau --- common/common.c | 42 ++++---- include/babeltrace/common-internal.h | 22 +++++ plugins/text/Makefile.am | 3 +- plugins/text/print.c | 140 +++++++++++++++++++++++---- plugins/text/text.c | 56 +++++++++++ plugins/text/text.h | 9 ++ 6 files changed, 232 insertions(+), 40 deletions(-) diff --git a/common/common.c b/common/common.c index 379b1301..1d7eea43 100644 --- a/common/common.c +++ b/common/common.c @@ -30,6 +30,7 @@ #include #include #include +#include #define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace/plugins" #define HOME_ENV_VAR "HOME" @@ -165,7 +166,8 @@ end: return ret; } -static bool supports_colors(void) +BT_HIDDEN +bool bt_common_colors_supported(void) { static bool supports_colors = false; static bool supports_colors_set = false; @@ -202,107 +204,109 @@ end: BT_HIDDEN const char *bt_common_color_reset(void) { - return supports_colors() ? "\033[0m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_RESET : ""; } BT_HIDDEN const char *bt_common_color_bold(void) { - return supports_colors() ? "\033[1m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_BOLD : ""; } BT_HIDDEN const char *bt_common_color_fg_default(void) { - return supports_colors() ? "\033[39m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_DEFAULT : ""; } BT_HIDDEN const char *bt_common_color_fg_red(void) { - return supports_colors() ? "\033[31m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_RED : ""; } BT_HIDDEN const char *bt_common_color_fg_green(void) { - return supports_colors() ? "\033[32m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_GREEN : ""; } BT_HIDDEN const char *bt_common_color_fg_yellow(void) { - return supports_colors() ? "\033[33m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_YELLOW : ""; } BT_HIDDEN const char *bt_common_color_fg_blue(void) { - return supports_colors() ? "\033[34m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_BLUE : ""; } BT_HIDDEN const char *bt_common_color_fg_magenta(void) { - return supports_colors() ? "\033[35m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_MAGENTA : ""; } BT_HIDDEN const char *bt_common_color_fg_cyan(void) { - return supports_colors() ? "\033[36m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_CYAN : ""; } BT_HIDDEN const char *bt_common_color_fg_light_gray(void) { - return supports_colors() ? "\033[37m" : ""; + return bt_common_colors_supported() ? + BT_COMMON_COLOR_FG_LIGHT_GRAY : ""; } BT_HIDDEN const char *bt_common_color_bg_default(void) { - return supports_colors() ? "\033[49m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_DEFAULT : ""; } BT_HIDDEN const char *bt_common_color_bg_red(void) { - return supports_colors() ? "\033[41m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_RED : ""; } BT_HIDDEN const char *bt_common_color_bg_green(void) { - return supports_colors() ? "\033[42m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_GREEN : ""; } BT_HIDDEN const char *bt_common_color_bg_yellow(void) { - return supports_colors() ? "\033[43m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_YELLOW : ""; } BT_HIDDEN const char *bt_common_color_bg_blue(void) { - return supports_colors() ? "\033[44m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_BLUE : ""; } BT_HIDDEN const char *bt_common_color_bg_magenta(void) { - return supports_colors() ? "\033[45m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_MAGENTA : ""; } BT_HIDDEN const char *bt_common_color_bg_cyan(void) { - return supports_colors() ? "\033[46m" : ""; + return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_CYAN : ""; } BT_HIDDEN const char *bt_common_color_bg_light_gray(void) { - return supports_colors() ? "\033[47m" : ""; + return bt_common_colors_supported() ? + BT_COMMON_COLOR_BG_LIGHT_GRAY : ""; } diff --git a/include/babeltrace/common-internal.h b/include/babeltrace/common-internal.h index 1beb9d9b..e4825aec 100644 --- a/include/babeltrace/common-internal.h +++ b/include/babeltrace/common-internal.h @@ -3,6 +3,25 @@ #include +#define BT_COMMON_COLOR_RESET "\033[0m" +#define BT_COMMON_COLOR_BOLD "\033[1m" +#define BT_COMMON_COLOR_FG_DEFAULT "\033[39m" +#define BT_COMMON_COLOR_FG_RED "\033[31m" +#define BT_COMMON_COLOR_FG_GREEN "\033[32m" +#define BT_COMMON_COLOR_FG_YELLOW "\033[33m" +#define BT_COMMON_COLOR_FG_BLUE "\033[34m" +#define BT_COMMON_COLOR_FG_MAGENTA "\033[35m" +#define BT_COMMON_COLOR_FG_CYAN "\033[36m" +#define BT_COMMON_COLOR_FG_LIGHT_GRAY "\033[37m" +#define BT_COMMON_COLOR_BG_DEFAULT "\033[49m" +#define BT_COMMON_COLOR_BG_RED "\033[41m" +#define BT_COMMON_COLOR_BG_GREEN "\033[42m" +#define BT_COMMON_COLOR_BG_YELLOW "\033[43m" +#define BT_COMMON_COLOR_BG_BLUE "\033[44m" +#define BT_COMMON_COLOR_BG_MAGENTA "\033[45m" +#define BT_COMMON_COLOR_BG_CYAN "\033[46m" +#define BT_COMMON_COLOR_BG_LIGHT_GRAY "\033[47m" + BT_HIDDEN bool bt_common_is_setuid_setgid(void); @@ -15,6 +34,9 @@ char *bt_common_get_home_plugin_path(void); BT_HIDDEN int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs); +BT_HIDDEN +bool bt_common_colors_supported(void); + BT_HIDDEN const char *bt_common_color_reset(void); diff --git a/plugins/text/Makefile.am b/plugins/text/Makefile.am index 943fc22c..0abdfb63 100644 --- a/plugins/text/Makefile.am +++ b/plugins/text/Makefile.am @@ -16,4 +16,5 @@ libbabeltrace_plugin_ctf_text_la_LDFLAGS = \ libbabeltrace_plugin_ctf_text_la_LIBADD = \ $(top_builddir)/lib/libbabeltrace.la \ - $(top_builddir)/formats/ctf/libbabeltrace-ctf.la + $(top_builddir)/formats/ctf/libbabeltrace-ctf.la \ + $(top_builddir)/common/libbabeltrace-common.la diff --git a/plugins/text/print.c b/plugins/text/print.c index 255dc63a..958b94d8 100644 --- a/plugins/text/print.c +++ b/plugins/text/print.c @@ -37,11 +37,22 @@ #include #include #include +#include #include #include "text.h" #define NSEC_PER_SEC 1000000000LL +#define COLOR_NAME BT_COMMON_COLOR_BOLD +#define COLOR_FIELD_NAME BT_COMMON_COLOR_FG_CYAN +#define COLOR_RST BT_COMMON_COLOR_RESET +#define COLOR_STRING_VALUE BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_GREEN +#define COLOR_NUMBER_VALUE BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_RED +#define COLOR_ENUM_MAPPING_NAME BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_CYAN +#define COLOR_UNKNOWN BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_RED +#define COLOR_EVENT_NAME BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_MAGENTA +#define COLOR_TIMESTAMP BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_YELLOW + static inline const char *rem_(const char *str) { @@ -60,6 +71,27 @@ static enum bt_component_status print_field(struct text_component *text, struct bt_ctf_field *field, bool print_names); +static +void print_name_equal(struct text_component *text, const char *name) +{ + if (text->use_colors) { + fprintf(text->out, "%s%s%s = ", COLOR_NAME, name, COLOR_RST); + } else { + fputs(name, text->out); + } +} + +static +void print_field_name_equal(struct text_component *text, const char *name) +{ + if (text->use_colors) { + fprintf(text->out, "%s%s%s = ", COLOR_FIELD_NAME, name, + COLOR_RST); + } else { + fputs(name, text->out); + } +} + static void print_timestamp_cycles(struct text_component *text, struct bt_ctf_clock_class *clock_class, @@ -232,21 +264,33 @@ enum bt_component_status print_event_timestamp(struct text_component *text, goto end; } - fputs(print_names ? "timestamp = " : "[", out); + if (print_names) { + print_name_equal(text, "timestamp"); + } else { + fputs("[", out); + } + if (text->use_colors) { + fputs(COLOR_TIMESTAMP, text->out); + } if (text->options.print_timestamp_cycles) { print_timestamp_cycles(text, clock_class, event); } else { print_timestamp_wall(text, clock_class, event); } + if (text->use_colors) { + fputs(COLOR_RST, text->out); + } if (!print_names) fputs("] ", out); if (text->options.print_delta_field) { - if (print_names) - fputs(", delta = ", text->out); - else + if (print_names) { + fputs(", ", text->out); + print_name_equal(text, "delta"); + } else { fputs("(", text->out); + } if (text->options.print_timestamp_cycles) { if (text->delta_cycles == -1ULL) { fputs("+??????????\?\?) ", text->out); /* Not a trigraph. */ @@ -323,7 +367,7 @@ enum bt_component_status print_event_header(struct text_component *text, } text->start_line = false; if (print_names) { - fputs("trace = ", text->out); + print_name_equal(text, "trace"); } fprintf(text->out, "%s", name); } @@ -341,7 +385,7 @@ enum bt_component_status print_event_header(struct text_component *text, } text->start_line = false; if (print_names) { - fputs("trace:hostname = ", text->out); + print_name_equal(text, "trace:hostname"); } if (bt_value_string_get(hostname_str, &str) == BT_VALUE_STATUS_OK) { @@ -363,7 +407,7 @@ enum bt_component_status print_event_header(struct text_component *text, } text->start_line = false; if (print_names) { - fputs("trace:domain = ", text->out); + print_name_equal(text, "trace:domain"); } if (bt_value_string_get(domain_str, &str) == BT_VALUE_STATUS_OK) { @@ -385,7 +429,7 @@ enum bt_component_status print_event_header(struct text_component *text, } text->start_line = false; if (print_names) { - fputs("trace:procname = ", text->out); + print_name_equal(text, "trace:procname"); } if (bt_value_string_get(procname_str, &str) == BT_VALUE_STATUS_OK) { @@ -407,7 +451,7 @@ enum bt_component_status print_event_header(struct text_component *text, } text->start_line = false; if (print_names) { - fputs("trace:vpid = ", text->out); + print_name_equal(text, "trace:vpid"); } if (bt_value_integer_get(vpid_value, &value) == BT_VALUE_STATUS_OK) { @@ -431,7 +475,7 @@ enum bt_component_status print_event_header(struct text_component *text, } text->start_line = false; if (print_names) { - fputs("loglevel = ", text->out); + print_name_equal(text, "loglevel"); } if (loglevel_str) { const char *str; @@ -466,7 +510,7 @@ enum bt_component_status print_event_header(struct text_component *text, } text->start_line = false; if (print_names) { - fputs("model.emf.uri = ", text->out); + print_name_equal(text, "model.emf.uri"); } if (uri_str) { const char *str; @@ -484,9 +528,15 @@ enum bt_component_status print_event_header(struct text_component *text, } text->start_line = false; if (print_names) { - fputs("name = ", text->out); + print_name_equal(text, "name"); + } + if (text->use_colors) { + fputs(COLOR_EVENT_NAME, text->out); } fputs(bt_ctf_event_class_get_name(event_class), text->out); + if (text->use_colors) { + fputs(COLOR_RST, text->out); + } end: bt_put(trace_class); bt_put(stream_class); @@ -507,6 +557,7 @@ enum bt_component_status print_integer(struct text_component *text, uint64_t u; int64_t s; } v; + bool rst_color = false; field_type = bt_ctf_field_get_type(field); if (!field_type) { @@ -544,6 +595,11 @@ enum bt_component_status print_integer(struct text_component *text, goto end; } + if (text->use_colors) { + fputs(COLOR_NUMBER_VALUE, text->out); + rst_color = true; + } + base = bt_ctf_field_type_integer_get_base(field_type); switch (base) { case BT_CTF_INTEGER_BASE_BINARY: @@ -617,6 +673,9 @@ enum bt_component_status print_integer(struct text_component *text, goto end; } end: + if (rst_color) { + fputs(COLOR_RST, text->out); + } bt_put(field_type); return ret; } @@ -689,13 +748,26 @@ enum bt_component_status print_enum(struct text_component *text, } if (nr_mappings++) fprintf(text->out, ", "); + if (text->use_colors) { + fputs(COLOR_ENUM_MAPPING_NAME, text->out); + } + // TODO: escape string fprintf(text->out, "\"%s\"", mapping_name); + if (text->use_colors) { + fputs(COLOR_RST, text->out); + } if (bt_ctf_field_type_enumeration_mapping_iterator_next(iter) < 0) { break; } } if (!nr_mappings) { + if (text->use_colors) { + fputs(COLOR_UNKNOWN, text->out); + } fprintf(text->out, ""); + if (text->use_colors) { + fputs(COLOR_RST, text->out); + } } fprintf(text->out, " : container = "); ret = print_integer(text, container_field); @@ -739,7 +811,7 @@ enum bt_component_status print_struct_field(struct text_component *text, fprintf(text->out, " "); } if (print_names) { - fprintf(text->out, "%s = ", rem_(field_name)); + print_field_name_equal(text, rem_(field_name)); } ret = print_field(text, field, print_names); end: @@ -874,7 +946,14 @@ enum bt_component_status print_array(struct text_component *text, text->depth--; if (is_string) { + if (text->use_colors) { + fputs(COLOR_STRING_VALUE, text->out); + } + // TODO: escape string fprintf(text->out, "\"%s\"", text->string->str); + if (text->use_colors) { + fputs(COLOR_RST, text->out); + } } else { fprintf(text->out, " ]"); } @@ -984,7 +1063,14 @@ enum bt_component_status print_sequence(struct text_component *text, text->depth--; if (is_string) { + if (text->use_colors) { + fputs(COLOR_STRING_VALUE, text->out); + } + // TODO: escape string fprintf(text->out, "\"%s\"", text->string->str); + if (text->use_colors) { + fputs(COLOR_RST, text->out); + } } else { fprintf(text->out, " ]"); } @@ -1037,7 +1123,7 @@ enum bt_component_status print_variant(struct text_component *text, ret = BT_COMPONENT_STATUS_ERROR; goto end; } - fprintf(text->out, "%s = ", rem_(tag_choice)); + print_field_name_equal(text, rem_(tag_choice)); bt_put(tag_field); bt_put(iter); } @@ -1069,13 +1155,27 @@ enum bt_component_status print_field(struct text_component *text, if (bt_ctf_field_floating_point_get_value(field, &v)) { return BT_COMPONENT_STATUS_ERROR; } + if (text->use_colors) { + fputs(COLOR_NUMBER_VALUE, text->out); + } fprintf(text->out, "%g", v); + if (text->use_colors) { + fputs(COLOR_RST, text->out); + } return BT_COMPONENT_STATUS_OK; } case CTF_TYPE_ENUM: return print_enum(text, field); case CTF_TYPE_STRING: - fprintf(text->out, "\"%s\"", bt_ctf_field_string_get_value(field)); + if (text->use_colors) { + fputs(COLOR_STRING_VALUE, text->out); + } + // TODO: escape the string value + fprintf(text->out, "\"%s\"", + bt_ctf_field_string_get_value(field)); + if (text->use_colors) { + fputs(COLOR_RST, text->out); + } return BT_COMPONENT_STATUS_OK; case CTF_TYPE_STRUCT: return print_struct(text, field, print_names); @@ -1114,7 +1214,7 @@ enum bt_component_status print_stream_packet_context(struct text_component *text } text->start_line = false; if (text->options.print_scope_field_names) { - fputs("stream.packet.context = ", text->out); + print_name_equal(text, "stream.packet.context"); } ret = print_field(text, main_field, text->options.print_context_field_names); @@ -1140,7 +1240,7 @@ enum bt_component_status print_event_header_raw(struct text_component *text, } text->start_line = false; if (text->options.print_scope_field_names) { - fputs("stream.event.header = ", text->out); + print_name_equal(text, "stream.event.header"); } ret = print_field(text, main_field, text->options.print_header_field_names); @@ -1165,7 +1265,7 @@ enum bt_component_status print_stream_event_context(struct text_component *text, } text->start_line = false; if (text->options.print_scope_field_names) { - fputs("stream.event.context = ", text->out); + print_name_equal(text, "stream.event.context"); } ret = print_field(text, main_field, text->options.print_context_field_names); @@ -1190,7 +1290,7 @@ enum bt_component_status print_event_context(struct text_component *text, } text->start_line = false; if (text->options.print_scope_field_names) { - fputs("event.context = ", text->out); + print_name_equal(text, "event.context"); } ret = print_field(text, main_field, text->options.print_context_field_names); @@ -1215,7 +1315,7 @@ enum bt_component_status print_event_payload(struct text_component *text, } text->start_line = false; if (text->options.print_scope_field_names) { - fputs("event.fields = ", text->out); + print_name_equal(text, "event.fields"); } ret = print_field(text, main_field, text->options.print_payload_field_names); diff --git a/plugins/text/text.c b/plugins/text/text.c index 78ddd4c5..163f3c2c 100644 --- a/plugins/text/text.c +++ b/plugins/text/text.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,7 @@ static const char *plugin_options[] = { + "color", "output-path", "debug-info-dir", "debug-info-target-prefix", @@ -289,6 +291,13 @@ end: return ret; } +static +void warn_wrong_color_param(struct text_component *text) +{ + fprintf(text->err, + "[warning] Accepted values for the \"color\" parameter are:\n \"always\", \"auto\", \"never\"\n"); +} + static enum bt_component_status apply_params(struct text_component *text, struct bt_value *params) @@ -317,6 +326,34 @@ enum bt_component_status apply_params(struct text_component *text, goto end; } /* Known parameters. */ + text->options.color = TEXT_COLOR_OPT_AUTO; + if (bt_value_map_has_key(params, "color")) { + struct bt_value *color_value; + const char *color; + + color_value = bt_value_map_get(params, "color"); + if (!color_value) { + goto end; + } + + ret = bt_value_string_get(color_value, &color); + if (ret) { + warn_wrong_color_param(text); + } else { + if (strcmp(color, "never") == 0) { + text->options.color = TEXT_COLOR_OPT_NEVER; + } else if (strcmp(color, "auto") == 0) { + text->options.color = TEXT_COLOR_OPT_AUTO; + } else if (strcmp(color, "always") == 0) { + text->options.color = TEXT_COLOR_OPT_ALWAYS; + } else { + warn_wrong_color_param(text); + } + } + + bt_put(color_value); + } + ret = apply_one_string("output-path", params, &text->options.output_path); @@ -603,6 +640,23 @@ end: return ret; } +static +void set_use_colors(struct text_component *text) +{ + switch (text->options.color) { + case TEXT_COLOR_OPT_ALWAYS: + text->use_colors = true; + break; + case TEXT_COLOR_OPT_AUTO: + text->use_colors = text->out == stdout && + bt_common_colors_supported(); + break; + case TEXT_COLOR_OPT_NEVER: + text->use_colors = false; + break; + } +} + static enum bt_component_status text_component_init( struct bt_component *component, struct bt_value *params, @@ -630,6 +684,8 @@ enum bt_component_status text_component_init( goto error; } + set_use_colors(text); + ret = bt_component_set_private_data(component, text); if (ret != BT_COMPONENT_STATUS_OK) { goto error; diff --git a/plugins/text/text.h b/plugins/text/text.h index 9f1706e4..3816fb19 100644 --- a/plugins/text/text.h +++ b/plugins/text/text.h @@ -37,6 +37,12 @@ enum text_default { TEXT_DEFAULT_HIDE, }; +enum text_color_option { + TEXT_COLOR_OPT_NEVER, + TEXT_COLOR_OPT_AUTO, + TEXT_COLOR_OPT_ALWAYS, +}; + struct text_options { char *output_path; char *debug_info_dir; @@ -64,6 +70,8 @@ struct text_options { bool clock_date; bool clock_gmt; bool debug_info_full_path; + + enum text_color_option color; }; struct text_component { @@ -74,6 +82,7 @@ struct text_component { bool start_line; GString *string; struct bt_value *plugin_opt_map; /* Temporary parameter map. */ + bool use_colors; uint64_t last_cycles_timestamp; uint64_t delta_cycles; -- 2.34.1