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