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