+void bt_ctf_hook(void)
+{
+ /*
+ * Dummy function to prevent the linker from discarding this format as
+ * "unused" in static builds.
+ */
+}
+
+static
+int ctf_timestamp_begin(struct bt_trace_descriptor *descriptor,
+ struct bt_trace_handle *handle, enum bt_clock_type type,
+ int64_t *timestamp)
+{
+ struct ctf_trace *tin;
+ int64_t begin = LLONG_MAX;
+ int i, j, ret;
+
+ tin = container_of(descriptor, struct ctf_trace, parent);
+
+ if (!tin || !timestamp) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* for each stream_class */
+ for (i = 0; i < tin->streams->len; i++) {
+ struct ctf_stream_declaration *stream_class;
+
+ stream_class = g_ptr_array_index(tin->streams, i);
+ if (!stream_class)
+ continue;
+ /* for each file_stream */
+ for (j = 0; j < stream_class->streams->len; j++) {
+ struct ctf_stream_definition *stream;
+ struct ctf_file_stream *cfs;
+ struct ctf_stream_pos *stream_pos;
+ struct packet_index *index;
+
+ stream = g_ptr_array_index(stream_class->streams, j);
+ cfs = container_of(stream, struct ctf_file_stream,
+ parent);
+ stream_pos = &cfs->pos;
+
+ if (!stream_pos->packet_index) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (stream_pos->packet_index->len <= 0)
+ continue;
+
+ index = &g_array_index(stream_pos->packet_index,
+ struct packet_index,
+ stream_pos->packet_index->len - 1);
+ if (type == BT_CLOCK_REAL) {
+ if (index->ts_real.timestamp_begin < begin)
+ begin = index->ts_real.timestamp_begin;
+ } else if (type == BT_CLOCK_CYCLES) {
+ if (index->ts_cycles.timestamp_begin < begin)
+ begin = index->ts_cycles.timestamp_begin;
+ } else {
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+ }
+ if (begin == LLONG_MAX) {
+ ret = -ENOENT;
+ goto error;
+ }
+ *timestamp = begin;
+ return 0;
+
+error:
+ return ret;
+}
+
+static
+int ctf_timestamp_end(struct bt_trace_descriptor *descriptor,
+ struct bt_trace_handle *handle, enum bt_clock_type type,
+ int64_t *timestamp)
+{
+ struct ctf_trace *tin;
+ int64_t end = LLONG_MIN;
+ int i, j, ret;
+
+ tin = container_of(descriptor, struct ctf_trace, parent);
+
+ if (!tin || !timestamp) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* for each stream_class */
+ for (i = 0; i < tin->streams->len; i++) {
+ struct ctf_stream_declaration *stream_class;
+
+ stream_class = g_ptr_array_index(tin->streams, i);
+ if (!stream_class)
+ continue;
+ /* for each file_stream */
+ for (j = 0; j < stream_class->streams->len; j++) {
+ struct ctf_stream_definition *stream;
+ struct ctf_file_stream *cfs;
+ struct ctf_stream_pos *stream_pos;
+ struct packet_index *index;
+
+ stream = g_ptr_array_index(stream_class->streams, j);
+ cfs = container_of(stream, struct ctf_file_stream,
+ parent);
+ stream_pos = &cfs->pos;
+
+ if (!stream_pos->packet_index) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (stream_pos->packet_index->len <= 0)
+ continue;
+
+ index = &g_array_index(stream_pos->packet_index,
+ struct packet_index,
+ stream_pos->packet_index->len - 1);
+ if (type == BT_CLOCK_REAL) {
+ if (index->ts_real.timestamp_end > end)
+ end = index->ts_real.timestamp_end;
+ } else if (type == BT_CLOCK_CYCLES) {
+ if (index->ts_cycles.timestamp_end > end)
+ end = index->ts_cycles.timestamp_end;
+ } else {
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+ }
+ if (end == LLONG_MIN) {
+ ret = -ENOENT;
+ goto error;
+ }
+ *timestamp = end;
+ return 0;
+
+error:
+ return ret;
+}
+
+/*
+ * Update stream current timestamp
+ */
+static
+void ctf_update_timestamp(struct ctf_stream_definition *stream,
+ struct definition_integer *integer_definition)
+{
+ struct declaration_integer *integer_declaration =
+ integer_definition->declaration;
+ uint64_t oldval, newval, updateval;
+
+ if (unlikely(integer_declaration->len == 64)) {
+ stream->cycles_timestamp = integer_definition->value._unsigned;
+ stream->real_timestamp = ctf_get_real_timestamp(stream,
+ stream->cycles_timestamp);
+ return;
+ }
+ /* keep low bits */
+ oldval = stream->cycles_timestamp;
+ oldval &= (1ULL << integer_declaration->len) - 1;
+ newval = integer_definition->value._unsigned;
+ /* Test for overflow by comparing low bits */
+ if (newval < oldval)
+ newval += 1ULL << integer_declaration->len;
+ /* updateval contains old high bits, and new low bits (sum) */
+ updateval = stream->cycles_timestamp;
+ updateval &= ~((1ULL << integer_declaration->len) - 1);
+ updateval += newval;
+ stream->cycles_timestamp = updateval;
+
+ /* convert to real timestamp */
+ stream->real_timestamp = ctf_get_real_timestamp(stream,
+ stream->cycles_timestamp);
+}
+
+/*
+ * Print timestamp, rescaling clock frequency to nanoseconds and
+ * applying offsets as needed (unix time).
+ */
+static
+void ctf_print_timestamp_real(FILE *fp,
+ struct ctf_stream_definition *stream,
+ int64_t timestamp)
+{
+ int64_t ts_sec = 0, ts_nsec;
+ uint64_t ts_sec_abs, ts_nsec_abs;
+ bool is_negative;
+
+ ts_nsec = timestamp;
+
+ /* Add command-line offset in ns */
+ ts_nsec += opt_clock_offset_ns;
+
+ /* Add command-line offset */
+ ts_sec += opt_clock_offset;
+
+ ts_sec += ts_nsec / NSEC_PER_SEC;
+ ts_nsec = ts_nsec % NSEC_PER_SEC;
+ if (ts_sec >= 0 && ts_nsec >= 0) {
+ is_negative = false;
+ ts_sec_abs = ts_sec;
+ ts_nsec_abs = ts_nsec;
+ } else if (ts_sec > 0 && ts_nsec < 0) {
+ is_negative = false;
+ ts_sec_abs = ts_sec - 1;
+ ts_nsec_abs = NSEC_PER_SEC + ts_nsec;
+ } else if (ts_sec == 0 && ts_nsec < 0) {
+ is_negative = true;
+ ts_sec_abs = ts_sec;
+ ts_nsec_abs = -ts_nsec;
+ } else if (ts_sec < 0 && ts_nsec > 0) {
+ is_negative = true;
+ ts_sec_abs = -(ts_sec + 1);
+ ts_nsec_abs = NSEC_PER_SEC - ts_nsec;
+ } else if (ts_sec < 0 && ts_nsec == 0) {
+ is_negative = true;
+ ts_sec_abs = -ts_sec;
+ ts_nsec_abs = ts_nsec;
+ } else { /* (ts_sec < 0 && ts_nsec < 0) */
+ is_negative = true;
+ ts_sec_abs = -ts_sec;
+ ts_nsec_abs = -ts_nsec;
+ }
+
+ if (!opt_clock_seconds) {
+ struct tm tm;
+ time_t time_s = (time_t) ts_sec_abs;
+
+ if (is_negative) {
+ fprintf(stderr, "[warning] Fallback to [sec.ns] to print negative time value. Use --clock-seconds.\n");
+ goto seconds;
+ }
+
+ if (!opt_clock_gmt) {
+ struct tm *res;
+
+ res = localtime_r(&time_s, &tm);
+ if (!res) {
+ fprintf(stderr, "[warning] Unable to get localtime.\n");
+ goto seconds;
+ }
+ } else {
+ struct tm *res;
+
+ res = gmtime_r(&time_s, &tm);
+ if (!res) {
+ fprintf(stderr, "[warning] Unable to get gmtime.\n");
+ goto seconds;
+ }
+ }
+ if (opt_clock_date) {
+ char timestr[26];
+ size_t res;
+
+ /* Print date and time */
+ res = strftime(timestr, sizeof(timestr),
+ "%F ", &tm);
+ if (!res) {
+ fprintf(stderr, "[warning] Unable to print ascii time.\n");
+ goto seconds;
+ }
+ fprintf(fp, "%s", timestr);
+ }
+ /* Print time in HH:MM:SS.ns */
+ fprintf(fp, "%02d:%02d:%02d.%09" PRIu64,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts_nsec_abs);
+ goto end;
+ }
+seconds:
+ fprintf(fp, "%s%" PRId64 ".%09" PRIu64,
+ is_negative ? "-" : "", ts_sec_abs, ts_nsec_abs);
+
+end:
+ return;
+}
+
+/*
+ * Print timestamp, in cycles
+ */
+static
+void ctf_print_timestamp_cycles(FILE *fp,
+ struct ctf_stream_definition *stream,
+ uint64_t timestamp)
+{
+ fprintf(fp, "%020" PRIu64, timestamp);
+}
+
+void ctf_print_timestamp(FILE *fp,
+ struct ctf_stream_definition *stream,
+ int64_t timestamp)
+{
+ if (opt_clock_cycles) {
+ ctf_print_timestamp_cycles(fp, stream, timestamp);
+ } else {
+ ctf_print_timestamp_real(fp, stream, timestamp);
+ }
+}
+
+static
+void print_uuid(FILE *fp, unsigned char *uuid)
+{
+ int i;
+
+ for (i = 0; i < BABELTRACE_UUID_LEN; i++)
+ fprintf(fp, "%x", (unsigned int) uuid[i]);
+}
+
+/*
+ * Discarded events can be either:
+ * - discarded after end of previous buffer due to buffer full:
+ * happened within range: [ prev_timestamp_end, timestamp_begin ]
+ * - discarded within current buffer due to either event too large or
+ * nested wrap-around:
+ * happened within range: [ timestamp_begin, timestamp_end ]
+ *
+ * Given we have discarded counters of those two types merged into the
+ * events_discarded counter, we need to use the union of those ranges:
+ * [ prev_timestamp_end, timestamp_end ]
+ *
+ * Lost packets occur if the tracer overwrote some subbuffer(s) before the
+ * consumer had time to extract them. We keep track of those gaps with the
+ * packet sequence number in each packet.
+ */
+static
+void ctf_print_discarded_lost(FILE *fp, struct ctf_stream_definition *stream)
+{
+ if ((!stream->events_discarded && !stream->packets_lost) ||
+ !babeltrace_ctf_console_output) {
+ return;
+ }
+ fflush(stdout);
+ if (stream->events_discarded) {
+ fprintf(fp, "[warning] Tracer discarded %" PRIu64 " events between [",
+ stream->events_discarded);
+ } else if (stream->packets_lost) {
+ fprintf(fp, "[warning] Tracer lost %" PRIu64 " trace packets between [",
+ stream->packets_lost);
+ }
+ if (opt_clock_cycles) {
+ ctf_print_timestamp(fp, stream,
+ stream->prev.cycles.end);
+ fprintf(fp, "] and [");
+ ctf_print_timestamp(fp, stream,
+ stream->current.cycles.end);
+ } else {
+ ctf_print_timestamp(fp, stream,
+ stream->prev.real.end);
+ fprintf(fp, "] and [");
+ ctf_print_timestamp(fp, stream,
+ stream->current.real.end);
+ }
+ fprintf(fp, "] in trace UUID ");
+ print_uuid(fp, stream->stream_class->trace->uuid);
+ if (stream->stream_class->trace->parent.path[0])
+ fprintf(fp, ", at path: \"%s\"",
+ stream->stream_class->trace->parent.path);
+
+ fprintf(fp, ", within stream id %" PRIu64, stream->stream_id);
+ if (stream->path[0])
+ fprintf(fp, ", at relative path: \"%s\"", stream->path);
+ fprintf(fp, ". ");
+ fprintf(fp, "You should consider recording a new trace with larger "
+ "buffers or with fewer events enabled.\n");
+ fflush(fp);
+}
+