Commit | Line | Data |
---|---|---|
34ac0e6c MD |
1 | /* |
2 | * babeltrace.c | |
3 | * | |
4 | * Babeltrace Trace Converter | |
5 | * | |
64fa3fec MD |
6 | * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation |
7 | * | |
8 | * Author: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
34ac0e6c MD |
9 | * |
10 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
11 | * of this software and associated documentation files (the "Software"), to deal | |
12 | * in the Software without restriction, including without limitation the rights | |
13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
14 | * copies of the Software, and to permit persons to whom the Software is | |
15 | * furnished to do so, subject to the following conditions: | |
16 | * | |
17 | * The above copyright notice and this permission notice shall be included in | |
18 | * all copies or substantial portions of the Software. | |
19 | */ | |
4c8bfb7e | 20 | |
afb48eae | 21 | #define _XOPEN_SOURCE 700 |
00f7fbf0 | 22 | #include <config.h> |
95d36295 | 23 | #include <babeltrace/babeltrace.h> |
7fb21036 | 24 | #include <babeltrace/format.h> |
95d36295 JD |
25 | #include <babeltrace/context.h> |
26 | #include <babeltrace/ctf/types.h> | |
31bdef5c | 27 | #include <babeltrace/ctf-text/types.h> |
6204d33c | 28 | #include <babeltrace/iterator.h> |
34ac0e6c MD |
29 | #include <popt.h> |
30 | #include <errno.h> | |
31 | #include <stdlib.h> | |
bbefb8dd MD |
32 | #include <ctype.h> |
33 | #include <sys/stat.h> | |
34 | #include <sys/types.h> | |
35 | #include <fcntl.h> | |
afb48eae AA |
36 | #include <ftw.h> |
37 | #include <dirent.h> | |
38 | #include <unistd.h> | |
70accc14 | 39 | #include <inttypes.h> |
4c8bfb7e | 40 | |
a44bc4c9 MD |
41 | #include <babeltrace/ctf-ir/metadata.h> /* for clocks */ |
42 | ||
afb48eae | 43 | #define DEFAULT_FILE_ARRAY_SIZE 1 |
bbefb8dd MD |
44 | static char *opt_input_format; |
45 | static char *opt_output_format; | |
34ac0e6c MD |
46 | |
47 | static const char *opt_input_path; | |
48 | static const char *opt_output_path; | |
49 | ||
afb48eae AA |
50 | static struct trace_collection trace_collection_read; |
51 | static struct format *fmt_read; | |
52 | ||
bbefb8dd MD |
53 | void strlower(char *str) |
54 | { | |
55 | while (*str) { | |
56 | *str = tolower(*str); | |
57 | str++; | |
58 | } | |
59 | } | |
60 | ||
34ac0e6c MD |
61 | enum { |
62 | OPT_NONE = 0, | |
63 | OPT_HELP, | |
7fb21036 | 64 | OPT_LIST, |
34ac0e6c MD |
65 | OPT_VERBOSE, |
66 | OPT_DEBUG, | |
d63ca2cd | 67 | OPT_NAMES, |
359d7456 | 68 | OPT_FIELDS, |
8d8ed9af | 69 | OPT_NO_DELTA, |
11ac6674 MD |
70 | OPT_CLOCK_OFFSET, |
71 | OPT_CLOCK_RAW, | |
72 | OPT_CLOCK_SECONDS, | |
73 | OPT_CLOCK_DATE, | |
74 | OPT_CLOCK_GMT, | |
34ac0e6c MD |
75 | }; |
76 | ||
77 | static struct poptOption long_options[] = { | |
78 | /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ | |
79 | { "input-format", 'i', POPT_ARG_STRING, &opt_input_format, OPT_NONE, NULL, NULL }, | |
80 | { "output-format", 'o', POPT_ARG_STRING, &opt_output_format, OPT_NONE, NULL, NULL }, | |
81 | { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, | |
7fb21036 | 82 | { "list", 'l', POPT_ARG_NONE, NULL, OPT_LIST, NULL, NULL }, |
34ac0e6c MD |
83 | { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL }, |
84 | { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL }, | |
cba1661c | 85 | { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL }, |
359d7456 | 86 | { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL }, |
8d8ed9af | 87 | { "no-delta", 0, POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL }, |
11ac6674 MD |
88 | { "clock-offset", 0, POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL }, |
89 | { "clock-raw", 0, POPT_ARG_NONE, NULL, OPT_CLOCK_RAW, NULL, NULL }, | |
90 | { "clock-seconds", 0, POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL }, | |
91 | { "clock-date", 0, POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL }, | |
92 | { "clock-gmt", 0, POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL }, | |
34ac0e6c MD |
93 | { NULL, 0, 0, NULL, 0, NULL, NULL }, |
94 | }; | |
95 | ||
7fb21036 MD |
96 | static void list_formats(FILE *fp) |
97 | { | |
98 | fprintf(fp, "\n"); | |
99 | bt_fprintf_format_list(fp); | |
100 | } | |
101 | ||
34ac0e6c | 102 | static void usage(FILE *fp) |
4c8bfb7e | 103 | { |
4c15b06b | 104 | fprintf(fp, "BabelTrace Trace Viewer and Converter %s\n\n", VERSION); |
4d5678b9 | 105 | fprintf(fp, "usage : babeltrace [OPTIONS] INPUT <OUTPUT>\n"); |
34ac0e6c | 106 | fprintf(fp, "\n"); |
4d5678b9 MD |
107 | fprintf(fp, " INPUT Input trace path\n"); |
108 | fprintf(fp, " OUTPUT Output trace path (default: stdout)\n"); | |
34ac0e6c | 109 | fprintf(fp, "\n"); |
d2b8ea6b MD |
110 | fprintf(fp, " -i, --input-format FORMAT Input trace format (default: ctf)\n"); |
111 | fprintf(fp, " -o, --output-format FORMAT Output trace format (default: text)\n"); | |
34ac0e6c | 112 | fprintf(fp, "\n"); |
4d5678b9 MD |
113 | fprintf(fp, " -h, --help This help message\n"); |
114 | fprintf(fp, " -l, --list List available formats\n"); | |
115 | fprintf(fp, " -v, --verbose Verbose mode\n"); | |
cba1661c | 116 | fprintf(fp, " (or set BABELTRACE_VERBOSE environment variable)\n"); |
4d5678b9 | 117 | fprintf(fp, " -d, --debug Debug mode\n"); |
cba1661c | 118 | fprintf(fp, " (or set BABELTRACE_DEBUG environment variable)\n"); |
8d8ed9af | 119 | fprintf(fp, " --no-delta Do not print time delta between consecutive events\n"); |
359d7456 | 120 | fprintf(fp, " -n, --names name1<,name2,...> Print field names:\n"); |
82662ad4 MD |
121 | fprintf(fp, " (payload OR args OR arg)\n"); |
122 | fprintf(fp, " all, scope, header, (context OR ctx)\n"); | |
cba1661c | 123 | fprintf(fp, " (payload active by default)\n"); |
359d7456 MD |
124 | fprintf(fp, " -f, --fields name1<,name2,...> Print additional fields:\n"); |
125 | fprintf(fp, " all, trace, trace:domain, trace:procname,\n"); | |
126 | fprintf(fp, " trace:vpid, loglevel.\n"); | |
11ac6674 MD |
127 | fprintf(fp, " --clock-raw Disregard internal clock offset (use raw value)\n"); |
128 | fprintf(fp, " --clock-offset seconds Clock offset in seconds\n"); | |
129 | fprintf(fp, " --clock-seconds Print the timestamps as [sec.ns]\n"); | |
130 | fprintf(fp, " (default is: [hh:mm:ss.ns])\n"); | |
131 | fprintf(fp, " --clock-date Print clock date\n"); | |
132 | fprintf(fp, " --clock-gmt Print clock in GMT time zone (default: local time zone)\n"); | |
7fb21036 | 133 | list_formats(fp); |
34ac0e6c MD |
134 | fprintf(fp, "\n"); |
135 | } | |
136 | ||
cba1661c MD |
137 | static int get_names_args(poptContext *pc) |
138 | { | |
139 | char *str, *strlist, *strctx; | |
140 | ||
141 | opt_payload_field_names = 0; | |
142 | strlist = (char *) poptGetOptArg(*pc); | |
143 | if (!strlist) { | |
144 | return -EINVAL; | |
145 | } | |
146 | str = strtok_r(strlist, ",", &strctx); | |
147 | do { | |
148 | if (!strcmp(str, "all")) | |
149 | opt_all_field_names = 1; | |
150 | else if (!strcmp(str, "scope")) | |
151 | opt_scope_field_names = 1; | |
152 | else if (!strcmp(str, "context") || !strcmp(str, "ctx")) | |
153 | opt_context_field_names = 1; | |
154 | else if (!strcmp(str, "header")) | |
155 | opt_header_field_names = 1; | |
156 | else if (!strcmp(str, "payload") || !strcmp(str, "args") || !strcmp(str, "arg")) | |
157 | opt_payload_field_names = 1; | |
359d7456 MD |
158 | else { |
159 | fprintf(stderr, "[error] unknown field name type %s\n", str); | |
160 | return -EINVAL; | |
161 | } | |
162 | } while ((str = strtok_r(NULL, ",", &strctx))); | |
163 | return 0; | |
164 | } | |
165 | ||
166 | static int get_fields_args(poptContext *pc) | |
167 | { | |
168 | char *str, *strlist, *strctx; | |
169 | ||
359d7456 MD |
170 | strlist = (char *) poptGetOptArg(*pc); |
171 | if (!strlist) { | |
172 | return -EINVAL; | |
173 | } | |
174 | str = strtok_r(strlist, ",", &strctx); | |
175 | do { | |
176 | if (!strcmp(str, "all")) | |
177 | opt_all_fields = 1; | |
82662ad4 | 178 | else if (!strcmp(str, "trace")) |
359d7456 | 179 | opt_trace_field = 1; |
8c250d87 | 180 | else if (!strcmp(str, "trace:domain")) |
359d7456 | 181 | opt_trace_domain_field = 1; |
8c250d87 | 182 | else if (!strcmp(str, "trace:procname")) |
359d7456 | 183 | opt_trace_procname_field = 1; |
8c250d87 | 184 | else if (!strcmp(str, "trace:vpid")) |
359d7456 | 185 | opt_trace_vpid_field = 1; |
d86d62f8 | 186 | else if (!strcmp(str, "loglevel")) |
359d7456 | 187 | opt_loglevel_field = 1; |
cba1661c | 188 | else { |
359d7456 | 189 | fprintf(stderr, "[error] unknown field type %s\n", str); |
cba1661c MD |
190 | return -EINVAL; |
191 | } | |
192 | } while ((str = strtok_r(NULL, ",", &strctx))); | |
193 | return 0; | |
194 | } | |
195 | ||
34ac0e6c MD |
196 | /* |
197 | * Return 0 if caller should continue, < 0 if caller should return | |
198 | * error, > 0 if caller should exit without reporting error. | |
199 | */ | |
bbefb8dd | 200 | static int parse_options(int argc, char **argv) |
34ac0e6c MD |
201 | { |
202 | poptContext pc; | |
203 | int opt, ret = 0; | |
204 | ||
0f980a35 MD |
205 | if (argc == 1) { |
206 | usage(stdout); | |
207 | return 1; /* exit cleanly */ | |
208 | } | |
209 | ||
bbefb8dd | 210 | pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0); |
34ac0e6c MD |
211 | poptReadDefaultConfig(pc, 0); |
212 | ||
cba1661c MD |
213 | /* set default */ |
214 | opt_payload_field_names = 1; | |
215 | ||
34ac0e6c MD |
216 | while ((opt = poptGetNextOpt(pc)) != -1) { |
217 | switch (opt) { | |
218 | case OPT_HELP: | |
7fb21036 | 219 | usage(stdout); |
34ac0e6c MD |
220 | ret = 1; /* exit cleanly */ |
221 | goto end; | |
7fb21036 MD |
222 | case OPT_LIST: |
223 | list_formats(stdout); | |
224 | ret = 1; | |
225 | goto end; | |
34ac0e6c MD |
226 | case OPT_VERBOSE: |
227 | babeltrace_verbose = 1; | |
228 | break; | |
cba1661c MD |
229 | case OPT_NAMES: |
230 | if (get_names_args(&pc)) { | |
231 | ret = -EINVAL; | |
232 | goto end; | |
233 | } | |
234 | break; | |
359d7456 MD |
235 | case OPT_FIELDS: |
236 | if (get_fields_args(&pc)) { | |
237 | ret = -EINVAL; | |
238 | goto end; | |
239 | } | |
240 | break; | |
34ac0e6c MD |
241 | case OPT_DEBUG: |
242 | babeltrace_debug = 1; | |
243 | break; | |
8d8ed9af | 244 | case OPT_NO_DELTA: |
359d7456 | 245 | opt_delta_field = 0; |
8d8ed9af | 246 | break; |
11ac6674 MD |
247 | case OPT_CLOCK_RAW: |
248 | opt_clock_raw = 1; | |
249 | break; | |
250 | case OPT_CLOCK_OFFSET: | |
251 | { | |
252 | char *str, *endptr; | |
253 | ||
254 | str = poptGetOptArg(pc); | |
255 | if (!str) { | |
256 | fprintf(stderr, "[error] Missing --clock-offset argument\n"); | |
257 | ret = -EINVAL; | |
258 | goto end; | |
259 | } | |
260 | errno = 0; | |
261 | opt_clock_offset = strtoull(str, &endptr, 0); | |
262 | if (*endptr != '\0' || str == endptr || errno != 0) { | |
263 | fprintf(stderr, "[error] Incorrect --clock-offset argument: %s\n", str); | |
264 | ret = -EINVAL; | |
265 | goto end; | |
266 | } | |
267 | break; | |
268 | } | |
269 | case OPT_CLOCK_SECONDS: | |
270 | opt_clock_seconds = 1; | |
271 | break; | |
272 | case OPT_CLOCK_DATE: | |
273 | opt_clock_date = 1; | |
274 | break; | |
275 | case OPT_CLOCK_GMT: | |
276 | opt_clock_gmt = 1; | |
277 | break; | |
278 | ||
34ac0e6c MD |
279 | default: |
280 | ret = -EINVAL; | |
281 | goto end; | |
282 | } | |
283 | } | |
284 | ||
285 | opt_input_path = poptGetArg(pc); | |
286 | if (!opt_input_path) { | |
287 | ret = -EINVAL; | |
288 | goto end; | |
289 | } | |
290 | opt_output_path = poptGetArg(pc); | |
cba1661c | 291 | |
34ac0e6c MD |
292 | end: |
293 | if (pc) { | |
294 | poptFreeContext(pc); | |
295 | } | |
296 | return ret; | |
297 | } | |
298 | ||
afb48eae AA |
299 | static void init_trace_collection(struct trace_collection *tc) |
300 | { | |
301 | tc->array = g_ptr_array_sized_new(DEFAULT_FILE_ARRAY_SIZE); | |
a44bc4c9 | 302 | tc->clocks = g_hash_table_new(g_direct_hash, g_direct_equal); |
0716bb06 MD |
303 | tc->single_clock_offset_avg = 0; |
304 | tc->offset_first = 0; | |
305 | tc->delta_offset_first_sum = 0; | |
306 | tc->offset_nr = 0; | |
afb48eae AA |
307 | } |
308 | ||
309 | /* | |
310 | * finalize_trace_collection() closes the opened traces for read | |
311 | * and free the memory allocated for trace collection | |
312 | */ | |
313 | static void finalize_trace_collection(struct trace_collection *tc) | |
314 | { | |
315 | int i; | |
316 | ||
317 | for (i = 0; i < tc->array->len; i++) { | |
318 | struct trace_descriptor *temp = | |
319 | g_ptr_array_index(tc->array, i); | |
320 | fmt_read->close_trace(temp); | |
321 | } | |
322 | g_ptr_array_free(tc->array, TRUE); | |
a44bc4c9 MD |
323 | g_hash_table_destroy(tc->clocks); |
324 | } | |
325 | ||
326 | struct clock_match { | |
327 | GHashTable *clocks; | |
328 | struct ctf_clock *clock_match; | |
fa709ab2 | 329 | struct trace_collection *tc; |
a44bc4c9 MD |
330 | }; |
331 | ||
332 | static void check_clock_match(gpointer key, gpointer value, gpointer user_data) | |
333 | { | |
334 | struct clock_match *match = user_data; | |
335 | struct ctf_clock *clock_a = value, *clock_b; | |
336 | ||
337 | if (clock_a->uuid != 0) { | |
338 | /* | |
339 | * Lookup the the trace clocks into the collection | |
340 | * clocks. | |
341 | */ | |
342 | clock_b = g_hash_table_lookup(match->clocks, | |
343 | (gpointer) (unsigned long) clock_a->uuid); | |
344 | if (clock_b) { | |
345 | match->clock_match = clock_b; | |
346 | return; | |
347 | } | |
348 | } else if (clock_a->absolute) { | |
349 | /* | |
350 | * Absolute time references, such as NTP, are looked up | |
351 | * by clock name. | |
352 | */ | |
353 | clock_b = g_hash_table_lookup(match->clocks, | |
354 | (gpointer) (unsigned long) clock_a->name); | |
355 | if (clock_b) { | |
356 | match->clock_match = clock_b; | |
357 | return; | |
358 | } | |
359 | } | |
360 | } | |
361 | ||
362 | static void clock_add(gpointer key, gpointer value, gpointer user_data) | |
363 | { | |
fa709ab2 MD |
364 | struct clock_match *clock_match = user_data; |
365 | GHashTable *tc_clocks = clock_match->clocks; | |
a44bc4c9 MD |
366 | struct ctf_clock *t_clock = value; |
367 | GQuark v; | |
368 | ||
369 | if (t_clock->absolute) | |
370 | v = t_clock->name; | |
371 | else | |
372 | v = t_clock->uuid; | |
70accc14 MD |
373 | if (v) { |
374 | struct ctf_clock *tc_clock; | |
375 | ||
376 | tc_clock = g_hash_table_lookup(tc_clocks, | |
377 | (gpointer) (unsigned long) v); | |
378 | if (!tc_clock) { | |
fa709ab2 MD |
379 | /* |
380 | * For now, we only support CTF that has one | |
0716bb06 | 381 | * single clock uuid or name (absolute ref). |
fa709ab2 MD |
382 | */ |
383 | if (g_hash_table_size(tc_clocks) > 0) { | |
384 | fprintf(stderr, "[error] Only CTF traces with a single clock description are supported by this babeltrace version.\n"); | |
385 | } | |
0716bb06 MD |
386 | if (!clock_match->tc->offset_nr) { |
387 | clock_match->tc->offset_first = | |
388 | (t_clock->offset_s * 1000000000ULL) + t_clock->offset; | |
389 | clock_match->tc->delta_offset_first_sum = 0; | |
390 | clock_match->tc->offset_nr++; | |
391 | clock_match->tc->single_clock_offset_avg = | |
392 | clock_match->tc->offset_first; | |
fa709ab2 | 393 | } |
70accc14 MD |
394 | g_hash_table_insert(tc_clocks, |
395 | (gpointer) (unsigned long) v, | |
396 | value); | |
397 | } else { | |
398 | int64_t diff_ns; | |
399 | ||
400 | /* | |
401 | * Check that the offsets match. If not, warn | |
402 | * the user that we do an arbitrary choice. | |
403 | */ | |
404 | diff_ns = tc_clock->offset_s; | |
405 | diff_ns -= t_clock->offset_s; | |
406 | diff_ns *= 1000000000ULL; | |
407 | diff_ns += tc_clock->offset; | |
408 | diff_ns -= t_clock->offset; | |
409 | printf_debug("Clock \"%s\" offset between traces has a delta of %" PRIu64 " ns.", | |
410 | g_quark_to_string(tc_clock->name), | |
411 | diff_ns < 0 ? -diff_ns : diff_ns); | |
412 | if (diff_ns > 10000) { | |
0716bb06 | 413 | fprintf(stderr, "[warning] Clock \"%s\" offset differs between traces (delta %" PRIu64 " ns). Using average.\n", |
fa709ab2 MD |
414 | g_quark_to_string(tc_clock->name), |
415 | diff_ns < 0 ? -diff_ns : diff_ns); | |
70accc14 | 416 | } |
0716bb06 MD |
417 | /* Compute average */ |
418 | clock_match->tc->delta_offset_first_sum += | |
419 | (t_clock->offset_s * 1000000000ULL) + t_clock->offset | |
420 | - clock_match->tc->offset_first; | |
421 | clock_match->tc->offset_nr++; | |
422 | clock_match->tc->single_clock_offset_avg = | |
423 | clock_match->tc->offset_first | |
424 | + (clock_match->tc->delta_offset_first_sum / clock_match->tc->offset_nr); | |
70accc14 MD |
425 | } |
426 | } | |
afb48eae AA |
427 | } |
428 | ||
a44bc4c9 MD |
429 | /* |
430 | * Whenever we add a trace to the trace collection, check that we can | |
431 | * correlate this trace with at least one other clock in the trace. | |
432 | */ | |
433 | static int trace_collection_add(struct trace_collection *tc, | |
434 | struct trace_descriptor *td) | |
afb48eae | 435 | { |
a44bc4c9 MD |
436 | struct ctf_trace *trace = container_of(td, struct ctf_trace, parent); |
437 | ||
afb48eae | 438 | g_ptr_array_add(tc->array, td); |
fa709ab2 | 439 | trace->collection = tc; |
a44bc4c9 MD |
440 | |
441 | if (tc->array->len > 1) { | |
442 | struct clock_match clock_match = { | |
443 | .clocks = tc->clocks, | |
444 | .clock_match = NULL, | |
fa709ab2 | 445 | .tc = NULL, |
a44bc4c9 MD |
446 | }; |
447 | ||
448 | /* | |
449 | * With two or more traces, we need correlation info | |
450 | * avalable. | |
451 | */ | |
452 | g_hash_table_foreach(trace->clocks, | |
453 | check_clock_match, | |
454 | &clock_match); | |
455 | if (!clock_match.clock_match) { | |
456 | fprintf(stderr, "[error] No clocks can be correlated and multiple traces are added to the collection.\n"); | |
457 | goto error; | |
458 | } | |
459 | } | |
fa709ab2 MD |
460 | |
461 | { | |
462 | struct clock_match clock_match = { | |
463 | .clocks = tc->clocks, | |
464 | .clock_match = NULL, | |
465 | .tc = tc, | |
466 | }; | |
467 | ||
468 | /* | |
469 | * Add each clock from the trace clocks into the trace | |
470 | * collection clocks. | |
471 | */ | |
472 | g_hash_table_foreach(trace->clocks, | |
473 | clock_add, | |
474 | &clock_match); | |
475 | } | |
a44bc4c9 MD |
476 | return 0; |
477 | error: | |
478 | return -EPERM; | |
afb48eae AA |
479 | } |
480 | ||
95d36295 JD |
481 | int convert_trace(struct trace_descriptor *td_write, |
482 | struct bt_context *ctx) | |
483 | { | |
e8c92a62 | 484 | struct bt_iter *iter; |
95d36295 JD |
485 | struct ctf_stream *stream; |
486 | struct ctf_stream_event *event; | |
487 | struct ctf_text_stream_pos *sout; | |
e8c92a62 | 488 | struct bt_iter_pos begin_pos; |
95d36295 JD |
489 | int ret; |
490 | ||
491 | sout = container_of(td_write, struct ctf_text_stream_pos, | |
492 | trace_descriptor); | |
493 | ||
494 | begin_pos.type = BT_SEEK_BEGIN; | |
e8c92a62 | 495 | iter = bt_iter_create(ctx, &begin_pos, NULL); |
95d36295 JD |
496 | if (!iter) { |
497 | ret = -1; | |
498 | goto error_iter; | |
499 | } | |
e8c92a62 | 500 | while (bt_iter_read_event(iter, &stream, &event) == 0) { |
95d36295 JD |
501 | ret = sout->parent.event_cb(&sout->parent, stream); |
502 | if (ret) { | |
3394d22e | 503 | fprintf(stderr, "[error] Writing event failed.\n"); |
95d36295 JD |
504 | goto end; |
505 | } | |
e8c92a62 | 506 | ret = bt_iter_next(iter); |
95d36295 JD |
507 | if (ret < 0) |
508 | goto end; | |
509 | } | |
510 | ret = 0; | |
511 | ||
512 | end: | |
e8c92a62 | 513 | bt_iter_destroy(iter); |
95d36295 JD |
514 | error_iter: |
515 | return ret; | |
516 | } | |
517 | ||
518 | ||
afb48eae AA |
519 | /* |
520 | * traverse_dir() is the callback functiion for File Tree Walk (nftw). | |
521 | * it receives the path of the current entry (file, dir, link..etc) with | |
522 | * a flag to indicate the type of the entry. | |
523 | * if the entry being visited is a directory and contains a metadata file, | |
524 | * then open it for reading and save a trace_descriptor to that directory | |
525 | * in the read trace collection. | |
526 | */ | |
527 | static int traverse_dir(const char *fpath, const struct stat *sb, | |
528 | int tflag, struct FTW *ftwbuf) | |
529 | { | |
530 | int dirfd; | |
531 | int fd; | |
532 | struct trace_descriptor *td_read; | |
a44bc4c9 | 533 | int ret; |
afb48eae AA |
534 | |
535 | if (tflag != FTW_D) | |
536 | return 0; | |
537 | dirfd = open(fpath, 0); | |
538 | if (dirfd < 0) { | |
3394d22e | 539 | fprintf(stderr, "[error] unable to open trace " |
afb48eae AA |
540 | "directory file descriptor.\n"); |
541 | return -1; | |
542 | } | |
543 | fd = openat(dirfd, "metadata", O_RDONLY); | |
544 | if (fd < 0) { | |
545 | close(dirfd); | |
546 | } else { | |
547 | close(fd); | |
548 | close(dirfd); | |
5b80ddfb MD |
549 | td_read = fmt_read->open_trace(fpath, O_RDONLY, |
550 | ctf_move_pos_slow, NULL); | |
afb48eae | 551 | if (!td_read) { |
3394d22e | 552 | fprintf(stderr, "Error opening trace \"%s\" " |
afb48eae AA |
553 | "for reading.\n\n", fpath); |
554 | return -1; /* error */ | |
555 | } | |
a44bc4c9 MD |
556 | ret = trace_collection_add(&trace_collection_read, td_read); |
557 | if (ret) { | |
558 | return -1; | |
559 | } | |
afb48eae AA |
560 | } |
561 | return 0; /* success */ | |
562 | } | |
563 | ||
bbefb8dd | 564 | int main(int argc, char **argv) |
34ac0e6c MD |
565 | { |
566 | int ret; | |
afb48eae AA |
567 | struct format *fmt_write; |
568 | struct trace_descriptor *td_write; | |
95d36295 | 569 | struct bt_context *ctx; |
34ac0e6c MD |
570 | |
571 | ret = parse_options(argc, argv); | |
572 | if (ret < 0) { | |
3394d22e MD |
573 | fprintf(stderr, "Error parsing options.\n\n"); |
574 | usage(stderr); | |
34ac0e6c MD |
575 | exit(EXIT_FAILURE); |
576 | } else if (ret > 0) { | |
577 | exit(EXIT_SUCCESS); | |
578 | } | |
579 | printf_verbose("Verbose mode active.\n"); | |
580 | printf_debug("Debug mode active.\n"); | |
581 | ||
bbefb8dd MD |
582 | if (opt_input_format) |
583 | strlower(opt_input_format); | |
584 | if (opt_output_format) | |
585 | strlower(opt_output_format); | |
586 | ||
afb48eae | 587 | printf_verbose("Converting from directory: %s\n", opt_input_path); |
34ac0e6c | 588 | printf_verbose("Converting from format: %s\n", |
b61922b5 | 589 | opt_input_format ? : "ctf <default>"); |
afb48eae | 590 | printf_verbose("Converting to directory: %s\n", |
478b6389 | 591 | opt_output_path ? : "<stdout>"); |
34ac0e6c | 592 | printf_verbose("Converting to format: %s\n", |
b61922b5 | 593 | opt_output_format ? : "text <default>"); |
bbefb8dd | 594 | |
b61922b5 MD |
595 | if (!opt_input_format) |
596 | opt_input_format = "ctf"; | |
597 | if (!opt_output_format) | |
598 | opt_output_format = "text"; | |
bbefb8dd MD |
599 | fmt_read = bt_lookup_format(g_quark_from_static_string(opt_input_format)); |
600 | if (!fmt_read) { | |
3394d22e | 601 | fprintf(stderr, "[error] Format \"%s\" is not supported.\n\n", |
bbefb8dd MD |
602 | opt_input_format); |
603 | exit(EXIT_FAILURE); | |
604 | } | |
bbefb8dd MD |
605 | fmt_write = bt_lookup_format(g_quark_from_static_string(opt_output_format)); |
606 | if (!fmt_write) { | |
3394d22e | 607 | fprintf(stderr, "[error] format \"%s\" is not supported.\n\n", |
bbefb8dd MD |
608 | opt_output_format); |
609 | exit(EXIT_FAILURE); | |
610 | } | |
611 | ||
afb48eae AA |
612 | /* |
613 | * pass the input path to nftw() . | |
614 | * specify traverse_dir() as the callback function. | |
615 | * depth = 10 which is the max number of file descriptors | |
616 | * that nftw() can open at a given time. | |
617 | * flags = 0 check nftw documentation for more info . | |
618 | */ | |
619 | init_trace_collection(&trace_collection_read); | |
620 | ret = nftw(opt_input_path, traverse_dir, 10, 0); | |
621 | if (ret != 0) { | |
3394d22e | 622 | fprintf(stderr, "[error] opening trace \"%s\" for reading.\n\n", |
bbefb8dd MD |
623 | opt_input_path); |
624 | goto error_td_read; | |
625 | } | |
afb48eae | 626 | if (trace_collection_read.array->len == 0) { |
3394d22e | 627 | fprintf(stderr, "[warning] no metadata file was found." |
afb48eae AA |
628 | " no output was generated\n"); |
629 | return 0; | |
630 | } | |
95d36295 JD |
631 | ctx = bt_context_create(&trace_collection_read); |
632 | if (!ctx) { | |
3394d22e | 633 | fprintf(stderr, "Error allocating a new context\n"); |
95d36295 JD |
634 | goto error_td_read; |
635 | } | |
5b80ddfb | 636 | td_write = fmt_write->open_trace(opt_output_path, O_RDWR, NULL, NULL); |
bbefb8dd | 637 | if (!td_write) { |
3394d22e | 638 | fprintf(stderr, "Error opening trace \"%s\" for writing.\n\n", |
b61922b5 | 639 | opt_output_path ? : "<none>"); |
bbefb8dd MD |
640 | goto error_td_write; |
641 | } | |
847bf71a | 642 | |
95d36295 | 643 | ret = convert_trace(td_write, ctx); |
46322b33 | 644 | if (ret) { |
3394d22e | 645 | fprintf(stderr, "Error printing trace.\n\n"); |
46322b33 MD |
646 | goto error_copy_trace; |
647 | } | |
847bf71a | 648 | |
bbefb8dd | 649 | fmt_write->close_trace(td_write); |
afb48eae | 650 | finalize_trace_collection(&trace_collection_read); |
95d36295 | 651 | bt_context_destroy(ctx); |
afb48eae AA |
652 | printf_verbose("finished converting. Output written to:\n%s\n", |
653 | opt_output_path ? : "<stdout>"); | |
bbefb8dd | 654 | exit(EXIT_SUCCESS); |
34ac0e6c | 655 | |
bbefb8dd | 656 | /* Error handling */ |
46322b33 | 657 | error_copy_trace: |
bbefb8dd MD |
658 | fmt_write->close_trace(td_write); |
659 | error_td_write: | |
afb48eae | 660 | finalize_trace_collection(&trace_collection_read); |
bbefb8dd MD |
661 | error_td_read: |
662 | exit(EXIT_FAILURE); | |
4c8bfb7e | 663 | } |