values.c: completely initialize the bt_value_null_instance object
[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,
283 "Duplicate parameter key: \"%s\"\n",
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");
725 fprintf(fp, " --clock-gmt Print timestamps in GMT time zone\n");
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");
730 fprintf(fp, " (default: \"/usr/lib/debug\")\n");
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");
734 fprintf(fp, " (default: \"/usr/lib/debug\")\n");
735 fprintf(fp, " -f, --fields=NAME[,NAME]... Print additional fields:\n");
736 fprintf(fp, " all, trace, trace:hostname, trace:domain,\n");
737 fprintf(fp, " trace:procname, trace:vpid, loglevel, emf,\n");
738 fprintf(fp, " callsite\n");
739 fprintf(fp, " (default: trace:hostname, trace:procname,\n");
740 fprintf(fp, " trace:vpid)\n");
741 fprintf(fp, " -n, --names=NAME[,NAME]... Print field names:\n");
742 fprintf(fp, " payload (or arg or args)\n");
743 fprintf(fp, " none, all, scope, header, context (or ctx)\n");
744 fprintf(fp, " (default: payload, context)\n");
745 fprintf(fp, " --no-delta Do not print time delta between consecutive\n");
746 fprintf(fp, " events\n");
747 fprintf(fp, " -w, --output=PATH Write output to PATH (default: standard output)\n");
748}
749
750/*
751 * Prints the Babeltrace 2.x usage.
752 */
753static
754void print_usage(FILE *fp)
755{
756 fprintf(fp, "Usage: babeltrace [OPTIONS]\n");
757 fprintf(fp, "\n");
c42c79ea 758 fprintf(fp, " -d, --debug Enable debug mode\n");
bdc61c70
PP
759 fprintf(fp, " -i, --source=PLUGIN.COMPCLS Instantiate a source component from plugin\n");
760 fprintf(fp, " PLUGIN and component class COMPCLS (may be\n");
761 fprintf(fp, " repeated)\n");
c42c79ea 762 fprintf(fp, " -l, --list List available plugins and their components\n");
bdc61c70
PP
763 fprintf(fp, " -o, --sink=PLUGIN.COMPCLS Instantiate a sink component from plugin\n");
764 fprintf(fp, " PLUGIN and component class COMPCLS (may be\n");
765 fprintf(fp, " repeated)\n");
766 fprintf(fp, " -P, --plugin-path=PATH[:PATH]... Set paths from which dynamic plugins can be\n");
c42c79ea 767 fprintf(fp, " loaded to PATH\n");
bdc61c70
PP
768 fprintf(fp, " -p, --params=PARAMS Set the parameters of the latest source or\n");
769 fprintf(fp, " sink instance (in command-line order) to\n");
770 fprintf(fp, " PARAMS (see the exact format below)\n");
771 fprintf(fp, " -h --help Show this help\n");
772 fprintf(fp, " --help-legacy Show Babeltrace 1.x legacy options\n");
c42c79ea
PP
773 fprintf(fp, " -v, --verbose Enable verbose output\n");
774 fprintf(fp, " -V, --version Show version\n");
bdc61c70
PP
775 fprintf(fp, "\n\n");
776 fprintf(fp, "Format of the --params option's argument\n");
777 fprintf(fp, "----------------------------------------\n");
c42c79ea 778 fprintf(fp, "\n");
bdc61c70 779 fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n");
c42c79ea 780 fprintf(fp, "\n");
bdc61c70
PP
781 fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n");
782 fprintf(fp, "where PARAM is the parameter name (C identifier plus [:.-] characters), and\n");
783 fprintf(fp, "VALUE can be one of:\n");
c42c79ea 784 fprintf(fp, "\n");
bdc61c70
PP
785 fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n");
786 fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n");
787 fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n");
788 fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n");
789 fprintf(fp, " (`0x` prefix) signed 64-bit integer.\n");
790 fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n");
791 fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n");
792 fprintf(fp, " the null and boolean value symbols above.\n");
793 fprintf(fp, "* Double-quoted string (accepts escape characters).\n");
c42c79ea 794 fprintf(fp, "\n");
bdc61c70 795 fprintf(fp, "Whitespaces are allowed around individual `=` and `,` tokens.\n");
c42c79ea 796 fprintf(fp, "\n");
bdc61c70 797 fprintf(fp, "Example:\n");
c42c79ea 798 fprintf(fp, "\n");
bdc61c70
PP
799 fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n");
800 fprintf(fp, " observe=3.14, simple=beef, needs-quotes=\"some string\",\n");
801 fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\"\n");
c42c79ea 802 fprintf(fp, "\n");
bdc61c70
PP
803 fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run babeltrace\n");
804 fprintf(fp, "from a shell.\n");
c42c79ea
PP
805}
806
807/*
808 * Destroys a component configuration.
809 */
810static
811void bt_config_component_destroy(struct bt_object *obj)
812{
813 struct bt_config_component *bt_config_component =
814 container_of(obj, struct bt_config_component, base);
815
816 if (!obj) {
817 goto end;
818 }
819
820 if (bt_config_component->plugin_name) {
821 g_string_free(bt_config_component->plugin_name, TRUE);
822 }
823
824 if (bt_config_component->component_name) {
825 g_string_free(bt_config_component->component_name, TRUE);
826 }
827
828 BT_PUT(bt_config_component->params);
829 g_free(bt_config_component);
830
831end:
832 return;
833}
834
835/*
bdc61c70
PP
836 * Creates a component configuration using the given plugin name and
837 * component name. plugin_name and component_name are copied (belong to
838 * the return value).
c42c79ea
PP
839 *
840 * Return value is owned by the caller.
841 */
842static
843struct bt_config_component *bt_config_component_create(const char *plugin_name,
bdc61c70 844 const char *component_name)
c42c79ea
PP
845{
846 struct bt_config_component *cfg_component = NULL;
847
848 cfg_component = g_new0(struct bt_config_component, 1);
849 if (!cfg_component) {
850 print_err_oom();
851 goto error;
852 }
853
854 bt_object_init(cfg_component, bt_config_component_destroy);
855 cfg_component->plugin_name = g_string_new(plugin_name);
856 if (!cfg_component->plugin_name) {
857 print_err_oom();
858 goto error;
859 }
860
861 cfg_component->component_name = g_string_new(component_name);
862 if (!cfg_component->component_name) {
863 print_err_oom();
864 goto error;
865 }
866
bdc61c70
PP
867 /* Start with empty parameters */
868 cfg_component->params = bt_value_map_create();
869 if (!cfg_component->params) {
870 print_err_oom();
871 goto error;
872 }
873
c42c79ea
PP
874 goto end;
875
876error:
877 BT_PUT(cfg_component);
878
879end:
880 return cfg_component;
881}
882
883/*
884 * Creates a component configuration from a command-line source/sink
bdc61c70 885 * option's argument.
c42c79ea
PP
886 */
887static
888struct bt_config_component *bt_config_component_from_arg(const char *arg)
889{
890 struct bt_config_component *bt_config_component = NULL;
891 char *plugin_name;
892 char *component_name;
c42c79ea
PP
893
894 plugin_component_names_from_arg(arg, &plugin_name, &component_name);
895 if (!plugin_name || !component_name) {
896 printf_err("Cannot get plugin or component name\n");
897 goto error;
898 }
899
c42c79ea 900 bt_config_component = bt_config_component_create(plugin_name,
bdc61c70 901 component_name);
c42c79ea
PP
902 if (!bt_config_component) {
903 goto error;
904 }
905
906 goto end;
907
908error:
909 BT_PUT(bt_config_component);
910
911end:
bdc61c70
PP
912 g_free(plugin_name);
913 g_free(component_name);
c42c79ea
PP
914 return bt_config_component;
915}
916
917/*
918 * Destroys a configuration.
919 */
920static
921void bt_config_destroy(struct bt_object *obj)
922{
923 struct bt_config *bt_config =
924 container_of(obj, struct bt_config, base);
925
926 if (!obj) {
927 goto end;
928 }
929
930 if (bt_config->sources) {
931 g_ptr_array_free(bt_config->sources, TRUE);
932 }
933
934 if (bt_config->sinks) {
935 g_ptr_array_free(bt_config->sinks, TRUE);
936 }
937
938 BT_PUT(bt_config->plugin_paths);
939 g_free(bt_config);
940
941end:
942 return;
943}
944
945/*
946 * Extracts the various paths from the string arg, delimited by ':',
947 * and converts them to an array value object.
948 *
949 * Returned array value object is empty if arg is empty.
950 *
951 * Return value is owned by the caller.
952 */
953static
954struct bt_value *plugin_paths_from_arg(const char *arg)
955{
956 struct bt_value *plugin_paths;
957 const char *at = arg;
958 const char *end = arg + strlen(arg);
959
960 plugin_paths = bt_value_array_create();
961 if (!plugin_paths) {
962 print_err_oom();
963 goto error;
964 }
965
966 while (at < end) {
967 int ret;
968 GString *path;
969 const char *next_colon;
970
971 next_colon = strchr(at, ':');
972 if (next_colon == at) {
973 /*
974 * Empty path: try next character (supported
975 * to conform to the typical parsing of $PATH).
976 */
977 at++;
978 continue;
979 } else if (!next_colon) {
980 /* No more colon: use the remaining */
981 next_colon = arg + strlen(arg);
982 }
983
984 path = g_string_new(NULL);
985 if (!path) {
986 print_err_oom();
987 goto error;
988 }
989
990 g_string_append_len(path, at, next_colon - at);
991 at = next_colon + 1;
992 ret = bt_value_array_append_string(plugin_paths, path->str);
993 g_string_free(path, TRUE);
994 if (ret) {
995 print_err_oom();
996 goto error;
997 }
998 }
999
1000 goto end;
1001
1002error:
1003 BT_PUT(plugin_paths);
1004
1005end:
1006 return plugin_paths;
1007}
1008
1009/*
1010 * Creates a simple lexical scanner for parsing comma-delimited names
1011 * and fields.
1012 *
1013 * Return value is owned by the caller.
1014 */
1015static
1016GScanner *create_csv_identifiers_scanner(void)
1017{
1018 GScannerConfig scanner_config = {
1019 .cset_skip_characters = " \t\n",
1020 .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "_",
1021 .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z ":_-",
1022 .case_sensitive = TRUE,
1023 .cpair_comment_single = NULL,
1024 .skip_comment_multi = TRUE,
1025 .skip_comment_single = TRUE,
1026 .scan_comment_multi = FALSE,
1027 .scan_identifier = TRUE,
1028 .scan_identifier_1char = TRUE,
1029 .scan_identifier_NULL = FALSE,
1030 .scan_symbols = FALSE,
1031 .symbol_2_token = FALSE,
1032 .scope_0_fallback = FALSE,
1033 .scan_binary = FALSE,
1034 .scan_octal = FALSE,
1035 .scan_float = FALSE,
1036 .scan_hex = FALSE,
1037 .scan_hex_dollar = FALSE,
1038 .numbers_2_int = FALSE,
1039 .int_2_float = FALSE,
1040 .store_int64 = FALSE,
1041 .scan_string_sq = FALSE,
1042 .scan_string_dq = FALSE,
1043 .identifier_2_string = FALSE,
1044 .char_2_token = TRUE,
1045 };
1046
1047 return g_scanner_new(&scanner_config);
1048}
1049
6e1bc0df
MD
1050/*
1051 * Inserts a string (if exists and not empty) or null to a map value
1052 * object.
1053 */
1054static
1055enum bt_value_status map_insert_string_or_null(struct bt_value *map,
1056 const char *key, GString *string)
1057{
1058 enum bt_value_status ret;
1059
1060 if (string && string->len > 0) {
1061 ret = bt_value_map_insert_string(map, key, string->str);
1062 } else {
1063 ret = bt_value_map_insert(map, key, bt_value_null);
1064 }
1065 return ret;
1066}
1067
c42c79ea
PP
1068/*
1069 * Converts a comma-delimited list of known names (--names option) to
1070 * an array value object containing those names as string value objects.
1071 *
1072 * Return value is owned by the caller.
1073 */
1074static
1075struct bt_value *names_from_arg(const char *arg)
1076{
1077 GScanner *scanner = NULL;
1078 struct bt_value *names = NULL;
6e1bc0df 1079 bool found_all = false, found_none = false, found_item = false;
c42c79ea
PP
1080
1081 names = bt_value_array_create();
1082 if (!names) {
1083 print_err_oom();
1084 goto error;
1085 }
1086
1087 scanner = create_csv_identifiers_scanner();
1088 if (!scanner) {
1089 print_err_oom();
1090 goto error;
1091 }
1092
1093 g_scanner_input_text(scanner, arg, strlen(arg));
1094
1095 while (true) {
1096 GTokenType token_type = g_scanner_get_next_token(scanner);
1097
1098 switch (token_type) {
1099 case G_TOKEN_IDENTIFIER:
1100 {
1101 const char *identifier = scanner->value.v_identifier;
1102
1103 if (!strcmp(identifier, "payload") ||
1104 !strcmp(identifier, "args") ||
1105 !strcmp(identifier, "arg")) {
6e1bc0df 1106 found_item = true;
c42c79ea
PP
1107 if (bt_value_array_append_string(names,
1108 "payload")) {
1109 goto error;
1110 }
1111 } else if (!strcmp(identifier, "context") ||
1112 !strcmp(identifier, "ctx")) {
6e1bc0df 1113 found_item = true;
c42c79ea
PP
1114 if (bt_value_array_append_string(names,
1115 "context")) {
1116 goto error;
1117 }
1118 } else if (!strcmp(identifier, "scope") ||
1119 !strcmp(identifier, "header")) {
6e1bc0df 1120 found_item = true;
c42c79ea
PP
1121 if (bt_value_array_append_string(names,
1122 identifier)) {
1123 goto error;
1124 }
6e1bc0df
MD
1125 } else if (!strcmp(identifier, "all")) {
1126 found_all = true;
1127 if (bt_value_array_append_string(names,
1128 identifier)) {
c42c79ea
PP
1129 goto error;
1130 }
6e1bc0df
MD
1131 } else if (!strcmp(identifier, "none")) {
1132 found_none = true;
c42c79ea
PP
1133 if (bt_value_array_append_string(names,
1134 identifier)) {
1135 goto error;
1136 }
c42c79ea
PP
1137 } else {
1138 printf_err("Unknown field name: \"%s\"\n",
1139 identifier);
1140 goto error;
1141 }
1142 break;
1143 }
1144 case G_TOKEN_COMMA:
1145 continue;
1146 case G_TOKEN_EOF:
1147 goto end;
1148 default:
1149 goto error;
1150 }
1151 }
1152
6e1bc0df
MD
1153end:
1154 if (found_none && found_all) {
1155 printf_err("Only either \"all\" or \"none\" can be specified in the list given to the \"--names\" option, but not both.\n");
1156 goto error;
1157 }
1158 /*
1159 * Legacy behavior is to clear the defaults (show none) when at
1160 * least one item is specified.
1161 */
1162 if (found_item && !found_none && !found_all) {
1163 if (bt_value_array_append_string(names, "none")) {
1164 goto error;
1165 }
1166 }
1167 if (scanner) {
1168 g_scanner_destroy(scanner);
1169 }
1170 return names;
c42c79ea
PP
1171
1172error:
1173 BT_PUT(names);
c42c79ea
PP
1174 if (scanner) {
1175 g_scanner_destroy(scanner);
1176 }
1177 return names;
1178}
1179
1180
1181/*
1182 * Converts a comma-delimited list of known fields (--fields option) to
1183 * an array value object containing those fields as string
1184 * value objects.
1185 *
1186 * Return value is owned by the caller.
1187 */
1188static
1189struct bt_value *fields_from_arg(const char *arg)
1190{
1191 GScanner *scanner = NULL;
1192 struct bt_value *fields;
1193
1194 fields = bt_value_array_create();
1195 if (!fields) {
1196 print_err_oom();
1197 goto error;
1198 }
1199
1200 scanner = create_csv_identifiers_scanner();
1201 if (!scanner) {
1202 print_err_oom();
1203 goto error;
1204 }
1205
1206 g_scanner_input_text(scanner, arg, strlen(arg));
1207
1208 while (true) {
1209 GTokenType token_type = g_scanner_get_next_token(scanner);
1210
1211 switch (token_type) {
1212 case G_TOKEN_IDENTIFIER:
1213 {
1214 const char *identifier = scanner->value.v_identifier;
1215
1216 if (!strcmp(identifier, "trace") ||
1217 !strcmp(identifier, "trace:hostname") ||
1218 !strcmp(identifier, "trace:domain") ||
1219 !strcmp(identifier, "trace:procname") ||
1220 !strcmp(identifier, "trace:vpid") ||
1221 !strcmp(identifier, "loglevel") ||
1222 !strcmp(identifier, "emf") ||
6e1bc0df
MD
1223 !strcmp(identifier, "callsite") ||
1224 !strcmp(identifier, "all")) {
c42c79ea
PP
1225 if (bt_value_array_append_string(fields,
1226 identifier)) {
1227 goto error;
1228 }
c42c79ea
PP
1229 } else {
1230 printf_err("Unknown field name: \"%s\"\n",
1231 identifier);
1232 goto error;
1233 }
1234 break;
1235 }
1236 case G_TOKEN_COMMA:
1237 continue;
1238 case G_TOKEN_EOF:
1239 goto end;
1240 default:
1241 goto error;
1242 }
1243 }
1244
1245 goto end;
1246
1247error:
1248 BT_PUT(fields);
1249
1250end:
1251 if (scanner) {
1252 g_scanner_destroy(scanner);
1253 }
1254 return fields;
1255}
1256
1257/*
1258 * Inserts the equivalent "prefix-name" true boolean value objects into
1259 * map_obj where the names are in array_obj.
1260 */
1261static
1262int insert_flat_names_fields_from_array(struct bt_value *map_obj,
1263 struct bt_value *array_obj, const char *prefix)
1264{
1265 int ret = 0;
1266 int i;
6e1bc0df 1267 GString *tmpstr = NULL, *default_value = NULL;
c42c79ea
PP
1268
1269 /*
1270 * array_obj may be NULL if no CLI options were specified to
1271 * trigger its creation.
1272 */
1273 if (!array_obj) {
1274 goto end;
1275 }
1276
1277 tmpstr = g_string_new(NULL);
1278 if (!tmpstr) {
1279 print_err_oom();
1280 ret = -1;
1281 goto end;
1282 }
1283
6e1bc0df
MD
1284 default_value = g_string_new(NULL);
1285 if (!default_value) {
1286 print_err_oom();
1287 ret = -1;
1288 goto end;
1289 }
1290
c42c79ea
PP
1291 for (i = 0; i < bt_value_array_size(array_obj); i++) {
1292 struct bt_value *str_obj = bt_value_array_get(array_obj, i);
1293 const char *suffix;
6e1bc0df 1294 bool is_default = false;
c42c79ea
PP
1295
1296 if (!str_obj) {
1297 printf_err("Unexpected error\n");
1298 ret = -1;
1299 goto end;
1300 }
1301
1302 ret = bt_value_string_get(str_obj, &suffix);
1303 BT_PUT(str_obj);
1304 if (ret) {
1305 printf_err("Unexpected error\n");
1306 goto end;
1307 }
1308
1309 g_string_assign(tmpstr, prefix);
1310 g_string_append(tmpstr, "-");
6e1bc0df
MD
1311
1312 /* Special-case for "all" and "none". */
1313 if (!strcmp(suffix, "all")) {
1314 is_default = true;
1315 g_string_assign(default_value, "show");
1316 } else if (!strcmp(suffix, "none")) {
1317 is_default = true;
1318 g_string_assign(default_value, "hide");
1319 }
1320 if (is_default) {
1321 g_string_append(tmpstr, "default");
1322 ret = map_insert_string_or_null(map_obj,
1323 tmpstr->str,
1324 default_value);
1325 if (ret) {
1326 print_err_oom();
1327 goto end;
1328 }
1329 } else {
1330 g_string_append(tmpstr, suffix);
1331 ret = bt_value_map_insert_bool(map_obj, tmpstr->str,
1332 true);
1333 if (ret) {
1334 print_err_oom();
1335 goto end;
1336 }
c42c79ea
PP
1337 }
1338 }
1339
1340end:
6e1bc0df
MD
1341 if (default_value) {
1342 g_string_free(default_value, TRUE);
1343 }
c42c79ea
PP
1344 if (tmpstr) {
1345 g_string_free(tmpstr, TRUE);
1346 }
1347
1348 return ret;
1349}
1350
c42c79ea
PP
1351/*
1352 * Returns the parameters (map value object) corresponding to the
1353 * legacy text format options.
1354 *
1355 * Return value is owned by the caller.
1356 */
1357static
1358struct bt_value *params_from_text_legacy_opts(
1359 struct text_legacy_opts *text_legacy_opts)
1360{
1361 struct bt_value *params;
1362
1363 params = bt_value_map_create();
1364 if (!params) {
1365 print_err_oom();
1366 goto error;
1367 }
1368
1369 if (map_insert_string_or_null(params, "output-path",
1370 text_legacy_opts->output)) {
1371 print_err_oom();
1372 goto error;
1373 }
1374
1375 if (map_insert_string_or_null(params, "debug-info-dir",
1376 text_legacy_opts->dbg_info_dir)) {
1377 print_err_oom();
1378 goto error;
1379 }
1380
1381 if (map_insert_string_or_null(params, "debug-info-target-prefix",
1382 text_legacy_opts->dbg_info_target_prefix)) {
1383 print_err_oom();
1384 goto error;
1385 }
1386
1387 if (bt_value_map_insert_bool(params, "debug-info-full-path",
1388 text_legacy_opts->dbg_info_full_path)) {
1389 print_err_oom();
1390 goto error;
1391 }
1392
1393 if (bt_value_map_insert_bool(params, "no-delta",
1394 text_legacy_opts->no_delta)) {
1395 print_err_oom();
1396 goto error;
1397 }
1398
1399 if (bt_value_map_insert_bool(params, "clock-cycles",
1400 text_legacy_opts->clock_cycles)) {
1401 print_err_oom();
1402 goto error;
1403 }
1404
1405 if (bt_value_map_insert_bool(params, "clock-seconds",
1406 text_legacy_opts->clock_seconds)) {
1407 print_err_oom();
1408 goto error;
1409 }
1410
1411 if (bt_value_map_insert_bool(params, "clock-date",
1412 text_legacy_opts->clock_date)) {
1413 print_err_oom();
1414 goto error;
1415 }
1416
1417 if (bt_value_map_insert_bool(params, "clock-gmt",
1418 text_legacy_opts->clock_gmt)) {
1419 print_err_oom();
1420 goto error;
1421 }
1422
1423 if (insert_flat_names_fields_from_array(params,
1424 text_legacy_opts->names, "name")) {
1425 goto error;
1426 }
1427
1428 if (insert_flat_names_fields_from_array(params,
1429 text_legacy_opts->fields, "field")) {
1430 goto error;
1431 }
1432
1433 goto end;
1434
1435error:
1436 BT_PUT(params);
1437
1438end:
1439 return params;
1440}
1441
1442static
1443int append_sinks_from_legacy_opts(GPtrArray *sinks,
1444 enum legacy_output_format legacy_output_format,
1445 struct text_legacy_opts *text_legacy_opts)
1446{
1447 int ret = 0;
1448 struct bt_value *params = NULL;
1449 const char *plugin_name;
1450 const char *component_name;
1451 struct bt_config_component *bt_config_component = NULL;
1452
1453 switch (legacy_output_format) {
1454 case LEGACY_OUTPUT_FORMAT_TEXT:
1455 plugin_name = "text";
1456 component_name = "text";
1457 break;
1458 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1459 plugin_name = "ctf";
1460 component_name = "metadata-text";
1461 break;
1462 case LEGACY_OUTPUT_FORMAT_DUMMY:
1463 plugin_name = "dummy";
1464 component_name = "dummy";
1465 break;
1466 default:
1467 assert(false);
1468 break;
1469 }
1470
1471 if (legacy_output_format == LEGACY_OUTPUT_FORMAT_TEXT) {
1472 /* Legacy "text" output format has parameters */
1473 params = params_from_text_legacy_opts(text_legacy_opts);
1474 if (!params) {
1475 goto error;
1476 }
1477 } else {
1478 /*
1479 * Legacy "dummy" and "ctf-metadata" output formats do
1480 * not have parameters.
1481 */
1482 params = bt_value_map_create();
1483 if (!params) {
1484 print_err_oom();
1485 goto error;
1486 }
1487 }
1488
1489 /* Create a component configuration */
1490 bt_config_component = bt_config_component_create(plugin_name,
bdc61c70 1491 component_name);
c42c79ea
PP
1492 if (!bt_config_component) {
1493 goto error;
1494 }
1495
bdc61c70
PP
1496 BT_MOVE(bt_config_component->params, params);
1497
c42c79ea
PP
1498 /* Move created component configuration to the array */
1499 g_ptr_array_add(sinks, bt_config_component);
1500
1501 goto end;
1502
1503error:
1504 ret = -1;
1505
1506end:
1507 BT_PUT(params);
1508
1509 return ret;
1510}
1511
1512/*
1513 * Returns the parameters (map value object) corresponding to the
1514 * given legacy CTF format options.
1515 *
1516 * Return value is owned by the caller.
1517 */
1518static
1519struct bt_value *params_from_ctf_legacy_opts(
1520 struct ctf_legacy_opts *ctf_legacy_opts)
1521{
1522 struct bt_value *params;
1523
1524 params = bt_value_map_create();
1525 if (!params) {
1526 print_err_oom();
1527 goto error;
1528 }
1529
1530 if (bt_value_map_insert_integer(params, "offset-s",
1531 ctf_legacy_opts->offset_s.value)) {
1532 print_err_oom();
1533 goto error;
1534 }
1535
1536 if (bt_value_map_insert_integer(params, "offset-ns",
1537 ctf_legacy_opts->offset_ns.value)) {
1538 print_err_oom();
1539 goto error;
1540 }
1541
1542 if (bt_value_map_insert_bool(params, "stream-intersection",
1543 ctf_legacy_opts->stream_intersection)) {
1544 print_err_oom();
1545 goto error;
1546 }
1547
1548 goto end;
1549
1550error:
1551 BT_PUT(params);
1552
1553end:
1554 return params;
1555}
1556
1557static
1558int append_sources_from_legacy_opts(GPtrArray *sources,
1559 enum legacy_input_format legacy_input_format,
1560 struct ctf_legacy_opts *ctf_legacy_opts,
1561 struct bt_value *legacy_input_paths)
1562{
1563 int ret = 0;
1564 int i;
1565 struct bt_value *base_params;
1566 struct bt_value *params = NULL;
1567 struct bt_value *input_path = NULL;
1568 struct bt_value *input_path_copy = NULL;
1569 const char *input_key;
1570 const char *component_name;
1571
1572 switch (legacy_input_format) {
1573 case LEGACY_INPUT_FORMAT_CTF:
1574 input_key = "path";
1575 component_name = "fs";
1576 break;
1577 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
1578 input_key = "url";
1579 component_name = "lttng-live";
1580 break;
1581 default:
1582 assert(false);
1583 break;
1584 }
1585
1586 base_params = params_from_ctf_legacy_opts(ctf_legacy_opts);
1587 if (!base_params) {
1588 goto error;
1589 }
1590
1591 for (i = 0; i < bt_value_array_size(legacy_input_paths); i++) {
1592 struct bt_config_component *bt_config_component = NULL;
1593
1594 /* Copy base parameters as current parameters */
1595 params = bt_value_copy(base_params);
1596 if (!params) {
1597 goto error;
1598 }
1599
1600 /* Get current input path string value object */
1601 input_path = bt_value_array_get(legacy_input_paths, i);
1602 if (!input_path) {
1603 goto error;
1604 }
1605
1606 /* Copy current input path value object */
1607 input_path_copy = bt_value_copy(input_path);
1608 if (!input_path_copy) {
1609 goto error;
1610 }
1611
1612 /* Insert input path value object into current parameters */
1613 ret = bt_value_map_insert(params, input_key, input_path_copy);
1614 if (ret) {
1615 goto error;
1616 }
1617
1618 /* Create a component configuration */
1619 bt_config_component = bt_config_component_create("ctf",
bdc61c70 1620 component_name);
c42c79ea
PP
1621 if (!bt_config_component) {
1622 goto error;
1623 }
1624
bdc61c70
PP
1625 BT_MOVE(bt_config_component->params, params);
1626
c42c79ea
PP
1627 /* Move created component configuration to the array */
1628 g_ptr_array_add(sources, bt_config_component);
1629
1630 /* Put current stuff */
1631 BT_PUT(input_path);
1632 BT_PUT(input_path_copy);
c42c79ea
PP
1633 }
1634
1635 goto end;
1636
1637error:
1638 ret = -1;
1639
1640end:
1641 BT_PUT(base_params);
1642 BT_PUT(params);
1643 BT_PUT(input_path);
1644 BT_PUT(input_path_copy);
1645 return ret;
1646}
1647
1648/*
1649 * Escapes a string for the shell. The string is escaped knowing that
1650 * it's a parameter string value (double-quoted), and that it will be
1651 * entered between single quotes in the shell.
1652 *
1653 * Return value is owned by the caller.
1654 */
1655static
1656char *str_shell_escape(const char *input)
1657{
1658 char *ret = NULL;
1659 const char *at = input;
1660 GString *str = g_string_new(NULL);
1661
1662 if (!str) {
1663 goto end;
1664 }
1665
1666 while (*at != '\0') {
1667 switch (*at) {
1668 case '\\':
1669 g_string_append(str, "\\\\");
1670 break;
1671 case '"':
1672 g_string_append(str, "\\\"");
1673 break;
1674 case '\'':
1675 g_string_append(str, "'\"'\"'");
1676 break;
1677 case '\n':
1678 g_string_append(str, "\\n");
1679 break;
1680 case '\t':
1681 g_string_append(str, "\\t");
1682 break;
1683 default:
1684 g_string_append_c(str, *at);
1685 break;
1686 }
1687
1688 at++;
1689 }
1690
1691end:
1692 if (str) {
1693 ret = str->str;
1694 g_string_free(str, FALSE);
1695 }
1696
1697 return ret;
1698}
1699
1700static
1701int append_prefixed_flag_params(GString *str, struct bt_value *flags,
1702 const char *prefix)
1703{
1704 int ret = 0;
1705 int i;
1706
1707 if (!flags) {
1708 goto end;
1709 }
1710
1711 for (i = 0; i < bt_value_array_size(flags); i++) {
1712 struct bt_value *value = bt_value_array_get(flags, i);
1713 const char *flag;
1714
1715 if (!value) {
1716 ret = -1;
1717 goto end;
1718 }
1719
1720 if (bt_value_string_get(value, &flag)) {
1721 BT_PUT(value);
1722 ret = -1;
1723 goto end;
1724 }
1725
1726 g_string_append_printf(str, ",%s-%s=true", prefix, flag);
1727 BT_PUT(value);
1728 }
1729
1730end:
1731 return ret;
1732}
1733
1734/*
1735 * Appends a boolean parameter string.
1736 */
1737static
1738void g_string_append_bool_param(GString *str, const char *name, bool value)
1739{
1740 g_string_append_printf(str, ",%s=%s", name, value ? "true" : "false");
1741}
1742
1743/*
1744 * Appends a path parameter string, or null if it's empty.
1745 */
1746static
1747int g_string_append_string_path_param(GString *str, const char *name,
1748 GString *path)
1749{
1750 int ret = 0;
1751
1752 if (path->len > 0) {
1753 char *escaped_path = str_shell_escape(path->str);
1754
1755 if (!escaped_path) {
1756 print_err_oom();
1757 goto error;
1758 }
1759
1760 g_string_append_printf(str, "%s=\"%s\"", name, escaped_path);
1761 free(escaped_path);
1762 } else {
1763 g_string_append_printf(str, "%s=null", name);
1764 }
1765
1766 goto end;
1767
1768error:
1769 ret = -1;
1770
1771end:
1772 return ret;
1773}
1774
1775/*
1776 * Prints the non-legacy sink options equivalent to the specified
1777 * legacy output format options.
1778 */
1779static
1780void print_output_legacy_to_sinks(
1781 enum legacy_output_format legacy_output_format,
1782 struct text_legacy_opts *text_legacy_opts)
1783{
1784 const char *input_format;
1785 GString *str = NULL;
1786
1787 str = g_string_new(" ");
1788 if (!str) {
1789 print_err_oom();
1790 goto end;
1791 }
1792
1793 switch (legacy_output_format) {
1794 case LEGACY_OUTPUT_FORMAT_TEXT:
1795 input_format = "text";
1796 break;
1797 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1798 input_format = "ctf-metadata";
1799 break;
1800 case LEGACY_OUTPUT_FORMAT_DUMMY:
1801 input_format = "dummy";
1802 break;
1803 default:
1804 assert(false);
1805 }
1806
1807 printf_err("Both \"%s\" legacy output format and non-legacy sink(s) specified.\n\n",
1808 input_format);
1809 printf_err("Specify the following non-legacy sink instead of the legacy \"%s\"\noutput format options:\n\n",
1810 input_format);
1811 g_string_append(str, "-o ");
1812
1813 switch (legacy_output_format) {
1814 case LEGACY_OUTPUT_FORMAT_TEXT:
1815 g_string_append(str, "text.text");
1816 break;
1817 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1818 g_string_append(str, "ctf.metadata-text");
1819 break;
1820 case LEGACY_OUTPUT_FORMAT_DUMMY:
1821 g_string_append(str, "dummy.dummy");
1822 break;
1823 default:
1824 assert(false);
1825 }
1826
1827 if (legacy_output_format == LEGACY_OUTPUT_FORMAT_TEXT &&
1828 text_legacy_opts_is_any_set(text_legacy_opts)) {
1829 int ret;
1830
bdc61c70 1831 g_string_append(str, " -p '");
c42c79ea
PP
1832
1833 if (g_string_append_string_path_param(str, "output-path",
1834 text_legacy_opts->output)) {
1835 goto end;
1836 }
1837
1838 g_string_append(str, ",");
1839
1840 if (g_string_append_string_path_param(str, "debug-info-dir",
1841 text_legacy_opts->dbg_info_dir)) {
1842 goto end;
1843 }
1844
1845 g_string_append(str, ",");
1846
1847 if (g_string_append_string_path_param(str,
1848 "debug-info-target-prefix",
1849 text_legacy_opts->dbg_info_target_prefix)) {
1850 goto end;
1851 }
1852
1853 g_string_append_bool_param(str, "no-delta",
1854 text_legacy_opts->no_delta);
1855 g_string_append_bool_param(str, "clock-cycles",
1856 text_legacy_opts->clock_cycles);
1857 g_string_append_bool_param(str, "clock-seconds",
1858 text_legacy_opts->clock_seconds);
1859 g_string_append_bool_param(str, "clock-date",
1860 text_legacy_opts->clock_date);
1861 g_string_append_bool_param(str, "clock-gmt",
1862 text_legacy_opts->clock_gmt);
1863 ret = append_prefixed_flag_params(str, text_legacy_opts->names,
1864 "name");
1865 if (ret) {
1866 goto end;
1867 }
1868
1869 ret = append_prefixed_flag_params(str, text_legacy_opts->fields,
1870 "field");
1871 if (ret) {
1872 goto end;
1873 }
1874
1875 /* Remove last comma and close single quote */
1876 g_string_append(str, "'");
1877 }
1878
1879 printf_err("%s\n\n", str->str);
1880
1881end:
1882 if (str) {
1883 g_string_free(str, TRUE);
1884 }
1885 return;
1886}
1887
1888/*
1889 * Prints the non-legacy source options equivalent to the specified
1890 * legacy input format options.
1891 */
1892static
1893void print_input_legacy_to_sources(enum legacy_input_format legacy_input_format,
1894 struct bt_value *legacy_input_paths,
1895 struct ctf_legacy_opts *ctf_legacy_opts)
1896{
1897 const char *input_format;
1898 GString *str = NULL;
1899 int i;
1900
1901 str = g_string_new(" ");
1902 if (!str) {
1903 print_err_oom();
1904 goto end;
1905 }
1906
1907 switch (legacy_input_format) {
1908 case LEGACY_INPUT_FORMAT_CTF:
1909 input_format = "ctf";
1910 break;
1911 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
1912 input_format = "lttng-live";
1913 break;
1914 default:
1915 assert(false);
1916 }
1917
1918 printf_err("Both \"%s\" legacy input format and non-legacy source(s) specified.\n\n",
1919 input_format);
1920 printf_err("Specify the following non-legacy source(s) instead of the legacy \"%s\"\ninput format options and positional arguments:\n\n",
1921 input_format);
1922
1923 for (i = 0; i < bt_value_array_size(legacy_input_paths); i++) {
1924 struct bt_value *input_value =
1925 bt_value_array_get(legacy_input_paths, i);
1926 const char *input = NULL;
1927 char *escaped_input;
1928 int ret;
1929
1930 assert(input_value);
1931 ret = bt_value_string_get(input_value, &input);
1932 BT_PUT(input_value);
1933 assert(!ret && input);
1934 escaped_input = str_shell_escape(input);
1935 if (!escaped_input) {
1936 print_err_oom();
1937 goto end;
1938 }
1939
1940 g_string_append(str, "-i ctf.");
1941
1942 switch (legacy_input_format) {
1943 case LEGACY_INPUT_FORMAT_CTF:
bdc61c70 1944 g_string_append(str, "fs -p 'path=\"");
c42c79ea
PP
1945 break;
1946 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
bdc61c70 1947 g_string_append(str, "lttng-live -p 'url=\"");
c42c79ea
PP
1948 break;
1949 default:
1950 assert(false);
1951 }
1952
1953 g_string_append(str, escaped_input);
1954 g_string_append(str, "\"");
1955 g_string_append_printf(str, ",offset-s=%" PRId64,
1956 ctf_legacy_opts->offset_s.value);
1957 g_string_append_printf(str, ",offset-ns=%" PRId64,
1958 ctf_legacy_opts->offset_ns.value);
1959 g_string_append_bool_param(str, "stream-intersection",
1960 ctf_legacy_opts->stream_intersection);
1961 g_string_append(str, "' ");
1962 g_free(escaped_input);
1963 }
1964
1965 printf_err("%s\n\n", str->str);
1966
1967end:
1968 if (str) {
1969 g_string_free(str, TRUE);
1970 }
1971 return;
1972}
1973
1974/*
1975 * Validates a given configuration, with optional legacy input and
1976 * output formats options. Prints useful error messages if anything
1977 * is wrong.
1978 *
1979 * Returns true when the configuration is valid.
1980 */
1981static
1982bool validate_cfg(struct bt_config *cfg,
1983 enum legacy_input_format *legacy_input_format,
1984 enum legacy_output_format *legacy_output_format,
1985 struct bt_value *legacy_input_paths,
1986 struct ctf_legacy_opts *ctf_legacy_opts,
1987 struct text_legacy_opts *text_legacy_opts)
1988{
1989 bool legacy_input = false;
1990 bool legacy_output = false;
1991
1992 /* Determine if the input and output should be legacy-style */
1993 if (*legacy_input_format != LEGACY_INPUT_FORMAT_NONE ||
1994 cfg->sources->len == 0 ||
1995 !bt_value_array_is_empty(legacy_input_paths) ||
1996 ctf_legacy_opts_is_any_set(ctf_legacy_opts)) {
1997 legacy_input = true;
1998 }
1999
2000 if (*legacy_output_format != LEGACY_OUTPUT_FORMAT_NONE ||
2001 cfg->sinks->len == 0 ||
2002 text_legacy_opts_is_any_set(text_legacy_opts)) {
2003 legacy_output = true;
2004 }
2005
2006 if (legacy_input) {
2007 /* If no legacy input format was specified, default to CTF */
2008 if (*legacy_input_format == LEGACY_INPUT_FORMAT_NONE) {
2009 *legacy_input_format = LEGACY_INPUT_FORMAT_CTF;
2010 }
2011
2012 /* Make sure at least one input path exists */
2013 if (bt_value_array_is_empty(legacy_input_paths)) {
2014 switch (*legacy_input_format) {
2015 case LEGACY_INPUT_FORMAT_CTF:
2016 printf_err("No input path specified for legacy \"ctf\" input format\n");
2017 break;
2018 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
2019 printf_err("No URL specified for legacy \"lttng-live\" input format\n");
2020 break;
2021 default:
2022 assert(false);
2023 }
2024 goto error;
2025 }
2026
2027 /* Make sure no non-legacy sources are specified */
2028 if (cfg->sources->len != 0) {
2029 print_input_legacy_to_sources(*legacy_input_format,
2030 legacy_input_paths, ctf_legacy_opts);
2031 goto error;
2032 }
2033 }
2034
2035 if (legacy_output) {
2036 /*
2037 * If no legacy output format was specified, default to
2038 * "text".
2039 */
2040 if (*legacy_output_format == LEGACY_OUTPUT_FORMAT_NONE) {
2041 *legacy_output_format = LEGACY_OUTPUT_FORMAT_TEXT;
2042 }
2043
2044 /*
2045 * If any "text" option was specified, the output must be
2046 * legacy "text".
2047 */
2048 if (text_legacy_opts_is_any_set(text_legacy_opts) &&
2049 *legacy_output_format !=
2050 LEGACY_OUTPUT_FORMAT_TEXT) {
2051 printf_err("Options for legacy \"text\" output format specified with a different legacy output format\n");
2052 goto error;
2053 }
2054
2055 /* Make sure no non-legacy sinks are specified */
2056 if (cfg->sinks->len != 0) {
2057 print_output_legacy_to_sinks(*legacy_output_format,
2058 text_legacy_opts);
2059 goto error;
2060 }
2061 }
2062
2063 /*
2064 * If the output is the legacy "ctf-metadata" format, then the
2065 * input should be the legacy "ctf" input format.
2066 */
2067 if (*legacy_output_format == LEGACY_OUTPUT_FORMAT_CTF_METADATA &&
2068 *legacy_input_format != LEGACY_INPUT_FORMAT_CTF) {
2069 printf_err("Legacy \"ctf-metadata\" output format requires using legacy \"ctf\" input format\n");
2070 goto error;
2071 }
2072
2073 return true;
2074
2075error:
2076 return false;
2077}
2078
2079/*
2080 * Parses a 64-bit signed integer.
2081 *
2082 * Returns a negative value if anything goes wrong.
2083 */
2084static
2085int parse_int64(const char *arg, int64_t *val)
2086{
2087 char *endptr;
2088
2089 errno = 0;
2090 *val = strtoll(arg, &endptr, 0);
2091 if (*endptr != '\0' || arg == endptr || errno != 0) {
2092 return -1;
2093 }
2094
2095 return 0;
2096}
2097
2098/* popt options */
2099enum {
2100 OPT_NONE = 0,
2101 OPT_CLOCK_CYCLES,
2102 OPT_CLOCK_DATE,
2103 OPT_CLOCK_FORCE_CORRELATE,
2104 OPT_CLOCK_GMT,
2105 OPT_CLOCK_OFFSET,
2106 OPT_CLOCK_OFFSET_NS,
2107 OPT_CLOCK_SECONDS,
2108 OPT_DEBUG,
2109 OPT_DEBUG_INFO_DIR,
2110 OPT_DEBUG_INFO_FULL_PATH,
2111 OPT_DEBUG_INFO_TARGET_PREFIX,
2112 OPT_FIELDS,
2113 OPT_HELP,
2114 OPT_HELP_LEGACY,
2115 OPT_INPUT_FORMAT,
2116 OPT_LIST,
2117 OPT_NAMES,
2118 OPT_NO_DELTA,
2119 OPT_OUTPUT_FORMAT,
2120 OPT_OUTPUT_PATH,
bdc61c70 2121 OPT_PARAMS,
c42c79ea
PP
2122 OPT_PLUGIN_PATH,
2123 OPT_SINK,
2124 OPT_SOURCE,
2125 OPT_STREAM_INTERSECTION,
2126 OPT_VERBOSE,
2127 OPT_VERSION,
2128};
2129
2130/* popt long option descriptions */
2131static struct poptOption long_options[] = {
2132 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
2133 { "clock-cycles", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL },
2134 { "clock-date", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL },
2135 { "clock-force-correlate", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL },
2136 { "clock-gmt", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL },
2137 { "clock-offset", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL },
2138 { "clock-offset-ns", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET_NS, NULL, NULL },
2139 { "clock-seconds", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL },
2140 { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL },
2141 { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_DIR, NULL, NULL },
2142 { "debug-info-full-path", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_INFO_FULL_PATH, NULL, NULL },
2143 { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL },
2144 { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL },
2145 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
2146 { "help-legacy", '\0', POPT_ARG_NONE, NULL, OPT_HELP_LEGACY, NULL, NULL },
2147 { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL },
2148 { "list", 'l', POPT_ARG_NONE, NULL, OPT_LIST, NULL, NULL },
2149 { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL },
2150 { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL },
2151 { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT_PATH, NULL, NULL },
2152 { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL },
bdc61c70
PP
2153 { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL },
2154 { "plugin-path", 'P', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL },
c42c79ea
PP
2155 { "sink", '\0', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL },
2156 { "source", '\0', POPT_ARG_STRING, NULL, OPT_SOURCE, NULL, NULL },
2157 { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL },
2158 { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL },
2159 { "version", 'V', POPT_ARG_NONE, NULL, OPT_VERSION, NULL, NULL },
2160 { NULL, 0, 0, NULL, 0, NULL, NULL },
2161};
2162
2163/*
2164 * Sets the value of a given legacy offset option and marks it as set.
2165 */
2166static void set_offset_value(struct offset_opt *offset_opt, int64_t value)
2167{
2168 offset_opt->value = value;
2169 offset_opt->is_set = true;
2170}
2171
bdc61c70
PP
2172enum bt_config_component_dest {
2173 BT_CONFIG_COMPONENT_DEST_SOURCE,
2174 BT_CONFIG_COMPONENT_DEST_SINK,
2175};
2176
2177/*
2178 * Adds a configuration component to the appropriate configuration
2179 * array depending on the destination.
2180 */
2181static void add_cfg_comp(struct bt_config *cfg,
2182 struct bt_config_component *cfg_comp,
2183 enum bt_config_component_dest dest)
2184{
2185 if (dest == BT_CONFIG_COMPONENT_DEST_SOURCE) {
2186 g_ptr_array_add(cfg->sources, cfg_comp);
2187 } else {
2188 g_ptr_array_add(cfg->sinks, cfg_comp);
2189 }
2190}
2191
c42c79ea
PP
2192/*
2193 * Returns a Babeltrace configuration, out of command-line arguments,
2194 * containing everything that is needed to instanciate specific
2195 * components with given parameters.
2196 *
2197 * *exit_code is set to the appropriate exit code to use as far as this
2198 * function goes.
2199 *
2200 * Return value is NULL on error, otherwise it's owned by the caller.
2201 */
2202struct bt_config *bt_config_from_args(int argc, char *argv[], int *exit_code)
2203{
2204 struct bt_config *cfg = NULL;
2205 poptContext pc = NULL;
2206 char *arg = NULL;
2207 struct ctf_legacy_opts ctf_legacy_opts = { 0 };
2208 struct text_legacy_opts text_legacy_opts = { 0 };
2209 enum legacy_input_format legacy_input_format = LEGACY_INPUT_FORMAT_NONE;
2210 enum legacy_output_format legacy_output_format =
2211 LEGACY_OUTPUT_FORMAT_NONE;
2212 struct bt_value *legacy_input_paths = NULL;
bdc61c70
PP
2213 struct bt_config_component *cur_cfg_comp = NULL;
2214 enum bt_config_component_dest cur_cfg_comp_dest =
2215 BT_CONFIG_COMPONENT_DEST_SOURCE;
c42c79ea 2216 int opt;
bdc61c70 2217 bool cur_cfg_comp_params_set = false;
c42c79ea
PP
2218
2219 *exit_code = 0;
2220
2221 if (argc <= 1) {
2222 print_usage(stdout);
2223 goto end;
2224 }
2225
2226 text_legacy_opts.output = g_string_new(NULL);
2227 if (!text_legacy_opts.output) {
2228 print_err_oom();
2229 goto error;
2230 }
2231
2232 text_legacy_opts.dbg_info_dir = g_string_new(NULL);
2233 if (!text_legacy_opts.dbg_info_dir) {
2234 print_err_oom();
2235 goto error;
2236 }
2237
2238 text_legacy_opts.dbg_info_target_prefix = g_string_new(NULL);
2239 if (!text_legacy_opts.dbg_info_target_prefix) {
2240 print_err_oom();
2241 goto error;
2242 }
2243
2244 /* Create config */
2245 cfg = g_new0(struct bt_config, 1);
2246 if (!cfg) {
2247 print_err_oom();
2248 goto error;
2249 }
2250
2251 bt_object_init(cfg, bt_config_destroy);
2252 cfg->sources = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put);
2253 if (!cfg->sources) {
2254 print_err_oom();
2255 goto error;
2256 }
2257
2258 cfg->sinks = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put);
2259 if (!cfg->sinks) {
2260 print_err_oom();
2261 goto error;
2262 }
2263
2264 legacy_input_paths = bt_value_array_create();
2265 if (!legacy_input_paths) {
2266 print_err_oom();
2267 goto error;
2268 }
2269
2270 /* Parse options */
2271 pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0);
2272 if (!pc) {
2273 printf_err("Cannot get popt context\n");
2274 goto error;
2275 }
2276
2277 poptReadDefaultConfig(pc, 0);
2278
2279 while ((opt = poptGetNextOpt(pc)) > 0) {
2280 arg = poptGetOptArg(pc);
2281
2282 switch (opt) {
2283 case OPT_PLUGIN_PATH:
2284 if (cfg->plugin_paths) {
2285 printf_err("Duplicate --plugin-path option\n");
2286 goto error;
2287 }
2288
2289 cfg->plugin_paths = plugin_paths_from_arg(arg);
2290 if (!cfg->plugin_paths) {
2291 printf_err("Invalid --plugin-path option's argument\n");
2292 goto error;
2293 }
2294 break;
2295 case OPT_OUTPUT_PATH:
2296 if (text_legacy_opts.output->len > 0) {
2297 printf_err("Duplicate --output option\n");
2298 goto error;
2299 }
2300
2301 g_string_assign(text_legacy_opts.output, arg);
2302 break;
2303 case OPT_DEBUG_INFO_DIR:
2304 if (text_legacy_opts.dbg_info_dir->len > 0) {
2305 printf_err("Duplicate --debug-info-dir option\n");
2306 goto error;
2307 }
2308
2309 g_string_assign(text_legacy_opts.dbg_info_dir, arg);
2310 break;
2311 case OPT_DEBUG_INFO_TARGET_PREFIX:
2312 if (text_legacy_opts.dbg_info_target_prefix->len > 0) {
2313 printf_err("Duplicate --debug-info-target-prefix option\n");
2314 goto error;
2315 }
2316
2317 g_string_assign(text_legacy_opts.dbg_info_target_prefix, arg);
2318 break;
2319 case OPT_INPUT_FORMAT:
2320 case OPT_SOURCE:
2321 {
c42c79ea
PP
2322 if (opt == OPT_INPUT_FORMAT) {
2323 if (!strcmp(arg, "ctf")) {
2324 /* Legacy CTF input format */
2325 if (legacy_input_format) {
2326 print_err_dup_legacy_input();
2327 goto error;
2328 }
2329
2330 legacy_input_format =
2331 LEGACY_INPUT_FORMAT_CTF;
2332 break;
2333 } else if (!strcmp(arg, "lttng-live")) {
2334 /* Legacy LTTng-live input format */
2335 if (legacy_input_format) {
2336 print_err_dup_legacy_input();
2337 goto error;
2338 }
2339
2340 legacy_input_format =
2341 LEGACY_INPUT_FORMAT_LTTNG_LIVE;
2342 break;
2343 }
2344 }
2345
2346 /* Non-legacy: try to create a component config */
bdc61c70
PP
2347 if (cur_cfg_comp) {
2348 add_cfg_comp(cfg, cur_cfg_comp,
2349 cur_cfg_comp_dest);
2350 }
2351
2352 cur_cfg_comp = bt_config_component_from_arg(arg);
2353 if (!cur_cfg_comp) {
c42c79ea
PP
2354 printf_err("Invalid source component format:\n %s\n",
2355 arg);
2356 goto error;
2357 }
2358
bdc61c70
PP
2359 cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE;
2360 cur_cfg_comp_params_set = false;
c42c79ea
PP
2361 break;
2362 }
2363 case OPT_OUTPUT_FORMAT:
2364 case OPT_SINK:
2365 {
c42c79ea
PP
2366 if (opt == OPT_OUTPUT_FORMAT) {
2367 if (!strcmp(arg, "text")) {
2368 /* Legacy CTF-text output format */
2369 if (legacy_output_format) {
2370 print_err_dup_legacy_output();
2371 goto error;
2372 }
2373
2374 legacy_output_format =
2375 LEGACY_OUTPUT_FORMAT_TEXT;
2376 break;
2377 } else if (!strcmp(arg, "dummy")) {
2378 /* Legacy dummy output format */
2379 if (legacy_output_format) {
2380 print_err_dup_legacy_output();
2381 goto error;
2382 }
2383
2384 legacy_output_format =
2385 LEGACY_OUTPUT_FORMAT_DUMMY;
2386 break;
2387 } else if (!strcmp(arg, "ctf-metadata")) {
2388 /* Legacy CTF-metadata output format */
2389 if (legacy_output_format) {
2390 print_err_dup_legacy_output();
2391 goto error;
2392 }
2393
2394 legacy_output_format =
2395 LEGACY_OUTPUT_FORMAT_CTF_METADATA;
2396 break;
2397 }
2398 }
2399
2400 /* Non-legacy: try to create a component config */
bdc61c70
PP
2401 if (cur_cfg_comp) {
2402 add_cfg_comp(cfg, cur_cfg_comp,
2403 cur_cfg_comp_dest);
2404 }
2405
2406 cur_cfg_comp = bt_config_component_from_arg(arg);
2407 if (!cur_cfg_comp) {
c42c79ea
PP
2408 printf_err("Invalid sink component format:\n %s\n",
2409 arg);
2410 goto error;
2411 }
2412
bdc61c70
PP
2413 cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK;
2414 cur_cfg_comp_params_set = false;
2415 break;
2416 }
2417 case OPT_PARAMS:
2418 {
2419 struct bt_value *params;
2420
2421 if (!cur_cfg_comp) {
2422 printf_err("--params option must follow a --source or --sink option\n");
2423 goto error;
2424 }
2425
2426 if (cur_cfg_comp_params_set) {
2427 printf_err("Duplicate --params option for the same current component (%s.%s)\n",
2428 cur_cfg_comp->plugin_name->str,
2429 cur_cfg_comp->component_name->str);
2430 goto error;
2431 }
2432
2433 params = bt_value_from_arg(arg);
2434 if (!params) {
2435 printf_err("Invalid format for --params option's argument:\n %s\n",
2436 arg);
2437 goto error;
2438 }
2439
2440 BT_MOVE(cur_cfg_comp->params, params);
2441 cur_cfg_comp_params_set = true;
c42c79ea
PP
2442 break;
2443 }
2444 case OPT_NAMES:
2445 if (text_legacy_opts.names) {
2446 printf_err("Duplicate --names option\n");
2447 goto error;
2448 }
2449
2450 text_legacy_opts.names = names_from_arg(arg);
2451 if (!text_legacy_opts.names) {
2452 printf_err("Invalid --names option's argument\n");
2453 goto error;
2454 }
2455 break;
2456 case OPT_FIELDS:
2457 if (text_legacy_opts.fields) {
2458 printf_err("Duplicate --fields option\n");
2459 goto error;
2460 }
2461
2462 text_legacy_opts.fields = fields_from_arg(arg);
2463 if (!text_legacy_opts.fields) {
2464 printf_err("Invalid --fields option's argument\n");
2465 goto error;
2466 }
2467 break;
2468 case OPT_NO_DELTA:
2469 text_legacy_opts.no_delta = true;
2470 break;
2471 case OPT_CLOCK_CYCLES:
2472 text_legacy_opts.clock_cycles = true;
2473 break;
2474 case OPT_CLOCK_SECONDS:
2475 text_legacy_opts.clock_seconds = true;
2476 break;
2477 case OPT_CLOCK_DATE:
2478 text_legacy_opts.clock_date = true;
2479 break;
2480 case OPT_CLOCK_GMT:
2481 text_legacy_opts.clock_gmt = true;
2482 break;
2483 case OPT_DEBUG_INFO_FULL_PATH:
2484 text_legacy_opts.dbg_info_full_path = true;
2485 break;
2486 case OPT_CLOCK_OFFSET:
2487 {
2488 int64_t val;
2489
2490 if (ctf_legacy_opts.offset_s.is_set) {
2491 printf_err("Duplicate --clock-offset option\n");
2492 goto error;
2493 }
2494
2495 if (parse_int64(arg, &val)) {
2496 printf_err("Invalid --clock-offset option's argument\n");
2497 goto error;
2498 }
2499
2500 set_offset_value(&ctf_legacy_opts.offset_s, val);
2501 break;
2502 }
2503 case OPT_CLOCK_OFFSET_NS:
2504 {
2505 int64_t val;
2506
2507 if (ctf_legacy_opts.offset_ns.is_set) {
2508 printf_err("Duplicate --clock-offset-ns option\n");
2509 goto error;
2510 }
2511
2512 if (parse_int64(arg, &val)) {
2513 printf_err("Invalid --clock-offset-ns option's argument\n");
2514 goto error;
2515 }
2516
2517 set_offset_value(&ctf_legacy_opts.offset_ns, val);
2518 break;
2519 }
2520 case OPT_STREAM_INTERSECTION:
2521 ctf_legacy_opts.stream_intersection = true;
2522 break;
2523 case OPT_CLOCK_FORCE_CORRELATE:
2524 cfg->force_correlate = true;
2525 break;
2526 case OPT_HELP:
2527 BT_PUT(cfg);
2528 print_usage(stdout);
2529 goto end;
2530 case OPT_HELP_LEGACY:
2531 BT_PUT(cfg);
2532 print_legacy_usage(stdout);
2533 goto end;
2534 case OPT_VERSION:
2535 BT_PUT(cfg);
2536 print_version();
2537 goto end;
2538 case OPT_LIST:
2539 cfg->do_list = true;
2540 goto end;
2541 case OPT_VERBOSE:
2542 cfg->verbose = true;
2543 break;
2544 case OPT_DEBUG:
2545 cfg->debug = true;
2546 break;
2547 default:
2548 printf_err("Unknown command-line option specified (option code %d)\n",
2549 opt);
2550 goto error;
2551 }
2552
2553 free(arg);
2554 arg = NULL;
2555 }
2556
bdc61c70
PP
2557 /* Append current component configuration, if any */
2558 if (cur_cfg_comp) {
2559 add_cfg_comp(cfg, cur_cfg_comp, cur_cfg_comp_dest);
2560 cur_cfg_comp = NULL;
2561 }
2562
c42c79ea
PP
2563 /* Check for option parsing error */
2564 if (opt < -1) {
2565 printf_err("While parsing command-line options, at option %s: %s\n",
2566 poptBadOption(pc, 0), poptStrerror(opt));
2567 goto error;
2568 }
2569
2570 /* Consume leftover arguments as legacy input paths */
2571 while (true) {
2572 const char *input_path = poptGetArg(pc);
2573
2574 if (!input_path) {
2575 break;
2576 }
2577
2578 if (bt_value_array_append_string(legacy_input_paths,
2579 input_path)) {
2580 print_err_oom();
2581 goto error;
2582 }
2583 }
2584
2585 /* Validate legacy/non-legacy options */
2586 if (!validate_cfg(cfg, &legacy_input_format, &legacy_output_format,
2587 legacy_input_paths, &ctf_legacy_opts,
2588 &text_legacy_opts)) {
2589 printf_err("Command-line options form an invalid configuration\n");
2590 goto error;
2591 }
2592
2593 /*
2594 * If there's a legacy input format, convert it to source
2595 * component configurations.
2596 */
2597 if (legacy_input_format) {
2598 if (append_sources_from_legacy_opts(cfg->sources,
2599 legacy_input_format, &ctf_legacy_opts,
2600 legacy_input_paths)) {
2601 printf_err("Cannot convert legacy input format options to source(s)\n");
2602 goto error;
2603 }
2604 }
2605
2606 /*
2607 * If there's a legacy output format, convert it to sink
2608 * component configurations.
2609 */
2610 if (legacy_output_format) {
2611 if (append_sinks_from_legacy_opts(cfg->sinks,
2612 legacy_output_format, &text_legacy_opts)) {
2613 printf_err("Cannot convert legacy output format options to sink(s)\n");
2614 goto error;
2615 }
2616 }
2617
2618 goto end;
2619
2620error:
2621 BT_PUT(cfg);
2622 cfg = NULL;
2623 *exit_code = 1;
2624
2625end:
2626 if (pc) {
2627 poptFreeContext(pc);
2628 }
2629
2630 if (text_legacy_opts.output) {
2631 g_string_free(text_legacy_opts.output, TRUE);
2632 }
2633
2634 if (text_legacy_opts.dbg_info_dir) {
2635 g_string_free(text_legacy_opts.dbg_info_dir, TRUE);
2636 }
2637
2638 if (text_legacy_opts.dbg_info_target_prefix) {
2639 g_string_free(text_legacy_opts.dbg_info_target_prefix, TRUE);
2640 }
2641
2642 free(arg);
bdc61c70 2643 BT_PUT(cur_cfg_comp);
c42c79ea
PP
2644 BT_PUT(text_legacy_opts.names);
2645 BT_PUT(text_legacy_opts.fields);
2646 BT_PUT(legacy_input_paths);
2647 return cfg;
2648}
This page took 0.120086 seconds and 4 git commands to generate.