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