+#include <babeltrace/ctf-ir/metadata.h> /* for clocks */
+
+#define PARTIAL_ERROR_SLEEP 3 /* 3 seconds */
+
+#define DEFAULT_FILE_ARRAY_SIZE 1
+static char *opt_input_format, *opt_output_format;
+/* Pointer into const argv */
+static const char *opt_input_format_arg, *opt_output_format_arg;
+
+static GPtrArray *opt_input_paths;
+static const char *opt_output_path;
+
+static struct format *fmt_read;
+
+static
+void strlower(char *str)
+{
+ while (*str) {
+ *str = tolower((int) *str);
+ str++;
+ }
+}
+
+enum {
+ OPT_NONE = 0,
+ OPT_HELP,
+ OPT_LIST,
+ OPT_VERBOSE,
+ OPT_DEBUG,
+ OPT_NAMES,
+ OPT_FIELDS,
+ OPT_NO_DELTA,
+ OPT_CLOCK_OFFSET,
+ OPT_CLOCK_CYCLES,
+ OPT_CLOCK_SECONDS,
+ OPT_CLOCK_DATE,
+ OPT_CLOCK_GMT,
+ OPT_CLOCK_FORCE_CORRELATE,
+};
+
+static struct poptOption long_options[] = {
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+ { "output", 'w', POPT_ARG_STRING, &opt_output_path, OPT_NONE, NULL, NULL },
+ { "input-format", 'i', POPT_ARG_STRING, &opt_input_format_arg, OPT_NONE, NULL, NULL },
+ { "output-format", 'o', POPT_ARG_STRING, &opt_output_format_arg, OPT_NONE, NULL, NULL },
+ { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
+ { "list", 'l', POPT_ARG_NONE, NULL, OPT_LIST, NULL, NULL },
+ { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL },
+ { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL },
+ { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL },
+ { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL },
+ { "no-delta", 0, POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL },
+ { "clock-offset", 0, POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL },
+ { "clock-cycles", 0, POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL },
+ { "clock-seconds", 0, POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL },
+ { "clock-date", 0, POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL },
+ { "clock-gmt", 0, POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL },
+ { "clock-force-correlate", 0, POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL },
+ { NULL, 0, 0, NULL, 0, NULL, NULL },
+};
+
+static void list_formats(FILE *fp)
+{
+ fprintf(fp, "\n");
+ bt_fprintf_format_list(fp);
+}
+
+static void usage(FILE *fp)
+{
+ fprintf(fp, "BabelTrace Trace Viewer and Converter %s\n\n", VERSION);
+ fprintf(fp, "usage : babeltrace [OPTIONS] FILE...\n");
+ fprintf(fp, "\n");
+ fprintf(fp, " FILE Input trace file(s) and/or directory(ies)\n");
+ fprintf(fp, " (space-separated)\n");
+ fprintf(fp, " -w, --output OUTPUT Output trace path (default: stdout)\n");
+ fprintf(fp, "\n");
+ fprintf(fp, " -i, --input-format FORMAT Input trace format (default: ctf)\n");
+ fprintf(fp, " -o, --output-format FORMAT Output trace format (default: text)\n");
+ fprintf(fp, "\n");
+ fprintf(fp, " -h, --help This help message\n");
+ fprintf(fp, " -l, --list List available formats\n");
+ fprintf(fp, " -v, --verbose Verbose mode\n");
+ fprintf(fp, " (or set BABELTRACE_VERBOSE environment variable)\n");
+ fprintf(fp, " -d, --debug Debug mode\n");
+ fprintf(fp, " (or set BABELTRACE_DEBUG environment variable)\n");
+ fprintf(fp, " --no-delta Do not print time delta between consecutive events\n");
+ fprintf(fp, " -n, --names name1<,name2,...> Print field names:\n");
+ fprintf(fp, " (payload OR args OR arg)\n");
+ fprintf(fp, " none, all, scope, header, (context OR ctx)\n");
+ fprintf(fp, " (default: payload,context)\n");
+ fprintf(fp, " -f, --fields name1<,name2,...> Print additional fields:\n");
+ fprintf(fp, " all, trace, trace:hostname, trace:domain,\n");
+ fprintf(fp, " trace:procname, trace:vpid, loglevel, emf, callsite.\n");
+ fprintf(fp, " (default: trace:hostname,trace:procname,trace:vpid)\n");
+ fprintf(fp, " --clock-cycles Timestamp in cycles\n");
+ fprintf(fp, " --clock-offset seconds Clock offset in seconds\n");
+ fprintf(fp, " --clock-seconds Print the timestamps as [sec.ns]\n");
+ fprintf(fp, " (default is: [hh:mm:ss.ns])\n");
+ fprintf(fp, " --clock-date Print clock date\n");
+ fprintf(fp, " --clock-gmt Print clock in GMT time zone (default: local time zone)\n");
+ fprintf(fp, " --clock-force-correlate Assume that clocks are inherently correlated\n");
+ fprintf(fp, " across traces.\n");
+ list_formats(fp);
+ fprintf(fp, "\n");
+}
+
+static int get_names_args(poptContext *pc)
+{
+ char *str, *strlist, *strctx;
+
+ opt_payload_field_names = 0;
+ opt_context_field_names = 0;
+ strlist = (char *) poptGetOptArg(*pc);
+ if (!strlist) {
+ return -EINVAL;
+ }
+ str = strtok_r(strlist, ",", &strctx);
+ do {
+ if (!strcmp(str, "all"))
+ opt_all_field_names = 1;
+ else if (!strcmp(str, "scope"))
+ opt_scope_field_names = 1;
+ else if (!strcmp(str, "context") || !strcmp(str, "ctx"))
+ opt_context_field_names = 1;
+ else if (!strcmp(str, "header"))
+ opt_header_field_names = 1;
+ else if (!strcmp(str, "payload") || !strcmp(str, "args") || !strcmp(str, "arg"))
+ opt_payload_field_names = 1;
+ else if (!strcmp(str, "none")) {
+ opt_all_field_names = 0;
+ opt_scope_field_names = 0;
+ opt_context_field_names = 0;
+ opt_header_field_names = 0;
+ opt_payload_field_names = 0;
+ } else {
+ fprintf(stderr, "[error] unknown field name type %s\n", str);
+ return -EINVAL;
+ }
+ } while ((str = strtok_r(NULL, ",", &strctx)));
+ return 0;
+}
+
+static int get_fields_args(poptContext *pc)
+{
+ char *str, *strlist, *strctx;
+
+ strlist = (char *) poptGetOptArg(*pc);
+ if (!strlist) {
+ return -EINVAL;
+ }
+ str = strtok_r(strlist, ",", &strctx);
+ do {
+ opt_trace_default_fields = 0;
+ if (!strcmp(str, "all"))
+ opt_all_fields = 1;
+ else if (!strcmp(str, "trace"))
+ opt_trace_field = 1;
+ else if (!strcmp(str, "trace:hostname"))
+ opt_trace_hostname_field = 1;
+ else if (!strcmp(str, "trace:domain"))
+ opt_trace_domain_field = 1;
+ else if (!strcmp(str, "trace:procname"))
+ opt_trace_procname_field = 1;
+ else if (!strcmp(str, "trace:vpid"))
+ opt_trace_vpid_field = 1;
+ else if (!strcmp(str, "loglevel"))
+ opt_loglevel_field = 1;
+ else if (!strcmp(str, "emf"))
+ opt_emf_field = 1;
+ else if (!strcmp(str, "callsite"))
+ opt_callsite_field = 1;
+ else {
+ fprintf(stderr, "[error] unknown field type %s\n", str);
+ return -EINVAL;
+ }
+ } while ((str = strtok_r(NULL, ",", &strctx)));
+ return 0;
+}
+
+/*
+ * Return 0 if caller should continue, < 0 if caller should return
+ * error, > 0 if caller should exit without reporting error.
+ */
+static int parse_options(int argc, char **argv)
+{
+ poptContext pc;
+ int opt, ret = 0;
+ const char *ipath;
+
+ if (argc == 1) {
+ usage(stdout);
+ return 1; /* exit cleanly */
+ }
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0);
+ poptReadDefaultConfig(pc, 0);
+
+ /* set default */
+ opt_context_field_names = 1;
+ opt_payload_field_names = 1;
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case OPT_HELP:
+ usage(stdout);
+ ret = 1; /* exit cleanly */
+ goto end;
+ case OPT_LIST:
+ list_formats(stdout);
+ ret = 1;
+ goto end;
+ case OPT_VERBOSE:
+ babeltrace_verbose = 1;
+ break;
+ case OPT_NAMES:
+ if (get_names_args(&pc)) {
+ ret = -EINVAL;
+ goto end;
+ }
+ break;
+ case OPT_FIELDS:
+ if (get_fields_args(&pc)) {
+ ret = -EINVAL;
+ goto end;
+ }
+ break;
+ case OPT_DEBUG:
+ babeltrace_debug = 1;
+ break;
+ case OPT_NO_DELTA:
+ opt_delta_field = 0;
+ break;
+ case OPT_CLOCK_CYCLES:
+ opt_clock_cycles = 1;
+ break;
+ case OPT_CLOCK_OFFSET:
+ {
+ const char *str;
+ char *endptr;
+
+ str = poptGetOptArg(pc);
+ if (!str) {
+ fprintf(stderr, "[error] Missing --clock-offset argument\n");
+ ret = -EINVAL;
+ goto end;
+ }
+ errno = 0;
+ opt_clock_offset = strtoull(str, &endptr, 0);
+ if (*endptr != '\0' || str == endptr || errno != 0) {
+ fprintf(stderr, "[error] Incorrect --clock-offset argument: %s\n", str);
+ ret = -EINVAL;
+ goto end;
+ }
+ break;
+ }
+ case OPT_CLOCK_SECONDS:
+ opt_clock_seconds = 1;
+ break;
+ case OPT_CLOCK_DATE:
+ opt_clock_date = 1;
+ break;
+ case OPT_CLOCK_GMT:
+ opt_clock_gmt = 1;
+ break;
+ case OPT_CLOCK_FORCE_CORRELATE:
+ opt_clock_force_correlate = 1;
+ break;
+
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+ }
+
+ do {
+ ipath = poptGetArg(pc);
+ if (ipath)
+ g_ptr_array_add(opt_input_paths, (gpointer) ipath);
+ } while (ipath);
+ if (opt_input_paths->len == 0) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+end:
+ if (pc) {
+ poptFreeContext(pc);
+ }
+ return ret;
+}
+
+static GPtrArray *traversed_paths = 0;
+
+/*
+ * traverse_trace_dir() is the callback function for File Tree Walk (nftw).
+ * it receives the path of the current entry (file, dir, link..etc) with
+ * a flag to indicate the type of the entry.
+ * if the entry being visited is a directory and contains a metadata file,
+ * then add the path to a global list to be processed later in
+ * add_traces_recursive.
+ */
+static int traverse_trace_dir(const char *fpath, const struct stat *sb,
+ int tflag, struct FTW *ftwbuf)