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