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