new file mode 100644
@@ -0,0 +1,171 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_cpu_read_buf, tracefs_cpu_buffered_read_buf, tracefs_cpu_flush_buf
+- Reading trace_pipe_raw data returning a kbuffer
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct kbuffer pass:[*]*tracefs_cpu_read_buf*(struct tracefs_cpu pass:[*]_tcpu_, bool _nonblock_);
+struct kbuffer pass:[*]*tracefs_cpu_buffered_read_buf*(struct tracefs_cpu pass:[*]_tcpu_, bool _nonblock_);
+struct kbuffer pass:[*]*tracefs_cpu_flush_buf*(struct tracefs_cpu pass:[*]_tcpu_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to read the raw data from the trace_pipe_raw
+files in the tracefs file system and return a kbuffer structure to read it with.
+
+The *tracefs_cpu_read_buf()* reads the trace_pipe_raw files associated to _tcpu_
+and returns a kbuffer structure that can be used to iterate the events.
+If _nonblock_ is set, and there's no data available, it will return immediately.
+Otherwise depending on how _tcpu_ was opened, it will block. If _tcpu_ was
+opened with nonblock set, then this _nonblock_ will make no difference.
+
+The *tracefs_cpu_buffered_read_buf()* is basically the same as *tracefs_cpu_read_buf()*
+except that it uses a pipe through splice to buffer reads. This will batch
+reads keeping the reading from the ring buffer less intrusive to the system,
+as just reading all the time can cause quite a disturbance. Note, one
+difference between this and *tracefs_cpu_read()* is that it will read only in
+sub buffer pages. If the ring buffer has not filled a page, then it will not
+return anything, even with _nonblock_ set. Calls to *tracefs_cpu_flush_buf()*
+or *tracefs_cpu_flush()* should be done to read the rest of the file at the
+end of the trace.
+
+The *tracefs_cpu_flush_buf()* reads the trace_pipe_raw file associated by the
+_tcpu_ and puts it into _buffer_, which must be the size of the sub buffer
+which is retrieved. This should be called at the end of tracing
+to get the rest of the data. This call will convert the file descriptor of
+trace_pipe_raw into non-blocking mode.
+
+RETURN VALUE
+------------
+The functions *tracefs_cpu_read_buf()*, tracefs_cpu_buffered_read_buf()* and
+*tracefs_cpu_flush()* returns a kbuffer descriptor that can be iterated
+over to find the events. Note, this descriptor is part of the tracefs_cpu structure
+and should not be freed. It will be freed. It returns NULL on error or if nonblock
+is set and there are no events available. In the case of no events, errno will be
+set with EAGAIN.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <ctype.h>
+#include <tracefs.h>
+
+static void read_page(struct tep_handle *tep, struct kbuffer *kbuf)
+{
+ static struct trace_seq seq;
+ struct tep_record record;
+
+ if (seq.buffer)
+ trace_seq_reset(&seq);
+ else
+ trace_seq_init(&seq);
+
+ while ((record.data = kbuffer_read_event(kbuf, &record.ts))) {
+ record.size = kbuffer_event_size(kbuf);
+ kbuffer_next_event(kbuf, NULL);
+ tep_print_event(tep, &seq, &record,
+ "%s-%d %9d\t%s: %s\n",
+ TEP_PRINT_COMM,
+ TEP_PRINT_PID,
+ TEP_PRINT_TIME,
+ TEP_PRINT_NAME,
+ TEP_PRINT_INFO);
+ trace_seq_do_printf(&seq);
+ trace_seq_reset(&seq);
+ }
+}
+
+int main (int argc, char **argv)
+{
+ struct tracefs_cpu *tcpu;
+ struct tep_handle *tep;
+ struct kbuffer *kbuf;
+ int cpu;
+
+ if (argc < 2 || !isdigit(argv[1][0])) {
+ printf("usage: %s cpu\n\n", argv[0]);
+ exit(-1);
+ }
+
+ cpu = atoi(argv[1]);
+
+ tep = tracefs_local_events(NULL);
+ if (!tep) {
+ perror("Reading trace event formats");
+ exit(-1);
+ }
+
+ tcpu = tracefs_cpu_open(NULL, cpu, 0);
+ if (!tcpu) {
+ perror("Open CPU 0 file");
+ exit(-1);
+ }
+
+ while ((kbuf = tracefs_cpu_buffered_read_buf(tcpu, true))) {
+ read_page(tep, kbuf);
+ }
+
+ kbuf = tracefs_cpu_flush_buf(tcpu);
+ if (kbuf)
+ read_page(tep, kbuf);
+
+ tracefs_cpu_close(tcpu);
+ tep_free(tep);
+
+ return 0;
+}
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+ Header file to include in order to have access to the library APIs.
+*-ltracefs*
+ Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*tracefs_cpu_open*(3)
+*tracefs_cpu_close*(3)
+*tracefs_cpu_read*(3)
+*tracefs_cpu_buffered_read*(3)
+*tracefs_cpu_flush*(3)
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+--
+REPORTING BUGS
+--------------
+Report bugs to <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2022 Google, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
@@ -212,6 +212,9 @@ SEE ALSO
--------
*tracefs_cpu_open*(3)
*tracefs_cpu_close*(3)
+*tracefs_cpu_read_buf*(3)
+*tracefs_cpu_buffered_read_buf*(3)
+*tracefs_cpu_flush_buf*(3)
*libtracefs*(3),
*libtraceevent*(3),
*trace-cmd*(1)
@@ -304,6 +304,9 @@ Recording of trace_pipe_raw files:
int *tracefs_cpu_flush*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_);
int *tracefs_cpu_flush_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_);
int *tracefs_cpu_pipe*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_);
+ struct kbuffer pass:[*]*tracefs_cpu_read_buf*(struct tracefs_cpu pass:[*]_tcpu_, bool _nonblock_);
+ struct kbuffer pass:[*]*tracefs_cpu_buffered_read_buf*(struct tracefs_cpu pass:[*]_tcpu_, bool _nonblock_);
+ struct kbuffer pass:[*]*tracefs_cpu_flush_buf*(struct tracefs_cpu pass:[*]_tcpu_);
Helper functions for guest tracing:
char pass:[*]*tracefs_find_cid_pid*(int _cid_);
@@ -9,6 +9,7 @@
#include <fcntl.h>
#include <sched.h>
#include <event-parse.h>
+#include <kbuffer.h>
char *tracefs_get_tracing_file(const char *name);
void tracefs_put_tracing_file(char *name);
@@ -641,10 +642,13 @@ void tracefs_cpu_close(struct tracefs_cpu *tcpu);
void tracefs_cpu_free_fd(struct tracefs_cpu *tcpu);
int tracefs_cpu_read_size(struct tracefs_cpu *tcpu);
int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock);
+struct kbuffer *tracefs_cpu_read_buf(struct tracefs_cpu *tcpu, bool nonblock);
int tracefs_cpu_buffered_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock);
+struct kbuffer *tracefs_cpu_buffered_read_buf(struct tracefs_cpu *tcpu, bool nonblock);
int tracefs_cpu_write(struct tracefs_cpu *tcpu, int wfd, bool nonblock);
int tracefs_cpu_stop(struct tracefs_cpu *tcpu);
int tracefs_cpu_flush(struct tracefs_cpu *tcpu, void *buffer);
+struct kbuffer *tracefs_cpu_flush_buf(struct tracefs_cpu *tcpu);
int tracefs_cpu_flush_write(struct tracefs_cpu *tcpu, int wfd);
int tracefs_cpu_pipe(struct tracefs_cpu *tcpu, int wfd, bool nonblock);
@@ -23,6 +23,7 @@ EXAMPLES += stream
EXAMPLES += instances-affinity
EXAMPLES += cpu
EXAMPLES += guest
+EXAMPLES += cpu-buf
TARGETS :=
TARGETS += sqlhist
@@ -34,6 +34,8 @@ struct tracefs_cpu {
int subbuf_size;
int buffered;
int splice_read_flags;
+ struct kbuffer *kbuf;
+ void *buffer;
};
/**
@@ -106,6 +108,7 @@ tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock)
{
struct tracefs_cpu *tcpu;
struct tep_handle *tep;
+ struct kbuffer *kbuf;
char path[128];
char *buf;
int mode = O_RDONLY;
@@ -138,6 +141,11 @@ tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock)
goto fail;
subbuf_size = tep_get_sub_buffer_size(tep);
+
+ kbuf = tep_kbuffer(tep);
+ if (!kbuf)
+ goto fail;
+
tep_free(tep);
tep = NULL;
@@ -145,6 +153,8 @@ tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock)
if (!tcpu)
goto fail;
+ tcpu->kbuf = kbuf;
+
return tcpu;
fail:
tep_free(tep);
@@ -173,6 +183,7 @@ void tracefs_cpu_free_fd(struct tracefs_cpu *tcpu)
close_fd(tcpu->splice_pipe[0]);
close_fd(tcpu->splice_pipe[1]);
+ kbuffer_free(tcpu->kbuf);
free(tcpu);
}
@@ -317,6 +328,49 @@ int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock)
return ret;
}
+static bool get_buffer(struct tracefs_cpu *tcpu)
+{
+ if (!tcpu->buffer) {
+ tcpu->buffer = malloc(tcpu->subbuf_size);
+ if (!tcpu->buffer)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * tracefs_cpu_read_buf - read from the raw trace file and return kbuffer
+ * @tcpu: The descriptor representing the raw trace file
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * Reads the trace_pipe_raw files associated to @tcpu and returns a kbuffer
+ * associated with the read that can be used to parse events.
+ *
+ * If @nonblock is set, and there's no data available, it will return
+ * immediately. Otherwise depending on how @tcpu was opened, it will
+ * block. If @tcpu was opened with nonblock set, then this @nonblock
+ * will make no difference.
+ *
+ * Returns a kbuffer associated to the next sub-buffer or NULL on error
+ * or no data to read with nonblock set (EAGAIN will be set).
+ *
+ * The kbuffer returned should not be freed!
+ */
+struct kbuffer *tracefs_cpu_read_buf(struct tracefs_cpu *tcpu, bool nonblock)
+{
+ int ret;
+
+ if (!get_buffer(tcpu))
+ return NULL;
+
+ ret = tracefs_cpu_read(tcpu, tcpu->buffer, nonblock);
+ if (ret <= 0)
+ return NULL;
+
+ kbuffer_load_subbuffer(tcpu->kbuf, tcpu->buffer);
+ return tcpu->kbuf;
+}
+
static int init_splice(struct tracefs_cpu *tcpu)
{
char *buf;
@@ -409,6 +463,42 @@ int tracefs_cpu_buffered_read(struct tracefs_cpu *tcpu, void *buffer, bool nonbl
return ret;
}
+/**
+ * tracefs_cpu_buffered_read_buf - Read the raw trace data buffering through a pipe
+ * @tcpu: The descriptor representing the raw trace file
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * This is basically the same as tracefs_cpu_read() except that it uses
+ * a pipe through splice to buffer reads. This will batch reads keeping
+ * the reading from the ring buffer less intrusive to the system, as
+ * just reading all the time can cause quite a disturbance.
+ *
+ * Note, one difference between this and tracefs_cpu_read() is that it
+ * will read only in sub buffer pages. If the ring buffer has not filled
+ * a page, then it will not return anything, even with @nonblock set.
+ * Calls to tracefs_cpu_flush() should be done to read the rest of
+ * the file at the end of the trace.
+ *
+ * Returns a kbuffer associated to the next sub-buffer or NULL on error
+ * or no data to read with nonblock set (EAGAIN will be set).
+ *
+ * The kbuffer returned should not be freed!
+ */
+struct kbuffer *tracefs_cpu_buffered_read_buf(struct tracefs_cpu *tcpu, bool nonblock)
+{
+ int ret;
+
+ if (!get_buffer(tcpu))
+ return NULL;
+
+ ret = tracefs_cpu_buffered_read(tcpu, tcpu->buffer, nonblock);
+ if (ret <= 0)
+ return NULL;
+
+ kbuffer_load_subbuffer(tcpu->kbuf, tcpu->buffer);
+ return tcpu->kbuf;
+}
+
/**
* tracefs_cpu_stop - Stop a blocked read of the raw tracing file
* @tcpu: The descriptor representing the raw trace file
@@ -492,6 +582,32 @@ int tracefs_cpu_flush(struct tracefs_cpu *tcpu, void *buffer)
return ret;
}
+/**
+ * tracefs_cpu_flush_buf - Finish out and read the rest of the raw tracing file
+ * @tcpu: The descriptor representing the raw trace file
+ *
+ * Reads the trace_pipe_raw file associated by the @tcpu and puts it
+ * into @buffer, which must be the size of the sub buffer which is retrieved.
+ * by tracefs_cpu_read_size(). This should be called at the end of tracing
+ * to get the rest of the data.
+ *
+ * This will set the file descriptor for reading to non-blocking mode.
+ */
+struct kbuffer *tracefs_cpu_flush_buf(struct tracefs_cpu *tcpu)
+{
+ int ret;
+
+ if (!get_buffer(tcpu))
+ return NULL;
+
+ ret = tracefs_cpu_flush(tcpu, tcpu->buffer);
+ if (ret <= 0)
+ return NULL;
+
+ kbuffer_load_subbuffer(tcpu->kbuf, tcpu->buffer);
+ return tcpu->kbuf;
+}
+
/**
* tracefs_cpu_flush_write - Finish out and read the rest of the raw tracing file
* @tcpu: The descriptor representing the raw trace file