#include <stdbool.h>
#include <assert.h>
#include <string.h>
-#include <babeltrace/ctf-ir/field-types.h>
+#include <babeltrace/babeltrace.h>
#include <babeltrace/ctf-ir/field-types-internal.h>
-#include <babeltrace/ctf-ir/field-path.h>
#include <babeltrace/ctf-ir/field-path-internal.h>
-#include <babeltrace/ctf-ir/fields.h>
-#include <babeltrace/ctf-ir/stream-class.h>
-#include <babeltrace/ctf-ir/packet.h>
-#include <babeltrace/ctf-ir/stream.h>
-#include <babeltrace/ctf-ir/clock-class.h>
-#include <babeltrace/ctf-ir/event-class.h>
-#include <babeltrace/graph/notification-packet.h>
-#include <babeltrace/graph/notification-event.h>
-#include <babeltrace/graph/notification-stream.h>
-#include <babeltrace/graph/clock-class-priority-map.h>
-#include <babeltrace/ref.h>
#include <glib.h>
#include <stdlib.h>
/* Current position from addr (bits) */
size_t at;
+
+ /* Position of the last event header from addr (bits) */
+ size_t last_eh_at;
} buf;
/* Binary type reader */
/* Current content size (bits) (-1 if unknown) */
int64_t cur_content_size;
+ /*
+ * Offset, in the underlying media, of the current packet's start
+ * (-1 if unknown).
+ */
+ off_t cur_packet_offset;
+
/* bt_ctf_clock_class to uint64_t. */
GHashTable *clock_states;
/* Restart at the beginning of the new medium buffer */
notit->buf.at = 0;
+ notit->buf.last_eh_at = SIZE_MAX;
/* New medium buffer size */
notit->buf.sz = buffer_sz;
notit->buf.sz, notit->buf.addr);
BT_LOGV_MEM(buffer_addr, buffer_sz, "Returned bytes at %p:",
buffer_addr);
+ } else if (m_status == BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF) {
+ struct bt_ctf_field_type *ph_ft =
+ bt_ctf_trace_get_packet_header_type(notit->meta.trace);
+ struct bt_ctf_field_type *eh_ft = NULL;
+ struct bt_ctf_field_type *sec_ft = NULL;
+ struct bt_ctf_field_type *ec_ft = NULL;
+
+ /*
+ * User returned end of stream: validate that we're not
+ * in the middle of a packet header, packet context, or
+ * event.
+ */
+ if (notit->state == STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN) {
+ /* Beginning of packet: always valid */
+ goto good_state;
+ }
+
+ if (!notit->meta.stream_class) {
+ goto bad_state;
+ }
+
+ if (notit->state == STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN) {
+ /*
+ * Beginning of packet context context is only
+ * valid if there's no packet header.
+ */
+ if (!ph_ft) {
+ goto good_state;
+ }
+ }
+
+ eh_ft = bt_ctf_stream_class_get_event_header_type(
+ notit->meta.stream_class);
+ sec_ft = bt_ctf_stream_class_get_event_context_type(
+ notit->meta.stream_class);
+
+ if (notit->state == STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN) {
+ /*
+ * Beginning of event's header is only valid if
+ * the packet is not supposed to have a specific
+ * size (whole packet sequence has in fact only
+ * one packet).
+ */
+ if (notit->cur_packet_size == -1) {
+ goto good_state;
+ }
+ }
+
+ if (notit->state == STATE_DSCOPE_STREAM_EVENT_CONTEXT_BEGIN) {
+ /*
+ * Beginning of event's stream event context is
+ * only valid if the packet is not supposed to
+ * have a specific size (whole packet sequence
+ * has in fact only one packet), and there's no
+ * event header.
+ */
+ if (notit->cur_packet_size == -1 && !eh_ft) {
+ goto good_state;
+ }
+ }
+
+ if (!notit->meta.event_class) {
+ goto bad_state;
+ }
+
+ ec_ft = bt_ctf_event_class_get_context_type(
+ notit->meta.event_class);
+
+ if (notit->state == STATE_DSCOPE_EVENT_CONTEXT_BEGIN) {
+ /*
+ * Beginning of event's context is only valid if
+ * the packet is not supposed to have a specific
+ * size (whole packet sequence has in fact only
+ * one packet), and there's no event header and
+ * no stream event context.
+ */
+ if (notit->cur_packet_size == -1 && !eh_ft && !sec_ft) {
+ goto good_state;
+ }
+ }
+
+ if (notit->state == STATE_DSCOPE_EVENT_PAYLOAD_BEGIN) {
+ /*
+ * Beginning of event's context is only valid if
+ * the packet is not supposed to have a specific
+ * size (whole packet sequence has in fact only
+ * one packet), and there's no event header, no
+ * stream event context, and no event context.
+ */
+ if (notit->cur_packet_size == -1 && !eh_ft && !sec_ft &&
+ !ec_ft) {
+ goto good_state;
+ }
+ }
+
+bad_state:
+ /* All other states are invalid */
+ BT_LOGW("User function returned %s, but notification iterator is in an unexpected state: "
+ "state=%s",
+ bt_ctf_notif_iter_medium_status_string(m_status),
+ state_string(notit->state));
+ m_status = BT_CTF_NOTIF_ITER_MEDIUM_STATUS_ERROR;
+
+good_state:
+ bt_put(ph_ft);
+ bt_put(eh_ft);
+ bt_put(sec_ft);
+ bt_put(ec_ft);
} else if (m_status < 0) {
BT_LOGW("User function failed: status=%s",
bt_ctf_notif_iter_medium_status_string(m_status));
enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
struct bt_ctf_field *packet_size_field = NULL;
struct bt_ctf_field *content_size_field = NULL;
- uint64_t content_size = -1, packet_size = -1;
+ uint64_t content_size = -1ULL, packet_size = -1ULL;
if (!notit->dscopes.stream_packet_context) {
goto end;
goto end;
}
- notit->cur_packet_size = packet_size;
+ if (packet_size != -1ULL) {
+ notit->cur_packet_size = packet_size;
+ } else {
+ /*
+ * Use the content size as packet size indicator if the
+ * packet size field is missing. This means there is no
+ * padding in this stream.
+ */
+ notit->cur_packet_size = content_size;
+ }
notit->cur_content_size = content_size;
BT_LOGV("Set current packet and content sizes: "
"notit-addr=%p, packet-size=%" PRIu64 ", content-size=%" PRIu64,
enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
struct bt_ctf_field_type *event_header_type = NULL;
+ /* Reset the position of the last event header */
+ notit->buf.last_eh_at = notit->buf.at;
+
/* Check if we have some content left */
if (notit->cur_content_size >= 0) {
if (packet_at(notit) == notit->cur_content_size) {
} else if (packet_at(notit) > notit->cur_content_size) {
/* That's not supposed to happen */
BT_LOGV("Before decoding event header field: cursor is passed the packet's content: "
- "notit-addr=%p, content-size=%zu, "
+ "notit-addr=%p, content-size=%" PRId64 ", "
"cur=%zu", notit, notit->cur_content_size,
packet_at(notit));
status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
if (id_field_type && event_id == -1ULL) {
/* Check "id" field */
struct bt_ctf_field *id_field = NULL;
- int ret = 0;
+ int ret_get_value = 0;
// TODO: optimalize!
id_field = bt_ctf_field_structure_get_field(
}
if (bt_ctf_field_is_integer(id_field)) {
- ret = bt_ctf_field_unsigned_integer_get_value(
+ ret_get_value = bt_ctf_field_unsigned_integer_get_value(
id_field, &event_id);
} else if (bt_ctf_field_is_enumeration(id_field)) {
struct bt_ctf_field *container;
container = bt_ctf_field_enumeration_get_container(
id_field);
assert(container);
- ret = bt_ctf_field_unsigned_integer_get_value(
+ ret_get_value = bt_ctf_field_unsigned_integer_get_value(
container, &event_id);
BT_PUT(container);
}
- assert(ret == 0);
+ assert(ret_get_value == 0);
BT_PUT(id_field);
}
notit->buf.addr = NULL;
notit->buf.sz = 0;
notit->buf.at = 0;
+ notit->buf.last_eh_at = SIZE_MAX;
notit->buf.packet_offset = 0;
notit->state = STATE_INIT;
notit->cur_content_size = -1;
notit->cur_packet_size = -1;
+ notit->cur_packet_offset = -1;
}
static
* iterator refer to the same stream class (the first one).
*/
assert(notit);
- BT_LOGV("Switching packet: notit-addr=%p, cur=%zu",
- notit, notit->buf.at);
+ if (notit->cur_packet_size != -1) {
+ notit->cur_packet_offset += notit->cur_packet_size;
+ }
+ BT_LOGV("Switching packet: notit-addr=%p, cur=%zu, "
+ "packet-offset=%" PRId64, notit, notit->buf.at,
+ notit->cur_packet_offset);
stack_clear(notit->stack);
BT_PUT(notit->meta.event_class);
BT_PUT(notit->packet);
struct bt_clock_class_priority_map *cc_prio_map,
struct bt_notification **notification)
{
- struct bt_ctf_event *event;
+ struct bt_ctf_event *event = NULL;
struct bt_notification *ret = NULL;
+ /* Make sure that the event contains at least one bit of data */
+ if (notit->buf.at == notit->buf.last_eh_at) {
+ BT_LOGE("Cannot create empty event with 0 bits of data: "
+ "notit-addr=%p, packet-cur=%zu",
+ notit, packet_at(notit));
+ goto end;
+ }
+
/* Create event */
event = create_event(notit);
if (!event) {
"data=%p, notit-addr=%p",
trace, bt_ctf_trace_get_name(trace), max_request_sz, data,
notit);
+ notit->cur_packet_offset = 0;
end:
return notit;
struct bt_ctf_field **packet_header_field,
struct bt_ctf_field **packet_context_field)
{
+ int ret;
enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
assert(notit);
*packet_context_field = bt_get(notit->dscopes.stream_packet_context);
}
+ ret = set_current_packet_content_sizes(notit);
+ if (ret) {
+ status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+ goto end;
+ }
end:
return status;
}
assert(notit);
notit->medium.data = medops_data;
}
+
+BT_HIDDEN
+enum bt_ctf_notif_iter_status bt_ctf_notif_iter_seek(
+ struct bt_ctf_notif_iter *notit, off_t offset)
+{
+ enum bt_ctf_notif_iter_status ret = BT_CTF_NOTIF_ITER_STATUS_OK;
+ enum bt_ctf_notif_iter_medium_status medium_status;
+
+ assert(notit);
+ if (offset < 0) {
+ BT_LOGE("Cannot seek to negative offset: offset=%jd", offset);
+ ret = BT_CTF_NOTIF_ITER_STATUS_INVAL;
+ goto end;
+ }
+
+ if (!notit->medium.medops.seek) {
+ ret = BT_CTF_NOTIF_ITER_STATUS_UNSUPPORTED;
+ BT_LOGD("Aborting seek as the iterator's underlying media does not implement seek support.");
+ goto end;
+ }
+
+ medium_status = notit->medium.medops.seek(
+ BT_CTF_NOTIF_ITER_SEEK_WHENCE_SET, offset,
+ notit->medium.data);
+ if (medium_status != BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK) {
+ if (medium_status == BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF) {
+ ret = BT_CTF_NOTIF_ITER_STATUS_EOF;
+ } else {
+ ret = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+ goto end;
+ }
+ }
+
+ bt_ctf_notif_iter_reset(notit);
+ notit->cur_packet_offset = offset;
+end:
+ return ret;
+}
+
+BT_HIDDEN
+off_t bt_ctf_notif_iter_get_current_packet_offset(
+ struct bt_ctf_notif_iter *notit)
+{
+ assert(notit);
+ return notit->cur_packet_offset;
+}
+
+BT_HIDDEN
+off_t bt_ctf_notif_iter_get_current_packet_size(
+ struct bt_ctf_notif_iter *notit)
+{
+ assert(notit);
+ return notit->cur_packet_size;
+}