Upgrade libtracefs to libtracefs-1.6.4 am: eee6e409c7 am: 1da5104322 am: e2afc71443

Original change: https://android-review.googlesource.com/c/platform/external/libtracefs/+/2394014

Change-Id: Ic954afaa5a2045e71b266bc1c6f86b54058bc4a6
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.gitignore b/.gitignore
index 88c9b42..3e72a58 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,5 +11,5 @@
 build_uninstall
 tfs_version.h
 *.o
-*.d
+.*.d
 sqlhist
diff --git a/Documentation/libtracefs-cpu-open.txt b/Documentation/libtracefs-cpu-open.txt
new file mode 100644
index 0000000..c5a900a
--- /dev/null
+++ b/Documentation/libtracefs-cpu-open.txt
@@ -0,0 +1,100 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_cpu_open, tracefs_cpu_close, tracefs_cpu_alloc_fd, tracefs_cpu_free_fd - Opening trace_pipe_raw data for reading
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_cpu pass:[*]*tracefs_cpu_open*(struct tracefs_instance pass:[*]_instance_,
+				     int _cpu_, bool _nonblock_);
+void *tracefs_cpu_close*(struct tracefs_cpu pass:[*]_tcpu_);
+
+struct tracefs_cpu pass:[*]*tracefs_cpu_alloc_fd*(int _fd_, int _subbuf_size_, bool _nonblock_);
+void *tracefs_cpu_free_fd*(struct tracefs_cpu pass:[*]_tcpu_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to open the raw data from the trace_pipe_raw
+files in the tracefs file system in oder to read them with the *tracefs_cpu_read*(3)
+functions.
+
+The *tracefs_cpu_open()* creates a descriptor that can read the tracefs
+trace_pipe_raw file for a given _cpu_ in a given _instance_. If _instance_ is
+NULL than the toplevel trace_pipe_raw file is used.
+
+The *tracefs_cpu_close()* closes all the file descriptors associated to the trace_pipe_raw
+opened by *tracefs_cpu_open()*.
+
+The *tracefs_cpu_alloc_fd()* will create a tracefs_cpu descriptor from an existing
+file descriptor _fd_. This is useful to use when connecting to a socket or pipe where
+the other end is feeding raw tracing data in the same format as the trace_pipe_raw
+file would (like in guest to host tracing). The caller is responsible for determining
+the _subbuf_size_ that will be used to break up the sub-buffers being read by the
+file descriptor. The _nonblock_ is treated the same as the same parameter in
+*tracefs_cpu_open()*.
+
+The *tracefs_cpu_free_fd()* is used to free the descriptor returned by *tracefs_cpu_alloc_fd()*.
+It does all the clean up that *tracefs_cpu_close()* performs, and that could also be
+used to free up the descriptor created by *tracefs_cpu_alloc_fd()* but will also close
+the file descriptor passed in. Note that *tracefs_cpu_free_fd()* should not be used
+on the descriptor returned by *tracefs_cpu_open()* as it will not close the file descriptor
+created by it.
+
+RETURN VALUE
+------------
+The *tracefs_cpu_open()* returns a struct tracefs_cpu descriptor that can be
+used by the other functions or NULL on error.
+
+The *tracefs_cpu_alloc_fd()* returns a struct tracefs_cpu descriptor that can
+be used by the *tracefs_cpu_read*(3) related functions, where the descriptor
+will be reading the passed in _fd_ file descriptor.
+
+EXAMPLE
+-------
+See *tracefs_cpu_read*(3) for an example.
+
+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
+--------
+*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).
diff --git a/Documentation/libtracefs-cpu.txt b/Documentation/libtracefs-cpu.txt
new file mode 100644
index 0000000..d6215d9
--- /dev/null
+++ b/Documentation/libtracefs-cpu.txt
@@ -0,0 +1,240 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_cpu_read_size, tracefs_cpu_read, tracefs_cpu_buffered_read, tracefs_cpu_write,
+tracefs_cpu_stop, tracefs_cpu_flush, tracefs_cpu_flush_write, tracefs_cpu_pipe
+- Reading trace_pipe_raw data
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_cpu_read_size*(struct tracefs_cpu pass:[*]_tcpu_);
+int *tracefs_cpu_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+int *tracefs_cpu_buffered_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+int *tracefs_cpu_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_);
+int *tracefs_cpu_stop*(struct tracefs_cpu pass:[*]_tcpu_);
+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_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to read the raw data from the trace_pipe_raw
+files in the tracefs file system.
+
+The *tracefs_cpu_read_size()* returns the subbuffer size of the trace_pipe_raw. This
+returns the minimum size of the buffer that is passed to the below functions.
+
+The *tracefs_cpu_read()* reads the trace_pipe_raw files associated to _tcpu_ into _buffer_.
+_buffer_ must be at least the size of the sub buffer of the ring buffer,
+which is returned by *tracefs_cpu_read_size()*. 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()* 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.
+
+The *tracefs_cpu_write()* will pipe the data from the trace_pipe_raw
+file associated with _tcpu_ into the _wfd_ file descriptor. If _nonblock_ is set,
+then it will not block on if there's nothing to write. Note, it will only write
+sub buffer size data to _wfd_. Calls to tracefs_cpu_flush_write() are needed to
+write out the rest.
+
+The *tracefs_cpu_stop()* will attempt to unblock a task blocked on _tcpu_ reading it.
+On older kernels, it may not do anything for the pipe reads, as older kernels do not
+wake up tasks waiting on the ring buffer. Returns 0 if it definitely woke up any possible
+waiters, but returns 1 if it is not sure it worked and waiters may need to have a signal
+sent to them.
+
+The *tracefs_cpu_flush()* 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 call will convert the file descriptor of
+trace_pipe_raw into non-blocking mode.
+
+The *tracefs_cpu_flush_write()* same as *trace_cpu_flush()* except it takes a file
+descriptor _wfd_ to flush the data into.
+
+The *tracefs_cpu_pipe()* is similar to *tracefs_cpu_write()* but the _wfd_ file descriptor
+must be a pipe. This call is an optimization of *tracefs_cpu_write()* that uses two calls
+to *splice*(2) in order to connect the trace_pipe_raw file descriptor with the write file
+descriptor. *splice*(2) requires that one of the passed in file descriptors is a pipe.
+If the application wants to pass the data to an existing pipe, there's no reason for
+there to be two *splice*(2) system calls and *tracefs_cpu_pipe()* can simply use a single
+call to _wfd_.
+
+RETURN VALUE
+------------
+The *tracefs_cpu_open()* returns a struct tracefs_cpu descriptor that can be
+used by the other functions or NULL on error.
+
+The *tracefs_cpu_read_size()* returns the minimum size of the buffers to be
+used with *tracefs_cpu_read()*, *tracefs_cpu_buffered_read()* and *tracefs_cpu_flush()*.
+Returns negative on error.
+
+The *tracefs_cpu_read()* returns the number of bytes read, or negative on error.
+
+The *tracefs_cpu_buffered_read()* returns the number of bytes read or negative on error.
+
+The *tracefs_cpu_write()* returns the number of bytes written to the file
+or negative on error.
+
+The *tracefs_cpu_stop()* returns zero if any waiters were guaranteed to be
+woken up from waiting on input, or returns one if this is an older kernel
+that does not supply that guarantee, and a signal may need to be sent to
+any waiters. Returns negative on error.
+
+The *tracefs_cpu_flush()* returns the number of bytes read or negative on error.
+
+The *tracefs_cpu_flush_write()* returns the number of bytes written to the
+file  or negative on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#define _LARGEFILE64_SOURCE
+#include <stdlib.h>
+#include <ctype.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <tracefs.h>
+
+struct thread_data {
+	struct tracefs_cpu	*tcpu;
+	int			done;
+	int			fd;
+};
+
+static void *thread_run(void *arg)
+{
+	struct thread_data *data = arg;
+	struct tracefs_cpu *tcpu = data->tcpu;
+	int fd = data->fd;
+	int ret;
+
+	while (!data->done) {
+		ret = tracefs_cpu_write(tcpu, fd, false);
+		printf("wrote %d\n", ret);
+	}
+	return NULL;
+}
+
+int main (int argc, char **argv)
+{
+	struct tracefs_instance *instance;
+	struct thread_data data;
+	pthread_t thread;
+	char *file;
+	int secs = 10;
+	int cpu;
+	int ret;
+
+	if (argc < 3 || !isdigit(argv[1][0])) {
+		printf("usage: %s cpu file_destination [sleep secs]\n\n", argv[0]);
+		exit(-1);
+	}
+
+	cpu = atoi(argv[1]);
+	file = argv[2];
+
+	if (argc > 3)
+		secs = atoi(argv[3]);
+
+	instance = tracefs_instance_create("cpu_write");
+	if (!instance) {
+		perror("create instance");
+		exit(-1);
+	}
+
+	memset(&data, 0, sizeof(data));
+
+	data.tcpu = tracefs_cpu_open(instance, cpu, 0);
+	if (!data.tcpu) {
+		perror("Open instance");
+		exit(-1);
+	}
+
+	data.fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
+	if (data.fd < 0) {
+		perror(file);
+		exit(-1);
+	}
+
+	pthread_create(&thread, NULL, thread_run, &data);
+
+	sleep(secs);
+
+	data.done = 1;
+	printf("stopping\n");
+	ret = tracefs_cpu_stop(data.tcpu);
+
+	printf("joining %d\n", ret);
+	pthread_join(thread, NULL);
+
+	tracefs_trace_off(instance);
+	do {
+		ret = tracefs_cpu_flush_write(data.tcpu, data.fd);
+		printf("flushed %d\n", ret);
+	} while (ret > 0);
+	tracefs_trace_on(instance);
+
+	tracefs_cpu_close(data.tcpu);
+	close(data.fd);
+
+	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)
+*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).
diff --git a/Documentation/libtracefs-events.txt b/Documentation/libtracefs-events.txt
index f998c79..90c54b8 100644
--- a/Documentation/libtracefs-events.txt
+++ b/Documentation/libtracefs-events.txt
@@ -4,7 +4,7 @@
 NAME
 ----
 tracefs_event_systems, tracefs_system_events, tracefs_event_enable, tracefs_event_disable,
-tracefs_iterate_raw_events, tracefs_iterate_stop - Work with trace systems and events.
+tracefs_event_is_enabled - Work with trace systems and events.
 
 SYNOPSIS
 --------
@@ -12,18 +12,21 @@
 --
 *#include <tracefs.h>*
 
+enum tracefs_event_state {
+	TRACEFS_ERROR = -1,
+	TRACEFS_ALL_DISABLED = 0,
+	TRACEFS_ALL_ENABLED = 1,
+	TRACEFS_SOME_ENABLED = 2,
+};
+
 char pass:[*]pass:[*]*tracefs_event_systems*(const char pass:[*]_tracing_dir_);
 char pass:[*]pass:[*]*tracefs_system_events*(const char pass:[*]_tracing_dir_, const char pass:[*]_system_);
 int *tracefs_event_enable*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_,
 			   const char pass:[*]_event_);
 int *tracefs_event_disable*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_,
 			    const char pass:[*]_event_);
-int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
-				 cpu_set_t pass:[*]_cpus_, int _cpu_size_,
-				 int (pass:[*]_callback_)(struct tep_event pass:[*], struct tep_record pass:[*], int, void pass:[*]),
-				 void pass:[*]_callback_context_);
-void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_);
-
+enum tracefs_enable_state *tracefs_event_is_enabled*(struct tracefs_instance pass:[*]_instance_,
+			 const char pass:[*]_system_, const char pass:[*]_event_);
 --
 
 DESCRIPTION
@@ -61,25 +64,23 @@
 is used. If both _system_ and _event_ are NULL then all events are disabled
 for the given _instance_, and so on.
 
-The *tracefs_iterate_raw_events()* function will read the tracefs raw
-data buffers and call the specified _callback_ function for every event it
-encounters. Events are iterated in sorted order: oldest first. An initialized
-_tep_ handler is required (See *tracefs_local_events*(3)). If _instance_ is
-NULL, then the toplevel tracefs buffer is used, otherwise the buffer for
-the corresponding _instance_ is read. To filter only on a subset of CPUs,
-_cpus_ and _cpu_size_ may be set to only call _callback_ with events that
-occurred on the CPUs specified, otherwise if _cpus_ is NULL then the _callback_
-function will be called for all events, and _cpu_size_ is ignored. The
-_callback_ function will be called with the following parameters: A
-pointer to a struct tep_event that corresponds to the type of event the
-record is; The record representing the event; The CPU that the event
-occurred on; and a pointer to user specified _callback_context_. If the _callback_
-returns non-zero, the iteration stops.
+The *tracefs_event_is_enabled()* returns if an event is enabled, a set of
+events are enabled, a system is enabled, or all events are enabled. If both
+_system_ and _event_ are NULL, then it returns the enable state of all events.
+If _system_ is not NULL and _event_ is NULL, then it will check if all the events
+in all the systems that _system_ and return the enable state of those events.
+If _system_ is NULL and _event_ is not NULL, then it will match all the events
+in all systems that match _event_ and return their enabled state. If both _system_
+and _event_ are not NULL, then it will return the enabled state of all matching
+events. The enabled state is defined as:
 
-Use *tracefs_iterate_stop()* to force a executing *tracefs_iterate_raw_events()*
-to halt. This can be called from either a callback that is called by
-the iterator (even though a return of non-zero will stop it), or from another
-thread.
+*TRACEFS_ERROR* - An error occurred including no event were matched.
+
+*TRACEFS_ALL_DISABLED* - All matching events are disabled.
+
+*TRACEFS_ALL_ENABLED* - All matching events are enabled.
+
+*TRACEFS_SOME_ENABLED* - Some matching events were enabled while others were not.
 
 RETURN VALUE
 ------------
@@ -95,8 +96,8 @@
 are found that match the _system_ and _event_ parameters, then -1 is returned
 and errno is not set.
 
-The *tracefs_iterate_raw_events()* function returns -1 in case of an error or
-0 otherwise.
+The *tracefs_event_is_enabled()* returns the enabled status of the matching events
+or TRACEFS_ERROR on error.
 
 EXAMPLE
 -------
diff --git a/Documentation/libtracefs-files.txt b/Documentation/libtracefs-files.txt
index 13e251d..d22e759 100644
--- a/Documentation/libtracefs-files.txt
+++ b/Documentation/libtracefs-files.txt
@@ -3,8 +3,8 @@
 
 NAME
 ----
-tracefs_get_tracing_file, tracefs_put_tracing_file, tracefs_tracing_dir -
-Find locations of trace directory and files.
+tracefs_get_tracing_file, tracefs_put_tracing_file, tracefs_tracing_dir, tracefs_debug_dir, tracefs_set_tracing_dir,
+tracefs_tracing_dir_is_mounted - Find and set locations of trace directory and files.
 
 SYNOPSIS
 --------
@@ -15,6 +15,9 @@
 char pass:[*]*tracefs_get_tracing_file*(const char pass:[*]_name_);
 void *tracefs_put_tracing_file*(char pass:[*]_name_);
 const char pass:[*]*tracefs_tracing_dir*(void);
+const char pass:[*]*tracefs_debug_dir*(void);
+int *tracefs_set_tracing_dir*(char pass:[*]_tracing_dir_)
+int *tracefs_tracing_dir_is_mounted*(bool _mount_, const char pass:[**]_path_);
 --
 
 DESCRIPTION
@@ -22,6 +25,13 @@
 This set of APIs can be used to find the full path of the trace file
 system mount point and trace files in it.
 
+The *tracefs_set_tracing_dir()* function sets a custom location of the
+system's tracing directory mount point. Usually, the library auto detects
+it using the information from the /proc/mounts file. Use this API only if the
+mount point is not standard and cannot be detected by the library. The _tracing_dir_
+argument can be NULL, in that case the custom location is deleted and the library
+auto detection logic is used.
+
 The *tracefs_get_tracing_file()* function returns the full path of the
 file with given _name_ in the trace file system. The function works
 only with files in the tracefs main directory, it is not trace instance
@@ -38,14 +48,33 @@
 if it is not mounted. On any subsequent call the cached path is returned.
 The return string must _not_ be freed.
 
+The *tracefs_debug_dir()* is similar to *tracefs_tracing_dir()* except
+that it will return where the debugfs file system is mounted. If it
+is not mounted it will try to mount it. The return string must _not_
+be freed.
+
+*tracefs_tracing_dir_is_mounted()* returns 1 if the tracing directory is
+already mounted and 0 if it is not. If _mount_ is true, it will try to
+mount it if it is not, and returns 0 if it succesfully mounted it and -1
+if it did not. If _path_ is set, it will be assigned to the path where it
+mounted it. _path_ is internal and should not be freed.
+
 RETURN VALUE
 ------------
+The *tracefs_set_tracing_dir()* function returns 0 on success, -1 otherwise.
+
 The *tracefs_get_tracing_file()* function returns a string or NULL in case
 of an error. The returned string must be freed with *tracefs_put_tracing_file()*.
 
 The *tracefs_tracing_dir()* function returns a constant string or NULL
 in case of an error. The returned string must _not_ be freed.
 
+The *tracefs_debug_dir()* function returns a constant string or NULL
+in case of an error. The returned string must _not_ be freed.
+
+The *tracefs_tracing_dir_is_mounted()* returns 1 if the tracing directory
+is already mounted, 0 if it is not, and -1 on error.
+
 EXAMPLE
 -------
 [source,c]
diff --git a/Documentation/libtracefs-hist.txt b/Documentation/libtracefs-hist.txt
index 3ac21ce..7503fd0 100644
--- a/Documentation/libtracefs-hist.txt
+++ b/Documentation/libtracefs-hist.txt
@@ -3,8 +3,8 @@
 
 NAME
 ----
-tracefs_hist_alloc, tracefs_hist_alloc_2d, tracefs_hist_alloc_nd, tracefs_hist_free,
-tracefs_hist_add_key, tracefs_hist_add_value - Create and destroy event histograms
+tracefs_hist_alloc, tracefs_hist_alloc_2d, tracefs_hist_alloc_nd, tracefs_hist_alloc_nd_cnt, tracefs_hist_free,
+tracefs_hist_add_key, tracefs_hist_add_key_cnt, tracefs_hist_add_value - Create and destroy event histograms
 
 SYNOPSIS
 --------
@@ -39,10 +39,15 @@
 struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd*(struct tracefs_tep pass:[*] _tep_,
 			const char pass:[*]_system_, const char pass:[*]_event_,
 			struct tracefs_hist_axis pass:[*]_axes_);
+struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd_cnt*(struct tep_handle pass:[*]_tep_,
+			  const char pass:[*]_system_, const char pass:[*]_event_name_,
+			  struct tracefs_hist_axis_cnt pass:[*]_axes_);
 void *tracefs_hist_free*(struct tracefs_hist pass:[*]_hist_);
 
 int *tracefs_hist_add_key*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_key_,
 			 enum tracefs_hist_key_type _type_);
+int *tracefs_hist_add_key_cnt*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_key_,
+			 enum tracefs_hist_key_type _type_, int _cnt_);
 int *tracefs_hist_add_value*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_value_);
 --
 
@@ -78,6 +83,12 @@
 system or group of the event. The _event_ is the event to attach the histogram to.
 The _axes_ is an array of _key_ / _type_ pairs, defining the dimensions of the histogram.
 
+*tracefs_hist_alloc_nd_cnt*() will initialize a histogram descriptor that will be attached to
+the _system_/_event_. This only initializes the descriptor with the given _axes_ keys as primaries.
+This only initializes the descriptor, it does not start the histogram in the kernel.
+The difference between this and *tracefs_hist_alloc_nd()* is that the _axes_ parameter
+is of type *struct tracefs_hist_axis_cnt* and not *struct tracefs_hist_axis*.
+
 *tracefs_hist_free*() frees the _tracefs_hist_ descriptor. Note, it does not stop
 or disable the running histogram if it was started. *tracefs_hist_destroy*() needs
 to be called to do so.
@@ -90,6 +101,14 @@
 histogram descriptor to add the _key_ to. The _type_ is the type of key to add
 (See KEY TYPES below).
 
+The *tracefs_hist_add_key_cnt*() is the same as *tracefs_hist_add_key*() except
+that it allows to add a counter for the given type. Currently, there's only
+the *buckets* type that requires a counter. When adding a key with the buckets
+type, *tracefs_hist_add_key*() is not sufficient, as the *buckets* type requires
+a counter or bucket size. Use *tracefs_hist_add_key_cnt*() when specifing
+a key that is broken up into  buckets, and pass in the size of those buckets
+into _cnt_.
+
 *tracefs_hist_add_value*() will add a value to record. By default, the value is
 simply the "hitcount" of the number of times a instance of the histogram's
 key was hit. The _hist_ is the histogram descriptor to add the value to.
@@ -140,6 +159,7 @@
 [source,c]
 --
 #include <stdlib.h>
+#include <ctype.h>
 #include <unistd.h>
 #include <tracefs.h>
 
@@ -162,14 +182,14 @@
 	}
 }
 
-static int parse_keys(char *keys, struct tracefs_hist_axis **axes)
+static int parse_keys(char *keys, struct tracefs_hist_axis_cnt **axes)
 {
 	char *sav = NULL;
 	char *key;
 	int cnt = 0;
 
 	for (key = strtok_r(keys, ",", &sav); key; key = strtok_r(NULL, ",", &sav)) {
-		struct tracefs_hist_axis *ax;
+		struct tracefs_hist_axis_cnt *ax;
 		char *att;
 
 		ax = realloc(*axes, sizeof(*ax) * (cnt + 2));
@@ -201,7 +221,14 @@
 				ax[cnt].type = TRACEFS_HIST_KEY_LOG;
 			else if (strcmp(att, "usecs") == 0)
 				ax[cnt].type = TRACEFS_HIST_KEY_USECS;
-			else {
+			else if (strncmp(att, "buckets", 7) == 0) {
+				if (att[7] != '=' && !isdigit(att[8])) {
+					fprintf(stderr, "'buckets' key type requires '=<size>'\n");
+					exit(-1);
+				}
+				ax[cnt].type = TRACEFS_HIST_KEY_BUCKETS;
+				ax[cnt].cnt = atoi(&att[8]);
+			} else {
 				fprintf(stderr, "Undefined attribute '%s'\n", att);
 				fprintf(stderr,"  Acceptable attributes:\n");
 				fprintf(stderr,"    hex, sym, sym_offset, syscall, exe, log, usecs\n");
@@ -220,7 +247,7 @@
 	struct tracefs_instance *instance = NULL;
 	struct tracefs_hist *hist;
 	struct tep_handle *tep;
-	struct tracefs_hist_axis *axes = NULL;
+	struct tracefs_hist_axis_cnt *axes = NULL;
 	char *system;
 	char *event;
 	char *sav;
@@ -268,6 +295,17 @@
 		exit(-1);
 	}
 
+	/* buckets require the nd_cnt function */
+	switch (cnt) {
+	case 2:
+		if (axes[1].type == TRACEFS_HIST_KEY_BUCKETS)
+			cnt = -1;
+		/* fall through */
+	case 1:
+		if (axes[0].type == TRACEFS_HIST_KEY_BUCKETS)
+			cnt = -1;
+	}
+
 	/* Show examples of hist1d and hist2d */
 	switch (cnt) {
 	case 1:
@@ -281,7 +319,7 @@
 		break;
 	default:
 		/* Really, 1 and 2 could use this too */
-		hist = tracefs_hist_alloc_nd(tep, system, event, axes);
+		hist = tracefs_hist_alloc_nd_cnt(tep, system, event, axes);
 	}
 	if (!hist) {
 		fprintf(stderr, "Failed hist create\n");
@@ -445,6 +483,7 @@
 	}
 	process_hist(cmd, instance, event, keys, vals, sort, ascend, desc);
 }
+
 --
 
 FILES
diff --git a/Documentation/libtracefs-instances-utils.txt b/Documentation/libtracefs-instances-utils.txt
index a79cc34..bc8c9a7 100644
--- a/Documentation/libtracefs-instances-utils.txt
+++ b/Documentation/libtracefs-instances-utils.txt
@@ -3,9 +3,8 @@
 
 NAME
 ----
-tracefs_instance_get_name, tracefs_instance_get_trace_dir,
-tracefs_instances_walk, tracefs_instance_exists -
-Helper functions for working with tracing instances.
+tracefs_instance_get_name, tracefs_instance_get_trace_dir, tracefs_instances_walk, tracefs_instance_exists,
+tracefs_instance_get_buffer_size, tracefs_instance_set_buffer_size - Helper functions for working with tracing instances.
 
 SYNOPSIS
 --------
@@ -17,7 +16,8 @@
 const char pass:[*]*tracefs_instance_get_trace_dir*(struct tracefs_instance pass:[*]_instance_);
 int *tracefs_instances_walk*(int (pass:[*]_callback_)(const char pass:[*], void pass:[*]), void pass:[*]_context)_;
 bool *tracefs_instance_exists*(const char pass:[*]_name_);
-
+size_t *tracefs_instance_get_buffer_size*(struct tracefs_instance pass:[*]_instance_, int _cpu_);
+int *tracefs_instance_set_buffer_size*(struct tracefs_instance pass:[*]_instance_, size_t _size_, int _cpu_);
 --
 
 DESCRIPTION
@@ -39,6 +39,15 @@
 The *tracefs_instance_exists()* function checks if an instance with the given
 _name_ exists in the system.
 
+The *tracefs_instace_get_buffer_size()* returns the size of the ring buffer. If _cpu_
+is negative, it returns the total size of all the per CPU ring buffers, otherwise
+it returns the size of the per CPU ring buffer for _cpu_.
+
+The *tracefs_instance_set_buffer_size()* function sets the size of the ring buffer.
+If _cpu_ is negative, then it sets all the per CPU ring buffers to _size_ (note
+the total size is the number of CPUs * _size_). If _cpu_ is specified, then it only
+sets the size of the per CPU ring buffer.
+
 RETURN VALUE
 ------------
 The *tracefs_instance_get_name()* returns a string or NULL in case of the top
@@ -53,6 +62,11 @@
 The *tracefs_instance_exists()* returns true if an instance with the given _name_
 exists in the system or false otherwise.
 
+The *tracefs_instance_get_buffer_size()* returns the size of the ring buffer depending on
+the _cpu_ value passed in, or -1 on error.
+
+The *tracefs_instance_set_buffer_size()* returns zero on success and -1 on error.
+
 EXAMPLE
 -------
 [source,c]
diff --git a/Documentation/libtracefs-iterator.txt b/Documentation/libtracefs-iterator.txt
new file mode 100644
index 0000000..b971bd0
--- /dev/null
+++ b/Documentation/libtracefs-iterator.txt
@@ -0,0 +1,229 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_iterate_raw_events, tracefs_iterate_stop, tracefs_follow_event, tracefs_follow_missed_events - Iterate over events in the ring buffer
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
+				 cpu_set_t pass:[*]_cpus_, int _cpu_size_,
+				 int (pass:[*]_callback_)(struct tep_event pass:[*], struct tep_record pass:[*], int, void pass:[*]),
+				 void pass:[*]_callback_context_);
+void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_);
+
+int *tracefs_follow_event*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
+			  const char pass:[*]_system_, const char pass:[*]_event_name_,
+			  int (pass:[*]_callback_)(struct tep_event pass:[*],
+					  struct tep_record pass:[*],
+					  int, void pass:[*]),
+			  void pass:[*]_callback_data_);
+int *tracefs_follow_missed_events*(struct tracefs_instance pass:[*]_instance_,
+			  int (pass:[*]_callback_)(struct tep_event pass:[*],
+					  struct tep_record pass:[*],
+					  int, void pass:[*]),
+			  void pass:[*]_callback_data_);
+--
+
+DESCRIPTION
+-----------
+Trace iterator over raw events.
+
+The *tracefs_iterate_raw_events()* function will read the tracefs raw
+data buffers and call the specified _callback_ function for every event it
+encounters. Events are iterated in sorted order: oldest first. An initialized
+_tep_ handler is required (See *tracefs_local_events*(3)). If _instance_ is
+NULL, then the toplevel tracefs buffer is used, otherwise the buffer for
+the corresponding _instance_ is read. To filter only on a subset of CPUs,
+_cpus_ and _cpu_size_ may be set to only call _callback_ with events that
+occurred on the CPUs specified, otherwise if _cpus_ is NULL then the _callback_
+function will be called for all events, and _cpu_size_ is ignored. The
+_callback_ function will be called with the following parameters: A
+pointer to a struct tep_event that corresponds to the type of event the
+record is; The record representing the event; The CPU that the event
+occurred on; and a pointer to user specified _callback_context_. If the _callback_
+returns non-zero, the iteration stops.
+
+Use *tracefs_iterate_stop()* to force a executing *tracefs_iterate_raw_events()*
+to halt. This can be called from either a callback that is called by
+the iterator (even though a return of non-zero will stop it), or from another
+thread.
+
+The *tracefs_follow_event()* is used with *tracefs_iterate_raw_events()* but
+intead of the callback being called for every event, it is only called for the
+specified _system_ / _event_name_ given to the function. The _callback_ is the
+same as for *tracefs_iterate_raw_events()*, and the passed in _callback_context_
+will be passed to the _callback_ as well. Note, if it returns something other
+than 0, it will stop the loop before the _callback_ of *tracefs_iterate_raw_events()*
+is called.
+
+The *tracefs_follow_missed_events()* will call the _callback_ when missed
+events are detected. It will set the _record_ parameter of the callback to the
+record that came after the missed events and _event_ will be of the type of
+event _record_ is. _cpu_ will be set to the CPU that missed the events, and
+_callback_data_ will be the content that was passed in to the function.
+
+RETURN VALUE
+------------
+The *tracefs_iterate_raw_events()* function returns -1 in case of an error or
+0 otherwise.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <unistd.h>
+#include <tracefs.h>
+#include <stdbool.h>
+#include <signal.h>
+
+struct my_struct {
+	bool		stopped;
+};
+
+#define MAX_COUNT 500000
+static int counter;
+
+static int callback(struct tep_event *event, struct tep_record *record,
+		    int cpu, void *data)
+{
+	struct my_struct *my_data = data;
+	static struct trace_seq seq;
+
+	if (counter++ > MAX_COUNT) {
+		my_data->stopped = true;
+		return 1;
+	}
+
+	if (!seq.buffer)
+		trace_seq_init(&seq);
+
+	tep_print_event(event->tep, &seq, record, "%16s-%-5d [%03d] %6.1000d %s: %s\n",
+			TEP_PRINT_COMM, TEP_PRINT_PID, TEP_PRINT_CPU,
+			TEP_PRINT_TIME, TEP_PRINT_NAME, TEP_PRINT_INFO);
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+	trace_seq_reset(&seq);
+	return 0;
+}
+
+static int sched_callback(struct tep_event *event, struct tep_record *record,
+			  int cpu, void *data)
+{
+	static struct tep_format_field *prev_pid;
+	static struct tep_format_field *next_pid;
+	unsigned long long pid;
+	int this_pid = *(int *)data;
+
+	if (!prev_pid) {
+		prev_pid = tep_find_field(event, "prev_pid");
+		next_pid = tep_find_field(event, "next_pid");
+		if (!prev_pid || !next_pid) {
+			fprintf(stderr, "No pid fields??\n");
+			return -1;
+		}
+	}
+
+	tep_read_number_field(prev_pid, record->data, &pid);
+	if (pid == this_pid)
+		printf("WE ARE LEAVING!\n");
+	tep_read_number_field(next_pid, record->data, &pid);
+	if (pid == this_pid)
+		printf("WE ARE ARRIVING!\n");
+	return 0;
+}
+
+static int missed_callback(struct tep_event *event, struct tep_record *record,
+			   int cpu, void *data)
+{
+	printf("OOPS! cpu %d dropped ", cpu);
+	if (record->missed_events > 0)
+		printf("%lld ", record->missed_events);
+	printf("events\n");
+	return 0;
+}
+
+static struct tracefs_instance *instance;
+static struct my_struct my_data;
+
+static void sig(int s)
+{
+	tracefs_iterate_stop(instance);
+	my_data.stopped = true;
+}
+
+int main (int argc, char **argv, char **env)
+{
+	struct tep_handle *tep;
+	int this_pid = getpid();
+
+	instance = tracefs_instance_create("my-buffer");
+	if (!instance)
+		return -1;
+
+	signal(SIGINT, sig);
+
+	tracefs_event_enable(instance, NULL, NULL);
+	sleep(1);
+	tracefs_event_disable(instance, NULL, NULL);
+	tep = tracefs_local_events(NULL);
+	tep_load_plugins(tep);
+	tracefs_follow_missed_events(instance, missed_callback, NULL);
+	tracefs_follow_event(tep, instance, "sched", "sched_switch", sched_callback, &this_pid);
+	tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, &my_data);
+	tracefs_instance_destroy(instance);
+
+	if (my_data.stopped) {
+		if (counter > MAX_COUNT)
+			printf("Finished max count\n");
+		else
+			printf("Finished via signal\n");
+	}
+
+	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
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+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) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-sql.txt b/Documentation/libtracefs-sql.txt
index ba4271e..6d606db 100644
--- a/Documentation/libtracefs-sql.txt
+++ b/Documentation/libtracefs-sql.txt
@@ -195,7 +195,7 @@
 
 [source,c]
 --
-  select CAST(common_pid AS comm, CAST(id AS syscall) FROM sys_enter
+  select CAST(common_pid AS comm), CAST(id AS syscall) FROM sys_enter
 --
 
 Which produces:
@@ -407,8 +407,13 @@
 			}
 		}
 		tracefs_synth_echo_cmd(&seq, synth);
-		if (execute)
-			tracefs_synth_create(synth);
+		if (execute) {
+			ret = tracefs_synth_create(synth);
+			if (ret < 0) {
+				fprintf(stderr, "%s\n", tracefs_error_last(NULL));
+				exit(-1);
+			}
+		}
 	} else {
 		struct tracefs_instance *instance = NULL;
 		struct tracefs_hist *hist;
@@ -430,8 +435,13 @@
 			}
 		}
 		tracefs_hist_echo_cmd(&seq, instance, hist, 0);
-		if (execute)
-			tracefs_hist_start(instance, hist);
+		if (execute) {
+			ret = tracefs_hist_start(instance, hist);
+			if (ret < 0) {
+				fprintf(stderr, "%s\n", tracefs_error_last(instance));
+				exit(-1);
+			}
+		}
 	}
 
 	tracefs_synth_free(synth);
diff --git a/Documentation/libtracefs-traceon.txt b/Documentation/libtracefs-traceon.txt
index 8f64928..28c79ed 100644
--- a/Documentation/libtracefs-traceon.txt
+++ b/Documentation/libtracefs-traceon.txt
@@ -62,7 +62,7 @@
 	ret = tracefs_trace_is_on(NULL);
 	if (ret == 0) {
 		/* Tracing is disabled in the top instance */
-	} else if (ret == 1) {"
+	} else if (ret == 1) {
 		/* Tracing is enabled in the top instance */
 	} else {
 		/* Error getting tracing state of the top instance */
diff --git a/Documentation/libtracefs-utils.txt b/Documentation/libtracefs-utils.txt
index ddbd675..ab16cc6 100644
--- a/Documentation/libtracefs-utils.txt
+++ b/Documentation/libtracefs-utils.txt
@@ -3,8 +3,8 @@
 
 NAME
 ----
-tracefs_tracers, tracefs_get_clock, tracefs_list_free, tracefs_list_add,
-tracefs_list_size - Helper functions for working with trace file system.
+tracefs_tracers, tracefs_tracer_available, tracefs_get_clock, tracefs_list_free,
+tracefs_list_add, tracefs_list_size - Helper functions for working with trace file system.
 
 SYNOPSIS
 --------
@@ -13,6 +13,7 @@
 *#include <tracefs.h>*
 
 char pass:[*]pass:[*]*tracefs_tracers*(const char pass:[*]_tracing_dir_);
+bool *tracefs_tracer_available*(const char pass:[*]_tracing_dir_, const char pass:[*]_tracer_);
 char pass:[*]*tracefs_get_clock*(struct tracefs_instance pass:[*]_instance_);
 void *tracefs_list_free*(char pass:[*]pass:[*]_list_);
 char pass:[**]*tracefs_list_add*(char **_list_, const char *_string_);
@@ -30,6 +31,9 @@
 of the tracefs directory from another machine. The last entry in the array
 as a NULL pointer. The array must be freed with *tracefs_list_free()* API.
 
+The *tracefs_tracer_available()* returns true if the _tracer_ is available
+in the given _tracing_dir_director_, and false otherwise.
+
 The *tracefs_get_clock()* function returns name of the current trace clock,
 used in the given _instance_. If _instance_ is NULL, the clock of the main
 trace instance is returned. The returned string must be freed with free().
@@ -56,6 +60,9 @@
 array is a NULL pointer. The array must be freed with *tracefs_list_free()* API.
 In case of an error, NULL is returned.
 
+The *tracefs_tracer_available()* returns true if the _tracer_ is available,
+and false otherwise.
+
 The *tracefs_get_clock()* returns string, that must be freed with free(), or NULL
 in case of an error.
 
diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt
index 82cb574..c3f448d 100644
--- a/Documentation/libtracefs.txt
+++ b/Documentation/libtracefs.txt
@@ -15,6 +15,9 @@
 	char pass:[*]*tracefs_get_tracing_file*(const char pass:[*]_name_);
 	void *tracefs_put_tracing_file*(char pass:[*]_name_);
 	const char pass:[*]*tracefs_tracing_dir*(void);
+	const char pass:[*]*tracefs_debug_dir*(void);
+	int *tracefs_set_tracing_dir*(char pass:[*]_tracing_dir_)
+	int *tracefs_tracing_dir_is_mounted*(bool _mount_, const char pass:[**]_path_);
 
 Trace instances:
 	struct tracefs_instance pass:[*]*tracefs_instance_create*(const char pass:[*]_name_);
@@ -43,6 +46,8 @@
 	char pass:[*]*tracefs_instance_get_affinity*(struct tracefs_instance pass:[*]_instance_);
 	int *tracefs_instance_get_affinity_set*(struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_set_, size_t _set_size_);
 	char pass:[*]*tracefs_instance_get_affinity_raw*(struct tracefs_instance pass:[*]_instance_);
+	size_t *tracefs_instance_get_buffer_size*(struct tracefs_instance pass:[*]_instance_, int _cpu_);
+	int *tracefs_instance_set_buffer_size*(struct tracefs_instance pass:[*]_instance_, size_t _size_, int _cpu_);
 
 Trace events:
 	char pass:[*]pass:[*]*tracefs_event_systems*(const char pass:[*]_tracing_dir_);
@@ -51,8 +56,21 @@
                            const char pass:[*]_event_);
 	int *tracefs_event_disable*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_,
                             const char pass:[*]_event_);
+	enum tracefs_enable_state *tracefs_event_is_enabled*(struct tracefs_instance pass:[*]_instance_,
+				 const char pass:[*]_system_, const char pass:[*]_event_);
 	int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_cpus_, int _cpu_size_, int (pass:[*]_callback_)(struct tep_event pass:[*], struct tep_record pass:[*], int, void pass:[*]), void pass:[*]_callback_context_);
 	void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_follow_event*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
+				  const char pass:[*]_system_, const char pass:[*]_event_name_,
+				  int (pass:[*]_callback_)(struct tep_event pass:[*],
+						  struct tep_record pass:[*],
+						  int, void pass:[*]),
+				  void pass:[*]_callback_data_);
+	int *tracefs_follow_missed_events*(struct tracefs_instance pass:[*]_instance_,
+				  int (pass:[*]_callback_)(struct tep_event pass:[*],
+						  struct tep_record pass:[*],
+						  int, void pass:[*]),
+				  void pass:[*]_callback_data_);
 	struct tep_handle pass:[*]*tracefs_local_events*(const char pass:[*]_tracing_dir_);
 	struct tep_handle pass:[*]*tracefs_local_events_system*(const char pass:[*]_tracing_dir_, const char pass:[*] const pass:[*]_sys_names_);
 	int *tracefs_fill_local_events*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_, int pass:[*]_parsing_failures_);
@@ -112,6 +130,7 @@
 
 Ftrace tracers:
 	char pass:[*]pass:[*]*tracefs_tracers*(const char pass:[*]_tracing_dir_);
+	bool *tracefs_tracer_available*(const char pass:[*]_tracing_dir_, const char pass:[*]_tracer_);
 	int *tracefs_tracer_set*(struct tracefs_instance pass:[*]_instance_, enum tracefs_tracers _tracer_);
 	int *tracefs_tracer_set*(struct tracefs_instance pass:[*]_instance_, enum tracefs_tracers _tracer_, const char pass:[*]_name_);
 	int *tracefs_tracer_clear*(struct tracefs_instance pass:[*]_instance_);
@@ -144,11 +163,15 @@
 Even probes (eprobes):
 	struct tracefs_dynevent pass:[*] *tracefs_eprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_target_system_, const char pass:[*]_target_event_, const char pass:[*]_fetchargs_);
 
-Kprobes and Kretprobes:
+Uprobes, Kprobes and Kretprobes:
 	struct tracefs_dynevent pass:[*] *tracefs_kprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_);
 	struct tracefs_dynevent pass:[*] *tracefs_kretprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_, unsigned int _max_);
 	int *tracefs_kprobe_raw*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_);
 	int *tracefs_kretprobe_raw*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_);
+	*tracefs_uprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+		     const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_)
+	*tracefs_uretprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+		     const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_);
 
 Synthetic events:
 	struct tracefs_synth pass:[*]*tracefs_sql*(struct tep_handle pass:[*]_tep_, const char pass:[*]_name_,
@@ -223,9 +246,14 @@
 	struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd*(struct tracefs_tep pass:[*] _tep_,
 				const char pass:[*]_system_, const char pass:[*]_event_,
 				struct tracefs_hist_axis pass:[*]_axes_);
+	struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd_cnt*(struct tep_handle pass:[*]_tep_,
+				  const char pass:[*]_system_, const char pass:[*]_event_name_,
+				  struct tracefs_hist_axis_cnt pass:[*]_axes_);
 	void *tracefs_hist_free*(struct tracefs_hist pass:[*]_hist_);
 	int *tracefs_hist_add_key*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_key_,
 				 enum tracefs_hist_key_type _type_);
+	int *tracefs_hist_add_key_cnt*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_key_,
+				 enum tracefs_hist_key_type _type_, int _cnt_);
 	int *tracefs_hist_add_value*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_value_);
 	int *tracefs_hist_add_sort_key*(struct tracefs_hist pass:[*]_hist_,
 				      const char pass:[*]_sort_key_);
@@ -255,6 +283,21 @@
 	int *tracefs_hist_continue*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
 	int *tracefs_hist_reset*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
 
+Recording of trace_pipe_raw files:
+	struct tracefs_cpu pass:[*]*tracefs_cpu_open*(struct tracefs_instance pass:[*]_instance_,
+					     int _cpu_, bool _nonblock_);
+	struct tracefs_cpu pass:[*]*tracefs_cpu_alloc_fd*(int _fd_, int _subbuf_size_, bool _nonblock_);
+	void *tracefs_cpu_close*(struct tracefs_cpu pass:[*]_tcpu_);
+	void *tracefs_cpu_free_fd*(struct tracefs_cpu pass:[*]_tcpu_);
+	int *tracefs_cpu_read_size*(struct tracefs_cpu pass:[*]_tcpu_);
+	int *tracefs_cpu_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+	int *tracefs_cpu_buffered_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+	int *tracefs_cpu_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_);
+	int *tracefs_cpu_stop*(struct tracefs_cpu pass:[*]_tcpu_);
+	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_);
+
 --
 
 DESCRIPTION
diff --git a/METADATA b/METADATA
index 7f1f1f5..33cda28 100644
--- a/METADATA
+++ b/METADATA
@@ -1,14 +1,19 @@
-name: "libtracefs"
-description:
-    "libtracefs is a library that provides APIs to access to the Linux kernel "
-    "tracefs file system"
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update libtracefs
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
 
+name: "libtracefs"
+description: "libtracefs is a library that provides APIs to access to the Linux kernel tracefs file system"
 third_party {
   url {
     type: GIT
     value: "https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git"
   }
-  version: "libtracefs-1.3.1"
-  last_upgrade_date { year: 2022 month: 5 day: 02 }
+  version: "libtracefs-1.6.4"
   license_type: RESTRICTED
+  last_upgrade_date {
+    year: 2023
+    month: 1
+    day: 18
+  }
 }
diff --git a/Makefile b/Makefile
index 80b8176..61ed976 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: LGPL-2.1
 # libtracefs version
 TFS_VERSION = 1
-TFS_PATCHLEVEL = 4
-TFS_EXTRAVERSION = dev
+TFS_PATCHLEVEL = 6
+TFS_EXTRAVERSION = 4
 TRACEFS_VERSION = $(TFS_VERSION).$(TFS_PATCHLEVEL).$(TFS_EXTRAVERSION)
 
 export TFS_VERSION
@@ -52,12 +52,12 @@
 
 libdir_relative ?= $(libdir_relative_temp)
 prefix ?= /usr/local
-man_dir = $(prefix)/share/man
+man_dir ?= $(prefix)/share/man
 man_dir_SQ = '$(subst ','\'',$(man_dir))'
-libdir = $(prefix)/$(libdir_relative)
+libdir ?= $(prefix)/$(libdir_relative)
 libdir_SQ = '$(subst ','\'',$(libdir))'
 includedir_relative ?= include/tracefs
-includedir = $(prefix)/$(includedir_relative)
+includedir ?= $(prefix)/$(includedir_relative)
 includedir_SQ = '$(subst ','\'',$(includedir))'
 pkgconfig_dir ?= $(word 1,$(shell $(PKG_CONFIG) 		\
 			--variable pc_path pkg-config | tr ":" " "))
@@ -132,7 +132,8 @@
 PKG_CONFIG_SOURCE_FILE = libtracefs.pc
 PKG_CONFIG_FILE := $(addprefix $(obj)/,$(PKG_CONFIG_SOURCE_FILE))
 
-LIBS = $(LIBTRACEEVENT_LIBS) -lpthread
+LPTHREAD ?= -lpthread
+LIBS = $(LIBTRACEEVENT_LIBS) $(LPTHREAD)
 
 export LIBS
 export LIBTRACEFS_STATIC LIBTRACEFS_SHARED
@@ -163,6 +164,9 @@
 # Append required CFLAGS
 override CFLAGS += -D_GNU_SOURCE $(LIBTRACEEVENT_INCLUDES) $(INCLUDES)
 
+# Make sure 32 bit stat() works on large file systems
+override CFLAGS += -D_FILE_OFFSET_BITS=64
+
 all: all_cmd
 
 LIB_TARGET  = libtracefs.a libtracefs.so.$(TRACEFS_VERSION)
@@ -330,7 +334,6 @@
 OBJS += tracefs-events.o
 
 OBJS := $(OBJS:%.o=$(bdir)/%.o)
-DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
 
 all: $(DEFAULT_TARGET)
 
@@ -388,3 +391,7 @@
 	  $(BUILD_PREFIX))
 
 .PHONY: clean
+
+# libtracefs.a and libtracefs.so would concurrently enter the same directory -
+# a recipe for collisions.
+.NOTPARALLEL:
diff --git a/check-manpages.sh b/check-manpages.sh
index d3ec6af..776365c 100755
--- a/check-manpages.sh
+++ b/check-manpages.sh
@@ -15,12 +15,19 @@
 MAIN=libtracefs
 MAIN_FILE=${MAIN}.txt
 
+PROCESSED=""
+
 # Ignore man pages that do not contain functions
 IGNORE="libtracefs-options.txt"
 
 for man in ${MAIN}-*.txt; do
 
-	sed -ne '/^NAME/,/^SYNOP/{/^[a-z]/{s/, *$//;s/,/\n/g;s/ //g;s/-.*$/-/;/-/{s/-//p;q};p}}' $man | while read a; do
+	for a in `sed -ne '/^NAME/,/^SYNOP/{/^[a-z]/{s/, *$//;s/,/\n/g;s/ //g;s/-.*$/-/;/-/{s/-//p;q};p}}' $man`; do
+		if [ "${PROCESSED/:${a} /}" != "${PROCESSED}" ]; then
+			P="${PROCESSED/:${a} */}"
+			echo "Found ${a} in ${man} and in ${P/* /}"
+		fi
+		PROCESSED="${man}:${a} ${PROCESSED}"
 		if [ "${IGNORE/$man/}" != "${IGNORE}" ]; then
 			continue
 		fi
diff --git a/include/tracefs-local.h b/include/tracefs-local.h
index 926fd02..2007d26 100644
--- a/include/tracefs-local.h
+++ b/include/tracefs-local.h
@@ -6,6 +6,8 @@
 #ifndef _TRACE_FS_LOCAL_H
 #define _TRACE_FS_LOCAL_H
 
+#include <pthread.h>
+
 #define __hidden __attribute__((visibility ("hidden")))
 #define __weak __attribute__((weak))
 
@@ -15,13 +17,25 @@
 #define BUILD_BUG_ON(cond)			\
 	do { if (!(1/!(cond))) { } } while (0)
 
+#define HASH_BITS 10
+
 struct tracefs_options_mask {
 	unsigned long long	mask;
 };
 
+struct follow_event {
+	struct tep_event	*event;
+	void			*callback_data;
+	int (*callback)(struct tep_event *,
+			struct tep_record *,
+			int, void *);
+};
+
 struct tracefs_instance {
 	struct tracefs_options_mask	supported_opts;
 	struct tracefs_options_mask	enabled_opts;
+	struct follow_event		*followers;
+	struct follow_event		*missed_followers;
 	char				*trace_dir;
 	char				*name;
 	pthread_mutex_t			lock;
@@ -31,6 +45,8 @@
 	int				ftrace_notrace_fd;
 	int				ftrace_marker_fd;
 	int				ftrace_marker_raw_fd;
+	int				nr_followers;
+	int				nr_missed_followers;
 	bool				pipe_keep_going;
 	bool				iterate_keep_going;
 };
@@ -50,7 +66,7 @@
 
 int str_read_file(const char *file, char **buffer, bool warn);
 char *trace_append_file(const char *dir, const char *name);
-char *trace_find_tracing_dir(void);
+char *trace_find_tracing_dir(bool debugfs);
 
 #ifndef ACCESSPERMS
 #define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) /* 0777 */
@@ -94,7 +110,7 @@
 int synth_add_start_field(struct tracefs_synth *synth,
 			  const char *start_field,
 			  const char *name,
-			  enum tracefs_hist_key_type type);
+			  enum tracefs_hist_key_type type, int cnt);
 
 /* Internal interface for ftrace dynamic events */
 
@@ -120,4 +136,6 @@
 struct tep_event *get_tep_event(struct tep_handle *tep,
 				const char *system, const char *name);
 
+unsigned int quick_hash(const char *str);
+
 #endif /* _TRACE_FS_LOCAL_H */
diff --git a/include/tracefs.h b/include/tracefs.h
index 27954a4..3547b5a 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -15,6 +15,9 @@
 
 /* the returned string must *not* be freed */
 const char *tracefs_tracing_dir(void);
+const char *tracefs_debug_dir(void);
+int tracefs_set_tracing_dir(char *tracing_dir);
+int tracefs_tracing_dir_is_mounted(bool mount, const char **path);
 
 /* ftrace instances */
 struct tracefs_instance;
@@ -53,6 +56,8 @@
 char *tracefs_instance_get_affinity_raw(struct tracefs_instance *instance);
 int tracefs_instance_get_affinity_set(struct tracefs_instance *instance,
 				      cpu_set_t *set, size_t set_size);
+ssize_t tracefs_instance_get_buffer_size(struct tracefs_instance *instance, int cpu);
+int tracefs_instance_set_buffer_size(struct tracefs_instance *instance, size_t size, int cpu);
 char **tracefs_instances(const char *regex);
 
 bool tracefs_instance_exists(const char *name);
@@ -65,8 +70,17 @@
 int tracefs_trace_on_fd(int fd);
 int tracefs_trace_off_fd(int fd);
 
+enum tracefs_enable_state {
+	TRACEFS_ERROR		= -1,
+	TRACEFS_ALL_DISABLED	= 0,
+	TRACEFS_ALL_ENABLED	= 1,
+	TRACEFS_SOME_ENABLED	= 2,
+};
+
 int tracefs_event_enable(struct tracefs_instance *instance, const char *system, const char *event);
 int tracefs_event_disable(struct tracefs_instance *instance, const char *system, const char *event);
+enum tracefs_enable_state tracefs_event_is_enabled(struct tracefs_instance *instance,
+			 const char *system, const char *event);
 
 char *tracefs_error_last(struct tracefs_instance *instance);
 char *tracefs_error_all(struct tracefs_instance *instance);
@@ -76,6 +90,8 @@
 char **tracefs_list_add(char **list, const char *string);
 int tracefs_list_size(char **list);
 
+bool tracefs_tracer_available(const char *tracing_dir, const char *tracer);
+
 /**
  * tracefs_trace_on_get_fd - Get a file descriptor of "tracing_on" in given instance
  * @instance: ftrace instance, can be NULL for the top instance
@@ -110,6 +126,17 @@
 						int, void *),
 				void *callback_context);
 void tracefs_iterate_stop(struct tracefs_instance *instance);
+int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instance,
+			  const char *system, const char *event_name,
+			  int (*callback)(struct tep_event *,
+					  struct tep_record *,
+					  int, void *),
+			  void *callback_data);
+int tracefs_follow_missed_events(struct tracefs_instance *instance,
+				 int (*callback)(struct tep_event *,
+						 struct tep_record *,
+						 int, void *),
+				 void *callback_data);
 
 char *tracefs_event_get_file(struct tracefs_instance *instance,
 			     const char *system, const char *event,
@@ -306,6 +333,7 @@
 	TRACEFS_HIST_KEY_EXECNAME,
 	TRACEFS_HIST_KEY_LOG,
 	TRACEFS_HIST_KEY_USECS,
+	TRACEFS_HIST_KEY_BUCKETS,
 	TRACEFS_HIST_KEY_MAX
 };
 
@@ -368,15 +396,27 @@
 	enum tracefs_hist_key_type type;
 };
 
+struct tracefs_hist_axis_cnt {
+	const char *key;
+	enum tracefs_hist_key_type type;
+	int cnt;
+};
+
 struct tracefs_hist *
 tracefs_hist_alloc_nd(struct tep_handle *tep,
 		      const char *system, const char *event_name,
 		      struct tracefs_hist_axis *axes);
+struct tracefs_hist *
+tracefs_hist_alloc_nd_cnt(struct tep_handle *tep,
+			  const char *system, const char *event_name,
+			  struct tracefs_hist_axis_cnt *axes);
 const char *tracefs_hist_get_name(struct tracefs_hist *hist);
 const char *tracefs_hist_get_event(struct tracefs_hist *hist);
 const char *tracefs_hist_get_system(struct tracefs_hist *hist);
 int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
 			 enum tracefs_hist_key_type type);
+int tracefs_hist_add_key_cnt(struct tracefs_hist *hist, const char *key,
+			 enum tracefs_hist_key_type type, int cnt);
 int tracefs_hist_add_value(struct tracefs_hist *hist, const char *value);
 int tracefs_hist_add_sort_key(struct tracefs_hist *hist,
 			      const char *sort_key);
@@ -578,4 +618,20 @@
 struct tep_event *
 tracefs_synth_get_event(struct tep_handle *tep, struct tracefs_synth *synth);
 
+struct tracefs_cpu;
+
+struct tracefs_cpu *tracefs_cpu_alloc_fd(int fd, int subbuf_size, bool nonblock);
+struct tracefs_cpu *tracefs_cpu_open(struct tracefs_instance *instance,
+				     int cpu, bool nonblock);
+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);
+int tracefs_cpu_buffered_read(struct tracefs_cpu *tcpu, void *buffer, 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);
+int tracefs_cpu_flush_write(struct tracefs_cpu *tcpu, int wfd);
+int tracefs_cpu_pipe(struct tracefs_cpu *tcpu, int wfd, bool nonblock);
+
 #endif /* _TRACE_FS_H */
diff --git a/samples/Makefile b/samples/Makefile
index 97a570d..743bddb 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -21,6 +21,7 @@
 EXAMPLES += tracer
 EXAMPLES += stream
 EXAMPLES += instances-affinity
+EXAMPLES += cpu
 
 TARGETS :=
 TARGETS += sqlhist
@@ -46,7 +47,7 @@
 $(TARGETS): $(sdir)
 
 # sqlhist is unique and stands on its own
-$(sdir)/sqlhist: $(bdir)/sqlhist.c
+$(sdir)/sqlhist: $(bdir)/sqlhist.c $(LIBTRACEFS_STATIC)
 	$(call do_sample_build,$@,$<)
 
 $(sdir)/%: $(bdir)/%.o
@@ -65,5 +66,10 @@
 $(bdir)/%.o: $(bdir)/%.c
 	$(call do_sample_obj,$@,$^)
 
+$(bdir)/XX.o: $(bdir)/hist.c
+	$(CC) -g -Wall $(CFLAGS) -c -o $@ $^ -I../include/ $(LIBTRACEEVENT_INCLUDES)
+
 clean:
 	$(Q)$(call do_clean,$(sdir)/* $(bdir)/sqlhist.c $(bdir)/sqlhist.o)
+
+.PHONY: sqlhist
diff --git a/scripts/utils.mk b/scripts/utils.mk
index 5f43de1..4d0f8bc 100644
--- a/scripts/utils.mk
+++ b/scripts/utils.mk
@@ -53,12 +53,12 @@
 
 do_fpic_compile =					\
 	($(print_fpic_compile)				\
-	$(CC) -c $(CPPFLAGS) $(CFLAGS) $(EXT) -fPIC $< -o $@)
+	$(CC) -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ -MP -c $(CPPFLAGS) $(CFLAGS) $(EXT) -fPIC $< -o $@)
 
 do_compile =							\
 	($(if $(GENERATE_PIC), $(do_fpic_compile),		\
 	 $(print_compile)					\
-	 $(CC) -c $(CPPFLAGS) $(CFLAGS) $(EXT) $< -o $@))
+	 $(CC) -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ -MP -c $(CPPFLAGS) $(CFLAGS) $(EXT) $< -o $@))
 
 do_app_build =						\
 	($(print_app_build)				\
@@ -101,7 +101,7 @@
 
 do_sample_build =							\
 	$(Q)($(print_sample_build)					\
-	$(CC) -o $1 $2 $(CFLAGS) $(LIBTRACEFS_STATIC) $(LIBTRACEEVENT_LIBS))
+	$(CC) -o $1 $2 $(CFLAGS) $(LIBTRACEFS_STATIC) $(LIBTRACEEVENT_LIBS) -lpthread)
 
 do_sample_obj =									\
 	$(Q)($(print_sample_obj)						\
diff --git a/src/Makefile b/src/Makefile
index 645d518..e2965bc 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -14,6 +14,7 @@
 OBJS += tracefs-dynevents.o
 OBJS += tracefs-eprobes.o
 OBJS += tracefs-uprobes.o
+OBJS += tracefs-record.o
 
 # Order matters for the the three below
 OBJS += sqlhist-lex.o
@@ -21,7 +22,6 @@
 OBJS += tracefs-sqlhist.o
 
 OBJS := $(OBJS:%.o=$(bdir)/%.o)
-DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
 
 $(LIBTRACEFS_STATIC): $(OBJS)
 	$(Q)$(call do_build_static_lib)
@@ -48,24 +48,14 @@
 $(bdir)/%.o: %.c
 	$(Q)$(call do_fpic_compile)
 
-$(DEPS): $(bdir)/.%.d: %.c
-	$(Q)$(CC) -M -MT $(bdir)/$*.o $(CPPFLAGS) $(CFLAGS) $< > $@
-
-$(OBJS): $(bdir)/%.o : $(bdir)/.%.d
-
 tracefs-sqlhist.o: sqlhist.tab.h
 
 $(OBJS): | $(bdir)
-$(DEPS): | $(bdir)
 
 clean:
 	$(Q)$(call do_clean,$(OBJS) .*.d)
 
-dep_includes := $(wildcard $(DEPS))
-
-ifneq ($(dep_includes),)
-  include $(dep_includes)
-endif
+-include .*.d
 
 $(bdir)/tracefs-sqlhist.o tracefs-sqlhist.o: sqlhist.tab.h
 
diff --git a/src/sqlhist-parse.h b/src/sqlhist-parse.h
index d5b86ca..7bd2a63 100644
--- a/src/sqlhist-parse.h
+++ b/src/sqlhist-parse.h
@@ -4,8 +4,9 @@
 #include <stdarg.h>
 #include <tracefs.h>
 
+#include <tracefs-local.h>
+
 struct str_hash;
-#define HASH_BITS 10
 
 struct sql_table;
 
diff --git a/src/tracefs-dynevents.c b/src/tracefs-dynevents.c
index 48bb26a..7a3c45c 100644
--- a/src/tracefs-dynevents.c
+++ b/src/tracefs-dynevents.c
@@ -713,9 +713,6 @@
 			&dynevent->address, &dynevent->format };
 	int i;
 
-	if (!dynevent)
-		return TRACEFS_DYNEVENT_UNKNOWN;
-
 	for (i = 0; i < ARRAY_SIZE(lv); i++) {
 		if (lv[i]) {
 			if (*rv[i]) {
diff --git a/src/tracefs-events.c b/src/tracefs-events.c
index 65d5707..c2adf41 100644
--- a/src/tracefs-events.c
+++ b/src/tracefs-events.c
@@ -20,15 +20,20 @@
 #include "tracefs.h"
 #include "tracefs-local.h"
 
+static struct follow_event *root_followers;
+static int nr_root_followers;
+
+static struct follow_event *root_missed_followers;
+static int nr_root_missed_followers;
+
 struct cpu_iterate {
+	struct tracefs_cpu *tcpu;
 	struct tep_record record;
 	struct tep_event *event;
 	struct kbuffer *kbuf;
 	void *page;
 	int psize;
-	int rsize;
 	int cpu;
-	int fd;
 };
 
 static int read_kbuf_record(struct cpu_iterate *cpu)
@@ -46,6 +51,7 @@
 	cpu->record.ts = ts;
 	cpu->record.size = kbuffer_event_size(cpu->kbuf);
 	cpu->record.record_size = kbuffer_curr_size(cpu->kbuf);
+	cpu->record.missed_events = kbuffer_missed_events(cpu->kbuf);
 	cpu->record.cpu = cpu->cpu;
 	cpu->record.data = ptr;
 	cpu->record.ref_count = 1;
@@ -59,9 +65,21 @@
 {
 	enum kbuffer_long_size long_size;
 	enum kbuffer_endian endian;
+	int r;
 
-	cpu->rsize = read(cpu->fd, cpu->page, cpu->psize);
-	if (cpu->rsize <= 0)
+	if (!cpu->tcpu)
+		return -1;
+
+	r = tracefs_cpu_buffered_read(cpu->tcpu, cpu->page, true);
+	/*
+	 * tracefs_cpu_buffered_read() only reads in full subbuffer size,
+	 * but this wants partial buffers as well. If the function returns
+	 * empty (-1 for EAGAIN), try tracefs_cpu_read() next, as that can
+	 * read partially filled buffers too, but isn't as efficient.
+	 */
+	if (r <= 0)
+		r = tracefs_cpu_read(cpu->tcpu, cpu->page, true);
+	if (r <= 0)
 		return -1;
 
 	if (!cpu->kbuf) {
@@ -81,8 +99,8 @@
 	}
 
 	kbuffer_load_subbuffer(cpu->kbuf, cpu->page);
-	if (kbuffer_subbuffer_size(cpu->kbuf) > cpu->rsize) {
-		tracefs_warning("%s: page_size > %d", __func__, cpu->rsize);
+	if (kbuffer_subbuffer_size(cpu->kbuf) > r) {
+		tracefs_warning("%s: page_size > %d", __func__, r);
 		return -1;
 	}
 
@@ -105,7 +123,122 @@
 	return -1;
 }
 
-static int read_cpu_pages(struct tep_handle *tep, struct cpu_iterate *cpus, int count,
+/**
+ * tracefs_follow_missed_events - Add callback for missed events for iterators
+ * @instance: The instance to follow
+ * @callback: The function to call when missed events is detected
+ * @callback_data: The data to pass to @callback
+ *
+ * This attaches a callback to an @instance or the root instance if @instance
+ * is NULL, where if tracefs_iterate_raw_events() is called, that if missed
+ * events are detected, it will call @callback, with the following parameters:
+ *  @event: The event pointer of the record with the missing events
+ *  @record; The event instance of @event.
+ *  @cpu: The cpu that the event happened on.
+ *  @callback_data: The same as @callback_data passed to the function.
+ *
+ * If the count of missing events is available, @record->missed_events
+ * will have a positive number holding the number of missed events since
+ * the last event on the same CPU, or just -1 if that number is unknown
+ * but missed events did happen.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int tracefs_follow_missed_events(struct tracefs_instance *instance,
+				 int (*callback)(struct tep_event *,
+						 struct tep_record *,
+						 int, void *),
+				 void *callback_data)
+{
+	struct follow_event **followers;
+	struct follow_event *follower;
+	struct follow_event follow;
+	int *nr_followers;
+
+	follow.event = NULL;
+	follow.callback = callback;
+	follow.callback_data = callback_data;
+
+	if (instance) {
+		followers = &instance->missed_followers;
+		nr_followers = &instance->nr_missed_followers;
+	} else {
+		followers = &root_missed_followers;
+		nr_followers = &nr_root_missed_followers;
+	}
+	follower = realloc(*followers, sizeof(*follower) *
+			    ((*nr_followers) + 1));
+	if (!follower)
+		return -1;
+
+	*followers = follower;
+	follower[(*nr_followers)++] = follow;
+
+	return 0;
+}
+
+static int call_missed_events(struct tracefs_instance *instance,
+			      struct tep_event *event, struct tep_record *record, int cpu)
+{
+	struct follow_event *followers;
+	int nr_followers;
+	int ret = 0;
+	int i;
+
+	if (instance) {
+		followers = instance->missed_followers;
+		nr_followers = instance->nr_missed_followers;
+	} else {
+		followers = root_missed_followers;
+		nr_followers = nr_root_missed_followers;
+	}
+
+	if (!followers)
+		return 0;
+
+	for (i = 0; i < nr_followers; i++) {
+		ret |= followers[i].callback(event, record,
+					     cpu, followers[i].callback_data);
+	}
+
+	return ret;
+}
+
+static int call_followers(struct tracefs_instance *instance,
+			  struct tep_event *event, struct tep_record *record, int cpu)
+{
+	struct follow_event *followers;
+	int nr_followers;
+	int ret = 0;
+	int i;
+
+	if (record->missed_events)
+		ret = call_missed_events(instance, event, record, cpu);
+	if (ret)
+		return ret;
+
+	if (instance) {
+		followers = instance->followers;
+		nr_followers = instance->nr_followers;
+	} else {
+		followers = root_followers;
+		nr_followers = nr_root_followers;
+	}
+
+	if (!followers)
+		return 0;
+
+	for (i = 0; i < nr_followers; i++) {
+		if (followers[i].event == event)
+			ret |= followers[i].callback(event, record,
+						     cpu, followers[i].callback_data);
+	}
+
+	return ret;
+}
+
+static int read_cpu_pages(struct tep_handle *tep, struct tracefs_instance *instance,
+			  struct cpu_iterate *cpus, int count,
 			  int (*callback)(struct tep_event *,
 					  struct tep_record *,
 					  int, void *),
@@ -131,7 +264,10 @@
 				j = i;
 		}
 		if (j < count) {
-			if (callback(cpus[j].event, &cpus[j].record, cpus[j].cpu, callback_context))
+			if (call_followers(instance, cpus[j].event, &cpus[j].record, cpus[j].cpu))
+				break;
+			if (callback &&
+			    callback(cpus[j].event, &cpus[j].record, cpus[j].cpu, callback_context))
 				break;
 			cpus[j].event = NULL;
 			read_next_record(tep, cpus + j);
@@ -146,64 +282,114 @@
 static int open_cpu_files(struct tracefs_instance *instance, cpu_set_t *cpus,
 			  int cpu_size, struct cpu_iterate **all_cpus, int *count)
 {
+	struct tracefs_cpu *tcpu;
 	struct cpu_iterate *tmp;
-	unsigned int p_size;
-	struct dirent *dent;
-	char file[PATH_MAX];
-	struct stat st;
-	int ret = -1;
-	int fd = -1;
-	char *path;
-	DIR *dir;
+	int nr_cpus;
 	int cpu;
 	int i = 0;
 
-	path = tracefs_instance_get_file(instance, "per_cpu");
-	if (!path)
-		return -1;
-	dir = opendir(path);
-	if (!dir)
-		goto out;
-	p_size = getpagesize();
-	while ((dent = readdir(dir))) {
-		const char *name = dent->d_name;
+	*all_cpus = NULL;
 
-		if (strlen(name) < 4 || strncmp(name, "cpu", 3) != 0)
-			continue;
-		cpu = atoi(name + 3);
+	nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+	for (cpu = 0; cpu < nr_cpus; cpu++) {
 		if (cpus && !CPU_ISSET_S(cpu, cpu_size, cpus))
 			continue;
-		sprintf(file, "%s/%s", path, name);
-		if (stat(file, &st) < 0 || !S_ISDIR(st.st_mode))
-			continue;
-
-		sprintf(file, "%s/%s/trace_pipe_raw", path, name);
-		fd = open(file, O_RDONLY | O_NONBLOCK);
-		if (fd < 0)
-			continue;
-		tmp = realloc(*all_cpus, (i + 1) * sizeof(struct cpu_iterate));
+		tcpu = tracefs_cpu_open(instance, cpu, true);
+		tmp = realloc(*all_cpus, (i + 1) * sizeof(*tmp));
 		if (!tmp) {
-			close(fd);
-			goto out;
+			i--;
+			goto error;
 		}
-		memset(tmp + i, 0, sizeof(struct cpu_iterate));
-		tmp[i].fd = fd;
-		tmp[i].cpu = cpu;
-		tmp[i].page =  malloc(p_size);
-		tmp[i].psize = p_size;
+
 		*all_cpus = tmp;
-		*count = i + 1;
+
+		memset(tmp + i, 0, sizeof(*tmp));
+
+		if (!tcpu)
+			goto error;
+
+		tmp[i].tcpu = tcpu;
+		tmp[i].cpu = cpu;
+		tmp[i].psize = tracefs_cpu_read_size(tcpu);
+		tmp[i].page =  malloc(tmp[i].psize);
+
 		if (!tmp[i++].page)
-			goto out;
+			goto error;
+	}
+	*count = i;
+	return 0;
+ error:
+	tmp = *all_cpus;
+	for (; i >= 0; i--) {
+		tracefs_cpu_close(tmp[i].tcpu);
+		free(tmp[i].page);
+	}
+	free(tmp);
+	*all_cpus = NULL;
+	return -1;
+}
+
+/**
+ * tracefs_follow_event - Add callback for specific events for iterators
+ * @tep: a handle to the trace event parser context
+ * @instance: The instance to follow
+ * @system: The system of the event to track
+ * @event_name: The name of the event to track
+ * @callback: The function to call when the event is hit in an iterator
+ * @callback_data: The data to pass to @callback
+ *
+ * This attaches a callback to an @instance or the root instance if @instance
+ * is NULL, where if tracefs_iterate_raw_events() is called, that if the specified
+ * event is hit, it will call @callback, with the following parameters:
+ *  @event: The event pointer that was found by @system and @event_name.
+ *  @record; The event instance of @event.
+ *  @cpu: The cpu that the event happened on.
+ *  @callback_data: The same as @callback_data passed to the function.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instance,
+			  const char *system, const char *event_name,
+			  int (*callback)(struct tep_event *,
+					  struct tep_record *,
+					  int, void *),
+			  void *callback_data)
+{
+	struct follow_event **followers;
+	struct follow_event *follower;
+	struct follow_event follow;
+	int *nr_followers;
+
+	if (!tep) {
+		errno = EINVAL;
+		return -1;
 	}
 
-	ret = 0;
+	follow.event = tep_find_event_by_name(tep, system, event_name);
+	if (!follow.event) {
+		errno = ENOENT;
+		return -1;
+	}
 
-out:
-	if (dir)
-		closedir(dir);
-	tracefs_put_tracing_file(path);
-	return ret;
+	follow.callback = callback;
+	follow.callback_data = callback_data;
+
+	if (instance) {
+		followers = &instance->followers;
+		nr_followers = &instance->nr_followers;
+	} else {
+		followers = &root_followers;
+		nr_followers = &nr_root_followers;
+	}
+	follower = realloc(*followers, sizeof(*follower) *
+			    ((*nr_followers) + 1));
+	if (!follower)
+		return -1;
+
+	*followers = follower;
+	follower[(*nr_followers)++] = follow;
+
+	return 0;
 }
 
 static bool top_iterate_keep_going;
@@ -233,22 +419,30 @@
 						int, void *),
 				void *callback_context)
 {
-	bool *keep_going = instance ? &instance->pipe_keep_going :
+	bool *keep_going = instance ? &instance->iterate_keep_going :
 				      &top_iterate_keep_going;
-	struct cpu_iterate *all_cpus = NULL;
+	struct follow_event *followers;
+	struct cpu_iterate *all_cpus;
 	int count = 0;
 	int ret;
 	int i;
 
 	(*(volatile bool *)keep_going) = true;
 
-	if (!tep || !callback)
+	if (!tep)
+		return -1;
+
+	if (instance)
+		followers = instance->followers;
+	else
+		followers = root_followers;
+	if (!callback && !followers)
 		return -1;
 
 	ret = open_cpu_files(instance, cpus, cpu_size, &all_cpus, &count);
 	if (ret < 0)
 		goto out;
-	ret = read_cpu_pages(tep, all_cpus, count,
+	ret = read_cpu_pages(tep, instance, all_cpus, count,
 			     callback, callback_context,
 			     keep_going);
 
@@ -256,7 +450,7 @@
 	if (all_cpus) {
 		for (i = 0; i < count; i++) {
 			kbuffer_free(all_cpus[i].kbuf);
-			close(all_cpus[i].fd);
+			tracefs_cpu_close(all_cpus[i].tcpu);
 			free(all_cpus[i].page);
 		}
 		free(all_cpus);
@@ -945,6 +1139,40 @@
 	return ret;
 }
 
+static void set_tep_cpus(const char *tracing_dir, struct tep_handle *tep)
+{
+	struct stat st;
+	char path[PATH_MAX];
+	int cpus = sysconf(_SC_NPROCESSORS_CONF);
+	int max_cpu = 0;
+	int ret;
+	int i;
+
+	if (!tracing_dir)
+		tracing_dir = tracefs_tracing_dir();
+
+	/*
+	 * Paranoid: in case sysconf() above does not work.
+	 * And we also only care about the number of tracing
+	 * buffers that exist. If cpus is 32, but the top half
+	 * is offline, there may only be 16 tracing buffers.
+	 * That's what we want to know.
+	 */
+	for (i = 0; !cpus || i < cpus; i++) {
+		snprintf(path, PATH_MAX, "%s/per_cpu/cpu%d", tracing_dir, i);
+		ret = stat(path, &st);
+		if (!ret && S_ISDIR(st.st_mode))
+			max_cpu = i + 1;
+		else if (i >= cpus)
+			break;
+	}
+
+	if (!max_cpu)
+		max_cpu = cpus;
+
+	tep_set_cpus(tep, max_cpu);
+}
+
 /**
  * tracefs_local_events_system - create a tep from the events of the specified subsystem.
  *
@@ -969,6 +1197,11 @@
 		tep = NULL;
 	}
 
+	set_tep_cpus(tracing_dir, tep);
+
+	/* Set the long size for this tep handle */
+	tep_set_long_size(tep, tep_get_header_page_size(tep));
+
 	return tep;
 }
 
@@ -1004,9 +1237,68 @@
 	return regexec(re, str, 0, NULL, 0) == 0;
 }
 
+enum event_state {
+	STATE_INIT,
+	STATE_ENABLED,
+	STATE_DISABLED,
+	STATE_MIXED,
+	STATE_ERROR,
+};
+
+static int read_event_state(struct tracefs_instance *instance, const char *file,
+			    enum event_state *state)
+{
+	char *val;
+	int ret = 0;
+
+	if (*state == STATE_ERROR)
+		return -1;
+
+	val = tracefs_instance_file_read(instance, file, NULL);
+	if (!val)
+		return -1;
+
+	switch (val[0]) {
+	case '0':
+		switch (*state) {
+		case STATE_INIT:
+			*state = STATE_DISABLED;
+			break;
+		case STATE_ENABLED:
+			*state = STATE_MIXED;
+			break;
+		default:
+			break;
+		}
+		break;
+	case '1':
+		switch (*state) {
+		case STATE_INIT:
+			*state = STATE_ENABLED;
+			break;
+		case STATE_DISABLED:
+			*state = STATE_MIXED;
+			break;
+		default:
+			break;
+		}
+		break;
+	case 'X':
+		*state = STATE_MIXED;
+		break;
+	default:
+		*state = TRACEFS_ERROR;
+		ret = -1;
+		break;
+	}
+	free(val);
+
+	return ret;
+}
+
 static int enable_disable_event(struct tracefs_instance *instance,
 				const char *system, const char *event,
-				bool enable)
+				bool enable, enum event_state *state)
 {
 	const char *str = enable ? "1" : "0";
 	char *system_event;
@@ -1016,14 +1308,18 @@
 	if (ret < 0)
 		return ret;
 
-	ret = tracefs_instance_file_write(instance, system_event, str);
+	if (state)
+		ret = read_event_state(instance, system_event, state);
+	else
+		ret = tracefs_instance_file_write(instance, system_event, str);
 	free(system_event);
 
 	return ret;
 }
 
 static int enable_disable_system(struct tracefs_instance *instance,
-				 const char *system, bool enable)
+				 const char *system, bool enable,
+				 enum event_state *state)
 {
 	const char *str = enable ? "1" : "0";
 	char *system_path;
@@ -1033,7 +1329,10 @@
 	if (ret < 0)
 		return ret;
 
-	ret = tracefs_instance_file_write(instance, system_path, str);
+	if (state)
+		ret = read_event_state(instance, system_path, state);
+	else
+		ret = tracefs_instance_file_write(instance, system_path, str);
 	free(system_path);
 
 	return ret;
@@ -1071,7 +1370,7 @@
 
 static int event_enable_disable(struct tracefs_instance *instance,
 				const char *system, const char *event,
-				bool enable)
+				bool enable, enum event_state *state)
 {
 	regex_t system_re, event_re;
 	char **systems;
@@ -1108,7 +1407,7 @@
 
 		/* Check for the short cut first */
 		if (!event) {
-			ret = enable_disable_system(instance, systems[s], enable);
+			ret = enable_disable_system(instance, systems[s], enable, state);
 			if (ret < 0)
 				break;
 			ret = 0;
@@ -1123,7 +1422,7 @@
 			if (!match(events[e], &event_re))
 				continue;
 			ret = enable_disable_event(instance, systems[s],
-						   events[e], enable);
+						   events[e], enable, state);
 			if (ret < 0)
 				break;
 			ret = 0;
@@ -1162,11 +1461,55 @@
 int tracefs_event_enable(struct tracefs_instance *instance,
 			 const char *system, const char *event)
 {
-	return event_enable_disable(instance, system, event, true);
+	return event_enable_disable(instance, system, event, true, NULL);
 }
 
 int tracefs_event_disable(struct tracefs_instance *instance,
 			  const char *system, const char *event)
 {
-	return event_enable_disable(instance, system, event, false);
+	return event_enable_disable(instance, system, event, false, NULL);
+}
+
+/**
+ * tracefs_event_is_enabled - return if the event is enabled or not
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @system: The name of the system to check
+ * @event: The name of the event to check
+ *
+ * Checks is an event or multiple events are enabled.
+ *
+ * If @system is NULL, then it will check all the systems where @event is
+ * a match.
+ *
+ * If @event is NULL, then it will check all events where @system is a match.
+ *
+ * If both @system and @event are NULL, then it will check all events
+ *
+ * Returns TRACEFS_ALL_ENABLED if all matching are enabled.
+ * Returns TRACEFS_SOME_ENABLED if some are enabled and some are not
+ * Returns TRACEFS_ALL_DISABLED if none of the events are enabled.
+ * Returns TRACEFS_ERROR if there is an error reading the events.
+ */
+enum tracefs_enable_state
+tracefs_event_is_enabled(struct tracefs_instance *instance,
+			 const char *system, const char *event)
+{
+	enum event_state state = STATE_INIT;
+	int ret;
+
+	ret = event_enable_disable(instance, system, event, false, &state);
+
+	if (ret < 0)
+		return TRACEFS_ERROR;
+
+	switch (state) {
+	case STATE_ENABLED:
+		return TRACEFS_ALL_ENABLED;
+	case STATE_DISABLED:
+		return TRACEFS_ALL_DISABLED;
+	case STATE_MIXED:
+		return TRACEFS_SOME_ENABLED;
+	default:
+		return TRACEFS_ERROR;
+	}
 }
diff --git a/src/tracefs-filter.c b/src/tracefs-filter.c
index 85d0be1..a3dd77b 100644
--- a/src/tracefs-filter.c
+++ b/src/tracefs-filter.c
@@ -35,6 +35,12 @@
 	.size			= 8,
 };
 
+static const struct tep_format_field common_comm = {
+	.type			= "char *",
+	.name			= "common_comm",
+	.size			= 16,
+};
+
 /*
  * This also must be able to accept fields that are OK via the histograms,
  * such as common_timestamp.
@@ -42,13 +48,19 @@
 static const struct tep_format_field *get_event_field(struct tep_event *event,
 					 const char *field_name)
 {
+	const struct tep_format_field *field;
+
 	if (!strcmp(field_name, TRACEFS_TIMESTAMP))
 		return &common_timestamp;
 
 	if (!strcmp(field_name, TRACEFS_TIMESTAMP_USECS))
 		return &common_timestamp_usecs;
 
-	return tep_find_any_field(event, field_name);
+	field = tep_find_any_field(event, field_name);
+	if (!field && (!strcmp(field_name, "COMM") || !strcmp(field_name, "comm")))
+		return &common_comm;
+
+	return field;
 }
 
 __hidden bool
@@ -423,7 +435,8 @@
 		free(*filter);
 		*filter = str;
 	}
-	return 0;
+
+	return ret;
 }
 
 static int error_msg(char **err, char *str,
diff --git a/src/tracefs-hist.c b/src/tracefs-hist.c
index e688eb3..fb6231e 100644
--- a/src/tracefs-hist.c
+++ b/src/tracefs-hist.c
@@ -280,25 +280,11 @@
 	return tracefs_hist_alloc_nd(tep, system, event_name, axis);
 }
 
-/**
- * tracefs_hist_alloc_nd - Initialize N-dimensional histogram
- * @tep: The tep handle that has the @system and @event.
- * @system: The system the histogram event is in
- * @event: The event that the histogram will be attached to
- * @axes: An array of histogram axes, terminated by a {NULL, 0} entry
- *
- * Will initialize a histogram descriptor that will be attached to
- * the @system/@event. This only initializes the descriptor with the given
- * @axes keys as primaries. This only initializes the descriptor, it does
- * not start the histogram in the kernel.
- *
- * Returns an initialized histogram on success.
- * NULL on failure.
- */
-struct tracefs_hist *
-tracefs_hist_alloc_nd(struct tep_handle *tep,
-		      const char *system, const char *event_name,
-		      struct tracefs_hist_axis *axes)
+static struct tracefs_hist *
+hist_alloc_nd(struct tep_handle *tep,
+	      const char *system, const char *event_name,
+	      struct tracefs_hist_axis *axes,
+	      struct tracefs_hist_axis_cnt *axes_cnt)
 {
 	struct tep_event *event;
 	struct tracefs_hist *hist;
@@ -326,25 +312,75 @@
 		if (tracefs_hist_add_key(hist, axes->key, axes->type) < 0)
 			goto fail;
 
+	for (; axes_cnt && axes_cnt->key; axes_cnt++)
+		if (tracefs_hist_add_key_cnt(hist, axes_cnt->key, axes_cnt->type, axes_cnt->cnt) < 0)
+			goto fail;
+
 	return hist;
 
  fail:
 	tracefs_hist_free(hist);
 	return NULL;
 }
+/**
+ * tracefs_hist_alloc_nd - Initialize N-dimensional histogram
+ * @tep: The tep handle that has the @system and @event.
+ * @system: The system the histogram event is in
+ * @event: The event that the histogram will be attached to
+ * @axes: An array of histogram axes, terminated by a {NULL, 0} entry
+ *
+ * Will initialize a histogram descriptor that will be attached to
+ * the @system/@event. This only initializes the descriptor with the given
+ * @axes keys as primaries. This only initializes the descriptor, it does
+ * not start the histogram in the kernel.
+ *
+ * Returns an initialized histogram on success.
+ * NULL on failure.
+ */
+struct tracefs_hist *
+tracefs_hist_alloc_nd(struct tep_handle *tep,
+		      const char *system, const char *event_name,
+		      struct tracefs_hist_axis *axes)
+{
+	return hist_alloc_nd(tep, system, event_name, axes, NULL);
+}
+
+/**
+ * tracefs_hist_alloc_nd_cnt - Initialize N-dimensional histogram
+ * @tep: The tep handle that has the @system and @event.
+ * @system: The system the histogram event is in
+ * @event: The event that the histogram will be attached to
+ * @axes: An array of histogram axes, terminated by a {NULL, 0} entry
+ *
+ * Will initialize a histogram descriptor that will be attached to
+ * the @system/@event. This only initializes the descriptor with the given
+ * @axes keys as primaries. This only initializes the descriptor, it does
+ * not start the histogram in the kernel.
+ *
+ * Returns an initialized histogram on success.
+ * NULL on failure.
+ */
+struct tracefs_hist *
+tracefs_hist_alloc_nd_cnt(struct tep_handle *tep,
+			  const char *system, const char *event_name,
+			  struct tracefs_hist_axis_cnt *axes)
+{
+	return hist_alloc_nd(tep, system, event_name, NULL, axes);
+}
 
 /**
  * tracefs_hist_add_key - add to a key to a histogram
  * @hist: The histogram to add the key to.
  * @key: The name of the key field.
  * @type: The type of the key format.
+ * @cnt: Some types require a counter, for those, this is used
  *
  * This adds a secondary or tertiary key to the histogram.
  *
  * Returns 0 on success, -1 on error.
  */
-int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
-			 enum tracefs_hist_key_type type)
+int tracefs_hist_add_key_cnt(struct tracefs_hist *hist, const char *key,
+			     enum tracefs_hist_key_type type, int cnt)
 {
 	bool use_key = false;
 	char *key_type = NULL;
@@ -377,6 +413,9 @@
 	case TRACEFS_HIST_KEY_USECS:
 		ret = asprintf(&key_type, "%s.usecs", key);
 		break;
+	case TRACEFS_HIST_KEY_BUCKETS:
+		ret = asprintf(&key_type, "%s.buckets=%d", key, cnt);
+		break;
 	case TRACEFS_HIST_KEY_MAX:
 		/* error */
 		break;
@@ -396,6 +435,22 @@
 }
 
 /**
+ * tracefs_hist_add_key - add to a key to a histogram
+ * @hist: The histogram to add the key to.
+ * @key: The name of the key field.
+ * @type: The type of the key format.
+ *
+ * This adds a secondary or tertiary key to the histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
+			 enum tracefs_hist_key_type type)
+{
+	return tracefs_hist_add_key_cnt(hist, key, type, 0);
+}
+
+/**
  * tracefs_hist_add_value - add to a value to a histogram
  * @hist: The histogram to add the value to.
  * @key: The name of the value field.
@@ -654,6 +709,12 @@
 	char				*save;
 };
 
+struct name_hash {
+	struct name_hash	*next;
+	char			*name;
+	char			*hash;
+};
+
 /*
  * @name: name of the synthetic event
  * @start_system: system of the starting event
@@ -684,6 +745,7 @@
 	struct action		*actions;
 	struct action		**next_action;
 	struct tracefs_dynevent	*dyn_event;
+	struct name_hash	*name_hash[1 << HASH_BITS];
 	char			*start_hist;
 	char			*end_hist;
 	char			*name;
@@ -724,6 +786,21 @@
 	free(action);
 }
 
+static void free_name_hash(struct name_hash **hash)
+{
+	struct name_hash *item;
+	int i;
+
+	for (i = 0; i < 1 << HASH_BITS; i++) {
+		while ((item = hash[i])) {
+			hash[i] = item->next;
+			free(item->name);
+			free(item->hash);
+			free(item);
+		}
+	}
+}
+
 /**
  * tracefs_synth_free - free the resources alloced to a synth
  * @synth: The tracefs_synth descriptor
@@ -750,6 +827,7 @@
 	tracefs_list_free(synth->end_keys);
 	tracefs_list_free(synth->start_vars);
 	tracefs_list_free(synth->end_vars);
+	free_name_hash(synth->name_hash);
 	free(synth->start_filter);
 	free(synth->end_filter);
 	free(synth->start_type);
@@ -773,6 +851,7 @@
 {
 	const struct tep_format_field *start_field;
 	const struct tep_format_field *end_field;
+	int start_flags, end_flags;
 
 	if (!trace_verify_event_field(start_event, start_field_name,
 				      &start_field))
@@ -783,7 +862,11 @@
 					      &end_field))
 			return false;
 
-		if (start_field->flags != end_field->flags ||
+		/* A pointer can still match a long */
+		start_flags = start_field->flags & ~TEP_FIELD_IS_POINTER;
+		end_flags = end_field->flags & ~TEP_FIELD_IS_POINTER;
+
+		if (start_flags != end_flags ||
 		    start_field->size != end_field->size) {
 			errno = EBADE;
 			return false;
@@ -1068,9 +1151,8 @@
 	if (!synth->name || !synth->start_keys || !synth->end_keys || ret) {
 		tracefs_synth_free(synth);
 		synth = NULL;
-	}
-
-	synth->new_format = has_new_format();
+	} else
+		synth->new_format = has_new_format();
 
 	return synth;
 }
@@ -1096,7 +1178,7 @@
 		return -1;
 	synth->synthetic_fields = list;
 
-	ret = asprintf(&str, "$%s", name);
+	ret = asprintf(&str, "$%s", var ? : name);
 	if (ret < 0) {
 		trace_list_pop(synth->synthetic_fields);
 		return -1;
@@ -1196,7 +1278,7 @@
 	return((unsigned)(seed/65536) % 32768);
 }
 
-static char *new_arg(struct tracefs_synth *synth)
+static char *new_name(struct tracefs_synth *synth, const char *name)
 {
 	int cnt = synth->arg_cnt + 1;
 	char *arg;
@@ -1205,9 +1287,9 @@
 	/* Create a unique argument name */
 	if (!synth->arg_name[0]) {
 		/* make_rand() returns at most 32768 (total 13 bytes in use) */
-		sprintf(synth->arg_name, "__arg_%u_", make_rand());
+		sprintf(synth->arg_name, "%u", make_rand());
 	}
-	ret = asprintf(&arg, "%s%d", synth->arg_name, cnt);
+	ret = asprintf(&arg, "__%s_%s_%d", name, synth->arg_name, cnt);
 	if (ret < 0)
 		return NULL;
 
@@ -1215,6 +1297,55 @@
 	return arg;
 }
 
+static struct name_hash *find_name(struct tracefs_synth *synth, const char *name)
+{
+	unsigned int key = quick_hash(name);
+	struct name_hash *hash = synth->name_hash[key];
+
+	for (; hash; hash = hash->next) {
+		if (!strcmp(hash->name, name))
+			return hash;
+	}
+	return NULL;
+}
+
+static const char *hash_name(struct tracefs_synth *synth, const char *name)
+{
+	struct name_hash *hash;
+	int key;
+
+	hash = find_name(synth, name);
+	if (hash)
+		return hash->hash;
+
+	hash = malloc(sizeof(*hash));
+	if (!hash)
+		return name;
+
+	hash->hash = new_name(synth, name);
+	if (!hash->hash) {
+		free(hash);
+		return name;
+	}
+
+	key = quick_hash(name);
+	hash->next = synth->name_hash[key];
+	synth->name_hash[key] = hash;
+
+	hash->name = strdup(name);
+	if (!hash->name) {
+		free(hash->hash);
+		free(hash);
+		return name;
+	}
+	return hash->hash;
+}
+
+static char *new_arg(struct tracefs_synth *synth)
+{
+	return new_name(synth, "arg");
+}
+
 /**
  * tracefs_synth_add_compare_field - add a comparison between start and end
  * @synth: The tracefs_synth descriptor
@@ -1245,6 +1376,7 @@
 				    const char *name)
 {
 	const struct tep_format_field *start_field;
+	const char *hname;
 	char *start_arg;
 	char *compare;
 	int ret;
@@ -1296,11 +1428,12 @@
 	if (ret < 0)
 		return -1;
 
-	ret = add_var(&synth->end_vars, name, compare, false);
+	hname = hash_name(synth, name);
+	ret = add_var(&synth->end_vars, hname, compare, false);
 	if (ret < 0)
 		goto out_free;
 
-	ret = add_synth_fields(synth, start_field, name, NULL);
+	ret = add_synth_fields(synth, start_field, name, hname);
 	if (ret < 0)
 		goto out_free;
 
@@ -1313,9 +1446,10 @@
 __hidden int synth_add_start_field(struct tracefs_synth *synth,
 				   const char *start_field,
 				   const char *name,
-				   enum tracefs_hist_key_type type)
+				   enum tracefs_hist_key_type type, int count)
 {
 	const struct tep_format_field *field;
+	const char *var;
 	char *start_arg;
 	char **tmp;
 	int *types;
@@ -1330,6 +1464,8 @@
 	if (!name)
 		name = start_field;
 
+	var = hash_name(synth, name);
+
 	if (!trace_verify_event_field(synth->start_event, start_field, &field))
 		return -1;
 
@@ -1341,11 +1477,11 @@
 	if (ret)
 		goto out_free;
 
-	ret = add_var(&synth->end_vars, name, start_arg, true);
+	ret = add_var(&synth->end_vars, var, start_arg, true);
 	if (ret)
 		goto out_free;
 
-	ret = add_synth_fields(synth, field, name, NULL);
+	ret = add_synth_fields(synth, field, name, var);
 	if (ret)
 		goto out_free;
 
@@ -1394,7 +1530,7 @@
 				  const char *start_field,
 				  const char *name)
 {
-	return synth_add_start_field(synth, start_field, name, 0);
+	return synth_add_start_field(synth, start_field, name, 0, 0);
 }
 
 /**
@@ -1417,6 +1553,7 @@
 				const char *name)
 {
 	const struct tep_format_field *field;
+	const char *hname = NULL;
 	char *tmp_var = NULL;
 	int ret;
 
@@ -1425,17 +1562,24 @@
 		return -1;
 	}
 
+	if (name) {
+		if (strncmp(name, "__arg", 5) != 0)
+			hname = hash_name(synth, name);
+		else
+			hname = name;
+	}
+
 	if (!name)
 		tmp_var = new_arg(synth);
 
 	if (!trace_verify_event_field(synth->end_event, end_field, &field))
 		return -1;
 
-	ret = add_var(&synth->end_vars, name ? : tmp_var, end_field, false);
+	ret = add_var(&synth->end_vars, name ? hname : tmp_var, end_field, false);
 	if (ret)
 		goto out;
 
-	ret = add_synth_fields(synth, field, name, tmp_var);
+	ret = add_synth_fields(synth, field, name, hname ? : tmp_var);
 	free(tmp_var);
  out:
 	return ret;
@@ -2194,7 +2338,7 @@
 		new_event = true;
 	}
 
-	path = trace_find_tracing_dir();
+	path = trace_find_tracing_dir(false);
 	if (!path)
 		goto out_free;
 
@@ -2227,11 +2371,6 @@
 			 hist, path, synth->end_event->system,
 			 synth->end_event->name);
 
-	if (new_event) {
-		tracefs_dynevent_free(synth->dyn_event);
-		synth->dyn_event = NULL;
-	}
-
 	ret = 0;
  out_free:
 	free(hist);
diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c
index 1493938..57f5c7f 100644
--- a/src/tracefs-instance.c
+++ b/src/tracefs-instance.c
@@ -334,7 +334,7 @@
 	int ret;
 
 	if (!instance) /* Top instance of default system trace directory */
-		return trace_find_tracing_dir();
+		return trace_find_tracing_dir(false);
 
 	if (!instance->name)
 		return strdup(instance->trace_dir);
@@ -364,6 +364,70 @@
 }
 
 /**
+ * tracefs_instance_get_buffer_size - return the buffer size of the ring buffer
+ * @instance: The instance to get the buffer size from
+ * @cpu: if less that zero, will return the total size, otherwise the cpu size
+ *
+ * Returns the buffer size. If @cpu is less than zero, it returns the total size
+ * of the ring buffer otherwise it returs the size of the buffer for the given
+ * CPU.
+ *
+ * Returns -1 on error.
+ */
+ssize_t tracefs_instance_get_buffer_size(struct tracefs_instance *instance, int cpu)
+{
+	unsigned long long size;
+	char *path;
+	char *val;
+	int ret;
+
+	if (cpu < 0) {
+		val = tracefs_instance_file_read(instance, "buffer_total_size_kb", NULL);
+	} else {
+		ret = asprintf(&path, "per_cpu/cpu%d/buffer_size_kb", cpu);
+		if (ret < 0)
+			return ret;
+
+		val = tracefs_instance_file_read(instance, path, NULL);
+		free(path);
+	}
+
+	if (!val)
+		return -1;
+
+	size = strtoull(val, NULL, 0);
+	free(val);
+	return size;
+}
+
+int tracefs_instance_set_buffer_size(struct tracefs_instance *instance, size_t size, int cpu)
+{
+	char *path;
+	char *val;
+	int ret;
+
+	ret = asprintf(&val, "%zd", size);
+	if (ret < 0)
+		return ret;
+
+	if (cpu < 0) {
+		ret = tracefs_instance_file_write(instance, "buffer_size_kb", val);
+	} else {
+		ret = asprintf(&path, "per_cpu/cpu%d/buffer_size_kb", cpu);
+		if (ret < 0) {
+			free(val);
+			return ret;
+		}
+
+		ret = tracefs_instance_file_write(instance, path, val);
+		free(path);
+	}
+	free(val);
+
+	return ret < 0 ? -1 : 0;
+}
+
+/**
  * tracefs_instance_get_trace_dir - return the top trace directory, where the instance is confuigred
  * @instance: ftrace instance
  *
diff --git a/src/tracefs-record.c b/src/tracefs-record.c
new file mode 100644
index 0000000..b078c86
--- /dev/null
+++ b/src/tracefs-record.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2022 Google Inc, Steven Rostedt <rostedt@goodmis.org>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+
+#include <kbuffer.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+enum {
+	TC_STOP			= 1 << 0,   /* Stop reading */
+	TC_PERM_NONBLOCK	= 1 << 1,   /* read is always non blocking */
+	TC_NONBLOCK		= 1 << 2,   /* read is non blocking */
+};
+
+struct tracefs_cpu {
+	int		fd;
+	int		flags;
+	int		nfds;
+	int		ctrl_pipe[2];
+	int		splice_pipe[2];
+	int		pipe_size;
+	int		subbuf_size;
+	int		buffered;
+	int		splice_read_flags;
+};
+
+/**
+ * tracefs_cpu_alloc_fd - create a tracefs_cpu instance for an existing fd
+ * @fd: The file descriptor to attach the tracefs_cpu to
+ * @subbuf_size: The expected size to read the subbuffer with
+ * @nonblock: If true, the file will be opened in O_NONBLOCK mode
+ *
+ * Return a descriptor that can read the tracefs trace_pipe_raw file
+ * that is associated with the given @fd and must be read in @subbuf_size.
+ *
+ * Returns NULL on error.
+ */
+struct tracefs_cpu *
+tracefs_cpu_alloc_fd(int fd, int subbuf_size, bool nonblock)
+{
+	struct tracefs_cpu *tcpu;
+	int mode = O_RDONLY;
+	int ret;
+
+	tcpu = calloc(1, sizeof(*tcpu));
+	if (!tcpu)
+		return NULL;
+
+	if (nonblock) {
+		mode |= O_NONBLOCK;
+		tcpu->flags |= TC_NONBLOCK | TC_PERM_NONBLOCK;
+	}
+
+	tcpu->splice_pipe[0] = -1;
+	tcpu->splice_pipe[1] = -1;
+
+	tcpu->fd = fd;
+
+	tcpu->subbuf_size = subbuf_size;
+
+	if (tcpu->flags & TC_PERM_NONBLOCK) {
+		tcpu->ctrl_pipe[0] = -1;
+		tcpu->ctrl_pipe[1] = -1;
+	} else {
+		/* ctrl_pipe is used to break out of blocked reads */
+		ret = pipe(tcpu->ctrl_pipe);
+		if (ret < 0)
+			goto fail;
+		if (tcpu->ctrl_pipe[0] > tcpu->fd)
+			tcpu->nfds = tcpu->ctrl_pipe[0] + 1;
+		else
+			tcpu->nfds = tcpu->fd + 1;
+	}
+
+	return tcpu;
+ fail:
+	free(tcpu);
+	return NULL;
+}
+
+/**
+ * tracefs_cpu_open - open an instance raw trace file
+ * @instance: the instance (NULL for toplevel) of the cpu raw file to open
+ * @cpu: The CPU that the raw trace file is associated with
+ * @nonblock: If true, the file will be opened in O_NONBLOCK mode
+ *
+ * Return a descriptor that can read the tracefs trace_pipe_raw file
+ * for a give @cpu in a given @instance.
+ *
+ * Returns NULL on error.
+ */
+struct tracefs_cpu *
+tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock)
+{
+	struct tracefs_cpu *tcpu;
+	struct tep_handle *tep;
+	char path[128];
+	char *buf;
+	int mode = O_RDONLY;
+	int subbuf_size;
+	int len;
+	int ret;
+	int fd;
+
+	if (nonblock)
+		mode |= O_NONBLOCK;
+
+	sprintf(path, "per_cpu/cpu%d/trace_pipe_raw", cpu);
+
+	fd = tracefs_instance_file_open(instance, path, mode);
+	if (fd < 0)
+		return NULL;
+
+	tep = tep_alloc();
+	if (!tep)
+		goto fail;
+
+	/* Get the size of the page */
+	buf = tracefs_instance_file_read(NULL, "events/header_page", &len);
+	if (!buf)
+		goto fail;
+
+	ret = tep_parse_header_page(tep, buf, len, sizeof(long));
+	free(buf);
+	if (ret < 0)
+		goto fail;
+
+	subbuf_size = tep_get_sub_buffer_size(tep);
+	tep_free(tep);
+	tep = NULL;
+
+	tcpu = tracefs_cpu_alloc_fd(fd, subbuf_size, nonblock);
+	if (!tcpu)
+		goto fail;
+
+	return tcpu;
+ fail:
+	tep_free(tep);
+	close(fd);
+	return NULL;
+}
+
+static void close_fd(int fd)
+{
+	if (fd < 0)
+		return;
+	close(fd);
+}
+
+/**
+ * tracefs_cpu_free_fd - clean up the tracefs_cpu descriptor
+ * @tcpu: The descriptor created with tracefs_cpu_alloc_fd()
+ *
+ * Closes all the internal file descriptors that were opened by
+ * tracefs_cpu_alloc_fd(), and frees the descriptor.
+ */
+void tracefs_cpu_free_fd(struct tracefs_cpu *tcpu)
+{
+	close_fd(tcpu->ctrl_pipe[0]);
+	close_fd(tcpu->ctrl_pipe[1]);
+	close_fd(tcpu->splice_pipe[0]);
+	close_fd(tcpu->splice_pipe[1]);
+
+	free(tcpu);
+}
+
+/**
+ * tracefs_cpu_close - clean up and close a raw trace descriptor
+ * @tcpu: The descriptor created with tracefs_cpu_open()
+ *
+ * Closes all the file descriptors associated to the trace_pipe_raw
+ * opened by tracefs_cpu_open().
+ */
+void tracefs_cpu_close(struct tracefs_cpu *tcpu)
+{
+	if (!tcpu)
+		return;
+
+	close(tcpu->fd);
+	tracefs_cpu_free_fd(tcpu);
+}
+
+/**
+ * tracefs_cpu_read_size - Return the size of the sub buffer
+ * @tcpu: The descriptor that holds the size of the sub buffer
+ *
+ * A lot of the functions that read the data from the trace_pipe_raw
+ * expect the caller to have allocated enough space to store a full
+ * subbuffer. Calling this function is a requirement to do so.
+ */
+int tracefs_cpu_read_size(struct tracefs_cpu *tcpu)
+{
+	if (!tcpu)
+		return -1;
+	return tcpu->subbuf_size;
+}
+
+static void set_nonblock(struct tracefs_cpu *tcpu)
+{
+	long flags;
+
+	if (tcpu->flags & TC_NONBLOCK)
+		return;
+
+	flags = fcntl(tcpu->fd, F_GETFL);
+	fcntl(tcpu->fd, F_SETFL, flags | O_NONBLOCK);
+	tcpu->flags |= TC_NONBLOCK;
+}
+
+static void unset_nonblock(struct tracefs_cpu *tcpu)
+{
+	long flags;
+
+	if (!(tcpu->flags & TC_NONBLOCK))
+		return;
+
+	flags = fcntl(tcpu->fd, F_GETFL);
+	flags &= ~O_NONBLOCK;
+	fcntl(tcpu->fd, F_SETFL, flags);
+	tcpu->flags &= ~TC_NONBLOCK;
+}
+
+/*
+ * If set to blocking mode, block until the watermark has been
+ * reached, or the control has said to stop. If the contol is
+ * set, then nonblock will be set to true on the way out.
+ */
+static int wait_on_input(struct tracefs_cpu *tcpu, bool nonblock)
+{
+	fd_set rfds;
+	int ret;
+
+	if (tcpu->flags & TC_PERM_NONBLOCK)
+		return 1;
+
+	if (nonblock) {
+		set_nonblock(tcpu);
+		return 1;
+	} else {
+		unset_nonblock(tcpu);
+	}
+
+	FD_ZERO(&rfds);
+	FD_SET(tcpu->fd, &rfds);
+	FD_SET(tcpu->ctrl_pipe[0], &rfds);
+
+	ret = select(tcpu->nfds, &rfds, NULL, NULL, NULL);
+
+	/* Let the application decide what to do with signals and such */
+	if (ret < 0)
+		return ret;
+
+	if (FD_ISSET(tcpu->ctrl_pipe[0], &rfds)) {
+		/* Flush the ctrl pipe */
+		read(tcpu->ctrl_pipe[0], &ret, 1);
+
+		/* Make nonblock as it is now stopped */
+		set_nonblock(tcpu);
+		/* Permanently set unblock */
+		tcpu->flags |= TC_PERM_NONBLOCK;
+	}
+
+	return FD_ISSET(tcpu->fd, &rfds);
+}
+
+/**
+ * tracefs_cpu_read - read from the raw trace file
+ * @tcpu: The descriptor representing the raw trace file
+ * @buffer: Where to read into (must be at least the size of the subbuffer)
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * Reads the trace_pipe_raw files associated to @tcpu into @buffer.
+ * @buffer must be at least the size of the sub buffer of the ring buffer,
+ * which is returned by tracefs_cpu_read_size().
+ *
+ * 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 the amount read or -1 on error.
+ */
+int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock)
+{
+	int ret;
+
+	/*
+	 * If nonblock is set, then the wait_on_input() will return
+	 * immediately, if there's nothing in the buffer, with
+	 * ret == 0.
+	 */
+	ret = wait_on_input(tcpu, nonblock);
+	if (ret <= 0)
+		return ret;
+
+	ret = read(tcpu->fd, buffer, tcpu->subbuf_size);
+
+	/* It's OK if there's no data to read */
+	if (ret < 0 && errno == EAGAIN) {
+		/* Reset errno */
+		errno = 0;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int init_splice(struct tracefs_cpu *tcpu)
+{
+	int ret;
+
+	if (tcpu->splice_pipe[0] >= 0)
+		return 0;
+
+	ret = pipe(tcpu->splice_pipe);
+	if (ret < 0)
+		return ret;
+
+	ret = fcntl(tcpu->splice_pipe[0], F_GETPIPE_SZ, &tcpu->pipe_size);
+	/*
+	 * F_GETPIPE_SZ was introduced in 2.6.35, ftrace was introduced
+	 * in 2.6.31. If we are running on an older kernel, just fall
+	 * back to using subbuf_size for splice(). It could also return
+	 * the size of the pipe and not set pipe_size.
+	 */
+	if (ret > 0 && !tcpu->pipe_size)
+		tcpu->pipe_size = ret;
+	else if (ret < 0)
+		tcpu->pipe_size = tcpu->subbuf_size;
+
+	tcpu->splice_read_flags = SPLICE_F_MOVE;
+	if (tcpu->flags & TC_NONBLOCK)
+		tcpu->splice_read_flags |= SPLICE_F_NONBLOCK;
+
+	return 0;
+}
+
+/**
+ * tracefs_cpu_buffered_read - Read the raw trace data buffering through a pipe
+ * @tcpu: The descriptor representing the raw trace file
+ * @buffer: Where to read into (must be at least the size of the subbuffer)
+ * @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 the amount read or -1 on error.
+ */
+int tracefs_cpu_buffered_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock)
+{
+	int mode = SPLICE_F_MOVE;
+	int ret;
+
+	if (tcpu->buffered < 0)
+		tcpu->buffered = 0;
+
+	if (tcpu->buffered)
+		goto do_read;
+
+	ret = wait_on_input(tcpu, nonblock);
+	if (ret <= 0)
+		return ret;
+
+	if (tcpu->flags & TC_NONBLOCK)
+		mode |= SPLICE_F_NONBLOCK;
+
+	ret = init_splice(tcpu);
+	if (ret < 0)
+		return ret;
+
+	ret = splice(tcpu->fd, NULL, tcpu->splice_pipe[1], NULL,
+		     tcpu->pipe_size, mode);
+	if (ret <= 0)
+		return ret;
+
+	tcpu->buffered = ret;
+
+ do_read:
+	ret = read(tcpu->splice_pipe[0], buffer, tcpu->subbuf_size);
+	if (ret > 0)
+		tcpu->buffered -= ret;
+	return ret;
+}
+
+/**
+ * tracefs_cpu_stop - Stop a blocked read of the raw tracing file
+ * @tcpu: The descriptor representing the raw trace file
+ *
+ * This will attempt to unblock a task blocked on @tcpu reading it.
+ * On older kernels, it may not do anything for the pipe reads, as
+ * older kernels do not wake up tasks waiting on the ring buffer.
+ *
+ * Returns 0 if the tasks reading the raw tracing file does not
+ * need a nudge.
+ *
+ * Returns 1 if that tasks may need a nudge (send a signal).
+ *
+ * Returns negative on error.
+ */
+int tracefs_cpu_stop(struct tracefs_cpu *tcpu)
+{
+	int ret = 1;
+
+	if (tcpu->flags & TC_PERM_NONBLOCK)
+		return 0;
+
+	ret = write(tcpu->ctrl_pipe[1], &ret, 1);
+	if (ret < 0)
+		return ret;
+
+	/* Calling ioctl() on recent kernels will wake up the waiters */
+	ret = ioctl(tcpu->fd, 0);
+	if (ret < 0)
+		ret = 1;
+	else
+		ret = 0;
+
+	set_nonblock(tcpu);
+
+	return ret;
+}
+
+/**
+ * tracefs_cpu_flush - Finish out and read the rest of the raw tracing file
+ * @tcpu: The descriptor representing the raw trace file
+ * @buffer: Where to read into (must be at least the size of the subbuffer)
+ *
+ * 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.
+ *
+ * Returns the number of bytes read, or negative on error.
+ */
+int tracefs_cpu_flush(struct tracefs_cpu *tcpu, void *buffer)
+{
+	int ret;
+
+	/* Make sure that reading is now non blocking */
+	set_nonblock(tcpu);
+
+	if (tcpu->buffered < 0)
+		tcpu->buffered = 0;
+
+	if (tcpu->buffered) {
+		ret = read(tcpu->splice_pipe[0], buffer, tcpu->subbuf_size);
+		if (ret > 0)
+			tcpu->buffered -= ret;
+		return ret;
+	}
+
+	ret = read(tcpu->fd, buffer, tcpu->subbuf_size);
+	if (ret > 0 && tcpu->buffered)
+		tcpu->buffered -= ret;
+
+	/* It's OK if there's no data to read */
+	if (ret < 0 && errno == EAGAIN) {
+		/* Reset errno */
+		errno = 0;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/**
+ * tracefs_cpu_flush_write - Finish out and read the rest of the raw tracing file
+ * @tcpu: The descriptor representing the raw trace file
+ * @wfd: The write file descriptor to write the data to
+ *
+ * Reads the trace_pipe_raw file associated by the @tcpu and writes it to
+ * @wfd. This should be called at the end of tracing to get the rest of the data.
+ *
+ * Returns the number of bytes written, or negative on error.
+ */
+int tracefs_cpu_flush_write(struct tracefs_cpu *tcpu, int wfd)
+{
+	char buffer[tcpu->subbuf_size];
+	int ret;
+
+	ret = tracefs_cpu_flush(tcpu, buffer);
+	if (ret > 0)
+		ret = write(wfd, buffer, ret);
+
+	/* It's OK if there's no data to read */
+	if (ret < 0 && errno == EAGAIN)
+		ret = 0;
+
+	return ret;
+}
+
+/**
+ * tracefs_cpu_write - Write the raw trace file into a file descriptor
+ * @tcpu: The descriptor representing the raw trace file
+ * @wfd: The write file descriptor to write the data to
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * This will pipe the data from the trace_pipe_raw file associated with @tcpu
+ * into the @wfd file descriptor. If @nonblock is set, then it will not
+ * block on if there's nothing to write. Note, it will only write sub buffer
+ * size data to @wfd. Calls to tracefs_cpu_flush_write() are needed to
+ * write out the rest.
+ *
+ * Returns the number of bytes read or negative on error.
+ */
+int tracefs_cpu_write(struct tracefs_cpu *tcpu, int wfd, bool nonblock)
+{
+	char buffer[tcpu->subbuf_size];
+	int mode = SPLICE_F_MOVE;
+	int tot_write = 0;
+	int tot;
+	int ret;
+
+	ret = wait_on_input(tcpu, nonblock);
+	if (ret <= 0)
+		return ret;
+
+	if (tcpu->flags & TC_NONBLOCK)
+		mode |= SPLICE_F_NONBLOCK;
+
+	ret = init_splice(tcpu);
+	if (ret < 0)
+		return ret;
+
+	tot = splice(tcpu->fd, NULL, tcpu->splice_pipe[1], NULL,
+		     tcpu->pipe_size, mode);
+	if (tot < 0)
+		return tot;
+
+	if (tot == 0)
+		return 0;
+
+	ret = splice(tcpu->splice_pipe[0], NULL, wfd, NULL,
+		     tot, SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+
+	if (ret >= 0)
+		return ret;
+
+	/* Some file systems do not allow splicing, try writing instead */
+	do {
+		int r = tcpu->subbuf_size;
+
+		if (r > tot)
+			r = tot;
+
+		ret = read(tcpu->splice_pipe[0], buffer, r);
+		if (ret > 0) {
+			tot -= ret;
+			ret = write(wfd, buffer, ret);
+		}
+		if (ret > 0)
+			tot_write += ret;
+	} while (ret > 0);
+
+	if (ret < 0)
+		return ret;
+
+	return tot_write;
+}
+
+/**
+ * tracefs_cpu_pipe - Write the raw trace file into a pipe descriptor
+ * @tcpu: The descriptor representing the raw trace file
+ * @wfd: The write file descriptor to write the data to (must be a pipe)
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * This will splice directly the file descriptor of the trace_pipe_raw
+ * file to the given @wfd, which must be a pipe. This can also be used
+ * if @tcpu was created with tracefs_cpu_create_fd() where the passed
+ * in @fd there was a pipe, then @wfd does not need to be a pipe.
+ *
+ * Returns the number of bytes read or negative on error.
+ */
+int tracefs_cpu_pipe(struct tracefs_cpu *tcpu, int wfd, bool nonblock)
+{
+	int mode = SPLICE_F_MOVE;
+	int ret;
+
+	ret = wait_on_input(tcpu, nonblock);
+	if (ret <= 0)
+		return ret;
+
+	if (tcpu->flags & TC_NONBLOCK)
+		mode |= SPLICE_F_NONBLOCK;
+
+	ret = splice(tcpu->fd, NULL, wfd, NULL,
+		     tcpu->pipe_size, mode);
+	return ret;
+}
diff --git a/src/tracefs-sqlhist.c b/src/tracefs-sqlhist.c
index 9811362..3f571b7 100644
--- a/src/tracefs-sqlhist.c
+++ b/src/tracefs-sqlhist.c
@@ -30,6 +30,12 @@
 	ALIAS_FIELD,
 };
 
+enum field_type {
+	FIELD_NONE,
+	FIELD_FROM,
+	FIELD_TO,
+};
+
 #define for_each_field(expr, field, table) \
 	for (expr = (table)->fields; expr; expr = (field)->next)
 
@@ -42,6 +48,7 @@
 	const char		*label;
 	const char		*field;
 	const char		*type;
+	enum field_type		ftype;
 };
 
 struct filter {
@@ -167,7 +174,7 @@
 	va_end(ap);
 }
 
-static inline unsigned int quick_hash(const char *str)
+__hidden unsigned int quick_hash(const char *str)
 {
 	unsigned int val = 0;
 	int len = strlen(str);
@@ -253,6 +260,7 @@
 
 	switch (expr->type) {
 	case EXPR_FIELD:
+		expr->field.label = name;
 		break;
 	case EXPR_COMPARE:
 		expr->compare.name = name;
@@ -285,6 +293,8 @@
 		if (!strcmp(field->raw, raw)) {
 			if (label && !field->label)
 				field->label = label;
+			if (label && strcmp(label, field->label) != 0)
+				continue;
 			return expr;
 		}
 
@@ -562,6 +572,9 @@
 		tfield = tep_find_any_field(field->event, field_name);
 	free(field_name);
 
+	if (!tfield && (!strcmp(field->field, "COMM") || !strcmp(field->field, "comm")))
+		tfield = (void *)1L;
+
 	if (tfield)
 		return 0;
 
@@ -580,6 +593,7 @@
 {
 	struct sqlhist_bison *sb = table->sb;
 	struct field *event_field = &expr->field;
+	enum field_type ftype = FIELD_NONE;
 	struct tep_event *event;
 	struct field *field;
 	const char *label;
@@ -589,6 +603,11 @@
 	const char *p;
 	int label_len = 0, event_len, system_len;
 
+	if (expr == table->to)
+		ftype = FIELD_TO;
+	else if (expr == table->from)
+		ftype = FIELD_FROM;
+
 	p = strchr(raw, '.');
 	if (p) {
 		char *str;
@@ -670,6 +689,7 @@
 		field->event_name = event_name;
 		field->event = event;
 		field->field = raw + len + 1;
+		field->ftype = ftype;
 
 		if (!strcmp(field->field, "TIMESTAMP"))
 			field->field = store_str(sb, TRACEFS_TIMESTAMP);
@@ -887,20 +907,21 @@
 }
 
 static int do_verify_filter(struct sqlhist_bison *sb, struct filter *filter,
-			    const char **system, const char **event)
+			    const char **system, const char **event,
+			    enum field_type *ftype)
 {
 	int ret;
 
 	if (filter->type == FILTER_OR ||
 	    filter->type == FILTER_AND) {
-		ret = do_verify_filter(sb, &filter->lval->filter, system, event);
+		ret = do_verify_filter(sb, &filter->lval->filter, system, event, ftype);
 		if (ret)
 			return ret;
-		return do_verify_filter(sb, &filter->rval->filter, system, event);
+		return do_verify_filter(sb, &filter->rval->filter, system, event, ftype);
 	}
 	if (filter->type == FILTER_GROUP ||
 	    filter->type == FILTER_NOT_GROUP) {
-		return do_verify_filter(sb, &filter->lval->filter, system, event);
+		return do_verify_filter(sb, &filter->lval->filter, system, event, ftype);
 	}
 
 	/*
@@ -910,6 +931,7 @@
 	if (!*system && !*event) {
 		*system = filter->lval->field.system;
 		*event = filter->lval->field.event_name;
+		*ftype = filter->lval->field.ftype;
 		return 0;
 	}
 
@@ -921,7 +943,8 @@
 }
 
 static int verify_filter(struct sqlhist_bison *sb, struct filter *filter,
-			 const char **system, const char **event)
+			 const char **system, const char **event,
+			 enum field_type *ftype)
 {
 	int ret;
 
@@ -932,17 +955,17 @@
 	case FILTER_NOT_GROUP:
 		break;
 	default:
-		return do_verify_filter(sb, filter, system, event);
+		return do_verify_filter(sb, filter, system, event, ftype);
 	}
 
-	ret = do_verify_filter(sb, &filter->lval->filter, system, event);
+	ret = do_verify_filter(sb, &filter->lval->filter, system, event, ftype);
 	if (ret)
 		return ret;
 
 	switch (filter->type) {
 	case FILTER_OR:
 	case FILTER_AND:
-		return do_verify_filter(sb, &filter->rval->filter, system, event);
+		return do_verify_filter(sb, &filter->rval->filter, system, event, ftype);
 	default:
 		return 0;
 	}
@@ -1260,7 +1283,7 @@
 
 static int verify_field_type(struct tep_handle *tep,
 			     struct sqlhist_bison *sb,
-			     struct expr *expr)
+			     struct expr *expr, int *cnt)
 {
 	struct field *field = &expr->field;
 	struct tep_event *event;
@@ -1339,6 +1362,17 @@
 		if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY))
 			goto fail_type;
 		ret = TRACEFS_HIST_KEY_LOG;
+	} else if (!strncmp(type, "buckets", 7)) {
+		if (type[7] != '=' || !isdigit(type[8])) {
+			parse_error(sb, field->raw,
+				    "buckets type must have '=[number]' after it\n");
+			ret = -1;
+			goto out;
+		}
+		*cnt = atoi(&type[8]);
+		if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY))
+			goto fail_type;
+		ret = TRACEFS_HIST_KEY_BUCKETS;
 	} else {
 		parse_error(sb, field->raw,
 			    "Cast of '%s' to unknown type '%s'\n",
@@ -1449,17 +1483,19 @@
 		if (expr->type == EXPR_FIELD) {
 			ret = -1;
 			field = &expr->field;
-			if (field->system == start_system &&
+			if (field->ftype != FIELD_TO &&
+			    field->system == start_system &&
 			    field->event_name == start_event) {
 				int type;
-				type = verify_field_type(tep, table->sb, expr);
+				int cnt = 0;
+				type = verify_field_type(tep, table->sb, expr, &cnt);
 				if (type < 0)
 					goto free;
 				if (type != HIST_COUNTER_TYPE)
 					non_val = true;
 				ret = synth_add_start_field(synth,
 						field->field, field->label,
-						type);
+						type, cnt);
 			} else if (table->to) {
 				ret = tracefs_synth_add_end_field(synth,
 						field->field, field->label);
@@ -1498,16 +1534,18 @@
 	for (expr = table->where; expr; expr = expr->next) {
 		const char *filter_system = NULL;
 		const char *filter_event = NULL;
+		enum field_type ftype = FIELD_NONE;
 		bool *started;
 		bool start;
 
 		ret = verify_filter(table->sb, &expr->filter, &filter_system,
-				    &filter_event);
+				    &filter_event, &ftype);
 		if (ret < 0)
 			goto free;
 
 		start = filter_system == start_system &&
-			filter_event == start_event;
+			filter_event == start_event &&
+			ftype != FIELD_TO;
 
 		if (start)
 			started = &started_start;
diff --git a/src/tracefs-tools.c b/src/tracefs-tools.c
index 489be28..8e7b46d 100644
--- a/src/tracefs-tools.c
+++ b/src/tracefs-tools.c
@@ -1055,7 +1055,7 @@
  out:
 	tracefs_put_tracing_file(tracer_path);
 	close(fd);
-	return ret;
+	return ret > 0 ? 0 : ret;
 }
 
 int  tracefs_tracer_clear(struct tracefs_instance *instance)
diff --git a/src/tracefs-utils.c b/src/tracefs-utils.c
index 7b1f816..9acf2ad 100644
--- a/src/tracefs-utils.c
+++ b/src/tracefs-utils.c
@@ -29,6 +29,7 @@
 #define STR(x) _STR(x)
 
 static int log_level = TEP_LOG_CRITICAL;
+static char *custom_tracing_dir;
 
 /**
  * tracefs_set_loglevel - set log level of the library
@@ -84,13 +85,8 @@
 	return ret;
 }
 
-/**
- * trace_find_tracing_dir - Find tracing directory
- *
- * Returns string containing the full path to the system's tracing directory.
- * The string must be freed by free()
- */
-__hidden char *trace_find_tracing_dir(void)
+/* Exported for testing purpose only */
+__hidden char *find_tracing_dir(bool debugfs, bool mount)
 {
 	char *debug_str = NULL;
 	char fspath[PATH_MAX+1];
@@ -109,9 +105,11 @@
 		      STR(PATH_MAX)
 		      "s %99s %*s %*d %*d\n",
 		      fspath, type) == 2) {
-		if (strcmp(type, "tracefs") == 0)
+		if (!debugfs && strcmp(type, "tracefs") == 0)
 			break;
 		if (!debug_str && strcmp(type, "debugfs") == 0) {
+			if (debugfs)
+				break;
 			debug_str = strdup(fspath);
 			if (!debug_str) {
 				fclose(fp);
@@ -121,14 +119,21 @@
 	}
 	fclose(fp);
 
-	if (strcmp(type, "tracefs") != 0) {
-		if (mount_tracefs() < 0) {
+	if (debugfs) {
+		if (strcmp(type, "debugfs") != 0) {
+			if (!mount || mount_debugfs() < 0)
+				return NULL;
+			strcpy(fspath, DEBUGFS_PATH);
+		}
+	} else if (strcmp(type, "tracefs") != 0) {
+		if (!mount || mount_tracefs() < 0) {
 			if (debug_str) {
 				strncpy(fspath, debug_str, PATH_MAX);
 				fspath[PATH_MAX] = 0;
 			} else {
-				if (mount_debugfs() < 0) {
-					tracefs_warning("debugfs not mounted, please mount");
+				if (!mount || mount_debugfs() < 0) {
+					if (mount)
+						tracefs_warning("debugfs not mounted, please mount");
 					free(debug_str);
 					return NULL;
 				}
@@ -156,6 +161,86 @@
 }
 
 /**
+ * tracefs_tracing_dir_is_mounted - test if the tracing dir is already mounted
+ * @mount: Mount it if it is not already mounted
+ * @path: the path to the tracing directory if mounted or was mounted
+ *
+ * Returns 1 if the tracing directory is already mounted and 0 if it is not.
+ * If @mount is set and it fails to mount, it returns -1.
+ *
+ * If path is not NULL, and the tracing directory is or was mounted, it holds
+ * the path to the tracing directory. It must not be freed.
+ */
+int tracefs_tracing_dir_is_mounted(bool mount, const char **path)
+{
+	const char *dir;
+
+	dir = find_tracing_dir(false, false);
+	if (dir) {
+		if (path)
+			*path = dir;
+		return 1;
+	}
+	if (!mount)
+		return 0;
+
+	dir = find_tracing_dir(false, mount);
+	if (!dir)
+		return -1;
+	if (path)
+		*path = dir;
+	return 0;
+}
+
+/**
+ * trace_find_tracing_dir - Find tracing directory
+ * @debugfs: Boolean to just return the debugfs directory
+ *
+ * Returns string containing the full path to the system's tracing directory.
+ * The string must be freed by free()
+ */
+__hidden char *trace_find_tracing_dir(bool debugfs)
+{
+	return find_tracing_dir(debugfs, false);
+}
+
+/**
+ * tracefs_set_tracing_dir - Set location of the tracing directory
+ * @tracing_dir: full path to the system's tracing directory mount point.
+ *
+ * Set the location to the system's tracing directory. This API should be used
+ * to set a custom location of the tracing directory. There is no need to call
+ * it if the location is standard, in that case the library will auto detect it.
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+int tracefs_set_tracing_dir(char *tracing_dir)
+{
+	if (custom_tracing_dir) {
+		free(custom_tracing_dir);
+		custom_tracing_dir = NULL;
+	}
+
+	if (tracing_dir) {
+		custom_tracing_dir = strdup(tracing_dir);
+		if (!custom_tracing_dir)
+			return -1;
+	}
+
+	return 0;
+}
+
+/* Used to check if the directory is still mounted */
+static int test_dir(const char *dir, const char *file)
+{
+	char path[strlen(dir) + strlen(file) + 2];
+	struct stat st;
+
+	sprintf(path, "%s/%s", dir, file);
+	return stat(path, &st) < 0 ? 0 : 1;
+}
+
+/**
  * tracefs_tracing_dir - Get tracing directory
  *
  * Returns string containing the full path to the system's tracing directory.
@@ -165,14 +250,36 @@
 {
 	static const char *tracing_dir;
 
-	if (tracing_dir)
+	/* Do not check custom_tracing_dir */
+	if (custom_tracing_dir)
+		return custom_tracing_dir;
+
+	if (tracing_dir && test_dir(tracing_dir, "trace"))
 		return tracing_dir;
 
-	tracing_dir = trace_find_tracing_dir();
+	tracing_dir = find_tracing_dir(false, true);
 	return tracing_dir;
 }
 
 /**
+ * tracefs_debug_dir - Get debugfs directory path
+ *
+ * Returns string containing the full path to the system's debugfs directory.
+ *
+ * The returned string must *not* be freed.
+ */
+const char *tracefs_debug_dir(void)
+{
+	static const char *debug_dir;
+
+	if (debug_dir && test_dir(debug_dir, "tracing"))
+		return debug_dir;
+
+	debug_dir = find_tracing_dir(true, true);
+	return debug_dir;
+}
+
+/**
  * tracefs_get_tracing_file - Get tracing file
  * @name: tracing file name
  *
@@ -487,3 +594,31 @@
 	list--;
 	return (int)*(unsigned long *)list;
 }
+
+/**
+ * tracefs_tracer_available - test if a tracer is available
+ * @tracing_dir: The directory that contains the tracing directory
+ * @tracer: The name of the tracer
+ *
+ * Return true if the tracer is available
+ */
+bool tracefs_tracer_available(const char *tracing_dir, const char *tracer)
+{
+	bool ret = false;
+	char **tracers = NULL;
+	int i;
+
+	tracers = tracefs_tracers(tracing_dir);
+	if (!tracers)
+		return false;
+
+	for (i = 0; tracers[i]; i++) {
+		if (strcmp(tracer, tracers[i]) == 0) {
+			ret = true;
+			break;
+		}
+	}
+
+	tracefs_list_free(tracers);
+	return ret;
+}
diff --git a/utest/Makefile b/utest/Makefile
index 74bf7e6..f10b709 100644
--- a/utest/Makefile
+++ b/utest/Makefile
@@ -15,27 +15,19 @@
 	$(obj)/lib/libtracefs.a
 
 OBJS := $(OBJS:%.o=$(bdir)/%.o)
-DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
 
 $(bdir):
 	@mkdir -p $(bdir)
 
 $(OBJS): | $(bdir)
-$(DEPS): | $(bdir)
 
-$(bdir)/trace-utest: $(OBJS)
+$(bdir)/trace-utest: $(OBJS) $(obj)/lib/libtracefs.a
 	$(Q)$(do_app_build)
 
 $(bdir)/%.o: %.c
 	$(Q)$(call do_fpic_compile)
 
-$(DEPS): $(bdir)/.%.d: %.c
-	$(Q)$(CC) -M $(CPPFLAGS) $(CFLAGS) $< > $@
-	$(Q)$(CC) -M -MT $(bdir)/$*.o $(CPPFLAGS) $(CFLAGS) $< > $@
-
-$(OBJS): $(bdir)/%.o : $(bdir)/.%.d
-
-dep_includes := $(wildcard $(DEPS))
+-include .*.d
 
 test: $(TARGETS)
 
diff --git a/utest/README b/utest/README
index ed92042..647e460 100644
--- a/utest/README
+++ b/utest/README
@@ -13,3 +13,6 @@
 	libcunit1
 	libcunit1-doc
 	libcunit1-dev
+
+ openSUSE and SLE:
+	cunit-devel
diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c
index 3f63837..e0e3c07 100644
--- a/utest/tracefs-utest.c
+++ b/utest/tracefs-utest.c
@@ -11,13 +11,18 @@
 #include <time.h>
 #include <dirent.h>
 #include <ftw.h>
+#include <libgen.h>
+#include <kbuffer.h>
+#include <pthread.h>
+
+#include <sys/mount.h>
 
 #include <CUnit/CUnit.h>
 #include <CUnit/Basic.h>
 
 #include "tracefs.h"
 
-#define TRACEFS_SUITE		"trasefs library"
+#define TRACEFS_SUITE		"tracefs library"
 #define TEST_INSTANCE_NAME	"cunit_test_iter"
 #define TEST_TRACE_DIR		"/tmp/trace_utest.XXXXXX"
 #define TEST_ARRAY_SIZE		5000
@@ -44,6 +49,10 @@
 #define SQL_5_SQL	"select end.common_pid as pid, (end.common_timestamp.usecs - start.common_timestamp.usecs) as irq_lat from irq_disable as start join irq_enable as end on start.common_pid = end.common_pid, start.parent_offs == end.parent_offs where start.common_pid != 0"
 #define SQL_5_START	"irq_disable"
 
+#define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug"
+#define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing"
+#define TRACEFS_DEFAULT2_PATH "/sys/kernel/debug/tracing"
+
 static struct tracefs_instance *test_instance;
 static struct tep_handle *test_tep;
 struct test_sample {
@@ -86,20 +95,51 @@
 	return 0;
 }
 
+static cpu_set_t *cpuset_save;
+static cpu_set_t *cpuset;
+static int cpu_size;
+
+static void save_affinity(void)
+{
+	int cpus;
+
+	cpus = sysconf(_SC_NPROCESSORS_CONF);
+	cpuset_save = CPU_ALLOC(cpus);
+	cpuset = CPU_ALLOC(cpus);
+	cpu_size = CPU_ALLOC_SIZE(cpus);
+	CU_TEST(cpuset_save != NULL && cpuset != NULL);
+	CU_TEST(sched_getaffinity(0, cpu_size, cpuset_save) == 0);
+}
+
+static void thread_affinity(void)
+{
+	sched_setaffinity(0, cpu_size, cpuset_save);
+}
+
+static void reset_affinity(void)
+{
+	sched_setaffinity(0, cpu_size, cpuset_save);
+	CPU_FREE(cpuset_save);
+	CPU_FREE(cpuset);
+}
+
+static void set_affinity(int cpu)
+{
+	CPU_ZERO_S(cpu_size, cpuset);
+	CPU_SET_S(cpu, cpu_size, cpuset);
+	CU_TEST(sched_setaffinity(0, cpu_size, cpuset) == 0);
+	sched_yield(); /* Force schedule */
+}
+
 static void test_iter_write(struct tracefs_instance *instance)
 {
-	int cpus = sysconf(_SC_NPROCESSORS_CONF);
-	cpu_set_t *cpuset, *cpusave;
-	int cpu_size;
 	char *path;
 	int i, fd;
+	int cpus;
 	int ret;
-	cpuset = CPU_ALLOC(cpus);
-	cpusave = CPU_ALLOC(cpus);
-	cpu_size = CPU_ALLOC_SIZE(cpus);
-	CPU_ZERO_S(cpu_size, cpuset);
 
-	sched_getaffinity(0, cpu_size, cpusave);
+	cpus = sysconf(_SC_NPROCESSORS_CONF);
+	save_affinity();
 
 	path = tracefs_instance_get_file(instance, "trace_marker");
 	CU_TEST(path != NULL);
@@ -113,17 +153,13 @@
 		if (!test_array[i].value)
 			test_array[i].value++;
 		CU_TEST(test_array[i].cpu < cpus);
-		CPU_ZERO_S(cpu_size, cpuset);
-		CPU_SET(test_array[i].cpu, cpuset);
-		sched_setaffinity(0, cpu_size, cpuset);
+		set_affinity(test_array[i].cpu);
 		ret = write(fd, test_array + i, sizeof(struct test_sample));
 		CU_TEST(ret == sizeof(struct test_sample));
 	}
 
-	sched_setaffinity(0, cpu_size, cpusave);
+	reset_affinity();
 	close(fd);
-	CPU_FREE(cpuset);
-	CPU_FREE(cpusave);
 }
 
 
@@ -382,6 +418,538 @@
 	test_instance_trace_sql(test_instance);
 }
 
+struct test_cpu_data {
+	struct tracefs_instance		*instance;
+	struct tracefs_cpu		*tcpu;
+	struct kbuffer			*kbuf;
+	struct tep_handle		*tep;
+	unsigned long long		missed_events;
+	void				*buf;
+	int				events_per_buf;
+	int				bufsize;
+	int				data_size;
+	int				this_pid;
+	int				fd;
+	bool				done;
+};
+
+static void cleanup_trace_cpu(struct test_cpu_data *data)
+{
+	close(data->fd);
+	tep_free(data->tep);
+	tracefs_cpu_close(data->tcpu);
+	free(data->buf);
+	kbuffer_free(data->kbuf);
+}
+
+#define EVENT_SYSTEM "syscalls"
+#define EVENT_NAME  "sys_enter_getppid"
+
+static int setup_trace_cpu(struct tracefs_instance *instance, struct test_cpu_data *data)
+{
+	struct tep_format_field **fields;
+	struct tep_event *event;
+	char tmpfile[] = "/tmp/utest-libtracefsXXXXXX";
+	int max = 0;
+	int ret;
+	int i;
+
+	/* Make sure tracing is on */
+	tracefs_trace_on(instance);
+
+	memset (data, 0, sizeof(*data));
+
+	data->instance = instance;
+
+	data->fd = mkstemp(tmpfile);
+	CU_TEST(data->fd >= 0);
+	unlink(tmpfile);
+	if (data->fd < 0)
+		return -1;
+
+	data->tep = tracefs_local_events(NULL);
+	CU_TEST(data->tep != NULL);
+	if (!data->tep)
+		goto fail;
+
+	data->tcpu = tracefs_cpu_open(instance, 0, true);
+	CU_TEST(data->tcpu != NULL);
+	if (!data->tcpu)
+		goto fail;
+
+	data->bufsize = tracefs_cpu_read_size(data->tcpu);
+
+	data->buf = calloc(1, data->bufsize);
+	CU_TEST(data->buf != NULL);
+	if (!data->buf)
+		goto fail;
+
+	data->kbuf = kbuffer_alloc(sizeof(long) == 8, !tep_is_bigendian());
+	CU_TEST(data->kbuf != NULL);
+	if (!data->kbuf)
+		goto fail;
+
+	data->data_size = data->bufsize - kbuffer_start_of_data(data->kbuf);
+
+	tracefs_instance_file_clear(instance, "trace");
+
+	event = tep_find_event_by_name(data->tep, EVENT_SYSTEM, EVENT_NAME);
+	CU_TEST(event != NULL);
+	if (!event)
+		goto fail;
+
+	fields = tep_event_fields(event);
+	CU_TEST(fields != NULL);
+	if (!fields)
+		goto fail;
+
+	for (i = 0; fields[i]; i++) {
+		int end = fields[i]->offset + fields[i]->size;
+		if (end > max)
+			max = end;
+	}
+	free(fields);
+
+	CU_TEST(max != 0);
+	if (!max)
+		goto fail;
+
+	data->events_per_buf = data->data_size / max;
+
+	data->this_pid = getpid();
+	ret = tracefs_event_enable(instance, EVENT_SYSTEM, EVENT_NAME);
+	CU_TEST(ret == 0);
+	if (ret)
+		goto fail;
+
+
+	save_affinity();
+	set_affinity(0);
+
+	return 0;
+ fail:
+	cleanup_trace_cpu(data);
+	return -1;
+}
+
+static void shutdown_trace_cpu(struct test_cpu_data *data)
+{
+	struct tracefs_instance *instance = data->instance;
+	int ret;
+
+	reset_affinity();
+
+	ret = tracefs_event_disable(instance, EVENT_SYSTEM, EVENT_NAME);
+	CU_TEST(ret == 0);
+
+	cleanup_trace_cpu(data);
+}
+
+static void call_getppid(int cnt)
+{
+	int i;
+
+	for (i = 0; i < cnt; i++)
+		getppid();
+}
+
+static void test_cpu_read(struct test_cpu_data *data, int expect)
+{
+	struct tracefs_cpu *tcpu = data->tcpu;
+	struct kbuffer *kbuf = data->kbuf;
+	struct tep_record record;
+	void *buf = data->buf;
+	unsigned long long ts;
+	bool first = true;
+	int pid;
+	int ret;
+	int cnt = 0;
+
+	call_getppid(expect);
+
+	for (;;) {
+		ret = tracefs_cpu_read(tcpu, buf, false);
+		CU_TEST(ret > 0 || !first);
+		if (ret <= 0)
+			break;
+		first = false;
+		ret = kbuffer_load_subbuffer(kbuf, buf);
+		CU_TEST(ret == 0);
+		for (;;) {
+			record.data = kbuffer_read_event(kbuf, &ts);
+			if (!record.data)
+				break;
+			record.ts = ts;
+			pid = tep_data_pid(data->tep, &record);
+			if (pid == data->this_pid)
+				cnt++;
+			kbuffer_next_event(kbuf, NULL);
+		}
+	}
+	CU_TEST(cnt == expect);
+}
+
+static void test_instance_trace_cpu_read(struct tracefs_instance *instance)
+{
+	struct test_cpu_data data;
+
+	if (setup_trace_cpu(instance, &data))
+		return;
+
+	test_cpu_read(&data, 1);
+	test_cpu_read(&data, data.events_per_buf / 2);
+	test_cpu_read(&data, data.events_per_buf);
+	test_cpu_read(&data, data.events_per_buf + 1);
+	test_cpu_read(&data, data.events_per_buf * 50);
+
+	shutdown_trace_cpu(&data);
+}
+
+static void test_trace_cpu_read(void)
+{
+	test_instance_trace_cpu_read(NULL);
+	test_instance_trace_cpu_read(test_instance);
+}
+
+struct follow_data {
+	struct tep_event *sched_switch;
+	struct tep_event *sched_waking;
+	struct tep_event *function;
+	int missed;
+};
+
+static int switch_callback(struct tep_event *event, struct tep_record *record,
+			   int cpu, void *data)
+{
+	struct follow_data *fdata = data;
+
+	CU_TEST(cpu == record->cpu);
+	CU_TEST(event->id == fdata->sched_switch->id);
+	return 0;
+}
+
+static int waking_callback(struct tep_event *event, struct tep_record *record,
+			   int cpu, void *data)
+{
+	struct follow_data *fdata = data;
+
+	CU_TEST(cpu == record->cpu);
+	CU_TEST(event->id == fdata->sched_waking->id);
+	return 0;
+}
+
+static int function_callback(struct tep_event *event, struct tep_record *record,
+			     int cpu, void *data)
+{
+	struct follow_data *fdata = data;
+
+	CU_TEST(cpu == record->cpu);
+	CU_TEST(event->id == fdata->function->id);
+	return 0;
+}
+
+static int missed_callback(struct tep_event *event, struct tep_record *record,
+			     int cpu, void *data)
+{
+	struct follow_data *fdata = data;
+
+	fdata->missed = record->missed_events;
+	return 0;
+}
+
+static int all_callback(struct tep_event *event, struct tep_record *record,
+			int cpu, void *data)
+{
+	struct follow_data *fdata = data;
+
+	CU_TEST(fdata->missed == record->missed_events);
+	fdata->missed = 0;
+	return 0;
+}
+
+static void *stop_thread(void *arg)
+{
+	struct tracefs_instance *instance = arg;
+
+	sleep(1);
+	tracefs_iterate_stop(instance);
+	return NULL;
+}
+
+static void test_instance_follow_events(struct tracefs_instance *instance)
+{
+	struct follow_data fdata;
+	struct tep_handle *tep;
+	pthread_t thread;
+	int ret;
+
+	memset(&fdata, 0, sizeof(fdata));
+
+	tep = tracefs_local_events(NULL);
+	CU_TEST(tep != NULL);
+	if (!tep)
+		return;
+
+	fdata.sched_switch = tep_find_event_by_name(tep, "sched", "sched_switch");
+	CU_TEST(fdata.sched_switch != NULL);
+	if (!fdata.sched_switch)
+		return;
+
+	fdata.sched_waking = tep_find_event_by_name(tep, "sched", "sched_waking");
+	CU_TEST(fdata.sched_waking != NULL);
+	if (!fdata.sched_waking)
+		return;
+
+	fdata.function = tep_find_event_by_name(tep, "ftrace", "function");
+	CU_TEST(fdata.function != NULL);
+	if (!fdata.function)
+		return;
+
+	ret = tracefs_follow_event(tep, instance, "sched", "sched_switch",
+				   switch_callback, &fdata);
+	CU_TEST(ret == 0);
+
+	ret = tracefs_follow_event(tep, instance, "sched", "sched_waking",
+				   waking_callback, &fdata);
+	CU_TEST(ret == 0);
+
+	ret = tracefs_follow_event(tep, instance, "ftrace", "function",
+				   function_callback, &fdata);
+	CU_TEST(ret == 0);
+
+	ret = tracefs_follow_missed_events(instance, missed_callback, &fdata);
+	CU_TEST(ret == 0);
+
+	ret = tracefs_event_enable(instance, "sched", "sched_switch");
+	CU_TEST(ret == 0);
+
+	ret = tracefs_event_enable(instance, "sched", "sched_waking");
+	CU_TEST(ret == 0);
+
+	ret = tracefs_tracer_set(instance, TRACEFS_TRACER_FUNCTION);
+	CU_TEST(ret == 0);
+
+	pthread_create(&thread, NULL, stop_thread, instance);
+
+	ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, all_callback, &fdata);
+	CU_TEST(ret == 0);
+
+	pthread_join(thread, NULL);
+
+	tracefs_tracer_clear(instance);
+	tracefs_event_disable(instance, NULL, NULL);
+}
+
+static void test_follow_events(void)
+{
+	test_instance_follow_events(NULL);
+	test_instance_follow_events(test_instance);
+}
+
+extern char *find_tracing_dir(bool debugfs, bool mount);
+static void test_mounting(void)
+{
+	const char *tracing_dir;
+	const char *debug_dir;
+	struct stat st;
+	char *save_tracing = NULL;
+	char *save_debug = NULL;
+	char *path;
+	char *dir;
+	int ret;
+
+	/* First, unmount all instances of debugfs */
+	do {
+		dir = find_tracing_dir(true, false);
+		if (dir) {
+			ret = umount(dir);
+			CU_TEST(ret == 0);
+			if (ret < 0)
+				return;
+			/* Save the first instance that's not /sys/kernel/debug */
+			if (!save_debug && strcmp(dir, DEBUGFS_DEFAULT_PATH) != 0)
+				save_debug = dir;
+			else
+				free(dir);
+		}
+	} while (dir);
+
+	/* Next, unmount all instances of tracefs */
+	do {
+		dir = find_tracing_dir(false, false);
+		if (dir) {
+			ret = umount(dir);
+			CU_TEST(ret == 0);
+			if (ret < 0)
+				return;
+			/* Save the first instance that's not in /sys/kernel/ */
+			if (!save_tracing && strncmp(dir, "/sys/kernel/", 12) != 0)
+				save_tracing = dir;
+			else
+				free(dir);
+		}
+	} while (dir);
+
+	/* Mount first the tracing dir (which should mount at /sys/kernel/tracing */
+	tracing_dir = tracefs_tracing_dir();
+	CU_TEST(tracing_dir != NULL);
+	if (tracing_dir != NULL) {
+		CU_TEST(strcmp(tracing_dir, TRACEFS_DEFAULT_PATH) == 0 ||
+			strcmp(tracing_dir, TRACEFS_DEFAULT2_PATH) == 0);
+		if (strncmp(tracing_dir, "/sys/kernel/", 12) != 0)
+			printf("Tracing directory mounted at '%s'\n",
+			       tracing_dir);
+
+		/* Make sure the directory has content.*/
+		asprintf(&path, "%s/trace", tracing_dir);
+		CU_TEST(stat(path, &st) == 0);
+		free(path);
+	}
+
+	/* Now mount debugfs dir, which should mount at /sys/kernel/debug */
+	debug_dir = tracefs_debug_dir();
+	CU_TEST(debug_dir != NULL);
+	if (debug_dir != NULL) {
+		CU_TEST(strcmp(debug_dir, DEBUGFS_DEFAULT_PATH) == 0);
+		if (strcmp(debug_dir, DEBUGFS_DEFAULT_PATH) != 0)
+			printf("debug directory mounted at '%s'\n",
+			       debug_dir);
+
+		/* Make sure the directory has content.*/
+		asprintf(&path, "%s/tracing", debug_dir);
+		CU_TEST(stat(path, &st) == 0);
+		free(path);
+	}
+
+	if (save_debug)
+		mount("debugfs", save_debug, "debugfs", 0, NULL);
+
+	if (save_tracing &&
+	    (!save_debug || strncmp(save_debug, save_tracing, strlen(save_debug)) != 0))
+		mount("tracefs", save_tracing, "tracefs", 0, NULL);
+
+	free(save_debug);
+	free(save_tracing);
+}
+
+static int read_trace_cpu_file(struct test_cpu_data *data)
+{
+	unsigned long long ts;
+	struct tep_record record;
+	struct kbuffer *kbuf = data->kbuf;
+	void *buf = data->buf;
+	bool first = true;
+	int bufsize = data->bufsize;
+	int fd = data->fd;
+	int missed;
+	int pid;
+	int ret;
+	int cnt = 0;
+
+	ret = lseek64(fd, 0, SEEK_SET);
+	CU_TEST(ret == 0);
+	if (ret)
+		return -1;
+
+	for (;;) {
+		ret = read(fd, buf, bufsize);
+		CU_TEST(ret > 0 || !first);
+		if (ret <= 0)
+			break;
+		first = false;
+
+		ret = kbuffer_load_subbuffer(kbuf, buf);
+		CU_TEST(ret == 0);
+		missed = kbuffer_missed_events(kbuf);
+		if (missed)
+			printf("missed events %d\n", missed);
+		for (;;) {
+			record.data = kbuffer_read_event(kbuf, &ts);
+			if (!record.data)
+				break;
+			record.ts = ts;
+			pid = tep_data_pid(data->tep, &record);
+			if (pid == data->this_pid)
+				cnt++;
+			kbuffer_next_event(kbuf, NULL);
+		}
+	}
+	return ret == 0 ? cnt : ret;
+}
+
+static void *trace_cpu_thread(void *arg)
+{
+	struct test_cpu_data *data = arg;
+	struct tracefs_cpu *tcpu = data->tcpu;
+	int fd = data->fd;
+	long ret = 0;
+
+	thread_affinity();
+
+	while (!data->done && ret >= 0) {
+		ret = tracefs_cpu_write(tcpu, fd, false);
+		if (ret < 0 && errno == EAGAIN)
+			ret = 0;
+	}
+	if (ret >= 0 || errno == EAGAIN) {
+		do {
+			ret = tracefs_cpu_flush_write(tcpu, fd);
+		} while (ret > 0);
+	}
+
+	return (void *)ret;
+}
+
+static void test_cpu_pipe(struct test_cpu_data *data, int expect)
+{
+	pthread_t thread;
+	void *retval;
+	long ret;
+	int cnt;
+
+	tracefs_instance_file_clear(data->instance, "trace");
+	ftruncate(data->fd, 0);
+
+	data->done = false;
+
+	pthread_create(&thread, NULL, trace_cpu_thread, data);
+	sleep(1);
+
+	call_getppid(expect);
+
+	data->done = true;
+	tracefs_cpu_stop(data->tcpu);
+	pthread_join(thread, &retval);
+	ret = (long)retval;
+	CU_TEST(ret >= 0);
+
+	cnt = read_trace_cpu_file(data);
+
+	CU_TEST(cnt == expect);
+}
+
+static void test_instance_trace_cpu_pipe(struct tracefs_instance *instance)
+{
+	struct test_cpu_data data;
+
+	if (setup_trace_cpu(instance, &data))
+		return;
+
+	test_cpu_pipe(&data, 1);
+	test_cpu_pipe(&data, data.events_per_buf / 2);
+	test_cpu_pipe(&data, data.events_per_buf);
+	test_cpu_pipe(&data, data.events_per_buf + 1);
+	test_cpu_pipe(&data, data.events_per_buf * 1000);
+
+	shutdown_trace_cpu(&data);
+}
+
+static void test_trace_cpu_pipe(void)
+{
+	test_instance_trace_cpu_pipe(NULL);
+	test_instance_trace_cpu_pipe(test_instance);
+}
+
 static struct tracefs_dynevent **get_dynevents_check(enum tracefs_dynevent_type types, int count)
 {
 	struct tracefs_dynevent **devents;
@@ -1404,6 +1972,9 @@
 	tracers = tracefs_tracers(tdir);
 	CU_TEST(tracers != NULL);
 
+	for (i = 0; tracers[i]; i++)
+		CU_TEST(tracefs_tracer_available(tdir, tracers[i]));
+
 	tfile = tracefs_instance_file_read(NULL, ALL_TRACERS, NULL);
 
 	tracer = strtok(tfile, " ");
@@ -1701,8 +2272,11 @@
 
 static void test_custom_trace_dir(void)
 {
+	char *tdir = "/tmp/custom_tracefs";
 	struct tracefs_instance *instance;
 	char *dname = copy_trace_dir();
+	const char *trace_dir;
+	char *tfile;
 
 	instance = tracefs_instance_alloc(dname, NULL);
 	CU_TEST(instance != NULL);
@@ -1717,6 +2291,22 @@
 	tracefs_instance_free(instance);
 	del_trace_dir(dname);
 	free(dname);
+
+	trace_dir = tracefs_tracing_dir();
+	CU_TEST(trace_dir != NULL);
+	CU_TEST(tracefs_set_tracing_dir(tdir) == 0);
+	CU_TEST(strcmp(tdir, tracefs_tracing_dir()) == 0);
+	tfile = tracefs_get_tracing_file("trace");
+	CU_TEST(tfile != NULL);
+	CU_TEST(strcmp(tdir, dirname(tfile)) == 0);
+	free(tfile);
+
+	CU_TEST(tracefs_set_tracing_dir(NULL) == 0);
+	CU_TEST(strcmp(trace_dir, tracefs_tracing_dir()) == 0);
+	tfile = tracefs_get_tracing_file("trace");
+	CU_TEST(tfile != NULL);
+	CU_TEST(strcmp(trace_dir, dirname(tfile)) == 0);
+	free(tfile);
 }
 
 static int test_suite_destroy(void)
@@ -1750,6 +2340,12 @@
 		fprintf(stderr, "Suite \"%s\" cannot be ceated\n", TRACEFS_SUITE);
 		return;
 	}
+
+	CU_add_test(suite, "Test tracefs/debugfs mounting", test_mounting);
+	CU_add_test(suite, "trace cpu read",
+		    test_trace_cpu_read);
+	CU_add_test(suite, "trace cpu pipe",
+		    test_trace_cpu_pipe);
 	CU_add_test(suite, "trace sql",
 		    test_trace_sql);
 	CU_add_test(suite, "tracing file / directory APIs",
@@ -1762,6 +2358,10 @@
 		    test_system_event);
 	CU_add_test(suite, "tracefs_iterate_raw_events API",
 		    test_iter_raw_events);
+
+	/* Follow events test must be after the iterate raw events above */
+	CU_add_test(suite, "Follow events", test_follow_events);
+
 	CU_add_test(suite, "tracefs_tracers API",
 		    test_tracers);
 	CU_add_test(suite, "tracefs_local events API",