X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=blobdiff_plain;f=converter%2Fbabeltrace.c;h=d80b1198fafe2cb58dacca8bc185f75bcf2b0f97;hp=21722d23f19f045d875b5f5eec46a6e1219b44f8;hb=a9049b491173a73f8544a16a95ab4e5375f28466;hpb=4e85cbfd81bdd5d58b9cf85c3c623dbce84d8702 diff --git a/converter/babeltrace.c b/converter/babeltrace.c index 21722d23..d80b1198 100644 --- a/converter/babeltrace.c +++ b/converter/babeltrace.c @@ -16,19 +16,28 @@ * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ -#define _GNU_SOURCE -#include #include #include #include +#include #include #include /* TODO: fix object model for format-agnostic callbacks */ #include #include #include +#include + #include #include #include @@ -47,14 +56,25 @@ #define PARTIAL_ERROR_SLEEP 3 /* 3 seconds */ #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; -/* Pointer into const argv */ -static const char *opt_input_format_arg, *opt_output_format_arg; +/* + * We are not freeing opt_input_paths ipath elements when exiting from + * main() for backward compatibility with libpop 0.13, which does not + * allocate copies for arguments returned by poptGetArg(), and for + * general compatibility with the documented behavior. This is known to + * cause a small memory leak with libpop 0.16. + */ static GPtrArray *opt_input_paths; -static const char *opt_output_path; +static char *opt_output_path; +static int opt_stream_intersection; -static struct format *fmt_read; +static struct bt_format *fmt_read; static void strlower(char *str) @@ -67,6 +87,9 @@ void strlower(char *str) enum { OPT_NONE = 0, + OPT_OUTPUT_PATH, + OPT_INPUT_FORMAT, + OPT_OUTPUT_FORMAT, OPT_HELP, OPT_LIST, OPT_VERBOSE, @@ -75,18 +98,31 @@ enum { 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, }; +/* + * We are _not_ using POPT_ARG_STRING ability to store directly into + * variables, because we want to cast the return to non-const, which is + * not possible without using poptGetOptArg explicitly. This helps us + * controlling memory allocation correctly without making assumptions + * about undocumented behaviors. poptGetOptArg is documented as + * requiring the returned const char * to be freed by the caller. + */ 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 }, + { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT_PATH, NULL, NULL }, + { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL }, + { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, 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 }, @@ -95,11 +131,18 @@ static struct poptOption long_options[] = { { "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 }, }; @@ -133,16 +176,25 @@ static void usage(FILE *fp) 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:domain, trace:procname,\n"); - fprintf(fp, " trace:vpid, loglevel.\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-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"); } @@ -150,6 +202,7 @@ static void usage(FILE *fp) static int get_names_args(poptContext *pc) { char *str, *strlist, *strctx; + int ret = 0; opt_payload_field_names = 0; opt_context_field_names = 0; @@ -177,15 +230,19 @@ static int get_names_args(poptContext *pc) opt_payload_field_names = 0; } else { fprintf(stderr, "[error] unknown field name type %s\n", str); - return -EINVAL; + ret = -EINVAL; + goto end; } } while ((str = strtok_r(NULL, ",", &strctx))); - return 0; +end: + free(strlist); + return ret; } static int get_fields_args(poptContext *pc) { char *str, *strlist, *strctx; + int ret = 0; strlist = (char *) poptGetOptArg(*pc); if (!strlist) { @@ -193,10 +250,13 @@ static int get_fields_args(poptContext *pc) } 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")) @@ -205,12 +265,19 @@ static int get_fields_args(poptContext *pc) 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; + ret = -EINVAL; + goto end; } } while ((str = strtok_r(NULL, ",", &strctx))); - return 0; +end: + free(strlist); + return ret; } /* @@ -237,6 +304,27 @@ static int parse_options(int argc, char **argv) while ((opt = poptGetNextOpt(pc)) != -1) { switch (opt) { + case OPT_OUTPUT_PATH: + opt_output_path = (char *) poptGetOptArg(pc); + if (!opt_output_path) { + ret = -EINVAL; + goto end; + } + break; + case OPT_INPUT_FORMAT: + opt_input_format = (char *) poptGetOptArg(pc); + if (!opt_input_format) { + ret = -EINVAL; + goto end; + } + break; + case OPT_OUTPUT_FORMAT: + opt_output_format = (char *) poptGetOptArg(pc); + if (!opt_output_format) { + ret = -EINVAL; + goto end; + } + break; case OPT_HELP: usage(stdout); ret = 1; /* exit cleanly */ @@ -271,10 +359,10 @@ static int parse_options(int argc, char **argv) break; case OPT_CLOCK_OFFSET: { - const char *str; + char *str; char *endptr; - str = poptGetOptArg(pc); + str = (char *) poptGetOptArg(pc); if (!str) { fprintf(stderr, "[error] Missing --clock-offset argument\n"); ret = -EINVAL; @@ -285,13 +373,38 @@ static int parse_options(int argc, char **argv) if (*endptr != '\0' || str == endptr || errno != 0) { fprintf(stderr, "[error] Incorrect --clock-offset argument: %s\n", str); ret = -EINVAL; + free(str); goto end; } + free(str); break; } 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; @@ -301,7 +414,26 @@ static int parse_options(int argc, char **argv) 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; @@ -360,15 +492,20 @@ static int traverse_trace_dir(const char *fpath, const struct stat *sb, /* 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 */ @@ -381,6 +518,7 @@ static int traverse_trace_dir(const char *fpath, const struct stat *sb, return 0; } + /* * bt_context_add_traces_recursive: Open a trace recursively * @@ -395,23 +533,31 @@ static int traverse_trace_dir(const char *fpath, const struct stat *sb, */ int bt_context_add_traces_recursive(struct bt_context *ctx, const char *path, const char *format_str, - void (*packet_seek)(struct stream_pos *pos, + 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; - int i; - + 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 */ if (ret >= 0) { + int i; + for (i = 0; i < traversed_paths->len; i++) { GString *trace_path = g_ptr_array_index(traversed_paths, i); @@ -427,11 +573,12 @@ int bt_context_add_traces_recursive(struct bt_context *ctx, const char *path, /* 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); } } + g_ptr_array_free(traversed_paths, TRUE); traversed_paths = NULL; @@ -440,28 +587,97 @@ int bt_context_add_traces_recursive(struct bt_context *ctx, const char *path, /* * 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; } -int convert_trace(struct trace_descriptor *td_write, +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; @@ -473,22 +689,25 @@ int convert_trace(struct trace_descriptor *td_write, 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; } int main(int argc, char **argv) { int ret, partial_error = 0, open_success = 0; - struct format *fmt_write; - struct trace_descriptor *td_write; + struct bt_format *fmt_write; + struct bt_trace_descriptor *td_write; struct bt_context *ctx; int i; @@ -507,22 +726,10 @@ int main(int argc, char **argv) printf_verbose("Verbose mode active.\n"); printf_debug("Debug mode active.\n"); - if (opt_input_format_arg) { - opt_input_format = strdup(opt_input_format_arg); - if (!opt_input_format) { - partial_error = 1; - goto end; - } + if (opt_input_format) strlower(opt_input_format); - } - if (opt_output_format_arg) { - opt_output_format = strdup(opt_output_format_arg); - if (!opt_output_format) { - partial_error = 1; - goto end; - } + if (opt_output_format) strlower(opt_output_format); - } printf_verbose("Converting from directory(ies):\n"); for (i = 0; i < opt_input_paths->len; i++) { @@ -531,7 +738,7 @@ int main(int argc, char **argv) } printf_verbose("Converting from format: %s\n", opt_input_format ? : "ctf "); - printf_verbose("Converting to directory: %s\n", + printf_verbose("Converting to target: %s\n", opt_output_path ? : ""); printf_verbose("Converting to format: %s\n", opt_output_format ? : "text "); @@ -605,9 +812,24 @@ int main(int argc, char **argv) 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; } @@ -630,6 +852,9 @@ error_td_read: end: 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);