Remove unused index version fields
[babeltrace.git] / converter / babeltrace-cfg.c
CommitLineData
c42c79ea
PP
1/*
2 * Babeltrace trace converter - parameter parsing
3 *
4 * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#include <errno.h>
26#include <stdlib.h>
27#include <string.h>
28#include <assert.h>
29#include <stdio.h>
30#include <stdbool.h>
31#include <inttypes.h>
32#include <babeltrace/babeltrace.h>
33#include <babeltrace/values.h>
34#include <popt.h>
35#include <glib.h>
36#include "babeltrace-cfg.h"
37
38/*
39 * Error printf() macro which prepends "Error: " the first time it's
40 * called. This gives a nicer feel than having a bunch of error prefixes
41 * (since the following lines usually describe the error and possible
42 * solutions), or the error prefix just at the end.
43 */
44#define printf_err(fmt, args...) \
45 do { \
46 if (is_first_error) { \
47 fprintf(stderr, "Error: "); \
48 is_first_error = false; \
49 } \
50 fprintf(stderr, fmt, ##args); \
51 } while (0)
52
53static bool is_first_error = true;
54
55/* INI-style parsing FSM states */
56enum ini_parsing_fsm_state {
57 /* Expect a map key (identifier) */
58 INI_EXPECT_MAP_KEY,
59
60 /* Expect an equal character ('=') */
61 INI_EXPECT_EQUAL,
62
63 /* Expect a value */
64 INI_EXPECT_VALUE,
65
66 /* Expect a negative number value */
67 INI_EXPECT_VALUE_NUMBER_NEG,
68
69 /* Expect a comma character (',') */
70 INI_EXPECT_COMMA,
71};
72
73/* INI-style parsing state variables */
74struct ini_parsing_state {
75 /* Lexical scanner (owned by this) */
76 GScanner *scanner;
77
78 /* Output map value object being filled (owned by this) */
79 struct bt_value *params;
80
81 /* Next expected FSM state */
82 enum ini_parsing_fsm_state expecting;
83
84 /* Last decoded map key (owned by this) */
85 char *last_map_key;
86
87 /* Complete INI-style string to parse (not owned by this) */
88 const char *arg;
89
90 /* Error buffer (not owned by this) */
91 GString *ini_error;
92};
93
94/* Offset option with "is set" boolean */
95struct offset_opt {
96 int64_t value;
97 bool is_set;
98};
99
100/* Legacy "ctf"/"lttng-live" format options */
101struct ctf_legacy_opts {
102 struct offset_opt offset_s;
103 struct offset_opt offset_ns;
104 bool stream_intersection;
105};
106
107/* Legacy "text" format options */
108struct text_legacy_opts {
109 /*
110 * output, dbg_info_dir, dbg_info_target_prefix, names,
111 * and fields are owned by this.
112 */
113 GString *output;
114 GString *dbg_info_dir;
115 GString *dbg_info_target_prefix;
116 struct bt_value *names;
117 struct bt_value *fields;
118
119 /* Flags */
120 bool no_delta;
121 bool clock_cycles;
122 bool clock_seconds;
123 bool clock_date;
124 bool clock_gmt;
125 bool dbg_info_full_path;
126};
127
128/* Legacy input format format */
129enum legacy_input_format {
130 LEGACY_INPUT_FORMAT_NONE = 0,
131 LEGACY_INPUT_FORMAT_CTF,
132 LEGACY_INPUT_FORMAT_LTTNG_LIVE,
133};
134
135/* Legacy output format format */
136enum legacy_output_format {
137 LEGACY_OUTPUT_FORMAT_NONE = 0,
138 LEGACY_OUTPUT_FORMAT_TEXT,
139 LEGACY_OUTPUT_FORMAT_CTF_METADATA,
140 LEGACY_OUTPUT_FORMAT_DUMMY,
141};
142
143/*
144 * Prints the "out of memory" error.
145 */
146static
147void print_err_oom(void)
148{
149 printf_err("Out of memory\n");
150}
151
152/*
153 * Prints duplicate legacy output format error.
154 */
155static
156void print_err_dup_legacy_output(void)
157{
158 printf_err("More than one legacy output format specified\n");
159}
160
161/*
162 * Prints duplicate legacy input format error.
163 */
164static
165void print_err_dup_legacy_input(void)
166{
167 printf_err("More than one legacy input format specified\n");
168}
169
170/*
171 * Checks if any of the "text" legacy options is set.
172 */
173static
174bool text_legacy_opts_is_any_set(struct text_legacy_opts *opts)
175{
176 return (opts->output && opts->output->len > 0) ||
177 (opts->dbg_info_dir && opts->dbg_info_dir->len > 0) ||
178 (opts->dbg_info_target_prefix &&
179 opts->dbg_info_target_prefix->len > 0) ||
180 bt_value_array_size(opts->names) > 0 ||
181 bt_value_array_size(opts->fields) > 0 ||
182 opts->no_delta || opts->clock_cycles || opts->clock_seconds ||
183 opts->clock_date || opts->clock_gmt ||
184 opts->dbg_info_full_path;
185}
186
187/*
188 * Checks if any of the "ctf" legacy options is set.
189 */
190static
191bool ctf_legacy_opts_is_any_set(struct ctf_legacy_opts *opts)
192{
193 return opts->offset_s.is_set || opts->offset_ns.is_set ||
194 opts->stream_intersection;
195}
196
197/*
198 * Appends an "expecting token" error to the INI-style parsing state's
199 * error buffer.
200 */
201static
202void ini_append_error_expecting(struct ini_parsing_state *state,
203 GScanner *scanner, const char *expecting)
204{
205 size_t i;
206 size_t pos;
207
208 g_string_append_printf(state->ini_error, "Expecting %s:\n", expecting);
209
210 /* Only print error if there's one line */
211 if (strchr(state->arg, '\n') != NULL || strlen(state->arg) == 0) {
212 return;
213 }
214
215 g_string_append_printf(state->ini_error, "\n %s\n", state->arg);
216 pos = g_scanner_cur_position(scanner) + 4;
217
218 if (!g_scanner_eof(scanner)) {
219 pos--;
220 }
221
222 for (i = 0; i < pos; ++i) {
223 g_string_append_printf(state->ini_error, " ");
224 }
225
226 g_string_append_printf(state->ini_error, "^\n\n");
227}
228
229static
230int ini_handle_state(struct ini_parsing_state *state)
231{
232 int ret = 0;
233 GTokenType token_type;
234 struct bt_value *value = NULL;
235
236 token_type = g_scanner_get_next_token(state->scanner);
237 if (token_type == G_TOKEN_EOF) {
238 if (state->expecting != INI_EXPECT_COMMA) {
239 switch (state->expecting) {
240 case INI_EXPECT_EQUAL:
241 ini_append_error_expecting(state,
242 state->scanner, "'='");
243 break;
244 case INI_EXPECT_VALUE:
245 case INI_EXPECT_VALUE_NUMBER_NEG:
246 ini_append_error_expecting(state,
247 state->scanner, "value");
248 break;
249 case INI_EXPECT_MAP_KEY:
250 ini_append_error_expecting(state,
251 state->scanner, "unquoted map key");
252 break;
253 default:
254 break;
255 }
256 goto error;
257 }
258
259 /* We're done! */
260 ret = 1;
261 goto success;
262 }
263
264 switch (state->expecting) {
265 case INI_EXPECT_MAP_KEY:
266 if (token_type != G_TOKEN_IDENTIFIER) {
267 ini_append_error_expecting(state, state->scanner,
268 "unquoted map key");
269 goto error;
270 }
271
272 free(state->last_map_key);
273 state->last_map_key =
274 strdup(state->scanner->value.v_identifier);
275 if (!state->last_map_key) {
276 g_string_append(state->ini_error,
277 "Out of memory\n");
278 goto error;
279 }
280
281 if (bt_value_map_has_key(state->params, state->last_map_key)) {
282 g_string_append_printf(state->ini_error,
6d1d5711 283 "Duplicate parameter key: `%s`\n",
c42c79ea
PP
284 state->last_map_key);
285 goto error;
286 }
287
288 state->expecting = INI_EXPECT_EQUAL;
289 goto success;
290 case INI_EXPECT_EQUAL:
291 if (token_type != G_TOKEN_CHAR) {
292 ini_append_error_expecting(state,
293 state->scanner, "'='");
294 goto error;
295 }
296
297 if (state->scanner->value.v_char != '=') {
298 ini_append_error_expecting(state,
299 state->scanner, "'='");
300 goto error;
301 }
302
303 state->expecting = INI_EXPECT_VALUE;
304 goto success;
305 case INI_EXPECT_VALUE:
306 {
307 switch (token_type) {
308 case G_TOKEN_CHAR:
309 if (state->scanner->value.v_char == '-') {
310 /* Negative number */
311 state->expecting =
312 INI_EXPECT_VALUE_NUMBER_NEG;
313 goto success;
314 } else {
315 ini_append_error_expecting(state,
316 state->scanner, "value");
317 goto error;
318 }
319 break;
320 case G_TOKEN_INT:
321 {
322 /* Positive integer */
323 uint64_t int_val = state->scanner->value.v_int64;
324
325 if (int_val > (1ULL << 63) - 1) {
326 g_string_append_printf(state->ini_error,
327 "Integer value %" PRIu64 " is outside the range of a 64-bit signed integer\n",
328 int_val);
329 goto error;
330 }
331
332 value = bt_value_integer_create_init(
333 (int64_t) int_val);
334 break;
335 }
336 case G_TOKEN_FLOAT:
337 /* Positive floating point number */
338 value = bt_value_float_create_init(
339 state->scanner->value.v_float);
340 break;
341 case G_TOKEN_STRING:
342 /* Quoted string */
343 value = bt_value_string_create_init(
344 state->scanner->value.v_string);
345 break;
346 case G_TOKEN_IDENTIFIER:
347 {
348 /*
349 * Using symbols would be appropriate here,
350 * but said symbols are allowed as map key,
351 * so it's easier to consider everything an
352 * identifier.
353 *
354 * If one of the known symbols is not
355 * recognized here, then fall back to creating
356 * a string value.
357 */
358 const char *id = state->scanner->value.v_identifier;
359
360 if (!strcmp(id, "null") || !strcmp(id, "NULL") ||
361 !strcmp(id, "nul")) {
362 value = bt_value_null;
363 } else if (!strcmp(id, "true") || !strcmp(id, "TRUE") ||
364 !strcmp(id, "yes") ||
365 !strcmp(id, "YES")) {
366 value = bt_value_bool_create_init(true);
367 } else if (!strcmp(id, "false") ||
368 !strcmp(id, "FALSE") ||
369 !strcmp(id, "no") ||
370 !strcmp(id, "NO")) {
371 value = bt_value_bool_create_init(false);
372 } else {
373 value = bt_value_string_create_init(id);
374 }
375 break;
376 }
377 default:
378 /* Unset value variable will trigger the error */
379 break;
380 }
381
382 if (!value) {
383 ini_append_error_expecting(state,
384 state->scanner, "value");
385 goto error;
386 }
387
388 state->expecting = INI_EXPECT_COMMA;
389 goto success;
390 }
391 case INI_EXPECT_VALUE_NUMBER_NEG:
392 {
393 switch (token_type) {
394 case G_TOKEN_INT:
395 {
396 /* Negative integer */
397 uint64_t int_val = state->scanner->value.v_int64;
398
399 if (int_val > (1ULL << 63) - 1) {
400 g_string_append_printf(state->ini_error,
401 "Integer value -%" PRIu64 " is outside the range of a 64-bit signed integer\n",
402 int_val);
403 goto error;
404 }
405
406 value = bt_value_integer_create_init(
407 -((int64_t) int_val));
408 break;
409 }
410 case G_TOKEN_FLOAT:
411 /* Negative floating point number */
412 value = bt_value_float_create_init(
413 -state->scanner->value.v_float);
414 break;
415 default:
416 /* Unset value variable will trigger the error */
417 break;
418 }
419
420 if (!value) {
421 ini_append_error_expecting(state,
422 state->scanner, "value");
423 goto error;
424 }
425
426 state->expecting = INI_EXPECT_COMMA;
427 goto success;
428 }
429 case INI_EXPECT_COMMA:
430 if (token_type != G_TOKEN_CHAR) {
431 ini_append_error_expecting(state,
432 state->scanner, "','");
433 goto error;
434 }
435
436 if (state->scanner->value.v_char != ',') {
437 ini_append_error_expecting(state,
438 state->scanner, "','");
439 goto error;
440 }
441
442 state->expecting = INI_EXPECT_MAP_KEY;
443 goto success;
444 default:
445 assert(false);
446 }
447
448error:
449 ret = -1;
450 goto end;
451
452success:
453 if (value) {
454 if (bt_value_map_insert(state->params,
455 state->last_map_key, value)) {
456 /* Only override return value on error */
457 ret = -1;
458 }
459 }
460
461end:
462 BT_PUT(value);
463 return ret;
464}
465
466/*
467 * Converts an INI-style argument to an equivalent map value object.
468 *
469 * Return value is owned by the caller.
470 */
471static
472struct bt_value *bt_value_from_ini(const char *arg, GString *ini_error)
473{
474 /* Lexical scanner configuration */
475 GScannerConfig scanner_config = {
476 /* Skip whitespaces */
477 .cset_skip_characters = " \t\n",
478
479 /* Identifier syntax is: [a-zA-Z_][a-zA-Z0-9_.:-]* */
480 .cset_identifier_first =
481 G_CSET_a_2_z
482 "_"
483 G_CSET_A_2_Z,
484 .cset_identifier_nth =
485 G_CSET_a_2_z
486 "_0123456789-.:"
487 G_CSET_A_2_Z,
488
489 /* "hello" and "Hello" two different keys */
490 .case_sensitive = TRUE,
491
492 /* No comments */
493 .cpair_comment_single = NULL,
494 .skip_comment_multi = TRUE,
495 .skip_comment_single = TRUE,
496 .scan_comment_multi = FALSE,
497
498 /*
499 * Do scan identifiers, including 1-char identifiers,
500 * but NULL is a normal identifier.
501 */
502 .scan_identifier = TRUE,
503 .scan_identifier_1char = TRUE,
504 .scan_identifier_NULL = FALSE,
505
506 /*
507 * No specific symbols: null and boolean "symbols" are
508 * scanned as plain identifiers.
509 */
510 .scan_symbols = FALSE,
511 .symbol_2_token = FALSE,
512 .scope_0_fallback = FALSE,
513
514 /*
515 * Scan "0b"-, "0"-, and "0x"-prefixed integers, but not
516 * integers prefixed with "$".
517 */
518 .scan_binary = TRUE,
519 .scan_octal = TRUE,
520 .scan_float = TRUE,
521 .scan_hex = TRUE,
522 .scan_hex_dollar = FALSE,
523
524 /* Convert scanned numbers to integer tokens */
525 .numbers_2_int = TRUE,
526
527 /* Support both integers and floating-point numbers */
528 .int_2_float = FALSE,
529
530 /* Scan integers as 64-bit signed integers */
531 .store_int64 = TRUE,
532
533 /* Only scan double-quoted strings */
534 .scan_string_sq = FALSE,
535 .scan_string_dq = TRUE,
536
537 /* Do not converter identifiers to string tokens */
538 .identifier_2_string = FALSE,
539
540 /* Scan characters as G_TOKEN_CHAR token */
541 .char_2_token = FALSE,
542 };
543 struct ini_parsing_state state = {
544 .scanner = NULL,
545 .params = NULL,
546 .expecting = INI_EXPECT_MAP_KEY,
547 .arg = arg,
548 .ini_error = ini_error,
549 };
550
551 state.params = bt_value_map_create();
552 if (!state.params) {
553 goto error;
554 }
555
556 state.scanner = g_scanner_new(&scanner_config);
557 if (!state.scanner) {
558 goto error;
559 }
560
561 /* Let the scan begin */
562 g_scanner_input_text(state.scanner, arg, strlen(arg));
563
564 while (true) {
565 int ret = ini_handle_state(&state);
566
567 if (ret < 0) {
568 /* Error */
569 goto error;
570 } else if (ret > 0) {
571 /* Done */
572 break;
573 }
574 }
575
576 goto end;
577
578error:
579 BT_PUT(state.params);
580
581end:
582 if (state.scanner) {
583 g_scanner_destroy(state.scanner);
584 }
585
586 free(state.last_map_key);
587 return state.params;
588}
589
590/*
591 * Returns the parameters map value object from a command-line
bdc61c70 592 * parameter option's argument.
c42c79ea
PP
593 *
594 * Return value is owned by the caller.
595 */
596static
597struct bt_value *bt_value_from_arg(const char *arg)
598{
599 struct bt_value *params = NULL;
c42c79ea
PP
600 GString *ini_error = NULL;
601
c42c79ea
PP
602 ini_error = g_string_new(NULL);
603 if (!ini_error) {
604 print_err_oom();
605 goto end;
606 }
607
608 /* Try INI-style parsing */
bdc61c70 609 params = bt_value_from_ini(arg, ini_error);
c42c79ea
PP
610 if (!params) {
611 printf_err("%s", ini_error->str);
612 goto end;
613 }
614
615end:
616 if (ini_error) {
617 g_string_free(ini_error, TRUE);
618 }
619 return params;
620}
621
622/*
623 * Returns the plugin and component names from a command-line
bdc61c70
PP
624 * source/sink option's argument. arg must have the following format:
625 *
626 * PLUGIN.COMPONENT
627 *
628 * where PLUGIN is the plugin name, and COMPONENT is the component
629 * name.
c42c79ea
PP
630 *
631 * On success, both *plugin and *component are not NULL. *plugin
632 * and *component are owned by the caller.
633 */
634static
635void plugin_component_names_from_arg(const char *arg, char **plugin,
636 char **component)
637{
638 const char *dot;
bdc61c70 639 const char *end;
c42c79ea
PP
640 size_t plugin_len;
641 size_t component_len;
642
bdc61c70 643 /* Initialize both return values to NULL: not found */
c42c79ea
PP
644 *plugin = NULL;
645 *component = NULL;
646
647 dot = strchr(arg, '.');
bdc61c70
PP
648 if (!dot) {
649 /* No dot */
c42c79ea
PP
650 goto end;
651 }
652
bdc61c70 653 end = arg + strlen(arg);
c42c79ea 654 plugin_len = dot - arg;
bdc61c70 655 component_len = end - dot - 1;
c42c79ea
PP
656 if (plugin_len == 0 || component_len == 0) {
657 goto end;
658 }
659
bdc61c70 660 *plugin = g_malloc0(plugin_len + 1);
c42c79ea
PP
661 if (!*plugin) {
662 print_err_oom();
663 goto end;
664 }
665
bdc61c70
PP
666 g_strlcpy(*plugin, arg, plugin_len + 1);
667 *component = g_malloc0(component_len + 1);
c42c79ea
PP
668 if (!*component) {
669 print_err_oom();
670 goto end;
671 }
672
bdc61c70 673 g_strlcpy(*component, dot + 1, component_len + 1);
c42c79ea
PP
674
675end:
676 return;
677}
678
679/*
680 * Prints the Babeltrace version.
681 */
682static
683void print_version(void)
684{
685 puts("Babeltrace " VERSION);
686}
687
688/*
689 * Prints the legacy, Babeltrace 1.x command usage. Those options are
690 * still compatible in Babeltrace 2.x, but it is recommended to use
691 * the more generic plugin/component parameters instead of those
692 * hard-coded option names.
693 */
694static
695void print_legacy_usage(FILE *fp)
696{
697 fprintf(fp, "Usage: babeltrace [OPTIONS] INPUT...\n");
698 fprintf(fp, "\n");
699 fprintf(fp, "The following options are compatible with the Babeltrace 1.x options:\n");
700 fprintf(fp, "\n");
701 fprintf(fp, " --help-legacy Show this help\n");
702 fprintf(fp, " -V, --version Show version\n");
703 fprintf(fp, " --clock-force-correlate Assume that clocks are inherently correlated\n");
704 fprintf(fp, " across traces\n");
705 fprintf(fp, " -d, --debug Enable debug mode\n");
706 fprintf(fp, " -i, --input-format=FORMAT Input trace format (default: ctf)\n");
707 fprintf(fp, " -l, --list List available formats\n");
708 fprintf(fp, " -o, --output-format=FORMAT Output trace format (default: text)\n");
709 fprintf(fp, " -v, --verbose Enable verbose output\n");
710 fprintf(fp, "\n");
711 fprintf(fp, " Available input formats: ctf, lttng-live, ctf-metadata\n");
712 fprintf(fp, " Available output formats: text, dummy\n");
713 fprintf(fp, "\n");
714 fprintf(fp, "Input formats specific options:\n");
715 fprintf(fp, "\n");
716 fprintf(fp, " INPUT... Input trace file(s), directory(ies), or URLs\n");
717 fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n");
718 fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS nanoseconds\n");
719 fprintf(fp, " --stream-intersection Only process events when all streams are active\n");
720 fprintf(fp, "\n");
721 fprintf(fp, "text output format specific options:\n");
722 fprintf(fp, " \n");
723 fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n");
724 fprintf(fp, " --clock-date Print timestamp dates\n");
528debdf 725 fprintf(fp, " --clock-gmt Print and parse timestamps in GMT time zone\n");
c42c79ea
PP
726 fprintf(fp, " (default: local time zone)\n");
727 fprintf(fp, " --clock-seconds Print the timestamps as [SEC.NS]\n");
728 fprintf(fp, " (default format: [HH:MM:SS.NS])\n");
729 fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n");
6d1d5711 730 fprintf(fp, " (default: `/usr/lib/debug`)\n");
c42c79ea
PP
731 fprintf(fp, " --debug-info-full-path Show full debug info source and binary paths\n");
732 fprintf(fp, " --debug-info-target-prefix=DIR Use directory DIR as a prefix when looking\n");
733 fprintf(fp, " up executables during debug info analysis\n");
6d1d5711 734 fprintf(fp, " (default: `/usr/lib/debug`)\n");
c42c79ea
PP
735 fprintf(fp, " -f, --fields=NAME[,NAME]... Print additional fields:\n");
736 fprintf(fp, " all, trace, trace:hostname, trace:domain,\n");
d9b99e4e 737 fprintf(fp, " trace:procname, trace:vpid, loglevel, emf\n");
c42c79ea
PP
738 fprintf(fp, " (default: trace:hostname, trace:procname,\n");
739 fprintf(fp, " trace:vpid)\n");
740 fprintf(fp, " -n, --names=NAME[,NAME]... Print field names:\n");
741 fprintf(fp, " payload (or arg or args)\n");
742 fprintf(fp, " none, all, scope, header, context (or ctx)\n");
743 fprintf(fp, " (default: payload, context)\n");
744 fprintf(fp, " --no-delta Do not print time delta between consecutive\n");
745 fprintf(fp, " events\n");
746 fprintf(fp, " -w, --output=PATH Write output to PATH (default: standard output)\n");
747}
748
749/*
750 * Prints the Babeltrace 2.x usage.
751 */
752static
753void print_usage(FILE *fp)
754{
755 fprintf(fp, "Usage: babeltrace [OPTIONS]\n");
756 fprintf(fp, "\n");
b07ffa28
PP
757 fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n");
758 fprintf(fp, " of the following source and sink component\n");
759 fprintf(fp, " instances (see the exact format of PARAMS\n");
760 fprintf(fp, " below)\n");
c42c79ea
PP
761 fprintf(fp, " -d, --debug Enable debug mode\n");
762 fprintf(fp, " -l, --list List available plugins and their components\n");
ad6a19bd
PP
763 fprintf(fp, " -P, --path=PATH Set the `path` parameter of the latest source\n");
764 fprintf(fp, " or sink component to PATH\n");
bdc61c70 765 fprintf(fp, " -p, --params=PARAMS Set the parameters of the latest source or\n");
b07ffa28
PP
766 fprintf(fp, " sink component instance (in command-line \n");
767 fprintf(fp, " order) to PARAMS (see the exact format of\n");
768 fprintf(fp, " PARAMS below)\n");
ad6a19bd 769 fprintf(fp, " --plugin-path=PATH[:PATH]... Set paths from which dynamic plugins can be\n");
015cee23 770 fprintf(fp, " loaded to PATH\n");
b07ffa28
PP
771 fprintf(fp, " -r, --reset-base-params Reset the current base parameters of the\n");
772 fprintf(fp, " following source and sink component\n");
773 fprintf(fp, " instances to an empty map\n");
015cee23
PP
774 fprintf(fp, " -o, --sink=PLUGIN.COMPCLS Instantiate a sink component from plugin\n");
775 fprintf(fp, " PLUGIN and component class COMPCLS (may be\n");
776 fprintf(fp, " repeated)\n");
777 fprintf(fp, " -i, --source=PLUGIN.COMPCLS Instantiate a source component from plugin\n");
778 fprintf(fp, " PLUGIN and component class COMPCLS (may be\n");
779 fprintf(fp, " repeated)\n");
0f9915c6
MD
780 fprintf(fp, " --begin Start time: [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n");
781 fprintf(fp, " --end End time: [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n");
782 fprintf(fp, " --timerange Time range: begin,end or [begin,end] (where [] are actual brackets)\n");
bdc61c70
PP
783 fprintf(fp, " -h --help Show this help\n");
784 fprintf(fp, " --help-legacy Show Babeltrace 1.x legacy options\n");
c42c79ea
PP
785 fprintf(fp, " -v, --verbose Enable verbose output\n");
786 fprintf(fp, " -V, --version Show version\n");
bdc61c70 787 fprintf(fp, "\n\n");
b07ffa28
PP
788 fprintf(fp, "Format of PARAMS\n");
789 fprintf(fp, "----------------\n");
c42c79ea 790 fprintf(fp, "\n");
bdc61c70 791 fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n");
c42c79ea 792 fprintf(fp, "\n");
bdc61c70
PP
793 fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n");
794 fprintf(fp, "where PARAM is the parameter name (C identifier plus [:.-] characters), and\n");
795 fprintf(fp, "VALUE can be one of:\n");
c42c79ea 796 fprintf(fp, "\n");
bdc61c70
PP
797 fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n");
798 fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n");
799 fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n");
800 fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n");
801 fprintf(fp, " (`0x` prefix) signed 64-bit integer.\n");
802 fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n");
803 fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n");
804 fprintf(fp, " the null and boolean value symbols above.\n");
805 fprintf(fp, "* Double-quoted string (accepts escape characters).\n");
c42c79ea 806 fprintf(fp, "\n");
bdc61c70 807 fprintf(fp, "Whitespaces are allowed around individual `=` and `,` tokens.\n");
c42c79ea 808 fprintf(fp, "\n");
bdc61c70 809 fprintf(fp, "Example:\n");
c42c79ea 810 fprintf(fp, "\n");
bdc61c70
PP
811 fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n");
812 fprintf(fp, " observe=3.14, simple=beef, needs-quotes=\"some string\",\n");
813 fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\"\n");
c42c79ea 814 fprintf(fp, "\n");
bdc61c70
PP
815 fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run babeltrace\n");
816 fprintf(fp, "from a shell.\n");
c42c79ea
PP
817}
818
819/*
820 * Destroys a component configuration.
821 */
822static
823void bt_config_component_destroy(struct bt_object *obj)
824{
825 struct bt_config_component *bt_config_component =
826 container_of(obj, struct bt_config_component, base);
827
828 if (!obj) {
829 goto end;
830 }
831
832 if (bt_config_component->plugin_name) {
833 g_string_free(bt_config_component->plugin_name, TRUE);
834 }
835
836 if (bt_config_component->component_name) {
837 g_string_free(bt_config_component->component_name, TRUE);
838 }
839
840 BT_PUT(bt_config_component->params);
841 g_free(bt_config_component);
842
843end:
844 return;
845}
846
847/*
bdc61c70
PP
848 * Creates a component configuration using the given plugin name and
849 * component name. plugin_name and component_name are copied (belong to
850 * the return value).
c42c79ea
PP
851 *
852 * Return value is owned by the caller.
853 */
854static
855struct bt_config_component *bt_config_component_create(const char *plugin_name,
bdc61c70 856 const char *component_name)
c42c79ea
PP
857{
858 struct bt_config_component *cfg_component = NULL;
859
860 cfg_component = g_new0(struct bt_config_component, 1);
861 if (!cfg_component) {
862 print_err_oom();
863 goto error;
864 }
865
866 bt_object_init(cfg_component, bt_config_component_destroy);
867 cfg_component->plugin_name = g_string_new(plugin_name);
868 if (!cfg_component->plugin_name) {
869 print_err_oom();
870 goto error;
871 }
872
873 cfg_component->component_name = g_string_new(component_name);
874 if (!cfg_component->component_name) {
875 print_err_oom();
876 goto error;
877 }
878
bdc61c70
PP
879 /* Start with empty parameters */
880 cfg_component->params = bt_value_map_create();
881 if (!cfg_component->params) {
882 print_err_oom();
883 goto error;
884 }
885
c42c79ea
PP
886 goto end;
887
888error:
889 BT_PUT(cfg_component);
890
891end:
892 return cfg_component;
893}
894
895/*
896 * Creates a component configuration from a command-line source/sink
bdc61c70 897 * option's argument.
c42c79ea
PP
898 */
899static
900struct bt_config_component *bt_config_component_from_arg(const char *arg)
901{
902 struct bt_config_component *bt_config_component = NULL;
903 char *plugin_name;
904 char *component_name;
c42c79ea
PP
905
906 plugin_component_names_from_arg(arg, &plugin_name, &component_name);
907 if (!plugin_name || !component_name) {
49849a47 908 printf_err("Cannot get plugin or component class name\n");
c42c79ea
PP
909 goto error;
910 }
911
c42c79ea 912 bt_config_component = bt_config_component_create(plugin_name,
bdc61c70 913 component_name);
c42c79ea
PP
914 if (!bt_config_component) {
915 goto error;
916 }
917
918 goto end;
919
920error:
921 BT_PUT(bt_config_component);
922
923end:
bdc61c70
PP
924 g_free(plugin_name);
925 g_free(component_name);
c42c79ea
PP
926 return bt_config_component;
927}
928
929/*
930 * Destroys a configuration.
931 */
932static
933void bt_config_destroy(struct bt_object *obj)
934{
935 struct bt_config *bt_config =
936 container_of(obj, struct bt_config, base);
937
938 if (!obj) {
939 goto end;
940 }
941
942 if (bt_config->sources) {
943 g_ptr_array_free(bt_config->sources, TRUE);
944 }
945
946 if (bt_config->sinks) {
947 g_ptr_array_free(bt_config->sinks, TRUE);
948 }
949
950 BT_PUT(bt_config->plugin_paths);
951 g_free(bt_config);
952
953end:
954 return;
955}
956
957/*
958 * Extracts the various paths from the string arg, delimited by ':',
959 * and converts them to an array value object.
960 *
961 * Returned array value object is empty if arg is empty.
962 *
963 * Return value is owned by the caller.
964 */
965static
966struct bt_value *plugin_paths_from_arg(const char *arg)
967{
968 struct bt_value *plugin_paths;
969 const char *at = arg;
970 const char *end = arg + strlen(arg);
971
972 plugin_paths = bt_value_array_create();
973 if (!plugin_paths) {
974 print_err_oom();
975 goto error;
976 }
977
978 while (at < end) {
979 int ret;
980 GString *path;
981 const char *next_colon;
982
983 next_colon = strchr(at, ':');
984 if (next_colon == at) {
985 /*
986 * Empty path: try next character (supported
987 * to conform to the typical parsing of $PATH).
988 */
989 at++;
990 continue;
991 } else if (!next_colon) {
992 /* No more colon: use the remaining */
993 next_colon = arg + strlen(arg);
994 }
995
996 path = g_string_new(NULL);
997 if (!path) {
998 print_err_oom();
999 goto error;
1000 }
1001
1002 g_string_append_len(path, at, next_colon - at);
1003 at = next_colon + 1;
1004 ret = bt_value_array_append_string(plugin_paths, path->str);
1005 g_string_free(path, TRUE);
1006 if (ret) {
1007 print_err_oom();
1008 goto error;
1009 }
1010 }
1011
1012 goto end;
1013
1014error:
1015 BT_PUT(plugin_paths);
1016
1017end:
1018 return plugin_paths;
1019}
1020
1021/*
1022 * Creates a simple lexical scanner for parsing comma-delimited names
1023 * and fields.
1024 *
1025 * Return value is owned by the caller.
1026 */
1027static
1028GScanner *create_csv_identifiers_scanner(void)
1029{
1030 GScannerConfig scanner_config = {
1031 .cset_skip_characters = " \t\n",
1032 .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "_",
1033 .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z ":_-",
1034 .case_sensitive = TRUE,
1035 .cpair_comment_single = NULL,
1036 .skip_comment_multi = TRUE,
1037 .skip_comment_single = TRUE,
1038 .scan_comment_multi = FALSE,
1039 .scan_identifier = TRUE,
1040 .scan_identifier_1char = TRUE,
1041 .scan_identifier_NULL = FALSE,
1042 .scan_symbols = FALSE,
1043 .symbol_2_token = FALSE,
1044 .scope_0_fallback = FALSE,
1045 .scan_binary = FALSE,
1046 .scan_octal = FALSE,
1047 .scan_float = FALSE,
1048 .scan_hex = FALSE,
1049 .scan_hex_dollar = FALSE,
1050 .numbers_2_int = FALSE,
1051 .int_2_float = FALSE,
1052 .store_int64 = FALSE,
1053 .scan_string_sq = FALSE,
1054 .scan_string_dq = FALSE,
1055 .identifier_2_string = FALSE,
1056 .char_2_token = TRUE,
1057 };
1058
1059 return g_scanner_new(&scanner_config);
1060}
1061
6e1bc0df
MD
1062/*
1063 * Inserts a string (if exists and not empty) or null to a map value
1064 * object.
1065 */
1066static
1067enum bt_value_status map_insert_string_or_null(struct bt_value *map,
1068 const char *key, GString *string)
1069{
1070 enum bt_value_status ret;
1071
1072 if (string && string->len > 0) {
1073 ret = bt_value_map_insert_string(map, key, string->str);
1074 } else {
1075 ret = bt_value_map_insert(map, key, bt_value_null);
1076 }
1077 return ret;
1078}
1079
c42c79ea
PP
1080/*
1081 * Converts a comma-delimited list of known names (--names option) to
1082 * an array value object containing those names as string value objects.
1083 *
1084 * Return value is owned by the caller.
1085 */
1086static
1087struct bt_value *names_from_arg(const char *arg)
1088{
1089 GScanner *scanner = NULL;
1090 struct bt_value *names = NULL;
6e1bc0df 1091 bool found_all = false, found_none = false, found_item = false;
c42c79ea
PP
1092
1093 names = bt_value_array_create();
1094 if (!names) {
1095 print_err_oom();
1096 goto error;
1097 }
1098
1099 scanner = create_csv_identifiers_scanner();
1100 if (!scanner) {
1101 print_err_oom();
1102 goto error;
1103 }
1104
1105 g_scanner_input_text(scanner, arg, strlen(arg));
1106
1107 while (true) {
1108 GTokenType token_type = g_scanner_get_next_token(scanner);
1109
1110 switch (token_type) {
1111 case G_TOKEN_IDENTIFIER:
1112 {
1113 const char *identifier = scanner->value.v_identifier;
1114
1115 if (!strcmp(identifier, "payload") ||
1116 !strcmp(identifier, "args") ||
1117 !strcmp(identifier, "arg")) {
6e1bc0df 1118 found_item = true;
c42c79ea
PP
1119 if (bt_value_array_append_string(names,
1120 "payload")) {
1121 goto error;
1122 }
1123 } else if (!strcmp(identifier, "context") ||
1124 !strcmp(identifier, "ctx")) {
6e1bc0df 1125 found_item = true;
c42c79ea
PP
1126 if (bt_value_array_append_string(names,
1127 "context")) {
1128 goto error;
1129 }
1130 } else if (!strcmp(identifier, "scope") ||
1131 !strcmp(identifier, "header")) {
6e1bc0df 1132 found_item = true;
c42c79ea
PP
1133 if (bt_value_array_append_string(names,
1134 identifier)) {
1135 goto error;
1136 }
6e1bc0df
MD
1137 } else if (!strcmp(identifier, "all")) {
1138 found_all = true;
1139 if (bt_value_array_append_string(names,
1140 identifier)) {
c42c79ea
PP
1141 goto error;
1142 }
6e1bc0df
MD
1143 } else if (!strcmp(identifier, "none")) {
1144 found_none = true;
c42c79ea
PP
1145 if (bt_value_array_append_string(names,
1146 identifier)) {
1147 goto error;
1148 }
c42c79ea 1149 } else {
6d1d5711 1150 printf_err("Unknown field name: `%s`\n",
c42c79ea
PP
1151 identifier);
1152 goto error;
1153 }
1154 break;
1155 }
1156 case G_TOKEN_COMMA:
1157 continue;
1158 case G_TOKEN_EOF:
1159 goto end;
1160 default:
1161 goto error;
1162 }
1163 }
1164
6e1bc0df
MD
1165end:
1166 if (found_none && found_all) {
6d1d5711 1167 printf_err("Only either `all` or `none` can be specified in the list given to the --names option, but not both.\n");
6e1bc0df
MD
1168 goto error;
1169 }
1170 /*
1171 * Legacy behavior is to clear the defaults (show none) when at
1172 * least one item is specified.
1173 */
1174 if (found_item && !found_none && !found_all) {
1175 if (bt_value_array_append_string(names, "none")) {
1176 goto error;
1177 }
1178 }
1179 if (scanner) {
1180 g_scanner_destroy(scanner);
1181 }
1182 return names;
c42c79ea
PP
1183
1184error:
1185 BT_PUT(names);
c42c79ea
PP
1186 if (scanner) {
1187 g_scanner_destroy(scanner);
1188 }
1189 return names;
1190}
1191
1192
1193/*
1194 * Converts a comma-delimited list of known fields (--fields option) to
1195 * an array value object containing those fields as string
1196 * value objects.
1197 *
1198 * Return value is owned by the caller.
1199 */
1200static
1201struct bt_value *fields_from_arg(const char *arg)
1202{
1203 GScanner *scanner = NULL;
1204 struct bt_value *fields;
1205
1206 fields = bt_value_array_create();
1207 if (!fields) {
1208 print_err_oom();
1209 goto error;
1210 }
1211
1212 scanner = create_csv_identifiers_scanner();
1213 if (!scanner) {
1214 print_err_oom();
1215 goto error;
1216 }
1217
1218 g_scanner_input_text(scanner, arg, strlen(arg));
1219
1220 while (true) {
1221 GTokenType token_type = g_scanner_get_next_token(scanner);
1222
1223 switch (token_type) {
1224 case G_TOKEN_IDENTIFIER:
1225 {
1226 const char *identifier = scanner->value.v_identifier;
1227
1228 if (!strcmp(identifier, "trace") ||
1229 !strcmp(identifier, "trace:hostname") ||
1230 !strcmp(identifier, "trace:domain") ||
1231 !strcmp(identifier, "trace:procname") ||
1232 !strcmp(identifier, "trace:vpid") ||
1233 !strcmp(identifier, "loglevel") ||
1234 !strcmp(identifier, "emf") ||
6e1bc0df
MD
1235 !strcmp(identifier, "callsite") ||
1236 !strcmp(identifier, "all")) {
c42c79ea
PP
1237 if (bt_value_array_append_string(fields,
1238 identifier)) {
1239 goto error;
1240 }
c42c79ea 1241 } else {
6d1d5711 1242 printf_err("Unknown field name: `%s`\n",
c42c79ea
PP
1243 identifier);
1244 goto error;
1245 }
1246 break;
1247 }
1248 case G_TOKEN_COMMA:
1249 continue;
1250 case G_TOKEN_EOF:
1251 goto end;
1252 default:
1253 goto error;
1254 }
1255 }
1256
1257 goto end;
1258
1259error:
1260 BT_PUT(fields);
1261
1262end:
1263 if (scanner) {
1264 g_scanner_destroy(scanner);
1265 }
1266 return fields;
1267}
1268
1269/*
1270 * Inserts the equivalent "prefix-name" true boolean value objects into
1271 * map_obj where the names are in array_obj.
1272 */
1273static
1274int insert_flat_names_fields_from_array(struct bt_value *map_obj,
1275 struct bt_value *array_obj, const char *prefix)
1276{
1277 int ret = 0;
1278 int i;
6e1bc0df 1279 GString *tmpstr = NULL, *default_value = NULL;
c42c79ea
PP
1280
1281 /*
1282 * array_obj may be NULL if no CLI options were specified to
1283 * trigger its creation.
1284 */
1285 if (!array_obj) {
1286 goto end;
1287 }
1288
1289 tmpstr = g_string_new(NULL);
1290 if (!tmpstr) {
1291 print_err_oom();
1292 ret = -1;
1293 goto end;
1294 }
1295
6e1bc0df
MD
1296 default_value = g_string_new(NULL);
1297 if (!default_value) {
1298 print_err_oom();
1299 ret = -1;
1300 goto end;
1301 }
1302
c42c79ea
PP
1303 for (i = 0; i < bt_value_array_size(array_obj); i++) {
1304 struct bt_value *str_obj = bt_value_array_get(array_obj, i);
1305 const char *suffix;
6e1bc0df 1306 bool is_default = false;
c42c79ea
PP
1307
1308 if (!str_obj) {
1309 printf_err("Unexpected error\n");
1310 ret = -1;
1311 goto end;
1312 }
1313
1314 ret = bt_value_string_get(str_obj, &suffix);
1315 BT_PUT(str_obj);
1316 if (ret) {
1317 printf_err("Unexpected error\n");
1318 goto end;
1319 }
1320
1321 g_string_assign(tmpstr, prefix);
1322 g_string_append(tmpstr, "-");
6e1bc0df
MD
1323
1324 /* Special-case for "all" and "none". */
1325 if (!strcmp(suffix, "all")) {
1326 is_default = true;
1327 g_string_assign(default_value, "show");
1328 } else if (!strcmp(suffix, "none")) {
1329 is_default = true;
1330 g_string_assign(default_value, "hide");
1331 }
1332 if (is_default) {
1333 g_string_append(tmpstr, "default");
1334 ret = map_insert_string_or_null(map_obj,
1335 tmpstr->str,
1336 default_value);
1337 if (ret) {
1338 print_err_oom();
1339 goto end;
1340 }
1341 } else {
1342 g_string_append(tmpstr, suffix);
1343 ret = bt_value_map_insert_bool(map_obj, tmpstr->str,
1344 true);
1345 if (ret) {
1346 print_err_oom();
1347 goto end;
1348 }
c42c79ea
PP
1349 }
1350 }
1351
1352end:
6e1bc0df
MD
1353 if (default_value) {
1354 g_string_free(default_value, TRUE);
1355 }
c42c79ea
PP
1356 if (tmpstr) {
1357 g_string_free(tmpstr, TRUE);
1358 }
1359
1360 return ret;
1361}
1362
c42c79ea
PP
1363/*
1364 * Returns the parameters (map value object) corresponding to the
1365 * legacy text format options.
1366 *
1367 * Return value is owned by the caller.
1368 */
1369static
1370struct bt_value *params_from_text_legacy_opts(
1371 struct text_legacy_opts *text_legacy_opts)
1372{
1373 struct bt_value *params;
1374
1375 params = bt_value_map_create();
1376 if (!params) {
1377 print_err_oom();
1378 goto error;
1379 }
1380
1381 if (map_insert_string_or_null(params, "output-path",
1382 text_legacy_opts->output)) {
1383 print_err_oom();
1384 goto error;
1385 }
1386
1387 if (map_insert_string_or_null(params, "debug-info-dir",
1388 text_legacy_opts->dbg_info_dir)) {
1389 print_err_oom();
1390 goto error;
1391 }
1392
1393 if (map_insert_string_or_null(params, "debug-info-target-prefix",
1394 text_legacy_opts->dbg_info_target_prefix)) {
1395 print_err_oom();
1396 goto error;
1397 }
1398
1399 if (bt_value_map_insert_bool(params, "debug-info-full-path",
1400 text_legacy_opts->dbg_info_full_path)) {
1401 print_err_oom();
1402 goto error;
1403 }
1404
1405 if (bt_value_map_insert_bool(params, "no-delta",
1406 text_legacy_opts->no_delta)) {
1407 print_err_oom();
1408 goto error;
1409 }
1410
1411 if (bt_value_map_insert_bool(params, "clock-cycles",
1412 text_legacy_opts->clock_cycles)) {
1413 print_err_oom();
1414 goto error;
1415 }
1416
1417 if (bt_value_map_insert_bool(params, "clock-seconds",
1418 text_legacy_opts->clock_seconds)) {
1419 print_err_oom();
1420 goto error;
1421 }
1422
1423 if (bt_value_map_insert_bool(params, "clock-date",
1424 text_legacy_opts->clock_date)) {
1425 print_err_oom();
1426 goto error;
1427 }
1428
1429 if (bt_value_map_insert_bool(params, "clock-gmt",
1430 text_legacy_opts->clock_gmt)) {
1431 print_err_oom();
1432 goto error;
1433 }
1434
1435 if (insert_flat_names_fields_from_array(params,
1436 text_legacy_opts->names, "name")) {
1437 goto error;
1438 }
1439
1440 if (insert_flat_names_fields_from_array(params,
1441 text_legacy_opts->fields, "field")) {
1442 goto error;
1443 }
1444
1445 goto end;
1446
1447error:
1448 BT_PUT(params);
1449
1450end:
1451 return params;
1452}
1453
1454static
1455int append_sinks_from_legacy_opts(GPtrArray *sinks,
1456 enum legacy_output_format legacy_output_format,
1457 struct text_legacy_opts *text_legacy_opts)
1458{
1459 int ret = 0;
1460 struct bt_value *params = NULL;
1461 const char *plugin_name;
1462 const char *component_name;
1463 struct bt_config_component *bt_config_component = NULL;
1464
1465 switch (legacy_output_format) {
1466 case LEGACY_OUTPUT_FORMAT_TEXT:
1467 plugin_name = "text";
1468 component_name = "text";
1469 break;
1470 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1471 plugin_name = "ctf";
1472 component_name = "metadata-text";
1473 break;
1474 case LEGACY_OUTPUT_FORMAT_DUMMY:
1475 plugin_name = "dummy";
1476 component_name = "dummy";
1477 break;
1478 default:
1479 assert(false);
1480 break;
1481 }
1482
1483 if (legacy_output_format == LEGACY_OUTPUT_FORMAT_TEXT) {
1484 /* Legacy "text" output format has parameters */
1485 params = params_from_text_legacy_opts(text_legacy_opts);
1486 if (!params) {
1487 goto error;
1488 }
1489 } else {
1490 /*
1491 * Legacy "dummy" and "ctf-metadata" output formats do
1492 * not have parameters.
1493 */
1494 params = bt_value_map_create();
1495 if (!params) {
1496 print_err_oom();
1497 goto error;
1498 }
1499 }
1500
1501 /* Create a component configuration */
1502 bt_config_component = bt_config_component_create(plugin_name,
bdc61c70 1503 component_name);
c42c79ea
PP
1504 if (!bt_config_component) {
1505 goto error;
1506 }
1507
bdc61c70
PP
1508 BT_MOVE(bt_config_component->params, params);
1509
c42c79ea
PP
1510 /* Move created component configuration to the array */
1511 g_ptr_array_add(sinks, bt_config_component);
1512
1513 goto end;
1514
1515error:
1516 ret = -1;
1517
1518end:
1519 BT_PUT(params);
1520
1521 return ret;
1522}
1523
1524/*
1525 * Returns the parameters (map value object) corresponding to the
1526 * given legacy CTF format options.
1527 *
1528 * Return value is owned by the caller.
1529 */
1530static
1531struct bt_value *params_from_ctf_legacy_opts(
1532 struct ctf_legacy_opts *ctf_legacy_opts)
1533{
1534 struct bt_value *params;
1535
1536 params = bt_value_map_create();
1537 if (!params) {
1538 print_err_oom();
1539 goto error;
1540 }
1541
1542 if (bt_value_map_insert_integer(params, "offset-s",
1543 ctf_legacy_opts->offset_s.value)) {
1544 print_err_oom();
1545 goto error;
1546 }
1547
1548 if (bt_value_map_insert_integer(params, "offset-ns",
1549 ctf_legacy_opts->offset_ns.value)) {
1550 print_err_oom();
1551 goto error;
1552 }
1553
1554 if (bt_value_map_insert_bool(params, "stream-intersection",
1555 ctf_legacy_opts->stream_intersection)) {
1556 print_err_oom();
1557 goto error;
1558 }
1559
1560 goto end;
1561
1562error:
1563 BT_PUT(params);
1564
1565end:
1566 return params;
1567}
1568
1569static
1570int append_sources_from_legacy_opts(GPtrArray *sources,
1571 enum legacy_input_format legacy_input_format,
1572 struct ctf_legacy_opts *ctf_legacy_opts,
528debdf 1573 struct bt_value *legacy_input_paths)
c42c79ea
PP
1574{
1575 int ret = 0;
1576 int i;
1577 struct bt_value *base_params;
1578 struct bt_value *params = NULL;
1579 struct bt_value *input_path = NULL;
1580 struct bt_value *input_path_copy = NULL;
1581 const char *input_key;
1582 const char *component_name;
1583
1584 switch (legacy_input_format) {
1585 case LEGACY_INPUT_FORMAT_CTF:
1586 input_key = "path";
1587 component_name = "fs";
1588 break;
1589 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
1590 input_key = "url";
1591 component_name = "lttng-live";
1592 break;
1593 default:
1594 assert(false);
1595 break;
1596 }
1597
1598 base_params = params_from_ctf_legacy_opts(ctf_legacy_opts);
1599 if (!base_params) {
1600 goto error;
1601 }
1602
1603 for (i = 0; i < bt_value_array_size(legacy_input_paths); i++) {
1604 struct bt_config_component *bt_config_component = NULL;
1605
1606 /* Copy base parameters as current parameters */
1607 params = bt_value_copy(base_params);
1608 if (!params) {
1609 goto error;
1610 }
1611
1612 /* Get current input path string value object */
1613 input_path = bt_value_array_get(legacy_input_paths, i);
1614 if (!input_path) {
1615 goto error;
1616 }
1617
1618 /* Copy current input path value object */
1619 input_path_copy = bt_value_copy(input_path);
1620 if (!input_path_copy) {
1621 goto error;
1622 }
1623
1624 /* Insert input path value object into current parameters */
1625 ret = bt_value_map_insert(params, input_key, input_path_copy);
1626 if (ret) {
1627 goto error;
1628 }
1629
1630 /* Create a component configuration */
1631 bt_config_component = bt_config_component_create("ctf",
bdc61c70 1632 component_name);
c42c79ea
PP
1633 if (!bt_config_component) {
1634 goto error;
1635 }
1636
bdc61c70
PP
1637 BT_MOVE(bt_config_component->params, params);
1638
c42c79ea
PP
1639 /* Move created component configuration to the array */
1640 g_ptr_array_add(sources, bt_config_component);
1641
1642 /* Put current stuff */
1643 BT_PUT(input_path);
1644 BT_PUT(input_path_copy);
c42c79ea
PP
1645 }
1646
1647 goto end;
1648
1649error:
1650 ret = -1;
1651
1652end:
1653 BT_PUT(base_params);
1654 BT_PUT(params);
1655 BT_PUT(input_path);
1656 BT_PUT(input_path_copy);
1657 return ret;
1658}
1659
1660/*
1661 * Escapes a string for the shell. The string is escaped knowing that
1662 * it's a parameter string value (double-quoted), and that it will be
1663 * entered between single quotes in the shell.
1664 *
1665 * Return value is owned by the caller.
1666 */
1667static
1668char *str_shell_escape(const char *input)
1669{
1670 char *ret = NULL;
1671 const char *at = input;
1672 GString *str = g_string_new(NULL);
1673
1674 if (!str) {
1675 goto end;
1676 }
1677
1678 while (*at != '\0') {
1679 switch (*at) {
1680 case '\\':
1681 g_string_append(str, "\\\\");
1682 break;
1683 case '"':
1684 g_string_append(str, "\\\"");
1685 break;
1686 case '\'':
1687 g_string_append(str, "'\"'\"'");
1688 break;
1689 case '\n':
1690 g_string_append(str, "\\n");
1691 break;
1692 case '\t':
1693 g_string_append(str, "\\t");
1694 break;
1695 default:
1696 g_string_append_c(str, *at);
1697 break;
1698 }
1699
1700 at++;
1701 }
1702
1703end:
1704 if (str) {
1705 ret = str->str;
1706 g_string_free(str, FALSE);
1707 }
1708
1709 return ret;
1710}
1711
1712static
1713int append_prefixed_flag_params(GString *str, struct bt_value *flags,
1714 const char *prefix)
1715{
1716 int ret = 0;
1717 int i;
1718
1719 if (!flags) {
1720 goto end;
1721 }
1722
1723 for (i = 0; i < bt_value_array_size(flags); i++) {
1724 struct bt_value *value = bt_value_array_get(flags, i);
1725 const char *flag;
1726
1727 if (!value) {
1728 ret = -1;
1729 goto end;
1730 }
1731
1732 if (bt_value_string_get(value, &flag)) {
1733 BT_PUT(value);
1734 ret = -1;
1735 goto end;
1736 }
1737
1738 g_string_append_printf(str, ",%s-%s=true", prefix, flag);
1739 BT_PUT(value);
1740 }
1741
1742end:
1743 return ret;
1744}
1745
1746/*
1747 * Appends a boolean parameter string.
1748 */
1749static
1750void g_string_append_bool_param(GString *str, const char *name, bool value)
1751{
1752 g_string_append_printf(str, ",%s=%s", name, value ? "true" : "false");
1753}
1754
1755/*
1756 * Appends a path parameter string, or null if it's empty.
1757 */
1758static
1759int g_string_append_string_path_param(GString *str, const char *name,
1760 GString *path)
1761{
1762 int ret = 0;
1763
1764 if (path->len > 0) {
1765 char *escaped_path = str_shell_escape(path->str);
1766
1767 if (!escaped_path) {
1768 print_err_oom();
1769 goto error;
1770 }
1771
1772 g_string_append_printf(str, "%s=\"%s\"", name, escaped_path);
1773 free(escaped_path);
1774 } else {
1775 g_string_append_printf(str, "%s=null", name);
1776 }
1777
1778 goto end;
1779
1780error:
1781 ret = -1;
1782
1783end:
1784 return ret;
1785}
1786
1787/*
1788 * Prints the non-legacy sink options equivalent to the specified
1789 * legacy output format options.
1790 */
1791static
1792void print_output_legacy_to_sinks(
1793 enum legacy_output_format legacy_output_format,
1794 struct text_legacy_opts *text_legacy_opts)
1795{
1796 const char *input_format;
1797 GString *str = NULL;
1798
1799 str = g_string_new(" ");
1800 if (!str) {
1801 print_err_oom();
1802 goto end;
1803 }
1804
1805 switch (legacy_output_format) {
1806 case LEGACY_OUTPUT_FORMAT_TEXT:
1807 input_format = "text";
1808 break;
1809 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1810 input_format = "ctf-metadata";
1811 break;
1812 case LEGACY_OUTPUT_FORMAT_DUMMY:
1813 input_format = "dummy";
1814 break;
1815 default:
1816 assert(false);
1817 }
1818
49849a47 1819 printf_err("Both `%s` legacy output format and non-legacy sink component\ninstances(s) specified.\n\n",
c42c79ea 1820 input_format);
49849a47 1821 printf_err("Specify the following non-legacy sink component instance instead of the\nlegacy `%s` output format options:\n\n",
c42c79ea
PP
1822 input_format);
1823 g_string_append(str, "-o ");
1824
1825 switch (legacy_output_format) {
1826 case LEGACY_OUTPUT_FORMAT_TEXT:
1827 g_string_append(str, "text.text");
1828 break;
1829 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1830 g_string_append(str, "ctf.metadata-text");
1831 break;
1832 case LEGACY_OUTPUT_FORMAT_DUMMY:
1833 g_string_append(str, "dummy.dummy");
1834 break;
1835 default:
1836 assert(false);
1837 }
1838
1839 if (legacy_output_format == LEGACY_OUTPUT_FORMAT_TEXT &&
1840 text_legacy_opts_is_any_set(text_legacy_opts)) {
1841 int ret;
1842
bdc61c70 1843 g_string_append(str, " -p '");
c42c79ea
PP
1844
1845 if (g_string_append_string_path_param(str, "output-path",
1846 text_legacy_opts->output)) {
1847 goto end;
1848 }
1849
1850 g_string_append(str, ",");
1851
1852 if (g_string_append_string_path_param(str, "debug-info-dir",
1853 text_legacy_opts->dbg_info_dir)) {
1854 goto end;
1855 }
1856
1857 g_string_append(str, ",");
1858
1859 if (g_string_append_string_path_param(str,
1860 "debug-info-target-prefix",
1861 text_legacy_opts->dbg_info_target_prefix)) {
1862 goto end;
1863 }
1864
1865 g_string_append_bool_param(str, "no-delta",
1866 text_legacy_opts->no_delta);
1867 g_string_append_bool_param(str, "clock-cycles",
1868 text_legacy_opts->clock_cycles);
1869 g_string_append_bool_param(str, "clock-seconds",
1870 text_legacy_opts->clock_seconds);
1871 g_string_append_bool_param(str, "clock-date",
1872 text_legacy_opts->clock_date);
1873 g_string_append_bool_param(str, "clock-gmt",
1874 text_legacy_opts->clock_gmt);
1875 ret = append_prefixed_flag_params(str, text_legacy_opts->names,
1876 "name");
1877 if (ret) {
1878 goto end;
1879 }
1880
1881 ret = append_prefixed_flag_params(str, text_legacy_opts->fields,
1882 "field");
1883 if (ret) {
1884 goto end;
1885 }
1886
1887 /* Remove last comma and close single quote */
1888 g_string_append(str, "'");
1889 }
1890
1891 printf_err("%s\n\n", str->str);
1892
1893end:
1894 if (str) {
1895 g_string_free(str, TRUE);
1896 }
1897 return;
1898}
1899
1900/*
1901 * Prints the non-legacy source options equivalent to the specified
1902 * legacy input format options.
1903 */
1904static
1905void print_input_legacy_to_sources(enum legacy_input_format legacy_input_format,
1906 struct bt_value *legacy_input_paths,
1907 struct ctf_legacy_opts *ctf_legacy_opts)
1908{
1909 const char *input_format;
1910 GString *str = NULL;
1911 int i;
1912
1913 str = g_string_new(" ");
1914 if (!str) {
1915 print_err_oom();
1916 goto end;
1917 }
1918
1919 switch (legacy_input_format) {
1920 case LEGACY_INPUT_FORMAT_CTF:
1921 input_format = "ctf";
1922 break;
1923 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
1924 input_format = "lttng-live";
1925 break;
1926 default:
1927 assert(false);
1928 }
1929
49849a47 1930 printf_err("Both `%s` legacy input format and non-legacy source component\ninstance(s) specified.\n\n",
c42c79ea 1931 input_format);
49849a47 1932 printf_err("Specify the following non-legacy source component instance(s) instead of the\nlegacy `%s` input format options and positional arguments:\n\n",
c42c79ea
PP
1933 input_format);
1934
1935 for (i = 0; i < bt_value_array_size(legacy_input_paths); i++) {
1936 struct bt_value *input_value =
1937 bt_value_array_get(legacy_input_paths, i);
1938 const char *input = NULL;
1939 char *escaped_input;
1940 int ret;
1941
1942 assert(input_value);
1943 ret = bt_value_string_get(input_value, &input);
1944 BT_PUT(input_value);
1945 assert(!ret && input);
1946 escaped_input = str_shell_escape(input);
1947 if (!escaped_input) {
1948 print_err_oom();
1949 goto end;
1950 }
1951
1952 g_string_append(str, "-i ctf.");
1953
1954 switch (legacy_input_format) {
1955 case LEGACY_INPUT_FORMAT_CTF:
bdc61c70 1956 g_string_append(str, "fs -p 'path=\"");
c42c79ea
PP
1957 break;
1958 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
bdc61c70 1959 g_string_append(str, "lttng-live -p 'url=\"");
c42c79ea
PP
1960 break;
1961 default:
1962 assert(false);
1963 }
1964
1965 g_string_append(str, escaped_input);
1966 g_string_append(str, "\"");
1967 g_string_append_printf(str, ",offset-s=%" PRId64,
1968 ctf_legacy_opts->offset_s.value);
1969 g_string_append_printf(str, ",offset-ns=%" PRId64,
1970 ctf_legacy_opts->offset_ns.value);
1971 g_string_append_bool_param(str, "stream-intersection",
1972 ctf_legacy_opts->stream_intersection);
1973 g_string_append(str, "' ");
1974 g_free(escaped_input);
1975 }
1976
1977 printf_err("%s\n\n", str->str);
1978
1979end:
1980 if (str) {
1981 g_string_free(str, TRUE);
1982 }
1983 return;
1984}
1985
1986/*
1987 * Validates a given configuration, with optional legacy input and
1988 * output formats options. Prints useful error messages if anything
1989 * is wrong.
1990 *
1991 * Returns true when the configuration is valid.
1992 */
1993static
1994bool validate_cfg(struct bt_config *cfg,
1995 enum legacy_input_format *legacy_input_format,
1996 enum legacy_output_format *legacy_output_format,
1997 struct bt_value *legacy_input_paths,
1998 struct ctf_legacy_opts *ctf_legacy_opts,
1999 struct text_legacy_opts *text_legacy_opts)
2000{
2001 bool legacy_input = false;
2002 bool legacy_output = false;
2003
2004 /* Determine if the input and output should be legacy-style */
2005 if (*legacy_input_format != LEGACY_INPUT_FORMAT_NONE ||
2006 cfg->sources->len == 0 ||
2007 !bt_value_array_is_empty(legacy_input_paths) ||
2008 ctf_legacy_opts_is_any_set(ctf_legacy_opts)) {
2009 legacy_input = true;
2010 }
2011
2012 if (*legacy_output_format != LEGACY_OUTPUT_FORMAT_NONE ||
2013 cfg->sinks->len == 0 ||
2014 text_legacy_opts_is_any_set(text_legacy_opts)) {
2015 legacy_output = true;
2016 }
2017
2018 if (legacy_input) {
2019 /* If no legacy input format was specified, default to CTF */
2020 if (*legacy_input_format == LEGACY_INPUT_FORMAT_NONE) {
2021 *legacy_input_format = LEGACY_INPUT_FORMAT_CTF;
2022 }
2023
2024 /* Make sure at least one input path exists */
2025 if (bt_value_array_is_empty(legacy_input_paths)) {
2026 switch (*legacy_input_format) {
2027 case LEGACY_INPUT_FORMAT_CTF:
6d1d5711 2028 printf_err("No input path specified for legacy `ctf` input format\n");
c42c79ea
PP
2029 break;
2030 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
6d1d5711 2031 printf_err("No URL specified for legacy `lttng-live` input format\n");
c42c79ea
PP
2032 break;
2033 default:
2034 assert(false);
2035 }
2036 goto error;
2037 }
2038
2039 /* Make sure no non-legacy sources are specified */
2040 if (cfg->sources->len != 0) {
2041 print_input_legacy_to_sources(*legacy_input_format,
2042 legacy_input_paths, ctf_legacy_opts);
2043 goto error;
2044 }
2045 }
2046
2047 if (legacy_output) {
2048 /*
2049 * If no legacy output format was specified, default to
2050 * "text".
2051 */
2052 if (*legacy_output_format == LEGACY_OUTPUT_FORMAT_NONE) {
2053 *legacy_output_format = LEGACY_OUTPUT_FORMAT_TEXT;
2054 }
2055
2056 /*
2057 * If any "text" option was specified, the output must be
2058 * legacy "text".
2059 */
2060 if (text_legacy_opts_is_any_set(text_legacy_opts) &&
2061 *legacy_output_format !=
2062 LEGACY_OUTPUT_FORMAT_TEXT) {
6d1d5711 2063 printf_err("Options for legacy `text` output format specified with a different legacy output format\n");
c42c79ea
PP
2064 goto error;
2065 }
2066
2067 /* Make sure no non-legacy sinks are specified */
2068 if (cfg->sinks->len != 0) {
2069 print_output_legacy_to_sinks(*legacy_output_format,
2070 text_legacy_opts);
2071 goto error;
2072 }
2073 }
2074
2075 /*
2076 * If the output is the legacy "ctf-metadata" format, then the
2077 * input should be the legacy "ctf" input format.
2078 */
2079 if (*legacy_output_format == LEGACY_OUTPUT_FORMAT_CTF_METADATA &&
2080 *legacy_input_format != LEGACY_INPUT_FORMAT_CTF) {
6d1d5711 2081 printf_err("Legacy `ctf-metadata` output format requires using legacy `ctf` input format\n");
c42c79ea
PP
2082 goto error;
2083 }
2084
2085 return true;
2086
2087error:
2088 return false;
2089}
2090
2091/*
2092 * Parses a 64-bit signed integer.
2093 *
2094 * Returns a negative value if anything goes wrong.
2095 */
2096static
2097int parse_int64(const char *arg, int64_t *val)
2098{
2099 char *endptr;
2100
2101 errno = 0;
2102 *val = strtoll(arg, &endptr, 0);
2103 if (*endptr != '\0' || arg == endptr || errno != 0) {
2104 return -1;
2105 }
2106
2107 return 0;
2108}
2109
2110/* popt options */
2111enum {
2112 OPT_NONE = 0,
b07ffa28 2113 OPT_BASE_PARAMS,
0f9915c6 2114 OPT_BEGIN,
c42c79ea
PP
2115 OPT_CLOCK_CYCLES,
2116 OPT_CLOCK_DATE,
2117 OPT_CLOCK_FORCE_CORRELATE,
2118 OPT_CLOCK_GMT,
2119 OPT_CLOCK_OFFSET,
2120 OPT_CLOCK_OFFSET_NS,
2121 OPT_CLOCK_SECONDS,
2122 OPT_DEBUG,
2123 OPT_DEBUG_INFO_DIR,
2124 OPT_DEBUG_INFO_FULL_PATH,
2125 OPT_DEBUG_INFO_TARGET_PREFIX,
0f9915c6 2126 OPT_END,
c42c79ea
PP
2127 OPT_FIELDS,
2128 OPT_HELP,
2129 OPT_HELP_LEGACY,
2130 OPT_INPUT_FORMAT,
2131 OPT_LIST,
2132 OPT_NAMES,
2133 OPT_NO_DELTA,
2134 OPT_OUTPUT_FORMAT,
2135 OPT_OUTPUT_PATH,
ad6a19bd 2136 OPT_PATH,
bdc61c70 2137 OPT_PARAMS,
c42c79ea 2138 OPT_PLUGIN_PATH,
b07ffa28 2139 OPT_RESET_BASE_PARAMS,
c42c79ea
PP
2140 OPT_SINK,
2141 OPT_SOURCE,
2142 OPT_STREAM_INTERSECTION,
0f9915c6 2143 OPT_TIMERANGE,
c42c79ea
PP
2144 OPT_VERBOSE,
2145 OPT_VERSION,
2146};
2147
2148/* popt long option descriptions */
2149static struct poptOption long_options[] = {
2150 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
b07ffa28 2151 { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL },
0f9915c6 2152 { "begin", '\0', POPT_ARG_STRING, NULL, OPT_BEGIN, NULL, NULL },
c42c79ea
PP
2153 { "clock-cycles", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL },
2154 { "clock-date", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL },
2155 { "clock-force-correlate", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL },
2156 { "clock-gmt", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL },
2157 { "clock-offset", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL },
2158 { "clock-offset-ns", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET_NS, NULL, NULL },
2159 { "clock-seconds", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL },
2160 { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL },
2161 { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_DIR, NULL, NULL },
2162 { "debug-info-full-path", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_INFO_FULL_PATH, NULL, NULL },
2163 { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL },
0f9915c6 2164 { "end", '\0', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL },
c42c79ea
PP
2165 { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL },
2166 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
2167 { "help-legacy", '\0', POPT_ARG_NONE, NULL, OPT_HELP_LEGACY, NULL, NULL },
2168 { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL },
2169 { "list", 'l', POPT_ARG_NONE, NULL, OPT_LIST, NULL, NULL },
2170 { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL },
2171 { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL },
2172 { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT_PATH, NULL, NULL },
2173 { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL },
ad6a19bd 2174 { "path", 'P', POPT_ARG_STRING, NULL, OPT_PATH, NULL, NULL },
bdc61c70 2175 { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL },
ad6a19bd 2176 { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL },
b07ffa28 2177 { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, NULL, NULL },
c42c79ea
PP
2178 { "sink", '\0', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL },
2179 { "source", '\0', POPT_ARG_STRING, NULL, OPT_SOURCE, NULL, NULL },
2180 { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL },
0f9915c6 2181 { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL },
c42c79ea
PP
2182 { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL },
2183 { "version", 'V', POPT_ARG_NONE, NULL, OPT_VERSION, NULL, NULL },
2184 { NULL, 0, 0, NULL, 0, NULL, NULL },
2185};
2186
2187/*
2188 * Sets the value of a given legacy offset option and marks it as set.
2189 */
2190static void set_offset_value(struct offset_opt *offset_opt, int64_t value)
2191{
2192 offset_opt->value = value;
2193 offset_opt->is_set = true;
2194}
2195
bdc61c70
PP
2196enum bt_config_component_dest {
2197 BT_CONFIG_COMPONENT_DEST_SOURCE,
2198 BT_CONFIG_COMPONENT_DEST_SINK,
2199};
2200
2201/*
2202 * Adds a configuration component to the appropriate configuration
2203 * array depending on the destination.
2204 */
2205static void add_cfg_comp(struct bt_config *cfg,
2206 struct bt_config_component *cfg_comp,
2207 enum bt_config_component_dest dest)
2208{
2209 if (dest == BT_CONFIG_COMPONENT_DEST_SOURCE) {
2210 g_ptr_array_add(cfg->sources, cfg_comp);
2211 } else {
2212 g_ptr_array_add(cfg->sinks, cfg_comp);
2213 }
2214}
2215
0f9915c6
MD
2216static int split_timerange(const char *arg, const char **begin, const char **end)
2217{
2218 const char *c;
2219
2220 /* Try to match [begin,end] */
2221 c = strchr(arg, '[');
2222 if (!c)
2223 goto skip;
2224 *begin = ++c;
2225 c = strchr(c, ',');
2226 if (!c)
2227 goto skip;
2228 *end = ++c;
2229 c = strchr(c, ']');
2230 if (!c)
2231 goto skip;
2232 goto found;
2233
2234skip:
2235 /* Try to match begin,end */
2236 c = arg;
2237 *begin = c;
2238 c = strchr(c, ',');
2239 if (!c)
2240 goto not_found;
2241 *end = ++c;
2242 /* fall-through */
2243found:
2244 return 0;
2245not_found:
2246 return -1;
2247}
2248
c42c79ea
PP
2249/*
2250 * Returns a Babeltrace configuration, out of command-line arguments,
2251 * containing everything that is needed to instanciate specific
2252 * components with given parameters.
2253 *
2254 * *exit_code is set to the appropriate exit code to use as far as this
2255 * function goes.
2256 *
2257 * Return value is NULL on error, otherwise it's owned by the caller.
2258 */
528debdf 2259struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_code)
c42c79ea
PP
2260{
2261 struct bt_config *cfg = NULL;
2262 poptContext pc = NULL;
2263 char *arg = NULL;
b7726e32
MD
2264 struct ctf_legacy_opts ctf_legacy_opts;
2265 struct text_legacy_opts text_legacy_opts;
c42c79ea
PP
2266 enum legacy_input_format legacy_input_format = LEGACY_INPUT_FORMAT_NONE;
2267 enum legacy_output_format legacy_output_format =
2268 LEGACY_OUTPUT_FORMAT_NONE;
2269 struct bt_value *legacy_input_paths = NULL;
bdc61c70
PP
2270 struct bt_config_component *cur_cfg_comp = NULL;
2271 enum bt_config_component_dest cur_cfg_comp_dest =
2272 BT_CONFIG_COMPONENT_DEST_SOURCE;
b07ffa28 2273 struct bt_value *cur_base_params = NULL;
c42c79ea 2274 int opt;
bdc61c70 2275 bool cur_cfg_comp_params_set = false;
c42c79ea 2276
b7726e32
MD
2277 memset(&ctf_legacy_opts, 0, sizeof(ctf_legacy_opts));
2278 memset(&text_legacy_opts, 0, sizeof(text_legacy_opts));
c42c79ea
PP
2279 *exit_code = 0;
2280
2281 if (argc <= 1) {
2282 print_usage(stdout);
2283 goto end;
2284 }
2285
2286 text_legacy_opts.output = g_string_new(NULL);
2287 if (!text_legacy_opts.output) {
2288 print_err_oom();
2289 goto error;
2290 }
2291
2292 text_legacy_opts.dbg_info_dir = g_string_new(NULL);
2293 if (!text_legacy_opts.dbg_info_dir) {
2294 print_err_oom();
2295 goto error;
2296 }
2297
2298 text_legacy_opts.dbg_info_target_prefix = g_string_new(NULL);
2299 if (!text_legacy_opts.dbg_info_target_prefix) {
2300 print_err_oom();
2301 goto error;
2302 }
2303
b07ffa28
PP
2304 cur_base_params = bt_value_map_create();
2305 if (!cur_base_params) {
2306 print_err_oom();
2307 goto error;
2308 }
2309
c42c79ea
PP
2310 /* Create config */
2311 cfg = g_new0(struct bt_config, 1);
2312 if (!cfg) {
2313 print_err_oom();
2314 goto error;
2315 }
2316
2317 bt_object_init(cfg, bt_config_destroy);
2318 cfg->sources = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put);
2319 if (!cfg->sources) {
2320 print_err_oom();
2321 goto error;
2322 }
2323
2324 cfg->sinks = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put);
2325 if (!cfg->sinks) {
2326 print_err_oom();
2327 goto error;
2328 }
2329
2330 legacy_input_paths = bt_value_array_create();
2331 if (!legacy_input_paths) {
2332 print_err_oom();
2333 goto error;
2334 }
2335
2336 /* Parse options */
2337 pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0);
2338 if (!pc) {
2339 printf_err("Cannot get popt context\n");
2340 goto error;
2341 }
2342
2343 poptReadDefaultConfig(pc, 0);
2344
2345 while ((opt = poptGetNextOpt(pc)) > 0) {
2346 arg = poptGetOptArg(pc);
2347
2348 switch (opt) {
2349 case OPT_PLUGIN_PATH:
2350 if (cfg->plugin_paths) {
2351 printf_err("Duplicate --plugin-path option\n");
2352 goto error;
2353 }
2354
2355 cfg->plugin_paths = plugin_paths_from_arg(arg);
2356 if (!cfg->plugin_paths) {
2357 printf_err("Invalid --plugin-path option's argument\n");
2358 goto error;
2359 }
2360 break;
2361 case OPT_OUTPUT_PATH:
2362 if (text_legacy_opts.output->len > 0) {
2363 printf_err("Duplicate --output option\n");
2364 goto error;
2365 }
2366
2367 g_string_assign(text_legacy_opts.output, arg);
2368 break;
2369 case OPT_DEBUG_INFO_DIR:
2370 if (text_legacy_opts.dbg_info_dir->len > 0) {
2371 printf_err("Duplicate --debug-info-dir option\n");
2372 goto error;
2373 }
2374
2375 g_string_assign(text_legacy_opts.dbg_info_dir, arg);
2376 break;
2377 case OPT_DEBUG_INFO_TARGET_PREFIX:
2378 if (text_legacy_opts.dbg_info_target_prefix->len > 0) {
2379 printf_err("Duplicate --debug-info-target-prefix option\n");
2380 goto error;
2381 }
2382
2383 g_string_assign(text_legacy_opts.dbg_info_target_prefix, arg);
2384 break;
2385 case OPT_INPUT_FORMAT:
2386 case OPT_SOURCE:
2387 {
c42c79ea
PP
2388 if (opt == OPT_INPUT_FORMAT) {
2389 if (!strcmp(arg, "ctf")) {
2390 /* Legacy CTF input format */
2391 if (legacy_input_format) {
2392 print_err_dup_legacy_input();
2393 goto error;
2394 }
2395
2396 legacy_input_format =
2397 LEGACY_INPUT_FORMAT_CTF;
2398 break;
2399 } else if (!strcmp(arg, "lttng-live")) {
2400 /* Legacy LTTng-live input format */
2401 if (legacy_input_format) {
2402 print_err_dup_legacy_input();
2403 goto error;
2404 }
2405
2406 legacy_input_format =
2407 LEGACY_INPUT_FORMAT_LTTNG_LIVE;
2408 break;
2409 }
2410 }
2411
2412 /* Non-legacy: try to create a component config */
bdc61c70
PP
2413 if (cur_cfg_comp) {
2414 add_cfg_comp(cfg, cur_cfg_comp,
2415 cur_cfg_comp_dest);
2416 }
2417
2418 cur_cfg_comp = bt_config_component_from_arg(arg);
2419 if (!cur_cfg_comp) {
49849a47 2420 printf_err("Invalid format for --source option's argument:\n %s\n",
c42c79ea
PP
2421 arg);
2422 goto error;
2423 }
2424
b07ffa28
PP
2425 assert(cur_base_params);
2426 bt_put(cur_cfg_comp->params);
c9313318
PP
2427 cur_cfg_comp->params = bt_value_copy(cur_base_params);
2428 if (!cur_cfg_comp) {
2429 print_err_oom();
2430 goto end;
2431 }
2432
bdc61c70
PP
2433 cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE;
2434 cur_cfg_comp_params_set = false;
c42c79ea
PP
2435 break;
2436 }
2437 case OPT_OUTPUT_FORMAT:
2438 case OPT_SINK:
2439 {
c42c79ea
PP
2440 if (opt == OPT_OUTPUT_FORMAT) {
2441 if (!strcmp(arg, "text")) {
2442 /* Legacy CTF-text output format */
2443 if (legacy_output_format) {
2444 print_err_dup_legacy_output();
2445 goto error;
2446 }
2447
2448 legacy_output_format =
2449 LEGACY_OUTPUT_FORMAT_TEXT;
2450 break;
2451 } else if (!strcmp(arg, "dummy")) {
2452 /* Legacy dummy output format */
2453 if (legacy_output_format) {
2454 print_err_dup_legacy_output();
2455 goto error;
2456 }
2457
2458 legacy_output_format =
2459 LEGACY_OUTPUT_FORMAT_DUMMY;
2460 break;
2461 } else if (!strcmp(arg, "ctf-metadata")) {
2462 /* Legacy CTF-metadata output format */
2463 if (legacy_output_format) {
2464 print_err_dup_legacy_output();
2465 goto error;
2466 }
2467
2468 legacy_output_format =
2469 LEGACY_OUTPUT_FORMAT_CTF_METADATA;
2470 break;
2471 }
2472 }
2473
2474 /* Non-legacy: try to create a component config */
bdc61c70
PP
2475 if (cur_cfg_comp) {
2476 add_cfg_comp(cfg, cur_cfg_comp,
2477 cur_cfg_comp_dest);
2478 }
2479
2480 cur_cfg_comp = bt_config_component_from_arg(arg);
2481 if (!cur_cfg_comp) {
49849a47 2482 printf_err("Invalid format for --sink option's argument:\n %s\n",
c42c79ea
PP
2483 arg);
2484 goto error;
2485 }
2486
b07ffa28
PP
2487 assert(cur_base_params);
2488 bt_put(cur_cfg_comp->params);
c9313318
PP
2489 cur_cfg_comp->params = bt_value_copy(cur_base_params);
2490 if (!cur_cfg_comp) {
2491 print_err_oom();
2492 goto end;
2493 }
2494
bdc61c70
PP
2495 cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK;
2496 cur_cfg_comp_params_set = false;
2497 break;
2498 }
2499 case OPT_PARAMS:
2500 {
2501 struct bt_value *params;
b07ffa28 2502 struct bt_value *params_to_set;
bdc61c70
PP
2503
2504 if (!cur_cfg_comp) {
2505 printf_err("--params option must follow a --source or --sink option\n");
2506 goto error;
2507 }
2508
2509 if (cur_cfg_comp_params_set) {
49849a47 2510 printf_err("Duplicate --params option for the same current component\ninstance (class %s.%s)\n",
bdc61c70
PP
2511 cur_cfg_comp->plugin_name->str,
2512 cur_cfg_comp->component_name->str);
2513 goto error;
2514 }
2515
2516 params = bt_value_from_arg(arg);
2517 if (!params) {
2518 printf_err("Invalid format for --params option's argument:\n %s\n",
2519 arg);
2520 goto error;
2521 }
2522
b07ffa28
PP
2523 params_to_set = bt_value_map_extend(cur_base_params,
2524 params);
2525 BT_PUT(params);
2526 if (!params_to_set) {
2527 printf_err("Cannot extend current base parameters with --params option's argument:\n %s\n",
2528 arg);
2529 goto error;
2530 }
2531
2532 BT_MOVE(cur_cfg_comp->params, params_to_set);
bdc61c70 2533 cur_cfg_comp_params_set = true;
c42c79ea
PP
2534 break;
2535 }
ad6a19bd
PP
2536 case OPT_PATH:
2537 if (!cur_cfg_comp) {
2538 printf_err("--path option must follow a --source or --sink option\n");
2539 goto error;
2540 }
2541
2542 assert(cur_cfg_comp->params);
2543
2544 if (bt_value_map_insert_string(cur_cfg_comp->params,
2545 "path", arg)) {
2546 print_err_oom();
2547 goto error;
2548 }
2549 break;
b07ffa28
PP
2550 case OPT_BASE_PARAMS:
2551 {
2552 struct bt_value *params = bt_value_from_arg(arg);
2553
2554 if (!params) {
2555 printf_err("Invalid format for --base-params option's argument:\n %s\n",
2556 arg);
2557 goto error;
2558 }
2559
2560 BT_MOVE(cur_base_params, params);
2561 break;
2562 }
2563 case OPT_RESET_BASE_PARAMS:
2564 BT_PUT(cur_base_params);
2565 cur_base_params = bt_value_map_create();
2566 if (!cur_base_params) {
2567 print_err_oom();
2568 goto error;
2569 }
2570 break;
c42c79ea
PP
2571 case OPT_NAMES:
2572 if (text_legacy_opts.names) {
2573 printf_err("Duplicate --names option\n");
2574 goto error;
2575 }
2576
2577 text_legacy_opts.names = names_from_arg(arg);
2578 if (!text_legacy_opts.names) {
2579 printf_err("Invalid --names option's argument\n");
2580 goto error;
2581 }
2582 break;
2583 case OPT_FIELDS:
2584 if (text_legacy_opts.fields) {
2585 printf_err("Duplicate --fields option\n");
2586 goto error;
2587 }
2588
2589 text_legacy_opts.fields = fields_from_arg(arg);
2590 if (!text_legacy_opts.fields) {
2591 printf_err("Invalid --fields option's argument\n");
2592 goto error;
2593 }
2594 break;
2595 case OPT_NO_DELTA:
2596 text_legacy_opts.no_delta = true;
2597 break;
2598 case OPT_CLOCK_CYCLES:
2599 text_legacy_opts.clock_cycles = true;
2600 break;
2601 case OPT_CLOCK_SECONDS:
2602 text_legacy_opts.clock_seconds = true;
2603 break;
2604 case OPT_CLOCK_DATE:
2605 text_legacy_opts.clock_date = true;
2606 break;
2607 case OPT_CLOCK_GMT:
2608 text_legacy_opts.clock_gmt = true;
2609 break;
2610 case OPT_DEBUG_INFO_FULL_PATH:
2611 text_legacy_opts.dbg_info_full_path = true;
2612 break;
2613 case OPT_CLOCK_OFFSET:
2614 {
2615 int64_t val;
2616
2617 if (ctf_legacy_opts.offset_s.is_set) {
2618 printf_err("Duplicate --clock-offset option\n");
2619 goto error;
2620 }
2621
2622 if (parse_int64(arg, &val)) {
2623 printf_err("Invalid --clock-offset option's argument\n");
2624 goto error;
2625 }
2626
2627 set_offset_value(&ctf_legacy_opts.offset_s, val);
2628 break;
2629 }
2630 case OPT_CLOCK_OFFSET_NS:
2631 {
2632 int64_t val;
2633
2634 if (ctf_legacy_opts.offset_ns.is_set) {
2635 printf_err("Duplicate --clock-offset-ns option\n");
2636 goto error;
2637 }
2638
2639 if (parse_int64(arg, &val)) {
2640 printf_err("Invalid --clock-offset-ns option's argument\n");
2641 goto error;
2642 }
2643
2644 set_offset_value(&ctf_legacy_opts.offset_ns, val);
2645 break;
2646 }
2647 case OPT_STREAM_INTERSECTION:
2648 ctf_legacy_opts.stream_intersection = true;
2649 break;
2650 case OPT_CLOCK_FORCE_CORRELATE:
2651 cfg->force_correlate = true;
2652 break;
0f9915c6
MD
2653 case OPT_BEGIN:
2654 if (!cur_cfg_comp) {
2655 //TODO
2656 printf_err("Unimplemented: apply to default source\n");
2657 goto error;
2658 }
2659 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2660 printf_err("--begin option must follow a --source option\n");
2661 goto error;
2662 }
2663 if (bt_value_map_insert_string(cur_cfg_comp->params,
2664 "begin", arg)) {
2665 print_err_oom();
2666 goto error;
2667 }
2668 break;
2669 case OPT_END:
2670 if (!cur_cfg_comp) {
2671 //TODO
2672 printf_err("Unimplemented: apply to default source\n");
2673 goto error;
2674 }
2675 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2676 printf_err("--end option must follow a --source option\n");
2677 goto error;
2678 }
2679 if (bt_value_map_insert_string(cur_cfg_comp->params,
2680 "end", arg)) {
2681 print_err_oom();
2682 goto error;
2683 }
2684 break;
2685 case OPT_TIMERANGE:
2686 {
2687 const char *begin, *end;
2688
2689 if (!cur_cfg_comp) {
2690 //TODO
2691 printf_err("Unimplemented: apply to default source\n");
2692 goto error;
2693 }
2694 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2695 printf_err("--timerange option must follow a --source option\n");
2696 goto error;
2697 }
2698 if (split_timerange(arg, &begin, &end)) {
2699 printf_err("Invalid --timerange format, expecting: begin,end or [begin,end] (where [] are actual brackets)\n");
2700 goto error;
2701 }
2702 if (bt_value_map_insert_string(cur_cfg_comp->params,
2703 "begin", begin)) {
2704 print_err_oom();
2705 goto error;
2706 }
2707 if (bt_value_map_insert_string(cur_cfg_comp->params,
2708 "end", end)) {
2709 print_err_oom();
2710 goto error;
2711 }
2712 break;
2713 }
c42c79ea
PP
2714 case OPT_HELP:
2715 BT_PUT(cfg);
2716 print_usage(stdout);
2717 goto end;
2718 case OPT_HELP_LEGACY:
2719 BT_PUT(cfg);
2720 print_legacy_usage(stdout);
2721 goto end;
2722 case OPT_VERSION:
2723 BT_PUT(cfg);
2724 print_version();
2725 goto end;
2726 case OPT_LIST:
2727 cfg->do_list = true;
2728 goto end;
2729 case OPT_VERBOSE:
2730 cfg->verbose = true;
2731 break;
2732 case OPT_DEBUG:
2733 cfg->debug = true;
2734 break;
2735 default:
2736 printf_err("Unknown command-line option specified (option code %d)\n",
2737 opt);
2738 goto error;
2739 }
2740
2741 free(arg);
2742 arg = NULL;
2743 }
2744
bdc61c70
PP
2745 /* Append current component configuration, if any */
2746 if (cur_cfg_comp) {
2747 add_cfg_comp(cfg, cur_cfg_comp, cur_cfg_comp_dest);
2748 cur_cfg_comp = NULL;
2749 }
2750
c42c79ea
PP
2751 /* Check for option parsing error */
2752 if (opt < -1) {
2753 printf_err("While parsing command-line options, at option %s: %s\n",
2754 poptBadOption(pc, 0), poptStrerror(opt));
2755 goto error;
2756 }
2757
2758 /* Consume leftover arguments as legacy input paths */
2759 while (true) {
2760 const char *input_path = poptGetArg(pc);
2761
2762 if (!input_path) {
2763 break;
2764 }
2765
2766 if (bt_value_array_append_string(legacy_input_paths,
2767 input_path)) {
2768 print_err_oom();
2769 goto error;
2770 }
2771 }
2772
2773 /* Validate legacy/non-legacy options */
2774 if (!validate_cfg(cfg, &legacy_input_format, &legacy_output_format,
2775 legacy_input_paths, &ctf_legacy_opts,
2776 &text_legacy_opts)) {
2777 printf_err("Command-line options form an invalid configuration\n");
2778 goto error;
2779 }
2780
2781 /*
2782 * If there's a legacy input format, convert it to source
2783 * component configurations.
2784 */
2785 if (legacy_input_format) {
2786 if (append_sources_from_legacy_opts(cfg->sources,
2787 legacy_input_format, &ctf_legacy_opts,
528debdf 2788 legacy_input_paths)) {
49849a47 2789 printf_err("Cannot convert legacy input format options to source component instance(s)\n");
c42c79ea
PP
2790 goto error;
2791 }
2792 }
2793
2794 /*
2795 * If there's a legacy output format, convert it to sink
2796 * component configurations.
2797 */
2798 if (legacy_output_format) {
2799 if (append_sinks_from_legacy_opts(cfg->sinks,
2800 legacy_output_format, &text_legacy_opts)) {
49849a47 2801 printf_err("Cannot convert legacy output format options to sink component instance(s)\n");
c42c79ea
PP
2802 goto error;
2803 }
2804 }
2805
2806 goto end;
2807
2808error:
2809 BT_PUT(cfg);
2810 cfg = NULL;
2811 *exit_code = 1;
2812
2813end:
2814 if (pc) {
2815 poptFreeContext(pc);
2816 }
2817
2818 if (text_legacy_opts.output) {
2819 g_string_free(text_legacy_opts.output, TRUE);
2820 }
2821
2822 if (text_legacy_opts.dbg_info_dir) {
2823 g_string_free(text_legacy_opts.dbg_info_dir, TRUE);
2824 }
2825
2826 if (text_legacy_opts.dbg_info_target_prefix) {
2827 g_string_free(text_legacy_opts.dbg_info_target_prefix, TRUE);
2828 }
2829
2830 free(arg);
bdc61c70 2831 BT_PUT(cur_cfg_comp);
b07ffa28 2832 BT_PUT(cur_base_params);
c42c79ea
PP
2833 BT_PUT(text_legacy_opts.names);
2834 BT_PUT(text_legacy_opts.fields);
2835 BT_PUT(legacy_input_paths);
2836 return cfg;
2837}
This page took 0.132051 seconds and 4 git commands to generate.