@@ -4,7 +4,7 @@ libtraceevent(3)
NAME
----
kbuffer_read_event, kbuffer_next_event, kbuffer_missed_events, kbuffer_event_size, kbuffer_curr_size,
-kbuffer_curr_offset, kbuffer_curr_index -
+kbuffer_curr_offset, kbuffer_curr_index, kbuffer_read_buffer -
Functions to read through the kbuffer sub buffer.
SYNOPSIS
@@ -21,6 +21,7 @@ int *kbuffer_event_size*(struct kbuffer pass:[*]_kbuf_);
int *kbuffer_curr_size*(struct kbuffer pass:[*]_kbuf_);
int *kbuffer_curr_offset*(struct kbuffer pass:[*]_kbuf_);
int *kbuffer_curr_index*(struct kbuffer pass:[*]_kbuf_);
+int *kbuffer_read_buffer*(struct kbuffer pass:[*]_kbuf_, void pass:[*]_buffer_, int _len_);
--
DESCRIPTION
@@ -64,6 +65,18 @@ The *kbuffer_curr_index()* function returns the index from the beginning of the
portion of the sub-buffer where the current evnet's meta data is located.
The first event will likely be zero, but may not be if there's a timestamp attached to it.
+The *kbuffer_read_buffer()* function will fill the given _buffer_ from the _kbuf_ the same
+way the kernel would do a read system call. That is, if the length _len_ is less than the
+sub buffer size, or the kbuffer current index is non-zero, it will start copying from the
+_kbuf_ current event and create _buffer_ as a new sub buffer (with a timestamp
+and commit header) with that event that was found and including all events after that can
+fit within _len_. The _len_ must include the size of the sub buffer header as well as the
+events to include. That is, _len_ is the allocate size of _buffer_ that can be filled.
+The return from this function is the index of the end of the last event that was added.
+If there are no more events then zero is returned, and if the buffer can not
+copy any events because _len_ was too small, then -1 is returned.
+
+
RETURN VALUE
------------
*kbuffer_read_event()* returns the event that the _kbuf_ descriptor is currently at,
@@ -92,6 +105,10 @@ sub-buffer.
*kbuf_curr_index()* returns the index of the current record from the beginning of the _kbuf_
data section.
+*kbuf_read_buffer()* returns the index of the end of the last event that was filled in
+_buffer_. If there are no more events to copy from _start_ then 0 is returned. If _len_
+is not big enough to hold any events, then -1 is returned.
+
EXAMPLE
-------
[source,c]
@@ -195,6 +195,7 @@ kbuffer parsing:
int *kbuffer_curr_size*(struct kbuffer pass:[*]_kbuf_);
int *kbuffer_curr_offset*(struct kbuffer pass:[*]_kbuf_);
int *kbuffer_curr_index*(struct kbuffer pass:[*]_kbuf_);
+ int *kbuffer_read_buffer*(struct kbuffer pass:[*]_kbuf_, void pass:[*]_buffer_, int _start_, int _len_);
--
DESCRIPTION
@@ -42,6 +42,7 @@ unsigned int kbuffer_ptr_delta(struct kbuffer *kbuf, void *ptr);
void *kbuffer_translate_data(int swap, void *data, unsigned int *size);
void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, unsigned long long *ts);
+int kbuffer_read_buffer(struct kbuffer *kbuf, void *buffer, int len);
int kbuffer_curr_index(struct kbuffer *kbuf);
@@ -86,6 +86,42 @@ static int do_swap(struct kbuffer *kbuf)
ENDIAN_MASK;
}
+static unsigned long long swap_8(unsigned long data)
+{
+ return ((data & 0xffULL) << 56) |
+ ((data & (0xffULL << 8)) << 40) |
+ ((data & (0xffULL << 16)) << 24) |
+ ((data & (0xffULL << 24)) << 8) |
+ ((data & (0xffULL << 32)) >> 8) |
+ ((data & (0xffULL << 40)) >> 24) |
+ ((data & (0xffULL << 48)) >> 40) |
+ ((data & (0xffULL << 56)) >> 56);
+}
+
+static unsigned int swap_4(unsigned int data)
+{
+ return ((data & 0xffULL) << 24) |
+ ((data & (0xffULL << 8)) << 8) |
+ ((data & (0xffULL << 16)) >> 8) |
+ ((data & (0xffULL << 24)) >> 24);
+}
+
+static void write_8(bool do_swap, void *ptr, unsigned long long data)
+{
+ if (do_swap)
+ *(unsigned long long *)ptr = swap_8(data);
+ else
+ *(unsigned long long *)ptr = data;
+}
+
+static void write_4(bool do_swap, void *ptr, unsigned int data)
+{
+ if (do_swap)
+ *(unsigned int *)ptr = swap_4(data);
+ else
+ *(unsigned int *)ptr = data;
+}
+
static unsigned long long __read_8(void *ptr)
{
unsigned long long data = *(unsigned long long *)ptr;
@@ -96,18 +132,8 @@ static unsigned long long __read_8(void *ptr)
static unsigned long long __read_8_sw(void *ptr)
{
unsigned long long data = *(unsigned long long *)ptr;
- unsigned long long swap;
- swap = ((data & 0xffULL) << 56) |
- ((data & (0xffULL << 8)) << 40) |
- ((data & (0xffULL << 16)) << 24) |
- ((data & (0xffULL << 24)) << 8) |
- ((data & (0xffULL << 32)) >> 8) |
- ((data & (0xffULL << 40)) >> 24) |
- ((data & (0xffULL << 48)) >> 40) |
- ((data & (0xffULL << 56)) >> 56);
-
- return swap;
+ return swap_8(data);
}
static unsigned int __read_4(void *ptr)
@@ -120,14 +146,8 @@ static unsigned int __read_4(void *ptr)
static unsigned int __read_4_sw(void *ptr)
{
unsigned int data = *(unsigned int *)ptr;
- unsigned int swap;
- swap = ((data & 0xffULL) << 24) |
- ((data & (0xffULL << 8)) << 8) |
- ((data & (0xffULL << 16)) >> 8) |
- ((data & (0xffULL << 24)) >> 24);
-
- return swap;
+ return swap_4(data);
}
static unsigned long long read_8(struct kbuffer *kbuf, void *ptr)
@@ -295,6 +315,13 @@ static unsigned int ts4host(struct kbuffer *kbuf,
return type_len_ts >> 5;
}
+static void set_curr_to_end(struct kbuffer *kbuf)
+{
+ kbuf->curr = kbuf->size;
+ kbuf->next = kbuf->size;
+ kbuf->index = kbuf->size;
+}
+
/*
* Linux 2.6.30 and earlier (not much ealier) had a different
* ring buffer format. It should be obsolete, but we handle it anyway.
@@ -339,9 +366,7 @@ static unsigned int old_update_pointers(struct kbuffer *kbuf)
case OLD_RINGBUF_TYPE_TIME_STAMP:
/* should never happen! */
- kbuf->curr = kbuf->size;
- kbuf->next = kbuf->size;
- kbuf->index = kbuf->size;
+ set_curr_to_end(kbuf);
return -1;
default:
if (len)
@@ -846,3 +871,108 @@ kbuffer_raw_get(struct kbuffer *kbuf, void *subbuf, struct kbuffer_raw_info *inf
return info;
}
+
+/**
+ * kbuffer_read_buffer - read a buffer like the kernel would perform a read
+ * @kbuf: the kbuffer handle
+ * @buffer: where to write the data into
+ * @len; The length of @buffer
+ *
+ * This will read the saved sub buffer within @kbuf like the systemcall
+ * of read() to the trace_pipe_raw would do. That is, if either @len
+ * can not fit the entire buffer, or if the current index in @kbuf
+ * is non-zero, it will write to @buffer a new subbuffer that could be
+ * loaded into kbuffer_load_subbuffer(). That is, it will write into
+ * @buffer a legitimate sub-buffer with a header and all that has the
+ * proper timestamp and commit fields.
+ *
+ * Returns the index after the last element written.
+ * 0 if nothing was copied.
+ * -1 on error (which includes not having enough space in len to
+ * copy the subbuffer header or any of its content. In otherwords,
+ * do not try again!
+ *
+ * @kbuf current index will be set to the next element to read.
+ */
+int kbuffer_read_buffer(struct kbuffer *kbuf, void *buffer, int len)
+{
+ int subbuf_size = kbuf->start + kbuf->size;
+ unsigned long long ts;
+ unsigned int type_len_ts;
+ bool do_swap = false;
+ int last_next;
+ int save_curr;
+
+ if (!kbuf->curr && len >= subbuf_size) {
+ memcpy(buffer, kbuf->subbuffer, subbuf_size);
+ set_curr_to_end(kbuf);
+ return kbuf->size;
+ }
+
+ /* Are we at the end of the buffer */
+ if (kbuf->curr >= kbuf->size)
+ return 0;
+
+ /* If we can not copy anyting, return -1 */
+ if (len < kbuf->start)
+ return -1;
+
+ /* Check if the first event can fit */
+ if (len < (kbuf->next - kbuf->curr) + kbuf->start)
+ return -1;
+
+ if (kbuf->read_8 == __read_8_sw)
+ do_swap = true;
+
+ /* Have this subbuffer timestamp be the current timestamp */
+ write_8(do_swap, buffer, kbuf->timestamp);
+
+ len -= kbuf->start;
+
+ save_curr = kbuf->curr;
+
+ /* Copy the rest of the buffer if it fits */
+ if (len >= kbuf->size - kbuf->curr) {
+ set_curr_to_end(kbuf);
+ last_next = kbuf->size;
+ } else {
+ /*
+ * The length doesn't hold the rest,
+ * need to find the last that fits
+ */
+
+ /* Due to timestamps, we must save the current next to use */
+ last_next = kbuf->next;
+
+ while (len > kbuf->next - save_curr) {
+ last_next = kbuf->next;
+ if (!kbuffer_next_event(kbuf, &ts))
+ break;
+ }
+ }
+
+ len = last_next - save_curr;
+ /* No event was found? */
+ if (!len)
+ return 0;
+
+ memcpy(buffer + kbuf->start, kbuf->data + save_curr, len);
+
+ /* Zero out the delta, as the sub-buffer has the timestamp */
+ type_len_ts = read_4(kbuf, buffer + kbuf->start);
+
+ if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
+ type_len_ts &= ~(((1 << 27) - 1));
+ else
+ type_len_ts &= ((1 << 5) - 1);
+
+ write_4(do_swap, buffer + kbuf->start, type_len_ts);
+
+ /* Update the size */
+ if (kbuf->read_long == __read_long_8)
+ write_8(do_swap, buffer + 8, len);
+ else
+ write_4(do_swap, buffer + 8, len);
+
+ return last_next;
+}