Visibility: split graph API into public and private interfaces
[babeltrace.git] / plugins / text / text.c
1 /*
2 * text.c
3 *
4 * Babeltrace CTF Text Output Plugin
5 *
6 * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 * Copyright 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
8 *
9 * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com>
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 */
29
30 #include <babeltrace/plugin/plugin-dev.h>
31 #include <babeltrace/component/component.h>
32 #include <babeltrace/component/private-component.h>
33 #include <babeltrace/component/component-sink.h>
34 #include <babeltrace/component/port.h>
35 #include <babeltrace/component/private-port.h>
36 #include <babeltrace/component/connection.h>
37 #include <babeltrace/component/private-connection.h>
38 #include <babeltrace/component/notification/notification.h>
39 #include <babeltrace/component/notification/iterator.h>
40 #include <babeltrace/component/notification/event.h>
41 #include <babeltrace/values.h>
42 #include <babeltrace/compiler.h>
43 #include <babeltrace/common-internal.h>
44 #include <plugins-common.h>
45 #include <stdio.h>
46 #include <stdbool.h>
47 #include <glib.h>
48 #include "text.h"
49 #include <assert.h>
50
51 static
52 const char *plugin_options[] = {
53 "color",
54 "output-path",
55 "debug-info-dir",
56 "debug-info-target-prefix",
57 "debug-info-full-path",
58 "no-delta",
59 "clock-cycles",
60 "clock-seconds",
61 "clock-date",
62 "clock-gmt",
63 "verbose",
64 "name-default", /* show/hide */
65 "name-payload",
66 "name-context",
67 "name-scope",
68 "name-header",
69 "field-default", /* show/hide */
70 "field-trace",
71 "field-trace:hostname",
72 "field-trace:domain",
73 "field-trace:procname",
74 "field-trace:vpid",
75 "field-loglevel",
76 "field-emf",
77 };
78
79 static
80 void destroy_text_data(struct text_component *text)
81 {
82 bt_put(text->input_iterator);
83 (void) g_string_free(text->string, TRUE);
84 g_free(text->options.output_path);
85 g_free(text->options.debug_info_dir);
86 g_free(text->options.debug_info_target_prefix);
87 g_free(text);
88 }
89
90 static
91 struct text_component *create_text(void)
92 {
93 struct text_component *text;
94
95 text = g_new0(struct text_component, 1);
96 if (!text) {
97 goto end;
98 }
99 text->string = g_string_new("");
100 if (!text->string) {
101 goto error;
102 }
103 end:
104 return text;
105
106 error:
107 g_free(text);
108 return NULL;
109 }
110
111 static
112 void destroy_text(struct bt_private_component *component)
113 {
114 void *data = bt_private_component_get_user_data(component);
115
116 destroy_text_data(data);
117 }
118
119 static
120 enum bt_component_status handle_notification(struct text_component *text,
121 struct bt_notification *notification)
122 {
123 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
124
125 if (!text) {
126 ret = BT_COMPONENT_STATUS_ERROR;
127 goto end;
128 }
129
130 switch (bt_notification_get_type(notification)) {
131 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
132 puts("<packet>");
133 break;
134 case BT_NOTIFICATION_TYPE_PACKET_END:
135 puts("</packet>");
136 break;
137 case BT_NOTIFICATION_TYPE_EVENT:
138 {
139 struct bt_ctf_event *event = bt_notification_event_get_event(
140 notification);
141
142 if (!event) {
143 ret = BT_COMPONENT_STATUS_ERROR;
144 goto end;
145 }
146 ret = text_print_event(text, event);
147 bt_put(event);
148 if (ret != BT_COMPONENT_STATUS_OK) {
149 goto end;
150 }
151 break;
152 }
153 case BT_NOTIFICATION_TYPE_STREAM_END:
154 puts("</stream>");
155 break;
156 default:
157 puts("Unhandled notification type");
158 }
159 end:
160 return ret;
161 }
162
163 static
164 enum bt_component_status text_accept_port_connection(
165 struct bt_private_component *component,
166 struct bt_private_port *self_port)
167 {
168 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
169 struct bt_private_connection *connection;
170 struct text_component *text;
171
172 text = bt_private_component_get_user_data(component);
173 assert(text);
174 assert(!text->input_iterator);
175 connection = bt_private_port_get_private_connection(self_port);
176 assert(connection);
177 text->input_iterator =
178 bt_private_connection_create_notification_iterator(connection);
179
180 if (!text->input_iterator) {
181 ret = BT_COMPONENT_STATUS_ERROR;
182 }
183
184 bt_put(connection);
185 return ret;
186 }
187
188 static
189 enum bt_component_status run(struct bt_private_component *component)
190 {
191 enum bt_component_status ret;
192 struct bt_notification *notification = NULL;
193 struct bt_notification_iterator *it;
194 struct text_component *text =
195 bt_private_component_get_user_data(component);
196
197 it = text->input_iterator;
198
199 if (likely(text->processed_first_event)) {
200 enum bt_notification_iterator_status it_ret;
201
202 it_ret = bt_notification_iterator_next(it);
203 switch (it_ret) {
204 case BT_NOTIFICATION_ITERATOR_STATUS_ERROR:
205 ret = BT_COMPONENT_STATUS_ERROR;
206 goto end;
207 case BT_NOTIFICATION_ITERATOR_STATUS_END:
208 ret = BT_COMPONENT_STATUS_END;
209 BT_PUT(text->input_iterator);
210 goto end;
211 default:
212 break;
213 }
214 }
215 notification = bt_notification_iterator_get_notification(it);
216 if (!notification) {
217 ret = BT_COMPONENT_STATUS_ERROR;
218 goto end;
219 }
220
221 ret = handle_notification(text, notification);
222 text->processed_first_event = true;
223 end:
224 bt_put(notification);
225 return ret;
226 }
227
228 static
229 enum bt_component_status add_params_to_map(struct bt_value *plugin_opt_map)
230 {
231 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
232 unsigned int i;
233
234 for (i = 0; i < BT_ARRAY_SIZE(plugin_options); i++) {
235 const char *key = plugin_options[i];
236 enum bt_value_status status;
237
238 status = bt_value_map_insert(plugin_opt_map, key, bt_value_null);
239 switch (status) {
240 case BT_VALUE_STATUS_OK:
241 break;
242 default:
243 ret = BT_COMPONENT_STATUS_ERROR;
244 goto end;
245 }
246 }
247 end:
248 return ret;
249 }
250
251 static
252 bool check_param_exists(const char *key, struct bt_value *object, void *data)
253 {
254 struct text_component *text = data;
255 struct bt_value *plugin_opt_map = text->plugin_opt_map;
256
257 if (!bt_value_map_get(plugin_opt_map, key)) {
258 fprintf(text->err,
259 "[warning] Parameter \"%s\" unknown to \"text\" plugin\n", key);
260 }
261 return true;
262 }
263
264 static
265 enum bt_component_status apply_one_string(const char *key,
266 struct bt_value *params,
267 char **option)
268 {
269 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
270 struct bt_value *value = NULL;
271 enum bt_value_status status;
272 const char *str;
273
274 value = bt_value_map_get(params, key);
275 if (!value) {
276 goto end;
277 }
278 if (bt_value_is_null(value)) {
279 goto end;
280 }
281 status = bt_value_string_get(value, &str);
282 switch (status) {
283 case BT_VALUE_STATUS_OK:
284 break;
285 default:
286 ret = BT_COMPONENT_STATUS_ERROR;
287 goto end;
288 }
289 *option = g_strdup(str);
290 end:
291 bt_put(value);
292 return ret;
293 }
294
295 static
296 enum bt_component_status apply_one_bool(const char *key,
297 struct bt_value *params,
298 bool *option,
299 bool *found)
300 {
301 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
302 struct bt_value *value = NULL;
303 enum bt_value_status status;
304
305 value = bt_value_map_get(params, key);
306 if (!value) {
307 goto end;
308 }
309 status = bt_value_bool_get(value, option);
310 switch (status) {
311 case BT_VALUE_STATUS_OK:
312 break;
313 default:
314 ret = BT_COMPONENT_STATUS_ERROR;
315 goto end;
316 }
317 if (found) {
318 *found = true;
319 }
320 end:
321 bt_put(value);
322 return ret;
323 }
324
325 static
326 void warn_wrong_color_param(struct text_component *text)
327 {
328 fprintf(text->err,
329 "[warning] Accepted values for the \"color\" parameter are:\n \"always\", \"auto\", \"never\"\n");
330 }
331
332 static
333 enum bt_component_status apply_params(struct text_component *text,
334 struct bt_value *params)
335 {
336 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
337 enum bt_value_status status;
338 bool value, found;
339 char *str = NULL;
340
341 text->plugin_opt_map = bt_value_map_create();
342 if (!text->plugin_opt_map) {
343 ret = BT_COMPONENT_STATUS_ERROR;
344 goto end;
345 }
346 ret = add_params_to_map(text->plugin_opt_map);
347 if (ret != BT_COMPONENT_STATUS_OK) {
348 goto end;
349 }
350 /* Report unknown parameters. */
351 status = bt_value_map_foreach(params, check_param_exists, text);
352 switch (status) {
353 case BT_VALUE_STATUS_OK:
354 break;
355 default:
356 ret = BT_COMPONENT_STATUS_ERROR;
357 goto end;
358 }
359 /* Known parameters. */
360 text->options.color = TEXT_COLOR_OPT_AUTO;
361 if (bt_value_map_has_key(params, "color")) {
362 struct bt_value *color_value;
363 const char *color;
364
365 color_value = bt_value_map_get(params, "color");
366 if (!color_value) {
367 goto end;
368 }
369
370 status = bt_value_string_get(color_value, &color);
371 if (status) {
372 warn_wrong_color_param(text);
373 } else {
374 if (strcmp(color, "never") == 0) {
375 text->options.color = TEXT_COLOR_OPT_NEVER;
376 } else if (strcmp(color, "auto") == 0) {
377 text->options.color = TEXT_COLOR_OPT_AUTO;
378 } else if (strcmp(color, "always") == 0) {
379 text->options.color = TEXT_COLOR_OPT_ALWAYS;
380 } else {
381 warn_wrong_color_param(text);
382 }
383 }
384
385 bt_put(color_value);
386 }
387
388 ret = apply_one_string("output-path",
389 params,
390 &text->options.output_path);
391 if (ret != BT_COMPONENT_STATUS_OK) {
392 goto end;
393 }
394
395 ret = apply_one_string("debug-info-dir",
396 params,
397 &text->options.debug_info_dir);
398 if (ret != BT_COMPONENT_STATUS_OK) {
399 goto end;
400 }
401
402 ret = apply_one_string("debug-info-target-prefix",
403 params,
404 &text->options.debug_info_target_prefix);
405 if (ret != BT_COMPONENT_STATUS_OK) {
406 goto end;
407 }
408
409 value = false; /* Default. */
410 ret = apply_one_bool("debug-info-full-path", params, &value, NULL);
411 if (ret != BT_COMPONENT_STATUS_OK) {
412 goto end;
413 }
414 text->options.debug_info_full_path = value;
415
416 value = false; /* Default. */
417 ret = apply_one_bool("no-delta", params, &value, NULL);
418 if (ret != BT_COMPONENT_STATUS_OK) {
419 goto end;
420 }
421 text->options.print_delta_field = !value; /* Reverse logic. */
422
423 value = false; /* Default. */
424 ret = apply_one_bool("clock-cycles", params, &value, NULL);
425 if (ret != BT_COMPONENT_STATUS_OK) {
426 goto end;
427 }
428 text->options.print_timestamp_cycles = value;
429
430 value = false; /* Default. */
431 ret = apply_one_bool("clock-seconds", params, &value, NULL);
432 if (ret != BT_COMPONENT_STATUS_OK) {
433 goto end;
434 }
435 text->options.clock_seconds = value;
436
437 value = false; /* Default. */
438 ret = apply_one_bool("clock-date", params, &value, NULL);
439 if (ret != BT_COMPONENT_STATUS_OK) {
440 goto end;
441 }
442 text->options.clock_date = value;
443
444 value = false; /* Default. */
445 ret = apply_one_bool("clock-gmt", params, &value, NULL);
446 if (ret != BT_COMPONENT_STATUS_OK) {
447 goto end;
448 }
449 text->options.clock_gmt = value;
450
451 value = false; /* Default. */
452 ret = apply_one_bool("verbose", params, &value, NULL);
453 if (ret != BT_COMPONENT_STATUS_OK) {
454 goto end;
455 }
456 text->options.verbose = value;
457
458 /* Names. */
459 ret = apply_one_string("name-default", params, &str);
460 if (ret != BT_COMPONENT_STATUS_OK) {
461 goto end;
462 }
463 if (!str) {
464 text->options.name_default = TEXT_DEFAULT_UNSET;
465 } else if (!strcmp(str, "show")) {
466 text->options.name_default = TEXT_DEFAULT_SHOW;
467 } else if (!strcmp(str, "hide")) {
468 text->options.name_default = TEXT_DEFAULT_HIDE;
469 } else {
470 ret = BT_COMPONENT_STATUS_ERROR;
471 goto end;
472 }
473 g_free(str);
474 str = NULL;
475
476 switch (text->options.name_default) {
477 case TEXT_DEFAULT_UNSET:
478 text->options.print_payload_field_names = true;
479 text->options.print_context_field_names = true;
480 text->options.print_header_field_names = false;
481 text->options.print_scope_field_names = false;
482 break;
483 case TEXT_DEFAULT_SHOW:
484 text->options.print_payload_field_names = true;
485 text->options.print_context_field_names = true;
486 text->options.print_header_field_names = true;
487 text->options.print_scope_field_names = true;
488 break;
489 case TEXT_DEFAULT_HIDE:
490 text->options.print_payload_field_names = false;
491 text->options.print_context_field_names = false;
492 text->options.print_header_field_names = false;
493 text->options.print_scope_field_names = false;
494 break;
495 default:
496 ret = BT_COMPONENT_STATUS_ERROR;
497 goto end;
498 }
499
500 value = false;
501 found = false;
502 ret = apply_one_bool("name-payload", params, &value, &found);
503 if (ret != BT_COMPONENT_STATUS_OK) {
504 goto end;
505 }
506 if (found) {
507 text->options.print_payload_field_names = value;
508 }
509
510 value = false;
511 found = false;
512 ret = apply_one_bool("name-context", params, &value, &found);
513 if (ret != BT_COMPONENT_STATUS_OK) {
514 goto end;
515 }
516 if (found) {
517 text->options.print_context_field_names = value;
518 }
519
520 value = false;
521 found = false;
522 ret = apply_one_bool("name-header", params, &value, &found);
523 if (ret != BT_COMPONENT_STATUS_OK) {
524 goto end;
525 }
526 if (found) {
527 text->options.print_header_field_names = value;
528 }
529
530 value = false;
531 found = false;
532 ret = apply_one_bool("name-scope", params, &value, &found);
533 if (ret != BT_COMPONENT_STATUS_OK) {
534 goto end;
535 }
536 if (found) {
537 text->options.print_scope_field_names = value;
538 }
539
540 /* Fields. */
541 ret = apply_one_string("field-default", params, &str);
542 if (ret != BT_COMPONENT_STATUS_OK) {
543 goto end;
544 }
545 if (!str) {
546 text->options.field_default = TEXT_DEFAULT_UNSET;
547 } else if (!strcmp(str, "show")) {
548 text->options.field_default = TEXT_DEFAULT_SHOW;
549 } else if (!strcmp(str, "hide")) {
550 text->options.field_default = TEXT_DEFAULT_HIDE;
551 } else {
552 ret = BT_COMPONENT_STATUS_ERROR;
553 goto end;
554 }
555 g_free(str);
556 str = NULL;
557
558 switch (text->options.field_default) {
559 case TEXT_DEFAULT_UNSET:
560 text->options.print_trace_field = false;
561 text->options.print_trace_hostname_field = true;
562 text->options.print_trace_domain_field = false;
563 text->options.print_trace_procname_field = true;
564 text->options.print_trace_vpid_field = true;
565 text->options.print_loglevel_field = false;
566 text->options.print_emf_field = false;
567 text->options.print_emf_field = false;
568 break;
569 case TEXT_DEFAULT_SHOW:
570 text->options.print_trace_field = true;
571 text->options.print_trace_hostname_field = true;
572 text->options.print_trace_domain_field = true;
573 text->options.print_trace_procname_field = true;
574 text->options.print_trace_vpid_field = true;
575 text->options.print_loglevel_field = true;
576 text->options.print_emf_field = true;
577 text->options.print_emf_field = true;
578 break;
579 case TEXT_DEFAULT_HIDE:
580 text->options.print_trace_field = false;
581 text->options.print_trace_hostname_field = false;
582 text->options.print_trace_domain_field = false;
583 text->options.print_trace_procname_field = false;
584 text->options.print_trace_vpid_field = false;
585 text->options.print_loglevel_field = false;
586 text->options.print_emf_field = false;
587 text->options.print_emf_field = false;
588 break;
589 default:
590 ret = BT_COMPONENT_STATUS_ERROR;
591 goto end;
592 }
593
594 value = false;
595 found = false;
596 ret = apply_one_bool("field-trace", params, &value, &found);
597 if (ret != BT_COMPONENT_STATUS_OK) {
598 goto end;
599 }
600 if (found) {
601 text->options.print_trace_field = value;
602 }
603
604 value = false;
605 found = false;
606 ret = apply_one_bool("field-trace:hostname", params, &value, &found);
607 if (ret != BT_COMPONENT_STATUS_OK) {
608 goto end;
609 }
610 if (found) {
611 text->options.print_trace_hostname_field = value;
612 }
613
614 value = false;
615 found = false;
616 ret = apply_one_bool("field-trace:domain", params, &value, &found);
617 if (ret != BT_COMPONENT_STATUS_OK) {
618 goto end;
619 }
620 if (found) {
621 text->options.print_trace_domain_field = value;
622 }
623
624 value = false;
625 found = false;
626 ret = apply_one_bool("field-trace:procname", params, &value, &found);
627 if (ret != BT_COMPONENT_STATUS_OK) {
628 goto end;
629 }
630 if (found) {
631 text->options.print_trace_procname_field = value;
632 }
633
634 value = false;
635 found = false;
636 ret = apply_one_bool("field-trace:vpid", params, &value, &found);
637 if (ret != BT_COMPONENT_STATUS_OK) {
638 goto end;
639 }
640 if (found) {
641 text->options.print_trace_vpid_field = value;
642 }
643
644 value = false;
645 found = false;
646 ret = apply_one_bool("field-loglevel", params, &value, &found);
647 if (ret != BT_COMPONENT_STATUS_OK) {
648 goto end;
649 }
650 if (found) {
651 text->options.print_loglevel_field = value;
652 }
653
654 value = false;
655 found = false;
656 ret = apply_one_bool("field-emf", params, &value, &found);
657 if (ret != BT_COMPONENT_STATUS_OK) {
658 goto end;
659 }
660 if (found) {
661 text->options.print_emf_field = value;
662 }
663
664 value = false;
665 found = false;
666 ret = apply_one_bool("field-emf", params, &value, &found);
667 if (ret != BT_COMPONENT_STATUS_OK) {
668 goto end;
669 }
670 if (found) {
671 text->options.print_emf_field = value;
672 }
673
674 end:
675 bt_put(text->plugin_opt_map);
676 text->plugin_opt_map = NULL;
677 g_free(str);
678 return ret;
679 }
680
681 static
682 void set_use_colors(struct text_component *text)
683 {
684 switch (text->options.color) {
685 case TEXT_COLOR_OPT_ALWAYS:
686 text->use_colors = true;
687 break;
688 case TEXT_COLOR_OPT_AUTO:
689 text->use_colors = text->out == stdout &&
690 bt_common_colors_supported();
691 break;
692 case TEXT_COLOR_OPT_NEVER:
693 text->use_colors = false;
694 break;
695 }
696 }
697
698 static
699 void init_stream_packet_context_quarks(void)
700 {
701 stream_packet_context_quarks[Q_TIMESTAMP_BEGIN] =
702 g_quark_from_string("timestamp_begin");
703 stream_packet_context_quarks[Q_TIMESTAMP_BEGIN] =
704 g_quark_from_string("timestamp_begin");
705 stream_packet_context_quarks[Q_TIMESTAMP_END] =
706 g_quark_from_string("timestamp_end");
707 stream_packet_context_quarks[Q_EVENTS_DISCARDED] =
708 g_quark_from_string("events_discarded");
709 stream_packet_context_quarks[Q_CONTENT_SIZE] =
710 g_quark_from_string("content_size");
711 stream_packet_context_quarks[Q_PACKET_SIZE] =
712 g_quark_from_string("packet_size");
713 stream_packet_context_quarks[Q_PACKET_SEQ_NUM] =
714 g_quark_from_string("packet_seq_num");
715 }
716
717 static
718 enum bt_component_status text_component_init(
719 struct bt_private_component *component,
720 struct bt_value *params,
721 UNUSED_VAR void *init_method_data)
722 {
723 enum bt_component_status ret;
724 struct text_component *text = create_text();
725
726 if (!text) {
727 ret = BT_COMPONENT_STATUS_NOMEM;
728 goto end;
729 }
730
731 text->out = stdout;
732 text->err = stderr;
733
734 text->delta_cycles = -1ULL;
735 text->last_cycles_timestamp = -1ULL;
736
737 text->delta_real_timestamp = -1ULL;
738 text->last_real_timestamp = -1ULL;
739
740 ret = apply_params(text, params);
741 if (ret != BT_COMPONENT_STATUS_OK) {
742 goto error;
743 }
744
745 set_use_colors(text);
746
747 ret = bt_private_component_set_user_data(component, text);
748 if (ret != BT_COMPONENT_STATUS_OK) {
749 goto error;
750 }
751
752 init_stream_packet_context_quarks();
753
754 end:
755 return ret;
756 error:
757 destroy_text_data(text);
758 return ret;
759 }
760
761 /* Initialize plug-in entry points. */
762 BT_PLUGIN(text);
763 BT_PLUGIN_DESCRIPTION("Babeltrace text output plug-in.");
764 BT_PLUGIN_AUTHOR("Jérémie Galarneau");
765 BT_PLUGIN_LICENSE("MIT");
766 BT_PLUGIN_SINK_COMPONENT_CLASS(text, run);
767 BT_PLUGIN_SINK_COMPONENT_CLASS_INIT_METHOD(text, text_component_init);
768 BT_PLUGIN_SINK_COMPONENT_CLASS_ACCEPT_PORT_CONNECTION_METHOD(text, text_accept_port_connection);
769 BT_PLUGIN_SINK_COMPONENT_CLASS_DESTROY_METHOD(text, destroy_text);
770 BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(text,
771 "Formats CTF-IR to text. Formerly known as ctf-text.");
This page took 0.045719 seconds and 4 git commands to generate.