Visibility hidden by default
[babeltrace.git] / src / plugins / text / pretty / pretty.c
1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
5 * Copyright 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6 */
7
8 #define BT_COMP_LOG_SELF_COMP (pretty->self_comp)
9 #define BT_LOG_OUTPUT_LEVEL (pretty->log_level)
10 #define BT_LOG_TAG "PLUGIN/SINK.TEXT.PRETTY"
11 #include "logging/comp-logging.h"
12
13 #include <babeltrace2/babeltrace.h>
14 #include "compat/compiler.h"
15 #include "common/common.h"
16 #include <stdio.h>
17 #include <stdbool.h>
18 #include <glib.h>
19 #include <string.h>
20 #include "common/assert.h"
21 #include "plugins/common/param-validation/param-validation.h"
22
23 #include "pretty.h"
24
25 static
26 const char * const in_port_name = "in";
27
28 static
29 void destroy_pretty_data(struct pretty_component *pretty)
30 {
31 uint64_t i;
32 if (!pretty) {
33 goto end;
34 }
35
36 bt_message_iterator_put_ref(pretty->iterator);
37
38 if (pretty->string) {
39 (void) g_string_free(pretty->string, TRUE);
40 }
41
42 if (pretty->tmp_string) {
43 (void) g_string_free(pretty->tmp_string, TRUE);
44 }
45
46 if (pretty->out != stdout) {
47 int ret;
48
49 ret = fclose(pretty->out);
50 if (ret) {
51 perror("close output file");
52 }
53 }
54
55 for (i = 0; i < ENUMERATION_MAX_BITFLAGS_COUNT; i++) {
56 if (pretty->enum_bit_labels[i]) {
57 g_ptr_array_free(pretty->enum_bit_labels[i], true);
58 }
59 }
60
61 g_free(pretty->options.output_path);
62 g_free(pretty);
63
64 end:
65 return;
66 }
67
68 static
69 struct pretty_component *create_pretty(void)
70 {
71 struct pretty_component *pretty;
72
73 pretty = g_new0(struct pretty_component, 1);
74 if (!pretty) {
75 goto end;
76 }
77 pretty->string = g_string_new("");
78 if (!pretty->string) {
79 goto error;
80 }
81 pretty->tmp_string = g_string_new("");
82 if (!pretty->tmp_string) {
83 goto error;
84 }
85 end:
86 return pretty;
87
88 error:
89 g_free(pretty);
90 return NULL;
91 }
92
93 void pretty_finalize(bt_self_component_sink *comp)
94 {
95 destroy_pretty_data(
96 bt_self_component_get_data(
97 bt_self_component_sink_as_self_component(comp)));
98 }
99
100 static
101 bt_message_iterator_class_next_method_status handle_message(
102 struct pretty_component *pretty,
103 const bt_message *message)
104 {
105 bt_message_iterator_class_next_method_status ret =
106 BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
107
108 BT_ASSERT_DBG(pretty);
109
110 switch (bt_message_get_type(message)) {
111 case BT_MESSAGE_TYPE_EVENT:
112 if (pretty_print_event(pretty, message)) {
113 BT_COMP_LOGE_APPEND_CAUSE(pretty->self_comp,
114 "Failed to print one event.");
115 ret = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
116 }
117 break;
118 case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
119 case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
120 if (pretty_print_discarded_items(pretty, message)) {
121 BT_COMP_LOGE_APPEND_CAUSE(pretty->self_comp,
122 "Failed to print discarded items.");
123 ret = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
124 }
125 break;
126 default:
127 break;
128 }
129
130 return ret;
131 }
132
133 bt_component_class_sink_graph_is_configured_method_status
134 pretty_graph_is_configured(bt_self_component_sink *self_comp_sink)
135 {
136 bt_component_class_sink_graph_is_configured_method_status status;
137 bt_message_iterator_create_from_sink_component_status
138 msg_iter_status;
139 struct pretty_component *pretty;
140 bt_self_component *self_comp =
141 bt_self_component_sink_as_self_component(self_comp_sink);
142 bt_self_component_port_input *in_port;
143
144 pretty = bt_self_component_get_data(self_comp);
145 BT_ASSERT(pretty);
146 BT_ASSERT(!pretty->iterator);
147
148 in_port = bt_self_component_sink_borrow_input_port_by_name(self_comp_sink,
149 in_port_name);
150 if (!bt_port_is_connected(bt_port_input_as_port_const(
151 bt_self_component_port_input_as_port_input(in_port)))) {
152 BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Single input port is not connected: "
153 "port-name=\"%s\"", in_port_name);
154 status = BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_ERROR;
155 goto end;
156 }
157
158 msg_iter_status = bt_message_iterator_create_from_sink_component(
159 self_comp_sink, in_port, &pretty->iterator);
160 if (msg_iter_status != BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_OK) {
161 status = (int) msg_iter_status;
162 goto end;
163 }
164
165 status = BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK;
166
167 end:
168 return status;
169 }
170
171 bt_component_class_sink_consume_method_status pretty_consume(
172 bt_self_component_sink *comp)
173 {
174 bt_component_class_sink_consume_method_status status;
175 bt_message_array_const msgs;
176 bt_message_iterator *it;
177 struct pretty_component *pretty = bt_self_component_get_data(
178 bt_self_component_sink_as_self_component(comp));
179 bt_message_iterator_next_status next_status;
180 uint64_t count = 0;
181 uint64_t i = 0;
182
183 it = pretty->iterator;
184 next_status = bt_message_iterator_next(it,
185 &msgs, &count);
186 if (next_status != BT_MESSAGE_ITERATOR_NEXT_STATUS_OK) {
187 status = (int) next_status;
188 goto end;
189 }
190
191 for (i = 0; i < count; i++) {
192 status = (int) handle_message(pretty, msgs[i]);
193 if (status) {
194 goto end;
195 }
196
197 bt_message_put_ref(msgs[i]);
198 }
199
200 status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
201
202 end:
203 for (; i < count; i++) {
204 bt_message_put_ref(msgs[i]);
205 }
206
207 return status;
208 }
209
210 static
211 void apply_one_string(const char *key, const bt_value *params, char **option)
212 {
213 const bt_value *value = NULL;
214 const char *str;
215
216 value = bt_value_map_borrow_entry_value_const(params, key);
217 if (!value) {
218 goto end;
219 }
220
221 str = bt_value_string_get(value);
222 *option = g_strdup(str);
223
224 end:
225 return;
226 }
227
228 /*
229 * Apply parameter with key `key` to `option`. Use `def` as the value, if
230 * the parameter is not specified.
231 */
232
233 static
234 void apply_one_bool_with_default(const char *key, const bt_value *params,
235 bool *option, bool def)
236 {
237 const bt_value *value;
238
239 value = bt_value_map_borrow_entry_value_const(params, key);
240 if (value) {
241 bt_bool bool_val = bt_value_bool_get(value);
242
243 *option = (bool) bool_val;
244 } else {
245 *option = def;
246 }
247 }
248
249 static
250 void apply_one_bool_if_specified(const char *key, const bt_value *params, bool *option)
251 {
252 const bt_value *value;
253
254 value = bt_value_map_borrow_entry_value_const(params, key);
255 if (value) {
256 bt_bool bool_val = bt_value_bool_get(value);
257
258 *option = (bool) bool_val;
259 }
260 }
261
262 static
263 int open_output_file(struct pretty_component *pretty)
264 {
265 int ret = 0;
266
267 if (!pretty->options.output_path) {
268 goto end;
269 }
270
271 pretty->out = fopen(pretty->options.output_path, "w");
272 if (!pretty->out) {
273 goto error;
274 }
275
276 goto end;
277
278 error:
279 ret = -1;
280
281 end:
282 return ret;
283 }
284
285 static const char *color_choices[] = { "never", "auto", "always", NULL };
286 static const char *show_hide_choices[] = { "show", "hide", NULL };
287
288 static
289 struct bt_param_validation_map_value_entry_descr pretty_params[] = {
290 { "color", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { BT_VALUE_TYPE_STRING, .string = {
291 .choices = color_choices,
292 } } },
293 { "path", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_STRING } },
294 { "no-delta", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
295 { "clock-cycles", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
296 { "clock-seconds", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
297 { "clock-date", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
298 { "clock-gmt", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
299 { "verbose", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
300
301 { "name-default", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { BT_VALUE_TYPE_STRING, .string = {
302 .choices = show_hide_choices,
303 } } },
304 { "name-payload", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
305 { "name-context", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
306 { "name-scope", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
307 { "name-header", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
308
309 { "field-default", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { BT_VALUE_TYPE_STRING, .string = {
310 .choices = show_hide_choices,
311 } } },
312 { "field-trace", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
313 { "field-trace:hostname", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
314 { "field-trace:domain", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
315 { "field-trace:procname", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
316 { "field-trace:vpid", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
317 { "field-loglevel", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
318 { "field-emf", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
319 { "field-callsite", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
320 { "print-enum-flags", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
321 BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
322 };
323
324 static
325 bt_component_class_initialize_method_status apply_params(
326 struct pretty_component *pretty, const bt_value *params)
327 {
328 int ret;
329 const bt_value *value;
330 bt_component_class_initialize_method_status status;
331 enum bt_param_validation_status validation_status;
332 gchar *validate_error = NULL;
333
334 validation_status = bt_param_validation_validate(params,
335 pretty_params, &validate_error);
336 if (validation_status == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
337 status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
338 goto end;
339 } else if (validation_status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
340 status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
341 BT_COMP_LOGE_APPEND_CAUSE(pretty->self_comp, "%s", validate_error);
342 goto end;
343 }
344
345 /* Known parameters. */
346 pretty->options.color = PRETTY_COLOR_OPT_AUTO;
347 value = bt_value_map_borrow_entry_value_const(params, "color");
348 if (value) {
349 const char *color = bt_value_string_get(value);
350
351 if (strcmp(color, "never") == 0) {
352 pretty->options.color = PRETTY_COLOR_OPT_NEVER;
353 } else if (strcmp(color, "auto") == 0) {
354 pretty->options.color = PRETTY_COLOR_OPT_AUTO;
355 } else {
356 BT_ASSERT(strcmp(color, "always") == 0);
357 pretty->options.color = PRETTY_COLOR_OPT_ALWAYS;
358 }
359 }
360
361 apply_one_string("path", params, &pretty->options.output_path);
362 ret = open_output_file(pretty);
363 if (ret) {
364 BT_COMP_LOGE_APPEND_CAUSE(pretty->self_comp,
365 "Failed to open output file: %s", validate_error);
366 status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
367 goto end;
368 }
369
370 apply_one_bool_with_default("no-delta", params,
371 &pretty->options.print_delta_field, false);
372 /* Reverse logic. */
373 pretty->options.print_delta_field = !pretty->options.print_delta_field;
374
375 apply_one_bool_with_default("clock-cycles", params,
376 &pretty->options.print_timestamp_cycles, false);
377
378 apply_one_bool_with_default("clock-seconds", params,
379 &pretty->options.clock_seconds , false);
380
381 apply_one_bool_with_default("clock-date", params,
382 &pretty->options.clock_date, false);
383
384 apply_one_bool_with_default("clock-gmt", params,
385 &pretty->options.clock_gmt, false);
386
387 apply_one_bool_with_default("verbose", params,
388 &pretty->options.verbose, false);
389
390 apply_one_bool_with_default("print-enum-flags", params,
391 &pretty->options.print_enum_flags, false);
392
393 /* Names. */
394 value = bt_value_map_borrow_entry_value_const(params, "name-default");
395 if (value) {
396 const char *str = bt_value_string_get(value);
397
398 if (strcmp(str, "show") == 0) {
399 pretty->options.name_default = PRETTY_DEFAULT_SHOW;
400 } else {
401 BT_ASSERT(strcmp(str, "hide") == 0);
402 pretty->options.name_default = PRETTY_DEFAULT_HIDE;
403 }
404 } else {
405 pretty->options.name_default = PRETTY_DEFAULT_UNSET;
406 }
407
408 switch (pretty->options.name_default) {
409 case PRETTY_DEFAULT_UNSET:
410 pretty->options.print_payload_field_names = true;
411 pretty->options.print_context_field_names = true;
412 pretty->options.print_header_field_names = false;
413 pretty->options.print_scope_field_names = false;
414 break;
415 case PRETTY_DEFAULT_SHOW:
416 pretty->options.print_payload_field_names = true;
417 pretty->options.print_context_field_names = true;
418 pretty->options.print_header_field_names = true;
419 pretty->options.print_scope_field_names = true;
420 break;
421 case PRETTY_DEFAULT_HIDE:
422 pretty->options.print_payload_field_names = false;
423 pretty->options.print_context_field_names = false;
424 pretty->options.print_header_field_names = false;
425 pretty->options.print_scope_field_names = false;
426 break;
427 default:
428 bt_common_abort();
429 }
430
431 apply_one_bool_if_specified("name-payload", params,
432 &pretty->options.print_payload_field_names);
433
434 apply_one_bool_if_specified("name-context", params,
435 &pretty->options.print_context_field_names);
436
437 apply_one_bool_if_specified("name-header", params,
438 &pretty->options.print_header_field_names);
439
440 apply_one_bool_if_specified("name-scope", params,
441 &pretty->options.print_scope_field_names);
442
443 /* Fields. */
444 value = bt_value_map_borrow_entry_value_const(params, "field-default");
445 if (value) {
446 const char *str = bt_value_string_get(value);
447
448 if (strcmp(str, "show") == 0) {
449 pretty->options.field_default = PRETTY_DEFAULT_SHOW;
450 } else {
451 BT_ASSERT(strcmp(str, "hide") == 0);
452 pretty->options.field_default = PRETTY_DEFAULT_HIDE;
453 }
454 } else {
455 pretty->options.field_default = PRETTY_DEFAULT_UNSET;
456 }
457
458 switch (pretty->options.field_default) {
459 case PRETTY_DEFAULT_UNSET:
460 pretty->options.print_trace_field = false;
461 pretty->options.print_trace_hostname_field = true;
462 pretty->options.print_trace_domain_field = false;
463 pretty->options.print_trace_procname_field = true;
464 pretty->options.print_trace_vpid_field = true;
465 pretty->options.print_loglevel_field = false;
466 pretty->options.print_emf_field = false;
467 pretty->options.print_callsite_field = false;
468 break;
469 case PRETTY_DEFAULT_SHOW:
470 pretty->options.print_trace_field = true;
471 pretty->options.print_trace_hostname_field = true;
472 pretty->options.print_trace_domain_field = true;
473 pretty->options.print_trace_procname_field = true;
474 pretty->options.print_trace_vpid_field = true;
475 pretty->options.print_loglevel_field = true;
476 pretty->options.print_emf_field = true;
477 pretty->options.print_callsite_field = true;
478 break;
479 case PRETTY_DEFAULT_HIDE:
480 pretty->options.print_trace_field = false;
481 pretty->options.print_trace_hostname_field = false;
482 pretty->options.print_trace_domain_field = false;
483 pretty->options.print_trace_procname_field = false;
484 pretty->options.print_trace_vpid_field = false;
485 pretty->options.print_loglevel_field = false;
486 pretty->options.print_emf_field = false;
487 pretty->options.print_callsite_field = false;
488 break;
489 default:
490 bt_common_abort();
491 }
492
493 apply_one_bool_if_specified("field-trace", params,
494 &pretty->options.print_trace_field);
495
496 apply_one_bool_if_specified("field-trace:hostname", params,
497 &pretty->options.print_trace_hostname_field);
498
499 apply_one_bool_if_specified("field-trace:domain", params,
500 &pretty->options.print_trace_domain_field);
501
502 apply_one_bool_if_specified("field-trace:procname", params,
503 &pretty->options.print_trace_procname_field);
504
505 apply_one_bool_if_specified("field-trace:vpid", params,
506 &pretty->options.print_trace_vpid_field);
507
508 apply_one_bool_if_specified("field-loglevel", params,
509 &pretty->options.print_loglevel_field);
510
511 apply_one_bool_if_specified("field-emf", params,
512 &pretty->options.print_emf_field);
513
514 apply_one_bool_if_specified("field-callsite", params,
515 &pretty->options.print_callsite_field);
516
517 pretty_print_init();
518 status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
519
520 end:
521 g_free(validate_error);
522
523 return status;
524 }
525
526 static
527 void set_use_colors(struct pretty_component *pretty)
528 {
529 switch (pretty->options.color) {
530 case PRETTY_COLOR_OPT_ALWAYS:
531 pretty->use_colors = true;
532 break;
533 case PRETTY_COLOR_OPT_AUTO:
534 pretty->use_colors = pretty->out == stdout &&
535 bt_common_colors_supported();
536 break;
537 case PRETTY_COLOR_OPT_NEVER:
538 pretty->use_colors = false;
539 break;
540 }
541 }
542
543 bt_component_class_initialize_method_status pretty_init(
544 bt_self_component_sink *self_comp_sink,
545 bt_self_component_sink_configuration *config,
546 const bt_value *params,
547 __attribute__((unused)) void *init_method_data)
548 {
549 bt_component_class_initialize_method_status status;
550 bt_self_component_add_port_status add_port_status;
551 struct pretty_component *pretty = create_pretty();
552 bt_self_component *self_comp =
553 bt_self_component_sink_as_self_component(self_comp_sink);
554 const bt_component *comp = bt_self_component_as_component(self_comp);
555 bt_logging_level log_level = bt_component_get_logging_level(comp);
556
557 if (!pretty) {
558 /*
559 * Don't use BT_COMP_LOGE_APPEND_CAUSE, as `pretty` is not
560 * initialized yet.
561 */
562 BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
563 "Failed to allocate component.");
564 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
565 self_comp, "Failed to allocate component.");
566 status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
567 goto error;
568 }
569
570 pretty->self_comp = self_comp;
571 pretty->log_level = log_level;
572
573 add_port_status = bt_self_component_sink_add_input_port(self_comp_sink,
574 in_port_name, NULL, NULL);
575 if (add_port_status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
576 status = (int) add_port_status;
577 goto error;
578 }
579
580 pretty->out = stdout;
581 pretty->err = stderr;
582
583 pretty->delta_cycles = -1ULL;
584 pretty->last_cycles_timestamp = -1ULL;
585
586 pretty->delta_real_timestamp = -1ULL;
587 pretty->last_real_timestamp = -1ULL;
588
589 status = apply_params(pretty, params);
590 if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK) {
591 goto error;
592 }
593
594 set_use_colors(pretty);
595
596 if (pretty->options.print_enum_flags) {
597 uint64_t i;
598 /*
599 * Allocate all label arrays during the initialization of the
600 * component and reuse the same set of arrays for all
601 * enumerations.
602 */
603 for (i = 0; i < ENUMERATION_MAX_BITFLAGS_COUNT; i++) {
604 pretty->enum_bit_labels[i] = g_ptr_array_new();
605 }
606 }
607 bt_self_component_set_data(self_comp, pretty);
608
609 status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
610 goto end;
611
612 error:
613 destroy_pretty_data(pretty);
614
615 end:
616 return status;
617 }
This page took 0.04187 seconds and 4 git commands to generate.