Upgrade trace-cmd to trace-cmd-v3.3.1

This project was upgraded with external_updater.
Usage: tools/external_updater/updater.sh update external/trace-cmd
For more info, check https://cs.android.com/android/platform/superproject/main/+/main:tools/external_updater/README.md

Manual additions to Android.bp:

 * add a few new .c files for compilation
 * -Dstrstrip=strstrip_trace: libtracefs also provides strstrip();
   it tries to hide this by visibility attributes, but this doesn't
   affect static linking. Hack in a rename for trace-cmd's version, so
   we dodge the naming conflict.

Bug: 393387208
Test: TreeHugger
Change-Id: Id5dc038c4a4a0a115f84de347eb34d94f5360b9f
diff --git a/Android.bp b/Android.bp
index 112786b..79f7f6a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -64,15 +64,18 @@
         "lib/trace-cmd/trace-blk-hack.c",
         "lib/trace-cmd/trace-compress.c",
         "lib/trace-cmd/trace-compress-zlib.c",
+        "lib/trace-cmd/trace-filter.c",
         "lib/trace-cmd/trace-filter-hash.c",
         "lib/trace-cmd/trace-ftrace.c",
         "lib/trace-cmd/trace-hash.c",
         "lib/trace-cmd/trace-hooks.c",
         "lib/trace-cmd/trace-input.c",
+        "lib/trace-cmd/trace-maps.c",
         "lib/trace-cmd/trace-msg.c",
         "lib/trace-cmd/trace-output.c",
         "lib/trace-cmd/trace-perf.c",
         "lib/trace-cmd/trace-plugin.c",
+        "lib/trace-cmd/trace-rbtree.c",
         "lib/trace-cmd/trace-recorder.c",
         "lib/trace-cmd/trace-timesync.c",
         "lib/trace-cmd/trace-timesync-kvm.c",
@@ -150,6 +153,9 @@
         "-D_GNU_SOURCE",
         "-DNO_AUDIT",
         "-DVSOCK",
+        // strstrip() is defined in libtracefs too; change our name to avoid
+        // duplicate symbols when statically linking.
+        "-Dstrstrip=strstrip_trace",
         "-Wno-unused-parameter",
         "-Wno-macro-redefined",
         "-Wno-visibility",
diff --git a/Documentation/libtracecmd/install-docs.sh.in b/Documentation/libtracecmd/install-docs.sh.in
new file mode 100755
index 0000000..eca9b1f
--- /dev/null
+++ b/Documentation/libtracecmd/install-docs.sh.in
@@ -0,0 +1,20 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+for section in 1 3 5; do
+    while IFS= read -r -d '' man; do
+        [ ! -d "${DESTDIR}@MANDIR@/man${section}" ] && install -d "${DESTDIR}@MANDIR@/man${section}"
+
+        echo Installing "${man}" to "${DESTDIR}@MANDIR@/man${section}"
+        install -m 0644 "${man}" "${DESTDIR}@MANDIR@/man${section}/"
+    done< <(find "@SRCDIR@" -name "*\.${section}" -type f -print0)
+done
+
+while IFS= read -r -d '' html; do
+    [ ! -d "${DESTDIR}@HTMLDIR@"  ] && install -d "${DESTDIR}@HTMLDIR@"
+
+    echo Installing "${html}" to "${DESTDIR}@HTMLDIR@"
+    install -m 0644 "${html}" "${DESTDIR}@HTMLDIR@"
+done< <(find "@SRCDIR@" -name "*\.html" -type f -print0)
diff --git a/Documentation/libtracecmd/libtracecmd-files.txt b/Documentation/libtracecmd/libtracecmd-files.txt
index 2de5d6d..f8292cb 100644
--- a/Documentation/libtracecmd/libtracecmd-files.txt
+++ b/Documentation/libtracecmd/libtracecmd-files.txt
@@ -4,7 +4,7 @@
 NAME
 ----
 tracecmd_open, tracecmd_open_fd, tracecmd_open_head, tracecmd_init_data,
-tracecmd_close - Open and close a trace file.
+tracecmd_close, tracecmd_set_private, tracecmd_get_private - Open and close a trace file.
 
 SYNOPSIS
 --------
@@ -17,12 +17,14 @@
 struct tracecmd_input pass:[*]*tracecmd_open_head*(const char pass:[*]_file_, int _flags_);
 int *tracecmd_init_data*(struct tracecmd_input pass:[*]_handle_);
 void *tracecmd_close*(struct tracecmd_input pass:[*]_handle_);
+void *tracecmd_set_private*(struct tracecmd_input pass:[*]_handle_, void pass:[*]_data_);
+void pass:[*]*tracecmd_get_private*(struct tracecmd_input pass:[*]_handle_);
 --
 
 DESCRIPTION
 -----------
 This set of APIs can be used to open and close a trace file recorded by
-_trace-cmd(1)_ and containing tracing information from ftrace, the official
+*trace-cmd(1)* and containing tracing information from ftrace, the official
 Linux kernel tracer. The opened file is represented by a _tracecmd_input_
 structure, all other library APIs that work with the file require a pointer
 to the structure. The APIs for opening a trace file have a _flag_ input
@@ -33,39 +35,47 @@
  TRACECMD_FL_LOAD_NO_SYSTEM_PLUGINS - Do not load system wide plugins, load only "local only"
 					plugins from user's home directory.
 
-The _tracecmd_open()_ function opens a given trace _file_, parses the
+The *tracecmd_open()* function opens a given trace _file_, parses the
 metadata headers from the file, allocates and initializes а _tracecmd_input_
 handler structure representing the file. It also initializes the handler
 for reading trace data from the file. The returned handler is ready to be
 used with _tracecmd_read__ APIs.
 
-The _tracecmd_open_fd()_ function does the same as _tracecmd_open()_, but
+The *tracecmd_open_fd()* function does the same as *tracecmd_open()*, but
 works with a file descriptor to a trace file, opened for reading.
 
-The _tracecmd_open_head()_ function is the same as _tracecmd_open()_, but
+The *tracecmd_open_head()* function is the same as *tracecmd_open()*, but
 does not initialize the handler for reading trace data. It reads and parses
-the metadata headers only. The _tracecmd_init_data()_ should be used before
+the metadata headers only. The *tracecmd_init_data()* should be used before
 using the _tracecmd_read__ APIs.
 
-The _tracecmd_init_data()_ function initializes a _handle_, allocated with
-_tracecmd_open_head()_, for reading trace data from the file associated with
+The *tracecmd_init_data()* function initializes a _handle_, allocated with
+*tracecmd_open_head()*, for reading trace data from the file associated with
 it. This API must be called before any of the _tracecmd_read__ APIs.
 
-The _tracecmd_close()_ function frees a _handle_, pointer to tracecmd_input
-structure, previously allocated with _tracecmd_open()_, _tracecmd_open_fd()_
-or _tracecmd_open_head()_ APIs.
+The *tracecmd_close()* function frees a _handle_, pointer to tracecmd_input
+structure, previously allocated with *tracecmd_open()*, *tracecmd_open_fd()*
+or *tracecmd_open_head()* APIs.
+
+The *tracecmd_set_private()* function allows to add specific _data_ to the
+_handle_ that can be retrieved later.
+
+The *tracecmd_get_private()* function will retrieve the _data_ set by
+*tracecmd_set_private()* for the given _handle_.
 
 RETURN VALUE
 ------------
-The _tracecmd_open()_, _tracecmd_open_fd()_ and _tracecmd_open_head()_
+The *tracecmd_open()*, *tracecmd_open_fd()* and *tracecmd_open_head()*
 functions return a pointer to tracecmd_input structure or NULL in case of
-an error. The returned structure must be free with _tracecmd_close()_.
-Note that if _tracecmd_open_fd()_ is used to allocate a tracecmd_input handler,
-when _tracecmd_close()_ is called to close it, that fd will be closed also.
+an error. The returned structure must be free with *tracecmd_close()*.
+Note that if *tracecmd_open_fd()* is used to allocate a tracecmd_input handler,
+when *tracecmd_close()* is called to close it, that fd will be closed also.
 
-The _tracecmd_init_data()_ function returns -1 in case of an error or
+The *tracecmd_init_data()* function returns -1 in case of an error or
 0 otherwise.
 
+The *tracecmd_get_private()* returns the _data_ set by *tracecmd_set_private()*.
+
 EXAMPLE
 -------
 [source,c]
@@ -75,32 +85,46 @@
 
 1. Open and initialise the trace file in а single step:
 
+#include <stdlib.h>
 #include <trace-cmd.h>
-...
-struct tracecmd_input *handle = tracecmd_open("trace.dat");
-	if (!handle) {
-		/* Failed to open trace.dat file */
+
+static int print_events(struct tracecmd_input *handle, struct tep_record *record, int cpu, void *data)
+{
+	static struct trace_seq seq;
+	struct tep_handle *tep = tracecmd_get_tep(handle);
+	const char *file = tracecmd_get_private(handle);
+
+	if (!seq.buffer)
+		trace_seq_init(&seq);
+
+	trace_seq_reset(&seq);
+	trace_seq_printf(&seq, "%s: ", file);
+	tep_print_event(tep, &seq, record, "%6.1000d [%03d] %s-%d %s: %s\n",
+			TEP_PRINT_TIME, TEP_PRINT_CPU, TEP_PRINT_COMM, TEP_PRINT_PID,
+			TEP_PRINT_NAME, TEP_PRINT_INFO);
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	struct tracecmd_input *handle;
+
+	if (argc < 2) {
+		printf("usage: %s trace.dat\n", argv[0]);
+		exit(-1);
 	}
-...
-	/* Read tracing data from the file, using the handle */
-...
+
+	handle = tracecmd_open(argv[i], 0);
+	if (!handle)
+		exit(-1);
+
+	tracecmd_set_private(handles[nr_handles], argv[i]);
+	tracecmd_iterate_events(handles, NULL, 0, print_events, NULL);
+
 	tracecmd_close(handle);
-...
-int fd;
-	fd = = open("trace.dat", O_RDONLY);
-	if (fd < 0) {
-		/* Failed to open trace file for reading */
-	}
-	handle = tracecmd_open_fd(fd);
-	if (!handle) {
-		close(fd);
-		/* Failed to initialise handler for reading the trace file */
-	}
-...
-	/* Read tracing data from the file, using the handle */
-...
-	tracecmd_close(handle);
-...
+}
 
 2. Open and initialise the trace file in two steps. This allows to perform
 some processing based on metadata, read from the file, before initialising
@@ -139,10 +163,10 @@
 
 SEE ALSO
 --------
-_libtracefs(3)_,
-_libtraceevent(3)_,
-_trace-cmd(1)_
-_trace-cmd.dat(5)_
+*libtracefs(3)*,
+*libtraceevent(3)*,
+*trace-cmd(1)*
+*trace-cmd.dat(5)*
 
 AUTHOR
 ------
diff --git a/Documentation/libtracecmd/libtracecmd-instances.txt b/Documentation/libtracecmd/libtracecmd-instances.txt
index df8fdc4..364085f 100644
--- a/Documentation/libtracecmd/libtracecmd-instances.txt
+++ b/Documentation/libtracecmd/libtracecmd-instances.txt
@@ -22,36 +22,36 @@
 This set of APIs can be used to get information and read tracing data
 from tracing instances stored in a trace file.
 
-The _tracecmd_buffer_instances()_ function gets the number of tracing
+The *tracecmd_buffer_instances()* function gets the number of tracing
 instances recorded in a trace file. The top instance is not counted.
 The _handle_ is a tracecmd_input handler returned by
-_tracecmd_open_head()_.
+*tracecmd_open_head()*.
 
-The _tracecmd_buffer_instance_name()_ function gets the name of the
+The *tracecmd_buffer_instance_name()* function gets the name of the
 tracing instance with given index _indx_, recorded in a trace file.
 The _indx_ is a number in the interval [0 .. count-1], where count
-is the number returned by _tracecmd_buffer_instances()_. The _handle_
-is a tracecmd_input handler returned by _tracecmd_open_head()_.
+is the number returned by *tracecmd_buffer_instances()*. The _handle_
+is a tracecmd_input handler returned by *tracecmd_open_head()*.
 
-The _tracecmd_buffer_instance_handle()_ allocates and initializes a
+The *tracecmd_buffer_instance_handle()* allocates and initializes a
 tracecmd_input handle, associated with trace instance with index
 _indx_ from a trace file.  The _handle_ is a tracecmd_input handler
-returned by _tracecmd_open_head()_. The _indx_ is a number in the
+returned by *tracecmd_open_head()*. The _indx_ is a number in the
 interval [0 .. count-1], where count is the number returned by
-_tracecmd_buffer_instances()_.
+*tracecmd_buffer_instances()*.
 
 RETURN VALUE
 ------------
-The _tracecmd_buffer_instances()_ function returns the number of tracing
+The *tracecmd_buffer_instances()* function returns the number of tracing
 instances recorded in a trace file.
 
-The _tracecmd_buffer_instance_name()_ function returns a string, the name
+The *tracecmd_buffer_instance_name()* function returns a string, the name
 of a tracing instance, or NULL in case of an error The string must *not*
 be freed.
 
-The _tracecmd_buffer_instance_handle()_ function returns a pointer to
+The *tracecmd_buffer_instance_handle()* function returns a pointer to
 newly allocated tracecmd_input handler or NULL in case if an error. The
-returned handler must be closed by _tracecmd_close()(3)_
+returned handler must be closed by *tracecmd_close()(3)*
 
 EXAMPLE
 -------
@@ -99,10 +99,10 @@
 
 SEE ALSO
 --------
-_libtracefs(3)_,
-_libtraceevent(3)_,
-_trace-cmd(1)_
-_trace-cmd.dat(5)_
+*libtracefs(3)*,
+*libtraceevent(3)*,
+*trace-cmd(1)*
+*trace-cmd.dat(5)*
 
 AUTHOR
 ------
diff --git a/Documentation/libtracecmd/libtracecmd-iterate.txt b/Documentation/libtracecmd/libtracecmd-iterate.txt
new file mode 100644
index 0000000..dc053cc
--- /dev/null
+++ b/Documentation/libtracecmd/libtracecmd-iterate.txt
@@ -0,0 +1,354 @@
+libtracecmd(3)
+=============
+
+NAME
+----
+tracecmd_iterate_events, tracecmd_iterate_events_multi, tracecmd_follow_event,
+tracecmd_follow_missed_events, tracecmd_filter_add, tracecmd_iterate_reset - Read events from a trace file
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <trace-cmd.h>*
+
+int *tracecmd_iterate_events*(struct tracecmd_input pass:[*]_handle_,
+			    cpu_set_t pass:[*]_cpus_, int _cpu_size_,
+			    int (pass:[*]_callback_)(struct tracecmd_input pass:[*],
+					    struct tep_record pass:[*],
+					    int, void pass:[*]),
+			    void pass:[*]_callback_data_);
+int *tracecmd_iterate_events_multi*(struct tracecmd_input pass:[**]_handles_,
+				  int _nr_handles_,
+				  int (pass:[*]_callback_)(struct tracecmd_input pass:[*],
+							   struct tep_record pass:[*],
+							   int, void pass:[*]),
+				  void pass:[*]_callback_data_);
+int *tracecmd_iterate_events_reverse*(struct tracecmd_input pass:[*]_handle_,
+			    cpu_set_t pass:[*]_cpus_, int _cpu_size_,
+			    int (pass:[*]_callback_)(struct tracecmd_input pass:[*],
+					    struct tep_record pass:[*],
+					    int, void pass:[*]),
+			    void pass:[*]_callback_data_, bool _cont_);
+int *tracecmd_follow_event*(struct tracecmd_input pass:[*]_handle_,
+			  const char pass:[*]_system_, const char pass:[*]_event_name_,
+			  int (pass:[*]_callback_)(struct tracecmd_input pass:[*],
+					  struct tep_event pass:[*],
+					  struct tep_record pass:[*],
+					  int, void pass:[*]),
+			  void pass:[*]_callback_data_);
+int *tracecmd_follow_missed_events*(struct tracecmd_input pass:[*]_handle_,
+				   int (pass:[*]_callback_)(struct tracecmd_input pass:[*],
+						   struct tep_event pass:[*],
+						   struct tep_record pass:[*],
+						   int, void pass:[*]),
+				   void pass:[*]_callback_data_);
+struct tracecmd_filter pass:[*]*tracecmd_filter_add*(struct tracecmd_input *_handle_,
+					    const char pass:[*]_filter_str_, bool _neg_);
+int *tracecmd_iterate_reset*(struct tracecmd_input pass:[*]_handle_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to iterate over events after opening a trace file
+using one of the open functions like *tracecmd_open(3)* or *tracecmd_open_fd(3)*.
+
+The function *tracecmd_iterate_events()* will iterate through all the
+events in the trace file defined by _handle_, where _handle_ is returned from
+one of the *tracecmd_open(3)* functions. It will call the _callback_() function
+on the events on the CPUs defined by _cpus_. The _cpu_size_ must be the size of
+_cpus_ (see *CPU_SET(3)*). If _cpus_ is NULL, then _cpu_size_ is ignored and _callback()_
+will be called for all events on all CPUs in the trace file. The _callback_data_
+is passed to the _callback()_ as its last parameter. _callback_ may be NULL, which
+is useful if *tracecmd_follow_event()* is used, but note if _callback_ is NULL, then
+_callback_data_ is ignored and not sent to the _callback_ of *tracecmd_follow_event()*.
+
+The function *tracecmd_iterate_events_multi()* is similar to *tracecmd_iterate_events()*
+except that it allows to iterate over more than one trace file. If *tracecmd agent(1)*
+is used to get a trace file for both the host and guest, make sure that the host trace
+file is the first entry in _handles_ and *tracecmd_iterate_events_multi()* will do
+the synchronization of the meta data for the guest files that come later in _handles_.
+_handles_ is an array of trace file descriptors that were opened by *tracecmd_open(3)*
+and friends. Note, unlike *tracecmd_iterate_events()*, *tracecmd_iterate_events_multi()*
+does not filter on CPUs, as it will cause the API to become too complex in knowing which
+handle to filter the CPUs on. If CPU filtering is desired, then the _callback_ should check
+the _record_->cpu to and return 0 if it is not the desired CPU to process. _nr_handles_
+denotes the number of elements in _handles_. The _callback_data_ is passed to the _callback_
+as its last parameter.  _callback_ may be NULL, which is useful if *tracecmd_follow_event()*
+is used, but note if _callback_ is NULL, then _callback_data_ is ignored and not sent to the
+_callback_ of *tracecmd_follow_event()*.
+
+The function *tracecmd_iterate_events_reverse()* works pretty much the same way as
+*tracecmd_iterate_events()* works, but instead of calling the _callback_() function for
+each event in order of the timestamp, it will call the _callback_() function for
+each event in reverse order of the timestamp. If _cont_ is false, it will start by
+calling the event with the oldest timestamp in the trace.dat file. If _cont_ is set
+to true, then it will start whereever the current position of the tracing data is.
+For instance, if the _callback()_ return something other than zero it will exit the
+iteration. If *tracecmd_iterate_events_reverse()* is called again with _cont_ to true
+it will continue where it left off. If _cont_ is false, it will start again at the event
+with the oldest timestamp. The _handle_, _cpus_, _cpu_size_, and _callback_data_ act the
+same as *tracecmd_iterate_events()*.
+
+The _callback()_ for both *tracecmd_iterate_events()*, *tracecmd_iterate_events_reverse()*
+and *tracecmd_iterate_events_multi()* is of the prototype:
+
+int _callback()_(struct tracecmd_input pass:[*]_handle_, struct tep_record pass:[*]_record_,
+		 int _cpu_, void pass:[*]_data_);
+
+The _handle_ is the same _handle_ passed to *tracecmd_iterate_events()* or the current
+handle of _handles_ passed to *tracecmd_iterate_events_multi()* that the _record_ belongs to.
+The _record_ is the current event record. The _cpu_ is the current CPU being processed. Note, for
+*tracecmd_iterate_events_multi()* it may not be the actual CPU of the file, but the nth
+CPU of all the _handles_ put together. Use _record_->cpu to get the actual CPU that the
+event is on.
+
+The *tracecmd_follow_event()* function will attach to a trace file descriptor _handle_
+and call the _callback_ when the event described by _system_ and _name_ matches an event
+in the iteration of *tracecmd_iterate_events()* or *tracecmd_iterate_events_multi()*.
+Note, the _cpu_ is the nth CPU for both *tracecmd_iterate_events()* and
+*tracecmd_iterate_events_multi()*. If the actual CPU of the _record_ is needed, use
+_record_->cpu.
+For *tracecmd_iterate_events_multi()*, the _callback_ is only called if the _handle_
+matches the current trace file descriptor within _handles_. The _callback_data_ is
+passed as the last parameter to the _callback()_ function. Note, this _callback()_
+function will be called before the _callback()_ function of either *tracecmd_iterate_events()* or *tracecmd_iterate_events_multi()*.
+
+The _callback()_ prototype for *tracecmd_follow_event()_ is:
+
+int _callback()_(struct tracecmd_input pass:[*]_handle_, struct tep_event pass:[*]_event,
+		 struct tep_record pass:[*]_record_, int _cpu_, void pass:[*]_data_);
+
+The *tracecmd_follow_missed_events()* function will attach to a trace file descriptor
+_handle_ and call the _callback_ when missed events are detected. The _event_ will
+hold the type of event that the _record_ is. The _record_ will hold the information
+of the missed events. The _cpu_ is the nth CPU for both *tracecmd_iterate_events()*
+and *tracecmd_iterate_events_multi()*. If the CPU that the missed events are for is
+needed, use _record_->cpu. If _record_->missed_events is a positive number, then it
+holds the number of missed events since the last event on its CPU, otherwise it
+will be negative, and that will mean that the number of missed events is unknown but
+missed events exist since the last event on the CPU.
+The _callback_ and _callback_data_ is the same format as *tracecmd_follow_event()* above.
+The missed events _callback_ is called before any of the other _callbacks_ and any
+filters that were added by *tracecmd_filter_add()* are ignored.
+If _callback_ returns a non zero, it will stop the iterator before it calls any of the
+other iterator callbacks for the given record.
+
+The *tracecmd_filter_add()* function, adds a filter to _handle_ that affects
+both *tracecmd_iterate_events()* and *tracecmd_iterate_events_multi()*.
+The _filter_str_ is a character string defining a filter in a format that
+is defined by *tep_filter_add_filter_str(3)*. If _neg_ is true, then the events
+that match the filter will be skipped, otherwise the events that match will execute
+the _callback()_ function in the iterators.
+
+The *tracecmd_iterate_reset()* sets the _handle_ back to start at the beginning, so that
+the next call to *tracecmd_iterate_events()* starts back at the first event again, instead
+of continuing where it left off.
+
+RETURN VALUE
+------------
+Both *tracecmd_iterate_events()*, *tracecmd_iterate_events_reverse()* and
+*tracecmd_iterate_events_multi()* return zero if they successfully iterated all events
+(handling the follow and filters appropriately). Or an error value, which can include
+returning a non-zero result from the _callback()_ function.
+
+*tracecmd_iterate_reset()* returns 0 on success and -1 if an error occurred. Note,
+if -1 is returned, a partial reset may have also happened.
+
+EXAMPLE
+-------
+[source,c]
+--
+#define _GNU_SOURCE
+#include <sched.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <trace-cmd.h>
+
+struct private_data {
+	int		cpu;
+	const char	*file;
+};
+
+static int print_events(struct tracecmd_input *handle, struct tep_record *record, int cpu, void *data)
+{
+	static struct trace_seq seq;
+	struct tep_handle *tep = tracecmd_get_tep(handle);
+	struct private_data *pdata = tracecmd_get_private(handle);
+
+	/* For multi handles we need this */
+	if (pdata->cpu >= 0 && pdata->cpu != record->cpu)
+		return 0;
+
+	if (!seq.buffer)
+		trace_seq_init(&seq);
+
+	trace_seq_reset(&seq);
+	trace_seq_printf(&seq, "%s: ", pdata->file);
+	tep_print_event(tep, &seq, record, "%6.1000d [%03d] %s-%d %s: %s\n",
+			TEP_PRINT_TIME, TEP_PRINT_CPU, TEP_PRINT_COMM, TEP_PRINT_PID,
+			TEP_PRINT_NAME, TEP_PRINT_INFO);
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+	return 0;
+}
+
+static int print_event(struct tracecmd_input *handle, struct tep_event *event,
+		       struct tep_record *record, int cpu, void *data)
+{
+	return print_events(handle, record, cpu, data);
+}
+
+static int missed_events(struct tracecmd_input *handle, struct tep_event *event,
+			 struct tep_record *record, int cpu, void *data)
+{
+	if (record->missed_events > 0)
+		printf("CPU [%03d] has %d missed events\n",
+			 record->cpu, record->missed_events);
+	else
+		printf("CPU [%03d] has missed events\n", record->cpu);
+	return 0;
+}
+
+static void usage(const char *argv0)
+{
+	printf("usage: [-c cpu][-f filter][-e event] %s trace.dat [trace.dat ...]\n",
+	       argv0);
+	exit(-1);
+}
+
+int main(int argc, char **argv)
+{
+	struct tracecmd_input **handles = NULL;
+	const char *filter_str = NULL;
+	const char *argv0 = argv[0];
+	struct private_data *priv;
+	cpu_set_t *cpuset = NULL;
+	char *event = NULL;
+	size_t cpusize = 0;
+	int nr_handles = 0;
+	int cpu = -1;
+	int i;
+	int c;
+
+	while ((c = getopt(argc, argv, "c:f:e:")) >= 0) {
+		switch (c) {
+		case 'c':
+			/* filter all trace data to this one CPU. */
+			cpu = atoi(optarg);
+			break;
+		case 'f':
+			filter_str = optarg;
+			break;
+		case 'e':
+			event = optarg;
+			break;
+		default:
+			usage(argv0);
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (argc == 0)
+		usage(argv0);
+
+	for (i = 0; i < argc; i++) {
+		handles = realloc(handles, sizeof(*handles) * (nr_handles + 1));
+		if (!handles)
+			exit(-1);
+		handles[nr_handles] = tracecmd_open(argv[i], 0);
+		if (!handles[nr_handles]) {
+			perror(argv[i]);
+			exit(-1);
+		}
+		if (filter_str) {
+			if (tracecmd_filter_add(handles[nr_handles], filter_str, false) == NULL) {
+				perror("adding filter");
+				exit(-1);
+			}
+		}
+		priv = calloc(1, sizeof(*priv));
+		if (!priv)
+			exit(-1);
+		priv->file = argv[i];
+		priv->cpu = cpu;
+		tracecmd_set_private(handles[nr_handles], priv);
+		if (event) {
+			if (tracecmd_follow_event(handles[nr_handles], NULL, event, print_event, NULL) < 0) {
+				printf("Could not follow event %s for file %s\n", event, argv[i]);
+				exit(-1);
+			}
+		}
+		tracecmd_follow_missed_events(handles[nr_handles], missed_events, NULL);
+		nr_handles++;
+	}
+
+	/* Shortcut */
+	if (nr_handles == 1) {
+		if (cpu >= 0) {
+			cpuset = CPU_ALLOC(cpu + 1);
+			if (!cpuset)
+				exit(-1);
+			cpusize = CPU_ALLOC_SIZE(cpu + 1);
+			CPU_SET_S(cpu, cpusize, cpuset);
+		}
+		if (event)
+			tracecmd_iterate_events(handles[0], cpuset, cpusize, NULL, NULL);
+		else
+			tracecmd_iterate_events(handles[0], cpuset, cpusize, print_events, NULL);
+	} else {
+		if (event)
+			tracecmd_iterate_events_multi(handles, nr_handles, NULL, NULL);
+		else
+			tracecmd_iterate_events_multi(handles, nr_handles, print_events, NULL);
+	}
+
+	for (i = 0; i < nr_handles; i++) {
+		priv = tracecmd_get_private(handles[i]);
+		free(priv);
+		tracecmd_close(handles[i]);
+	}
+	free(handles);
+}
+--
+FILES
+-----
+[verse]
+--
+*trace-cmd.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracecmd*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs(3)*,
+*libtraceevent(3)*,
+*trace-cmd(1)*
+*trace-cmd.dat(5)*
+
+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
+-------
+libtracecmd is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.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/libtracecmd/libtracecmd-log.txt b/Documentation/libtracecmd/libtracecmd-log.txt
index de5c277..38e17bf 100644
--- a/Documentation/libtracecmd/libtracecmd-log.txt
+++ b/Documentation/libtracecmd/libtracecmd-log.txt
@@ -16,8 +16,8 @@
 
 DESCRIPTION
 -----------
-The _tracecmd_set_loglevel()_ function sets the level of the library logs that will be printed on
-the console. See _libtraceevent(3)_ for detailed desciription of the log levels. Setting the log
+The *tracecmd_set_loglevel()* function sets the level of the library logs that will be printed on
+the console. See *libtraceevent(3)* for detailed desciription of the log levels. Setting the log
 level to specific value means that logs from the previous levels will be printed too. For example
 _TEP_LOG_WARNING_ will print any logs with severity _TEP_LOG_WARNING_, _TEP_LOG_ERROR_ and
 _TEP_LOG_CRITICAL_. The default log level is _TEP_LOG_CRITICAL_.  When a new level is set, it is
@@ -48,10 +48,10 @@
 
 SEE ALSO
 --------
-_libtracefs(3)_,
-_libtraceevent(3)_,
-_trace-cmd(1)_
-_trace-cmd.dat(5)_
+*libtracefs(3)*,
+*libtraceevent(3)*,
+*trace-cmd(1)*
+*trace-cmd.dat(5)*
 
 AUTHOR
 ------
diff --git a/Documentation/libtracecmd/libtracecmd-maps.txt b/Documentation/libtracecmd/libtracecmd-maps.txt
new file mode 100644
index 0000000..8c1fb69
--- /dev/null
+++ b/Documentation/libtracecmd/libtracecmd-maps.txt
@@ -0,0 +1,177 @@
+libtracecmd(3)
+=============
+
+NAME
+----
+tracecmd_map_vcpus, tracecmd_get_cpu_map, tracecmd_map_find_by_host_pid, tracecmd_map_get_host_pid,
+tracecmd_map_get_guest, tracecmd_map_set_private, tracecmd_map_get_private - Mapping host and guest data
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <trace-cmd.h>*
+
+int *tracecmd_map_vcpus*(struct tracecmd_input pass:[**]handles, int nr_handles);
+struct tracecmd_cpu_map pass:[*]*tracecmd_get_cpu_map*(struct tracecmd_input pass:[*]handle, int cpu);
+struct tracecmd_cpu_map pass:[*]*tracecmd_map_find_by_host_pid*(struct tracecmd_input pass:[*]handle,
+						      int host_pid);
+int *tracecmd_map_get_host_pid*(struct tracecmd_cpu_map pass:[*]map);
+struct tracecmd_input pass:[*]*tracecmd_map_get_guest*(struct tracecmd_cpu_map pass:[*]map);
+void *tracecmd_map_set_private*(struct tracecmd_cpu_map pass:[*]map, void pass:[*]priv);
+void pass:[*]*tracecmd_map_get_private*(struct tracecmd_cpu_map pass:[*]map);
+--
+
+DESCRIPTION
+-----------
+This set of APIs is used to map host and guest trace files for to facilitate
+further tracing analysis.
+
+The *tracecmd_map_vcpus()* takes an array of _handles_ where each item in that
+array was created by one of the *tracecmd_open(3)* functions, and the number
+of handles as _nr_handles_. The first handle in the array of _handles_ is expected
+to be the descriptor for the host tracing file, and the rest are guest trace
+files that run on the host, and were created by the *trace-cmd record(1)* and
+*trace-cmd agent(1)* interactions. It returns the number of guests found in
+_handles_ that were associated with the host, or negative on error.
+
+The *tracecmd_get_cpu_map()* returns a descriptor for a given CPU for a handle.
+If the _handle_ was a guest defined from *tracecmd_map_vcpus()* then the mapping
+created from that function that is associated to this particular vCPU (denoted by
+_cpu_) from _handle_. This destriptor can be used by *tarcecmd_map_get_guest()*,
+*tracecmd_map_set_private()* and *tracecmd_map_get_private()* functions.
+
+The *tracecmd_map_find_by_host_pid()* will return a mapping for a guest virtual
+CPU that is handled by the given _host_pid_. Note, the _handle_ passed in can be
+either the host handle or one of the guest's handles for that host that was
+mapped by *tracecmd_map_vcpus()*, even if the guest handle does not have the vCPU
+that the _host_pid_ represents.
+
+The *tracecmd_map_get_host_pid()* will recturn the host_pid for a given _map_
+that was retrieved by one of the above functions.
+
+The *tracecmd_map_get_guest()* will recturn the guest_handle for a given _map_
+that was retrieved by one of the above functions.
+
+The *tracecmd_map_set_private()* allows the application to assign private data
+for a given guest vCPU to host thread mapping defined by _map_.
+
+The *tracecmd_map_get_private()* retrieves the _priv_ data from _map_ that was
+set by *tracecmd_map_set_private()*.
+
+RETURN VALUE
+------------
+*tracecmd_map_vcpus()* returns the number of guests in the _handles_ array that
+were mapped to the host handle that is the first entry in _handles_. It returns
+-1 on error.
+
+*tracecmd_get_cpu_map()* returns a map created by *tracecmd_map_vcpus()* for
+a given _cpu_ for a given _handle_, or NULL if it is not found.
+
+*tracecmd_map_find_by_host_pid()* returns a map that is associated by the host
+task with _host_pid_ as its process ID. _handle_ can be either a the host
+handle, or one of the guest handles that were mapped to the host via
+*tracecmd_map_vcpus()*, even if the guest handle is another guest than
+the one that the mapping is for. It returns NULL if not found.
+
+*tracecmd_map_get_host_pid()* returns the host process ID for an associated
+mapping defined by _map_.
+
+*tracecmd_map_get_guest()* returns the guest handle for an associated
+mapping defined by _map_.
+
+*tracecmd_map_get_private()* returns the private data of a mapping defined
+by _map_ that was set by *tracecmd_map_set_private()*.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <errno.h>
+#include <trace-cmd.h>
+
+int main(int argc, char **argv)
+{
+	struct tracecmd_input **handles = NULL;
+	int nr_handles;
+	int i;
+
+	if (argc < 2) {
+		printf("usage: host_trace.dat guest1_trace.dat [guest2_trace.dat ...]\n");
+		exit(-1);
+	}
+
+	for (i = 1; i < argc; i++) {
+		handles = realloc(handles, sizeof(*handles) * (nr_handles + 1));
+		if (!handles)
+			exit(-1);
+		handles[nr_handles] = tracecmd_open(argv[i], 0);
+		if (!handles[nr_handles]) {
+			perror(argv[1]);
+			exit(-1);
+		}
+		tracecmd_set_private(handles[nr_handles], argv[i]);
+		nr_handles++;
+	}
+
+	tracecmd_map_vcpus(handles, nr_handles);
+
+	for (i = 1; i < nr_handles; i++) {
+		struct tracecmd_cpu_map *map;
+		struct tep_handle *tep;
+		const char *file = tracecmd_get_private(handles[i]);
+		int cpus, cpu;
+
+		printf("Mappings for guest %s:\n", file);
+		tep = tracecmd_get_tep(handles[i]);
+		cpus = tep_get_cpus(tep);
+		for (cpu = 0; cpu < cpus; cpu++) {
+			printf("  [%03d] ", cpu);
+			map = tracecmd_get_cpu_map(handles[i], cpu);
+			if (!map) {
+				printf("Has no mapping!\n");
+				continue;
+			}
+			printf("host_pid: %d\n", tracecmd_map_get_host_pid(map));
+		}
+	}
+	for (i = 0; i < nr_handles; i++)
+		tracecmd_close(handles[i]);
+	free(handles);
+	exit(0);
+}
+--
+FILES
+-----
+[verse]
+--
+*trace-cmd.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracecmd*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs(3)*,
+*libtraceevent(3)*,
+*trace-cmd(1)*
+*trace-cmd.dat(5)*
+
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracecmd is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.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/libtracecmd/libtracecmd-peer.txt b/Documentation/libtracecmd/libtracecmd-peer.txt
index 2e3232c..c9774c8 100644
--- a/Documentation/libtracecmd/libtracecmd-peer.txt
+++ b/Documentation/libtracecmd/libtracecmd-peer.txt
@@ -25,13 +25,13 @@
 trace file. These APIs use that information to extract and synchronize
 the trace data.
 
-The _tracecmd_get_traceid()_ function returns the trace ID stored in
+The *tracecmd_get_traceid()* function returns the trace ID stored in
 the trace file metadata associated with _handle_. Each peer from a trace
 session has an ID unique for that peer and that trace session only.
 This ID is used to match multiple trace files recorded in a same trace
 session.
 
-The _tracecmd_get_guest_cpumap()_ function gets the mapping of guest
+The *tracecmd_get_guest_cpumap()* function gets the mapping of guest
 virtual CPUs (VCPU) to the host process that represents those VCPUs and is
 stored in the metadata of the trace file associated with _handle_. This
 information is gathered during a host-guest trace session and is stored
@@ -47,9 +47,9 @@
 
 RETURN VALUE
 ------------
-The _tracecmd_get_traceid()_ function returns a 64 bit trace ID.
+The *tracecmd_get_traceid()* function returns a 64 bit trace ID.
 
-The _tracecmd_get_guest_cpumap()_ function returns -1 in case of
+The *tracecmd_get_guest_cpumap()* function returns -1 in case of
 an error or 0 otherwise. If 0 is returned, then the _name_, _vcpu_count_
 and _cpu_pid_ parameters contain the requested information.
 
@@ -107,10 +107,10 @@
 
 SEE ALSO
 --------
-_libtracefs(3)_,
-_libtraceevent(3)_,
-_trace-cmd(1)_
-_trace-cmd.dat(5)_
+*libtracefs(3)*,
+*libtraceevent(3)*,
+*trace-cmd(1)*
+*trace-cmd.dat(5)*
 
 AUTHOR
 ------
diff --git a/Documentation/libtracecmd/libtracecmd-record.txt b/Documentation/libtracecmd/libtracecmd-record.txt
index aa1a4a6..6943955 100644
--- a/Documentation/libtracecmd/libtracecmd-record.txt
+++ b/Documentation/libtracecmd/libtracecmd-record.txt
@@ -22,38 +22,38 @@
 DESCRIPTION
 -----------
 This set of APIs can be used to read tracing data from a trace file opened
-with _tracecmd_open()(3)_, _tracecmd_open_fd()(3)_ or _tracecmd_open_head()(3)_.
+with *tracecmd_open()(3)*, *tracecmd_open_fd()(3)* or *tracecmd_open_head()(3)*.
 
-The _tracecmd_read_cpu_first()_ function reads the first trace record
+The *tracecmd_read_cpu_first()* function reads the first trace record
 for a given _cpu_ from a trace file associated with _handle_. The returned
-record must be freed with _tracecmd_free_record()_.
+record must be freed with *tracecmd_free_record()*.
 
-The _tracecmd_read_data()_ function reads the next trace record for
+The *tracecmd_read_data()* function reads the next trace record for
 a given _cpu_ from a trace file associated with _handle_ and increments
-the read location pointer, so that the next call to _tracecmd_read_data()_
+the read location pointer, so that the next call to *tracecmd_read_data()*
 will not read the same record again. The returned record must be freed
-with _tracecmd_free_record()_.
+with *tracecmd_free_record()*.
 
-The _tracecmd_read_at()_ function reads a trace record from a specific
+The *tracecmd_read_at()* function reads a trace record from a specific
 _offset_ within the file associated with _handle_. The CPU on which the
 recorded event occurred is stored in the _cpu_. The function does not
 change the current read location pointer. The returned record must be
-freed with _tracecmd_free_record()_.
+freed with *tracecmd_free_record()*.
 
-The _tracecmd_free_record()_ function frees a _record_ returned by any
+The *tracecmd_free_record()* function frees a _record_ returned by any
 of the _tracecmd_read__ APIs.
 
-The _tracecmd_get_tep()_ function returns a tep context for a given
+The *tracecmd_get_tep()* function returns a tep context for a given
 _handle_.
 
 RETURN VALUE
 ------------
-The _tracecmd_read_cpu_first()_, _tracecmd_read_data()_ and
-_tracecmd_read_at()_ functions return a pointer to struct tep_record or
+The *tracecmd_read_cpu_first()*, *tracecmd_read_data()* and
+*tracecmd_read_at()* functions return a pointer to struct tep_record or
 NULL in case of an error.The returned record must be freed with
-_tracecmd_free_record()_.
+*tracecmd_free_record()*.
 
-The _tracecmd_get_tep()_ function returns a pointer to tep context or
+The *tracecmd_get_tep()* function returns a pointer to tep context or
 NULL if there is no tep context for the given _handle_. The returned
 tep pointer must *not* be freed.
 
@@ -108,10 +108,10 @@
 
 SEE ALSO
 --------
-_libtracefs(3)_,
-_libtraceevent(3)_,
-_trace-cmd(1)_
-_trace-cmd.dat(5)_
+*libtracefs(3)*,
+*libtraceevent(3)*,
+*trace-cmd(1)*
+*trace-cmd.dat(5)*
 
 AUTHOR
 ------
diff --git a/Documentation/libtracecmd/libtracecmd-timestamp.txt b/Documentation/libtracecmd/libtracecmd-timestamp.txt
new file mode 100644
index 0000000..88a84e5
--- /dev/null
+++ b/Documentation/libtracecmd/libtracecmd-timestamp.txt
@@ -0,0 +1,160 @@
+libtracecmd(3)
+=============
+
+NAME
+----
+tracecmd_get_first_ts, tracecmd_add_ts_offset, tracecmd_get_tsc2nsec - Handle time stamps from a trace file.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <trace-cmd.h>*
+
+unsigned long long *tracecmd_get_first_ts*(struct tracecmd_input pass:[*]_handle_);
+void *tracecmd_add_ts_offset*(struct tracecmd_input pass:[*]_handle_, long long _offset_);
+int *tracecmd_get_tsc2nsec*(struct tracecmd_input pass:[*]_handle_, int pass:[*]_mult_, int pass[*]_shift_, unsigned long long pass:[*]_offset_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to read tracing data from a trace file opened
+with _tracecmd_open()(3)_, _tracecmd_open_fd()(3)_ or _tracecmd_open_head()(3)_.
+
+The *tracecmd_get_first_ts()* function returns the time stamp of the first
+record in the _handle_.
+
+The *tracecmd_add_ts_offset()* function adds an offset to each of the records
+in the _handle_ that represents a trace file. This is useful for associating two
+different tracing files by their offset (for example a trace file from a host
+and a trace file from a guest that were not synchronized when created).
+
+The *tracecmd_get_tsc2nsec* returns the calculation values that convert the
+raw timestamp into nanoseconds. The parameters are pointers to the storage to save
+the values, or NULL to ignore them. The multiplier will be saved in _mult_, the
+shift value will be saved in _shift_, and the offset value will be saved in
+_offset_, if the corresponding parameters are not NULL.
+
+RETURN VALUE
+------------
+The *tracecmd_get_first_ts()* function returns the timestamp of the first
+record in a trace file for the given _handle_.
+
+The *tracecmd_get_tsc2nsec*() returns 0 if the tracing clock supports the
+shift values and -1 otherwise. Note, that if the trace file has the TSC2NSEC
+option, the values returned in the parameters may still be valid even if the
+function itself returns -1. The return value only notes if the values will
+be used in the calculations of the given clock.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <trace-cmd.h>
+
+static int print_events(struct tracecmd_input *handle, struct tep_record *record, int cpu, void *data)
+{
+	static struct trace_seq seq;
+	struct tep_handle *tep = tracecmd_get_tep(handle);
+	const char *file = tracecmd_get_private(handle);
+
+	if (!seq.buffer)
+		trace_seq_init(&seq);
+
+	trace_seq_reset(&seq);
+	trace_seq_printf(&seq, "%s: ", file);
+	tep_print_event(tep, &seq, record, "%6.1000d [%03d] %s-%d %s: %s\n",
+			TEP_PRINT_TIME, TEP_PRINT_CPU, TEP_PRINT_COMM, TEP_PRINT_PID,
+			TEP_PRINT_NAME, TEP_PRINT_INFO);
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	struct tracecmd_input **handles = NULL;
+	unsigned long long ts, first_ts = 0;
+	unsigned long long offset;
+	int multi;
+	int shift;
+	int nr_handles = 0;
+	int ret;
+	int i;
+
+	if (argc < 2) {
+		printf("usage: %s trace.dat [trace.dat ...]\n", argv[0]);
+		exit(-1);
+	}
+
+	for (i = 1; i < argc; i++) {
+		handles = realloc(handles, sizeof(*handles) * (nr_handles + 1));
+		if (!handles)
+			exit(-1);
+		handles[nr_handles] = tracecmd_open(argv[i], 0);
+		if (!handles[nr_handles])
+			exit(-1);
+
+		ret = tracecmd_get_tsc2nsec(handles[nr_handles], &multi, &shift, &offset);
+		if (!ret)
+			printf(" %s has tsc2nsec calculations of mult:%d shift:%d offset:%lld\n",
+				argv[i], multi, shift, offset);
+		tracecmd_set_private(handles[nr_handles], argv[i]);
+		ts = tracecmd_get_first_ts(handles[nr_handles]);
+		if (!first_ts || ts < first_ts)
+			first_ts = ts;
+		nr_handles++;
+	}
+
+	/* Set the time stamp to start at the first record found */
+	for (i = 0; i < nr_handles; i++)
+		tracecmd_add_ts_offset(handles[i], -first_ts);
+
+	tracecmd_iterate_events_multi(handles, nr_handles, print_events, NULL);
+
+	for (i = 0; i < nr_handles; i++)
+		tracecmd_close(handles[i]);
+	free(handles);
+}
+--
+FILES
+-----
+[verse]
+--
+*trace-cmd.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracecmd*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+_libtracefs(3)_,
+_libtraceevent(3)_,
+_trace-cmd(1)_
+_trace-cmd.dat(5)_
+
+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
+-------
+libtracecmd is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.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/libtracecmd/libtracecmd.txt b/Documentation/libtracecmd/libtracecmd.txt
index dc528ce..52d4e59 100644
--- a/Documentation/libtracecmd/libtracecmd.txt
+++ b/Documentation/libtracecmd/libtracecmd.txt
@@ -16,6 +16,8 @@
 	struct tracecmd_input pass:[*]*tracecmd_open_fd*(int _fd_, int _flags_);
 	struct tracecmd_input pass:[*]*tracecmd_open_head*(const char pass:[*]_file_, int _flags_);
 	void *tracecmd_close*(struct tracecmd_input pass:[*]_handle_);
+	void *tracecmd_set_private*(struct tracecmd_input pass:[*]_handle_, void pass:[*]_data_);
+	void pass:[*]*tracecmd_get_private*(struct tracecmd_input pass:[*]_handle_);
 
 Read tracing records from a trace file:
 	int *tracecmd_init_data*(struct tracecmd_input pass:[*]_handle_);
@@ -25,15 +27,66 @@
 	void *tracecmd_free_record*(struct tep_record pass:[*]_record_);
 	struct tep_handle pass:[*]*tracecmd_get_tep*(struct tracecmd_input pass:[*]_handle_);
 
+Iterating over events in a trace file:
+	int *tracecmd_iterate_events*(struct tracecmd_input pass:[*]_handle_,
+				    cpu_set_t pass:[*]_cpus_, int _cpu_size_,
+				    int (pass:[*]_callback_)(struct tracecmd_input pass:[*],
+						    struct tep_record pass:[*],
+						    int, void pass:[*]),
+				    void pass:[*]_callback_data_);
+	int *tracecmd_iterate_events_multi*(struct tracecmd_input pass:[**]_handles_,
+					  int _nr_handles_,
+					  int (pass:[*]_callback_)(struct tracecmd_input pass:[*],
+								   struct tep_record pass:[*],
+							   int, void pass:[*]),
+					  void pass:[*]_callback_data_);
+	int *tracecmd_iterate_events_reverse*(struct tracecmd_input pass:[*]_handle_,
+				    cpu_set_t pass:[*]_cpus_, int _cpu_size_,
+				    int (pass:[*]_callback_)(struct tracecmd_input pass:[*],
+						    struct tep_record pass:[*],
+						    int, void pass:[*]),
+				    void pass:[*]_callback_data_, bool _cont_);
+	int *tracecmd_follow_event*(struct tracecmd_input pass:[*]_handle_,
+				  const char pass:[*]_system_, const char pass:[*]_event_name_,
+				  int (pass:[*]_callback_)(struct tracecmd_input pass:[*],
+						  struct tep_event pass:[*],
+						  struct tep_record pass:[*],
+						  int, void pass:[*]),
+				  void pass:[*]_callback_data_);
+	int *tracecmd_follow_missed_events*(struct tracecmd_input pass:[*]_handle_,
+					   int (pass:[*]_callback_)(struct tracecmd_input pass:[*],
+							   struct tep_event pass:[*],
+							   struct tep_record pass:[*],
+							   int, void pass:[*]),
+					   void pass:[*]_callback_data_);
+	struct tracecmd_filter pass:[*]*tracecmd_filter_add*(struct tracecmd_input *_handle_,
+						    const char pass:[*]_filter_str_, bool _neg_);
+	int *tracecmd_iterate_reset*(struct tracecmd_input pass:[*]_handle_);
+
 Read tracing instances from a trace file:
 	int *tracecmd_buffer_instances*(struct tracecmd_input pass:[*]_handle_);
 	const char pass:[*]*tracecmd_buffer_instance_name*(struct tracecmd_input pass:[*]_handle_, int _indx_);
 	struct tracecmd_input pass:[*]*tracecmd_buffer_instance_handle*(struct tracecmd_input pass:[*]_handle_, int _indx_);
 
+Handle time stamps from a trace file:
+	unsigned long long *tracecmd_get_first_ts*(struct tracecmd_input pass:[*]_handle_);
+	void *tracecmd_add_ts_offset*(struct tracecmd_input pass:[*]_handle_, long long _offset_);
+	int *tracecmd_get_tsc2nsec*(struct tracecmd_input pass:[*]_handle_, int pass:[*]_mult_, int pass[*]_shift_, unsigned long long pass:[*]_offset_);
+
 Get traceing peer information from a trace file:
 	unsigned long long *tracecmd_get_traceid*(struct tracecmd_input pass:[*]_handle_);
 	int *tracecmd_get_guest_cpumap*(struct tracecmd_input pass:[*]_handle_, unsigned long long _trace_id_, const char pass:[*]pass:[*]_name_, int pass:[*]_vcpu_count_, const int pass:[*]pass:[*]_cpu_pid_);
 
+Mapping host and guest trace files:
+	int *tracecmd_map_vcpus*(struct tracecmd_input pass:[**]handles, int nr_handles);
+	struct tracecmd_cpu_map pass:[*]*tracecmd_get_cpu_map*(struct tracecmd_input pass:[*]handle, int cpu);
+	struct tracecmd_cpu_map pass:[*]*tracecmd_map_find_by_host_pid*(struct tracecmd_input pass:[*]handle,
+						      int host_pid);
+	int *tracecmd_map_get_host_pid*(struct tracecmd_cpu_map pass:[*]map);
+	struct tracecmd_input pass:[*]*tracecmd_map_get_guest*(struct tracecmd_cpu_map pass:[*]map);
+	void *tracecmd_map_set_private*(struct tracecmd_cpu_map pass:[*]map, void pass:[*]priv);
+	void pass:[*]*tracecmd_map_get_private*(struct tracecmd_cpu_map pass:[*]map);
+
 Control library logs:
 	int *tracecmd_set_loglevel*(enum tep_loglevel _level_);
 --
@@ -56,10 +109,10 @@
 
 SEE ALSO
 --------
-_libtraceevent(3)_
-_libtracefs(3)_
-_trace-cmd(1)_
-_trace-cmd.dat(5)_
+*libtraceevent(3)*
+*libtracefs(3)*
+*trace-cmd(1)*
+*trace-cmd.dat(5)*
 
 AUTHOR
 ------
diff --git a/Documentation/libtracecmd/meson.build b/Documentation/libtracecmd/meson.build
new file mode 100644
index 0000000..c9fb166
--- /dev/null
+++ b/Documentation/libtracecmd/meson.build
@@ -0,0 +1,168 @@
+# SPDX-License-Identifier: LGPL-2.1
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+# input text file: man page section
+
+sources = {
+    'libtracecmd-files.txt': '3',
+    'libtracecmd-instances.txt': '3',
+    'libtracecmd-iterate.txt': '3',
+    'libtracecmd-log.txt': '3',
+    'libtracecmd-maps.txt': '3',
+    'libtracecmd-peer.txt': '3',
+    'libtracecmd-record.txt': '3',
+    'libtracecmd-timestamp.txt': '3',
+    'libtracecmd.txt': '3',
+}
+
+confdir = meson.current_source_dir() + '/../'
+top_source_dir = meson.current_source_dir() + '/../../'
+
+#
+# For asciidoc ...
+#   -7.1.2,     no extra settings are needed.
+#    8.0-,      set ASCIIDOC8.
+#
+
+#
+# For docbook-xsl ...
+#   -1.68.1,         set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0)
+#    1.69.0,         no extra settings are needed?
+#    1.69.1-1.71.0,  set DOCBOOK_SUPPRESS_SP?
+#    1.71.1,         no extra settings are needed?
+#    1.72.0,         set DOCBOOK_XSL_172.
+#    1.73.0-,        set ASCIIDOC_NO_ROFF
+#
+
+#
+# If you had been using DOCBOOK_XSL_172 in an attempt to get rid
+# of 'the ".ft C" problem' in your generated manpages, and you
+# instead ended up with weird characters around callouts, try
+# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8).
+#
+
+if get_option('asciidoctor')
+    asciidoc = find_program('asciidoctor')
+    asciidoc_extra  = ['-a', 'compat-mode']
+    asciidoc_extra += ['-I.']
+    asciidoc_extra += ['-r', 'asciidoctor-extensions']
+    asciidoc_extra += ['-a', 'mansource=libtraceevent']
+    asciidoc_extra += ['-a', 'manmanual="libtraceevent Manual"']
+    asciidoc_html = 'xhtml5'
+else
+    asciidoc = find_program('asciidoc')
+    asciidoc_extra  = ['--unsafe']
+    asciidoc_extra += ['-f', confdir + 'asciidoc.conf']
+    asciidoc_html = 'xhtml11'
+
+    r = run_command(asciidoc, '--version', check: true)
+    v = r.stdout().strip()
+    if v.version_compare('>=8.0')
+        asciidoc_extra += ['-a', 'asciidoc7compatible']
+    endif
+endif
+
+manpage_xsl = confdir + 'manpage-normal.xsl'
+
+if get_option('docbook-xls-172')
+    asciidoc_extra += ['-a', 'libtraceevent-asciidoc-no-roff']
+    manpage_xsl = confdir + 'manpage-1.72.xsl'
+elif get_option('asciidoc-no-roff')
+    # docbook-xsl after 1.72 needs the regular XSL, but will not
+    # pass-thru raw roff codes from asciidoc.conf, so turn them off.
+    asciidoc_extra += ['-a', 'libtraceevent-asciidoc-no-roff']
+endif
+
+xmlto = find_program('xmlto')
+xmlto_extra = []
+
+if get_option('man-bold-literal')
+    xmlto_extra += ['-m ', confdir + 'manpage-bold-literal.xsl']
+endif
+
+if get_option('docbook-suppress-sp')
+    xmlto_extra += ['-m ',  confdir + 'manpage-suppress-sp.xsl']
+endif
+
+check_doc = custom_target(
+    'check-doc',
+    output: 'dummy',
+    command : [
+        top_source_dir + 'check-manpages.sh',
+        meson.current_source_dir()])
+
+gen = generator(
+    asciidoc,
+    output: '@BASENAME@.xml',
+    arguments: [
+        '-b', 'docbook',
+        '-d', 'manpage',
+        '-a', 'libtraceevent_version=' + meson.project_version(),
+        '-o', '@OUTPUT@']
+        + asciidoc_extra
+        +  ['@INPUT@'])
+
+man = []
+html = []
+foreach txt, section : sources
+    # build man page(s)
+    xml = gen.process(txt)
+    man += custom_target(
+        txt.underscorify() + '_man',
+        input: xml,
+        output: '@BASENAME@.' + section,
+        depends: check_doc,
+        command: [
+            xmlto,
+            '-m', manpage_xsl,
+            'man',
+            '-o', '@OUTPUT@']
+            + xmlto_extra
+            + ['@INPUT@'])
+
+    # build html pages
+    html += custom_target(
+        txt.underscorify() + '_html',
+        input: txt,
+        output: '@BASENAME@.html',
+        depends: check_doc,
+        command: [
+            asciidoc,
+            '-b', asciidoc_html,
+            '-d', 'manpage',
+            '-a', 'libtraceevent_version=' + meson.project_version(),
+            '-o', '@OUTPUT@']
+            + asciidoc_extra
+            + ['@INPUT@'])
+endforeach
+
+# Install path workaround because:
+#
+# - xmlto might generate more than one file and we would to tell meson
+#   about those output files. We could figure out which files are generated
+#   (see sed match in check-manpages.sh).
+#
+# - The man page generation puts all the generated files under sub dirs
+#   and it's not obvious how to tell Meson it should not do this without
+#   causing the install step to fail (confusion where the generated files
+#   are stored)
+#
+# - The documentation build is not part of the 'build' target. The user
+#   has explicitly to trigger the doc build. Hence the documentation is
+#   not added to the 'install' target.
+#
+# Thus just use a plain old shell script to move the generated files to the
+# right location.
+
+conf = configuration_data()
+conf.set('SRCDIR', meson.current_build_dir())
+conf.set('MANDIR', mandir)
+conf.set('HTMLDIR', htmldir)
+configure_file(
+    input: 'install-docs.sh.in',
+    output: 'install-docs.sh',
+    configuration: conf)
+
+meson.add_install_script(
+    join_paths(meson.current_build_dir(), 'install-docs.sh'))
diff --git a/Documentation/trace-cmd/install-docs.sh.in b/Documentation/trace-cmd/install-docs.sh.in
new file mode 100755
index 0000000..eca9b1f
--- /dev/null
+++ b/Documentation/trace-cmd/install-docs.sh.in
@@ -0,0 +1,20 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+for section in 1 3 5; do
+    while IFS= read -r -d '' man; do
+        [ ! -d "${DESTDIR}@MANDIR@/man${section}" ] && install -d "${DESTDIR}@MANDIR@/man${section}"
+
+        echo Installing "${man}" to "${DESTDIR}@MANDIR@/man${section}"
+        install -m 0644 "${man}" "${DESTDIR}@MANDIR@/man${section}/"
+    done< <(find "@SRCDIR@" -name "*\.${section}" -type f -print0)
+done
+
+while IFS= read -r -d '' html; do
+    [ ! -d "${DESTDIR}@HTMLDIR@"  ] && install -d "${DESTDIR}@HTMLDIR@"
+
+    echo Installing "${html}" to "${DESTDIR}@HTMLDIR@"
+    install -m 0644 "${html}" "${DESTDIR}@HTMLDIR@"
+done< <(find "@SRCDIR@" -name "*\.html" -type f -print0)
diff --git a/Documentation/trace-cmd/meson.build b/Documentation/trace-cmd/meson.build
new file mode 100644
index 0000000..875d728
--- /dev/null
+++ b/Documentation/trace-cmd/meson.build
@@ -0,0 +1,178 @@
+# SPDX-License-Identifier: LGPL-2.1
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+# input text file: man page section
+
+sources = {
+    'trace-cmd.1.txt': '1',
+    'trace-cmd-agent.1.txt': '1',
+    'trace-cmd-check-events.1.txt': '1',
+    'trace-cmd-clear.1.txt': '1',
+    'trace-cmd-convert.1.txt': '1',
+    'trace-cmd.dat.v6.5.txt': '5',
+    'trace-cmd.dat.v7.5.txt': '5',
+    'trace-cmd-dump.1.txt': '1',
+    'trace-cmd-extract.1.txt': '1',
+    'trace-cmd-hist.1.txt': '1',
+    'trace-cmd-list.1.txt': '1',
+    'trace-cmd-listen.1.txt': '1',
+    'trace-cmd-mem.1.txt': '1',
+    'trace-cmd-options.1.txt': '1',
+    'trace-cmd-profile.1.txt': '1',
+    'trace-cmd-record.1.txt': '1',
+    'trace-cmd-report.1.txt': '1',
+    'trace-cmd-reset.1.txt': '1',
+    'trace-cmd-restore.1.txt': '1',
+    'trace-cmd-set.1.txt': '1',
+    'trace-cmd-show.1.txt': '1',
+    'trace-cmd-snapshot.1.txt': '1',
+    'trace-cmd-split.1.txt': '1',
+    'trace-cmd-stack.1.txt': '1',
+    'trace-cmd-start.1.txt': '1',
+    'trace-cmd-stat.1.txt': '1',
+    'trace-cmd-stop.1.txt': '1',
+    'trace-cmd-stream.1.txt': '1',
+}
+
+confdir = meson.current_source_dir() + '/../'
+top_source_dir = meson.current_source_dir() + '/../../'
+
+#
+# For asciidoc ...
+#   -7.1.2,     no extra settings are needed.
+#    8.0-,      set ASCIIDOC8.
+#
+
+#
+# For docbook-xsl ...
+#   -1.68.1,         set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0)
+#    1.69.0,         no extra settings are needed?
+#    1.69.1-1.71.0,  set DOCBOOK_SUPPRESS_SP?
+#    1.71.1,         no extra settings are needed?
+#    1.72.0,         set DOCBOOK_XSL_172.
+#    1.73.0-,        set ASCIIDOC_NO_ROFF
+#
+
+#
+# If you had been using DOCBOOK_XSL_172 in an attempt to get rid
+# of 'the ".ft C" problem' in your generated manpages, and you
+# instead ended up with weird characters around callouts, try
+# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8).
+#
+
+if get_option('asciidoctor')
+    asciidoc = find_program('asciidoctor')
+    asciidoc_extra  = ['-a', 'compat-mode']
+    asciidoc_extra += ['-I.']
+    asciidoc_extra += ['-r', 'asciidoctor-extensions']
+    asciidoc_extra += ['-a', 'mansource=libtraceevent']
+    asciidoc_extra += ['-a', 'manmanual="libtraceevent Manual"']
+    asciidoc_html = 'xhtml5'
+else
+    asciidoc = find_program('asciidoc')
+    asciidoc_extra  = ['--unsafe']
+    asciidoc_extra += ['-f', confdir + 'asciidoc.conf']
+    asciidoc_html = 'xhtml11'
+
+    r = run_command(asciidoc, '--version', check: true)
+    v = r.stdout().strip()
+    if v.version_compare('>=8.0')
+        asciidoc_extra += ['-a', 'asciidoc7compatible']
+    endif
+endif
+
+manpage_xsl = confdir + 'manpage-normal.xsl'
+
+if get_option('docbook-xls-172')
+    asciidoc_extra += ['-a', 'libtraceevent-asciidoc-no-roff']
+    manpage_xsl = confdir + 'manpage-1.72.xsl'
+elif get_option('asciidoc-no-roff')
+    # docbook-xsl after 1.72 needs the regular XSL, but will not
+    # pass-thru raw roff codes from asciidoc.conf, so turn them off.
+    asciidoc_extra += ['-a', 'libtraceevent-asciidoc-no-roff']
+endif
+
+xmlto = find_program('xmlto')
+xmlto_extra = []
+
+if get_option('man-bold-literal')
+    xmlto_extra += ['-m ', confdir + 'manpage-bold-literal.xsl']
+endif
+
+if get_option('docbook-suppress-sp')
+    xmlto_extra += ['-m ',  confdir + 'manpage-suppress-sp.xsl']
+endif
+
+gen = generator(
+    asciidoc,
+    output: '@BASENAME@.xml',
+    arguments: [
+        '-b', 'docbook',
+        '-d', 'manpage',
+        '-a', 'libtraceevent_version=' + meson.project_version(),
+        '-o', '@OUTPUT@']
+        + asciidoc_extra
+        +  ['@INPUT@'])
+
+man = []
+html = []
+foreach txt, section : sources
+    # build man page(s)
+    xml = gen.process(txt)
+    man += custom_target(
+        txt.underscorify() + '_man',
+        input: xml,
+        output: '@BASENAME@.' + section,
+        command: [
+            xmlto,
+            '-m', manpage_xsl,
+            'man',
+            '-o', '@OUTPUT@']
+            + xmlto_extra
+            + ['@INPUT@'])
+
+    # build html pages
+    html += custom_target(
+        txt.underscorify() + '_html',
+        input: txt,
+        output: '@BASENAME@.html',
+        command: [
+            asciidoc,
+            '-b', asciidoc_html,
+            '-d', 'manpage',
+            '-a', 'libtraceevent_version=' + meson.project_version(),
+            '-o', '@OUTPUT@']
+            + asciidoc_extra
+            + ['@INPUT@'])
+endforeach
+
+# Install path workaround because:
+#
+# - xmlto might generate more than one file and we would to tell meson
+#   about those output files. We could figure out which files are generated
+#   (see sed match in check-manpages.sh).
+#
+# - The man page generation puts all the generated files under sub dirs
+#   and it's not obvious how to tell Meson it should not do this without
+#   causing the install step to fail (confusion where the generated files
+#   are stored)
+#
+# - The documentation build is not part of the 'build' target. The user
+#   has explicitly to trigger the doc build. Hence the documentation is
+#   not added to the 'install' target.
+#
+# Thus just use a plain old shell script to move the generated files to the
+# right location.
+
+conf = configuration_data()
+conf.set('SRCDIR', meson.current_build_dir())
+conf.set('MANDIR', mandir)
+conf.set('HTMLDIR', htmldir)
+configure_file(
+    input: 'install-docs.sh.in',
+    output: 'install-docs.sh',
+    configuration: conf)
+
+meson.add_install_script(
+    join_paths(meson.current_build_dir(), 'install-docs.sh'))
diff --git a/Documentation/trace-cmd/trace-cmd-agent.1.txt b/Documentation/trace-cmd/trace-cmd-agent.1.txt
index f247d41..8c1721d 100644
--- a/Documentation/trace-cmd/trace-cmd-agent.1.txt
+++ b/Documentation/trace-cmd/trace-cmd-agent.1.txt
@@ -32,6 +32,11 @@
     This options causes trace-cmd agent to go into a daemon mode and run in
     the background.
 
+*-P* 'cid'::
+    Allow an agent to also act as a proxy server, where it can be run on a host
+    and connect with a guest. 'cid' is the context ID (see *vsock*(7)) of the
+    client (e.g., guest VM) it will allow to connect.
+
 *--verbose*[='level']::
      Set the log level. Supported log levels are "none", "critical", "error", "warning",
      "info", "debug", "all" or their identifiers "0", "1", "2", "3", "4", "5", "6". Setting the log
diff --git a/Documentation/trace-cmd/trace-cmd-attach.1.txt b/Documentation/trace-cmd/trace-cmd-attach.1.txt
new file mode 100644
index 0000000..1edef4a
--- /dev/null
+++ b/Documentation/trace-cmd/trace-cmd-attach.1.txt
@@ -0,0 +1,124 @@
+TRACE-CMD-ATTACH(1)
+===================
+
+NAME
+----
+trace-cmd-attach - attach a guest trace.dat file to a host trace.dat file
+
+SYNOPSIS
+--------
+*trace-cmd attach* ['OPTIONS'] host-trace-file guest-trace-file guest-pid [guest-pid ...]
+
+DESCRIPTION
+-----------
+The trace-cmd(1) attach is used to take a trace.dat file created on a guest and
+attach it to a trace.dat file that was created on the host. In most cases,
+trace-cmd-agent(1) can be used to automate this, but if for some reason, the
+agent isn't appropriate, it may be required to start trace-cmd recording on
+the guest with trace-cmd-record(1). If the host recording is activated at the
+same time, one can use trace-cmd attach(1) to connect the guest and host files
+as if they were created by the trace-cmd agent.
+
+*host-trace-file*::
+    The trace.dat file created by the host. Must have kvm_exit and kvm_entry
+    events, and use the "tsc2nsec" clock.
+
+*guest-trace-file*::
+    The trace.dat file created by the guest. Must use the "x86-tsc" clock.
+    For now, this is only supported on x86, it may support other achitectures
+    later.
+
+*guest-pid*::
+    The process ID of the host thread that represents the guests threads.
+    Each process ID that represents all of the guest vCPUs should be listed.
+    Note, you can add more than just the threads that represent the guest
+    vCPUs, as the tool will search the *host-trace-file* for kvm_exit and
+    kvm_entry events to match these PIDs with the vCPUs that they represent.
+
+OPTIONS
+-------
+*-c* 'cpus'::
+    Specify a the number of CPUS the guest has.
+
+*-s* 'timeshift'::
+    A comma separated list of the format _offset_,_scale_,_frac_,_timestamp_
+    These values map to what are given in /sys/kernel/kvm/<pid>/vcpu/*
+
+    *offset*: Is the offset of the guest. "tsc-offest" in the directory. Note
+              that the value listed here is the negative of what is listed in
+              the directory.
+
+    *scale*:  The scaling factor. "tsc-scaling-ratio"
+
+    *frac*:   The fraction bits. "tsc-scaling-ratio-frac-bits"
+
+    *timestamp*: The timestamp to start using the above. In some cases, the
+              values may change over time. By adding a timestamp, it will
+              take effect after the timestamp has been hit. Normally
+              this would be zero.
+
+   Currently, only one timeshift is given per CPU. One *-s* option should be
+   given for each CPU. If there are less options than CPUs, then the last option
+   given will be used for the rest of the CPUs. If only one option is given, then
+   the values for that option will be used for all CPUs.
+
+EXAMPLES
+--------
+
+Enable all events for tracing:
+
+[source,shell]
+----
+  $ # find the process for a given guest
+  $ ps aux |grep qemu
+ libvirt+   63170  5.6  1.6 13994848 4257540 ?    Sl   May02 2884:49 /usr/bin/qemu-system-x86_64...
+
+  $ # Use 63170 to find all the PIDs for the give guest
+  $ ls /proc/63170/task
+ 1541591  63170  63198  63209  63211  63213  63214  63215  63216  63217  63218  63219  63234
+
+  $ # Find the tsc offset
+  $ su
+  # cat /sys/kernel/debug/kvm/63170-15/vcpu0/tsc-offset
+ -27950965013436847
+
+  # trace-cmd record -C tsc2nesc -e kvm -e sched -e irq -e timer
+
+# on guest:
+
+  # trace-cmd record -C x86-tsc -e sched -e irq -e timer sleep 10
+
+# back on host, hit Ctrl^C to stop tracing after the guest is done
+
+  # # Make the trace.dat user owned by your user account.
+  # chown user.user trace.dat
+  # exit
+
+  $ scp root@guest:trace.dat trace-guest.dat
+
+  $ # now attach the two files (guest has 8 CPUs)
+  $ trace-cmd attach -c 8 -s 27950965013436847 trace.dat trace-guest.dat 1541591  63170  63198  63209  63211  63213  63214  63215  63216  63217  63218  63219  63234
+
+  $ trace-cmd report -i trace.dat -i trace-guest.dat
+  $ # now you should see the guest trace interleaved within the host trace.
+----
+
+
+SEE ALSO
+--------
+trace-cmd(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1),
+trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1),
+trace-cmd-list(1), trace-cmd-listen(1), trace-cmd-profile(1)
+
+AUTHOR
+------
+Written by Steven Rostedt (Google) <rostedt@goodmis.org>
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/
+
+COPYING
+-------
+Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/trace-cmd/trace-cmd-convert.1.txt b/Documentation/trace-cmd/trace-cmd-convert.1.txt
index 7c13cf3..21f580d 100644
--- a/Documentation/trace-cmd/trace-cmd-convert.1.txt
+++ b/Documentation/trace-cmd/trace-cmd-convert.1.txt
@@ -13,7 +13,8 @@
 -----------
 The trace-cmd(1) convert command converts trace file. It reads the input file and copies the data
 into an output file. The output file may be in different format, depending on the command line
-arguments.
+arguments. The default output is in version 7 and compressed (if
+compiled with compression support).
 
 OPTIONS
 -------
diff --git a/Documentation/trace-cmd/trace-cmd-dump.1.txt b/Documentation/trace-cmd/trace-cmd-dump.1.txt
index 9c95244..318a0fe 100644
--- a/Documentation/trace-cmd/trace-cmd-dump.1.txt
+++ b/Documentation/trace-cmd/trace-cmd-dump.1.txt
@@ -65,6 +65,8 @@
 EXAMPLES
 --------
 
+[source,shell]
+----
 # trace-cmd dump --summary -i trace.dat
 
  Tracing meta data in file trace.dat:
@@ -83,8 +85,10 @@
 	8 [CPUs with tracing data]
 	[12 options]
 	[Flyrecord tracing data]
-------------------------------------------
+----
 
+[source,shell]
+----
 # trace-cmd dump --flyrecord -i trace.dat
 	[Flyrecord tracing data]
 		 7176192 0	[offset, size of cpu 0]
@@ -95,8 +99,10 @@
 		 7184384 0	[offset, size of cpu 5]
 		 7184384 0	[offset, size of cpu 6]
 		 7184384 0	[offset, size of cpu 7]
-------------------------------------------
+----
 
+[source,shell]
+----
 # trace-cmd dump --summary --systems -i trace.dat
 
  Tracing meta data in file trace.dat:
@@ -118,11 +124,14 @@
 	8 [CPUs with tracing data]
 	[11 options]
 	[Flyrecord tracing data]
-------------------------------------------
+----
 
+[source,shell]
+----
 # trace-cmd dump --summary --systems -i trace.dat
 File trace.dat is a valid trace-cmd file
-------------------------------------------
+----
+
 SEE ALSO
 --------
 trace-cmd(1), trace-cmd.dat(1)
diff --git a/Documentation/trace-cmd/trace-cmd-record.1.txt b/Documentation/trace-cmd/trace-cmd-record.1.txt
index 6b8e3b4..7cb652b 100644
--- a/Documentation/trace-cmd/trace-cmd-record.1.txt
+++ b/Documentation/trace-cmd/trace-cmd-record.1.txt
@@ -77,9 +77,10 @@
     may depend on what version of the kernel you have. Basically, it will
     let you use C notation to check if an event should be processed or not.
 
-----------------------------------------
+[source,bison]
+----
     ==, >=, <=, >, <, &, |, && and ||
-----------------------------------------
+----
 
     The above are usually safe to use to compare fields.
 
@@ -197,6 +198,21 @@
     inside the kernel. Using "-b 10000" on a machine with 4 CPUs will make
     Ftrace have a total buffer size of 40 Megs.
 
+*--subbuf-size*::
+    The Linux kernel tracing ring buffer is broken up into sub-buffers.
+    These sub-buffers are typically the size of the architecture "page-size".
+    (4096 or x86). An event can only be as big as the data portion of a
+    sub-buffer, but in most cases that's not an issue. But the time the
+    writer takes to switch from one sub-buffer to the next has a bit more
+    overhead than adding events within the sub-buffer. By increasing its
+    size, it will allow bigger events (although that is seldom an issue)
+    but also speed up the tracing itself.
+
+    The downside of larger sub-buffers is that a "read" of the ring buffer
+    will pull the sub-buffer size out of the ring buffer and replace it
+    with a new sub-buffer. This may not have any real impact, but it may
+    change the behavior slightly. Or it may not!
+
 *-B* 'buffer-name'::
     If the kernel supports multiple buffers, this will add a buffer with
     the given name. If the buffer name already exists, that buffer is just
@@ -343,6 +359,14 @@
     any events (like sched_switch), unless they are specifically specified
     on the command line (i.e. -p function -e sched_switch -e sched_wakeup)
 
+*--temp* 'directory'::
+    When *trace-cmd* is recording the trace, it records the per CPU data into
+    a separate file for each CPU. At the end of the trace, these files are
+    concatenated onto the final trace.dat file. If the final file is on a network
+    file system, it may not be appropriate to copy these temp files into the
+    same location. *--temp* can be used to tell *trace-cmd* where those temp
+    files should be created.
+
 *--ts-offset offset*::
     Add an offset for the timestamp in the trace.dat file. This will add a
     offset option into the trace.dat file such that a trace-cmd report will
@@ -354,9 +378,9 @@
 *--tsync-interval*::
     Set the loop interval, in ms, for timestamps synchronization with guests:
         If a negative number is specified, timestamps synchronization is disabled
-        If 0 is specified, no loop is performed - timestamps offset is calculated only twice,"
+        If 0 is specified, no loop is performed - timestamps offset is calculated only twice,
         at the beginning and at the end of the trace.
-        Timestamps synchronization with guests works only if there is support for VSOCK.\n"
+        Timestamps synchronization with guests works only if there is support for VSOCK.
 
 *--tsc2nsec*::
     Convert the current clock to nanoseconds, using tsc multiplier and shift from the Linux
@@ -403,27 +427,37 @@
     'name' - the name of the desired compression algorithms. Available algorithms can be listed with
     trace-cmd list -c
 
+*--proxy* 'vsocket'::
+    Use a vsocket proxy to reach the agent. Acts the same as *-A* (for an
+    agent) but will send the proxy connection to the agent. It is expected to
+    run on a privileged guest that the host is aware of (as denoted by the
+    'cid' in the *-P* option for the agent).
+
+*--daemonize*
+    Run trace-cmd in the background as a daemon after recording has started.
+    Creates a pidfile at /var/run/trace-cmd-record.pid with the pid of trace-cmd during the recording.
+
 EXAMPLES
 --------
 
 The basic way to trace all events:
 
-------------------------------
+[source,shell]
+----
  # trace-cmd record -e all ls > /dev/null
  # trace-cmd report
        trace-cmd-13541 [003] 106260.693809: filemap_fault: address=0x128122 offset=0xce
        trace-cmd-13543 [001] 106260.693809: kmalloc: call_site=81128dd4 ptr=0xffff88003dd83800 bytes_req=768 bytes_alloc=1024 gfp_flags=GFP_KERNEL|GFP_ZERO
               ls-13545 [002] 106260.693809: kfree: call_site=810a7abb ptr=0x0
               ls-13545 [002] 106260.693818: sys_exit_write:       0x1
-
-
-------------------------------
+----
 
 
 
 To use the function tracer with sched switch tracing:
 
-------------------------------
+[source,shell]
+----
  # trace-cmd record -p function -e sched_switch ls > /dev/null
  # trace-cmd report
               ls-13587 [002] 106467.860310: function: hrtick_start_fair <-- pick_next_task_fair
@@ -434,12 +468,11 @@
        trace-cmd-13585 [001] 106467.860318: function: _raw_spin_unlock <-- __do_fault
               ls-13587 [002] 106467.860320: function: native_load_sp0 <-- __switch_to
        trace-cmd-13586 [003] 106467.860322: function: down_read_trylock <-- do_page_fault
-
-
-------------------------------
+----
 
 Here is a nice way to find what interrupts have the highest latency:
-------------------------------------------
+[source,shell]
+----
  # trace-cmd record -p function_graph -e irq_handler_entry  -l do_IRQ sleep 10
  # trace-cmd report
           <idle>-0     [000] 157412.933969: funcgraph_entry:                  |  do_IRQ() {
@@ -457,12 +490,11 @@
           <idle>-0     [000] 157417.888373: funcgraph_entry:                  |  do_IRQ() {
           <idle>-0     [000] 157417.888381: irq_handler_entry:    irq=48 name=eth0
           <idle>-0     [000] 157417.888398: funcgraph_exit:       + 25.943 us |  }
-
-
-------------------------------------------
+----
 
 An example of the profile:
-------------------------------------------
+[source,shell]
+----
  # trace-cmd record --profile sleep 1
  # trace-cmd report --profile --comm sleep
 task: sleep-21611
@@ -496,7 +528,36 @@
        => ttwu_do_wakeup (0xffffffff810606eb)
        => ttwu_do_activate.constprop.124 (0xffffffff810607c8)
        => try_to_wake_up (0xffffffff8106340a)
-------------------------------------------
+----
+
+An example of using --daemonize together with guest/host tracing:
+[source,shell]
+----
+$ sudo trace-cmd record --daemonize -p nop -e 'sched:sched_process_exec' -A guest -p nop -e net &&
+> ping -c 1 10.20.1.2 &&
+> sudo start-stop-daemon --stop --signal INT --retry 20 --pidfile /var/run/trace-cmd-record.pid &&
+> sudo trace-cmd report -i trace.dat -i trace-guest.dat | head
+Negotiated kvm time sync protocol with guest guest
+Send SIGINT to pid 3071371 to stop recording
+PING 10.20.1.2 (10.20.1.2) 56(84) bytes of data.
+64 bytes from 10.20.1.2: icmp_seq=1 ttl=64 time=0.134 ms
+--- 10.20.1.2 ping statistics ---
+1 packets transmitted, 1 received, 0% packet loss, time 0ms
+rtt min/avg/max/mdev = 0.134/0.134/0.134/0.000 ms
+CPU0 data recorded at offset=0x14f000
+    229 bytes in size (4096 uncompressed)
+....
+      trace.dat: cpus=28
+trace-guest.dat: cpus=1
+      trace.dat:           ping-3071450 [013] 1196830.834258: sched_process_exec:     filename=/bin/ping pid=3071450 old_pid=3071450
+trace-guest.dat:           <idle>-0     [000] 1196830.835990: napi_gro_receive_entry: dev=eth1 napi_id=0x2002 queue_mapping=1 skbaddr=0xffff95d051a5c400 vlan_tagged=0 vlan_proto=0x0000 vlan_tci=0x0000 protocol=0x0800 ip_summed=0 hash=0x00000000 l4_hash=0 len=84 data_len=0 truesize=768 mac_header_valid=1 mac_header=-14 nr_frags=0 gso_size=0 gso_type=0
+trace-guest.dat:           <idle>-0     [000] 1196830.835997: napi_gro_receive_exit:  ret=3
+trace-guest.dat:           <idle>-0     [000] 1196830.835998: netif_receive_skb:      dev=eth1 skbaddr=0xffff95d051a5c400x len=84
+trace-guest.dat:           <idle>-0     [000] 1196830.836021: net_dev_queue:          dev=eth1 skbaddr=0xffff95d051a5c700x len=98
+trace-guest.dat:           <idle>-0     [000] 1196830.836024: net_dev_start_xmit:     dev=eth1 queue_mapping=0 skbaddr=0xffff95d051a5c700 vlan_tagged=0 vlan_proto=0x0000 vlan_tci=0x0000 protocol=0x0800 ip_summed=0 len=98 data_len=0 network_offset=14 transport_offset_valid=1 transport_offset=34 tx_flags=0 gso_size=0 gso_segs=0 gso_type=0
+trace-guest.dat:           <idle>-0     [000] 1196830.836069: net_dev_xmit:           dev=eth1 skbaddr=0xffff95d051a5c700 len=98 rc=0
+      trace.dat:           sudo-3071451 [015] 1196830.838262: sched_process_exec:     filename=/usr/bin/sudo pid=3071451 old_pid=3071451
+----
 
 SEE ALSO
 --------
diff --git a/Documentation/trace-cmd/trace-cmd-report.1.txt b/Documentation/trace-cmd/trace-cmd-report.1.txt
index aad8ab5..41f23b2 100644
--- a/Documentation/trace-cmd/trace-cmd-report.1.txt
+++ b/Documentation/trace-cmd/trace-cmd-report.1.txt
@@ -7,7 +7,7 @@
 
 SYNOPSIS
 --------
-*trace-cmd report* ['OPTIONS'] ['input-file']
+*trace-cmd report* ['OPTIONS'] ['input-file' ['input-file' ...]]
 
 DESCRIPTION
 -----------
@@ -69,10 +69,15 @@
     is only to the microsecond. To see the full timestamp, add the *-t* option.
 
 *-F* 'filter'::
-    Add a filter to limit what events are displayed. The format of the filter
-    is:
+    Add a filter to limit what events are displayed.  Filters defined
+    after an input file (specified with *-i*) only apply to that
+    input file. Filters provided before any input file is given are
+    considered global and apply to all input files.
 
-------------------------------------------
+    The format of the filter is:
+
+[source,bison]
+----
     <events> ':' <filter>
     <events> = SYSTEM'/'EVENT  | SYSTEM | EVENT | <events> ',' <events>
     <filter> = EVENT_FIELD <op> <value> | <filter> '&&' <filter> |
@@ -80,7 +85,7 @@
     <op> = '==' | '!=' | '>=' | '<=' | '>' | '<' | '&' | '|' | '^' |
            '+' | '-' | '*' | '/' | '%'
     <value> = NUM | STRING | EVENT_FIELD
-------------------------------------------
+----
 
     SYSTEM is the name of the system to filter on. If the EVENT is left out,
     then it applies to all events under the SYSTEM. If only one string is used
@@ -101,9 +106,10 @@
     filtered. If the event does not contain the EVENT_FIELD, that part of the
     equation will be considered false.
 
-------------------------------------------
+[source,shell]
+----
     -F 'sched : bogus == 1 || common_pid == 2'
-------------------------------------------
+----
 
     The "bogus == 1" will always evaluate to FALSE because no event has a
     field called "bogus", but the "common_pid == 2" will still be evaluated
@@ -119,9 +125,10 @@
     value displayed. For example, to filter on all tasks that were in the
     running state at a context switch:
 
-------------------------------------------
+[source,shell]
+----
     -F 'sched/sched_switch : prev_state==0'
-------------------------------------------
+----
 
     Although the output displays 'R', having 'prev_stat=="R"' will not work.
 
@@ -129,9 +136,10 @@
     task name (or comm) of the record to compare. For example, to filter out
     all of the "trace-cmd" tasks:
 
-------------------------------------------
+[source,shell]
+----
     -F '.*:COMM != "trace-cmd"'
-------------------------------------------
+----
 
 *-I*::
     Do not print events where the HARDIRQ latency flag is set.
@@ -148,9 +156,10 @@
     This causes the following filters of *-F* to filter out the matching
     events.
 
-------------------------------------------
+[source,shell]
+----
     -v -F 'sched/sched_switch : prev_state == 0'
-------------------------------------------
+----
 
     Will not display any sched_switch events that have a prev_state of 0.
     Removing the *-v* will only print out those events.
@@ -211,17 +220,43 @@
     will set display this information with 6 characters. When one of the
     fields is zero or N/A a \'.\' is shown.
 
-------------------------------------------
+[source,shell]
+----
       <idle>-0       0d.h1. 106467.859747: function:             ktime_get <-- tick_check_idle
-------------------------------------------
+----
 
-    The 0d.h1. denotes this information. The first character is never a '.'
-    and represents what CPU the trace was recorded on (CPU 0). The 'd' denotes
-    that interrupts were disabled. The 'h' means that this was called inside
-    an interrupt handler. The '1' is the preemption disabled (preempt_count)
-    was set to one.  The two '.'s are "need_resched" flag and kernel lock
-    counter.  If the "need_resched" flag is set, then that character would be a
-    'N'.
+    The 0d.h1. denotes this information.
+
+    It starts with a number. This represents the CPU number that the event occurred
+    on.
+
+    The second character is one of the following:
+
+	'd' - Interrupts are disabled
+	'.' - Interrupts are enabled
+	'X' - Has flags that are not yet known by trace-cmd
+
+    The third character is the "need rescheduling" flag.
+
+	'N' - A schedule is set to take place
+	'.' - No scheduling is set
+
+    The fourth character represents the context the event was in when it triggered
+
+	'h' - Hard interrupt context
+	's' - Soft interrupt context
+	'H' - Hard interrupt context that interrupted a soft interrupt
+	'.' - Normal context
+
+    The next is a number (should be less than 10), that represents the preemption
+    depth (the number of times preempt_disable() is called without preempt_enable()).
+    '.' means preemption is enabled.
+
+    On some systems, "migrate disable" may exist, in which case a number will be
+    shown for that, or '.' meaning migration is enabled.
+
+    If lockdep in enabled on the system, then the number represents the depth of
+    locks that are held when the event triggered. '.' means no locks are held.
 
 *-w*::
     If both the 'sched_switch' and 'sched_wakeup' events are enabled, then
@@ -254,6 +289,12 @@
 *--cpus*::
     List the CPUs that have data in the trace file then exit.
 
+*--first-event*::
+    Show the timestamp of the first event of all CPUs that have data.
+
+*--last-event*::
+    Show the timestamp of the last event of all CPUs that have data.
+
 *--stat*::
     If the trace.dat file recorded the final stats (outputed at the end of record)
     the *--stat* option can be used to retrieve them.
@@ -336,132 +377,246 @@
 
 Using a trace.dat file that was created with:
 
-------------------------------------------
+[source,shell]
+----
     # trace-cmd record -p function -e all sleep 5
-
-
-------------------------------------------
+----
 
 The default report shows:
 
-------------------------------------------
+[source,shell]
+----
  # trace-cmd report
-       trace-cmd-16129 [002] 158126.498411: function: __mutex_unlock_slowpath <-- mutex_unlock
-       trace-cmd-16131 [000] 158126.498411: kmem_cache_alloc: call_site=811223c5 ptr=0xffff88003ecf2b40 bytes_req=272 bytes_alloc=320 gfp_flags=GFP_KERNEL|GFP_ZERO
-       trace-cmd-16130 [003] 158126.498411: function:             do_splice_to <-- sys_splice
-           sleep-16133 [001] 158126.498412: function: inotify_inode_queue_event <-- vfs_write
-       trace-cmd-16129 [002] 158126.498420: lock_release: 0xffff88003f1fa4f8 &sb->s_type->i_mutex_key
-       trace-cmd-16131 [000] 158126.498421: function: security_file_alloc <-- get_empty_filp
-           sleep-16133 [001] 158126.498422: function: __fsnotify_parent <-- vfs_write
-       trace-cmd-16130 [003] 158126.498422: function: rw_verify_area <-- do_splice_to
-       trace-cmd-16131 [000] 158126.498424: function: cap_file_alloc_security <-- security_file_alloc
-       trace-cmd-16129 [002] 158126.498425: function: syscall_trace_leave <-- int_check_syscall_exit_work
-           sleep-16133 [001] 158126.498426: function: inotify_dentry_parent_queue_event <-- vfs_write
-       trace-cmd-16130 [003] 158126.498426: function: security_file_permission <-- rw_verify_area
-       trace-cmd-16129 [002] 158126.498428: function: audit_syscall_exit <-- syscall_trace_leave
+cpus=8
+           sleep-89142 [001] ...1. 162573.215752: function:             mutex_unlock
+           sleep-89142 [001] ...1. 162573.215754: function:             __mutex_unlock_slowpath
+           sleep-89142 [001] ..... 162573.215755: lock_release:         0xffffffff855e7448 trace_types_lock
+           sleep-89142 [001] ..... 162573.215757: lock_release:         0xffff892a01b54420 sb_writers
+           sleep-89142 [001] ...1. 162573.215757: function:             preempt_count_add
+           sleep-89142 [001] ...1. 162573.215758: preempt_disable:      caller=vfs_write+0x147 parent=vfs_write+0x147
+           sleep-89142 [001] ...2. 162573.215758: function:             rcu_read_lock_any_held
+           sleep-89142 [001] ...2. 162573.215759: function:                rcu_lockdep_current_cpu_online
+           sleep-89142 [001] ...2. 162573.215759: function:             preempt_count_sub
+           sleep-89142 [001] ...1. 162573.215760: preempt_enable:       caller=vfs_write+0x176 parent=vfs_write+0x176
+           sleep-89142 [001] ...1. 162573.215761: function:             __f_unlock_pos
+           sleep-89142 [001] ...1. 162573.215761: function:             mutex_unlock
 [...]
+----
 
+The note on the third column:
 
-------------------------------------------
+[source,shell]
+----
+           sleep-89998 [002] ...1. 223087.004011: lock_acquire:         0xffff892b7cf32c20 lock
+           sleep-89998 [002] ...1. 223087.004011: lock_acquire:         0xffffffff85517f00 read rcu_read_lock
+          <idle>-0     [005] dNh2. 223087.004012: sched_wakeup:         trace-cmd:89992 [120] CPU:005
+           sleep-89998 [002] ...1. 223087.004012: lock_acquire:         0xffffffff85517f00 read rcu_read_lock
+           sleep-89998 [002] ...1. 223087.004013: lock_release:         0xffffffff85517f00 rcu_read_lock
+----
+
+It follows the same as shown in the Linux kernel `/sys/kernel/tracing/trace` file.
+
+[source,shell]
+----
+# cat /sys/kernel/tracing/trace
+# tracer: nop
+#
+# entries-in-buffer/entries-written: 0/0   #P:8
+#
+#                                _-----=> irqs-off/BH-disabled
+#                               / _----=> need-resched
+#                              | / _---=> hardirq/softirq
+#                              || / _--=> preempt-depth
+#                              ||| / _-=> migrate-disable
+#                              |||| /     delay
+#           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
+#              | |         |   |||||     |         |
+----
+
+Is the same as explained in the *-l* option.
+Where the first position is:
+
+  '.' - means interrupts and bottom halves enabled
+  'd' - means interrupts and bottom halves are disabled
+
+The second position:
+
+   'N' - means that the "NEED_RESCHED" flag is set and the kernel should try to
+         schedule as soon as possible.
+
+The third position:
+
+   '.' - In normal/schedulable context
+   's' - In soft interrupt context
+   'h' - In hard interrupt context
+   'H' - in hard interrupt context that interrupted a soft interrupt
+
+The forth position is the preempt count depth:
+
+   'pass:[.]' - preemption is enabled
+   '#' - the depth of preemption disabled (nested)
+
+The fifth column is the migration disabled counter:
+
+   '.' - migration is enabled
+   '#' - the depth of migration being disabled (nested)
+
 
 To see everything but the function traces:
 
-------------------------------------------
+[source,shell]
+----
  # trace-cmd report -v -F 'function'
-       trace-cmd-16131 [000] 158126.498411: kmem_cache_alloc: call_site=811223c5 ptr=0xffff88003ecf2b40 bytes_req=272 bytes_alloc=320 gfp_flags=GFP_KERNEL|GFP_ZERO
-       trace-cmd-16129 [002] 158126.498420: lock_release: 0xffff88003f1fa4f8 &sb->s_type->i_mutex_key
-       trace-cmd-16130 [003] 158126.498436: lock_acquire: 0xffffffff8166bf78 read all_cpu_access_lock
-       trace-cmd-16131 [000] 158126.498438: lock_acquire: 0xffff88003df5b520 read &fs->lock
-       trace-cmd-16129 [002] 158126.498446: kfree: call_site=810a7abb ptr=0x0
-       trace-cmd-16130 [003] 158126.498448: lock_acquire: 0xffff880002250a80 &per_cpu(cpu_access_lock, cpu)
-       trace-cmd-16129 [002] 158126.498450: sys_exit_splice:      0xfffffff5
-       trace-cmd-16131 [000] 158126.498454: lock_release: 0xffff88003df5b520 &fs->lock
-           sleep-16133 [001] 158126.498456: kfree: call_site=810a7abb ptr=0x0
-           sleep-16133 [001] 158126.498460: sys_exit_write:       0x1
-       trace-cmd-16130 [003] 158126.498462: kmalloc: call_site=810bf95b ptr=0xffff88003dedc040 bytes_req=24 bytes_alloc=32 gfp_flags=GFP_KERNEL|GFP_ZERO
-
-
-------------------------------------------
+cpus=8
+           sleep-89142 [001] ..... 162573.215755: lock_release:         0xffffffff855e7448 trace_types_lock
+           sleep-89142 [001] ..... 162573.215757: lock_release:         0xffff892a01b54420 sb_writers
+           sleep-89142 [001] ...1. 162573.215758: preempt_disable:      caller=vfs_write+0x147 parent=vfs_write+0x147
+           sleep-89142 [001] ...1. 162573.215760: preempt_enable:       caller=vfs_write+0x176 parent=vfs_write+0x176
+           sleep-89142 [001] ..... 162573.215762: lock_release:         0xffff892a19601ac8 &f->f_pos_lock
+           sleep-89142 [001] ..... 162573.215764: sys_exit:             NR 1 = 1
+           sleep-89142 [001] ..... 162573.215766: sys_exit_write:       0x1
+           sleep-89142 [001] d.... 162573.215767: irq_disable:          caller=syscall_exit_to_user_mode+0x15 parent=0x0
+           sleep-89142 [001] d.... 162573.215768: irq_enable:           caller=syscall_exit_to_user_mode+0xed parent=0x0
+           sleep-89142 [001] ..... 162573.215773: lock_acquire:         0xffff892a4ad29318 read &mm->mmap_lock
+           sleep-89142 [001] ..... 162573.215775: lock_release:         0xffff892a4ad29318 &mm->mmap_lock
+           sleep-89142 [001] ..... 162573.215778: lock_acquire:         0xffff892a4ad29318 read &mm->mmap_lock
+[...]
+----
 
 To see only the kmalloc calls that were greater than 1000 bytes:
 
-------------------------------------------
- #trace-cmd report -F 'kmalloc: bytes_req > 1000'
-          <idle>-0     [000] 158128.126641: kmalloc: call_site=81330635 ptr=0xffff88003c2fd000 bytes_req=2096 bytes_alloc=4096 gfp_flags=GFP_ATOMIC
-
-
-------------------------------------------
+[source,shell]
+----
+ # trace-cmd report -F 'kmalloc: bytes_req > 1000'
+cpus=8
+           sleep-89142 [001] ..... 162573.219401: kmalloc:              (tomoyo_find_next_domain+0x84) call_site=tomoyo_find_next_domain+0x84 ptr=0xffff892a176c0000 bytes_req=4096 bytes_alloc=4096 gfp_flags=0xd40 node=-1 accounted=false
+           sleep-89142 [001] ..... 162573.219511: kmalloc:              (tomoyo_realpath_from_path+0x42) call_site=tomoyo_realpath_from_path+0x42 ptr=0xffff892a176c6000 bytes_req=4096 bytes_alloc=4096 gfp_flags=0xc40 node=-1 accounted=false
+       trace-cmd-89135 [000] ..... 162573.244301: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a63f84000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false
+       trace-cmd-89135 [000] ..... 162573.244471: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a63f84000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false
+       trace-cmd-89134 [007] ..... 162573.267148: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a628d4000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false
+       trace-cmd-89134 [007] ..... 162573.267403: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a628d4000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false
+       trace-cmd-89141 [002] ..... 162573.290583: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a12d3c000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false
+       trace-cmd-89141 [002] ..... 162573.290754: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a12d3c000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false
+       trace-cmd-89139 [004] ..... 162573.784636: kmalloc:              (kvmalloc_node_noprof+0x43) call_site=kvmalloc_node_noprof+0x43 ptr=0xffff892a63d70000 bytes_req=8193 bytes_alloc=16384 gfp_flags=0x12dc0 node=-1 accounted=false
+[...]
+----
 
 To see wakeups and sched switches that left the previous task in the running
 state:
-------------------------------------------
- # trace-cmd report -F 'sched: prev_state == 0 || (success == 1)'
-       trace-cmd-16132 [002] 158126.499951: sched_wakeup: comm=trace-cmd pid=16129 prio=120 success=1 target_cpu=002
-       trace-cmd-16132 [002] 158126.500401: sched_switch: prev_comm=trace-cmd prev_pid=16132 prev_prio=120 prev_state=R ==> next_comm=trace-cmd next_pid=16129 next_prio=120
-          <idle>-0     [003] 158126.500585: sched_wakeup: comm=trace-cmd pid=16130 prio=120 success=1 target_cpu=003
-          <idle>-0     [003] 158126.501241: sched_switch: prev_comm=swapper prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=trace-cmd next_pid=16130 next_prio=120
-       trace-cmd-16132 [000] 158126.502475: sched_wakeup: comm=trace-cmd pid=16131 prio=120 success=1 target_cpu=000
-       trace-cmd-16131 [002] 158126.506516: sched_wakeup: comm=trace-cmd pid=16129 prio=120 success=1 target_cpu=002
-          <idle>-0     [003] 158126.550110: sched_switch: prev_comm=swapper prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=trace-cmd next_pid=16130 next_prio=120
-       trace-cmd-16131 [003] 158126.570243: sched_wakeup: comm=trace-cmd pid=16129 prio=120 success=1 target_cpu=003
-       trace-cmd-16130 [002] 158126.618202: sched_switch: prev_comm=trace-cmd prev_pid=16130 prev_prio=120 prev_state=R ==> next_comm=yum-updatesd next_pid=3088 next_prio=1 20
-       trace-cmd-16129 [003] 158126.622379: sched_wakeup: comm=trace-cmd pid=16131 prio=120 success=1 target_cpu=003
-       trace-cmd-16129 [000] 158126.649287: sched_wakeup: comm=trace-cmd pid=16131 prio=120 success=1 target_cpu=000
-
-
-------------------------------------------
+[source,shell]
+----
+ # trace-cmd report -F 'sched: prev_state == 0' -F 'sched_waking'
+cpus=8
+           sleep-89142 [001] d.h6. 162573.215941: sched_waking:         comm=trace-cmd pid=89135 prio=120 target_cpu=000
+          <idle>-0     [000] dNh7. 162573.216219: sched_waking:         comm=trace-cmd pid=89134 prio=120 target_cpu=007
+          <idle>-0     [000] d..2. 162573.216423: sched_switch:         swapper/0:0 [120] R ==> trace-cmd:89135 [120]
+          <idle>-0     [007] dNh7. 162573.216511: sched_waking:         comm=trace-cmd pid=89141 prio=120 target_cpu=002
+          <idle>-0     [007] d..2. 162573.216698: sched_switch:         swapper/7:0 [120] R ==> trace-cmd:89134 [120]
+          <idle>-0     [002] dNh7. 162573.216776: sched_waking:         comm=trace-cmd pid=89136 prio=120 target_cpu=001
+          <idle>-0     [002] d..2. 162573.216951: sched_switch:         swapper/2:0 [120] R ==> trace-cmd:89141 [120]
+           sleep-89142 [001] d.s3. 162573.231260: sched_waking:         comm=rcu_preempt pid=17 prio=120 target_cpu=002
+          <idle>-0     [002] d..2. 162573.231568: sched_switch:         swapper/2:0 [120] R ==> rcu_preempt:17 [120]
+           sleep-89142 [001] d.s2. 162573.240425: sched_waking:         comm=rcu_preempt pid=17 prio=120 target_cpu=002
+          <idle>-0     [002] d..2. 162573.240719: sched_switch:         swapper/2:0 [120] R ==> rcu_preempt:17 [120]
+           sleep-89142 [001] d.h7. 162573.241983: sched_waking:         comm=trace-cmd pid=89135 prio=120 target_cpu=000
+----
 
 The above needs a little explanation. The filter specifies the "sched"
-subsystem, which includes both sched_switch and sched_wakeup events. Any event
-that does not have the format field "prev_state" or "success", will evaluate
-those expressions as FALSE, and will not produce a match. Using "||" will have
-the "prev_state" test happen for the "sched_switch" event and the "success"
-test happen for the "sched_wakeup" event.
+subsystem, which includes all scheduling events. Any event
+that does not have the format field "prev_state", will evaluate
+those expressions as FALSE, and will not produce a match. Only the sched_switch
+event will match that. The second "-F" will include the sched_waking event.
 
 
-------------------------------------------
+[source,shell]
+----
   # trace-cmd report -w -F 'sched_switch, sched_wakeup.*'
 [...]
-       trace-cmd-16130 [003] 158131.580616: sched_wakeup: comm=trace-cmd pid=16131 prio=120 success=1 target_cpu=003
-       trace-cmd-16129 [000] 158131.581502: sched_switch: prev_comm=trace-cmd prev_pid=16129 prev_prio=120 prev_state=S ==> next_comm=trace-cmd next_pid=16131 next_prio=120 Latency: 885.901 usecs
-       trace-cmd-16131 [000] 158131.582414: sched_wakeup: comm=trace-cmd pid=16129 prio=120 success=1 target_cpu=000
-       trace-cmd-16132 [001] 158131.583219: sched_switch: prev_comm=trace-cmd prev_pid=16132 prev_prio=120 prev_state=S ==> next_comm=trace-cmd next_pid=16129 next_prio=120 Latency: 804.809 usecs
-           sleep-16133 [002] 158131.584121: sched_wakeup: comm=trace-cmd pid=16120 prio=120 success=1 target_cpu=002
-       trace-cmd-16129 [001] 158131.584128: sched_wakeup: comm=trace-cmd pid=16132 prio=120 success=1 target_cpu=001
-           sleep-16133 [002] 158131.584275: sched_switch: prev_comm=sleep prev_pid=16133 prev_prio=120 prev_state=R ==> next_comm=trace-cmd next_pid=16120 next_prio=120 Latency: 153.915 usecs
-       trace-cmd-16130 [003] 158131.585284: sched_switch: prev_comm=trace-cmd prev_pid=16130 prev_prio=120 prev_state=S ==> next_comm=trace-cmd next_pid=16132 next_prio=120 Latency: 1155.677 usecs
+       trace-cmd-89141 [007] d..2. 162583.263060: sched_switch:         trace-cmd:89141 [120] R ==> trace-cmd:89135 [120]
+   kworker/u36:1-51219 [000] d..2. 162583.266957: sched_switch:         kworker/u36:1:51219 [120] R ==> kworker/u33:2:49692 [120] Latency: 4024.977 usecs
+       trace-cmd-89135 [007] d..2. 162583.267109: sched_switch:         trace-cmd:89135 [120] R ==> trace-cmd:89141 [120]
+       trace-cmd-89139 [001] d..2. 162583.267147: sched_switch:         trace-cmd:89139 [120] D ==> swapper/1:0 [120]
+   kworker/u36:2-88857 [002] d..2. 162583.267913: sched_switch:         kworker/u36:2:88857 [120] R ==> trace-cmd:89136 [120]
+   kworker/u33:2-49692 [000] d..2. 162583.268334: sched_switch:         kworker/u33:2:49692 [120] I ==> kworker/u36:1:51219 [120]
+          <idle>-0     [001] dNh4. 162583.268747: sched_wakeup:         sleep:89142 [120] CPU:001
+          <idle>-0     [001] d..2. 162583.268833: sched_switch:         swapper/1:0 [120] R ==> sleep:89142 [120] Latency: 85.751 usecs
+           sleep-89142 [001] d.h4. 162583.269022: sched_wakeup:         trace-cmd:89139 [120] CPU:001
+       trace-cmd-89141 [007] d..2. 162583.271009: sched_switch:         trace-cmd:89141 [120] R ==> trace-cmd:89135 [120]
+       trace-cmd-89136 [002] d..2. 162583.271020: sched_switch:         trace-cmd:89136 [120] R ==> kworker/u36:2:88857 [120]
+   kworker/u36:2-88857 [002] d..2. 162583.271079: sched_switch:         kworker/u36:2:88857 [120] I ==> trace-cmd:89136 [120]
+       trace-cmd-89137 [006] d.h2. 162583.273950: sched_wakeup:         trace-cmd:89133 [120] CPU:006
+           sleep-89142 [001] d..2. 162583.274064: sched_switch:         sleep:89142 [120] Z ==> trace-cmd:89139 [120] Latency: 5042.285 usecs
+       trace-cmd-89135 [007] d..2. 162583.275043: sched_switch:         trace-cmd:89135 [120] R ==> trace-cmd:89141 [120]
+       trace-cmd-89137 [006] d..2. 162583.275158: sched_switch:         trace-cmd:89137 [120] R ==> trace-cmd:89133 [120] Latency: 1207.327 usecs
+       trace-cmd-89136 [002] dNh3. 162583.275229: sched_wakeup:         rcu_preempt:17 [120] CPU:002
+       trace-cmd-89136 [002] d..2. 162583.275294: sched_switch:         trace-cmd:89136 [120] R ==> rcu_preempt:17 [120] Latency: 65.255 usecs
+     rcu_preempt-17    [002] d..2. 162583.275399: sched_switch:         rcu_preempt:17 [120] I ==> trace-cmd:89136 [120]
 
-Average wakeup latency: 26626.656 usecs
+Average wakeup latency: 20082.580 usecs
+Maximum Latency: 1032049.277 usecs at timestamp: 162574.787022
+Minimum Latency: 29.023 usecs at timestamp: 162583.189731
 
+RT task timings:
 
-------------------------------------------
+Average wakeup latency: 139.568 usecs
+Maximum Latency: 220.583 usecs at timestamp: 162577.347038
+Minimum Latency: 75.902 usecs at timestamp: 162577.719121
+----
 
 The above trace produces the wakeup latencies of the tasks. The "sched_switch"
 event reports each individual latency after writing the event information.
-At the end of the report, the average wakeup latency is reported.
+At the end of the report, the average wakeup latency is reported, as well
+as the maxim and minimum latency and the timestamp they happened at. It does
+this for both normal tasks as well as real-time tasks.
 
-------------------------------------------
+[source,shell]
+----
   # trace-cmd report -w -F 'sched_switch, sched_wakeup.*: prio < 100 || next_prio < 100'
-          <idle>-0     [003] 158131.516753: sched_wakeup: comm=ksoftirqd/3 pid=13 prio=49 success=1 target_cpu=003
-          <idle>-0     [003] 158131.516855: sched_switch: prev_comm=swapper prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/3 next_pid=13 next_prio=49 Latency: 101.244 usecs
-          <idle>-0     [003] 158131.533781: sched_wakeup: comm=ksoftirqd/3 pid=13 prio=49 success=1 target_cpu=003
-          <idle>-0     [003] 158131.533897: sched_switch: prev_comm=swapper prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/3 next_pid=13 next_prio=49 Latency: 115.608 usecs
-          <idle>-0     [003] 158131.569730: sched_wakeup: comm=ksoftirqd/3 pid=13 prio=49 success=1 target_cpu=003
-          <idle>-0     [003] 158131.569851: sched_switch: prev_comm=swapper prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/3 next_pid=13 next_prio=49 Latency: 121.024 usecs
+cpus=8
+          <idle>-0     [001] dNh5. 162573.291142: sched_wakeup:         migration/1:23 [0] CPU:001
+          <idle>-0     [001] d..2. 162573.291237: sched_switch:         swapper/1:0 [120] R ==> migration/1:23 [0] Latency: 94.643 usecs
+       trace-cmd-89141 [002] dNh6. 162573.346785: sched_wakeup:         migration/2:28 [0] CPU:002
+       trace-cmd-89141 [002] d..2. 162573.346929: sched_switch:         trace-cmd:89141 [120] R ==> migration/2:28 [0] Latency: 143.971 usecs
+       trace-cmd-89134 [003] dNh4. 162573.410852: sched_wakeup:         migration/3:33 [0] CPU:003
+       trace-cmd-89134 [003] d..2. 162573.411039: sched_switch:         trace-cmd:89134 [120] R ==> migration/3:33 [0] Latency: 187.640 usecs
+          <idle>-0     [004] dNh5. 162573.490944: sched_wakeup:         migration/4:38 [0] CPU:004
+          <idle>-0     [004] d..2. 162573.491098: sched_switch:         swapper/4:0 [120] R ==> migration/4:38 [0] Latency: 153.913 usecs
+          <idle>-0     [005] dNh5. 162573.574955: sched_wakeup:         migration/5:43 [0] CPU:005
+          <idle>-0     [005] d..2. 162573.575107: sched_switch:         swapper/5:0 [120] R ==> migration/5:43 [0] Latency: 152.875 usecs
+          <idle>-0     [006] dNh5. 162573.646878: sched_wakeup:         migration/6:48 [0] CPU:006
+          <idle>-0     [006] d..2. 162573.646992: sched_switch:         swapper/6:0 [120] R ==> migration/6:48 [0] Latency: 113.788 usecs
+       trace-cmd-89140 [002] dNh7. 162577.346818: sched_wakeup:         migration/2:28 [0] CPU:002
+       trace-cmd-89140 [002] d..2. 162577.347038: sched_switch:         trace-cmd:89140 [120] R ==> migration/2:28 [0] Latency: 220.583 usecs
+       trace-cmd-89134 [003] dNh5. 162577.410869: sched_wakeup:         migration/3:33 [0] CPU:003
+       trace-cmd-89141 [005] dNh6. 162577.574792: sched_wakeup:         migration/5:43 [0] CPU:005
+       trace-cmd-89141 [005] d..2. 162577.574915: sched_switch:         trace-cmd:89141 [120] R ==> migration/5:43 [0] Latency: 122.648 usecs
+       trace-cmd-89136 [007] dNh6. 162577.719045: sched_wakeup:         migration/7:53 [0] CPU:007
+       trace-cmd-89136 [007] d..2. 162577.719121: sched_switch:         trace-cmd:89136 [120] R ==> migration/7:53 [0] Latency: 75.902 usecs
+       trace-cmd-89140 [005] dNh4. 162581.574827: sched_wakeup:         migration/5:43 [0] CPU:005
+       trace-cmd-89140 [005] d..2. 162581.574957: sched_switch:         trace-cmd:89140 [120] R ==> migration/5:43 [0] Latency: 129.717 usecs
+   kworker/u46:1-51211 [006] dNh4. 162581.646809: sched_wakeup:         migration/6:48 [0] CPU:006
 
-Average wakeup latency: 110.021 usecs
+Average wakeup latency: 139.568 usecs
+Maximum Latency: 220.583 usecs at timestamp: 162577.347038
+Minimum Latency: 75.902 usecs at timestamp: 162577.719121
 
+RT task timings:
 
-------------------------------------------
+Average wakeup latency: 139.568 usecs
+Maximum Latency: 220.583 usecs at timestamp: 162577.347038
+Minimum Latency: 75.902 usecs at timestamp: 162577.719121
+----
 
 The above version will only show the wakeups and context switches of Real Time
 tasks. The 'prio' used inside the kernel starts at 0 for highest priority.
 That is 'prio' 0 is equivalent to user space real time priority 99, and
 priority 98 is equivalent to user space real time priority 1.
-Prios less than 100 represent Real Time tasks.
+Prios less than 100 represent Real Time tasks. Notice that the total wake up timings
+are identical to the RT task timings.
 
 An example of the profile:
-------------------------------------------
+[source,shell]
+----
  # trace-cmd record --profile sleep 1
  # trace-cmd report --profile --comm sleep
 task: sleep-21611
@@ -495,7 +650,7 @@
        => ttwu_do_wakeup (0xffffffff810606eb)
        => ttwu_do_activate.constprop.124 (0xffffffff810607c8)
        => try_to_wake_up (0xffffffff8106340a)
-------------------------------------------
+----
 
 SEE ALSO
 --------
diff --git a/Documentation/trace-cmd/trace-cmd-set.1.txt b/Documentation/trace-cmd/trace-cmd-set.1.txt
index a182d19..141606f 100644
--- a/Documentation/trace-cmd/trace-cmd-set.1.txt
+++ b/Documentation/trace-cmd/trace-cmd-set.1.txt
@@ -62,9 +62,10 @@
     may depend on what version of the kernel you have. Basically, it will
     let you use C notation to check if an event should be processed or not.
 
-----------------------------------------
+[source,bison]
+----
     ==, >=, <=, >, <, &, |, && and ||
-----------------------------------------
+----
 
     The above are usually safe to use to compare fields.
 
@@ -242,15 +243,17 @@
 
 Enable all events for tracing:
 
-------------------------------
+[source,shell]
+----
  # trace-cmd set -e all
-------------------------------
+----
 
 Set the function tracer:
 
-------------------------------
+[source,shell]
+----
  # trace-cmd set -p function
-------------------------------
+----
 
 
 SEE ALSO
diff --git a/Documentation/trace-cmd/trace-cmd-show.1.txt b/Documentation/trace-cmd/trace-cmd-show.1.txt
index ea2fda2..6d397b8 100644
--- a/Documentation/trace-cmd/trace-cmd-show.1.txt
+++ b/Documentation/trace-cmd/trace-cmd-show.1.txt
@@ -60,6 +60,19 @@
 *--buffer_total_size*::
      Show the total size of all buffers.
 
+*--buffer_subbuf_size*::
+     Show the size in kilobytes of the sub-buffers of the ring buffer.
+     The ring buffer is broken up into equal size sub-buffers were an event can only
+     be as big as the sub-buffer data section (the size minus its meta data).
+
+*--buffer_percent*::
+    Show the percentage the buffer must be filled before a reader that is blocked
+    on the trace_pipe_raw file will be woken up.
+
+  0       : wake up immediately on any new data
+  1 - 99  : wake up on this percentage of the sub-buffers being full
+  100     : wake up after the buffer is full and the writer is on the last sub-buffer
+
 *--ftrace_filter*::
      Show what function filters are set.
 
@@ -75,6 +88,12 @@
 *--graph_notrace*::
      Show the functions that will not be graphed.
 
+*--hist* '[system:]event'::
+     Show the content of a histogram "hist" file for a given event
+
+*--trigger* '[system:]event'::
+     Show the content of the "trigger" file for a given event
+
 *--cpumask*::
      Show the mask of CPUs that tracing will trace.
 
diff --git a/Documentation/trace-cmd/trace-cmd-split.1.txt b/Documentation/trace-cmd/trace-cmd-split.1.txt
index 2538579..d2fea25 100644
--- a/Documentation/trace-cmd/trace-cmd-split.1.txt
+++ b/Documentation/trace-cmd/trace-cmd-split.1.txt
@@ -86,6 +86,31 @@
 
     This will split out all the events for cpu 1 in the file.
 
+*--top*::
+    This allows to keep the top buffer.
+    The top buffer can be renamed using the '-b' option.
+
+    trace-cmd split --top
+
+    This will keep only the top buffer.
+
+    trace-cmd split --top -b old_top
+
+    This will keep only the top buffer and rename it 'old_top'.
+
+*-B* 'buffer'::
+    This allows to keep the selected buffer.
+    A buffer can be promoted to the top buffer using the '-t' option.
+
+    trace-cmd split -B timer -B sched
+
+    This will keep the 'timer' and 'sched' buffers.
+
+    trace-cmd split -B timer -t -B sched
+
+    This will keep the 'timer' and 'sched' buffers, with the events
+    from the 'timer' buffer promoted to the top instance.
+
 SEE ALSO
 --------
 trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),
diff --git a/Documentation/trace-cmd/trace-cmd-sqlhist.1.txt b/Documentation/trace-cmd/trace-cmd-sqlhist.1.txt
new file mode 100644
index 0000000..f2b1ce4
--- /dev/null
+++ b/Documentation/trace-cmd/trace-cmd-sqlhist.1.txt
@@ -0,0 +1,339 @@
+TRACE-CMD-SQLHIST(1)
+====================
+
+NAME
+----
+trace-cmd-sqlhist - Use SQL language to create / show creation of tracefs histograms and synthetic events
+
+SYNOPSIS
+--------
+*trace-cmd sqlhist* ['OPTIONS'] ['SQL-select-command']
+
+DESCRIPTION
+-----------
+The trace-cmd sqlhist(1) will take an SQL like statement to create tracefs histograms and
+synthetic events that can perform various actions for various handling of the
+data.
+
+The tracefs file system interfaces with the Linux tracing infrastructure that
+has various dynamic and static events through out the kernel. Each of these
+events can have a "histogram" attached to it, where the fields of the event
+will define the buckets of the histogram.
+
+A synthetic event is a way to attach two separate events and use the fields
+and time stamps of those events to create a new dynamic event. This new
+dynamic event is call a synthetic event. The fields of each event can have
+simple calculations done on them where, for example, the delta between
+a field of one event to a field of the other event can be taken. This also
+works for the time stamps of the events where the time delta between the
+two events can also be extracted and placed into the synthetic event.
+
+Other actions can be done from the fields of the events. A snapshot can
+be taken of the kernel ring buffer a variable used in the synthetic
+event creating hits a max, or simply changes.
+
+The commands to create histograms and synthetic events are complex and
+not easy to remember. *trace-cmd sqlhist* is used to convert SQL syntax into the
+commands needed to create the histogram or synthetic event.
+
+The *SQL-select-command* is a SQL string defined by *tracefs_sqlhist*(3).
+
+Note, this must be run as root (or sudo) as interacting with the tracefs
+directory requires root privilege, unless the *-t* option is given with
+a copy of the _tracefs_ directory and its events.
+
+OPTIONS
+-------
+*-n* 'name'::
+    The name of the synthetic event to create. This event can then be
+    used like any other event, and enabled via *trace-cmd record*(1).
+
+*-t* 'tracefs-dir'::
+    In order to test this out as non root user, a copy of the tracefs directory
+    can be used, and passing that directory with this option will allow
+    the program to work. Obviously, *-e* will not work as non-root because
+    it will not be able to execute.
+
+    # mkdir /tmp/tracing
+    # cp -r /sys/kernel/tracing/events /tmp/tracing
+    # exit
+    $ trace-cmd sqlhist -t /tmp/tracing ...
+
+*-e*::
+    Not only display the commands to create the histogram, but also execute them.
+    This requires root privilege.
+
+*-f* 'file'::
+    Instead of reading the SQL commands from the command line, read them from
+    _file_. If _file_ is '-' then read from standard input.
+
+*-m* 'var'::
+    Do the given action when the variable _var_ hits a new maximum. This can
+    not be used with *-c*. The _var_ must be defined in the *SQL-select-command*.
+
+*-c* 'var'::
+    Do the given action when the variable _var_ changes its value. This can
+    not be used with *-m*. The _var_ must be defined in the *SQL-select-command*.
+
+*-s*::
+    Perform a snapshot instead of calling the synthetic event.
+
+*-T*::
+    Perform both a snapshot and trace the synthetic event.
+
+*-S* 'fields[,fields]'::
+    Save the given fields. The fields must be fields of the "end" event given
+    in the *SQL-select-command*
+
+*-B* 'instance'::
+    For simple statements that only produce a histogram, the instance given here
+    will be where the histogram will be created. This is ignored for full synthetic
+    event creation, as sythetic events have a global affect on all tracing instances,
+    where as, histograms only affect a single instance.
+
+EXAMPLES
+--------
+
+As described above, for testing purposes, make a copy of the event directory:
+[source, c]
+--
+   $ mkdir /tmp/tracing
+   $ sudo cp -r /sys/kernel/tracing/events /tmp/tracing/
+   $ sudo chmod -R 0644 /tmp/tracing/
+--
+
+For an example of simple histogram output using the copy of the tracefs directory.
+[source, c]
+--
+   $ trace-cmd sqlhist -t /tmp/tracing/ 'SELECT CAST(call_site as SYM-OFFSET), bytes_req, CAST(bytes_alloc AS _COUNTER_) FROM kmalloc'
+--
+
+Produces the output:
+[source, c]
+--
+   echo 'hist:keys=call_site.sym-offset,bytes_req:vals=bytes_alloc' > /sys/kernel/tracing/events/kmem/kmalloc/trigger
+--
+
+Which could be used by root:
+[source, c]
+--
+   # echo 'hist:keys=call_site.sym-offset,bytes_req:vals=bytes_alloc' > /sys/kernel/tracing/events/kmem/kmalloc/trigger
+   # cat /sys/kernel/tracing/events/kmem/kmalloc/hist
+# event histogram
+#
+# trigger info: hist:keys=call_site.sym-offset,bytes_req:vals=hitcount,bytes_alloc:sort=hitcount:size=2048 [active]
+#
+
+{ call_site: [ffffffff813f8d8a] load_elf_phdrs+0x4a/0xb0                               , bytes_req:        728 } hitcount:          1  bytes_alloc:       1024
+{ call_site: [ffffffffc0c69e74] nf_ct_ext_add+0xd4/0x1d0 [nf_conntrack]                , bytes_req:        128 } hitcount:          1  bytes_alloc:        128
+{ call_site: [ffffffff818355e6] dma_resv_get_fences+0xf6/0x440                         , bytes_req:          8 } hitcount:          1  bytes_alloc:          8
+{ call_site: [ffffffffc06dc73f] intel_gt_get_buffer_pool+0x15f/0x290 [i915]            , bytes_req:        424 } hitcount:          1  bytes_alloc:        512
+{ call_site: [ffffffff813f8d8a] load_elf_phdrs+0x4a/0xb0                               , bytes_req:        616 } hitcount:          1  bytes_alloc:       1024
+{ call_site: [ffffffff8161a44c] __sg_alloc_table+0x11c/0x180                           , bytes_req:         32 } hitcount:          1  bytes_alloc:         32
+{ call_site: [ffffffffc070749d] shmem_get_pages+0xad/0x5d0 [i915]                      , bytes_req:         16 } hitcount:          1  bytes_alloc:         16
+{ call_site: [ffffffffc07507f5] intel_framebuffer_create+0x25/0x60 [i915]              , bytes_req:        408 } hitcount:          1  bytes_alloc:        512
+{ call_site: [ffffffffc06fc20f] eb_parse+0x34f/0x910 [i915]                            , bytes_req:        408 } hitcount:          1  bytes_alloc:        512
+{ call_site: [ffffffffc0700ebd] i915_gem_object_get_pages_internal+0x5d/0x270 [i915]   , bytes_req:         16 } hitcount:          1  bytes_alloc:         16
+{ call_site: [ffffffffc0771188] intel_frontbuffer_get+0x38/0x220 [i915]                , bytes_req:        400 } hitcount:          1  bytes_alloc:        512
+{ call_site: [ffffffff8161a44c] __sg_alloc_table+0x11c/0x180                           , bytes_req:        128 } hitcount:          1  bytes_alloc:        128
+{ call_site: [ffffffff813f8f45] load_elf_binary+0x155/0x1680                           , bytes_req:         28 } hitcount:          1  bytes_alloc:         32
+{ call_site: [ffffffffc07038c8] __assign_mmap_offset+0x208/0x3d0 [i915]                , bytes_req:        288 } hitcount:          1  bytes_alloc:        512
+{ call_site: [ffffffff813737b2] alloc_bprm+0x32/0x2f0                                  , bytes_req:        416 } hitcount:          1  bytes_alloc:        512
+{ call_site: [ffffffff813f9027] load_elf_binary+0x237/0x1680                           , bytes_req:         64 } hitcount:          1  bytes_alloc:         64
+{ call_site: [ffffffff8161a44c] __sg_alloc_table+0x11c/0x180                           , bytes_req:         64 } hitcount:          1  bytes_alloc:         64
+{ call_site: [ffffffffc040ffe7] drm_vma_node_allow+0x27/0xe0 [drm]                     , bytes_req:         40 } hitcount:          2  bytes_alloc:        128
+{ call_site: [ffffffff813cda98] __do_sys_timerfd_create+0x58/0x1c0                     , bytes_req:        336 } hitcount:          2  bytes_alloc:       1024
+{ call_site: [ffffffff818355e6] dma_resv_get_fences+0xf6/0x440                         , bytes_req:         40 } hitcount:          2  bytes_alloc:        128
+{ call_site: [ffffffff8139b75a] single_open+0x2a/0xa0                                  , bytes_req:         32 } hitcount:          2  bytes_alloc:         64
+{ call_site: [ffffffff815df715] bio_kmalloc+0x25/0x80                                  , bytes_req:        136 } hitcount:          2  bytes_alloc:        384
+{ call_site: [ffffffffc071e5cd] i915_vma_work+0x1d/0x50 [i915]                         , bytes_req:        416 } hitcount:          3  bytes_alloc:       1536
+{ call_site: [ffffffff81390d0d] alloc_fdtable+0x4d/0x100                               , bytes_req:         56 } hitcount:          3  bytes_alloc:        192
+{ call_site: [ffffffffc06ff65f] i915_gem_do_execbuffer+0x158f/0x2440 [i915]            , bytes_req:         16 } hitcount:          4  bytes_alloc:         64
+{ call_site: [ffffffff8137713c] alloc_pipe_info+0x5c/0x230                             , bytes_req:        384 } hitcount:          5  bytes_alloc:       2560
+{ call_site: [ffffffff813771b4] alloc_pipe_info+0xd4/0x230                             , bytes_req:        640 } hitcount:          5  bytes_alloc:       5120
+{ call_site: [ffffffff81834cdb] dma_resv_list_alloc+0x1b/0x40                          , bytes_req:         40 } hitcount:          6  bytes_alloc:        384
+{ call_site: [ffffffff81834cdb] dma_resv_list_alloc+0x1b/0x40                          , bytes_req:         56 } hitcount:          9  bytes_alloc:        576
+{ call_site: [ffffffff8120086e] tracing_map_sort_entries+0x9e/0x3e0                    , bytes_req:         24 } hitcount:         60  bytes_alloc:       1920
+
+Totals:
+    Hits: 122
+    Entries: 30
+    Dropped: 0
+--
+
+Note, although the examples use uppercase for the SQL keywords, they do not have
+to be. 'SELECT' could also be 'select' or even 'sElEcT'.
+
+By using the full SQL language, synthetic events can be made and processed.
+For example, using *trace-cmd sqlhist* along with *trace-cmd record*(1), wake up latency can
+be recorded by creating a synthetic event by attaching the _sched_waking_
+and the _sched_switch_ events.
+
+[source, c]
+--
+  # trace-cmd sqlhist -n wakeup_lat -e -T -m lat 'SELECT end.next_comm AS comm, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) AS lat FROM ' \
+    'sched_waking AS start JOIN sched_switch AS end ON start.pid = end.next_pid WHERE end.next_prio < 100 && end.next_comm == "cyclictest"'
+  # trace-cmd start -e all -e wakeup_lat -R stacktrace
+  # cyclictest -l 1000 -p80 -i250  -a -t -q -m -d 0 -b 1000 --tracemark
+  # trace-cmd show -s | tail -30
+          <idle>-0       [002] dNh4 23454.902246: sched_wakeup: comm=cyclictest pid=12272 prio=120 target_cpu=002
+          <idle>-0       [005] ...1 23454.902246: cpu_idle: state=4294967295 cpu_id=5
+          <idle>-0       [007] d..1 23454.902246: cpu_idle: state=0 cpu_id=7
+          <idle>-0       [002] dNh1 23454.902247: hrtimer_expire_exit: hrtimer=0000000037956dc2
+          <idle>-0       [005] d..1 23454.902248: cpu_idle: state=0 cpu_id=5
+          <idle>-0       [002] dNh1 23454.902248: write_msr: 6e0, value 4866ce957272
+          <idle>-0       [006] ...1 23454.902248: cpu_idle: state=4294967295 cpu_id=6
+          <idle>-0       [002] dNh1 23454.902249: local_timer_exit: vector=236
+          <idle>-0       [006] d..1 23454.902250: cpu_idle: state=0 cpu_id=6
+          <idle>-0       [002] .N.1 23454.902250: cpu_idle: state=4294967295 cpu_id=2
+          <idle>-0       [002] dN.1 23454.902251: rcu_utilization: Start context switch
+          <idle>-0       [002] dN.1 23454.902252: rcu_utilization: End context switch
+          <idle>-0       [001] ...1 23454.902252: cpu_idle: state=4294967295 cpu_id=1
+          <idle>-0       [002] dN.3 23454.902253: prandom_u32: ret=3692516021
+          <idle>-0       [001] d..1 23454.902254: cpu_idle: state=0 cpu_id=1
+          <idle>-0       [002] d..2 23454.902254: sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=cyclictest next_pid=12275 next_prio=19
+          <idle>-0       [002] d..4 23454.902256: wakeup_lat: next_comm=cyclictest lat=17
+          <idle>-0       [002] d..5 23454.902258: <stack trace>
+ => trace_event_raw_event_synth
+ => action_trace
+ => event_hist_trigger
+ => event_triggers_call
+ => trace_event_buffer_commit
+ => trace_event_raw_event_sched_switch
+ => __traceiter_sched_switch
+ => __schedule
+ => schedule_idle
+ => do_idle
+ => cpu_startup_entry
+ => secondary_startup_64_no_verify
+--
+
+Here's the options for above example explained:
+
+ *-n wakeup_lat* ::
+     Name the synthetic event to use *wakeup_lat*.
+
+ *-e*::
+     Execute the commands that are printed.
+
+ *-T*::
+     Perform both a trace action and then a snapshot action (swap the buffer into the static 'snapshot' buffer).
+
+ *-m lat*::
+     Trigger the actions whenever 'lat' hits a new maximum value.
+
+Now a breakdown of the SQL statement:
+[source, c]
+--
+ 'SELECT end.next_comm AS comm, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) AS lat FROM ' \
+    'sched_waking AS start JOIN sched_switch AS end ON start.pid = end.next_pid WHERE end.next_prio < 100 && end.next_comm == "cyclictest"'
+--
+ *end.next_comm AS comm*::
+   Save the 'sched_switch' field *next_comm* and place it into the *comm* field of the 'wakeup_lat' synthetic event.
+
+ *(end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) AS lat*::
+   Take the delta of the time stamps from the 'sched_switch' event and the 'sched_waking' event.
+   As time stamps are usually recorded in nanoseconds, *TIMESTAMP* would give the full nanosecond time stamp,
+   but here, the *TIMESTAMP_USECS* will truncate it into microseconds. The value is saved in the
+   variable *lat*, which will also be recorded in the synthetic event.
+
+ *FROM 'sched_waking' AS start JOIN sched_switch AS end ON start.pid = end.next_pid*::
+   Create the synthetic event by joining _sched_waking_ to _sched_switch_, matching
+   the _sched_waking_ 'pid' field with the _sched_switch_ 'next_pid' field.
+   Also make *start* an alias for _sched_waking_ and *end* an alias for _sched_switch_
+   which then an use *start* and *end* as a subsitute for _sched_waking_ and _sched_switch_
+   respectively through out the rest of the SQL statement.
+
+ *WHERE end.next_prio < 100 && end.next_comm == "cyclictest"*::
+   Filter the logic where it executes only if _sched_waking_ 'next_prio' field
+   is less than 100. (Note, in the Kernel, priorities are inverse, and the real-time
+   priorities are represented from 0-100 where 0 is the highest priority).
+   Also only trace when the 'next_comm' (the task scheduling in) of the _sched_switch_
+   event has the name "cyclictest".
+
+For the *trace-cmd*(3) command:
+[source, c]
+--
+   trace-cmd start -e all -e wakeup_lat -R stacktrace
+--
+
+ *trace-cmd start*::
+   Enables tracing (does not record to a file).
+
+ *-e all*::
+   Enable all events
+
+ *-e wakeup_lat -R stacktrace*::
+   have the "wakeup_lat" event (our synthetic event) enable the *stacktrace* trigger, were
+   for every instance of the "wakeup_lat" event, a kernel stack trace will be recorded
+   in the ring buffer.
+
+After calling *cyclictest* (a real-time tool to measure wakeup latency), read the snapshot
+buffer.
+
+ *trace-cmd show -s*::
+   *trace-cmd show* reads the kernel ring buffer, and the *-s* option will read the *snapshot*
+   buffer instead of the normal one.
+
+[source, c]
+--
+ <idle>-0       [002] d..4 23454.902256: wakeup_lat: next_comm=cyclictest lat=17
+--
+  We see on the "wakeup_lat" event happened on CPU 2, with a wake up latency 17 microseconds.
+
+This can be extracted into a *trace.dat* file that *trace-cmd*(3) can read and do further
+analysis, as well as *kernelshark*.
+
+[source, c]
+--
+    # trace-cmd extract -s
+    # trace-cmd report --cpu 2 | tail -30
+          <idle>-0     [002] 23454.902238: prandom_u32:          ret=1633425088
+          <idle>-0     [002] 23454.902239: sched_wakeup:         cyclictest:12275 [19] CPU:002
+          <idle>-0     [002] 23454.902241: hrtimer_expire_exit:  hrtimer=0xffffbbd68286fe60
+          <idle>-0     [002] 23454.902241: hrtimer_cancel:       hrtimer=0xffffbbd6826efe70
+          <idle>-0     [002] 23454.902242: hrtimer_expire_entry: hrtimer=0xffffbbd6826efe70 now=23455294430750 function=hrtimer_wakeup/0x0
+          <idle>-0     [002] 23454.902243: sched_waking:         comm=cyclictest pid=12272 prio=120 target_cpu=002
+          <idle>-0     [002] 23454.902244: prandom_u32:          ret=1102749734
+          <idle>-0     [002] 23454.902246: sched_wakeup:         cyclictest:12272 [120] CPU:002
+          <idle>-0     [002] 23454.902247: hrtimer_expire_exit:  hrtimer=0xffffbbd6826efe70
+          <idle>-0     [002] 23454.902248: write_msr:            6e0, value 4866ce957272
+          <idle>-0     [002] 23454.902249: local_timer_exit:     vector=236
+          <idle>-0     [002] 23454.902250: cpu_idle:             state=4294967295 cpu_id=2
+          <idle>-0     [002] 23454.902251: rcu_utilization:      Start context switch
+          <idle>-0     [002] 23454.902252: rcu_utilization:      End context switch
+          <idle>-0     [002] 23454.902253: prandom_u32:          ret=3692516021
+          <idle>-0     [002] 23454.902254: sched_switch:         swapper/2:0 [120] R ==> cyclictest:12275 [19]
+          <idle>-0     [002] 23454.902256: wakeup_lat:           next_comm=cyclictest lat=17
+          <idle>-0     [002] 23454.902258: kernel_stack:         <stack trace >
+=> trace_event_raw_event_synth (ffffffff8121a0db)
+=> action_trace (ffffffff8121e9fb)
+=> event_hist_trigger (ffffffff8121ca8d)
+=> event_triggers_call (ffffffff81216c72)
+=> trace_event_buffer_commit (ffffffff811f7618)
+=> trace_event_raw_event_sched_switch (ffffffff8110fda4)
+=> __traceiter_sched_switch (ffffffff8110d449)
+=> __schedule (ffffffff81c02002)
+=> schedule_idle (ffffffff81c02c86)
+=> do_idle (ffffffff8111e898)
+=> cpu_startup_entry (ffffffff8111eba9)
+=> secondary_startup_64_no_verify (ffffffff81000107)
+--
+
+SEE ALSO
+--------
+trace-cmd(1), tracefs_sqlhist(3)
+
+AUTHOR
+------
+Written by Steven Rostedt, <rostedt@goodmis.org>
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/
+
+COPYING
+-------
+Copyright \(C) 2021 , Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
+
diff --git a/Documentation/trace-cmd/trace-cmd.1.txt b/Documentation/trace-cmd/trace-cmd.1.txt
index 7e16127..ab25cc4 100644
--- a/Documentation/trace-cmd/trace-cmd.1.txt
+++ b/Documentation/trace-cmd/trace-cmd.1.txt
@@ -76,6 +76,8 @@
 
   convert   - convert trace files
 
+  attach   - attach a host trace.dat file to a guest trace.dat file
+
   dump    - read out the meta data from a trace file
 
 OPTIONS
@@ -92,7 +94,8 @@
 trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1),
 trace-cmd-restore(1), trace-cmd-stack(1), trace-cmd-convert(1),
 trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1),
-trace-cmd.dat(5), trace-cmd-check-events(1) trace-cmd-stat(1)
+trace-cmd.dat(5), trace-cmd-check-events(1), trace-cmd-stat(1),
+trace-cmd-attach(1)
 
 AUTHOR
 ------
diff --git a/Documentation/trace-cmd/trace-cmd.dat.v7.5.txt b/Documentation/trace-cmd/trace-cmd.dat.v7.5.txt
index e5bcac7..2f7e6a4 100644
--- a/Documentation/trace-cmd/trace-cmd.dat.v7.5.txt
+++ b/Documentation/trace-cmd/trace-cmd.dat.v7.5.txt
@@ -71,7 +71,7 @@
   <2 bytes> unsigned short integer, section flags:
     1 = the section is compressed.
   <4 bytes> ID of a string, description of the section.
-  <4 bytes> unsigned integer, size of the section in the file.
+  <8 bytes> long long unsigned integer, size of the section in the file.
 
   If the section is compressed, the above is the compressed size.
   The section must be uncompressed on reading. The described format of
@@ -226,6 +226,10 @@
        <4 bytes> unsigned integer, time shift.
        <8 bytes> unsigned long long integer, time offset.
 
+  STRINGS: id 15, size vary
+    The STRINGS option holds a list of nul terminated strings that holds the names of the
+    other sections.
+
   HEADER_INFO: id 16, size 8
     The HEADER_INFO option data is:
       <8 bytes> long long unsigned integer, offset into the trace file where the HEADER INFO
@@ -419,14 +423,18 @@
 BUFFER TEXT SECTION
 ------------------------
 
+  Section ID: 22
+
   This section contains latency tracing data, ASCII text taken from the
   target's debugfs/tracing/trace file.
 
 STRINGS SECTION
 ------------------------
 
-  All strings from trace file metadata are stored in string section in the file. The section
-  contains a list of NULL terminated ASCII strings. An ID of the string is used in the file
+  Section ID: 15
+
+  All strings of the trace file metadata are stored in a string section within the file. The section
+  contains a list of nul terminated ASCII strings. An ID of the string is used in the file
   meta data, which is the offset of the actual string into the string section. Strings can be stored
   into multiple string sections in the file.
 
diff --git a/METADATA b/METADATA
index eca2378..8fdfb37 100644
--- a/METADATA
+++ b/METADATA
@@ -1,13 +1,19 @@
-name: "trace-cmd"
-description:
-    "trace-cmd is a front-end to the ftrace Linux kernel tracer."
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update external/trace-cmd
+# For more info, check https://cs.android.com/android/platform/superproject/main/+/main:tools/external_updater/README.md
 
+name: "trace-cmd"
+description: "trace-cmd is a front-end to the ftrace Linux kernel tracer."
 third_party {
-  url {
-    type: GIT
-    value: "https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git"
-  }
-  version: "trace-cmd-v3.0.2"
-  last_upgrade_date { year: 2022 month: 5 day: 02 }
   license_type: RESTRICTED
+  last_upgrade_date {
+    year: 2025
+    month: 1
+    day: 30
+  }
+  identifier {
+    type: "Git"
+    value: "https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git"
+    version: "trace-cmd-v3.3.1"
+  }
 }
diff --git a/Makefile b/Makefile
index 982514b..e044a0f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 # trace-cmd version
 TC_VERSION = 3
-TC_PATCHLEVEL = 0
-TC_EXTRAVERSION = 3
+TC_PATCHLEVEL = 3
+TC_EXTRAVERSION = 1
 TRACECMD_VERSION = $(TC_VERSION).$(TC_PATCHLEVEL).$(TC_EXTRAVERSION)
 
 export TC_VERSION
@@ -11,8 +11,8 @@
 export TRACECMD_VERSION
 
 LIBTC_VERSION = 1
-LIBTC_PATCHLEVEL = 1
-LIBTC_EXTRAVERSION = 3
+LIBTC_PATCHLEVEL = 5
+LIBTC_EXTRAVERSION = 2
 LIBTRACECMD_VERSION = $(LIBTC_VERSION).$(LIBTC_PATCHLEVEL).$(LIBTC_EXTRAVERSION)
 
 export LIBTC_VERSION
@@ -23,7 +23,7 @@
 VERSION_FILE = ltc_version.h
 
 LIBTRACEEVENT_MIN_VERSION = 1.5
-LIBTRACEFS_MIN_VERSION = 1.3
+LIBTRACEFS_MIN_VERSION = 1.8
 
 MAKEFLAGS += --no-print-directory
 
@@ -144,7 +144,7 @@
 ifndef NO_PYTHON
 PYTHON		:= ctracecmd.so
 
-PYTHON_VERS ?= python
+PYTHON_VERS ?= python3
 PYTHON_PKGCONFIG_VERS ?= $(PYTHON_VERS)
 
 # Can build python?
@@ -163,7 +163,7 @@
 # $(call test-build, snippet, ret) -> ret if snippet compiles
 #                                  -> empty otherwise
 test-build = $(if $(shell sh -c 'echo "$(1)" | \
-	$(CC) -o /dev/null -c -x c - > /dev/null 2>&1 && echo y'), $2)
+	$(CC) -o /dev/null -x c - > /dev/null 2>&1 && echo y'), $2)
 
 UDIS86_AVAILABLE := $(call test-build,\#include <udis86.h>, y)
 ifneq ($(strip $(UDIS86_AVAILABLE)), y)
@@ -184,6 +184,15 @@
 # have flush/fua block layer instead of barriers?
 blk-flags := $(call test-build,$(BLK_TC_FLUSH_SOURCE),-DHAVE_BLK_TC_FLUSH)
 
+define MEMFD_CREATE_SOURCE
+#define _GNU_SOURCE
+#include <sys/mman.h>
+int main(void) { return memfd_create(\"test\", 0); }
+endef
+
+# have memfd_create available
+memfd-flags := $(call test-build,$(MEMFD_CREATE_SOURCE),-DHAVE_MEMFD_CREATE)
+
 ifeq ("$(origin O)", "command line")
 
   saved-output := $(O)
@@ -207,13 +216,13 @@
 
 export prefix bindir src obj
 
-LIBS = -ldl
+LIBS ?= -ldl
 
 LIBTRACECMD_DIR = $(obj)/lib/trace-cmd
 LIBTRACECMD_STATIC = $(LIBTRACECMD_DIR)/libtracecmd.a
 LIBTRACECMD_SHARED = $(LIBTRACECMD_DIR)/libtracecmd.so.$(LIBTRACECMD_VERSION)
-LIBTRACECMD_SHARED_VERSION = $(shell echo $(LIBTRACECMD_SHARED) | sed -e 's/\(\.so\.[0-9]*\).*/\1/')
-LIBTRACECMD_SHARED_SO = $(shell echo $(LIBTRACECMD_SHARED) | sed -e 's/\(\.so\).*/\1/')
+LIBTRACECMD_SHARED_VERSION := $(shell echo $(LIBTRACECMD_SHARED) | sed -e 's/\(\.so\.[0-9]*\).*/\1/')
+LIBTRACECMD_SHARED_SO := $(shell echo $(LIBTRACECMD_SHARED) | sed -e 's/\(\.so\).*/\1/')
 
 export LIBTRACECMD_STATIC LIBTRACECMD_SHARED
 export LIBTRACECMD_SHARED_VERSION LIBTRACECMD_SHARED_SO
@@ -221,12 +230,12 @@
 LIBTRACEEVENT=libtraceevent
 LIBTRACEFS=libtracefs
 
-TEST_LIBTRACEEVENT = $(shell sh -c "$(PKG_CONFIG) --atleast-version $(LIBTRACEEVENT_MIN_VERSION) $(LIBTRACEEVENT) > /dev/null 2>&1 && echo y")
-TEST_LIBTRACEFS = $(shell sh -c "$(PKG_CONFIG) --atleast-version $(LIBTRACEFS_MIN_VERSION) $(LIBTRACEFS) > /dev/null 2>&1 && echo y")
+TEST_LIBTRACEEVENT := $(shell sh -c "$(PKG_CONFIG) --atleast-version $(LIBTRACEEVENT_MIN_VERSION) $(LIBTRACEEVENT) > /dev/null 2>&1 && echo y")
+TEST_LIBTRACEFS := $(shell sh -c "$(PKG_CONFIG) --atleast-version $(LIBTRACEFS_MIN_VERSION) $(LIBTRACEFS) > /dev/null 2>&1 && echo y")
 
 ifeq ("$(TEST_LIBTRACEEVENT)", "y")
-LIBTRACEEVENT_CFLAGS = $(shell sh -c "$(PKG_CONFIG) --cflags $(LIBTRACEEVENT)")
-LIBTRACEEVENT_LDLAGS = $(shell sh -c "$(PKG_CONFIG) --libs $(LIBTRACEEVENT)")
+LIBTRACEEVENT_CFLAGS := $(shell sh -c "$(PKG_CONFIG) --cflags $(LIBTRACEEVENT)")
+LIBTRACEEVENT_LDLAGS := $(shell sh -c "$(PKG_CONFIG) --libs $(LIBTRACEEVENT)")
 else
 .PHONY: warning
 warning:
@@ -244,8 +253,8 @@
 export LIBTRACEEVENT_CFLAGS LIBTRACEEVENT_LDLAGS
 
 ifeq ("$(TEST_LIBTRACEFS)", "y")
-LIBTRACEFS_CFLAGS = $(shell sh -c "$(PKG_CONFIG) --cflags $(LIBTRACEFS)")
-LIBTRACEFS_LDLAGS = $(shell sh -c "$(PKG_CONFIG) --libs $(LIBTRACEFS)")
+LIBTRACEFS_CFLAGS := $(shell sh -c "$(PKG_CONFIG) --cflags $(LIBTRACEFS)")
+LIBTRACEFS_LDLAGS := $(shell sh -c "$(PKG_CONFIG) --libs $(LIBTRACEFS)")
 else
 .PHONY: warning
 warning:
@@ -307,15 +316,19 @@
 ZLIB_INSTALLED := $(shell if (printf "$(pound)include <zlib.h>\n void main(){deflateInit(NULL, Z_BEST_COMPRESSION);}" | $(CC) -o /dev/null -x c - -lz >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)
 ifeq ($(ZLIB_INSTALLED), 1)
 export ZLIB_INSTALLED
+ZLIB_LDLAGS = -lz
 CFLAGS += -DHAVE_ZLIB
 $(info    Have zlib compression support)
 endif
 
-TEST_LIBZSTD = $(shell sh -c "$(PKG_CONFIG) --atleast-version 1.4.0 libzstd > /dev/null 2>&1 && echo y")
+export ZLIB_LDLAGS
+
+ifndef NO_LIBZSTD
+TEST_LIBZSTD := $(shell sh -c "$(PKG_CONFIG) --atleast-version 1.4.0 libzstd > /dev/null 2>&1 && echo y")
 
 ifeq ("$(TEST_LIBZSTD)", "y")
-LIBZSTD_CFLAGS = $(shell sh -c "$(PKG_CONFIG) --cflags libzstd")
-LIBZSTD_LDLAGS = $(shell sh -c "$(PKG_CONFIG) --libs libzstd")
+LIBZSTD_CFLAGS := $(shell sh -c "$(PKG_CONFIG) --cflags libzstd")
+LIBZSTD_LDLAGS := $(shell sh -c "$(PKG_CONFIG) --libs libzstd")
 CFLAGS += -DHAVE_ZSTD
 ZSTD_INSTALLED=1
 $(info    Have ZSTD compression support)
@@ -326,6 +339,7 @@
 endif
 
 export LIBZSTD_CFLAGS LIBZSTD_LDLAGS ZSTD_INSTALLED
+endif
 
 CUNIT_INSTALLED := $(shell if (printf "$(pound)include <CUnit/Basic.h>\n void main(){CU_initialize_registry();}" | $(CC) -o /dev/null -x c - -lcunit >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)
 export CUNIT_INSTALLED
@@ -336,6 +350,9 @@
 # Required CFLAGS
 override CFLAGS += -D_GNU_SOURCE
 
+# Make sure 32 bit stat() works on large file systems
+override CFLAGS += -D_FILE_OFFSET_BITS=64
+
 ifndef NO_PTRACE
 ifneq ($(call try-cc,$(SOURCE_PTRACE),),y)
 	NO_PTRACE = 1
@@ -363,7 +380,7 @@
 # Append required CFLAGS
 override CFLAGS += $(INCLUDES) $(VAR_DIR)
 override CFLAGS += $(PLUGIN_DIR_TRACECMD_SQ)
-override CFLAGS += $(udis86-flags) $(blk-flags)
+override CFLAGS += $(udis86-flags) $(blk-flags) $(memfd-flags)
 override LDFLAGS += $(udis86-ldflags)
 
 CMD_TARGETS = trace-cmd $(BUILD_PYTHON)
@@ -433,12 +450,15 @@
 	@echo "  Please use its new home at https://git.kernel.org/pub/scm/utils/trace-cmd/kernel-shark.git/"
 	@echo "***************************"
 
-test: force $(LIBTRACECMD_STATIC)
+test: force trace-cmd
 ifneq ($(CUNIT_INSTALLED),1)
 	$(error CUnit framework not installed, cannot build unit tests))
 endif
 	$(Q)$(MAKE) -C $(src)/utest $@
 
+test_mem: force test
+	$(Q)$(MAKE) -C $(src)/utest $@
+
 plugins_tracecmd: force $(obj)/lib/trace-cmd/plugins/tracecmd_plugin_dir
 	$(Q)$(MAKE) -C $(src)/lib/trace-cmd/plugins
 
@@ -468,7 +488,8 @@
 
 cscope: force
 	$(RM) cscope*
-	$(call find_tag_files) | cscope -b -q
+	$(call find_tag_files) > cscope.files
+	cscope -b -q -f cscope.out
 
 install_plugins_tracecmd: force
 	$(Q)$(MAKE) -C $(src)/lib/trace-cmd/plugins install_plugins
@@ -494,7 +515,7 @@
 install_libs: libs
 	$(Q)$(MAKE) -C $(src)/lib/trace-cmd/ $@
 
-doc:
+doc: check_doc
 	$(MAKE) -C $(src)/Documentation all
 
 doc_clean:
@@ -503,7 +524,10 @@
 install_doc:
 	$(MAKE) -C $(src)/Documentation install
 
-clean:
+check_doc: force
+	$(Q)$(src)/check-manpages.sh $(src)/Documentation/libtracecmd
+
+clean: clean_meson
 	$(RM) *.o *~ *.a *.so .*.d
 	$(RM) tags TAGS cscope* $(PKG_CONFIG_SOURCE_FILE) $(VERSION_FILE)
 	$(MAKE) -C $(src)/lib/trace-cmd clean
@@ -574,6 +598,18 @@
 PHONY += python
 python: $(PYTHON)
 
+meson:
+	$(MAKE) -f Makefile.meson
+
+meson_install:
+	$(MAKE) -f Makefile.meson install
+
+meson_docs:
+	$(MAKE) -f Makefile.meson docs
+
+PHONY += clean_meson
+clean_meson:
+	$(Q)$(MAKE) -f Makefile.meson $@
 
 dist:
 	git archive --format=tar --prefix=trace-cmd-$(TRACECMD_VERSION)/ HEAD \
diff --git a/Makefile.meson b/Makefile.meson
new file mode 100644
index 0000000..71d6bf3
--- /dev/null
+++ b/Makefile.meson
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: GPL-2.0
+
+undefine CFLAGS
+
+# Makefiles suck: This macro sets a default value of $(2) for the
+# variable named by $(1), unless the variable has been set by
+# environment or command line. This is necessary for CC and AR
+# because make sets default values, so the simpler ?= approach
+# won't work as expected.
+define allow-override
+  $(if $(or $(findstring environment,$(origin $(1))),\
+            $(findstring command line,$(origin $(1)))),,\
+    $(eval $(1) = $(2)))
+endef
+
+$(call allow-override,MESON,meson)
+$(call allow-override,MESON_BUILD_DIR,build)
+
+
+all: compile
+
+PHONY += compile
+compile: $(MESON_BUILD_DIR) force
+	$(MESON) compile -C $(MESON_BUILD_DIR)
+
+$(MESON_BUILD_DIR):
+	$(MESON) setup --prefix=$(prefix) $(MESON_BUILD_DIR)
+
+install: compile
+	$(MESON) install -C $(MESON_BUILD_DIR)
+
+docs: $(MESON_BUILD_DIR)
+	$(MESON) compile -C build docs
+
+PHONY += clean_meson
+clean_meson:
+	$(Q)$(RM) -rf $(MESON_BUILD_DIR)
+
+PHONY += force
+force:
diff --git a/README b/README
index 9d8c127..e823843 100644
--- a/README
+++ b/README
@@ -57,6 +57,9 @@
 To install the gui
     make install_gui
 
+To install libtracecmd libraries
+    make install_libs
+
 Note: The default install is relative to /usr/local
     The default install directory is /usr/local/bin
     The default plugin directory is /usr/local/lib/trace-cmd/plugins
diff --git a/check-manpages.sh b/check-manpages.sh
new file mode 100755
index 0000000..f73a6c5
--- /dev/null
+++ b/check-manpages.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1
+# Copyright (C) 2022, Google Inc, Steven Rostedt <rostedt@goodmis.org>
+#
+# This checks if any function is listed in a man page that is not listed
+# in the main man page.
+
+if [ $# -lt 1 ]; then
+	echo "usage: check-manpages man-page-path"
+	exit 1
+fi
+
+cd $1
+
+MAIN=libtracecmd
+MAIN_FILE=${MAIN}.txt
+
+PROCESSED=""
+
+# Ignore man pages that do not contain functions
+IGNORE=""
+
+for man in ${MAIN}-*.txt; 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
+		if ! grep -q '\*'${a}'\*' $MAIN_FILE; then
+			if [ "$last" == "" ]; then
+				echo
+			fi
+			if [ "$last" != "$man" ]; then
+				echo "Missing functions from $MAIN_FILE that are in $man"
+				last=$man
+			fi
+			echo "   ${a}"
+		fi
+	done
+done
+
+DEPRECATED=""
+
+sed -ne 's/^[a-z].*[ \*]\([a-z_][a-z_]*\)(.*/\1/p' -e 's/^\([a-z_][a-z_]*\)(.*/\1/p' ../../include/trace-cmd/trace-cmd.h | while read f; do
+	if ! grep -q '\*'${f}'\*' $MAIN_FILE; then
+		if [ "${DEPRECATED/\*$f\*/}" != "${DEPRECATED}" ]; then
+			continue;
+		fi
+		if [ "$last" == "" ]; then
+			echo
+			echo "Missing functions from $MAIN_FILE that are in tracefs.h"
+			last=$f
+		fi
+		echo "   ${f}"
+	fi
+done
diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 5d71e8b..55a0c51 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -9,6 +9,10 @@
 #include "event-parse.h"
 #include "tracefs.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct tracecmd_input;
 
 enum tracecmd_open_flags {
@@ -44,10 +48,76 @@
 			      int *vcpu_count, const int **cpu_pid);
 unsigned long long tracecmd_get_first_ts(struct tracecmd_input *handle);
 void tracecmd_add_ts_offset(struct tracecmd_input *handle, long long offset);
+int tracecmd_get_tsc2nsec(struct tracecmd_input *handle,
+			  int *mult, int *shift, unsigned long long *offset);
 int tracecmd_buffer_instances(struct tracecmd_input *handle);
 const char *tracecmd_buffer_instance_name(struct tracecmd_input *handle, int indx);
 struct tracecmd_input *tracecmd_buffer_instance_handle(struct tracecmd_input *handle, int indx);
 
+void tracecmd_set_private(struct tracecmd_input *handle, void *data);
+void *tracecmd_get_private(struct tracecmd_input *handle);
+
+int tracecmd_follow_event(struct tracecmd_input *handle,
+			  const char *system, const char *event_name,
+			  int (*callback)(struct tracecmd_input *handle,
+					  struct tep_event *,
+					  struct tep_record *,
+					  int, void *),
+			  void *callback_data);
+
+int tracecmd_follow_missed_events(struct tracecmd_input *handle,
+				  int (*callback)(struct tracecmd_input *handle,
+						  struct tep_event *,
+						  struct tep_record *,
+						  int, void *),
+				  void *callback_data);
+
+int tracecmd_iterate_reset(struct tracecmd_input *handle);
+
+int tracecmd_iterate_events(struct tracecmd_input *handle,
+			    cpu_set_t *cpus, int cpu_size,
+			    int (*callback)(struct tracecmd_input *handle,
+					    struct tep_record *,
+					    int, void *),
+			    void *callback_data);
+int tracecmd_iterate_events_multi(struct tracecmd_input **handles,
+				  int nr_handles,
+				  int (*callback)(struct tracecmd_input *handle,
+						  struct tep_record *,
+						  int, void *),
+				  void *callback_data);
+int tracecmd_iterate_events_reverse(struct tracecmd_input *handle,
+				    cpu_set_t *cpus, int cpu_size,
+				    int (*callback)(struct tracecmd_input *handle,
+						    struct tep_record *,
+						    int, void *),
+				    void *callback_data, bool cont);
+
 void tracecmd_set_loglevel(enum tep_loglevel level);
 
+enum tracecmd_filters {
+	TRACECMD_FILTER_NONE		= TEP_ERRNO__NO_FILTER,
+	TRACECMD_FILTER_NOT_FOUND	= TEP_ERRNO__FILTER_NOT_FOUND,
+	TRACECMD_FILTER_MISS		= TEP_ERRNO__FILTER_MISS,
+	TRACECMD_FILTER_MATCH		= TEP_ERRNO__FILTER_MATCH,
+};
+
+struct tracecmd_filter;
+struct tracecmd_filter *tracecmd_filter_add(struct tracecmd_input *handle,
+					    const char *filter_str, bool neg);
+
+struct tracecmd_cpu_map;
+int tracecmd_map_vcpus(struct tracecmd_input **handles, int nr_handles);
+struct tracecmd_cpu_map *tracecmd_get_cpu_map(struct tracecmd_input *handle, int cpu);
+struct tracecmd_cpu_map *tracecmd_map_find_by_host_pid(struct tracecmd_input *handle,
+						      int host_pid);
+struct tracecmd_input *tracecmd_map_get_guest(struct tracecmd_cpu_map *map);
+int tracecmd_map_get_host_pid(struct tracecmd_cpu_map *map);
+void tracecmd_map_set_private(struct tracecmd_cpu_map *map, void *priv);
+void *tracecmd_map_get_private(struct tracecmd_cpu_map *map);
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* _TRACE_CMD_H */
diff --git a/lib/Documentation b/lib/Documentation
new file mode 120000
index 0000000..fb996f2
--- /dev/null
+++ b/lib/Documentation
@@ -0,0 +1 @@
+../Documentation
\ No newline at end of file
diff --git a/lib/check-manpages.sh b/lib/check-manpages.sh
new file mode 120000
index 0000000..d941666
--- /dev/null
+++ b/lib/check-manpages.sh
@@ -0,0 +1 @@
+../check-manpages.sh
\ No newline at end of file
diff --git a/lib/meson.build b/lib/meson.build
new file mode 100644
index 0000000..ded2712
--- /dev/null
+++ b/lib/meson.build
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: LGPL-2.1
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+project(
+    'libtracecmd', ['c'],
+    meson_version: '>= 0.50.0',
+    license: 'GPL-2.0',
+    version: '1.5.2',
+    default_options: [
+        'c_std=gnu99',
+        'buildtype=debug',
+        'default_library=both',
+        'prefix=/usr/local',
+        'warning_level=1'])
+
+cc = meson.get_compiler('c')
+
+prefixdir = get_option('prefix')
+mandir    = join_paths(prefixdir, get_option('mandir'))
+htmldir   = join_paths(prefixdir, get_option('htmldir'))
+
+libtracecmd_standalone_build = true
+
+library_version = meson.project_version()
+
+conf = configuration_data()
+
+libtraceevent_dep = dependency('libtraceevent', version: '>= 1.5.0', required: true)
+libtracefs_dep = dependency('libtracefs', version: '>= 1.6.0', required: true)
+
+threads_dep = dependency('threads', required: true)
+dl_dep = cc.find_library('dl', required : false)
+
+zlib_dep = dependency('zlib', required: false)
+conf.set('HAVE_ZLIB', zlib_dep.found(), description: 'Is zlib avialable?')
+
+libzstd_dep = dependency('libzstd', version: '>= 1.4.0', required: false)
+conf.set('HAVE_ZSTD', libzstd_dep.found(), description: 'Is libzstd available?')
+
+cunit_dep = dependency('cunit', required : false)
+
+vsock_defined = get_option('vsock') and cc.has_header('linux/vm_sockets.h')
+conf.set('VSOCK', vsock_defined, description: 'Is vsock available?')
+
+perf_defined = cc.has_header('linux/perf_event.h')
+conf.set('PERF', perf_defined, description: 'Is perf available?')
+
+have_ptrace = get_option('ptrace') and cc.compiles(
+    '''
+    #include <stdio.h>
+    #include <sys/ptrace.h>
+
+    int main (void)
+    {
+            int ret;
+            ret = ptrace(PTRACE_ATTACH, 0, NULL, 0);
+            ptrace(PTRACE_TRACEME, 0, NULL, 0);
+            ptrace(PTRACE_GETSIGINFO, 0, NULL, NULL);
+            ptrace(PTRACE_GETEVENTMSG, 0, NULL, NULL);
+            ptrace(PTRACE_SETOPTIONS, NULL, NULL,
+                           PTRACE_O_TRACEFORK |
+                           PTRACE_O_TRACEVFORK |
+                           PTRACE_O_TRACECLONE |
+                           PTRACE_O_TRACEEXIT);
+            ptrace(PTRACE_CONT, NULL, NULL, 0);
+            ptrace(PTRACE_DETACH, 0, NULL, NULL);
+            ptrace(PTRACE_SETOPTIONS, 0, NULL,
+                   PTRACE_O_TRACEFORK |
+                   PTRACE_O_TRACEVFORK |
+                   PTRACE_O_TRACECLONE |
+                   PTRACE_O_TRACEEXIT);
+            return ret;
+    }
+    ''',
+    name: 'ptrace')
+if not have_ptrace
+    conf.set10('NO_PTRACE', true, description: 'Is ptrace missing?')
+    conf.set('WARN_NO_PTRACE', true, description: 'Issue no ptrace warning?')
+endif
+
+audit_dep = dependency('audit', required: false)
+if not audit_dep.found()
+    conf.set10('NO_AUDIT', true, description: 'Is audit missing?')
+    conf.set('WARN_NO_AUDIT', true, description: 'Issue no audit warning?')
+endif
+
+add_project_arguments(
+    [
+        '-D_GNU_SOURCE',
+        '-include', 'trace-cmd/include/private/config.h',
+    ],
+    language : 'c')
+
+libtracecmd_ext_incdir = include_directories(
+    [
+        '../include',
+        '../include/trace-cmd',
+        '../tracecmd/include'
+    ])
+
+subdir('trace-cmd/include')
+subdir('trace-cmd/include/private')
+subdir('trace-cmd')
+if libtracecmd_standalone_build
+    subdir('Documentation/libtracecmd')
+
+    custom_target(
+        'docs',
+        output: 'docs',
+        depends: [html, man],
+        command: ['echo'])
+endif
+
+install_headers(
+    '../include/trace-cmd/trace-cmd.h',
+     subdir: 'trace-cmd')
diff --git a/lib/meson_options.txt b/lib/meson_options.txt
new file mode 100644
index 0000000..9f5ed6d
--- /dev/null
+++ b/lib/meson_options.txt
@@ -0,0 +1,19 @@
+# -*- mode: meson -*-
+# SPDX-License-Identifier: LGPL-2.1
+
+option('vsock', type : 'boolean', value : true,
+       description : 'build with vsock support')
+option('ptrace', type : 'boolean', value : true,
+       description : 'build with ptrace support')
+option('htmldir', type : 'string', value : 'share/doc/libtracecmd-doc',
+       description : 'directory for HTML documentation')
+option('asciidoctor', type : 'boolean', value: false,
+       description : 'use asciidoctor instead of asciidoc')
+option('docbook-xls-172', type : 'boolean', value : false,
+       description : 'enable docbook XLS 172 workaround')
+option('asciidoc-no-roff', type : 'boolean', value : false,
+       description : 'enable no roff workaround')
+option('man-bold-literal', type : 'boolean', value : false,
+       description : 'enable bold literals')
+option('docbook-suppress-sp', type : 'boolean', value : false,
+       description : 'docbook suppress sp')
diff --git a/lib/trace-cmd/Makefile b/lib/trace-cmd/Makefile
index 9374b16..aba6fda 100644
--- a/lib/trace-cmd/Makefile
+++ b/lib/trace-cmd/Makefile
@@ -9,14 +9,17 @@
 
 OBJS =
 OBJS += trace-hash.o
+OBJS += trace-rbtree.o
 OBJS += trace-hooks.o
 OBJS += trace-input.o
 OBJS += trace-output.o
 OBJS += trace-recorder.o
 OBJS += trace-util.o
 OBJS += trace-filter-hash.o
+OBJS += trace-filter.o
 OBJS += trace-msg.o
 OBJS += trace-plugin.o
+OBJS += trace-maps.o
 ifeq ($(PERF_DEFINED), 1)
 OBJS += trace-perf.o
 endif
@@ -51,11 +54,9 @@
 $(LIBTRACECMD_STATIC): $(OBJS)
 	$(Q)$(call do_build_static_lib)
 
-LIBS = $(LIBTRACEEVENT_LDLAGS) $(LIBTRACEFS_LDLAGS) $(LIBZSTD_LDLAGS) -lpthread
+LPTHREAD ?= -lpthread
 
-ifeq ($(ZLIB_INSTALLED), 1)
-LIBS += -lz
-endif
+LIBS = $(LIBTRACEEVENT_LDLAGS) $(LIBTRACEFS_LDLAGS) $(ZLIB_LDLAGS) $(LIBZSTD_LDLAGS) $(LPTHREAD)
 
 $(LIBTRACECMD_SHARED_VERSION): $(LIBTRACECMD_SHARED)
 	@ln -sf $(<F) $@
diff --git a/lib/trace-cmd/include/meson.build b/lib/trace-cmd/include/meson.build
new file mode 100644
index 0000000..c1c8388
--- /dev/null
+++ b/lib/trace-cmd/include/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+libtracecmd_incdir = include_directories(['.'])
diff --git a/lib/trace-cmd/include/private/meson.build b/lib/trace-cmd/include/private/meson.build
new file mode 100644
index 0000000..0f36339
--- /dev/null
+++ b/lib/trace-cmd/include/private/meson.build
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: LGPL-2.1
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+config_h = configure_file(
+    output: 'config.h',
+    configuration: conf
+)
+
+libtracecmd_private_incdir = include_directories(['.'])
+
+config_dep = declare_dependency(
+    include_directories : libtracecmd_private_incdir,
+    sources: config_h)
diff --git a/lib/trace-cmd/include/private/trace-cmd-private-python.h b/lib/trace-cmd/include/private/trace-cmd-private-python.h
new file mode 100644
index 0000000..ddc52f1
--- /dev/null
+++ b/lib/trace-cmd/include/private/trace-cmd-private-python.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ * Private interface exposed to the python module. See python/ctracecmd.i and
+ * python/tracecmd.py.
+ */
+#ifndef _TRACE_CMD_PRIVATE_PYTHON_H
+#define _TRACE_CMD_PRIVATE_PYTHON_H
+
+int tracecmd_long_size(struct tracecmd_input *handle);
+int tracecmd_cpus(struct tracecmd_input *handle);
+
+struct tep_record *
+tracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu);
+
+struct tep_record *
+tracecmd_peek_data(struct tracecmd_input *handle, int cpu);
+
+static inline struct tep_record *
+tracecmd_peek_data_ref(struct tracecmd_input *handle, int cpu)
+{
+	struct tep_record *rec = tracecmd_peek_data(handle, cpu);
+	if (rec)
+		rec->ref_count++;
+	return rec;
+}
+
+#endif /* _TRACE_CMD_PRIVATE_PYTHON_H */
diff --git a/lib/trace-cmd/include/private/trace-cmd-private.h b/lib/trace-cmd/include/private/trace-cmd-private.h
index 3cc3e9d..b55a161 100644
--- a/lib/trace-cmd/include/private/trace-cmd-private.h
+++ b/lib/trace-cmd/include/private/trace-cmd-private.h
@@ -10,6 +10,7 @@
 #include <sys/types.h>
 #include "event-parse.h"
 #include "trace-cmd/trace-cmd.h"
+#include "trace-cmd-private-python.h"
 
 #define __packed __attribute__((packed))
 #define __hidden __attribute__((visibility ("hidden")))
@@ -51,6 +52,9 @@
 void tracecmd_set_debug(bool set_debug);
 bool tracecmd_get_debug(void);
 
+void tracecmd_set_notimeout(bool set_notimeout);
+bool tracecmd_get_notimeout(void);
+
 bool tracecmd_is_version_supported(unsigned int version);
 int tracecmd_default_file_version(void);
 
@@ -103,7 +107,7 @@
 const char *tracecmd_get_cpustats(struct tracecmd_input *handle);
 const char *tracecmd_get_uname(struct tracecmd_input *handle);
 const char *tracecmd_get_version(struct tracecmd_input *handle);
-off64_t tracecmd_get_cpu_file_size(struct tracecmd_input *handle, int cpu);
+off_t tracecmd_get_cpu_file_size(struct tracecmd_input *handle, int cpu);
 
 static inline int tracecmd_host_bigendian(void)
 {
@@ -175,8 +179,8 @@
 };
 
 struct tracecmd_proc_addr_map {
-	unsigned long long	start;
-	unsigned long long	end;
+	size_t			start;
+	size_t			end;
 	char			*lib_name;
 };
 
@@ -191,9 +195,7 @@
 int tracecmd_read_headers(struct tracecmd_input *handle,
 			  enum tracecmd_file_states state);
 int tracecmd_get_parsing_failures(struct tracecmd_input *handle);
-int tracecmd_long_size(struct tracecmd_input *handle);
 int tracecmd_page_size(struct tracecmd_input *handle);
-int tracecmd_cpus(struct tracecmd_input *handle);
 int tracecmd_copy_headers(struct tracecmd_input *in_handle,
 			  struct tracecmd_output *out_handle,
 			  enum tracecmd_file_states start_state,
@@ -227,27 +229,12 @@
 void tracecmd_print_uname(struct tracecmd_input *handle);
 void tracecmd_print_version(struct tracecmd_input *handle);
 
-struct tep_record *
-tracecmd_peek_data(struct tracecmd_input *handle, int cpu);
-
-static inline struct tep_record *
-tracecmd_peek_data_ref(struct tracecmd_input *handle, int cpu)
-{
-	struct tep_record *rec = tracecmd_peek_data(handle, cpu);
-	if (rec)
-		rec->ref_count++;
-	return rec;
-}
-
 int tracecmd_latency_data_read(struct tracecmd_input *handle, char **buf, size_t *size);
 
 struct tep_record *
 tracecmd_read_prev(struct tracecmd_input *handle, struct tep_record *record);
 
 struct tep_record *
-tracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu);
-
-struct tep_record *
 tracecmd_peek_next_data(struct tracecmd_input *handle, int *rec_cpu);
 
 struct tep_record *
@@ -265,7 +252,7 @@
 				   unsigned long long time);
 
 int tracecmd_set_cursor(struct tracecmd_input *handle,
-			int cpu, unsigned long long offset);
+			int cpu, size_t offset);
 unsigned long long
 tracecmd_get_cursor(struct tracecmd_input *handle, int cpu);
 
@@ -333,10 +320,12 @@
 
 int tracecmd_write_cpus(struct tracecmd_output *handle, int cpus);
 int tracecmd_write_cmdlines(struct tracecmd_output *handle);
+int tracecmd_prepare_options(struct tracecmd_output *handle, off_t offset, int whence);
 int tracecmd_write_options(struct tracecmd_output *handle);
 int tracecmd_write_meta_strings(struct tracecmd_output *handle);
 int tracecmd_append_options(struct tracecmd_output *handle);
 void tracecmd_output_close(struct tracecmd_output *handle);
+void tracecmd_output_flush(struct tracecmd_output *handle);
 void tracecmd_output_free(struct tracecmd_output *handle);
 struct tracecmd_output *tracecmd_copy(struct tracecmd_input *ihandle, const char *file,
 				      enum tracecmd_file_states state, int file_version,
@@ -350,7 +339,7 @@
 				    const char *name, int cpus, char * const *cpu_data_files);
 struct tracecmd_output *tracecmd_get_output_handle_fd(int fd);
 unsigned long tracecmd_get_out_file_version(struct tracecmd_output *handle);
-unsigned long long tracecmd_get_out_file_offset(struct tracecmd_output *handle);
+size_t tracecmd_get_out_file_offset(struct tracecmd_output *handle);
 
 /* --- Reading the Fly Recorder Trace --- */
 
@@ -365,37 +354,41 @@
 void tracecmd_free_recorder(struct tracecmd_recorder *recorder);
 struct tracecmd_recorder *tracecmd_create_recorder(const char *file, int cpu, unsigned flags);
 struct tracecmd_recorder *tracecmd_create_recorder_fd(int fd, int cpu, unsigned flags);
-struct tracecmd_recorder *tracecmd_create_recorder_virt(const char *file, int cpu, unsigned flags, int trace_fd);
+struct tracecmd_recorder *tracecmd_create_recorder_virt(const char *file, int cpu, unsigned flags, int trace_fd, int maxkb);
 struct tracecmd_recorder *tracecmd_create_recorder_maxkb(const char *file, int cpu, unsigned flags, int maxkb);
-struct tracecmd_recorder *tracecmd_create_buffer_recorder_fd(int fd, int cpu, unsigned flags, const char *buffer);
-struct tracecmd_recorder *tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags, const char *buffer);
-struct tracecmd_recorder *tracecmd_create_buffer_recorder_maxkb(const char *file, int cpu, unsigned flags, const char *buffer, int maxkb);
+struct tracecmd_recorder *tracecmd_create_buffer_recorder_fd(int fd, int cpu, unsigned flags, struct tracefs_instance *instance);
+struct tracecmd_recorder *tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags, struct tracefs_instance *instance);
+struct tracecmd_recorder *tracecmd_create_buffer_recorder_maxkb(const char *file, int cpu, unsigned flags, struct tracefs_instance *instance, int maxkb);
 
 int tracecmd_start_recording(struct tracecmd_recorder *recorder, unsigned long sleep);
-void tracecmd_stop_recording(struct tracecmd_recorder *recorder);
-long tracecmd_flush_recording(struct tracecmd_recorder *recorder);
+int tracecmd_stop_recording(struct tracecmd_recorder *recorder);
+long tracecmd_flush_recording(struct tracecmd_recorder *recorder, bool finish);
 
 enum tracecmd_msg_flags {
 	TRACECMD_MSG_FL_USE_TCP		= 1 << 0,
 	TRACECMD_MSG_FL_USE_VSOCK	= 1 << 1,
+	TRACECMD_MSG_FL_PROXY		= 1 << 2,
 };
 
-/* for both client and server */
 #ifdef __ANDROID__
 #define MSG_CACHE_FILE "/data/local/tmp/trace_msg_cacheXXXXXX"
 #else	/* !__ANDROID__ */
 #define MSG_CACHE_FILE "/tmp/trace_msg_cacheXXXXXX"
 #endif	/* __ANDROID__ */
 
+/* for both client and server */
 struct tracecmd_msg_handle {
 	int			fd;
 	short			cpu_count;
 	short			version;	/* Current protocol version */
 	unsigned long		flags;
+	off_t			cache_start_offset;
 	bool			done;
 	bool			cache;
 	int			cfd;
+#ifndef HAVE_MEMFD_CREATE
 	char			cfile[sizeof(MSG_CACHE_FILE)];
+#endif
 };
 
 struct tracecmd_tsync_protos {
@@ -415,10 +408,13 @@
 int tracecmd_msg_data_send(struct tracecmd_msg_handle *msg_handle,
 			       const char *buf, int size);
 int tracecmd_msg_finish_sending_data(struct tracecmd_msg_handle *msg_handle);
+int tracecmd_msg_flush_data(struct tracecmd_msg_handle *msg_handle);
 int tracecmd_msg_send_close_msg(struct tracecmd_msg_handle *msg_handle);
 int tracecmd_msg_send_close_resp_msg(struct tracecmd_msg_handle *msg_handle);
 int tracecmd_msg_wait_close(struct tracecmd_msg_handle *msg_handle);
 int tracecmd_msg_wait_close_resp(struct tracecmd_msg_handle *msg_handle);
+int tracecmd_msg_cont(struct tracecmd_msg_handle *msg_handle);
+int tracecmd_msg_wait(struct tracecmd_msg_handle *msg_handle);
 
 /* for server */
 int tracecmd_msg_initial_setting(struct tracecmd_msg_handle *msg_handle);
@@ -428,15 +424,30 @@
 int tracecmd_msg_collect_data(struct tracecmd_msg_handle *msg_handle, int ofd);
 bool tracecmd_msg_done(struct tracecmd_msg_handle *msg_handle);
 void tracecmd_msg_set_done(struct tracecmd_msg_handle *msg_handle);
+int tracecmd_msg_read_options(struct tracecmd_msg_handle *msg_handle,
+			      struct tracecmd_output *handle);
+int tracecmd_msg_send_options(struct tracecmd_msg_handle *msg_handle,
+			      struct tracecmd_output *handle);
 
 int tracecmd_msg_send_trace_req(struct tracecmd_msg_handle *msg_handle,
 				int argc, char **argv, bool use_fifos,
 				unsigned long long trace_id,
 				struct tracecmd_tsync_protos *protos);
+int tracecmd_msg_send_trace_proxy(struct tracecmd_msg_handle *msg_handle,
+				  int argc, char **argv, bool use_fifos,
+				  unsigned long long trace_id,
+				  struct tracecmd_tsync_protos *protos,
+				  unsigned int nr_cpus,
+				  unsigned int siblings);
 int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
 				int *argc, char ***argv, bool *use_fifos,
 				unsigned long long *trace_id,
 				struct tracecmd_tsync_protos **protos);
+int tracecmd_msg_recv_trace_proxy(struct tracecmd_msg_handle *msg_handle,
+				  int *argc, char ***argv, bool *use_fifos,
+				  unsigned long long *trace_id,
+				  struct tracecmd_tsync_protos **protos,
+				  unsigned int *cpus, unsigned int *siblings);
 
 int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
 				 int nr_cpus, int page_size,
@@ -499,9 +510,8 @@
 int tracecmd_tsync_proto_getall(struct tracecmd_tsync_protos **protos, const char *clock, int role);
 bool tsync_proto_is_supported(const char *proto_name);
 struct tracecmd_time_sync *
-tracecmd_tsync_with_host(int fd,
-			 const struct tracecmd_tsync_protos *tsync_protos,
-			 const char *clock, int remote_id, int local_id);
+tracecmd_tsync_with_host(int fd, const char *proto, const char *clock,
+			 int remote_id, int local_id);
 int tracecmd_tsync_with_host_stop(struct tracecmd_time_sync *tsync);
 struct tracecmd_time_sync *
 tracecmd_tsync_with_guest(unsigned long long trace_id, int loop_interval,
@@ -511,8 +521,8 @@
 int tracecmd_tsync_get_offsets(struct tracecmd_time_sync *tsync, int cpu,
 			       int *count, long long **ts,
 			       long long **offsets, long long **scalings, long long **frac);
-int tracecmd_tsync_get_selected_proto(struct tracecmd_time_sync *tsync,
-				      char **selected_proto);
+const char *tracecmd_tsync_get_proto(const struct tracecmd_tsync_protos *protos,
+			 const char *clock, enum tracecmd_time_sync_role role);
 void tracecmd_tsync_free(struct tracecmd_time_sync *tsync);
 int tracecmd_write_guest_time_shift(struct tracecmd_output *handle,
 				    struct tracecmd_time_sync *tsync);
@@ -521,8 +531,8 @@
 struct tracecmd_compress_chunk {
 	unsigned int		size;
 	unsigned int		zsize;
-	off64_t			zoffset;
-	off64_t			offset;
+	off_t			zoffset;
+	off_t			offset;
 };
 struct tracecmd_compression;
 struct tracecmd_compression_proto {
@@ -544,20 +554,20 @@
 int tracecmd_compress_block(struct tracecmd_compression *handle);
 int tracecmd_uncompress_block(struct tracecmd_compression *handle);
 void tracecmd_compress_reset(struct tracecmd_compression *handle);
-int tracecmd_compress_buffer_read(struct tracecmd_compression *handle, char *dst, int len);
-int tracecmd_compress_pread(struct tracecmd_compression *handle, char *dst, int len, off_t offset);
+ssize_t tracecmd_compress_buffer_read(struct tracecmd_compression *handle, char *dst, size_t len);
+ssize_t tracecmd_compress_pread(struct tracecmd_compression *handle, char *dst, size_t len, off_t offset);
 int tracecmd_compress_buffer_write(struct tracecmd_compression *handle,
-				   const void *data, unsigned long long size);
-off64_t tracecmd_compress_lseek(struct tracecmd_compression *handle, off64_t offset, int whence);
+				   const void *data, size_t size);
+off_t tracecmd_compress_lseek(struct tracecmd_compression *handle, off_t offset, int whence);
 int tracecmd_compress_proto_get_name(struct tracecmd_compression *compress,
 				     const char **name, const char **version);
 bool tracecmd_compress_is_supported(const char *name, const char *version);
 int tracecmd_compress_protos_get(char ***names, char ***versions);
 int tracecmd_compress_proto_register(struct tracecmd_compression_proto *proto);
 int tracecmd_compress_copy_from(struct tracecmd_compression *handle, int fd, int chunk_size,
-				unsigned long long *read_size, unsigned long long *write_size);
+				size_t *read_size, size_t *write_size);
 int tracecmd_uncompress_copy_to(struct tracecmd_compression *handle, int fd,
-				unsigned long long *read_size, unsigned long long *write_size);
+				size_t *read_size, size_t *write_size);
 int tracecmd_uncompress_chunk(struct tracecmd_compression *handle,
 			      struct tracecmd_compress_chunk *chunk, char *data);
 int tracecmd_load_chunks_info(struct tracecmd_compression *handle,
diff --git a/lib/trace-cmd/include/private/trace-rbtree.h b/lib/trace-cmd/include/private/trace-rbtree.h
new file mode 100644
index 0000000..8221119
--- /dev/null
+++ b/lib/trace-cmd/include/private/trace-rbtree.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ * Copyright (C) 2023 Google, Steven Rostedt <rostedt@goodmis.org>
+ *
+ */
+#ifndef _TRACE_RBTREE_H
+#define _TRACE_RBTREE_H
+
+struct trace_rbtree_node {
+	struct trace_rbtree_node	*parent;
+	struct trace_rbtree_node	*left;
+	struct trace_rbtree_node	*right;
+	int				color;
+};
+
+typedef int (*trace_rbtree_cmp_fn)(const struct trace_rbtree_node *A, const struct trace_rbtree_node *B);
+
+typedef int (*trace_rbtree_search_fn)(const struct trace_rbtree_node *n, const void *data);
+
+struct trace_rbtree {
+	struct trace_rbtree_node	*node;
+	trace_rbtree_search_fn		search;
+	trace_rbtree_cmp_fn		cmp;
+	size_t				nr_nodes;
+};
+
+void trace_rbtree_init(struct trace_rbtree *tree, trace_rbtree_cmp_fn cmp_fn,
+		       trace_rbtree_search_fn search_fn);
+struct trace_rbtree_node *trace_rbtree_find(struct trace_rbtree *tree, const void *data);
+void trace_rbtree_delete(struct trace_rbtree *tree, struct trace_rbtree_node *node);
+int trace_rbtree_insert(struct trace_rbtree *tree, struct trace_rbtree_node *node);
+struct trace_rbtree_node *trace_rbtree_pop_nobalance(struct trace_rbtree *tree);
+
+#endif /* _TRACE_RBTREE_H */
diff --git a/lib/trace-cmd/include/trace-cmd-local.h b/lib/trace-cmd/include/trace-cmd-local.h
index 6ac3413..1a37e81 100644
--- a/lib/trace-cmd/include/trace-cmd-local.h
+++ b/lib/trace-cmd/include/trace-cmd-local.h
@@ -49,6 +49,15 @@
 	unsigned long long	file_data_offset;
 };
 
+enum tracecmd_filters tracecmd_filter_match(struct tracecmd_filter *filter,
+					    struct tep_record *record);
+
+void trace_set_guest_map(struct tracecmd_input *handle, struct tracecmd_cpu_map *map);
+struct tracecmd_cpu_map *trace_get_guest_map(struct tracecmd_input *handle);
+void trace_set_guest_map_cnt(struct tracecmd_input *handle, int count);
+int trace_get_guest_map_cnt(struct tracecmd_input *handle);
+void trace_guest_map_free(struct tracecmd_cpu_map *map);
+
 void tracecmd_compress_init(void);
 void tracecmd_compress_free(void);
 
@@ -84,15 +93,22 @@
 
 struct cpu_data_source {
 	int fd;
-	int size;
-	off64_t offset;
+	ssize_t size;
+	off_t offset;
 };
 
 int out_write_cpu_data(struct tracecmd_output *handle, int cpus,
 		       struct cpu_data_source *data, const char *buff_name);
 int out_write_emty_cpu_data(struct tracecmd_output *handle, int cpus);
-off64_t msg_lseek(struct tracecmd_msg_handle *msg_handle, off64_t offset, int whence);
+off_t msg_lseek(struct tracecmd_msg_handle *msg_handle, off_t offset, int whence);
 unsigned long long get_last_option_offset(struct tracecmd_input *handle);
 unsigned int get_meta_strings_size(struct tracecmd_input *handle);
+int trace_append_options(struct tracecmd_output *handle, void *buf, size_t len);
+void *trace_get_options(struct tracecmd_output *handle, size_t *len);
+
+/* filters */
+struct tracecmd_filter *tracecmd_filter_get(struct tracecmd_input *handle);
+void tracecmd_filter_set(struct tracecmd_input *handle, struct tracecmd_filter *filter);
+void tracecmd_filter_free(struct tracecmd_filter *filter);
 
 #endif /* _TRACE_CMD_LOCAL_H */
diff --git a/lib/trace-cmd/include/trace-tsync-local.h b/lib/trace-cmd/include/trace-tsync-local.h
index 5bbc1db..27baa59 100644
--- a/lib/trace-cmd/include/trace-tsync-local.h
+++ b/lib/trace-cmd/include/trace-tsync-local.h
@@ -8,6 +8,8 @@
 
 #include <stdbool.h>
 
+struct tsync_proto;
+
 struct tracecmd_time_sync {
 	pthread_t			thread;
 	bool				thread_running;
@@ -19,6 +21,7 @@
 	pthread_barrier_t		first_sync;
 	char				*clock_str;
 	struct tracecmd_msg_handle	*msg_handle;
+	struct tsync_proto		*proto;
 	void				*context;
 	int				guest_pid;
 	int				vcpu_count;
diff --git a/lib/trace-cmd/meson.build b/lib/trace-cmd/meson.build
new file mode 100644
index 0000000..25bdd36
--- /dev/null
+++ b/lib/trace-cmd/meson.build
@@ -0,0 +1,93 @@
+# SPDX-License-Identifier: LGPL-2.1
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+sources = [
+    'trace-hash.c',
+    'trace-rbtree.c',
+    'trace-hooks.c',
+    'trace-input.c',
+    'trace-output.c',
+    'trace-recorder.c',
+    'trace-util.c',
+    'trace-filter-hash.c',
+    'trace-filter.c',
+    'trace-msg.c',
+    'trace-plugin.c',
+    'trace-maps.c',
+    'trace-timesync.c',
+    'trace-timesync-ptp.c',
+    'trace-compress.c',
+    'trace-blk-hack.c',
+    'trace-ftrace.c',
+]
+
+if perf_defined
+    sources += 'trace-perf.c'
+endif
+
+if vsock_defined
+    sources += 'trace-timesync-kvm.c'
+endif
+
+if zlib_dep.found()
+    sources += 'trace-compress-zlib.c'
+endif
+
+if libzstd_dep.found()
+    sources += 'trace-compress-zstd.c'
+endif
+
+if libtracecmd_standalone_build
+    libtracecmd = library(
+        'tracecmd',
+        sources,
+        version: library_version,
+        dependencies: [
+            libtraceevent_dep,
+            libtracefs_dep,
+            threads_dep,
+            dl_dep,
+            zlib_dep,
+            libzstd_dep,
+            audit_dep],
+        include_directories: [
+            libtracecmd_incdir,
+            libtracecmd_private_incdir,
+            libtracecmd_ext_incdir],
+        install: true)
+
+    pkg = import('pkgconfig')
+    pkg.generate(
+        libtracecmd,
+        subdirs: 'trace-cmd',
+        libraries: [
+            libtracefs_dep,
+            libtraceevent_dep],
+        filebase: meson.project_name(),
+        name: meson.project_name(),
+        version: meson.project_version(),
+        description: 'Library for creating and reading trace-cmd data files',
+        url: 'https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/')
+
+    libtracecmd_dep = declare_dependency(
+       include_directories: ['.'],
+       link_with: libtracecmd)
+else
+    static_libtracecmd = static_library(
+        'tracecmd',
+        sources,
+        dependencies: [
+            libtraceevent_dep,
+            libtracefs_dep,
+            threads_dep,
+            dl_dep,
+            zlib_dep,
+            libzstd_dep,
+            audit_dep],
+        include_directories: [
+            libtracecmd_incdir,
+            libtracecmd_private_incdir,
+            libtracecmd_ext_incdir],
+        install: false)
+endif
diff --git a/lib/trace-cmd/trace-compress.c b/lib/trace-cmd/trace-compress.c
index a63295e..03215ad 100644
--- a/lib/trace-cmd/trace-compress.c
+++ b/lib/trace-cmd/trace-compress.c
@@ -30,9 +30,9 @@
 
 struct tracecmd_compression {
 	int				fd;
-	unsigned int			capacity;
-	unsigned int			capacity_read;
-	unsigned long			pointer;
+	size_t				capacity;
+	size_t				capacity_read;
+	size_t				pointer;
 	char				*buffer;
 	struct compress_proto		*proto;
 	struct tep_handle		*tep;
@@ -40,10 +40,10 @@
 	void				*context;
 };
 
-static int read_fd(int fd, char *dst, int len)
+static ssize_t read_fd(int fd, char *dst, int len)
 {
 	size_t size = 0;
-	int r;
+	ssize_t r;
 
 	do {
 		r = read(fd, dst+size, len);
@@ -59,10 +59,10 @@
 	return size;
 }
 
-static long long write_fd(int fd, const void *data, size_t size)
+static ssize_t  write_fd(int fd, const void *data, size_t size)
 {
-	long long tot = 0;
-	long long w;
+	ssize_t  tot = 0;
+	ssize_t  w;
 
 	do {
 		w = write(fd, data + tot, size - tot);
@@ -77,8 +77,8 @@
 	return tot;
 }
 
-static long long do_write(struct tracecmd_compression *handle,
-			  const void *data, unsigned long long size)
+static ssize_t  do_write(struct tracecmd_compression *handle,
+			  const void *data, size_t size)
 {
 	int ret;
 
@@ -91,9 +91,9 @@
 	return write_fd(handle->fd, data, size);
 }
 
-static inline int buffer_extend(struct tracecmd_compression *handle, unsigned int size)
+static inline int buffer_extend(struct tracecmd_compression *handle, size_t size)
 {
-	int extend;
+	ssize_t  extend;
 	char *buf;
 
 	if (size <= handle->capacity)
@@ -117,12 +117,12 @@
  *
  * Returns the new file pointer on success, or -1 in case of an error.
  */
-off64_t tracecmd_compress_lseek(struct tracecmd_compression *handle, off64_t offset, int whence)
+off_t tracecmd_compress_lseek(struct tracecmd_compression *handle, off_t offset, int whence)
 {
 	unsigned long p;
 
 	if (!handle || !handle->buffer)
-		return (off64_t)-1;
+		return (off_t)-1;
 
 	switch (whence) {
 	case SEEK_CUR:
@@ -135,18 +135,18 @@
 		p = offset;
 		break;
 	default:
-		return (off64_t)-1;
+		return (off_t)-1;
 	}
 
 	if (buffer_extend(handle, p))
-		return (off64_t)-1;
+		return (off_t)-1;
 
 	handle->pointer = p;
 
 	return p;
 }
 
-static int compress_read(struct tracecmd_compression *handle, char *dst, int len)
+static ssize_t compress_read(struct tracecmd_compression *handle, char *dst, size_t len)
 {
 
 	if (handle->pointer > handle->capacity_read)
@@ -172,9 +172,9 @@
  *
  * On success returns the number of bytes read, or -1 on failure.
  */
-int tracecmd_compress_pread(struct tracecmd_compression *handle, char *dst, int len, off_t offset)
+ssize_t tracecmd_compress_pread(struct tracecmd_compression *handle, char *dst, size_t len, off_t offset)
 {
-	int ret;
+	ssize_t ret;
 
 	if (!handle || !handle->buffer || offset > handle->capacity_read)
 		return -1;
@@ -195,9 +195,9 @@
  *
  * On success returns the number of bytes read, or -1 on failure.
  */
-int tracecmd_compress_buffer_read(struct tracecmd_compression *handle, char *dst, int len)
+ssize_t tracecmd_compress_buffer_read(struct tracecmd_compression *handle, char *dst, size_t len)
 {
-	int ret;
+	ssize_t ret;
 
 	if (!handle || !handle->buffer)
 		return -1;
@@ -363,7 +363,7 @@
  * Returns 0 on success, or -1 on failure.
  */
 int tracecmd_compress_buffer_write(struct tracecmd_compression *handle,
-				   const void *data, unsigned long long size)
+				   const void *data, size_t size)
 {
 	if (!handle)
 		return -1;
@@ -667,18 +667,18 @@
  * read and written data.
  */
 int tracecmd_compress_copy_from(struct tracecmd_compression *handle, int fd, int chunk_size,
-				unsigned long long *read_size, unsigned long long *write_size)
+				size_t *read_size, size_t *write_size)
 {
-	unsigned int rchunk = 0;
-	unsigned int chunks = 0;
-	unsigned int wsize = 0;
-	unsigned int rsize = 0;
-	unsigned int rmax = 0;
-	unsigned int csize;
-	unsigned int size;
-	unsigned int all;
-	unsigned int r;
-	off64_t offset;
+	size_t rchunk = 0;
+	size_t chunks = 0;
+	size_t rsize = 0;
+	size_t rmax = 0;
+	size_t csize;
+	size_t size;
+	size_t all;
+	size_t r;
+	off_t end_offset;
+	off_t offset;
 	char *buf_from;
 	char *buf_to;
 	int endian4;
@@ -696,11 +696,13 @@
 		return -1;
 
 	buf_to = malloc(csize);
-	if (!buf_to)
+	if (!buf_to) {
+		free(buf_from);
 		return -1;
+	}
 
 	/* save the initial offset and write 0 as initial chunk count */
-	offset = lseek64(handle->fd, 0, SEEK_CUR);
+	offset = lseek(handle->fd, 0, SEEK_CUR);
 	write_fd(handle->fd, &chunks, 4);
 
 	do {
@@ -712,10 +714,10 @@
 
 		do {
 			r = read(fd, buf_from + all, rchunk - all);
-			all += r;
-
 			if (r <= 0)
 				break;
+
+			all += r;
 		} while (all != rchunk);
 
 
@@ -748,8 +750,6 @@
 			ret = write_fd(handle->fd, buf_to, size);
 			if (ret != size)
 				break;
-			/* data + compress header */
-			wsize += (size + 8);
 			chunks++;
 		}
 	} while (all > 0);
@@ -760,19 +760,20 @@
 	if (all)
 		return -1;
 
-	if (lseek64(handle->fd, offset, SEEK_SET) == (off_t)-1)
+	if (lseek(handle->fd, offset, SEEK_SET) == (off_t)-1)
 		return -1;
 
 	endian4 = tep_read_number(handle->tep, &chunks, 4);
 	/* write chunks count*/
 	write_fd(handle->fd, &chunks, 4);
-	if (lseek64(handle->fd, 0, SEEK_END) == (off_t)-1)
+	end_offset = lseek(handle->fd, 0, SEEK_END);
+	if (end_offset == (off_t)-1)
 		return -1;
 
 	if (read_size)
 		*read_size = rsize;
 	if (write_size)
-		*write_size = wsize;
+		*write_size = end_offset - offset;
 	return 0;
 }
 
@@ -794,9 +795,9 @@
 			      struct tracecmd_compress_chunk **chunks_info)
 {
 	struct tracecmd_compress_chunk *chunks = NULL;
-	unsigned long long size = 0;
+	size_t size = 0;
 	unsigned int count = 0;
-	off64_t offset;
+	off_t offset;
 	int ret = -1;
 	char buf[4];
 	int i;
@@ -804,8 +805,8 @@
 	if (!handle)
 		return -1;
 
-	offset = lseek64(handle->fd, 0, SEEK_CUR);
-	if (offset == (off64_t)-1)
+	offset = lseek(handle->fd, 0, SEEK_CUR);
+	if (offset == (off_t)-1)
 		return -1;
 
 	if (read(handle->fd, buf, 4) != 4)
@@ -822,7 +823,7 @@
 		goto out;
 
 	for (i = 0; i < count; i++) {
-		chunks[i].zoffset = lseek64(handle->fd, 0, SEEK_CUR);
+		chunks[i].zoffset = lseek(handle->fd, 0, SEEK_CUR);
 		if (chunks[i].zoffset == (off_t)-1)
 			goto out;
 		if (read(handle->fd, buf, 4) != 4)
@@ -833,13 +834,13 @@
 			goto out;
 		chunks[i].size = tep_read_number(handle->tep, buf, 4);
 		size += chunks[i].size;
-		if (lseek64(handle->fd, chunks[i].zsize, SEEK_CUR) == (off64_t)-1)
+		if (lseek(handle->fd, chunks[i].zsize, SEEK_CUR) == (off_t)-1)
 			goto out;
 	}
 
 	ret = count;
 out:
-	if (lseek64(handle->fd, offset, SEEK_SET) == (off64_t)-1)
+	if (lseek(handle->fd, offset, SEEK_SET) == (off_t)-1)
 		ret = -1;
 
 	if (ret > 0 && chunks_info)
@@ -872,7 +873,7 @@
 	if (!handle || !handle->proto || !handle->proto->uncompress_block || !chunk || !data)
 		return -1;
 
-	if (lseek64(handle->fd, chunk->zoffset + 8, SEEK_SET) == (off_t)-1)
+	if (lseek(handle->fd, chunk->zoffset + 8, SEEK_SET) == (off_t)-1)
 		return -1;
 
 	bytes_in = malloc(chunk->zsize);
@@ -912,12 +913,12 @@
  * the size of read and written data.
  */
 int tracecmd_uncompress_copy_to(struct tracecmd_compression *handle, int fd,
-				unsigned long long *read_size, unsigned long long *write_size)
+				size_t *read_size, size_t *write_size)
 {
-	unsigned int s_uncompressed;
-	unsigned int s_compressed;
-	unsigned int rsize = 0;
-	unsigned int wsize = 0;
+	size_t s_uncompressed;
+	size_t s_compressed;
+	size_t rsize = 0;
+	size_t wsize = 0;
 	char *bytes_out = NULL;
 	char *bytes_in = NULL;
 	int size_out = 0;
diff --git a/lib/trace-cmd/trace-filter.c b/lib/trace-cmd/trace-filter.c
new file mode 100644
index 0000000..9942322
--- /dev/null
+++ b/lib/trace-cmd/trace-filter.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2022, Google Inc, Steven Rostedt <rostedt@goodmis.org>
+*/
+#include <stdlib.h>
+#include <trace-cmd.h>
+#include <trace-cmd-local.h>
+
+struct filter {
+	struct tep_event_filter		*filter;
+};
+
+struct tracecmd_filter {
+	struct tep_handle	*tep;
+	struct filter		**event_filters;
+	struct filter		**event_notrace;
+	bool			*last_printed;
+	int			nr_cpus;
+	int			nr_filters;
+	int			nr_notrace;
+	int			kernel_stacktrace_id;
+	int			user_stacktrace_id;
+};
+
+static bool test_stacktrace(struct tracecmd_filter *filter, struct tep_record *record,
+			    int stacktrace_id)
+{
+	struct tep_handle *tep = filter->tep;
+	int id;
+
+	if (stacktrace_id < 0)
+		return false;
+
+	id = tep_data_type(tep, record);
+	if (id != stacktrace_id)
+		return false;
+
+	return filter->last_printed[record->cpu];
+}
+
+static bool test_stacktraces(struct tracecmd_filter *filter, struct tep_record *record)
+{
+	return test_stacktrace(filter, record, filter->kernel_stacktrace_id) ||
+		test_stacktrace(filter, record, filter->user_stacktrace_id);
+}
+
+__hidden enum tracecmd_filters tracecmd_filter_match(struct tracecmd_filter *filter,
+						     struct tep_record *record)
+{
+	bool is_stack = false;
+	bool found = false;
+	int ret;
+	int i;
+
+	if (!filter)
+		return TRACECMD_FILTER_NONE;
+
+	/* Setup stack traces. If a event is shown, still show stack traces */
+	if (!filter->kernel_stacktrace_id) {
+		struct tep_handle *tep = filter->tep;
+		struct tep_event *event;
+
+		/* In case the below logic fails, do not do this again */
+		filter->kernel_stacktrace_id = -1;
+
+		event = tep_find_event_by_name(tep, "ftrace", "kernel_stack");
+		if (event)
+			filter->kernel_stacktrace_id = event->id;
+
+		event = tep_find_event_by_name(tep, "ftrace", "user_stack");
+		if (event)
+			filter->user_stacktrace_id = event->id;
+
+		filter->nr_cpus = tep_get_cpus(tep);
+		filter->last_printed = calloc(filter->nr_cpus, sizeof(*filter->last_printed));
+		if (!filter->last_printed) {
+			tracecmd_warning("Could not allocate last_printed array for stack trace filtering");
+			filter->kernel_stacktrace_id = -1;
+			filter->user_stacktrace_id = -1;
+		}
+	}
+
+	for (i = 0; i < filter->nr_filters; i++) {
+		ret = tep_filter_match(filter->event_filters[i]->filter, record);
+		switch (ret) {
+		case TRACECMD_FILTER_NONE:
+		case TRACECMD_FILTER_MATCH:
+			found = true;
+		}
+		if (found)
+			break;
+	}
+
+	if (!found && filter->nr_filters) {
+		/* If this is a stack trace and the last event was printed continue */
+		if (!test_stacktraces(filter, record))
+			return TRACECMD_FILTER_MISS;
+
+		is_stack = true;
+	}
+
+	found = false;
+	/* We need to test all negative filters */
+	for (i = 0; i < filter->nr_notrace; i++) {
+		ret = tep_filter_match(filter->event_notrace[i]->filter, record);
+		switch (ret) {
+		case TRACECMD_FILTER_NONE:
+		case TRACECMD_FILTER_MATCH:
+			found = true;
+		}
+		if (found)
+			break;
+	}
+
+	if (filter->last_printed)
+		filter->last_printed[record->cpu] = !is_stack && !found;
+
+	return found ? TRACECMD_FILTER_MISS : TRACECMD_FILTER_MATCH;
+}
+
+struct tracecmd_filter *tracecmd_filter_add(struct tracecmd_input *handle,
+					    const char *filter_str, bool neg)
+{
+	struct tracecmd_filter *trace_filter;
+	struct tep_handle *tep;
+	struct filter ***filter_ptr;
+	struct filter **filters;
+	struct filter *filter;
+	int *nr;
+	int ret;
+
+	filter = calloc(1, sizeof(*filter));
+	if (!filter)
+		return NULL;
+
+	tep = tracecmd_get_tep(handle);
+
+	trace_filter = tracecmd_filter_get(handle);
+	if (!trace_filter) {
+		trace_filter = calloc(1, sizeof(*trace_filter));
+		if (!trace_filter)
+			goto fail;
+		tracecmd_filter_set(handle, trace_filter);
+		trace_filter->tep = tep;
+	}
+
+	filter->filter = tep_filter_alloc(tep);
+	if (!filter->filter)
+		goto fail;
+
+	ret = tep_filter_add_filter_str(filter->filter, filter_str);
+	if (ret < 0)
+		goto fail;
+
+	if (neg) {
+		filter_ptr = &trace_filter->event_notrace;
+		nr = &trace_filter->nr_notrace;
+	} else {
+		filter_ptr = &trace_filter->event_filters;
+		nr = &trace_filter->nr_filters;
+	}
+
+	filters = realloc(*filter_ptr, sizeof(*filters) * (*nr + 1));
+	if (!filters)
+		goto fail;
+
+	*filter_ptr = filters;
+	filters[*nr] = filter;
+	(*nr)++;
+	return trace_filter;
+ fail:
+	if (filter) {
+		tep_filter_free(filter->filter);
+		free(filter);
+	}
+	return NULL;
+}
+
+static void free_filters (struct filter **filter, int nr)
+{
+	int i;
+
+	for (i = 0; i < nr; i++) {
+		tep_filter_free(filter[i]->filter);
+		free(filter[i]);
+	}
+
+	free(filter);
+}
+
+__hidden void tracecmd_filter_free(struct tracecmd_filter *trace_filter)
+{
+	if (!trace_filter)
+		return;
+
+	free_filters(trace_filter->event_filters, trace_filter->nr_filters);
+	free_filters(trace_filter->event_notrace, trace_filter->nr_notrace);
+
+	free(trace_filter);
+}
diff --git a/lib/trace-cmd/trace-ftrace.c b/lib/trace-cmd/trace-ftrace.c
index f74f7c2..d0e6a8e 100644
--- a/lib/trace-cmd/trace-ftrace.c
+++ b/lib/trace-cmd/trace-ftrace.c
@@ -10,6 +10,9 @@
 
 #include "trace-cmd-private.h"
 
+#define MAX_LINUX_ERRNO   4095
+#define IS_LINUX_ERR_VALUE(x) ((unsigned long long)(void *)(x) >= (unsigned long long)-MAX_LINUX_ERRNO)
+
 struct tep_plugin_option trace_ftrace_options[] = {
 	{
 		.name = "tailprint",
@@ -24,12 +27,33 @@
 		"Show the depth of each entry",
 	},
 	{
+		.name = "retval-skip",
+		.plugin_alias = "fgraph",
+		.description =
+		"Skip printing function retval in function graph",
+	},
+	{
+		.name = "retval-dec",
+		.plugin_alias = "fgraph",
+		.description =
+		"Print function retval in decimal at function exit in function graph",
+	},
+	{
+		.name = "retval-hex",
+		.plugin_alias = "fgraph",
+		.description =
+		"Print function retval in hex at function exit in function graph",
+	},
+	{
 		.name = NULL,
 	}
 };
 
 static struct tep_plugin_option *fgraph_tail = &trace_ftrace_options[0];
 static struct tep_plugin_option *fgraph_depth = &trace_ftrace_options[1];
+static struct tep_plugin_option *fgraph_retval_skip = &trace_ftrace_options[2];
+static struct tep_plugin_option *fgraph_retval_dec = &trace_ftrace_options[3];
+static struct tep_plugin_option *fgraph_retval_hex = &trace_ftrace_options[4];
 
 static int find_ret_event(struct tracecmd_ftrace *finfo, struct tep_handle *pevent)
 {
@@ -185,6 +209,8 @@
 	unsigned long long rettime, calltime;
 	unsigned long long duration, depth;
 	unsigned long long val;
+	unsigned long long retval;
+	bool fgraph_retval_supported = true;
 	const char *func;
 	int ret;
 	int i;
@@ -195,6 +221,13 @@
 	if (tep_get_field_val(s, finfo->fgraph_ret_event, "calltime", ret_rec, &calltime, 1))
 		return trace_seq_putc(s, '!');
 
+	if (!tep_find_field(finfo->fgraph_ret_event, "retval")) {
+		fgraph_retval_supported = false;
+	} else {
+		if (tep_get_field_val(s, finfo->fgraph_ret_event, "retval", ret_rec, &retval, 1))
+			return trace_seq_putc(s, '!');
+	}
+
 	duration = rettime - calltime;
 
 	/* Overhead */
@@ -222,6 +255,21 @@
 	if (ret && fgraph_depth->set)
 		ret = trace_seq_printf(s, " (%lld)", depth);
 
+	/* Return Value */
+	if (ret && fgraph_retval_supported && !fgraph_retval_skip->set) {
+		if (fgraph_retval_dec->set) {
+			ret = trace_seq_printf(s, " (ret=%lld)", retval);
+		} else if (fgraph_retval_hex->set) {
+			ret = trace_seq_printf(s, " (ret=0x%llx)", retval);
+		} else {
+			/* Error codes are in decimal; others are in hex */
+			if (!IS_LINUX_ERR_VALUE(retval))
+				ret = trace_seq_printf(s, " (ret=0x%llx)", retval);
+			else
+				ret = trace_seq_printf(s, " (ret=%lld)", retval);
+		}
+	}
+
 	return ret;
 }
 
@@ -283,6 +331,14 @@
 		return trace_seq_putc(s, '!');
 
 	rec = tracecmd_peek_next_data(tracecmd_curr_thread_handle, &cpu);
+
+	/*
+	 * If the next event is on another CPU, show it.
+	 * Even if the next event is the return of this function.
+	 */
+	if (cpu != record->cpu)
+		rec = NULL;
+
 	if (rec)
 		rec = get_return_for_leaf(s, cpu, pid, val, rec, finfo);
 
@@ -308,6 +364,8 @@
 	unsigned long long duration, depth;
 	unsigned long long val;
 	const char *func;
+	unsigned long long retval;
+	bool fgraph_retval_supported = true;
 	int i;
 
 	ret_event_check(finfo, event->tep);
@@ -318,6 +376,13 @@
 	if (tep_get_field_val(s, event, "calltime", record, &calltime, 1))
 		return trace_seq_putc(s, '!');
 
+	if (!tep_find_field(event, "retval")) {
+		fgraph_retval_supported = false;
+	} else {
+		if (tep_get_field_val(s, event, "retval", record, &retval, 1))
+			return trace_seq_putc(s, '!');
+	}
+
 	duration = rettime - calltime;
 
 	/* Overhead */
@@ -347,6 +412,21 @@
 	if (fgraph_depth->set)
 		trace_seq_printf(s, " (%lld)", depth);
 
+	/* Return Value */
+	if (fgraph_retval_supported && !fgraph_retval_skip->set) {
+		if (fgraph_retval_dec->set) {
+			trace_seq_printf(s, " (ret=%lld)", retval);
+		} else if (fgraph_retval_hex->set) {
+			trace_seq_printf(s, " (ret=0x%llx)", retval);
+		} else {
+			/* Error codes are in decimal; others are in hex */
+			if (!IS_LINUX_ERR_VALUE(retval))
+				trace_seq_printf(s, " (ret=0x%llx)", retval);
+			else
+				trace_seq_printf(s, " (ret=%lld)", retval);
+		}
+	}
+
 	return 0;
 }
 
diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c
index 8ffdf04..25114a4 100644
--- a/lib/trace-cmd/trace-input.c
+++ b/lib/trace-cmd/trace-input.c
@@ -3,7 +3,6 @@
  * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
  *
  */
-#define _LARGEFILE64_SOURCE
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -17,6 +16,7 @@
 
 #include "trace-write-local.h"
 #include "trace-cmd-local.h"
+#include "trace-rbtree.h"
 #include "trace-local.h"
 #include "kbuffer.h"
 #include "list.h"
@@ -37,15 +37,24 @@
 
 struct page_map {
 	struct list_head	list;
-	off64_t			offset;
-	off64_t			size;
+	off_t			offset;
+	off_t			size;
 	void			*map;
 	int			ref_count;
 };
 
+struct follow_event {
+	struct tep_event	*event;
+	void			*callback_data;
+	int (*callback)(struct tracecmd_input *handle,
+			struct tep_event *,
+			struct tep_record *,
+			int, void *);
+};
+
 struct page {
 	struct list_head	list;
-	off64_t			offset;
+	off_t			offset;
 	struct tracecmd_input	*handle;
 	struct page_map		*page_map;
 	void			*map;
@@ -58,7 +67,7 @@
 };
 
 struct zchunk_cache {
-	struct list_head		list;
+	struct trace_rbtree_node	node;
 	struct tracecmd_compress_chunk *chunk;
 	void				*map;
 	int				ref;
@@ -75,7 +84,7 @@
 
 	unsigned int		count;
 	unsigned int		last_chunk;
-	struct list_head	cache;
+	struct trace_rbtree	cache;
 	struct tracecmd_compress_chunk	*chunks;
 };
 
@@ -170,6 +179,10 @@
 	struct tep_handle	*pevent;
 	struct tep_plugin_list	*plugin_list;
 	struct tracecmd_input	*parent;
+	struct tracecmd_filter	*filter;
+	struct follow_event	*followers;
+	struct follow_event	*missed_followers;
+	struct tracecmd_cpu_map *map;
 	unsigned long		file_state;
 	unsigned long long	trace_id;
 	unsigned long long	next_offset;
@@ -180,7 +193,10 @@
 	int			page_map_size;
 	int			max_cpu;
 	int			cpus;
+	int			start_cpu;
 	int			ref;
+	int			nr_followers;
+	int			nr_missed_followers;
 	int			nr_buffers;	/* buffer instances */
 	bool			use_trace_clock;
 	bool			read_page;
@@ -188,6 +204,7 @@
 	bool			read_zpage; /* uncompress pages in memory, do not use tmp files */
 	bool			cpu_compressed;
 	int			file_version;
+	int			map_cnt;
 	unsigned int		cpustats_size;
 	struct cpu_zdata	latz;
 	struct cpu_data 	*cpu_data;
@@ -224,6 +241,8 @@
 
 	/* For custom profilers. */
 	tracecmd_show_data_func	show_data_func;
+
+	void			*private;
 };
 
 __thread struct tracecmd_input *tracecmd_curr_thread_handle;
@@ -254,6 +273,16 @@
 	return handle->file_state;
 }
 
+void tracecmd_set_private(struct tracecmd_input *handle, void *data)
+{
+	handle->private = data;
+}
+
+void *tracecmd_get_private(struct tracecmd_input *handle)
+{
+	return handle->private;
+}
+
 #if DEBUG_RECORD
 static void remove_record(struct page *page, struct tep_record *record)
 {
@@ -305,6 +334,34 @@
 }
 #endif
 
+/**
+ * trace_set_guest_map - set map to input handle
+ * @handle: The handle to set the cpu map to
+ * @map: The cpu map for this handle (to the host)
+ *
+ * Assign the mapping of host to guest for a guest handle.
+ */
+__hidden void trace_set_guest_map(struct tracecmd_input *handle,
+				  struct tracecmd_cpu_map *map)
+{
+	handle->map = map;
+}
+
+__hidden struct tracecmd_cpu_map *trace_get_guest_map(struct tracecmd_input *handle)
+{
+	return handle->map;
+}
+
+__hidden void trace_set_guest_map_cnt(struct tracecmd_input *handle, int count)
+{
+	handle->map_cnt = count;
+}
+
+__hidden int trace_get_guest_map_cnt(struct tracecmd_input *handle)
+{
+	return handle->map_cnt;
+}
+
 static int init_cpu(struct tracecmd_input *handle, int cpu);
 
 static ssize_t do_read_fd(int fd, void *data, size_t size)
@@ -400,15 +457,13 @@
 		str = realloc(str, size);
 		if (!str)
 			return NULL;
-		memcpy(str + (size - i), buf, i);
-		str[size] = 0;
+		memcpy(str + (size - i), buf, i + 1);
 	} else {
 		size = i + 1;
 		str = malloc(size);
 		if (!str)
 			return NULL;
-		memcpy(str, buf, i);
-		str[i] = 0;
+		memcpy(str, buf, i + 1);
 	}
 
 	return str;
@@ -494,7 +549,7 @@
 	if (!sec)
 		return NULL;
 
-	if (lseek64(handle->fd, sec->data_offset, SEEK_SET) == (off64_t)-1)
+	if (lseek(handle->fd, sec->data_offset, SEEK_SET) == (off_t)-1)
 		return NULL;
 
 	if ((sec->flags & TRACECMD_SEC_FL_COMPRESS) && in_uncompress_block(handle))
@@ -546,7 +601,7 @@
 
 	if (!HAS_SECTIONS(handle))
 		section_add_or_update(handle, TRACECMD_OPTION_HEADER_INFO, 0, 0,
-				      lseek64(handle->fd, 0, SEEK_CUR));
+				      lseek(handle->fd, 0, SEEK_CUR));
 
 	if (do_read_check(handle, buf, 12))
 		return -1;
@@ -619,6 +674,7 @@
 	line = strtok(buf, "\n");
 	if (!line) {
 		tracecmd_warning("No newline found in '%s'", buf);
+		free(buf);
 		return 0;
 	}
 	/* skip name if it is there */
@@ -753,7 +809,7 @@
 
 	if (!HAS_SECTIONS(handle))
 		section_add_or_update(handle, TRACECMD_OPTION_FTRACE_EVENTS, 0, 0,
-				      lseek64(handle->fd, 0, SEEK_CUR));
+				      lseek(handle->fd, 0, SEEK_CUR));
 
 	if (regex) {
 		sreg = &spreg;
@@ -828,7 +884,7 @@
 
 	if (!HAS_SECTIONS(handle))
 		section_add_or_update(handle, TRACECMD_OPTION_EVENT_FORMATS, 0, 0,
-				      lseek64(handle->fd, 0, SEEK_CUR));
+				      lseek(handle->fd, 0, SEEK_CUR));
 
 	if (regex) {
 		sreg = &spreg;
@@ -915,7 +971,7 @@
 		return 0;
 	if (!HAS_SECTIONS(handle))
 		section_add_or_update(handle, TRACECMD_OPTION_KALLSYMS, 0, 0,
-				      lseek64(handle->fd, 0, SEEK_CUR));
+				      lseek(handle->fd, 0, SEEK_CUR));
 
 	if (read4(handle, &size) < 0)
 		return -1;
@@ -952,7 +1008,7 @@
 
 	if (!HAS_SECTIONS(handle))
 		section_add_or_update(handle, TRACECMD_OPTION_PRINTK, 0, 0,
-				      lseek64(handle->fd, 0, SEEK_CUR));
+				      lseek(handle->fd, 0, SEEK_CUR));
 
 	if (read4(handle, &size) < 0)
 		return -1;
@@ -1126,7 +1182,7 @@
 	unsigned long long size;
 	int ret;
 
-	if (lseek64(handle->fd, section->section_offset, SEEK_SET) == (off_t)-1)
+	if (lseek(handle->fd, section->section_offset, SEEK_SET) == (off_t)-1)
 		return -1;
 	if (read_section_header(handle, &id, &flags, &size, NULL))
 		return -1;
@@ -1134,7 +1190,7 @@
 	if (id != section->id)
 		return -1;
 
-	section->data_offset = lseek64(handle->fd, 0, SEEK_CUR);
+	section->data_offset = lseek(handle->fd, 0, SEEK_CUR);
 	if ((section->flags & TRACECMD_SEC_FL_COMPRESS) && in_uncompress_block(handle))
 		return -1;
 
@@ -1178,7 +1234,7 @@
 	if (!handle->options_start)
 		return -1;
 
-	if (lseek64(handle->fd, handle->options_start, SEEK_SET) == (off64_t)-1) {
+	if (lseek(handle->fd, handle->options_start, SEEK_SET) == (off_t)-1) {
 		tracecmd_warning("Filed to goto options offset %lld", handle->options_start);
 		return -1;
 	}
@@ -1222,11 +1278,11 @@
 	return offset & ~(handle->page_size - 1);
 }
 
-static int read_page(struct tracecmd_input *handle, off64_t offset,
+static int read_page(struct tracecmd_input *handle, off_t offset,
 		     int cpu, void *map)
 {
-	off64_t save_seek;
-	off64_t ret;
+	off_t save_seek;
+	off_t ret;
 
 	if (handle->use_pipe) {
 		ret = read(handle->cpu_data[cpu].pipe_fd, map, handle->page_size);
@@ -1244,9 +1300,9 @@
 	}
 
 	/* other parts of the code may expect the pointer to not move */
-	save_seek = lseek64(handle->fd, 0, SEEK_CUR);
+	save_seek = lseek(handle->fd, 0, SEEK_CUR);
 
-	ret = lseek64(handle->fd, offset, SEEK_SET);
+	ret = lseek(handle->fd, offset, SEEK_SET);
 	if (ret < 0)
 		return -1;
 	ret = read(handle->fd, map, handle->page_size);
@@ -1254,7 +1310,7 @@
 		return -1;
 
 	/* reset the file pointer back */
-	lseek64(handle->fd, save_seek, SEEK_SET);
+	lseek(handle->fd, save_seek, SEEK_SET);
 
 	return 0;
 }
@@ -1295,13 +1351,13 @@
 	if (CHUNK_CHECK_OFFSET(b, a->offset))
 		return 0;
 
-	if (b->offset < a->offset)
+	if (a->offset < b->offset)
 		return -1;
 
 	return 1;
 }
 
-static struct tracecmd_compress_chunk *get_zchunk(struct cpu_data *cpu, off64_t offset)
+static struct tracecmd_compress_chunk *get_zchunk(struct cpu_data *cpu, off_t offset)
 {
 	struct cpu_zdata *cpuz = &cpu->compress;
 	struct tracecmd_compress_chunk *chunk;
@@ -1332,29 +1388,35 @@
 	return chunk;
 }
 
-static void free_zpage(struct cpu_data *cpu_data, void *map)
+static void free_zpage(struct cpu_data *cpu_data, off_t offset)
 {
+	struct trace_rbtree_node *node;
 	struct zchunk_cache *cache;
 
-	list_for_each_entry(cache, &cpu_data->compress.cache, list) {
-		if (map <= cache->map && map > (cache->map + cache->chunk->size))
-			goto found;
-	}
-	return;
+	offset -= cpu_data->file_offset;
 
-found:
+	node = trace_rbtree_find(&cpu_data->compress.cache, (void *)&offset);
+
+	if (!node)
+		return;
+
+	cache = container_of(node, struct zchunk_cache, node);
+
 	cache->ref--;
 	if (cache->ref)
 		return;
-	list_del(&cache->list);
+
+	trace_rbtree_delete(&cpu_data->compress.cache, node);
+
 	free(cache->map);
 	free(cache);
 }
 
-static void *read_zpage(struct tracecmd_input *handle, int cpu, off64_t offset)
+static void *read_zpage(struct tracecmd_input *handle, int cpu, off_t offset)
 {
 	struct cpu_data *cpu_data = &handle->cpu_data[cpu];
 	struct tracecmd_compress_chunk *chunk;
+	struct trace_rbtree_node *node;
 	struct zchunk_cache *cache;
 	void *map = NULL;
 	int pindex;
@@ -1363,11 +1425,11 @@
 	offset -= cpu_data->file_offset;
 
 	/* Look in the cache of already loaded chunks */
-	list_for_each_entry(cache, &cpu_data->compress.cache, list) {
-		if (CHUNK_CHECK_OFFSET(cache->chunk, offset)) {
-			cache->ref++;
-			goto out;
-		}
+	node = trace_rbtree_find(&cpu_data->compress.cache, (void *)&offset);
+	if (node) {
+		cache = container_of(node, struct zchunk_cache, node);
+		cache->ref++;
+		goto out;
 	}
 
 	chunk =  get_zchunk(cpu_data, offset);
@@ -1389,7 +1451,7 @@
 	cache->ref = 1;
 	cache->chunk = chunk;
 	cache->map = map;
-	list_add(&cache->list, &cpu_data->compress.cache);
+	trace_rbtree_insert(&cpu_data->compress.cache, &cache->node);
 
 	/* a chunk can hold multiple pages, get the requested one */
 out:
@@ -1401,18 +1463,21 @@
 }
 
 static void *allocate_page_map(struct tracecmd_input *handle,
-			       struct page *page, int cpu, off64_t offset)
+			       struct page *page, int cpu, off_t offset)
 {
 	struct cpu_data *cpu_data = &handle->cpu_data[cpu];
 	struct page_map *page_map;
-	off64_t map_size;
-	off64_t map_offset;
+	off_t map_size;
+	off_t map_offset;
 	void *map;
 	int ret;
 	int fd;
 
-	if (handle->cpu_compressed && handle->read_zpage)
-		return read_zpage(handle, cpu, offset);
+	if (handle->cpu_compressed) {
+		if (handle->read_zpage)
+			return read_zpage(handle, cpu, offset);
+		offset -= cpu_data->file_offset;
+	}
 
 	if (handle->read_page) {
 		map = malloc(handle->page_size);
@@ -1429,7 +1494,7 @@
 	map_size = handle->page_map_size;
 	map_offset = offset & ~(map_size - 1);
 
-	if (map_offset < cpu_data->file_offset) {
+	if (!handle->cpu_compressed && map_offset < cpu_data->file_offset) {
 		map_size -= cpu_data->file_offset - map_offset;
 		map_offset = cpu_data->file_offset;
 	}
@@ -1452,10 +1517,9 @@
 		map_size -= map_offset + map_size -
 			(cpu_data->file_offset + cpu_data->file_size);
 
-	if (cpu_data->compress.fd >= 0) {
-		map_offset -= cpu_data->file_offset;
+	if (cpu_data->compress.fd >= 0)
 		fd = cpu_data->compress.fd;
-	} else
+	else
 		fd = handle->fd;
  again:
 	page_map->size = map_size;
@@ -1495,7 +1559,7 @@
 }
 
 static struct page *allocate_page(struct tracecmd_input *handle,
-				  int cpu, off64_t offset)
+				  int cpu, off_t offset)
 {
 	struct cpu_data *cpu_data = &handle->cpu_data[cpu];
 	struct page **pages;
@@ -1558,7 +1622,7 @@
 	if (handle->read_page)
 		free(page->map);
 	else if (handle->read_zpage)
-		free_zpage(cpu_data, page->map);
+		free_zpage(cpu_data, page->offset);
 	else
 		free_page_map(page->page_map);
 
@@ -1802,7 +1866,7 @@
  *        -1 on error
  */
 static int get_page(struct tracecmd_input *handle, int cpu,
-		    off64_t offset)
+		    off_t offset)
 {
 	/* Don't map if the page is already where we want */
 	if (handle->cpu_data[cpu].offset == offset &&
@@ -1846,7 +1910,7 @@
 
 static int get_next_page(struct tracecmd_input *handle, int cpu)
 {
-	off64_t offset;
+	off_t offset;
 
 	if (!handle->cpu_data[cpu].page && !handle->use_pipe)
 		return 0;
@@ -2054,13 +2118,45 @@
 	/* If the page was already mapped, we need to reset it */
 	if (ret)
 		update_page_info(handle, cpu);
-		
+
 	free_next(handle, cpu);
 
 	return tracecmd_read_data(handle, cpu);
 }
 
 /**
+ * tracecmd_iterate_reset - Set the handle to iterate from the beginning
+ * @handle: input handle for the trace.dat file
+ *
+ * This causes tracecmd_iterate_events*() to start from the beginning
+ * of the trace.dat file.
+ */
+int tracecmd_iterate_reset(struct tracecmd_input *handle)
+{
+	unsigned long long page_offset;
+	int cpu;
+	int ret = 0;
+	int r;
+
+	for (cpu = 0; cpu < handle->cpus; cpu++) {
+		page_offset = calc_page_offset(handle, handle->cpu_data[cpu].file_offset);
+
+		r = get_page(handle, cpu, page_offset);
+		if (r < 0) {
+			ret = -1;
+			continue; /* ?? */
+		}
+
+		/* If the page was already mapped, we need to reset it */
+		if (r)
+			update_page_info(handle, cpu);
+
+		free_next(handle, cpu);
+	}
+	return ret;
+}
+
+/**
  * tracecmd_read_cpu_last - get the last record in a CPU
  * @handle: input handle for the trace.dat file
  * @cpu: the CPU to search
@@ -2073,7 +2169,7 @@
 tracecmd_read_cpu_last(struct tracecmd_input *handle, int cpu)
 {
 	struct tep_record *record = NULL;
-	off64_t offset, page_offset;
+	off_t offset, page_offset;
 
 	offset = handle->cpu_data[cpu].file_offset +
 		handle->cpu_data[cpu].file_size;
@@ -2134,7 +2230,7 @@
 			      unsigned long long ts)
 {
 	struct cpu_data *cpu_data = &handle->cpu_data[cpu];
-	off64_t start, end, next;
+	off_t start, end, next;
 
 	if (cpu < 0 || cpu >= handle->cpus) {
 		errno = -EINVAL;
@@ -2256,8 +2352,7 @@
  *  Now the next tracecmd_peek_data or tracecmd_read_data will return
  *  the original record.
  */
-int tracecmd_set_cursor(struct tracecmd_input *handle,
-			int cpu, unsigned long long offset)
+int tracecmd_set_cursor(struct tracecmd_input *handle, int cpu, size_t offset)
 {
 	struct cpu_data *cpu_data = &handle->cpu_data[cpu];
 	unsigned long long page_offset;
@@ -2520,6 +2615,561 @@
 }
 
 /**
+ * tracecmd_follow_event - Add callback for specific events for iterators
+ * @handle: The handle to get a callback from
+ * @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 @handle where if tracecmd_iterate_events()
+ * or tracecmd_iterate_events_multi() is called, that if the specified
+ * event is hit, it will call @callback, with the following parameters:
+ *  @handle: Same handle as passed to this function.
+ *  @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.
+ *
+ * Note that when used with tracecmd_iterate_events_multi() that @cpu
+ * may be the nth CPU of all handles it is processing, so if the CPU
+ * that the @record is on is desired, then use @record->cpu.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int tracecmd_follow_event(struct tracecmd_input *handle,
+			  const char *system, const char *event_name,
+			  int (*callback)(struct tracecmd_input *handle,
+					  struct tep_event *,
+					  struct tep_record *,
+					  int, void *),
+			  void *callback_data)
+{
+	struct tep_handle *tep = tracecmd_get_tep(handle);
+	struct follow_event *followers;
+	struct follow_event follow;
+
+	if (!tep) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	follow.event = tep_find_event_by_name(tep, system, event_name);
+	if (!follow.event) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	follow.callback = callback;
+	follow.callback_data = callback_data;
+
+	followers = realloc(handle->followers, sizeof(*followers) *
+			    (handle->nr_followers + 1));
+	if (!followers)
+		return -1;
+
+	handle->followers = followers;
+	followers[handle->nr_followers++] = follow;
+
+	return 0;
+}
+
+/**
+ * tracecmd_follow_missed_events - Add callback for missed events for iterators
+ * @handle: The handle to get a callback from
+ * @callback: The function to call when missed events is detected
+ * @callback_data: The data to pass to @callback
+ *
+ * This attaches a callback to @handle where if tracecmd_iterate_events()
+ * or tracecmd_iterate_events_multi() is called, that if missed events
+ * is detected, it will call @callback, with the following parameters:
+ *  @handle: Same handle as passed to this function.
+ *  @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.
+ *
+ * Note that when used with tracecmd_iterate_events_multi() that @cpu
+ * may be the nth CPU of all handles it is processing, so if the CPU
+ * that the @record is on is desired, then use @record->cpu.
+ *
+ * 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 tracecmd_follow_missed_events(struct tracecmd_input *handle,
+				  int (*callback)(struct tracecmd_input *handle,
+						  struct tep_event *,
+						  struct tep_record *,
+						  int, void *),
+				  void *callback_data)
+{
+	struct follow_event *followers;
+	struct follow_event follow;
+
+	follow.event = NULL;
+	follow.callback = callback;
+	follow.callback_data = callback_data;
+
+	followers = realloc(handle->missed_followers, sizeof(*followers) *
+			    (handle->nr_missed_followers + 1));
+	if (!followers)
+		return -1;
+
+	handle->missed_followers = followers;
+	followers[handle->nr_missed_followers++] = follow;
+
+	return 0;
+}
+
+static int call_followers(struct tracecmd_input *handle,
+			  struct tep_record *record, int cpu)
+{
+	struct tep_handle *tep = tracecmd_get_tep(handle);
+	struct follow_event *followers = handle->followers;
+	struct tep_event *event;
+	int ret = 0;
+	int i;
+
+	event = tep_find_event_by_record(tep, record);
+	if (!event)
+		return -1;
+
+	for (i = 0; i < handle->nr_followers; i++) {
+		if (handle->followers[i].event == event)
+			ret |= followers[i].callback(handle, event, record,
+						     cpu, followers[i].callback_data);
+	}
+
+	return ret;
+}
+
+static int call_missed_events(struct tracecmd_input *handle,
+			      struct tep_record *record, int cpu)
+{
+	struct tep_handle *tep = tracecmd_get_tep(handle);
+	struct follow_event *followers = handle->missed_followers;
+	struct tep_event *event;
+	int ret = 0;
+	int i;
+
+	event = tep_find_event_by_record(tep, record);
+	if (!event)
+		return -1;
+
+	for (i = 0; i < handle->nr_missed_followers; i++) {
+		ret |= followers[i].callback(handle, event, record,
+					     cpu, followers[i].callback_data);
+	}
+
+	return ret;
+}
+
+static int call_callbacks(struct tracecmd_input *handle, struct tep_record *record,
+			  int next_cpu,
+			  int (*callback)(struct tracecmd_input *handle,
+					  struct tep_record *,
+					  int, void *),
+			  void *callback_data)
+{
+	int ret = 0;
+
+	if (!record)
+		return 0;
+
+	if (record->missed_events)
+		ret = call_missed_events(handle, record, next_cpu);
+
+	if (ret)
+		return ret;
+
+	if (!handle->filter ||
+	    tracecmd_filter_match(handle->filter, record) == TRACECMD_FILTER_MATCH) {
+		if (handle->nr_followers)
+			ret = call_followers(handle, record, next_cpu);
+		if (!ret && callback)
+			ret = callback(handle, record, next_cpu, callback_data);
+	}
+
+	return ret;
+}
+
+/**
+ * tracecmd_iterate_events - iterate events over a given handle
+ * @handle: The handle to iterate over
+ * @cpus: The CPU set to filter on (NULL for all CPUs)
+ * @cpu_size: The size of @cpus (ignored if @cpus is NULL)
+ * @callback: The callback function for each event
+ * @callback_data: The data to pass to the @callback.
+ *
+ * Will loop over all events in @handle (filtered by the given @cpus),
+ * and will call @callback for each event in order of the event's records
+ * timestamp.
+ *
+ * Returns the -1 on error, or the value of the callbacks.
+ */
+int tracecmd_iterate_events(struct tracecmd_input *handle,
+			    cpu_set_t *cpus, int cpu_size,
+			    int (*callback)(struct tracecmd_input *handle,
+					    struct tep_record *,
+					    int, void *),
+			    void *callback_data)
+{
+	struct tep_record *record;
+	unsigned long long *timestamps;
+	unsigned long long ts, last_timestamp = 0;
+	int *cpu_list;
+	int cpu_count = 0;
+	int next_cpu;
+	int cpu;
+	int ret = 0;
+	int i;
+
+	if (!callback && !handle->nr_followers) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	timestamps = calloc(handle->cpus, sizeof(*timestamps));
+	if (!timestamps)
+		return -1;
+
+	cpu_list = calloc(handle->cpus, sizeof(*cpu_list));
+	if (!cpu_list) {
+		free(timestamps);
+		return -1;
+	}
+
+	for (cpu = 0; cpu < handle->cpus; cpu++) {
+		if (cpus && !CPU_ISSET_S(cpu, cpu_size, cpus))
+			continue;
+		cpu_list[cpu_count++] = cpu;
+	}
+
+	for (i = 0; i < cpu_count; i++) {
+		cpu = cpu_list[i];
+		record = tracecmd_peek_data(handle, cpu);
+		timestamps[cpu] = record ? record->ts : -1ULL;
+	}
+
+	do {
+		next_cpu = -1;
+		for (i = 0; i < cpu_count; i++) {
+			cpu = cpu_list[i];
+			ts = timestamps[cpu];
+			if (ts == -1ULL)
+				continue;
+
+			if (next_cpu < 0 || ts < last_timestamp) {
+				next_cpu = cpu;
+				last_timestamp = ts;
+			}
+		}
+		if (next_cpu >= 0) {
+			record = tracecmd_peek_data(handle, next_cpu);
+
+			/* Make sure the record is still what we expect it to be */
+			if (!record || record->ts != last_timestamp) {
+				timestamps[next_cpu] = record ? record->ts : -1ULL;
+				continue;
+			}
+
+			/* Need to call read_data to increment to the next record */
+			record = tracecmd_read_data(handle, next_cpu);
+
+			ret = call_callbacks(handle, record, next_cpu,
+					     callback, callback_data);
+
+			tracecmd_free_record(record);
+
+			record = tracecmd_peek_data(handle, next_cpu);
+			timestamps[next_cpu] = record ? record->ts : -1ULL;
+		}
+	} while (next_cpu >= 0 && ret == 0);
+
+	free(timestamps);
+	free(cpu_list);
+
+	return ret;
+}
+
+static struct tep_record *
+load_records(struct tracecmd_input *handle, int cpu,
+	     unsigned long long page_offset, unsigned long long start_offset)
+{
+	struct tep_record *last_record = NULL;
+	struct tep_record *record;
+	unsigned long long page_end = page_offset + handle->page_size;
+
+	if (get_page(handle, cpu, page_offset) < 0)
+		return NULL;
+
+	update_page_info(handle, cpu);
+
+	if (start_offset)
+		page_end = start_offset + 1;
+
+	for (;;) {
+		record = tracecmd_read_data(handle, cpu);
+		if (!record || record->offset >= page_end) {
+			/* Make sure the cpu_data page is still valid */
+			get_page(handle, cpu, page_offset);
+			tracecmd_free_record(record);
+			break;
+		}
+		/*
+		 * Hijack the record->priv, as we know that it points
+		 * to handle->cpu_data[cpu].page, and use that as
+		 * a link list of all the records on this page going
+		 * backwards.
+		 */
+		record->priv = last_record;
+		last_record = record;
+	}
+
+	return last_record;
+}
+
+static void initialize_last_events(struct tracecmd_input *handle,
+				   struct tep_record **last_records,
+				   cpu_set_t *cpu_set, int cpu_size,
+				   int cpus, bool cont)
+{
+	unsigned long long page_offset;
+	unsigned long long start_offset = 0;
+	struct tep_record *record;
+	int cpu;
+
+	for (cpu = 0; cpu < cpus; cpu++) {
+		if (cpu_set && !CPU_ISSET_S(cpu, cpu_size, cpu_set))
+			continue;
+
+		if (!handle->cpu_data[cpu].file_size)
+			continue;
+
+		if (cont) {
+			record = tracecmd_read_data(handle, cpu);
+			if (record)
+				page_offset = start_offset = record->offset;
+			tracecmd_free_record(record);
+		}
+
+		if (!start_offset) {
+			/* Find the start of the last page for this CPU */
+			page_offset = handle->cpu_data[cpu].file_offset +
+				handle->cpu_data[cpu].file_size;
+		}
+		page_offset = calc_page_offset(handle, page_offset - 1);
+
+		last_records[cpu] = load_records(handle, cpu, page_offset, start_offset);
+	}
+}
+
+static struct tep_record *peek_last_event(struct tracecmd_input *handle,
+					  struct tep_record **last_records, int cpu)
+{
+	struct tep_record *record = last_records[cpu];
+	struct page *page = handle->cpu_data[cpu].page;
+	unsigned long long page_offset;
+
+	if (record)
+		return record;
+
+	/* page can be NULL if the size is zero */
+	if (!page)
+		return NULL;
+
+	page_offset = page->offset - handle->page_size;
+	if (page_offset < handle->cpu_data[cpu].file_offset)
+		return NULL;
+
+	last_records[cpu] = load_records(handle, cpu, page_offset, 0);
+	return peek_last_event(handle, last_records, cpu);
+}
+
+static struct tep_record *next_last_event(struct tracecmd_input *handle,
+					  struct tep_record **last_records, int cpu)
+{
+	struct tep_record *record = last_records[cpu];
+	struct page *page = handle->cpu_data[cpu].page;
+
+	if (!record)
+		return NULL;
+
+	last_records[cpu] = record->priv;
+	record->priv = page;
+
+	return record;
+}
+
+/**
+ * tracecmd_iterate_events_reverse - iterate events over a given handle backwards
+ * @handle: The handle to iterate over
+ * @cpus: The CPU set to filter on (NULL for all CPUs)
+ * @cpu_size: The size of @cpus (ignored if @cpus is NULL)
+ * @callback: The callback function for each event
+ * @callback_data: The data to pass to the @callback.
+ * @cont: If true, start where it left off, otherwise start at the end.
+ *
+ * Will loop over all events in @handle (filtered by the given @cpus),
+ * and will call @callback for each event in reverse order.
+ *
+ * Returns the -1 on error, or the value of the callbacks.
+ */
+int tracecmd_iterate_events_reverse(struct tracecmd_input *handle,
+				    cpu_set_t *cpus, int cpu_size,
+				    int (*callback)(struct tracecmd_input *handle,
+						    struct tep_record *,
+						    int, void *),
+				    void *callback_data, bool cont)
+{
+	unsigned long long last_timestamp = 0;
+	struct tep_record **records;
+	struct tep_record *record;
+	int next_cpu;
+	int max_cpus = handle->cpus;
+	int cpu;
+	int ret = 0;
+
+	if (!callback && !handle->nr_followers) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	records = calloc(max_cpus, sizeof(*records));
+	if (!records)
+		return -1;
+
+	initialize_last_events(handle, records, cpus, cpu_size, max_cpus, cont);
+
+	do {
+		next_cpu = -1;
+		for (cpu = 0; cpu < max_cpus; cpu++) {
+			if (cpus && !CPU_ISSET_S(cpu, cpu_size, cpus))
+				continue;
+			record = peek_last_event(handle, records, cpu);
+			if (!record)
+				continue;
+
+			if (next_cpu < 0 || record->ts > last_timestamp) {
+				next_cpu = cpu;
+				last_timestamp = record->ts;
+			}
+		}
+		if (next_cpu >= 0) {
+			record = next_last_event(handle, records, next_cpu);;
+			ret = call_callbacks(handle, record, next_cpu,
+					     callback, callback_data);
+			tracecmd_free_record(record);
+		}
+	} while (next_cpu >= 0 && ret == 0);
+
+	free(records);
+
+	return ret;
+}
+
+struct record_handle {
+	unsigned long long		ts;
+	struct tracecmd_input		*handle;
+};
+
+/**
+ * tracecmd_iterate_events_multi - iterate events over multiple handles
+ * @handles: An array of handles to iterate over
+ * @nr_handles: The number of handles in the @handles array.
+ * @callback: The callback function for each event
+ * @callback_data: The data to pass to the @callback.
+ *
+ * Will loop over all CPUs for each handle in @handles and call the
+ * @callback in the order of the timestamp for each event's record
+ * for each handle.
+ *
+ * Returns the -1 on error, or the value of the callbacks.
+ */
+int tracecmd_iterate_events_multi(struct tracecmd_input **handles,
+				  int nr_handles,
+				  int (*callback)(struct tracecmd_input *handle,
+						  struct tep_record *,
+						  int, void *),
+				  void *callback_data)
+{
+	struct tracecmd_input *handle;
+	struct record_handle *records;
+	struct tep_record *record;
+	unsigned long long ts, last_timestamp = 0;
+	int next_cpu;
+	int cpus = 0;
+	int all_cpus = 0;
+	int cpu;
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < nr_handles; i++) {
+		handle = handles[i];
+		cpus += handle->cpus;
+	}
+
+	records = calloc(cpus, sizeof(*records));
+	if (!records)
+		return -1;
+
+	for (i = 0; i < nr_handles; i++) {
+		handle = handles[i];
+		handle->start_cpu = all_cpus;
+		for (cpu = 0; cpu < handle->cpus; cpu++) {
+			record = tracecmd_peek_data(handle, cpu);
+			records[all_cpus + cpu].ts = record ? record->ts : -1ULL;
+			records[all_cpus + cpu].handle = handle;
+		}
+		all_cpus += cpu;
+	}
+
+	do {
+		next_cpu = -1;
+		for (cpu = 0; cpu < all_cpus; cpu++) {
+			ts = records[cpu].ts;
+			if (ts == -1ULL)
+				continue;
+
+			if (next_cpu < 0 || ts < last_timestamp) {
+				next_cpu = cpu;
+				last_timestamp = ts;
+			}
+		}
+		if (next_cpu >= 0) {
+			handle = records[next_cpu].handle;
+			cpu = next_cpu - handle->start_cpu;
+
+			/* Refresh record as callback could have changed */
+			record = tracecmd_peek_data(handle, cpu);
+
+			/* If the record updated, try again */
+			if (!record || record->ts != last_timestamp) {
+				records[next_cpu].ts = record ? record->ts : -1ULL;
+				continue;
+			}
+
+			/* Need to call read_data to increment to the next record */
+			record = tracecmd_read_data(handle, cpu);
+
+			ret = call_callbacks(handle, record, next_cpu,
+					     callback, callback_data);
+
+			tracecmd_free_record(record);
+		}
+
+	} while (next_cpu >= 0 && ret == 0);
+
+	free(records);
+
+	return ret;
+}
+
+/**
  * tracecmd_peek_next_data - return the next record
  * @handle: input handle to the trace.dat file
  * @rec_cpu: return pointer to the CPU that the record belongs to
@@ -2663,12 +3313,12 @@
 static int init_cpu_zfile(struct tracecmd_input *handle, int cpu)
 {
 	struct cpu_data *cpu_data;
-	unsigned long long size;
-	off64_t offset;
+	off_t offset;
+	size_t size;
 
 	cpu_data = &handle->cpu_data[cpu];
-	offset = lseek64(handle->fd, 0, SEEK_CUR);
-	if (lseek64(handle->fd, cpu_data->file_offset, SEEK_SET) == (off_t)-1)
+	offset = lseek(handle->fd, 0, SEEK_CUR);
+	if (lseek(handle->fd, cpu_data->file_offset, SEEK_SET) == (off_t)-1)
 		return -1;
 
 	strcpy(cpu_data->compress.file, COMPR_TEMP_FILE);
@@ -2679,7 +3329,7 @@
 	if (tracecmd_uncompress_copy_to(handle->compress, cpu_data->compress.fd, NULL, &size))
 		return -1;
 
-	if (lseek64(handle->fd, offset, SEEK_SET) == (off_t)-1)
+	if (lseek(handle->fd, offset, SEEK_SET) == (off_t)-1)
 		return -1;
 
 	cpu_data->file_offset = handle->next_offset;
@@ -2698,7 +3348,7 @@
 	int count;
 	int i;
 
-	if (lseek64(handle->fd, cpu_data->file_offset, SEEK_SET) == (off_t)-1)
+	if (lseek(handle->fd, cpu_data->file_offset, SEEK_SET) == (off_t)-1)
 		return -1;
 
 	count = tracecmd_load_chunks_info(handle->compress, &cpu_data->compress.chunks);
@@ -2709,17 +3359,47 @@
 	cpu_data->compress.last_chunk = 0;
 
 	cpu_data->file_offset = handle->next_offset;
+	cpu_data->file_size = 0;
 
 	for (i = 0; i < count; i++)
 		cpu_data->file_size += cpu_data->compress.chunks[i].size;
 
 	cpu_data->offset = cpu_data->file_offset;
 	cpu_data->size = cpu_data->file_size;
-	handle->next_offset = (handle->next_offset + cpu_data->size + handle->page_size - 1) &
+	handle->next_offset = (handle->next_offset + cpu_data->file_size + handle->page_size - 1) &
 		~(handle->page_size - 1);
 	return 0;
 }
 
+static int compress_cmp(const struct trace_rbtree_node *A,
+			const struct trace_rbtree_node *B)
+{
+	const struct zchunk_cache *cacheA;
+	const struct zchunk_cache *cacheB;
+
+	cacheA = container_of(A, struct zchunk_cache, node);
+	cacheB = container_of(B, struct zchunk_cache, node);
+
+	return chunk_cmp(cacheA->chunk, cacheB->chunk);
+}
+
+static int compress_search(const struct trace_rbtree_node *A,
+			   const void *data)
+{
+	const struct zchunk_cache *cache;
+	off_t offset = *(off_t *)data;
+
+	cache = container_of(A, struct zchunk_cache, node);
+
+	if (CHUNK_CHECK_OFFSET(cache->chunk, offset))
+		return 0;
+
+	if (cache->chunk->offset < offset)
+		return -1;
+
+	return 1;
+}
+
 static int init_cpu(struct tracecmd_input *handle, int cpu)
 {
 	struct cpu_data *cpu_data = &handle->cpu_data[cpu];
@@ -2740,7 +3420,8 @@
 	cpu_data->timestamp = 0;
 
 	list_head_init(&cpu_data->page_maps);
-	list_head_init(&cpu_data->compress.cache);
+
+	trace_rbtree_init(&cpu_data->compress.cache, compress_cmp, compress_search);
 
 	if (!cpu_data->size) {
 		tracecmd_info("CPU %d is empty", cpu);
@@ -3068,7 +3749,7 @@
 		*line = '\0';
 		if (strlen(buf) > STR_PROCMAP_LINE_MAX)
 			break;
-		res = sscanf(buf, "%llx %llx %s", &maps->lib_maps[i].start,
+		res = sscanf(buf, "%zx %zx %s", &maps->lib_maps[i].start,
 			     &maps->lib_maps[i].end, mapname);
 		if (res != 3)
 			break;
@@ -3175,7 +3856,7 @@
 	if (size < 8)
 		return -1;
 
-	offset = lseek64(handle->fd, 0, SEEK_CUR);
+	offset = lseek(handle->fd, 0, SEEK_CUR);
 	if (offset >= size)
 		handle->options_last_offset = offset - size;
 
@@ -3183,7 +3864,7 @@
 	if (!offset)
 		return 0;
 
-	if (lseek64(handle->fd, offset, SEEK_SET) == (off_t)-1)
+	if (lseek(handle->fd, offset, SEEK_SET) == (off_t)-1)
 		return -1;
 
 	return handle_options(handle);
@@ -3340,7 +4021,7 @@
 	int ret;
 
 	if (!HAS_SECTIONS(handle)) {
-		handle->options_start = lseek64(handle->fd, 0, SEEK_CUR);
+		handle->options_start = lseek(handle->fd, 0, SEEK_CUR);
 	} else {
 		if (read_section_header(handle, &id, &flags, NULL, NULL))
 			return -1;
@@ -3493,6 +4174,8 @@
 								 buf + 4, 4);
 			handle->tsc_calc.offset = tep_read_number(handle->pevent,
 								  buf + 8, 8);
+			if (!(handle->flags & TRACECMD_FL_RAW_TS))
+				handle->flags |= TRACECMD_FL_IN_USECS;
 			break;
 		case TRACECMD_OPTION_HEADER_INFO:
 		case TRACECMD_OPTION_FTRACE_EVENTS:
@@ -3679,7 +4362,7 @@
 
 int init_latency_data(struct tracecmd_input *handle)
 {
-	unsigned long long wsize;
+	size_t wsize;
 	int ret;
 
 	if (!handle->cpu_compressed)
@@ -3699,7 +4382,7 @@
 		if (ret)
 			return -1;
 
-		lseek64(handle->latz.fd, 0, SEEK_SET);
+		lseek(handle->latz.fd, 0, SEEK_SET);
 	}
 
 	return 0;
@@ -3715,7 +4398,7 @@
 	if (handle->cpu_data)
 		return -1;
 
-	if (lseek64(handle->fd, buffer->offset, SEEK_SET) == (off_t)-1)
+	if (lseek(handle->fd, buffer->offset, SEEK_SET) == (off_t)-1)
 		return -1;
 	if (read_section_header(handle, &id, &flags, NULL, NULL))
 		return -1;
@@ -3843,7 +4526,7 @@
 
 	if (!HAS_SECTIONS(handle))
 		section_add_or_update(handle, TRACECMD_OPTION_CMDLINES, 0, 0,
-				      lseek64(handle->fd, 0, SEEK_CUR));
+				      lseek(handle->fd, 0, SEEK_CUR));
 
 
 	if (read_data_and_size(handle, &cmdlines, &size) < 0)
@@ -3875,6 +4558,10 @@
 	if (handle->flags & TRACECMD_FL_RAW_TS)
 		handle->flags &= ~TRACECMD_FL_IN_USECS;
 
+	/* tsc_calc is a conversion to nanoseconds */
+	if (handle->tsc_calc.mult)
+		return;
+
 	/* Clear usecs if not one of the specified clocks */
 	if (strcmp(clock, "local") && strcmp(clock, "global") &&
 	    strcmp(clock, "uptime") && strcmp(clock, "perf") &&
@@ -4147,9 +4834,9 @@
 	unsigned short id;
 	unsigned int csize, rsize;
 	unsigned long long size;
-	off64_t offset;
+	off_t offset;
 
-	offset = lseek64(handle->fd, 0, SEEK_CUR);
+	offset = lseek(handle->fd, 0, SEEK_CUR);
 	do {
 		if (read_section_header(handle, &id, &flags, &size, NULL))
 			break;
@@ -4168,12 +4855,12 @@
 			if (flags & TRACECMD_SEC_FL_COMPRESS)
 				in_uncompress_reset(handle);
 		} else {
-			if (lseek64(handle->fd, size, SEEK_CUR) == (off_t)-1)
+			if (lseek(handle->fd, size, SEEK_CUR) == (off_t)-1)
 				break;
 		}
 	} while (1);
 
-	if (lseek64(handle->fd, offset, SEEK_SET) == (off_t)-1)
+	if (lseek(handle->fd, offset, SEEK_SET) == (off_t)-1)
 		return -1;
 
 	return found ? 0 : -1;
@@ -4276,9 +4963,9 @@
 	handle->page_size = page_size;
 	handle->next_offset = page_size;
 
-	offset = lseek64(handle->fd, 0, SEEK_CUR);
-	handle->total_file_size = lseek64(handle->fd, 0, SEEK_END);
-	lseek64(handle->fd, offset, SEEK_SET);
+	offset = lseek(handle->fd, 0, SEEK_CUR);
+	handle->total_file_size = lseek(handle->fd, 0, SEEK_END);
+	lseek(handle->fd, offset, SEEK_SET);
 
 	if (HAS_COMPRESSION(handle)) {
 		zname = read_string(handle);
@@ -4497,10 +5184,10 @@
 				close(cpu_data->compress.fd);
 				unlink(cpu_data->compress.file);
 			}
-			while (!list_empty(&cpu_data->compress.cache)) {
-				cache = container_of(cpu_data->compress.cache.next,
-						     struct zchunk_cache, list);
-				list_del(&cache->list);
+			while (cpu_data->compress.cache.node) {
+				struct trace_rbtree_node *node;
+				node = trace_rbtree_pop_nobalance(&cpu_data->compress.cache);
+				cache = container_of(node, struct zchunk_cache, node);
 				free(cache->map);
 				free(cache);
 			}
@@ -4518,6 +5205,9 @@
 	free(handle->trace_clock);
 	free(handle->strings);
 	free(handle->version);
+	free(handle->followers);
+	free(handle->missed_followers);
+	trace_guest_map_free(handle->map);
 	close(handle->fd);
 	free(handle->latz.chunks);
 	if (handle->latz.fd >= 0) {
@@ -4544,6 +5234,8 @@
 	trace_tsync_offset_free(&handle->host);
 	trace_guests_free(handle);
 
+	tracecmd_filter_free(handle->filter);
+
 	if (handle->flags & TRACECMD_FL_BUFFER_INSTANCE)
 		tracecmd_close(handle->parent);
 	else {
@@ -5053,9 +5745,8 @@
 		if (end_state <= in_handle->file_state)
 			return 0;
 
-		ret = copy_command_lines(in_handle, out_handle);
-		if (ret < 0)
-			goto out;
+		/* Optional */
+		copy_command_lines(in_handle, out_handle);
 
 		/* fallthrough */
 	case TRACECMD_FILE_CPU_COUNT:
@@ -5123,7 +5814,7 @@
 			if (!next)
 				break;
 
-			if (do_lseek(in_handle, next, SEEK_SET) == (off64_t)-1)
+			if (do_lseek(in_handle, next, SEEK_SET) == (off_t)-1)
 				return -1;
 
 			if (read_section_header(in_handle, &id, &flags, NULL, NULL))
@@ -5238,7 +5929,7 @@
 	if (!in_handle->options_start)
 		return 0;
 
-	if (lseek64(in_handle->fd, in_handle->options_start, SEEK_SET) == (off64_t)-1)
+	if (lseek(in_handle->fd, in_handle->options_start, SEEK_SET) == (off_t)-1)
 		return -1;
 
 	if (copy_options(in_handle, out_handle) < 0)
@@ -5385,12 +6076,14 @@
 static int copy_trace_data_from_v7(struct tracecmd_input *in_handle,
 				   struct tracecmd_output *out_handle)
 {
-	int ret = 0;
+	int ret;
 	int i;
 
 	/* Force using temporary files for trace data decompression */
 	in_handle->read_zpage = false;
-	tracecmd_init_data(in_handle);
+	ret = tracecmd_init_data(in_handle);
+	if (ret < 0)
+		return ret;
 	tracecmd_set_out_clock(out_handle, in_handle->trace_clock);
 
 	/* copy top buffer */
@@ -5568,10 +6261,10 @@
 	new_handle->pid_maps = NULL;
 	if (!HAS_SECTIONS(handle)) {
 		/* Save where we currently are */
-		offset = lseek64(handle->fd, 0, SEEK_CUR);
+		offset = lseek(handle->fd, 0, SEEK_CUR);
 
-		ret = lseek64(handle->fd, buffer->offset, SEEK_SET);
-		if (ret == (off64_t)-1) {
+		ret = lseek(handle->fd, buffer->offset, SEEK_SET);
+		if (ret == (off_t)-1) {
 			tracecmd_warning("could not seek to buffer %s offset %ld",
 					  buffer->name, buffer->offset);
 			goto error;
@@ -5589,7 +6282,7 @@
 			tracecmd_warning("failed to read sub buffer %s", buffer->name);
 			goto error;
 		}
-		ret = lseek64(handle->fd, offset, SEEK_SET);
+		ret = lseek(handle->fd, offset, SEEK_SET);
 		if (ret < 0) {
 			tracecmd_warning("could not seek to back to offset %ld", offset);
 			goto error;
@@ -5631,7 +6324,7 @@
 }
 
 /**
- * tracecmd_page_size - return the number of CPUs recorded
+ * tracecmd_cpus - return the number of CPUs recorded
  * @handle: input handle for the trace.dat file
  */
 int tracecmd_cpus(struct tracecmd_input *handle)
@@ -5707,6 +6400,33 @@
 }
 
 /**
+ * tracecmd_get_tsc2nsec - get the calculation numbers to convert to nsecs
+ * @mult: If not NULL, points to where to save the multiplier
+ * @shift: If not NULL, points to where to save the shift.
+ * @offset: If not NULL, points to where to save the offset.
+ *
+ * This only returns a value if the clock is of a raw type.
+ * (currently just x86-tsc is supported).
+ *
+ * Returns 0 on success, or -1 on not supported clock (but may still fill
+ * in the values).
+ */
+int tracecmd_get_tsc2nsec(struct tracecmd_input *handle,
+			  int *mult, int *shift, unsigned long long *offset)
+{
+	if (mult)
+		*mult = handle->tsc_calc.mult;
+	if (shift)
+		*shift = handle->tsc_calc.shift;
+	if (offset)
+		*offset = handle->tsc_calc.offset;
+
+	return handle->top_buffer.clock &&
+		(strcmp(handle->top_buffer.clock, "x86-tsc") == 0 ||
+		 strcmp(handle->top_buffer.clock, "tsc2nsec") == 0) ? 0 : -1;
+}
+
+/**
  * tracecmd_get_cpustats - return the saved cpu stats
  * @handle: input handle for the trace.dat file
  *
@@ -5759,13 +6479,13 @@
  *
  * Provides a method to extract the cpu file size saved in @handle.
  *
- * Returns the cpu file size saved in trace.dat file or (off64_t)-1 for
+ * Returns the cpu file size saved in trace.dat file or (off_t)-1 for
  * invalid cpu index.
  */
-off64_t tracecmd_get_cpu_file_size(struct tracecmd_input *handle, int cpu)
+off_t tracecmd_get_cpu_file_size(struct tracecmd_input *handle, int cpu)
 {
 	if (cpu < 0 || cpu >= handle->cpus)
-		return (off64_t)-1;
+		return (off_t)-1;
 	return handle->cpu_data[cpu].file_size;
 }
 
@@ -5884,3 +6604,19 @@
 	return 0;
 }
 
+__hidden struct tracecmd_filter *tracecmd_filter_get(struct tracecmd_input *handle)
+{
+	return handle->filter;
+}
+
+__hidden void tracecmd_filter_set(struct tracecmd_input *handle,
+				  struct tracecmd_filter *filter)
+{
+	/* This can be used to set filter to NULL though. */
+	if (handle->filter && filter) {
+		tracecmd_warning("Filter exists and setting a new one");
+		return;
+	}
+
+	handle->filter = filter;
+}
diff --git a/lib/trace-cmd/trace-maps.c b/lib/trace-cmd/trace-maps.c
new file mode 100644
index 0000000..bd16486
--- /dev/null
+++ b/lib/trace-cmd/trace-maps.c
@@ -0,0 +1,179 @@
+#include <stdlib.h>
+
+#include "trace-cmd-local.h"
+#include "trace-local.h"
+
+/*
+ * Structure to hold the mapping between host and guest.
+ * @self - A pointer back to the guest's mapping (for the host copy to use)
+ * @host_handle - The handle for the host for this mapping.
+ * @guest_handle - The handle for the guest for this mapping.
+ * @guest_vcpu - The vCPU # for this mapping.
+ * @host_pid - The pid of the task on the host that runs when this vCPU executes.
+ * @private - Private data for applications to use.
+ */
+struct tracecmd_cpu_map {
+	struct tracecmd_cpu_map		*self;
+	struct tracecmd_input		*host_handle;
+	struct tracecmd_input		*guest_handle;
+	int				guest_vcpu;
+	int				host_pid;
+	void				*private;
+};
+
+static int cmp_map(const void *A, const void *B)
+{
+	const struct tracecmd_cpu_map *a = A;
+	const struct tracecmd_cpu_map *b = B;
+
+	if (a->host_pid < b->host_pid)
+		return -1;
+	return a->host_pid > b->host_pid;
+}
+
+int tracecmd_map_vcpus(struct tracecmd_input **handles, int nr_handles)
+{
+	struct tracecmd_input *host_handle = handles[0];
+	unsigned long long traceid;
+	struct tracecmd_cpu_map *vcpu_maps = NULL;
+	struct tracecmd_cpu_map *gmap;
+	struct tracecmd_cpu_map *map;
+	const int *cpu_pids;
+	const char *name;
+	int nr_vcpu_maps = 0;
+	int vcpu_count;
+	int mappings = 0;
+	int ret;
+	int i, k;
+
+	/* handles[0] is the host handle, do for each guest handle */
+	for (i = 1; i < nr_handles; i++) {
+		traceid = tracecmd_get_traceid(handles[i]);
+
+		/*
+		 * Retrieve the host mapping of the guest for this handle.
+		 * cpu_pids is an array of pids that map 1-1 the host vcpus where
+		 * cpu_pids[vCPU_num] = host_task_pid
+		 */
+		ret = tracecmd_get_guest_cpumap(host_handle, traceid,
+						&name, &vcpu_count, &cpu_pids);
+		if (ret)
+			continue;
+
+		mappings++;
+
+		gmap = calloc(sizeof(*gmap), vcpu_count);
+		if (!gmap)
+			goto fail;
+
+		for (k = 0; k < vcpu_count; k++) {
+			gmap[k].host_handle = handles[0];
+			gmap[k].guest_handle = handles[i];
+			gmap[k].guest_vcpu = k;
+			gmap[k].host_pid = cpu_pids[k];
+			gmap[k].self = &gmap[k];
+		}
+
+		trace_set_guest_map(handles[i], gmap);
+		trace_set_guest_map_cnt(handles[i], vcpu_count);
+
+		/* Update the host mapping of all guests to the host */
+		map = realloc(vcpu_maps, sizeof(*map) * (nr_vcpu_maps + vcpu_count));
+		if (!map)
+			goto fail;
+		memset(map + nr_vcpu_maps, 0, sizeof(*map) * (vcpu_count - nr_vcpu_maps));
+
+		vcpu_maps = map;
+		map += nr_vcpu_maps;
+		nr_vcpu_maps += vcpu_count;
+
+		for (k = 0; k < vcpu_count; k++)
+			map[k] = gmap[k];
+	}
+	if (!vcpu_maps)
+		return 0;
+
+	/* We want to do a binary search via host_pid to find these mappings */
+	qsort(vcpu_maps, nr_vcpu_maps, sizeof(*map), cmp_map);
+
+	trace_set_guest_map(handles[0], vcpu_maps);
+	trace_set_guest_map_cnt(handles[0], nr_vcpu_maps);
+
+	return mappings;
+
+ fail:
+	free(vcpu_maps);
+	return -1;
+}
+
+__hidden void trace_guest_map_free(struct tracecmd_cpu_map *map)
+{
+	free(map);
+}
+
+struct tracecmd_cpu_map *tracecmd_map_find_by_host_pid(struct tracecmd_input *handle,
+						       int host_pid)
+{
+	struct tracecmd_cpu_map *map;
+	struct tracecmd_cpu_map key;
+	int nr_maps;
+
+	map = trace_get_guest_map(handle);
+	if (!map)
+		return NULL;
+
+	/* The handle could be from the guest, get the host handle */
+	handle = map->host_handle;
+
+	/* And again, get the mapping of the host, as it has all the mappings */
+	map = trace_get_guest_map(handle);
+	if (!map)
+		return NULL;
+
+	nr_maps = trace_get_guest_map_cnt(handle);
+
+	key.host_pid = host_pid;
+
+	map = bsearch(&key, map, nr_maps, sizeof(*map), cmp_map);
+
+	return map ? map->self : NULL;
+}
+
+void tracecmd_map_set_private(struct tracecmd_cpu_map *map, void *priv)
+{
+	/* Only set the guest private */
+	map = map->self;
+	map->private = priv;
+}
+
+void *tracecmd_map_get_private(struct tracecmd_cpu_map *map)
+{
+	/* Return the guest private */
+	map = map->self;
+	return map->private;
+}
+
+struct tracecmd_input *tracecmd_map_get_guest(struct tracecmd_cpu_map *map)
+{
+	return map->guest_handle;
+}
+
+int tracecmd_map_get_host_pid(struct tracecmd_cpu_map *map)
+{
+	return map->host_pid;
+}
+
+struct tracecmd_cpu_map *tracecmd_get_cpu_map(struct tracecmd_input *handle, int cpu)
+{
+	struct tracecmd_cpu_map *map;
+	int cnt;
+
+	map = trace_get_guest_map(handle);
+	/* Make sure it's for the guest handle, as this could be a host handle */
+	map = map->self;
+	cnt = trace_get_guest_map_cnt(map->guest_handle);
+	if (cnt <= cpu)
+		return NULL;
+
+	return map + cpu;
+}
diff --git a/lib/trace-cmd/trace-msg.c b/lib/trace-cmd/trace-msg.c
index 39465ad..f5c604f 100644
--- a/lib/trace-cmd/trace-msg.c
+++ b/lib/trace-cmd/trace-msg.c
@@ -19,6 +19,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <arpa/inet.h>
+#include <sys/mman.h>
 #include <sys/types.h>
 #include <linux/types.h>
 
@@ -70,6 +71,12 @@
 	u64 trace_id;
 } __packed;
 
+struct tracecmd_msg_trace_proxy {
+	struct tracecmd_msg_trace_req req;
+	be32 cpus;
+	be32 siblings;
+} __packed;
+
 struct tracecmd_msg_trace_resp {
 	be32 flags;
 	be32 cpus;
@@ -100,7 +107,9 @@
 	C(TRACE_REQ,	6,	sizeof(struct tracecmd_msg_trace_req)),	\
 	C(TRACE_RESP,	7,	sizeof(struct tracecmd_msg_trace_resp)),\
 	C(CLOSE_RESP,	8,	0),					\
-	C(TIME_SYNC,	9,	sizeof(struct tracecmd_msg_tsync)),
+	C(TIME_SYNC,	9,	sizeof(struct tracecmd_msg_tsync)),	\
+	C(TRACE_PROXY,	10,	sizeof(struct tracecmd_msg_trace_proxy)), \
+	C(CONT,		11,	0),
 
 #undef C
 #define C(a,b,c)	MSG_##a = b
@@ -133,6 +142,7 @@
 		struct tracecmd_msg_tinit	tinit;
 		struct tracecmd_msg_rinit	rinit;
 		struct tracecmd_msg_trace_req	trace_req;
+		struct tracecmd_msg_trace_proxy	trace_proxy;
 		struct tracecmd_msg_trace_resp	trace_resp;
 		struct tracecmd_msg_tsync	tsync;
 	};
@@ -173,15 +183,29 @@
 	return __do_write_check(fd, msg->buf, data_size);
 }
 
-__hidden off64_t msg_lseek(struct tracecmd_msg_handle *msg_handle, off64_t offset, int whence)
+__hidden off_t msg_lseek(struct tracecmd_msg_handle *msg_handle, off_t offset, int whence)
 {
+	off_t cache_offset = msg_handle->cache_start_offset;
+	off_t ret;
+
 	/*
 	 * lseek works only if the handle is in cache mode,
 	 * cannot seek on a network socket
 	 */
 	if (!msg_handle->cache || msg_handle->cfd < 0)
-		return (off64_t)-1;
-	return lseek64(msg_handle->cfd, offset, whence);
+		return (off_t)-1;
+
+	if (whence == SEEK_SET) {
+		if (offset < cache_offset)
+			return (off_t)-1;
+		offset -= cache_offset;
+	}
+
+	ret = lseek(msg_handle->cfd, offset, whence);
+	if (ret == (off_t)-1)
+		return ret;
+
+	return ret + cache_offset;
 }
 
 static int msg_write(struct tracecmd_msg_handle *msg_handle, struct tracecmd_msg *msg)
@@ -437,7 +461,7 @@
 
 	pfd.fd = fd;
 	pfd.events = POLLIN;
-	ret = poll(&pfd, 1, tracecmd_get_debug() ? -1 : msg_wait_to);
+	ret = poll(&pfd, 1, tracecmd_get_notimeout() ? -1 : msg_wait_to);
 	if (ret < 0)
 		return -errno;
 	else if (ret == 0)
@@ -590,11 +614,17 @@
 int tracecmd_msg_handle_cache(struct tracecmd_msg_handle *msg_handle)
 {
 	if (msg_handle->cfd < 0) {
+#ifdef HAVE_MEMFD_CREATE
+		msg_handle->cfd = memfd_create("trace_msg_cache", 0);
+		if (msg_handle->cfd < 0)
+			return -1;
+#else
 		strcpy(msg_handle->cfile, MSG_CACHE_FILE);
 		msg_handle->cfd = mkstemp(msg_handle->cfile);
 		if (msg_handle->cfd < 0)
 			return -1;
 		unlink(msg_handle->cfile);
+#endif
 	}
 	msg_handle->cache = true;
 	return 0;
@@ -603,15 +633,16 @@
 static int flush_cache(struct tracecmd_msg_handle *msg_handle)
 {
 	char buf[MSG_MAX_DATA_LEN];
+	int fd = msg_handle->cfd;
 	int ret;
 
-	if (!msg_handle->cache || msg_handle->cfd < 0)
+	if (!msg_handle->cache || fd < 0)
 		return 0;
 	msg_handle->cache = false;
-	if (lseek64(msg_handle->cfd, 0, SEEK_SET) == (off64_t)-1)
+	if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
 		return -1;
 	do {
-		ret = read(msg_handle->cfd, buf, MSG_MAX_DATA_LEN);
+		ret = read(fd, buf, MSG_MAX_DATA_LEN);
 		if (ret <= 0)
 			break;
 		ret = tracecmd_msg_data_send(msg_handle, buf, ret);
@@ -619,7 +650,11 @@
 			break;
 	} while (ret >= 0);
 
-	close(msg_handle->cfd);
+	msg_handle->cache_start_offset = lseek(fd, 0, SEEK_CUR);
+	if (msg_handle->cache_start_offset == (off_t)-1)
+		return -1;
+
+	close(fd);
 	msg_handle->cfd = -1;
 	return ret;
 }
@@ -749,6 +784,14 @@
 	return tracecmd_msg_send(msg_handle, &msg);
 }
 
+int tracecmd_msg_cont(struct tracecmd_msg_handle *msg_handle)
+{
+	struct tracecmd_msg msg;
+
+	tracecmd_msg_init(MSG_CONT, &msg);
+	return tracecmd_msg_send(msg_handle, &msg);
+}
+
 int tracecmd_msg_data_send(struct tracecmd_msg_handle *msg_handle,
 			   const char *buf, int size)
 {
@@ -789,6 +832,62 @@
 	return ret;
 }
 
+/**
+ * tracecmd_msg_send_options - Send options over the network
+ * @msg_handle: message handle, holding the communication context
+ * @handle: The output file that has the options to send
+ *
+ * Send options over the network. This is used when the output handle
+ * has more options to send over the network after the trace. Some
+ * options are sent before, and some sent afterward. Since the receiving
+ * side needs to know the location to update the indexes, it will
+ * handle the section header. This just sends out the raw content to
+ * the receiver (requires that both sides have the same endianess, as
+ * no conversion is made of the content of the options).
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int tracecmd_msg_send_options(struct tracecmd_msg_handle *msg_handle,
+			      struct tracecmd_output *handle)
+{
+	struct tracecmd_msg msg;
+	size_t len;
+	void *buf;
+	int ret;
+
+	buf = trace_get_options(handle, &len);
+	if (!buf)
+		return -1;
+
+	ret = tracecmd_msg_data_send(msg_handle, buf, len);
+	free(buf);
+	if (ret < 0)
+		return ret;
+
+	tracecmd_msg_init(MSG_FIN_DATA, &msg);
+	return tracecmd_msg_send(msg_handle, &msg);
+}
+
+/**
+ * tracecmd_msg_flush_data - Send the current cache data over the network
+ * @msg_handle: message handle, holding the communication context
+ *
+ * Send the content in the cache file over the nework, reset the file
+ * and start the cache up again (with nothing in it).
+ */
+int tracecmd_msg_flush_data(struct tracecmd_msg_handle *msg_handle)
+{
+	struct tracecmd_msg msg;
+	int ret;
+
+	flush_cache(msg_handle);
+	tracecmd_msg_init(MSG_FIN_DATA, &msg);
+	ret = tracecmd_msg_send(msg_handle, &msg);
+	if (ret < 0)
+		return ret;
+	return tracecmd_msg_handle_cache(msg_handle);
+}
+
 int tracecmd_msg_finish_sending_data(struct tracecmd_msg_handle *msg_handle)
 {
 	struct tracecmd_msg msg;
@@ -802,32 +901,91 @@
 	return 0;
 }
 
+static int read_msg_data(struct tracecmd_msg_handle *msg_handle,
+			 struct tracecmd_msg *msg)
+{
+	int cmd;
+	int ret;
+
+	ret = tracecmd_msg_recv_wait(msg_handle->fd, msg);
+	if (ret < 0) {
+		tracecmd_warning("reading client %d (%s)", ret, strerror(ret));
+		return ret;
+	}
+
+	cmd = ntohl(msg->hdr.cmd);
+	if (cmd == MSG_FIN_DATA) {
+		/* Finish receiving data */
+		return 0;
+	} else if (cmd != MSG_SEND_DATA) {
+		ret = handle_unexpected_msg(msg_handle, msg);
+		if (ret < 0)
+			return -1;
+		return 0;
+	}
+
+	return msg_buf_len(msg);
+}
+
+/**
+ * tracecmd_msg_read_options - Receive options from over the network
+ * @msg_handle: message handle, holding the communication context
+ * @handle: The output file to write the options to.
+ *
+ * Receive the options sent by tracecmd_msg_send_options().
+ * See that function's documentation for mor details.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int tracecmd_msg_read_options(struct tracecmd_msg_handle *msg_handle,
+			      struct tracecmd_output *handle)
+{
+	struct tracecmd_msg msg;
+	size_t len = 0;
+	void *buf = NULL;
+	void *tmp;
+	int ret;
+	int n;
+
+	memset(&msg, 0, sizeof(msg));
+	while (!tracecmd_msg_done(msg_handle)) {
+		n = read_msg_data(msg_handle, &msg);
+		if (n <= 0)
+			break;
+
+		tmp = realloc(buf, n + len);
+		if (!tmp)
+			goto error;
+		buf = tmp;
+		memcpy(buf + len, msg.buf, n);
+		len += n;
+		msg_free(&msg);
+	}
+	msg_free(&msg);
+
+	ret = trace_append_options(handle, buf, len);
+	free(buf);
+
+	return ret;
+ error:
+	msg_free(&msg);
+	free(buf);
+	return -1;
+}
+
 int tracecmd_msg_read_data(struct tracecmd_msg_handle *msg_handle, int ofd)
 {
 	struct tracecmd_msg msg;
-	int t, n, cmd;
+	int t, n;
 	ssize_t s;
 	int ret;
 
+	memset(&msg, 0, sizeof(msg));
+
 	while (!tracecmd_msg_done(msg_handle)) {
-		ret = tracecmd_msg_recv_wait(msg_handle->fd, &msg);
-		if (ret < 0) {
-			tracecmd_warning("reading client %d (%s)", ret, strerror(ret));
-			return ret;
-		}
-
-		cmd = ntohl(msg.hdr.cmd);
-		if (cmd == MSG_FIN_DATA) {
-			/* Finish receiving data */
+		n = read_msg_data(msg_handle, &msg);
+		if (n <= 0)
 			break;
-		} else if (cmd != MSG_SEND_DATA) {
-			ret = handle_unexpected_msg(msg_handle, &msg);
-			if (ret < 0)
-				goto error;
-			goto next;
-		}
-
-		n = msg_buf_len(&msg);
 		t = n;
 		s = 0;
 		while (t > 0) {
@@ -842,10 +1000,9 @@
 			t -= s;
 			s = n - t;
 		}
-
-next:
 		msg_free(&msg);
 	}
+	msg_free(&msg);
 
 	return 0;
 
@@ -893,6 +1050,11 @@
 	return ret;
 }
 
+int tracecmd_msg_wait(struct tracecmd_msg_handle *msg_handle)
+{
+	return tracecmd_msg_wait_for_cmd(msg_handle, MSG_CONT);
+}
+
 int tracecmd_msg_wait_close(struct tracecmd_msg_handle *msg_handle)
 {
 	return tracecmd_msg_wait_for_cmd(msg_handle, MSG_CLOSE);
@@ -1000,7 +1162,7 @@
 	msg->buf = buf;
 	msg->hdr.size = htonl(ntohl(msg->hdr.size) + size);
 
-	return 0;
+	return size;
 }
 
 int tracecmd_msg_send_trace_req(struct tracecmd_msg_handle *msg_handle,
@@ -1019,6 +1181,26 @@
 	return tracecmd_msg_send(msg_handle, &msg);
 }
 
+int tracecmd_msg_send_trace_proxy(struct tracecmd_msg_handle *msg_handle,
+				  int argc, char **argv, bool use_fifos,
+				  unsigned long long trace_id,
+				  struct tracecmd_tsync_protos *protos,
+				  unsigned int nr_cpus,
+				  unsigned int siblings)
+{
+	struct tracecmd_msg msg;
+	int ret;
+
+	tracecmd_msg_init(MSG_TRACE_PROXY, &msg);
+	ret = make_trace_req(&msg, argc, argv, use_fifos, trace_id, protos);
+	if (ret < 0)
+		return ret;
+
+	msg.trace_proxy.cpus = htonl(nr_cpus);
+	msg.trace_proxy.siblings = htonl(siblings);
+	return tracecmd_msg_send(msg_handle, &msg);
+}
+
 static int get_trace_req_protos(char *buf, int length,
 				struct tracecmd_tsync_protos **protos)
 {
@@ -1065,7 +1247,6 @@
 	unsigned int nr_args;
 	char *p, *buf_end;
 	char **args = NULL;
-	char *vagrs = NULL;
 	int ret;
 	int i;
 
@@ -1084,15 +1265,8 @@
 		goto out;
 	}
 
-	vagrs = calloc(length, sizeof(char));
-	if (!vagrs) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	memcpy(vagrs, buf, length);
-	buf_end = vagrs + length;
-	for (i = 0, p = vagrs; i < nr_args; i++, p++) {
+	buf_end = buf + length;
+	for (i = 0, p = buf; i < nr_args; i++, p++) {
 		if (p >= buf_end) {
 			ret = -EINVAL;
 			goto out;
@@ -1107,21 +1281,16 @@
 
 out:
 	free(args);
-	free(vagrs);
 	return ret;
 
 }
 
-/*
- * NOTE: On success, the returned `argv` should be freed with:
- *     free(argv[0]);
- *     free(argv);
- * and `tsync_protos` with free(tsync_protos);
- */
-int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
-				int *argc, char ***argv, bool *use_fifos,
-				unsigned long long *trace_id,
-				struct tracecmd_tsync_protos **protos)
+static int msg_recv_trace_req_proxy(struct tracecmd_msg_handle *msg_handle,
+				    int *argc, char ***argv, bool *use_fifos,
+				    unsigned long long *trace_id,
+				    struct tracecmd_tsync_protos **protos,
+				    unsigned int *cpus,
+				    unsigned int *siblings)
 {
 	struct tracecmd_msg msg;
 	unsigned int param_id;
@@ -1134,7 +1303,16 @@
 	if (ret < 0)
 		return ret;
 
-	if (ntohl(msg.hdr.cmd) != MSG_TRACE_REQ) {
+	switch (ntohl(msg.hdr.cmd)) {
+	case MSG_TRACE_PROXY:
+		if (cpus)
+			*cpus = ntohl(msg.trace_proxy.cpus);
+		if (siblings)
+			*siblings = ntohl(msg.trace_proxy.siblings);
+		/* fall through */
+	case MSG_TRACE_REQ:
+		break;
+	default:
 		ret = -ENOTSUP;
 		goto out;
 	}
@@ -1185,6 +1363,37 @@
 	return ret;
 }
 
+/*
+ * NOTE: On success, the returned `argv` should be freed with:
+ *     free(argv[0]);
+ *     free(argv);
+ * and `tsync_protos` with free(tsync_protos);
+ */
+int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
+				int *argc, char ***argv, bool *use_fifos,
+				unsigned long long *trace_id,
+				struct tracecmd_tsync_protos **protos)
+{
+	return msg_recv_trace_req_proxy(msg_handle, argc, argv, use_fifos,
+					trace_id, protos, NULL, NULL);
+}
+
+/*
+ * NOTE: On success, the returned `argv` should be freed with:
+ *     free(argv[0]);
+ *     free(argv);
+ * and `tsync_protos` with free(tsync_protos);
+ */
+int tracecmd_msg_recv_trace_proxy(struct tracecmd_msg_handle *msg_handle,
+				  int *argc, char ***argv, bool *use_fifos,
+				  unsigned long long *trace_id,
+				  struct tracecmd_tsync_protos **protos,
+				  unsigned int *cpus, unsigned int *siblings)
+{
+	return msg_recv_trace_req_proxy(msg_handle, argc, argv, use_fifos,
+					trace_id, protos, cpus, siblings);
+}
+
 /**
  * tracecmd_msg_send_time_sync - Send a time sync packet
  * @msg_handle: message handle, holding the communication context
diff --git a/lib/trace-cmd/trace-output.c b/lib/trace-cmd/trace-output.c
index ca7132e..66e11dd 100644
--- a/lib/trace-cmd/trace-output.c
+++ b/lib/trace-cmd/trace-output.c
@@ -3,7 +3,6 @@
  * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
  *
  */
-#define _LARGEFILE64_SOURCE
 #include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -69,6 +68,7 @@
 	unsigned long		strings_offs;
 
 	unsigned long long	options_start;
+	unsigned long long	options_next;
 	bool			big_endian;
 	bool			do_compress;
 	struct tracecmd_compression *compress;
@@ -111,7 +111,7 @@
 	return __do_write_check(handle->fd, data, size);
 }
 
-static inline off64_t do_lseek(struct tracecmd_output *handle, off_t offset, int whence)
+static inline off_t do_lseek(struct tracecmd_output *handle, off_t offset, int whence)
 {
 	if (handle->do_compress)
 		return tracecmd_compress_lseek(handle->compress, offset, whence);
@@ -119,7 +119,7 @@
 	if (handle->msg_handle)
 		return msg_lseek(handle->msg_handle, offset, whence);
 
-	return lseek64(handle->fd, offset, whence);
+	return lseek(handle->fd, offset, whence);
 }
 
 static inline int do_preed(struct tracecmd_output *handle, void *dst, int len, off_t offset)
@@ -280,7 +280,7 @@
 	free(handle);
 }
 
-void tracecmd_output_close(struct tracecmd_output *handle)
+void tracecmd_output_flush(struct tracecmd_output *handle)
 {
 	if (!handle)
 		return;
@@ -292,6 +292,14 @@
 		/* write strings section */
 		save_string_section(handle, true);
 	}
+}
+
+void tracecmd_output_close(struct tracecmd_output *handle)
+{
+	if (!handle)
+		return;
+
+	tracecmd_output_flush(handle);
 
 	if (handle->fd >= 0) {
 		close(handle->fd);
@@ -383,9 +391,9 @@
 						 unsigned long long *write_size,
 						 int page)
 {
-	unsigned long long rsize = 0;
-	unsigned long long wsize = 0;
-	unsigned long long size;
+	size_t rsize = 0;
+	size_t wsize = 0;
+	size_t size;
 	int ret;
 
 	if (handle->compress) {
@@ -517,12 +525,12 @@
 	/* Section ID */
 	endian2 = convert_endian_2(handle, header_id);
 	if (do_write_check(handle, &endian2, 2))
-		return (off64_t)-1;
+		return (off_t)-1;
 
 	/* Section flags */
 	endian2 = convert_endian_2(handle, flags);
 	if (do_write_check(handle, &endian2, 2))
-		return (off64_t)-1;
+		return (off_t)-1;
 
 	/* Section description */
 	if (description)
@@ -531,13 +539,13 @@
 		desc = -1;
 	endian4 = convert_endian_4(handle, desc);
 	if (do_write_check(handle, &endian4, 4))
-		return (off64_t)-1;
+		return (off_t)-1;
 
 	offset = do_lseek(handle, 0, SEEK_CUR);
 	size = 0;
 	/* Reserve for section size */
 	if (do_write_check(handle, &size, 8))
-		return (off64_t)-1;
+		return (off_t)-1;
 	return offset;
 }
 
@@ -558,13 +566,13 @@
 	if (size < 8)
 		return -1;
 	size -= 8;
-	if (do_lseek(handle, offset, SEEK_SET) == (off64_t)-1)
+	if (do_lseek(handle, offset, SEEK_SET) == (off_t)-1)
 		return -1;
 
 	endian8 = convert_endian_8(handle, size);
 	if (do_write_check(handle, &endian8, 8))
 		return -1;
-	if (do_lseek(handle, current, SEEK_SET) == (off64_t)-1)
+	if (do_lseek(handle, current, SEEK_SET) == (off_t)-1)
 		return -1;
 	return 0;
 }
@@ -586,7 +594,7 @@
 	if (compress)
 		flags |= TRACECMD_SEC_FL_COMPRESS;
 	offset = out_write_section_header(handle, TRACECMD_OPTION_STRINGS, "strings", flags, false);
-	if (offset == (off64_t)-1)
+	if (offset == (off_t)-1)
 		return -1;
 
 	out_compression_start(handle, compress);
@@ -636,8 +644,10 @@
 		flags |= TRACECMD_SEC_FL_COMPRESS;
 	offset = out_write_section_header(handle, TRACECMD_OPTION_HEADER_INFO,
 					  "headers", flags, true);
-	if (offset == (off64_t)-1)
+	if (offset == (off_t)-1) {
+		put_tracing_file(path);
 		return -1;
+	}
 
 	out_compression_start(handle, compress);
 	ret = stat(path, &st);
@@ -663,23 +673,22 @@
 	fd = open(path, O_RDONLY);
 	if (fd < 0) {
 		tracecmd_warning("can't read '%s'", path);
-		goto out_close;
+		goto out_free;
 	}
 
 	/* unfortunately, you can not stat debugfs files for size */
 	size = get_size_fd(fd);
 
 	if (do_write_check(handle, "header_page", 12))
-		goto out_close;
+		goto out_free;
 	endian8 = convert_endian_8(handle, size);
 	if (do_write_check(handle, &endian8, 8))
-		goto out_close;
+		goto out_free;
 	check_size = copy_file_fd(handle, fd, 0);
-	close(fd);
 	if (size != check_size) {
 		tracecmd_warning("wrong size for '%s' size=%lld read=%lld", path, size, check_size);
 		errno = EINVAL;
-		goto out_close;
+		goto out_free;
 	}
 	put_tracing_file(path);
 
@@ -687,24 +696,25 @@
 	if (!path)
 		goto out_close;
 
+	close(fd);
 	fd = open(path, O_RDONLY);
 	if (fd < 0) {
 		tracecmd_warning("can't read '%s'", path);
-		goto out_close;
+		goto out_free;
 	}
 
 	size = get_size_fd(fd);
 
 	if (do_write_check(handle, "header_event", 13))
-		goto out_close;
+		goto out_free;
 	endian8 = convert_endian_8(handle, size);
 	if (do_write_check(handle, &endian8, 8))
-		goto out_close;
+		goto out_free;
 	check_size = copy_file_fd(handle, fd, 0);
 	close(fd);
 	if (size != check_size) {
 		tracecmd_warning("wrong size for '%s'", path);
-		goto out_close;
+		goto out_free;
 	}
 	put_tracing_file(path);
 	if (out_compression_end(handle, compress))
@@ -716,6 +726,8 @@
 
 	return 0;
 
+ out_free:
+	put_tracing_file(path);
  out_close:
 	out_compression_reset(handle, compress);
 	if (fd >= 0)
@@ -856,8 +868,10 @@
 
 	path = malloc(events_len + strlen(str) +
 		      strlen("/format") + 2);
-	if (!path)
+	if (!path) {
+		put_tracing_file(events_path);
 		return;
+	}
 	path[0] = '\0';
 	strcat(path, events_path);
 	strcat(path, "/");
@@ -964,7 +978,7 @@
 		flags |= TRACECMD_SEC_FL_COMPRESS;
 	offset = out_write_section_header(handle, TRACECMD_OPTION_FTRACE_EVENTS,
 					  "ftrace events", flags, true);
-	if (offset == (off64_t)-1)
+	if (offset == (off_t)-1)
 		return -1;
 
 	create_event_list_item(handle, &systems, &list);
@@ -1023,7 +1037,7 @@
 		flags |= TRACECMD_SEC_FL_COMPRESS;
 	offset = out_write_section_header(handle, TRACECMD_OPTION_EVENT_FORMATS,
 					  "events format", flags, true);
-	if (offset == (off64_t)-1)
+	if (offset == (off_t)-1)
 		return -1;
 	/*
 	 * If any of the list is the special keyword "all" then
@@ -1109,7 +1123,7 @@
 	if (write(fd, &buf, 1) > 0)
 		ret = 0;
 err:
-	if (fd > 0)
+	if (fd >= 0)
 		close(fd);
 	if (ret)
 		tracecmd_warning("can't set kptr_restrict");
@@ -1137,7 +1151,7 @@
 		flags |= TRACECMD_SEC_FL_COMPRESS;
 	offset = out_write_section_header(handle, TRACECMD_OPTION_KALLSYMS,
 					  "kallsyms", flags, true);
-	if (offset == (off64_t)-1)
+	if (offset == (off_t)-1)
 		return -1;
 
 	out_compression_start(handle, compress);
@@ -1201,8 +1215,10 @@
 	if (compress)
 		flags |= TRACECMD_SEC_FL_COMPRESS;
 	offset = out_write_section_header(handle, TRACECMD_OPTION_PRINTK, "printk", flags, true);
-	if (offset == (off64_t)-1)
+	if (offset == (off_t)-1) {
+		put_tracing_file(path);
 		return -1;
+	}
 
 	out_compression_start(handle, compress);
 	ret = stat(path, &st);
@@ -1246,8 +1262,8 @@
 	unsigned long long endian8;
 	char *file = NULL;
 	struct stat st;
-	off64_t check_size;
-	off64_t size;
+	off_t check_size;
+	off_t size;
 	int ret = -1;
 
 	file = get_tracing_file(handle, filename);
@@ -1841,10 +1857,123 @@
 	return 0;
 }
 
+static int update_options_start(struct tracecmd_output *handle, off_t offset)
+{
+	if (do_lseek(handle, handle->options_start, SEEK_SET) == (off_t)-1)
+		return -1;
+	offset = convert_endian_8(handle, offset);
+	if (do_write_check(handle, &offset, 8))
+		return -1;
+	return 0;
+}
+
+/**
+ * tracecmd_pepare_options - perpare a previous options for the next
+ * @handle: The handle to update the options for.
+ * @offset: The offset to set the previous options to.
+ * @whence: Where in the file to offset from.
+ *
+ * In a case of cached writes for network access, the options offset
+ * cannot be written once it goes over the network. This is used
+ * to update the next options to a known location.
+ *
+ * tracecmd_write_options() must be called when the offset is at the next
+ * location, otherwise the data file will end up corrupted.
+ *
+ * Returns zero on success and -1 on error.
+ */
+int tracecmd_prepare_options(struct tracecmd_output *handle, off_t offset, int whence)
+{
+	tsize_t curr;
+	int ret;
+
+	/* No options to start with? */
+	if (!handle->options_start)
+		return 0;
+
+	curr = do_lseek(handle, 0, SEEK_CUR);
+
+	switch (whence) {
+	case SEEK_SET:
+		/* just use offset */
+		break;
+	case SEEK_CUR:
+		offset += curr;
+		break;
+	case SEEK_END:
+		offset = do_lseek(handle, offset, SEEK_END);
+		if (offset == (off_t)-1)
+			return -1;
+		break;
+	}
+	ret = update_options_start(handle, offset);
+	if (ret < 0)
+		return -1;
+
+	handle->options_next = offset;
+
+	curr = do_lseek(handle, curr, SEEK_SET);
+
+	return curr == -1 ? -1 : 0;
+}
+
+static tsize_t write_options_start(struct tracecmd_output *handle)
+{
+	tsize_t offset;
+	int ret;
+
+	offset = do_lseek(handle, 0, SEEK_CUR);
+
+	if (handle->options_next) {
+		/* options_start was already updated */
+		if (handle->options_next != offset) {
+			tracecmd_warning("Options offset (%lld) does not match expected (%lld)",
+					 offset, handle->options_next);
+			return -1;
+		}
+		handle->options_next = 0;
+		/* Will be updated at the end */
+		handle->options_start = 0;
+	}
+
+	/* Append to the previous options section, if any */
+	if (handle->options_start) {
+		ret = update_options_start(handle, offset);
+		if (ret < 0)
+			return -1;
+		offset = do_lseek(handle, offset, SEEK_SET);
+		if (offset == (off_t)-1)
+			return -1;
+	}
+
+	return out_write_section_header(handle, TRACECMD_OPTION_DONE, "options", 0, false);
+}
+
+static tsize_t write_options_end(struct tracecmd_output *handle, tsize_t offset)
+{
+	unsigned long long endian8;
+	unsigned short endian2;
+	unsigned int endian4;
+
+	endian2 = convert_endian_2(handle, TRACECMD_OPTION_DONE);
+	if (do_write_check(handle, &endian2, 2))
+		return -1;
+	endian4 = convert_endian_4(handle, 8);
+	if (do_write_check(handle, &endian4, 4))
+		return -1;
+	endian8 = 0;
+	handle->options_start = do_lseek(handle, 0, SEEK_CUR);
+	if (do_write_check(handle, &endian8, 8))
+		return -1;
+	if (out_update_section_header(handle, offset))
+		return -1;
+
+	return 0;
+}
+
 static int write_options(struct tracecmd_output *handle)
 {
 	struct tracecmd_option *options;
-	unsigned long long endian8;
 	unsigned short endian2;
 	unsigned int endian4;
 	bool new = false;
@@ -1857,22 +1986,15 @@
 			break;
 		}
 	}
-	if (!new)
+	/*
+	 * Even if there are no new options, if options_next is set, it requires
+	 * adding a new empty options section as the previous one already
+	 * points to it.
+	 */
+	if (!new && !handle->options_next)
 		return 0;
-	offset = do_lseek(handle, 0, SEEK_CUR);
 
-	/* Append to the previous options section, if any */
-	if (handle->options_start) {
-		if (do_lseek(handle, handle->options_start, SEEK_SET) == (off64_t)-1)
-			return -1;
-		endian8 = convert_endian_8(handle, offset);
-		if (do_write_check(handle, &endian8, 8))
-			return -1;
-		if (do_lseek(handle, offset, SEEK_SET) == (off_t)-1)
-			return -1;
-	}
-
-	offset = out_write_section_header(handle, TRACECMD_OPTION_DONE, "options", 0, false);
+	offset = write_options_start(handle);
 	if (offset == (off_t)-1)
 		return -1;
 
@@ -1892,20 +2014,104 @@
 			return -1;
 	}
 
-	endian2 = convert_endian_2(handle, TRACECMD_OPTION_DONE);
-	if (do_write_check(handle, &endian2, 2))
-		return -1;
-	endian4 = convert_endian_4(handle, 8);
-	if (do_write_check(handle, &endian4, 4))
-		return -1;
-	endian8 = 0;
-	handle->options_start = do_lseek(handle, 0, SEEK_CUR);
-	if (do_write_check(handle, &endian8, 8))
-		return -1;
-	if (out_update_section_header(handle, offset))
+	return write_options_end(handle, offset);
+}
+
+/**
+ * trace_get_options - Get the current options from the output file handle
+ * @handle: The output file descriptor that has options.
+ * @len: Returns the length of the buffer allocated and returned.
+ *
+ * Reads the options that have not been written to the file yet,
+ * puts them into an allocated buffer and sets @len to the size
+ * added. Used by trace-msg.c to send options over the network.
+ *
+ * Note, the options cannot be referenced again once this is called.
+ *  New options can be added and referenced.
+ *
+ * Returns an allocated buffer (must be freed with free()) that contains
+ *   the options to send, with @len set to the size of the content.
+ *   NULL on error (and @len is undefined).
+ */
+__hidden void *trace_get_options(struct tracecmd_output *handle, size_t *len)
+{
+	struct tracecmd_msg_handle msg_handle;
+	struct tracecmd_output out_handle;
+	struct tracecmd_option *options;
+	unsigned short endian2;
+	unsigned int endian4;
+	tsize_t offset;
+	void *buf = NULL;
+
+	/* Use the msg_cache as our output */
+	memset(&msg_handle, 0, sizeof(msg_handle));
+	msg_handle.cfd = -1;
+	if (tracecmd_msg_handle_cache(&msg_handle) < 0)
+		return NULL;
+
+	out_handle = *handle;
+	out_handle.fd = msg_handle.cfd;
+	out_handle.msg_handle = &msg_handle;
+
+	list_for_each_entry(options, &handle->options, list) {
+		/* Option is already saved, skip it */
+		if (options->offset)
+			continue;
+		endian2 = convert_endian_2(handle, options->id);
+		if (do_write_check(&out_handle, &endian2, 2))
+			goto out;
+		endian4 = convert_endian_4(handle, options->size);
+		if (do_write_check(&out_handle, &endian4, 4))
+			goto out;
+		/* The option can not be referenced again */
+		options->offset = -1;
+		if (do_write_check(&out_handle, options->data, options->size))
+			goto out;
+	}
+
+	offset = do_lseek(&out_handle, 0, SEEK_CUR);
+	buf = malloc(offset);
+	if (!buf)
+		goto out;
+
+	if (do_lseek(&out_handle, 0, SEEK_SET) == (off_t)-1)
+		goto out;
+	*len = read(msg_handle.cfd, buf, offset);
+	if (*len != offset) {
+		free(buf);
+		buf = NULL;
+	}
+
+ out:
+	close(msg_handle.cfd);
+	return buf;
+}
+
+/**
+ * trace_append_options - Append options to the file
+ * @handle: The output file descriptor that has options.
+ * @buf: The options to append.
+ * @len: The length of @buf.
+ *
+ * Will add an options section header for the content of @buf to
+ * be written as options into the @handle.
+ * Used by trace-msg.c to retrieve options over the network.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+__hidden int trace_append_options(struct tracecmd_output *handle, void *buf,
+				  size_t len)
+{
+	tsize_t offset;
+
+	offset = write_options_start(handle);
+	if (offset == (off_t)-1)
 		return -1;
 
-	return 0;
+	if (do_write_check(handle, buf, len))
+		return -1;
+
+	return write_options_end(handle, offset);
 }
 
 int tracecmd_write_meta_strings(struct tracecmd_output *handle)
@@ -2079,7 +2285,7 @@
 		flags |= TRACECMD_SEC_FL_COMPRESS;
 	offset = out_write_section_header(handle, TRACECMD_OPTION_CMDLINES,
 					  "command lines", flags, true);
-	if (offset == (off64_t)-1)
+	if (offset == (off_t)-1)
 		return -1;
 
 	out_compression_start(handle, compress);
@@ -2328,7 +2534,7 @@
 	current = do_lseek(handle, 0, SEEK_CUR);
 
 	/* Go to the option data, where will write the offest */
-	if (do_lseek(handle, b_offset, SEEK_SET) == (off64_t)-1) {
+	if (do_lseek(handle, b_offset, SEEK_SET) == (off_t)-1) {
 		tracecmd_warning("could not seek to %lld", b_offset);
 		return -1;
 	}
@@ -2337,7 +2543,7 @@
 		return -1;
 
 	/* Go back to end of file */
-	if (do_lseek(handle, current, SEEK_SET) == (off64_t)-1) {
+	if (do_lseek(handle, current, SEEK_SET) == (off_t)-1) {
 		tracecmd_warning("could not seek to %lld", offset);
 		return -1;
 	}
@@ -2461,7 +2667,7 @@
 		data_files[i].data_offset &= ~(page_size - 1);
 
 		ret = do_lseek(handle, data_files[i].data_offset, SEEK_SET);
-		if (ret == (off64_t)-1)
+		if (ret == (off_t)-1)
 			goto out_free;
 
 		if (!tracecmd_get_quiet(handle))
@@ -2469,7 +2675,7 @@
 				i, (unsigned long long)data_files[i].data_offset);
 
 		if (data[i].size) {
-			if (lseek64(data[i].fd, data[i].offset, SEEK_SET) == (off64_t)-1)
+			if (lseek(data[i].fd, data[i].offset, SEEK_SET) == (off_t)-1)
 				goto out_free;
 			read_size = out_copy_fd_compress(handle, data[i].fd,
 							 data[i].size, &data_files[i].write_size,
@@ -2477,7 +2683,7 @@
 
 			if (read_size != data_files[i].file_size) {
 				errno = EINVAL;
-				tracecmd_warning("did not match size of %lld to %lld",
+				tracecmd_warning("did not match size of %llu to %llu",
 						 read_size, data_files[i].file_size);
 				goto out_free;
 			}
@@ -2487,19 +2693,19 @@
 
 		if (!HAS_SECTIONS(handle)) {
 			/* Write the real CPU data offset in the file */
-			if (do_lseek(handle, data_files[i].file_data_offset, SEEK_SET) == (off64_t)-1)
+			if (do_lseek(handle, data_files[i].file_data_offset, SEEK_SET) == (off_t)-1)
 				goto out_free;
 			endian8 = convert_endian_8(handle, data_files[i].data_offset);
 			if (do_write_check(handle, &endian8, 8))
 				goto out_free;
 			/* Write the real CPU data size in the file */
-			if (do_lseek(handle, data_files[i].file_write_size, SEEK_SET) == (off64_t)-1)
+			if (do_lseek(handle, data_files[i].file_write_size, SEEK_SET) == (off_t)-1)
 				goto out_free;
 			endian8 = convert_endian_8(handle, data_files[i].write_size);
 			if (do_write_check(handle, &endian8, 8))
 				goto out_free;
 			offset = data_files[i].data_offset + data_files[i].write_size;
-			if (do_lseek(handle, offset, SEEK_SET) == (off64_t)-1)
+			if (do_lseek(handle, offset, SEEK_SET) == (off_t)-1)
 				goto out_free;
 		}
 		if (!tracecmd_get_quiet(handle)) {
@@ -2518,7 +2724,7 @@
 		goto out_free;
 
 	free(data_files);
-	if (do_lseek(handle, 0, SEEK_END) == (off64_t)-1)
+	if (do_lseek(handle, 0, SEEK_END) == (off_t)-1)
 		return -1;
 
 	if (out_update_section_header(handle, offset))
@@ -2787,7 +2993,7 @@
 			return -1;
 
 		new = do_lseek(handle, 0, SEEK_CUR);
-		if (do_lseek(handle, handle->options_start, SEEK_SET) == (off64_t)-1)
+		if (do_lseek(handle, handle->options_start, SEEK_SET) == (off_t)-1)
 			return -1;
 
 		en8 = convert_endian_8(handle, start);
@@ -2795,7 +3001,7 @@
 			return -1;
 
 		handle->options_start = new;
-		if (do_lseek(handle, new, SEEK_SET) == (off64_t)-1)
+		if (do_lseek(handle, new, SEEK_SET) == (off_t)-1)
 			return -1;
 	} else {
 		handle->options_start = start;
@@ -2813,7 +3019,7 @@
 	return handle->file_version;
 }
 
-unsigned long long tracecmd_get_out_file_offset(struct tracecmd_output *handle)
+size_t tracecmd_get_out_file_offset(struct tracecmd_output *handle)
 {
 	return do_lseek(handle, 0, SEEK_CUR);
 }
diff --git a/lib/trace-cmd/trace-plugin.c b/lib/trace-cmd/trace-plugin.c
index 127771e..72cc8ce 100644
--- a/lib/trace-cmd/trace-plugin.c
+++ b/lib/trace-cmd/trace-plugin.c
@@ -126,13 +126,13 @@
 	if (!func) {
 		tracecmd_warning("could not find func '%s' in plugin '%s'\n%s",
 				 TRACECMD_PLUGIN_LOADER_NAME, plugin, dlerror());
-		goto out_free;
+		goto out_close;
 	}
 
 	list = malloc(sizeof(*list));
 	if (!list) {
 		tracecmd_warning("could not allocate plugin memory");
-		goto out_free;
+		goto out_close;
 	}
 
 	list->next = *plugin_list;
@@ -144,6 +144,8 @@
 	func(trace);
 	return;
 
+ out_close:
+	dlclose(handle);
  out_free:
 	free(plugin);
 }
diff --git a/lib/trace-cmd/trace-rbtree.c b/lib/trace-cmd/trace-rbtree.c
new file mode 100644
index 0000000..9067181
--- /dev/null
+++ b/lib/trace-cmd/trace-rbtree.c
@@ -0,0 +1,442 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2023 Google, Steven Rostedt <rostedt@goodmis.org>
+ *
+ */
+#include <stdlib.h>
+#include <stdbool.h>
+#include "trace-local.h"
+#include "trace-rbtree.h"
+
+enum {
+	RED,
+	BLACK,
+};
+
+void __hidden trace_rbtree_init(struct trace_rbtree *tree, trace_rbtree_cmp_fn cmp_fn,
+				trace_rbtree_search_fn search_fn)
+{
+	memset(tree, 0, sizeof(*tree));
+	tree->search = search_fn;
+	tree->cmp = cmp_fn;
+}
+
+static bool is_left(struct trace_rbtree_node *node)
+{
+	return node == node->parent->left;
+}
+
+static struct trace_rbtree_node **get_parent_ptr(struct trace_rbtree *tree,
+						 struct trace_rbtree_node *node)
+{
+	if (!node->parent)
+		return &tree->node;
+	else if (is_left(node))
+		return &node->parent->left;
+	else
+		return &node->parent->right;
+}
+
+static void rotate_left(struct trace_rbtree *tree,
+			struct trace_rbtree_node *node)
+{
+	struct trace_rbtree_node **parent_ptr = get_parent_ptr(tree, node);
+	struct trace_rbtree_node *parent = node->parent;
+	struct trace_rbtree_node *old_right = node->right;
+
+	*parent_ptr = old_right;
+	node->right = old_right->left;
+	old_right->left = node;
+
+	if (node->right)
+		node->right->parent = node;
+	node->parent = old_right;
+	old_right->parent = parent;
+}
+
+static void rotate_right(struct trace_rbtree *tree,
+			 struct trace_rbtree_node *node)
+{
+	struct trace_rbtree_node **parent_ptr = get_parent_ptr(tree, node);
+	struct trace_rbtree_node *parent = node->parent;
+	struct trace_rbtree_node *old_left = node->left;
+
+	*parent_ptr = old_left;
+	node->left = old_left->right;
+	old_left->right = node;
+
+	if (node->left)
+		node->left->parent = node;
+	node->parent = old_left;
+	old_left->parent = parent;
+}
+
+static void insert_tree(struct trace_rbtree *tree,
+			struct trace_rbtree_node *node)
+{
+	struct trace_rbtree_node *next = tree->node;
+	struct trace_rbtree_node *last_next = NULL;
+	bool went_left = false;
+
+	while (next) {
+		last_next = next;
+		if (tree->cmp(next, node) > 0) {
+			next = next->right;
+			went_left = false;
+		} else {
+			next = next->left;
+			went_left = true;
+		}
+	}
+
+	if (!last_next) {
+		tree->node = node;
+		return;
+	}
+
+	if (went_left)
+		last_next->left = node;
+	else
+		last_next->right = node;
+
+	node->parent = last_next;
+}
+
+#if 0
+static int check_node(struct trace_rbtree *tree, struct trace_rbtree_node *node)
+{
+	if (!node->parent) {
+		if (tree->node != node)
+			goto fail;
+	} else {
+		if (!is_left(node)) {
+			if (node->parent->right != node)
+				goto fail;
+		}
+	}
+	return 0;
+fail:
+	printf("FAILED ON NODE!");
+	breakpoint();
+	return -1;
+}
+
+static void check_tree(struct trace_rbtree *tree)
+{
+	struct trace_rbtree_node *node = tree->node;
+
+	if (node) {
+		if (check_node(tree, node))
+			return;
+		while (node->left) {
+			node = node->left;
+			if (check_node(tree, node))
+				return;
+		}
+	}
+
+	while (node) {
+		if (check_node(tree, node))
+			return;
+		if (node->right) {
+			node = node->right;
+			if (check_node(tree, node))
+				return;
+			while (node->left) {
+				node = node->left;
+				if (check_node(tree, node))
+				    return;
+			}
+			continue;
+		}
+		while (node->parent) {
+			if (is_left(node))
+				break;
+			node = node->parent;
+			if (check_node(tree, node))
+				return;
+		}
+		node = node->parent;
+	}
+}
+#else
+static inline void check_tree(struct trace_rbtree *tree) { }
+#endif
+
+int __hidden trace_rbtree_insert(struct trace_rbtree *tree,
+				 struct trace_rbtree_node *node)
+{
+	struct trace_rbtree_node *uncle;
+
+	memset(node, 0, sizeof(*node));
+
+	insert_tree(tree, node);
+	node->color = RED;
+	while (node && node->parent && node->parent->color == RED) {
+		if (is_left(node->parent)) {
+			uncle = node->parent->parent->right;
+			if (uncle && uncle->color == RED) {
+				node->parent->color = BLACK;
+				uncle->color = BLACK;
+				node->parent->parent->color = RED;
+				node = node->parent->parent;
+			} else {
+				if (!is_left(node)) {
+					node = node->parent;
+					rotate_left(tree, node);
+					check_tree(tree);
+				}
+				node->parent->color = BLACK;
+				node->parent->parent->color = RED;
+				rotate_right(tree, node->parent->parent);
+				check_tree(tree);
+			}
+		} else {
+			uncle = node->parent->parent->left;
+			if (uncle && uncle->color == RED) {
+				node->parent->color = BLACK;
+				uncle->color = BLACK;
+				node->parent->parent->color = RED;
+				node = node->parent->parent;
+			} else {
+				if (is_left(node)) {
+					node = node->parent;
+					rotate_right(tree, node);
+					check_tree(tree);
+				}
+				node->parent->color = BLACK;
+				node->parent->parent->color = RED;
+				rotate_left(tree, node->parent->parent);
+				check_tree(tree);
+			}
+		}
+	}
+	check_tree(tree);
+	tree->node->color = BLACK;
+	tree->nr_nodes++;
+	return 0;
+}
+
+struct trace_rbtree_node *trace_rbtree_find(struct trace_rbtree *tree, const void *data)
+{
+	struct trace_rbtree_node *node = tree->node;
+	int ret;
+
+	while (node) {
+		ret = tree->search(node, data);
+		if (!ret)
+			return node;
+		if (ret > 0)
+			node = node->right;
+		else
+			node = node->left;
+	}
+	return NULL;
+}
+
+static struct trace_rbtree_node *next_node(struct trace_rbtree_node *node)
+{
+	if (node->right) {
+		node = node->right;
+		while (node->left)
+			node = node->left;
+		return node;
+	}
+
+	while (node->parent && !is_left(node))
+		node = node->parent;
+
+	return node->parent;
+}
+
+static void tree_fixup(struct trace_rbtree *tree, struct trace_rbtree_node *node)
+{
+	while (node->parent && node->color == BLACK) {
+		if (is_left(node)) {
+			struct trace_rbtree_node *old_right = node->parent->right;
+
+			if (old_right->color == RED) {
+				old_right->color = BLACK;
+				node->parent->color = RED;
+				rotate_left(tree, node->parent);
+				old_right = node->parent->right;
+			}
+			if (old_right->left->color == BLACK &&
+			    old_right->right->color == BLACK) {
+				old_right->color = RED;
+				node = node->parent;
+			} else {
+				if (old_right->right->color == BLACK) {
+					old_right->left->color = BLACK;
+					old_right->color = RED;
+					rotate_right(tree, old_right);
+					old_right = node->parent->right;
+				}
+				old_right->color = node->parent->color;
+				node->parent->color = BLACK;
+				old_right->right->color = BLACK;
+				rotate_left(tree, node->parent);
+				node = tree->node;
+			}
+		} else {
+			struct trace_rbtree_node *old_left = node->parent->left;
+
+			if (old_left->color == RED) {
+				old_left->color = BLACK;
+				node->parent->color = RED;
+				rotate_right(tree, node->parent);
+				old_left = node->parent->left;
+			}
+			if (old_left->right->color == BLACK &&
+			    old_left->left->color == BLACK) {
+				old_left->color = RED;
+				node = node->parent;
+			} else {
+				if (old_left->left->color == BLACK) {
+					old_left->right->color = BLACK;
+					old_left->color = RED;
+					rotate_left(tree, old_left);
+					old_left = node->parent->left;
+				}
+				old_left->color = node->parent->color;
+				node->parent->color = BLACK;
+				old_left->left->color = BLACK;
+				rotate_right(tree, node->parent);
+				node = tree->node;
+			}
+		}
+	}
+	node->color = BLACK;
+}
+
+void trace_rbtree_delete(struct trace_rbtree *tree, struct trace_rbtree_node *node)
+{
+	struct trace_rbtree_node *x, *y;
+	bool do_fixup = false;
+
+	if (!node->left && !node->right && !node->parent) {
+		tree->node = NULL;
+		goto out;
+	}
+
+	if (!node->left || !node->right)
+		y = node;
+	else
+		y = next_node(node);
+
+	if (y->left)
+		x = y->left;
+	else
+		x = y->right;
+
+	if (x)
+		x->parent = y->parent;
+
+	if (!y->parent) {
+		tree->node = x;
+	} else {
+		if (is_left(y))
+			y->parent->left = x;
+		else
+			y->parent->right = x;
+	}
+
+	do_fixup = y->color == BLACK;
+
+	if (y != node) {
+		y->color = node->color;
+		y->parent = node->parent;
+		y->left = node->left;
+		y->right = node->right;
+		if (y->left)
+			y->left->parent = y;
+		if (y->right)
+			y->right->parent = y;
+		if (!y->parent) {
+			tree->node = y;
+		} else {
+			if (is_left(node))
+				y->parent->left = y;
+			else
+				y->parent->right = y;
+		}
+	}
+
+	if (do_fixup)
+		tree_fixup(tree, x);
+
+ out:
+	node->parent = node->left = node->right = NULL;
+	tree->nr_nodes--;
+	check_tree(tree);
+}
+
+__hidden struct trace_rbtree_node *trace_rbtree_next(struct trace_rbtree *tree,
+						     struct trace_rbtree_node *node)
+{
+	check_tree(tree);
+	/*
+	 * When either starting or the previous iteration returned a
+	 * node with a right branch, then go to the first node (if starting)
+	 * or the right node, and then return the left most node.
+	 */
+	if (!node || node->right) {
+		if (!node)
+			node = tree->node;
+		else
+			node = node->right;
+		while (node && node->left)
+			node = node->left;
+		return node;
+	}
+
+	/*
+	 * If we are here, then the previous iteration returned the
+	 * left most node of the tree or the right branch. If this
+	 * is a left node, then simply return the parent. If this
+	 * is a right node, then keep going up until its a left node,
+	 * or we finished the iteration.
+	 *
+	 * If we are here and are the top node, then there is no right
+	 * node, and this is finished (return NULL).
+	 */
+	if (!node->parent || is_left(node))
+		return node->parent;
+
+	do {
+		node = node->parent;
+	} while (node->parent && !is_left(node));
+
+	return node->parent;
+}
+
+/*
+ * Used for freeing a tree, just quickly pop off the children in
+ * no particular order. This will corrupt the tree! That is,
+ * do not do any inserting or deleting of this tree after calling
+ * this function.
+ */
+struct trace_rbtree_node *trace_rbtree_pop_nobalance(struct trace_rbtree *tree)
+{
+	struct trace_rbtree_node *node = tree->node;
+
+	if (!node)
+		return NULL;
+
+	while (node->left)
+		node = node->left;
+
+	while (node->right)
+		node = node->right;
+
+	if (node->parent) {
+		if (is_left(node))
+			node->parent->left = NULL;
+		else
+			node->parent->right = NULL;
+	} else {
+		tree->node = NULL;
+	}
+
+	return node;
+}
diff --git a/lib/trace-cmd/trace-recorder.c b/lib/trace-cmd/trace-recorder.c
index c833378..44f245d 100644
--- a/lib/trace-cmd/trace-recorder.c
+++ b/lib/trace-cmd/trace-recorder.c
@@ -3,7 +3,6 @@
  * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
  *
  */
-#define _LARGEFILE64_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <fcntl.h>
@@ -32,20 +31,17 @@
 #define POLL_TIMEOUT_MS		1000
 
 struct tracecmd_recorder {
+	struct tracefs_cpu *tcpu;
 	int		fd;
 	int		fd1;
 	int		fd2;
-	int		trace_fd;
-	int		brass[2];
-	int		pipe_size;
 	int		page_size;
+	int		subbuf_size;
 	int		cpu;
 	int		stop;
 	int		max;
 	int		pages;
 	int		count;
-	unsigned	fd_flags;
-	unsigned	trace_fd_flags;
 	unsigned	flags;
 };
 
@@ -54,7 +50,7 @@
 	char buf[size];
 	int r;
 
-	lseek64(src, 0, SEEK_SET);
+	lseek(src, 0, SEEK_SET);
 
 	/* If there's an error, then we are pretty much screwed :-p */
 	do {
@@ -85,23 +81,16 @@
 					  recorder->fd2, recorder->fd1);
 			/* Error on copying, then just keep fd1 */
 			if (ret) {
-				lseek64(recorder->fd1, 0, SEEK_END);
+				lseek(recorder->fd1, 0, SEEK_END);
 				goto close;
 			}
-			lseek64(recorder->fd1, 0, SEEK_SET);
+			lseek(recorder->fd1, 0, SEEK_SET);
 			ftruncate(recorder->fd1, 0);
 		}
 		append_file(recorder->page_size, recorder->fd1, recorder->fd2);
 	}
  close:
-	if (recorder->brass[0] >= 0)
-		close(recorder->brass[0]);
-
-	if (recorder->brass[1] >= 0)
-		close(recorder->brass[1]);
-
-	if (recorder->trace_fd >= 0)
-		close(recorder->trace_fd);
+	tracefs_cpu_close(recorder->tcpu);
 
 	if (recorder->fd1 >= 0)
 		close(recorder->fd1);
@@ -112,46 +101,24 @@
 	free(recorder);
 }
 
-static void set_nonblock(struct tracecmd_recorder *recorder)
+static int set_nonblock(struct tracecmd_recorder *recorder)
 {
-	long flags;
-
-	/* Do not block on reads */
-	flags = fcntl(recorder->trace_fd, F_GETFL);
-	fcntl(recorder->trace_fd, F_SETFL, flags | O_NONBLOCK);
-
-	/* Do not block on streams */
-	recorder->fd_flags |= SPLICE_F_NONBLOCK;
+	return tracefs_cpu_stop(recorder->tcpu);
 }
 
-struct tracecmd_recorder *
-tracecmd_create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,
-				    const char *buffer, int maxkb)
+static struct tracecmd_recorder *
+create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,
+			   struct tracefs_instance *instance, int maxkb, int tfd)
 {
 	struct tracecmd_recorder *recorder;
-	char *path = NULL;
-	int pipe_size = 0;
-	int ret;
+	bool nonblock = false;
 
 	recorder = malloc(sizeof(*recorder));
 	if (!recorder)
 		return NULL;
 
-	recorder->cpu = cpu;
 	recorder->flags = flags;
 
-	recorder->fd_flags = SPLICE_F_MOVE;
-
-	if (!(recorder->flags & TRACECMD_RECORD_BLOCK_SPLICE))
-		recorder->fd_flags |= SPLICE_F_NONBLOCK;
-
-	recorder->trace_fd_flags = SPLICE_F_MOVE;
-
-	/* Init to know what to free and release */
-	recorder->trace_fd = -1;
-	recorder->brass[0] = -1;
-	recorder->brass[1] = -1;
-
 	recorder->page_size = getpagesize();
 	if (maxkb) {
 		int kb_per_page = recorder->page_size >> 10;
@@ -174,45 +141,18 @@
 	recorder->fd1 = fd;
 	recorder->fd2 = fd2;
 
-	if (buffer) {
-		if (flags & TRACECMD_RECORD_SNAPSHOT)
-			ret = asprintf(&path, "%s/per_cpu/cpu%d/snapshot_raw",
-				       buffer, cpu);
-		else
-			ret = asprintf(&path, "%s/per_cpu/cpu%d/trace_pipe_raw",
-				       buffer, cpu);
-		if (ret < 0)
-			goto out_free;
-
-		recorder->trace_fd = open(path, O_RDONLY);
-		free(path);
-
-		if (recorder->trace_fd < 0)
-			goto out_free;
-	}
-
-	if (!(recorder->flags & (TRACECMD_RECORD_NOSPLICE |
-				 TRACECMD_RECORD_NOBRASS))) {
-		ret = pipe(recorder->brass);
-		if (ret < 0)
-			goto out_free;
-
-		ret = fcntl(recorder->brass[0], F_GETPIPE_SZ, &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 page_size for splice(). It could also return
-		 * success, but not modify pipe_size.
-		 */
-		if (ret < 0 || !pipe_size)
-			pipe_size = recorder->page_size;
-
-		recorder->pipe_size = pipe_size;
-	}
-
 	if (recorder->flags & TRACECMD_RECORD_POLL)
-		set_nonblock(recorder);
+		nonblock = true;
 
+	if (tfd >= 0)
+		recorder->tcpu = tracefs_cpu_alloc_fd(tfd, recorder->page_size, nonblock);
+	else
+		recorder->tcpu = tracefs_cpu_open(instance, cpu, nonblock);
+
+	if (!recorder->tcpu)
+		goto out_free;
+
+	recorder->subbuf_size = tracefs_cpu_read_size(recorder->tcpu);
 	return recorder;
 
  out_free:
@@ -221,26 +161,60 @@
 }
 
 struct tracecmd_recorder *
-tracecmd_create_buffer_recorder_fd(int fd, int cpu, unsigned flags, const char *buffer)
+tracecmd_create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,
+				    struct tracefs_instance *instance, int maxkb)
 {
-	return tracecmd_create_buffer_recorder_fd2(fd, -1, cpu, flags, buffer, 0);
+	return create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb, -1);
+}
+
+struct tracecmd_recorder *
+tracecmd_create_buffer_recorder_fd(int fd, int cpu, unsigned flags, struct tracefs_instance *instance)
+{
+	return tracecmd_create_buffer_recorder_fd2(fd, -1, cpu, flags, instance, 0);
 }
 
 static struct tracecmd_recorder *
 __tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags,
-				  const char *buffer)
+				  struct tracefs_instance *instance, int tfd, int maxkb)
 {
 	struct tracecmd_recorder *recorder;
-	int fd;
+	int fd, fd2 = -1;
+	char *file2;
 
-	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
+	fd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
 	if (fd < 0)
 		return NULL;
 
-	recorder = tracecmd_create_buffer_recorder_fd(fd, cpu, flags, buffer);
+	if (maxkb) {
+		int len = strlen(file);
+
+		file2 = malloc(len + 3);
+		if (!file2)
+			return NULL;
+
+		sprintf(file2, "%s.1", file);
+
+		fd2 = open(file2, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
+		if (fd2 < 0) {
+			close(fd);
+			unlink(file);
+			free(file2);
+			return NULL;
+		}
+	}
+
+	recorder = create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb, tfd);
 	if (!recorder) {
 		close(fd);
 		unlink(file);
+		if (fd2 != -1)
+			close(fd2);
+	}
+
+	if (fd2 != -1) {
+		/* Unlink file2, we need to add everything to file at the end */
+		unlink(file2);
+		free(file2);
 	}
 
 	return recorder;
@@ -248,7 +222,7 @@
 
 struct tracecmd_recorder *
 tracecmd_create_buffer_recorder_maxkb(const char *file, int cpu, unsigned flags,
-				      const char *buffer, int maxkb)
+				      struct tracefs_instance *instance, int maxkb)
 {
 	struct tracecmd_recorder *recorder = NULL;
 	char *file2;
@@ -257,7 +231,7 @@
 	int fd2;
 
 	if (!maxkb)
-		return tracecmd_create_buffer_recorder(file, cpu, flags, buffer);
+		return tracecmd_create_buffer_recorder(file, cpu, flags, instance);
 
 	len = strlen(file);
 	file2 = malloc(len + 3);
@@ -274,7 +248,7 @@
 	if (fd2 < 0)
 		goto err;
 
-	recorder = tracecmd_create_buffer_recorder_fd2(fd, fd2, cpu, flags, buffer, maxkb);
+	recorder = tracecmd_create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb);
 	if (!recorder)
 		goto err2;
  out:
@@ -293,9 +267,9 @@
 
 struct tracecmd_recorder *
 tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags,
-				const char *buffer)
+				struct tracefs_instance *instance)
 {
-	return __tracecmd_create_buffer_recorder(file, cpu, flags, buffer);
+	return __tracecmd_create_buffer_recorder(file, cpu, flags, instance, -1, 0);
 }
 
 /**
@@ -308,55 +282,25 @@
  */
 struct tracecmd_recorder *
 tracecmd_create_recorder_virt(const char *file, int cpu, unsigned flags,
-			      int trace_fd)
+			      int trace_fd, int maxkb)
 {
-	struct tracecmd_recorder *recorder;
-
-	recorder = __tracecmd_create_buffer_recorder(file, cpu, flags, NULL);
-	if (recorder)
-		recorder->trace_fd = trace_fd;
-
-	return recorder;
+	return __tracecmd_create_buffer_recorder(file, cpu, flags, NULL, trace_fd, maxkb);
 }
 
 struct tracecmd_recorder *tracecmd_create_recorder_fd(int fd, int cpu, unsigned flags)
 {
-	const char *tracing;
-
-	tracing = tracefs_tracing_dir();
-	if (!tracing) {
-		errno = ENODEV;
-		return NULL;
-	}
-
-	return tracecmd_create_buffer_recorder_fd(fd, cpu, flags, tracing);
+	return tracecmd_create_buffer_recorder_fd(fd, cpu, flags, NULL);
 }
 
 struct tracecmd_recorder *tracecmd_create_recorder(const char *file, int cpu, unsigned flags)
 {
-	const char *tracing;
-
-	tracing = tracefs_tracing_dir();
-	if (!tracing) {
-		errno = ENODEV;
-		return NULL;
-	}
-
-	return tracecmd_create_buffer_recorder(file, cpu, flags, tracing);
+	return tracecmd_create_buffer_recorder(file, cpu, flags, NULL);
 }
 
 struct tracecmd_recorder *
 tracecmd_create_recorder_maxkb(const char *file, int cpu, unsigned flags, int maxkb)
 {
-	const char *tracing;
-
-	tracing = tracefs_tracing_dir();
-	if (!tracing) {
-		errno = ENODEV;
-		return NULL;
-	}
-
-	return tracecmd_create_buffer_recorder_maxkb(file, cpu, flags, tracing, maxkb);
+	return tracecmd_create_buffer_recorder_maxkb(file, cpu, flags, NULL, maxkb);
 }
 
 static inline void update_fd(struct tracecmd_recorder *recorder, int size)
@@ -369,8 +313,8 @@
 	recorder->count += size;
 
 	if (recorder->count >= recorder->page_size) {
+		recorder->pages += recorder->count / recorder->page_size;
 		recorder->count = 0;
-		recorder->pages++;
 	}
 
 	if (recorder->pages < recorder->max)
@@ -387,7 +331,7 @@
 		fd = recorder->fd1;
 
 	/* Zero out the new file we are writing to */
-	lseek64(fd, 0, SEEK_SET);
+	lseek(fd, 0, SEEK_SET);
 	ftruncate(fd, 0);
 
 	recorder->fd = fd;
@@ -397,101 +341,16 @@
  * Returns -1 on error.
  *          or bytes of data read.
  */
-static long splice_data(struct tracecmd_recorder *recorder)
-{
-	long total_read = 0;
-	long read;
-	long ret;
-
-	read = splice(recorder->trace_fd, NULL, recorder->brass[1], NULL,
-		      recorder->pipe_size, recorder->trace_fd_flags);
-	if (read < 0) {
-		if (errno == EAGAIN || errno == EINTR || errno == ENOTCONN)
-			return 0;
-
-		tracecmd_warning("recorder error in splice input");
-		return -1;
-	} else if (read == 0)
-		return 0;
-
- again:
-	ret = splice(recorder->brass[0], NULL, recorder->fd, NULL,
-		     read, recorder->fd_flags);
-	if (ret < 0) {
-		if (errno != EAGAIN && errno != EINTR) {
-			tracecmd_warning("recorder error in splice output");
-			return -1;
-		}
-		return total_read;
-	} else
-		update_fd(recorder, ret);
-	total_read = ret;
-	read -= ret;
-	if (read)
-		goto again;
-
-	return total_read;
-}
-
-/*
- * Returns -1 on error.
- *          or bytes of data read.
- */
-static long direct_splice_data(struct tracecmd_recorder *recorder)
-{
-	struct pollfd pfd = {
-		.fd = recorder->trace_fd,
-		.events = POLLIN,
-	};
-	long read;
-	int ret;
-
-	/*
-	 * splice(2) in Linux used to not check O_NONBLOCK flag of pipe file
-	 * descriptors before [1]. To avoid getting blocked in the splice(2)
-	 * call below after the user had requested to stop tracing, we poll(2)
-	 * here. This poll() is not necessary on newer kernels.
-	 *
-	 * [1] https://github.com/torvalds/linux/commit/ee5e001196d1345b8fee25925ff5f1d67936081e
-	 */
-	ret = poll(&pfd, 1, POLL_TIMEOUT_MS);
-	if (ret < 0)
-		return -1;
-
-	if (!(pfd.revents | POLLIN))
-		return 0;
-
-	read = splice(recorder->trace_fd, NULL, recorder->fd, NULL,
-		      recorder->pipe_size, recorder->fd_flags);
-	if (read < 0) {
-		if (errno == EAGAIN || errno == EINTR || errno == ENOTCONN)
-			return 0;
-
-		tracecmd_warning("recorder error in splice input");
-		return -1;
-	}
-
-	return read;
-}
-
-/*
- * Returns -1 on error.
- *          or bytes of data read.
- */
 static long read_data(struct tracecmd_recorder *recorder)
 {
-	char buf[recorder->page_size];
+	bool nonblock = recorder->stop;
+	char buf[recorder->subbuf_size];
 	long left;
 	long r, w;
 
-	r = read(recorder->trace_fd, buf, recorder->page_size);
-	if (r < 0) {
-		if (errno == EAGAIN || errno == EINTR || errno == ENOTCONN)
-			return 0;
-
-		tracecmd_warning("recorder error in read input");
-		return -1;
-	}
+	r = tracefs_cpu_read(recorder->tcpu, buf, nonblock);
+	if (r < 0)
+		return r;
 
 	left = r;
 	do {
@@ -508,49 +367,60 @@
 	return r;
 }
 
+/*
+ * Returns -1 on error.
+ *          or bytes of data read.
+ */
+static long direct_splice_data(struct tracecmd_recorder *recorder)
+{
+	bool nonblock = recorder->stop;
+	return tracefs_cpu_pipe(recorder->tcpu, recorder->fd, nonblock);
+}
+
 static long move_data(struct tracecmd_recorder *recorder)
 {
+	bool nonblock = recorder->stop;
+	long ret;
+
 	if (recorder->flags & TRACECMD_RECORD_NOSPLICE)
 		return read_data(recorder);
 
 	if (recorder->flags & TRACECMD_RECORD_NOBRASS)
 		return direct_splice_data(recorder);
 
-	return splice_data(recorder);
+	ret = tracefs_cpu_write(recorder->tcpu, recorder->fd, nonblock);
+	if (ret > 0)
+		update_fd(recorder, ret);
+	return ret;
 }
 
-long tracecmd_flush_recording(struct tracecmd_recorder *recorder)
+long tracecmd_flush_recording(struct tracecmd_recorder *recorder, bool finish)
 {
-	char buf[recorder->page_size];
+	char buf[recorder->subbuf_size];
 	long total = 0;
 	long wrote = 0;
 	long ret;
 
+	if (!recorder)
+		return 0;
+
+	if (!finish)
+		return tracefs_cpu_flush_write(recorder->tcpu, recorder->fd);
+
 	set_nonblock(recorder);
 
 	do {
-		ret = move_data(recorder);
-		if (ret < 0)
-			return ret;
-		total += ret;
-	} while (ret);
-
-	/* splice only reads full pages */
-	do {
-		ret = read(recorder->trace_fd, buf, recorder->page_size);
-		if (ret > 0) {
-			write(recorder->fd, buf, ret);
+		ret = tracefs_cpu_flush_write(recorder->tcpu, recorder->fd);
+		if (ret > 0)
 			wrote += ret;
-		}
-
 	} while (ret > 0);
 
 	/* Make sure we finish off with a page size boundary */
-	wrote &= recorder->page_size - 1;
+	wrote &= recorder->subbuf_size - 1;
 	if (wrote) {
-		memset(buf, 0, recorder->page_size);
-		write(recorder->fd, buf, recorder->page_size - wrote);
-		total += recorder->page_size;
+		memset(buf, 0, recorder->subbuf_size);
+		write(recorder->fd, buf, recorder->subbuf_size - wrote);
+		total += recorder->subbuf_size;
 	}
 
 	return total;
@@ -575,14 +445,20 @@
 		read = 0;
 		do {
 			ret = move_data(recorder);
-			if (ret < 0)
+			if (ret < 0) {
+				if (errno == EINTR)
+					continue;
+				if ((recorder->flags & TRACECMD_RECORD_POLL) &&
+				    errno == EAGAIN)
+					continue;
 				return ret;
+			}
 			read += ret;
-		} while (ret);
+		} while (ret > 0 && !recorder->stop);
 	} while (!recorder->stop);
 
 	/* Flush out the rest */
-	ret = tracecmd_flush_recording(recorder);
+	ret = tracecmd_flush_recording(recorder, true);
 
 	if (ret < 0)
 		return ret;
@@ -590,12 +466,12 @@
 	return 0;
 }
 
-void tracecmd_stop_recording(struct tracecmd_recorder *recorder)
+int tracecmd_stop_recording(struct tracecmd_recorder *recorder)
 {
 	if (!recorder)
-		return;
-
-	set_nonblock(recorder);
+		return -1;
 
 	recorder->stop = 1;
+
+	return set_nonblock(recorder);
 }
diff --git a/lib/trace-cmd/trace-timesync-kvm.c b/lib/trace-cmd/trace-timesync-kvm.c
index 12a22d4..bbef8b6 100644
--- a/lib/trace-cmd/trace-timesync-kvm.c
+++ b/lib/trace-cmd/trace-timesync-kvm.c
@@ -10,13 +10,13 @@
 #include <sys/stat.h>
 #include <dirent.h>
 #include <ctype.h>
+#include <limits.h>
 
 #include "trace-cmd.h"
 #include "trace-cmd-private.h"
 #include "tracefs.h"
 #include "trace-tsync-local.h"
 
-#define KVM_DEBUG_FS "/sys/kernel/debug/kvm"
 #define KVM_DEBUG_OFFSET_FILE	"tsc-offset"
 #define KVM_DEBUG_SCALING_FILE	"tsc-scaling-ratio"
 #define KVM_DEBUG_FRACTION_FILE	"tsc-scaling-ratio-frac-bits"
@@ -34,15 +34,20 @@
 #define KVM_ACCURACY	0
 #define KVM_NAME	"kvm"
 
+struct kvm_clock_files {
+	int		vcpu;
+	char		*offsets;
+	char		*scalings;
+	char		*frac;
+};
+
 struct kvm_clock_sync {
-	int vcpu_count;
-	char **vcpu_offsets;
-	char **vcpu_scalings;
-	char **vcpu_frac;
-	int marker_fd;
-	struct tep_handle *tep;
-	int raw_id;
-	unsigned long long ts;
+	int			vcpu_count;
+	int			marker_fd;
+	struct kvm_clock_files	*clock_files;
+	struct tep_handle	*tep;
+	int			raw_id;
+	unsigned long long	ts;
 };
 
 struct kvm_clock_offset_msg {
@@ -73,24 +78,29 @@
 	return 0;
 }
 
+/*
+ * Returns true if both scaling and fraction exist or both do
+ * not exist. false if one exists without the other or if there
+ * is a memory error.
+ */
 static bool kvm_scaling_check_vm_cpu(char *vname, char *cpu)
 {
-	long long scaling, frac;
 	bool has_scaling = false;
 	bool has_frac = false;
+	struct stat st;
 	char *path;
 	int ret;
 
 	if (asprintf(&path, "%s/%s/%s", vname, cpu, KVM_DEBUG_SCALING_FILE) < 0)
 		return false;
-	ret = read_ll_from_file(path, &scaling);
+	ret = stat(path, &st);
 	free(path);
 	if (!ret)
 		has_scaling = true;
 
 	if (asprintf(&path, "%s/%s/%s", vname, cpu, KVM_DEBUG_FRACTION_FILE) < 0)
 		return false;
-	ret = read_ll_from_file(path, &frac);
+	ret = stat(path, &st);
 	free(path);
 	if (!ret)
 		has_frac = true;
@@ -101,67 +111,112 @@
 	return true;
 }
 
+static const char *kvm_debug_dir(void)
+{
+	const char *debugfs;
+	static char *kvm_dir;
+
+	if (kvm_dir)
+		return kvm_dir;
+
+	debugfs = tracefs_debug_dir();
+	if (!debugfs)
+		return NULL;
+
+	if (asprintf(&kvm_dir, "%s/kvm", debugfs) < 0)
+		return NULL;
+
+	return kvm_dir;
+}
+
+/*
+ * Returns true if a VCPU exists with a tsc-offset file and that
+ * the scaling files for ratio and fraction both exist or both
+ * do not exist. False if there is no VM with a tsc-offset or
+ * there is only one of the two scaling files, or there's a
+ * memory issue.
+ */
 static bool kvm_scaling_check_vm(char *name)
 {
 	struct dirent *entry;
+	const char *kvm;
 	char *vdir;
 	DIR *dir;
+	bool valid = false;
 
-	if (asprintf(&vdir, "%s/%s", KVM_DEBUG_FS, name) < 0)
-		return true;
+	kvm = kvm_debug_dir();
+	if (!kvm)
+		return false;
+
+	if (asprintf(&vdir, "%s/%s", kvm, name) < 0)
+		return false;
 
 	dir = opendir(vdir);
 	if (!dir) {
 		free(vdir);
-		return true;
+		return false;
 	}
 	while ((entry = readdir(dir))) {
-		if (entry->d_type == DT_DIR && !strncmp(entry->d_name, "vcpu", 4) &&
-		    !kvm_scaling_check_vm_cpu(vdir, entry->d_name))
-			break;
+		if (entry->d_type == DT_DIR && !strncmp(entry->d_name, "vcpu", 4)) {
+			if (!kvm_scaling_check_vm_cpu(vdir, entry->d_name))
+				break;
+			valid = true;
+		}
 	}
 
 	closedir(dir);
 	free(vdir);
-	return entry == NULL;
+	return valid && entry == NULL;
 }
+
+/*
+ * Returns true if all VMs have a tsc-offset file and that
+ * the scaling files for ratio and fraction both exist or both
+ * do not exist. False if a VM with a tsc-offset or there is only
+ * one of the two scaling files, or no VM exists or there's a memory issue.
+ */
 static bool kvm_scaling_check(void)
 {
 	struct dirent *entry;
+	const char *kvm;
 	DIR *dir;
+	bool valid = false;
 
-	dir = opendir(KVM_DEBUG_FS);
+	kvm = kvm_debug_dir();
+	if (!kvm)
+		return false;
+
+	dir = opendir(kvm);
 	if (!dir)
 		return true;
 
 	while ((entry = readdir(dir))) {
-		if (entry->d_type == DT_DIR && isdigit(entry->d_name[0]) &&
-		    !kvm_scaling_check_vm(entry->d_name))
-			break;
+		if (entry->d_type == DT_DIR && isdigit(entry->d_name[0])) {
+			if (!kvm_scaling_check_vm(entry->d_name))
+				break;
+			valid = true;
+		}
 	}
 	closedir(dir);
-	return entry == NULL;
+	return valid && entry == NULL;
 }
 
 static bool kvm_support_check(bool guest)
 {
-	struct stat st;
-	int ret;
+	const char *kvm;
 
+	/* The kvm files are only in the host so we can ignore guests */
 	if (guest)
 		return true;
 
-	ret = stat(KVM_DEBUG_FS, &st);
-	if (ret < 0)
-		return false;
-
-	if (!S_ISDIR(st.st_mode))
+	kvm = kvm_debug_dir();
+	if (!kvm)
 		return false;
 
 	return kvm_scaling_check();
 }
 
-static int kvm_open_vcpu_dir(struct kvm_clock_sync *kvm, int cpu, char *dir_str)
+static int kvm_open_vcpu_dir(struct kvm_clock_sync *kvm, int i, char *dir_str)
 {
 	struct dirent *entry;
 	char path[PATH_MAX];
@@ -175,21 +230,21 @@
 			if (!strcmp(entry->d_name, KVM_DEBUG_OFFSET_FILE)) {
 				snprintf(path, sizeof(path), "%s/%s",
 					 dir_str, entry->d_name);
-				kvm->vcpu_offsets[cpu] = strdup(path);
+				kvm->clock_files[i].offsets = strdup(path);
 			}
 			if (!strcmp(entry->d_name, KVM_DEBUG_SCALING_FILE)) {
 				snprintf(path, sizeof(path), "%s/%s",
 					 dir_str, entry->d_name);
-				kvm->vcpu_scalings[cpu] = strdup(path);
+				kvm->clock_files[i].scalings = strdup(path);
 			}
 			if (!strcmp(entry->d_name, KVM_DEBUG_FRACTION_FILE)) {
 				snprintf(path, sizeof(path), "%s/%s",
 					 dir_str, entry->d_name);
-				kvm->vcpu_frac[cpu] = strdup(path);
+				kvm->clock_files[i].frac = strdup(path);
 			}
 		}
 	}
-	if (!kvm->vcpu_offsets[cpu])
+	if (!kvm->clock_files[i].offsets)
 		goto error;
 	closedir(dir);
 	return 0;
@@ -197,15 +252,25 @@
 error:
 	if (dir)
 		closedir(dir);
-	free(kvm->vcpu_offsets[cpu]);
-	kvm->vcpu_offsets[cpu] = NULL;
-	free(kvm->vcpu_scalings[cpu]);
-	kvm->vcpu_scalings[cpu] = NULL;
-	free(kvm->vcpu_frac[cpu]);
-	kvm->vcpu_frac[cpu] = NULL;
+	free(kvm->clock_files[i].offsets);
+	kvm->clock_files[i].offsets = NULL;
+	free(kvm->clock_files[i].scalings);
+	kvm->clock_files[i].scalings = NULL;
+	free(kvm->clock_files[i].frac);
+	kvm->clock_files[i].frac = NULL;
 	return -1;
 }
 
+static int cmp_clock(const void *A, const void *B)
+{
+	const struct kvm_clock_files *a = A;
+	const struct kvm_clock_files *b = B;
+
+	if (a->vcpu < b->vcpu)
+		return -1;
+	return a->vcpu > b->vcpu;
+}
+
 static int kvm_open_debug_files(struct kvm_clock_sync *kvm, int pid)
 {
 	char *vm_dir_str = NULL;
@@ -216,7 +281,7 @@
 	DIR *dir;
 	int i;
 
-	dir = opendir(KVM_DEBUG_FS);
+	dir = opendir(kvm_debug_dir());
 	if (!dir)
 		goto error;
 	if (asprintf(&pid_str, "%d-", pid) <= 0)
@@ -225,7 +290,7 @@
 		if (!(entry->d_type == DT_DIR &&
 		    !strncmp(entry->d_name, pid_str, strlen(pid_str))))
 			continue;
-		asprintf(&vm_dir_str, "%s/%s", KVM_DEBUG_FS, entry->d_name);
+		asprintf(&vm_dir_str, "%s/%s", kvm_debug_dir(), entry->d_name);
 		break;
 	}
 	closedir(dir);
@@ -235,21 +300,25 @@
 	dir = opendir(vm_dir_str);
 	if (!dir)
 		goto error;
+	i = 0;
 	while ((entry = readdir(dir))) {
 		if (!(entry->d_type == DT_DIR &&
 		    !strncmp(entry->d_name, KVM_DEBUG_VCPU_DIR, strlen(KVM_DEBUG_VCPU_DIR))))
 			continue;
-		vcpu =  strtol(entry->d_name + strlen(KVM_DEBUG_VCPU_DIR), NULL, 10);
-		if (vcpu < 0 || vcpu >= kvm->vcpu_count)
-			continue;
+		if (i == kvm->vcpu_count)
+			goto error;
+		vcpu = strtol(entry->d_name + strlen(KVM_DEBUG_VCPU_DIR), NULL, 10);
+		kvm->clock_files[i].vcpu = vcpu;
 		snprintf(path, sizeof(path), "%s/%s", vm_dir_str, entry->d_name);
-		if (kvm_open_vcpu_dir(kvm, vcpu, path) < 0)
+		if (kvm_open_vcpu_dir(kvm, i, path) < 0)
 			goto error;
+		i++;
 	}
-	for (i = 0; i < kvm->vcpu_count; i++) {
-		if (!kvm->vcpu_offsets[i])
-			goto error;
-	}
+	if (i < kvm->vcpu_count)
+		goto error;
+
+	qsort(kvm->clock_files, kvm->vcpu_count, sizeof(*kvm->clock_files), cmp_clock);
+
 	closedir(dir);
 	free(pid_str);
 	free(vm_dir_str);
@@ -266,19 +335,15 @@
 				    struct kvm_clock_sync *kvm)
 {
 	kvm->vcpu_count = tsync->vcpu_count;
-	kvm->vcpu_offsets = calloc(kvm->vcpu_count, sizeof(char *));
-	kvm->vcpu_scalings = calloc(kvm->vcpu_count, sizeof(char *));
-	kvm->vcpu_frac = calloc(kvm->vcpu_count, sizeof(char *));
-	if (!kvm->vcpu_offsets || !kvm->vcpu_scalings || !kvm->vcpu_frac)
+	kvm->clock_files = calloc(kvm->vcpu_count, sizeof(*kvm->clock_files));
+	if (!kvm->clock_files)
 		goto error;
 	if (kvm_open_debug_files(kvm, tsync->guest_pid) < 0)
 		goto error;
 	return 0;
 
 error:
-	free(kvm->vcpu_offsets);
-	free(kvm->vcpu_scalings);
-	free(kvm->vcpu_frac);
+	free(kvm->clock_files);
 	return -1;
 }
 
@@ -365,12 +430,9 @@
 		kvm = (struct kvm_clock_sync *)clock_context->proto_data;
 	if (kvm) {
 		for (i = 0; i < kvm->vcpu_count; i++) {
-			free(kvm->vcpu_offsets[i]);
-			kvm->vcpu_offsets[i] = NULL;
-			free(kvm->vcpu_scalings[i]);
-			kvm->vcpu_scalings[i] = NULL;
-			free(kvm->vcpu_frac[i]);
-			kvm->vcpu_frac[i] = NULL;
+			free(kvm->clock_files[i].offsets);
+			free(kvm->clock_files[i].scalings);
+			free(kvm->clock_files[i].frac);
 		}
 		if (kvm->tep)
 			tep_free(kvm->tep);
@@ -400,23 +462,23 @@
 	clock_context = (struct clock_sync_context *)tsync->context;
 	if (clock_context)
 		kvm = (struct kvm_clock_sync *)clock_context->proto_data;
-	if (!kvm || !kvm->vcpu_offsets || !kvm->vcpu_offsets[0])
+	if (!kvm || !kvm->clock_files || !kvm->clock_files[0].offsets)
 		return -1;
 	if (cpu >= kvm->vcpu_count)
 		return -1;
-	ret = read_ll_from_file(kvm->vcpu_offsets[cpu], &kvm_offset);
+	ret = read_ll_from_file(kvm->clock_files[cpu].offsets, &kvm_offset);
 	if (ret < 0)
 		return -1;
 
-	if (kvm->vcpu_scalings && kvm->vcpu_scalings[cpu]) {
-		read_ll_from_file(kvm->vcpu_scalings[cpu], &kvm_scaling);
+	if (kvm->clock_files[cpu].scalings) {
+		read_ll_from_file(kvm->clock_files[cpu].scalings, &kvm_scaling);
 		if (kvm_scaling == KVM_SCALING_AMD_DEFAULT ||
 		    kvm_scaling == KVM_SCALING_INTEL_DEFAULT)
 			kvm_scaling = 1;
 	}
 
-	if (kvm->vcpu_frac && kvm->vcpu_frac[cpu] && kvm_scaling != 1)
-		ret = read_ll_from_file(kvm->vcpu_frac[cpu], &kvm_frac);
+	if (kvm->clock_files[cpu].frac && kvm_scaling != 1)
+		ret = read_ll_from_file(kvm->clock_files[cpu].frac, &kvm_frac);
 	msg = (char *)&packet;
 	size = sizeof(packet);
 	ret = tracecmd_msg_recv_time_sync(tsync->msg_handle,
diff --git a/lib/trace-cmd/trace-timesync-ptp.c b/lib/trace-cmd/trace-timesync-ptp.c
index 20e6e6f..402edeb 100644
--- a/lib/trace-cmd/trace-timesync-ptp.c
+++ b/lib/trace-cmd/trace-timesync-ptp.c
@@ -608,8 +608,10 @@
 					  sync_proto, &sync_msg,
 					  &size, (char **)&results);
 	if (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) ||
-	    sync_msg != PTP_SYNC_PKT_PROBES || size == 0 || results == NULL)
+	    sync_msg != PTP_SYNC_PKT_PROBES || size == 0 || results == NULL) {
+		free(results);
 		return -1;
+	}
 
 	ntoh_ptp_results(results);
 	if (ptp->flags & PTP_FLAG_USE_MARKER)
diff --git a/lib/trace-cmd/trace-timesync.c b/lib/trace-cmd/trace-timesync.c
index cc44af3..6ee4e64 100644
--- a/lib/trace-cmd/trace-timesync.c
+++ b/lib/trace-cmd/trace-timesync.c
@@ -240,6 +240,22 @@
 }
 
 /**
+ * tracecmd_tsync_get_proto - return the appropriate synchronization protocol
+ * @protos: The list of synchronization protocols to choose from
+ * @clock: The clock that is being used (or NULL for unknown).
+ *
+ * Retuns pointer to a protocol name, that can be used with the peer, or NULL
+ *	  in case there is no match with supported protocols.
+ *	  The returned string MUST NOT be freed by the caller
+ */
+__hidden const char *
+tracecmd_tsync_get_proto(const struct tracecmd_tsync_protos *protos,
+			 const char *clock, enum tracecmd_time_sync_role role)
+{
+	return tsync_proto_select(protos, clock, role);
+}
+
+/**
  * tracecmd_tsync_proto_getall - Returns list of all supported
  *				 time sync protocols
  * @protos: return, allocated list of time sync protocol names,
@@ -274,7 +290,7 @@
 		goto error;
 	plist->names = calloc(count, sizeof(char *));
 	if (!plist->names)
-		return -1;
+		goto error;
 
 	for (i = 0, proto = tsync_proto_list; proto && i < (count - 1); proto = proto->next) {
 		if (!(proto->roles & role))
@@ -366,8 +382,7 @@
 	tracefs_instance_free(inst);
 }
 
-static int clock_context_init(struct tracecmd_time_sync *tsync,
-			      struct tsync_proto **proto, bool guest)
+static int clock_context_init(struct tracecmd_time_sync *tsync, bool guest)
 {
 	struct clock_sync_context *clock = NULL;
 	struct tsync_proto *protocol;
@@ -401,7 +416,7 @@
 	if (protocol->clock_sync_init && protocol->clock_sync_init(tsync) < 0)
 		goto error;
 
-	*proto = protocol;
+	tsync->proto = protocol;
 
 	return 0;
 error:
@@ -523,9 +538,9 @@
 	CPU_FREE(mask);
 }
 
-static int tsync_send(struct tracecmd_time_sync *tsync,
-		      struct tsync_proto *proto, unsigned int cpu)
+static int tsync_send(struct tracecmd_time_sync *tsync, unsigned int cpu)
 {
+	struct tsync_proto *proto = tsync->proto;
 	cpu_set_t *old_set = NULL;
 	long long timestamp = 0;
 	long long scaling = 0;
@@ -545,16 +560,11 @@
 {
 	char protocol[TRACECMD_TSYNC_PNAME_LENGTH];
 	struct tsync_probe_request_msg probe;
-	struct tsync_proto *proto;
 	unsigned int command;
 	unsigned int size;
 	char *msg;
 	int ret;
 
-	clock_context_init(tsync, &proto, true);
-	if (!tsync->context)
-		return;
-
 	msg = (char *)&probe;
 	size = sizeof(probe);
 	while (true) {
@@ -566,7 +576,7 @@
 		if (ret || strncmp(protocol, TRACECMD_TSYNC_PROTO_NONE, TRACECMD_TSYNC_PNAME_LENGTH) ||
 		    command != TRACECMD_TIME_SYNC_CMD_PROBE)
 			break;
-		ret = tsync_send(tsync, proto, probe.cpu);
+		ret = tsync_send(tsync, probe.cpu);
 		if (ret)
 			break;
 	}
@@ -614,8 +624,9 @@
 }
 
 static int tsync_get_sample(struct tracecmd_time_sync *tsync, unsigned int cpu,
-			    struct tsync_proto *proto, int array_step)
+			    int array_step)
 {
+	struct tsync_proto *proto = tsync->proto;
 	struct clock_sync_context *clock;
 	long long timestamp = 0;
 	long long scaling = 0;
@@ -656,19 +667,12 @@
 {
 	struct tsync_probe_request_msg probe;
 	int ts_array_size = CLOCK_TS_ARRAY;
-	struct tsync_proto *proto;
 	struct timespec timeout;
 	bool first = true;
 	bool end = false;
 	int ret;
 	int i;
 
-	clock_context_init(tsync, &proto, false);
-	if (!tsync->context) {
-		pthread_barrier_wait(&tsync->first_sync);
-		return -1;
-	}
-
 	if (tsync->loop_interval > 0 &&
 	    tsync->loop_interval < (CLOCK_TS_ARRAY * 1000))
 		ts_array_size = (CLOCK_TS_ARRAY * 1000) / tsync->loop_interval;
@@ -681,7 +685,7 @@
 							  TRACECMD_TSYNC_PROTO_NONE,
 							  TRACECMD_TIME_SYNC_CMD_PROBE,
 							  sizeof(probe), (char *)&probe);
-			ret = tsync_get_sample(tsync, i, proto, ts_array_size);
+			ret = tsync_get_sample(tsync, i, ts_array_size);
 			if (ret)
 				break;
 		}
@@ -777,6 +781,10 @@
 	pthread_attr_init(&attrib);
 	pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_JOINABLE);
 
+	clock_context_init(tsync, false);
+	if (!tsync->context)
+		goto error;
+
 	ret = pthread_create(&tsync->thread, &attrib, tsync_host_thread, tsync);
 	if (ret)
 		goto error;
@@ -827,7 +835,7 @@
 	int i, j;
 	int ret = -1;
 
-	if (!tsync->vcpu_count)
+	if (!tsync || !tsync->vcpu_count)
 		return -1;
 	vcount = 3 + (5 * tsync->vcpu_count);
 	vector = calloc(vcount, sizeof(struct iovec));
@@ -921,34 +929,15 @@
 static void *tsync_agent_thread(void *data)
 {
 	struct tracecmd_time_sync *tsync = data;
-	long ret = 0;
-	int sd;
-
-	while (true) {
-		tracecmd_debug("Listening on fd:%d\n", tsync->msg_handle->fd);
-		sd = accept(tsync->msg_handle->fd, NULL, NULL);
-		tracecmd_debug("Accepted fd:%d\n", sd);
-		if (sd < 0) {
-			if (errno == EINTR)
-				continue;
-			ret = -1;
-			goto out;
-		}
-		break;
-	}
-	close(tsync->msg_handle->fd);
-	tsync->msg_handle->fd = sd;
 
 	tsync_with_host(tsync);
-
-out:
-	pthread_exit((void *)ret);
+	pthread_exit(NULL);
 }
 
 /**
  * tracecmd_tsync_with_host - Synchronize timestamps with host
  * @fd: File descriptor connecting with the host
- * @tsync_protos: List of tsync protocols, supported by the host
+ * @proto: The selected protocol
  * @clock: Trace clock, used for that session
  * @port: returned, VSOCKET port, on which the guest listens for tsync requests
  * @remote_id: Identifier to uniquely identify the remote host
@@ -961,25 +950,19 @@
  * until tracecmd_tsync_with_host_stop() is called.
  */
 struct tracecmd_time_sync *
-tracecmd_tsync_with_host(int fd,
-			 const struct tracecmd_tsync_protos *tsync_protos,
-			 const char *clock, int remote_id, int local_id)
+tracecmd_tsync_with_host(int fd, const char *proto, const char *clock,
+			 int remote_id, int local_id)
 {
 	struct tracecmd_time_sync *tsync;
 	cpu_set_t *pin_mask = NULL;
 	pthread_attr_t attrib;
 	size_t mask_size = 0;
-	const char *proto;
 	int ret;
 
 	tsync = calloc(1, sizeof(struct tracecmd_time_sync));
 	if (!tsync)
 		return NULL;
 
-	proto = tsync_proto_select(tsync_protos, clock,
-				   TRACECMD_TIME_SYNC_ROLE_GUEST);
-	if (!proto)
-		goto error;
 	tsync->proto_name = strdup(proto);
 	tsync->msg_handle = tracecmd_msg_handle_alloc(fd, 0);
 	if (clock)
@@ -992,6 +975,10 @@
 	tsync->vcpu_count = tracecmd_count_cpus();
 	pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_JOINABLE);
 
+	clock_context_init(tsync, true);
+	if (!tsync->context)
+		goto error;
+
 	ret = pthread_create(&tsync->thread, &attrib, tsync_agent_thread, tsync);
 	if (ret) {
 		pthread_attr_destroy(&attrib);
@@ -1033,25 +1020,3 @@
 {
 	return pthread_join(tsync->thread, NULL);
 }
-
-/**
- * tracecmd_tsync_get_selected_proto - Return the seleceted time sync protocol
- * @tsync: Time sync context, representing a running time sync session
- * @selected_proto: return, name of the selected time sync protocol for this session
- *
- * Returns 0 on success, or -1 in case of an error.
- *
- */
-int tracecmd_tsync_get_selected_proto(struct tracecmd_time_sync *tsync,
-				      char **selected_proto)
-{
-	if (!tsync)
-		return -1;
-
-	if (selected_proto) {
-		if (!tsync->proto_name)
-			return -1;
-		(*selected_proto) = strdup(tsync->proto_name);
-	}
-	return 0;
-}
diff --git a/lib/trace-cmd/trace-util.c b/lib/trace-cmd/trace-util.c
index 9564c81..37caab4 100644
--- a/lib/trace-cmd/trace-util.c
+++ b/lib/trace-cmd/trace-util.c
@@ -30,7 +30,8 @@
 #define PROC_STACK_FILE "/proc/sys/kernel/stack_tracer_enabled"
 
 static bool debug;
-static int log_level = TEP_LOG_INFO;
+static bool notimeout;
+static int log_level = TEP_LOG_WARNING;
 static FILE *logfp;
 
 const static struct {
@@ -110,21 +111,28 @@
 	return debug;
 }
 
-void tracecmd_parse_cmdlines(struct tep_handle *pevent,
-			     char *file, int size __maybe_unused)
+/**
+ * tracecmd_set_notimeout - Do not timeout waiting for responses
+ * @set_notimeout: True or false to set notimeout mode.
+ *
+ * If @set_notimeout is true, then the library will not fail waiting for
+ * responses. This is useful when running the code under gdb.
+ * Note, if debug is set, then this makes no difference as it will always
+ * not timeout.
+ */
+void tracecmd_set_notimeout(bool set_notimeout)
 {
-	char *comm;
-	char *line;
-	char *next = NULL;
-	int pid;
+	notimeout = set_notimeout;
+}
 
-	line = strtok_r(file, "\n", &next);
-	while (line) {
-		sscanf(line, "%d %m[^\n]s", &pid, &comm);
-		tep_register_comm(pevent, comm, pid);
-		free(comm);
-		line = strtok_r(NULL, "\n", &next);
-	}
+/**
+ * tracecmd_get_notimeout - Get setting of notimeout of tracecmd library
+ * Returns true, if the tracecmd library has notimeout set.
+ *
+ */
+bool tracecmd_get_notimeout(void)
+{
+	return notimeout || debug;
 }
 
 void tracecmd_parse_proc_kallsyms(struct tep_handle *pevent,
@@ -254,11 +262,11 @@
 	if (!ptr)
 		goto out_free;
 
+	pdata->files = ptr;
 	ptr[pdata->index] = strdup(name);
 	if (!ptr[pdata->index])
 		goto out_free;
 
-	pdata->files = ptr;
 	pdata->index++;
 	pdata->files[pdata->index] = NULL;
 	return;
@@ -368,8 +376,6 @@
 void tracecmd_set_loglevel(enum tep_loglevel level)
 {
 	log_level = level;
-	tracefs_set_loglevel(level);
-	tep_set_loglevel(level);
 }
 
 void __weak tracecmd_warning(const char *fmt, ...)
@@ -430,7 +436,6 @@
 #define LOG_BUF_SIZE 1024
 static void __plog(const char *prefix, const char *fmt, va_list ap, FILE *fp)
 {
-	static int newline = 1;
 	char buf[LOG_BUF_SIZE];
 	int r;
 
@@ -440,11 +445,7 @@
 		r = LOG_BUF_SIZE;
 
 	if (logfp) {
-		if (newline)
-			fprintf(logfp, "[%d]%s%.*s", getpid(), prefix, r, buf);
-		else
-			fprintf(logfp, "[%d]%s%.*s", getpid(), prefix, r, buf);
-		newline = buf[r - 1] == '\n';
+		fprintf(logfp, "[%d]%s%.*s", getpid(), prefix, r, buf);
 		fflush(logfp);
 		return;
 	}
@@ -534,6 +535,7 @@
 
 	buf[n] = 0;
 
+	errno = 0;
 	num = strtol(buf, NULL, 10);
 
 	/* Check for various possible errors */
diff --git a/make-trace-cmd.sh b/make-trace-cmd.sh
index 31f3259..c16edf2 100755
--- a/make-trace-cmd.sh
+++ b/make-trace-cmd.sh
@@ -4,7 +4,7 @@
 	echo
 	echo 'Error: No $INSTALL_PATH defined'
 	echo
-	echo "   usage: [PREFIX=prefix][BUILD_PATH=/path/to/build] INSTALL_PATH=/path/to/install make-trace-cmd.sh install|install_libs|clean|uninstall"
+	echo "   usage: [PREFIX=prefix][BUILD_PATH=/path/to/build][CFLAGS=custom-cflags] INSTALL_PATH=/path/to/install make-trace-cmd.sh install|install_libs|clean|uninstall"
 	echo
 	echo "     Used to create a self contained directory to copy to other machines."
 	echo
@@ -36,4 +36,8 @@
 	WITH_PATH="--with-path=$INSTALL_PATH$PKG_PATH"
 fi
 
-PKG_CONFIG_PATH="$INSTALL_PATH/$PKG_PATH" PKG_CONFIG="pkg-config $WITH_PATH --define-variable=prefix=$INSTALL_PATH/$PREFIX" CFLAGS="-g -Wall -I$INSTALL_PATH/$PREFIX/include" make DESTDIR=$INSTALL_PATH  $O_PATH prefix=$PREFIX $@
+if [ -z "$CFLAGS" ]; then
+    CFLAGS="-g -Wall"
+fi
+
+PKG_CONFIG_PATH="$INSTALL_PATH/$PKG_PATH" PKG_CONFIG="pkg-config $WITH_PATH --define-variable=prefix=$INSTALL_PATH/$PREFIX" CFLAGS="-I$INSTALL_PATH/$PREFIX/include $CFLAGS" make DESTDIR=$INSTALL_PATH  $O_PATH prefix=$PREFIX $@
diff --git a/meson-vcs-tag.sh b/meson-vcs-tag.sh
new file mode 100755
index 0000000..8ce6924
--- /dev/null
+++ b/meson-vcs-tag.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eu
+set -o pipefail
+
+dir="${1:?}"
+fallback="${2:?}"
+
+# Apparently git describe has a bug where it always considers the work-tree
+# dirty when invoked with --git-dir (even though 'git status' is happy). Work
+# around this issue by cd-ing to the source directory.
+cd "$dir"
+# Check that we have either .git/ (a normal clone) or a .git file (a work-tree)
+# and that we don't get confused if a tarball is extracted in a higher-level
+# git repository.
+[ -e .git ] && git describe --abbrev=7 --dirty=+ 2>/dev/null | sed 's/^v//' || echo "$fallback"
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..4773f4a
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,152 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+project(
+    'trace-cmd', ['c'],
+    meson_version: '>= 0.50.0',
+    license: 'GPL-2.0',
+    version: '3.3.1',
+    default_options: [
+        'c_std=gnu99',
+        'buildtype=debug',
+        'default_library=both',
+        'prefix=/usr/local',
+        'warning_level=1'])
+
+cc = meson.get_compiler('c')
+
+prefixdir = get_option('prefix')
+datadir = join_paths(prefixdir, get_option('datadir'))
+bindir = join_paths(prefixdir, get_option('bindir'))
+mandir = join_paths(prefixdir, get_option('mandir'))
+htmldir = join_paths(prefixdir, get_option('htmldir'))
+
+conf = configuration_data()
+
+libtraceevent_dep = dependency('libtraceevent', version: '>= 1.5.0', required: true)
+libtracefs_dep = dependency('libtracefs', version: '>= 1.8.0', required: true)
+
+threads_dep = dependency('threads', required: true)
+dl_dep = cc.find_library('dl', required : false)
+
+zlib_dep = dependency('zlib', required: false)
+conf.set('HAVE_ZLIB', zlib_dep.found(), description: 'Is zlib avialable?')
+
+libzstd_dep = dependency('libzstd', version: '>= 1.4.0', required: false)
+conf.set('HAVE_ZSTD', libzstd_dep.found(), description: 'Is libzstd available?')
+
+cunit_dep = dependency('cunit', required : false)
+
+vsock_defined = get_option('vsock') and cc.has_header('linux/vm_sockets.h')
+conf.set('VSOCK', vsock_defined, description: 'Is vsock available?')
+
+perf_defined = cc.has_header('linux/perf_event.h')
+conf.set('PERF', perf_defined, description: 'Is perf available?')
+
+have_ptrace = get_option('ptrace') and cc.compiles(
+    '''
+    #include <stdio.h>
+    #include <sys/ptrace.h>
+
+    int main (void)
+    {
+            int ret;
+            ret = ptrace(PTRACE_ATTACH, 0, NULL, 0);
+            ptrace(PTRACE_TRACEME, 0, NULL, 0);
+            ptrace(PTRACE_GETSIGINFO, 0, NULL, NULL);
+            ptrace(PTRACE_GETEVENTMSG, 0, NULL, NULL);
+            ptrace(PTRACE_SETOPTIONS, NULL, NULL,
+                           PTRACE_O_TRACEFORK |
+                           PTRACE_O_TRACEVFORK |
+                           PTRACE_O_TRACECLONE |
+                           PTRACE_O_TRACEEXIT);
+            ptrace(PTRACE_CONT, NULL, NULL, 0);
+            ptrace(PTRACE_DETACH, 0, NULL, NULL);
+            ptrace(PTRACE_SETOPTIONS, 0, NULL,
+                   PTRACE_O_TRACEFORK |
+                   PTRACE_O_TRACEVFORK |
+                   PTRACE_O_TRACECLONE |
+                   PTRACE_O_TRACEEXIT);
+            return ret;
+    }
+    ''',
+    name: 'ptrace')
+if not have_ptrace
+    conf.set10('NO_PTRACE', true, description: 'Is ptrace missing?')
+    conf.set('WARN_NO_PTRACE', true, description: 'Issue no ptrace warning?')
+endif
+
+audit_dep = dependency('audit', required: false)
+if not audit_dep.found()
+    conf.set10('NO_AUDIT', true, description: 'Is audit missing?')
+    conf.set('WARN_NO_AUDIT', true, description: 'Issue no audit warning?')
+endif
+
+config_h = configure_file(
+    output: 'config.h',
+    configuration: conf
+)
+
+version = meson.project_version().split('.')
+
+vconf = configuration_data()
+vconf.set('VERSION_CODE', version[0].to_int() * 256 + version[1].to_int())
+vconf.set('EXTRAVERSION', '"@0@"'.format(version[2]))
+vconf.set('FILE_VERSION', '""')
+vconf.set('VERSION_STRING', '"@0@"'.format(meson.project_version()))
+
+version_tag = get_option('version-tag')
+if version_tag != ''
+    vconf.set('VERSION_GIT', '"@0@"'.format(version_tag))
+else
+    r = run_command(
+        'meson-vcs-tag.sh',
+        meson.current_source_dir(),
+        meson.project_version(),
+        check: true)
+    vconf.set('VERSION_GIT', '"@0@"'.format(r.stdout().strip()))
+endif
+version_h = configure_file(
+    output: 'tc_version.h',
+    configuration: vconf)
+
+add_project_arguments(
+    [
+      '-D_GNU_SOURCE',
+      '-include', 'config.h',
+    ],
+    language : 'c')
+
+incdir = include_directories(['.', 'include'])
+
+# libtracecmd: trace-cmd currently depends on a statically linked
+# libtracecmd.  libtracecmd is sill very strongly coupled with
+# trace-cmd (or the other way around). To reduce the development setup
+# complexity we add some of the 'top meson.build' from libtracecmd and
+# make it simpler to use.
+library_version = '1.5.1'
+libtracecmd_standalone_build = false
+libtracecmd_ext_incdir = include_directories(
+    [
+        'include',
+        'include/trace-cmd',
+        'tracecmd/include'
+    ])
+subdir('lib/trace-cmd/include')
+subdir('lib/trace-cmd/include/private')
+subdir('lib/trace-cmd')
+
+# trace-cmd
+subdir('tracecmd')
+subdir('python')
+if cunit_dep.found()
+    subdir('utest')
+endif
+subdir('Documentation/trace-cmd')
+
+custom_target(
+    'docs',
+    output: 'docs',
+    depends: [html, man],
+    command: ['echo'])
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..2d5d745
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,23 @@
+# -*- mode: meson -*-
+# SPDX-License-Identifier: GPL-2.0
+
+option('version-tag', type : 'string',
+       description : 'override the git version string')
+option('vsock', type : 'boolean', value : true,
+       description : 'build with vsock support')
+option('ptrace', type : 'boolean', value : true,
+       description : 'build with ptrace support')
+option('htmldir', type : 'string', value : 'share/doc/trace-cmd-doc',
+       description : 'directory for HTML documentation')
+option('asciidoctor', type : 'boolean', value: false,
+       description : 'use asciidoctor instead of asciidoc')
+option('docbook-xls-172', type : 'boolean', value : false,
+       description : 'enable docbook XLS 172 workaround')
+option('asciidoc-no-roff', type : 'boolean', value : false,
+       description : 'enable no roff workaround')
+option('man-bold-literal', type : 'boolean', value : false,
+       description : 'enable bold literals')
+option('docbook-suppress-sp', type : 'boolean', value : false,
+       description : 'docbook suppress sp')
+option('python', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'Generate trac-cmd Python bindings')
diff --git a/python/Makefile b/python/Makefile
index 63f5736..926e64c 100644
--- a/python/Makefile
+++ b/python/Makefile
@@ -9,9 +9,12 @@
 endif
 
 ctracecmd.so: ctracecmd.i $(LIBTRACECMD_STATIC)
-	swig -Wall -python -noproxy -I$(src)/include/trace-cmd $(LIBTRACEEVENT_CFLAGS) ctracecmd.i
+	swig -Wall -python -noproxy \
+		-I$(src)/include/trace-cmd -I$(src)/lib/trace-cmd/include/private \
+		$(LIBTRACEEVENT_CFLAGS) ctracecmd.i
 	$(CC) -fpic -c $(CPPFLAGS) $(CFLAGS) $(PYTHON_INCLUDES)  ctracecmd_wrap.c
-	$(CC) --shared $(LIBTRACECMD_STATIC) $(LDFLAGS) ctracecmd_wrap.o -o ctracecmd.so $(TRACE_LIBS)
+	$(CC) --shared $(LIBTRACECMD_STATIC) $(LDFLAGS) $(LIBZSTD_LDLAGS) $(ZLIB_LDLAGS) \
+		ctracecmd_wrap.o -o ctracecmd.so $(TRACE_LIBS)
 
 $(PYTHON_SO_INSTALL): %.install : %.so force
 	$(Q)$(call do_install_data,$<,$(python_dir_SQ))
diff --git a/python/ctracecmd.i b/python/ctracecmd.i
index 6d0179e..3856460 100644
--- a/python/ctracecmd.i
+++ b/python/ctracecmd.i
@@ -15,6 +15,7 @@
 
 %{
 #include "trace-cmd.h"
+#include "trace-cmd-private-python.h"
 #include "event-parse.h"
 #include "event-utils.h"
 #include <Python.h>
@@ -176,14 +177,14 @@
 				strnlen((char *)r->data + f->offset, f->size));
 }
 
-static PyObject *py_format_get_keys(struct tep_event *ef)
+static PyObject *py_format_get_keys(struct tep_event *ef, bool common_keys)
 {
 	PyObject *list;
 	struct tep_format_field *f;
 
 	list = PyList_New(0);
 
-	for (f = ef->format.fields; f; f = f->next) {
+	for (f = common_keys ? ef->format.common_fields : ef->format.fields; f; f = f->next) {
 		if (PyList_Append(list, PyUnicode_FromString(f->name))) {
 			Py_DECREF(list);
 			return NULL;
@@ -214,7 +215,7 @@
 		SWIG_NewPointerObj(SWIG_as_voidptr(event),
 				   SWIGTYPE_p_tep_event, 0));
 
-	result = PyEval_CallObject(context, arglist);
+	result = PyObject_Call(context, arglist, NULL);
 	Py_XDECREF(arglist);
 	if (result && result != Py_None) {
 		if (!PyInt_Check(result)) {
@@ -239,6 +240,8 @@
 
 %ignore trace_seq_vprintf;
 %ignore vpr_stat;
+%ignore tep_plugin_kvm_get_func;
+%ignore tep_plugin_kvm_put_func;
 
 /* SWIG can't grok these, define them to nothing */
 #define __trace
@@ -246,5 +249,6 @@
 #define __thread
 
 %include "trace-cmd.h"
+%include "trace-cmd-private-python.h"
 %include <trace-seq.h>
 %include <event-parse.h>
diff --git a/python/meson.build b/python/meson.build
new file mode 100644
index 0000000..d009489
--- /dev/null
+++ b/python/meson.build
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+want_python = get_option('python')
+if want_python != 'false'
+    python3 = import('python').find_installation('python3')
+    py3_dep = python3.dependency(required: want_python == 'true')
+    swig = find_program('swig', required: want_python == 'true')
+    header_found = cc.has_header('Python.h', dependencies: py3_dep)
+    have_python_support = py3_dep.found() and swig.found() and header_found
+else
+    have_python_support = false
+endif
+
+if have_python_support
+    pymod_swig = custom_target(
+        'ctracecmd.py',
+        input:   ['ctracecmd.i'],
+        output:  ['ctracecmd.py', 'ctracecmd_wrap.c'],
+        command: [
+            swig,
+            '-python',
+            '-I' + meson.current_source_dir() + '/../include/trace-cmd',
+            '-I' + meson.current_source_dir() + '/../lib/trace-cmd/include/private',
+            '-I' + libtraceevent_dep.get_pkgconfig_variable('prefix') + '/include/traceevent',
+            '-o', '@OUTPUT1@',
+            '@INPUT0@'],
+        install: true,
+        install_dir: [ python3.get_install_dir(pure: false, subdir: 'trace-cmd'), false])
+
+    incdir_py = include_directories(['.', '../include/trace-cmd', '../lib/trace-cmd/include/private'])
+
+    pyctracecmd_clib = python3.extension_module(
+        '_ctracecmd',
+        pymod_swig[1],
+        dependencies : [libtraceevent_dep, libtracefs_dep, py3_dep],
+        include_directories: [incdir, incdir_py],
+        install: true,
+        subdir: 'trace-cmd')
+endif
diff --git a/python/tracecmd.py b/python/tracecmd.py
index 4d48157..6761f8a 100644
--- a/python/tracecmd.py
+++ b/python/tracecmd.py
@@ -18,9 +18,10 @@
 # 2009-Dec-17:	Initial version by Darren Hart <dvhltc@us.ibm.com>
 #
 
-from functools import update_wrapper
+from functools import cached_property
+from collections.abc import Mapping
+from itertools import chain
 from ctracecmd import *
-from UserDict import DictMixin
 
 """
 Python interface to the tracecmd library for parsing ftrace traces
@@ -33,25 +34,7 @@
 TODO: consider a complete class hierarchy of ftrace events...
 """
 
-def cached_property(func, name=None):
-    if name is None:
-        name = func.__name__
-    def _get(self):
-        try:
-            return self.__cached_properties[name]
-        except AttributeError:
-            self.__cached_properties = {}
-        except KeyError:
-            pass
-        value = func(self)
-        self.__cached_properties[name] = value
-        return value
-    update_wrapper(_get, func)
-    def _del(self):
-        self.__cached_properties.pop(name, None)
-    return property(_get, None, _del)
-
-class Event(object, DictMixin):
+class Event(Mapping):
     """
     This class can be used to access event data
     according to an event's record and format.
@@ -67,16 +50,30 @@
                 self.num_field("common_pid"), self.comm, self.type)
 
     def __del__(self):
-        free_record(self._record)
+        tracecmd_free_record(self._record)
 
     def __getitem__(self, n):
-        f = tep_find_field(self._format, n)
+        if n.startswith('common_'):
+            f = tep_find_common_field(self._format, n)
+        else:
+            f = tep_find_field(self._format, n)
         if f is None:
             raise KeyError("no field '%s'" % n)
         return Field(self._record, f)
 
+    def __iter__(self):
+        yield from chain(self.common_keys, self.keys)
+
+    def __len__(self):
+        return len(self.common_keys) + len(self.keys)
+
+    @cached_property
+    def common_keys(self):
+        return py_format_get_keys(self._format, True)
+
+    @cached_property
     def keys(self):
-        return py_format_get_keys(self._format)
+        return py_format_get_keys(self._format, False)
 
     @cached_property
     def comm(self):
@@ -88,7 +85,7 @@
 
     @cached_property
     def name(self):
-        return event_format_name_get(self._format)
+        return tep_event_name_get(self._format)
 
     @cached_property
     def pid(self):
@@ -182,15 +179,8 @@
     used to manage the trace and extract events from it.
     """
     def __init__(self, filename):
-        self._handle = tracecmd_alloc(filename)
-
-        if tracecmd_read_headers(self._handle):
-            raise FileFormatError("Invalid headers")
-
-        if tracecmd_init_data(self._handle):
-            raise FileFormatError("Failed to init data")
-
-        self._pevent = tracecmd_get_pevent(self._handle)
+        self._handle = tracecmd_open(filename, 0)
+        self._pevent = tracecmd_get_tep(self._handle)
 
     @cached_property
     def cpus(self):
@@ -242,8 +232,12 @@
 # Basic builtin test, execute module directly
 if __name__ == "__main__":
     t = Trace("trace.dat")
-    print("Trace contains data for %d cpus" % (t.cpus))
+    print(f"Trace contains data for {t.cpus} cpus, long has {t.long_size} bytes")
 
+    print("Peek the first event on CPU0")
+    print("\t%s" % (t.peek_event(0)))
+
+    print("Events by CPUs")
     for cpu in range(0, t.cpus):
         print("CPU %d" % (cpu))
         ev = t.read_event(cpu)
@@ -251,5 +245,10 @@
             print("\t%s" % (ev))
             ev = t.read_event(cpu)
 
+    t = Trace("trace.dat")
 
-
+    print("Events by time")
+    ev = t.read_next_event()
+    while ev:
+        print("\t%s" % (ev))
+        ev = t.read_next_event()
diff --git a/tracecmd/Makefile b/tracecmd/Makefile
index 0114948..d56d7a5 100644
--- a/tracecmd/Makefile
+++ b/tracecmd/Makefile
@@ -37,8 +37,11 @@
 TRACE_CMD_OBJS += trace-clear.o
 TRACE_CMD_OBJS += trace-vm.o
 TRACE_CMD_OBJS += trace-convert.o
+TRACE_CMD_OBJS += trace-attach.o
 TRACE_CMD_OBJS += trace-agent.o
+TRACE_CMD_OBJS += trace-tsync.o
 TRACE_CMD_OBJS += trace-setup-guest.o
+TRACE_CMD_OBJS += trace-sqlhist.o
 ifeq ($(VSOCK_DEFINED), 1)
 TRACE_CMD_OBJS += trace-vsock.o
 endif
@@ -48,13 +51,12 @@
 all_objs := $(sort $(ALL_OBJS))
 all_deps := $(all_objs:$(bdir)/%.o=$(bdir)/.%.d)
 
-CONFIG_INCLUDES =
-CONFIG_LIBS	= -lrt -lpthread $(TRACE_LIBS) $(LIBZSTD_LDLAGS)
-CONFIG_FLAGS	=
+LPTHREAD ?= -lpthread
+LRT ?= -lrt
 
-ifeq ($(ZLIB_INSTALLED), 1)
-CONFIG_LIBS += -lz
-endif
+CONFIG_INCLUDES =
+CONFIG_LIBS	= $(LRT) $(LPTHREAD) $(TRACE_LIBS) $(ZLIB_LDLAGS) $(LIBZSTD_LDLAGS)
+CONFIG_FLAGS	=
 
 all: $(TARGETS)
 
diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index e3fec13..1515fbb 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -11,6 +11,8 @@
 #include <ctype.h>	/* for isdigit() */
 #include <errno.h>
 #include <limits.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
 
 #include "trace-cmd-private.h"
 #include "event-utils.h"
@@ -40,6 +42,7 @@
 extern int silence_warnings;
 extern int show_status;
 
+void trace_set_loglevel(int level);
 int trace_set_verbose(char *level);
 
 enum port_type {
@@ -117,12 +120,17 @@
 
 void trace_dump(int argc, char **argv);
 
+void trace_attach(int argc, char **argv);
+
 void trace_convert(int argc, char **argv);
 
+void trace_sqlhist (int argc, char **argv);
+
 int trace_record_agent(struct tracecmd_msg_handle *msg_handle,
 		       int cpus, int *fds,
-		       int argc, char **argv, bool use_fifos,
-		       unsigned long long trace_id, const char *host);
+		       int argc, char **argv,
+		       bool use_fifos, struct tracecmd_time_sync *tsync,
+		       unsigned long long trace_id, int rcid, const char *host);
 
 struct hook_list;
 
@@ -135,7 +143,7 @@
 trace_stream_init(struct buffer_instance *instance, int cpu, int fd, int cpus,
 		  struct hook_list *hooks,
 		  tracecmd_handle_init_func handle_init, int global);
-int trace_stream_read(struct pid_record_data *pids, int nr_pids, struct timeval *tv);
+int trace_stream_read(struct pid_record_data *pids, int nr_pids, long sleep_us);
 
 void trace_show_data(struct tracecmd_input *handle, struct tep_record *record);
 
@@ -190,6 +198,7 @@
 	BUFFER_FL_HAS_CLOCK	= 1 << 4,
 	BUFFER_FL_TSC2NSEC	= 1 << 5,
 	BUFFER_FL_NETWORK	= 1 << 6,
+	BUFFER_FL_PROXY		= 1 << 7,
 };
 
 struct func_list {
@@ -230,6 +239,8 @@
 	unsigned long long	trace_id;
 	char			*cpumask;
 	char			*output_file;
+	const char		*temp_dir;
+	char			*temp_file;
 	struct event_list	*events;
 	struct event_list	**event_next;
 	bool			delete;
@@ -277,8 +288,13 @@
 	int			tracing_on_init_val;
 	int			tracing_on_fd;
 	int			buffer_size;
+	int			old_buffer_size;
+	int			subbuf_size;
+	int			old_subbuf_size;
 	int			cpu_count;
 
+	int			proxy_fd;
+
 	int			argc;
 	char			**argv;
 
@@ -305,7 +321,11 @@
 
 #define is_agent(instance)	((instance)->flags & BUFFER_FL_AGENT)
 #define is_guest(instance)	((instance)->flags & BUFFER_FL_GUEST)
+#define is_proxy(instance)	((instance)->flags & BUFFER_FL_PROXY)
 #define is_network(instance)	((instance)->flags & BUFFER_FL_NETWORK)
+#define is_proxy_server(instance)					\
+	((instance)->msg_handle &&					\
+	 (instance)->msg_handle->flags & TRACECMD_MSG_FL_PROXY)
 
 #define START_PORT_SEARCH 1500
 #define MAX_PORT_SEARCH 6000
@@ -328,6 +348,7 @@
 struct trace_guest {
 	struct tracefs_instance *instance;
 	char *name;
+	unsigned long long trace_id;
 	int cid;
 	int pid;
 	int cpu_max;
@@ -339,6 +360,19 @@
 void read_qemu_guests(void);
 int get_guest_pid(unsigned int guest_cid);
 int get_guest_vcpu_pid(unsigned int guest_cid, unsigned int guest_vcpu);
+void trace_add_guest_info(struct tracecmd_output *handle, struct buffer_instance *instance);
+
+struct tracecmd_time_sync *
+trace_tsync_as_host(int fd, unsigned long long trace_id,
+		    int loop_interval, int guest_id,
+		    int guest_cpus, const char *proto_name,
+		    const char *clock);
+
+struct tracecmd_time_sync *
+trace_tsync_as_guest(int fd, const char *tsync_proto, const char *clock,
+	       unsigned int remote_id, unsigned int local_id);
+
+char *strparse(char *str, char delim, char **save);
 
 /* moved from trace-cmd.h */
 void tracecmd_remove_instances(void);
@@ -434,4 +468,16 @@
 
 bool trace_tsc2nsec_is_supported(void);
 
+void make_pid_name(char *buf, const char *pidfile_basename);
+void remove_pid_file(const char *pidfile_basename);
+void make_pid_file(const char *pidfile_basename);
+
+static inline void set_tcp_no_delay(int sockfd, int socktype)
+{
+	int flag = 1;
+
+	if (socktype == SOCK_STREAM)
+		setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
+}
+
 #endif /* __TRACE_LOCAL_H */
diff --git a/tracecmd/meson.build b/tracecmd/meson.build
new file mode 100644
index 0000000..a23376b
--- /dev/null
+++ b/tracecmd/meson.build
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+sources = [
+    'trace-agent.c',
+    'trace-attach.c',
+    'trace-check-events.c',
+    'trace-clear.c',
+    'trace-cmd.c',
+    'trace-convert.c',
+    'trace-dump.c',
+    'trace-hist.c',
+    'trace-list.c',
+    'trace-listen.c',
+    'trace-mem.c',
+    'trace-profile.c',
+    'trace-read.c',
+    'trace-record.c',
+    'trace-restore.c',
+    'trace-setup-guest.c',
+    'trace-show.c',
+    'trace-snapshot.c',
+    'trace-split.c',
+    'trace-stack.c',
+    'trace-stat.c',
+    'trace-stream.c',
+    'trace-tsync.c',
+    'trace-usage.c',
+    'trace-vm.c',
+    'trace-sqlhist.c',
+]
+
+if vsock_defined
+    sources += 'trace-vsock.c'
+endif
+
+trace_cmd_incdir = include_directories(['.', 'include'])
+
+executable(
+    'trace-cmd',
+    sources,
+    dependencies: [
+        libtraceevent_dep,
+        libtracefs_dep,
+        zlib_dep,
+        libzstd_dep,
+        audit_dep],
+    include_directories: [
+        incdir,
+        trace_cmd_incdir,
+        libtracecmd_incdir,
+        libtracecmd_private_incdir,
+        libtracecmd_ext_incdir],
+    link_with: [static_libtracecmd],
+    install: true,
+    install_dir: bindir)
+
+install_data(
+    'trace-cmd.bash',
+    install_dir: datadir + '/bash-completion/completions')
diff --git a/tracecmd/trace-agent.c b/tracecmd/trace-agent.c
index f0723a6..b6b44f5 100644
--- a/tracecmd/trace-agent.c
+++ b/tracecmd/trace-agent.c
@@ -22,6 +22,8 @@
 #include "trace-local.h"
 #include "trace-msg.h"
 
+#define GUEST_NAME	"::GUEST::"
+
 #define dprint(fmt, ...)	tracecmd_debug(fmt, ##__VA_ARGS__)
 
 static void make_vsocks(int nr, int *fds, unsigned int *ports)
@@ -122,17 +124,46 @@
 		tracecmd_debug("Could not print connection fd:%d\n", fd);
 }
 
-static void agent_handle(int sd, int nr_cpus, int page_size, const char *network)
+static int wait_for_connection(int fd)
+{
+	int sd;
+
+	if (fd < 0)
+		return -1;
+
+	while (true) {
+		tracecmd_debug("Listening on fd:%d\n", fd);
+		sd = accept(fd, NULL, NULL);
+		tracecmd_debug("Accepted fd:%d\n", sd);
+		if (sd < 0) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		break;
+	}
+	close(fd);
+	return sd;
+}
+
+static void agent_handle(int sd, int nr_cpus, int page_size,
+			 int cid, int rcid, const char *network)
 {
 	struct tracecmd_tsync_protos *tsync_protos = NULL;
 	struct tracecmd_time_sync *tsync = NULL;
 	struct tracecmd_msg_handle *msg_handle;
-	char *tsync_proto = NULL;
+	const char *tsync_proto = NULL;
+	struct trace_guest *guest;
+	unsigned long long peer_trace_id;
 	unsigned long long trace_id;
+	unsigned long flags = rcid >= 0 ? TRACECMD_MSG_FL_PROXY : 0;
+	enum tracecmd_time_sync_role tsync_role = TRACECMD_TIME_SYNC_ROLE_GUEST;
 	unsigned int remote_id;
 	unsigned int local_id;
 	unsigned int tsync_port = 0;
 	unsigned int *ports;
+	unsigned int client_cpus = 0;
+	unsigned int guests = 0;
 	char **argv = NULL;
 	int argc = 0;
 	bool use_fifos;
@@ -145,22 +176,38 @@
 	if (!fds || !ports)
 		die("Failed to allocate memory");
 
-	msg_handle = tracecmd_msg_handle_alloc(sd, 0);
+	msg_handle = tracecmd_msg_handle_alloc(sd, flags);
 	if (!msg_handle)
 		die("Failed to allocate message handle");
 
-	ret = tracecmd_msg_recv_trace_req(msg_handle, &argc, &argv,
-					  &use_fifos, &trace_id,
-					  &tsync_protos);
+	if (rcid >= 0) {
+		tsync_role = TRACECMD_TIME_SYNC_ROLE_HOST;
+		ret = tracecmd_msg_recv_trace_proxy(msg_handle, &argc, &argv,
+						    &use_fifos, &peer_trace_id,
+						    &tsync_protos,
+						    &client_cpus,
+						    &guests);
+		/* Update the guests peer_trace_id */
+		guest = trace_get_guest(rcid, NULL);
+		if (guest)
+			guest->trace_id = peer_trace_id;
+	} else {
+		ret = tracecmd_msg_recv_trace_req(msg_handle, &argc, &argv,
+						  &use_fifos, &peer_trace_id,
+						  &tsync_protos);
+	}
 	if (ret < 0)
 		die("Failed to receive trace request");
 
+	tsync_proto = tracecmd_tsync_get_proto(tsync_protos, get_clock(argc, argv),
+					       tsync_role);
+
 	if (use_fifos && open_agent_fifos(nr_cpus, fds))
 		use_fifos = false;
 
 	if (!use_fifos)
 		make_sockets(nr_cpus, fds, ports, network);
-	if (tsync_protos && tsync_protos->names) {
+	if (tsync_proto) {
 		if (network) {
 			/* For now just use something */
 			remote_id = 2;
@@ -183,18 +230,6 @@
 				fd = -1;
 			}
 		}
-		if (fd >= 0) {
-			tsync = tracecmd_tsync_with_host(fd, tsync_protos,
-							 get_clock(argc, argv),
-							 remote_id, local_id);
-		}
-		if (tsync) {
-			tracecmd_tsync_get_selected_proto(tsync, &tsync_proto);
-		} else {
-			warning("Failed to negotiate timestamps synchronization with the host");
-			if (fd >= 0)
-				close(fd);
-		}
 	}
 	trace_id = tracecmd_generate_traceid();
 	ret = tracecmd_msg_send_trace_resp(msg_handle, nr_cpus, page_size,
@@ -203,11 +238,28 @@
 	if (ret < 0)
 		die("Failed to send trace response");
 
+	if (tsync_proto) {
+		fd = wait_for_connection(fd);
+
+		if (rcid >= 0) {
+			tsync = trace_tsync_as_host(fd, trace_id, 0, rcid,
+						    client_cpus, tsync_proto,
+						    get_clock(argc, argv));
+		} else {
+			tsync = trace_tsync_as_guest(fd, tsync_proto,
+						     get_clock(argc, argv),
+						     remote_id, local_id);
+		}
+		if (!tsync)
+			close(fd);
+	}
+
 	trace_record_agent(msg_handle, nr_cpus, fds, argc, argv,
-			   use_fifos, trace_id, network);
+			   use_fifos, tsync, trace_id, rcid, network);
 
 	if (tsync) {
-		tracecmd_tsync_with_host_stop(tsync);
+		if (rcid < 0)
+			tracecmd_tsync_with_host_stop(tsync);
 		tracecmd_tsync_free(tsync);
 	}
 
@@ -249,14 +301,15 @@
 	return fork();
 }
 
-static void agent_serve(unsigned int port, bool do_daemon, const char *network)
+static void agent_serve(unsigned int port, bool do_daemon, int proxy_id,
+			const char *network)
 {
 	struct sockaddr_storage net_addr;
 	struct sockaddr *addr = NULL;
 	socklen_t *addr_len_p = NULL;
 	socklen_t addr_len = sizeof(net_addr);
 	int sd, cd, nr_cpus;
-	unsigned int cid;
+	unsigned int cid = -1, rcid = -1;
 	pid_t pid;
 
 	signal(SIGCHLD, handle_sigchld);
@@ -295,6 +348,21 @@
 				continue;
 			die("accept");
 		}
+		if (proxy_id >= 0) {
+			/* Only works with vsockets */
+			if (get_vsocket_params(cd, NULL, &rcid) < 0) {
+				dprint("Failed to find connected cid");
+				close(cd);
+				continue;
+			}
+			if (rcid != proxy_id) {
+				dprint("Cid %d does not match expected cid %d\n",
+				       rcid, proxy_id);
+				close(cd);
+				continue;
+			}
+		}
+
 		if (tracecmd_get_debug())
 			trace_print_connection(cd, network);
 
@@ -311,7 +379,7 @@
 		if (pid == 0) {
 			close(sd);
 			signal(SIGCHLD, SIG_DFL);
-			agent_handle(cd, nr_cpus, page_size, network);
+			agent_handle(cd, nr_cpus, page_size, cid, rcid, network);
 		}
 		if (pid > 0)
 			handler_pid = pid;
@@ -323,14 +391,17 @@
 
 enum {
 	OPT_verbose	= 254,
-	DO_DEBUG	= 255
+	OPT_debug	= 255,
+	OPT_notimeout	= 256,
 };
 
 void trace_agent(int argc, char **argv)
 {
+	struct trace_guest *guest;
 	bool do_daemon = false;
 	unsigned int port = TRACE_AGENT_DEFAULT_PORT;
 	const char *network = NULL;
+	int proxy_id = -1;
 
 	if (argc < 2)
 		usage(argv);
@@ -343,12 +414,13 @@
 		static struct option long_options[] = {
 			{"port", required_argument, NULL, 'p'},
 			{"help", no_argument, NULL, '?'},
-			{"debug", no_argument, NULL, DO_DEBUG},
+			{"debug", no_argument, NULL, OPT_debug},
+			{"notimeout", no_argument, NULL, OPT_notimeout},
 			{"verbose", optional_argument, NULL, OPT_verbose},
 			{NULL, 0, NULL, 0}
 		};
 
-		c = getopt_long(argc-1, argv+1, "+hp:DN:",
+		c = getopt_long(argc-1, argv+1, "+hp:DN:P:",
 				long_options, &option_index);
 		if (c == -1)
 			break;
@@ -361,13 +433,26 @@
 			break;
 		case 'p':
 			port = atoi(optarg);
+			if (proxy_id >= 0)
+				die("-N cannot be used with -P");
 			break;
 		case 'D':
 			do_daemon = true;
 			break;
-		case DO_DEBUG:
+		case 'P':
+			proxy_id = atoi(optarg);
+
+			guest = trace_get_guest(proxy_id, GUEST_NAME);
+			if (!guest)
+				die("Failed to allocate guest instance");
+
+			break;
+		case OPT_debug:
 			tracecmd_set_debug(true);
 			break;
+		case OPT_notimeout:
+			tracecmd_set_notimeout(true);
+			break;
 		case OPT_verbose:
 			if (trace_set_verbose(optarg) < 0)
 				die("invalid verbose level %s", optarg);
@@ -380,5 +465,5 @@
 	if (optind < argc-1)
 		usage(argv);
 
-	agent_serve(port, do_daemon, network);
+	agent_serve(port, do_daemon, proxy_id, network);
 }
diff --git a/tracecmd/trace-attach.c b/tracecmd/trace-attach.c
new file mode 100644
index 0000000..c20c753
--- /dev/null
+++ b/tracecmd/trace-attach.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Google Inc, Steven Rostedt <rostedt@goodmis.org>
+ *
+ */
+#include <stdlib.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <errno.h>
+
+#include "tracefs.h"
+#include "trace-local.h"
+
+struct timeshift_sample {
+	struct timeshift_sample *next;
+	long long		offset;
+	long long		scaling;
+	long long		timestamp;
+	long long		fract;
+};
+
+struct vcpu_pid {
+	struct vcpu_pid		*next;
+	int			pid;
+	int			cpu;
+};
+
+static unsigned int num_cpus;
+
+static void *vcpu_pids;
+
+static struct timeshift_sample *tshifts;
+static struct timeshift_sample **tshifts_next = &tshifts;
+
+static u64 set_value(const char *str, const char *type, u64 def)
+{
+	if (str && str[0] != '\0' && str[0] != '-' && !isdigit(str[0]))
+		die("Bad %s value", type);
+
+	if (str && str[0])
+		return strtoull(str, NULL, 0);
+
+	return def;
+}
+
+static void add_timeshift(char *shift)
+{
+	struct timeshift_sample *tshift;
+	char *timestamp_str;
+	char *offset_str;
+	char *scale_str;
+	char *fract_str;
+	char *saveptr;
+	u64 timestamp;
+	u64 offset;
+	u64 scale;
+	u64 fract;
+
+	offset_str = strparse(shift, ',', &saveptr);
+	scale_str = strparse(NULL, ',', &saveptr);
+	fract_str = strparse(NULL, ',', &saveptr);
+	timestamp_str = strparse(NULL, ',', &saveptr);
+
+	if (!offset_str)
+		die("Bad timeshift argument");
+
+	offset = set_value(offset_str, "offset", 0);
+	scale = set_value(scale_str, "scaling", 1);
+	fract = set_value(fract_str, "fraction", 0);
+	timestamp = set_value(timestamp_str, "timestamp", 0);
+
+	tshift = calloc(1, sizeof(*tshift));
+	if (!tshift)
+		die("Could not allocate timeshift");
+
+	*tshifts_next = tshift;
+	tshifts_next = &tshift->next;
+
+	tshift->offset = offset;
+	tshift->scaling = scale;
+	tshift->fract = fract;
+	tshift->timestamp = timestamp;
+}
+
+static void free_timeshifts(void)
+{
+	struct timeshift_sample *tshift;
+
+	while (tshifts) {
+		tshift = tshifts;
+		tshifts = tshift->next;
+		free(tshift);
+	}
+}
+
+static void add_vcpu_pid(const char *pid)
+{
+	struct vcpu_pid *vpid;
+
+	vpid = calloc(1, sizeof(*vpid));
+	vpid->pid = atoi(pid);
+	vpid->cpu = -1;
+	vpid->next = vcpu_pids;
+	vcpu_pids = vpid;
+}
+
+static void free_vcpu_pids(void)
+{
+	struct vcpu_pid *vpid;
+
+	while (vcpu_pids) {
+		vpid = vcpu_pids;
+		vcpu_pids = vpid->next;
+		free(vpid);
+	}
+}
+
+static inline int test_vcpu_id(struct tep_format_field **vcpu_id_field,
+				struct tep_event *event, struct tep_record *record)
+{
+	unsigned long long val;
+	struct vcpu_pid *vpid;
+	bool done = true;
+	int pid;
+	int cnt = 0;
+
+	if (!*vcpu_id_field) {
+		*vcpu_id_field = tep_find_field(event, "vcpu_id");
+		 if (!*vcpu_id_field)
+			 die("Could not find vcpu_id field");
+	}
+
+	pid = tep_data_pid(event->tep, record);
+	for (vpid = vcpu_pids; vpid; vpid = vpid->next) {
+		if (vpid->cpu < 0) {
+			done = false;
+		} else {
+			cnt++;
+			continue;
+		}
+		if (vpid->pid == pid)
+			break;
+	}
+
+	if (done || (num_cpus && cnt == num_cpus))
+		return -1;
+
+	if (!vpid)
+		return 0;
+
+	if (tep_read_number_field(*vcpu_id_field, record->data, &val))
+		die("Could not read data vcpu_id field");
+
+	vpid->cpu = (int)val;
+
+	return 0;
+}
+
+static int entry_callback(struct tracecmd_input *handle, struct tep_event *event,
+			  struct tep_record *record, int cpu, void *data)
+{
+	static struct tep_format_field *vcpu_id_field;
+
+	return test_vcpu_id(&vcpu_id_field, event, record);
+}
+
+static int exit_callback(struct tracecmd_input *handle, struct tep_event *event,
+			  struct tep_record *record, int cpu, void *data)
+{
+	static struct tep_format_field *vcpu_id_field;
+
+	return test_vcpu_id(&vcpu_id_field, event, record);
+}
+
+static int cmp_vcpus(const void *A, const void *B)
+{
+	struct vcpu_pid * const *a = A;
+	struct vcpu_pid * const *b = B;
+
+	if ((*a)->cpu < (*b)->cpu)
+		return -1;
+
+	return (*a)->cpu > (*b)->cpu;
+}
+
+static void update_end(char **end, void *data, int size, const char *stop)
+{
+	char *str = *end;
+
+	if (str + size > stop)
+		die("Error in calculating buffer size");
+
+	memcpy(str, data, size);
+	*end = str + size;
+}
+
+static void add_guest_to_host(struct tracecmd_output *host_ohandle,
+			      struct tracecmd_input *guest_ihandle)
+{
+	unsigned long long guest_id;
+	struct vcpu_pid **vcpu_list;
+	struct vcpu_pid *vpid;
+	char *name = ""; /* TODO, add name for guest */
+	char *stop;
+	char *buf;
+	char *end;
+	int cpus = 0;
+	int cpu;
+	int size;
+
+	guest_id = tracecmd_get_traceid(guest_ihandle);
+
+	for (vpid = vcpu_pids; vpid ; vpid = vpid->next) {
+		if (vpid->cpu < 0)
+			continue;
+		cpus++;
+	}
+
+	vcpu_list = calloc(cpus, sizeof(*vcpu_list));
+	if (!vcpu_list)
+		die("Could not allocate vCPU list");
+
+	cpus = 0;
+	for (vpid = vcpu_pids; vpid ; vpid = vpid->next) {
+		if (vpid->cpu < 0)
+			continue;
+		vcpu_list[cpus++] = vpid;
+	}
+
+	qsort(vcpu_list, cpus, sizeof(*vcpu_list), cmp_vcpus);
+
+	size = strlen(name) + 1;
+	size += sizeof(int) + sizeof(long long);
+	size += cpus * (sizeof(int) * 2);
+	buf = calloc(1, size);
+	if (!buf)
+		die("Failed allocation");
+
+	end = buf;
+	stop = buf + size;
+
+	/* TODO match endianess of existing file */
+	update_end(&end, name, strlen(name) + 1, stop);
+	update_end(&end, &guest_id, sizeof(guest_id), stop);
+	update_end(&end, &cpus, sizeof(cpus), stop);
+
+	for (cpu = 0; cpu < cpus; cpu++) {
+		int vcpu = vcpu_list[cpu]->cpu;
+		int pid = vcpu_list[cpu]->pid;
+		update_end(&end, &cpu, sizeof(vcpu), stop);
+		update_end(&end, &pid, sizeof(pid), stop);
+	}
+
+	if (tracecmd_add_option(host_ohandle, TRACECMD_OPTION_GUEST, size, buf) == NULL)
+		die("Failed to add GUEST option to host");
+
+	free(vcpu_list);
+	free(buf);
+}
+
+static void add_timeshift_to_guest(struct tracecmd_output *guest_ohandle,
+				   struct tracecmd_input *host_ihandle)
+{
+	struct timeshift_sample *tshift = tshifts;
+	struct timeshift_sample *last_tshift = NULL;
+	unsigned long long host_id;
+	char *stop;
+	char *end;
+	char *buf;
+	int proto;
+	int size = 0;
+	int cpus;
+	int cpu;
+
+	host_id = tracecmd_get_traceid(host_ihandle);
+	cpus = num_cpus;
+	proto = 0; /* For now we just have zero */
+
+	/*
+	 * option size is:
+	 *   trace id:		8 bytes
+	 *   protocol flags:	4 bytes
+	 *   CPU count:		4 bytes
+	 *
+	 * For each CPU:
+	 *   sample cnt:	4 bytes
+	 *   list of times:	8 bytes * sample cnt
+	 *   list of offsets:	8 bytes * sample cnt
+	 *   list of scaling:	8 bytes * sample cnt
+	 *
+	 * For each CPU:
+	 *    list of fract:	8 bytes * CPU count
+	 */
+	size = 8 + 4 + 4;
+
+	/* Include fraction bits here */
+	size += 8 * cpus;
+
+	/* We only have one sample per CPU (for now) */
+	size += (4 + 8 * 3) * cpus;
+
+	buf = calloc(1, size);
+	if (!buf)
+		die("Failed to allocate timeshift buffer");
+
+	end = buf;
+	stop = buf + size;
+	update_end(&end, &host_id, sizeof(host_id), stop);
+	update_end(&end, &proto, sizeof(proto), stop);
+	update_end(&end, &cpus, sizeof(cpus), stop);
+
+	for (cpu = 0; cpu < cpus; cpu++) {
+		struct timeshift_sample *tsample = tshift;
+		unsigned long long sample;
+		int cnt = 1;
+
+		if (!tsample)
+			tsample = last_tshift;
+
+		if (!tsample)
+			die("No samples given");
+
+		last_tshift = tsample;
+
+		update_end(&end, &cnt, sizeof(cnt), stop);
+		sample = tsample->timestamp;
+		update_end(&end, &sample, sizeof(sample), stop);
+
+		sample = tsample->offset;
+		update_end(&end, &sample, sizeof(sample), stop);
+
+		sample = tsample->scaling;
+		update_end(&end, &sample, sizeof(sample), stop);
+	}
+
+	tshift = tshifts;
+	last_tshift = NULL;
+
+	for (cpu = 0; cpu < cpus; cpu++) {
+		struct timeshift_sample *tsample = tshift;
+		unsigned long long sample;
+
+		if (!tsample)
+			tsample = last_tshift;
+		last_tshift = tsample;
+
+		sample = tsample->fract;
+
+		update_end(&end, &sample, sizeof(sample), stop);
+	}
+
+	if (tracecmd_add_option(guest_ohandle, TRACECMD_OPTION_TIME_SHIFT, size, buf) == NULL)
+		die("Failed to add TIME SHIFT option");
+
+	free(buf);
+}
+
+static void add_tsc2nsec_to_guest(struct tracecmd_output *guest_ohandle,
+				  struct tracecmd_input *host_ihandle)
+{
+	unsigned long long offset;
+	int mult;
+	int shift;
+	int ret;
+	char buf[sizeof(int) * 2 + sizeof(long long)];
+	char *stop;
+	char *end;
+	int size = sizeof(buf);
+
+	ret = tracecmd_get_tsc2nsec(host_ihandle, &mult, &shift, &offset);
+	if (ret < 0)
+		die("Host does not have tsc2nsec info");
+
+	end = buf;
+	stop = buf + size;
+	update_end(&end, &mult, sizeof(mult), stop);
+	update_end(&end, &shift, sizeof(shift), stop);
+	update_end(&end, &offset, sizeof(offset), stop);
+
+	if (tracecmd_add_option(guest_ohandle, TRACECMD_OPTION_TSC2NSEC, size, buf) == NULL)
+		die("Failed to add TSC2NSEC option");
+
+}
+
+static void map_cpus(struct tracecmd_input *handle)
+{
+	int entry_ret;
+	int exit_ret;
+
+	entry_ret = tracecmd_follow_event(handle, "kvm", "kvm_entry", entry_callback, NULL);
+	exit_ret = tracecmd_follow_event(handle, "kvm", "kvm_exit", exit_callback, NULL);
+
+	if (entry_ret < 0 && exit_ret < 0)
+		die("Host needs kvm_exit or kvm_entry events to attach");
+
+	tracecmd_iterate_events(handle, NULL, 0, NULL, NULL);
+}
+
+void trace_attach(int argc, char **argv)
+{
+	struct tracecmd_input *guest_ihandle;
+	struct tracecmd_input *host_ihandle;
+	struct tracecmd_output *guest_ohandle;
+	struct tracecmd_output *host_ohandle;
+	unsigned long long guest_id;
+	char *guest_file;
+	char *host_file;
+	int ret;
+	int fd;
+
+	for (;;) {
+		int c;
+
+		c = getopt(argc-1, argv+1, "c:s:h");
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			usage(argv);
+			break;
+		case 's':
+			add_timeshift(optarg);
+			break;
+		case 'c':
+			num_cpus = atoi(optarg);
+			break;
+		default:
+			usage(argv);
+		}
+	}
+
+	/* Account for "attach" */
+	optind++;
+
+	if ((argc - optind) < 3)
+		usage(argv);
+
+	host_file = argv[optind++];
+	guest_file = argv[optind++];
+
+	for (; optind < argc; optind++)
+		add_vcpu_pid(argv[optind]);
+
+
+	host_ihandle = tracecmd_open(host_file,TRACECMD_FL_LOAD_NO_PLUGINS );
+	guest_ihandle = tracecmd_open(guest_file,TRACECMD_FL_LOAD_NO_PLUGINS );
+
+	if (!host_ihandle)
+		die("Could not read %s\n", host_file);
+
+	if (!guest_ihandle)
+		die("Could not read %s\n", guest_file);
+
+	guest_id = tracecmd_get_traceid(guest_ihandle);
+	if (!guest_id)
+		die("Guest data file does not contain traceid");
+
+	map_cpus(host_ihandle);
+
+	ret = tracecmd_get_guest_cpumap(host_ihandle, guest_id,
+					NULL, NULL, NULL);
+	if (ret == 0) {
+		printf("Guest is already mapped in host (id=0x%llx) .. skipping ...\n",
+		       guest_id);
+	} else {
+
+		fd = open(host_file, O_RDWR);
+		if (fd < 0)
+			die("Could not write %s", host_file);
+
+		host_ohandle = tracecmd_get_output_handle_fd(fd);
+		if (!host_ohandle)
+			die("Error setting up %s for write", host_file);
+
+		add_guest_to_host(host_ohandle, guest_ihandle);
+		tracecmd_output_close(host_ohandle);
+	}
+
+	fd = open(guest_file, O_RDWR);
+	if (fd < 0)
+		die("Could not write %s", guest_file);
+
+	guest_ohandle = tracecmd_get_output_handle_fd(fd);
+	if (!guest_ohandle)
+		die("Error setting up %s for write", guest_file);
+
+	add_timeshift_to_guest(guest_ohandle, host_ihandle);
+	add_tsc2nsec_to_guest(guest_ohandle, host_ihandle);
+
+	tracecmd_output_close(guest_ohandle);
+
+	tracecmd_close(guest_ihandle);
+	tracecmd_close(host_ihandle);
+
+	free_timeshifts();
+	free_vcpu_pids();
+
+	return;
+}
diff --git a/tracecmd/trace-cmd.c b/tracecmd/trace-cmd.c
index 69800d2..21309f0 100644
--- a/tracecmd/trace-cmd.c
+++ b/tracecmd/trace-cmd.c
@@ -50,6 +50,27 @@
 	return data;
 }
 
+/* Same as strtok_r(), but allows empty tokens */
+char *strparse(char *str, char delim, char **save)
+{
+	char *next;
+
+	if (!str) {
+		str = *save;
+		if ((*save)[0] == '\0')
+			return NULL;
+	}
+
+	next = strchr(str, delim);
+	if (next) {
+		*next = '\0';
+		*save = next + 1;
+	} else {
+		*save = str + strlen(str);
+	}
+	return str;
+}
+
 void tracecmd_debug(const char *fmt, ...)
 {
 	va_list ap;
@@ -76,6 +97,13 @@
 	{ .id = TEP_LOG_ALL, .name = "all" },
 };
 
+void trace_set_loglevel(int level)
+{
+	tracecmd_set_loglevel(level);
+	tracefs_set_loglevel(level);
+	tep_set_loglevel(level);
+}
+
 int trace_set_verbose(char *level)
 {
 	int id;
@@ -89,7 +117,7 @@
 		if (id >= TEP_LOG_NONE) {
 			if (id > TEP_LOG_ALL)
 				id = TEP_LOG_ALL;
-			tracecmd_set_loglevel(id);
+			trace_set_loglevel(id);
 			return 0;
 		}
 	} else {
@@ -98,7 +126,7 @@
 
 		for (i = 0; i < size; i++) {
 			if (!strncmp(level, log_severity[i].name, strlen(log_severity[i].name))) {
-				tracecmd_set_loglevel(log_severity[i].id);
+				trace_set_loglevel(log_severity[i].id);
 				return 0;
 			}
 		}
@@ -149,7 +177,9 @@
 	{"list", trace_list},
 	{"help", trace_usage},
 	{"dump", trace_dump},
+	{"attach", trace_attach},
 	{"convert", trace_convert},
+	{"sqlhist", trace_sqlhist},
 	{"-h", trace_usage},
 };
 
diff --git a/tracecmd/trace-convert.c b/tracecmd/trace-convert.c
index 88935dc..e72a352 100644
--- a/tracecmd/trace-convert.c
+++ b/tracecmd/trace-convert.c
@@ -104,6 +104,8 @@
 		input_file = DEFAULT_INPUT_FILE;
 	if (!output_file)
 		usage(argv);
+	if (file_version >= FILE_VERSION_COMPRESSION && !compression)
+		compression = "any";
 
 	convert_file(input_file, output_file, file_version, compression);
 }
diff --git a/tracecmd/trace-dump.c b/tracecmd/trace-dump.c
index 22e3d87..11c1baf 100644
--- a/tracecmd/trace-dump.c
+++ b/tracecmd/trace-dump.c
@@ -101,7 +101,7 @@
 	if (read_compress)
 		return tracecmd_compress_lseek(compress, offset, whence);
 
-	return lseek64(fd, offset, whence);
+	return lseek(fd, offset, whence);
 }
 
 static int read_file_string(int fd, char *dst, int len)
@@ -446,7 +446,7 @@
 	const char *desc;
 	int desc_id;
 
-	offset = lseek64(fd, 0, SEEK_CUR);
+	offset = lseek(fd, 0, SEEK_CUR);
 	if (read_file_number(fd, &id, 2))
 		die("cannot read the section id");
 
@@ -500,13 +500,13 @@
 		return;
 	}
 
-	current = lseek64(fd, 0, SEEK_CUR);
-	if (lseek64(fd, offset, SEEK_SET) == (off_t)-1)
+	current = lseek(fd, 0, SEEK_CUR);
+	if (lseek(fd, offset, SEEK_SET) == (off_t)-1)
 		die("cannot goto buffer offset %lld", offset);
 
 	dump_section_header(fd, FLYRECORD, &flags);
 
-	if (lseek64(fd, current, SEEK_SET) == (off_t)-1)
+	if (lseek(fd, current, SEEK_SET) == (off_t)-1)
 		die("cannot go back to buffer option");
 
 	do_print(OPTIONS|FLYRECORD, "\t\t[Option BUFFER, %d bytes]\n", size);
@@ -773,7 +773,7 @@
 	unsigned short flags;
 
 	while (sec) {
-		if (lseek64(fd, sec->offset, SEEK_SET) == (off_t)-1)
+		if (lseek(fd, sec->offset, SEEK_SET) == (off_t)-1)
 			die("cannot goto option offset %lld", sec->offset);
 
 		dump_section_header(fd, sec->verbosity, &flags);
@@ -826,7 +826,7 @@
 	if (!offset)
 		return 0;
 
-	if (lseek64(fd, offset, SEEK_SET) == (off_t)-1)
+	if (lseek(fd, offset, SEEK_SET) == (off_t)-1)
 		die("cannot goto next options offset %lld", offset);
 
 	do_print(OPTIONS, "\n\n");
@@ -1006,7 +1006,7 @@
 		else if (strncmp(str, HEAD_FLYRECORD, 10) == 0)
 			dump_flyrecord(fd);
 		else {
-			lseek64(fd, -10, SEEK_CUR);
+			lseek(fd, -10, SEEK_CUR);
 			break;
 		}
 	}
@@ -1060,7 +1060,7 @@
 	unsigned short fl, id;
 	int desc_id;
 
-	offset = lseek64(fd, 0, SEEK_CUR);
+	offset = lseek(fd, 0, SEEK_CUR);
 	do {
 		if (read_file_number(fd, &id, 2))
 			break;
@@ -1074,7 +1074,7 @@
 			if ((fl & TRACECMD_SEC_FL_COMPRESS)) {
 				read_file_number(fd, &csize, 4);
 				read_file_number(fd, &rsize, 4);
-				lseek64(fd, -8, SEEK_CUR);
+				lseek(fd, -8, SEEK_CUR);
 				if (uncompress_block())
 					break;
 			} else {
@@ -1083,12 +1083,12 @@
 			read_metadata_strings(fd, rsize);
 			uncompress_reset();
 		} else {
-			if (lseek64(fd, size, SEEK_CUR) == (off_t)-1)
+			if (lseek(fd, size, SEEK_CUR) == (off_t)-1)
 				break;
 		}
 	} while (1);
 
-	if (lseek64(fd, offset, SEEK_SET) == (off_t)-1)
+	if (lseek(fd, offset, SEEK_SET) == (off_t)-1)
 		die("cannot restore the original file location");
 }
 
@@ -1102,9 +1102,9 @@
 	int desc_id;
 	const char *desc;
 
-	offset = lseek64(fd, 0, SEEK_CUR);
+	offset = lseek(fd, 0, SEEK_CUR);
 	do {
-		soffset = lseek64(fd, 0, SEEK_CUR);
+		soffset = lseek(fd, 0, SEEK_CUR);
 		if (read_file_number(fd, &id, 2))
 			break;
 
@@ -1149,11 +1149,11 @@
 				 id, soffset, desc, fl, size);
 		}
 
-		if (lseek64(fd, size, SEEK_CUR) == (off_t)-1)
+		if (lseek(fd, size, SEEK_CUR) == (off_t)-1)
 			break;
 	} while (1);
 
-	if (lseek64(fd, offset, SEEK_SET) == (off_t)-1)
+	if (lseek(fd, offset, SEEK_SET) == (off_t)-1)
 		die("cannot restore the original file location");
 
 	return count;
@@ -1170,7 +1170,7 @@
 	get_meta_strings(fd);
 	sections = walk_v7_sections(fd);
 
-	if (lseek64(fd, offset, SEEK_SET) == (off_t)-1)
+	if (lseek(fd, offset, SEEK_SET) == (off_t)-1)
 		die("cannot goto options offset %lld", offset);
 
 	dump_options(fd);
diff --git a/tracecmd/trace-hist.c b/tracecmd/trace-hist.c
index efb790a..47dcd6f 100644
--- a/tracecmd/trace-hist.c
+++ b/tracecmd/trace-hist.c
@@ -5,7 +5,6 @@
  * Several of the ideas in this file came from Arnaldo Carvalho de Melo's
  * work on the perf ui.
  */
-#define _LARGEFILE64_SOURCE
 #include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -1044,8 +1043,10 @@
 		die("can't open %s\n", input_file);
 
 	ret = tracecmd_read_headers(handle, 0);
-	if (ret)
+	if (ret) {
+		tracecmd_close(handle);
 		return;
+	}
 
 	ret = tracecmd_init_data(handle);
 	if (ret < 0)
diff --git a/tracecmd/trace-listen.c b/tracecmd/trace-listen.c
index 86d2b9e..e11bca0 100644
--- a/tracecmd/trace-listen.c
+++ b/tracecmd/trace-listen.c
@@ -3,7 +3,6 @@
  * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
  *
  */
-#define _LARGEFILE64_SOURCE
 #include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -35,6 +34,8 @@
 
 #define VAR_RUN_DIR		VAR_DIR_Q(VAR_DIR) "/run"
 
+#define LISTEN_PIDFILE		"trace-cmd-net.pid"
+
 static char *default_output_dir = ".";
 static char *output_dir;
 static char *default_output_file = "trace";
@@ -53,7 +54,8 @@
 #define pdie(fmt, ...)					\
 	do {						\
 		tracecmd_plog_error(fmt, ##__VA_ARGS__);\
-		remove_pid_file();			\
+		if (do_daemon)				\
+			remove_pid_file(LISTEN_PIDFILE);\
 		exit(-1);				\
 	} while (0)
 
@@ -127,21 +129,16 @@
 	done = true;
 }
 
-static void make_pid_name(int mode, char *buf)
+void make_pid_name(char *buf, const char *pidfile_basename)
 {
-	snprintf(buf, PATH_MAX, VAR_RUN_DIR "/trace-cmd-net.pid");
+	snprintf(buf, PATH_MAX, VAR_RUN_DIR "/%s", pidfile_basename);
 }
 
-static void remove_pid_file(void)
+void remove_pid_file(const char *pidfile_basename)
 {
 	char buf[PATH_MAX];
-	int mode = do_daemon;
 
-	if (!do_daemon)
-		return;
-
-	make_pid_name(mode, buf);
-
+	make_pid_name(buf, pidfile_basename);
 	unlink(buf);
 }
 
@@ -271,6 +268,7 @@
 		if (sd < 0)
 			continue;
 
+		set_tcp_no_delay(sd, rp->ai_socktype);
 		if (bind(sd, rp->ai_addr, rp->ai_addrlen) == 0)
 			break;
 
@@ -455,12 +453,14 @@
 				goto out;
 
 			ret = -EIO;
+			t = size;
+			s = 0;
 			do {
-				t = size;
-				s = 0;
 				s = read(fd, option+s, t);
-				if (s <= 0)
+				if (s <= 0) {
+					free(option);
 					goto out;
+				}
 				t -= s;
 				s = size - t;
 			} while (t);
@@ -699,8 +699,10 @@
 	ofd = create_client_file(node, port);
 
 	pid_array = create_all_readers(node, port, pagesize, msg_handle);
-	if (!pid_array)
+	if (!pid_array) {
+		close(ofd);
 		return -ENOMEM;
+	}
 
 	/* on signal stop this msg */
 	stop_msg_handle = msg_handle;
@@ -728,6 +730,7 @@
 					msg_handle->version < V3_PROTOCOL);
 
 	destroy_all_readers(cpus, pid_array, node, port);
+	close(ofd);
 
 	return ret;
 }
@@ -992,16 +995,12 @@
 	clean_up();
 }
 
-static void make_pid_file(void)
+void make_pid_file(const char *pidfile_basename)
 {
 	char buf[PATH_MAX];
-	int mode = do_daemon;
 	int fd;
 
-	if (!do_daemon)
-		return;
-
-	make_pid_name(mode, buf);
+	make_pid_name(buf, pidfile_basename);
 
 	fd = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0644);
 	if (fd < 0) {
@@ -1055,6 +1054,7 @@
 		if (sfd < 0)
 			continue;
 
+		set_tcp_no_delay(sfd, rp->ai_socktype);
 		if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
 			break;
 
@@ -1076,7 +1076,8 @@
 	if (!tracecmd_get_debug())
 		signal_setup(SIGCHLD, sigstub);
 
-	make_pid_file();
+	if (do_daemon)
+		make_pid_file(LISTEN_PIDFILE);
 
 	if (use_vsock)
 		sfd = get_vsock(port);
@@ -1091,7 +1092,8 @@
 
 	kill_clients();
 
-	remove_pid_file();
+	if (do_daemon)
+		remove_pid_file(LISTEN_PIDFILE);
 }
 
 static void start_daemon(void)
diff --git a/tracecmd/trace-mem.c b/tracecmd/trace-mem.c
index 25eb086..3e1ac9f 100644
--- a/tracecmd/trace-mem.c
+++ b/tracecmd/trace-mem.c
@@ -11,7 +11,6 @@
  * was written for the newer version. I decided to do some of it here
  * in C.
  */
-#define _LARGEFILE64_SOURCE
 #include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/tracecmd/trace-profile.c b/tracecmd/trace-profile.c
index 6a2cc3d..d12a0e5 100644
--- a/tracecmd/trace-profile.c
+++ b/tracecmd/trace-profile.c
@@ -5,7 +5,6 @@
  */
 
 /** FIXME: Convert numbers based on machine and file */
-#define _LARGEFILE64_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/tracecmd/trace-read.c b/tracecmd/trace-read.c
index df559d2..a5a0e10 100644
--- a/tracecmd/trace-read.c
+++ b/tracecmd/trace-read.c
@@ -3,7 +3,6 @@
  * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
  *
  */
-#define _LARGEFILE64_SOURCE
 #include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -41,32 +40,27 @@
 } *filter_strings;
 static struct filter_str **filter_next = &filter_strings;
 
-struct filter {
-	struct filter		*next;
-	struct tep_event_filter	*filter;
-};
-
 struct event_str {
 	struct event_str	*next;
 	const char		*event;
 };
 
+struct input_files;
+
 struct handle_list {
 	struct list_head	list;
 	struct tracecmd_input	*handle;
+	struct input_files	*input_file;
 	const char		*file;
 	int			cpus;
-	int			done;
-	struct tep_record	*record;
-	struct filter		*event_filters;
-	struct filter		*event_filter_out;
-	unsigned long long	*last_timestamp;
 };
 static struct list_head handle_list;
 
 struct input_files {
 	struct list_head	list;
 	const char		*file;
+	struct filter_str	*filter_str;
+	struct filter_str	**filter_str_next;
 	long long		tsoffset;
 	unsigned long long	ts2secs;
 };
@@ -98,7 +92,6 @@
 static int wakeup_id;
 static int wakeup_new_id;
 static int sched_id;
-static int stacktrace_id;
 
 static int profile;
 
@@ -157,7 +150,8 @@
 
 enum time_fmt {
 	TIME_FMT_LAT		= 1,
-	TIME_FMT_NORMAL		= 2,
+	TIME_FMT_NORMAL,
+	TIME_FMT_TS,
 };
 
 static const char *time_format(struct tracecmd_input *handle, enum time_fmt tf)
@@ -172,11 +166,11 @@
 	default:
 		if (tracecmd_get_flags(handle) & TRACECMD_FL_IN_USECS) {
 			if (tep_test_flag(tep, TEP_NSEC_OUTPUT))
-				return " %9.1d:";
+				return tf == TIME_FMT_NORMAL ? " %9.1d:" : "%9.1d";
 			else
-				return " %6.1000d:";
+				return tf == TIME_FMT_NORMAL ? " %6.1000d:" : "%6.1000d";
 		} else
-			return "%12d:";
+			return tf == TIME_FMT_NORMAL ? "%12d:" : "%12d";
 	}
 }
 
@@ -200,7 +194,7 @@
 #define TEST_READ_AT 0
 #if TEST_READ_AT
 #define DO_TEST
-static off64_t test_read_at_offset;
+static off_t test_read_at_offset;
 static int test_read_at_copy = 100;
 static int test_read_at_index;
 static void show_test(struct tracecmd_input *handle)
@@ -338,29 +332,42 @@
 }
 #endif
 
-static void add_input(const char *file)
+static void free_filter_strings(struct filter_str *filter_str)
+{
+	struct filter_str *filter;
+
+	while (filter_str) {
+		filter = filter_str;
+		filter_str = filter->next;
+		free(filter->filter);
+		free(filter);
+	}
+}
+
+static struct input_files *add_input(const char *file)
 {
 	struct input_files *item;
 
-	item = malloc(sizeof(*item));
+	item = calloc(1, sizeof(*item));
 	if (!item)
 		die("Failed to allocate for %s", file);
-	memset(item, 0, sizeof(*item));
 	item->file = file;
+	item->filter_str_next = &item->filter_str;
 	list_add_tail(&item->list, &input_files);
 	last_input_file = item;
+	return item;
 }
 
-static void add_handle(struct tracecmd_input *handle, const char *file)
+static void add_handle(struct tracecmd_input *handle, struct input_files *input_files)
 {
 	struct handle_list *item;
+	const char *file = input_files ? input_files->file : input_file;
 
-	item = malloc(sizeof(*item));
+	item = calloc(1, sizeof(*item));
 	if (!item)
 		die("Failed ot allocate for %s", file);
-	memset(item, 0, sizeof(*item));
 	item->handle = handle;
-	if (file) {
+	if (input_files) {
 		item->file = file + strlen(file);
 		/* we want just the base name */
 		while (item->file >= file && *item->file != '/')
@@ -368,6 +375,8 @@
 		item->file++;
 		if (strlen(item->file) > max_file_size)
 			max_file_size = strlen(item->file);
+
+		item->input_file = input_files;
 	}
 	list_add_tail(&item->list, &handle_list);
 }
@@ -379,6 +388,7 @@
 	while (!list_empty(&input_files)) {
 		item = container_of(input_files.next, struct input_files, list);
 		list_del(&item->list);
+		free_filter_strings(item->filter_str);
 		free(item);
 	}
 }
@@ -394,7 +404,7 @@
 	}
 }
 
-static void add_filter(const char *filter, int neg)
+static void add_filter(struct input_files *input_file, const char *filter, int neg)
 {
 	struct filter_str *ftr;
 
@@ -408,8 +418,13 @@
 	ftr->neg = neg;
 
 	/* must maintain order of command line */
-	*filter_next = ftr;
-	filter_next = &ftr->next;
+	if (input_file) {
+		*input_file->filter_str_next = ftr;
+		input_file->filter_str_next = &ftr->next;
+	} else {
+		*filter_next = ftr;
+		filter_next = &ftr->next;
+	}
 }
 
 static void __add_filter(struct pid_list **head, const char *arg)
@@ -467,7 +482,7 @@
 	if (!curr_filter) {
 		filter = malloc(len);
 		if (!filter)
-			die("Failed to allocate for filter %s", curr_filter);
+			die("Failed to allocate pid filter");
 		sprintf(filter, ".*:" FILTER_FMT, pid, pid, pid);
 	} else {
 		curr_len = strlen(curr_filter);
@@ -519,7 +534,8 @@
 	}
 }
 
-static void make_pid_filter(struct tracecmd_input *handle)
+static void make_pid_filter(struct tracecmd_input *handle,
+			    struct input_files *input_files)
 {
 	struct pid_list *list;
 	char *str = NULL;
@@ -534,7 +550,7 @@
 		str = append_pid_filter(str, list->pid);
 	}
 
-	add_filter(str, 0);
+	add_filter(input_files, str, 0);
 	free(str);
 
 	while (pid_list) {
@@ -546,53 +562,40 @@
 	}
 }
 
+static int __process_filters(struct tracecmd_input *handle,
+			     struct filter_str *filters)
+{
+	struct tracecmd_filter *trace_filter;
+
+	for (; filters; filters = filters->next) {
+		trace_filter = tracecmd_filter_add(handle,
+						   filters->filter,
+						   filters->neg);
+		if (!trace_filter)
+			die("Failed to create event filter: %s",
+			    filters->filter);
+	}
+
+	return !!filters;
+}
+
 static void process_filters(struct handle_list *handles)
 {
-	struct filter **filter_next = &handles->event_filters;
-	struct filter **filter_out_next = &handles->event_filter_out;
-	struct filter *event_filter;
-	struct filter_str *filter;
-	struct tep_handle *pevent;
-	char errstr[200];
-	int filters = 0;
-	int ret;
+	struct input_files *input_file = handles->input_file ?: last_input_file;
+	int added = 0;
 
-	pevent = tracecmd_get_tep(handles->handle);
+	make_pid_filter(handles->handle, input_file);
 
-	make_pid_filter(handles->handle);
+	/*
+	 * Order of filter processing matters. Apply the global filters
+	 * before file-specific ones.
+	 */
+	added += __process_filters(handles->handle, filter_strings);
+	if (input_file)
+		added += __process_filters(handles->handle,
+					   input_file->filter_str);
 
-	while (filter_strings) {
-		filter = filter_strings;
-		filter_strings = filter->next;
-
-		event_filter = malloc(sizeof(*event_filter));
-		if (!event_filter)
-			die("Failed to allocate for event filter");
-		event_filter->next = NULL;
-		event_filter->filter = tep_filter_alloc(pevent);
-		if (!event_filter->filter)
-			die("malloc");
-
-		ret = tep_filter_add_filter_str(event_filter->filter,
-						   filter->filter);
-		if (ret < 0) {
-			tep_strerror(pevent, ret, errstr, sizeof(errstr));
-			die("Error filtering: %s\n%s",
-			    filter->filter, errstr);
-		}
-
-		if (filter->neg) {
-			*filter_out_next = event_filter;
-			filter_out_next = &event_filter->next;
-		} else {
-			*filter_next = event_filter;
-			filter_next = &event_filter->next;
-		}
-		filters++;
-		free(filter->filter);
-		free(filter);
-	}
-	if (filters && test_filters_mode)
+	if (added && test_filters_mode)
 		exit(0);
 }
 
@@ -867,14 +870,13 @@
 			TEP_PRINT_PID,
 			TEP_PRINT_CPU);
 
-	if (latency_format) {
-		if (raw_format)
-			trace_seq_printf(&s, "-0x%x",
-					 tep_data_flags(pevent, record));
-		else
-			tep_print_event(pevent, &s, record, "%s",
-					TEP_PRINT_LATENCY);
-	}
+	if (raw_format)
+		trace_seq_printf(&s, "-0x%x",
+				 tep_data_flags(pevent, record));
+	else
+		tep_print_event(pevent, &s, record,
+				latency_format ? "%s" : " %s",
+				TEP_PRINT_LATENCY);
 
 	tep_print_event(pevent, &s, record, tfmt, TEP_PRINT_TIME);
 
@@ -968,10 +970,8 @@
 }
 
 static int
-test_filters(struct tep_handle *pevent, struct filter *event_filters,
-	     struct tep_record *record, int neg)
+test_filters(struct tep_handle *pevent, struct tep_record *record)
 {
-	int found = 0;
 	int ret = FILTER_NONE;
 	int flags;
 
@@ -983,19 +983,6 @@
 			return FILTER_MISS;
 	}
 
-	while (event_filters) {
-		ret = tep_filter_match(event_filters->filter, record);
-		switch (ret) {
-			case FILTER_NONE:
-			case FILTER_MATCH: 
-				found = 1;
-		}
-		/* We need to test all negative filters */
-		if (!neg && found)
-			break;
-		event_filters = event_filters->next;
-	}
-
 	return ret;
 }
 
@@ -1008,171 +995,9 @@
 	struct stack_info	*next;
 	struct handle_list	*handles;
 	struct stack_info_cpu	*cpus;
-	int			stacktrace_id;
 	int			nr_cpus;
 };
 
-static int
-test_stacktrace(struct handle_list *handles, struct tep_record *record,
-		int last_printed)
-{
-	static struct stack_info *infos;
-	struct stack_info *info;
-	struct stack_info_cpu *cpu_info;
-	struct handle_list *h;
-	struct tracecmd_input *handle;
-	struct tep_handle *pevent;
-	struct tep_event *event;
-	static int init;
-	int ret;
-	int id;
-
-	if (!init) {
-		init = 1;
-
-		list_for_each_entry(h, &handle_list, list) {
-			info = malloc(sizeof(*info));
-			if (!info)
-				die("Failed to allocate handle");
-			info->handles = h;
-			info->nr_cpus = tracecmd_cpus(h->handle);
-
-			info->cpus = malloc(sizeof(*info->cpus) * info->nr_cpus);
-			if (!info->cpus)
-				die("Failed to allocate for %d cpus", info->nr_cpus);
-			memset(info->cpus, 0, sizeof(*info->cpus));
-
-			pevent = tracecmd_get_tep(h->handle);
-			event = tep_find_event_by_name(pevent, "ftrace",
-						       "kernel_stack");
-			if (event)
-				info->stacktrace_id = event->id;
-			else
-				info->stacktrace_id = 0;
-
-			info->next = infos;
-			infos = info;
-		}
-
-
-	}
-
-	handle = handles->handle;
-	pevent = tracecmd_get_tep(handle);
-
-	for (info = infos; info; info = info->next)
-		if (info->handles == handles)
-			break;
-
-	if (!info->stacktrace_id)
-		return 0;
-
-	cpu_info = &info->cpus[record->cpu];
-
-	id = tep_data_type(pevent, record);
-
-	/*
-	 * Print the stack trace if the previous event was printed.
-	 * But do not print the stack trace if it is explicitly
-	 * being filtered out.
-	 */
-	if (id == info->stacktrace_id) {
-		ret = test_filters(pevent, handles->event_filter_out, record, 1);
-		if (ret != FILTER_MATCH)
-			return cpu_info->last_printed;
-		return 0;
-	}
-
-	cpu_info->last_printed = last_printed;
-	return 0;
-}
-
-static struct tep_record *get_next_record(struct handle_list *handles)
-{
-	struct tep_record *record;
-	struct tep_handle *pevent;
-	int found = 0;
-	int cpu;
-	int ret;
-
-	if (handles->record)
-		return handles->record;
-
-	if (handles->done)
-		return NULL;
-
-	pevent = tracecmd_get_tep(handles->handle);
-
-	do {
-		if (filter_cpus) {
-			long long last_stamp = -1;
-			struct tep_record *precord;
-			int first_record = 1;
-			int next_cpu = -1;
-			int i;
-
-			for (i = 0; (cpu = filter_cpus[i]) >= 0; i++) {
-				precord = tracecmd_peek_data(handles->handle, cpu);
-				if (precord &&
-				    (first_record || precord->ts < last_stamp)) {
-					next_cpu = cpu;
-					last_stamp = precord->ts;
-					first_record = 0;
-				}
-			}
-			if (!first_record)
-				record = tracecmd_read_data(handles->handle, next_cpu);
-			else
-				record = NULL;
-		} else
-			record = tracecmd_read_next_data(handles->handle, &cpu);
-
-		if (record) {
-			ret = test_filters(pevent, handles->event_filters, record, 0);
-			switch (ret) {
-			case FILTER_NOEXIST:
-				/* Stack traces may still filter this */
-				if (stacktrace_id &&
-				    test_stacktrace(handles, record, 0))
-					found = 1;
-				else
-					tracecmd_free_record(record);
-				break;
-			case FILTER_NONE:
-			case FILTER_MATCH:
-				/* Test the negative filters (-v) */
-				ret = test_filters(pevent, handles->event_filter_out,
-						   record, 1);
-				if (ret != FILTER_MATCH) {
-					found = 1;
-					break;
-				}
-				/* fall through */
-			default:
-				tracecmd_free_record(record);
-			}
-		}
-	} while (record && !found);
-
-	if (record && stacktrace_id)
-		test_stacktrace(handles, record, 1);
-
-	handles->record = record;
-	if (!record)
-		handles->done = 1;
-
-	return record;
-}
-
-static void free_handle_record(struct handle_list *handles)
-{
-	if (!handles->record)
-		return;
-
-	tracecmd_free_record(handles->record);
-	handles->record = NULL;
-}
-
 static void print_handle_file(struct handle_list *handles)
 {
 	/* Only print file names if more than one file is read */
@@ -1184,17 +1009,157 @@
 		printf("%*s  ", max_file_size, "");
 }
 
-static void free_filters(struct filter *event_filter)
+static bool skip_record(struct handle_list *handles, struct tep_record *record, int cpu)
 {
-	struct filter *filter;
+	struct tep_handle *tep;
+	bool found = false;
+	int ret;
 
-	while (event_filter) {
-		filter = event_filter;
-		event_filter = filter->next;
+	tep = tracecmd_get_tep(handles->handle);
 
-		tep_filter_free(filter->filter);
-		free(filter);
+	if (filter_cpus) {
+		int i;
+
+		for (i = 0; filter_cpus[i] >= 0; i++) {
+			if (filter_cpus[i] == cpu) {
+				found = true;
+				break;
+			}
+		}
+
+		if (!found)
+			return true;
+		found = false;
 	}
+
+	ret = test_filters(tep, record);
+	switch (ret) {
+	case FILTER_NOEXIST:
+		break;
+	case FILTER_NONE:
+	case FILTER_MATCH:
+		/* Test the negative filters (-v) */
+		ret = test_filters(tep, record);
+		if (ret != FILTER_MATCH) {
+			found = true;
+			break;
+		}
+	}
+
+	return !found;
+}
+
+struct kvm_cpu_map {
+	struct tracecmd_input		*guest_handle;
+	int				guest_vcpu;
+	int				host_pid;
+};
+
+static struct kvm_cpu_map *vcpu_maps;
+static int nr_vcpu_maps;
+
+static int cmp_map(const void *A, const void *B)
+{
+	const struct kvm_cpu_map *a = A;
+	const struct kvm_cpu_map *b = B;
+
+	if (a->host_pid < b->host_pid)
+		return -1;
+	return a->host_pid > b->host_pid;
+}
+
+static void map_vcpus(struct tracecmd_input **handles, int nr_handles)
+{
+	struct tracecmd_input *host_handle = handles[0];
+	unsigned long long traceid;
+	struct kvm_cpu_map *map;
+	const int *cpu_pids;
+	const char *name;
+	int vcpu_count;
+	int ret;
+	int i, k;
+
+	for (i = 1; i < nr_handles; i++) {
+		traceid = tracecmd_get_traceid(handles[i]);
+		ret = tracecmd_get_guest_cpumap(host_handle, traceid,
+						&name, &vcpu_count, &cpu_pids);
+		if (ret)
+			continue;
+		map = realloc(vcpu_maps, sizeof(*map) * (nr_vcpu_maps + vcpu_count));
+		if (!map)
+			die("Could not allocate vcpu maps");
+
+		vcpu_maps = map;
+		map += nr_vcpu_maps;
+		nr_vcpu_maps += vcpu_count;
+
+		for (k = 0; k < vcpu_count; k++) {
+			map[k].guest_handle = handles[i];
+			map[k].guest_vcpu = k;
+			map[k].host_pid = cpu_pids[k];
+		}
+	}
+	if (!vcpu_maps)
+		return;
+
+	qsort(vcpu_maps, nr_vcpu_maps, sizeof(*map), cmp_map);
+}
+
+
+const char *tep_plugin_kvm_get_func(struct tep_event *event,
+				    struct tep_record *record,
+				    unsigned long long *val)
+{
+	struct tep_handle *tep;
+	struct kvm_cpu_map *map;
+	struct kvm_cpu_map key;
+	unsigned long long rip = *val;
+	const char *func;
+	int pid;
+
+	if (!vcpu_maps || !nr_vcpu_maps)
+		return NULL;
+
+	/*
+	 * A kvm event is referencing an address of the guest.
+	 * get the PID of this event, and then find which guest
+	 * it belongs to. Then return the function name from that guest's
+	 * handle.
+	 */
+	pid = tep_data_pid(event->tep, record);
+
+	key.host_pid = pid;
+	map = bsearch(&key, vcpu_maps, nr_vcpu_maps, sizeof(*vcpu_maps), cmp_map);
+
+	if (!map)
+		return NULL;
+
+	tep = tracecmd_get_tep(map->guest_handle);
+	func = tep_find_function(tep, rip);
+	if (func)
+		*val = tep_find_function_address(tep, rip);
+	return func;
+}
+
+static int process_record(struct tracecmd_input *handle, struct tep_record *record,
+			  int cpu, void *data)
+{
+	struct handle_list *handles = tracecmd_get_private(handle);
+	unsigned long long *last_timestamp = data;
+
+	if (skip_record(handles, record, cpu))
+		return 0;
+
+	if (tscheck && *last_timestamp > record->ts) {
+		errno = 0;
+		warning("WARNING: Record on cpu %d went backwards: %lld to %lld delta: -%lld\n",
+			cpu, *last_timestamp, record->ts, *last_timestamp - record->ts);
+	}
+	*last_timestamp = record->ts;
+
+	print_handle_file(handles);
+	trace_show_data(handle, record);
+	return 0;
 }
 
 enum output_type {
@@ -1209,17 +1174,17 @@
 {
 	unsigned long long ts, first_ts;
 	struct handle_list *handles;
-	struct handle_list *last_handle;
-	struct tep_record *record;
-	struct tep_record *last_record;
-	struct tep_handle *pevent;
-	struct tep_event *event;
+	struct tracecmd_input **handle_array;
+	unsigned long long last_timestamp = 0;
+	int nr_handles = 0;
 	int first = 1;
 	int ret;
 
 	list_for_each_entry(handles, handle_list, list) {
 		int cpus;
 
+		nr_handles++;
+
 		if (!tracecmd_is_buffer_instance(handles->handle)) {
 			ret = tracecmd_init_data(handles->handle);
 			if (ret < 0)
@@ -1227,9 +1192,8 @@
 		}
 		cpus = tracecmd_cpus(handles->handle);
 		handles->cpus = cpus;
-		handles->last_timestamp = calloc(cpus, sizeof(*handles->last_timestamp));
-		if (!handles->last_timestamp)
-			die("allocating timestamps");
+
+		process_filters(handles);
 
 		/* Don't process instances that we added here */
 		if (tracecmd_is_buffer_instance(handles->handle))
@@ -1268,12 +1232,6 @@
 			continue;
 		}
 
-		/* Find the kernel_stacktrace if available */
-		pevent = tracecmd_get_tep(handles->handle);
-		event = tep_find_event_by_name(pevent, "ftrace", "kernel_stack");
-		if (event)
-			stacktrace_id = event->id;
-
 		init_wakeup(handles->handle);
 		if (last_hook)
 			last_hook->next = tracecmd_hooks(handles->handle);
@@ -1282,15 +1240,17 @@
 		if (profile)
 			trace_init_profile(handles->handle, hooks, global);
 
-		process_filters(handles);
-
 		/* If this file has buffer instances, get the handles for them */
 		instances = tracecmd_buffer_instances(handles->handle);
 		if (instances) {
 			struct tracecmd_input *new_handle;
+			struct input_files *file_input;
+			const char *save_name;
 			const char *name;
 			int i;
 
+			file_input = handles->input_file;
+
 			for (i = 0; i < instances; i++) {
 				name = tracecmd_buffer_instance_name(handles->handle, i);
 				if (!name)
@@ -1300,7 +1260,16 @@
 					warning("could not retrieve handle %s", name);
 					continue;
 				}
-				add_handle(new_handle, name);
+				if (file_input) {
+					save_name = file_input->file;
+					file_input->file = name;
+				} else {
+					save_name = NULL;
+					file_input = add_input(name);
+				}
+				add_handle(new_handle, file_input);
+				if (save_name)
+					file_input->file = save_name;
 			}
 		}
 	}
@@ -1314,47 +1283,27 @@
 		}
 	}
 
-	do {
-		last_handle = NULL;
-		last_record = NULL;
+	handle_array = calloc(nr_handles, sizeof(*handle_array));
+	if (!handle_array)
+		die("Could not allocate memory for handle list");
 
-		list_for_each_entry(handles, handle_list, list) {
-			record = get_next_record(handles);
-			if (!record)
-				continue;
-			if (!last_record ||
-			    (record && record->ts < last_record->ts)) {
-				last_record = record;
-				last_handle = handles;
-			}
-		}
-		if (last_record) {
-			int cpu = last_record->cpu;
-			if (cpu >= last_handle->cpus)
-				die("cpu %d greater than %d\n", cpu, last_handle->cpus);
-			if (tscheck &&
-			    last_handle->last_timestamp[cpu] > last_record->ts) {
-				errno = 0;
-				warning("WARNING: Record on cpu %d went backwards: %lld to %lld delta: -%lld\n",
-					cpu, last_handle->last_timestamp[cpu],
-					last_record->ts,
-					last_handle->last_timestamp[cpu] - last_record->ts);
-			}
-			last_handle->last_timestamp[cpu] = last_record->ts;
-			print_handle_file(last_handle);
-			trace_show_data(last_handle->handle, last_record);
-			free_handle_record(last_handle);
-		}
-	} while (last_record);
+	nr_handles = 0;
+	list_for_each_entry(handles, handle_list, list) {
+		tracecmd_set_private(handles->handle, handles);
+		handle_array[nr_handles++] = handles->handle;
+	}
+
+	map_vcpus(handle_array, nr_handles);
+
+	tracecmd_iterate_events_multi(handle_array, nr_handles,
+				      process_record, &last_timestamp);
+
+	free(handle_array);
 
 	if (profile)
 		do_trace_profile();
 
 	list_for_each_entry(handles, handle_list, list) {
-		free_filters(handles->event_filters);
-		free_filters(handles->event_filter_out);
-		free(handles->last_timestamp);
-
 		show_test(handles->handle);
 	}
 }
@@ -1507,6 +1456,19 @@
 	}
 }
 
+static void show_event_ts(struct tracecmd_input *handle,
+			  struct tep_record *record)
+{
+	const char *tfmt = time_format(handle, TIME_FMT_TS);
+	struct tep_handle *tep = tracecmd_get_tep(handle);
+	struct trace_seq s;
+
+	trace_seq_init(&s);
+	tep_print_event(tep, &s, record, tfmt, TEP_PRINT_TIME);
+	printf("%s", s.buffer);
+	trace_seq_destroy(&s);
+}
+
 static void add_hook(const char *arg)
 {
 	struct hook_list *hook;
@@ -1519,6 +1481,21 @@
 		last_hook = hook;
 }
 
+static void add_first_input(const char *input_file, long long tsoffset)
+{
+	struct input_files *item;
+
+	/* Copy filter strings to this input file */
+	item = add_input(input_file);
+	item->filter_str = filter_strings;
+	if (filter_strings)
+		item->filter_str_next = filter_next;
+	else
+		item->filter_str_next = &item->filter_str;
+	/* Copy the tsoffset to this input file */
+	item->tsoffset = tsoffset;
+}
+
 enum {
 	OPT_verbose	= 234,
 	OPT_align_ts	= 235,
@@ -1543,6 +1520,8 @@
 	OPT_events	= 254,
 	OPT_cpu		= 255,
 	OPT_cpus	= 256,
+	OPT_first	= 257,
+	OPT_last	= 258,
 };
 
 void trace_report (int argc, char **argv)
@@ -1571,6 +1550,8 @@
 	int show_version = 0;
 	int show_events = 0;
 	int show_cpus = 0;
+	int show_first = 0;
+	int show_last = 0;
 	int print_events = 0;
 	int nanosec = 0;
 	int no_date = 0;
@@ -1593,6 +1574,8 @@
 
 	signal(SIGINT, sig_end);
 
+	trace_set_loglevel(TEP_LOG_ERROR);
+
 	for (;;) {
 		int option_index = 0;
 		static struct option long_options[] = {
@@ -1601,6 +1584,7 @@
 			{"events", no_argument, NULL, OPT_events},
 			{"event", required_argument, NULL, OPT_event},
 			{"filter-test", no_argument, NULL, 'T'},
+			{"first-event", no_argument, NULL, OPT_first},
 			{"kallsyms", required_argument, NULL, OPT_kallsyms},
 			{"pid", required_argument, NULL, OPT_pid},
 			{"comm", required_argument, NULL, OPT_comm},
@@ -1610,6 +1594,7 @@
 			{"stat", no_argument, NULL, OPT_stat},
 			{"boundary", no_argument, NULL, OPT_boundary},
 			{"debug", no_argument, NULL, OPT_debug},
+			{"last-event", no_argument, NULL, OPT_last},
 			{"profile", no_argument, NULL, OPT_profile},
 			{"uname", no_argument, NULL, OPT_uname},
 			{"version", no_argument, NULL, OPT_version},
@@ -1635,18 +1620,15 @@
 			break;
 		case 'i':
 			if (input_file) {
-				if (!multi_inputs) {
-					add_input(input_file);
-					if (tsoffset)
-						last_input_file->tsoffset = tsoffset;
-				}
 				multi_inputs++;
 				add_input(optarg);
-			} else
+			} else {
 				input_file = optarg;
+				add_first_input(input_file, tsoffset);
+			}
 			break;
 		case 'F':
-			add_filter(optarg, neg);
+			add_filter(last_input_file, optarg, neg);
 			break;
 		case 'H':
 			add_hook(optarg);
@@ -1754,6 +1736,14 @@
 		case OPT_stat:
 			show_stat = 1;
 			break;
+		case OPT_first:
+			show_first = 1;
+			show_cpus = 1;
+			break;
+		case OPT_last:
+			show_last = 1;
+			show_cpus = 1;
+			break;
 		case OPT_boundary:
 			/* Debug to look at buffer breaks */
 			buffer_breaks = 1;
@@ -1815,15 +1805,18 @@
 		if (input_file)
 			usage(argv);
 		input_file = argv[optind + 1];
+		add_first_input(input_file, tsoffset);
+		for (int i = optind + 2; i < argc; i++) {
+			multi_inputs++;
+			add_input(argv[i]);
+		}
 	}
 
-	if (!input_file)
-		input_file = default_input_file;
-
 	if (!multi_inputs) {
-		add_input(input_file);
-		if (tsoffset)
-			last_input_file->tsoffset = tsoffset;
+		if (!input_file) {
+			input_file = default_input_file;
+			add_first_input(input_file, tsoffset);
+		}
 	} else if (show_wakeup)
 		die("Wakeup tracing can only be done on a single input file");
 
@@ -1833,7 +1826,7 @@
 			die("error reading header for %s", inputs->file);
 
 		/* If used with instances, top instance will have no tag */
-		add_handle(handle, multi_inputs ? inputs->file : NULL);
+		add_handle(handle, multi_inputs ? inputs : NULL);
 
 		if (no_date)
 			tracecmd_set_flag(handle, TRACECMD_FL_IGNORE_DATE);
@@ -1923,6 +1916,7 @@
 		}
 
 		if (show_cpus) {
+			struct tep_record *record;
 			int cpus;
 			int ret;
 			int i;
@@ -1935,8 +1929,23 @@
 			cpus = tracecmd_cpus(handle);
 			printf("List of CPUs in %s with data:\n", inputs->file);
 			for (i = 0; i < cpus; i++) {
-				if (tracecmd_read_cpu_first(handle, i))
-					printf("  %d\n", i);
+				if ((record = tracecmd_read_cpu_first(handle, i))) {
+					printf("  %d", i);
+					if (show_first) {
+						printf("\tFirst event:");
+						show_event_ts(handle, record);
+					}
+					if (show_last) {
+						tracecmd_free_record(record);
+						record = tracecmd_read_cpu_last(handle, i);
+						if (record) {
+							printf("\tLast event:");
+							show_event_ts(handle, record);
+						}
+					}
+					tracecmd_free_record(record);
+					printf("\n");
+				}
 			}
 			continue;
 		}
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index 27c4e7b..0063d52 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -46,6 +46,8 @@
 #define _STR(x) #x
 #define STR(x) _STR(x)
 
+#define RECORD_PIDFILE	"trace-cmd-record.pid"
+
 #define TRACE_CTRL	"tracing_on"
 #define TRACE		"trace"
 #define AVAILABLE	"available_tracers"
@@ -73,7 +75,7 @@
 static int keep;
 
 static int latency;
-static int sleep_time = 1000;
+static long sleep_time = 1000;
 static int recorder_threads;
 static struct pid_record_data *pids;
 static int buffers;
@@ -91,6 +93,10 @@
 
 static bool fork_process;
 
+static bool do_daemonize;
+
+static bool created_pidfile;
+
 /* Max size to let a per cpu file get */
 static int max_kb;
 
@@ -195,6 +201,7 @@
 	enum trace_cmd curr_cmd;
 	struct buffer_instance *instance;
 	const char *output;
+	const char *temp;
 	char *date2ts;
 	char *user;
 	const char *clock;
@@ -381,11 +388,9 @@
 	return instance;
 
 error:
-	if (instance) {
-		free(instance->name);
-		tracefs_instance_free(instance->tracefs);
-		free(instance);
-	}
+	free(instance->name);
+	tracefs_instance_free(instance->tracefs);
+	free(instance);
 	return NULL;
 }
 
@@ -417,7 +422,7 @@
 	}
 
 	while ((dent = readdir(dir))) {
-		const char *name = strdup(dent->d_name);
+		const char *name = dent->d_name;
 		char *instance_path;
 		struct buffer_instance *instance;
 
@@ -526,6 +531,23 @@
 	char *file = NULL;
 	int size;
 
+	if (instance->temp_dir) {
+		if (!instance->temp_file) {
+			const char *f = output_file + strlen(output_file) - 1;;
+			int ret;
+
+			for (; f > output_file && *f != '/'; f--)
+				;
+			if (*f == '/')
+				f++;
+			ret = asprintf(&instance->temp_file, "%s/%s",
+				       instance->temp_dir, f);
+			if (ret < 0)
+				die("Failed to create temp file");
+		}
+		output_file = instance->temp_file;
+	}
+
 	name = tracefs_instance_get_name(instance->tracefs);
 	if (name) {
 		size = snprintf(file, 0, "%s.%s.cpu%d", output_file, name, cpu);
@@ -537,7 +559,7 @@
 		size = snprintf(file, 0, "%s.cpu%d", output_file, cpu);
 		file = malloc(size + 1);
 		if (!file)
-			die("Failed to allocate temp file for %s", name);
+			die("Failed to allocate temp file");
 		sprintf(file, "%s.cpu%d", output_file, cpu);
 	}
 
@@ -570,10 +592,15 @@
 
 static void delete_temp_file(struct buffer_instance *instance, int cpu)
 {
-	const char *output_file = instance->output_file;
+	const char *output_file;
 	const char *name;
 	char file[PATH_MAX];
 
+	if (instance->temp_file)
+		output_file = instance->temp_file;
+	else
+		output_file = instance->output_file;
+
 	name = tracefs_instance_get_name(instance->tracefs);
 	if (name)
 		snprintf(file, PATH_MAX, "%s.%s.cpu%d", output_file, name, cpu);
@@ -623,6 +650,9 @@
 	else
 		ret = -1;
 
+	if (created_pidfile)
+		remove_pid_file(RECORD_PIDFILE);
+
 	kill_threads();
 	va_start(ap, fmt);
 	fprintf(stderr, "  ");
@@ -686,6 +716,12 @@
 	tracecmd_add_option_v(handle, TRACECMD_OPTION_TSC2NSEC, vector, 3);
 }
 
+static void guest_tsync_complete(struct buffer_instance *instance)
+{
+	tracecmd_tsync_with_host_stop(instance->tsync);
+	tracecmd_tsync_free(instance->tsync);
+}
+
 static void host_tsync_complete(struct common_record_context *ctx,
 				struct buffer_instance *instance)
 {
@@ -720,20 +756,33 @@
 
 	/* Send close message to guests */
 	for_all_instances(instance) {
-		if (is_guest(instance))
+		if (is_guest(instance)) {
 			tracecmd_msg_send_close_msg(instance->msg_handle);
+			if (is_proxy(instance) && instance->proxy_fd >= 0) {
+				/* The proxy will send more data now */
+				if (tracecmd_msg_read_data(instance->msg_handle, instance->proxy_fd))
+					warning("Failed receiving finishing metadata");
+				close(instance->proxy_fd);
+			}
+		}
 	}
 
 	for_all_instances(instance) {
-		if (is_guest(instance))
-			host_tsync_complete(ctx, instance);
+		if (is_guest(instance)) {
+			if (is_proxy(instance))
+				guest_tsync_complete(instance);
+			else
+				host_tsync_complete(ctx, instance);
+		}
 	}
 
 	/* Wait for guests to acknowledge */
 	for_all_instances(instance) {
 		if (is_guest(instance)) {
-			tracecmd_msg_wait_close_resp(instance->msg_handle);
-			tracecmd_msg_handle_close(instance->msg_handle);
+			if (!is_proxy(instance)) {
+				tracecmd_msg_wait_close_resp(instance->msg_handle);
+				tracecmd_msg_handle_close(instance->msg_handle);
+			}
 		}
 	}
 }
@@ -756,7 +805,7 @@
 	/* Flush out the pipes */
 	if (type & TRACE_TYPE_STREAM) {
 		do {
-			ret = trace_stream_read(pids, recorder_threads, NULL);
+			ret = trace_stream_read(pids, recorder_threads, 0);
 		} while (ret > 0);
 	}
 }
@@ -1181,12 +1230,12 @@
 				      (maps->nr_lib_maps + 1) * sizeof(*map));
 			if (!map)
 				goto out_fail;
+			maps->lib_maps = map;
 			map[maps->nr_lib_maps].end = end;
 			map[maps->nr_lib_maps].start = begin;
 			map[maps->nr_lib_maps].lib_name = strdup(mapname);
 			if (!map[maps->nr_lib_maps].lib_name)
 				goto out_fail;
-			maps->lib_maps = map;
 			maps->nr_lib_maps++;
 		}
 	}
@@ -1264,7 +1313,6 @@
 
 static pid_t trace_waitpid(enum trace_type type, pid_t pid, int *status, int options)
 {
-	struct timeval tv = { 1, 0 };
 	int ret;
 
 	if (type & TRACE_TYPE_STREAM)
@@ -1276,7 +1324,7 @@
 			return ret;
 
 		if (type & TRACE_TYPE_STREAM)
-			trace_stream_read(pids, recorder_threads, &tv);
+			trace_stream_read(pids, recorder_threads, sleep_time);
 	} while (1);
 }
 
@@ -1595,15 +1643,129 @@
 
 #endif /* NO_PTRACE */
 
+static bool child_detached;
+
+static void daemonize_set_child_detached(int s)
+{
+	child_detached = true;
+}
+
+static void daemonize_start(void)
+{
+	int devnull;
+	int status;
+	int pid;
+	int ret;
+
+	pid = fork();
+	if (pid == -1)
+		die("daemonize: fork failed");
+
+	if (pid == 0) { /* child */
+		/*
+		 * We keep stdout and stderr open to allow the user to
+		 * see output and errors after the daemonization (the user can
+		 * choose to supress it with >/dev/null if the user wants).
+		 *
+		 * No reason to keep stdin open (it might interfere with the
+		 * shell), we redirect it to /dev/null.
+		 */
+		devnull = open("/dev/null", O_RDONLY);
+		if (devnull == -1)
+			die("daemonize: open /dev/null failed");
+
+		if (devnull > 0) {
+			if (dup2(devnull, 0) == -1)
+				die("daemonize: dup2");
+			close(0);
+		}
+
+		return;
+
+		/*
+		 * The child returns to back to the caller, but the parent waits until
+		 * SIGRTMIN is received from the child (by calling daemonize_finish()),
+		 * or the child exits for some reason (usually an indication of
+		 * an error), which ever comes first.
+		 *
+		 * Then the parent exits (with the status code of the child,
+		 * if it finished early, or with 0 if SIGRTMIN was received),
+		 * which causes the child (and its entire process tree) to be
+		 * inherited by init.
+		 *
+		 * Note that until the child calls daemonize_finish(), it still has
+		 * the same session id as the parent, so it can die together with
+		 * the parent before daemonization finished (purposefully, since the
+		 * user might send a quick Ctrl^C to cancel the command, and we don't
+		 * want background processes staying alive in that case)
+		 */
+	} else { /* parent */
+		struct sigaction sa = {
+			/* disable SA_RESTART, to allow waitpid() to be interrupted by SIGRTMIN */
+			.sa_flags = 0,
+			.sa_handler = daemonize_set_child_detached
+		};
+
+		if (sigemptyset(&sa.sa_mask) == -1)
+			die("daemonize: sigemptyset failed");
+
+		if (sigaddset(&sa.sa_mask, SIGRTMIN) == -1)
+			die("daemonize: sigaddset failed");
+
+		if (sigprocmask(SIG_UNBLOCK, &sa.sa_mask, NULL) == -1)
+			die("daemonize: sigprocmask failed");
+
+		if (sigaction(SIGRTMIN, &sa, NULL) == -1)
+			die("daemonize: sigaction failed");
+
+		do {
+			ret = waitpid(pid, &status, 0);
+		} while (!child_detached && ((ret < 0) && (errno == EINTR)));
+
+		if (child_detached)
+			exit(0);
+		else if (ret == pid)
+			exit(WIFEXITED(status));
+		else
+			die("daemonize: waitpid failed");
+
+		__builtin_unreachable();
+	}
+}
+
+static void daemonize_finish(void)
+{
+	/*
+	 * setsid() will also set the sid to be the pgid to all currently
+	 * running threads in the process group (such as the tsync thread).
+	 */
+	if (setsid() == -1)
+		die("daemonize: setsid");
+
+	if (kill(getppid(), SIGRTMIN) == -1)
+		die("daemonize: kill");
+
+	make_pid_file(RECORD_PIDFILE);
+	created_pidfile = true;
+}
+
 static void trace_or_sleep(enum trace_type type, bool pwait)
 {
-	struct timeval tv = { 1 , 0 };
+	int i;
 
 	if (pwait)
 		ptrace_wait(type);
-	else if (type & TRACE_TYPE_STREAM)
-		trace_stream_read(pids, recorder_threads, &tv);
-	else
+	else if (type & TRACE_TYPE_STREAM) {
+		/* Returns zero if it did not read anything (and did a sleep) */
+		if (trace_stream_read(pids, recorder_threads, sleep_time) > 0)
+			return;
+		/* Force a flush if nothing was read (including on errors) */
+		for (i = 0; i < recorder_threads; i++) {
+			if (pids[i].pid > 0) {
+				kill(pids[i].pid, SIGUSR2);
+			}
+		}
+	} else
 		sleep(10);
 }
 
@@ -1634,6 +1796,52 @@
 	return 0;
 }
 
+static void execute_program(int argc, char **argv)
+{
+	char buf[PATH_MAX + NAME_MAX + 1];
+	char *path;
+	char *entry;
+	char *saveptr;
+
+	/*
+	 * if command specified by user is neither absolute nor
+	 * relative than we search for it in $PATH.
+	 */
+	if (!strchr(argv[0], '/')) {
+		path = getenv("PATH");
+
+		if (!path)
+			die("can't search for '%s' if $PATH is NULL", argv[0]);
+
+		/* Do not modify the actual environment variable */
+		path = strdup(path);
+		if (!path)
+			die("Failed to allocate PATH");
+
+		for (entry = strtok_r(path, ":", &saveptr);
+		     entry; entry = strtok_r(NULL, ":", &saveptr)) {
+
+			snprintf(buf, sizeof(buf), "%s/%s", entry, argv[0]);
+
+			/* does it exist and can we execute it? */
+			if (access(buf, X_OK) == 0)
+				break;
+
+		}
+		free(path);
+	} else {
+		strncpy(buf, argv[0], sizeof(buf));
+	}
+
+	tracecmd_enable_tracing();
+	if (execve(buf, argv, environ)) {
+		fprintf(stderr, "\n********************\n");
+		fprintf(stderr, " Unable to exec %s\n", argv[0]);
+		fprintf(stderr, "********************\n");
+		die("Failed to exec %s", argv[0]);
+	}
+}
+
 static void run_cmd(enum trace_type type, const char *user, int argc, char **argv)
 {
 	int status;
@@ -1644,7 +1852,6 @@
 	if (!pid) {
 		/* child */
 		update_task_filter();
-		tracecmd_enable_tracing();
 		if (!fork_process)
 			enable_ptrace();
 		/*
@@ -1660,13 +1867,11 @@
 		if (change_user(user) < 0)
 			die("Failed to change user to %s", user);
 
-		if (execvp(argv[0], argv)) {
-			fprintf(stderr, "\n********************\n");
-			fprintf(stderr, " Unable to exec %s\n", argv[0]);
-			fprintf(stderr, "********************\n");
-			die("Failed to exec %s", argv[0]);
-		}
+		execute_program(argc, argv);
 	}
+
+	if (do_daemonize)
+		daemonize_finish();
 	if (fork_process)
 		exit(0);
 	if (do_ptrace) {
@@ -2301,6 +2506,7 @@
 		filter[len+1] = '\0';
 		write_file(file, filter);
 	}
+	free(buf);
 }
 
 static void update_reset_triggers(void)
@@ -2317,6 +2523,26 @@
 	}
 }
 
+static void reset_buffer_files_instance(struct buffer_instance *instance)
+{
+	if (instance->old_buffer_size != instance->buffer_size)
+		tracefs_instance_set_buffer_size(instance->tracefs,
+						 instance->old_buffer_size, -1);
+
+	if (instance->old_subbuf_size != instance->subbuf_size)
+		tracefs_instance_set_subbuf_size(instance->tracefs,
+						 instance->old_subbuf_size);
+}
+
+static void reset_buffer_files(void)
+{
+	struct buffer_instance *instance;
+
+	for_all_instances(instance) {
+		reset_buffer_files_instance(instance);
+	}
+}
+
 static void update_reset_files(void)
 {
 	struct reset_file *reset;
@@ -2331,6 +2557,8 @@
 		free(reset->reset);
 		free(reset);
 	}
+
+	reset_buffer_files();
 }
 
 static void
@@ -2514,10 +2742,10 @@
 
 	tracecmd_disable_tracing();
 
-	if (disable_tracer) {
-		disable_func_stack_trace();
+	disable_func_stack_trace();
+
+	if (disable_tracer)
 		set_plugin("nop");
-	}
 
 	reset_events();
 
@@ -2651,6 +2879,7 @@
 	} while (ret >= 0 && len);
 
  out:
+	free(filter);
 	close(fd);
 }
 
@@ -3113,14 +3342,45 @@
 		expand_event_instance(instance);
 }
 
-static void finish(int sig)
+static void finish(void)
 {
+	static int secs = 1;
+
+	sleep_time = 0;
+
 	/* all done */
-	if (recorder)
+	if (recorder) {
 		tracecmd_stop_recording(recorder);
+		/*
+		 * We could just call the alarm if the above returned non zero,
+		 * as zero is suppose to guarantee that the reader woke up.
+		 * But as this is called from a signal handler, that may not
+		 * necessarily be the case.
+		 */
+		alarm(secs++);
+	}
 	finished = 1;
 }
 
+static void flush(void)
+{
+	if (recorder)
+		tracecmd_flush_recording(recorder, false);
+}
+
+static void do_sig(int sig)
+{
+	switch (sig) {
+	case SIGALRM:
+	case SIGUSR1:
+	case SIGINT:
+	case SIGTERM:
+		return finish();
+	case SIGUSR2:
+		return flush();
+	}
+}
+
 static struct addrinfo *do_getaddrinfo(const char *host, unsigned int port,
 				       enum port_type type)
 {
@@ -3157,6 +3417,8 @@
 			     rp->ai_protocol);
 		if (sfd == -1)
 			continue;
+
+		set_tcp_no_delay(sfd, rp->ai_socktype);
 		if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
 			break;
 		close(sfd);
@@ -3215,38 +3477,6 @@
 	return -1;
 }
 
-/* Find all the tasks associated with the guest pid */
-static void find_tasks(struct trace_guest *guest)
-{
-	struct dirent *dent;
-	char *path;
-	DIR *dir;
-	int ret;
-	int tasks = 0;
-
-	ret = asprintf(&path, "/proc/%d/task", guest->pid);
-	if (ret < 0)
-		return;
-
-	dir = opendir(path);
-	free(path);
-	if (!dir)
-		return;
-
-	while ((dent = readdir(dir))) {
-		int *pids;
-		if (!(dent->d_type == DT_DIR && is_digits(dent->d_name)))
-			continue;
-		pids = realloc(guest->task_pids, sizeof(int) * (tasks + 2));
-		if (!pids)
-			break;
-		pids[tasks++] = strtol(dent->d_name, NULL, 0);
-		pids[tasks] = -1;
-		guest->task_pids = pids;
-	}
-	closedir(dir);
-}
-
 static char *parse_guest_name(char *gname, int *cid, int *port,
 			      struct addrinfo **res)
 {
@@ -3287,9 +3517,6 @@
 		guest = trace_get_guest(*cid, gname);
 	if (guest) {
 		*cid = guest->cid;
-		/* Mapping not found, search for them */
-		if (!guest->cpu_pid)
-			find_tasks(guest);
 		return guest->name;
 	}
 
@@ -3319,19 +3546,11 @@
 {
 	struct tracecmd_recorder *recorder;
 	unsigned flags = recorder_flags | TRACECMD_RECORD_BLOCK_SPLICE;
-	char *path;
-
-	path = tracefs_instance_get_dir(instance->tracefs);
-
-	if (!path)
-		die("malloc");
 
 	/* This is already the child */
 	close(brass[0]);
 
-	recorder = tracecmd_create_buffer_recorder_fd(brass[1], cpu, flags, path);
-
-	tracefs_put_tracing_file(path);
+	recorder = tracecmd_create_buffer_recorder_fd(brass[1], cpu, flags, instance->tracefs);
 
 	return recorder;
 }
@@ -3342,7 +3561,6 @@
 {
 	struct tracecmd_recorder *record;
 	struct addrinfo *result;
-	char *path;
 
 	if (is_guest(instance)) {
 		int fd;
@@ -3370,7 +3588,7 @@
 			flags |= TRACECMD_RECORD_NOBRASS;
 		else if (!trace_vsock_can_splice_read())
 			flags |= TRACECMD_RECORD_NOSPLICE;
-		return tracecmd_create_recorder_virt(file, cpu, flags, fd);
+		return tracecmd_create_recorder_virt(file, cpu, flags, fd, max_kb);
 	}
 
 	if (brass)
@@ -3379,12 +3597,8 @@
 	if (!tracefs_instance_get_name(instance->tracefs))
 		return tracecmd_create_recorder_maxkb(file, cpu, recorder_flags, max_kb);
 
-	path = tracefs_instance_get_dir(instance->tracefs);
-
 	record = tracecmd_create_buffer_recorder_maxkb(file, cpu, recorder_flags,
-						       path, max_kb);
-	tracefs_put_tracing_file(path);
-
+						       instance->tracefs, max_kb);
 	return record;
 }
 
@@ -3395,6 +3609,7 @@
 static int create_recorder(struct buffer_instance *instance, int cpu,
 			   enum trace_type type, int *brass)
 {
+	struct tracefs_instance *recorder_instance = NULL;
 	long ret;
 	char *file;
 	pid_t pid;
@@ -3409,7 +3624,10 @@
 			return pid;
 
 		signal(SIGINT, SIG_IGN);
-		signal(SIGUSR1, finish);
+		signal(SIGTERM, SIG_IGN);
+		signal(SIGUSR1, do_sig);
+		signal(SIGUSR2, do_sig);
+		signal(SIGALRM, do_sig);
 
 		if (rt_prio)
 			set_prio(rt_prio);
@@ -3420,7 +3638,6 @@
 
 	if ((instance->client_ports && !is_guest(instance)) || is_agent(instance)) {
 		unsigned int flags = recorder_flags;
-		char *path = NULL;
 		int fd;
 
 		if (is_agent(instance)) {
@@ -3433,6 +3650,7 @@
 				    !trace_net_cmp_connection_fd(fd, instance->host)) {
 					dprint("Client does not match '%s' for cpu:%d\n",
 					       instance->host, cpu);
+					close(fd);
 					goto again;
 				}
 			}
@@ -3442,19 +3660,10 @@
 		}
 		if (fd < 0)
 			die("Failed connecting to client");
-		if (tracefs_instance_get_name(instance->tracefs) && !is_agent(instance)) {
-			path = tracefs_instance_get_dir(instance->tracefs);
-		} else {
-			const char *dir = tracefs_tracing_dir();
+		if (tracefs_instance_get_name(instance->tracefs) && !is_agent(instance))
+			recorder_instance = instance->tracefs;
 
-			if (dir)
-				path = strdup(dir);
-		}
-		if (!path)
-			die("can't get the tracing directory");
-
-		recorder = tracecmd_create_buffer_recorder_fd(fd, cpu, flags, path);
-		tracefs_put_tracing_file(path);
+		recorder = tracecmd_create_buffer_recorder_fd(fd, cpu, flags, recorder_instance);
 	} else {
 		file = get_temp_file(instance, cpu);
 		recorder = create_recorder_instance(instance, file, cpu, brass);
@@ -3465,7 +3674,7 @@
 		die ("can't create recorder");
 
 	if (type == TRACE_TYPE_EXTRACT) {
-		ret = tracecmd_flush_recording(recorder);
+		ret = tracecmd_flush_recording(recorder, true);
 		tracecmd_free_recorder(recorder);
 		recorder = NULL;
 		return ret;
@@ -3646,7 +3855,7 @@
 	char *port;
 	char *p;
 
-	if (!strchr(host, ':')) {
+	if (!strchr(thost, ':')) {
 		server = strdup("localhost");
 		if (!server)
 			die("alloctating server");
@@ -3678,19 +3887,24 @@
 {
 	struct tracecmd_msg_handle *msg_handle = NULL;
 	enum port_type type = instance->port_type;
+	char *thost = strdup(host);
 	int sfd;
 
+	if (!thost)
+		die("Failed to allocate host");
 again:
 	switch (type) {
 	case USE_VSOCK:
-		sfd = connect_vsock(host);
+		sfd = connect_vsock(thost);
 		break;
 	default:
-		sfd = connect_ip(host);
+		sfd = connect_ip(thost);
 	}
 
-	if (sfd < 0)
+	if (sfd < 0) {
+		free(thost);
 		return NULL;
+	}
 
 	if (msg_handle) {
 		msg_handle->fd = sfd;
@@ -3720,6 +3934,7 @@
 			/* reconnect to the server for using the v1 protocol */
 			close(sfd);
 			free(host);
+			host = NULL;
 			goto again;
 		}
 		communicate_with_listener_v3(msg_handle, &instance->client_ports);
@@ -3728,6 +3943,7 @@
 	if (msg_handle->version == V1_PROTOCOL)
 		communicate_with_listener_v1(msg_handle, instance);
 
+	free(thost);
 	return msg_handle;
 }
 
@@ -3796,19 +4012,18 @@
 		if (ret)
 			goto error;
 	} else {
+		/*
+		 * V3 can handle compression, but V1 can not.
+		 * Set the file version back to 6.
+		 */
+		ctx->file_version = FILE_VERSION_MIN;
+		ctx->compression = false;
 		network_handle = tracecmd_output_create_fd(msg_handle->fd);
 		if (!network_handle)
 			goto error;
 		if (tracecmd_output_set_version(network_handle, ctx->file_version))
 			goto error;
 
-		if (ctx->compression) {
-			if (tracecmd_output_set_compression(network_handle, ctx->compression))
-				goto error;
-		} else if (ctx->file_version >= FILE_VERSION_COMPRESSION) {
-			tracecmd_output_set_compression(network_handle, "any");
-		}
-
 		if (tracecmd_output_write_headers(network_handle, listed_events))
 			goto error;
 		tracecmd_set_quiet(network_handle, quiet);
@@ -3859,198 +4074,47 @@
 	return i;
 }
 
-struct trace_mapping {
-	struct tep_event		*kvm_entry;
-	struct tep_format_field		*vcpu_id;
-	struct tep_format_field		*common_pid;
-	int				*pids;
-	int				*map;
-	int				max_cpus;
-};
-
-static void start_mapping_vcpus(struct trace_guest *guest)
-{
-	char *pids = NULL;
-	char *t;
-	int len = 0;
-	int s;
-	int i;
-
-	if (!guest->task_pids)
-		return;
-
-	guest->instance = tracefs_instance_create("map_guest_pids");
-	if (!guest->instance)
-		return;
-
-	for (i = 0; guest->task_pids[i] >= 0; i++) {
-		s = snprintf(NULL, 0, "%d ", guest->task_pids[i]);
-		t = realloc(pids, len + s + 1);
-		if (!t) {
-			free(pids);
-			pids = NULL;
-			break;
-		}
-		pids = t;
-		sprintf(pids + len, "%d ", guest->task_pids[i]);
-		len += s;
-	}
-	if (pids) {
-		tracefs_instance_file_write(guest->instance, "set_event_pid", pids);
-		free(pids);
-	}
-	tracefs_instance_file_write(guest->instance, "events/kvm/kvm_entry/enable", "1");
-}
-
-static int map_vcpus(struct tep_event *event, struct tep_record *record,
-		     int cpu, void *context)
-{
-	struct trace_mapping *tmap = context;
-	unsigned long long val;
-	int type;
-	int pid;
-	int ret;
-	int i;
-
-	/* Do we have junk in the buffer? */
-	type = tep_data_type(event->tep, record);
-	if (type != tmap->kvm_entry->id)
-		return 0;
-
-	ret = tep_read_number_field(tmap->common_pid, record->data, &val);
-	if (ret < 0)
-		return 0;
-	pid = (int)val;
-
-	for (i = 0; tmap->pids[i] >= 0; i++) {
-		if (pid == tmap->pids[i])
-			break;
-	}
-	/* Is this thread one we care about ? */
-	if (tmap->pids[i] < 0)
-		return 0;
-
-	ret = tep_read_number_field(tmap->vcpu_id, record->data, &val);
-	if (ret < 0)
-		return 0;
-
-	cpu = (int)val;
-
-	/* Sanity check, warn? */
-	if (cpu >= tmap->max_cpus)
-		return 0;
-
-	/* Already have this one? Should we check if it is the same? */
-	if (tmap->map[cpu] >= 0)
-		return 0;
-
-	tmap->map[cpu] = pid;
-
-	/* Did we get them all */
-	for (i = 0; i < tmap->max_cpus; i++) {
-		if (tmap->map[i] < 0)
-			break;
-	}
-
-	return i == tmap->max_cpus;
-}
-
-static void stop_mapping_vcpus(struct buffer_instance *instance,
-			       struct trace_guest *guest)
-{
-	struct trace_mapping tmap = { };
-	struct tep_handle *tep;
-	const char *systems[] = { "kvm", NULL };
-	int i;
-
-	if (!guest->instance)
-		return;
-
-	tmap.pids = guest->task_pids;
-	tmap.max_cpus = instance->cpu_count;
-
-	tmap.map = malloc(sizeof(*tmap.map) * tmap.max_cpus);
-	if (!tmap.map)
-		return;
-
-	for (i = 0; i < tmap.max_cpus; i++)
-		tmap.map[i] = -1;
-
-	tracefs_instance_file_write(guest->instance, "events/kvm/kvm_entry/enable", "0");
-
-	tep = tracefs_local_events_system(NULL, systems);
-	if (!tep)
-		goto out;
-
-	tmap.kvm_entry = tep_find_event_by_name(tep, "kvm", "kvm_entry");
-	if (!tmap.kvm_entry)
-		goto out_free;
-
-	tmap.vcpu_id = tep_find_field(tmap.kvm_entry, "vcpu_id");
-	if (!tmap.vcpu_id)
-		goto out_free;
-
-	tmap.common_pid = tep_find_any_field(tmap.kvm_entry, "common_pid");
-	if (!tmap.common_pid)
-		goto out_free;
-
-	tracefs_iterate_raw_events(tep, guest->instance, NULL, 0, map_vcpus, &tmap);
-
-	for (i = 0; i < tmap.max_cpus; i++) {
-		if (tmap.map[i] < 0)
-			break;
-	}
-	/* We found all the mapped CPUs */
-	if (i == tmap.max_cpus) {
-		guest->cpu_pid = tmap.map;
-		guest->cpu_max = tmap.max_cpus;
-		tmap.map = NULL;
-	}
-
- out_free:
-	tep_free(tep);
- out:
-	free(tmap.map);
-	tracefs_instance_destroy(guest->instance);
-	tracefs_instance_free(guest->instance);
-}
+static bool clock_is_supported(struct tracefs_instance *instance, const char *clock);
 
 static int host_tsync(struct common_record_context *ctx,
 		      struct buffer_instance *instance,
 		      unsigned int tsync_port, char *proto)
 {
-	struct trace_guest *guest;
-	int guest_pid = -1;
+	struct buffer_instance *iter_instance;
+	int guest_id = -1;
 	int fd;
 
 	if (!proto)
 		return -1;
 
+	/* If connecting to a proxy, the clock may still need to be set */
+	if (strcmp(proto, "kvm") == 0 &&
+	    clock_is_supported(NULL, TSC_CLOCK)) {
+		ctx->clock = TSC_CLOCK;
+		for_all_instances(iter_instance) {
+			iter_instance->clock = TSC_CLOCK;
+			set_clock(ctx, iter_instance);
+		}
+	}
+
 	if (is_network(instance)) {
 		fd = connect_port(instance->name, tsync_port,
 				  instance->port_type);
 	} else {
-		guest = trace_get_guest(instance->cid, NULL);
-		if (guest == NULL)
-			return -1;
-
-		guest_pid = guest->pid;
-		start_mapping_vcpus(guest);
+		guest_id = instance->cid;
 		fd = trace_vsock_open(instance->cid, tsync_port);
 	}
 
-	instance->tsync = tracecmd_tsync_with_guest(top_instance.trace_id,
-						    instance->tsync_loop_interval,
-						    fd, guest_pid,
-						    instance->cpu_count,
-						    proto, ctx->clock);
-	if (!is_network(instance))
-		stop_mapping_vcpus(instance, guest);
-
-	if (!instance->tsync)
-		return -1;
-
-	return 0;
+	if (is_proxy(instance)) {
+		instance->tsync = trace_tsync_as_guest(fd, proto, ctx->clock,
+						       guest_id, -1);
+	} else {
+		instance->tsync = trace_tsync_as_host(fd, top_instance.trace_id,
+						      instance->tsync_loop_interval,
+						      guest_id, instance->cpu_count,
+						      proto, ctx->clock);
+	}
+	return instance->tsync ? 0 : -1;
 }
 
 static void connect_to_agent(struct common_record_context *ctx,
@@ -4065,20 +4129,25 @@
 	unsigned int *ports;
 	int i, *fds = NULL;
 	bool use_fifos = false;
+	int siblings = 0;
 
 	if (!no_fifos) {
 		nr_fifos = open_guest_fifos(instance->name, &fds);
 		use_fifos = nr_fifos > 0;
 	}
 
-	if (ctx->instance->result) {
+	if (instance->result) {
 		role = TRACECMD_TIME_SYNC_ROLE_CLIENT;
-		sd = connect_addr(ctx->instance->result);
+		sd = connect_addr(instance->result);
 		if (sd < 0)
 			die("Failed to connect to host %s:%u",
 			    instance->name, instance->port);
 	} else {
-		role = TRACECMD_TIME_SYNC_ROLE_HOST;
+		/* If connecting to a proxy, then this is the guest */
+		if (is_proxy(instance))
+			role = TRACECMD_TIME_SYNC_ROLE_GUEST;
+		else
+			role = TRACECMD_TIME_SYNC_ROLE_HOST;
 		sd = trace_vsock_open(instance->cid, instance->port);
 		if (sd < 0)
 			die("Failed to connect to vsocket @%u:%u",
@@ -4095,11 +4164,19 @@
 	if (instance->tsync_loop_interval >= 0)
 		tracecmd_tsync_proto_getall(&protos, instance->clock, role);
 
-	ret = tracecmd_msg_send_trace_req(msg_handle, instance->argc,
-					  instance->argv, use_fifos,
-					  top_instance.trace_id, protos);
+	if (is_proxy(instance))
+		ret = tracecmd_msg_send_trace_proxy(msg_handle, instance->argc,
+						    instance->argv, use_fifos,
+						    top_instance.trace_id, protos,
+						    tracecmd_count_cpus(),
+						    siblings);
+	else
+		ret = tracecmd_msg_send_trace_req(msg_handle, instance->argc,
+						  instance->argv, use_fifos,
+						  top_instance.trace_id, protos);
 	if (ret < 0)
-		die("Failed to send trace request");
+		die("Failed to send trace %s",
+		    is_proxy(instance) ? "proxy" : "request");
 
 	if (protos) {
 		free(protos->names);
@@ -4168,7 +4245,15 @@
 	/* Start reading tracing metadata */
 	if (tracecmd_msg_read_data(msg_handle, fd))
 		die("Failed receiving metadata");
-	close(fd);
+
+	/*
+	 * If connected to a proxy, then it still needs to send
+	 * the host / guest timings from its POV.
+	 */
+	if (is_proxy(instance))
+		instance->proxy_fd = fd;
+	else
+		close(fd);
 }
 
 static void setup_agent(struct buffer_instance *instance,
@@ -4181,13 +4266,18 @@
 	tracecmd_write_cmdlines(network_handle);
 	tracecmd_write_cpus(network_handle, instance->cpu_count);
 	tracecmd_write_buffer_info(network_handle);
-	tracecmd_write_options(network_handle);
-	tracecmd_write_meta_strings(network_handle);
-	tracecmd_msg_finish_sending_data(instance->msg_handle);
+	if (instance->msg_handle->flags & TRACECMD_MSG_FL_PROXY) {
+		tracecmd_prepare_options(network_handle, 0, SEEK_CUR);
+		tracecmd_msg_flush_data(instance->msg_handle);
+	} else {
+		tracecmd_write_options(network_handle);
+		tracecmd_write_meta_strings(network_handle);
+		tracecmd_msg_finish_sending_data(instance->msg_handle);
+	}
 	instance->network_handle = network_handle;
 }
 
-void start_threads(enum trace_type type, struct common_record_context *ctx)
+static void start_threads(enum trace_type type, struct common_record_context *ctx)
 {
 	struct buffer_instance *instance;
 	int total_cpu_count = 0;
@@ -4210,6 +4300,9 @@
 		int *brass = NULL;
 		int x, pid;
 
+		/* May be set by setup_guest() but all others is -1 */
+		instance->proxy_fd = -1;
+
 		if (is_agent(instance)) {
 			setup_agent(instance, ctx);
 		} else if (is_guest(instance)) {
@@ -4298,58 +4391,6 @@
 }
 
 static void
-add_guest_info(struct tracecmd_output *handle, struct buffer_instance *instance)
-{
-	struct trace_guest *guest;
-	const char *name;
-	char *buf, *p;
-	int size;
-	int pid;
-	int i;
-
-	if (is_network(instance)) {
-		name = instance->name;
-	} else {
-		guest = trace_get_guest(instance->cid, NULL);
-		if (!guest)
-			return;
-		name = guest->name;
-	}
-
-	size = strlen(name) + 1;
-	size += sizeof(long long);	/* trace_id */
-	size += sizeof(int);		/* cpu count */
-	size += instance->cpu_count * 2 * sizeof(int);	/* cpu,pid pair */
-
-	buf = calloc(1, size);
-	if (!buf)
-		return;
-	p = buf;
-	strcpy(p, name);
-	p += strlen(name) + 1;
-
-	memcpy(p, &instance->trace_id, sizeof(long long));
-	p += sizeof(long long);
-
-	memcpy(p, &instance->cpu_count, sizeof(int));
-	p += sizeof(int);
-	for (i = 0; i < instance->cpu_count; i++) {
-		pid = -1;
-		if (!is_network(instance)) {
-			if (i < guest->cpu_max)
-				pid = guest->cpu_pid[i];
-		}
-		memcpy(p, &i, sizeof(int));
-		p += sizeof(int);
-		memcpy(p, &pid, sizeof(int));
-		p += sizeof(int);
-	}
-
-	tracecmd_add_option(handle, TRACECMD_OPTION_GUEST, size, buf);
-	free(buf);
-}
-
-static void
 add_pid_maps(struct tracecmd_output *handle, struct buffer_instance *instance)
 {
 	struct pid_addr_maps *maps = instance->pid_maps;
@@ -4366,7 +4407,7 @@
 		trace_seq_printf(&s, "%x %x %s\n",
 				 maps->pid, maps->nr_lib_maps, maps->proc_name);
 		for (i = 0; i < maps->nr_lib_maps; i++)
-			trace_seq_printf(&s, "%llx %llx %s\n",
+			trace_seq_printf(&s, "%zx %zx %s\n",
 					maps->lib_maps[i].start,
 					maps->lib_maps[i].end,
 					maps->lib_maps[i].lib_name);
@@ -4488,6 +4529,7 @@
 	DATA_FL_DATE		= 1,
 	DATA_FL_OFFSET		= 2,
 	DATA_FL_GUEST		= 4,
+	DATA_FL_PROXY		= 8,
 };
 
 static void add_options(struct tracecmd_output *handle, struct common_record_context *ctx)
@@ -4589,15 +4631,18 @@
 {
 	struct tracecmd_output *handle;
 	struct buffer_instance *instance;
+	bool have_proxy = false;
 	bool local = false;
 	int max_cpu_count = local_cpu_count;
 	char **temp_files;
 	int i;
 
 	for_all_instances(instance) {
-		if (is_guest(instance))
+		if (is_guest(instance)) {
 			write_guest_file(instance);
-		else if (host && instance->msg_handle)
+			if (is_proxy(instance))
+				have_proxy = true;
+		} else if (host && instance->msg_handle)
 			finish_network(instance->msg_handle);
 		else
 			local = true;
@@ -4646,6 +4691,22 @@
 
 		add_options(handle, ctx);
 
+		/*
+		 * If we connected to a proxy, then it will now send us
+		 * the tsync data for our file.
+		 */
+		if (have_proxy) {
+			for_all_instances(instance) {
+				if (!is_proxy(instance))
+					continue;
+				/* Tell proxy we are ready for the rest */
+				tracecmd_msg_cont(instance->msg_handle);
+				tracecmd_msg_read_options(instance->msg_handle, handle);
+				tracecmd_msg_wait_close_resp(instance->msg_handle);
+				tracecmd_msg_handle_close(instance->msg_handle);
+			}
+		}
+
 		/* Only record the top instance under TRACECMD_OPTION_CPUSTAT*/
 		if (!no_top_instance() && !top_instance.msg_handle) {
 			struct trace_seq *s = top_instance.s_save;
@@ -4679,7 +4740,7 @@
 
 		for_all_instances(instance) {
 			if (is_guest(instance))
-				add_guest_info(handle, instance);
+				trace_add_guest_info(handle, instance);
 		}
 
 		if (ctx->tsc2nsec.mult) {
@@ -5065,10 +5126,7 @@
 static void set_buffer_size_instance(struct buffer_instance *instance)
 {
 	int buffer_size = instance->buffer_size;
-	char buf[BUFSIZ];
-	char *path;
 	int ret;
-	int fd;
 
 	if (is_guest(instance))
 		return;
@@ -5079,29 +5137,40 @@
 	if (buffer_size < 0)
 		die("buffer size must be positive");
 
-	snprintf(buf, BUFSIZ, "%d", buffer_size);
-
-	path = tracefs_instance_get_file(instance->tracefs, "buffer_size_kb");
-	fd = open(path, O_WRONLY);
-	if (fd < 0) {
-		warning("can't open %s", path);
-		goto out;
-	}
-
-	ret = write(fd, buf, strlen(buf));
+	instance->old_buffer_size = tracefs_instance_get_buffer_size(instance->tracefs, 0);
+	ret = tracefs_instance_set_buffer_size(instance->tracefs, buffer_size, -1);
 	if (ret < 0)
-		warning("Can't write to %s", path);
-	close(fd);
- out:
-	tracefs_put_tracing_file(path);
+		warning("Can't set buffer size");
+}
+
+static void set_subbuf_size_instance(struct buffer_instance *instance)
+{
+	int subbuf_size = instance->subbuf_size;
+	int ret;
+
+	if (is_guest(instance))
+		return;
+
+	if (!subbuf_size)
+		return;
+
+	if (subbuf_size < 0)
+		die("sub-buffer size must be positive");
+
+	instance->old_subbuf_size = tracefs_instance_get_subbuf_size(instance->tracefs);
+	ret = tracefs_instance_set_subbuf_size(instance->tracefs, subbuf_size);
+	if (ret < 0)
+		warning("Can't set sub-buffer size");
 }
 
 void set_buffer_size(void)
 {
 	struct buffer_instance *instance;
 
-	for_all_instances(instance)
+	for_all_instances(instance) {
 		set_buffer_size_instance(instance);
+		set_subbuf_size_instance(instance);
+	}
 }
 
 static int
@@ -5878,6 +5947,11 @@
 	OPT_cmdlines_size	= 258,
 	OPT_poll		= 259,
 	OPT_name		= 260,
+	OPT_proxy		= 261,
+	OPT_temp		= 262,
+	OPT_notimeout		= 264,
+	OPT_daemonize		= 265,
+	OPT_subbuf		= 266,
 };
 
 void trace_stop(int argc, char **argv)
@@ -6142,7 +6216,8 @@
 		del = instances;
 		instances = instances->next;
 		free(del->name);
-		tracefs_instance_destroy(del->tracefs);
+		if (tracefs_instance_is_new(del->tracefs))
+			tracefs_instance_destroy(del->tracefs);
 		tracefs_instance_free(del->tracefs);
 		free(del);
 	}
@@ -6260,6 +6335,7 @@
 	char *sav;
 	int name_counter = 0;
 	int negative = 0;
+	bool is_proxy = false;
 	struct buffer_instance *instance, *del_list = NULL;
 	int do_children = 0;
 	int fpids_count = 0;
@@ -6287,6 +6363,7 @@
 			{"cmdlines-size", required_argument, NULL, OPT_cmdlines_size},
 			{"no-filter", no_argument, NULL, OPT_no_filter},
 			{"debug", no_argument, NULL, OPT_debug},
+			{"notimeout", no_argument, NULL, OPT_notimeout},
 			{"quiet", no_argument, NULL, OPT_quiet},
 			{"help", no_argument, NULL, '?'},
 			{"proc-map", no_argument, NULL, OPT_procmap},
@@ -6300,6 +6377,10 @@
 			{"verbose", optional_argument, NULL, OPT_verbose},
 			{"compression", required_argument, NULL, OPT_compression},
 			{"file-version", required_argument, NULL, OPT_file_ver},
+			{"proxy", required_argument, NULL, OPT_proxy},
+			{"temp", required_argument, NULL, OPT_temp},
+			{"subbuf-size", required_argument, NULL, OPT_subbuf},
+			{"daemonize", no_argument, NULL, OPT_daemonize},
 			{NULL, 0, NULL, 0}
 		};
 
@@ -6315,7 +6396,8 @@
 		 * If the current instance is to record a guest, then save
 		 * all the arguments for this instance.
 		 */
-		if (c != 'B' && c != 'A' && c != OPT_name && is_guest(ctx->instance)) {
+		if (c != 'B' && (c != 'A' || is_proxy) && c != OPT_name &&
+		    is_guest(ctx->instance) && c != OPT_proxy) {
 			add_arg(ctx->instance, c, opts, long_options, optarg);
 			if (c == 'C')
 				ctx->instance->flags |= BUFFER_FL_HAS_CLOCK;
@@ -6387,12 +6469,16 @@
 				die("Failed to allocate name");
 			break;
 
+		case OPT_proxy:
+			is_proxy = true;
+			/* fall through */
 		case 'A': {
 			char *name = NULL;
 			int cid = -1, port = -1;
 
 			if (!IS_RECORD(ctx))
-				die("-A is only allowed for record operations");
+				die("%s is only allowed for record operations",
+				    is_proxy ? "--proxy" : "-A");
 
 			name = parse_guest_name(optarg, &cid, &port, &result);
 			if (cid == -1 && !result)
@@ -6419,6 +6505,9 @@
 				ctx->instance->port_type = USE_TCP;
 			}
 
+			if (is_proxy)
+				ctx->instance->flags |= BUFFER_FL_PROXY;
+
 			ctx->instance->flags |= BUFFER_FL_GUEST;
 			ctx->instance->result = result;
 			ctx->instance->cid = cid;
@@ -6426,6 +6515,11 @@
 			ctx->instance->name = name;
 			add_instance(ctx->instance, 0);
 			ctx->data_flags |= DATA_FL_GUEST;
+
+			/* Do not send a clock to a proxy */
+			if (is_proxy)
+				ctx->instance->flags |= BUFFER_FL_HAS_CLOCK;
+
 			break;
 		}
 		case 'F':
@@ -6559,6 +6653,11 @@
 				}
 			}
 			break;
+		case OPT_temp:
+			if (ctx->temp)
+				die("Only one temp directory can be listed");
+			ctx->temp = optarg;
+			break;
 		case 'O':
 			check_instance_die(ctx->instance, "-O");
 			option = optarg;
@@ -6641,10 +6740,13 @@
 			ctx->instance->buffer_size = atoi(optarg);
 			break;
 		case 'B':
+			/* Turn off proxy for the next options */
+			is_proxy = false;
 			ctx->instance = allocate_instance(optarg);
 			if (!ctx->instance)
 				die("Failed to create instance");
-			ctx->instance->delete = negative;
+			if (IS_CMDSET(ctx))
+				ctx->instance->delete = negative;
 			negative = 0;
 			if (ctx->instance->delete) {
 				ctx->instance->next = del_list;
@@ -6733,6 +6835,9 @@
 		case OPT_debug:
 			tracecmd_set_debug(true);
 			break;
+		case OPT_notimeout:
+			tracecmd_set_notimeout(true);
+			break;
 		case OPT_module:
 			check_instance_die(ctx->instance, "--module");
 			if (ctx->instance->filter_mod)
@@ -6750,6 +6855,11 @@
 				die("--fork option used for 'start' command only");
 			fork_process = true;
 			break;
+		case OPT_daemonize:
+			if (!IS_RECORD(ctx))
+				die("--daemonize option used for 'record' command only");
+			do_daemonize = true;
+			break;
 		case OPT_tsc2nsec:
 			ret = get_tsc_nsec(&ctx->tsc2nsec.shift,
 					   &ctx->tsc2nsec.mult);
@@ -6757,6 +6867,10 @@
 				die("TSC to nanosecond is not supported");
 			ctx->instance->flags |= BUFFER_FL_TSC2NSEC;
 			break;
+		case OPT_subbuf:
+			check_instance_die(ctx->instance, "--subbuf-size");
+			ctx->instance->subbuf_size = atoi(optarg);
+			break;
 		case OPT_poll:
 			cmd_check_die(ctx, CMD_set, *(argv+1), "--poll");
 			recorder_flags |= TRACECMD_RECORD_POLL;
@@ -6764,7 +6878,6 @@
 		case OPT_compression:
 			cmd_check_die(ctx, CMD_start, *(argv+1), "--compression");
 			cmd_check_die(ctx, CMD_set, *(argv+1), "--compression");
-			cmd_check_die(ctx, CMD_extract, *(argv+1), "--compression");
 			cmd_check_die(ctx, CMD_stream, *(argv+1), "--compression");
 			cmd_check_die(ctx, CMD_profile, *(argv+1), "--compression");
 			if (strcmp(optarg, "any") && strcmp(optarg, "none") &&
@@ -6890,6 +7003,16 @@
 		if (instance->flags & BUFFER_FL_KEEP)
 			write_tracing_on(instance,
 					 instance->tracing_on_init_val);
+		if (is_proxy_server(instance) && instance->network_handle) {
+			/* Now wait for the recorder to be ready for us to send more */
+			tracecmd_msg_wait(ctx->instance->msg_handle);
+			if (ctx->tsc2nsec.mult)
+				add_tsc2nsec(ctx->instance->network_handle, &ctx->tsc2nsec);
+			tracecmd_write_guest_time_shift(ctx->instance->network_handle,
+							ctx->instance->tsync);
+			tracecmd_msg_send_options(ctx->instance->msg_handle,
+						  ctx->instance->network_handle);
+		}
 		if (is_agent(instance)) {
 			tracecmd_msg_send_close_resp_msg(instance->msg_handle);
 			tracecmd_output_close(instance->network_handle);
@@ -6984,6 +7107,9 @@
 	struct buffer_instance *instance;
 	struct filter_pids *pid;
 
+	if (do_daemonize)
+		daemonize_start();
+
 	/*
 	 * If top_instance doesn't have any plugins or events, then
 	 * remove it from being processed.
@@ -7002,14 +7128,22 @@
 	if (!ctx->output)
 		ctx->output = DEFAULT_INPUT_FILE;
 
-	if (ctx->data_flags & DATA_FL_GUEST)
+	/* Make sure top_instance.output_file exists */
+	if (!top_instance.output_file)
+		top_instance.output_file = strdup(ctx->output);
+
+	if (ctx->data_flags & (DATA_FL_GUEST | DATA_FL_PROXY))
 		set_tsync_params(ctx);
 
 	make_instances();
 
 	/* Save the state of tracing_on before starting */
 	for_all_instances(instance) {
-		instance->output_file = strdup(ctx->output);
+		if (ctx->temp)
+			instance->temp_dir = ctx->temp;
+		/* The -o could have been done after -B */
+		if (!instance->output_file)
+			instance->output_file = strdup(ctx->output);
 		if (!instance->output_file)
 			die("Failed to allocate output file name for instance");
 		if (!ctx->manual && instance->flags & BUFFER_FL_PROFILE)
@@ -7066,7 +7200,8 @@
 	allocate_seq();
 
 	if (type & (TRACE_TYPE_RECORD | TRACE_TYPE_STREAM)) {
-		signal(SIGINT, finish);
+		signal(SIGINT, do_sig);
+		signal(SIGTERM, do_sig);
 		if (!latency)
 			start_threads(type, ctx);
 	}
@@ -7100,8 +7235,15 @@
 				}
 			}
 		}
-		/* sleep till we are woken with Ctrl^C */
-		printf("Hit Ctrl^C to stop recording\n");
+
+		if (do_daemonize) {
+			daemonize_finish();
+			printf("Send SIGINT/SIGTERM to pid %d to stop recording\n", getpid());
+		} else {
+			/* sleep till we are woken with Ctrl^C */
+			printf("Hit Ctrl^C to stop recording\n");
+		}
+
 		for_all_instances(instance) {
 			/* If an instance is not tracing individual processes
 			 * or there is an error while waiting for a process to
@@ -7113,6 +7255,9 @@
 		}
 		while (!finished && wait_indefinitely)
 			trace_or_sleep(type, pwait);
+		/* Streams need to be flushed one more time */
+		if (type & TRACE_TYPE_STREAM)
+			trace_stream_read(pids, recorder_threads, -1);
 	}
 
 	tell_guests_to_stop(ctx);
@@ -7125,6 +7270,16 @@
 	if (!latency)
 		wait_threads();
 
+	if (is_proxy_server(ctx->instance) && ctx->instance->network_handle) {
+		tracecmd_tsync_with_guest_stop(ctx->instance->tsync);
+		trace_add_guest_info(ctx->instance->network_handle, ctx->instance);
+		if (ctx->tsc2nsec.mult)
+			add_tsc2nsec(ctx->instance->network_handle, &ctx->tsc2nsec);
+		tracecmd_write_options(ctx->instance->network_handle);
+		tracecmd_write_meta_strings(ctx->instance->network_handle);
+		tracecmd_msg_finish_sending_data(ctx->instance->msg_handle);
+	}
+
 	if (IS_RECORD(ctx)) {
 		record_data(ctx);
 		delete_thread_data();
@@ -7136,6 +7291,9 @@
 
 	destroy_stats();
 	finalize_record_trace(ctx);
+
+	if (created_pidfile)
+		remove_pid_file(RECORD_PIDFILE);
 }
 
 /*
@@ -7177,7 +7335,7 @@
 
 	type = get_trace_cmd_type(ctx.curr_cmd);
 
-	update_first_instance(ctx.instance, 1);
+	update_first_instance(ctx.instance, ctx.topt);
 	check_function_plugin();
 
 	if (!ctx.output)
@@ -7245,11 +7403,15 @@
 {
 	struct common_record_context ctx;
 
+	/* Default sleep time is half a second for streaming */
+	sleep_time = 500000;
+
 	parse_record_options(argc, argv, CMD_stream, &ctx);
 	record_trace_command(argc, argv, &ctx);
 	exit(0);
 }
 
+
 void trace_profile(int argc, char **argv)
 {
 	struct common_record_context ctx;
@@ -7279,11 +7441,30 @@
 	exit(0);
 }
 
+/**
+ * trace_record_agent - record command running from the agent
+ * @msg_handle: The handle to communicate with the peer
+ * @cpus: The number of CPUs the agent has to record
+ * @fds: The array of file descriptors for the CPUs
+ * @argc: The number of arguments to pass to the record session
+ * @argv: The arguments to pass to the record session
+ * @use_fifos: True if fifos are used instead of sockets.
+ * @trace_id: The agent's trace_id
+ * @rcid: Remote cid if the agent is a proxy, negative otherwise.
+ * @host: Set if this is an IP connection and not a vsocket one
+ *
+ * This is used to enable tracing via the record command just
+ * like trace-cmd record, but it is being done via the agent
+ * and all the data is being transfered to the peer that is
+ * connected on the other end of the sockets.
+ *
+ *  Returns zero on success, negative otherwise.
+ */
 int trace_record_agent(struct tracecmd_msg_handle *msg_handle,
 		       int cpus, int *fds,
 		       int argc, char **argv,
-		       bool use_fifos,
-		       unsigned long long trace_id, const char *host)
+		       bool use_fifos, struct tracecmd_time_sync *tsync,
+		       unsigned long long trace_id, int rcid, const char *host)
 {
 	struct common_record_context ctx;
 	char **argv_plus;
@@ -7311,8 +7492,12 @@
 	ctx.instance->fds = fds;
 	ctx.instance->use_fifos = use_fifos;
 	ctx.instance->flags |= BUFFER_FL_AGENT;
+	if (rcid >= 0)
+		ctx.data_flags |= DATA_FL_PROXY;
 	ctx.instance->msg_handle = msg_handle;
 	ctx.instance->host = host;
+	ctx.instance->tsync = tsync;
+	ctx.instance->cid = rcid;
 	msg_handle->version = V3_PROTOCOL;
 	top_instance.trace_id = trace_id;
 	record_trace(argc, argv, &ctx);
diff --git a/tracecmd/trace-restore.c b/tracecmd/trace-restore.c
index 5bf29c5..e9ca322 100644
--- a/tracecmd/trace-restore.c
+++ b/tracecmd/trace-restore.c
@@ -3,7 +3,6 @@
  * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
  *
  */
-#define _LARGEFILE64_SOURCE
 #include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/tracecmd/trace-setup-guest.c b/tracecmd/trace-setup-guest.c
index f20b48e..daeb4e6 100644
--- a/tracecmd/trace-setup-guest.c
+++ b/tracecmd/trace-setup-guest.c
@@ -121,7 +121,7 @@
 		"  <target type='virtio' name='%s%d'/>\n"
 		"</channel>";
 	char tmp_path[PATH_MAX], path[PATH_MAX];
-	char cmd[PATH_MAX], xml[PATH_MAX];
+	char cmd[PATH_MAX + 256], xml[PATH_MAX + 256];
 	int i, fd, ret = 0;
 
 #ifdef __ANDROID__
diff --git a/tracecmd/trace-show.c b/tracecmd/trace-show.c
index eb32852..cf70d86 100644
--- a/tracecmd/trace-show.c
+++ b/tracecmd/trace-show.c
@@ -11,16 +11,20 @@
 #include "trace-local.h"
 
 enum {
-	OPT_tracing_on			= 255,
-	OPT_current_tracer		= 254,
-	OPT_buffer_size_kb		= 253,
-	OPT_buffer_total_size_kb	= 252,
-	OPT_ftrace_filter		= 251,
-	OPT_ftrace_notrace		= 250,
-	OPT_ftrace_pid			= 249,
-	OPT_graph_function		= 248,
-	OPT_graph_notrace		= 247,
-	OPT_cpumask			= 246,
+	OPT_cpumask			= 240,
+	OPT_graph_notrace,
+	OPT_graph_function,
+	OPT_ftrace_pid,
+	OPT_ftrace_notrace,
+	OPT_ftrace_filter,
+	OPT_buffer_subbuf_size_kb,
+	OPT_buffer_total_size_kb,
+	OPT_buffer_size_kb,
+	OPT_buffer_percent,
+	OPT_current_tracer,
+	OPT_tracing_on,
+	OPT_hist,
+	OPT_trigger,
 };
 
 void trace_show(int argc, char **argv)
@@ -29,6 +33,8 @@
 	const char *file = "trace";
 	const char *cpu = NULL;
 	struct buffer_instance *instance = &top_instance;
+	char *hist = NULL;
+	char *trigger = NULL;
 	char cpu_path[128];
 	char *path;
 	int snap = 0;
@@ -38,10 +44,14 @@
 	int stop = 0;
 	int c;
 	static struct option long_options[] = {
+		{"hist", required_argument, NULL, OPT_hist},
+		{"trigger", required_argument, NULL, OPT_trigger},
 		{"tracing_on", no_argument, NULL, OPT_tracing_on},
 		{"current_tracer", no_argument, NULL, OPT_current_tracer},
 		{"buffer_size", no_argument, NULL, OPT_buffer_size_kb},
 		{"buffer_total_size", no_argument, NULL, OPT_buffer_total_size_kb},
+		{"buffer_subbuf_size", no_argument, NULL, OPT_buffer_subbuf_size_kb},
+		{"buffer_percent", no_argument, NULL, OPT_buffer_percent},
 		{"ftrace_filter", no_argument, NULL, OPT_ftrace_filter},
 		{"ftrace_notrace", no_argument, NULL, OPT_ftrace_notrace},
 		{"ftrace_pid", no_argument, NULL, OPT_ftrace_pid},
@@ -86,6 +96,13 @@
 			if (snap)
 				die("Can not have -s and -p together");
 			break;
+		case OPT_hist:
+			hist = optarg;
+			break;
+		case OPT_trigger:
+			trigger = optarg;
+			break;
+
 		case OPT_tracing_on:
 			show_instance_file(instance, "tracing_on");
 			stop = 1;
@@ -102,6 +119,14 @@
 			show_instance_file(instance, "buffer_total_size_kb");
 			stop = 1;
 			break;
+		case OPT_buffer_subbuf_size_kb:
+			show_instance_file(instance, "buffer_subbuf_size_kb");
+			stop = 1;
+			break;
+		case OPT_buffer_percent:
+			show_instance_file(instance, "buffer_percent");
+			stop = 1;
+			break;
 		case OPT_ftrace_filter:
 			show_instance_file(instance, "set_ftrace_filter");
 			stop = 1;
@@ -137,6 +162,44 @@
 	else if (snap)
 		file = "snapshot";
 
+	if (hist || trigger) {
+		char **systems = NULL;
+		char *system = NULL;
+		char *event = hist ? hist : trigger;
+		char *file = hist ? "hist" : "trigger";
+		char *p;
+
+		if ((p = strstr(event, ":"))) {
+			system = event;
+			event = p + 1;
+			*p = '\0';
+		}
+
+		if (!system) {
+			systems = tracefs_event_systems(NULL);
+
+			for (int i = 0; systems && systems[i]; i++) {
+				system = systems[i];
+				if (tracefs_event_file_exists(instance->tracefs,
+							      system, event, file))
+					break;
+			}
+			if (!system)
+				die("Could not find system of event %s",
+				    event);
+		}
+
+		path = tracefs_event_file_read(instance->tracefs,
+					       system, event, file, NULL);
+		tracefs_list_free(systems);
+		if (!path)
+			die("Could not find hist for %s%s%s",
+			    system ? system : "", system ? ":":"", event);
+		printf("%s\n", path);
+		free(path);
+		exit(0);
+	}
+
 	if (cpu) {
 		char *endptr;
 		long val;
diff --git a/tracecmd/trace-split.c b/tracecmd/trace-split.c
index 83c5402..13a10c8 100644
--- a/tracecmd/trace-split.c
+++ b/tracecmd/trace-split.c
@@ -3,7 +3,6 @@
  * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
  *
  */
-#define _LARGEFILE64_SOURCE
 #include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -20,10 +19,12 @@
 #include <ctype.h>
 #include <errno.h>
 
+#include "list.h"
 #include "trace-local.h"
 
 static unsigned int page_size;
 static const char *default_input_file = DEFAULT_INPUT_FILE;
+static const char *default_top_instance_name = "top";
 static const char *input_file;
 
 enum split_types {
@@ -40,6 +41,7 @@
 struct cpu_data {
 	unsigned long long		ts;
 	unsigned long long		offset;
+	unsigned long long		missed_events;
 	struct tep_record		*record;
 	int				cpu;
 	int				fd;
@@ -49,6 +51,115 @@
 	char				*file;
 };
 
+struct handle_list {
+	struct list_head		list;
+	char				*name;
+	int				index;
+	struct tracecmd_input		*handle;
+
+	/* Identify the top instance in the input trace. */
+	bool				was_top_instance;
+};
+
+static struct list_head handle_list;
+
+/**
+ * get_handle - Obtain a handle that must be closed once finished.
+ */
+static struct tracecmd_input *get_handle(struct handle_list *item)
+{
+	struct tracecmd_input *top_handle, *handle;
+
+	top_handle = tracecmd_open(input_file, 0);
+	if (!top_handle)
+		die("Error reading %s", input_file);
+
+	if (item->was_top_instance) {
+		return top_handle;
+	} else {
+		handle = tracecmd_buffer_instance_handle(top_handle, item->index);
+		if (!handle)
+			warning("Could not retrieve handle %s", item->name);
+
+		tracecmd_close(top_handle);
+		return handle;
+	}
+}
+
+static void add_handle(const char *name, int index, bool was_top_instance)
+{
+	struct handle_list *item;
+
+	item = calloc(1, sizeof(*item));
+	if (!item)
+		die("Failed to allocate handle item");
+
+	item->name = strdup(name);
+	if (!item->name)
+		die("Failed to duplicate %s", name);
+
+	item->index = index;
+	item->was_top_instance = was_top_instance;
+	item->handle = get_handle(item);
+	list_add_tail(&item->list, &handle_list);
+}
+
+static void free_handles(struct list_head *list)
+{
+	struct handle_list *item, *n;
+
+	list_for_each_entry_safe(item, n, list, list) {
+		list_del(&item->list);
+		free(item->name);
+		tracecmd_close(item->handle);
+		free(item);
+	}
+}
+
+static struct list_head inst_list;
+
+struct inst_list {
+	struct list_head		list;
+	char				*name;
+	struct handle_list		*handle;
+
+	/* Identify the top instance in the input trace. */
+	bool				was_top_instance;
+
+	/* Identify the top instance in the output trace. */
+	bool				is_top_instance;
+};
+
+static void free_inst(struct list_head *list)
+{
+	struct inst_list *item, *n;
+
+	list_for_each_entry_safe(item, n, list, list) {
+		list_del(&item->list);
+		free(item->name);
+		free(item);
+	}
+}
+
+static struct inst_list *add_inst(const char *name, bool was_top_instance,
+				  bool is_top_instance)
+{
+	struct inst_list *item;
+
+	item = calloc(1, sizeof(*item));
+	if (!item)
+		die("Failed to allocate output_file item");
+
+	item->name = strdup(name);
+	if (!item->name)
+		die("Failed to duplicate %s", name);
+
+	item->was_top_instance = was_top_instance;
+	item->is_top_instance = is_top_instance;
+	list_add_tail(&item->list, &inst_list);
+	return item;
+}
+
 static int create_type_len(struct tep_handle *pevent, int time, int len)
 {
 	static int bigendian = -1;
@@ -137,18 +248,37 @@
 	return 1;
 }
 
+#define MISSING_EVENTS (1UL << 31)
+#define MISSING_STORED (1UL << 30)
+
+#define COMMIT_MASK ((1 << 27) - 1)
+
 static void write_page(struct tep_handle *pevent,
 		       struct cpu_data *cpu_data, int long_size)
 {
+	unsigned long long *ptr = NULL;
+	unsigned int flags = 0;
+
+	if (cpu_data->missed_events) {
+		flags |= MISSING_EVENTS;
+		if (cpu_data->missed_events > 0) {
+			flags |= MISSING_STORED;
+			ptr = cpu_data->page + cpu_data->index;
+		}
+	}
+
 	if (long_size == 8) {
-		unsigned long long index = cpu_data->index - 16;
+		unsigned long long index = cpu_data->index - 16 + flags;;
 		*(unsigned long long *)cpu_data->commit =
 				tep_read_number(pevent, &index, 8);
 	} else {
-		unsigned int index = cpu_data->index - 12;
+		unsigned int index = cpu_data->index - 12 + flags;;
 		*(unsigned int *)cpu_data->commit =
 			tep_read_number(pevent, &index, 4);
 	}
+	if (ptr)
+		*ptr = tep_read_number(pevent, &cpu_data->missed_events, 8);
+
 	write(cpu_data->fd, cpu_data->page, page_size);
 }
 
@@ -179,7 +309,7 @@
 		     unsigned long long start,
 		     unsigned long long end,
 		     int count_limit, int percpu, int cpu,
-		     enum split_types type)
+		     enum split_types type, bool *end_reached)
 {
 	struct tep_record *record;
 	struct tep_handle *pevent;
@@ -224,7 +354,8 @@
 		start = record->ts;
 
 	while (record && (!end || record->ts <= end)) {
-		if (cpu_data[cpu].index + record->record_size > page_size) {
+		if ((cpu_data[cpu].index + record->record_size > page_size) ||
+		    record->missed_events) {
 
 			if (type == SPLIT_PAGES && ++pages > count_limit)
 				break;
@@ -237,6 +368,8 @@
 					die("Failed to allocate page");
 			}
 
+			cpu_data[cpu].missed_events = record->missed_events;
+
 			memset(cpu_data[cpu].page, 0, page_size);
 			ptr = cpu_data[cpu].page;
 
@@ -303,6 +436,11 @@
 		}
 	}
 
+	if (record && (record->ts > end))
+		*end_reached = true;
+	else
+		*end_reached = false;
+
 	if (record)
 		tracecmd_free_record(record);
 
@@ -325,103 +463,238 @@
 	return 0;
 }
 
-static double parse_file(struct tracecmd_input *handle,
-			 const char *output_file,
-			 unsigned long long start,
-			 unsigned long long end, int percpu, int only_cpu,
-			 int count, enum split_types type)
+static char *get_temp_file(const char *output_file, const char *name, int cpu)
 {
-	unsigned long long current;
-	struct tracecmd_output *ohandle;
-	struct cpu_data *cpu_data;
-	struct tep_record *record;
-	char **cpu_list;
+	const char *dot;
+	char *file = NULL;
 	char *output;
 	char *base;
-	char *file;
 	char *dir;
-	int cpus;
-	int cpu;
-	int fd;
+	int ret;
+
+	if (name)
+		dot = ".";
+	else
+		dot = name = "";
 
 	output = strdup(output_file);
-	dir = dirname(output);
+	if (!output)
+		die("Failed to duplicate %s", output_file);
+
+	/* Extract basename() first, as dirname() truncates output */
 	base = basename(output);
+	dir = dirname(output);
+
+	ret = asprintf(&file, "%s/.tmp.%s.%s%s%d", dir, base, name, dot, cpu);
+	if (ret < 0)
+		die("Failed to allocate file for %s %s %s %d", dir, base, name, cpu);
+	free(output);
+	return file;
+}
+
+static void delete_temp_file(const char *name)
+{
+	unlink(name);
+}
+
+static void put_temp_file(char *file)
+{
+	free(file);
+}
+
+static void touch_file(const char *file)
+{
+	int fd;
+
+	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+	if (fd < 0)
+		die("could not create file %s\n", file);
+	close(fd);
+}
+
+static unsigned long long parse_file(struct tracecmd_input *handle,
+				     const char *output_file,
+				     unsigned long long start,
+				     unsigned long long end, int percpu,
+				     int only_cpu, int count,
+				     enum split_types type,
+				     bool *end_reached)
+{
+	unsigned long long current = 0;
+	struct tracecmd_output *ohandle;
+	struct inst_list *inst_entry;
+	struct cpu_data *cpu_data;
+	struct tep_record *record;
+	bool all_end_reached = true;
+	char **cpu_list;
+	char *file;
+	int cpus;
+	int cpu;
+	int ret;
+	int fd;
 
 	ohandle = tracecmd_copy(handle, output_file, TRACECMD_FILE_CMD_LINES, 0, NULL);
-
-	cpus = tracecmd_cpus(handle);
-	cpu_data = malloc(sizeof(*cpu_data) * cpus);
-	if (!cpu_data)
-		die("Failed to allocate cpu_data for %d cpus", cpus);
-
-	for (cpu = 0; cpu < cpus; cpu++) {
-		int ret;
-
-		ret = asprintf(&file, "%s/.tmp.%s.%d", dir, base, cpu);
-		if (ret < 0)
-			die("Failed to allocate file for %s %s %d", dir, base, cpu);
-		fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
-		cpu_data[cpu].cpu = cpu;
-		cpu_data[cpu].fd = fd;
-		cpu_data[cpu].file = file;
-		cpu_data[cpu].offset = 0;
-		if (start)
-			tracecmd_set_cpu_to_timestamp(handle, cpu, start);
-	}
-
-	if (only_cpu >= 0) {
-		parse_cpu(handle, cpu_data, start, end, count,
-			  1, only_cpu, type);
-	} else if (percpu) {
-		for (cpu = 0; cpu < cpus; cpu++)
-			parse_cpu(handle, cpu_data, start,
-				  end, count, percpu, cpu, type);
-	} else
-		parse_cpu(handle, cpu_data, start,
-			  end, count, percpu, -1, type);
-
-	cpu_list = malloc(sizeof(*cpu_list) * cpus);
-	if (!cpu_list)
-		die("Failed to allocate cpu_list for %d cpus", cpus);
-	for (cpu = 0; cpu < cpus; cpu ++)
-		cpu_list[cpu] = cpu_data[cpu].file;
-
 	tracecmd_set_out_clock(ohandle, tracecmd_get_trace_clock(handle));
-	if (tracecmd_append_cpu_data(ohandle, cpus, cpu_list) < 0)
-		die("Failed to append tracing data\n");
 
-	current = end;
-	for (cpu = 0; cpu < cpus; cpu++) {
-		/* Set the tracecmd cursor to the next set of records */
-		if (cpu_data[cpu].offset) {
-			record = tracecmd_read_at(handle, cpu_data[cpu].offset, NULL);
-			if (record && (!current || record->ts > current))
-				current = record->ts + 1;
-			tracecmd_free_record(record);
+	list_for_each_entry(inst_entry, &inst_list, list) {
+		struct tracecmd_input *curr_handle;
+		bool curr_end_reached = false;
+
+		curr_handle = inst_entry->handle->handle;
+		cpus = tracecmd_cpus(curr_handle);
+		cpu_data = malloc(sizeof(*cpu_data) * cpus);
+		if (!cpu_data)
+			die("Failed to allocate cpu_data for %d cpus", cpus);
+
+		for (cpu = 0; cpu < cpus; cpu++) {
+			file = get_temp_file(output_file, inst_entry->name, cpu);
+			touch_file(file);
+
+			fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
+			cpu_data[cpu].cpu = cpu;
+			cpu_data[cpu].fd = fd;
+			cpu_data[cpu].file = file;
+			cpu_data[cpu].offset = 0;
+			if (start)
+				tracecmd_set_cpu_to_timestamp(curr_handle, cpu, start);
 		}
-		unlink(cpu_data[cpu].file);
-		free(cpu_data[cpu].file);
+
+		if (only_cpu >= 0) {
+			parse_cpu(curr_handle, cpu_data, start, end, count,
+				  1, only_cpu, type, &curr_end_reached);
+		} else if (percpu) {
+			for (cpu = 0; cpu < cpus; cpu++)
+				parse_cpu(curr_handle, cpu_data, start,
+					  end, count, percpu, cpu, type, &curr_end_reached);
+		} else {
+			parse_cpu(curr_handle, cpu_data, start,
+				  end, count, percpu, -1, type, &curr_end_reached);
+		}
+
+		/* End is reached when all instances finished. */
+		all_end_reached &= curr_end_reached;
+
+		cpu_list = malloc(sizeof(*cpu_list) * cpus);
+		if (!cpu_list)
+			die("Failed to allocate cpu_list for %d cpus", cpus);
+		for (cpu = 0; cpu < cpus; cpu++)
+			cpu_list[cpu] = cpu_data[cpu].file;
+
+		if (inst_entry->is_top_instance)
+			ret = tracecmd_append_cpu_data(ohandle, cpus, cpu_list);
+		else
+			ret = tracecmd_append_buffer_cpu_data(ohandle, inst_entry->name, cpus,
+							      cpu_list);
+		if (ret < 0)
+			die("Failed to append tracing data\n");
+
+		for (cpu = 0; cpu < cpus; cpu++) {
+			/* Set the tracecmd cursor to the next set of records */
+			if (cpu_data[cpu].offset) {
+				record = tracecmd_read_at(curr_handle, cpu_data[cpu].offset, NULL);
+				if (record && (!current || record->ts > current))
+					current = record->ts + 1;
+				tracecmd_free_record(record);
+			}
+		}
+
+		for (cpu = 0; cpu < cpus; cpu++) {
+			close(cpu_data[cpu].fd);
+			delete_temp_file(cpu_data[cpu].file);
+			put_temp_file(cpu_data[cpu].file);
+		}
+		free(cpu_data);
+		free(cpu_list);
 	}
-	free(cpu_data);
-	free(cpu_list);
-	free(output);
+
 	tracecmd_output_close(ohandle);
 
+	*end_reached = all_end_reached;
 	return current;
 }
 
+/* Map the instance names to their handle. */
+static void map_inst_handle(void)
+{
+	struct handle_list *handle_entry;
+	struct inst_list *inst_entry;
+
+	/*
+	 * No specific instance was given for this output file.
+	 * Add all the available instances.
+	 */
+	if (list_empty(&inst_list)) {
+		list_for_each_entry(handle_entry, &handle_list, list) {
+			add_inst(handle_entry->name, handle_entry->was_top_instance,
+				 handle_entry->was_top_instance);
+		}
+	}
+
+	list_for_each_entry(inst_entry, &inst_list, list) {
+		list_for_each_entry(handle_entry, &handle_list, list) {
+			if ((inst_entry->was_top_instance &&
+			     handle_entry->was_top_instance) ||
+			    (!inst_entry->was_top_instance &&
+			     !strcmp(handle_entry->name, inst_entry->name))) {
+				inst_entry->handle = handle_entry;
+				goto found;
+			}
+		}
+
+		warning("Requested instance %s was not found in trace.", inst_entry->name);
+		break;
+found:
+		continue;
+	}
+}
+
+static bool is_top_instance_unique(void)
+{
+	struct inst_list *inst_entry;
+	bool has_top_buffer = false;
+
+	/* Check there is at most one top buffer. */
+	list_for_each_entry(inst_entry, &inst_list, list) {
+		if (inst_entry->is_top_instance) {
+			if (has_top_buffer)
+				return false;
+			has_top_buffer = true;
+		}
+	}
+
+	return true;
+}
+
+enum {
+	OPT_top = 237,
+};
+
+/*
+ * Used to identify the arg. previously parsed.
+ * E.g. '-b' can only follow '--top'.
+ */
+enum prev_arg_type {
+	PREV_IS_NONE,
+	PREV_IS_TOP,
+	PREV_IS_BUFFER,
+};
+
 void trace_split (int argc, char **argv)
 {
 	struct tracecmd_input *handle;
 	unsigned long long start_ns = 0, end_ns = 0;
 	unsigned long long current;
+	enum prev_arg_type prev_arg_type;
+	struct inst_list *prev_inst = NULL;
+	int prev_arg_idx;
+	bool end_reached = false;
 	double start, end;
 	char *endptr;
 	char *output = NULL;
 	char *output_file;
 	enum split_types split_type = SPLIT_NONE;
 	enum split_types type = SPLIT_NONE;
+	int instances;
 	int count;
 	int repeat = 0;
 	int percpu = 0;
@@ -429,10 +702,22 @@
 	int ac;
 	int c;
 
+	static struct option long_options[] = {
+		{"top", optional_argument, NULL, OPT_top},
+		{NULL, 0, NULL, 0},
+	};
+	int option_index = 0;
+
+	prev_arg_type = PREV_IS_NONE;
+
+	list_head_init(&handle_list);
+	list_head_init(&inst_list);
+
 	if (strcmp(argv[1], "split") != 0)
 		usage(argv);
 
-	while ((c = getopt(argc-1, argv+1, "+ho:i:s:m:u:e:p:rcC:")) >= 0) {
+	while ((c = getopt_long(argc - 1, argv + 1, "+ho:i:s:m:u:e:p:rcC:B:b:t",
+				long_options, &option_index)) >= 0) {
 		switch (c) {
 		case 'h':
 			usage(argv);
@@ -475,11 +760,47 @@
 		case 'i':
 			input_file = optarg;
 			break;
+		case OPT_top:
+			prev_arg_type = PREV_IS_TOP;
+			prev_arg_idx = optind;
+			prev_inst = add_inst(default_top_instance_name, true, true);
+			break;
+		case 'b':
+			/* 1 as --top takes no argument. */
+			if (prev_arg_type != PREV_IS_TOP &&
+			    (prev_arg_idx != optind - 1))
+				usage(argv);
+			prev_arg_type = PREV_IS_NONE;
+
+			prev_inst->is_top_instance = false;
+
+			free(prev_inst->name);
+			prev_inst->name = strdup(optarg);
+			if (!prev_inst->name)
+				die("Failed to duplicate %s", optarg);
+			break;
+		case 'B':
+			prev_arg_type = PREV_IS_BUFFER;
+			prev_arg_idx = optind;
+			prev_inst = add_inst(optarg, false, false);
+			break;
+		case 't':
+			/* 2 as -B takes an argument. */
+			if (prev_arg_type != PREV_IS_BUFFER &&
+			    (prev_arg_idx != optind - 2))
+				usage(argv);
+			prev_arg_type = PREV_IS_NONE;
+
+			prev_inst->is_top_instance = true;
+			break;
 		default:
 			usage(argv);
 		}
 	}
 
+	if (!is_top_instance_unique())
+		die("Can only have one top instance.");
+
 	ac = (argc - optind);
 
 	if (ac >= 2) {
@@ -523,17 +844,32 @@
 	if (!output)
 		output = strdup(input_file);
 
-	if (!repeat) {
+	if (!repeat && strcmp(output, input_file) == 0) {
 		output = realloc(output, strlen(output) + 3);
 		strcat(output, ".1");
 	}
 
-	current = start_ns;
 	output_file = malloc(strlen(output) + 50);
 	if (!output_file)
 		die("Failed to allocate for %s", output);
 	c = 1;
 
+	add_handle(default_top_instance_name, -1, true);
+	instances = tracecmd_buffer_instances(handle);
+	if (instances) {
+		const char *name;
+		int i;
+
+		for (i = 0; i < instances; i++) {
+			name = tracecmd_buffer_instance_name(handle, i);
+			if (!name)
+				die("error in reading buffer instance");
+			add_handle(name, i, false);
+		}
+	}
+
+	map_inst_handle();
+
 	do {
 		if (repeat)
 			sprintf(output_file, "%s.%04d", output, c++);
@@ -541,16 +877,19 @@
 			strcpy(output_file, output);
 			
 		current = parse_file(handle, output_file, start_ns, end_ns,
-				     percpu, cpu, count, type);
+				     percpu, cpu, count, type, &end_reached);
+
 		if (!repeat)
 			break;
 		start_ns = 0;
-	} while (current && (!end_ns || current < end_ns));
+	} while (!end_reached && (current && (!end_ns || current < end_ns)));
 
 	free(output);
 	free(output_file);
 
 	tracecmd_close(handle);
+	free_handles(&handle_list);
+	free_inst(&inst_list);
 
 	return;
 }
diff --git a/tracecmd/trace-sqlhist.c b/tracecmd/trace-sqlhist.c
new file mode 100644
index 0000000..f53feb2
--- /dev/null
+++ b/tracecmd/trace-sqlhist.c
@@ -0,0 +1,246 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <tracefs.h>
+
+#include "trace-local.h"
+
+enum action {
+	ACTION_DEFAULT	= 0,
+	ACTION_SNAPSHOT	= (1 << 0),
+	ACTION_TRACE	= (1 << 1),
+	ACTION_SAVE	= (1 << 2),
+	ACTION_MAX	= (1 << 3),
+	ACTION_CHANGE	= (1 << 4),
+};
+
+#define ACTIONS ((ACTION_MAX - 1))
+
+static int do_sql(const char *instance_name,
+		  const char *buffer, const char *name, const char *var,
+		  const char *trace_dir, bool execute, int action,
+		  char **save_fields)
+{
+	struct tracefs_synth *synth;
+	struct tep_handle *tep;
+	struct trace_seq seq;
+	enum tracefs_synth_handler handler;
+	char *err;
+	int ret;
+
+	if ((action & ACTIONS) && !var)
+		die("Error: -s, -S and -T not supported without -m or -c");
+
+	if (!name)
+		name = "Anonymous";
+
+	trace_seq_init(&seq);
+	tep = tracefs_local_events(trace_dir);
+	if (!tep)
+		die("Could not read %s", trace_dir ? trace_dir : "tracefs directory");
+
+	synth = tracefs_sql(tep, name, buffer, &err);
+	if (!synth)
+		die("Failed creating synthetic event!\n%s", err ? err : "");
+
+	if (tracefs_synth_complete(synth)) {
+		if (var) {
+			if (action & ACTION_MAX)
+				handler = TRACEFS_SYNTH_HANDLE_MAX;
+			else
+				handler = TRACEFS_SYNTH_HANDLE_CHANGE;
+
+			/* Default to trace if other actions are not set */
+			if (!(action & (ACTION_SAVE | ACTION_SNAPSHOT)))
+				action |= ACTION_TRACE;
+
+			if (action & ACTION_SAVE) {
+				ret = tracefs_synth_save(synth, handler, var, save_fields);
+				if (ret < 0) {
+					err = "adding save";
+					goto failed_action;
+				}
+			}
+			if (action & ACTION_TRACE) {
+				/*
+				 * By doing the trace before snapshot, it will be included
+				 * in the snapshot.
+				 */
+				ret = tracefs_synth_trace(synth, handler, var);
+				if (ret < 0) {
+					err = "adding trace";
+					goto failed_action;
+				}
+			}
+			if (action & ACTION_SNAPSHOT) {
+				ret = tracefs_synth_snapshot(synth, handler, var);
+				if (ret < 0) {
+					err = "adding snapshot";
+ failed_action:
+					perror(err);
+					if (errno == ENODEV)
+						fprintf(stderr, "ERROR: '%s' is not a variable\n",
+							var);
+					exit(-1);
+				}
+			}
+		}
+		tracefs_synth_echo_cmd(&seq, synth);
+		if (execute) {
+			ret = tracefs_synth_create(synth);
+			if (ret < 0)
+				die("%s\n", tracefs_error_last(NULL));
+		}
+	} else {
+		struct tracefs_instance *instance = NULL;
+		struct tracefs_hist *hist;
+
+		hist = tracefs_synth_get_start_hist(synth);
+		if (!hist)
+			die("get_start_hist");
+
+		if (instance_name) {
+			if (execute)
+				instance = tracefs_instance_create(instance_name);
+			else
+				instance = tracefs_instance_alloc(trace_dir,
+								  instance_name);
+			if (!instance)
+				die("Failed to create instance");
+		}
+		tracefs_hist_echo_cmd(&seq, instance, hist, 0);
+		if (execute) {
+			ret = tracefs_hist_start(instance, hist);
+			if (ret < 0)
+				die("%s\n", tracefs_error_last(instance));
+		}
+	}
+
+	tracefs_synth_free(synth);
+
+	trace_seq_do_printf(&seq);
+	trace_seq_destroy(&seq);
+	return 0;
+}
+
+void trace_sqlhist (int argc, char **argv)
+{
+	char *trace_dir = NULL;
+	char *buffer = NULL;
+	char buf[BUFSIZ];
+	int buffer_size = 0;
+	const char *file = NULL;
+	const char *instance = NULL;
+	bool execute = false;
+	char **save_fields = NULL;
+	const char *name;
+	const char *var;
+	char **save_argv;
+	int action = 0;
+	char *tok;
+	FILE *fp;
+	size_t r;
+	int c;
+	int i;
+
+	/* Remove 'trace-cmd' */
+	save_argv = argv;
+	argc -= 1;
+	argv += 1;
+
+	if (argc < 2)
+		usage(save_argv);
+
+	for (;;) {
+		c = getopt(argc, argv, "ht:f:en:m:c:sS:TB:");
+		if (c == -1)
+			break;
+
+		switch(c) {
+		case 'h':
+			usage(save_argv);
+		case 't':
+			trace_dir = optarg;
+			break;
+		case 'f':
+			file = optarg;
+			break;
+		case 'e':
+			execute = true;
+			break;
+		case 'm':
+			action |= ACTION_MAX;
+			var = optarg;
+			break;
+		case 'c':
+			action |= ACTION_CHANGE;
+			var = optarg;
+			break;
+		case 's':
+			action |= ACTION_SNAPSHOT;
+			break;
+		case 'S':
+			action |= ACTION_SAVE;
+			tok = strtok(optarg, ",");
+			while (tok) {
+				save_fields = tracefs_list_add(save_fields, tok);
+				tok = strtok(NULL, ",");
+			}
+			if (!save_fields) {
+				perror(optarg);
+				exit(-1);
+			}
+			break;
+		case 'T':
+			action |= ACTION_TRACE | ACTION_SNAPSHOT;
+			break;
+		case 'B':
+			instance = optarg;
+			break;
+		case 'n':
+			name = optarg;
+			break;
+		}
+	}
+
+	if ((action & (ACTION_MAX|ACTION_CHANGE)) == (ACTION_MAX|ACTION_CHANGE)) {
+		fprintf(stderr, "Can not use both -m and -c together\n");
+		exit(-1);
+	}
+	if (file) {
+		if (!strcmp(file, "-"))
+			fp = stdin;
+		else
+			fp = fopen(file, "r");
+		if (!fp) {
+			perror(file);
+			exit(-1);
+		}
+		while ((r = fread(buf, 1, BUFSIZ, fp)) > 0) {
+			buffer = realloc(buffer, buffer_size + r + 1);
+			strncpy(buffer + buffer_size, buf, r);
+			buffer_size += r;
+		}
+		fclose(fp);
+		if (buffer_size)
+			buffer[buffer_size] = '\0';
+	} else if (argc == optind) {
+		usage(save_argv);
+	} else {
+		for (i = optind; i < argc; i++) {
+			r = strlen(argv[i]);
+			buffer = realloc(buffer, buffer_size + r + 2);
+			if (i != optind)
+				buffer[buffer_size++] = ' ';
+			strcpy(buffer + buffer_size, argv[i]);
+			buffer_size += r;
+		}
+	}
+
+	do_sql(instance, buffer, name, var, trace_dir, execute, action, save_fields);
+	free(buffer);
+}
+
diff --git a/tracecmd/trace-stat.c b/tracecmd/trace-stat.c
index a5fb777..1d95d99 100644
--- a/tracecmd/trace-stat.c
+++ b/tracecmd/trace-stat.c
@@ -24,19 +24,6 @@
 	return instance == &top_instance;
 }
 
-static int get_instance_file_fd(struct buffer_instance *instance,
-				const char *file)
-{
-	char *path;
-	int fd;
-
-	path = tracefs_instance_get_file(instance->tracefs, file);
-	fd = open(path, O_RDONLY);
-	tracefs_put_tracing_file(path);
-
-	return fd;
-}
-
 char *strstrip(char *str)
 {
 	char *s;
@@ -71,21 +58,24 @@
 
 static char *get_fd_content(int fd, const char *file)
 {
+	size_t total = 0;
+	size_t alloc;
 	char *str = NULL;
-	int cnt = 0;
 	int ret;
 
 	for (;;) {
-		str = realloc(str, BUFSIZ * ++cnt);
+		alloc = ((total + BUFSIZ) / BUFSIZ) * BUFSIZ;
+		str = realloc(str, alloc + 1);
 		if (!str)
 			die("malloc");
-		ret = read(fd, str + BUFSIZ * (cnt - 1), BUFSIZ);
+		ret = read(fd, str + total, alloc - total);
 		if (ret < 0)
 			die("reading %s\n", file);
-		if (ret < BUFSIZ)
+		total += ret;
+		if (!ret)
 			break;
 	}
-	str[BUFSIZ * (cnt-1) + ret] = 0;
+	str[total] = 0;
 
 	return str;
 }
@@ -105,22 +95,6 @@
 	return str;
 }
 
-static char *get_instance_file_content(struct buffer_instance *instance,
-				       const char *file)
-{
-	char *str = NULL;
-	int fd;
-
-	fd = get_instance_file_fd(instance, file);
-	if (fd < 0)
-		return NULL;
-
-	str = get_fd_content(fd, file);
-
-	close(fd);
-	return str;
-}
-
 static void report_file(struct buffer_instance *instance,
 			char *name, char *def_value, char *description)
 {
@@ -129,7 +103,7 @@
 
 	if (!tracefs_file_exists(instance->tracefs, name))
 		return;
-	str = get_instance_file_content(instance, name);
+	str = tracefs_instance_file_read(instance->tracefs, name, NULL);
 	if (!str)
 		return;
 	cont = strstrip(str);
@@ -343,7 +317,7 @@
 	enum event_process processed = PROCESSED_NONE;
 	enum event_process processed_part = PROCESSED_NONE;
 
-	str = get_instance_file_content(instance, "events/enable");
+	str = tracefs_instance_file_read(instance->tracefs, "events/enable", NULL);
 	if (!str)
 		return;
 
@@ -659,9 +633,11 @@
 	char *str;
 	char *cont;
 	char file[FILE_SIZE];
+	int pagesize;
+	int bufsize;
 	int cpu;
 
-	str = get_instance_file_content(instance, "buffer_size_kb");
+	str = tracefs_instance_file_read(instance->tracefs, "buffer_size_kb", NULL);
 	if (!str)
 		return;
 
@@ -681,7 +657,7 @@
 	for (cpu = 0; ; cpu++) {
 
 		snprintf(file, FILE_SIZE, "per_cpu/cpu%d/buffer_size_kb", cpu);
-		str = get_instance_file_content(instance, file);
+		str = tracefs_instance_file_read(instance->tracefs, file, NULL);
 		if (!str)
 			break;
 
@@ -696,7 +672,7 @@
  total:
 	free(str);
 
-	str = get_instance_file_content(instance, "buffer_total_size_kb");
+	str = tracefs_instance_file_read(instance->tracefs, "buffer_total_size_kb", NULL);
 	if (!str)
 		return;
 
@@ -704,6 +680,11 @@
 	printf("\nBuffer total size in kilobytes:\n");
 	printf("   %s\n", str);
 
+	pagesize = getpagesize();
+	bufsize = tracefs_instance_get_subbuf_size(instance->tracefs);
+	if (bufsize > 0 && bufsize * 1024 != pagesize)
+		printf("\nSub-buffer size in kilobytes:\n   %d\n", bufsize);
+
  out:
 	free(str);
 }
@@ -730,7 +711,7 @@
 	int n;
 	int i;
 
-	str = get_instance_file_content(instance, "tracing_cpumask");
+	str = tracefs_instance_file_read(instance->tracefs, "tracing_cpumask", NULL);
 	if (!str)
 		return;
 
@@ -780,7 +761,7 @@
 	int newline;
 	int i;
 
-	str = get_instance_file_content(instance, file);
+	str = tracefs_instance_file_read(instance->tracefs, file, NULL);
 	if (!str)
 		return;
 
@@ -815,12 +796,17 @@
 	report_probes(instance, "uprobe_events", "Uprobe events");
 }
 
+static void report_synthetic(struct buffer_instance *instance)
+{
+	report_probes(instance, "synthetic_events", "Synthetic events");
+}
+
 static void report_traceon(struct buffer_instance *instance)
 {
 	char *str;
 	char *cont;
 
-	str = get_instance_file_content(instance, "tracing_on");
+	str = tracefs_instance_file_read(instance->tracefs, "tracing_on", NULL);
 	if (!str)
 		return;
 
@@ -856,9 +842,13 @@
 	report_file(instance, "tracing_max_latency", "0", "Max Latency: ");
 	report_kprobes(instance);
 	report_uprobes(instance);
+	report_synthetic(instance);
 	report_file(instance, "set_event_pid", "", "Filtered event PIDs:\n");
+	report_file(instance, "set_event_notrace_pid", "", "Filtered notrace event PIDs:\n");
 	report_file(instance, "set_ftrace_pid", "no pid",
 		    "Filtered function tracer PIDs:\n");
+	report_file(instance, "set_ftrace_notrace_pid", "no pid",
+		    "Filtered function tracer notrace PIDs:\n");
 	if (opt) {
 		printf("\nOptions:\n");
 		show_options("   ", instance);
diff --git a/tracecmd/trace-stream.c b/tracecmd/trace-stream.c
index ee310f3..ec6a10f 100644
--- a/tracecmd/trace-stream.c
+++ b/tracecmd/trace-stream.c
@@ -4,6 +4,7 @@
  *
  */
 #include <stdio.h>
+#include <poll.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -24,11 +25,10 @@
 		  struct hook_list *hooks,
 		  tracecmd_handle_init_func handle_init, int global)
 {
-	struct tracecmd_input *trace_input;
 	struct tracecmd_output *trace_output;
+	struct tracecmd_input *trace_input;
 	static FILE *fp = NULL;
 	static int tfd;
-	static int ofd;
 	long flags;
 
 	if (instance->handle) {
@@ -42,23 +42,21 @@
 			return NULL;
 		tfd = fileno(fp);
 
-		ofd = dup(tfd);
-		trace_output = tracecmd_output_create_fd(ofd);
-		if (!trace_output) {
-			fclose(fp);
-			return NULL;
-		}
+		trace_output = tracecmd_output_create_fd(tfd);
+		if (!trace_output)
+			goto fail;
+
 		tracecmd_output_write_headers(trace_output, NULL);
+		tracecmd_output_flush(trace_output);
+		/* Don't close the descriptor, use it for reading */
 		tracecmd_output_free(trace_output);
 	}
 
-	lseek(ofd, 0, SEEK_SET);
+	lseek(tfd, 0, SEEK_SET);
 
-	trace_input = tracecmd_alloc_fd(ofd, 0);
-	if (!trace_input) {
-		close(ofd);
+	trace_input = tracecmd_alloc_fd(tfd, 0);
+	if (!trace_input)
 		goto fail;
-	}
 
 	if (tracecmd_read_headers(trace_input, TRACECMD_FILE_PRINTK) < 0)
 		goto fail_free_input;
@@ -82,21 +80,23 @@
 	tracecmd_close(trace_input);
  fail:
 	fclose(fp);
-
+	fp = NULL; /* Try again later? */
 	return NULL;
 }
 
-int trace_stream_read(struct pid_record_data *pids, int nr_pids, struct timeval *tv)
+int trace_stream_read(struct pid_record_data *pids, int nr_pids, long sleep_us)
 {
-	struct tep_record *record;
-	struct pid_record_data *pid;
 	struct pid_record_data *last_pid;
-	fd_set rfds;
-	int top_rfd = 0;
-	int nr_fd;
+	struct pid_record_data *pid;
+	struct tep_record *record;
+	struct pollfd pollfd[nr_pids];
+	long sleep_ms = sleep_us > 0 ? (sleep_us + 999) / 1000 : sleep_us;
 	int ret;
 	int i;
 
+	if (!nr_pids)
+		return 0;
+
 	last_pid = NULL;
 
  again:
@@ -121,25 +121,18 @@
 		return 1;
 	}
 
-	nr_fd = 0;
-	FD_ZERO(&rfds);
-
 	for (i = 0; i < nr_pids; i++) {
 		/* Do not process closed pipes */
-		if (pids[i].closed)
+		if (pids[i].closed) {
+			memset(pollfd + i, 0, sizeof(*pollfd));
 			continue;
-		nr_fd++;
-		if (pids[i].brass[0] > top_rfd)
-			top_rfd = pids[i].brass[0];
+		}
 
-		FD_SET(pids[i].brass[0], &rfds);
+		pollfd[i].fd = pids[i].brass[0];
+		pollfd[i].events = POLLIN;
 	}
 
-	if (!nr_fd)
-		return 0;
-
-	ret = select(top_rfd + 1, &rfds, NULL, NULL, tv);
-
+	ret = poll(pollfd, nr_pids, sleep_ms);
 	if (ret > 0)
 		goto again;
 
diff --git a/tracecmd/trace-tsync.c b/tracecmd/trace-tsync.c
new file mode 100644
index 0000000..57baff3
--- /dev/null
+++ b/tracecmd/trace-tsync.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Google Inc, Steven Rostedt <rostedt@goodmis.org>
+ * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "trace-local.h"
+#include "trace-msg.h"
+
+struct trace_mapping {
+	struct tep_event		*kvm_entry;
+	struct tep_format_field		*vcpu_id;
+	struct tep_format_field		*common_pid;
+	int				*pids;
+	int				*map;
+	int				*vcpu;
+	int				max_cpus;
+};
+
+static int cmp_tmap_vcpu(const void *A, const void *B)
+{
+	const int *a = A;
+	const int *b = B;
+
+	if (*a < *b)
+		return -1;
+	return *a > *b;
+}
+
+static int map_kvm_vcpus(int guest_pid, struct trace_mapping *tmap)
+{
+	struct dirent *entry;
+	const char *debugfs;
+	char *vm_dir_str = NULL;
+	char *pid_file = NULL;
+	char *kvm_dir;
+	int pid_file_len;
+	bool found = false;
+	DIR *dir;
+	int ret = -1;
+	int i;
+
+	tmap->vcpu = malloc(sizeof(*tmap->vcpu) * tmap->max_cpus);
+	if (!tmap->vcpu)
+		return -1;
+
+	memset(tmap->vcpu, -1, sizeof(*tmap->vcpu) * tmap->max_cpus);
+
+	debugfs = tracefs_debug_dir();
+	if (!debugfs)
+		return -1;
+
+	if (asprintf(&kvm_dir, "%s/kvm", debugfs) < 0)
+		return -1;
+
+	dir = opendir(kvm_dir);
+	if (!dir)
+		goto out;
+
+	if (asprintf(&pid_file, "%d-", guest_pid) <= 0)
+		goto out;
+
+	pid_file_len = strlen(pid_file);
+
+	while ((entry = readdir(dir))) {
+		if (entry->d_type != DT_DIR ||
+		    strncmp(entry->d_name, pid_file, pid_file_len) != 0)
+			continue;
+		if (asprintf(&vm_dir_str, "%s/%s", kvm_dir, entry->d_name) < 0)
+			goto out;
+		found = true;
+		break;
+	}
+	if (!found)
+		goto out;
+
+	closedir(dir);
+	dir = opendir(vm_dir_str);
+	if (!dir)
+		goto out;
+	i = 0;
+	while ((entry = readdir(dir))) {
+		if (entry->d_type != DT_DIR ||
+		    strncmp(entry->d_name, "vcpu", 4))
+			continue;
+		if (i == tmap->max_cpus)
+			goto out;
+		tmap->vcpu[i] = strtol(entry->d_name + 4, NULL, 10);
+		i++;
+	}
+
+	if (i < tmap->max_cpus)
+		goto out;
+
+	qsort(tmap->vcpu, tmap->max_cpus, sizeof(*tmap->vcpu), cmp_tmap_vcpu);
+
+	ret = 0;
+
+ out:
+	if (dir)
+		closedir(dir);
+	free(vm_dir_str);
+	free(pid_file);
+	free(kvm_dir);
+
+	return ret;
+}
+
+static int map_vcpus(struct tep_event *event, struct tep_record *record,
+		     int cpu, void *context)
+{
+	struct trace_mapping *tmap = context;
+	unsigned long long val;
+	int *vcpu;
+	int type;
+	int pid;
+	int ret;
+	int i;
+
+	/* Do we have junk in the buffer? */
+	type = tep_data_type(event->tep, record);
+	if (type != tmap->kvm_entry->id)
+		return 0;
+
+	ret = tep_read_number_field(tmap->common_pid, record->data, &val);
+	if (ret < 0)
+		return 0;
+	pid = (int)val;
+
+	for (i = 0; tmap->pids[i] >= 0; i++) {
+		if (pid == tmap->pids[i])
+			break;
+	}
+	/* Is this thread one we care about ? */
+	if (tmap->pids[i] < 0)
+		return 0;
+
+	ret = tep_read_number_field(tmap->vcpu_id, record->data, &val);
+	if (ret < 0)
+		return 0;
+
+	cpu = (int)val;
+
+	vcpu = bsearch(&cpu, tmap->vcpu, tmap->max_cpus, sizeof(cpu), cmp_tmap_vcpu);
+	/* Sanity check, warn? */
+	if (!vcpu)
+		return 0;
+
+	cpu = vcpu - tmap->vcpu;
+
+	/* Already have this one? Should we check if it is the same? */
+	if (tmap->map[cpu] >= 0)
+		return 0;
+
+	tmap->map[cpu] = pid;
+
+	/* Did we get them all */
+	for (i = 0; i < tmap->max_cpus; i++) {
+		if (tmap->map[i] < 0)
+			break;
+	}
+
+	return i == tmap->max_cpus;
+}
+
+static void start_mapping_vcpus(struct trace_guest *guest)
+{
+	char *pids = NULL;
+	char *t;
+	int len = 0;
+	int s;
+	int i;
+
+	if (!guest->task_pids)
+		return;
+
+	guest->instance = tracefs_instance_create("map_guest_pids");
+	if (!guest->instance)
+		return;
+
+	for (i = 0; guest->task_pids[i] >= 0; i++) {
+		s = snprintf(NULL, 0, "%d ", guest->task_pids[i]);
+		t = realloc(pids, len + s + 1);
+		if (!t) {
+			free(pids);
+			pids = NULL;
+			break;
+		}
+		pids = t;
+		sprintf(pids + len, "%d ", guest->task_pids[i]);
+		len += s;
+	}
+	if (pids) {
+		tracefs_instance_file_write(guest->instance, "set_event_pid", pids);
+		free(pids);
+	}
+	tracefs_instance_file_write(guest->instance, "events/kvm/kvm_entry/enable", "1");
+}
+
+static void stop_mapping_vcpus(int cpu_count, struct trace_guest *guest)
+{
+	struct trace_mapping tmap = { };
+	struct tep_handle *tep;
+	const char *systems[] = { "kvm", NULL };
+	int i;
+
+	if (!guest->instance)
+		return;
+
+	tmap.pids = guest->task_pids;
+	tmap.max_cpus = cpu_count;
+
+	tmap.map = malloc(sizeof(*tmap.map) * tmap.max_cpus);
+	if (!tmap.map)
+		return;
+
+	/* Check if the kvm vcpu mappings are the same */
+	if (map_kvm_vcpus(guest->pid, &tmap) < 0)
+		goto out;
+
+	for (i = 0; i < tmap.max_cpus; i++)
+		tmap.map[i] = -1;
+
+	tracefs_instance_file_write(guest->instance, "events/kvm/kvm_entry/enable", "0");
+
+	tep = tracefs_local_events_system(NULL, systems);
+	if (!tep)
+		goto out;
+
+	tmap.kvm_entry = tep_find_event_by_name(tep, "kvm", "kvm_entry");
+	if (!tmap.kvm_entry)
+		goto out_free;
+
+	tmap.vcpu_id = tep_find_field(tmap.kvm_entry, "vcpu_id");
+	if (!tmap.vcpu_id)
+		goto out_free;
+
+	tmap.common_pid = tep_find_any_field(tmap.kvm_entry, "common_pid");
+	if (!tmap.common_pid)
+		goto out_free;
+
+	tracefs_iterate_raw_events(tep, guest->instance, NULL, 0, map_vcpus, &tmap);
+
+	for (i = 0; i < tmap.max_cpus; i++) {
+		if (tmap.map[i] < 0)
+			break;
+	}
+	/* We found all the mapped CPUs */
+	if (i == tmap.max_cpus) {
+		guest->cpu_pid = tmap.map;
+		guest->cpu_max = tmap.max_cpus;
+		tmap.map = NULL;
+	}
+
+ out_free:
+	tep_free(tep);
+ out:
+	free(tmap.map);
+	tracefs_instance_destroy(guest->instance);
+	tracefs_instance_free(guest->instance);
+}
+
+/**
+ * trace_tsync_as_host - tsync from the host side
+ * @fd: The descriptor to the peer for tsync
+ * @trace_id: The trace_id of the host
+ * @loop_interval: The loop interval for tsyncs that do periodic syncs
+ * @guest_id: The id for guests (negative if this is over network)
+ * @guest_cpus: The number of CPUs the guest has
+ * @proto_name: The protocol name to sync with
+ * @clock: The clock name to use for tracing
+ *
+ * Start the time synchronization from the host side.
+ * This will start the mapping of the virtual CPUs to host threads
+ * if it is a vsocket connection (not a network).
+ *
+ * Returns a pointer to the tsync descriptor on success or NULL on error.
+ */
+struct tracecmd_time_sync *
+trace_tsync_as_host(int fd, unsigned long long trace_id,
+		    int loop_interval, int guest_id,
+		    int guest_cpus, const char *proto_name,
+		    const char *clock)
+{
+	struct tracecmd_time_sync *tsync;
+	struct trace_guest *guest;
+	int guest_pid = -1;
+
+	if (fd < 0)
+		return NULL;
+
+	if (guest_id >= 0) {
+		guest = trace_get_guest(guest_id, NULL);
+		if (guest == NULL)
+			return NULL;
+		guest_pid = guest->pid;
+		start_mapping_vcpus(guest);
+	}
+
+	tsync = tracecmd_tsync_with_guest(trace_id, loop_interval, fd,
+					  guest_pid, guest_cpus, proto_name,
+					  clock);
+
+	if (guest_id >= 0)
+		stop_mapping_vcpus(guest_cpus, guest);
+
+	return tsync;
+}
+
+/**
+ * trace_tsync_a_guest - tsync from the guest side
+ * @fd: The file descriptor to the peer for tsync
+ * @tsync_proto: The protocol name to sync with
+ * @clock: The clock name to use for tracing
+ * @remote_id: The id to differentiate the remote server with
+ * @loca_id: The id to differentiate the local machine with
+ *
+ * Start the time synchronization from the guest side.
+ *
+ * Returns a pointer to the tsync descriptor on success or NULL on error.
+ */
+struct tracecmd_time_sync *
+trace_tsync_as_guest(int fd, const char *tsync_proto, const char *clock,
+	       unsigned int remote_id, unsigned int local_id)
+{
+	struct tracecmd_time_sync *tsync = NULL;
+
+	if (fd < 0)
+		 return NULL;
+
+	tsync = tracecmd_tsync_with_host(fd, tsync_proto,
+					 clock, remote_id, local_id);
+	if (!tsync) {
+		warning("Failed to negotiate timestamps synchronization with the host");
+		return NULL;
+	}
+
+	return tsync;
+}
diff --git a/tracecmd/trace-usage.c b/tracecmd/trace-usage.c
index 2cfa64f..8bbf2e3 100644
--- a/tracecmd/trace-usage.c
+++ b/tracecmd/trace-usage.c
@@ -19,7 +19,8 @@
 		"record a trace into a trace.dat file",
 		" %s record [-v][-e event [-f filter]][-p plugin][-F][-d][-D][-o file] \\\n"
 		"           [-q][-s usecs][-O option ][-l func][-g func][-n func] \\\n"
-		"           [-P pid][-N host:port][-t][-r prio][-b size][-B buf][command ...]\n"
+		"           [-P pid][-N host:port][-t][-r prio][-b size][-B buf] \\\n"
+		"           [--proxy vsock][command ...]\n"
 		"           [-m max][-C clock]\n"
 		"          -e run command with event enabled\n"
 		"          -f filter for previous -e event\n"
@@ -53,6 +54,8 @@
 		"          -q print no output to the screen\n"
 		"          -G when profiling, set soft and hard irqs as global\n"
 		"          --quiet print no output to the screen\n"
+		"          --temp specify a directory to store the temp files used to create trace.dat\n"
+		"          --subbuf-size to specify the sub-buffer size in kilobytes\n"
 		"          --module filter module name\n"
 		"          --by-comm used with --profile, merge events for related comms\n"
 		"          --profile enable tracing options needed for report --profile\n"
@@ -77,6 +80,11 @@
 		"                            none - do not compress the trace file\n"
 		"                            name - the name of the desired compression algorithms\n"
 		"                        available algorithms can be listed with trace-cmd list -c\n"
+		"          --proxy vsocket to reach the agent. Acts the same as -A (for an agent)\n"
+		"              but will send the proxy connection to the agent.\n"
+		"          --daemonize run trace-cmd in the background as a daemon after recording has started.\n"
+		"                      creates a pidfile at /var/run/trace-cmd-record.pid with the pid of trace-cmd\n"
+		"                      during the recording.\n"
 	},
 	{
 		"set",
@@ -173,6 +181,8 @@
 		"          --current_tracer\n"
 		"          --buffer_size (for buffer_size_kb)\n"
 		"          --buffer_total_size (for buffer_total_size_kb)\n"
+		"          --buffer_subbuf_size (for buffer_subbuf_size_kb)\n"
+		"          --buffer_percent (for buffer_percent)\n"
 		"          --ftrace_filter (for set_ftrace_filter)\n"
 		"          --ftrace_notrace (for set_ftrace_notrace)\n"
 		"          --ftrace_pid (for set_ftrace_pid)\n"
@@ -230,6 +240,8 @@
 		"          --cpu <cpu1,cpu2,...> - filter events according to the given cpu list.\n"
 		"                                  A range of CPUs can be specified using 'cpuX-cpuY' notation.\n"
 		"          --cpus - List the CPUs that have content in it then exit.\n"
+		"          --first-event - Show the timestamp of the first event for all CPUs.\n"
+		"          --last-event - Show the timestamp of the last event for all CPUs.\n"
 		"          --check-events return whether all event formats can be parsed\n"
 		"          --stat - show the buffer stats that were reported at the end of the record.\n"
 		"          --uname - show uname of the record, if it was saved\n"
@@ -299,6 +311,13 @@
 		"          -u n  split file up by n microseconds\n"
 		"          -e n  split file up by n events\n"
 		"          -p n  split file up by n pages\n"
+		"          -C n  select CPU n\n"
+		"          -B buffer  keep buffer in resulting .dat file\n"
+		"                     Use -t to promote the buffer to the top instance.\n"
+		"          -t    promote preceding buffer to the top instance.\n"
+		"                Must follow -B.\n"
+		"          --top keep top buffer in resulting .dat file.\n"
+		"          -b    new name of the top instance. Must follow --top.\n"
 		"          -r    repeat from start to end\n"
 		"          -c    per cpu, that is -p 2 will be 2 pages for each CPU\n"
 		"          if option is specified, it will split the file\n"
@@ -328,13 +347,15 @@
 	{
 		"agent",
 		"listen on a vsocket for trace clients",
-		" %s agent -p port[-D]\n"
+		" %s agent -p port[-D][-N IP][-P cid]\n"
 		"          Creates a vsocket to listen for clients.\n"
 		"          -N Connect to IP via TCP instead of vsockets\n"
 		"             *** Insecure setting, only use on a trusted network ***\n"
 		"             ***   Only use if the client is totally trusted.    ***\n"
 		"          -p port number to listen on.\n"
 		"          -D run in daemon mode.\n"
+		"          -P Also act as a proxy server, with a single client denoted\n"
+		"             by a context ID (cid).\n"
 		"          --verbose 'level' Set the desired log level\n"
 	},
 	{
@@ -422,6 +443,15 @@
 		"          --verbose 'level' Set the desired log level\n"
 	},
 	{
+		"attach",
+		"Attach a host and guest trace.dat file",
+		" %s attach [options] host_file guest_file vcpu_pid,...\n"
+		"          -s  offset,scale,fraction[,timestamp] conversion to sync guest timestamp\n"
+		"          host_file The trace.dat file from the host\n"
+		"          guest_file The trace.dat file from the guest\n"
+		"          vcpu_pid list of process ids from the host that represent the vCPUs of the guest\n"
+	},
+	{
 		"convert",
 		"convert trace file to different version",
 		" %s convert [options]\n"
@@ -433,7 +463,25 @@
 		"                            any  - auto select the best available compression algorithm\n"
 		"                            none - do not compress the trace file\n"
 		"                            name - the name of the desired compression algorithms\n"
-		"                        available algorithms can be listed with trace-cmd list -c\n"	},
+		"                        available algorithms can be listed with trace-cmd list -c\n"
+	},
+	{
+		"sqlhist",
+		"Run a SQL like query to create histogram or synthetic events (see man tracefs_sql(3))\n",
+		"%s sql [-n name][-e][-s][-S fields][-m var][-c var][-T][-t dir][-f file | 'sql-command-line']\n"
+		"  -n name - name of synthetic event 'Anonymous' if left off\n"
+		"  -t dir - use dir instead of /sys/kernel/tracing\n"
+		"  -e - execute the commands to create the synthetic event\n"
+		"  -m - trigger the action when var is a new max.\n"
+		"  -c - trigger the action when var changes.\n"
+		"  -s - used with -m or -c to do a snapshot of the tracing buffer\n"
+		"  -S - used with -m or -c to save fields of the end event (comma deliminated)\n"
+		"  -T - used with -m or -c to do both a snapshot and a trace\n"
+		"  -f file - read sql lines from file otherwise from the command line\n"
+		"	    if file is '-' then read from standard input.\n\n"
+		" See man tracefs_sql(3) for sql-command-line\n"
+	},
+
 	{
 		NULL, NULL, NULL
 	}
diff --git a/tracecmd/trace-vm.c b/tracecmd/trace-vm.c
index 57dbef8..960ed5d 100644
--- a/tracecmd/trace-vm.c
+++ b/tracecmd/trace-vm.c
@@ -35,7 +35,7 @@
 {
 	int i;
 
-	if (!guests)
+	if (!guests || !strlen(name))
 		return NULL;
 
 	for (i = 0; i < guests_len; i++)
@@ -54,20 +54,61 @@
 	return true;
 }
 
+/* Find all the tasks associated with the guest pid */
+static void find_tasks(struct trace_guest *guest)
+{
+	struct dirent *dent;
+	char *path;
+	DIR *dir;
+	int ret;
+	int tasks = 0;
+
+	ret = asprintf(&path, "/proc/%d/task", guest->pid);
+	if (ret < 0)
+		return;
+
+	dir = opendir(path);
+	free(path);
+	if (!dir)
+		return;
+
+	while ((dent = readdir(dir))) {
+		int *pids;
+		if (!(dent->d_type == DT_DIR && is_digits(dent->d_name)))
+			continue;
+		pids = realloc(guest->task_pids, sizeof(int) * (tasks + 2));
+		if (!pids)
+			break;
+		pids[tasks++] = strtol(dent->d_name, NULL, 0);
+		pids[tasks] = -1;
+		guest->task_pids = pids;
+	}
+	closedir(dir);
+}
+
+static void find_pid_by_cid(struct trace_guest *guest);
+
 static struct trace_guest *add_guest(unsigned int cid, const char *name)
 {
+	struct trace_guest *guest;
+
 	guests = realloc(guests, (guests_len + 1) * sizeof(*guests));
 	if (!guests)
 		die("allocating new guest");
-	memset(&guests[guests_len], 0, sizeof(struct trace_guest));
-	guests[guests_len].name = strdup(name);
-	if (!guests[guests_len].name)
-		die("allocating guest name");
-	guests[guests_len].cid = cid;
-	guests[guests_len].pid = -1;
-	guests_len++;
 
-	return &guests[guests_len - 1];
+	guest = &guests[guests_len++];
+
+	memset(guest, 0, sizeof(*guest));
+	guest->name = strdup(name);
+	if (!guest->name)
+		die("allocating guest name");
+	guest->cid = cid;
+	guest->pid = -1;
+
+	find_pid_by_cid(guest);
+	find_tasks(guest);
+
+	return guest;
 }
 
 static struct tracefs_instance *start_trace_connect(void)
@@ -239,6 +280,7 @@
 
 	tep = tracefs_local_events_system(NULL, systems);
 
+	trace_fields.found_pid = -1;
 	trace_fields.sched_waking = tep_find_event_by_name(tep, "sched", "sched_waking");
 	if (!trace_fields.sched_waking)
 		goto out;
@@ -254,7 +296,6 @@
 	if (!trace_fields.sched_next)
 		goto out;
 
-	trace_fields.found_pid = -1;
 	trace_fields.pids = NULL;
 	add_pid(&trace_fields.pids, getpid());
 	tracefs_iterate_raw_events(tep, open_instance, NULL, 0, callback, &trace_fields);
@@ -303,11 +344,8 @@
 
 	if (cid > 0) {
 		guest = get_guest_by_cid(cid);
-		if (!guest && name) {
+		if (!guest && name)
 			guest = add_guest(cid, name);
-			if (guest)
-				find_pid_by_cid(guest);
-		}
 	}
 	return guest;
 }
@@ -317,7 +355,6 @@
 #define VM_CID_ID	"address='"
 static void read_guest_cid(char *name)
 {
-	struct trace_guest *guest;
 	char *cmd = NULL;
 	char line[512];
 	char *cid;
@@ -339,9 +376,7 @@
 		cid_id = strtol(cid + strlen(VM_CID_ID), NULL, 10);
 		if ((cid_id == INT_MIN || cid_id == INT_MAX) && errno == ERANGE)
 			continue;
-		guest = add_guest(cid_id, name);
-		if (guest)
-			find_pid_by_cid(guest);
+		add_guest(cid_id, name);
 		break;
 	}
 
@@ -386,3 +421,76 @@
 	}
 	return -1;
 }
+
+/**
+ * trace_add_guest_info - Add the guest info into the trace file option
+ * @handle: The file handle that the guest info option is added to
+ * @instance: The instance that that represents the guest
+ *
+ * Adds information about the guest from the @instance into an option
+ * for the @instance. It records the trace_id, the number of CPUs,
+ * as well as the PIDs of the host that represent the CPUs.
+ */
+void
+trace_add_guest_info(struct tracecmd_output *handle, struct buffer_instance *instance)
+{
+	unsigned long long trace_id;
+	struct trace_guest *guest;
+	const char *name;
+	char *buf, *p;
+	int cpus;
+	int size;
+	int pid;
+	int i;
+
+	if (is_network(instance)) {
+		name = instance->name;
+		cpus = instance->cpu_count;
+		trace_id = instance->trace_id;
+	} else {
+		guest = trace_get_guest(instance->cid, NULL);
+		if (!guest)
+			return;
+		cpus = guest->cpu_max;
+		name = guest->name;
+		/*
+		 * If this is a proxy, the trace_id of the guest is
+		 * in the guest descriptor (added in trace_tsync_as_host().
+		 */
+		if (guest->trace_id)
+			trace_id = guest->trace_id;
+		else
+			trace_id = instance->trace_id;
+	}
+
+	size = strlen(name) + 1;
+	size += sizeof(long long);	/* trace_id */
+	size += sizeof(int);		/* cpu count */
+	size += cpus * 2 * sizeof(int);	/* cpu,pid pair */
+
+	buf = calloc(1, size);
+	if (!buf)
+		return;
+	p = buf;
+	strcpy(p, name);
+	p += strlen(name) + 1;
+
+	memcpy(p, &trace_id, sizeof(long long));
+	p += sizeof(long long);
+
+	memcpy(p, &cpus, sizeof(int));
+	p += sizeof(int);
+	for (i = 0; i < cpus; i++) {
+		if (is_network(instance))
+			pid = -1;
+		else
+			pid = guest->cpu_pid[i];
+		memcpy(p, &i, sizeof(int));
+		p += sizeof(int);
+		memcpy(p, &pid, sizeof(int));
+		p += sizeof(int);
+	}
+
+	tracecmd_add_option(handle, TRACECMD_OPTION_GUEST, size, buf);
+	free(buf);
+}
diff --git a/tracecmd/trace-vsock.c b/tracecmd/trace-vsock.c
index 39294e7..df1a980 100644
--- a/tracecmd/trace-vsock.c
+++ b/tracecmd/trace-vsock.c
@@ -19,8 +19,10 @@
 	if (sd < 0)
 		return -errno;
 
-	if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)))
+	if (connect(sd, (struct sockaddr *)&addr, sizeof(addr))) {
+		close(sd);
 		return -errno;
+	}
 
 	return sd;
 }
@@ -41,12 +43,16 @@
 	setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
 
 	if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)))
-		return -errno;
+		goto error;
 
 	if (listen(sd, SOMAXCONN))
-		return -errno;
+		goto error;
 
 	return sd;
+
+error:
+	close(sd);
+	return -errno;
 }
 
 int __hidden trace_vsock_make_any(void)
@@ -76,20 +82,24 @@
 	struct sockaddr_vm addr;
 	socklen_t addr_len = sizeof(addr);
 
-	memset(&addr, 0, sizeof(addr));
-	if (getsockname(fd, (struct sockaddr *)&addr, &addr_len))
-		return -1;
-	if (addr.svm_family != AF_VSOCK)
-		return -1;
-	*lcid = addr.svm_cid;
+	if (lcid) {
+		memset(&addr, 0, sizeof(addr));
+		if (getsockname(fd, (struct sockaddr *)&addr, &addr_len))
+			return -1;
+		if (addr.svm_family != AF_VSOCK)
+			return -1;
+		*lcid = addr.svm_cid;
+	}
 
-	memset(&addr, 0, sizeof(addr));
-	addr_len = sizeof(addr);
-	if (getpeername(fd, (struct sockaddr *)&addr, &addr_len))
-		return -1;
-	if (addr.svm_family != AF_VSOCK)
-		return -1;
-	*rcid = addr.svm_cid;
+	if (rcid) {
+		memset(&addr, 0, sizeof(addr));
+		addr_len = sizeof(addr);
+		if (getpeername(fd, (struct sockaddr *)&addr, &addr_len))
+			return -1;
+		if (addr.svm_family != AF_VSOCK)
+			return -1;
+		*rcid = addr.svm_cid;
+	}
 
 	return 0;
 }
diff --git a/utest/Makefile b/utest/Makefile
index 2cf9974..480edcd 100644
--- a/utest/Makefile
+++ b/utest/Makefile
@@ -8,20 +8,24 @@
 
 OBJS =
 OBJS += trace-utest.o
-OBJS += tracefs-utest.o
+OBJS += tracecmd-utest.o
 
-LIBS += -lcunit $(LIBTRACEEVENT_LDLAGS) $(LIBTRACEFS_LDLAGS)
+LIBS += $(LIBTRACECMD_STATIC) -lcunit $(LIBTRACEEVENT_LDLAGS) $(LIBTRACEFS_LDLAGS)
+
+LIBS += $(ZLIB_LDLAGS) $(LIBZSTD_LDLAGS)
 
 OBJS := $(OBJS:%.o=$(bdir)/%.o)
 DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
 
+VALGRIND = $(shell which valgrind)
+
 $(bdir):
 	@mkdir -p $(bdir)
 
 $(OBJS): | $(bdir)
 $(DEPS): | $(bdir)
 
-$(bdir)/trace-utest: $(OBJS)
+$(bdir)/trace-utest: $(OBJS) $(LIBTRACECMD_STATIC)
 	$(Q)$(do_app_build)
 
 $(bdir)/%.o: %.c
@@ -37,5 +41,18 @@
 
 test: $(TARGETS)
 
+test_mem: test
+ifeq (, $(VALGRIND))
+	$(error "No valgrind in $(PATH), cannot run memory test")
+endif
+ifneq ($(shell id -u), 0)
+	$(error "The memory test should be run as root, as it reuqires full access to tracefs")
+endif
+	CK_FORK=no $(VALGRIND) \
+		--show-leak-kinds=all --leak-resolution=high \
+		--leak-check=full --show-possibly-lost=yes \
+		--track-origins=yes -s \
+		$(bdir)/trace-utest
+
 clean:
 	$(RM) $(TARGETS) $(bdir)/*.o $(bdir)/.*.d
diff --git a/utest/meson.build b/utest/meson.build
new file mode 100644
index 0000000..c1cc837
--- /dev/null
+++ b/utest/meson.build
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2023 Daniel Wagner, SUSE LLC
+
+sources = [
+    'tracecmd-utest.c',
+    'trace-utest.c',
+]
+
+e = executable(
+   'trace-utest',
+   sources,
+   include_directories: [
+       incdir,
+       libtracecmd_incdir,
+       libtracecmd_private_incdir,
+       libtracecmd_ext_incdir],
+   dependencies: [
+       libtraceevent_dep,
+       libtracefs_dep,
+       zlib_dep,
+       libzstd_dep,
+       cunit_dep],
+   link_with: [static_libtracecmd])
+
+test('trace-utest', e)
diff --git a/utest/trace-utest.c b/utest/trace-utest.c
index 58d4d4e..6bb190e 100644
--- a/utest/trace-utest.c
+++ b/utest/trace-utest.c
@@ -7,15 +7,19 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <stdlib.h>
+#include <libgen.h>
 
 #include <CUnit/CUnit.h>
 #include <CUnit/Basic.h>
 
 #include "trace-utest.h"
 
+const char *argv0;
+bool show_output;
+
 enum unit_tests {
 	RUN_NONE	= 0,
-	RUN_TRACEFS	= (1 << 0),
+	RUN_TRACECMD	= (1 << 0),
 	RUN_ALL		= 0xFFFF
 };
 
@@ -24,7 +28,7 @@
 	printf("Usage: %s [OPTIONS]\n", basename(argv[0]));
 	printf("\t-s, --silent\tPrint test summary\n");
 	printf("\t-r, --run test\tRun specific test:\n");
-	printf("\t\t  tracefs   run libtracefs tests\n");
+	printf("\t\t  trace-cmd   run trace-cmd tests\n");
 	printf("\t-h, --help\tPrint usage information\n");
 	exit(0);
 }
@@ -34,13 +38,16 @@
 	CU_BasicRunMode verbose = CU_BRM_VERBOSE;
 	enum unit_tests tests = RUN_NONE;
 
+	argv0 = argv[0];
+
 	for (;;) {
 		int c;
 		int index = 0;
-		const char *opts = "+hsr:";
+		const char *opts = "+hsr:v";
 		static struct option long_options[] = {
 			{"silent", no_argument, NULL, 's'},
 			{"run", required_argument, NULL, 'r'},
+			{"verbose", no_argument, NULL, 'v'},
 			{"help", no_argument, NULL, 'h'},
 			{NULL, 0, NULL, 0}
 		};
@@ -50,14 +57,17 @@
 			break;
 		switch (c) {
 		case 'r':
-			if (strcmp(optarg, "tracefs") == 0)
-				tests |= RUN_TRACEFS;
+			if (strcmp(optarg, "trace-cmd") == 0)
+				tests |= RUN_TRACECMD;
 			else
 				print_help(argv);
 			break;
 		case 's':
 			verbose = CU_BRM_SILENT;
 			break;
+		case 'v':
+			show_output = true;
+			break;
 		case 'h':
 		default:
 			print_help(argv);
@@ -73,8 +83,8 @@
 		return -1;
 	}
 
-	if (tests & RUN_TRACEFS)
-		test_tracefs_lib();
+	if (tests & RUN_TRACECMD)
+		test_tracecmd_lib();
 
 	CU_basic_set_mode(verbose);
 	CU_basic_run_tests();
diff --git a/utest/trace-utest.h b/utest/trace-utest.h
index 917c0e7..b57e646 100644
--- a/utest/trace-utest.h
+++ b/utest/trace-utest.h
@@ -6,6 +6,11 @@
 #ifndef _TRACE_UTEST_H_
 #define _TRACE_UTEST_H_
 
-void test_tracefs_lib(void);
+#include <stdbool.h>
+
+extern const char *argv0;
+extern bool show_output;
+
+void test_tracecmd_lib(void);
 
 #endif /* _TRACE_UTEST_H_ */
diff --git a/utest/tracecmd-utest.c b/utest/tracecmd-utest.c
new file mode 100644
index 0000000..164f5da
--- /dev/null
+++ b/utest/tracecmd-utest.c
@@ -0,0 +1,659 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+
+#include <CUnit/CUnit.h>
+#include <CUnit/Basic.h>
+
+#include <trace-cmd.h>
+
+#include "trace-utest.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+static char tracecmd_exec[PATH_MAX];
+
+#define TRACECMD_SUITE		"trace-cmd"
+#define TRACECMD_FILE		"__trace_test__.dat"
+#define TRACECMD_FILE2		"__trace_test__2.dat"
+#define TRACECMD_OUT		"-o", TRACECMD_FILE
+#define TRACECMD_OUT2		"-o", TRACECMD_FILE2
+#define TRACECMD_IN		"-i", TRACECMD_FILE
+#define TRACECMD_IN2		"-i", TRACECMD_FILE2
+
+#define TRACECMD_SQL_HIST	"SELECT irq FROM irq_handler_entry"
+#define TRACECMD_SQL_READ_HIST	"show", "--hist", "irq_handler_entry"
+
+#define SYNTH_EVENT		"wakeup"
+#define TRACECMD_SQL_SYNTH	"-e", "-n", SYNTH_EVENT, "SELECT start.pid AS this_pid, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) AS delta FROM sched_waking as start JOIN sched_switch AS end ON start.pid = end.next_pid"
+#define TRACECMD_SQL_START_SYNTH "start", "-e", SYNTH_EVENT
+
+static char **get_args(const char *cmd, va_list ap)
+{
+	const char *param;
+	char **argv;
+	char **tmp;
+
+	argv = tracefs_list_add(NULL, tracecmd_exec);
+	if (!argv)
+		return NULL;
+
+	tmp = tracefs_list_add(argv, cmd);
+	if (!tmp)
+		goto fail;
+	argv = tmp;
+
+	for (param = va_arg(ap, const char *);
+	     param; param = va_arg(ap, const char *)) {
+		tmp = tracefs_list_add(argv, param);
+		if (!tmp)
+			goto fail;
+		argv = tmp;
+	}
+
+	return argv;
+ fail:
+	tracefs_list_free(argv);
+	return NULL;
+}
+
+static void silent_output(void)
+{
+	close(STDOUT_FILENO);
+	open("/dev/null", O_WRONLY);
+	close(STDERR_FILENO);
+	open("/dev/null", O_WRONLY);
+}
+
+static int wait_for_exec(int pid)
+{
+	int status;
+	int ret;
+
+	ret = waitpid(pid, &status, 0);
+	if (ret != pid)
+		return -1;
+
+	return WEXITSTATUS(status) ? -1 : 0;
+}
+
+static int run_trace(const char *cmd, ...)
+{
+	char **argv;
+	va_list ap;
+	int ret = -1;
+	pid_t pid;
+
+	va_start(ap, cmd);
+	argv = get_args(cmd, ap);
+	va_end(ap);
+
+	if (!argv)
+		return -1;
+
+	pid = fork();
+	if (pid < 0)
+		goto out;
+
+	if (!pid) {
+		if (!show_output)
+			silent_output();
+		ret = execvp(tracecmd_exec, argv);
+		exit (ret);
+	}
+
+	ret = wait_for_exec(pid);
+ out:
+	tracefs_list_free(argv);
+	return ret;
+}
+
+static int pipe_it(int *ofd, int *efd, int (*func)(void *),
+		   void *data)
+{
+	int obrass[2];
+	int ebrass[2];
+	pid_t pid;
+	int ret;
+
+	if (pipe(obrass) < 0)
+		return -1;
+
+	if (pipe(ebrass) < 0)
+		goto fail_out;
+
+	pid = fork();
+	if (pid < 0)
+		goto fail;
+
+	if (!pid) {
+		char shret[32];
+
+		close(obrass[0]);
+		close(STDOUT_FILENO);
+		if (dup2(obrass[1], STDOUT_FILENO) < 0)
+			exit(-1);
+
+		close(ebrass[0]);
+		close(STDERR_FILENO);
+		if (dup2(obrass[1], STDERR_FILENO) < 0)
+			exit(-1);
+
+		ret = func(data);
+
+		/*
+		 * valgrind triggers its reports when the application
+		 * exits. If the application does a fork() and the child
+		 * exits, it will still trigger the valgrind report for
+		 * all the allocations that were not freed by the parent.
+		 *
+		 * To prevent valgrind from triggering, do an execl() on
+		 * a basic shell that will simply exit with the return value.
+		 * This will quiet valgrind from reporting memory that has
+		 * been allocated by the parent up to here.
+		 */
+		snprintf(shret, 32, "exit %d", ret);
+		execl("/usr/bin/sh", "/usr/bin/sh", "-c", shret, NULL);
+		execl("/bin/sh", "/bin/sh", "-c", shret, NULL);
+
+		/* If the above execl() fails, simply do an exit */
+		exit(ret);
+	}
+
+	close(obrass[1]);
+	close(ebrass[1]);
+
+	*ofd = obrass[0];
+	*efd = ebrass[0];
+
+	return pid;
+
+ fail:
+	close(ebrass[0]);
+	close(ebrass[1]);
+ fail_out:
+	close(obrass[0]);
+	close(obrass[1]);
+	return -1;
+}
+
+struct do_grep {
+	const char		*cmd;
+	va_list			*ap;
+};
+
+static int do_grep(void *data)
+{
+	struct do_grep *gdata = data;
+	char **argv;
+	int ret;
+
+	argv = get_args(gdata->cmd, *gdata->ap);
+	if (!argv)
+		exit(-1);
+
+	ret = execvp(tracecmd_exec, argv);
+	tracefs_list_free(argv);
+	return ret;
+}
+
+struct do_grep_it {
+	const char		*match;
+	const char		*cmd;
+	va_list			*ap;
+};
+
+static int do_grep_it(void *data)
+{
+	struct do_grep_it *dgdata = data;
+	struct do_grep gdata;
+	FILE *fp;
+	regex_t reg;
+	char *buf = NULL;
+	ssize_t n;
+	size_t l = 0;
+	int ofd;
+	int efd;
+	int pid;
+	int ret;
+
+	if (regcomp(&reg, dgdata->match, REG_ICASE|REG_NOSUB))
+		return -1;
+
+	gdata.cmd = dgdata->cmd;
+	gdata.ap = dgdata->ap;
+	pid = pipe_it(&ofd, &efd, do_grep, &gdata);
+
+	if (pid < 0) {
+		regfree(&reg);
+		return -1;
+	}
+
+	fp = fdopen(ofd, "r");
+	if (!fp)
+		goto out;
+
+	do {
+		n = getline(&buf, &l, fp);
+		if (n > 0 && regexec(&reg, buf, 0, NULL, 0) == 0)
+			printf("%s", buf);
+	} while (n >= 0);
+
+	free(buf);
+ out:
+	ret = wait_for_exec(pid);
+	if (fp)
+		fclose(fp);
+	else
+		perror("fp");
+	close(ofd);
+	close(efd);
+	regfree(&reg);
+
+	return ret > 0 ? 0 : ret;
+}
+
+struct do_grep_match {
+	const char		*match;
+	const char		*cmd;
+	va_list			*ap;
+};
+
+static int grep_match(const char *match, const char *cmd, ...)
+{
+	struct do_grep_it gdata;
+	FILE *fp;
+	va_list ap;
+	char *buf = NULL;
+	ssize_t n;
+	size_t l = 0;
+	bool found = false;
+	int ofd;
+	int efd;
+	int pid;
+	int ret;
+
+	va_start(ap, cmd);
+	gdata.match = match;
+	gdata.cmd = cmd;
+	gdata.ap = &ap;
+	pid = pipe_it(&ofd, &efd, do_grep_it, &gdata);
+	va_end(ap);
+
+	if (pid < 0)
+		return -1;
+
+	fp = fdopen(ofd, "r");
+	if (!fp)
+		goto out;
+
+	do {
+		n = getline(&buf, &l, fp);
+		if (n > 0) {
+			if (show_output)
+				printf("%s", buf);
+			found = true;
+		}
+	} while (n >= 0);
+
+	free(buf);
+ out:
+	ret = wait_for_exec(pid);
+	if (ret)
+		n = 1;
+	if (fp)
+		fclose(fp);
+	else {
+		perror("fp");
+		close(ofd);
+	}
+	close(efd);
+
+	return found ? 0 : 1;
+}
+
+static void test_trace_record_report(void)
+{
+	int ret;
+
+	ret = run_trace("record", TRACECMD_OUT, "-e", "sched", "sleep", "1", NULL);
+	CU_TEST(ret == 0);
+	ret = run_trace("convert", "--file-version", "6", TRACECMD_IN, TRACECMD_OUT2, NULL);
+	CU_TEST(ret == 0);
+}
+
+static void test_trace_sqlhist_hist(void)
+{
+	int ret;
+
+	ret = run_trace("sqlhist", "-e", TRACECMD_SQL_HIST, NULL);
+	CU_TEST(ret == 0);
+	ret = grep_match(" *Hits: [0-9][0-9]*", TRACECMD_SQL_READ_HIST, NULL);
+	CU_TEST(ret == 0);
+	ret = run_trace("sqlhist", TRACECMD_SQL_SYNTH, NULL);
+	CU_TEST(ret == 0);
+	ret = run_trace(TRACECMD_SQL_START_SYNTH, NULL);
+	CU_TEST(ret == 0);
+	sleep(1);
+	ret = grep_match(SYNTH_EVENT ":", "show", NULL);
+	CU_TEST(ret == 0);
+
+	tracefs_instance_reset(NULL);
+}
+
+static int read_stats(const char *out, const char *match, const char *cmd, ...)
+{
+	struct do_grep_it gdata;
+	FILE *fp;
+	va_list ap;
+	bool found = false;
+	char *buf = NULL;
+	char *p;
+	ssize_t n;
+	size_t l = 0;
+	int ofd;
+	int efd;
+	int pid;
+	int ret;
+	int val;
+
+	va_start(ap, cmd);
+	gdata.match = match;
+	gdata.cmd = cmd;
+	gdata.ap = &ap;
+	pid = pipe_it(&ofd, &efd, do_grep_it, &gdata);
+	va_end(ap);
+
+	if (pid < 0)
+		return -1;
+
+	fp = fdopen(ofd, "r");
+	if (!fp)
+		goto out;
+
+	do {
+		n = getline(&buf, &l, fp);
+		if (n > 0) {
+			for (p = buf; isspace(*p); p++)
+				;
+			val = atoi(p);
+			found = true;
+			if (show_output)
+				printf("%s", buf);
+			CU_TEST(val < 10000000);
+		}
+	} while (n >= 0);
+
+	free(buf);
+ out:
+	ret = wait_for_exec(pid);
+	if (fp)
+		fclose(fp);
+	else {
+		perror("fp");
+	}
+	if (!found)
+		ret = -1;
+	close(ofd);
+	close(efd);
+	return ret > 0 ? 0 : ret;
+}
+
+static void test_trace_record_max(void)
+{
+	int ret;
+
+	ret = run_trace("record", TRACECMD_OUT, "-p", "function", "-m", "5000",
+			"sleep", "10", NULL);
+	CU_TEST(ret == 0);
+
+	ret = read_stats(TRACECMD_FILE, ".*bytes in size.*", "report", TRACECMD_IN, "--stat", NULL);
+	CU_TEST(ret == 0);
+}
+
+static void test_trace_convert6(void)
+{
+	struct stat st;
+	int ret;
+
+	/* If the trace data is already created, just use it, otherwise make it again */
+	if (stat(TRACECMD_FILE, &st) < 0) {
+		ret = run_trace("record", TRACECMD_OUT, "-e", "sched", "sleep", "1", NULL);
+		CU_TEST(ret == 0);
+	}
+	ret = grep_match("[ \t]6[ \t]*\\[Version\\]", "dump", TRACECMD_IN2, NULL);
+	CU_TEST(ret == 0);
+}
+
+struct callback_data {
+	long			counter;
+	struct trace_seq	seq;
+};
+
+static int read_events(struct tracecmd_input *handle, struct tep_record *record,
+		       int cpu, void *data)
+{
+	struct tep_handle *tep = tracecmd_get_tep(handle);
+	struct callback_data *cd = data;
+	struct trace_seq *seq = &cd->seq;
+
+	cd->counter++;
+
+	trace_seq_reset(seq);
+	tep_print_event(tep, seq, record, "%6.1000d", TEP_PRINT_TIME);
+	trace_seq_printf(seq, " [%03d] ", cpu);
+	tep_print_event(tep, seq, record, "%s-%d %s %s\n",
+			TEP_PRINT_COMM, TEP_PRINT_PID,
+			TEP_PRINT_NAME, TEP_PRINT_INFO);
+	if (show_output)
+		trace_seq_do_printf(seq);
+	return 0;
+}
+
+static int read_events_10(struct tracecmd_input *handle, struct tep_record *record,
+			  int cpu, void *data)
+{
+	struct callback_data *cd = data;
+
+	read_events(handle, record, cpu, data);
+	return  cd->counter < 10 ? 0 : 1;
+}
+
+static void test_trace_library_read(void)
+{
+	struct tracecmd_input *handle;
+	struct callback_data data;
+	struct stat st;
+	int ret;
+
+	data.counter = 0;
+	trace_seq_init(&data.seq);
+
+	/* If the trace data is already created, just use it, otherwise make it again */
+	if (stat(TRACECMD_FILE, &st) < 0) {
+		ret = run_trace("record", TRACECMD_OUT, "-e", "sched", "sleep", "1", NULL);
+		CU_TEST(ret == 0);
+	}
+
+	handle = tracecmd_open(TRACECMD_FILE, 0);
+	CU_TEST(handle != NULL);
+	ret = tracecmd_iterate_events(handle, NULL, 0, read_events, &data);
+	CU_TEST(ret == 0);
+
+	tracecmd_close(handle);
+
+	CU_TEST(data.counter > 0);
+	trace_seq_destroy(&data.seq);
+}
+
+static void test_trace_library_read_inc(void)
+{
+	struct tracecmd_input *handle;
+	struct callback_data data;
+	struct stat st;
+	long save_count;
+	long total = 0;
+	int ret;
+
+	data.counter = 0;
+	trace_seq_init(&data.seq);
+
+	/* If the trace data is already created, just use it, otherwise make it again */
+	if (stat(TRACECMD_FILE, &st) < 0) {
+		ret = run_trace("record", TRACECMD_OUT, "-e", "sched", "sleep", "1", NULL);
+		CU_TEST(ret == 0);
+	}
+
+	/* First read all again */
+	handle = tracecmd_open(TRACECMD_FILE, 0);
+	CU_TEST(handle != NULL);
+	ret = tracecmd_iterate_events(handle, NULL, 0, read_events, &data);
+	CU_TEST(ret == 0);
+	CU_TEST(data.counter > 0);
+
+	/* Save the counter */
+	save_count = data.counter;
+
+	tracecmd_iterate_reset(handle);
+
+	/* Read 10 at a time */
+	do {
+		data.counter = 0;
+		ret = tracecmd_iterate_events(handle, NULL, 0, read_events_10, &data);
+		CU_TEST(ret >= 0);
+		CU_TEST(data.counter <= 10);
+		total += data.counter;
+	} while (data.counter);
+	CU_TEST(ret == 0);
+
+	CU_TEST(total == save_count);
+
+	trace_seq_destroy(&data.seq);
+	tracecmd_close(handle);
+}
+
+static void test_trace_library_read_back(void)
+{
+	struct tracecmd_input *handle;
+	struct callback_data data;
+	struct stat st;
+	long save_count;
+	int ret;
+
+	data.counter = 0;
+	trace_seq_init(&data.seq);
+
+	/* If the trace data is already created, just use it, otherwise make it again */
+	if (stat(TRACECMD_FILE, &st) < 0) {
+		ret = run_trace("record", TRACECMD_OUT, "-e", "sched", "sleep", "1", NULL);
+		CU_TEST(ret == 0);
+	}
+
+	/* First read all again */
+	handle = tracecmd_open(TRACECMD_FILE, 0);
+	CU_TEST(handle != NULL);
+	ret = tracecmd_iterate_events(handle, NULL, 0, read_events, &data);
+	CU_TEST(ret == 0);
+	CU_TEST(data.counter > 0);
+
+	/* Save the counter */
+	save_count = data.counter;
+
+	tracecmd_iterate_reset(handle);
+
+	/* Read backwards */
+	data.counter = 0;
+	ret = tracecmd_iterate_events_reverse(handle, NULL, 0, read_events, &data, false);
+	CU_TEST(ret == 0);
+	CU_TEST(data.counter == save_count);
+
+	/* Read forward again */
+	data.counter = 0;
+	ret = tracecmd_iterate_events(handle, NULL, 0, read_events, &data);
+	CU_TEST(ret == 0);
+	CU_TEST(data.counter == save_count);
+
+	/* Read backwards from where we left off */
+	data.counter = 0;
+	ret = tracecmd_iterate_events_reverse(handle, NULL, 0, read_events, &data, true);
+	CU_TEST(ret == 0);
+	CU_TEST(data.counter == save_count);
+
+	trace_seq_destroy(&data.seq);
+	tracecmd_close(handle);
+}
+
+static int test_suite_destroy(void)
+{
+	unlink(TRACECMD_FILE);
+	unlink(TRACECMD_FILE2);
+	return 0;
+}
+
+static int test_suite_init(void)
+{
+	struct stat st;
+	const char *p;
+
+	/* The test must be in the utest directory */
+	for (p = argv0 + strlen(argv0) - 1; p > argv0 && *p != '/'; p--)
+		;
+
+	if (*p == '/')
+		snprintf(tracecmd_exec, PATH_MAX, "%.*s/../tracecmd/trace-cmd",
+			 (int)(p - argv0), argv0);
+	else
+		strncpy(tracecmd_exec, "../tracecmd/trace-cmd", PATH_MAX);
+
+	if (stat(tracecmd_exec, &st) < 0) {
+		fprintf(stderr, "In tree trace-cmd executable not found\n");
+		return 1;
+	}
+
+	if (!(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+		fprintf(stderr, "In tree trace-cmd executable not executable\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+void test_tracecmd_lib(void)
+{
+	CU_pSuite suite = NULL;
+
+	suite = CU_add_suite(TRACECMD_SUITE, test_suite_init, test_suite_destroy);
+	if (suite == NULL) {
+		fprintf(stderr, "Suite \"%s\" cannot be ceated\n", TRACECMD_SUITE);
+		return;
+	}
+	CU_add_test(suite, "Simple record and report",
+		    test_trace_record_report);
+	CU_add_test(suite, "Create a histogram",
+		    test_trace_sqlhist_hist);
+	CU_add_test(suite, "Test convert from v7 to v6",
+		    test_trace_convert6);
+	CU_add_test(suite, "Use libraries to read file",
+		    test_trace_library_read);
+	CU_add_test(suite, "Use libraries to read file incremental",
+		    test_trace_library_read_inc);
+	CU_add_test(suite, "Use libraries to read file backwards",
+		    test_trace_library_read_back);
+	CU_add_test(suite, "Test max length",
+		    test_trace_record_max);
+}
diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c
deleted file mode 100644
index 9c9eee0..0000000
--- a/utest/tracefs-utest.c
+++ /dev/null
@@ -1,630 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1
-/*
- * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
- *
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <time.h>
-#include <dirent.h>
-
-#include <CUnit/CUnit.h>
-#include <CUnit/Basic.h>
-
-#include "tracefs.h"
-
-#define TRACEFS_SUITE		"trasefs library"
-#define TEST_INSTANCE_NAME	"cunit_test_iter"
-#define TEST_ARRAY_SIZE		500
-
-static struct tracefs_instance *test_instance;
-static struct tep_handle *test_tep;
-struct test_sample {
-	int cpu;
-	int value;
-};
-static struct test_sample test_array[TEST_ARRAY_SIZE];
-static int test_found;
-
-static int test_callback(struct tep_event *event, struct tep_record *record,
-			  int cpu, void *context)
-{
-	struct tep_format_field *field;
-	struct test_sample *sample;
-	int *cpu_test = (int *)context;
-	int i;
-
-	if (cpu_test && *cpu_test >= 0 && *cpu_test != cpu)
-		return 0;
-	field = tep_find_field(event, "buf");
-	if (field) {
-		sample = ((struct test_sample *)(record->data + field->offset));
-		for (i = 0; i < TEST_ARRAY_SIZE; i++) {
-			if (test_array[i].value == sample->value &&
-			    test_array[i].cpu == cpu) {
-				test_array[i].value = 0;
-				test_found++;
-				break;
-			}
-		}
-	}
-
-	return 0;
-}
-
-static void test_iter_write(void)
-{
-	int cpus = sysconf(_SC_NPROCESSORS_CONF);
-	cpu_set_t *cpuset, *cpusave;
-	int cpu_size;
-	char *path;
-	int i, fd;
-	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);
-
-	path = tracefs_instance_get_file(test_instance, "trace_marker");
-	CU_TEST(path != NULL);
-	fd = open(path, O_WRONLY);
-	tracefs_put_tracing_file(path);
-	CU_TEST(fd >= 0);
-
-	for (i = 0; i < TEST_ARRAY_SIZE; i++) {
-		test_array[i].cpu = rand() % cpus;
-		test_array[i].value = random();
-		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);
-		ret = write(fd, test_array + i, sizeof(struct test_sample));
-		CU_TEST(ret == sizeof(struct test_sample));
-	}
-
-	sched_setaffinity(0, cpu_size, cpusave);
-	close(fd);
-}
-
-
-static void iter_raw_events_on_cpu(int cpu)
-{
-	int check = 0;
-	int ret;
-	int i;
-
-	test_found = 0;
-	test_iter_write();
-	ret = tracefs_iterate_raw_events(test_tep, test_instance, NULL, 0,
-					 test_callback, &cpu);
-	CU_TEST(ret == 0);
-	if (cpu < 0) {
-		CU_TEST(test_found == TEST_ARRAY_SIZE);
-	} else {
-		for (i = 0; i < TEST_ARRAY_SIZE; i++) {
-			if (test_array[i].cpu == cpu) {
-				check++;
-				CU_TEST(test_array[i].value == 0)
-			} else {
-				CU_TEST(test_array[i].value != 0)
-			}
-		}
-		CU_TEST(test_found == check);
-	}
-}
-
-static void test_iter_raw_events(void)
-{
-	int cpus = sysconf(_SC_NPROCESSORS_CONF);
-	int ret;
-	int i;
-
-	ret = tracefs_iterate_raw_events(NULL, test_instance, NULL, 0, test_callback, NULL);
-	CU_TEST(ret < 0);
-	ret = tracefs_iterate_raw_events(test_tep, NULL, NULL, 0, test_callback, NULL);
-	CU_TEST(ret == 0);
-	ret = tracefs_iterate_raw_events(test_tep, test_instance, NULL, 0, NULL, NULL);
-	CU_TEST(ret < 0);
-
-	iter_raw_events_on_cpu(-1);
-	for (i = 0; i < cpus; i++)
-		iter_raw_events_on_cpu(i);
-}
-
-#define RAND_STR_SIZE 20
-#define RAND_ASCII "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-static const char *get_rand_str()
-{
-	static char str[RAND_STR_SIZE];
-	static char sym[] = RAND_ASCII;
-	struct timespec clk;
-	int i;
-
-	clock_gettime(CLOCK_REALTIME, &clk);
-	srand(clk.tv_nsec);
-	for (i = 0; i < RAND_STR_SIZE; i++)
-		str[i] = sym[rand() % (sizeof(sym) - 1)];
-
-	str[RAND_STR_SIZE - 1] = 0;
-	return str;
-}
-
-static void test_trace_file(void)
-{
-	const char *tmp = get_rand_str();
-	const char *tdir;
-	struct stat st;
-	char *file;
-
-	tdir  = tracefs_tracing_dir();
-	CU_TEST(tdir != NULL);
-	CU_TEST(stat(tdir, &st) == 0);
-	CU_TEST(S_ISDIR(st.st_mode));
-
-	file = tracefs_get_tracing_file(NULL);
-	CU_TEST(file == NULL);
-	file = tracefs_get_tracing_file(tmp);
-	CU_TEST(file != NULL);
-	CU_TEST(stat(file, &st) != 0);
-	tracefs_put_tracing_file(file);
-
-	file = tracefs_get_tracing_file("trace");
-	CU_TEST(file != NULL);
-	CU_TEST(stat(file, &st) == 0);
-	tracefs_put_tracing_file(file);
-}
-
-static void test_instance_file_read(struct tracefs_instance *inst, char *fname)
-{
-	const char *tdir  = tracefs_tracing_dir();
-	char buf[BUFSIZ];
-	char *fpath;
-	char *file;
-	size_t fsize = 0;
-	int size = 0;
-	int fd;
-
-	if (inst) {
-		CU_TEST(asprintf(&fpath, "%s/instances/%s/%s",
-			tdir, tracefs_instance_get_name(inst), fname) > 0);
-	} else {
-		CU_TEST(asprintf(&fpath, "%s/%s", tdir, fname) > 0);
-	}
-
-	memset(buf, 0, BUFSIZ);
-	fd = open(fpath, O_RDONLY);
-	CU_TEST(fd >= 0);
-	fsize = read(fd, buf, BUFSIZ);
-	CU_TEST(fsize >= 0);
-	close(fd);
-	buf[BUFSIZ - 1] = 0;
-
-	file = tracefs_instance_file_read(inst, fname, &size);
-	CU_TEST(file != NULL);
-	CU_TEST(size == fsize);
-	CU_TEST(strcmp(file, buf) == 0);
-
-	free(fpath);
-	free(file);
-}
-
-#define ALL_TRACERS	"available_tracers"
-#define CUR_TRACER	"current_tracer"
-#define PER_CPU		"per_cpu"
-static void test_instance_file(void)
-{
-	struct tracefs_instance *instance = NULL;
-	struct tracefs_instance *second = NULL;
-	const char *name = get_rand_str();
-	const char *inst_name = NULL;
-	const char *tdir;
-	char *inst_file;
-	char *inst_dir;
-	struct stat st;
-	char *fname;
-	char *file1;
-	char *file2;
-	char *tracer;
-	int size;
-	int ret;
-
-	tdir  = tracefs_tracing_dir();
-	CU_TEST(tdir != NULL);
-	CU_TEST(asprintf(&inst_dir, "%s/instances/%s", tdir, name) > 0);
-	CU_TEST(stat(inst_dir, &st) != 0);
-
-	CU_TEST(tracefs_instance_exists(name) == false);
-	instance = tracefs_instance_create(name);
-	CU_TEST(instance != NULL);
-	CU_TEST(tracefs_instance_is_new(instance));
-	second = tracefs_instance_create(name);
-	CU_TEST(second != NULL);
-	CU_TEST(!tracefs_instance_is_new(second));
-	tracefs_instance_free(second);
-	CU_TEST(tracefs_instance_exists(name) == true);
-	CU_TEST(stat(inst_dir, &st) == 0);
-	CU_TEST(S_ISDIR(st.st_mode));
-	inst_name = tracefs_instance_get_name(instance);
-	CU_TEST(inst_name != NULL);
-	CU_TEST(strcmp(inst_name, name) == 0);
-
-	fname = tracefs_instance_get_dir(NULL);
-	CU_TEST(fname != NULL);
-	CU_TEST(strcmp(fname, tdir) == 0);
-	free(fname);
-
-	fname = tracefs_instance_get_dir(instance);
-	CU_TEST(fname != NULL);
-	CU_TEST(strcmp(fname, inst_dir) == 0);
-	free(fname);
-
-	CU_TEST(asprintf(&fname, "%s/"ALL_TRACERS, tdir) > 0);
-	CU_TEST(fname != NULL);
-	inst_file = tracefs_instance_get_file(NULL, ALL_TRACERS);
-	CU_TEST(inst_file != NULL);
-	CU_TEST(strcmp(fname, inst_file) == 0);
-	tracefs_put_tracing_file(inst_file);
-	free(fname);
-
-	CU_TEST(asprintf(&fname, "%s/instances/%s/"ALL_TRACERS, tdir, name) > 0);
-	CU_TEST(fname != NULL);
-	CU_TEST(stat(fname, &st) == 0);
-	inst_file = tracefs_instance_get_file(instance, ALL_TRACERS);
-	CU_TEST(inst_file != NULL);
-	CU_TEST(strcmp(fname, inst_file) == 0);
-
-	test_instance_file_read(NULL, ALL_TRACERS);
-	test_instance_file_read(instance, ALL_TRACERS);
-
-	file1 = tracefs_instance_file_read(instance, ALL_TRACERS, NULL);
-	CU_TEST(file1 != NULL);
-	tracer = strtok(file1, " ");
-	CU_TEST(tracer != NULL);
-	ret = tracefs_instance_file_write(instance, CUR_TRACER, tracer);
-	CU_TEST(ret == strlen(tracer));
-	file2 = tracefs_instance_file_read(instance, CUR_TRACER, &size);
-	CU_TEST(file2 != NULL);
-	CU_TEST(size >= strlen(tracer));
-	CU_TEST(strncmp(file2, tracer, strlen(tracer)) == 0);
-	free(file1);
-	free(file2);
-
-	tracefs_put_tracing_file(inst_file);
-	free(fname);
-
-	CU_TEST(tracefs_file_exists(NULL, (char *)name) == false);
-	CU_TEST(tracefs_dir_exists(NULL, (char *)name) == false);
-	CU_TEST(tracefs_file_exists(instance, (char *)name) == false);
-	CU_TEST(tracefs_dir_exists(instance, (char *)name) == false);
-
-	CU_TEST(tracefs_file_exists(NULL, CUR_TRACER) == true);
-	CU_TEST(tracefs_dir_exists(NULL, CUR_TRACER) == false);
-	CU_TEST(tracefs_file_exists(instance, CUR_TRACER) == true);
-	CU_TEST(tracefs_dir_exists(instance, CUR_TRACER) == false);
-
-	CU_TEST(tracefs_file_exists(NULL, PER_CPU) == false);
-	CU_TEST(tracefs_dir_exists(NULL, PER_CPU) == true);
-	CU_TEST(tracefs_file_exists(instance, PER_CPU) == false);
-	CU_TEST(tracefs_dir_exists(instance, PER_CPU) == true);
-
-	CU_TEST(tracefs_instance_destroy(NULL) != 0);
-	CU_TEST(tracefs_instance_destroy(instance) == 0);
-	CU_TEST(tracefs_instance_destroy(instance) != 0);
-	tracefs_instance_free(instance);
-	CU_TEST(stat(inst_dir, &st) != 0);
-	free(inst_dir);
-}
-
-static void exclude_string(char **strings, char *name)
-{
-	int i;
-
-	for (i = 0; strings[i]; i++) {
-		if (strcmp(strings[i], name) == 0) {
-			free(strings[i]);
-			strings[i] = strdup("/");
-			return;
-		}
-	}
-}
-
-static void test_check_files(const char *fdir, char **files)
-{
-	struct dirent *dent;
-	DIR *dir;
-	int i;
-
-	dir = opendir(fdir);
-	CU_TEST(dir != NULL);
-
-	while ((dent = readdir(dir)))
-		exclude_string(files, dent->d_name);
-
-	closedir(dir);
-
-	for (i = 0; files[i]; i++)
-		CU_TEST(files[i][0] == '/');
-}
-
-static void test_system_event(void)
-{
-	const char *tdir;
-	char **systems;
-	char **events;
-	char *sdir = NULL;
-
-	tdir  = tracefs_tracing_dir();
-	CU_TEST(tdir != NULL);
-
-	systems = tracefs_event_systems(tdir);
-	CU_TEST(systems != NULL);
-
-	events = tracefs_system_events(tdir, systems[0]);
-	CU_TEST(events != NULL);
-
-	asprintf(&sdir, "%s/events/%s", tdir, systems[0]);
-	CU_TEST(sdir != NULL);
-	test_check_files(sdir, events);
-	free(sdir);
-	sdir = NULL;
-
-	asprintf(&sdir, "%s/events", tdir);
-	CU_TEST(sdir != NULL);
-	test_check_files(sdir, systems);
-
-	tracefs_list_free(systems);
-	tracefs_list_free(events);
-
-	free(sdir);
-}
-
-static void test_tracers(void)
-{
-	const char *tdir;
-	char **tracers;
-	char *tfile;
-	char *tracer;
-	int i;
-
-	tdir  = tracefs_tracing_dir();
-	CU_TEST(tdir != NULL);
-
-	tracers = tracefs_tracers(tdir);
-	CU_TEST(tracers != NULL);
-
-	tfile = tracefs_instance_file_read(NULL, ALL_TRACERS, NULL);
-
-	tracer = strtok(tfile, " ");
-	while (tracer) {
-		exclude_string(tracers, tracer);
-		tracer = strtok(NULL, " ");
-	}
-
-	for (i = 0; tracers[i]; i++)
-		CU_TEST(tracers[i][0] == '/');
-
-	tracefs_list_free(tracers);
-	free(tfile);
-}
-
-static void test_check_events(struct tep_handle *tep, char *system, bool exist)
-{
-	struct dirent *dent;
-	char file[PATH_MAX];
-	char buf[1024];
-	char *edir = NULL;
-	const char *tdir;
-	DIR *dir;
-	int fd;
-
-	tdir  = tracefs_tracing_dir();
-	CU_TEST(tdir != NULL);
-
-	asprintf(&edir, "%s/events/%s", tdir, system);
-	dir = opendir(edir);
-	CU_TEST(dir != NULL);
-
-	while ((dent = readdir(dir))) {
-		if (dent->d_name[0] == '.')
-			continue;
-		sprintf(file, "%s/%s/id", edir, dent->d_name);
-		fd = open(file, O_RDONLY);
-		if (fd < 0)
-			continue;
-		CU_TEST(read(fd, buf, 1024) > 0);
-		if (exist) {
-			CU_TEST(tep_find_event(tep, atoi(buf)) != NULL);
-		} else {
-			CU_TEST(tep_find_event(tep, atoi(buf)) == NULL);
-		}
-
-		close(fd);
-	}
-
-	closedir(dir);
-	free(edir);
-
-}
-
-static void test_local_events(void)
-{
-	struct tep_handle *tep;
-	const char *tdir;
-	char **systems;
-	char *lsystems[3];
-	int i;
-
-	tdir  = tracefs_tracing_dir();
-	CU_TEST(tdir != NULL);
-
-	tep = tracefs_local_events(tdir);
-	CU_TEST(tep != NULL);
-
-	systems = tracefs_event_systems(tdir);
-	CU_TEST(systems != NULL);
-
-	for (i = 0; systems[i]; i++)
-		test_check_events(tep, systems[i], true);
-	tep_free(tep);
-
-	memset(lsystems, 0, sizeof(lsystems));
-	for (i = 0; systems[i]; i++) {
-		if (!lsystems[0])
-			lsystems[0] = systems[i];
-		else if (!lsystems[2])
-			lsystems[2] = systems[i];
-		else
-			break;
-	}
-
-	if (lsystems[0] && lsystems[2]) {
-		tep = tracefs_local_events_system(tdir,
-						  (const char * const *)lsystems);
-		CU_TEST(tep != NULL);
-		test_check_events(tep, lsystems[0], true);
-		test_check_events(tep, lsystems[2], false);
-	}
-	tep_free(tep);
-
-	tep = tep_alloc();
-	CU_TEST(tep != NULL);
-	CU_TEST(tracefs_fill_local_events(tdir, tep, NULL) == 0);
-	for (i = 0; systems[i]; i++)
-		test_check_events(tep, systems[i], true);
-
-	tep_free(tep);
-
-	tracefs_list_free(systems);
-}
-
-struct test_walk_instance {
-	struct tracefs_instance *instance;
-	bool found;
-};
-#define WALK_COUNT 10
-int test_instances_walk_cb(const char *name, void *data)
-{
-	struct test_walk_instance *instances  = (struct test_walk_instance *)data;
-	int i;
-
-	CU_TEST(instances != NULL);
-	CU_TEST(name != NULL);
-
-	for (i = 0; i < WALK_COUNT; i++) {
-		if (!strcmp(name,
-			    tracefs_instance_get_name(instances[i].instance))) {
-			instances[i].found = true;
-			break;
-		}
-	}
-
-	return 0;
-}
-
-static void test_instances_walk(void)
-{
-	struct test_walk_instance instances[WALK_COUNT];
-	int i;
-
-	memset(instances, 0, WALK_COUNT * sizeof(struct test_walk_instance));
-	for (i = 0; i < WALK_COUNT; i++) {
-		instances[i].instance = tracefs_instance_create(get_rand_str());
-		CU_TEST(instances[i].instance != NULL);
-	}
-
-	CU_TEST(tracefs_instances_walk(test_instances_walk_cb, instances) == 0);
-	for (i = 0; i < WALK_COUNT; i++) {
-		CU_TEST(instances[i].found);
-		tracefs_instance_destroy(instances[i].instance);
-		instances[i].found = false;
-	}
-
-	CU_TEST(tracefs_instances_walk(test_instances_walk_cb, instances) == 0);
-	for (i = 0; i < WALK_COUNT; i++) {
-		CU_TEST(!instances[i].found);
-		tracefs_instance_free(instances[i].instance);
-	}
-}
-
-static void current_clock_check(const char *clock)
-{
-	int size = 0;
-	char *clocks;
-	char *str;
-
-	clocks = tracefs_instance_file_read(test_instance, "trace_clock", &size);
-	CU_TEST(clocks != NULL);
-	CU_TEST(size > strlen(clock));
-	str = strstr(clocks, clock);
-	CU_TEST(str != NULL);
-	CU_TEST(str != clocks);
-	CU_TEST(*(str - 1) == '[');
-	CU_TEST(*(str + strlen(clock)) == ']');
-	free(clocks);
-}
-
-static void test_get_clock(void)
-{
-	const char *clock;
-
-	clock = tracefs_get_clock(test_instance);
-	CU_TEST(clock != NULL);
-	current_clock_check(clock);
-	free((char *)clock);
-}
-
-static int test_suite_destroy(void)
-{
-	tracefs_instance_destroy(test_instance);
-	tracefs_instance_free(test_instance);
-	tep_free(test_tep);
-	return 0;
-}
-
-static int test_suite_init(void)
-{
-	const char *systems[] = {"ftrace", NULL};
-
-	test_tep = tracefs_local_events_system(NULL, systems);
-	if (test_tep == NULL)
-		return 1;
-	test_instance = tracefs_instance_create(TEST_INSTANCE_NAME);
-	if (!test_instance)
-		return 1;
-
-	return 0;
-}
-
-void test_tracefs_lib(void)
-{
-	CU_pSuite suite = NULL;
-
-	suite = CU_add_suite(TRACEFS_SUITE, test_suite_init, test_suite_destroy);
-	if (suite == NULL) {
-		fprintf(stderr, "Suite \"%s\" cannot be ceated\n", TRACEFS_SUITE);
-		return;
-	}
-	CU_add_test(suite, "tracing file / directory APIs",
-		    test_trace_file);
-	CU_add_test(suite, "instance file / directory APIs",
-		    test_instance_file);
-	CU_add_test(suite, "systems and events APIs",
-		    test_system_event);
-	CU_add_test(suite, "tracefs_iterate_raw_events API",
-		    test_iter_raw_events);
-	CU_add_test(suite, "tracefs_tracers API",
-		    test_tracers);
-	CU_add_test(suite, "tracefs_local events API",
-		    test_local_events);
-	CU_add_test(suite, "tracefs_instances_walk API",
-		    test_instances_walk);
-	CU_add_test(suite, "tracefs_get_clock API",
-		    test_get_clock);
-}