* SOFTWARE.
*/
-#define _GNU_SOURCE
-#include <config.h>
#include <babeltrace/babeltrace.h>
#include <babeltrace/format.h>
#include <babeltrace/context.h>
+#include <babeltrace/context-internal.h>
#include <babeltrace/ctf/types.h>
#include <babeltrace/ctf/events.h>
/* TODO: fix object model for format-agnostic callbacks */
#include <babeltrace/ctf/events-internal.h>
#include <babeltrace/ctf/iterator.h>
#include <babeltrace/ctf-text/types.h>
+#include <babeltrace/debuginfo.h>
+
#include <babeltrace/iterator.h>
#include <popt.h>
#include <errno.h>
#define DEFAULT_FILE_ARRAY_SIZE 1
+#define NET_URL_PREFIX "net://"
+#define NET4_URL_PREFIX "net4://"
+#define NET6_URL_PREFIX "net6://"
+
static char *opt_input_format, *opt_output_format;
/*
*/
static GPtrArray *opt_input_paths;
static char *opt_output_path;
+static int opt_stream_intersection;
static struct bt_format *fmt_read;
OPT_FIELDS,
OPT_NO_DELTA,
OPT_CLOCK_OFFSET,
+ OPT_CLOCK_OFFSET_NS,
OPT_CLOCK_CYCLES,
OPT_CLOCK_SECONDS,
OPT_CLOCK_DATE,
OPT_CLOCK_GMT,
OPT_CLOCK_FORCE_CORRELATE,
+ OPT_STREAM_INTERSECTION,
+ OPT_DEBUG_INFO_DIR,
+ OPT_DEBUG_INFO_FULL_PATH,
+ OPT_DEBUG_INFO_TARGET_PREFIX,
};
/*
{ "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-offset-ns", 0, POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET_NS, 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 },
+ { "stream-intersection", 0, POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL },
+#ifdef ENABLE_DEBUGINFO
+ { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_DIR, NULL, NULL },
+ { "debug-info-full-path", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_INFO_FULL_PATH, NULL, NULL },
+ { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL },
+#endif
{ NULL, 0, 0, NULL, 0, NULL, NULL },
};
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-offset-ns ns Clock offset in nanoseconds\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");
+ fprintf(fp, " --stream-intersection Only print events when all streams are active.\n");
+#ifdef ENABLE_DEBUGINFO
+ fprintf(fp, " --debug-info-dir Directory in which to look for debugging information\n");
+ fprintf(fp, " files. (default: /usr/lib/debug/)\n");
+ fprintf(fp, " --debug-info-target-prefix Directory to use as a prefix for executable lookup\n");
+ fprintf(fp, " --debug-info-full-path Show full debug info source and binary paths (if available)\n");
+#endif
list_formats(fp);
fprintf(fp, "\n");
}
opt_payload_field_names = 0;
} else {
fprintf(stderr, "[error] unknown field name type %s\n", str);
- free(strlist);
ret = -EINVAL;
goto end;
}
case OPT_CLOCK_SECONDS:
opt_clock_seconds = 1;
break;
+ case OPT_CLOCK_OFFSET_NS:
+ {
+ char *str;
+ char *endptr;
+
+ str = (char *) poptGetOptArg(pc);
+ if (!str) {
+ fprintf(stderr, "[error] Missing --clock-offset-ns argument\n");
+ ret = -EINVAL;
+ goto end;
+ }
+ errno = 0;
+ opt_clock_offset_ns = strtoull(str, &endptr, 0);
+ if (*endptr != '\0' || str == endptr || errno != 0) {
+ fprintf(stderr, "[error] Incorrect --clock-offset-ns argument: %s\n", str);
+ ret = -EINVAL;
+ free(str);
+ goto end;
+ }
+ free(str);
+ break;
+ }
+
case OPT_CLOCK_DATE:
opt_clock_date = 1;
break;
case OPT_CLOCK_FORCE_CORRELATE:
opt_clock_force_correlate = 1;
break;
-
+ case OPT_STREAM_INTERSECTION:
+ opt_stream_intersection = 1;
+ break;
+ case OPT_DEBUG_INFO_DIR:
+ opt_debug_info_dir = (char *) poptGetOptArg(pc);
+ if (!opt_debug_info_dir) {
+ ret = -EINVAL;
+ goto end;
+ }
+ break;
+ case OPT_DEBUG_INFO_FULL_PATH:
+ opt_debug_info_full_path = 1;
+ break;
+ case OPT_DEBUG_INFO_TARGET_PREFIX:
+ opt_debug_info_target_prefix = (char *) poptGetOptArg(pc);
+ if (!opt_debug_info_target_prefix) {
+ ret = -EINVAL;
+ goto end;
+ }
+ break;
default:
ret = -EINVAL;
goto end;
/* No meta data, just return */
return 0;
} else {
+ int err_close = 0;
+
closeret = close(metafd);
if (closeret < 0) {
perror("close");
- return -1; /* failure */
+ err_close = 1;
}
closeret = close(dirfd);
if (closeret < 0) {
perror("close");
- return -1; /* failure */
+ err_close = 1;
+ }
+ if (err_close) {
+ return -1;
}
/* Add path to the global list */
void (*packet_seek)(struct bt_stream_pos *pos,
size_t offset, int whence))
{
+ int ret = 0, trace_ids = 0;
- GArray *trace_ids;
- int ret = 0;
-
+ if ((strncmp(path, NET4_URL_PREFIX, sizeof(NET4_URL_PREFIX) - 1)) == 0 ||
+ (strncmp(path, NET6_URL_PREFIX, sizeof(NET6_URL_PREFIX) - 1)) == 0 ||
+ (strncmp(path, NET_URL_PREFIX, sizeof(NET_URL_PREFIX) - 1)) == 0) {
+ ret = bt_context_add_trace(ctx,
+ path, format_str, packet_seek, NULL, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "[warning] [Context] cannot open trace \"%s\" "
+ "for reading.\n", path);
+ }
+ return ret;
+ }
/* Should lock traversed_paths mutex here if used in multithread */
traversed_paths = g_ptr_array_new();
- trace_ids = g_array_new(FALSE, TRUE, sizeof(int));
-
ret = nftw(path, traverse_trace_dir, 10, 0);
/* Process the array if ntfw did not return a fatal error */
/* Allow to skip erroneous traces. */
ret = 1; /* partial error */
} else {
- g_array_append_val(trace_ids, trace_id);
+ trace_ids++;
}
g_string_free(trace_path, TRUE);
}
/*
* Return an error if no trace can be opened.
*/
- if (trace_ids->len == 0) {
+ if (trace_ids == 0) {
fprintf(stderr, "[error] Cannot open any trace for reading.\n\n");
ret = -ENOENT; /* failure */
}
- g_array_free(trace_ids, TRUE);
return ret;
}
+static
+int trace_pre_handler(struct bt_trace_descriptor *td_write,
+ struct bt_context *ctx)
+{
+ struct ctf_text_stream_pos *sout;
+ struct trace_collection *tc;
+ int ret, i;
+
+ sout = container_of(td_write, struct ctf_text_stream_pos,
+ trace_descriptor);
+
+ if (!sout->parent.pre_trace_cb)
+ return 0;
+
+ tc = ctx->tc;
+ for (i = 0; i < tc->array->len; i++) {
+ struct bt_trace_descriptor *td =
+ g_ptr_array_index(tc->array, i);
+
+ ret = sout->parent.pre_trace_cb(&sout->parent, td);
+ if (ret) {
+ fprintf(stderr, "[error] Writing to trace pre handler failed.\n");
+ goto end;
+ }
+ }
+ ret = 0;
+end:
+ return ret;
+}
+
+static
+int trace_post_handler(struct bt_trace_descriptor *td_write,
+ struct bt_context *ctx)
+{
+ struct ctf_text_stream_pos *sout;
+ struct trace_collection *tc;
+ int ret, i;
+
+ sout = container_of(td_write, struct ctf_text_stream_pos,
+ trace_descriptor);
+
+ if (!sout->parent.post_trace_cb)
+ return 0;
+
+ tc = ctx->tc;
+ for (i = 0; i < tc->array->len; i++) {
+ struct bt_trace_descriptor *td =
+ g_ptr_array_index(tc->array, i);
+
+ ret = sout->parent.post_trace_cb(&sout->parent, td);
+ if (ret) {
+ fprintf(stderr, "[error] Writing to trace post handler failed.\n");
+ goto end;
+ }
+ }
+ ret = 0;
+end:
+ return ret;
+}
+
+static
int convert_trace(struct bt_trace_descriptor *td_write,
struct bt_context *ctx)
{
struct bt_ctf_iter *iter;
struct ctf_text_stream_pos *sout;
- struct bt_iter_pos begin_pos;
+ struct bt_iter_pos *begin_pos = NULL, *end_pos = NULL;
struct bt_ctf_event *ctf_event;
int ret;
sout = container_of(td_write, struct ctf_text_stream_pos,
trace_descriptor);
- begin_pos.type = BT_SEEK_BEGIN;
- iter = bt_ctf_iter_create(ctx, &begin_pos, NULL);
+ if (!sout->parent.event_cb) {
+ return 0;
+ }
+
+ if (opt_stream_intersection) {
+ iter = bt_ctf_iter_create_intersect(ctx, &begin_pos, &end_pos);
+ } else {
+ begin_pos = bt_iter_create_time_pos(NULL, 0);
+ begin_pos->type = BT_SEEK_BEGIN;
+ iter = bt_ctf_iter_create(ctx, begin_pos, NULL);
+ }
if (!iter) {
ret = -1;
goto error_iter;
goto end;
}
ret = bt_iter_next(bt_ctf_get_iter(iter));
- if (ret < 0)
+ if (ret < 0) {
goto end;
+ }
}
ret = 0;
end:
bt_ctf_iter_destroy(iter);
error_iter:
+ bt_iter_free_pos(begin_pos);
+ bt_iter_free_pos(end_pos);
return ret;
}
}
printf_verbose("Converting from format: %s\n",
opt_input_format ? : "ctf <default>");
- printf_verbose("Converting to directory: %s\n",
+ printf_verbose("Converting to target: %s\n",
opt_output_path ? : "<stdout>");
printf_verbose("Converting to format: %s\n",
opt_output_format ? : "text <default>");
if (partial_error)
sleep(PARTIAL_ERROR_SLEEP);
- ret = convert_trace(td_write, ctx);
+ ret = trace_pre_handler(td_write, ctx);
+ if (ret) {
+ fprintf(stderr, "Error in trace pre handle.\n\n");
+ goto error_copy_trace;
+ }
+
+ /* For now, we support only CTF iterators */
+ if (fmt_read->name == g_quark_from_static_string("ctf")) {
+ ret = convert_trace(td_write, ctx);
+ if (ret) {
+ fprintf(stderr, "Error printing trace.\n\n");
+ goto error_copy_trace;
+ }
+ }
+
+ ret = trace_post_handler(td_write, ctx);
if (ret) {
- fprintf(stderr, "Error printing trace.\n\n");
+ fprintf(stderr, "Error in trace post handle.\n\n");
goto error_copy_trace;
}
free(opt_input_format);
free(opt_output_format);
free(opt_output_path);
+ free(opt_debug_info_dir);
+ free(opt_debug_info_target_prefix);
g_ptr_array_free(opt_input_paths, TRUE);
if (partial_error)
exit(EXIT_FAILURE);