+2012-08-27 Babeltrace 1.0.0-rc5
+ * Change default printout to add host, process names and vpid
+ * Add support for trace:hostname field
+ * Fix: allow specifying more than one input trace path
+ * Fix: make warnings (partial errors) visible
+ * Fix: --clock-force-correlate to handle trace collections gathered from v
+ * Documentation: update API doc with enum functions
+ * Fix: API: remove unsupported BT_SEEK_END from API
+ * API documentation
+ * Cleanup: shut up gcc uninitialized var warning
+ * Fix: support large files on 32-bit systems
+ * Fix: remove unused fts.h include
+ * Fix: add missing enum support to API
+ * Fix: handle clock offset with frequency different from 1GHz
+ * Cleanup: update ifdef wrapper name
+ * Fix: clarify bt_ctf_get_field_list
+ * Fix trace-collection.h: No such file or directory that build code with l
+ * Fix: check return value of bt_context_create
+ * Fix: ensure mmap_base_offset is zeroed on initialization
+ * Fix: Reswitch to FTW for add_traces_recursive
+ * Fix: don't free unallocated index
+ * Fix: don't close the metadata FD if a FP is passed
+ * Add BT_SEEK_LAST type to bt_iter_pos
+ * Fix: iterator.c BT_SEEK_RESTORE: check return value
+ * Fix: complete error handling of babeltrace API
+ * cleanup: protected -> hidden: cleanup symbol table
+ * Fix: add mmap_base_offset to ctf_stream_pos
+ * Fix: assign the current clock for mmap traces
+ * Fix: libbabeltrace add missing static declaration
+ * Fix: safety checks for opening mmap traces
+ * Remove trace-collection.h from include_headers
+ * Fix: protect visibility of ctf-parser functions
+ * Fix: correct name of bt_ctf_field_get_error in comments and typo in man
+ * Fix: wrong type in bt_ctf_get_uint64/int64
+ * API cleanup name get_timestamp and get_cycles
+ * fix comment struct bt_saved_pos
+ * Fix: Add missing clock-types.h
+ * Get rid of clock-raw and use real clock
+ * Cleanup (messages): Make the wording of the signedness warning clearer
+ * Fix: error path if heap_init fails
+ * Fix: Remove obsolete bt_iter_seek function
+ * Make the signedness warning useful with the field name
+ * Fix: Restore heap for SEEK_BEGIN
+ * Fix: check if handle is valid
+ * Fix: iterator set_pos
+ * Fix: get rid of consumed flag
+ * Fix: add missing heap_copy
+ * Fix: babeltrace assert() triggered by directories within trace
+ * Several fixes for bt_iter_pos related functions
+ * Fix iterator: various fixes
+ * Fix: remove duplicate yydebug var
+ * Fix babeltrace iterator lib: seek at time 0
+
2012-05-30 Babeltrace 1.0.0-rc4
* Add CodingStyle to tarball
* Add coding style document
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
-AC_INIT([babeltrace],[1.0.0-rc4],[mathieu dot desnoyers at efficios dot com])
+AC_INIT([babeltrace],[1.0.0-rc5],[mathieu dot desnoyers at efficios dot com])
AC_CONFIG_AUX_DIR([config])
AC_CANONICAL_TARGET
AC_CANONICAL_HOST
AC_CONFIG_HEADERS([config.h])
+AC_SYS_LARGEFILE
+
# Checks for programs.
AC_PROG_CC
AC_PROG_MAKE_SET
AC_SUBST(PACKAGE_LIBS)
LIBS="$LIBS $GMODULE_LIBS"
-PACKAGE_CFLAGS="$GMODULE_CFLAGS -Wall -Wformat"
+PACKAGE_CFLAGS="$GMODULE_CFLAGS -Wall -Wformat -include config.h"
AC_SUBST(PACKAGE_CFLAGS)
babeltraceincludedir="${includedir}/babeltrace"
#include <fcntl.h>
#include <unistd.h>
#include <inttypes.h>
-#include <fts.h>
+#include <ftw.h>
#include <string.h>
#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 const char *opt_input_path;
+static GPtrArray *opt_input_paths;
static const char *opt_output_path;
static struct format *fmt_read;
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 },
static void usage(FILE *fp)
{
fprintf(fp, "BabelTrace Trace Viewer and Converter %s\n\n", VERSION);
- fprintf(fp, "usage : babeltrace [OPTIONS] INPUT <OUTPUT>\n");
+ fprintf(fp, "usage : babeltrace [OPTIONS] FILE...\n");
fprintf(fp, "\n");
- fprintf(fp, " INPUT Input trace path\n");
- fprintf(fp, " OUTPUT Output trace path (default: stdout)\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, " 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.\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");
}
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"))
{
poptContext pc;
int opt, ret = 0;
+ const char *ipath;
if (argc == 1) {
usage(stdout);
}
}
- opt_input_path = poptGetArg(pc);
- if (!opt_input_path) {
+ 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;
}
- opt_output_path = poptGetArg(pc);
end:
if (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)
+{
+ int dirfd, metafd;
+ int closeret;
+
+ if (tflag != FTW_D)
+ return 0;
+
+ dirfd = open(fpath, 0);
+ if (dirfd < 0) {
+ fprintf(stderr, "[error] [Context] Unable to open trace "
+ "directory file descriptor.\n");
+ return 0; /* partial error */
+ }
+ metafd = openat(dirfd, "metadata", O_RDONLY);
+ if (metafd < 0) {
+ closeret = close(dirfd);
+ if (closeret < 0) {
+ perror("close");
+ return -1;
+ }
+ /* No meta data, just return */
+ return 0;
+ } else {
+ closeret = close(metafd);
+ if (closeret < 0) {
+ perror("close");
+ return -1; /* failure */
+ }
+ closeret = close(dirfd);
+ if (closeret < 0) {
+ perror("close");
+ return -1; /* failure */
+ }
+
+ /* Add path to the global list */
+ if (traversed_paths == NULL) {
+ fprintf(stderr, "[error] [Context] Invalid open path array.\n");
+ return -1;
+ }
+ g_ptr_array_add(traversed_paths, g_string_new(fpath));
+ }
+
+ return 0;
+}
/*
* bt_context_add_traces_recursive: Open a trace recursively
*
void (*packet_seek)(struct stream_pos *pos,
size_t offset, int whence))
{
- FTS *tree;
- FTSENT *node;
+
GArray *trace_ids;
- char lpath[PATH_MAX];
- char * const paths[2] = { lpath, NULL };
int ret = 0;
+ int i;
- /*
- * Need to copy path, because fts_open can change it.
- * It is the pointer array, not the strings, that are constant.
- */
- strncpy(lpath, path, PATH_MAX);
- lpath[PATH_MAX - 1] = '\0';
-
- tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL, 0);
- if (tree == NULL) {
- fprintf(stderr, "[error] [Context] Cannot traverse \"%s\" for reading.\n",
- path);
- return -EINVAL;
- }
+ /* 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));
- while ((node = fts_read(tree))) {
- int dirfd, metafd;
- int closeret;
-
- if (!(node->fts_info & FTS_D))
- continue;
-
- dirfd = open(node->fts_accpath, 0);
- if (dirfd < 0) {
- fprintf(stderr, "[error] [Context] Unable to open trace "
- "directory file descriptor.\n");
- ret = 1; /* partial error */
- goto error;
- }
- metafd = openat(dirfd, "metadata", O_RDONLY);
- if (metafd < 0) {
- closeret = close(dirfd);
- if (closeret < 0) {
- perror("close");
- ret = -1; /* failure */
- goto error;
- }
- continue;
- } else {
- int trace_id;
-
- closeret = close(metafd);
- if (closeret < 0) {
- perror("close");
- ret = -1; /* failure */
- goto error;
- }
- closeret = close(dirfd);
- if (closeret < 0) {
- perror("close");
- ret = -1; /* failure */
- goto error;
- }
-
- trace_id = bt_context_add_trace(ctx,
- node->fts_accpath, format_str,
- packet_seek, NULL, NULL);
+ ret = nftw(path, traverse_trace_dir, 10, 0);
+
+ /* Process the array if ntfw did not return a fatal error */
+ if (ret >= 0) {
+ for (i = 0; i < traversed_paths->len; i++) {
+ GString *trace_path = g_ptr_array_index(traversed_paths,
+ i);
+ int trace_id = bt_context_add_trace(ctx,
+ trace_path->str,
+ format_str,
+ packet_seek,
+ NULL,
+ NULL);
if (trace_id < 0) {
- fprintf(stderr, "[warning] [Context] opening trace \"%s\" from %s "
- "for reading.\n", node->fts_accpath, path);
+ fprintf(stderr, "[warning] [Context] cannot open trace \"%s\" from %s "
+ "for reading.\n", trace_path->str, path);
/* Allow to skip erroneous traces. */
ret = 1; /* partial error */
- continue;
+ } else {
+ g_array_append_val(trace_ids, trace_id);
}
- g_array_append_val(trace_ids, trace_id);
+ g_string_free(trace_path, TRUE);
}
}
+ g_ptr_array_free(traversed_paths, TRUE);
+ traversed_paths = NULL;
+
+ /* Should unlock traversed paths mutex here if used in multithread */
-error:
/*
* Return an error if no trace can be opened.
*/
return ret;
}
-
-
int convert_trace(struct trace_descriptor *td_write,
struct bt_context *ctx)
{
int main(int argc, char **argv)
{
- int ret, partial_error = 0;
+ int ret, partial_error = 0, open_success = 0;
struct format *fmt_write;
struct trace_descriptor *td_write;
struct bt_context *ctx;
+ int i;
+
+ opt_input_paths = g_ptr_array_new();
ret = parse_options(argc, argv);
if (ret < 0) {
fprintf(stderr, "Error parsing options.\n\n");
usage(stderr);
+ g_ptr_array_free(opt_input_paths, TRUE);
exit(EXIT_FAILURE);
} else if (ret > 0) {
+ g_ptr_array_free(opt_input_paths, TRUE);
exit(EXIT_SUCCESS);
}
printf_verbose("Verbose mode active.\n");
strlower(opt_output_format);
}
- printf_verbose("Converting from directory: %s\n", opt_input_path);
+ printf_verbose("Converting from directory(ies):\n");
+ for (i = 0; i < opt_input_paths->len; i++) {
+ const char *ipath = g_ptr_array_index(opt_input_paths, i);
+ printf_verbose(" %s\n", ipath);
+ }
printf_verbose("Converting from format: %s\n",
opt_input_format ? : "ctf <default>");
printf_verbose("Converting to directory: %s\n",
}
ctx = bt_context_create();
+ if (!ctx) {
+ goto error_td_read;
+ }
- ret = bt_context_add_traces_recursive(ctx, opt_input_path,
- opt_input_format, NULL);
- if (ret < 0) {
- fprintf(stderr, "[error] opening trace \"%s\" for reading.\n\n",
- opt_input_path);
+ for (i = 0; i < opt_input_paths->len; i++) {
+ const char *ipath = g_ptr_array_index(opt_input_paths, i);
+ ret = bt_context_add_traces_recursive(ctx, ipath,
+ opt_input_format, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "[error] opening trace \"%s\" for reading.\n\n",
+ ipath);
+ } else if (ret > 0) {
+ fprintf(stderr, "[warning] errors occurred when opening trace \"%s\" for reading, continuing anyway.\n\n",
+ ipath);
+ open_success = 1; /* some traces were OK */
+ partial_error = 1;
+ } else {
+ open_success = 1; /* all traces were OK */
+ }
+ }
+ if (!open_success) {
+ fprintf(stderr, "[error] none of the specified trace paths could be opened.\n\n");
goto error_td_read;
- } else if (ret > 0) {
- fprintf(stderr, "[warning] errors occurred when opening trace \"%s\" for reading, continuing anyway.\n\n",
- opt_input_path);
- partial_error = 1;
}
td_write = fmt_write->open_trace(opt_output_path, O_RDWR, NULL, NULL);
goto error_td_write;
}
+ /*
+ * Errors happened when opening traces, but we continue anyway.
+ * sleep to let user see the stderr output before stdout.
+ */
+ if (partial_error)
+ sleep(PARTIAL_ERROR_SLEEP);
+
ret = convert_trace(td_write, ctx);
if (ret) {
fprintf(stderr, "Error printing trace.\n\n");
end:
free(opt_input_format);
free(opt_output_format);
+ g_ptr_array_free(opt_input_paths, TRUE);
if (partial_error)
exit(EXIT_FAILURE);
else
--- /dev/null
+Babeltrace API documentation
+
+Babeltrace provides trace read and write libraries, as well as a trace
+converter. A plugin can be created for any trace format to allow its
+conversion to/from another trace format.
+
+The main format expected to be converted to/from is the Common Trace
+Format (CTF). The latest version of the CTF specification can be found at:
+ git tree: git://git.efficios.com/ctf.git
+ gitweb: http://git.efficios.com/?p=ctf.git
+
+This document describes the main concepts to use the libbabeltrace,
+which exposes the Babeltrace trace reading capability.
+
+
+TERMINOLOGY
+¯¯¯¯¯¯¯¯¯¯¯
+
+* A "callback" is a reference to a piece of executable code (such as a
+ function) that is passed as an argument to another piece of code
+ (like another function).
+
+* A "context" is a structure that represents an object in which a trace
+ collection is opened.
+
+* An "iterator" is a structure that enables the user to traverse a trace.
+
+* A "trace handle" is a unique identifier representing a trace file.
+ It allows the user to manipulate a trace directly.
+
+
+
+USAGE
+¯¯¯¯¯¯
+
+Context:
+
+In order to use libbabeltrace to read a trace, the first step is to create a
+context structure and to add a trace to it. This is done using the
+bt_context_create() and bt_context_add_trace() functions. As long as this
+context structure is allocated and the trace is valid, the trace can be
+manipulated by the library.
+
+The context can be destroyed by calling one more bt_context_put() than
+bt_context_get(), functions which respectively decrement and increment the
+refcount of the context. These functions ensures that the context won't be
+destroyed when it is in use.
+
+Once a trace is added to the context, it can be read and seeked using iterators
+and callbacks.
+
+
+Iterator:
+
+An iterator can be created using the bt_iter_create() function. As of now, only
+ctf iterator are supported. These are used to traverse a ctf-formatted trace.
+Such iterators can be created with bt_ctf_iter_create().
+
+While creating an iterator, a begin and an end position may be specified. To do
+so, one or two struct bt_iter_pos must be passed. Such struct have two
+attributes: type and u. "type" is the seek type, can be either:
+ BT_SEEK_TIME
+ BT_SEEK_RESTORE
+ BT_SEEK_CUR
+ BT_SEEK_BEGIN
+ BT_SEEK_END
+and "u" is a union of the seek time (if using BT_SEEK_TIME) and the restore
+position (if using BT_SEEK_RESTORE).
+
+Once the iterator is created, various functions become available. We have
+bt_ctf_iter_read_event() which returns the ctf event of the trace where the
+iterator is set. There is also bt_ctf_iter_destroy() which frees the iterator.
+Note that only one iterator can be created in a context at the same time. If
+more than one iterator is being created for the same context, the second
+creation will return NULL. The previous iterator must be destroyed before
+creation of the new iterator. In the future, creation of multiples iterators
+will be allowed.
+
+Finally, we have the bt_ctf_get_iter() function which returns a struct bt_iter
+with which the iterator can be moved using one of these functions:
+ bt_iter_next(), moves the iterator to the next event
+ bt_iter_set_pos(), moves the iterator to the specified position
+
+To get the current position (struct bt_iter_pos) of the iterator, the function
+bt_iter_get_pos() must be used. To create an arbitrary position based on a
+specific time, bt_iter_create_time_pos() is the function to use. The
+bt_iter_pos structure returned by these two functions must be freed with
+bt_iter_free_pos() after use.
+
+
+CTF Event:
+
+A CTF event is obtained from an iterator via the bt_ctf_iter_read_event()
+function or via the call_data parameter of a callback. To read the data of a
+CTF event :
+ * bt_ctf_event_name() returns the name of the event;
+ * bt_ctf_get_timestamp() returns the timestamp of the event
+ offsetted with the system clock
+ source (in ns);
+ * bt_ctf_get_cycles() returns the timestamp of the event as
+ written in the packet (in cycles).
+
+The payload of an event is divided in various scopes depending on the type of
+information. There are six top-level scopes (defined in the bt_ctf_scope enum)
+which can be accessed by the bt_ctf_get_top_level_scope() function :
+ BT_TRACE_PACKET_HEADER = 0,
+ BT_STREAM_PACKET_CONTEXT = 1,
+ BT_STREAM_EVENT_HEADER = 2,
+ BT_STREAM_EVENT_CONTEXT = 3,
+ BT_EVENT_CONTEXT = 4,
+ BT_EVENT_FIELDS = 5.
+
+In order to access a field or a field list, the user needs to pass a scope as
+argument, this scope can be a top-level scope or a scope relative to an
+arbitrary field in the case of compound types (array, sequence, structure or
+variant)
+
+For more information on each scope, see the CTF specifications.
+
+The function to get a field list is the bt_ctf_get_field_list(). The function
+to get the definition of a specific field is bt_ctf_get_field().
+
+Once the field is obtained, we can obtain its name and type using the
+bt_ctf_field_name() and bt_ctf_field_type() functions respectively. The
+possible types are defined in the ctf_type_id enum:
+ CTF_TYPE_UNKNOWN = 0,
+ CTF_TYPE_INTEGER,
+ CTF_TYPE_FLOAT,
+ CTF_TYPE_ENUM,
+ CTF_TYPE_STRING,
+ CTF_TYPE_STRUCT,
+ CTF_TYPE_UNTAGGED_VARIANT,
+ CTF_TYPE_VARIANT,
+ CTF_TYPE_ARRAY,
+ CTF_TYPE_SEQUENCE,
+ NR_CTF_TYPES.
+
+Depending on the field type, we can get informations about the field with these
+functions:
+ * bt_ctf_get_index() return the element at the index
+ position of an array of a sequence;
+
+ * bt_ctf_get_array_len() return the length of an array;
+
+ * bt_ctf_get_int_signedness() return the signedness of an integer;
+
+ * bt_ctf_get_int_base() return the base of an integer;
+
+ * bt_ctf_get_int_byte_order() return the byte order of an integer;
+
+ * bt_ctf_get_int_len() return the size in bits of an integer;
+
+ * bt_ctf_get_encoding() return the encoding of an int or a
+ string defined in the
+ ctf_string_encoding enum:
+ CTF_STRING_NONE = 0,
+ CTF_STRING_UTF8,
+ CTF_STRING_ASCII,
+ CTF_STRING_UNKNOWN.
+
+These functions give access to the value associated with a field :
+ * bt_ctf_get_uint64();
+ * bt_ctf_get_int64();
+ * bt_ctf_get_char_array();
+ * bt_ctf_get_string();
+ * bt_ctf_get_enum_int();
+ * bt_ctf_get_enum_str().
+
+If the field does not exist or is not of the type requested, the value returned
+with these four functions is undefined. To check if an error occured, use the
+bt_ctf_field_get_error() function after accessing a field. If no error
+occured, the function will return 0.
+
+It is also possible to access the declaration fields, the same way as the
+definition ones. bt_ctf_get_event_decl_list() sets a list to an array of
+bt_ctf_event_decl pointers and bt_ctf_get_event_decl_fields() sets a list to an
+array of bt_ctf_field_decl pointers. From the first type, the name of the
+event can be obtained with bt_ctf_get_decl_event_name(). For the second type,
+the field decl name is obtained with bt_ctf_get_decl_field_name().
+
+The declaration functions allow the user to list the events, fields and
+contexts fields enabled in the trace once it is opened, whereas the definition
+functions apply on the current event being read.
+
+
+Callback:
+
+The iterator allow the user to read the trace, in order to access the events
+and fields, the user can either call the functions listed previously on each
+event, or register callbacks functions that are called when specific (or all)
+events are read.
+
+This is done with the bt_ctf_iter_add_callback() function. It requires a valid
+ctf iterator as the first argument. Here are all arguments:
+ iter: trace collection iterator (input)
+ event: event to target. 0 for all events.
+ private_data: private data pointer to pass to the callback
+ flags: specific flags controlling the behavior of this callback
+ (or'd).
+ callback: function pointer to call
+ depends: struct bt_dependency detailing the required computation
+ results. Ends with 0.
+ weak_depends: struct bt_dependency detailing the optional computation
+ results that can be optionally consumed by this
+ callback.
+ provides: struct bt_dependency detailing the computation results
+ provided by this callback.
+ Ends with 0.
+
+"depends", "weak_depends" and "provides" memory is handled by the babeltrace
+library after this call succeeds or fails. These objects can still be used by
+the caller until the babeltrace iterator is destroyed, but they belong to the
+babeltrace library.
+
+As of now the flags and dependencies are not used, the callbacks are
+processed in FIFO order.
+
+Note: once implemented, the dependency graph will be calculated when
+bt_ctf_iter_read_event() is executed after a bt_ctf_iter_add_callback(). It is
+valid to create/add callbacks/read/add more callbacks/read some more.
+
+The callback function passed to bt_ctf_iter_add_callback() must return a
+bt_cb_ret value:
+ BT_CB_OK = 0,
+ BT_CB_OK_STOP = 1,
+ BT_CB_ERROR_STOP = 2,
+ BT_CB_ERROR_CONTINUE = 3.
+
+
+Trace handle:
+
+When a trace is added to a context, bt_context_add_trace() returns a trace
+handle id. This id is associated with its corresponding trace handle. With
+that id, it is possible to manipulate directly the trace.
+
+ * bt_trace_handle_get_path()
+ -> returns the path of the trace handle (path to the trace).
+
+ * bt_trace_handle_get_timestamp_begin()
+ * bt_trace_handle_get_timestamp_end()
+ -> return the creation/destruction timestamps (in ns or cycles
+ depending on the type specified) of the buffers of a
+ trace.
+
+ * bt_ctf_event_get_handle_id()
+ -> returns the handle id associated with an event.
+
+
+For more information on CTF, see the CTF documentation.
dist_man_MANS = babeltrace.1 babeltrace-log.1
+
+dist_doc_DATA = API.txt
.PP
.nf
-babeltrace [OPTIONS] INPUT <OUTPUT>
+babeltrace [OPTIONS] FILE...
.fi
.SH "DESCRIPTION"
.PP
.TP
-.BR "INPUT"
-Input trace path
+.BR "FILE"
+Input trace FILE(s) or directory(ies)
.TP
-.BR "OUTPUT"
+.BR "-w, --output OUTPUT"
Output trace path (default: stdout)
.TP
.BR "-i, --input-format FORMAT"
(context OR ctx), (default: payload,context).
.TP
.BR "-f, --fields name1<,name2,...>"
-Print additional fields: all, trace, trace:domain, trace:procname,
-trace:vpid, loglevel.
+Print additional fields: all, trace, trace:hostname, trace:domain,
+trace:procname, trace:vpid, loglevel.
.TP
.BR "--clock-raw"
Disregard internal clock offset (use raw value)
opt_trace_domain_field,
opt_trace_procname_field,
opt_trace_vpid_field,
+ opt_trace_hostname_field,
+ opt_trace_default_fields = 1,
opt_loglevel_field,
opt_delta_field = 1;
else
fprintf(pos->fp, " ");
}
- if ((opt_trace_domain_field && !opt_all_fields) && stream_class->trace->env.domain[0] != '\0') {
+ if ((opt_trace_hostname_field || opt_all_fields || opt_trace_default_fields)
+ && stream_class->trace->env.hostname[0] != '\0') {
+ set_field_names_print(pos, ITEM_HEADER);
+ if (pos->print_names) {
+ fprintf(pos->fp, "trace:hostname = ");
+ }
+ fprintf(pos->fp, "%s", stream_class->trace->env.hostname);
+ if (pos->print_names)
+ fprintf(pos->fp, ", ");
+ dom_print = 1;
+ }
+ if ((opt_trace_domain_field || opt_all_fields) && stream_class->trace->env.domain[0] != '\0') {
set_field_names_print(pos, ITEM_HEADER);
if (pos->print_names) {
fprintf(pos->fp, "trace:domain = ");
fprintf(pos->fp, ", ");
dom_print = 1;
}
- if ((opt_trace_procname_field && !opt_all_fields) && stream_class->trace->env.procname[0] != '\0') {
+ if ((opt_trace_procname_field || opt_all_fields || opt_trace_default_fields)
+ && stream_class->trace->env.procname[0] != '\0') {
set_field_names_print(pos, ITEM_HEADER);
if (pos->print_names) {
fprintf(pos->fp, "trace:procname = ");
fprintf(pos->fp, ", ");
dom_print = 1;
}
- if ((opt_trace_vpid_field && !opt_all_fields) && stream_class->trace->env.vpid != -1) {
+ if ((opt_trace_vpid_field || opt_all_fields || opt_trace_default_fields)
+ && stream_class->trace->env.vpid != -1) {
set_field_names_print(pos, ITEM_HEADER);
if (pos->print_names) {
fprintf(pos->fp, "trace:vpid = ");
int i, stream_id;
gpointer *event_id_ptr;
unsigned long event_id;
- struct trace_collection *tc = iter->parent.ctx->tc;
+ struct trace_collection *tc;
+ if (!iter || !callback)
+ return -EINVAL;
+
+ tc = iter->parent.ctx->tc;
for (i = 0; i < tc->array->len; i++) {
struct ctf_trace *tin;
struct trace_descriptor *td_read;
enum bt_cb_ret ret;
struct bt_ctf_event ctf_data;
+ assert(iter && stream);
+
ret = extract_ctf_stream_event(stream, &ctf_data);
/* process all events callback first */
void ctf_init_pos(struct ctf_stream_pos *pos, int fd, int open_flags)
{
pos->fd = fd;
- pos->mmap_offset = 0;
- pos->packet_size = 0;
- pos->content_size = 0;
- pos->content_size_loc = NULL;
- pos->base_mma = NULL;
- pos->offset = 0;
- pos->dummy = false;
- pos->cur_index = 0;
- pos->packet_real_index = NULL;
- if (fd >= 0)
+ if (fd >= 0) {
pos->packet_cycles_index = g_array_new(FALSE, TRUE,
sizeof(struct packet_index));
- else
+ pos->packet_real_index = g_array_new(FALSE, TRUE,
+ sizeof(struct packet_index));
+ } else {
pos->packet_cycles_index = NULL;
+ pos->packet_real_index = NULL;
+ }
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
pos->prot = PROT_READ;
assert(0);
}
}
- (void) g_array_free(pos->packet_cycles_index, TRUE);
- (void) g_array_free(pos->packet_real_index, TRUE);
+ if (pos->packet_cycles_index)
+ (void) g_array_free(pos->packet_cycles_index, TRUE);
+ if (pos->packet_real_index)
+ (void) g_array_free(pos->packet_real_index, TRUE);
}
/*
if (metadata_fp) {
fp = metadata_fp;
+ metadata_stream->pos.fd = -1;
} else {
td->metadata = &metadata_stream->parent;
metadata_stream->pos.fd = openat(td->dirfd, "metadata", O_RDONLY);
fclose(fp);
free(buf);
end_stream:
- close(metadata_stream->pos.fd);
+ if (metadata_stream->pos.fd >= 0)
+ close(metadata_stream->pos.fd);
if (ret)
g_free(metadata_stream);
return ret;
if (ret)
goto error_def;
/*
- * For now, only a single slock is supported.
+ * For now, only a single clock per trace is supported.
*/
file_stream->parent.current_clock = td->single_clock;
ret = create_stream_packet_index(td, file_stream);
if (ret)
goto error_index;
+ /*
+ * For now, only a single clock per trace is supported.
+ */
+ file_stream->parent.current_clock = td->single_clock;
+
/* Add stream file to stream class */
g_ptr_array_add(file_stream->parent.stream_class->streams,
&file_stream->parent);
cfs = container_of(stream, struct ctf_file_stream,
parent);
stream_pos = &cfs->pos;
- stream_pos->packet_real_index = g_array_new(FALSE, TRUE,
- sizeof(struct packet_index));
-
if (!stream_pos->packet_cycles_index)
continue;
#include <babeltrace/ctf/events.h>
#include <babeltrace/ctf-ir/metadata.h>
-
-static inline
-uint64_t ctf_get_timestamp_raw(struct ctf_stream_definition *stream,
- uint64_t timestamp)
-{
- uint64_t ts_nsec;
-
- if (stream->current_clock->freq == 1000000000ULL) {
- ts_nsec = timestamp;
- } else {
- ts_nsec = (uint64_t) ((double) timestamp * 1000000000.0
- / (double) stream->current_clock->freq);
- }
- return ts_nsec;
-}
+#include <babeltrace/clock-internal.h>
static inline
uint64_t ctf_get_real_timestamp(struct ctf_stream_definition *stream,
uint64_t ts_nsec;
struct ctf_trace *trace = stream->stream_class->trace;
struct trace_collection *tc = trace->collection;
- uint64_t tc_offset = tc->single_clock_offset_avg;
+ uint64_t tc_offset;
+
+ if (tc->clock_use_offset_avg)
+ tc_offset = tc->single_clock_offset_avg;
+ else
+ tc_offset = trace->single_clock->offset;
- ts_nsec = ctf_get_timestamp_raw(stream, timestamp);
+ ts_nsec = clock_cycles_to_ns(stream->current_clock, timestamp);
ts_nsec += tc_offset; /* Add offset */
return ts_nsec;
}
enum bt_ctf_scope scope)
{
struct definition *tmp = NULL;
- struct ctf_event_definition *event = ctf_event->parent;
+ struct ctf_event_definition *event;
+ if (!ctf_event)
+ return NULL;
+
+ event = ctf_event->parent;
switch (scope) {
case BT_TRACE_PACKET_HEADER:
if (!event->stream)
struct definition *def;
char *field_underscore;
- if (scope) {
- def = lookup_definition(scope, field);
- /*
- * optionally a field can have an underscore prefix, try
- * to lookup the field with this prefix if it failed
- */
- if (!def) {
- field_underscore = g_new(char, strlen(field) + 2);
- field_underscore[0] = '_';
- strcpy(&field_underscore[1], field);
- def = lookup_definition(scope, field_underscore);
- g_free(field_underscore);
- }
- if (bt_ctf_field_type(def) == CTF_TYPE_VARIANT) {
- struct definition_variant *variant_definition;
- variant_definition = container_of(def,
- struct definition_variant, p);
- return variant_definition->current_field;
- }
- return def;
+ if (!ctf_event || !scope || !field)
+ return NULL;
+
+ def = lookup_definition(scope, field);
+ /*
+ * optionally a field can have an underscore prefix, try
+ * to lookup the field with this prefix if it failed
+ */
+ if (!def) {
+ field_underscore = g_new(char, strlen(field) + 2);
+ field_underscore[0] = '_';
+ strcpy(&field_underscore[1], field);
+ def = lookup_definition(scope, field_underscore);
+ g_free(field_underscore);
}
- return NULL;
+ if (bt_ctf_field_type(bt_ctf_get_field_decl(def)) == CTF_TYPE_VARIANT) {
+ struct definition_variant *variant_definition;
+ variant_definition = container_of(def,
+ struct definition_variant, p);
+ return variant_definition->current_field;
+ }
+ return def;
}
const struct definition *bt_ctf_get_index(const struct bt_ctf_event *ctf_event,
{
struct definition *ret = NULL;
- if (bt_ctf_field_type(field) == CTF_TYPE_ARRAY) {
+ if (!ctf_event || !field)
+ return NULL;
+
+ if (bt_ctf_field_type(bt_ctf_get_field_decl(field)) == CTF_TYPE_ARRAY) {
struct definition_array *array_definition;
array_definition = container_of(field,
struct definition_array, p);
ret = array_index(array_definition, index);
- } else if (bt_ctf_field_type(field) == CTF_TYPE_SEQUENCE) {
+ } else if (bt_ctf_field_type(bt_ctf_get_field_decl(field)) == CTF_TYPE_SEQUENCE) {
struct definition_sequence *sequence_definition;
sequence_definition = container_of(field,
struct definition_sequence, p);
{
struct ctf_event_declaration *event_class;
struct ctf_stream_declaration *stream_class;
- struct ctf_event_definition *event = ctf_event->parent;
+ struct ctf_event_definition *event;
- if (!event)
+ if (!ctf_event)
return NULL;
+
+ event = ctf_event->parent;
stream_class = event->stream->stream_class;
event_class = g_ptr_array_index(stream_class->events_by_id,
event->stream->event_id);
const char *bt_ctf_field_name(const struct definition *def)
{
- if (def)
- return rem_(g_quark_to_string(def->name));
- return NULL;
+ if (!def)
+ return NULL;
+
+ return rem_(g_quark_to_string(def->name));
}
-enum ctf_type_id bt_ctf_field_type(const struct definition *def)
+enum ctf_type_id bt_ctf_field_type(const struct declaration *decl)
{
- if (def)
- return def->declaration->id;
- return CTF_TYPE_UNKNOWN;
+ if (!decl)
+ return CTF_TYPE_UNKNOWN;
+
+ return decl->id;
}
int bt_ctf_get_field_list(const struct bt_ctf_event *ctf_event,
struct definition const * const **list,
unsigned int *count)
{
- switch (bt_ctf_field_type(scope)) {
+ if (!ctf_event || !scope || !list || !count)
+ return -EINVAL;
+
+ switch (bt_ctf_field_type(bt_ctf_get_field_decl(scope))) {
case CTF_TYPE_INTEGER:
case CTF_TYPE_FLOAT:
case CTF_TYPE_STRING:
} else {
goto error;
}
+ break;
}
case CTF_TYPE_UNTAGGED_VARIANT:
goto error;
} else {
goto error;
}
+ break;
}
case CTF_TYPE_ARRAY:
{
} else {
goto error;
}
+ break;
}
case CTF_TYPE_SEQUENCE:
{
} else {
goto error;
}
+ break;
}
default:
break;
struct bt_context *ret = NULL;
struct ctf_file_stream *cfs;
struct ctf_trace *trace;
- struct ctf_event_definition *event = ctf_event->parent;
+ struct ctf_event_definition *event;
+
+ if (!ctf_event)
+ return NULL;
+ event = ctf_event->parent;
cfs = container_of(event->stream, struct ctf_file_stream,
parent);
trace = cfs->parent.stream_class->trace;
int ret = -1;
struct ctf_file_stream *cfs;
struct ctf_trace *trace;
- struct ctf_event_definition *event = ctf_event->parent;
+ struct ctf_event_definition *event;
+
+ if (!ctf_event)
+ return -EINVAL;
+ event = ctf_event->parent;
cfs = container_of(event->stream, struct ctf_file_stream,
parent);
trace = cfs->parent.stream_class->trace;
uint64_t bt_ctf_get_timestamp(const struct bt_ctf_event *ctf_event)
{
- struct ctf_event_definition *event = ctf_event->parent;
+ struct ctf_event_definition *event;
+
+ if (!ctf_event)
+ return -1ULL;
+
+ event = ctf_event->parent;
if (event && event->stream->has_timestamp)
return event->stream->real_timestamp;
else
uint64_t bt_ctf_get_cycles(const struct bt_ctf_event *ctf_event)
{
- struct ctf_event_definition *event = ctf_event->parent;
+ struct ctf_event_definition *event;
+
+ if (!ctf_event)
+ return -1ULL;
+
+ event = ctf_event->parent;
if (event && event->stream->has_timestamp)
return event->stream->cycles_timestamp;
else
return ret;
}
-int bt_ctf_get_int_signedness(const struct definition *field)
+static struct declaration_integer *get_declaration_integer(const struct declaration *decl)
+{
+ struct declaration_field *field_decl;
+ struct declaration_integer *ret = NULL;
+
+ if (decl && bt_ctf_field_type(decl) == CTF_TYPE_INTEGER) {
+ field_decl = (struct declaration_field *) decl;
+ ret = ((struct declaration_integer *) field_decl->declaration);
+ }
+
+ return ret;
+}
+
+static struct declaration_string *get_declaration_string(const struct declaration *decl)
+{
+ struct declaration_field *field_decl;
+ struct declaration_string *ret = NULL;
+
+ if (decl && bt_ctf_field_type(decl) == CTF_TYPE_STRING) {
+ field_decl = (struct declaration_field *) decl;
+ ret = ((struct declaration_string *) field_decl->declaration);
+ }
+
+ return ret;
+}
+
+static struct declaration_array *get_declaration_array(const struct declaration *decl)
+{
+ struct declaration_field *field_decl;
+ struct declaration_array *ret = NULL;
+
+ if (decl && bt_ctf_field_type(decl) == CTF_TYPE_ARRAY) {
+ field_decl = (struct declaration_field *) decl;
+ ret = ((struct declaration_array *) field_decl->declaration);
+ }
+
+ return ret;
+}
+
+int bt_ctf_get_int_signedness(const struct declaration *decl)
{
int ret;
+ struct declaration_integer *integer;
- if (field && bt_ctf_field_type(field) == CTF_TYPE_INTEGER) {
- ret = get_int_signedness(field);
+ integer = get_declaration_integer(decl);
+ if (integer) {
+ ret = integer->signedness;
} else {
- ret = -1;
+ ret = -EINVAL;
bt_ctf_field_set_error(-EINVAL);
}
return ret;
}
-int bt_ctf_get_int_base(const struct definition *field)
+int bt_ctf_get_int_base(const struct declaration *decl)
{
int ret;
+ struct declaration_integer *integer;
- if (field && bt_ctf_field_type(field) == CTF_TYPE_INTEGER) {
- ret = get_int_base(field);
+ integer = get_declaration_integer(decl);
+ if (integer) {
+ ret = integer->base;
} else {
- ret = -1;
+ ret = -EINVAL;
bt_ctf_field_set_error(-EINVAL);
}
return ret;
}
-int bt_ctf_get_int_byte_order(const struct definition *field)
+int bt_ctf_get_int_byte_order(const struct declaration *decl)
{
int ret;
+ struct declaration_integer *integer;
- if (field && bt_ctf_field_type(field) == CTF_TYPE_INTEGER) {
- ret = get_int_byte_order(field);
+ integer = get_declaration_integer(decl);
+ if (integer) {
+ ret = integer->byte_order;
} else {
- ret = -1;
+ ret = -EINVAL;
bt_ctf_field_set_error(-EINVAL);
}
return ret;
}
-ssize_t bt_ctf_get_int_len(const struct definition *field)
+ssize_t bt_ctf_get_int_len(const struct declaration *decl)
{
ssize_t ret;
+ struct declaration_integer *integer;
- if (field && bt_ctf_field_type(field) == CTF_TYPE_INTEGER) {
- ret = (ssize_t) get_int_len(field);
+ integer = get_declaration_integer(decl);
+ if (integer) {
+ ret = (ssize_t) integer->len;
} else {
- ret = -1;
+ ret = -EINVAL;
bt_ctf_field_set_error(-EINVAL);
}
return ret;
}
-enum ctf_string_encoding bt_ctf_get_encoding(const struct definition *field)
+const struct definition *bt_ctf_get_enum_int(const struct definition *field)
{
- enum ctf_string_encoding ret = 0;
+ struct definition_enum *def_enum;
- if (!field)
- goto end;
+ if (!field || bt_ctf_field_type(bt_ctf_get_field_decl(field)) != CTF_TYPE_ENUM) {
+ bt_ctf_field_set_error(-EINVAL);
+ return NULL;
+ }
+ def_enum = container_of(field, struct definition_enum, p);
+ return &def_enum->integer->p;
+}
- if (bt_ctf_field_type(field) == CTF_TYPE_INTEGER)
- ret = get_int_encoding(field);
- else if (bt_ctf_field_type(field) == CTF_TYPE_STRING)
- ret = get_string_encoding(field);
- else
+const char *bt_ctf_get_enum_str(const struct definition *field)
+{
+ struct definition_enum *def_enum;
+ struct declaration_enum *decl_enum;
+ GArray *array;
+ const char *ret;
+
+ if (!field || bt_ctf_field_type(bt_ctf_get_field_decl(field)) != CTF_TYPE_ENUM) {
+ bt_ctf_field_set_error(-EINVAL);
+ return NULL;
+ }
+ def_enum = container_of(field, struct definition_enum, p);
+ decl_enum = def_enum->declaration;
+ if (get_int_signedness(&def_enum->integer->p)) {
+ array = enum_int_to_quark_set(decl_enum,
+ get_signed_int(&def_enum->integer->p));
+ } else {
+ array = enum_uint_to_quark_set(decl_enum,
+ get_unsigned_int(&def_enum->integer->p));
+ }
+ if (!array) {
+ bt_ctf_field_set_error(-ENOENT);
+ return NULL;
+ }
+
+ if (array->len == 0) {
+ g_array_unref(array);
+ bt_ctf_field_set_error(-ENOENT);
+ return NULL;
+ }
+ /* Return first string. Arbitrary choice. */
+ ret = g_quark_to_string(g_array_index(array, GQuark, 0));
+ g_array_unref(array);
+ return ret;
+}
+
+enum ctf_string_encoding bt_ctf_get_encoding(const struct declaration *decl)
+{
+ enum ctf_string_encoding ret = 0;
+ struct declaration_integer *integer;
+ struct declaration_string *string;
+
+ if (!decl)
goto error;
-end:
+ if (bt_ctf_field_type(decl) == CTF_TYPE_INTEGER) {
+ integer = get_declaration_integer(decl);
+ if (integer) {
+ ret = integer->encoding;
+ } else {
+ goto error;
+ }
+ } else if (bt_ctf_field_type(decl) == CTF_TYPE_STRING) {
+ string = get_declaration_string(decl);
+ if (string) {
+ ret = string->encoding;
+ } else {
+ goto error;
+ }
+ } else {
+ goto error;
+ }
return ret;
error:
return -1;
}
-int bt_ctf_get_array_len(const struct definition *field)
+int bt_ctf_get_array_len(const struct declaration *decl)
{
int ret;
+ struct declaration_array *array;
- if (field && bt_ctf_field_type(field) == CTF_TYPE_ARRAY) {
- ret = get_array_len(field);
+ if (decl && bt_ctf_field_type(decl) == CTF_TYPE_ARRAY) {
+ array = get_declaration_array(decl);
+ if (array) {
+ ret = array->len;
+ } else {
+ goto error;
+ }
} else {
- ret = -1;
- bt_ctf_field_set_error(-EINVAL);
+ goto error;
}
return ret;
+
+error:
+ bt_ctf_field_set_error(-EINVAL);
+ return -1;
}
uint64_t bt_ctf_get_uint64(const struct definition *field)
{
uint64_t ret = 0;
- if (field && bt_ctf_field_type(field) == CTF_TYPE_INTEGER)
+ if (field && bt_ctf_field_type(bt_ctf_get_field_decl(field)) == CTF_TYPE_INTEGER)
ret = get_unsigned_int(field);
else
bt_ctf_field_set_error(-EINVAL);
{
int64_t ret = 0;
- if (field && bt_ctf_field_type(field) == CTF_TYPE_INTEGER)
+ if (field && bt_ctf_field_type(bt_ctf_get_field_decl(field)) == CTF_TYPE_INTEGER)
ret = get_signed_int(field);
else
bt_ctf_field_set_error(-EINVAL);
char *bt_ctf_get_char_array(const struct definition *field)
{
char *ret = NULL;
+ GString *char_array;
- if (field && bt_ctf_field_type(field) == CTF_TYPE_ARRAY)
- ret = get_char_array(field)->str;
- else
- bt_ctf_field_set_error(-EINVAL);
+ if (field && bt_ctf_field_type(bt_ctf_get_field_decl(field)) == CTF_TYPE_ARRAY) {
+ char_array = get_char_array(field);
+ if (char_array) {
+ ret = char_array->str;
+ goto end;
+ }
+ }
+ bt_ctf_field_set_error(-EINVAL);
+end:
return ret;
}
{
char *ret = NULL;
- if (field && bt_ctf_field_type(field) == CTF_TYPE_STRING)
+ if (field && bt_ctf_field_type(bt_ctf_get_field_decl(field)) == CTF_TYPE_STRING)
ret = get_string(field);
else
bt_ctf_field_set_error(-EINVAL);
struct trace_descriptor *td;
struct ctf_trace *tin;
- if (!ctx)
+ if (!ctx || !list || !count)
goto error;
handle = g_hash_table_lookup(ctx->trace_handles,
{
if (!event)
return NULL;
+
return g_quark_to_string(event->parent.name);
}
int ret = 0;
*count = 0;
+ if (!event_decl || !list || !count)
+ return -EINVAL;
+
switch (scope) {
case BT_EVENT_CONTEXT:
if (event_decl->context_decl) {
const char *bt_ctf_get_decl_field_name(const struct bt_ctf_field_decl *field)
{
- if (field)
- return rem_(g_quark_to_string(((struct declaration_field *) field)->name));
+ if (!field)
+ return NULL;
+
+ return rem_(g_quark_to_string(((struct declaration_field *) field)->name));
+}
+
+const struct declaration *bt_ctf_get_field_decl(const struct definition *def)
+{
+ if (def)
+ return def->declaration;
+
return NULL;
}
struct bt_ctf_iter *iter;
int ret;
+ if (!ctx)
+ return NULL;
+
iter = g_new0(struct bt_ctf_iter, 1);
ret = bt_iter_init(&iter->parent, ctx, begin_pos, end_pos);
if (ret) {
struct bt_callback_chain *bt_chain;
int i, j;
+ assert(iter);
+
/* free all events callbacks */
if (iter->main_callbacks.callback)
g_array_free(iter->main_callbacks.callback, TRUE);
struct bt_iter *bt_ctf_get_iter(struct bt_ctf_iter *iter)
{
+ if (!iter)
+ return NULL;
+
return &iter->parent;
}
struct bt_ctf_event *bt_ctf_iter_read_event(struct bt_ctf_iter *iter)
{
struct ctf_file_stream *file_stream;
- struct bt_ctf_event *ret = &iter->current_ctf_event;
+ struct bt_ctf_event *ret;
struct ctf_stream_definition *stream;
+ /*
+ * We do not want to fail for any other reason than end of
+ * trace, hence the assert.
+ */
+ assert(iter);
+
+ ret = &iter->current_ctf_event;
file_stream = heap_maximum(iter->parent.stream_heap);
if (!file_stream) {
/* end of file for all streams */
#include "ctf-parser.h"
#include "ctf-ast.h"
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src);
static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
#include "ctf-parser.h"
#include "ctf-ast.h"
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
int yydebug;
/* Join two lists, put "add" at the end of "head". */
}
}
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
int yyparse(struct ctf_scanner *scanner);
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
int yylex(union YYSTYPE *yyval, struct ctf_scanner *scanner);
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
int yylex_init_extra(struct ctf_scanner *scanner, yyscan_t * ptr_yy_globals);
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
int yylex_destroy(yyscan_t yyscanner);
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
void yyrestart(FILE * in_str, yyscan_t scanner);
struct gc_string {
[ NODE_STRUCT ] = "NODE_STRUCT",
};
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
const char *node_type(struct ctf_node *node)
{
if (node->type < NR_NODE_TYPES)
* gsrc will be garbage collected immediately, and gstr might be.
* Should only be used to append characters to a string literal or constant.
*/
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
struct gc_string *gc_string_append(struct ctf_scanner *scanner,
struct gc_string *gstr,
struct gc_string *gsrc)
return ret;
}
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
int is_type(struct ctf_scanner *scanner, const char *id)
{
struct ctf_scanner_scope *it;
return 0;
}
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
void yyerror(struct ctf_scanner *scanner, const char *str)
{
fprintf(stderr, "error %s\n", str);
}
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
int yywrap(void)
{
return 1;
return scanner->ast;
}
-__attribute__((visibility("protected")))
+__attribute__((visibility("hidden")))
int is_type(struct ctf_scanner *scanner, const char *id);
#endif /* _CTF_SCANNER_H */
}
if (!CTF_CLOCK_FIELD_IS_SET(clock, name)) {
ret = -EPERM;
- fprintf(fd, "[error] %s: missing namefield in clock declaration\n", __func__);
+ fprintf(fd, "[error] %s: missing name field in clock declaration\n", __func__);
goto error;
}
if (g_hash_table_size(trace->clocks) > 0) {
strncpy(env->procname, right, TRACER_ENV_LEN);
env->procname[TRACER_ENV_LEN - 1] = '\0';
printf_verbose("env.procname = \"%s\"\n", env->procname);
+ } else if (!strcmp(left, "hostname")) {
+ char *right;
+
+ if (env->hostname[0]) {
+ fprintf(fd, "[warning] %s: duplicated env hostname\n", __func__);
+ goto error; /* ret is 0, so not an actual error, just warn. */
+ }
+ right = concatenate_unary_strings(&node->u.ctf_expression.right);
+ if (!right) {
+ fprintf(fd, "[warning] %s: unexpected unary expression for env hostname\n", __func__);
+ goto error; /* ret is 0, so not an actual error, just warn. */
+ }
+ strncpy(env->hostname, right, TRACER_ENV_LEN);
+ env->hostname[TRACER_ENV_LEN - 1] = '\0';
+ printf_verbose("env.hostname = \"%s\"\n", env->hostname);
} else if (!strcmp(left, "domain")) {
char *right;
trace->env.vpid = -1;
trace->env.procname[0] = '\0';
+ trace->env.hostname[0] = '\0';
trace->env.domain[0] = '\0';
trace->env.sysname[0] = '\0';
trace->env.release[0] = '\0';
return -EINVAL;
}
tmpfloat = container_of(tmpdef, struct definition_float, p);
+ memset(&destp, 0, sizeof(destp));
ctf_init_pos(&destp, -1, O_RDWR);
mmap_align_set_addr(&mma, (char *) u.bits);
destp.base_mma = &mma;
if (!integer_declaration->signedness) {
if (integer_declaration->byte_order == LITTLE_ENDIAN)
- bt_bitfield_read_le(mmap_align_addr(pos->base_mma), unsigned long,
+ bt_bitfield_read_le(mmap_align_addr(pos->base_mma) +
+ pos->mmap_base_offset, unsigned long,
pos->offset, integer_declaration->len,
&integer_definition->value._unsigned);
else
- bt_bitfield_read_be(mmap_align_addr(pos->base_mma), unsigned long,
+ bt_bitfield_read_be(mmap_align_addr(pos->base_mma) +
+ pos->mmap_base_offset, unsigned long,
pos->offset, integer_declaration->len,
&integer_definition->value._unsigned);
} else {
if (integer_declaration->byte_order == LITTLE_ENDIAN)
- bt_bitfield_read_le(mmap_align_addr(pos->base_mma), unsigned long,
+ bt_bitfield_read_le(mmap_align_addr(pos->base_mma) +
+ pos->mmap_base_offset, unsigned long,
pos->offset, integer_declaration->len,
&integer_definition->value._signed);
else
- bt_bitfield_read_be(mmap_align_addr(pos->base_mma), unsigned long,
+ bt_bitfield_read_be(mmap_align_addr(pos->base_mma) +
+ pos->mmap_base_offset, unsigned long,
pos->offset, integer_declaration->len,
&integer_definition->value._signed);
}
goto end;
if (!integer_declaration->signedness) {
if (integer_declaration->byte_order == LITTLE_ENDIAN)
- bt_bitfield_write_le(mmap_align_addr(pos->base_mma), unsigned long,
+ bt_bitfield_write_le(mmap_align_addr(pos->base_mma) +
+ pos->mmap_base_offset, unsigned long,
pos->offset, integer_declaration->len,
integer_definition->value._unsigned);
else
- bt_bitfield_write_be(mmap_align_addr(pos->base_mma), unsigned long,
+ bt_bitfield_write_be(mmap_align_addr(pos->base_mma) +
+ pos->mmap_base_offset, unsigned long,
pos->offset, integer_declaration->len,
integer_definition->value._unsigned);
} else {
if (integer_declaration->byte_order == LITTLE_ENDIAN)
- bt_bitfield_write_le(mmap_align_addr(pos->base_mma), unsigned long,
+ bt_bitfield_write_le(mmap_align_addr(pos->base_mma) +
+ pos->mmap_base_offset, unsigned long,
pos->offset, integer_declaration->len,
integer_definition->value._signed);
else
- bt_bitfield_write_be(mmap_align_addr(pos->base_mma), unsigned long,
+ bt_bitfield_write_be(mmap_align_addr(pos->base_mma) +
+ pos->mmap_base_offset, unsigned long,
pos->offset, integer_declaration->len,
integer_definition->value._signed);
}
babeltrace/align.h \
babeltrace/babeltrace-internal.h \
babeltrace/bitfield.h \
+ babeltrace/clock-internal.h \
babeltrace/compiler.h \
babeltrace/context-internal.h \
babeltrace/iterator-internal.h \
uint64_t offset_first;
int64_t delta_offset_first_sum;
int offset_nr;
+ int clock_use_offset_avg;
};
extern int opt_all_field_names,
opt_trace_domain_field,
opt_trace_procname_field,
opt_trace_vpid_field,
+ opt_trace_hostname_field,
+ opt_trace_default_fields,
opt_loglevel_field,
opt_delta_field,
opt_clock_cycles,
#include <babeltrace/context.h>
#include <babeltrace/format.h>
#include <babeltrace/iterator.h>
-#include <babeltrace/trace-collection.h>
#include <babeltrace/trace-handle.h>
#endif /* _BABELTRACE_H */
--- /dev/null
+#ifndef _BABELTRACE_CLOCK_INTERNAL_H
+#define _BABELTRACE_CLOCK_INTERNAL_H
+
+/*
+ * BabelTrace
+ *
+ * clocks header (internal)
+ *
+ * Copyright 2012 EfficiOS Inc. and Linux Foundation
+ *
+ * Author: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Julien Desfossez <julien.desfossez@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ */
+
+static inline
+uint64_t clock_cycles_to_ns(struct ctf_clock *clock, uint64_t cycles)
+{
+ if (clock->freq == 1000000000ULL) {
+ /* 1GHZ freq, no need to scale cycles value */
+ return cycles;
+ } else {
+ return (double) cycles * 1000000000.0
+ / (double) clock->freq;
+ }
+}
+
+#endif /* _BABELTRACE_CLOCK_INTERNAL_H */
-#ifndef _BABELTRACE_CLOCKS_H
-#define _BABELTRACE_CLOCKS_H
+#ifndef _BABELTRACE_CLOCK_TYPES_H
+#define _BABELTRACE_CLOCK_TYPES_H
/*
* BabelTrace
*
- * clocks header
+ * Clock types header
*
* Copyright 2012 EfficiOS Inc. and Linux Foundation
*
* all copies or substantial portions of the Software.
*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/*
- * the Babeltrace clock representations
+ * The Babeltrace clock representations
*/
enum bt_clock_type {
BT_CLOCK_CYCLES = 0,
BT_CLOCK_REAL,
};
-#endif /* _BABELTRACE_CLOCKS_H */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BABELTRACE_CLOCK_TYPES_H */
#include <unistd.h>
#include <babeltrace/format.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* struct bt_context is opaque to the user */
struct bt_context;
struct stream_pos;
*
* stream_list is a linked list of streams, it is used to open a trace where
* the trace data is located in memory mapped areas instead of trace files,
- * this argument should be set to NULL when path is not NULL.
+ * this argument should be set to NULL when path is NULL.
*
* The metadata parameter acts as a metadata override when not NULL, otherwise
* the format handles the metadata opening.
/*
* bt_context_remove_trace: Remove a trace from the context.
*
- * Effectively closing the trace.
+ * Effectively closing the trace. Return negative error value if trace
+ * is not in context.
*/
-void bt_context_remove_trace(struct bt_context *ctx, int trace_id);
+int bt_context_remove_trace(struct bt_context *ctx, int trace_id);
/*
* bt_context_get and bt_context_put : increments and decrement the
*/
struct bt_context *bt_ctf_event_get_context(const struct bt_ctf_event *event);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _BABELTRACE_CONTEXT_H */
/* All strings below: "" if unset. */
char procname[TRACER_ENV_LEN];
+ char hostname[TRACER_ENV_LEN];
char domain[TRACER_ENV_LEN];
char sysname[TRACER_ENV_LEN];
char release[TRACER_ENV_LEN];
#include <babeltrace/format.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Forward declarations */
struct bt_ctf_iter;
struct bt_dependencies;
*
* @callback: function pointer to call
* @depends: struct bt_dependency detailing the required computation results.
- * Ends with 0.
+ * Ends with 0. NULL is accepted as empty dependency.
* @weak_depends: struct bt_dependency detailing the optional computation
* results that can be optionally consumed by this
- * callback.
+ * callback. NULL is accepted as empty dependency.
* @provides: struct bt_dependency detailing the computation results
* provided by this callback.
- * Ends with 0.
+ * Ends with 0. NULL is accepted as empty dependency.
*
* "depends", "weak_depends" and "provides" memory is handled by the
* babeltrace library after this call succeeds or fails. These objects
BT_FLAGS_FREE_PRIVATE_DATA = (1 << 0),
};
+#ifdef __cplusplus
+}
+#endif
+
#endif /*_BABELTRACE_CTF_CALLBACKS_H */
#include <babeltrace/context.h>
#include <babeltrace/clock-types.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct definition;
+struct declaration;
struct bt_ctf_event;
struct bt_ctf_event_decl;
struct bt_ctf_field_decl;
uint64_t bt_ctf_get_timestamp(const struct bt_ctf_event *event);
/*
- * bt_ctf_get_field_list: set list pointer to an array of definition
+ * bt_ctf_get_field_list: obtain the list of fields for compound type
+ *
+ * This function can be used to obtain the list of fields
+ * contained within a compound type: array, sequence,
+ * structure, or variant.
+
+ * This function sets the "list" pointer to an array of definition
* pointers and set count to the number of elements in the array.
* Return 0 on success and a negative value on error.
+ *
+ * The content pointed to by "list" should *not* be freed. It stays
+ * valid as long as the event is unchanged (as long as the iterator
+ * from which the event is extracted is unchanged).
*/
int bt_ctf_get_field_list(const struct bt_ctf_event *event,
const struct definition *scope,
*/
const char *bt_ctf_field_name(const struct definition *def);
+/*
+ * bt_ctf_get_field_decl: return the declaration of a field or NULL
+ * on error
+ */
+const struct declaration *bt_ctf_get_field_decl(const struct definition *def);
+
/*
* bt_ctf_field_type: returns the type of a field or -1 if unknown
*/
-enum ctf_type_id bt_ctf_field_type(const struct definition *def);
+enum ctf_type_id bt_ctf_field_type(const struct declaration *decl);
/*
* bt_ctf_get_int_signedness: return the signedness of an integer
* return 1 if signed
* return -1 on error
*/
-int bt_ctf_get_int_signedness(const struct definition *field);
+int bt_ctf_get_int_signedness(const struct declaration *decl);
/*
* bt_ctf_get_int_base: return the base of an int or a negative value on error
*/
-int bt_ctf_get_int_base(const struct definition *field);
+int bt_ctf_get_int_base(const struct declaration *decl);
/*
* bt_ctf_get_int_byte_order: return the byte order of an int or a negative
* value on error
*/
-int bt_ctf_get_int_byte_order(const struct definition *field);
+int bt_ctf_get_int_byte_order(const struct declaration *decl);
/*
* bt_ctf_get_int_len: return the size, in bits, of an int or a negative
* value on error
*/
-ssize_t bt_ctf_get_int_len(const struct definition *field);
+ssize_t bt_ctf_get_int_len(const struct declaration *decl);
/*
* bt_ctf_get_encoding: return the encoding of an int or a string.
* return a negative value on error
*/
-enum ctf_string_encoding bt_ctf_get_encoding(const struct definition *field);
+enum ctf_string_encoding bt_ctf_get_encoding(const struct declaration *decl);
/*
* bt_ctf_get_array_len: return the len of an array or a negative
* value on error
*/
-int bt_ctf_get_array_len(const struct definition *field);
+int bt_ctf_get_array_len(const struct declaration *decl);
/*
* Field access functions
* If the field does not exist or is not of the type requested, the value
* returned is undefined. To check if an error occured, use the
* bt_ctf_field_get_error() function after accessing a field.
+ *
+ * bt_ctf_get_enum_int gets the integer field of an enumeration.
+ * bt_ctf_get_enum_str gets the string matching the current enumeration
+ * value, or NULL if the current value does not match any string.
*/
uint64_t bt_ctf_get_uint64(const struct definition *field);
int64_t bt_ctf_get_int64(const struct definition *field);
+const struct definition *bt_ctf_get_enum_int(const struct definition *field);
+const char *bt_ctf_get_enum_str(const struct definition *field);
char *bt_ctf_get_char_array(const struct definition *field);
char *bt_ctf_get_string(const struct definition *field);
*/
const char *bt_ctf_get_decl_field_name(const struct bt_ctf_field_decl *field);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _BABELTRACE_CTF_EVENTS_H */
#include <babeltrace/iterator.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct bt_ctf_iter;
struct bt_ctf_event;
/*
* bt_ctf_iter_read_event: Read the iterator's current event data.
*
- * @iter: trace collection iterator (input)
+ * @iter: trace collection iterator (input). Should NOT be NULL.
*
* Return current event on success, NULL on end of trace.
*/
struct bt_ctf_event *bt_ctf_iter_read_event(struct bt_ctf_iter *iter);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _BABELTRACE_CTF_ITERATOR_H */
/* Current position */
off_t mmap_offset; /* mmap offset in the file, in bytes */
+ off_t mmap_base_offset; /* offset of start of packet in mmap, in bytes */
size_t packet_size; /* current packet size, in bits */
size_t content_size; /* current content size, in bits */
uint32_t *content_size_loc; /* pointer to current content size */
{
/* Only makes sense to get the address after aligning on CHAR_BIT */
assert(!(pos->offset % CHAR_BIT));
- return mmap_align_addr(pos->base_mma) + (pos->offset / CHAR_BIT);
+ return mmap_align_addr(pos->base_mma) +
+ pos->mmap_base_offset + (pos->offset / CHAR_BIT);
}
static inline
#include <stdint.h>
#include <stdio.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef int bt_intern_str;
/* forward declaration */
extern int bt_register_format(struct format *format);
/* TBD: format unregistration */
+#ifdef __cplusplus
+}
+#endif
#endif /* _BABELTRACE_FORMAT_H */
#include <babeltrace/format.h>
#include <babeltrace/context.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Forward declarations */
struct bt_iter;
struct bt_saved_pos;
* is expressed in nanoseconds
* - restore is a position saved with bt_iter_get_pos, it is used with
* BT_SEEK_RESTORE.
+ *
+ * Note about BT_SEEK_LAST: if many events happen to be at the last
+ * timestamp, it is implementation-defined which event will be the last,
+ * and the order of events with the same timestamp may not be the same
+ * as normal iteration on the trace. Therefore, it is recommended to
+ * only use BT_SEEK_LAST to get the timestamp of the last event(s) in
+ * the trace.
*/
struct bt_iter_pos {
enum {
BT_SEEK_RESTORE, /* uses u.restore */
BT_SEEK_CUR,
BT_SEEK_BEGIN,
- BT_SEEK_END,
+ BT_SEEK_LAST,
} type;
union {
uint64_t seek_time;
struct bt_iter_pos *bt_iter_create_time_pos(struct bt_iter *iter,
uint64_t timestamp);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _BABELTRACE_ITERATOR_H */
found in the Linux kernel headers to enable people familiar with
the latter find their way in these sources as well. */
+#ifdef __cplusplus
+extern "C" {
+#endif
/* Basic type for the double-link list. */
struct bt_list_head
BT_INIT_LIST_HEAD(old);
}
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _BT_LIST_H */
* all copies or substantial portions of the Software.
*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct trace_collection;
void init_trace_collection(struct trace_collection *tc);
int trace_collection_remove(struct trace_collection *tc,
struct trace_descriptor *td);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _BABELTRACE_TRACE_COLLECTION_H */
#include <stdint.h>
#include <babeltrace/clock-types.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/*
* trace_handle : unique identifier of a trace
*
*/
int bt_ctf_event_get_handle_id(const struct bt_ctf_event *event);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _BABELTRACE_TRACE_HANDLE_H */
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <errno.h>
-#include <fts.h>
#include <fcntl.h> /* For O_RDONLY */
#include <glib.h>
struct bt_trace_handle *handle;
int ret;
+ if (!ctx || !format_name || (!path && !stream_list))
+ return -EINVAL;
+
fmt = bt_lookup_format(g_quark_from_string(format_name));
if (!fmt) {
fprintf(stderr, "[error] [Context] Format \"%s\" unknown.\n\n",
return ret;
}
-void bt_context_remove_trace(struct bt_context *ctx, int handle_id)
+int bt_context_remove_trace(struct bt_context *ctx, int handle_id)
{
struct bt_trace_handle *handle;
+ if (!ctx)
+ return -EINVAL;
+
handle = g_hash_table_lookup(ctx->trace_handles,
(gpointer) (unsigned long) handle_id);
- assert(handle != NULL);
+ if (!handle)
+ return -ENOENT;
/* Remove from containers */
trace_collection_remove(ctx->tc, handle->td);
/* Remove and free the handle */
g_hash_table_remove(ctx->trace_handles,
(gpointer) (unsigned long) handle_id);
-
+ return 0;
}
static
void bt_context_destroy(struct bt_context *ctx)
{
+ assert(ctx);
finalize_trace_collection(ctx->tc);
/* Remote all traces. The g_hash_table_destroy will call
void bt_context_get(struct bt_context *ctx)
{
+ assert(ctx);
ctx->refcount++;
}
void bt_context_put(struct bt_context *ctx)
{
+ assert(ctx);
ctx->refcount--;
if (ctx->refcount == 0)
bt_context_destroy(ctx);
*
* Return 0 if the seek succeded, EOF if we didn't find any packet
* containing the timestamp, or a positive integer for error.
+ *
+ * TODO: this should be turned into a binary search! It is currently
+ * doing a linear search in the packets. This is a O(n) operation on a
+ * very frequent code path.
*/
static int seek_file_stream_by_timestamp(struct ctf_file_stream *cfs,
uint64_t timestamp)
return found ? 0 : EOF;
}
+/*
+ * Find timestamp of last event in the stream.
+ *
+ * Return value: 0 if OK, positive error value on error, EOF if no
+ * events were found.
+ */
+static int find_max_timestamp_ctf_file_stream(struct ctf_file_stream *cfs,
+ uint64_t *timestamp_end)
+{
+ int ret, count = 0, i;
+ uint64_t timestamp = 0;
+ struct ctf_stream_pos *stream_pos;
+
+ stream_pos = &cfs->pos;
+ /*
+ * We start by the last packet, and iterate backwards until we
+ * either find at least one event, or we reach the first packet
+ * (some packets can be empty).
+ */
+ for (i = stream_pos->packet_real_index->len - 1; i >= 0; i--) {
+ stream_pos->packet_seek(&stream_pos->parent, i, SEEK_SET);
+ count = 0;
+ /* read each event until we reach the end of the stream */
+ do {
+ ret = stream_read_event(cfs);
+ if (ret == 0) {
+ count++;
+ timestamp = cfs->parent.real_timestamp;
+ }
+ } while (ret == 0);
+
+ /* Error */
+ if (ret > 0)
+ goto end;
+ assert(ret == EOF);
+ if (count)
+ break;
+ }
+
+ if (count) {
+ *timestamp_end = timestamp;
+ ret = 0;
+ } else {
+ /* Return EOF if no events were found */
+ ret = EOF;
+ }
+end:
+ return ret;
+}
+
+/*
+ * Find the stream within a stream class that contains the event with
+ * the largest timestamp, and save that timestamp.
+ *
+ * Return 0 if OK, EOF if no events were found in the streams, or
+ * positive value on error.
+ */
+static int find_max_timestamp_ctf_stream_class(
+ struct ctf_stream_declaration *stream_class,
+ struct ctf_file_stream **cfsp,
+ uint64_t *max_timestamp)
+{
+ int ret = EOF, i;
+
+ for (i = 0; i < stream_class->streams->len; i++) {
+ struct ctf_stream_definition *stream;
+ struct ctf_file_stream *cfs;
+ uint64_t current_max_ts = 0;
+
+ stream = g_ptr_array_index(stream_class->streams, i);
+ if (!stream)
+ continue;
+ cfs = container_of(stream, struct ctf_file_stream, parent);
+ ret = find_max_timestamp_ctf_file_stream(cfs, ¤t_max_ts);
+ if (ret == EOF)
+ continue;
+ if (ret != 0)
+ break;
+ if (current_max_ts >= *max_timestamp) {
+ *max_timestamp = current_max_ts;
+ *cfsp = cfs;
+ }
+ }
+ assert(ret >= 0 || ret == EOF);
+ return ret;
+}
+
+/*
+ * seek_last_ctf_trace_collection: seek trace collection to last event.
+ *
+ * Return 0 if OK, EOF if no events were found, or positive error value
+ * on error.
+ */
+static int seek_last_ctf_trace_collection(struct trace_collection *tc,
+ struct ctf_file_stream **cfsp)
+{
+ int i, j, ret;
+ int found = 0;
+ uint64_t max_timestamp = 0;
+
+ if (!tc)
+ return 1;
+
+ /* For each trace in the trace_collection */
+ for (i = 0; i < tc->array->len; i++) {
+ struct ctf_trace *tin;
+ struct trace_descriptor *td_read;
+
+ td_read = g_ptr_array_index(tc->array, i);
+ if (!td_read)
+ continue;
+ tin = container_of(td_read, struct ctf_trace, parent);
+ /* For each stream_class in the trace */
+ for (j = 0; j < tin->streams->len; j++) {
+ struct ctf_stream_declaration *stream_class;
+
+ stream_class = g_ptr_array_index(tin->streams, j);
+ if (!stream_class)
+ continue;
+ ret = find_max_timestamp_ctf_stream_class(stream_class,
+ cfsp, &max_timestamp);
+ if (ret > 0)
+ goto end;
+ if (ret == 0)
+ found = 1;
+ assert(ret == EOF || ret == 0);
+ }
+ }
+ /*
+ * Now we know in which file stream the last event is located,
+ * and we know its timestamp.
+ */
+ if (!found) {
+ ret = EOF;
+ } else {
+ ret = seek_file_stream_by_timestamp(*cfsp, max_timestamp);
+ assert(ret == 0);
+ }
+end:
+ return ret;
+}
+
int bt_iter_set_pos(struct bt_iter *iter, const struct bt_iter_pos *iter_pos)
{
struct trace_collection *tc;
int i, ret;
+ if (!iter || !iter_pos)
+ return -EINVAL;
+
switch (iter_pos->type) {
case BT_SEEK_RESTORE:
if (!iter_pos->u.restore)
stream_pos->cur_index,
stream_pos->offset, stream->real_timestamp);
- stream_read_event(saved_pos->file_stream);
+ ret = stream_read_event(saved_pos->file_stream);
+ if (ret != 0) {
+ goto error;
+ }
/* Add to heap */
ret = heap_insert(iter->stream_heap,
}
}
break;
+ case BT_SEEK_LAST:
+ {
+ struct ctf_file_stream *cfs = NULL;
+
+ tc = iter->ctx->tc;
+ ret = seek_last_ctf_trace_collection(tc, &cfs);
+ if (ret != 0 || !cfs)
+ goto error;
+ /* remove all streams from the heap */
+ heap_free(iter->stream_heap);
+ /* Create a new empty heap */
+ ret = heap_init(iter->stream_heap, 0, stream_compare);
+ if (ret < 0)
+ goto error;
+ /* Insert the stream that contains the last event */
+ ret = heap_insert(iter->stream_heap, cfs);
+ if (ret)
+ goto error;
+ break;
+ }
default:
/* not implemented */
return -EINVAL;
struct bt_iter_pos *bt_iter_get_pos(struct bt_iter *iter)
{
struct bt_iter_pos *pos;
- struct trace_collection *tc = iter->ctx->tc;
+ struct trace_collection *tc;
struct ctf_file_stream *file_stream = NULL, *removed;
struct ptr_heap iter_heap_copy;
int ret;
+ if (!iter)
+ return NULL;
+
+ tc = iter->ctx->tc;
pos = g_new0(struct bt_iter_pos, 1);
pos->type = BT_SEEK_RESTORE;
pos->u.restore = g_new0(struct bt_saved_pos, 1);
{
struct bt_iter_pos *pos;
+ if (!iter)
+ return NULL;
+
pos = g_new0(struct bt_iter_pos, 1);
pos->type = BT_SEEK_TIME;
pos->u.seek_time = timestamp;
{
int ret = 0;
+ if (!file_stream || !begin_pos)
+ return -EINVAL;
+
switch (begin_pos->type) {
case BT_SEEK_CUR:
/*
break;
case BT_SEEK_TIME:
case BT_SEEK_RESTORE:
- case BT_SEEK_END:
default:
assert(0); /* Not yet defined */
}
int i, stream_id;
int ret = 0;
+ if (!iter || !ctx)
+ return -EINVAL;
+
if (ctx->current_iterator) {
ret = -1;
goto error_ctx;
struct bt_iter *iter;
int ret;
+ if (!ctx)
+ return NULL;
+
iter = g_new0(struct bt_iter, 1);
ret = bt_iter_init(iter, ctx, begin_pos, end_pos);
if (ret) {
void bt_iter_fini(struct bt_iter *iter)
{
+ assert(iter);
if (iter->stream_heap) {
heap_free(iter->stream_heap);
g_free(iter->stream_heap);
void bt_iter_destroy(struct bt_iter *iter)
{
+ assert(iter);
bt_iter_fini(iter);
g_free(iter);
}
struct ctf_file_stream *file_stream, *removed;
int ret;
+ if (!iter)
+ return -EINVAL;
+
file_stream = heap_maximum(iter->stream_heap);
if (!file_stream) {
/* end of file for all streams */
{
if (!init_done)
return NULL;
+
return g_hash_table_lookup(format_registry,
(gconstpointer) (unsigned long) name);
}
{
struct walk_data data;
+ assert(fp);
+
data.fp = fp;
data.iter = 0;
int bt_register_format(struct format *format)
{
+ if (!format)
+ return -EINVAL;
+
if (!init_done)
format_init();
#include <babeltrace/ctf-text/types.h>
#include <babeltrace/trace-collection.h>
#include <babeltrace/ctf-ir/metadata.h> /* for clocks */
+#include <babeltrace/clock-internal.h>
#include <inttypes.h>
struct clock_match *match = user_data;
struct ctf_clock *clock_a = value, *clock_b;
- if (clock_a->uuid != 0) {
+ if (clock_a->absolute) {
/*
- * Lookup the the trace clocks into the collection
- * clocks.
+ * Absolute time references, such as NTP, are looked up
+ * by clock name.
*/
clock_b = g_hash_table_lookup(match->clocks,
- (gpointer) (unsigned long) clock_a->uuid);
+ (gpointer) (unsigned long) clock_a->name);
if (clock_b) {
match->clock_match = clock_b;
return;
}
- } else if (clock_a->absolute) {
+ } else if (clock_a->uuid != 0) {
/*
- * Absolute time references, such as NTP, are looked up
- * by clock name.
+ * Lookup the the trace clocks into the collection
+ * clocks.
*/
clock_b = g_hash_table_lookup(match->clocks,
- (gpointer) (unsigned long) clock_a->name);
+ (gpointer) (unsigned long) clock_a->uuid);
if (clock_b) {
match->clock_match = clock_b;
return;
}
}
+/*
+ * Note: if using a frequency different from 1GHz for clock->offset, it
+ * is recommended to express the seconds in offset_s, otherwise there
+ * will be a loss of precision caused by the limited size of the double
+ * mantissa.
+ */
+static
+uint64_t clock_offset_ns(struct ctf_clock *clock)
+{
+ return clock->offset_s * 1000000000ULL
+ + clock_cycles_to_ns(clock, clock->offset);
+}
+
static void clock_add(gpointer key, gpointer value, gpointer user_data)
{
struct clock_match *clock_match = user_data;
(gpointer) (unsigned long) v);
if (!tc_clock) {
/*
- * For now, we only support CTF that has one
- * single clock uuid or name (absolute ref).
+ * For now we only support CTF that has one
+ * single clock uuid or name (absolute ref) per
+ * trace.
*/
if (g_hash_table_size(tc_clocks) > 0) {
fprintf(stderr, "[error] Only CTF traces with a single clock description are supported by this babeltrace version.\n");
}
if (!clock_match->tc->offset_nr) {
- clock_match->tc->offset_first =
- (t_clock->offset_s * 1000000000ULL) + t_clock->offset;
+ clock_match->tc->offset_first = clock_offset_ns(t_clock);
clock_match->tc->delta_offset_first_sum = 0;
clock_match->tc->offset_nr++;
clock_match->tc->single_clock_offset_avg =
g_hash_table_insert(tc_clocks,
(gpointer) (unsigned long) v,
value);
- } else {
+ } else if (!t_clock->absolute) {
int64_t diff_ns;
/*
- * Check that the offsets match. If not, warn
- * the user that we do an arbitrary choice.
+ * For non-absolute clocks, check that the
+ * offsets match. If not, warn the user that we
+ * do an arbitrary choice.
*/
- diff_ns = tc_clock->offset_s;
- diff_ns -= t_clock->offset_s;
- diff_ns *= 1000000000ULL;
- diff_ns += tc_clock->offset;
- diff_ns -= t_clock->offset;
+ diff_ns = clock_offset_ns(tc_clock) - clock_offset_ns(t_clock);
printf_debug("Clock \"%s\" offset between traces has a delta of %" PRIu64 " ns.",
g_quark_to_string(tc_clock->name),
diff_ns < 0 ? -diff_ns : diff_ns);
- if (diff_ns > 10000) {
+ if (diff_ns > 10000 || diff_ns < -10000) {
fprintf(stderr, "[warning] Clock \"%s\" offset differs between traces (delta %" PRIu64 " ns). Using average.\n",
g_quark_to_string(tc_clock->name),
diff_ns < 0 ? -diff_ns : diff_ns);
}
/* Compute average */
clock_match->tc->delta_offset_first_sum +=
- (t_clock->offset_s * 1000000000ULL) + t_clock->offset
- - clock_match->tc->offset_first;
+ clock_offset_ns(t_clock) - clock_match->tc->offset_first;
clock_match->tc->offset_nr++;
clock_match->tc->single_clock_offset_avg =
clock_match->tc->offset_first
+ (clock_match->tc->delta_offset_first_sum / clock_match->tc->offset_nr);
+ /* Time need to use offset average */
+ clock_match->tc->clock_use_offset_avg = 1;
}
}
}
int trace_collection_add(struct trace_collection *tc,
struct trace_descriptor *td)
{
- struct ctf_trace *trace = container_of(td, struct ctf_trace, parent);
+ struct ctf_trace *trace;
+ if (!tc || !td)
+ return -EINVAL;
+
+ trace = container_of(td, struct ctf_trace, parent);
g_ptr_array_add(tc->array, td);
trace->collection = tc;
int trace_collection_remove(struct trace_collection *tc,
struct trace_descriptor *td)
{
+ if (!tc || !td)
+ return -EINVAL;
+
if (g_ptr_array_remove(tc->array, td)) {
return 0;
} else {
void init_trace_collection(struct trace_collection *tc)
{
+ assert(tc);
tc->array = g_ptr_array_new();
tc->clocks = g_hash_table_new(g_direct_hash, g_direct_equal);
tc->single_clock_offset_avg = 0;
*/
void finalize_trace_collection(struct trace_collection *tc)
{
+ assert(tc);
g_ptr_array_free(tc->array, TRUE);
g_hash_table_destroy(tc->clocks);
}
{
struct bt_trace_handle *th;
+ if (!ctx)
+ return NULL;
+
th = g_new0(struct bt_trace_handle, 1);
th->id = ctx->last_trace_handle_id++;
return th;
int bt_trace_handle_get_id(struct bt_trace_handle *th)
{
+ if (!th)
+ return -1;
+
return th->id;
}
{
struct bt_trace_handle *handle;
+ if (!ctx)
+ return NULL;
+
handle = g_hash_table_lookup(ctx->trace_handles,
(gpointer) (unsigned long) handle_id);
if (!handle)
struct bt_trace_handle *handle;
uint64_t ret;
+ if (!ctx)
+ return -1ULL;
+
handle = g_hash_table_lookup(ctx->trace_handles,
(gpointer) (unsigned long) handle_id);
if (!handle) {
struct bt_trace_handle *handle;
uint64_t ret;
+ if (!ctx)
+ return -1ULL;
+
handle = g_hash_table_lookup(ctx->trace_handles,
(gpointer) (unsigned long) handle_id);
if (!handle) {