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