/* * babeltrace_filestream_seek: seek a filestream to given position. * * The stream_id parameter is only useful for BT_SEEK_RESTORE. */ static int babeltrace_filestream_seek(struct ctf_file_stream *file_stream, const struct bt_iter_pos *begin_pos, unsigned long stream_id) { int ret = 0; if (!file_stream || !begin_pos) return -EINVAL; switch (begin_pos->type) { case BT_SEEK_CUR: /* * just insert into the heap we should already know * the timestamps */ break; case BT_SEEK_BEGIN: file_stream->pos.packet_seek(&file_stream->pos.parent, 0, SEEK_SET); ret = stream_read_event(file_stream); break; case BT_SEEK_TIME: case BT_SEEK_RESTORE: default: assert(0); /* Not yet defined */ } return ret; }
/* * seek_file_stream_by_timestamp * * Browse a filestream by index, if an index contains the timestamp passed in * argument, seek inside the corresponding packet it until we find the event we * are looking for (either the exact timestamp or the event just after the * timestamp). * * 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) { struct ctf_stream_pos *stream_pos; struct packet_index *index; int i, ret; stream_pos = &cfs->pos; for (i = 0; i < stream_pos->packet_index->len; i++) { index = &g_array_index(stream_pos->packet_index, struct packet_index, i); if (index->ts_real.timestamp_end < timestamp) continue; stream_pos->packet_seek(&stream_pos->parent, i, SEEK_SET); do { ret = stream_read_event(cfs); } while (cfs->parent.real_timestamp < timestamp && ret == 0); /* Can return either EOF, 0, or error (> 0). */ return ret; } /* * Cannot find the timestamp within the stream packets, return * EOF. */ return EOF; }
int bt_iter_next(struct bt_iter *iter) { struct ctf_file_stream *file_stream, *removed; int ret; bool event_outside_interval = false; if (!iter) return -EINVAL; file_stream = bt_heap_maximum(iter->stream_heap); if (!file_stream) { /* end of file for all streams */ ret = 0; goto end; } ret = stream_read_event(file_stream); if (file_stream->pos.parent.trace && file_stream->pos.parent.trace->interval_set) { event_outside_interval = file_stream->parent.real_timestamp > file_stream->pos.parent.trace->interval_real.timestamp_end; } if (ret == EOF || event_outside_interval) { removed = bt_heap_remove(iter->stream_heap); assert(removed == file_stream); ret = 0; goto end; } else if (ret == EAGAIN) { /* * Live streaming: the stream is inactive for now, we * just updated the timestamp_end to skip over this * stream up to a certain point in time. * * Since we can't guarantee that a stream will ever have * any activity, we can't rely on the fact that * bt_iter_next will be called for each stream and deal * with inactive streams. So instead, we return 0 here * to the caller and let the read API handle the * retry case. */ ret = 0; goto reinsert; } else if (ret) { goto end; } reinsert: /* Reinsert the file stream into the heap, and rebalance. */ removed = bt_heap_replace_max(iter->stream_heap, file_stream); assert(removed == file_stream); end: return ret; }
/* * 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_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; }
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) return -EINVAL; bt_heap_free(iter->stream_heap); ret = bt_heap_init(iter->stream_heap, 0, stream_compare); if (ret < 0) goto error_heap_init; for (i = 0; i < iter_pos->u.restore->stream_saved_pos->len; i++) { struct stream_saved_pos *saved_pos; struct ctf_stream_pos *stream_pos; struct ctf_stream_definition *stream; saved_pos = &g_array_index( iter_pos->u.restore->stream_saved_pos, struct stream_saved_pos, i); stream = &saved_pos->file_stream->parent; stream_pos = &saved_pos->file_stream->pos; stream_pos->packet_seek(&stream_pos->parent, saved_pos->cur_index, SEEK_SET); /* * the timestamp needs to be restored after * packet_seek, because this function resets * the timestamp to the beginning of the packet */ stream->real_timestamp = saved_pos->current_real_timestamp; stream->cycles_timestamp = saved_pos->current_cycles_timestamp; stream_pos->offset = saved_pos->offset; stream_pos->last_offset = LAST_OFFSET_POISON; stream->current.real.begin = 0; stream->current.real.end = 0; stream->current.cycles.begin = 0; stream->current.cycles.end = 0; stream->prev.real.begin = 0; stream->prev.real.end = 0; stream->prev.cycles.begin = 0; stream->prev.cycles.end = 0; printf_debug("restored to cur_index = %" PRId64 " and " "offset = %" PRId64 ", timestamp = %" PRIu64 "\n", stream_pos->cur_index, stream_pos->offset, stream->real_timestamp); ret = stream_read_event(saved_pos->file_stream); if (ret != 0) { goto error; } /* Add to heap */ ret = bt_heap_insert(iter->stream_heap, saved_pos->file_stream); if (ret) goto error; } return 0; case BT_SEEK_TIME: tc = iter->ctx->tc; bt_heap_free(iter->stream_heap); ret = bt_heap_init(iter->stream_heap, 0, stream_compare); if (ret < 0) goto error_heap_init; /* for each trace in the trace_collection */ for (i = 0; i < tc->array->len; i++) { struct ctf_trace *tin; struct bt_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); ret = seek_ctf_trace_by_timestamp(tin, iter_pos->u.seek_time, iter->stream_heap); /* * Positive errors are failure. Negative value * is EOF (for which we continue with other * traces). 0 is success. Note: on EOF, it just * means that no stream has been added to the * iterator for that trace, which is fine. */ if (ret != 0 && ret != EOF) goto error; } return 0; case BT_SEEK_BEGIN: tc = iter->ctx->tc; bt_heap_free(iter->stream_heap); ret = bt_heap_init(iter->stream_heap, 0, stream_compare); if (ret < 0) goto error_heap_init; for (i = 0; i < tc->array->len; i++) { struct ctf_trace *tin; struct bt_trace_descriptor *td_read; int stream_id; td_read = g_ptr_array_index(tc->array, i); if (!td_read) continue; tin = container_of(td_read, struct ctf_trace, parent); /* Populate heap with each stream */ for (stream_id = 0; stream_id < tin->streams->len; stream_id++) { struct ctf_stream_declaration *stream; int filenr; stream = g_ptr_array_index(tin->streams, stream_id); if (!stream) continue; for (filenr = 0; filenr < stream->streams->len; filenr++) { struct ctf_file_stream *file_stream; file_stream = g_ptr_array_index( stream->streams, filenr); if (!file_stream) continue; ret = babeltrace_filestream_seek( file_stream, iter_pos, stream_id); if (ret != 0 && ret != EOF) { goto error; } if (ret == EOF) { /* Do not add EOF streams */ continue; } ret = bt_heap_insert(iter->stream_heap, file_stream); if (ret) goto error; } } } 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 */ bt_heap_free(iter->stream_heap); /* Create a new empty heap */ ret = bt_heap_init(iter->stream_heap, 0, stream_compare); if (ret < 0) goto error; /* Insert the stream that contains the last event */ ret = bt_heap_insert(iter->stream_heap, cfs); if (ret) goto error; break; } default: /* not implemented */ return -EINVAL; } return 0; error: bt_heap_free(iter->stream_heap); error_heap_init: if (bt_heap_init(iter->stream_heap, 0, stream_compare) < 0) { bt_heap_free(iter->stream_heap); g_free(iter->stream_heap); iter->stream_heap = NULL; ret = -ENOMEM; } return ret; }