cli: remove the global volatile `interrupted` variable
[babeltrace.git] / src / cli / babeltrace2.c
1 /*
2 * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
3 *
4 * Author: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 #define BT_LOG_TAG "CLI"
26 #include "logging.h"
27
28 #include <babeltrace2/babeltrace.h>
29 #include "common/common.h"
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <popt.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <glib.h>
36 #include <inttypes.h>
37 #include <unistd.h>
38 #include <signal.h>
39 #include "babeltrace2-cfg.h"
40 #include "babeltrace2-cfg-cli-args.h"
41 #include "babeltrace2-cfg-cli-args-default.h"
42 #include "babeltrace2-plugins.h"
43
44 #define ENV_BABELTRACE_WARN_COMMAND_NAME_DIRECTORY_CLASH "BABELTRACE_CLI_WARN_COMMAND_NAME_DIRECTORY_CLASH"
45 #define ENV_BABELTRACE_CLI_LOG_LEVEL "BABELTRACE_CLI_LOG_LEVEL"
46 #define NSEC_PER_SEC 1000000000LL
47
48 /*
49 * Known environment variable names for the log levels of the project's
50 * modules.
51 */
52 static const char* log_level_env_var_names[] = {
53 "BABELTRACE_PLUGIN_CTF_METADATA_LOG_LEVEL",
54 "BABELTRACE_PYTHON_BT2_LOG_LEVEL",
55 NULL,
56 };
57
58 /* Application's interrupter (owned by this) */
59 static bt_interrupter *the_interrupter;
60
61 #ifdef __MINGW32__
62
63 #include <windows.h>
64
65 static
66 BOOL WINAPI signal_handler(DWORD signal) {
67 if (the_interrupter) {
68 bt_interrupter_set(the_interrupter);
69 }
70
71 return TRUE;
72 }
73
74 static
75 void set_signal_handler(void)
76 {
77 if (!SetConsoleCtrlHandler(signal_handler, TRUE)) {
78 BT_LOGE("Failed to set the Ctrl+C handler.");
79 }
80 }
81
82 #else /* __MINGW32__ */
83
84 static
85 void signal_handler(int signum)
86 {
87 if (signum != SIGINT) {
88 return;
89 }
90
91 if (the_interrupter) {
92 bt_interrupter_set(the_interrupter);
93 }
94 }
95
96 static
97 void set_signal_handler(void)
98 {
99 struct sigaction new_action, old_action;
100
101 new_action.sa_handler = signal_handler;
102 sigemptyset(&new_action.sa_mask);
103 new_action.sa_flags = 0;
104 sigaction(SIGINT, NULL, &old_action);
105
106 if (old_action.sa_handler != SIG_IGN) {
107 sigaction(SIGINT, &new_action, NULL);
108 }
109 }
110
111 #endif /* __MINGW32__ */
112
113 static
114 int query(struct bt_config *cfg, const bt_component_class *comp_cls,
115 const char *obj, const bt_value *params,
116 const bt_value **user_result, const char **fail_reason)
117 {
118 const bt_value *result = NULL;
119 bt_query_executor_query_status query_status;
120 bt_query_executor *query_exec;
121 *fail_reason = "unknown error";
122 int ret = 0;
123
124 BT_ASSERT(fail_reason);
125 BT_ASSERT(user_result);
126 query_exec = bt_query_executor_create();
127 if (!query_exec) {
128 BT_CLI_LOGE_APPEND_CAUSE("Cannot create a query executor.");
129 goto error;
130 }
131
132 bt_query_executor_add_interrupter(query_exec, the_interrupter);
133
134 while (true) {
135 query_status = bt_query_executor_query(
136 query_exec, comp_cls, obj, params,
137 cfg->log_level, &result);
138 switch (query_status) {
139 case BT_QUERY_EXECUTOR_QUERY_STATUS_OK:
140 goto ok;
141 case BT_QUERY_EXECUTOR_QUERY_STATUS_AGAIN:
142 {
143 const uint64_t sleep_time_us = 100000;
144
145 if (bt_interrupter_is_set(the_interrupter)) {
146 *fail_reason = "interrupted by user";
147 goto error;
148 }
149
150 /* Wait 100 ms and retry */
151 BT_LOGD("Got BT_QUERY_EXECUTOR_QUERY_STATUS_AGAIN: sleeping: "
152 "time-us=%" PRIu64, sleep_time_us);
153
154 if (usleep(sleep_time_us)) {
155 if (bt_interrupter_is_set(the_interrupter)) {
156 BT_CLI_LOGW_APPEND_CAUSE(
157 "Query was interrupted by user: "
158 "comp-cls-addr=%p, comp-cls-name=\"%s\", "
159 "query-obj=\"%s\"", comp_cls,
160 bt_component_class_get_name(comp_cls),
161 obj);
162 *fail_reason = "interrupted by user";
163 goto error;
164 }
165 }
166
167 continue;
168 }
169 case BT_QUERY_EXECUTOR_QUERY_STATUS_ERROR:
170 if (bt_interrupter_is_set(the_interrupter)) {
171 *fail_reason = "interrupted by user";
172 goto error;
173 }
174
175 goto error;
176 case BT_QUERY_EXECUTOR_QUERY_STATUS_INVALID_OBJECT:
177 *fail_reason = "invalid or unknown query object";
178 goto error;
179 case BT_QUERY_EXECUTOR_QUERY_STATUS_INVALID_PARAMS:
180 *fail_reason = "invalid query parameters";
181 goto error;
182 case BT_QUERY_EXECUTOR_QUERY_STATUS_MEMORY_ERROR:
183 *fail_reason = "not enough memory";
184 goto error;
185 default:
186 BT_LOGF("Unknown query status: status=%s",
187 bt_common_func_status_string(query_status));
188 abort();
189 }
190 }
191
192 ok:
193 *user_result = result;
194 result = NULL;
195 goto end;
196
197 error:
198 ret = -1;
199
200 end:
201 bt_query_executor_put_ref(query_exec);
202 bt_value_put_ref(result);
203 return ret;
204 }
205
206
207
208 typedef const void *(*plugin_borrow_comp_cls_func_t)(
209 const bt_plugin *, const char *);
210
211 static
212 const void *find_component_class_from_plugin(const char *plugin_name,
213 const char *comp_class_name,
214 plugin_borrow_comp_cls_func_t plugin_borrow_comp_cls_func)
215 {
216 const void *comp_class = NULL;
217 const bt_plugin *plugin;
218
219 BT_LOGI("Finding component class: plugin-name=\"%s\", "
220 "comp-cls-name=\"%s\"", plugin_name, comp_class_name);
221
222 plugin = find_loaded_plugin(plugin_name);
223 if (!plugin) {
224 goto end;
225 }
226
227 comp_class = plugin_borrow_comp_cls_func(plugin, comp_class_name);
228 bt_object_get_ref(comp_class);
229 BT_PLUGIN_PUT_REF_AND_RESET(plugin);
230
231 end:
232 if (comp_class) {
233 BT_LOGI("Found component class: plugin-name=\"%s\", "
234 "comp-cls-name=\"%s\"", plugin_name, comp_class_name);
235 } else {
236 BT_LOGI("Cannot find source component class: "
237 "plugin-name=\"%s\", comp-cls-name=\"%s\"",
238 plugin_name, comp_class_name);
239 }
240
241 return comp_class;
242 }
243
244 static
245 const bt_component_class_source *find_source_component_class(
246 const char *plugin_name, const char *comp_class_name)
247 {
248 return (const void *) find_component_class_from_plugin(
249 plugin_name, comp_class_name,
250 (plugin_borrow_comp_cls_func_t)
251 bt_plugin_borrow_source_component_class_by_name_const);
252 }
253
254 static
255 const bt_component_class_filter *find_filter_component_class(
256 const char *plugin_name, const char *comp_class_name)
257 {
258 return (const void *) find_component_class_from_plugin(
259 plugin_name, comp_class_name,
260 (plugin_borrow_comp_cls_func_t)
261 bt_plugin_borrow_filter_component_class_by_name_const);
262 }
263
264 static
265 const bt_component_class_sink *find_sink_component_class(
266 const char *plugin_name, const char *comp_class_name)
267 {
268 return (const void *) find_component_class_from_plugin(plugin_name,
269 comp_class_name,
270 (plugin_borrow_comp_cls_func_t)
271 bt_plugin_borrow_sink_component_class_by_name_const);
272 }
273
274 static
275 const bt_component_class *find_component_class(const char *plugin_name,
276 const char *comp_class_name,
277 bt_component_class_type comp_class_type)
278 {
279 const bt_component_class *comp_cls = NULL;
280
281 switch (comp_class_type) {
282 case BT_COMPONENT_CLASS_TYPE_SOURCE:
283 comp_cls = bt_component_class_source_as_component_class_const(find_source_component_class(plugin_name, comp_class_name));
284 break;
285 case BT_COMPONENT_CLASS_TYPE_FILTER:
286 comp_cls = bt_component_class_filter_as_component_class_const(find_filter_component_class(plugin_name, comp_class_name));
287 break;
288 case BT_COMPONENT_CLASS_TYPE_SINK:
289 comp_cls = bt_component_class_sink_as_component_class_const(find_sink_component_class(plugin_name, comp_class_name));
290 break;
291 default:
292 abort();
293 }
294
295 return comp_cls;
296 }
297
298 static
299 void print_indent(FILE *fp, size_t indent)
300 {
301 size_t i;
302
303 for (i = 0; i < indent; i++) {
304 fprintf(fp, " ");
305 }
306 }
307
308 static
309 const char *component_type_str(bt_component_class_type type)
310 {
311 switch (type) {
312 case BT_COMPONENT_CLASS_TYPE_SOURCE:
313 return "source";
314 case BT_COMPONENT_CLASS_TYPE_SINK:
315 return "sink";
316 case BT_COMPONENT_CLASS_TYPE_FILTER:
317 return "filter";
318 default:
319 return "(unknown)";
320 }
321 }
322
323 static
324 void print_plugin_comp_cls_opt(FILE *fh, const char *plugin_name,
325 const char *comp_cls_name, bt_component_class_type type)
326 {
327 GString *shell_plugin_name = NULL;
328 GString *shell_comp_cls_name = NULL;
329
330 if (plugin_name) {
331 shell_plugin_name = bt_common_shell_quote(plugin_name, false);
332 if (!shell_plugin_name) {
333 goto end;
334 }
335 }
336
337 shell_comp_cls_name = bt_common_shell_quote(comp_cls_name, false);
338 if (!shell_comp_cls_name) {
339 goto end;
340 }
341
342 fprintf(fh, "'%s%s%s%s",
343 bt_common_color_bold(),
344 bt_common_color_fg_cyan(),
345 component_type_str(type),
346 bt_common_color_fg_default());
347
348 if (shell_plugin_name) {
349 fprintf(fh, ".%s%s%s",
350 bt_common_color_fg_blue(),
351 shell_plugin_name->str,
352 bt_common_color_fg_default());
353 }
354
355 fprintf(fh, ".%s%s%s'",
356 bt_common_color_fg_yellow(),
357 shell_comp_cls_name->str,
358 bt_common_color_reset());
359
360 end:
361 if (shell_plugin_name) {
362 g_string_free(shell_plugin_name, TRUE);
363 }
364
365 if (shell_comp_cls_name) {
366 g_string_free(shell_comp_cls_name, TRUE);
367 }
368 }
369
370 static
371 void print_value(FILE *, const bt_value *, size_t);
372
373 static
374 void print_value_rec(FILE *, const bt_value *, size_t);
375
376 struct print_map_value_data {
377 size_t indent;
378 FILE *fp;
379 };
380
381 static
382 bt_bool print_map_value(const char *key, const bt_value *object,
383 void *data)
384 {
385 struct print_map_value_data *print_map_value_data = data;
386
387 print_indent(print_map_value_data->fp, print_map_value_data->indent);
388 fprintf(print_map_value_data->fp, "%s: ", key);
389 BT_ASSERT(object);
390
391 if (bt_value_is_array(object) &&
392 bt_value_array_is_empty(object)) {
393 fprintf(print_map_value_data->fp, "[ ]\n");
394 return true;
395 }
396
397 if (bt_value_is_map(object) &&
398 bt_value_map_is_empty(object)) {
399 fprintf(print_map_value_data->fp, "{ }\n");
400 return true;
401 }
402
403 if (bt_value_is_array(object) ||
404 bt_value_is_map(object)) {
405 fprintf(print_map_value_data->fp, "\n");
406 }
407
408 print_value_rec(print_map_value_data->fp, object,
409 print_map_value_data->indent + 2);
410 return BT_TRUE;
411 }
412
413 static
414 void print_value_rec(FILE *fp, const bt_value *value, size_t indent)
415 {
416 bt_bool bool_val;
417 int64_t int_val;
418 uint64_t uint_val;
419 double dbl_val;
420 const char *str_val;
421 int size;
422 int i;
423
424 if (!value) {
425 return;
426 }
427
428 switch (bt_value_get_type(value)) {
429 case BT_VALUE_TYPE_NULL:
430 fprintf(fp, "%snull%s\n", bt_common_color_bold(),
431 bt_common_color_reset());
432 break;
433 case BT_VALUE_TYPE_BOOL:
434 bool_val = bt_value_bool_get(value);
435 fprintf(fp, "%s%s%s%s\n", bt_common_color_bold(),
436 bt_common_color_fg_cyan(), bool_val ? "yes" : "no",
437 bt_common_color_reset());
438 break;
439 case BT_VALUE_TYPE_UNSIGNED_INTEGER:
440 uint_val = bt_value_integer_unsigned_get(value);
441 fprintf(fp, "%s%s%" PRIu64 "%s\n", bt_common_color_bold(),
442 bt_common_color_fg_red(), uint_val,
443 bt_common_color_reset());
444 break;
445 case BT_VALUE_TYPE_SIGNED_INTEGER:
446 int_val = bt_value_integer_signed_get(value);
447 fprintf(fp, "%s%s%" PRId64 "%s\n", bt_common_color_bold(),
448 bt_common_color_fg_red(), int_val,
449 bt_common_color_reset());
450 break;
451 case BT_VALUE_TYPE_REAL:
452 dbl_val = bt_value_real_get(value);
453 fprintf(fp, "%s%s%lf%s\n", bt_common_color_bold(),
454 bt_common_color_fg_red(), dbl_val,
455 bt_common_color_reset());
456 break;
457 case BT_VALUE_TYPE_STRING:
458 str_val = bt_value_string_get(value);
459 fprintf(fp, "%s%s%s%s\n", bt_common_color_bold(),
460 bt_common_color_fg_green(), str_val,
461 bt_common_color_reset());
462 break;
463 case BT_VALUE_TYPE_ARRAY:
464 size = bt_value_array_get_size(value);
465 if (size < 0) {
466 goto error;
467 }
468
469 if (size == 0) {
470 print_indent(fp, indent);
471 fprintf(fp, "[ ]\n");
472 break;
473 }
474
475 for (i = 0; i < size; i++) {
476 const bt_value *element =
477 bt_value_array_borrow_element_by_index_const(
478 value, i);
479
480 if (!element) {
481 goto error;
482 }
483 print_indent(fp, indent);
484 fprintf(fp, "- ");
485
486 if (bt_value_is_array(element) &&
487 bt_value_array_is_empty(element)) {
488 fprintf(fp, "[ ]\n");
489 continue;
490 }
491
492 if (bt_value_is_map(element) &&
493 bt_value_map_is_empty(element)) {
494 fprintf(fp, "{ }\n");
495 continue;
496 }
497
498 if (bt_value_is_array(element) ||
499 bt_value_is_map(element)) {
500 fprintf(fp, "\n");
501 }
502
503 print_value_rec(fp, element, indent + 2);
504 }
505 break;
506 case BT_VALUE_TYPE_MAP:
507 {
508 struct print_map_value_data data = {
509 .indent = indent,
510 .fp = fp,
511 };
512
513 if (bt_value_map_is_empty(value)) {
514 print_indent(fp, indent);
515 fprintf(fp, "{ }\n");
516 break;
517 }
518
519 bt_value_map_foreach_entry_const(value, print_map_value, &data);
520 break;
521 }
522 default:
523 abort();
524 }
525 return;
526
527 error:
528 BT_LOGE("Error printing value of type %s.",
529 bt_common_value_type_string(bt_value_get_type(value)));
530 }
531
532 static
533 void print_value(FILE *fp, const bt_value *value, size_t indent)
534 {
535 if (!bt_value_is_array(value) && !bt_value_is_map(value)) {
536 print_indent(fp, indent);
537 }
538
539 print_value_rec(fp, value, indent);
540 }
541
542 static
543 void print_bt_config_component(struct bt_config_component *bt_config_component)
544 {
545 fprintf(stderr, " ");
546 print_plugin_comp_cls_opt(stderr, bt_config_component->plugin_name->str,
547 bt_config_component->comp_cls_name->str,
548 bt_config_component->type);
549 fprintf(stderr, ":\n");
550
551 if (bt_config_component->instance_name->len > 0) {
552 fprintf(stderr, " Name: %s\n",
553 bt_config_component->instance_name->str);
554 }
555
556 fprintf(stderr, " Parameters:\n");
557 print_value(stderr, bt_config_component->params, 8);
558 }
559
560 static
561 void print_bt_config_components(GPtrArray *array)
562 {
563 size_t i;
564
565 for (i = 0; i < array->len; i++) {
566 struct bt_config_component *cfg_component =
567 bt_config_get_component(array, i);
568 print_bt_config_component(cfg_component);
569 BT_OBJECT_PUT_REF_AND_RESET(cfg_component);
570 }
571 }
572
573 static
574 void print_plugin_paths(const bt_value *plugin_paths)
575 {
576 fprintf(stderr, " Plugin paths:\n");
577 print_value(stderr, plugin_paths, 4);
578 }
579
580 static
581 void print_cfg_run(struct bt_config *cfg)
582 {
583 size_t i;
584
585 print_plugin_paths(cfg->plugin_paths);
586 fprintf(stderr, " Source component instances:\n");
587 print_bt_config_components(cfg->cmd_data.run.sources);
588
589 if (cfg->cmd_data.run.filters->len > 0) {
590 fprintf(stderr, " Filter component instances:\n");
591 print_bt_config_components(cfg->cmd_data.run.filters);
592 }
593
594 fprintf(stderr, " Sink component instances:\n");
595 print_bt_config_components(cfg->cmd_data.run.sinks);
596 fprintf(stderr, " Connections:\n");
597
598 for (i = 0; i < cfg->cmd_data.run.connections->len; i++) {
599 struct bt_config_connection *cfg_connection =
600 g_ptr_array_index(cfg->cmd_data.run.connections,
601 i);
602
603 fprintf(stderr, " %s%s%s -> %s%s%s\n",
604 cfg_connection->upstream_comp_name->str,
605 cfg_connection->upstream_port_glob->len > 0 ? "." : "",
606 cfg_connection->upstream_port_glob->str,
607 cfg_connection->downstream_comp_name->str,
608 cfg_connection->downstream_port_glob->len > 0 ? "." : "",
609 cfg_connection->downstream_port_glob->str);
610 }
611 }
612
613 static
614 void print_cfg_list_plugins(struct bt_config *cfg)
615 {
616 print_plugin_paths(cfg->plugin_paths);
617 }
618
619 static
620 void print_cfg_help(struct bt_config *cfg)
621 {
622 print_plugin_paths(cfg->plugin_paths);
623 }
624
625 static
626 void print_cfg_print_ctf_metadata(struct bt_config *cfg)
627 {
628 print_plugin_paths(cfg->plugin_paths);
629 fprintf(stderr, " Path: %s\n",
630 cfg->cmd_data.print_ctf_metadata.path->str);
631 }
632
633 static
634 void print_cfg_print_lttng_live_sessions(struct bt_config *cfg)
635 {
636 print_plugin_paths(cfg->plugin_paths);
637 fprintf(stderr, " URL: %s\n",
638 cfg->cmd_data.print_lttng_live_sessions.url->str);
639 }
640
641 static
642 void print_cfg_query(struct bt_config *cfg)
643 {
644 print_plugin_paths(cfg->plugin_paths);
645 fprintf(stderr, " Object: `%s`\n", cfg->cmd_data.query.object->str);
646 fprintf(stderr, " Component class:\n");
647 print_bt_config_component(cfg->cmd_data.query.cfg_component);
648 }
649
650 static
651 void print_cfg(struct bt_config *cfg)
652 {
653 if (!BT_LOG_ON_INFO) {
654 return;
655 }
656
657 BT_LOGI_STR("CLI configuration:");
658 BT_LOGI(" Debug mode: %s\n", cfg->debug ? "yes" : "no");
659 BT_LOGI(" Verbose mode: %s\n", cfg->verbose ? "yes" : "no");
660
661 switch (cfg->command) {
662 case BT_CONFIG_COMMAND_RUN:
663 print_cfg_run(cfg);
664 break;
665 case BT_CONFIG_COMMAND_LIST_PLUGINS:
666 print_cfg_list_plugins(cfg);
667 break;
668 case BT_CONFIG_COMMAND_HELP:
669 print_cfg_help(cfg);
670 break;
671 case BT_CONFIG_COMMAND_QUERY:
672 print_cfg_query(cfg);
673 break;
674 case BT_CONFIG_COMMAND_PRINT_CTF_METADATA:
675 print_cfg_print_ctf_metadata(cfg);
676 break;
677 case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS:
678 print_cfg_print_lttng_live_sessions(cfg);
679 break;
680 default:
681 abort();
682 }
683 }
684
685 static
686 void print_plugin_info(const bt_plugin *plugin)
687 {
688 unsigned int major, minor, patch;
689 const char *extra;
690 bt_property_availability version_avail;
691 const char *plugin_name;
692 const char *path;
693 const char *author;
694 const char *license;
695 const char *plugin_description;
696
697 plugin_name = bt_plugin_get_name(plugin);
698 path = bt_plugin_get_path(plugin);
699 author = bt_plugin_get_author(plugin);
700 license = bt_plugin_get_license(plugin);
701 plugin_description = bt_plugin_get_description(plugin);
702 version_avail = bt_plugin_get_version(plugin, &major, &minor,
703 &patch, &extra);
704 printf("%s%s%s%s:\n", bt_common_color_bold(),
705 bt_common_color_fg_blue(), plugin_name,
706 bt_common_color_reset());
707 if (path) {
708 printf(" %sPath%s: %s\n", bt_common_color_bold(),
709 bt_common_color_reset(), path);
710 } else {
711 puts(" Built-in");
712 }
713
714 if (version_avail == BT_PROPERTY_AVAILABILITY_AVAILABLE) {
715 printf(" %sVersion%s: %u.%u.%u",
716 bt_common_color_bold(), bt_common_color_reset(),
717 major, minor, patch);
718
719 if (extra) {
720 printf("%s", extra);
721 }
722
723 printf("\n");
724 }
725
726 printf(" %sDescription%s: %s\n", bt_common_color_bold(),
727 bt_common_color_reset(),
728 plugin_description ? plugin_description : "(None)");
729 printf(" %sAuthor%s: %s\n", bt_common_color_bold(),
730 bt_common_color_reset(), author ? author : "(Unknown)");
731 printf(" %sLicense%s: %s\n", bt_common_color_bold(),
732 bt_common_color_reset(),
733 license ? license : "(Unknown)");
734 }
735
736 static
737 int cmd_query(struct bt_config *cfg)
738 {
739 int ret = 0;
740 const bt_component_class *comp_cls = NULL;
741 const bt_value *results = NULL;
742 const char *fail_reason = NULL;
743
744 comp_cls = find_component_class(
745 cfg->cmd_data.query.cfg_component->plugin_name->str,
746 cfg->cmd_data.query.cfg_component->comp_cls_name->str,
747 cfg->cmd_data.query.cfg_component->type);
748 if (!comp_cls) {
749 BT_CLI_LOGE_APPEND_CAUSE(
750 "Cannot find component class: plugin-name=\"%s\", "
751 "comp-cls-name=\"%s\", comp-cls-type=%d",
752 cfg->cmd_data.query.cfg_component->plugin_name->str,
753 cfg->cmd_data.query.cfg_component->comp_cls_name->str,
754 cfg->cmd_data.query.cfg_component->type);
755 ret = -1;
756 goto end;
757 }
758
759 ret = query(cfg, comp_cls, cfg->cmd_data.query.object->str,
760 cfg->cmd_data.query.cfg_component->params,
761 &results, &fail_reason);
762 if (ret) {
763 goto failed;
764 }
765
766 print_value(stdout, results, 0);
767 goto end;
768
769 failed:
770 BT_CLI_LOGE_APPEND_CAUSE(
771 "Failed to query component class: %s: plugin-name=\"%s\", "
772 "comp-cls-name=\"%s\", comp-cls-type=%d "
773 "object=\"%s\"", fail_reason,
774 cfg->cmd_data.query.cfg_component->plugin_name->str,
775 cfg->cmd_data.query.cfg_component->comp_cls_name->str,
776 cfg->cmd_data.query.cfg_component->type,
777 cfg->cmd_data.query.object->str);
778 ret = -1;
779
780 end:
781 bt_component_class_put_ref(comp_cls);
782 bt_value_put_ref(results);
783 return ret;
784 }
785
786 static
787 void print_component_class_help(const char *plugin_name,
788 const bt_component_class *comp_cls)
789 {
790 const char *comp_class_name =
791 bt_component_class_get_name(comp_cls);
792 const char *comp_class_description =
793 bt_component_class_get_description(comp_cls);
794 const char *comp_class_help =
795 bt_component_class_get_help(comp_cls);
796 bt_component_class_type type =
797 bt_component_class_get_type(comp_cls);
798
799 print_plugin_comp_cls_opt(stdout, plugin_name, comp_class_name, type);
800 printf("\n");
801 printf(" %sDescription%s: %s\n", bt_common_color_bold(),
802 bt_common_color_reset(),
803 comp_class_description ? comp_class_description : "(None)");
804
805 if (comp_class_help) {
806 printf("\n%s\n", comp_class_help);
807 }
808 }
809
810 static
811 int cmd_help(struct bt_config *cfg)
812 {
813 int ret = 0;
814 const bt_plugin *plugin = NULL;
815 const bt_component_class *needed_comp_cls = NULL;
816
817 plugin = find_loaded_plugin(cfg->cmd_data.help.cfg_component->plugin_name->str);
818 if (!plugin) {
819 BT_CLI_LOGE_APPEND_CAUSE(
820 "Cannot find plugin: plugin-name=\"%s\"",
821 cfg->cmd_data.help.cfg_component->plugin_name->str);
822 ret = -1;
823 goto end;
824 }
825
826 print_plugin_info(plugin);
827 printf(" %sSource component classes%s: %d\n",
828 bt_common_color_bold(),
829 bt_common_color_reset(),
830 (int) bt_plugin_get_source_component_class_count(plugin));
831 printf(" %sFilter component classes%s: %d\n",
832 bt_common_color_bold(),
833 bt_common_color_reset(),
834 (int) bt_plugin_get_filter_component_class_count(plugin));
835 printf(" %sSink component classes%s: %d\n",
836 bt_common_color_bold(),
837 bt_common_color_reset(),
838 (int) bt_plugin_get_sink_component_class_count(plugin));
839
840 if (strlen(cfg->cmd_data.help.cfg_component->comp_cls_name->str) == 0) {
841 /* Plugin help only */
842 goto end;
843 }
844
845 needed_comp_cls = find_component_class(
846 cfg->cmd_data.help.cfg_component->plugin_name->str,
847 cfg->cmd_data.help.cfg_component->comp_cls_name->str,
848 cfg->cmd_data.help.cfg_component->type);
849 if (!needed_comp_cls) {
850 BT_CLI_LOGE_APPEND_CAUSE(
851 "Cannot find component class: plugin-name=\"%s\", "
852 "comp-cls-name=\"%s\", comp-cls-type=%d",
853 cfg->cmd_data.help.cfg_component->plugin_name->str,
854 cfg->cmd_data.help.cfg_component->comp_cls_name->str,
855 cfg->cmd_data.help.cfg_component->type);
856 ret = -1;
857 goto end;
858 }
859
860 printf("\n");
861 print_component_class_help(
862 cfg->cmd_data.help.cfg_component->plugin_name->str,
863 needed_comp_cls);
864
865 end:
866 bt_component_class_put_ref(needed_comp_cls);
867 bt_plugin_put_ref(plugin);
868 return ret;
869 }
870
871 typedef void *(* plugin_borrow_comp_cls_by_index_func_t)(const bt_plugin *,
872 uint64_t);
873 typedef const bt_component_class *(* spec_comp_cls_borrow_comp_cls_func_t)(
874 void *);
875
876 void cmd_list_plugins_print_component_classes(const bt_plugin *plugin,
877 const char *cc_type_name, uint64_t count,
878 plugin_borrow_comp_cls_by_index_func_t borrow_comp_cls_by_index_func,
879 spec_comp_cls_borrow_comp_cls_func_t spec_comp_cls_borrow_comp_cls_func)
880 {
881 uint64_t i;
882
883 if (count == 0) {
884 printf(" %s%s component classes%s: (none)\n",
885 bt_common_color_bold(),
886 cc_type_name,
887 bt_common_color_reset());
888 goto end;
889 } else {
890 printf(" %s%s component classes%s:\n",
891 bt_common_color_bold(),
892 cc_type_name,
893 bt_common_color_reset());
894 }
895
896 for (i = 0; i < count; i++) {
897 const bt_component_class *comp_class =
898 spec_comp_cls_borrow_comp_cls_func(
899 borrow_comp_cls_by_index_func(plugin, i));
900 const char *comp_class_name =
901 bt_component_class_get_name(comp_class);
902 const char *comp_class_description =
903 bt_component_class_get_description(comp_class);
904 bt_component_class_type type =
905 bt_component_class_get_type(comp_class);
906
907 printf(" ");
908 print_plugin_comp_cls_opt(stdout,
909 bt_plugin_get_name(plugin), comp_class_name,
910 type);
911
912 if (comp_class_description) {
913 printf(": %s", comp_class_description);
914 }
915
916 printf("\n");
917 }
918
919 end:
920 return;
921 }
922
923 static
924 int cmd_list_plugins(struct bt_config *cfg)
925 {
926 int ret = 0;
927 int plugins_count, component_classes_count = 0, i;
928
929 printf("From the following plugin paths:\n\n");
930 print_value(stdout, cfg->plugin_paths, 2);
931 printf("\n");
932 plugins_count = get_loaded_plugins_count();
933 if (plugins_count == 0) {
934 printf("No plugins found.\n");
935 goto end;
936 }
937
938 for (i = 0; i < plugins_count; i++) {
939 const bt_plugin *plugin = borrow_loaded_plugin(i);
940
941 component_classes_count +=
942 bt_plugin_get_source_component_class_count(plugin) +
943 bt_plugin_get_filter_component_class_count(plugin) +
944 bt_plugin_get_sink_component_class_count(plugin);
945 }
946
947 printf("Found %s%d%s component classes in %s%d%s plugins.\n",
948 bt_common_color_bold(),
949 component_classes_count,
950 bt_common_color_reset(),
951 bt_common_color_bold(),
952 plugins_count,
953 bt_common_color_reset());
954
955 for (i = 0; i < plugins_count; i++) {
956 const bt_plugin *plugin = borrow_loaded_plugin(i);
957
958 printf("\n");
959 print_plugin_info(plugin);
960 cmd_list_plugins_print_component_classes(plugin, "Source",
961 bt_plugin_get_source_component_class_count(plugin),
962 (plugin_borrow_comp_cls_by_index_func_t)
963 bt_plugin_borrow_source_component_class_by_index_const,
964 (spec_comp_cls_borrow_comp_cls_func_t)
965 bt_component_class_source_as_component_class);
966 cmd_list_plugins_print_component_classes(plugin, "Filter",
967 bt_plugin_get_filter_component_class_count(plugin),
968 (plugin_borrow_comp_cls_by_index_func_t)
969 bt_plugin_borrow_filter_component_class_by_index_const,
970 (spec_comp_cls_borrow_comp_cls_func_t)
971 bt_component_class_filter_as_component_class);
972 cmd_list_plugins_print_component_classes(plugin, "Sink",
973 bt_plugin_get_sink_component_class_count(plugin),
974 (plugin_borrow_comp_cls_by_index_func_t)
975 bt_plugin_borrow_sink_component_class_by_index_const,
976 (spec_comp_cls_borrow_comp_cls_func_t)
977 bt_component_class_sink_as_component_class);
978 }
979
980 end:
981 return ret;
982 }
983
984 static
985 int cmd_print_lttng_live_sessions(struct bt_config *cfg)
986 {
987 int ret = 0;
988 const bt_component_class *comp_cls = NULL;
989 const bt_value *results = NULL;
990 bt_value *params = NULL;
991 const bt_value *map = NULL;
992 const bt_value *v = NULL;
993 static const char * const plugin_name = "ctf";
994 static const char * const comp_cls_name = "lttng-live";
995 static const bt_component_class_type comp_cls_type =
996 BT_COMPONENT_CLASS_TYPE_SOURCE;
997 int64_t array_size, i;
998 const char *fail_reason = NULL;
999 FILE *out_stream = stdout;
1000
1001 BT_ASSERT(cfg->cmd_data.print_lttng_live_sessions.url);
1002 comp_cls = find_component_class(plugin_name, comp_cls_name,
1003 comp_cls_type);
1004 if (!comp_cls) {
1005 BT_CLI_LOGE_APPEND_CAUSE(
1006 "Cannot find component class: plugin-name=\"%s\", "
1007 "comp-cls-name=\"%s\", comp-cls-type=%d",
1008 plugin_name, comp_cls_name,
1009 BT_COMPONENT_CLASS_TYPE_SOURCE);
1010 goto error;
1011 }
1012
1013 params = bt_value_map_create();
1014 if (!params) {
1015 goto error;
1016 }
1017
1018 ret = bt_value_map_insert_string_entry(params, "url",
1019 cfg->cmd_data.print_lttng_live_sessions.url->str);
1020 if (ret) {
1021 goto error;
1022 }
1023
1024 ret = query(cfg, comp_cls, "sessions", params,
1025 &results, &fail_reason);
1026 if (ret) {
1027 goto failed;
1028 }
1029
1030 BT_ASSERT(results);
1031
1032 if (!bt_value_is_array(results)) {
1033 BT_CLI_LOGE_APPEND_CAUSE(
1034 "Expecting an array for LTTng live `sessions` query.");
1035 goto error;
1036 }
1037
1038 if (cfg->cmd_data.print_lttng_live_sessions.output_path->len > 0) {
1039 out_stream =
1040 fopen(cfg->cmd_data.print_lttng_live_sessions.output_path->str,
1041 "w");
1042 if (!out_stream) {
1043 ret = -1;
1044 BT_LOGE_ERRNO("Cannot open file for writing",
1045 ": path=\"%s\"",
1046 cfg->cmd_data.print_lttng_live_sessions.output_path->str);
1047 (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(
1048 "Babeltrace CLI",
1049 "Cannot open file for writing: path=\"%s\"",
1050 cfg->cmd_data.print_lttng_live_sessions.output_path->str);
1051 goto end;
1052 }
1053 }
1054
1055 array_size = bt_value_array_get_size(results);
1056 for (i = 0; i < array_size; i++) {
1057 const char *url_text;
1058 int64_t timer_us, streams, clients;
1059
1060 map = bt_value_array_borrow_element_by_index_const(results, i);
1061 if (!map) {
1062 BT_CLI_LOGE_APPEND_CAUSE("Unexpected empty array entry.");
1063 goto error;
1064 }
1065 if (!bt_value_is_map(map)) {
1066 BT_CLI_LOGE_APPEND_CAUSE("Unexpected entry type.");
1067 goto error;
1068 }
1069
1070 v = bt_value_map_borrow_entry_value_const(map, "url");
1071 if (!v) {
1072 BT_CLI_LOGE_APPEND_CAUSE("Missing `url` entry.");
1073 goto error;
1074 }
1075 url_text = bt_value_string_get(v);
1076 fprintf(out_stream, "%s", url_text);
1077 v = bt_value_map_borrow_entry_value_const(map, "timer-us");
1078 if (!v) {
1079 BT_CLI_LOGE_APPEND_CAUSE("Missing `timer-us` entry.");
1080 goto error;
1081 }
1082 timer_us = bt_value_integer_signed_get(v);
1083 fprintf(out_stream, " (timer = %" PRIu64 ", ", timer_us);
1084 v = bt_value_map_borrow_entry_value_const(map, "stream-count");
1085 if (!v) {
1086 BT_CLI_LOGE_APPEND_CAUSE(
1087 "Missing `stream-count` entry.");
1088 goto error;
1089 }
1090 streams = bt_value_integer_signed_get(v);
1091 fprintf(out_stream, "%" PRIu64 " stream(s), ", streams);
1092 v = bt_value_map_borrow_entry_value_const(map, "client-count");
1093 if (!v) {
1094 BT_CLI_LOGE_APPEND_CAUSE(
1095 "Missing `client-count` entry.");
1096 goto error;
1097 }
1098 clients = bt_value_integer_signed_get(v);
1099 fprintf(out_stream, "%" PRIu64 " client(s) connected)\n", clients);
1100 }
1101
1102 goto end;
1103
1104 failed:
1105 BT_CLI_LOGE_APPEND_CAUSE("Failed to query `sessions` object: %s",
1106 fail_reason);
1107
1108 error:
1109 ret = -1;
1110
1111 end:
1112 bt_value_put_ref(results);
1113 bt_value_put_ref(params);
1114 bt_component_class_put_ref(comp_cls);
1115
1116 if (out_stream && out_stream != stdout) {
1117 int fclose_ret = fclose(out_stream);
1118
1119 if (fclose_ret) {
1120 BT_LOGE_ERRNO("Cannot close file stream",
1121 ": path=\"%s\"",
1122 cfg->cmd_data.print_lttng_live_sessions.output_path->str);
1123 }
1124 }
1125
1126 return ret;
1127 }
1128
1129 static
1130 int cmd_print_ctf_metadata(struct bt_config *cfg)
1131 {
1132 int ret = 0;
1133 const bt_component_class *comp_cls = NULL;
1134 const bt_value *results = NULL;
1135 bt_value *params = NULL;
1136 const bt_value *metadata_text_value = NULL;
1137 const char *metadata_text = NULL;
1138 static const char * const plugin_name = "ctf";
1139 static const char * const comp_cls_name = "fs";
1140 static const bt_component_class_type comp_cls_type =
1141 BT_COMPONENT_CLASS_TYPE_SOURCE;
1142 const char *fail_reason = NULL;
1143 FILE *out_stream = stdout;
1144
1145 BT_ASSERT(cfg->cmd_data.print_ctf_metadata.path);
1146 comp_cls = find_component_class(plugin_name, comp_cls_name,
1147 comp_cls_type);
1148 if (!comp_cls) {
1149 BT_CLI_LOGE_APPEND_CAUSE(
1150 "Cannot find component class: plugin-name=\"%s\", "
1151 "comp-cls-name=\"%s\", comp-cls-type=%d",
1152 plugin_name, comp_cls_name,
1153 BT_COMPONENT_CLASS_TYPE_SOURCE);
1154 ret = -1;
1155 goto end;
1156 }
1157
1158 params = bt_value_map_create();
1159 if (!params) {
1160 ret = -1;
1161 goto end;
1162 }
1163
1164 ret = bt_value_map_insert_string_entry(params, "path",
1165 cfg->cmd_data.print_ctf_metadata.path->str);
1166 if (ret) {
1167 ret = -1;
1168 goto end;
1169 }
1170
1171 ret = query(cfg, comp_cls, "metadata-info",
1172 params, &results, &fail_reason);
1173 if (ret) {
1174 goto failed;
1175 }
1176
1177 metadata_text_value = bt_value_map_borrow_entry_value_const(results,
1178 "text");
1179 if (!metadata_text_value) {
1180 BT_CLI_LOGE_APPEND_CAUSE(
1181 "Cannot find `text` string value in the resulting metadata info object.");
1182 ret = -1;
1183 goto end;
1184 }
1185
1186 metadata_text = bt_value_string_get(metadata_text_value);
1187
1188 if (cfg->cmd_data.print_ctf_metadata.output_path->len > 0) {
1189 out_stream =
1190 fopen(cfg->cmd_data.print_ctf_metadata.output_path->str,
1191 "w");
1192 if (!out_stream) {
1193 BT_LOGE_ERRNO("Cannot open file for writing",
1194 ": path=\"%s\"",
1195 cfg->cmd_data.print_ctf_metadata.output_path->str);
1196 (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(
1197 "Babeltrace CLI",
1198 "Cannot open file for writing: path=\"%s\"",
1199 cfg->cmd_data.print_ctf_metadata.output_path->str);
1200 ret = -1;
1201 goto end;
1202 }
1203 }
1204
1205 ret = fprintf(out_stream, "%s\n", metadata_text);
1206 if (ret < 0) {
1207 BT_CLI_LOGE_APPEND_CAUSE(
1208 "Cannot write whole metadata text to output stream: "
1209 "ret=%d", ret);
1210 goto end;
1211 }
1212
1213 ret = 0;
1214 goto end;
1215
1216 failed:
1217 ret = -1;
1218 BT_CLI_LOGE_APPEND_CAUSE(
1219 "Failed to query `metadata-info` object: %s", fail_reason);
1220
1221 end:
1222 bt_value_put_ref(results);
1223 bt_value_put_ref(params);
1224 bt_component_class_put_ref(comp_cls);
1225
1226 if (out_stream && out_stream != stdout) {
1227 int fclose_ret = fclose(out_stream);
1228
1229 if (fclose_ret) {
1230 BT_LOGE_ERRNO("Cannot close file stream",
1231 ": path=\"%s\"",
1232 cfg->cmd_data.print_ctf_metadata.output_path->str);
1233 }
1234 }
1235
1236 return ret;
1237 }
1238
1239 struct port_id {
1240 char *instance_name;
1241 char *port_name;
1242 };
1243
1244 struct trace_range {
1245 uint64_t intersection_range_begin_ns;
1246 uint64_t intersection_range_end_ns;
1247 };
1248
1249 static
1250 guint port_id_hash(gconstpointer v)
1251 {
1252 const struct port_id *id = v;
1253
1254 BT_ASSERT(id->instance_name);
1255 BT_ASSERT(id->port_name);
1256
1257 return g_str_hash(id->instance_name) ^ g_str_hash(id->port_name);
1258 }
1259
1260 static
1261 gboolean port_id_equal(gconstpointer v1, gconstpointer v2)
1262 {
1263 const struct port_id *id1 = v1;
1264 const struct port_id *id2 = v2;
1265
1266 return strcmp(id1->instance_name, id2->instance_name) == 0 &&
1267 strcmp(id1->port_name, id2->port_name) == 0;
1268 }
1269
1270 static
1271 void port_id_destroy(gpointer data)
1272 {
1273 struct port_id *id = data;
1274
1275 free(id->instance_name);
1276 free(id->port_name);
1277 free(id);
1278 }
1279
1280 static
1281 void trace_range_destroy(gpointer data)
1282 {
1283 free(data);
1284 }
1285
1286 struct cmd_run_ctx {
1287 /* Owned by this */
1288 GHashTable *src_components;
1289
1290 /* Owned by this */
1291 GHashTable *flt_components;
1292
1293 /* Owned by this */
1294 GHashTable *sink_components;
1295
1296 /* Owned by this */
1297 bt_graph *graph;
1298
1299 /* Weak */
1300 struct bt_config *cfg;
1301
1302 bool connect_ports;
1303
1304 bool stream_intersection_mode;
1305
1306 /*
1307 * Association of struct port_id -> struct trace_range.
1308 */
1309 GHashTable *intersections;
1310 };
1311
1312 /* Returns a timestamp of the form "(-)s.ns" */
1313 static
1314 char *s_from_ns(int64_t ns)
1315 {
1316 int ret;
1317 char *s_ret = NULL;
1318 bool is_negative;
1319 int64_t ts_sec_abs, ts_nsec_abs;
1320 int64_t ts_sec = ns / NSEC_PER_SEC;
1321 int64_t ts_nsec = ns % NSEC_PER_SEC;
1322
1323 if (ts_sec >= 0 && ts_nsec >= 0) {
1324 is_negative = false;
1325 ts_sec_abs = ts_sec;
1326 ts_nsec_abs = ts_nsec;
1327 } else if (ts_sec > 0 && ts_nsec < 0) {
1328 is_negative = false;
1329 ts_sec_abs = ts_sec - 1;
1330 ts_nsec_abs = NSEC_PER_SEC + ts_nsec;
1331 } else if (ts_sec == 0 && ts_nsec < 0) {
1332 is_negative = true;
1333 ts_sec_abs = ts_sec;
1334 ts_nsec_abs = -ts_nsec;
1335 } else if (ts_sec < 0 && ts_nsec > 0) {
1336 is_negative = true;
1337 ts_sec_abs = -(ts_sec + 1);
1338 ts_nsec_abs = NSEC_PER_SEC - ts_nsec;
1339 } else if (ts_sec < 0 && ts_nsec == 0) {
1340 is_negative = true;
1341 ts_sec_abs = -ts_sec;
1342 ts_nsec_abs = ts_nsec;
1343 } else { /* (ts_sec < 0 && ts_nsec < 0) */
1344 is_negative = true;
1345 ts_sec_abs = -ts_sec;
1346 ts_nsec_abs = -ts_nsec;
1347 }
1348
1349 ret = asprintf(&s_ret, "%s%" PRId64 ".%09" PRId64,
1350 is_negative ? "-" : "", ts_sec_abs, ts_nsec_abs);
1351 if (ret < 0) {
1352 s_ret = NULL;
1353 }
1354 return s_ret;
1355 }
1356
1357 static
1358 int cmd_run_ctx_connect_upstream_port_to_downstream_component(
1359 struct cmd_run_ctx *ctx,
1360 const bt_component *upstream_comp,
1361 const bt_port_output *out_upstream_port,
1362 struct bt_config_connection *cfg_conn)
1363 {
1364 typedef uint64_t (*input_port_count_func_t)(void *);
1365 typedef const bt_port_input *(*borrow_input_port_by_index_func_t)(
1366 const void *, uint64_t);
1367 const bt_port *upstream_port =
1368 bt_port_output_as_port_const(out_upstream_port);
1369
1370 int ret = 0;
1371 GQuark downstreamp_comp_name_quark;
1372 void *downstream_comp;
1373 uint64_t downstream_port_count;
1374 uint64_t i;
1375 input_port_count_func_t port_count_fn;
1376 borrow_input_port_by_index_func_t port_by_index_fn;
1377 bt_graph_connect_ports_status connect_ports_status =
1378 BT_GRAPH_CONNECT_PORTS_STATUS_OK;
1379 bool insert_trimmer = false;
1380 bt_value *trimmer_params = NULL;
1381 char *intersection_begin = NULL;
1382 char *intersection_end = NULL;
1383 const bt_component_filter *trimmer = NULL;
1384 const bt_component_class_filter *trimmer_class = NULL;
1385 const bt_port_input *trimmer_input = NULL;
1386 const bt_port_output *trimmer_output = NULL;
1387
1388 if (ctx->intersections &&
1389 bt_component_get_class_type(upstream_comp) ==
1390 BT_COMPONENT_CLASS_TYPE_SOURCE) {
1391 struct trace_range *range;
1392 struct port_id port_id = {
1393 .instance_name = (char *) bt_component_get_name(upstream_comp),
1394 .port_name = (char *) bt_port_get_name(upstream_port)
1395 };
1396
1397 if (!port_id.instance_name || !port_id.port_name) {
1398 goto error;
1399 }
1400
1401 range = (struct trace_range *) g_hash_table_lookup(
1402 ctx->intersections, &port_id);
1403 if (range) {
1404 bt_value_map_insert_entry_status insert_status;
1405
1406 intersection_begin = s_from_ns(
1407 range->intersection_range_begin_ns);
1408 intersection_end = s_from_ns(
1409 range->intersection_range_end_ns);
1410 if (!intersection_begin || !intersection_end) {
1411 BT_CLI_LOGE_APPEND_CAUSE(
1412 "Cannot create trimmer argument timestamp string.");
1413 goto error;
1414 }
1415
1416 insert_trimmer = true;
1417 trimmer_params = bt_value_map_create();
1418 if (!trimmer_params) {
1419 goto error;
1420 }
1421
1422 insert_status = bt_value_map_insert_string_entry(
1423 trimmer_params, "begin", intersection_begin);
1424 if (insert_status < 0) {
1425 goto error;
1426 }
1427 insert_status = bt_value_map_insert_string_entry(
1428 trimmer_params,
1429 "end", intersection_end);
1430 if (insert_status < 0) {
1431 goto error;
1432 }
1433 }
1434
1435 trimmer_class = find_filter_component_class("utils", "trimmer");
1436 if (!trimmer_class) {
1437 goto error;
1438 }
1439 }
1440
1441 BT_LOGI("Connecting upstream port to the next available downstream port: "
1442 "upstream-port-addr=%p, upstream-port-name=\"%s\", "
1443 "downstream-comp-name=\"%s\", conn-arg=\"%s\"",
1444 upstream_port, bt_port_get_name(upstream_port),
1445 cfg_conn->downstream_comp_name->str,
1446 cfg_conn->arg->str);
1447 downstreamp_comp_name_quark = g_quark_from_string(
1448 cfg_conn->downstream_comp_name->str);
1449 BT_ASSERT(downstreamp_comp_name_quark > 0);
1450 downstream_comp = g_hash_table_lookup(ctx->flt_components,
1451 GUINT_TO_POINTER(downstreamp_comp_name_quark));
1452 port_count_fn = (input_port_count_func_t)
1453 bt_component_filter_get_input_port_count;
1454 port_by_index_fn = (borrow_input_port_by_index_func_t)
1455 bt_component_filter_borrow_input_port_by_index_const;
1456
1457 if (!downstream_comp) {
1458 downstream_comp = g_hash_table_lookup(ctx->sink_components,
1459 GUINT_TO_POINTER(downstreamp_comp_name_quark));
1460 port_count_fn = (input_port_count_func_t)
1461 bt_component_sink_get_input_port_count;
1462 port_by_index_fn = (borrow_input_port_by_index_func_t)
1463 bt_component_sink_borrow_input_port_by_index_const;
1464 }
1465
1466 if (!downstream_comp) {
1467 BT_CLI_LOGE_APPEND_CAUSE("Cannot find downstream component: "
1468 "comp-name=\"%s\", conn-arg=\"%s\"",
1469 cfg_conn->downstream_comp_name->str,
1470 cfg_conn->arg->str);
1471 goto error;
1472 }
1473
1474 downstream_port_count = port_count_fn(downstream_comp);
1475
1476 for (i = 0; i < downstream_port_count; i++) {
1477 const bt_port_input *in_downstream_port =
1478 port_by_index_fn(downstream_comp, i);
1479 const bt_port *downstream_port =
1480 bt_port_input_as_port_const(in_downstream_port);
1481 const char *upstream_port_name;
1482 const char *downstream_port_name;
1483
1484 BT_ASSERT(downstream_port);
1485
1486 /* Skip port if it's already connected. */
1487 if (bt_port_is_connected(downstream_port)) {
1488 BT_LOGI("Skipping downstream port: already connected: "
1489 "port-addr=%p, port-name=\"%s\"",
1490 downstream_port,
1491 bt_port_get_name(downstream_port));
1492 continue;
1493 }
1494
1495 downstream_port_name = bt_port_get_name(downstream_port);
1496 BT_ASSERT(downstream_port_name);
1497 upstream_port_name = bt_port_get_name(upstream_port);
1498 BT_ASSERT(upstream_port_name);
1499
1500 if (!bt_common_star_glob_match(
1501 cfg_conn->downstream_port_glob->str, SIZE_MAX,
1502 downstream_port_name, SIZE_MAX)) {
1503 continue;
1504 }
1505
1506 if (insert_trimmer) {
1507 /*
1508 * In order to insert the trimmer between the
1509 * two components that were being connected, we
1510 * create a connection configuration entry which
1511 * describes a connection from the trimmer's
1512 * output to the original input that was being
1513 * connected.
1514 *
1515 * Hence, the creation of the trimmer will cause
1516 * the graph "new port" listener to establish
1517 * all downstream connections as its output port
1518 * is connected. We will then establish the
1519 * connection between the original upstream
1520 * source and the trimmer.
1521 */
1522 char *trimmer_name = NULL;
1523 bt_graph_add_component_status add_comp_status;
1524
1525 ret = asprintf(&trimmer_name,
1526 "stream-intersection-trimmer-%s",
1527 upstream_port_name);
1528 if (ret < 0) {
1529 goto error;
1530 }
1531 ret = 0;
1532
1533 ctx->connect_ports = false;
1534 add_comp_status = bt_graph_add_filter_component(
1535 ctx->graph, trimmer_class, trimmer_name,
1536 trimmer_params, ctx->cfg->log_level,
1537 &trimmer);
1538 free(trimmer_name);
1539 if (add_comp_status !=
1540 BT_GRAPH_ADD_COMPONENT_STATUS_OK) {
1541 goto error;
1542 }
1543 BT_ASSERT(trimmer);
1544
1545 trimmer_input =
1546 bt_component_filter_borrow_input_port_by_index_const(
1547 trimmer, 0);
1548 if (!trimmer_input) {
1549 goto error;
1550 }
1551 trimmer_output =
1552 bt_component_filter_borrow_output_port_by_index_const(
1553 trimmer, 0);
1554 if (!trimmer_output) {
1555 goto error;
1556 }
1557
1558 /*
1559 * Replace the current downstream port by the trimmer's
1560 * upstream port.
1561 */
1562 in_downstream_port = trimmer_input;
1563 downstream_port =
1564 bt_port_input_as_port_const(in_downstream_port);
1565 downstream_port_name = bt_port_get_name(
1566 downstream_port);
1567 BT_ASSERT(downstream_port_name);
1568 }
1569
1570 /* We have a winner! */
1571 connect_ports_status = bt_graph_connect_ports(ctx->graph,
1572 out_upstream_port, in_downstream_port, NULL);
1573 downstream_port = NULL;
1574 switch (connect_ports_status) {
1575 case BT_GRAPH_CONNECT_PORTS_STATUS_OK:
1576 break;
1577 default:
1578 BT_CLI_LOGE_APPEND_CAUSE(
1579 "Cannot create connection: graph refuses to connect ports: "
1580 "upstream-comp-addr=%p, upstream-comp-name=\"%s\", "
1581 "upstream-port-addr=%p, upstream-port-name=\"%s\", "
1582 "downstream-comp-addr=%p, downstream-comp-name=\"%s\", "
1583 "downstream-port-addr=%p, downstream-port-name=\"%s\", "
1584 "conn-arg=\"%s\"",
1585 upstream_comp, bt_component_get_name(upstream_comp),
1586 upstream_port, bt_port_get_name(upstream_port),
1587 downstream_comp, cfg_conn->downstream_comp_name->str,
1588 downstream_port, downstream_port_name,
1589 cfg_conn->arg->str);
1590 goto error;
1591 }
1592
1593 BT_LOGI("Connected component ports: "
1594 "upstream-comp-addr=%p, upstream-comp-name=\"%s\", "
1595 "upstream-port-addr=%p, upstream-port-name=\"%s\", "
1596 "downstream-comp-addr=%p, downstream-comp-name=\"%s\", "
1597 "downstream-port-addr=%p, downstream-port-name=\"%s\", "
1598 "conn-arg=\"%s\"",
1599 upstream_comp, bt_component_get_name(upstream_comp),
1600 upstream_port, bt_port_get_name(upstream_port),
1601 downstream_comp, cfg_conn->downstream_comp_name->str,
1602 downstream_port, downstream_port_name,
1603 cfg_conn->arg->str);
1604
1605 if (insert_trimmer) {
1606 /*
1607 * The first connection, from the source to the trimmer,
1608 * has been done. We now connect the trimmer to the
1609 * original downstream port.
1610 */
1611 ret = cmd_run_ctx_connect_upstream_port_to_downstream_component(
1612 ctx,
1613 bt_component_filter_as_component_const(trimmer),
1614 trimmer_output, cfg_conn);
1615 if (ret) {
1616 goto error;
1617 }
1618 ctx->connect_ports = true;
1619 }
1620
1621 /*
1622 * We found a matching downstream port: the search is
1623 * over.
1624 */
1625 goto end;
1626 }
1627
1628 /* No downstream port found */
1629 BT_CLI_LOGE_APPEND_CAUSE(
1630 "Cannot create connection: cannot find a matching downstream port for upstream port: "
1631 "upstream-port-addr=%p, upstream-port-name=\"%s\", "
1632 "downstream-comp-name=\"%s\", conn-arg=\"%s\"",
1633 upstream_port, bt_port_get_name(upstream_port),
1634 cfg_conn->downstream_comp_name->str,
1635 cfg_conn->arg->str);
1636
1637 error:
1638 ret = -1;
1639
1640 end:
1641 free(intersection_begin);
1642 free(intersection_end);
1643 BT_VALUE_PUT_REF_AND_RESET(trimmer_params);
1644 BT_COMPONENT_CLASS_FILTER_PUT_REF_AND_RESET(trimmer_class);
1645 BT_COMPONENT_FILTER_PUT_REF_AND_RESET(trimmer);
1646 return ret;
1647 }
1648
1649 static
1650 int cmd_run_ctx_connect_upstream_port(struct cmd_run_ctx *ctx,
1651 const bt_port_output *upstream_port)
1652 {
1653 int ret = 0;
1654 const char *upstream_port_name;
1655 const char *upstream_comp_name;
1656 const bt_component *upstream_comp = NULL;
1657 size_t i;
1658
1659 BT_ASSERT(ctx);
1660 BT_ASSERT(upstream_port);
1661 upstream_port_name = bt_port_get_name(
1662 bt_port_output_as_port_const(upstream_port));
1663 BT_ASSERT(upstream_port_name);
1664 upstream_comp = bt_port_borrow_component_const(
1665 bt_port_output_as_port_const(upstream_port));
1666 BT_ASSERT(upstream_comp);
1667 upstream_comp_name = bt_component_get_name(upstream_comp);
1668 BT_ASSERT(upstream_comp_name);
1669 BT_LOGI("Connecting upstream port: comp-addr=%p, comp-name=\"%s\", "
1670 "port-addr=%p, port-name=\"%s\"",
1671 upstream_comp, upstream_comp_name,
1672 upstream_port, upstream_port_name);
1673
1674 for (i = 0; i < ctx->cfg->cmd_data.run.connections->len; i++) {
1675 struct bt_config_connection *cfg_conn =
1676 g_ptr_array_index(
1677 ctx->cfg->cmd_data.run.connections, i);
1678
1679 if (strcmp(cfg_conn->upstream_comp_name->str,
1680 upstream_comp_name)) {
1681 continue;
1682 }
1683
1684 if (!bt_common_star_glob_match(
1685 cfg_conn->upstream_port_glob->str,
1686 SIZE_MAX, upstream_port_name, SIZE_MAX)) {
1687 continue;
1688 }
1689
1690 ret = cmd_run_ctx_connect_upstream_port_to_downstream_component(
1691 ctx, upstream_comp, upstream_port, cfg_conn);
1692 if (ret) {
1693 BT_CLI_LOGE_APPEND_CAUSE(
1694 "Cannot connect upstream port: "
1695 "port-addr=%p, port-name=\"%s\"",
1696 upstream_port,
1697 upstream_port_name);
1698 goto error;
1699 }
1700 goto end;
1701 }
1702
1703 BT_CLI_LOGE_APPEND_CAUSE(
1704 "Cannot connect upstream port: port does not match any connection argument: "
1705 "port-addr=%p, port-name=\"%s\"", upstream_port,
1706 upstream_port_name);
1707
1708 error:
1709 ret = -1;
1710
1711 end:
1712 return ret;
1713 }
1714
1715 static
1716 bt_graph_listener_func_status
1717 graph_output_port_added_listener(struct cmd_run_ctx *ctx,
1718 const bt_port_output *out_port)
1719 {
1720 const bt_component *comp;
1721 const bt_port *port = bt_port_output_as_port_const(out_port);
1722 bt_graph_listener_func_status ret =
1723 BT_GRAPH_LISTENER_FUNC_STATUS_OK;
1724
1725 comp = bt_port_borrow_component_const(port);
1726 BT_LOGI("Port added to a graph's component: comp-addr=%p, "
1727 "comp-name=\"%s\", port-addr=%p, port-name=\"%s\"",
1728 comp, comp ? bt_component_get_name(comp) : "",
1729 port, bt_port_get_name(port));
1730
1731 if (!ctx->connect_ports) {
1732 goto end;
1733 }
1734
1735 BT_ASSERT(comp);
1736
1737 if (bt_port_is_connected(port)) {
1738 BT_LOGW_STR("Port is already connected.");
1739 goto end;
1740 }
1741
1742 if (cmd_run_ctx_connect_upstream_port(ctx, out_port)) {
1743 BT_CLI_LOGE_APPEND_CAUSE("Cannot connect upstream port.");
1744 ret = BT_GRAPH_LISTENER_FUNC_STATUS_ERROR;
1745 goto end;
1746 }
1747
1748 end:
1749 return ret;
1750 }
1751
1752 static
1753 bt_graph_listener_func_status graph_source_output_port_added_listener(
1754 const bt_component_source *component,
1755 const bt_port_output *port, void *data)
1756 {
1757 return graph_output_port_added_listener(data, port);
1758 }
1759
1760 static
1761 bt_graph_listener_func_status graph_filter_output_port_added_listener(
1762 const bt_component_filter *component,
1763 const bt_port_output *port, void *data)
1764 {
1765 return graph_output_port_added_listener(data, port);
1766 }
1767
1768 static
1769 void cmd_run_ctx_destroy(struct cmd_run_ctx *ctx)
1770 {
1771 if (!ctx) {
1772 return;
1773 }
1774
1775 if (ctx->src_components) {
1776 g_hash_table_destroy(ctx->src_components);
1777 ctx->src_components = NULL;
1778 }
1779
1780 if (ctx->flt_components) {
1781 g_hash_table_destroy(ctx->flt_components);
1782 ctx->flt_components = NULL;
1783 }
1784
1785 if (ctx->sink_components) {
1786 g_hash_table_destroy(ctx->sink_components);
1787 ctx->sink_components = NULL;
1788 }
1789
1790 if (ctx->intersections) {
1791 g_hash_table_destroy(ctx->intersections);
1792 ctx->intersections = NULL;
1793 }
1794
1795 BT_GRAPH_PUT_REF_AND_RESET(ctx->graph);
1796 ctx->cfg = NULL;
1797 }
1798
1799 static
1800 int cmd_run_ctx_init(struct cmd_run_ctx *ctx, struct bt_config *cfg)
1801 {
1802 int ret = 0;
1803 bt_graph_add_listener_status add_listener_status;
1804
1805 ctx->cfg = cfg;
1806 ctx->connect_ports = false;
1807 ctx->src_components = g_hash_table_new_full(g_direct_hash,
1808 g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref);
1809 if (!ctx->src_components) {
1810 goto error;
1811 }
1812
1813 ctx->flt_components = g_hash_table_new_full(g_direct_hash,
1814 g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref);
1815 if (!ctx->flt_components) {
1816 goto error;
1817 }
1818
1819 ctx->sink_components = g_hash_table_new_full(g_direct_hash,
1820 g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref);
1821 if (!ctx->sink_components) {
1822 goto error;
1823 }
1824
1825 if (cfg->cmd_data.run.stream_intersection_mode) {
1826 ctx->stream_intersection_mode = true;
1827 ctx->intersections = g_hash_table_new_full(port_id_hash,
1828 port_id_equal, port_id_destroy, trace_range_destroy);
1829 if (!ctx->intersections) {
1830 goto error;
1831 }
1832 }
1833
1834 ctx->graph = bt_graph_create();
1835 if (!ctx->graph) {
1836 goto error;
1837 }
1838
1839 bt_graph_add_interrupter(ctx->graph, the_interrupter);
1840 add_listener_status = bt_graph_add_source_component_output_port_added_listener(
1841 ctx->graph, graph_source_output_port_added_listener, NULL, ctx,
1842 NULL);
1843 if (add_listener_status != BT_GRAPH_ADD_LISTENER_STATUS_OK) {
1844 BT_CLI_LOGE_APPEND_CAUSE(
1845 "Cannot add \"port added\" listener to graph.");
1846 goto error;
1847 }
1848
1849 add_listener_status = bt_graph_add_filter_component_output_port_added_listener(
1850 ctx->graph, graph_filter_output_port_added_listener, NULL, ctx,
1851 NULL);
1852 if (add_listener_status != BT_GRAPH_ADD_LISTENER_STATUS_OK) {
1853 BT_CLI_LOGE_APPEND_CAUSE(
1854 "Cannot add \"port added\" listener to graph.");
1855 goto error;
1856 }
1857
1858 goto end;
1859
1860 error:
1861 cmd_run_ctx_destroy(ctx);
1862 ret = -1;
1863
1864 end:
1865 return ret;
1866 }
1867
1868 static
1869 int set_stream_intersections(struct cmd_run_ctx *ctx,
1870 struct bt_config_component *cfg_comp,
1871 const bt_component_class_source *src_comp_cls)
1872 {
1873 int ret = 0;
1874 uint64_t trace_idx;
1875 int64_t trace_count;
1876 const char *path = NULL;
1877 const bt_value *query_result = NULL;
1878 const bt_value *trace_info = NULL;
1879 const bt_value *intersection_range = NULL;
1880 const bt_value *intersection_begin = NULL;
1881 const bt_value *intersection_end = NULL;
1882 const bt_value *stream_infos = NULL;
1883 const bt_value *stream_info = NULL;
1884 struct port_id *port_id = NULL;
1885 struct trace_range *trace_range = NULL;
1886 const char *fail_reason = NULL;
1887 const bt_component_class *comp_cls =
1888 bt_component_class_source_as_component_class_const(src_comp_cls);
1889
1890 ret = query(ctx->cfg, comp_cls, "trace-info",
1891 cfg_comp->params, &query_result,
1892 &fail_reason);
1893 if (ret) {
1894 BT_LOGD("Component class does not support the `trace-info` query: %s: "
1895 "comp-class-name=\"%s\"", fail_reason,
1896 bt_component_class_get_name(comp_cls));
1897 ret = -1;
1898 goto error;
1899 }
1900
1901 BT_ASSERT(query_result);
1902
1903 if (!bt_value_is_array(query_result)) {
1904 BT_LOGD("Unexpected format of \'trace-info\' query result: "
1905 "component-class-name=%s",
1906 bt_component_class_get_name(comp_cls));
1907 ret = -1;
1908 goto error;
1909 }
1910
1911 trace_count = bt_value_array_get_size(query_result);
1912 if (trace_count < 0) {
1913 ret = -1;
1914 goto error;
1915 }
1916
1917 for (trace_idx = 0; trace_idx < trace_count; trace_idx++) {
1918 int64_t begin, end;
1919 uint64_t stream_idx;
1920 int64_t stream_count;
1921
1922 trace_info = bt_value_array_borrow_element_by_index_const(
1923 query_result, trace_idx);
1924 if (!trace_info || !bt_value_is_map(trace_info)) {
1925 ret = -1;
1926 BT_LOGD_STR("Cannot retrieve trace from query result.");
1927 goto error;
1928 }
1929
1930 intersection_range = bt_value_map_borrow_entry_value_const(
1931 trace_info, "intersection-range-ns");
1932 if (!intersection_range) {
1933 ret = -1;
1934 BT_LOGD_STR("Cannot retrieve \'intersetion-range-ns\' field from query result.");
1935 goto error;
1936 }
1937
1938 intersection_begin = bt_value_map_borrow_entry_value_const(intersection_range,
1939 "begin");
1940 if (!intersection_begin) {
1941 ret = -1;
1942 BT_LOGD_STR("Cannot retrieve intersection-range-ns \'begin\' field from query result.");
1943 goto error;
1944 }
1945
1946 intersection_end = bt_value_map_borrow_entry_value_const(intersection_range,
1947 "end");
1948 if (!intersection_end) {
1949 ret = -1;
1950 BT_LOGD_STR("Cannot retrieve intersection-range-ns \'end\' field from query result.");
1951 goto error;
1952 }
1953
1954 begin = bt_value_integer_signed_get(intersection_begin);
1955 end = bt_value_integer_signed_get(intersection_end);
1956
1957 if (begin < 0 || end < 0 || end < begin) {
1958 BT_CLI_LOGE_APPEND_CAUSE(
1959 "Invalid trace stream intersection values: "
1960 "intersection-range-ns:begin=%" PRId64
1961 ", intersection-range-ns:end=%" PRId64,
1962 begin, end);
1963 ret = -1;
1964 goto error;
1965 }
1966
1967 stream_infos = bt_value_map_borrow_entry_value_const(trace_info,
1968 "streams");
1969 if (!stream_infos || !bt_value_is_array(stream_infos)) {
1970 ret = -1;
1971 BT_LOGD_STR("Cannot retrieve stream information from trace in query result.");
1972 goto error;
1973 }
1974
1975 stream_count = bt_value_array_get_size(stream_infos);
1976 if (stream_count < 0) {
1977 ret = -1;
1978 goto error;
1979 }
1980
1981 for (stream_idx = 0; stream_idx < stream_count; stream_idx++) {
1982 const bt_value *port_name;
1983
1984 port_id = g_new0(struct port_id, 1);
1985 if (!port_id) {
1986 ret = -1;
1987 BT_CLI_LOGE_APPEND_CAUSE(
1988 "Cannot allocate memory for port_id structure.");
1989 goto error;
1990 }
1991 port_id->instance_name = strdup(cfg_comp->instance_name->str);
1992 if (!port_id->instance_name) {
1993 ret = -1;
1994 BT_CLI_LOGE_APPEND_CAUSE(
1995 "Cannot allocate memory for port_id component instance name.");
1996 goto error;
1997 }
1998
1999 trace_range = g_new0(struct trace_range, 1);
2000 if (!trace_range) {
2001 ret = -1;
2002 BT_CLI_LOGE_APPEND_CAUSE(
2003 "Cannot allocate memory for trace_range structure.");
2004 goto error;
2005 }
2006 trace_range->intersection_range_begin_ns = begin;
2007 trace_range->intersection_range_end_ns = end;
2008
2009 stream_info = bt_value_array_borrow_element_by_index_const(
2010 stream_infos, stream_idx);
2011 if (!stream_info || !bt_value_is_map(stream_info)) {
2012 ret = -1;
2013 BT_CLI_LOGE_APPEND_CAUSE(
2014 "Cannot retrieve stream informations from trace in query result.");
2015 goto error;
2016 }
2017
2018 port_name = bt_value_map_borrow_entry_value_const(stream_info, "port-name");
2019 if (!port_name || !bt_value_is_string(port_name)) {
2020 ret = -1;
2021 BT_CLI_LOGE_APPEND_CAUSE(
2022 "Cannot retrieve port name in query result.");
2023 goto error;
2024 }
2025
2026 port_id->port_name = g_strdup(bt_value_string_get(port_name));
2027 if (!port_id->port_name) {
2028 ret = -1;
2029 BT_CLI_LOGE_APPEND_CAUSE(
2030 "Cannot allocate memory for port_id port_name.");
2031 goto error;
2032 }
2033
2034 BT_LOGD("Inserting stream intersection ");
2035
2036 g_hash_table_insert(ctx->intersections, port_id, trace_range);
2037
2038 port_id = NULL;
2039 trace_range = NULL;
2040 }
2041 }
2042
2043 goto end;
2044
2045 error:
2046 BT_CLI_LOGE_APPEND_CAUSE(
2047 "Cannot determine stream intersection of trace: path=\"%s\"",
2048 path ? path : "(unknown)");
2049
2050 end:
2051 bt_value_put_ref(query_result);
2052 g_free(port_id);
2053 g_free(trace_range);
2054 return ret;
2055 }
2056
2057 static
2058 int cmd_run_ctx_create_components_from_config_components(
2059 struct cmd_run_ctx *ctx, GPtrArray *cfg_components)
2060 {
2061 size_t i;
2062 const void *comp_cls = NULL;
2063 const void *comp = NULL;
2064 int ret = 0;
2065
2066 for (i = 0; i < cfg_components->len; i++) {
2067 struct bt_config_component *cfg_comp =
2068 g_ptr_array_index(cfg_components, i);
2069 GQuark quark;
2070
2071 switch (cfg_comp->type) {
2072 case BT_COMPONENT_CLASS_TYPE_SOURCE:
2073 comp_cls = find_source_component_class(
2074 cfg_comp->plugin_name->str,
2075 cfg_comp->comp_cls_name->str);
2076 break;
2077 case BT_COMPONENT_CLASS_TYPE_FILTER:
2078 comp_cls = find_filter_component_class(
2079 cfg_comp->plugin_name->str,
2080 cfg_comp->comp_cls_name->str);
2081 break;
2082 case BT_COMPONENT_CLASS_TYPE_SINK:
2083 comp_cls = find_sink_component_class(
2084 cfg_comp->plugin_name->str,
2085 cfg_comp->comp_cls_name->str);
2086 break;
2087 default:
2088 abort();
2089 }
2090
2091 if (!comp_cls) {
2092 BT_CLI_LOGE_APPEND_CAUSE(
2093 "Cannot find component class: plugin-name=\"%s\", "
2094 "comp-cls-name=\"%s\", comp-cls-type=%d",
2095 cfg_comp->plugin_name->str,
2096 cfg_comp->comp_cls_name->str,
2097 cfg_comp->type);
2098 goto error;
2099 }
2100
2101 BT_ASSERT(cfg_comp->log_level >= BT_LOG_TRACE);
2102
2103 switch (cfg_comp->type) {
2104 case BT_COMPONENT_CLASS_TYPE_SOURCE:
2105 ret = bt_graph_add_source_component(ctx->graph,
2106 comp_cls, cfg_comp->instance_name->str,
2107 cfg_comp->params, cfg_comp->log_level,
2108 (void *) &comp);
2109 break;
2110 case BT_COMPONENT_CLASS_TYPE_FILTER:
2111 ret = bt_graph_add_filter_component(ctx->graph,
2112 comp_cls, cfg_comp->instance_name->str,
2113 cfg_comp->params, cfg_comp->log_level,
2114 (void *) &comp);
2115 break;
2116 case BT_COMPONENT_CLASS_TYPE_SINK:
2117 ret = bt_graph_add_sink_component(ctx->graph,
2118 comp_cls, cfg_comp->instance_name->str,
2119 cfg_comp->params, cfg_comp->log_level,
2120 (void *) &comp);
2121 break;
2122 default:
2123 abort();
2124 }
2125
2126 if (ret) {
2127 BT_CLI_LOGE_APPEND_CAUSE(
2128 "Cannot create component: plugin-name=\"%s\", "
2129 "comp-cls-name=\"%s\", comp-cls-type=%d, "
2130 "comp-name=\"%s\"",
2131 cfg_comp->plugin_name->str,
2132 cfg_comp->comp_cls_name->str,
2133 cfg_comp->type, cfg_comp->instance_name->str);
2134 goto error;
2135 }
2136
2137 if (ctx->stream_intersection_mode &&
2138 cfg_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) {
2139 ret = set_stream_intersections(ctx, cfg_comp, comp_cls);
2140 if (ret) {
2141 goto error;
2142 }
2143 }
2144
2145 BT_LOGI("Created and inserted component: comp-addr=%p, comp-name=\"%s\"",
2146 comp, cfg_comp->instance_name->str);
2147 quark = g_quark_from_string(cfg_comp->instance_name->str);
2148 BT_ASSERT(quark > 0);
2149
2150 switch (cfg_comp->type) {
2151 case BT_COMPONENT_CLASS_TYPE_SOURCE:
2152 g_hash_table_insert(ctx->src_components,
2153 GUINT_TO_POINTER(quark), (void *) comp);
2154 break;
2155 case BT_COMPONENT_CLASS_TYPE_FILTER:
2156 g_hash_table_insert(ctx->flt_components,
2157 GUINT_TO_POINTER(quark), (void *) comp);
2158 break;
2159 case BT_COMPONENT_CLASS_TYPE_SINK:
2160 g_hash_table_insert(ctx->sink_components,
2161 GUINT_TO_POINTER(quark), (void *) comp);
2162 break;
2163 default:
2164 abort();
2165 }
2166
2167 comp = NULL;
2168 BT_OBJECT_PUT_REF_AND_RESET(comp_cls);
2169 }
2170
2171 goto end;
2172
2173 error:
2174 ret = -1;
2175
2176 end:
2177 bt_object_put_ref(comp);
2178 bt_object_put_ref(comp_cls);
2179 return ret;
2180 }
2181
2182 static
2183 int cmd_run_ctx_create_components(struct cmd_run_ctx *ctx)
2184 {
2185 int ret = 0;
2186
2187 /*
2188 * Make sure that, during this phase, our graph's "port added"
2189 * listener does not connect ports while we are creating the
2190 * components because we have a special, initial phase for
2191 * this.
2192 */
2193 ctx->connect_ports = false;
2194
2195 ret = cmd_run_ctx_create_components_from_config_components(
2196 ctx, ctx->cfg->cmd_data.run.sources);
2197 if (ret) {
2198 ret = -1;
2199 goto end;
2200 }
2201
2202 ret = cmd_run_ctx_create_components_from_config_components(
2203 ctx, ctx->cfg->cmd_data.run.filters);
2204 if (ret) {
2205 ret = -1;
2206 goto end;
2207 }
2208
2209 ret = cmd_run_ctx_create_components_from_config_components(
2210 ctx, ctx->cfg->cmd_data.run.sinks);
2211 if (ret) {
2212 ret = -1;
2213 goto end;
2214 }
2215
2216 end:
2217 return ret;
2218 }
2219
2220 typedef uint64_t (*output_port_count_func_t)(const void *);
2221 typedef const bt_port_output *(*borrow_output_port_by_index_func_t)(
2222 const void *, uint64_t);
2223
2224 static
2225 int cmd_run_ctx_connect_comp_ports(struct cmd_run_ctx *ctx,
2226 void *comp, output_port_count_func_t port_count_fn,
2227 borrow_output_port_by_index_func_t port_by_index_fn)
2228 {
2229 int ret = 0;
2230 uint64_t count;
2231 uint64_t i;
2232
2233 count = port_count_fn(comp);
2234
2235 for (i = 0; i < count; i++) {
2236 const bt_port_output *upstream_port = port_by_index_fn(comp, i);
2237
2238 BT_ASSERT(upstream_port);
2239 ret = cmd_run_ctx_connect_upstream_port(ctx, upstream_port);
2240 if (ret) {
2241 goto end;
2242 }
2243 }
2244
2245 end:
2246 return ret;
2247 }
2248
2249 static
2250 int cmd_run_ctx_connect_ports(struct cmd_run_ctx *ctx)
2251 {
2252 int ret = 0;
2253 GHashTableIter iter;
2254 gpointer g_name_quark, g_comp;
2255
2256 ctx->connect_ports = true;
2257 g_hash_table_iter_init(&iter, ctx->src_components);
2258
2259 while (g_hash_table_iter_next(&iter, &g_name_quark, &g_comp)) {
2260 ret = cmd_run_ctx_connect_comp_ports(ctx, g_comp,
2261 (output_port_count_func_t)
2262 bt_component_source_get_output_port_count,
2263 (borrow_output_port_by_index_func_t)
2264 bt_component_source_borrow_output_port_by_index_const);
2265 if (ret) {
2266 goto end;
2267 }
2268 }
2269
2270 g_hash_table_iter_init(&iter, ctx->flt_components);
2271
2272 while (g_hash_table_iter_next(&iter, &g_name_quark, &g_comp)) {
2273 ret = cmd_run_ctx_connect_comp_ports(ctx, g_comp,
2274 (output_port_count_func_t)
2275 bt_component_filter_get_output_port_count,
2276 (borrow_output_port_by_index_func_t)
2277 bt_component_filter_borrow_output_port_by_index_const);
2278 if (ret) {
2279 goto end;
2280 }
2281 }
2282
2283 end:
2284 return ret;
2285 }
2286
2287 static
2288 int cmd_run(struct bt_config *cfg)
2289 {
2290 int ret = 0;
2291 struct cmd_run_ctx ctx = { 0 };
2292
2293 /* Initialize the command's context and the graph object */
2294 if (cmd_run_ctx_init(&ctx, cfg)) {
2295 BT_CLI_LOGE_APPEND_CAUSE(
2296 "Cannot initialize the command's context.");
2297 goto error;
2298 }
2299
2300 if (bt_interrupter_is_set(the_interrupter)) {
2301 BT_CLI_LOGW_APPEND_CAUSE(
2302 "Interrupted by user before creating components.");
2303 goto error;
2304 }
2305
2306 BT_LOGI_STR("Creating components.");
2307
2308 /* Create the requested component instances */
2309 if (cmd_run_ctx_create_components(&ctx)) {
2310 BT_CLI_LOGE_APPEND_CAUSE("Cannot create components.");
2311 goto error;
2312 }
2313
2314 if (bt_interrupter_is_set(the_interrupter)) {
2315 BT_CLI_LOGW_APPEND_CAUSE(
2316 "Interrupted by user before connecting components.");
2317 goto error;
2318 }
2319
2320 BT_LOGI_STR("Connecting components.");
2321
2322 /* Connect the initially visible component ports */
2323 if (cmd_run_ctx_connect_ports(&ctx)) {
2324 BT_CLI_LOGE_APPEND_CAUSE(
2325 "Cannot connect initial component ports.");
2326 goto error;
2327 }
2328
2329 BT_LOGI_STR("Running the graph.");
2330
2331 /* Run the graph */
2332 while (true) {
2333 bt_graph_run_status run_status = bt_graph_run(ctx.graph);
2334
2335 /*
2336 * Reset console in case something messed with console
2337 * codes during the graph's execution.
2338 */
2339 printf("%s", bt_common_color_reset());
2340 fflush(stdout);
2341 fprintf(stderr, "%s", bt_common_color_reset());
2342 BT_LOGT("bt_graph_run() returned: status=%s",
2343 bt_common_func_status_string(run_status));
2344
2345 switch (run_status) {
2346 case BT_GRAPH_RUN_STATUS_OK:
2347 break;
2348 case BT_GRAPH_RUN_STATUS_AGAIN:
2349 if (bt_interrupter_is_set(the_interrupter)) {
2350 BT_CLI_LOGW_APPEND_CAUSE(
2351 "Graph was interrupted by user.");
2352 goto error;
2353 }
2354
2355 if (cfg->cmd_data.run.retry_duration_us > 0) {
2356 BT_LOGT("Got BT_GRAPH_RUN_STATUS_AGAIN: sleeping: "
2357 "time-us=%" PRIu64,
2358 cfg->cmd_data.run.retry_duration_us);
2359
2360 if (usleep(cfg->cmd_data.run.retry_duration_us)) {
2361 if (bt_interrupter_is_set(the_interrupter)) {
2362 BT_CLI_LOGW_APPEND_CAUSE(
2363 "Graph was interrupted by user.");
2364 goto error;
2365 }
2366 }
2367 }
2368 break;
2369 case BT_GRAPH_RUN_STATUS_END:
2370 goto end;
2371 default:
2372 if (bt_interrupter_is_set(the_interrupter)) {
2373 BT_CLI_LOGW_APPEND_CAUSE(
2374 "Graph was interrupted by user and failed: "
2375 "status=%s",
2376 bt_common_func_status_string(run_status));
2377 goto error;
2378 }
2379
2380 BT_CLI_LOGE_APPEND_CAUSE(
2381 "Graph failed to complete successfully");
2382 goto error;
2383 }
2384 }
2385
2386 goto end;
2387
2388 error:
2389 if (ret == 0) {
2390 ret = -1;
2391 }
2392
2393 end:
2394 cmd_run_ctx_destroy(&ctx);
2395 return ret;
2396 }
2397
2398 static
2399 void warn_command_name_and_directory_clash(struct bt_config *cfg)
2400 {
2401 const char *env_clash;
2402
2403 if (!cfg->command_name) {
2404 return;
2405 }
2406
2407 env_clash = getenv(ENV_BABELTRACE_WARN_COMMAND_NAME_DIRECTORY_CLASH);
2408 if (env_clash && strcmp(env_clash, "0") == 0) {
2409 return;
2410 }
2411
2412 if (g_file_test(cfg->command_name,
2413 G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
2414 _bt_log_write_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__,
2415 BT_LOG_WARNING, BT_LOG_TAG,
2416 "The `%s` command was executed. "
2417 "If you meant to convert a trace located in "
2418 "the local `%s` directory, please use:\n\n"
2419 " babeltrace2 convert %s [OPTIONS]",
2420 cfg->command_name, cfg->command_name,
2421 cfg->command_name);
2422 }
2423 }
2424
2425 static
2426 void init_log_level(void)
2427 {
2428 bt_cli_log_level = bt_log_get_level_from_env(ENV_BABELTRACE_CLI_LOG_LEVEL);
2429 }
2430
2431 static
2432 void set_auto_log_levels(struct bt_config *cfg)
2433 {
2434 const char **env_var_name;
2435
2436 /*
2437 * Override the configuration's default log level if
2438 * BABELTRACE_VERBOSE or BABELTRACE_DEBUG environment variables
2439 * are found for backward compatibility with legacy Babetrace 1.
2440 */
2441 if (getenv("BABELTRACE_DEBUG") &&
2442 strcmp(getenv("BABELTRACE_DEBUG"), "1") == 0) {
2443 cfg->log_level = BT_LOG_TRACE;
2444 } else if (getenv("BABELTRACE_VERBOSE") &&
2445 strcmp(getenv("BABELTRACE_VERBOSE"), "1") == 0) {
2446 cfg->log_level = BT_LOG_INFO;
2447 }
2448
2449 /*
2450 * Set log levels according to --debug or --verbose. For
2451 * backward compatibility, --debug is more verbose than
2452 * --verbose. So:
2453 *
2454 * --verbose: INFO log level
2455 * --debug: TRACE log level (includes DEBUG, which is
2456 * is less verbose than TRACE in the internal
2457 * logging framework)
2458 */
2459 if (!getenv("LIBBABELTRACE2_INIT_LOG_LEVEL")) {
2460 if (cfg->verbose) {
2461 bt_logging_set_global_level(BT_LOG_INFO);
2462 } else if (cfg->debug) {
2463 bt_logging_set_global_level(BT_LOG_TRACE);
2464 } else {
2465 /*
2466 * Set library's default log level if not
2467 * explicitly specified.
2468 */
2469 bt_logging_set_global_level(cfg->log_level);
2470 }
2471 }
2472
2473 if (!getenv(ENV_BABELTRACE_CLI_LOG_LEVEL)) {
2474 if (cfg->verbose) {
2475 bt_cli_log_level = BT_LOG_INFO;
2476 } else if (cfg->debug) {
2477 bt_cli_log_level = BT_LOG_TRACE;
2478 } else {
2479 /*
2480 * Set CLI's default log level if not explicitly
2481 * specified.
2482 */
2483 bt_cli_log_level = cfg->log_level;
2484 }
2485 }
2486
2487 env_var_name = log_level_env_var_names;
2488
2489 while (*env_var_name) {
2490 if (!getenv(*env_var_name)) {
2491 if (cfg->verbose) {
2492 g_setenv(*env_var_name, "INFO", 1);
2493 } else if (cfg->debug) {
2494 g_setenv(*env_var_name, "TRACE", 1);
2495 } else {
2496 char val[2] = { 0 };
2497
2498 /*
2499 * Set module's default log level if not
2500 * explicitly specified.
2501 */
2502 val[0] = bt_log_get_letter_from_level(
2503 cfg->log_level);
2504 g_setenv(*env_var_name, val, 1);
2505 }
2506 }
2507
2508 env_var_name++;
2509 }
2510 }
2511
2512 static
2513 void print_error_causes(void)
2514 {
2515 const bt_error *error = bt_current_thread_take_error();
2516 int64_t i;
2517 GString *folded = NULL;
2518 unsigned int columns;
2519
2520 if (!error || bt_error_get_cause_count(error) == 0) {
2521 fprintf(stderr, "%s%sUnknown command-line error.%s\n",
2522 bt_common_color_bold(), bt_common_color_fg_red(),
2523 bt_common_color_reset());
2524 goto end;
2525 }
2526
2527 /* Try to get terminal width to fold the error cause messages */
2528 if (bt_common_get_term_size(&columns, NULL) < 0) {
2529 /* Width not found: default to 80 */
2530 columns = 80;
2531 }
2532
2533 /*
2534 * This helps visually separate the error causes from the last
2535 * logging statement.
2536 */
2537 fprintf(stderr, "\n");
2538
2539 /* Reverse order: deepest (root) cause printed at the end */
2540 for (i = bt_error_get_cause_count(error) - 1; i >= 0; i--) {
2541 const bt_error_cause *cause =
2542 bt_error_borrow_cause_by_index(error, (uint64_t) i);
2543 const char *prefix_fmt =
2544 i == bt_error_get_cause_count(error) - 1 ?
2545 "%s%sERROR%s: " : "%s%sCAUSED BY%s ";
2546
2547 /* Print prefix */
2548 fprintf(stderr, prefix_fmt,
2549 bt_common_color_bold(), bt_common_color_fg_red(),
2550 bt_common_color_reset());
2551
2552 /* Print actor name */
2553 fprintf(stderr, "[");
2554 switch (bt_error_cause_get_actor_type(cause)) {
2555 case BT_ERROR_CAUSE_ACTOR_TYPE_UNKNOWN:
2556 fprintf(stderr, "%s%s%s",
2557 bt_common_color_bold(),
2558 bt_error_cause_get_module_name(cause),
2559 bt_common_color_reset());
2560 break;
2561 case BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT:
2562 fprintf(stderr, "%s%s%s: ",
2563 bt_common_color_bold(),
2564 bt_error_cause_component_actor_get_component_name(cause),
2565 bt_common_color_reset());
2566 print_plugin_comp_cls_opt(stderr,
2567 bt_error_cause_component_actor_get_plugin_name(cause),
2568 bt_error_cause_component_actor_get_component_class_name(cause),
2569 bt_error_cause_component_actor_get_component_class_type(cause));
2570 break;
2571 case BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT_CLASS:
2572 print_plugin_comp_cls_opt(stderr,
2573 bt_error_cause_component_class_actor_get_plugin_name(cause),
2574 bt_error_cause_component_class_actor_get_component_class_name(cause),
2575 bt_error_cause_component_class_actor_get_component_class_type(cause));
2576 break;
2577 case BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR:
2578 fprintf(stderr, "%s%s%s (%s%s%s): ",
2579 bt_common_color_bold(),
2580 bt_error_cause_message_iterator_actor_get_component_name(cause),
2581 bt_common_color_reset(),
2582 bt_common_color_bold(),
2583 bt_error_cause_message_iterator_actor_get_component_output_port_name(cause),
2584 bt_common_color_reset());
2585 print_plugin_comp_cls_opt(stderr,
2586 bt_error_cause_message_iterator_actor_get_plugin_name(cause),
2587 bt_error_cause_message_iterator_actor_get_component_class_name(cause),
2588 bt_error_cause_message_iterator_actor_get_component_class_type(cause));
2589 break;
2590 default:
2591 abort();
2592 }
2593
2594 /* Print file name and line number */
2595 fprintf(stderr, "] (%s%s%s%s:%s%" PRIu64 "%s)\n",
2596 bt_common_color_bold(),
2597 bt_common_color_fg_magenta(),
2598 bt_error_cause_get_file_name(cause),
2599 bt_common_color_reset(),
2600 bt_common_color_fg_green(),
2601 bt_error_cause_get_line_number(cause),
2602 bt_common_color_reset());
2603
2604 /* Print message */
2605 folded = bt_common_fold(bt_error_cause_get_message(cause),
2606 columns, 2);
2607 if (!folded) {
2608 BT_LOGE_STR("Could not fold string.");
2609 fprintf(stderr, "%s\n",
2610 bt_error_cause_get_message(cause));
2611 continue;
2612 }
2613
2614 fprintf(stderr, "%s\n", folded->str);
2615 g_string_free(folded, TRUE);
2616 folded = NULL;
2617 }
2618
2619 end:
2620 if (folded) {
2621 g_string_free(folded, TRUE);
2622 }
2623
2624 if (error) {
2625 bt_error_release(error);
2626 }
2627 }
2628
2629 int main(int argc, const char **argv)
2630 {
2631 int ret;
2632 int retcode;
2633 struct bt_config *cfg;
2634
2635 init_log_level();
2636 set_signal_handler();
2637 init_loaded_plugins();
2638 cfg = bt_config_cli_args_create_with_default(argc, argv, &retcode);
2639
2640 if (retcode < 0) {
2641 /* Quit without errors; typically usage/version */
2642 retcode = 0;
2643 BT_LOGI_STR("Quitting without errors.");
2644 goto end;
2645 }
2646
2647 if (retcode > 0) {
2648 BT_CLI_LOGE_APPEND_CAUSE(
2649 "Command-line error: retcode=%d", retcode);
2650 goto end;
2651 }
2652
2653 if (!cfg) {
2654 BT_CLI_LOGE_APPEND_CAUSE(
2655 "Failed to create a valid Babeltrace configuration.");
2656 retcode = 1;
2657 goto end;
2658 }
2659
2660 set_auto_log_levels(cfg);
2661 print_cfg(cfg);
2662
2663 if (cfg->command_needs_plugins) {
2664 ret = require_loaded_plugins(cfg->plugin_paths);
2665 if (ret) {
2666 BT_CLI_LOGE_APPEND_CAUSE(
2667 "Failed to load plugins: ret=%d", ret);
2668 retcode = 1;
2669 goto end;
2670 }
2671 }
2672
2673 BT_ASSERT(!the_interrupter);
2674 the_interrupter = bt_interrupter_create();
2675 if (!the_interrupter) {
2676 BT_CLI_LOGE_APPEND_CAUSE("Failed to create an interrupter object.");
2677 retcode = 1;
2678 goto end;
2679 }
2680
2681 BT_LOGI("Executing command: cmd=%d, command-name=\"%s\"",
2682 cfg->command, cfg->command_name);
2683
2684 switch (cfg->command) {
2685 case BT_CONFIG_COMMAND_RUN:
2686 ret = cmd_run(cfg);
2687 break;
2688 case BT_CONFIG_COMMAND_LIST_PLUGINS:
2689 ret = cmd_list_plugins(cfg);
2690 break;
2691 case BT_CONFIG_COMMAND_HELP:
2692 ret = cmd_help(cfg);
2693 break;
2694 case BT_CONFIG_COMMAND_QUERY:
2695 ret = cmd_query(cfg);
2696 break;
2697 case BT_CONFIG_COMMAND_PRINT_CTF_METADATA:
2698 ret = cmd_print_ctf_metadata(cfg);
2699 break;
2700 case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS:
2701 ret = cmd_print_lttng_live_sessions(cfg);
2702 break;
2703 default:
2704 BT_LOGF("Invalid/unknown command: cmd=%d", cfg->command);
2705 abort();
2706 }
2707
2708 BT_LOGI("Command completed: cmd=%d, command-name=\"%s\", ret=%d",
2709 cfg->command, cfg->command_name, ret);
2710 warn_command_name_and_directory_clash(cfg);
2711 retcode = ret ? 1 : 0;
2712
2713 end:
2714 BT_OBJECT_PUT_REF_AND_RESET(cfg);
2715 fini_loaded_plugins();
2716 bt_interrupter_put_ref(the_interrupter);
2717
2718 if (retcode != 0) {
2719 print_error_causes();
2720 }
2721
2722 /*
2723 * Clear current thread's error in case there is one to avoid a
2724 * memory leak.
2725 */
2726 bt_current_thread_clear_error();
2727 return retcode;
2728 }
This page took 0.112103 seconds and 5 git commands to generate.