+/*
+ * Find the union of all active regions in the trace collection's traces.
+ * Returns "real" timestamps.
+ *
+ * Return 0 on success.
+ * Return 1 if no intersections are found.
+ * Return a negative value on error.
+ */
+int ctf_find_tc_stream_packet_intersection_union(struct bt_context *ctx,
+ int64_t *_ts_begin, int64_t *_ts_end)
+{
+ int ret = 0, i;
+ int64_t ts_begin = INT64_MAX, ts_end = INT64_MIN;
+
+ if (!ctx || !ctx->tc || !ctx->tc->array || !_ts_begin || !_ts_end) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ for (i = 0; i < ctx->tc->array->len; i++) {
+ struct bt_trace_descriptor *td_read;
+ struct packet_index_time intersection_real;
+
+ td_read = g_ptr_array_index(ctx->tc->array, i);
+ if (!td_read) {
+ continue;
+ }
+ ret = ctf_find_stream_intersection(td_read, &intersection_real,
+ NULL);
+ if (ret == 1) {
+ /* Empty trace or no stream intersection. */
+ continue;
+ } else if (ret < 0) {
+ goto end;
+ }
+
+ ts_begin = min(intersection_real.timestamp_begin, ts_begin);
+ ts_end = max(intersection_real.timestamp_end, ts_end);
+ }
+
+ if (ts_end < ts_begin) {
+ ret = 1;
+ goto end;
+ }
+ *_ts_begin = ts_begin;
+ *_ts_end = ts_end;
+end:
+ return ret;
+}
+
+int ctf_tc_set_stream_intersection_mode(struct bt_context *ctx)
+{
+ int ret = 0, i;
+
+ if (!ctx || !ctx->tc || !ctx->tc->array) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ for (i = 0; i < ctx->tc->array->len; i++) {
+ struct bt_trace_descriptor *td_read;
+ struct packet_index_time intersection_real;
+
+ td_read = g_ptr_array_index(ctx->tc->array, i);
+ if (!td_read) {
+ continue;
+ }
+
+ ret = ctf_find_stream_intersection(td_read, &intersection_real,
+ NULL);
+ if (ret == 1) {
+ /* Empty trace or no stream intersection. */
+ continue;
+ } else if (ret < 0) {
+ goto end;
+ }
+
+ td_read->interval_real = intersection_real;
+ td_read->interval_set = true;
+ }
+end:
+ return ret;
+}
+
+/*
+ * for SEEK_CUR: go to next packet.
+ * for SEEK_SET: go to packet numer (index).
+ */
+void ctf_packet_seek(struct bt_stream_pos *stream_pos, size_t index, int whence)
+{
+ struct ctf_stream_pos *pos =
+ container_of(stream_pos, struct ctf_stream_pos, parent);
+ struct ctf_file_stream *file_stream =
+ container_of(pos, struct ctf_file_stream, pos);
+ int ret;
+ struct packet_index *packet_index, *prev_index;
+
+ switch (whence) {
+ case SEEK_CUR:
+ case SEEK_SET: /* Fall-through */
+ break; /* OK */
+ default:
+ assert(0);
+ }
+
+ if ((pos->prot & PROT_WRITE) && pos->content_size_loc)
+ *pos->content_size_loc = pos->offset;
+
+ if (pos->base_mma) {
+ /* unmap old base */
+ ret = munmap_align(pos->base_mma);
+ if (ret) {
+ fprintf(stderr, "[error] Unable to unmap old base: %s.\n",
+ strerror(errno));
+ assert(0);
+ }
+ pos->base_mma = NULL;
+ }
+
+ /*
+ * The caller should never ask for ctf_move_pos across packets,
+ * except to get exactly at the beginning of the next packet.
+ */
+ if (pos->prot & PROT_WRITE) {
+ switch (whence) {
+ case SEEK_CUR:
+ /* The writer will add padding */
+ pos->mmap_offset += pos->packet_size / CHAR_BIT;
+ break;
+ case SEEK_SET:
+ assert(index == 0); /* only seek supported for now */
+ pos->cur_index = 0;
+ break;
+ default:
+ assert(0);
+ }
+ pos->content_size = -1U; /* Unknown at this point */
+ pos->packet_size = WRITE_PACKET_LEN;
+ do {
+ ret = bt_posix_fallocate(pos->fd, pos->mmap_offset,
+ pos->packet_size / CHAR_BIT);
+ } while (ret == EINTR);
+ assert(ret == 0);
+ pos->offset = 0;
+ } else {
+ read_next_packet:
+ switch (whence) {
+ case SEEK_CUR:
+ {
+ if (pos->offset == EOF) {
+ return;
+ }
+ assert(pos->cur_index < pos->packet_index->len);
+ /* The reader will expect us to skip padding */
+ ++pos->cur_index;
+ break;
+ }
+ case SEEK_SET:
+ if (index >= pos->packet_index->len) {
+ pos->offset = EOF;
+ return;
+ }
+ pos->cur_index = index;
+ break;
+ default:
+ assert(0);
+ }
+
+ if (pos->cur_index >= pos->packet_index->len) {
+ pos->offset = EOF;
+ return;
+ }
+
+ packet_index = &g_array_index(pos->packet_index,
+ struct packet_index, pos->cur_index);
+ if (pos->cur_index > 0) {
+ prev_index = &g_array_index(pos->packet_index,
+ struct packet_index,
+ pos->cur_index - 1);
+ } else {
+ prev_index = NULL;
+ }
+ ctf_update_current_packet_index(&file_stream->parent,
+ prev_index, packet_index);
+
+ /*
+ * We need to check if we are in trace read or called
+ * from packet indexing. In this last case, the
+ * collection is not there, so we cannot print the
+ * timestamps.
+ */
+ if ((&file_stream->parent)->stream_class->trace->parent.collection) {
+ ctf_print_discarded_lost(stderr, &file_stream->parent);
+ }
+
+ packet_index = &g_array_index(pos->packet_index,
+ struct packet_index,
+ pos->cur_index);
+ file_stream->parent.cycles_timestamp = packet_index->ts_cycles.timestamp_begin;
+
+ file_stream->parent.real_timestamp = packet_index->ts_real.timestamp_begin;
+
+ /* Lookup context/packet size in index */
+ if (packet_index->data_offset == -1) {
+ ret = find_data_offset(pos, file_stream, packet_index);
+ if (ret < 0) {
+ return;
+ }
+ }
+ pos->content_size = packet_index->content_size;
+ pos->packet_size = packet_index->packet_size;
+ pos->mmap_offset = packet_index->offset;
+ pos->data_offset = packet_index->data_offset;
+ if (pos->data_offset < packet_index->content_size) {
+ pos->offset = 0; /* will read headers */
+ } else if (pos->data_offset == packet_index->content_size) {
+ /* empty packet */
+ pos->offset = packet_index->data_offset;
+ whence = SEEK_CUR;
+ goto read_next_packet;
+ } else {