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