Snap for 10447354 from 287e80b3a36113050663245e7f2c00d274188f18 to mainline-networking-release

Change-Id: I1af67cd601959f99b566776962b39fada73ebc2f
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3e72a58
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*~
+\#*\#
+.pc
+libtracefs.pc
+TAGS
+tags
+lib/
+bin/
+patches/
+build_prefix
+build_uninstall
+tfs_version.h
+*.o
+.*.d
+sqlhist
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..4b2b251
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,69 @@
+package {
+    default_applicable_licenses: ["external_libtracefs_license"],
+}
+
+// See: http://go/android-license-faq
+license {
+    name: "external_libtracefs_license",
+
+    visibility: [":__subpackages__"],
+
+    license_kinds: [
+        "SPDX-license-identifier-GPL-2.0",
+        "SPDX-license-identifier-GPL-2.0-only",
+        "SPDX-license-identifier-GPL-2.0+",
+        "SPDX-license-identifier-GPL-2.0-or-later",
+        "SPDX-license-identifier-GPL-3.0",
+        "SPDX-license-identifier-LGPL-2.1",
+        "SPDX-license-identifier-LGPL-2.1+",
+    ],
+
+    license_text: [
+        "LICENSE",
+    ],
+}
+
+cc_library {
+    name: "libtracefs",
+
+    // Restrict visibility due to GPL license
+    visibility: [
+        "//external/trace-cmd:__subpackages__",
+    ],
+
+    local_include_dirs: [
+        "include",
+        "src",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    srcs: ["src/*.c"],
+
+    shared: {
+        shared_libs: [
+            "libtraceevent",
+        ],
+        export_shared_lib_headers: [
+            "libtraceevent",
+        ],
+    },
+
+    static: {
+        static_libs: [
+            "libtraceevent",
+        ],
+        export_static_lib_headers: [
+            "libtraceevent",
+        ],
+    },
+
+    cflags: [
+        "-D_GNU_SOURCE",
+        "-Wno-unused-parameter",
+    ],
+
+    c_std: "gnu99",
+}
diff --git a/Documentation/.gitignore b/Documentation/.gitignore
new file mode 100644
index 0000000..4d2414d
--- /dev/null
+++ b/Documentation/.gitignore
@@ -0,0 +1,5 @@
+*.3
+sqlhist.1
+*.m
+*.xml
+*.html
diff --git a/Documentation/Makefile b/Documentation/Makefile
new file mode 100644
index 0000000..afcb6a5
--- /dev/null
+++ b/Documentation/Makefile
@@ -0,0 +1,247 @@
+# SPDX-License-Identifier: LGPL-2.1
+
+include $(src)/scripts/utils.mk
+
+
+# This Makefile and manpage XSL files were taken from libtraceevent
+# and modified for libtracefs.
+
+MAN3_TXT= \
+	$(wildcard libtracefs-*.txt) \
+	libtracefs.txt
+
+MAN1_TEXT= \
+	$(wildcard libtracefs-*.txt.1)
+
+MAN_TXT = $(MAN3_TXT)
+_MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
+_MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT))
+_DOC_MAN3=$(patsubst %.txt,%.m,$(MAN3_TXT))
+
+MAN_XML=$(addprefix $(OUTPUT),$(_MAN_XML))
+MAN_HTML=$(addprefix $(OUTPUT),$(_MAN_HTML))
+DOC_MAN3=$(addprefix $(OUTPUT),$(_DOC_MAN3))
+
+_MAN1_XML=$(patsubst %.txt.1,%.xml,$(MAN1_TEXT))
+_MAN1_HTML=$(patsubst %.txt.1,%.html,$(MAN1_TEXT))
+_DOC_MAN1=$(patsubst %.txt.1,%.m,$(MAN1_TEXT))
+
+MAN1_XML=$(addprefix $(OUTPUT),$(_MAN1_XML))
+MAN1_HTML=$(addprefix $(OUTPUT),$(_MAN1_HTML))
+DOC_MAN1=$(addprefix $(OUTPUT),$(_DOC_MAN1))
+
+
+# Make the path relative to DESTDIR, not prefix
+ifndef DESTDIR
+prefix?=$(HOME)
+endif
+bindir?=$(prefix)/bin
+htmldir?=$(prefix)/share/doc/libtracefs-doc
+pdfdir?=$(prefix)/share/doc/libtracefs-doc
+mandir?=$(prefix)/share/man
+man3dir=$(mandir)/man3
+man1dir=$(mandir)/man1
+
+ASCIIDOC=asciidoc
+ASCIIDOC_EXTRA = --unsafe -f asciidoc.conf
+ASCIIDOC_HTML = xhtml11
+MANPAGE_XSL = manpage-normal.xsl
+XMLTO_EXTRA =
+INSTALL?=install
+RM ?= rm -f
+
+ifdef USE_ASCIIDOCTOR
+ASCIIDOC = asciidoctor
+ASCIIDOC_EXTRA = -a compat-mode
+ASCIIDOC_EXTRA += -I. -rasciidoctor-extensions
+ASCIIDOC_EXTRA += -a mansource="libtracefs" -a manmanual="libtracefs Manual"
+ASCIIDOC_HTML = xhtml5
+endif
+
+ASCIIDOC_INSTALLED := $(shell command -v $(ASCIIDOC) 2> /dev/null)
+ifndef ASCIIDOC_INSTALLED
+	missing_tools += $(ASCIIDOC)
+endif
+
+XMLTO=xmlto
+XMLTO_INSTALLED := $(shell command -v $(XMLTO) 2> /dev/null)
+ifndef XMLTO_INSTALLED
+	missing_tools += $(XMLTO)
+endif
+
+#
+# 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).
+#
+
+ifdef ASCIIDOC8
+ASCIIDOC_EXTRA += -a asciidoc7compatible
+endif
+ifdef DOCBOOK_XSL_172
+ASCIIDOC_EXTRA += -a libtracefs-asciidoc-no-roff
+MANPAGE_XSL = manpage-1.72.xsl
+else
+	ifdef 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 libtracefs-asciidoc-no-roff
+	endif
+endif
+ifdef MAN_BOLD_LITERAL
+XMLTO_EXTRA += -m manpage-bold-literal.xsl
+endif
+ifdef DOCBOOK_SUPPRESS_SP
+XMLTO_EXTRA += -m manpage-suppress-sp.xsl
+endif
+
+SHELL_PATH ?= $(SHELL)
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+DESTDIR ?=
+DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
+
+export DESTDIR DESTDIR_SQ
+
+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1  =
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifneq ($(V),1)
+	QUIET_ASCIIDOC	= @echo '  ASCIIDOC '$@;
+	QUIET_XMLTO	= @echo '  XMLTO    '$@;
+	QUIET_SUBDIR0	= +@subdir=
+	QUIET_SUBDIR1	= ;$(NO_SUBDIR) \
+			   echo '  SUBDIR   ' $$subdir; \
+			  $(MAKE) $(PRINT_DIR) -C $$subdir
+	export V
+endif
+endif
+
+all: check-man-tools html man
+
+man: man3 man1
+man3: $(DOC_MAN3)
+man1: $(DOC_MAN1)
+
+html: $(MAN_HTML) $(MAN1_HTML)
+
+$(MAN_HTML) $(MAN1_HTML) $(DOC_MAN3) $(DOC_MAN1): asciidoc.conf
+
+install: check-man-tools install-man install-html
+
+check-man-tools:
+ifdef missing_tools
+	$(error "You need to install $(missing_tools) for man pages")
+endif
+
+install-%.3: $(OUTPUT)%.3
+	$(Q)$(call do_install,$<,$(man3dir),644);
+
+install-%.1: $(OUTPUT)%.1
+	$(Q)$(call do_install,$<,$(man1dir),644);
+
+do-install-man: man $(addprefix install-,$(wildcard $(OUTPUT)*.3)) $(addprefix install-,$(wildcard $(OUTPUT)*.1))
+
+install-man: man
+	$(Q)$(MAKE) -C . do-install-man
+
+install-%.txt: $(OUTPUT)%.html
+	$(Q)$(call do_install,$<,$(htmldir),644);
+
+install-%.txt.1: $(OUTPUT)%.html
+	$(Q)$(call do_install,$<,$(htmldir),644);
+
+do-install-html: html $(addprefix install-,$(wildcard *.txt)) $(addprefix install-,$(wildcard *.txt.1))
+
+install-html: html do-install-html
+
+uninstall: uninstall-man uninstall-html
+
+uninstall-man:
+	$(Q)$(RM) $(addprefix $(DESTDIR)$(man3dir)/,$(DOC_MAN3)) $(addprefix $(DESTDIR)$(man1dir)/,$(DOC_MAN1))
+
+uninstall-html:
+	$(Q)$(RM) $(addprefix $(DESTDIR)$(htmldir)/,$(MAN_HTML)) $(addprefix $(DESTDIR)$(htmldir)/,$(MAN1_HTML))
+
+ifdef missing_tools
+  DO_INSTALL_MAN = $(warning Please install $(missing_tools) to have the man pages installed)
+else
+  DO_INSTALL_MAN = do-install-man
+endif
+
+CLEAN_FILES =					\
+	$(MAN_XML) $(addsuffix +,$(MAN_XML))	\
+	$(MAN_HTML) $(addsuffix +,$(MAN_HTML))	\
+	$(MAN1_HTML) $(addsuffix +,$(MAN1_HTML))	\
+	$(filter-out $(MAN1_TEXT),$(wildcard *.1)) \
+	$(DOC_MAN3) *.3 *.m
+
+clean:
+	$(Q) $(RM) $(CLEAN_FILES)
+
+ifdef USE_ASCIIDOCTOR
+$(OUTPUT)%.3 : $(OUTPUT)%.txt
+	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+	$(ASCIIDOC) -b manpage -d manpage \
+		$(ASCIIDOC_EXTRA) -alibtracefs_version=$(TRACEFS_VERSION) -o $@+ $< && \
+	mv $@+ $@
+$(OUTPUT)%.1 : $(OUTPUT)%.txt.1
+	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+	$(ASCIIDOC) -b manpage -d manpage \
+		$(ASCIIDOC_EXTRA) -alibtracefs_version=$(TRACEFS_VERSION) -o $@+ $< && \
+	mv $@+ $@
+endif
+
+$(OUTPUT)%.m : $(OUTPUT)%.xml
+	$(QUIET_XMLTO)$(RM) $@ && \
+	$(XMLTO) -o $(OUTPUT). -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<; \
+	touch $@
+
+$(OUTPUT)%.xml : %.txt
+	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+	$(ASCIIDOC) -b docbook -d manpage \
+		$(ASCIIDOC_EXTRA) -alibtracefs_version=$(TRACEFS_VERSION) -o $@+ $< && \
+	mv $@+ $@
+
+$(OUTPUT)%.xml : %.txt.1
+	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+	$(ASCIIDOC) -b docbook -d manpage \
+		$(ASCIIDOC_EXTRA) -alibtracefs_version=$(TRACEFS_VERSION) -o $@+ $< && \
+	mv $@+ $@
+
+$(MAN_HTML): $(OUTPUT)%.html : %.txt
+	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+	$(ASCIIDOC) -b $(ASCIIDOC_HTML) -d manpage \
+		$(ASCIIDOC_EXTRA) -alibtracefs_version=$(TRACEFS_VERSION) -o $@+ $< && \
+	mv $@+ $@
+
+$(MAN1_HTML): $(OUTPUT)%.html : %.txt.1
+	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+	$(ASCIIDOC) -b $(ASCIIDOC_HTML) -d manpage \
+		$(ASCIIDOC_EXTRA) -alibtracefs_version=$(TRACEFS_VERSION) -o $@+ $< && \
+	mv $@+ $@
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
new file mode 100644
index 0000000..c15aa13
--- /dev/null
+++ b/Documentation/asciidoc.conf
@@ -0,0 +1,120 @@
+## linktep: macro
+#
+# Usage: linktep:command[manpage-section]
+#
+# Note, {0} is the manpage section, while {target} is the command.
+#
+# Show TEP link as: <command>(<section>); if section is defined, else just show
+# the command.
+
+[macros]
+(?su)[\\]?(?P<name>linktep):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
+
+[attributes]
+asterisk=&#42;
+plus=&#43;
+caret=&#94;
+startsb=&#91;
+endsb=&#93;
+tilde=&#126;
+
+ifdef::backend-docbook[]
+[linktep-inlinemacro]
+{0%{target}}
+{0#<citerefentry>}
+{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>}
+{0#</citerefentry>}
+endif::backend-docbook[]
+
+ifdef::backend-docbook[]
+ifndef::tep-asciidoc-no-roff[]
+# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
+# v1.72 breaks with this because it replaces dots not in roff requests.
+[listingblock]
+<example><title>{title}</title>
+<literallayout>
+ifdef::doctype-manpage[]
+&#10;.ft C&#10;
+endif::doctype-manpage[]
+|
+ifdef::doctype-manpage[]
+&#10;.ft&#10;
+endif::doctype-manpage[]
+</literallayout>
+{title#}</example>
+endif::tep-asciidoc-no-roff[]
+
+ifdef::tep-asciidoc-no-roff[]
+ifdef::doctype-manpage[]
+# The following two small workarounds insert a simple paragraph after screen
+[listingblock]
+<example><title>{title}</title>
+<literallayout>
+|
+</literallayout><simpara></simpara>
+{title#}</example>
+
+[verseblock]
+<formalpara{id? id="{id}"}><title>{title}</title><para>
+{title%}<literallayout{id? id="{id}"}>
+{title#}<literallayout>
+|
+</literallayout>
+{title#}</para></formalpara>
+{title%}<simpara></simpara>
+endif::doctype-manpage[]
+endif::tep-asciidoc-no-roff[]
+endif::backend-docbook[]
+
+ifdef::doctype-manpage[]
+ifdef::backend-docbook[]
+[header]
+template::[header-declarations]
+<refentry>
+<refmeta>
+<refentrytitle>{mantitle}</refentrytitle>
+<manvolnum>{manvolnum}</manvolnum>
+<refmiscinfo class="source">libtracefs</refmiscinfo>
+<refmiscinfo class="version">{libtracefs_version}</refmiscinfo>
+<refmiscinfo class="manual">libtracefs Manual</refmiscinfo>
+</refmeta>
+<refnamediv>
+  <refname>{manname1}</refname>
+  <refname>{manname2}</refname>
+  <refname>{manname3}</refname>
+  <refname>{manname4}</refname>
+  <refname>{manname5}</refname>
+  <refname>{manname6}</refname>
+  <refname>{manname7}</refname>
+  <refname>{manname8}</refname>
+  <refname>{manname9}</refname>
+  <refname>{manname10}</refname>
+  <refname>{manname11}</refname>
+  <refname>{manname12}</refname>
+  <refname>{manname13}</refname>
+  <refname>{manname14}</refname>
+  <refname>{manname15}</refname>
+  <refname>{manname16}</refname>
+  <refname>{manname17}</refname>
+  <refname>{manname18}</refname>
+  <refname>{manname19}</refname>
+  <refname>{manname20}</refname>
+  <refname>{manname21}</refname>
+  <refname>{manname22}</refname>
+  <refname>{manname23}</refname>
+  <refname>{manname24}</refname>
+  <refname>{manname25}</refname>
+  <refname>{manname26}</refname>
+  <refname>{manname27}</refname>
+  <refname>{manname28}</refname>
+  <refname>{manname29}</refname>
+  <refname>{manname30}</refname>
+  <refpurpose>{manpurpose}</refpurpose>
+</refnamediv>
+endif::backend-docbook[]
+endif::doctype-manpage[]
+
+ifdef::backend-xhtml11[]
+[linktep-inlinemacro]
+<a href="{target}.html">{target}{0?({0})}</a>
+endif::backend-xhtml11[]
diff --git a/Documentation/libtracefs-cpu-open.txt b/Documentation/libtracefs-cpu-open.txt
new file mode 100644
index 0000000..c5a900a
--- /dev/null
+++ b/Documentation/libtracefs-cpu-open.txt
@@ -0,0 +1,100 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_cpu_open, tracefs_cpu_close, tracefs_cpu_alloc_fd, tracefs_cpu_free_fd - Opening trace_pipe_raw data for reading
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_cpu pass:[*]*tracefs_cpu_open*(struct tracefs_instance pass:[*]_instance_,
+				     int _cpu_, bool _nonblock_);
+void *tracefs_cpu_close*(struct tracefs_cpu pass:[*]_tcpu_);
+
+struct tracefs_cpu pass:[*]*tracefs_cpu_alloc_fd*(int _fd_, int _subbuf_size_, bool _nonblock_);
+void *tracefs_cpu_free_fd*(struct tracefs_cpu pass:[*]_tcpu_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to open the raw data from the trace_pipe_raw
+files in the tracefs file system in oder to read them with the *tracefs_cpu_read*(3)
+functions.
+
+The *tracefs_cpu_open()* creates a descriptor that can read the tracefs
+trace_pipe_raw file for a given _cpu_ in a given _instance_. If _instance_ is
+NULL than the toplevel trace_pipe_raw file is used.
+
+The *tracefs_cpu_close()* closes all the file descriptors associated to the trace_pipe_raw
+opened by *tracefs_cpu_open()*.
+
+The *tracefs_cpu_alloc_fd()* will create a tracefs_cpu descriptor from an existing
+file descriptor _fd_. This is useful to use when connecting to a socket or pipe where
+the other end is feeding raw tracing data in the same format as the trace_pipe_raw
+file would (like in guest to host tracing). The caller is responsible for determining
+the _subbuf_size_ that will be used to break up the sub-buffers being read by the
+file descriptor. The _nonblock_ is treated the same as the same parameter in
+*tracefs_cpu_open()*.
+
+The *tracefs_cpu_free_fd()* is used to free the descriptor returned by *tracefs_cpu_alloc_fd()*.
+It does all the clean up that *tracefs_cpu_close()* performs, and that could also be
+used to free up the descriptor created by *tracefs_cpu_alloc_fd()* but will also close
+the file descriptor passed in. Note that *tracefs_cpu_free_fd()* should not be used
+on the descriptor returned by *tracefs_cpu_open()* as it will not close the file descriptor
+created by it.
+
+RETURN VALUE
+------------
+The *tracefs_cpu_open()* returns a struct tracefs_cpu descriptor that can be
+used by the other functions or NULL on error.
+
+The *tracefs_cpu_alloc_fd()* returns a struct tracefs_cpu descriptor that can
+be used by the *tracefs_cpu_read*(3) related functions, where the descriptor
+will be reading the passed in _fd_ file descriptor.
+
+EXAMPLE
+-------
+See *tracefs_cpu_read*(3) for an example.
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2022 Google, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-cpu.txt b/Documentation/libtracefs-cpu.txt
new file mode 100644
index 0000000..d6215d9
--- /dev/null
+++ b/Documentation/libtracefs-cpu.txt
@@ -0,0 +1,240 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_cpu_read_size, tracefs_cpu_read, tracefs_cpu_buffered_read, tracefs_cpu_write,
+tracefs_cpu_stop, tracefs_cpu_flush, tracefs_cpu_flush_write, tracefs_cpu_pipe
+- Reading trace_pipe_raw data
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_cpu_read_size*(struct tracefs_cpu pass:[*]_tcpu_);
+int *tracefs_cpu_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+int *tracefs_cpu_buffered_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+int *tracefs_cpu_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_);
+int *tracefs_cpu_stop*(struct tracefs_cpu pass:[*]_tcpu_);
+int *tracefs_cpu_flush*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_);
+int *tracefs_cpu_flush_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_);
+int *tracefs_cpu_pipe*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to read the raw data from the trace_pipe_raw
+files in the tracefs file system.
+
+The *tracefs_cpu_read_size()* returns the subbuffer size of the trace_pipe_raw. This
+returns the minimum size of the buffer that is passed to the below functions.
+
+The *tracefs_cpu_read()* reads the trace_pipe_raw files associated to _tcpu_ into _buffer_.
+_buffer_ must be at least the size of the sub buffer of the ring buffer,
+which is returned by *tracefs_cpu_read_size()*. If _nonblock_ is set, and
+there's no data available, it will return immediately. Otherwise depending
+on how _tcpu_ was opened, it will block. If _tcpu_ was opened with nonblock
+set, then this _nonblock_ will make no difference.
+
+The *tracefs_cpu_buffered_read()* is basically the same as *tracefs_cpu_read()*
+except that it uses a pipe through splice to buffer reads. This will batch
+reads keeping the reading from the ring buffer less intrusive to the system,
+as just reading all the time can cause quite a disturbance. Note, one
+difference between this and *tracefs_cpu_read()* is that it will read only in
+sub buffer pages. If the ring buffer has not filled a page, then it will not
+return anything, even with _nonblock_ set.  Calls to *tracefs_cpu_flush()*
+should be done to read the rest of the file at the end of the trace.
+
+The *tracefs_cpu_write()* will pipe the data from the trace_pipe_raw
+file associated with _tcpu_ into the _wfd_ file descriptor. If _nonblock_ is set,
+then it will not block on if there's nothing to write. Note, it will only write
+sub buffer size data to _wfd_. Calls to tracefs_cpu_flush_write() are needed to
+write out the rest.
+
+The *tracefs_cpu_stop()* will attempt to unblock a task blocked on _tcpu_ reading it.
+On older kernels, it may not do anything for the pipe reads, as older kernels do not
+wake up tasks waiting on the ring buffer. Returns 0 if it definitely woke up any possible
+waiters, but returns 1 if it is not sure it worked and waiters may need to have a signal
+sent to them.
+
+The *tracefs_cpu_flush()* reads the trace_pipe_raw file associated by the _tcpu_ and puts it
+into _buffer_, which must be the size of the sub buffer which is retrieved.
+by *tracefs_cpu_read_size()*. This should be called at the end of tracing
+to get the rest of the data. This call will convert the file descriptor of
+trace_pipe_raw into non-blocking mode.
+
+The *tracefs_cpu_flush_write()* same as *trace_cpu_flush()* except it takes a file
+descriptor _wfd_ to flush the data into.
+
+The *tracefs_cpu_pipe()* is similar to *tracefs_cpu_write()* but the _wfd_ file descriptor
+must be a pipe. This call is an optimization of *tracefs_cpu_write()* that uses two calls
+to *splice*(2) in order to connect the trace_pipe_raw file descriptor with the write file
+descriptor. *splice*(2) requires that one of the passed in file descriptors is a pipe.
+If the application wants to pass the data to an existing pipe, there's no reason for
+there to be two *splice*(2) system calls and *tracefs_cpu_pipe()* can simply use a single
+call to _wfd_.
+
+RETURN VALUE
+------------
+The *tracefs_cpu_open()* returns a struct tracefs_cpu descriptor that can be
+used by the other functions or NULL on error.
+
+The *tracefs_cpu_read_size()* returns the minimum size of the buffers to be
+used with *tracefs_cpu_read()*, *tracefs_cpu_buffered_read()* and *tracefs_cpu_flush()*.
+Returns negative on error.
+
+The *tracefs_cpu_read()* returns the number of bytes read, or negative on error.
+
+The *tracefs_cpu_buffered_read()* returns the number of bytes read or negative on error.
+
+The *tracefs_cpu_write()* returns the number of bytes written to the file
+or negative on error.
+
+The *tracefs_cpu_stop()* returns zero if any waiters were guaranteed to be
+woken up from waiting on input, or returns one if this is an older kernel
+that does not supply that guarantee, and a signal may need to be sent to
+any waiters. Returns negative on error.
+
+The *tracefs_cpu_flush()* returns the number of bytes read or negative on error.
+
+The *tracefs_cpu_flush_write()* returns the number of bytes written to the
+file  or negative on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#define _LARGEFILE64_SOURCE
+#include <stdlib.h>
+#include <ctype.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <tracefs.h>
+
+struct thread_data {
+	struct tracefs_cpu	*tcpu;
+	int			done;
+	int			fd;
+};
+
+static void *thread_run(void *arg)
+{
+	struct thread_data *data = arg;
+	struct tracefs_cpu *tcpu = data->tcpu;
+	int fd = data->fd;
+	int ret;
+
+	while (!data->done) {
+		ret = tracefs_cpu_write(tcpu, fd, false);
+		printf("wrote %d\n", ret);
+	}
+	return NULL;
+}
+
+int main (int argc, char **argv)
+{
+	struct tracefs_instance *instance;
+	struct thread_data data;
+	pthread_t thread;
+	char *file;
+	int secs = 10;
+	int cpu;
+	int ret;
+
+	if (argc < 3 || !isdigit(argv[1][0])) {
+		printf("usage: %s cpu file_destination [sleep secs]\n\n", argv[0]);
+		exit(-1);
+	}
+
+	cpu = atoi(argv[1]);
+	file = argv[2];
+
+	if (argc > 3)
+		secs = atoi(argv[3]);
+
+	instance = tracefs_instance_create("cpu_write");
+	if (!instance) {
+		perror("create instance");
+		exit(-1);
+	}
+
+	memset(&data, 0, sizeof(data));
+
+	data.tcpu = tracefs_cpu_open(instance, cpu, 0);
+	if (!data.tcpu) {
+		perror("Open instance");
+		exit(-1);
+	}
+
+	data.fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
+	if (data.fd < 0) {
+		perror(file);
+		exit(-1);
+	}
+
+	pthread_create(&thread, NULL, thread_run, &data);
+
+	sleep(secs);
+
+	data.done = 1;
+	printf("stopping\n");
+	ret = tracefs_cpu_stop(data.tcpu);
+
+	printf("joining %d\n", ret);
+	pthread_join(thread, NULL);
+
+	tracefs_trace_off(instance);
+	do {
+		ret = tracefs_cpu_flush_write(data.tcpu, data.fd);
+		printf("flushed %d\n", ret);
+	} while (ret > 0);
+	tracefs_trace_on(instance);
+
+	tracefs_cpu_close(data.tcpu);
+	close(data.fd);
+
+	return 0;
+}
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*tracefs_cpu_open*(3)
+*tracefs_cpu_close*(3)
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2022 Google, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-dynevents.txt b/Documentation/libtracefs-dynevents.txt
new file mode 100644
index 0000000..2c6db47
--- /dev/null
+++ b/Documentation/libtracefs-dynevents.txt
@@ -0,0 +1,283 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_dynevent_create, tracefs_dynevent_destroy, tracefs_dynevent_destroy_all,
+tracefs_dynevent_free, tracefs_dynevent_list_free, tracefs_dynevent_get, tracefs_dynevent_get_all,
+tracefs_dynevent_info, tracefs_dynevent_get_event - Create, destroy, free and get dynamic events.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct *tracefs_dynevent*;
+enum *tracefs_dynevent_type*;
+int *tracefs_dynevent_create*(struct tracefs_dynevent pass:[*]_devent_);
+int *tracefs_dynevent_destroy*(struct tracefs_dynevent pass:[*]_devent_, bool _force_);
+int *tracefs_dynevent_destroy_all*(unsigned int _types_, bool _force_);
+void *tracefs_dynevent_free*(struct tracefs_dynevent pass:[*]_devent_);
+void *tracefs_dynevent_list_free*(struct tracefs_dynevent pass:[*]pass:[*]_events_);
+struct tracefs_dynevent pass:[*]*tracefs_dynevent_get*(enum tracefs_dynevent_type _type_, const char pass:[*]_system_, const char pass:[*]_event_);
+struct tracefs_dynevent pass:[*]pass:[*]*tracefs_dynevent_get_all*(unsigned int _types_, const char pass:[*]_system_);
+enum tracefs_dynevent_type *tracefs_dynevent_info*(struct tracefs_dynevent pass:[*]_dynevent_, char pass:[*]pass:[*]_system_, char pass:[*]pass:[*]_event_, char pass:[*]pass:[*]_prefix_, char pass:[*]pass:[*]_addr_, char pass:[*]pass:[*]_format_);
+struct tep_event pass:[*]*tracefs_dynevent_get_event*(struct tep_handle pass:[*]_tep_, struct tracefs_dynevent pass:[*]_dynevent_);
+--
+
+DESCRIPTION
+-----------
+
+The *tracefs_dynevent_create*() function creates dynamic event _devent_ in the system.
+
+The *tracefs_dynevent_destroy*() function removes dynamic event _devent_ from the system. If _force_
+is true, the function will attempt to disable all events in all trace instances, before removing
+the dynamic event. The _devent_ context is not freed, use *tracefs_dynevent_free*() to free it.
+
+The *tracefs_dynevent_destroy_all*() function removes all dynamic events of given types from the
+system. The _types_ parameter is a type of specific dynamic event, or a bitmask of dynamic events
+types *tracefs_dynevent_type*, that will be removed. If _types_ is 0, dynamic events from all types
+will be removed.  If _force_ is true, the function will attempt to disable all events in all trace
+instances, before removing the dynamic events.
+
+The *tracefs_dynevent_get*() function allocates and returns a single instance of a dynamic
+event that matches the given *type*, *system* and *event* that is passed to it. NULL is returned
+if there is no match. The returned event is what is found in the system, and must be freed
+with *tracefs_dynevent_free*(). If *system* is NULL, then the first *event* of any system
+of the given type that has the name of *event* will be returned.
+
+The *tracefs_dynevent_get_all*() function allocates and returns an array of pointers to dynamic
+events of given types that exist in the system. The last element of the array is a NULL pointer.
+The array must be freed with *tracefs_dynevent_list_free*(). If there are no events a NULL pointer is
+returned. The _types_ parameter is a type of specific dynamic event, or a bitmask of dynamic events
+types *tracefs_dynevent_type*, that will be retrieved. If _types_ is 0, dynamic events from all
+types will be retrieved.
+
+The *tracefs_dynevent_free*() function frees a dynamic event context _devent_.
+
+The *tracefs_dynevent_list_free*() function frees an array of pointers to dynamic event, returned
+by *tracefs_dynevent_get_all()* API.
+
+The *tracefs_dynevent_info*() function returns the type and information of a given dynamic event
+_dynevent_. If any of the _system_, _event_, _prefix_, _addr_ or _format_ arguments are not NULL,
+then strings are allocated and returned back via these arguments. The _system_ and _event_ holds the
+system and the name of the dynamic event. If _prefix_ is non NULL, then it will hold an allocated
+string that holds the prefix portion of the dynamic event (the content up to the ":", exluding it).
+If _addr_ is non NULL, it will hold the address or function that the dynamic event is attached to,
+if relevant for this event type. If _format_ is non NULL, it will hold the format string of the
+dynamic event. Note, that the content in _group_, _event_, _prefix_, _addr_, and _format_ must be
+freed with free(3) if they are set.
+
+The *tracefs_dynevent_get_event*() function returns a tep event, describing the given dynamic event.
+The API detects any newly created or removed dynamic events. The returned pointer to tep event is
+controlled by @tep and must not be freed.
+
+RETURN VALUE
+------------
+
+*tracefs_dynevent_create*() returns 0 on success, or -1 on error. If a parsing error occurs then
+*tracefs_error_last*(3) may be used to retrieve the error message explaining the parsing issue.
+
+*tracefs_dynevent_destroy*() and *tracefs_dynevent_destroy_all*() return 0 on success, or -1 on
+error. If _force_ is enabled, the functions may fail on disabling the events.
+
+*tracefs_dynevent_get*() function returns an allocated dynamic event from the system that matches
+the type, system and event given.
+
+*tracefs_dynevent_get_all*() function returns allocated array of pointers to dynamic events, or NULL
+in case of an error or in case there are no events in the system. That array must be freed by
+*tracefs_dynevent_list_free*().
+
+*tracefs_dynevent_info*() returns the type of the given dynamic event  or TRACEFS_DYNEVENT_UNKNOWN
+on error. If _system_, _event_, _prefix_, _addr_, or _format_ are non NULL, they will contain
+allocated strings that must be freed by free(3).
+
+The *tracefs_dynevent_get_event*() function returns a pointer to a tep event or NULL in case of an
+error or if the requested dynamic event is missing. The returned pointer to tep event is controlled
+by @tep and must not be freed.
+
+ERRORS
+------
+The following errors are for all the above calls:
+
+*ENODEV* dynamic events of requested type are not configured for the running kernel.
+
+*ENOMEM* Memory allocation error.
+
+*tracefs_dynevent_create*() can fail with the following errors:
+
+*EINVAL*  Most likely a parsing error occurred (use *tracefs_error_last*(3) to possibly
+          see what that error was).
+
+Other errors may also happen caused by internal system calls.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <tracefs.h>
+
+static struct tep_event *open_event;
+static struct tep_format_field *file_field;
+
+static struct tep_event *openret_event;
+static struct tep_format_field *ret_field;
+
+static int callback(struct tep_event *event, struct tep_record *record,
+		    int cpu, void *data)
+{
+	struct trace_seq seq;
+
+	trace_seq_init(&seq);
+	tep_print_event(event->tep, &seq, record, "%d-%s: ", TEP_PRINT_PID, TEP_PRINT_COMM);
+
+	if (event->id == open_event->id) {
+		trace_seq_puts(&seq, "open file='");
+		tep_print_field(&seq, record->data, file_field);
+		trace_seq_puts(&seq, "'\n");
+	} else if (event->id == openret_event->id) {
+		unsigned long long ret;
+		tep_read_number_field(ret_field, record->data, &ret);
+		trace_seq_printf(&seq, "open ret=%lld\n", ret);
+	} else {
+		goto out;
+	}
+
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+out:
+	trace_seq_destroy(&seq);
+
+	return 0;
+}
+
+static pid_t run_exec(char **argv, char **env)
+{
+	pid_t pid;
+
+	pid = fork();
+	if (pid)
+		return pid;
+
+	execve(argv[0], argv, env);
+	perror("exec");
+	exit(-1);
+}
+
+const char *mykprobe = "my_kprobes";
+
+int main (int argc, char **argv, char **env)
+{
+	struct tracefs_dynevent *kprobe, *kretprobe;
+	const char *sysnames[] = { mykprobe, NULL };
+	struct tracefs_instance *instance;
+	struct tep_handle *tep;
+	pid_t pid;
+
+	if (argc < 2) {
+		printf("usage: %s command\n", argv[0]);
+		exit(-1);
+	}
+
+	instance = tracefs_instance_create("exec_open");
+	if (!instance) {
+		perror("creating instance");
+		exit(-1);
+	}
+
+	tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, true);
+
+	kprobe = tracefs_kprobe_alloc(mykprobe, "open", "do_sys_openat2",
+				      "file=+0($arg2):ustring flags=+0($arg3):x64 mode=+8($arg3):x64\n");
+	kretprobe = tracefs_kretprobe_alloc(mykprobe, "openret", "do_sys_openat2", "ret=%ax", 0);
+	if (!kprobe || !kretprobe) {
+		perror("allocating dynamic events");
+		exit(-1);
+	}
+
+	if (tracefs_dynevent_create(kprobe) || tracefs_dynevent_create(kretprobe)){
+		char *err = tracefs_error_last(NULL);
+		perror("Failed to create kprobes:");
+		if (err && strlen(err))
+			fprintf(stderr, "%s\n", err);
+		exit(-1);
+	}
+
+	tep = tracefs_local_events_system(NULL, sysnames);
+	if (!tep) {
+		perror("reading events");
+		exit(-1);
+	}
+	open_event = tep_find_event_by_name(tep, mykprobe, "open");
+	file_field = tep_find_field(open_event, "file");
+
+	openret_event = tep_find_event_by_name(tep, mykprobe, "openret");
+	ret_field = tep_find_field(openret_event, "ret");
+
+	tracefs_event_enable(instance, mykprobe, NULL);
+	pid = run_exec(&argv[1], env);
+
+	/* Let the child start to run */
+	sched_yield();
+
+	do {
+		tracefs_load_cmdlines(NULL, tep);
+		tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, NULL);
+	} while (waitpid(pid, NULL, WNOHANG) != pid);
+
+	/* Will disable the events */
+	tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, true);
+	tracefs_dynevent_free(kprobe);
+	tracefs_dynevent_free(kretprobe);
+	tracefs_instance_destroy(instance);
+	tep_free(tep);
+
+	return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*Yordan Karadzhov* <y.karadz@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2021 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-eprobes.txt b/Documentation/libtracefs-eprobes.txt
new file mode 100644
index 0000000..a96ad1b
--- /dev/null
+++ b/Documentation/libtracefs-eprobes.txt
@@ -0,0 +1,187 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_eprobe_alloc - Allocate new event probe (eprobe)
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_dynevent pass:[*]
+*tracefs_eprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+		       const char pass:[*]_target_system_, const char pass:[*]_target_event_,
+		       const char pass:[*]_fetchargs_);
+--
+
+DESCRIPTION
+-----------
+*tracefs_eprobe_alloc*() allocates a new eprobe context. The ebrobe is not configured in the system.
+The new eprobe will be in the _system_ group (or eprobes if _system_ is NULL) and have the name of
+_event_. The eprobe will be attached to _target_event_, located in _target_system_. The list of
+arguments, described in _fetchargs_, will be fetched from _target_event_. The returned pointer to
+the event probe must be freed with *tracefs_dynevent_free*().
+
+
+RETURN VALUE
+------------
+The *tracefs_eprobe_alloc*() API returns a pointer to an allocated tracefs_dynevent structure,
+describing the event probe. This pointer must be freed by *tracefs_dynevent_free*(3). Note, this
+only allocates a descriptor representing the eprobe. It does not modify the running system. On error
+NULL is returned.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <tracefs.h>
+
+static struct tep_event *open_event;
+static struct tep_format_field *file_field;
+
+static int callback(struct tep_event *event, struct tep_record *record,
+		    int cpu, void *data)
+{
+	struct trace_seq seq;
+
+	trace_seq_init(&seq);
+	tep_print_event(event->tep, &seq, record, "%d-%s: ", TEP_PRINT_PID, TEP_PRINT_COMM);
+
+	if (event->id == open_event->id) {
+		trace_seq_puts(&seq, "open file='");
+		tep_print_field(&seq, record->data, file_field);
+		trace_seq_puts(&seq, "'\n");
+	}
+
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+	trace_seq_destroy(&seq);
+
+	return 0;
+}
+
+static pid_t run_exec(char **argv, char **env)
+{
+	pid_t pid;
+
+	pid = fork();
+	if (pid)
+		return pid;
+
+	execve(argv[0], argv, env);
+	perror("exec");
+	exit(-1);
+}
+
+const char *myprobe = "my_eprobes";
+
+int main (int argc, char **argv, char **env)
+{
+	struct tracefs_dynevent *eprobe;
+	struct tracefs_instance *instance;
+	struct tep_handle *tep;
+	const char *sysnames[] = { myprobe, NULL };
+	pid_t pid;
+
+	if (argc < 2) {
+		printf("usage: %s command\n", argv[0]);
+		exit(-1);
+	}
+
+	instance = tracefs_instance_create("exec_open");
+	if (!instance) {
+		perror("creating instance");
+		exit(-1);
+	}
+
+	tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_EPROBE, true);
+
+	eprobe = tracefs_eprobe_alloc(myprobe, "sopen", "syscalls", "sys_enter_openat2",
+				      "file=+0($filename):ustring");
+	if (!eprobe) {
+		perror("allocating event probe");
+		exit(-1);
+	}
+
+	if (tracefs_dynevent_create(eprobe)) {
+		perror("creating event probe");
+		exit(-1);
+	}
+
+	tep = tracefs_local_events_system(NULL, sysnames);
+	if (!tep) {
+		perror("reading events");
+		exit(-1);
+	}
+
+	open_event = tep_find_event_by_name(tep, myprobe, "sopen");
+	file_field = tep_find_field(open_event, "file");
+
+	tracefs_event_enable(instance, myprobe, "sopen");
+	pid = run_exec(&argv[1], env);
+
+	/* Let the child start to run */
+	sched_yield();
+
+	do {
+		tracefs_load_cmdlines(NULL, tep);
+		tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, NULL);
+	} while (waitpid(pid, NULL, WNOHANG) != pid);
+
+	/* Will disable the events */
+	tracefs_dynevent_destroy(eprobe, true);
+	tracefs_dynevent_free(eprobe);
+	tracefs_instance_destroy(instance);
+	tep_free(tep);
+
+	return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2021 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-error.txt b/Documentation/libtracefs-error.txt
new file mode 100644
index 0000000..a45332d
--- /dev/null
+++ b/Documentation/libtracefs-error.txt
@@ -0,0 +1,137 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_error_last, tracefs_error_all, tracefs_error_clear -
+functions to read and clear the tracefs error log.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+char pass:[*]*tracefs_error_last*(struct tracefs_instance pass:[*]_instance_);
+char pass:[*]*tracefs_error_all*(struct tracefs_instance pass:[*]_instance_);
+int *tracefs_error_clear*(struct tracefs_instance pass:[*]_instance_);
+--
+
+DESCRIPTION
+-----------
+The *tracefs_error_last*() returns the last error message in the tracefs
+error log. Several actions that require proper syntax written into the
+tracefs file system may produce error messages in the error log. This
+function will show the most recent error in the error log.
+
+The *tracefs_error_all*() returns all messages saved in the error log.
+Note, this may not be all messages that were ever produced, as the kernel
+only keeps a limited amount of messages, and older ones may be discarded
+by the kernel.
+
+The *tracefs_error_clear*() will clear the error log.
+
+RETURN VALUE
+------------
+Both *tracefs_error_last*() and *tracefs_error_all*() will return an allocated
+string an error exists in the log, otherwise NULL is returned. If an error
+occurs, errno will be set, otherwise if there is no error messages to display
+then errno is not touched.
+
+*tracefs_error_clear*() returns 0 on success or -1 on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <tracefs.h>
+
+int main (int argc, char **argv, char **env)
+{
+	struct tracefs_dynevent *kevent;
+	char *system = NULL;
+	char *kprobe;
+	char *format;
+	char *addr;
+	int arg = 1;
+	int ret;
+
+	if (argc < 4) {
+		printf("usage: %s [system] kprobe addr fmt\n", argv[0]);
+		exit(-1);
+	}
+
+	if (argc > 5)
+		system = argv[arg++];
+
+	kprobe = argv[arg++];
+	addr = argv[arg++];
+	format = argv[arg++];
+
+	tracefs_error_clear(NULL);
+	kevent = tracefs_dynevent_get(TRACEFS_DYNEVENT_KPROBE, system, kprobe);
+	if (kevent) {
+		tracefs_dynevent_destroy(kevent, true);
+		tracefs_dynevent_free(kevent);
+	}
+
+	ret = tracefs_kprobe_raw(system, kprobe, addr, format);
+	if (ret < 0) {
+		char *err;
+
+		perror("Failed creating kprobe");
+		errno = 0;
+		err = tracefs_error_last(NULL);
+		if (err)
+			fprintf(stderr, "%s\n", err);
+		else if (errno)
+			perror("Failed reading error log");
+		free(err);
+	}
+
+	exit(ret);
+}
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-events-file.txt b/Documentation/libtracefs-events-file.txt
new file mode 100644
index 0000000..425eebd
--- /dev/null
+++ b/Documentation/libtracefs-events-file.txt
@@ -0,0 +1,217 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_event_get_file, tracefs_event_file_read, tracefs_event_file_write, tracefs_event_file_append,
+tracefs_event_file_clear, tracefs_event_file_exists - Work with trace event files.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+char pass:[*]*tracefs_event_get_file*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+			     const char pass:[*]_file_);
+char pass:[*]*tracefs_event_file_read*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+			      const char pass:[*]_file_, int pass:[*]_psize_);
+int *tracefs_event_file_write*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+			     const char pass:[*]_file_, const char pass:[*]_str_);
+int *tracefs_event_file_append*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+			      const char pass:[*]_file_, const char pass:[*]_str_);
+int *tracefs_event_file_clear*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+			     const char pass:[*]_file_);
+bool *tracefs_event_file_exists*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+			       const char pass:[*]_file_)
+
+--
+
+DESCRIPTION
+-----------
+These are functions for accessing tracefs event specific files.
+These functions act similar to the tracefs instance file functions
+but are easier to get to if the system and events are known before hand.
+
+The *tracefs_event_get_file()* returns the full path of the _file_ for
+the given _system_ and _event_ that is within the given _instance_.
+If _instance_ is NULL, then the file for the _event_ for the top level
+instance is returned. Note, there is no check to see if the file actually
+exists or even if the system and event exist. It only creates the path
+name for such an event if it did exist. This acts similar to the
+*tracefs_instance_get_file*(3), but is to be used to get to event files
+if the _system_ and _event_ are already known.
+
+The *tracefs_event_file_read()* reads the content for the _event_ _file_
+for the given _instance_ or the top level instance if _instance_ is
+NULL. The content of the file is returned and _psize_ is set to the amount
+of data that was read. The returned content must be freed with *free*(3).
+This acts similar to the *tracefs_instance_file_read*(3), but is
+to be used to read the event file if the _system_ and _event_ are already
+known.
+
+The *tracefs_event_file_write()* writes _str_ to the _event_ _file_.
+It will truncate anything that is already in that file.
+This acts similar to the *tracefs_instance_file_write*(3), but is
+to be used to read the event file if the _system_ and _event_ are already
+known.
+
+The *tracefs_event_file_append()* appends _str_ to the _event_ _file_.
+It will not clear out the file as it writes _sting_.
+This acts similar to the *tracefs_instance_file_append*(3), but is
+to be used to read the event file if the _system_ and _event_ are already
+known.
+
+The *tracefs_event_file_clear()* clears the content of the _event_ _file_.
+This acts similar to the *tracefs_instance_file_clear*(3), but is
+to be used to read the event file if the _system_ and _event_ are already
+known.
+
+The *tracefs_event_file_exists()* returns true if the _event_ _file_
+exists, and false otherwise. This acts similar to the *tracefs_instance_file_exists*(3),
+but is to be used to read the event file if the _system_ and _event_ are already
+known.
+
+RETURN VALUE
+------------
+*tracefs_event_get_file()* returns the path of the given _system_/_event_ _file_ on
+success and NULL on error. The return value must be freed with *tracefs_put_tracing_file*(3).
+
+*tracefs_event_file_read()* reads the content of the _system_/_event_ _file_ or
+NULL on error. The return pointer must be freed with *free*(3).
+
+*tracefs_event_file_write()* and *tracefs_event_file_append()* returns the number of
+bytes written to the _system_/_event_ _file_ or negative on error.
+
+*tracefs_event_file_clear()* returns zero on success and -1 on error.
+
+*tracefs_event_file_exists()* returns true if the _system_/_event_ _file_ exists for
+the given _instance_ (or top level if _instance_ is NULL) or false otherwise.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <tracefs.h>
+
+int main(int argc, char **argv)
+{
+	char *system;
+	char *event;
+	char *file;
+	char *cmd = NULL;
+	char *buf;
+	char *str;
+	char ch = 'r';
+	int size;
+
+	if (argc < 4) {
+		printf("usage: %s sytem event file [(-a|-w) write | -c]\n"
+		       "   reads the system/event file or writes if [write is supplied]\n",
+		       argv[0]);
+		exit(0);
+	}
+
+	system = argv[1];
+	event = argv[2];
+	file = argv[3];
+	if (argc > 4)
+		cmd = argv[4];
+
+	if (!tracefs_event_file_exists(NULL, system, event, file)) {
+		fprintf(stderr, "File %s/%s/%s does not exist\n",
+				system, event, file);
+		exit(-1);
+	}
+
+	if (cmd) {
+		if (cmd[0] != '-')
+			ch = cmd[0];
+		else
+			ch = cmd[1];
+		if (!ch)
+			ch = 'c';
+	}
+
+	switch (ch) {
+	case 'r':
+		buf = tracefs_event_file_read(NULL, system, event, file, &size);
+		if (buf)
+			printf("%s", buf);
+		else
+			fprintf(stderr, "Failed to read %s/%s/%s\n",
+				system, event, file);
+		free(buf);
+		break;
+	case 'w':
+	case 'a':
+		if (argc < 6) {
+			fprintf(stderr, "%s command requires something to write\n",
+				ch == 'w' ? "write" : "append");
+			exit(-1);
+		}
+		if (ch == 'w')
+			size = tracefs_event_file_write(NULL, system, event, file, argv[5]);
+		else
+			size = tracefs_event_file_append(NULL, system, event, file, argv[5]);
+		if (size < 0) {
+			fprintf(stderr, "Failed to write '%s' to %s/%s/%s\n",
+					argv[5], system, event, file);
+			exit(-1);
+		}
+		break;
+	case 'c':
+		if (tracefs_event_file_clear(NULL, system, event, file) < 0) {
+			fprintf(stderr, "Failed to clear %s/%s/%s\n",
+				system, event, file);
+			exit(-1);
+		}
+		break;
+	default:
+		fprintf(stderr, "Unknown command '%c'\n", ch);
+		exit(-1);
+	}
+	exit(0);
+}
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2022 Google, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-events-tep.txt b/Documentation/libtracefs-events-tep.txt
new file mode 100644
index 0000000..22d3dd5
--- /dev/null
+++ b/Documentation/libtracefs-events-tep.txt
@@ -0,0 +1,148 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_local_events, tracefs_local_events_system, tracefs_fill_local_events,
+tracefs_load_cmdlines -
+Initialize a tep handler with trace events from the local system.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tep_handle pass:[*]*tracefs_local_events*(const char pass:[*]_tracing_dir_);
+struct tep_handle pass:[*]*tracefs_local_events_system*(const char pass:[*]_tracing_dir_, const char pass:[*] const pass:[*]_sys_names_);
+int *tracefs_fill_local_events*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_, int pass:[*]_parsing_failures_);
+int *tracefs_load_cmdlines*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_);
+--
+
+DESCRIPTION
+-----------
+Functions for initializing a tep handler with trace events from the local system.
+
+The *tracefs_local_events()* function allocates a new _tep_ handler and
+initializes it with events from all trace systems, located in the given
+_tracing_dir_ directory. This could be NULL or the location of the tracefs
+mount point for the trace systems of the local machine, or it may be a path
+to a copy of the tracefs directory from another machine.
+
+The *tracefs_local_events_system()* function allocates a new _tep_ handler
+and initializes it with events from specified trace systems _sys_names_,
+located in the given _tracing_dir_ directory. This could be NULL or the
+location of the tracefs mount point for the trace systems of the local
+machine, or it may be a path to a copy of the tracefs directory from another
+machine. The _sys_names_ argument is an array of trace system names, that
+will be used for _tep_ handler initialization. The last element in that
+array must be a NULL pointer.
+
+The *tracefs_fill_local_events()* function initializes already allocated _tep_
+handler with events from all trace systems, located in the given _tracing_dir_
+directory. This could be NULL or the location of the tracefs mount point
+for the trace systems of the local machine, or it may be a path to a copy
+of the tracefs directory from another machine. The _tep_ argument must be
+a pointer to already allocated tep handler, that is going to be initialized.
+The _parsing_failures_ argument could be NULL or a pointer to an integer,
+where the number of failures while parsing the event files are returned.
+
+The above functions will also load the mappings between pids and the process
+command line names. In some cases the _tep_ handle is created with one
+of the above before tracing begins. As the mappings get updated during the
+trace, there may be a need to read the mappings again after the trace.
+The *tracefs_load_cmdlines()* does just that. The _tracing_dir_ is
+the directory of the mount point to load from, or NULL to use the
+mount point of the tracefs file system.
+
+RETURN VALUE
+------------
+The *tracefs_local_events()* and *tracefs_local_events_system()* functions
+return pointer to allocated and initialized _tep_ handler, or NULL in
+case of an error. The returned _tep_ handler must be freed with *tep_free*(3).
+
+The *tracefs_fill_local_events()* function returns -1 in case of an error or
+0 otherwise.
+
+The *tracefs_load_cmdlines()* function returns -1 in case of an error, or
+0 otherwise.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+
+struct tep_handle *tep;
+...
+	tep = tracefs_local_events(NULL);
+	if (!tep) {
+		/* Failed to initialise tep handler with local events from top instance */
+		...
+	}
+...
+	tep_free(tep);
+...
+	const char *systems[] = {"ftrace", "irq", NULL};
+	tep = tracefs_local_events_system(NULL, systems);
+	if (!tep) {
+		/* Failed to initialise tep handler with local events from
+		 * ftrace and irq systems in top instance.
+		 */
+		...
+	}
+...
+	tep_free(tep);
+...
+	int parsing_failures;
+	tep = tep_alloc();
+	if (!tep) {
+		/* Failed to allocate a tep handler */
+		...
+	}
+	if (tracefs_fill_local_events(NULL, tep, &parsing_failures) < 0) {
+		/* Failed to initialise tep handler with local events from top instance */
+	}
+	tracefs_load_cmdlines(NULL, tep);
+...
+	tep_free(tep);
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-events.txt b/Documentation/libtracefs-events.txt
new file mode 100644
index 0000000..90c54b8
--- /dev/null
+++ b/Documentation/libtracefs-events.txt
@@ -0,0 +1,195 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_event_systems, tracefs_system_events, tracefs_event_enable, tracefs_event_disable,
+tracefs_event_is_enabled - Work with trace systems and events.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+enum tracefs_event_state {
+	TRACEFS_ERROR = -1,
+	TRACEFS_ALL_DISABLED = 0,
+	TRACEFS_ALL_ENABLED = 1,
+	TRACEFS_SOME_ENABLED = 2,
+};
+
+char pass:[*]pass:[*]*tracefs_event_systems*(const char pass:[*]_tracing_dir_);
+char pass:[*]pass:[*]*tracefs_system_events*(const char pass:[*]_tracing_dir_, const char pass:[*]_system_);
+int *tracefs_event_enable*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_,
+			   const char pass:[*]_event_);
+int *tracefs_event_disable*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_,
+			    const char pass:[*]_event_);
+enum tracefs_enable_state *tracefs_event_is_enabled*(struct tracefs_instance pass:[*]_instance_,
+			 const char pass:[*]_system_, const char pass:[*]_event_);
+--
+
+DESCRIPTION
+-----------
+Trace systems and events related APIs.
+
+The *tracefs_event_systems()* function returns array of strings with the
+names of all registered trace systems, located in the given _tracing_dir_
+directory. This could be NULL or the location of the tracefs mount point
+for the trace systems of the local machine, or it may be a path to a copy
+of the tracefs directory from another machine. The last entry in the array
+is a NULL pointer. The array must be freed with *tracefs_list_free()* API.
+
+The *tracefs_system_events()* function returns array of strings with the
+names of all registered trace events for given trace system specified by
+_system_, located in the given _tracing_dir_ directory. This could be NULL
+or the location of the tracefs mount point for the trace systems of the
+local machine, or it may be a path to a copy of the tracefs directory
+from another machine. The last entry in the array as a NULL pointer.
+The array must be freed with *tracefs_list_free()* API.
+
+The *tracefs_event_enable()* function enables a given event based on
+the _system_ and _event_ passed in for the given _instance_. If _instance_
+is NULL, then the top level tracing directory is used. If _system_
+and _event_ are both NULL, then all events are enabled for the  _instance_.
+If _event_ is NULL then all events within the _system_ are enabled.
+If _system_ is NULL, then all systems are searched and any event within
+a system that matches _event_ is enabled. Both _system_ and _event_ may
+be regular expressions as defined by *regex*(3).
+
+The *tracefs_event_disable()* function disables the events that match
+the _system_ and _event_ parameters for the given _instance_. What events
+are disable follow the same rules as *tracefs_event_enable()* for matching
+events. That is, if _instance_ is NULL, then the top level tracing directory
+is used. If both _system_ and _event_ are NULL then all events are disabled
+for the given _instance_, and so on.
+
+The *tracefs_event_is_enabled()* returns if an event is enabled, a set of
+events are enabled, a system is enabled, or all events are enabled. If both
+_system_ and _event_ are NULL, then it returns the enable state of all events.
+If _system_ is not NULL and _event_ is NULL, then it will check if all the events
+in all the systems that _system_ and return the enable state of those events.
+If _system_ is NULL and _event_ is not NULL, then it will match all the events
+in all systems that match _event_ and return their enabled state. If both _system_
+and _event_ are not NULL, then it will return the enabled state of all matching
+events. The enabled state is defined as:
+
+*TRACEFS_ERROR* - An error occurred including no event were matched.
+
+*TRACEFS_ALL_DISABLED* - All matching events are disabled.
+
+*TRACEFS_ALL_ENABLED* - All matching events are enabled.
+
+*TRACEFS_SOME_ENABLED* - Some matching events were enabled while others were not.
+
+RETURN VALUE
+------------
+The *tracefs_event_systems()* and *tracefs_system_events()* functions return
+an array of strings. The last element in that array is a NULL pointer. The array
+must be freed with *tracefs_list_free()* API. In case of an error, NULL is returned.
+
+Both *tracefs_event_enable()* and *tracefs_event_disable()* return 0 if they found
+any matching events (Note it does not check the previous status of the event. If
+*tracefs_event_enable()* finds an event that is already enabled, and there are no
+other errors, then it will return 0). If an error occurs, even if other events were
+found, it will return -1 and errno will be set. If no errors occur, but no events
+are found that match the _system_ and _event_ parameters, then -1 is returned
+and errno is not set.
+
+The *tracefs_event_is_enabled()* returns the enabled status of the matching events
+or TRACEFS_ERROR on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+
+char **systems = tracefs_event_systems(NULL);
+
+	if (systems) {
+		int i = 0;
+		/* Got registered trace systems from the top trace instance */
+		while (systems[i]) {
+			char **events = tracefs_system_events(NULL, systems[i]);
+			if (events) {
+				/* Got registered events in system[i] from the top trace instance */
+				int j = 0;
+
+				while (events[j]) {
+					/* Got event[j] in system[i] from the top trace instance */
+					j++;
+				}
+				tracefs_list_free(events);
+			}
+			i++;
+		}
+		tracefs_list_free(systems);
+	}
+....
+static int records_walk(struct tep_event *tep, struct tep_record *record, int cpu, void *context)
+{
+	/* Got recorded event on cpu */
+	return 0;
+}
+...
+struct tep_handle *tep = tracefs_local_events(NULL);
+
+	if (!tep) {
+		/* Failed to initialise tep handler with local events */
+		...
+	}
+
+	errno = 0;
+	ret = tracefs_event_enable(NULL, "sched", NULL);
+	if (ret < 0 && !errno)
+		printf("Could not find 'sched' events\n");
+	tracefs_event_enable(NULL, "irq", "irq_handler_\(enter\|exit\)");
+
+	if (tracefs_iterate_raw_events(tep, NULL, NULL, 0, records_walk, NULL) < 0) {
+		/* Error walking through the recorded raw events */
+	}
+
+	/* Disable all events */
+	tracefs_event_disable(NULL, NULL, NULL);
+	tep_free(tep);
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-files.txt b/Documentation/libtracefs-files.txt
new file mode 100644
index 0000000..d22e759
--- /dev/null
+++ b/Documentation/libtracefs-files.txt
@@ -0,0 +1,131 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_get_tracing_file, tracefs_put_tracing_file, tracefs_tracing_dir, tracefs_debug_dir, tracefs_set_tracing_dir,
+tracefs_tracing_dir_is_mounted - Find and set locations of trace directory and files.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+char pass:[*]*tracefs_get_tracing_file*(const char pass:[*]_name_);
+void *tracefs_put_tracing_file*(char pass:[*]_name_);
+const char pass:[*]*tracefs_tracing_dir*(void);
+const char pass:[*]*tracefs_debug_dir*(void);
+int *tracefs_set_tracing_dir*(char pass:[*]_tracing_dir_)
+int *tracefs_tracing_dir_is_mounted*(bool _mount_, const char pass:[**]_path_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to find the full path of the trace file
+system mount point and trace files in it.
+
+The *tracefs_set_tracing_dir()* function sets a custom location of the
+system's tracing directory mount point. Usually, the library auto detects
+it using the information from the /proc/mounts file. Use this API only if the
+mount point is not standard and cannot be detected by the library. The _tracing_dir_
+argument can be NULL, in that case the custom location is deleted and the library
+auto detection logic is used.
+
+The *tracefs_get_tracing_file()* function returns the full path of the
+file with given _name_ in the trace file system. The function works
+only with files in the tracefs main directory, it is not trace instance
+aware. It is recommended to use *tracefs_instance_get_file()* and
+*tracefs_instance_get_dir()* instead. The returned string must be freed
+with *tracefs_put_tracing_file()*.
+
+The *tracefs_put_tracing_file()* function frees trace file name,
+returned by *tracefs_get_tracing_file()*.
+
+The *tracefs_tracing_dir()* function returns the full path to the
+trace file system. In the first function call, the mount point of the
+tracing file system is located, cached and returned. It will mount it,
+if it is not mounted. On any subsequent call the cached path is returned.
+The return string must _not_ be freed.
+
+The *tracefs_debug_dir()* is similar to *tracefs_tracing_dir()* except
+that it will return where the debugfs file system is mounted. If it
+is not mounted it will try to mount it. The return string must _not_
+be freed.
+
+*tracefs_tracing_dir_is_mounted()* returns 1 if the tracing directory is
+already mounted and 0 if it is not. If _mount_ is true, it will try to
+mount it if it is not, and returns 0 if it succesfully mounted it and -1
+if it did not. If _path_ is set, it will be assigned to the path where it
+mounted it. _path_ is internal and should not be freed.
+
+RETURN VALUE
+------------
+The *tracefs_set_tracing_dir()* function returns 0 on success, -1 otherwise.
+
+The *tracefs_get_tracing_file()* function returns a string or NULL in case
+of an error. The returned string must be freed with *tracefs_put_tracing_file()*.
+
+The *tracefs_tracing_dir()* function returns a constant string or NULL
+in case of an error. The returned string must _not_ be freed.
+
+The *tracefs_debug_dir()* function returns a constant string or NULL
+in case of an error. The returned string must _not_ be freed.
+
+The *tracefs_tracing_dir_is_mounted()* returns 1 if the tracing directory
+is already mounted, 0 if it is not, and -1 on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+...
+char *trace_on = tracefs_get_tracing_file("tracing_on");
+	if (trace_on) {
+		...
+		tracefs_put_tracing_file(trace_on);
+	}
+...
+const char *trace_dir = tracefs_tracing_dir();
+
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-filter.txt b/Documentation/libtracefs-filter.txt
new file mode 100644
index 0000000..12726b9
--- /dev/null
+++ b/Documentation/libtracefs-filter.txt
@@ -0,0 +1,345 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_filter_string_append, tracefs_filter_string_verify, tracefs_event_filter_apply, tracefs_event_filter_clear -
+Add, verify and apply event filters
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_filter_string_append*(struct tep_event pass:[*]_event_, char pass:[**]_filter_,
+				 struct tracefs_filter _type_, const char pass:[*]_field_,
+				 enum tracefs_synth_compare _compare_, const char pass:[*]_val_);
+int *tracefs_filter_string_verify*(struct tep_event pass:[*]_event_, const char pass:[*]_filter_, char pass:[**]_err_);
+int *tracefs_event_filter_apply*(struct tracefs_instance pass:[*]_instance_, struct tep_event pass:[*]_event_, const char pass:[*]_filter_);
+int *tracefs_event_filter_clear*(struct tracefs_instance pass:[*]_instance_, struct tep_event pass:[*]_event_);
+
+--
+
+DESCRIPTION
+-----------
+*tracefs_filter_string_append*() is a way to create and verify event filters for
+a given event. It will verify that the _field_ belongs to the event and that
+the _compare_ option that is used is valid for the type of the field, as well
+as _val_. For the _type_ that is not of *TRACEFS_FILTER_COMPARE*, it will build
+the logical string and also make sure that the syntax is correct. For example,
+there are no more close parenthesis than open parenthesis. An AND (&&) or OR
+(||) is not misplaced, etc.
+
+*tracefs_synth_append_start_filter*() creates a filter or appends to it for the
+starting event. Depending on _type_, it will build a string of tokens for
+parenthesis or logic statemens, or it may add a comparison of _field_
+to _val_ based on _compare_.
+
+If _type_ is:
+*TRACEFS_FILTER_COMPARE*     -  See below
+*TRACEFS_FILTER_AND*         -  Append "&&" to the filter
+*TRACEFS_FILTER_OR*          -  Append "||" to the filter
+*TRACEFS_FILTER_NOT*         -  Append "!" to the filter
+*TRACEFS_FILTER_OPEN_PAREN*  -  Append "(" to the filter
+*TRACEFS_FILTER_CLOSE_PAREN* -  Append ")" to the filter
+
+_field_, _compare_, and _val_ are ignored unless _type_ is equal to
+*TRACEFS_FILTER_COMPARE*, then _compare will be used for the following:
+
+*TRACEFS_COMPARE_EQ* - _field_ == _val_
+
+*TRACEFS_COMPARE_NE* - _field_ != _val_
+
+*TRACEFS_COMPARE_GT* - _field_ > _val_
+
+*TRACEFS_COMPARE_GE* - _field_ >= _val_
+
+*TRACEFS_COMPARE_LT* - _field_ < _val_
+
+*TRACEFS_COMPARE_LE* - _field_ <pass:[=] _val_
+
+*TRACEFS_COMPARE_RE* - _field_ ~ "_val_" : where _field_ is a string.
+
+*TRACEFS_COMPARE_AND* - _field_ & _val_ : where _field_ is a flags field.
+
+*tracefs_filter_string_verify*() will parse _filter_ to make sure that the
+fields are for the _event_, and that the syntax is correct. If there's an
+error in the syntax, and _err_ is not NULL, then it will be allocated with an
+error message stating what was found wrong with the filter. _err_ must be freed
+with *free*().
+
+*tracefs_event_filter_apply*() applies given _filter_ string on _event_ in given _instance_.
+
+*tracefs_event_filter_clear*() clear all filters on _event_ in given _instance_.
+
+RETURN VALUE
+------------
+*tracefs_filter_string_append*() returns 0 on success and -1 on error.
+
+*tracefs_filter_string_verify*() returns 0 on success and -1 on error. if there
+is an error, and _errno_ is not *ENOMEM*, then _err_ is allocated and will
+contain a string describing what was found wrong with _filter_. _err_ must be
+freed with *free*().
+
+*tracefs_event_filter_apply*() returns 0 on success and -1 on error.
+
+*tracefs_event_filter_clear*() returns 0 on success and -1 on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <tracefs.h>
+
+static void usage(char **argv)
+{
+	fprintf(stderr, "usage: %s [system] event filter\n", argv[0]);
+	exit(-1);
+}
+
+int main (int argc, char **argv)
+{
+	struct tep_handle *tep;
+	struct tep_event *event;
+	const char *system = NULL;
+	const char *event_name;
+	const char *filter;
+	char *new_filter = NULL;
+	char *err = NULL;
+	int i;
+
+	if (argc < 3)
+		usage(argv);
+
+	if (argc < 4) {
+		event_name = argv[1];
+		filter = argv[2];
+	} else {
+		system = argv[1];
+		event_name = argv[2];
+		filter = argv[3];
+	}
+
+	/* Load all events from the system */
+	tep = tracefs_local_events(NULL);
+	if (!tep) {
+		perror("tep");
+		exit(-1);
+	}
+
+	event = tep_find_event_by_name(tep, system, event_name);
+	if (!event) {
+		fprintf(stderr, "Event %s%s%s not found\n",
+			system ? system : "" , system ? " " : "",
+			event_name);
+		exit(-1);
+	}
+
+	if (tracefs_filter_string_verify(event, filter, &err) < 0) {
+		perror("tracecfs_event_verify_filter");
+		if (err)
+			fprintf(stderr, "%s", err);
+		free(err);
+		exit(-1);
+	}
+
+	for (i = 0; filter[i]; i++) {
+		char buf[strlen(filter)];
+		char *field = NULL;
+		char *val = NULL;
+		enum tracefs_filter type;
+		enum tracefs_compare compare = 0;
+		int start_i, n;
+		int quote;
+		bool backslash;
+
+		while (isspace(filter[i]))
+			i++;
+
+		switch(filter[i]) {
+		case '(':
+			type = TRACEFS_FILTER_OPEN_PAREN;
+			break;
+		case ')':
+			type = TRACEFS_FILTER_CLOSE_PAREN;
+			break;
+		case '!':
+			type = TRACEFS_FILTER_NOT;
+			break;
+		case '&':
+			type = TRACEFS_FILTER_AND;
+			i++;
+			break;
+		case '|':
+			type = TRACEFS_FILTER_OR;
+			i++;
+			break;
+		default:
+			type = TRACEFS_FILTER_COMPARE;
+
+			while (isspace(filter[i]))
+				i++;
+
+			start_i = i;
+			for (; filter[i]; i++) {
+				switch(filter[i]) {
+				case 'a' ... 'z':
+				case 'A' ... 'Z':
+				case '0' ... '9':
+				case '_':
+					continue;
+				}
+				break;
+			}
+
+			n = i - start_i;
+			field = buf;
+			strncpy(field, filter + start_i, n);
+			field[n++] = '\0';
+
+			val = buf + n;
+
+			while (isspace(filter[i]))
+				i++;
+
+			start_i = i;
+			switch(filter[i++]) {
+			case '>':
+				compare = TRACEFS_COMPARE_GT;
+				if (filter[i] == '=') {
+					i++;
+					compare = TRACEFS_COMPARE_GE;
+				}
+				break;
+			case '<':
+				compare = TRACEFS_COMPARE_LT;
+				if (filter[i] == '=') {
+					i++;
+					compare = TRACEFS_COMPARE_LE;
+				}
+				break;
+			case '=':
+				compare = TRACEFS_COMPARE_EQ;
+				i++;
+				break;
+			case '!':
+				compare = TRACEFS_COMPARE_NE;
+				i++;
+				break;
+			case '~':
+				compare = TRACEFS_COMPARE_RE;
+				break;
+			case '&':
+				compare = TRACEFS_COMPARE_AND;
+				break;
+			}
+
+			while (isspace(filter[i]))
+				i++;
+
+			quote = 0;
+			backslash = false;
+			start_i = i;
+			for (; filter[i]; i++) {
+				if (quote) {
+					if (backslash)
+						backslash = false;
+					else if (filter[i] == '\\')
+						backslash = true;
+					else if (filter[i] == quote)
+						quote = 0;
+					continue;
+				}
+				switch(filter[i]) {
+				case '"': case '\'':
+					quote = filter[i];
+					continue;
+				case 'a' ... 'z':
+				case 'A' ... 'Z':
+				case '0' ... '9':
+				case '_':
+					continue;
+				}
+				break;
+			}
+			n = i - start_i;
+			strncpy(val, filter + start_i, n);
+			val[n] = '\0';
+			break;
+		}
+		n = tracefs_filter_string_append(event, &new_filter, type,
+						field, compare, val);
+		if (n < 0) {
+			fprintf(stderr, "Failed making new filter:\n'%s'\n",
+				new_filter ? new_filter : "(null)");
+			exit(-1);
+		}
+	}
+
+	if (tracefs_event_filter_apply(NULL, event, new_filter))
+		fprintf(stderr, "Failed to apply filter on event");
+
+	tep_free(tep);
+
+	printf("Created new filter: '%s'\n", new_filter);
+	free(new_filter);
+
+	exit(0);
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+*tracefs_hist_alloc*(3),
+*tracefs_hist_alloc_2d*(3),
+*tracefs_hist_alloc_nd*(3),
+*tracefs_hist_free*(3),
+*tracefs_hist_add_key*(3),
+*tracefs_hist_add_value*(3),
+*tracefs_hist_add_name*(3),
+*tracefs_hist_start*(3),
+*tracefs_hist_destory*(3),
+*tracefs_hist_add_sort_key*(3),
+*tracefs_hist_sort_key_direction*(3)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-function-filter.txt b/Documentation/libtracefs-function-filter.txt
new file mode 100644
index 0000000..2a141fd
--- /dev/null
+++ b/Documentation/libtracefs-function-filter.txt
@@ -0,0 +1,237 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_function_filter, tracefs_function_notrace, tracefs_filter_functions
+- Functions to modify the the function trace filters
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_function_filter*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_filter_, const char pass:[*]_module_, int _flags_);
+int *tracefs_function_notrace*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_filter_, const char pass:[*]_module_, int _flags_);
+int *tracefs_filter_functions*(const char pass:[*]_filter_, const char pass:[*]_module_, char pass:[*]pass:[*]pass:[*]_list_);
+--
+
+DESCRIPTION
+-----------
+*tracefs_function_filter* and *tracefs_function_notrace* can be used to limit the
+Linux kernel functions that would be traced by the function and function-graph tracers.
+The *tracefs_function_filter* defines a list of functions that can be traced.
+The *tracefs_function_notrace* defines a list of functions that will not be traced.
+If a function is in both lists, it will not be traced.
+
+They take an _instance_ , that can be NULL for the top level tracing,
+_filter_, a string that represents a filter that should
+be applied to define what functions are to be traced,
+_module_, to limit the filtering on a specific module (or NULL to filter on all functions),
+_flags_ which holds control knobs on how the filters will be handled (see *FLAGS*)
+section below.
+
+The *tracefs_filter_functions* returns a list of functions that can be filtered on
+via the _filter_ and _module_ that are supplied. If both _filter_ and _module_ are
+NULL then, all available functions that can be filtered is returned.
+On success, _list_ must be freed with *tracefs_list_free()*(3).
+
+The _filter_ may be either a straight match of a
+function, a glob or regex(3). A glob is where 'pass:[*]' matches zero or more
+characters, '?' will match zero or one character, and '.' only matches a
+period. If the _filter_ is determined to be a regex (where it contains
+anything other than alpha numeric characters, or '.', 'pass:[*]', '?') the _filter_
+will be processed as a regex(3) following the rules of regex(3), and '.' is
+not a period, but will match any one character. To force a regular
+expression, either prefix _filter_ with a '^' or append it with a '$' as
+the _filter_ does complete matches of the functions anyway.
+
+If _module_ is set and _filter_ is NULL, this will imply the same as _filter_ being
+equal to "pass:[*]". Which will enable all functions for a given _module_. Otherwise
+the _filter_ may be NULL if a previous call to *tracefs_function_filter()* with
+the same _instance_ had *TRACEFS_FL_CONTINUE* set and this call does not. This is
+useful to simply commit the previous filters. It may also be NULL
+if *TRACEFS_FL_RESET* is set and the previous call did not have the same _instance_
+and *TRACEFS_FL_CONTINUE* set. This is useful to just clear the filter.
+
+FLAGS
+-----
+
+The _flags_ parameter may have the following set, or be zero.
+
+*TRACEFS_FL_RESET* :
+If _flags_ contains *TRACEFS_FL_RESET*, then it will clear the filters that
+are currently set before applying _filter_. Otherwise, _filter_ is added to
+the current set of filters already enabled. If this flag is set and the
+previous call to tracefs_function_filter() had the same _instance_ and the
+*TRACEFS_FL_CONTINUE* flag was set, then the function will fail with a
+return of -1 and errno set to EBUSY.
+
+*TRACEFS_FL_CONTINUE* :
+If _flags_ contains *TRACEFS_FL_CONTINUE*, then _filter_ will not take
+effect after a successful call to tracefs_function_filter(). This allows for
+multiple calls to tracefs_function_filter() to update the filter function
+and then a single call (one without the *TRACEFS_FL_CONTINUE* flag set) to
+commit all the filters.
+It can be called multiple times to add more filters. A call without this
+flag set will commit the changes before returning (if the _filter_ passed in
+successfully matched). A tracefs_function_filter() call after one that had
+the *TRACEFS_FL_CONTINUE* flag set for the same instance will fail if
+*TRACEFS_FL_RESET* flag is set, as the reset flag is only applicable for the
+first filter to be added before committing.
+
+*TRACEFS_FL_FUTURE* :
+If _flags_ contains *TRACEFS_FL_FUTURE* and _module_ holds a string of a module,
+then if the module is not loaded it will attemp to write the filter with the module
+in the filter file. Starting in Linux v4.13 module functions could be added to the
+filter before they are loaded. The filter will be cached, and when the module is
+loaded, the filter will be set before the module executes, allowing to trace
+init functions of a module. This will only work if the _filter_ is not a
+regular expression.
+
+RETURN VALUE
+------------
+
+For *tracefs_function_filter()* and *tracefs_function_notrace()* a
+return of 0 means success. If the there is an error but the filtering was not
+started, then 1 is returned. If filtering was started but an error occurs,
+then -1 is returned. The state of the filtering may be in an unknown state.
+
+If *TRACEFS_FL_CONTINUE* was set, and 0 or -1 was returned, then another call
+to *tracefs_function_filter()* must be done without *TRACEFS_FL_CONTINUE* set
+in order to commit (and close) the filtering.
+
+For *tracefs_filter_functions()*, a return of 0 means success, and the _list_
+parameter is filled with a list of function names that matched _filter_ and
+_module_. _list_ is a string array, where the last string pointer in the
+array is NULL. The _list_ must be freed with *tracefs_list_free()*.
+On failure, a negative is returned, and _list_ is ignored.
+
+ERRORS
+------
+
+*tracefs_function_filter*() can fail with the following errors:
+
+*EINVAL* The filter is invalid or did not match any functions.
+
+*EBUSY* The previous call of *tracefs_function_filter*() was called
+with the same instance and *TRACEFS_FL_CONTINUE* set and the current call
+had *TRACEFS_FL_RESET* set.
+
+Other errors may also happen caused by internal system calls.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdio.h>
+#include <errno.h>
+#include <tracefs.h>
+
+#define INST "dummy"
+
+static const char *filters[] = { "run_init_process", "try_to_run_init_process", "dummy1", NULL };
+
+int main(int argc, char *argv[])
+{
+	struct tracefs_instance *inst = tracefs_instance_create(INST);
+	char **func_list;
+	int ret;
+	int i;
+
+	if (!inst) {
+		/* Error creating new trace instance */
+	}
+
+	if (tracefs_filter_functions("*lock*", NULL, &func_list) < 0) {
+		printf("Failed to read filter functions\n");
+		goto out;
+	}
+	printf("Ignoring the following functions:\n");
+	for (i = 0; func_list[i]; i++)
+		printf("  %s\n", func_list[i]);
+	tracefs_list_free(func_list);
+
+	/* Do not trace any function with the word "lock" in it */
+	ret = tracefs_function_notrace(inst, "*lock*", NULL, TRACEFS_FL_RESET);
+	if (ret) {
+		printf("Failed to set the notrace filter\n");
+		goto out;
+	}
+
+	/* First reset the filter */
+	ret = tracefs_function_filter(inst, NULL, NULL,
+				      TRACEFS_FL_RESET | TRACEFS_FL_CONTINUE);
+	if (ret) {
+		printf("Failed to reset the filter\n");
+		/* Make sure it is closed, -1 means filter was started */
+		if (ret < 0)
+			tracefs_function_filter(inst, NULL, NULL, 0);
+	}
+
+	for (i = 0; filters[i]; i++) {
+		ret = tracefs_function_filter(inst, filters[i], NULL,
+					      TRACEFS_FL_CONTINUE);
+
+		if (ret) {
+			if (errno == EINVAL)
+				printf("Filter %s did not match\n", filters[i]);
+			else
+				printf("Failed writing %s\n", filters[i]);
+		}
+	}
+
+	ret = tracefs_function_filter(inst, "*", "ext4", 0);
+	if (ret) {
+		printf("Failed to set filters for ext4\n");
+		/* Force the function to commit previous filters */
+		tracefs_function_filter(inst, NULL, NULL, 0);
+	}
+
+ out:
+	tracefs_instance_destroy(inst);
+	return ret;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-hist-cont.txt b/Documentation/libtracefs-hist-cont.txt
new file mode 100644
index 0000000..7159e27
--- /dev/null
+++ b/Documentation/libtracefs-hist-cont.txt
@@ -0,0 +1,222 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_hist_start, tracefs_hist_destroy, tracefs_hist_pause,
+tracefs_hist_continue, tracefs_hist_reset - Pause, continue, or clear an existing histogram
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_hist_start*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
+int *tracefs_hist_destroy*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
+int *tracefs_hist_pause*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
+int *tracefs_hist_continue*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
+int *tracefs_hist_reset*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
+
+--
+
+DESCRIPTION
+-----------
+
+*tracefs_hist_start()* is called to actually start the histogram _hist_.
+The _instance_ is the instance to start the histogram in, NULL if it
+should start at the top level.
+
+*tracefs_hist_pause()* is called to pause the histogram _hist_.
+The _instance_ is the instance to pause the histogram in, NULL if it
+is in the top level.
+
+*tracefs_hist_continue()* is called to continue a paused histogram _hist_.
+The _instance_ is the instance to continue the histogram, NULL if it
+is in the top level.
+
+*tracefs_hist_reset()* is called to clear / reset the histogram _hist_.
+The _instance_ is the instance to clear the histogram, NULL if it
+is in the top level.
+
+*tracefs_hist_destroy()* is called to delete the histogram where it will no longer
+exist.  The _instance_ is the instance to delete the histogram from, NULL if it
+is in the top level.
+
+RETURN VALUE
+------------
+All the return zero on success or -1 on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <tracefs.h>
+
+enum commands {
+	START,
+	PAUSE,
+	CONT,
+	RESET,
+	DELETE,
+	SHOW,
+};
+
+int main (int argc, char **argv, char **env)
+{
+	struct tracefs_instance *instance;
+	struct tracefs_hist *hist;
+	struct tep_handle *tep;
+	enum commands cmd;
+	char *cmd_str;
+	int ret;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s command\n", argv[0]);
+		exit(-1);
+	}
+
+	cmd_str = argv[1];
+
+	if (!strcmp(cmd_str, "start"))
+		cmd = START;
+	else if (!strcmp(cmd_str, "pause"))
+		cmd = PAUSE;
+	else if (!strcmp(cmd_str, "cont"))
+		cmd = CONT;
+	else if (!strcmp(cmd_str, "reset"))
+		cmd = RESET;
+	else if (!strcmp(cmd_str, "delete"))
+		cmd = DELETE;
+	else if (!strcmp(cmd_str, "show"))
+		cmd = SHOW;
+	else {
+		fprintf(stderr, "Unknown command %s\n", cmd_str);
+		exit(-1);
+	}
+
+	tep = tracefs_local_events(NULL);
+	if (!tep) {
+		perror("Reading tracefs");
+		exit(-1);
+	}
+
+	instance = tracefs_instance_create("hist_test");
+	if (!instance) {
+		fprintf(stderr, "Failed instance create\n");
+		exit(-1);
+	}
+
+	hist = tracefs_hist_alloc_2d(tep, "kmem", "kmalloc",
+				     "call_site",TRACEFS_HIST_KEY_SYM,
+				     "bytes_req", 0);
+	if (!hist) {
+		fprintf(stderr, "Failed hist create\n");
+		exit(-1);
+	}
+
+	ret = tracefs_hist_add_value(hist, "bytes_alloc");
+	ret |= tracefs_hist_add_sort_key(hist, "bytes_req");
+	ret |= tracefs_hist_add_sort_key(hist, "bytes_alloc");
+
+	ret |= tracefs_hist_sort_key_direction(hist, "bytes_alloc",
+					       TRACEFS_HIST_SORT_DESCENDING);
+	if (ret) {
+		fprintf(stderr, "Failed modifying histogram\n");
+		exit(-1);
+	}
+
+	tracefs_error_clear(instance);
+
+	switch (cmd) {
+	case START:
+		ret = tracefs_hist_start(instance, hist);
+		if (ret) {
+			char *err = tracefs_error_last(instance);
+			if (err)
+				fprintf(stderr, "\n%s\n", err);
+		}
+		break;
+	case PAUSE:
+		ret = tracefs_hist_pause(instance, hist);
+		break;
+	case CONT:
+		ret = tracefs_hist_continue(instance, hist);
+		break;
+	case RESET:
+		ret = tracefs_hist_reset(instance, hist);
+		break;
+	case DELETE:
+		ret = tracefs_hist_destroy(instance, hist);
+		break;
+	case SHOW: {
+		char *content;
+		content = tracefs_event_file_read(instance, "kmem", "kmalloc",
+						  "hist", NULL);
+		ret = content ? 0 : -1;
+		if (content) {
+			printf("%s\n", content);
+			free(content);
+		}
+		break;
+	}
+	}
+	if (ret)
+		fprintf(stderr, "Failed: command\n");
+	exit(ret);
+}
+
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+*tracefs_hist_alloc*(3),
+*tracefs_hist_alloc_2d*(3),
+*tracefs_hist_alloc_nd*(3),
+*tracefs_hist_free*(3),
+*tracefs_hist_add_key*(3),
+*tracefs_hist_add_value*(3),
+*tracefs_hist_add_name*(3),
+*tracefs_hist_start*(3),
+*tracefs_hist_destory*(3),
+*tracefs_hist_add_sort_key*(3),
+*tracefs_hist_sort_key_direction*(3)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-hist-mod.txt b/Documentation/libtracefs-hist-mod.txt
new file mode 100644
index 0000000..b308c7e
--- /dev/null
+++ b/Documentation/libtracefs-hist-mod.txt
@@ -0,0 +1,540 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_hist_add_sort_key, tracefs_hist_set_sort_key, tracefs_hist_sort_key_direction,
+tracefs_hist_add_name, tracefs_hist_append_filter, tracefs_hist_echo_cmd, tracefs_hist_command,
+tracefs_hist_get_name, tracefs_hist_get_event, tracefs_hist_get_system - Update and describe an event histogram
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_hist_add_sort_key*(struct tracefs_hist pass:[*]_hist_,
+			      const char pass:[*]_sort_key_);
+
+int *tracefs_hist_set_sort_key*(struct tracefs_hist pass:[*]_hist_,
+			      const char pass:[*]_sort_key_, _..._);
+int *tracefs_hist_sort_key_direction*(struct tracefs_hist pass:[*]_hist_,
+				    const char pass:[*]_sort_key_,
+				    enum tracefs_hist_sort_direction _dir_);
+
+int *tracefs_hist_add_name*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_name_);
+
+int *tracefs_hist_append_filter*(struct tracefs_hist pass:[*]_hist_,
+			       enum tracefs_filter _type_,
+			       const char pass:[*]_field_,
+			       enum tracefs_compare _compare_,
+			       const char pass:[*]_val_);
+
+int *tracefs_hist_echo_cmd*(struct trace_seq pass:[*]_s_, struct tracefs_instance pass:[*]_instance_,
+			  struct tracefs_hist pass:[*]_hist_,
+			  enum tracefs_hist_command _command_);
+
+int *tracefs_hist_command*(struct tracefs_instance pass:[*]_instance_,
+			 struct tracefs_hist pass:[*]_hist_,
+			 enum tracefs_hist_command _command_);
+
+const char pass:[*]*tracefs_hist_get_name*(struct tracefs_hist pass:[*]_hist_);
+
+const char pass:[*]*tracefs_hist_get_event*(struct tracefs_hist pass:[*]_hist_);
+
+const char pass:[*]*tracefs_hist_get_system*(struct tracefs_hist pass:[*]_hist_);
+
+--
+
+DESCRIPTION
+-----------
+Event histograms are created by the trigger file in the event directory.
+The syntax can be complex and difficult to get correct. This API handles the
+syntax, and facilitates the creation and interaction with the event histograms.
+See https://www.kernel.org/doc/html/latest/trace/histogram.html for more information.
+
+*tracefs_hist_add_sort_key*() will add a key to sort on. The _hist_ is the
+histogram descriptor to add the sort key to. The _sort_key_ is a string
+that must match either an already defined key of the histogram, or an already
+defined value. If _hist_  already has sorting keys (previously added) the new
+_sort_key_ will have lower priority(be secondary or so on) when sorting.
+
+*tracefs_hist_set_sort_key*() will reset the list of key to sort on. The _hist_ is
+the histogram descriptor to reset the sort key to. The _sort_key_ is a string
+that must match either an already defined key of the histogram, or an already
+defined value. Multiple sort keys may be added to denote a secondary, sort order
+and so on, but all sort keys must match an existing key or value, or be
+TRACEFS_HIST_HITCOUNT. The last parameter of *tracefs_hist_add_sort_key*() must
+be NULL.
+
+*tracefs_hist_sort_key_direction*() allows to change the direction of an
+existing sort key of _hist_. The _sort_key_ is the sort key to change, and
+_dir_ can be either TRACEFS_HIST_SORT_ASCENDING or TRACEFS_HIST_SORT_DESCENDING,
+to make the direction of the sort key either ascending or descending respectively.
+
+*tracefs_hist_add_name*() adds a name to a histogram. A histogram may be
+named and if the name matches between more than one event, and they have
+compatible keys, the multiple histograms with the same name will be merged
+into a single histogram (shown by either event's hist file). The _hist_
+is the histogram to name, and the _name_ is the name to give it.
+
+*tracefs_hist_append_filter*() creates a filter or appends to it for the
+histogram  event. Depending on _type_, it will build a string of tokens for
+parenthesis or logic statements, or it may add a comparison of _field_
+to _val_ based on _compare_.
+
+If _type_ is:
+*TRACEFS_FILTER_COMPARE*     -  See below
+*TRACEFS_FILTER_AND*         -  Append "&&" to the filter
+*TRACEFS_FILTER_OR*          -  Append "||" to the filter
+*TRACEFS_FILTER_NOT*         -  Append "!" to the filter
+*TRACEFS_FILTER_OPEN_PAREN*  -  Append "(" to the filter
+*TRACEFS_FILTER_CLOSE_PAREN* -  Append ")" to the filter
+
+_field_, _compare_, and _val_ are ignored unless _type_ is equal to
+*TRACEFS_FILTER_COMPARE*, then _compare_ will be used for the following:
+
+*TRACEFS_COMPARE_EQ* - _field_ == _val_
+
+*TRACEFS_COMPARE_NE* - _field_ != _val_
+
+*TRACEFS_COMPARE_GT* - _field_ > _val_
+
+*TRACEFS_COMPARE_GE* - _field_ >= _val_
+
+*TRACEFS_COMPARE_LT* - _field_ < _val_
+
+*TRACEFS_COMPARE_LE* - _field_ <pass:[=] _val_
+
+*TRACEFS_COMPARE_RE* - _field_ ~ "_val_" : where _field_ is a string.
+
+*TRACEFS_COMPARE_AND* - _field_ & _val_ : where _field_ is a flags field.
+
+*trace_hist_echo_cmd*() prints the commands needed to create the given histogram
+in the given _instance_, or NULL for the top level, into the _seq_.
+The command that is printed is described by _command_ and shows the functionality
+that would be done by *tracefs_hist_command*(3).
+
+*tracefs_hist_command*() is called to process a command on the histogram
+_hist_ for its event in the given _instance_, or NULL for the top level.
+The _cmd_ can be one of:
+
+*TRACEFS_HIST_CMD_START* or zero to start execution of the histogram.
+
+*TRACEFS_HIST_CMD_PAUSE* to pause the given histogram.
+
+*TRACEFS_HIST_CMD_CONT* to continue a paused histogram.
+
+*TRACEFS_HIST_CMD_CLEAR* to reset the values of a histogram.
+
+*TRACEFS_HIST_CMD_DESTROY* to destroy the histogram (undo a START).
+
+The below functions are wrappers to tracefs_hist_command() to make the
+calling conventions a bit easier to understand what is happening.
+
+KEY TYPES
+---------
+
+*tracefs_hist_alloc_nd*() and *tracefs_hist_add_key*() both add a key and requires
+that key to have a type. The types may be:
+
+*TRACEFS_HIST_KEY_NORMAL* or zero (0) which is to not modify the type.
+
+*TRACEFS_HIST_KEY_HEX* to display the key in hex.
+
+*TRACEFS_HIST_KEY_SYM* to display the key as a kernel symbol (if found). If
+the key is an address, this is useful as it will display the function names instead
+of just a number.
+
+*TRACEFS_HIST_KEY_SYM_OFFSET* similar to *TRACEFS_HIST_KEY_SYM* but will also include
+the offset of the function to match the exact address.
+
+*TRACEFS_HIST_KEY_SYSCALL* If the key is a system call "id" (the number passed from user
+space to the kernel to tell it what system call it is calling), then the name of
+the system call is displayed.
+
+*TRACEFS_HIST_KEY_EXECNAME* If "common_pid" is the key (the pid of the executing task),
+instead of showing the number, show the name of the running task.
+
+*TRACEFS_HIST_KEY_LOG* will display the key in a binary logarithmic scale.
+
+*TRACEFS_HIST_KEY_USECS* for use with "common_timestamp" or TRACEFS_HIST_TIMESTAMP,
+in which case it will show the timestamp in microseconds instead of nanoseconds.
+
+RETURN VALUE
+------------
+*tracefs_hist_get_name*() returns the name of the histogram or NULL on error.
+The returned string belongs to the histogram object and is freed with the histogram
+by *tracefs_hist_free*().
+
+*tracefs_hist_get_event*() returns the event name of the histogram or NULL on error.
+The returned string belongs to the histogram object and is freed with the histogram
+by *tracefs_hist_free*().
+
+*tracefs_hist_get_system*() returns the system name of the histogram or NULL on error.
+The returned string belongs to the histogram object and is freed with the histogram
+by *tracefs_hist_free*().
+
+*tracefs_hist_alloc_nd*() returns an allocated histogram descriptor which must
+be freed by *tracefs_hist_free*() or NULL on error.
+
+*tracefs_hist_get_name*(), *tracefs_hist_get_event*() and *tracefs_hist_get_system*()
+return strings owned by the histogram object.
+
+All the other functions return zero on success or -1 on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <unistd.h>
+#include <tracefs.h>
+
+enum commands {
+	START,
+	PAUSE,
+	CONT,
+	RESET,
+	DELETE,
+	SHOW,
+};
+
+static void parse_system_event(char *group, char **system, char **event)
+{
+	*system = strtok(group, "/");
+	*event = strtok(NULL, "/");
+	if (!*event) {
+		*event = *system;
+		*system = NULL;
+	}
+}
+
+static int parse_keys(char *keys, struct tracefs_hist_axis **axes)
+{
+	char *sav = NULL;
+	char *key;
+	int cnt = 0;
+
+	for (key = strtok_r(keys, ",", &sav); key; key = strtok_r(NULL, ",", &sav)) {
+		struct tracefs_hist_axis *ax;
+		char *att;
+
+		ax = realloc(*axes, sizeof(*ax) * (cnt + 2));
+		if (!ax) {
+			perror("Failed to allocate axes");
+			exit(-1);
+		}
+		ax[cnt].key = key;
+		ax[cnt].type = 0;
+		ax[cnt + 1].key = NULL;
+		ax[cnt + 1].type = 0;
+
+		*axes = ax;
+
+		att = strchr(key, '.');
+		if (att) {
+			*att++ = '\0';
+			if (strcmp(att, "hex") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_HEX;
+			else if (strcmp(att, "sym") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_SYM;
+			else if (strcmp(att, "sym_offset") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_SYM_OFFSET;
+			else if (strcmp(att, "syscall") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_SYSCALL;
+			else if (strcmp(att, "exec") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_EXECNAME;
+			else if (strcmp(att, "log") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_LOG;
+			else if (strcmp(att, "usecs") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_USECS;
+			else {
+				fprintf(stderr, "Undefined attribute '%s'\n", att);
+				fprintf(stderr,"  Acceptable attributes:\n");
+				fprintf(stderr,"    hex, sym, sym_offset, syscall, exe, log, usecs\n");
+				exit(-1);
+			}
+		}
+		cnt++;
+	}
+	return cnt;
+}
+
+static void process_hist(enum commands cmd, const char *instance_name,
+			 char *group, char *keys, char *vals, char *sort,
+			 char *ascend, char *desc)
+{
+	struct tracefs_instance *instance = NULL;
+	struct tracefs_hist *hist;
+	struct tep_handle *tep;
+	struct tracefs_hist_axis *axes = NULL;
+	char *system;
+	char *event;
+	char *sav;
+	char *val;
+	int ret;
+	int cnt;
+
+	if (instance_name) {
+		instance = tracefs_instance_create(instance_name);
+		if (!instance) {
+			fprintf(stderr, "Failed instance create\n");
+			exit(-1);
+		}
+	}
+
+	tep = tracefs_local_events(NULL);
+	if (!tep) {
+		perror("Could not read events");
+		exit(-1);
+	}
+
+	parse_system_event(group, &system, &event);
+
+	if (cmd == SHOW) {
+		char *content;
+		content = tracefs_event_file_read(instance, system, event,
+						  "hist", NULL);
+		if (!content) {
+			perror("Reading hist file");
+			exit(-1);
+		}
+		printf("%s\n", content);
+		free(content);
+		return;
+	}
+
+	if (!keys) {
+		fprintf(stderr, "Command needs -k option\n");
+		exit(-1);
+	}
+
+	cnt = parse_keys(keys, &axes);
+	if (!cnt) {
+		fprintf(stderr, "No keys??\n");
+		exit(-1);
+	}
+
+	/* Show examples of hist1d and hist2d */
+	switch (cnt) {
+	case 1:
+		hist = tracefs_hist_alloc(tep, system, event,
+					  axes[0].key, axes[0].type);
+		break;
+	case 2:
+		hist = tracefs_hist_alloc_2d(tep, system, event,
+					     axes[0].key, axes[0].type,
+					     axes[1].key, axes[1].type);
+		break;
+	default:
+		/* Really, 1 and 2 could use this too */
+		hist = tracefs_hist_alloc_nd(tep, system, event, axes);
+	}
+	if (!hist) {
+		fprintf(stderr, "Failed hist create\n");
+		exit(-1);
+	}
+
+	if (vals) {
+		sav = NULL;
+		for (val = strtok_r(vals, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
+			ret = tracefs_hist_add_value(hist, val);
+			if (ret) {
+				fprintf(stderr, "Failed to add value %s\n", val);
+				exit(-1);
+			}
+		}
+	}
+
+	if (sort) {
+		sav = NULL;
+		for (val = strtok_r(sort, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
+			ret = tracefs_hist_add_sort_key(hist, val);
+			if (ret) {
+				fprintf(stderr, "Failed to add sort key/val %s\n", val);
+				exit(-1);
+			}
+		}
+	}
+
+	if (ascend) {
+		sav = NULL;
+		for (val = strtok_r(ascend, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
+			ret = tracefs_hist_sort_key_direction(hist, val, TRACEFS_HIST_SORT_ASCENDING);
+			if (ret) {
+				fprintf(stderr, "Failed to add ascending key/val %s\n", val);
+				exit(-1);
+			}
+		}
+	}
+
+	if (desc) {
+		sav = NULL;
+		for (val = strtok_r(desc, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
+			ret = tracefs_hist_sort_key_direction(hist, val, TRACEFS_HIST_SORT_DESCENDING);
+			if (ret) {
+				fprintf(stderr, "Failed to add descending key/val %s\n", val);
+				exit(-1);
+			}
+		}
+	}
+
+	tracefs_error_clear(instance);
+
+	switch (cmd) {
+	case START:
+		ret = tracefs_hist_start(instance, hist);
+		if (ret) {
+			char *err = tracefs_error_last(instance);
+			if (err)
+				fprintf(stderr, "\n%s\n", err);
+		}
+		break;
+	case PAUSE:
+		ret = tracefs_hist_pause(instance, hist);
+		break;
+	case CONT:
+		ret = tracefs_hist_continue(instance, hist);
+		break;
+	case RESET:
+		ret = tracefs_hist_reset(instance, hist);
+		break;
+	case DELETE:
+		ret = tracefs_hist_destroy(instance, hist);
+		break;
+	case SHOW:
+		/* Show was already done */
+		break;
+	}
+	if (ret)
+		fprintf(stderr, "Failed: command\n");
+	exit(ret);
+}
+
+int main (int argc, char **argv, char **env)
+{
+	enum commands cmd;
+	char *instance = NULL;
+	char *cmd_str;
+	char *event = NULL;
+	char *keys = NULL;
+	char *vals = NULL;
+	char *sort = NULL;
+	char *desc = NULL;
+	char *ascend = NULL;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s command [-B instance][-e [system/]event][-k keys][-v vals][-s sort]\n", argv[0]);
+		fprintf(stderr, "      [-a ascending][-d descending]\n");
+		exit(-1);
+	}
+
+	cmd_str = argv[1];
+
+	if (!strcmp(cmd_str, "start"))
+		cmd = START;
+	else if (!strcmp(cmd_str, "pause"))
+		cmd = PAUSE;
+	else if (!strcmp(cmd_str, "cont"))
+		cmd = CONT;
+	else if (!strcmp(cmd_str, "reset"))
+		cmd = RESET;
+	else if (!strcmp(cmd_str, "delete"))
+		cmd = DELETE;
+	else if (!strcmp(cmd_str, "show"))
+		cmd = SHOW;
+	else {
+		fprintf(stderr, "Unknown command %s\n", cmd_str);
+		exit(-1);
+	}
+
+	for (;;) {
+		int c;
+
+		c = getopt(argc - 1, argv + 1, "e:k:v:B:s:d:a:");
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'e':
+			event = optarg;
+			break;
+		case 'k':
+			keys = optarg;
+			break;
+		case 'v':
+			vals = optarg;
+			break;
+		case 'B':
+			instance = optarg;
+			break;
+		case 's':
+			sort = optarg;
+			break;
+		case 'd':
+			desc = optarg;
+			break;
+		case 'a':
+			ascend = optarg;
+			break;
+		}
+	}
+	if (!event) {
+		event = "kmem/kmalloc";
+		if (!keys)
+			keys = "call_site.sym,bytes_req";
+		if (!vals)
+			vals = "bytes_alloc";
+		if (!sort)
+			sort = "bytes_req,bytes_alloc";
+		if (!desc)
+			desc = "bytes_alloc";
+	}
+	process_hist(cmd, instance, event, keys, vals, sort, ascend, desc);
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+*tracefs_hist_pause*(3),
+*tracefs_hist_continue*(3),
+*tracefs_hist_reset*(3)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-hist.txt b/Documentation/libtracefs-hist.txt
new file mode 100644
index 0000000..7503fd0
--- /dev/null
+++ b/Documentation/libtracefs-hist.txt
@@ -0,0 +1,531 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_hist_alloc, tracefs_hist_alloc_2d, tracefs_hist_alloc_nd, tracefs_hist_alloc_nd_cnt, tracefs_hist_free,
+tracefs_hist_add_key, tracefs_hist_add_key_cnt, tracefs_hist_add_value - Create and destroy event histograms
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+enum *tracefs_hist_key_type* {
+	*TRACEFS_HIST_KEY_NORMAL* = 0,
+	*TRACEFS_HIST_KEY_HEX*,
+	*TRACEFS_HIST_KEY_SYM*,
+	*TRACEFS_HIST_KEY_SYM_OFFSET*,
+	*TRACEFS_HIST_KEY_SYSCALL*,
+	*TRACEFS_HIST_KEY_EXECNAME*,
+	*TRACEFS_HIST_KEY_LOG*,
+	*TRACEFS_HIST_KEY_USECS*,
+	*TRACEFS_HIST_KEY_MAX*
+};
+
+struct *tracefs_hist_axis* {
+	const char pass:[*]_key_;
+	enum tracefs_hist_key_type _type_;
+};
+
+struct tracefs_hist pass:[*]*tracefs_hist_alloc*(struct tracefs_tep pass:[*] _tep_,
+			const char pass:[*]_system_, const char pass:[*]_event_,
+			const char pass:[*]_key_, enum tracefs_hist_key_type _type_);
+struct tracefs_hist pass:[*]*tracefs_hist_alloc_2d*(struct tracefs_tep pass:[*] _tep_,
+			const char pass:[*]_system_, const char pass:[*]_event_,
+			const char pass:[*]_key1_, enum tracefs_hist_key_type _type1_,
+			const char pass:[*]_key2_, enum tracefs_hist_key_type _type2_));
+struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd*(struct tracefs_tep pass:[*] _tep_,
+			const char pass:[*]_system_, const char pass:[*]_event_,
+			struct tracefs_hist_axis pass:[*]_axes_);
+struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd_cnt*(struct tep_handle pass:[*]_tep_,
+			  const char pass:[*]_system_, const char pass:[*]_event_name_,
+			  struct tracefs_hist_axis_cnt pass:[*]_axes_);
+void *tracefs_hist_free*(struct tracefs_hist pass:[*]_hist_);
+
+int *tracefs_hist_add_key*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_key_,
+			 enum tracefs_hist_key_type _type_);
+int *tracefs_hist_add_key_cnt*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_key_,
+			 enum tracefs_hist_key_type _type_, int _cnt_);
+int *tracefs_hist_add_value*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_value_);
+--
+
+DESCRIPTION
+-----------
+Event histograms are created by the trigger file in the event directory.
+The syntax can be complex and difficult to get correct. This API handles the
+syntax, and facilitates the creation and interaction with the event histograms.
+See https://www.kernel.org/doc/html/latest/trace/histogram.html for more information.
+
+*tracefs_hist_alloc*() allocates a "struct tracefs_hist" descriptor of a one-dimensional
+histogram and returns the address of it. This descriptor must be freed by *tracefs_hist_free*().
+The _tep_ is a trace event handle (see *tracefs_local_events*(3)), that holds the
+_system_ and _event_ that the histogram will be attached to. The _system_ is the
+system or group of the event. The _event_ is the event to attach the histogram to.
+The _key_ is a field of the event that will be used as the key(dimension) of the histogram.
+The _type_ is the type of the _key_. See KEY TYPES below.
+
+*tracefs_hist_alloc_2d*() allocates a "struct tracefs_hist" descriptor of a two-dimensional
+histogram and returns the address of it. This descriptor must be freed by *tracefs_hist_free*().
+The _tep_ is a trace event handle (see *tracefs_local_events*(3)), that holds the
+_system_ and _event_ that the histogram will be attached to. The _system_ is the
+system or group of the event. The _event_ is the event to attach the histogram to.
+The _key1_ is the first field of the event that will be used as the key(dimension)
+of the histogram. The _type1_ is the type of the _key1_. See KEY TYPES below.
+The _key2_ is the second field of the event that will be used as the key(dimension)
+of the histogram. The _type2_ is the type of the _key2_. See KEY TYPES below.
+
+*tracefs_hist_alloc_nd*() allocates a "struct tracefs_hist" descriptor of an N-dimensional
+histogram and returns the address of it. This descriptor must be freed by *tracefs_hist_free*().
+The _tep_ is a trace event handle (see *tracefs_local_events*(3)), that holds the
+_system_ and _event_ that the histogram will be attached to. The _system_ is the
+system or group of the event. The _event_ is the event to attach the histogram to.
+The _axes_ is an array of _key_ / _type_ pairs, defining the dimensions of the histogram.
+
+*tracefs_hist_alloc_nd_cnt*() will initialize a histogram descriptor that will be attached to
+the _system_/_event_. This only initializes the descriptor with the given _axes_ keys as primaries.
+This only initializes the descriptor, it does not start the histogram in the kernel.
+The difference between this and *tracefs_hist_alloc_nd()* is that the _axes_ parameter
+is of type *struct tracefs_hist_axis_cnt* and not *struct tracefs_hist_axis*.
+
+*tracefs_hist_free*() frees the _tracefs_hist_ descriptor. Note, it does not stop
+or disable the running histogram if it was started. *tracefs_hist_destroy*() needs
+to be called to do so.
+
+*tracefs_hist_add_key*() Adds a secondary or tertiary key to the histogram.
+The key passed to *tracefs_hist_alloc_nd*() is the primary key of the histogram.
+The first time this function is called, it will add a secondary key (or two dimensional
+histogram). If this function is called again on the same histogram, it will add
+a _tertiary_ key (or three dimensional histogram). The _hist_ parameter is the
+histogram descriptor to add the _key_ to. The _type_ is the type of key to add
+(See KEY TYPES below).
+
+The *tracefs_hist_add_key_cnt*() is the same as *tracefs_hist_add_key*() except
+that it allows to add a counter for the given type. Currently, there's only
+the *buckets* type that requires a counter. When adding a key with the buckets
+type, *tracefs_hist_add_key*() is not sufficient, as the *buckets* type requires
+a counter or bucket size. Use *tracefs_hist_add_key_cnt*() when specifing
+a key that is broken up into  buckets, and pass in the size of those buckets
+into _cnt_.
+
+*tracefs_hist_add_value*() will add a value to record. By default, the value is
+simply the "hitcount" of the number of times a instance of the histogram's
+key was hit. The _hist_ is the histogram descriptor to add the value to.
+The _value_ is a field in the histogram to add to when an instance of the
+key is hit.
+
+KEY TYPES
+---------
+
+*tracefs_hist_alloc_nd*() and *tracefs_hist_add_key*() both add a key and requires
+that key to have a type. The types may be:
+
+*TRACEFS_HIST_KEY_NORMAL* or zero (0) which is to not modify the type.
+
+*TRACEFS_HIST_KEY_HEX* to display the key in hex.
+
+*TRACEFS_HIST_KEY_SYM* to display the key as a kernel symbol (if found). If
+the key is an address, this is useful as it will display the function names instead
+of just a number.
+
+*TRACEFS_HIST_KEY_SYM_OFFSET* similar to *TRACEFS_HIST_KEY_SYM* but will also include
+the offset of the function to match the exact address.
+
+*TRACEFS_HIST_KEY_SYSCALL* If the key is a system call "id" (the number passed from user
+space to the kernel to tell it what system call it is calling), then the name of
+the system call is displayed.
+
+*TRACEFS_HIST_KEY_EXECNAME* If "common_pid" is the key (the pid of the executing task),
+instead of showing the number, show the name of the running task.
+
+*TRACEFS_HIST_KEY_LOG* will display the key in a binary logarithmic scale.
+
+*TRACEFS_HIST_KEY_USECS* for use with "common_timestamp" or TRACEFS_HIST_TIMESTAMP,
+in which case it will show the timestamp in microseconds instead of nanoseconds.
+
+RETURN VALUE
+------------
+*tracefs_hist_alloc_nd*() returns an allocated histogram descriptor which must
+be freed by *tracefs_hist_free*() or NULL on error.
+
+All the other functions return zero on success or -1 on error.
+
+If *tracefs_hist_start*() returns an error, a message may be displayed
+in the kernel that can be retrieved by *tracefs_error_last()*.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <tracefs.h>
+
+enum commands {
+	START,
+	PAUSE,
+	CONT,
+	RESET,
+	DELETE,
+	SHOW,
+};
+
+static void parse_system_event(char *group, char **system, char **event)
+{
+	*system = strtok(group, "/");
+	*event = strtok(NULL, "/");
+	if (!*event) {
+		*event = *system;
+		*system = NULL;
+	}
+}
+
+static int parse_keys(char *keys, struct tracefs_hist_axis_cnt **axes)
+{
+	char *sav = NULL;
+	char *key;
+	int cnt = 0;
+
+	for (key = strtok_r(keys, ",", &sav); key; key = strtok_r(NULL, ",", &sav)) {
+		struct tracefs_hist_axis_cnt *ax;
+		char *att;
+
+		ax = realloc(*axes, sizeof(*ax) * (cnt + 2));
+		if (!ax) {
+			perror("Failed to allocate axes");
+			exit(-1);
+		}
+		ax[cnt].key = key;
+		ax[cnt].type = 0;
+		ax[cnt + 1].key = NULL;
+		ax[cnt + 1].type = 0;
+
+		*axes = ax;
+
+		att = strchr(key, '.');
+		if (att) {
+			*att++ = '\0';
+			if (strcmp(att, "hex") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_HEX;
+			else if (strcmp(att, "sym") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_SYM;
+			else if (strcmp(att, "sym_offset") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_SYM_OFFSET;
+			else if (strcmp(att, "syscall") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_SYSCALL;
+			else if (strcmp(att, "exec") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_EXECNAME;
+			else if (strcmp(att, "log") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_LOG;
+			else if (strcmp(att, "usecs") == 0)
+				ax[cnt].type = TRACEFS_HIST_KEY_USECS;
+			else if (strncmp(att, "buckets", 7) == 0) {
+				if (att[7] != '=' && !isdigit(att[8])) {
+					fprintf(stderr, "'buckets' key type requires '=<size>'\n");
+					exit(-1);
+				}
+				ax[cnt].type = TRACEFS_HIST_KEY_BUCKETS;
+				ax[cnt].cnt = atoi(&att[8]);
+			} else {
+				fprintf(stderr, "Undefined attribute '%s'\n", att);
+				fprintf(stderr,"  Acceptable attributes:\n");
+				fprintf(stderr,"    hex, sym, sym_offset, syscall, exe, log, usecs\n");
+				exit(-1);
+			}
+		}
+		cnt++;
+	}
+	return cnt;
+}
+
+static void process_hist(enum commands cmd, const char *instance_name,
+			 char *group, char *keys, char *vals, char *sort,
+			 char *ascend, char *desc)
+{
+	struct tracefs_instance *instance = NULL;
+	struct tracefs_hist *hist;
+	struct tep_handle *tep;
+	struct tracefs_hist_axis_cnt *axes = NULL;
+	char *system;
+	char *event;
+	char *sav;
+	char *val;
+	int ret;
+	int cnt;
+
+	if (instance_name) {
+		instance = tracefs_instance_create(instance_name);
+		if (!instance) {
+			fprintf(stderr, "Failed instance create\n");
+			exit(-1);
+		}
+	}
+
+	tep = tracefs_local_events(NULL);
+	if (!tep) {
+		perror("Could not read events");
+		exit(-1);
+	}
+
+	parse_system_event(group, &system, &event);
+
+	if (cmd == SHOW) {
+		char *content;
+		content = tracefs_event_file_read(instance, system, event,
+						  "hist", NULL);
+		if (!content) {
+			perror("Reading hist file");
+			exit(-1);
+		}
+		printf("%s\n", content);
+		free(content);
+		return;
+	}
+
+	if (!keys) {
+		fprintf(stderr, "Command needs -k option\n");
+		exit(-1);
+	}
+
+	cnt = parse_keys(keys, &axes);
+	if (!cnt) {
+		fprintf(stderr, "No keys??\n");
+		exit(-1);
+	}
+
+	/* buckets require the nd_cnt function */
+	switch (cnt) {
+	case 2:
+		if (axes[1].type == TRACEFS_HIST_KEY_BUCKETS)
+			cnt = -1;
+		/* fall through */
+	case 1:
+		if (axes[0].type == TRACEFS_HIST_KEY_BUCKETS)
+			cnt = -1;
+	}
+
+	/* Show examples of hist1d and hist2d */
+	switch (cnt) {
+	case 1:
+		hist = tracefs_hist_alloc(tep, system, event,
+					  axes[0].key, axes[0].type);
+		break;
+	case 2:
+		hist = tracefs_hist_alloc_2d(tep, system, event,
+					     axes[0].key, axes[0].type,
+					     axes[1].key, axes[1].type);
+		break;
+	default:
+		/* Really, 1 and 2 could use this too */
+		hist = tracefs_hist_alloc_nd_cnt(tep, system, event, axes);
+	}
+	if (!hist) {
+		fprintf(stderr, "Failed hist create\n");
+		exit(-1);
+	}
+
+	if (vals) {
+		sav = NULL;
+		for (val = strtok_r(vals, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
+			ret = tracefs_hist_add_value(hist, val);
+			if (ret) {
+				fprintf(stderr, "Failed to add value %s\n", val);
+				exit(-1);
+			}
+		}
+	}
+
+	if (sort) {
+		sav = NULL;
+		for (val = strtok_r(sort, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
+			ret = tracefs_hist_add_sort_key(hist, val);
+			if (ret) {
+				fprintf(stderr, "Failed to add sort key/val %s\n", val);
+				exit(-1);
+			}
+		}
+	}
+
+	if (ascend) {
+		sav = NULL;
+		for (val = strtok_r(ascend, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
+			ret = tracefs_hist_sort_key_direction(hist, val, TRACEFS_HIST_SORT_ASCENDING);
+			if (ret) {
+				fprintf(stderr, "Failed to add ascending key/val %s\n", val);
+				exit(-1);
+			}
+		}
+	}
+
+	if (desc) {
+		sav = NULL;
+		for (val = strtok_r(desc, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
+			ret = tracefs_hist_sort_key_direction(hist, val, TRACEFS_HIST_SORT_DESCENDING);
+			if (ret) {
+				fprintf(stderr, "Failed to add descending key/val %s\n", val);
+				exit(-1);
+			}
+		}
+	}
+
+	tracefs_error_clear(instance);
+
+	switch (cmd) {
+	case START:
+		ret = tracefs_hist_start(instance, hist);
+		if (ret) {
+			char *err = tracefs_error_last(instance);
+			if (err)
+				fprintf(stderr, "\n%s\n", err);
+		}
+		break;
+	case PAUSE:
+		ret = tracefs_hist_pause(instance, hist);
+		break;
+	case CONT:
+		ret = tracefs_hist_continue(instance, hist);
+		break;
+	case RESET:
+		ret = tracefs_hist_reset(instance, hist);
+		break;
+	case DELETE:
+		ret = tracefs_hist_destroy(instance, hist);
+		break;
+	case SHOW:
+		/* Show was already done */
+		break;
+	}
+	if (ret)
+		fprintf(stderr, "Failed: command\n");
+	exit(ret);
+}
+
+int main (int argc, char **argv, char **env)
+{
+	enum commands cmd;
+	char *instance = NULL;
+	char *cmd_str;
+	char *event = NULL;
+	char *keys = NULL;
+	char *vals = NULL;
+	char *sort = NULL;
+	char *desc = NULL;
+	char *ascend = NULL;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s command [-B instance][-e [system/]event][-k keys][-v vals][-s sort]\n", argv[0]);
+		fprintf(stderr, "      [-a ascending][-d descending]\n");
+		exit(-1);
+	}
+
+	cmd_str = argv[1];
+
+	if (!strcmp(cmd_str, "start"))
+		cmd = START;
+	else if (!strcmp(cmd_str, "pause"))
+		cmd = PAUSE;
+	else if (!strcmp(cmd_str, "cont"))
+		cmd = CONT;
+	else if (!strcmp(cmd_str, "reset"))
+		cmd = RESET;
+	else if (!strcmp(cmd_str, "delete"))
+		cmd = DELETE;
+	else if (!strcmp(cmd_str, "show"))
+		cmd = SHOW;
+	else {
+		fprintf(stderr, "Unknown command %s\n", cmd_str);
+		exit(-1);
+	}
+
+	for (;;) {
+		int c;
+
+		c = getopt(argc - 1, argv + 1, "e:k:v:B:s:d:a:");
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'e':
+			event = optarg;
+			break;
+		case 'k':
+			keys = optarg;
+			break;
+		case 'v':
+			vals = optarg;
+			break;
+		case 'B':
+			instance = optarg;
+			break;
+		case 's':
+			sort = optarg;
+			break;
+		case 'd':
+			desc = optarg;
+			break;
+		case 'a':
+			ascend = optarg;
+			break;
+		}
+	}
+	if (!event) {
+		event = "kmem/kmalloc";
+		if (!keys)
+			keys = "call_site.sym,bytes_req";
+		if (!vals)
+			vals = "bytes_alloc";
+		if (!sort)
+			sort = "bytes_req,bytes_alloc";
+		if (!desc)
+			desc = "bytes_alloc";
+	}
+	process_hist(cmd, instance, event, keys, vals, sort, ascend, desc);
+}
+
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+*tracefs_hist_pause*(3),
+*tracefs_hist_continue*(3),
+*tracefs_hist_reset*(3)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-instances-affinity.txt b/Documentation/libtracefs-instances-affinity.txt
new file mode 100644
index 0000000..39dd44c
--- /dev/null
+++ b/Documentation/libtracefs-instances-affinity.txt
@@ -0,0 +1,200 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_instance_set_affinity, tracefs_instance_set_affinity_set, tracefs_instance_set_affinity_raw,
+tracefs_instance_get_affinity, tracefs_instance_get_affinity_set, tracefs_instance_get_affinity_raw
+- Sets or retrieves the affinity for an instance or top level for what CPUs enable tracing.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_instance_set_affinity*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_cpu_str_);
+int *tracefs_instance_set_affinity_set*(struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_set_, size_t _set_size_);
+int *tracefs_instance_set_affinity_raw*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_mask_);
+
+char pass:[*]*tracefs_instance_get_affinity*(struct tracefs_instance pass:[*]_instance_);
+int *tracefs_instance_get_affinity_set*(struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_set_, size_t _set_size_);
+char pass:[*]*tracefs_instance_get_affinity_raw*(struct tracefs_instance pass:[*]_instance_);
+--
+
+DESCRIPTION
+-----------
+These functions set or retrieve the CPU affinity that limits what CPUs will have tracing enabled
+for a given instance defined by the _instance_ parameter. If _instance_ is NULL, then
+the top level instance is affected.
+
+The *tracefs_instance_set_affinity()* function takes a string _cpu_str_ that is a
+list of CPUs to set the affinity for. If _cpu_str_ is NULL, then all the CPUs in
+the system will be set. The format of _cpu_str_ is a comma delimited string of
+decimal numbers with no spaces. A range may be specified by a hyphen.
+
+For example: "1,4,6-8"
+
+The numbers do not need to be in order except for ranges, where the second number
+must be equal to or greater than the first.
+
+The *tracefs_instance_set_affinity_set()* function takes a CPU set defined by
+*CPU_SET*(3). The size of the set defined by _set_size_ is the size in bytes of
+_set_. If _set_ is NULL then all the CPUs on the system will be set, and _set_size_
+is ignored.
+
+The *tracefs_instance_set_affinity_raw()* function takes a string that holds
+a hexidecimal bitmask, where each 32 bits is separated by a comma. For a
+machine with more that 32 CPUs, to set CPUS 1-10 and CPU 40:
+
+ "100,000007fe"
+
+Where the above is a hex representation of bits 1-10 and bit 40 being set.
+
+The *tracefs_instance_get_affinity()* will retrieve the affinity in a human readable
+form.
+
+For example: "1,4,6-8"
+
+The string returned must be freed with *free*(3).
+
+The *tracefs_instance_get_affinity_set()* will set all the bits in the passed in
+cpu set (from *CPU_SET*(3)). Note it will not clear any bits that are already set
+in the set but the CPUs are not. If only the bits for the CPUs that are enabled
+should be set, a CPU_ZERO_S() should be performed on the set before calling this
+function.
+
+The *tracefs_instance_get_affinity_raw()* will simply read the instance tracing_cpumask
+and return that string. The returned string must be freed with *free*(3).
+
+RETURN VALUE
+------------
+All the set functions return 0 on success and -1 on error.
+
+The functions *tracefs_instance_get_affinity()* and *tracefs_instance_get_affinity_raw()*
+returns an allocated string that must be freed with *free*(3), or NULL on error.
+
+The function *tracefs_instance_get_affinity_set()* returns the number of CPUs that
+were found set, or -1 on error.
+
+
+ERRORS
+------
+The following errors are for all the above calls:
+
+*EFBIG* if a CPU is set that is greater than what is in the system.
+
+*EINVAL* One of the parameters was invalid.
+
+The following errors are for *tracefs_instance_set_affinity*() and *tracefs_instance_set_affinity_set*():
+
+*ENOMEM* Memory allocation error.
+
+*ENODEV* dynamic events of requested type are not configured for the running kernel.
+
+The following errors are just for *tracefs_instance_set_affinity*()
+
+*EACCES* The _cpu_str_ was modified by another thread when processing it.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <tracefs.h>
+
+int main (int argc, char **argv)
+{
+	struct trace_seq seq;
+	cpu_set_t *set;
+	size_t set_size;
+	char *c;
+	int cpu1;
+	int cpu2;
+	int i;
+
+	c = tracefs_instance_get_affinity(NULL);
+	printf("The affinity was %s\n", c);
+	free(c);
+
+	if (argc < 2) {
+		tracefs_instance_set_affinity(NULL, NULL);
+		exit(0);
+	}
+	/* Show example using a set */
+	if (argc == 2 && !strchr(argv[1],',')) {
+		cpu1 = atoi(argv[1]);
+		c = strchr(argv[1], '-');
+		if (c++)
+			cpu2 = atoi(c);
+		else
+			cpu2 = cpu1;
+		if (cpu2 < cpu1) {
+			fprintf(stderr, "Invalid CPU range\n");
+			exit(-1);
+		}
+		set = CPU_ALLOC(cpu2 + 1);
+		set_size = CPU_ALLOC_SIZE(cpu2 + 1);
+		CPU_ZERO_S(set_size, set);
+		for ( ; cpu1 <= cpu2; cpu1++)
+			CPU_SET(cpu1, set);
+		tracefs_instance_set_affinity_set(NULL, set, set_size);
+		CPU_FREE(set);
+		exit(0);
+	}
+
+	trace_seq_init(&seq);
+	for (i = 1; i < argc; i++) {
+		if (i > 1)
+			trace_seq_putc(&seq, ',');
+		trace_seq_puts(&seq, argv[i]);
+	}
+	trace_seq_terminate(&seq);
+	tracefs_instance_set_affinity(NULL, seq.buffer);
+	trace_seq_destroy(&seq);
+	exit(0);
+
+	return 0;
+}
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-instances-file-manip.txt b/Documentation/libtracefs-instances-file-manip.txt
new file mode 100644
index 0000000..8c04240
--- /dev/null
+++ b/Documentation/libtracefs-instances-file-manip.txt
@@ -0,0 +1,199 @@
+libtracefs(3)
+=============
+
+NAME
+----
+
+tracefs_instance_file_open,
+tracefs_instance_file_write, tracefs_instance_file_append, tracefs_instance_file_clear,
+tracefs_instance_file_read, tracefs_instance_file_read_number - Work with files in tracing instances.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_instance_file_open*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, int _mode_);
+int *tracefs_instance_file_write*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, const char pass:[*]_str_);
+int *tracefs_instance_file_append*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, const char pass:[*]_str_);
+int *tracefs_instance_file_clear*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_);
+char pass:[*]*tracefs_instance_file_read*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, int pass:[*]_psize_);
+int *tracefs_instance_file_read_number*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, long long int pass:[*]_res_);
+
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to work with trace files in all trace instances.
+Each of these APIs take an _instance_ argument, that can be NULL to act
+on the top level instance. Otherwise, it acts on an instance created with
+*tracefs_insance_create*(3)
+
+The *tracefs_instance_file_open()* function opens trace _file_ from given _instance_ and returns
+a file descriptor to it. The file access _mode_ can be specified, see *open*(3) for more details.
+If -1 is passed as _mode_, default O_RDWR is used.
+
+The *tracefs_instance_file_write()* function writes a string _str_ in a _file_ from
+the given _instance_, without the terminating NULL character. When opening the file, this function
+tries to truncates the size of the file to zero, which clears all previously existing settings.
+
+The *tracefs_instance_file_append()* function writes a string _str_ in a _file_ from
+the given _instance_, without the terminating NULL character.  This function is similar to
+*tracefs_instance_file_write()*, but the existing content of the is not cleared. Thus the
+new settings are appended to the existing ones (if any).
+
+The *tracefs_instance_file_clear()* function tries to truncates the size of the file to zero,
+which clears all previously existing settings. If the file has content that does not get
+cleared in this way, this will not have any effect.
+
+The *tracefs_instance_file_read()* function reads the content of a _file_ from
+the given _instance_.
+
+The *tracefs_instance_file_read_number()* function reads the content of a _file_ from
+the given _instance_ and converts it to a long long integer, which is stored in _res_.
+
+RETURN VALUE
+------------
+The *tracefs_instance_file_open()* function returns a file descriptor to the opened file. It must be
+closed with *close*(3). In case of an error, -1 is returned.
+
+The *tracefs_instance_file_write()* function returns the number of written bytes,
+or -1 in case of an error.
+
+The *tracefs_instance_file_append()* function returns the number of written bytes,
+or -1 in case of an error.
+
+The *tracefs_instance_file_clear()* function returns 0 on success, or -1 in case of an error.
+
+The *tracefs_instance_file_read()* function returns a pointer to a NULL terminated
+string, read from the file, or NULL in case of an error. The returned string must
+be freed with free().
+
+The *tracefs_instance_file_read_number()* function returns 0 if a valid integer is read from
+the file and stored in _res_ or -1 in case of an error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+
+struct tracefs_instance *inst = tracefs_instance_create("foo");
+	if (!inst) {
+		/* Error creating a new trace instance */
+		...
+	}
+
+	if (tracefs_file_exists(inst,"trace_clock")) {
+		/* The instance foo supports trace clock */
+		char *path, *clock;
+		int size;
+
+		path =  = tracefs_instance_get_file(inst, "trace_clock")
+		if (!path) {
+			/* Error getting the path to trace_clock file in instance foo */
+			...
+		}
+		...
+		tracefs_put_tracing_file(path);
+
+		clock = tracefs_instance_file_read(inst, "trace_clock", &size);
+		if (!clock) {
+			/* Failed to read trace_clock file in instance foo */
+			...
+		}
+		...
+		free(clock);
+
+		if (tracefs_instance_file_write(inst, "trace_clock", "global") != strlen("global")) {
+			/* Failed to set gloabl trace clock in instance foo */
+			...
+		}
+	} else {
+		/* The instance foo does not support trace clock */
+	}
+
+	if (tracefs_dir_exists(inst,"options")) {
+		/* The instance foo supports trace options */
+		char *path = tracefs_instance_get_file(inst, "options");
+		if (!path) {
+			/* Error getting the path to options directory in instance foo */
+			...
+		}
+
+		tracefs_put_tracing_file(path);
+	} else {
+		/* The instance foo does not support trace options */
+	}
+
+	...
+
+	if (tracefs_instance_is_new(inst))
+		tracefs_instance_destroy(inst);
+	else
+		tracefs_instance_free(inst);
+	...
+
+	long long int res;
+	if (tracefs_instance_file_read_number(NULL, "tracing_on", &res) == 0) {
+		if (res == 0) {
+			/* tracing is disabled in the top instance */
+		} else if (res == 1) {
+			/* tracing is enabled in the top instance */
+		} else {
+			/* Unknown tracing state of the top instance */
+		}
+	} else {
+		/* Failed to read integer from tracing_on file */
+	}
+
+	...
+
+	int fd;
+	fd = tracefs_instance_file_open(NULL, "tracing_on", O_WRONLY);
+	if (fd >= 0) {
+		/* Got file descriptor to the tracing_on file from the top instance for writing */
+		...
+		close(fd);
+	}
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-instances-files.txt b/Documentation/libtracefs-instances-files.txt
new file mode 100644
index 0000000..e298557
--- /dev/null
+++ b/Documentation/libtracefs-instances-files.txt
@@ -0,0 +1,173 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_file_exists, tracefs_dir_exists,
+tracefs_instance_get_file, tracefs_instance_get_dir - Work with files directories in tracing instances.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+bool *tracefs_file_exists*(struct tracefs_instance pass:[*]_instance_, char pass:[*]_name_);
+bool *tracefs_dir_exists*(struct tracefs_instance pass:[*]_instance_, char pass:[*]_name_);
+char pass:[*]*tracefs_instance_get_file*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_);
+char pass:[*]*tracefs_instance_get_dir*(struct tracefs_instance pass:[*]_instance_);
+
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to work with trace files in all trace instances.
+Each of these APIs take an _instance_ argument, that can be NULL to act
+on the top level instance. Otherwise, it acts on an instance created with
+*tracefs_insance_create*(3)
+
+The *tracefs_file_exists()* function checks if a file with _name_ exists in _instance_.
+
+The *tracefs_dir_exists()* function checks if a directory with _name_ exists in _instance_.
+
+The *tracefs_instance_get_file()* function returns the full path of the file
+with given _name_ in _instance_. Note, it does not check if the file exists in
+the instance.
+
+The *tracefs_instance_get_dir()* function  returns the full path of the directory
+with given _name_ in _instance_. Note, it does not check if the directory exists
+in the instance.
+
+RETURN VALUE
+------------
+The *tracefs_file_exists()* and *tracefs_dir_exists()* functions return true if the
+file / directory exist in the given instance or false if it does not exist.
+
+The *tracefs_instance_get_file()* and *tracefs_instance_get_dir()* functions return
+a string or NULL in case of an error. The returned string must be freed with
+*tracefs_put_tracing_file()*.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+
+struct tracefs_instance *inst = tracefs_instance_create("foo");
+	if (!inst) {
+		/* Error creating a new trace instance */
+		...
+	}
+
+	if (tracefs_file_exists(inst,"trace_clock")) {
+		/* The instance foo supports trace clock */
+		char *path, *clock;
+		int size;
+
+		path =  = tracefs_instance_get_file(inst, "trace_clock")
+		if (!path) {
+			/* Error getting the path to trace_clock file in instance foo */
+			...
+		}
+		...
+		tracefs_put_tracing_file(path);
+
+		clock = tracefs_instance_file_read(inst, "trace_clock", &size);
+		if (!clock) {
+			/* Failed to read trace_clock file in instance foo */
+			...
+		}
+		...
+		free(clock);
+
+		if (tracefs_instance_file_write(inst, "trace_clock", "global") != strlen("global")) {
+			/* Failed to set gloabl trace clock in instance foo */
+			...
+		}
+	} else {
+		/* The instance foo does not support trace clock */
+	}
+
+	if (tracefs_dir_exists(inst,"options")) {
+		/* The instance foo supports trace options */
+		char *path = tracefs_instance_get_file(inst, "options");
+		if (!path) {
+			/* Error getting the path to options directory in instance foo */
+			...
+		}
+
+		tracefs_put_tracing_file(path);
+	} else {
+		/* The instance foo does not support trace options */
+	}
+
+	...
+
+	if (tracefs_instance_is_new(inst))
+		tracefs_instance_destroy(inst);
+	else
+		tracefs_instance_free(inst);
+	...
+
+	long long int res;
+	if (tracefs_instance_file_read_number(NULL, "tracing_on", &res) == 0) {
+		if (res == 0) {
+			/* tracing is disabled in the top instance */
+		} else if (res == 1) {
+			/* tracing is enabled in the top instance */
+		} else {
+			/* Unknown tracing state of the top instance */
+		}
+	} else {
+		/* Failed to read integer from tracing_on file */
+	}
+
+	...
+
+	int fd;
+	fd = tracefs_instance_file_open(NULL, "tracing_on", O_WRONLY);
+	if (fd >= 0) {
+		/* Got file descriptor to the tracing_on file from the top instance for writing */
+		...
+		close(fd);
+	}
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-instances-manage.txt b/Documentation/libtracefs-instances-manage.txt
new file mode 100644
index 0000000..c03a272
--- /dev/null
+++ b/Documentation/libtracefs-instances-manage.txt
@@ -0,0 +1,150 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_instance_create, tracefs_instance_destroy, tracefs_instance_alloc, tracefs_instance_free,
+tracefs_instance_is_new, tracefs_instances - Manage trace instances.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_instance pass:[*]*tracefs_instance_create*(const char pass:[*]_name_);
+int *tracefs_instance_destroy*(struct tracefs_instance pass:[*]_instance_);
+struct tracefs_instance pass:[*]*tracefs_instance_alloc*(const char pass:[*]_tracing_dir_, const char pass:[*]_name_);
+void *tracefs_instance_free*(struct tracefs_instance pass:[*]_instance_);
+bool *tracefs_instance_is_new*(struct tracefs_instance pass:[*]_instance_);
+char pass:[**]*tracefs_instances*(const char pass:[*]_regex_);
+
+--
+
+DESCRIPTION
+-----------
+This set of functions can be used to manage trace instances. A trace
+instance is a sub buffer used by the Linux tracing system. Given a unique
+name, the events enabled in an instance do not affect the main tracing
+system, nor other instances, as events enabled in the main tracing system
+or other instances do not affect the given instance.
+
+The *tracefs_instance_create()* function allocates and initializes a new
+tracefs_instance structure and returns it. If the instance with _name_ does
+not yet exist in the system, it will be created. The _name_ could be NULL,
+then the new tracefs_instance structure is initialized for the top instance.
+Note that the top instance cannot be created in the system, if it does not
+exist.
+
+The *tracefs_instance_destroy()* removes the instance from the system, but
+does not free the structure. *tracefs_instance_free()* must still be called
+on _instance_.
+
+The tracefs_instance_alloc()* function allocates a new tracefs_instance structure
+for existing trace instance. If the instance does not exist in the system, the function
+fails. The _tracing_dir_ parameter points to the system trace directory. It can be
+NULL, then default system trace directory is used. This parameter is useful to allocate
+instances to trace directories, copied from another machine. The _name_ is the name of
+the instance, or NULL for the top instance in the given _tracing_dir_.
+
+The *tracefs_instance_free()* function frees the tracefs_instance structure,
+without removing the trace instance from the system.
+
+The *tracefs_instance_is_new()* function checks if the given _instance_ is
+newly created by *tracefs_instance_create()*, or it has been in the system
+before that.
+
+The *tracefs_instances*() function returns a list of instances that exist in
+the system that match the regular expression _regex_. If _regex_ is NULL, then
+it will match all instances that exist. The returned list must be freed with
+*tracefs_list_free*(3). Note, if no instances are found an empty list is returned
+and that too needs to be free with *tracefs_list_free*(3).
+
+RETURN VALUE
+------------
+The *tracefs_instance_create()* and *tracefs_instance_alloc()* functions return a pointer to
+a newly allocated tracefs_instance structure. It must be freed with *tracefs_instance_free()*.
+
+The *tracefs_instance_destroy()* function returns 0 if it succeeds to remove
+the instance, otherwise it returns -1 if the instance does not exist or it
+fails to remove it.
+
+The *tracefs_instance_is_new()* function returns true if the
+*tracefs_instance_create()* that allocated _instance_ also created the
+trace instance in the system, or false if the trace instance already
+existed in the system when _instance_ was allocated by
+*tracefs_instance_create()* or *tracefs_instance_alloc()*.
+
+The *tracefs_instances()* returns a list of instance names that exist on the system.
+The list must be freed with *tracefs_list_free*(3). An empty list is returned if
+no instance exists that matches _regex_, and this needs to be freed with
+*tracefs_list_free*(3) as well. NULL is returned on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+
+struct tracefs_instance *inst = tracefs_instance_create("foo");
+	if (!inst) {
+		/* Error creating a new trace instance */
+		...
+	}
+
+	...
+
+	if (tracefs_instance_is_new(inst))
+		tracefs_instance_destroy(inst);
+	tracefs_instance_free(inst);
+...
+
+struct tracefs_instance *inst = tracefs_instance_alloc(NULL, "bar");
+	if (!inst) {
+		/* Error allocating 'bar' trace instance */
+		...
+	}
+
+	...
+
+	tracefs_instance_free(inst);
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-instances-utils.txt b/Documentation/libtracefs-instances-utils.txt
new file mode 100644
index 0000000..bc8c9a7
--- /dev/null
+++ b/Documentation/libtracefs-instances-utils.txt
@@ -0,0 +1,141 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_instance_get_name, tracefs_instance_get_trace_dir, tracefs_instances_walk, tracefs_instance_exists,
+tracefs_instance_get_buffer_size, tracefs_instance_set_buffer_size - Helper functions for working with tracing instances.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+const char pass:[*]*tracefs_instance_get_name*(struct tracefs_instance pass:[*]_instance_);
+const char pass:[*]*tracefs_instance_get_trace_dir*(struct tracefs_instance pass:[*]_instance_);
+int *tracefs_instances_walk*(int (pass:[*]_callback_)(const char pass:[*], void pass:[*]), void pass:[*]_context)_;
+bool *tracefs_instance_exists*(const char pass:[*]_name_);
+size_t *tracefs_instance_get_buffer_size*(struct tracefs_instance pass:[*]_instance_, int _cpu_);
+int *tracefs_instance_set_buffer_size*(struct tracefs_instance pass:[*]_instance_, size_t _size_, int _cpu_);
+--
+
+DESCRIPTION
+-----------
+Helper functions for working with trace instances.
+
+The *tracefs_instance_get_name()* function returns the name of the given _instance_.
+Note that the top instance has no name, the function returns NULL for it.
+
+The *tracefs_instance_get_trace_dir()* function returns the tracing directory, where
+the given _instance_ is configured.
+
+The *tracefs_instances_walk()* function walks through all configured tracing
+instances in the system and calls _callback_ for each one of them. The _context_
+argument is passed to the _callback_, together with the instance name. If the
+_callback_ returns non-zero, the iteration stops. Note, the _callback_ is not
+called for the top top instance.
+
+The *tracefs_instance_exists()* function checks if an instance with the given
+_name_ exists in the system.
+
+The *tracefs_instace_get_buffer_size()* returns the size of the ring buffer. If _cpu_
+is negative, it returns the total size of all the per CPU ring buffers, otherwise
+it returns the size of the per CPU ring buffer for _cpu_.
+
+The *tracefs_instance_set_buffer_size()* function sets the size of the ring buffer.
+If _cpu_ is negative, then it sets all the per CPU ring buffers to _size_ (note
+the total size is the number of CPUs * _size_). If _cpu_ is specified, then it only
+sets the size of the per CPU ring buffer.
+
+RETURN VALUE
+------------
+The *tracefs_instance_get_name()* returns a string or NULL in case of the top
+instance. The returned string must _not_ be freed.
+
+The *tracefs_instance_get_trace_dir()* returns a string or NULL in case of an error.
+The returned string must _not_ be freed.
+
+The *tracefs_instances_walk()* function returns 0, if all instances were iterated, 1
+if the iteration was stopped by the _callback_, or -1 in case of an error.
+
+The *tracefs_instance_exists()* returns true if an instance with the given _name_
+exists in the system or false otherwise.
+
+The *tracefs_instance_get_buffer_size()* returns the size of the ring buffer depending on
+the _cpu_ value passed in, or -1 on error.
+
+The *tracefs_instance_set_buffer_size()* returns zero on success and -1 on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+
+struct tracefs_instance *inst;
+....
+char *name = tracefs_instance_get_name(inst);
+	if(name) {
+		/* Got name of the instance */
+	}
+char *dir = tracefs_instance_get_trace_dir(inst);
+	if(dir) {
+		/* Got tracing directory of the instance */
+	}
+...
+static int instance_walk(char *name, void *context)
+{
+	/* Got instance with name */
+	return 0;
+}
+...
+	if (tracefs_instances_walk(instance_walk, NULL) < 0) {
+		/* Error walking through the instances */
+	}
+...
+	if (tracefs_instance_exists("foo")) {
+		/* There is instance with name foo in the system */
+	} else {
+		/* There is no instance with name foo in the system */
+	}
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-iterator.txt b/Documentation/libtracefs-iterator.txt
new file mode 100644
index 0000000..b971bd0
--- /dev/null
+++ b/Documentation/libtracefs-iterator.txt
@@ -0,0 +1,229 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_iterate_raw_events, tracefs_iterate_stop, tracefs_follow_event, tracefs_follow_missed_events - Iterate over events in the ring buffer
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
+				 cpu_set_t pass:[*]_cpus_, int _cpu_size_,
+				 int (pass:[*]_callback_)(struct tep_event pass:[*], struct tep_record pass:[*], int, void pass:[*]),
+				 void pass:[*]_callback_context_);
+void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_);
+
+int *tracefs_follow_event*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
+			  const char pass:[*]_system_, const char pass:[*]_event_name_,
+			  int (pass:[*]_callback_)(struct tep_event pass:[*],
+					  struct tep_record pass:[*],
+					  int, void pass:[*]),
+			  void pass:[*]_callback_data_);
+int *tracefs_follow_missed_events*(struct tracefs_instance pass:[*]_instance_,
+			  int (pass:[*]_callback_)(struct tep_event pass:[*],
+					  struct tep_record pass:[*],
+					  int, void pass:[*]),
+			  void pass:[*]_callback_data_);
+--
+
+DESCRIPTION
+-----------
+Trace iterator over raw events.
+
+The *tracefs_iterate_raw_events()* function will read the tracefs raw
+data buffers and call the specified _callback_ function for every event it
+encounters. Events are iterated in sorted order: oldest first. An initialized
+_tep_ handler is required (See *tracefs_local_events*(3)). If _instance_ is
+NULL, then the toplevel tracefs buffer is used, otherwise the buffer for
+the corresponding _instance_ is read. To filter only on a subset of CPUs,
+_cpus_ and _cpu_size_ may be set to only call _callback_ with events that
+occurred on the CPUs specified, otherwise if _cpus_ is NULL then the _callback_
+function will be called for all events, and _cpu_size_ is ignored. The
+_callback_ function will be called with the following parameters: A
+pointer to a struct tep_event that corresponds to the type of event the
+record is; The record representing the event; The CPU that the event
+occurred on; and a pointer to user specified _callback_context_. If the _callback_
+returns non-zero, the iteration stops.
+
+Use *tracefs_iterate_stop()* to force a executing *tracefs_iterate_raw_events()*
+to halt. This can be called from either a callback that is called by
+the iterator (even though a return of non-zero will stop it), or from another
+thread.
+
+The *tracefs_follow_event()* is used with *tracefs_iterate_raw_events()* but
+intead of the callback being called for every event, it is only called for the
+specified _system_ / _event_name_ given to the function. The _callback_ is the
+same as for *tracefs_iterate_raw_events()*, and the passed in _callback_context_
+will be passed to the _callback_ as well. Note, if it returns something other
+than 0, it will stop the loop before the _callback_ of *tracefs_iterate_raw_events()*
+is called.
+
+The *tracefs_follow_missed_events()* will call the _callback_ when missed
+events are detected. It will set the _record_ parameter of the callback to the
+record that came after the missed events and _event_ will be of the type of
+event _record_ is. _cpu_ will be set to the CPU that missed the events, and
+_callback_data_ will be the content that was passed in to the function.
+
+RETURN VALUE
+------------
+The *tracefs_iterate_raw_events()* function returns -1 in case of an error or
+0 otherwise.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <unistd.h>
+#include <tracefs.h>
+#include <stdbool.h>
+#include <signal.h>
+
+struct my_struct {
+	bool		stopped;
+};
+
+#define MAX_COUNT 500000
+static int counter;
+
+static int callback(struct tep_event *event, struct tep_record *record,
+		    int cpu, void *data)
+{
+	struct my_struct *my_data = data;
+	static struct trace_seq seq;
+
+	if (counter++ > MAX_COUNT) {
+		my_data->stopped = true;
+		return 1;
+	}
+
+	if (!seq.buffer)
+		trace_seq_init(&seq);
+
+	tep_print_event(event->tep, &seq, record, "%16s-%-5d [%03d] %6.1000d %s: %s\n",
+			TEP_PRINT_COMM, TEP_PRINT_PID, TEP_PRINT_CPU,
+			TEP_PRINT_TIME, TEP_PRINT_NAME, TEP_PRINT_INFO);
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+	trace_seq_reset(&seq);
+	return 0;
+}
+
+static int sched_callback(struct tep_event *event, struct tep_record *record,
+			  int cpu, void *data)
+{
+	static struct tep_format_field *prev_pid;
+	static struct tep_format_field *next_pid;
+	unsigned long long pid;
+	int this_pid = *(int *)data;
+
+	if (!prev_pid) {
+		prev_pid = tep_find_field(event, "prev_pid");
+		next_pid = tep_find_field(event, "next_pid");
+		if (!prev_pid || !next_pid) {
+			fprintf(stderr, "No pid fields??\n");
+			return -1;
+		}
+	}
+
+	tep_read_number_field(prev_pid, record->data, &pid);
+	if (pid == this_pid)
+		printf("WE ARE LEAVING!\n");
+	tep_read_number_field(next_pid, record->data, &pid);
+	if (pid == this_pid)
+		printf("WE ARE ARRIVING!\n");
+	return 0;
+}
+
+static int missed_callback(struct tep_event *event, struct tep_record *record,
+			   int cpu, void *data)
+{
+	printf("OOPS! cpu %d dropped ", cpu);
+	if (record->missed_events > 0)
+		printf("%lld ", record->missed_events);
+	printf("events\n");
+	return 0;
+}
+
+static struct tracefs_instance *instance;
+static struct my_struct my_data;
+
+static void sig(int s)
+{
+	tracefs_iterate_stop(instance);
+	my_data.stopped = true;
+}
+
+int main (int argc, char **argv, char **env)
+{
+	struct tep_handle *tep;
+	int this_pid = getpid();
+
+	instance = tracefs_instance_create("my-buffer");
+	if (!instance)
+		return -1;
+
+	signal(SIGINT, sig);
+
+	tracefs_event_enable(instance, NULL, NULL);
+	sleep(1);
+	tracefs_event_disable(instance, NULL, NULL);
+	tep = tracefs_local_events(NULL);
+	tep_load_plugins(tep);
+	tracefs_follow_missed_events(instance, missed_callback, NULL);
+	tracefs_follow_event(tep, instance, "sched", "sched_switch", sched_callback, &this_pid);
+	tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, &my_data);
+	tracefs_instance_destroy(instance);
+
+	if (my_data.stopped) {
+		if (counter > MAX_COUNT)
+			printf("Finished max count\n");
+		else
+			printf("Finished via signal\n");
+	}
+
+	return 0;
+}
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-kprobes.txt b/Documentation/libtracefs-kprobes.txt
new file mode 100644
index 0000000..593ef9e
--- /dev/null
+++ b/Documentation/libtracefs-kprobes.txt
@@ -0,0 +1,273 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_kprobe_alloc, tracefs_kretprobe_alloc, tracefs_kprobe_raw, tracefs_kretprobe_raw -
+Allocate, get, and create kprobes
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_dynevent pass:[*]
+*tracefs_kprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+		       const char pass:[*]_addr_, const char pass:[*]_format_);
+struct tracefs_dynevent pass:[*]
+*tracefs_kretprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+			  const char pass:[*]_addr_, const char pass:[*]_format_, unsigned int _max_);
+int *tracefs_kprobe_raw*(const char pass:[*]_system_, const char pass:[*]_event_,
+			 const char pass:[*]_addr_, const char pass:[*]_format_);
+int *tracefs_kretprobe_raw*(const char pass:[*]_system_, const char pass:[*]_event_,
+			    const char pass:[*]_addr_, const char pass:[*]_format_);
+--
+
+DESCRIPTION
+-----------
+*tracefs_kprobe_alloc*() allocates a new kprobe context. The kbrobe is not configured in the system.
+The new kprobe will be in the _system_ group (or kprobes if _system_ is NULL) and have the name of
+_event_ (or _addr_ if _event_ is NULL). The kprobe will be inserted to _addr_ (function name, with
+or without offset, or a address), and the _format_ will define the format of the kprobe. See the
+Linux documentation file under: Documentation/trace/kprobetrace.rst
+
+*tracefs_kretprobe_alloc*() is the same as *tracefs_kprobe_alloc*, but allocates context for
+kretprobe. It has one additional parameter, which is optional, _max_ - maxactive count.
+See description of kretprobes in the Documentation/trace/kprobetrace.rst file.
+
+*tracefs_kprobe_raw*() will create a kprobe event. If _system_ is NULL, then
+the default "kprobes" is used for the group (event system). Otherwise if _system_
+is specified then the kprobe will be created under the group by that name. The
+_event_ is the name of the kprobe event to create. The _addr_ can be a function,
+a function and offset, or a kernel address. This is where the location of the
+kprobe will be inserted in the kernel. The _format_ is the kprobe format as
+specified as FETCHARGS in the Linux kernel source in the Documentation/trace/kprobetrace.rst
+document.
+
+*tracefs_kretprobe_raw*() is the same as *tracefs_kprobe_raw()*, except that it
+creates a kretprobe instead of a kprobe. The difference is also described
+in the Linux kernel source in the Documentation/trace/kprobetrace.rst file.
+
+RETURN VALUE
+------------
+
+*tracefs_kprobe_raw*() and *tracefs_kretprobe_raw*() return 0 on success, or -1 on error.
+If a parsing error occurs on *tracefs_kprobe_raw*() or *tracefs_kretprobe_raw*() then
+*tracefs_error_last*(3) may be used to retrieve the error message explaining the parsing issue.
+
+The *tracefs_kprobe_alloc*() and *tracefs_kretprobe_alloc*() APIs return a pointer to an allocated
+tracefs_dynevent structure, describing the probe. This pointer must be freed by
+*tracefs_dynevent_free*(3). Note, this only allocates a descriptor representing the kprobe. It does
+not modify the running system.
+
+ERRORS
+------
+The following errors are for all the above calls:
+
+*EPERM* Not run as root user
+
+*ENODEV* Kprobe events are not configured for the running kernel.
+
+*ENOMEM* Memory allocation error.
+
+*tracefs_kprobe_raw*(), *tracefs_kretprobe_raw*(), *tracefs_kprobe_alloc*(),
+and *tracefs_kretprobe_alloc*() can fail with the following errors:
+
+*EBADMSG* if _addr_ is NULL.
+
+*EINVAL*  Most likely a parsing error occurred (use *tracefs_error_last*(3) to possibly
+          see what that error was).
+
+Other errors may also happen caused by internal system calls.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <tracefs.h>
+
+static struct tep_event *open_event;
+static struct tep_format_field *file_field;
+
+static struct tep_event *openret_event;
+static struct tep_format_field *ret_field;
+
+static int callback(struct tep_event *event, struct tep_record *record,
+		    int cpu, void *data)
+{
+	struct trace_seq seq;
+
+	trace_seq_init(&seq);
+	tep_print_event(event->tep, &seq, record, "%d-%s: ", TEP_PRINT_PID, TEP_PRINT_COMM);
+
+	if (event->id == open_event->id) {
+		trace_seq_puts(&seq, "open file='");
+		tep_print_field(&seq, record->data, file_field);
+		trace_seq_puts(&seq, "'\n");
+	} else if (event->id == openret_event->id) {
+		unsigned long long ret;
+		tep_read_number_field(ret_field, record->data, &ret);
+		trace_seq_printf(&seq, "open ret=%lld\n", ret);
+	} else {
+		goto out;
+	}
+
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+out:
+	trace_seq_destroy(&seq);
+
+	return 0;
+}
+
+static pid_t run_exec(char **argv, char **env)
+{
+	pid_t pid;
+
+	pid = fork();
+	if (pid)
+		return pid;
+
+	execve(argv[0], argv, env);
+	perror("exec");
+	exit(-1);
+}
+
+const char *mykprobe = "my_kprobes";
+
+enum kprobe_type {
+	KPROBE,
+	KRETPROBE,
+};
+
+static void __kprobe_create(enum kprobe_type type, const char *event,
+			    const char *addr, const char *fmt)
+{
+	char *err;
+	int r;
+
+	if (type == KPROBE)
+		r = tracefs_kprobe_raw(mykprobe, event, addr, fmt);
+	else
+		r = tracefs_kretprobe_raw(mykprobe, event, addr, fmt);
+	if (r < 0) {
+		err = tracefs_error_last(NULL);
+		perror("Failed to create kprobe:");
+		if (err && strlen(err))
+			fprintf(stderr, "%s\n", err);
+	}
+}
+
+static void kprobe_create(const char *event, const char *addr,
+			  const char *fmt)
+{
+	__kprobe_create(KPROBE, event, addr, fmt);
+}
+
+static void kretprobe_create(const char *event, const char *addr,
+			     const char *fmt)
+{
+	__kprobe_create(KRETPROBE, event, addr, fmt);
+}
+
+int main (int argc, char **argv, char **env)
+{
+	struct tracefs_instance *instance;
+	struct tep_handle *tep;
+	const char *sysnames[] = { mykprobe, NULL };
+	pid_t pid;
+
+	if (argc < 2) {
+		printf("usage: %s command\n", argv[0]);
+		exit(-1);
+	}
+
+	instance = tracefs_instance_create("exec_open");
+	if (!instance) {
+		perror("creating instance");
+		exit(-1);
+	}
+
+	tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, true);
+
+	kprobe_create("open", "do_sys_openat2",
+		      "file=+0($arg2):ustring flags=+0($arg3):x64 mode=+8($arg3):x64\n");
+
+	kretprobe_create("openret", "do_sys_openat2", "ret=%ax");
+
+	tep = tracefs_local_events_system(NULL, sysnames);
+	if (!tep) {
+		perror("reading events");
+		exit(-1);
+	}
+	open_event = tep_find_event_by_name(tep, mykprobe, "open");
+	file_field = tep_find_field(open_event, "file");
+
+	openret_event = tep_find_event_by_name(tep, mykprobe, "openret");
+	ret_field = tep_find_field(openret_event, "ret");
+
+	tracefs_event_enable(instance, mykprobe, NULL);
+	pid = run_exec(&argv[1], env);
+
+	/* Let the child start to run */
+	sched_yield();
+
+	do {
+		tracefs_load_cmdlines(NULL, tep);
+		tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, NULL);
+	} while (waitpid(pid, NULL, WNOHANG) != pid);
+
+	/* Will disable the events */
+	tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, true);
+	tracefs_instance_destroy(instance);
+	tep_free(tep);
+
+	return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2021 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-log.txt b/Documentation/libtracefs-log.txt
new file mode 100644
index 0000000..4b72df1
--- /dev/null
+++ b/Documentation/libtracefs-log.txt
@@ -0,0 +1,76 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_set_loglevel - Set log level of the library
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_set_loglevel*(enum tep_loglevel _level_);
+--
+
+DESCRIPTION
+-----------
+The *tracefs_set_loglevel()* function sets the level of the library logs that will be printed on
+the console. See _libtraceevent(3)_ for detailed description 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
+also propagated to the libtraceevent.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+
+tracefs_set_loglevel(TEP_LOG_ALL);
+...
+/* call libtracefs or libtraceevent APIs and observe any logs they produce */
+...
+tracefs_set_loglevel(TEP_LOG_CRITICAL);
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2021 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-marker.txt b/Documentation/libtracefs-marker.txt
new file mode 100644
index 0000000..adc5419
--- /dev/null
+++ b/Documentation/libtracefs-marker.txt
@@ -0,0 +1,116 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_print_init, tracefs_print_close, tracefs_printf, tracefs_vprintf -
+Open, close and write formated strings in the trace buffer.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_print_init*(struct tracefs_instance pass:[*]_instance_);
+int *tracefs_printf*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_fmt_, _..._);
+int *tracefs_vprintf*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_fmt_, va_list _ap_);
+void *tracefs_print_close*(struct tracefs_instance pass:[*]_instance_);
+
+--
+
+DESCRIPTION
+-----------
+Set of functions to write formated strings in the trace buffer.
+See Documentation/trace/ftrace.rst from the Linux kernel tree for more information about writing
+data from user space in the trace buffer. All these APIs have _instance_ as a first argument. If
+NULL is passed as _instance_, the top trace instance is used.
+
+The *tracefs_print_init()* function initializes the library for writing into the trace buffer of
+the selected _instance_. It is not mandatory to call this API before writing strings, any of
+the printf APIs will call it automatically, if the library is not yet initialized. But calling
+*tracefs_print_init()* in advance will speed up the writing.
+
+The *tracefs_printf()* function writes a formatted string in the trace buffer of the selected
+_instance_. The _fmt_ argument is a string in printf format, followed by variable arguments _..._.
+
+The *tracefs_vprintf()* function writes a formatted string in the trace buffer of the selected
+_instance_. The _fmt_ argument is a string in printf format, followed by list _ap_ of arguments.
+
+The *tracefs_print_close()* function closes the resources, used by the library for writing in
+the trace buffer of the selected instance.
+
+RETURN VALUE
+------------
+The *tracefs_print_init()*, *tracefs_printf()*, and *tracefs_vprintf()* functions return 0 if
+the operation is successful, or -1 in case of an error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+
+if (tracefs_print_init(NULL) < 0) {
+ /* Failed to initialize the library for writing in the trace buffer of the top trace instance */
+}
+
+void foo_print(char *format, ...)
+{
+	va_list ap;
+	va_start(ap, format);
+	if (tracefs_vprintf(NULL, format, ap) < 0) {
+		/* Failed to print in the trace buffer */
+	}
+	va_end(ap);
+}
+
+void foo_print_string(char *message)
+{
+	if (tracefs_printf(NULL, "Message from user space: %s", message) < 0) {
+		/* Failed to print in the trace buffer */
+	}
+}
+
+tracefs_print_close();
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+Documentation/trace/ftrace.rst from the Linux kernel tree
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2021 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-marker_raw.txt b/Documentation/libtracefs-marker_raw.txt
new file mode 100644
index 0000000..9682f20
--- /dev/null
+++ b/Documentation/libtracefs-marker_raw.txt
@@ -0,0 +1,102 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_binary_init, tracefs_binary_close, tracefs_binary_write -
+Open, close and write binary data in the trace buffer.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_binary_init*(struct tracefs_instance pass:[*]_instance_);
+int *tracefs_binary_write*(struct tracefs_instance pass:[*]_instance_, void pass:[*]_data_, int _len_);
+void *tracefs_binary_close*(struct tracefs_instance pass:[*]_instance_);
+
+--
+
+DESCRIPTION
+-----------
+Set of functions to write binary data in the trace buffer.
+See Documentation/trace/ftrace.rst from the Linux kernel tree for more information about writing
+data from user space in the trace buffer. All these APIs have _instance_ as a first argument. If
+NULL is passed as _instance_, the top trace instance is used.
+
+The *tracefs_binary_init()* function initializes the library for writing into the trace buffer of
+the selected _instance_. It is not mandatory to call this API before writing data, the
+*tracefs_binary_write()* will call it automatically, if the library is not yet initialized.
+But calling *tracefs_binary_init()* in advance will speed up the writing.
+
+The *tracefs_binary_write()* function writes a binary data in the trace buffer of the selected
+_instance_. The _data_ points to the data with length _len_, that is going to be written in
+the trace buffer.
+
+The *tracefs_binary_close()* function closes the resources, used by the library for writing in
+the trace buffer of the selected instance.
+
+RETURN VALUE
+------------
+The *tracefs_binary_init()*, and *tracefs_binary_write()* functions return 0 if the operation is
+successful, or -1 in case of an error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+
+if (tracefs_binary_init(NULL) < 0) {
+ /* Failed to initialize the library for writing in the trace buffer of the top trace instance */
+}
+
+unsigned int data = 0xdeadbeef;
+
+	if (tracefs_binary_write(NULL, &data, sizeof(data)) < 0) {
+		/* Failed to write in the trace buffer */
+	}
+
+tracefs_binary_close();
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+Documentation/trace/ftrace.rst from the Linux kernel tree
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2021 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-option-get.txt b/Documentation/libtracefs-option-get.txt
new file mode 100644
index 0000000..8a688a7
--- /dev/null
+++ b/Documentation/libtracefs-option-get.txt
@@ -0,0 +1,141 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_options_get_supported, tracefs_option_is_supported, tracefs_options_get_enabled,
+tracefs_option_is_enabled, tracefs_option_mask_is_set, tracefs_option_id
+- Get and check ftrace options.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+const struct tracefs_options_mask pass:[*]*tracefs_options_get_supported*(struct tracefs_instance pass:[*]_instance_);
+bool *tracefs_option_is_supported*(struct tracefs_instance pass:[*]_instance_, enum tracefs_option_id _id_);
+const struct tracefs_options_mask pass:[*]*tracefs_options_get_enabled*(struct tracefs_instance pass:[*]_instance_);
+bool *tracefs_option_is_enabled*(struct tracefs_instance pass:[*]_instance_, enum tracefs_option_id _id_);
+bool *tracefs_option_mask_is_set*(const struct tracefs_options_mask *options, enum tracefs_option_id id);
+enum tracefs_option_id *tracefs_option_id*(const char pass:[*]_name_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to get and check current ftrace options. Supported ftrace options may
+depend on the kernel version and the kernel configuration.
+
+The *tracefs_options_get_supported()* function gets all ftrace options supported by the system in
+the given _instance_. If _instance_ is NULL, supported options of the top trace instance are
+returned. The set of supported options is the same in all created trace instances, but may be different
+than the top trace instance.
+
+The *tracefs_option_is_supported()/ function checks if the option with given _id_ is supported by
+the system in the given _instance_. If _instance_ is NULL, the top trace instance is used. If an
+option is supported at the top trace instance, it it may not be supported in a created trace instance.
+
+The *tracefs_options_get_enabled()* function gets all ftrace options, currently enabled in
+the given _instance_. If _instance_ is NULL, enabled options of the top trace instance are returned.
+
+The *tracefs_option_is_enabled()* function checks if the option with given _id_ is enabled in the
+given _instance_. If _instance_ is NULL, the top trace instance is used.
+
+The *tracefs_option_mask_is_set()* function checks if the bit, corresponding to the option with _id_ is
+set in the _options_ bitmask returned from *tracefs_option_get_enabled()* and *tracefs_option_is_supported()*.
+
+The *tracefs_option_id()* converts an option _name_ into its corresponding id, if it is found.
+This allows to find the option _id_ to use in the other functions if only the _name_ is known.
+
+RETURN VALUE
+------------
+The *tracefs_options_get_supported()* and *tracefs_options_get_enabled()* functions, on success,
+return a pointer to the bitmask within the instance, or a global bitmask for the top level,
+or NULL in case of an error. As the returned bitmask is part of the instance structure (or a
+global variable) and must not be freed or modified.
+
+The *tracefs_option_is_supported()* and *tracefs_option_is_enabled()* functions return true if the
+option in supported / enabled, or false otherwise.
+
+The *tracefs_option_mask_is_set()* returns true if the corresponding option is set in the mask
+or false otherwise.
+
+The *tracefs_option_id()* returns the corresponding id defined by *tracefs_options*(3) from
+the given _name_. If the _name_ can not be found, then TRACEFS_OPTION_INVALID is returned.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+...
+const struct tracefs_options_mask *options;
+...
+options = tracefs_options_get_supported(NULL);
+if (!options) {
+	/* Failed to get supported options */
+} else {
+	...
+}
+...
+options = tracefs_options_get_enabled(NULL);
+if (!options) {
+	/* Failed to get options, enabled in the top instance */
+} else {
+	...
+}
+if (tracefs_options_mask_is_set(options, TRACEFS_OPTION_LATENCY_FORMAT)) {
+	...
+}
+...
+
+if (tracefs_option_is_supported(NULL, TRACEFS_OPTION_LATENCY_FORMAT)) {
+	/* Latency format option is supprted */
+}
+
+...
+
+if (tracefs_option_is_enabled(NULL, TRACEFS_OPTION_STACKTRACE)) {
+	/* Stacktrace option is enabled in the top instance */
+}
+
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-option-misc.txt b/Documentation/libtracefs-option-misc.txt
new file mode 100644
index 0000000..c690bfd
--- /dev/null
+++ b/Documentation/libtracefs-option-misc.txt
@@ -0,0 +1,100 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_option_enable, tracefs_option_disable, tracefs_option_name -
+Various trace option functions.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_option_enable*(struct tracefs_instance pass:[*]_instance_, enum tracefs_option_id _id_);
+int *tracefs_option_disable*(struct tracefs_instance pass:[*]_instance_, enum tracefs_option_id _id_);
+const char pass:[*]*tracefs_option_name*(enum tracefs_option_id _id_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to enable and disable ftrace options and to get the name of an option.
+
+The *tracefs_option_enable()* function enables the option with _id_ in the given _instance_. If
+_instance_ is NULL, the option is enabled in the top trace instance.
+
+The *tracefs_option_disable()* function disables the option with _id_ in the given _instance_. If
+_instance_ is NULL, the option is disabled in the top trace instance.
+
+The *tracefs_option_name()* function returns a string, representing the option with _id_. The string
+must *not* be freed.
+
+
+RETURN VALUE
+------------
+The *tracefs_option_enable()* and *tracefs_option_disable()* functions return 0 if the state of the
+option is set successfully, or -1 in case of an error.
+
+The *tracefs_option_name()* function returns string with option name, or "unknown" in case of an
+error. The returned string must *not* be freed.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+...
+if (tracefs_option_enable(NULL, TRACEFS_OPTION_ANNOTATE)) {
+	/* Failed to enable annotate option in top trace instance */
+}
+...
+if (tracefs_option_disable(NULL, TRACEFS_OPTION_CONTEXT_INFO)) {
+	/* Failed to disable context info option in top trace instance */
+}
+...
+char *name = tracefs_option_name(TRACEFS_OPTION_FUNC_STACKTRACE);
+if (strcmp(name, "unknown")) {
+	/* Cannot get the name of the option */
+}
+
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-options.txt b/Documentation/libtracefs-options.txt
new file mode 100644
index 0000000..2c720f2
--- /dev/null
+++ b/Documentation/libtracefs-options.txt
@@ -0,0 +1,159 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_options - ftrace options, that can be controlled using tracefs library.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+enum tracefs_option_id {
+	*TRACEFS_OPTION_INVALID*,
+	*TRACEFS_OPTION_ANNOTATE*,
+	*TRACEFS_OPTION_BIN*,
+	*TRACEFS_OPTION_BLK_CGNAME*,
+	*TRACEFS_OPTION_BLK_CGROUP*,
+	*TRACEFS_OPTION_BLK_CLASSIC*,
+	*TRACEFS_OPTION_BLOCK*,
+	*TRACEFS_OPTION_CONTEXT_INFO*,
+	*TRACEFS_OPTION_DISABLE_ON_FREE*,
+	*TRACEFS_OPTION_DISPLAY_GRAPH*,
+	*TRACEFS_OPTION_EVENT_FORK*,
+	*TRACEFS_OPTION_FGRAPH_ABSTIME*,
+	*TRACEFS_OPTION_FGRAPH_CPU*,
+	*TRACEFS_OPTION_FGRAPH_DURATION*,
+	*TRACEFS_OPTION_FGRAPH_IRQS*,
+	*TRACEFS_OPTION_FGRAPH_OVERHEAD*,
+	*TRACEFS_OPTION_FGRAPH_OVERRUN*,
+	*TRACEFS_OPTION_FGRAPH_PROC*,
+	*TRACEFS_OPTION_FGRAPH_TAIL*,
+	*TRACEFS_OPTION_FUNC_STACKTRACE*,
+	*TRACEFS_OPTION_FUNCTION_FORK*,
+	*TRACEFS_OPTION_FUNCTION_TRACE*,
+	*TRACEFS_OPTION_GRAPH_TIME*,
+	*TRACEFS_OPTION_HEX*,
+	*TRACEFS_OPTION_IRQ_INFO*,
+	*TRACEFS_OPTION_LATENCY_FORMAT*,
+	*TRACEFS_OPTION_MARKERS*,
+	*TRACEFS_OPTION_OVERWRITE*,
+	*TRACEFS_OPTION_PAUSE_ON_TRACE*,
+	*TRACEFS_OPTION_PRINTK_MSG_ONLY*,
+	*TRACEFS_OPTION_PRINT_PARENT*,
+	*TRACEFS_OPTION_RAW*,
+	*TRACEFS_OPTION_RECORD_CMD*,
+	*TRACEFS_OPTION_RECORD_TGID*,
+	*TRACEFS_OPTION_SLEEP_TIME*,
+	*TRACEFS_OPTION_STACKTRACE*,
+	*TRACEFS_OPTION_SYM_ADDR*,
+	*TRACEFS_OPTION_SYM_OFFSET*,
+	*TRACEFS_OPTION_SYM_USEROBJ*,
+	*TRACEFS_OPTION_TRACE_PRINTK*,
+	*TRACEFS_OPTION_USERSTACKTRACE*,
+	*TRACEFS_OPTION_VERBOSE*,
+};
+--
+
+DESCRIPTION
+-----------
+This enum contains all ftrace options, that can be manipulated using tracefs library. More detailed
+information about each option is available in Documentation/trace/ftrace.rst from the Linux
+kernel tree, in the trace_options section. Note that some ftrace options cannot be manipulated by
+this library, as they are intended for internal, debug purposes. These options control the tracers
+or the trace output. All options have two states - on and off, the default state is different for
+each of them.
+[verse]
+--
+Common options for all tracers:
+	*TRACEFS_OPTION_INVALID* Not a valid ftrace option, used by the API to indicate an error.
+	*TRACEFS_OPTION_ANNOTATE* Display when a new CPU buffer started.
+	*TRACEFS_OPTION_BIN* Display the formats in raw binary.
+	*TRACEFS_OPTION_CONTEXT_INFO* Show only the event data. Hides the comm, PID, timestamp, CPU, and other useful data.
+	*TRACEFS_OPTION_BLOCK* When set, reading trace_pipe will not block when polled.
+	*TRACEFS_OPTION_DISABLE_ON_FREE* When the free_buffer is closed, tracing will stop.
+	*TRACEFS_OPTION_DISPLAY_GRAPH* When set, the latency tracers will use function graph tracing instead of function tracing.
+	*TRACEFS_OPTION_EVENT_FORK* When set, tasks with PIDs listed in set_event_pid will have the PIDs of their children added to set_event_pid when those tasks fork.
+	*TRACEFS_OPTION_FUNCTION_FORK* When set, tasks with PIDs listed in set_ftrace_pid will have the PIDs of their children added to set_ftrace_pid when those tasks fork.
+	*TRACEFS_OPTION_FUNCTION_TRACE* When enabled, the latency tracers will trace functions.
+	*TRACEFS_OPTION_HEX* Display numbers in a hexadecimal format.
+	*TRACEFS_OPTION_IRQ_INFO* Display the interrupt, preempt count, need resched data.
+	*TRACEFS_OPTION_LATENCY_FORMAT* Display additional information about the latency.
+	*TRACEFS_OPTION_MARKERS* When set, the trace_marker is enabled - writable (only by root).
+	*TRACEFS_OPTION_OVERWRITE* Controls what happens when the trace buffer is full. If set, the oldest events are discarded and overwritten. If disabled, then the newest events are discarded.
+	*TRACEFS_OPTION_PAUSE_ON_TRACE* When set, opening the trace file for read, will pause writing to the ring buffer. When the file is closed, tracing will be enabled again.
+	*TRACEFS_OPTION_PRINTK_MSG_ONLY* When set, trace_printk()s will only show the format and not their parameters.
+	*TRACEFS_OPTION_PRINT_PARENT* On function traces, display the calling (parent) function as well as the function being traced.
+	*TRACEFS_OPTION_RAW* Display raw numbers.
+	*TRACEFS_OPTION_RECORD_CMD* Save a mapping with a pid and corresponding command.
+	*TRACEFS_OPTION_RECORD_TGID* Save a mapping with a pid and corresponding Thread Group IDs.
+	*TRACEFS_OPTION_STACKTRACE* Record a stack trace after any trace event.
+	*TRACEFS_OPTION_SYM_ADDR* Display the function address as well as the function name.
+	*TRACEFS_OPTION_SYM_OFFSET* Display not only the function name, but also the offset in the function.
+	*TRACEFS_OPTION_SYM_USEROBJ* When *TRACEFS_OPTION_USERSTACKTRACE* is set, look up which object the address belongs to, and print the object and a relative address.
+	*TRACEFS_OPTION_TRACE_PRINTK* Disable trace_printk() from writing into the buffer.
+	*TRACEFS_OPTION_USERSTACKTRACE* Records a stack trace of the current user space thread after each trace event.
+	*TRACEFS_OPTION_VERBOSE* When *TRACEFS_OPTION_LATENCY_FORMAT* is enabled, print more detailed information.
+
+Options, specific to function tracer:
+	*TRACEFS_OPTION_FUNC_STACKTRACE* Record a stack trace after every function.
+
+Options, specific to function_graph tracer:
+	*TRACEFS_OPTION_FGRAPH_ABSTIME* Display the timestamp at each line.
+	*TRACEFS_OPTION_FGRAPH_CPU* Display the CPU number of the CPU where the trace occurred.
+	*TRACEFS_OPTION_FGRAPH_DURATION* Display the duration of the amount of time at the end of each function, in microseconds.
+	*TRACEFS_OPTION_FGRAPH_IRQS* Trace functions that happen inside an interrupt.
+	*TRACEFS_OPTION_FGRAPH_OVERHEAD* Display a marker if a function takes longer than a certain amount of time.
+	*TRACEFS_OPTION_FGRAPH_OVERRUN* Display "overrun" of the call graph, in the case of functions missed due to big callstack.
+	*TRACEFS_OPTION_FGRAPH_PROC* Display the command of each process at every line.
+	*TRACEFS_OPTION_FGRAPH_TAIL* Display the function name on its return.
+	*TRACEFS_OPTION_SLEEP_TIME* Account time the task has been scheduled out as part of the function call.
+	*TRACEFS_OPTION_GRAPH_TIME* Display the time to call nested functions, if function profiler is enabled.
+
+Options, specific to blk tracer:
+	*TRACEFS_OPTION_BLK_CGNAME*
+	*TRACEFS_OPTION_BLK_CGROUP*
+	*TRACEFS_OPTION_BLK_CLASSIC* Display a more minimalistic output.
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+_Documentation/trace/ftrace.rst_ from the Linux kernel tree.
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2021 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-sql.txt b/Documentation/libtracefs-sql.txt
new file mode 100644
index 0000000..6d606db
--- /dev/null
+++ b/Documentation/libtracefs-sql.txt
@@ -0,0 +1,628 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_sql - Create a synthetic event via an SQL statement
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_synth pass:[*]*tracefs_sql*(struct tep_handle pass:[*]_tep_, const char pass:[*]_name_,
+					const char pass:[*]_sql_buffer_, char pass:[**]_err_);
+--
+
+DESCRIPTION
+-----------
+Synthetic events are dynamically created events that attach two existing events
+together via one or more matching fields between the two events. It can be used
+to find the latency between the events, or to simply pass fields of the first event
+on to the second event to display as one event.
+
+The Linux kernel interface to create synthetic events is complex, and there needs
+to be a better way to create synthetic events that is easy and can be understood
+via existing technology.
+
+If you think of each event as a table, where the fields are the column of the table
+and each instance of the event as a row, you can understand how SQL can be used
+to attach two events together and form another event (table). Utilizing the
+SQL *SELECT* *FROM* *JOIN* *ON* [ *WHERE* ] syntax, a synthetic event can easily
+be created from two different events.
+
+For simple SQL queries to make a histogram instead of a synthetic event, see
+HISTOGRAMS below.
+
+*tracefs_sql*() takes in a _tep_ handler (See _tep_local_events_(3)) that is used to
+verify the events within the _sql_buffer_ expression. The _name_ is the name of the
+synthetic event to create. If _err_ points to an address of a string, it will be filled
+with a detailed message on any type of parsing error, including fields that do not belong
+to an event, or if the events or fields are not properly compared.
+
+The example program below is a fully functional parser where it will create a synthetic
+event from a SQL syntax passed in via the command line or a file.
+
+The SQL format is as follows:
+
+*SELECT* <fields> *FROM* <start-event> *JOIN* <end-event> *ON* <matching-fields> *WHERE* <filter>
+
+Note, although the examples show the SQL commands in uppercase, they are not required to
+be so. That is, you can use "SELECT" or "select" or "sElEct".
+
+For example:
+[source,c]
+--
+SELECT syscalls.sys_enter_read.fd, syscalls.sys_exit_read.ret FROM syscalls.sys_enter_read
+   JOIN syscalls.sys_exit_read
+   ON syscalls.sys_enter_read.common_pid = syscalls.sys_exit_write.common_pid
+--
+
+Will create a synthetic event that with the fields:
+
+  u64 fd; s64 ret;
+
+Because the function takes a _tep_ handle, and usually all event names are unique, you can
+leave off the system (group) name of the event, and *tracefs_sql*() will discover the
+system for you.
+
+That is, the above statement would work with:
+
+[source,c]
+--
+SELECT sys_enter_read.fd, sys_exit_read.ret FROM sys_enter_read JOIN sys_exit_read
+   ON sys_enter_read.common_pid = sys_exit_write.common_pid
+--
+
+The *AS* keyword can be used to name the fields as well as to give an alias to the
+events, such that the above can be simplified even more as:
+
+[source,c]
+--
+SELECT start.fd, end.ret FROM sys_enter_read AS start JOIN sys_exit_read AS end ON start.common_pid = end.common_pid
+--
+
+The above aliases _sys_enter_read_ as *start* and _sys_exit_read_ as *end* and uses
+those aliases to reference the event throughout the statement.
+
+Using the *AS* keyword in the selection portion of the SQL statement will define what
+those fields will be called in the synthetic event.
+
+[source,c]
+--
+SELECT start.fd AS filed, end.ret AS return FROM sys_enter_read AS start JOIN sys_exit_read AS end
+   ON start.common_pid = end.common_pid
+--
+
+The above labels the _fd_ of _start_ as *filed* and the _ret_ of _end_ as *return* where
+the synthetic event that is created will now have the fields:
+
+  u64 filed; s64 return;
+
+The fields can also be calculated with results passed to the synthetic event:
+
+[source,c]
+--
+select start.truesize, end.len, (start.truesize - end.len) as diff from napi_gro_receive_entry as start
+   JOIN netif_receive_skb as end ON start.skbaddr = end.skbaddr
+--
+
+Which would show the *truesize* of the _napi_gro_receive_entry_ event, the actual
+_len_ of the content, shown by the _netif_receive_skb_, and the delta between
+the two and expressed by the field *diff*.
+
+The code also supports recording the timestamps at either event, and performing calculations
+on them. For wakeup latency, you have:
+
+[source,c]
+--
+select start.pid, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) as lat from sched_waking as start
+   JOIN sched_switch as end ON start.pid = end.next_pid
+--
+
+The above will create a synthetic event that records the _pid_ of the task being woken up,
+and the time difference between the _sched_waking_ event and the _sched_switch_ event.
+The *TIMESTAMP_USECS* will truncate the time down to microseconds as the timestamp usually
+recorded in the tracing buffer has nanosecond resolution. If you do not want that
+truncation, use *TIMESTAMP* instead of *TIMESTAMP_USECS*.
+
+Finally, the *WHERE* clause can be added, that will let you add filters on either or both events.
+
+[source,c]
+--
+select start.pid, (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 start.prio < 100 && (!(end.prev_pid < 1 || end.prev_prio > 100) || end.prev_pid == 0)
+--
+
+*NOTE*
+
+Although both events can be used together in the *WHERE* clause, they must not be mixed outside
+the top most "&&" statements. You can not OR (||) the events together, where a filter of one
+event is OR'd to a filter of the other event. This does not make sense, as the synthetic event
+requires both events to take place to be recorded. If one is filtered out, then the synthetic
+event does not execute.
+
+[source,c]
+--
+select start.pid, (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 start.prio < 100 && end.prev_prio < 100
+--
+
+The above is valid.
+
+Where as the below is not.
+
+[source,c]
+--
+select start.pid, (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 start.prio < 100 || end.prev_prio < 100
+--
+
+
+KEYWORDS AS EVENT FIELDS
+------------------------
+
+In some cases, an event may have a keyword. For example, regcache_drop_region has "from"
+as a field and the following will not work
+
+[source,c]
+--
+  select from from regcache_drop_region
+--
+
+In such cases, add a backslash to the conflicting field, and this will tell the parser
+that the "from" is a field and not a keyword:
+
+[source,c]
+--
+  select \from from regcache_drop_region
+--
+
+HISTOGRAMS
+----------
+
+Simple SQL statements without the *JOIN* *ON* may also be used, which will create a histogram
+instead. When doing this, the struct tracefs_hist descriptor can be retrieved from the
+returned synthetic event descriptor via the *tracefs_synth_get_start_hist*(3).
+
+In order to utilize the histogram types (see xxx) the CAST command of SQL can be used.
+
+That is:
+
+[source,c]
+--
+  select CAST(common_pid AS comm), CAST(id AS syscall) FROM sys_enter
+--
+
+Which produces:
+
+[source,c]
+--
+ # echo 'hist:keys=common_pid.execname,id.syscall' > events/raw_syscalls/sys_enter/trigger
+
+ # cat events/raw_syscalls/sys_enter/hist
+
+{ common_pid: bash            [     18248], id: sys_setpgid                   [109] } hitcount:          1
+{ common_pid: sendmail        [      1812], id: sys_read                      [  0] } hitcount:          1
+{ common_pid: bash            [     18247], id: sys_getpid                    [ 39] } hitcount:          1
+{ common_pid: bash            [     18247], id: sys_dup2                      [ 33] } hitcount:          1
+{ common_pid: gmain           [     13684], id: sys_inotify_add_watch         [254] } hitcount:          1
+{ common_pid: cat             [     18247], id: sys_access                    [ 21] } hitcount:          1
+{ common_pid: bash            [     18248], id: sys_getpid                    [ 39] } hitcount:          1
+{ common_pid: cat             [     18247], id: sys_fadvise64                 [221] } hitcount:          1
+{ common_pid: sendmail        [      1812], id: sys_openat                    [257] } hitcount:          1
+{ common_pid: less            [     18248], id: sys_munmap                    [ 11] } hitcount:          1
+{ common_pid: sendmail        [      1812], id: sys_close                     [  3] } hitcount:          1
+{ common_pid: gmain           [      1534], id: sys_poll                      [  7] } hitcount:          1
+{ common_pid: bash            [     18247], id: sys_execve                    [ 59] } hitcount:          1
+--
+
+Note, string fields may not be cast.
+
+The possible types to cast to are:
+
+*HEX* - convert the value to use hex and not decimal
+
+*SYM* - convert a pointer to symbolic (kallsyms values)
+
+*SYM-OFFSET* - convert a pointer to symbolic and include the offset.
+
+*SYSCALL* - convert the number to the mapped system call name
+
+*EXECNAME* or *COMM* - can only be used with the common_pid field. Will show the task
+name of the process.
+
+*LOG* or *LOG2* - bucket the key values in a log 2 values (1, 2, 3-4, 5-8, 9-16, 17-32, ...)
+
+The above fields are not case sensitive, and "LOG2" works as good as "log".
+
+A special CAST to _COUNTER_ or __COUNTER__ will make the field a value and not
+a key. For example:
+
+[source,c]
+--
+  SELECT common_pid, CAST(bytes_req AS _COUNTER_) FROM kmalloc
+--
+
+Which will create
+
+[source,c]
+--
+  echo 'hist:keys=common_pid:vals=bytes_req' > events/kmem/kmalloc/trigger
+
+  cat events/kmem/kmalloc/hist
+
+{ common_pid:       1812 } hitcount:          1  bytes_req:         32
+{ common_pid:       9111 } hitcount:          2  bytes_req:        272
+{ common_pid:       1768 } hitcount:          3  bytes_req:       1112
+{ common_pid:          0 } hitcount:          4  bytes_req:        512
+{ common_pid:      18297 } hitcount:         11  bytes_req:       2004
+--
+
+RETURN VALUE
+------------
+Returns 0 on success and -1 on failure. On failure, if _err_ is defined, it will be
+allocated to hold a detailed description of what went wrong if it the error was caused
+by a parsing error, or that an event, field does not exist or is not compatible with
+what it was combined with.
+
+CREATE A TOOL
+-------------
+
+The below example is a functional program that can be used to parse SQL commands into
+synthetic events.
+
+[source, c]
+--
+   man tracefs_sql | sed -ne '/^EXAMPLE/,/FILES/ { /EXAMPLE/d ; /FILES/d ; p}' > sqlhist.c
+   gcc -o sqlhist sqlhist.c `pkg-config --cflags --libs libtracefs`
+--
+
+Then you can run the above examples:
+
+[source, c]
+--
+  sudo ./sqlhist 'select start.pid, (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 start.prio < 100 || end.prev_prio < 100'
+--
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <tracefs.h>
+
+static void usage(char **argv)
+{
+	fprintf(stderr, "usage: %s [-ed][-n name][-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",
+		argv[0]);
+	exit(-1);
+}
+
+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) {
+		fprintf(stderr, "Error: -s, -S and -T not supported without -m or -c");
+		exit(-1);
+	}
+
+	if (!name)
+		name = "Anonymous";
+
+	trace_seq_init(&seq);
+	tep = tracefs_local_events(trace_dir);
+	if (!tep) {
+		if (!trace_dir)
+			trace_dir = "tracefs directory";
+		perror(trace_dir);
+		exit(-1);
+	}
+
+	synth = tracefs_sql(tep, name, buffer, &err);
+	if (!synth) {
+		perror("Failed creating synthetic event!");
+		if (err)
+			fprintf(stderr, "%s", err);
+		free(err);
+		exit(-1);
+	}
+
+	if (tracefs_synth_complete(synth)) {
+		if (var) {
+			if (action & ACTION_MAX)
+				handler = TRACEFS_SYNTH_HANDLE_MAX;
+			else
+				handler = TRACEFS_SYNTH_HANDLE_CHANGE;
+
+			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) {
+				fprintf(stderr, "%s\n", tracefs_error_last(NULL));
+				exit(-1);
+			}
+		}
+	} else {
+		struct tracefs_instance *instance = NULL;
+		struct tracefs_hist *hist;
+
+		hist = tracefs_synth_get_start_hist(synth);
+		if (!hist) {
+			perror("get_start_hist");
+			exit(-1);
+		}
+		if (instance_name) {
+			if (execute)
+				instance = tracefs_instance_create(instance_name);
+			else
+				instance = tracefs_instance_alloc(trace_dir,
+								  instance_name);
+			if (!instance) {
+				perror("Failed to create instance");
+				exit(-1);
+			}
+		}
+		tracefs_hist_echo_cmd(&seq, instance, hist, 0);
+		if (execute) {
+			ret = tracefs_hist_start(instance, hist);
+			if (ret < 0) {
+				fprintf(stderr, "%s\n", tracefs_error_last(instance));
+				exit(-1);
+			}
+		}
+	}
+
+	tracefs_synth_free(synth);
+
+	trace_seq_do_printf(&seq);
+	trace_seq_destroy(&seq);
+	return 0;
+}
+
+int main (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;
+	int action = 0;
+	char *tok;
+	FILE *fp;
+	size_t r;
+	int c;
+	int i;
+
+	for (;;) {
+		c = getopt(argc, argv, "ht:f:en:m:c:sS:TB:");
+		if (c == -1)
+			break;
+
+		switch(c) {
+		case 'h':
+			usage(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(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);
+
+	return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*sqlhist*(1),
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+*tracefs_synth_init*(3),
+*tracefs_synth_add_match_field*(3),
+*tracefs_synth_add_compare_field*(3),
+*tracefs_synth_add_start_field*(3),
+*tracefs_synth_add_end_field*(3),
+*tracefs_synth_append_start_filter*(3),
+*tracefs_synth_append_end_filter*(3),
+*tracefs_synth_create*(3),
+*tracefs_synth_destroy*(3),
+*tracefs_synth_free*(3),
+*tracefs_synth_echo_cmd*(3),
+*tracefs_hist_alloc*(3),
+*tracefs_hist_alloc_2d*(3),
+*tracefs_hist_alloc_nd*(3),
+*tracefs_hist_free*(3),
+*tracefs_hist_add_key*(3),
+*tracefs_hist_add_value*(3),
+*tracefs_hist_add_name*(3),
+*tracefs_hist_start*(3),
+*tracefs_hist_destory*(3),
+*tracefs_hist_add_sort_key*(3),
+*tracefs_hist_sort_key_direction*(3)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-sqlhist.txt.1 b/Documentation/libtracefs-sqlhist.txt.1
new file mode 100644
index 0000000..875b250
--- /dev/null
+++ b/Documentation/libtracefs-sqlhist.txt.1
@@ -0,0 +1,356 @@
+SQLHIST(1)
+==========
+
+NAME
+----
+sqlhist - Tool that uses SQL language to create / show creation of tracefs histograms and synthetic events.
+
+SYNOPSIS
+--------
+*sqlhist* ['OPTIONS'] ['SQL-select-command']
+
+DESCRIPTION
+-----------
+The 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. *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_sql*(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.
+
+The *sqlhist* is a simple program where its code actual exists in the
+*tracefs_sql*(3) man page.
+
+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*(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
+    $ ./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*.
+
+*-c* 'var'::
+    Do the given action when the variable _var_ changes its value. This can
+    not be used with *-m*.
+
+*-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
+--------
+
+Create the sqlhist executable:
+
+[source, c]
+--
+   man tracefs_sql | sed -ne '/^EXAMPLE/,/FILES/ { /EXAMPLE/d ; /FILES/d ; p}' > sqlhist.c
+   gcc -o sqlhist sqlhist.c `pkg-config --cflags --libs libtracefs`
+--
+
+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]
+--
+   $ ./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 *sqlhist* along with *trace-cmd*(1), wake up latency can
+be recorded by creating a synthetic event by attaching the _sched_waking_
+and the _sched_switch_ events.
+
+[source, c]
+--
+  # 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 *sqlhist* 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)
+--
+
+BUGS
+----
+
+As *sqlhist* is just example code from a man page, it is guaranteed to contain
+lots of bugs. For one thing, not all error paths are covered properly.
+
+SEE ALSO
+--------
+trace-cmd(1), tracefs_sql(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/libtracefs-stream.txt b/Documentation/libtracefs-stream.txt
new file mode 100644
index 0000000..8008be8
--- /dev/null
+++ b/Documentation/libtracefs-stream.txt
@@ -0,0 +1,126 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_trace_pipe_stream, tracefs_trace_pipe_print, tracefs_trace_pipe_stop -
+redirect the stream of trace data to an output or stdout.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+ssize_t *tracefs_trace_pipe_stream*(int _fd_, struct tracefs_instance pass:[*]_instance_, int _flags_);
+ssize_t *tracefs_trace_pipe_print*(struct tracefs_instance pass:[*]_instance_, int _flags_);
+void *tracefs_trace_pipe_stop*(struct tracefs_instance pass:[*]_instance_);
+
+
+--
+
+DESCRIPTION
+-----------
+If NULL is passed as _instance_, the top trace instance is used.
+
+The reading of the trace_pipe file can be stopped by calling *tracefs_trace_pipe_stop()*
+which could be placed in a signal handler in case the application wants to stop the
+reading, for example, with the user pressing Ctrl-C.
+
+The *tracefs_trace_pipe_stream()* function redirects the stream of trace data to an output
+file. The "splice" system call is used to moves the data without copying between kernel
+address space and user address space. The _fd_ is the file descriptor of the output file
+and _flags_ is a bit mask of flags to be passed to the open system call of the trace_pipe
+file (see ). If flags contain O_NONBLOCK, then that is also passed to the splice calls
+that may read the file to the output stream file descriptor.
+
+The *tracefs_trace_pipe_print()* function is similar to *tracefs_trace_pipe_stream()*, but
+the stream of trace data is redirected to stdout.
+
+
+RETURN VALUE
+------------
+The *tracefs_trace_pipe_stream()*, and *tracefs_trace_pipe_print()* functions return the
+number of bytes transfered if the operation is successful, or -1 in case of an error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include <tracefs.h>
+
+void stop(int sig)
+{
+	tracefs_trace_pipe_stop(NULL);
+}
+
+int main(int argc, char **argv)
+{
+	mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+	const char *filename;
+	int fd;
+	int ret;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s output_file\n", argv[0]);
+		exit(-1);
+	}
+	filename = argv[1];
+	fd = creat(filename, mode);
+	if (fd < 0) {
+		perror(filename);
+		exit(-1);
+	}
+	signal(SIGINT, stop);
+	ret = tracefs_trace_pipe_stream(fd, NULL, SPLICE_F_NONBLOCK);
+	close(fd);
+
+	return ret;
+}
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+Documentation/trace/ftrace.rst from the Linux kernel tree
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2021 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-synth-info.txt b/Documentation/libtracefs-synth-info.txt
new file mode 100644
index 0000000..6ee0320
--- /dev/null
+++ b/Documentation/libtracefs-synth-info.txt
@@ -0,0 +1,298 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_synth_echo_cmd, tracefs_synth_get_start_hist, tracefs_synth_get_name,
+tracefs_synth_raw_fmt, tracefs_synth_show_event, tracefs_synth_show_start_hist, tracefs_synth_show_end_hist,
+tracefs_synth_get_event - Retrieve data of synthetic events.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_synth_echo_cmd*(struct trace_seq pass:[*]_seq_, struct tracefs_synth pass:[*]_synth_);
+struct tracefs_hist pass:[*]*tracefs_synth_get_start_hist*(struct tracefs_synth pass:[*]_synth_);
+
+const char pass:[*]*tracefs_synth_get_name*(struct tracefs_synth pass:[*]_synth_);
+int *tracefs_synth_raw_fmt*(struct trace_seq pass:[*]_seq_, struct tracefs_synth pass:[*]_synth_);
+const char pass:[*]*tracefs_synth_show_event*(struct tracefs_synth pass:[*]_synth_);
+const char pass:[*]*tracefs_synth_show_start_hist*(struct tracefs_synth pass:[*]_synth_);
+const char pass:[*]*tracefs_synth_show_end_hist*(struct tracefs_synth pass:[*]_synth_);
+struct tep_event pass:[*]*tracefs_synth_get_event*(struct tep_handle pass:[*]_tep_, struct tracefs_synth pass:[*]_synth_);
+
+--
+
+DESCRIPTION
+-----------
+Synthetic events are dynamic events that are created by matching
+two other events which triggers a synthetic event. One event is the starting
+event which some field is recorded, and when the second event is executed,
+if it has a field (or fields) that matches the starting event's field (or fields)
+then it will trigger the synthetic event. The field values other than the matching
+fields may be passed from the starting event to the end event to perform calculations
+on, or to simply pass as a parameter to the synthetic event.
+
+One common use case is to set "sched_waking" as the starting event. This event is
+triggered when a process is awoken. Then set "sched_switch" as the ending event.
+This event is triggered when a new task is scheduled on the CPU. By setting
+the "common_pid" of both events as the matching fields, the time between the
+two events is considered the wake up latency of that process. Use *TRACEFS_TIMESTAMP*
+as a field for both events to calculate the delta in nanoseconds, or use
+*TRACEFS_TIMESTAMP_USECS* as the compare fields for both events to calculate the
+delta in microseconds. This is used as the example below.
+
+See *tracefs_synth_alloc*(3) for allocation of synthetic events, and
+*tracefs_synth_create*() for creating the synthetic event on the system.
+
+*tracefs_synth_echo_cmd*() acts like *tracefs_synth_create*(), but instead of creating
+the synthetic event in the system, it will write the echo commands to manually create
+it in the _seq_ given.
+
+*tracefs_synth_get_start_hist*() returns a struct tracefs_hist descriptor describing
+the histogram used to create the synthetic event.
+
+[verse]
+--
+enum tracefs_synth_handler {
+	*TRACEFS_SYNTH_HANDLE_MATCH*,
+	*TRACEFS_SYNTH_HANDLE_MAX*,
+	*TRACEFS_SYNTH_HANDLE_CHANGE*,
+};
+--
+
+*tracefs_synth_get_name*() returns the name of the synthetic event or NULL on error.
+The returned string belongs to the synth event object and is freed with the event
+by *tracefs_synth_free*().
+
+*tracefs_synth_raw_fmt*() writes the raw format strings (dynamic event and histograms) of
+the synthetic event in the _seq_ given.
+
+*tracefs_synth_show_event*() returns the format of the dynamic event used by the synthetic
+event or NULL on error. The returned string belongs to the synth event object and is freed
+with the event by *tracefs_synth_free*().
+
+*tracefs_synth_show_start_hist*() returns the format of the start histogram used by the
+synthetic event or NULL on error. The returned string belongs to the synth event object
+and is freed with the event by *tracefs_synth_free*().
+
+*tracefs_synth_show_end_hist*() returns the format of the end histogram used by the
+synthetic event or NULL on error. The returned string belongs to the synth event object
+and is freed with the event by *tracefs_synth_free*().
+
+The *tracefs_synth_get_event*() function returns a tep event, describing the given synthetic
+event. The API detects any newly created or removed dynamic events. The returned pointer to
+tep event is controlled by @tep and must not be freed.
+
+RETURN VALUE
+------------
+*tracefs_synth_get_name*(), *tracefs_synth_show_event*(), *tracefs_synth_show_start_hist*()
+and *tracefs_synth_show_end_hist*()  return a string owned by the synth event object.
+
+The *tracefs_synth_get_event*() function returns a pointer to a tep event or NULL in case of an
+error or if the requested synthetic event is missing. The returned pointer to tep event is
+controlled by @tep and must not be freed.
+
+All other functions return zero on success or -1 on error.
+
+ERRORS
+------
+The following errors are for all the above calls:
+
+*EPERM* Not run as root user when required.
+
+*EINVAL* Either a parameter is not valid (NULL when it should not be)
+  or a field that is not compatible for calculations.
+
+*ENODEV* An event or one of its fields is not found.
+
+*EBADE* The fields of the start and end events are not compatible for
+  either matching or comparing.
+
+*ENOMEM* not enough memory is available.
+
+And more errors may have happened from the system calls to the system.
+
+EXAMPLE
+-------
+See *tracefs_sql*(3) for a more indepth use of some of this code.
+
+[source,c]
+--
+#include <stdlib.h>
+#include <tracefs.h>
+
+#define start_event "sched_waking"
+#define start_field "pid"
+
+#define end_event "sched_switch"
+#define end_field "next_pid"
+
+#define match_name "pid"
+
+static struct tracefs_synth *synth;
+
+static void make_event(void)
+{
+	struct tep_handle *tep;
+
+	/* Load all events from the system */
+	tep = tracefs_local_events(NULL);
+
+	/* Initialize the synthetic event */
+	synth = tracefs_synth_alloc(tep, "wakeup_lat",
+				    NULL, start_event,
+				    NULL, end_event,
+				    start_field, end_field,
+				    match_name);
+
+	/* The tep is no longer needed */
+	tep_free(tep);
+
+
+	/* Save the "prio" field as "prio" from the start event */
+	tracefs_synth_add_start_field(synth, "prio", NULL);
+
+	/* Save the "next_comm" as "comm" from the end event */
+	tracefs_synth_add_end_field(synth, "next_comm", "comm");
+
+	/* Save the "prev_prio" as "prev_prio" from the end event */
+	tracefs_synth_add_end_field(synth, "prev_prio", NULL);
+
+	/*
+	 * Take a microsecond time difference between end and start
+	 * and record as "delta"
+	 */
+	tracefs_synth_add_compare_field(synth, TRACEFS_TIMESTAMP_USECS,
+					TRACEFS_TIMESTAMP_USECS,
+					TRACEFS_SYNTH_DELTA_END, "delta");
+
+	/* Only record if start event "prio" is less than 100 */
+	tracefs_synth_append_start_filter(synth, TRACEFS_FILTER_COMPARE,
+					  "prio", TRACEFS_COMPARE_LT, "100");
+
+	/*
+	 * Only record if end event "next_prio" is less than 50
+	 * or the previous task's prio was not greater than or equal to 100.
+	 *   next_prio < 50 || !(prev_prio >= 100)
+	 */
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_COMPARE,
+					"next_prio", TRACEFS_COMPARE_LT, "50");
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_OR, NULL, 0, NULL);
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_NOT, NULL, 0, NULL);
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_OPEN_PAREN, NULL, 0, NULL);
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_COMPARE,
+					"prev_prio", TRACEFS_COMPARE_GE, "100");
+	/*
+	 * Note, the above only added: "next_prio < 50 || !(prev_prio >= 100"
+	 * That's because, when the synth is executed, the remaining close parenthesis
+	 * will be added. That is, the string will end up being:
+	 * "next_prio < 50 || !(prev_prio >= 100)" when one of tracefs_sync_create()
+	 * or tracefs_sync_echo_cmd() is run.
+	 */
+}
+
+/* Display how to create the synthetic event */
+static void show_event(void)
+{
+	struct trace_seq s;
+
+	trace_seq_init(&s);
+
+	tracefs_synth_echo_cmd(&s, synth);
+	trace_seq_terminate(&s);
+	trace_seq_do_printf(&s);
+	trace_seq_destroy(&s);
+}
+
+int main (int argc, char **argv)
+{
+	make_event();
+
+	if (argc > 1) {
+		if (!strcmp(argv[1], "create")) {
+			/* Create the synthetic event */
+			tracefs_synth_create(synth);
+		} else if (!strcmp(argv[1], "delete")) {
+			/* Delete the synthetic event */
+			tracefs_synth_destroy(synth);
+		} else {
+			printf("usage: %s [create|delete]\n", argv[0]);
+			exit(-1);
+		}
+	} else
+		show_event();
+
+	tracefs_synth_free(synth);
+
+	return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+*tracefs_hist_alloc*(3),
+*tracefs_hist_alloc_2d*(3),
+*tracefs_hist_alloc_nd*(3),
+*tracefs_hist_free*(3),
+*tracefs_hist_add_key*(3),
+*tracefs_hist_add_value*(3),
+*tracefs_hist_add_name*(3),
+*tracefs_hist_start*(3),
+*tracefs_hist_destory*(3),
+*tracefs_hist_add_sort_key*(3),
+*tracefs_hist_sort_key_direction*(3),
+*tracefs_synth_alloc*(3),
+*tracefs_synth_add_match_field*(3),
+*tracefs_synth_add_compare_field*(3),
+*tracefs_synth_add_start_field*(3),
+*tracefs_synth_add_end_field*(3),
+*tracefs_synth_append_start_filter*(3),
+*tracefs_synth_append_end_filter*(3),
+*tracefs_synth_free*(3),
+*tracefs_synth_create*(3),
+*tracefs_synth_destroy*(3),
+*tracefs_synth_complete*(3),
+*tracefs_synth_trace*(3),
+*tracefs_synth_snapshot*(3),
+*tracefs_synth_save*(3),
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-synth.txt b/Documentation/libtracefs-synth.txt
new file mode 100644
index 0000000..c57725d
--- /dev/null
+++ b/Documentation/libtracefs-synth.txt
@@ -0,0 +1,368 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_synth_alloc, tracefs_synth_add_match_field, tracefs_synth_add_compare_field, tracefs_synth_add_start_field,
+tracefs_synth_add_end_field, tracefs_synth_append_start_filter, tracefs_synth_append_end_filter, tracefs_synth_free,
+- Creation of a synthetic event descriptor
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_synth pass:[*]*tracefs_synth_alloc*(struct tep_handle pass:[*]_tep_,
+					 const char pass:[*]_name_,
+					 const char pass:[*]_start_system_,
+					 const char pass:[*]_start_event_,
+					 const char pass:[*]_end_system_,
+					 const char pass:[*]_end_event_,
+					 const char pass:[*]_start_match_field_,
+					 const char pass:[*]_end_match_field_,
+					 const char pass:[*]_match_name_);
+int *tracefs_synth_add_match_field*(struct tracefs_synth pass:[*]_synth_,
+				  const char pass:[*]_start_match_field_,
+				  const char pass:[*]_end_match_field_,
+				  const char pass:[*]_name_);
+int *tracefs_synth_add_compare_field*(struct tracefs_synth pass:[*]_synth_,
+				    const char pass:[*]_start_compare_field_,
+				    const char pass:[*]_end_compare_field_,
+				    enum tracefs_synth_calc _calc_,
+				    const char pass:[*]_name_);
+int *tracefs_synth_add_start_field*(struct tracefs_synth pass:[*]_synth_,
+				  const char pass:[*]_start_field_,
+				  const char pass:[*]_name_);
+int *tracefs_synth_add_end_field*(struct tracefs_synth pass:[*]_synth_,
+				const char pass:[*]_end_field_,
+				const char pass:[*]_name_);
+int *tracefs_synth_append_start_filter*(struct tracefs_synth pass:[*]_synth_,
+				      struct tracefs_filter _type_,
+				      const char pass:[*]_field_,
+				      enum tracefs_synth_compare _compare_,
+				      const char pass:[*]_val_);
+int *tracefs_synth_append_end_filter*(struct tracefs_synth pass:[*]_synth_,
+				    struct tracefs_filter _type_,
+				    const char pass:[*]_field_,
+				    enum tracefs_synth_compare _compare_,
+				    const char pass:[*]_val_);
+void *tracefs_synth_free*(struct tracefs_synth pass:[*]_synth_);
+--
+
+DESCRIPTION
+-----------
+Synthetic events are dynamic events that are created by matching
+two other events which triggers a synthetic event. One event is the starting
+event which some field is recorded, and when the second event is executed,
+if it has a field (or fields) that matches the starting event's field (or fields)
+then it will trigger the synthetic event. The field values other than the matching
+fields may be passed from the starting event to the end event to perform calculations
+on, or to simply pass as a parameter to the synthetic event.
+
+One common use case is to set "sched_waking" as the starting event. This event is
+triggered when a process is awoken. Then set "sched_switch" as the ending event.
+This event is triggered when a new task is scheduled on the CPU. By setting
+the "common_pid" of both events as the matching fields, the time between the
+two events is considered the wake up latency of that process. Use *TRACEFS_TIMESTAMP*
+as a field for both events to calculate the delta in nanoseconds, or use
+*TRACEFS_TIMESTAMP_USECS" as the compare fields for both events to calculate the
+delta in microseconds. This is used as the example below.
+
+*tracefs_synth_alloc*() allocates and initializes a synthetic event.
+It does not create the synthetic event, but supplies the minimal information
+to do so. See *tracefs_synth_create*(3) for how to create the synthetic
+event in the system. It requires a _tep_ handler that can be created by
+*tracefs_local_events*(3) for more information. The _name_ holds the name
+of the synthetic event that will be created. The _start_system_ is the name
+of the system for the starting event. It may be NULL and the first event
+with the name of _start_event_ will be chosen. The _end_system_ is the
+name of the system for theh ending event. It may be NULL and the first event
+with the name of _end_event_ will be chosen as the ending event. If _match_name_
+is given, then this will be the field of the created synthetic event that
+holds the matching keys of the starting event's _start_match_field_ and
+the ending event's _end_match_field_. If _match_name_ is NULL, then it will
+not be recorded in the created synthetic event.
+
+*tracefs_synth_add_match_field*() will add a second key to match between the
+starting event and the ending event. If _name_ is given, then the content
+of the matching field will be saved by this _name_ in the synthetic event.
+The _start_match_field_ is the field of the starting event to mach with the
+ending event's _end_match_field_.
+
+*tracefs_synth_add_compare_field*() is used to compare the _start_compare_field_
+of the starting event with the _end_compare_field_ of the ending event. The _name_
+must be given so that the result will be saved by the synthetic event. It makes
+no sense to not pass this to the synthetic event after doing the work of
+the compared fields, as it serves no other purpose. The _calc_ parameter
+can be one of:
+
+*TRACEFS_SYNTH_DELTA_END* - calculate the difference between the content in
+ the _end_compare_field_ from the content of the _start_compare_field_.
+
+_name_ = _end_compare_field_ - _start_compare_field_
+
+*TRACEFS_SYNTH_DELTA_START* - calculate the difference between the content in
+ the _start_compare_field_ from the content of the _end_compare_field_.
+
+_name_ = _start_compare_field_ - _end_compare_field_
+
+*TRACEFS_SYNTH_ADD* - Add the content of the _start_compare_field_ to the
+  content of the _end_compare_field_.
+
+_name_ = _start_compare_field_ + _end_compare_field_
+
+*tracefs_synth_add_start_field*() - Records the _start_field_ of the starting
+event as _name_ in the synthetic event. If _name_ is NULL, then the name used
+will be the same as _start_field_.
+
+*tracefs_synth_add_end_field*() - Records the _end_field_ of the ending
+event as _name_ in the synthetic event. If _name_ is NULL, then the name used
+will be the same as _end_field_.
+
+*tracefs_synth_append_start_filter*() creates a filter or appends to it for the
+starting event. Depending on _type_, it will build a string of tokens for
+parenthesis or logic statements, or it may add a comparison of _field_
+to _val_ based on _compare_.
+
+If _type_ is:
+*TRACEFS_FILTER_COMPARE*     -  See below
+*TRACEFS_FILTER_AND*         -  Append "&&" to the filter
+*TRACEFS_FILTER_OR*          -  Append "||" to the filter
+*TRACEFS_FILTER_NOT*         -  Append "!" to the filter
+*TRACEFS_FILTER_OPEN_PAREN*  -  Append "(" to the filter
+*TRACEFS_FILTER_CLOSE_PAREN* -  Append ")" to the filter
+
+_field_, _compare_, and _val_ are ignored unless _type_ is equal to
+*TRACEFS_FILTER_COMPARE*, then _compare will be used for the following:
+
+*TRACEFS_COMPARE_EQ* - _field_ == _val_
+
+*TRACEFS_COMPARE_NE* - _field_ != _val_
+
+*TRACEFS_COMPARE_GT* - _field_ > _val_
+
+*TRACEFS_COMPARE_GE* - _field_ >= _val_
+
+*TRACEFS_COMPARE_LT* - _field_ < _val_
+
+*TRACEFS_COMPARE_LE* - _field_ <pass:[=] _val_
+
+*TRACEFS_COMPARE_RE* - _field_ ~ "_val_" : where _field_ is a string.
+
+*TRACEFS_COMPARE_AND* - _field_ & _val_ : where _field_ is a flags field.
+
+*tracefs_synth_append_end_filter*() is the same as *tracefs_synth_append_start_filter* but
+filters on the ending event.
+
+*tracefs_synth_free*() frees the allocated descriptor returned by
+*tracefs_synth_alloc*().
+
+RETURN VALUE
+------------
+*tracefs_synth_alloc*() returns an allocated struct tracefs_synth descriptor
+on success or NULL on error.
+
+All other functions that return an integer returns zero on success or -1
+on error.
+
+ERRORS
+------
+The following errors are for all the above calls:
+
+*EPERM* Not run as root user when required.
+
+*EINVAL* Either a parameter is not valid (NULL when it should not be)
+  or a field that is not compatible for calculations.
+
+*ENODEV* An event or one of its fields is not found.
+
+*EBADE* The fields of the start and end events are not compatible for
+  either matching or comparing.
+
+*ENOMEM* not enough memory is available.
+
+And more errors may have happened from the system calls to the system.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <tracefs.h>
+
+#define start_event "sched_waking"
+#define start_field "pid"
+
+#define end_event "sched_switch"
+#define end_field "next_pid"
+
+#define match_name "pid"
+
+static struct tracefs_synth *synth;
+
+static void make_event(void)
+{
+	struct tep_handle *tep;
+
+	/* Load all events from the system */
+	tep = tracefs_local_events(NULL);
+
+	/* Initialize the synthetic event */
+	synth = tracefs_synth_alloc(tep, "wakeup_lat",
+				    NULL, start_event,
+				    NULL, end_event,
+				    start_field, end_field,
+				    match_name);
+
+	/* The tep is no longer needed */
+	tep_free(tep);
+
+
+	/* Save the "prio" field as "prio" from the start event */
+	tracefs_synth_add_start_field(synth, "prio", NULL);
+
+	/* Save the "next_comm" as "comm" from the end event */
+	tracefs_synth_add_end_field(synth, "next_comm", "comm");
+
+	/* Save the "prev_prio" as "prev_prio" from the end event */
+	tracefs_synth_add_end_field(synth, "prev_prio", NULL);
+
+	/*
+	 * Take a microsecond time difference between end and start
+	 * and record as "delta"
+	 */
+	tracefs_synth_add_compare_field(synth, TRACEFS_TIMESTAMP_USECS,
+					TRACEFS_TIMESTAMP_USECS,
+					TRACEFS_SYNTH_DELTA_END, "delta");
+
+	/* Only record if start event "prio" is less than 100 */
+	tracefs_synth_append_start_filter(synth, TRACEFS_FILTER_COMPARE,
+					  "prio", TRACEFS_COMPARE_LT, "100");
+
+	/*
+	 * Only record if end event "next_prio" is less than 50
+	 * or the previous task's prio was not greater than or equal to 100.
+	 *   next_prio < 50 || !(prev_prio >= 100)
+	 */
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_COMPARE,
+					"next_prio", TRACEFS_COMPARE_LT, "50");
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_OR, NULL, 0, NULL);
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_NOT, NULL, 0, NULL);
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_OPEN_PAREN, NULL, 0, NULL);
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_COMPARE,
+					"prev_prio", TRACEFS_COMPARE_GE, "100");
+	/*
+	 * Note, the above only added: "next_prio < 50 || !(prev_prio >= 100"
+	 * That's because, when the synth is executed, the remaining close parenthesis
+	 * will be added. That is, the string will end up being:
+	 * "next_prio < 50 || !(prev_prio >= 100)" when one of tracefs_sync_create()
+	 * or tracefs_sync_echo_cmd() is run.
+	 */
+}
+
+/* Display how to create the synthetic event */
+static void show_event(void)
+{
+	struct trace_seq s;
+
+	trace_seq_init(&s);
+
+	tracefs_synth_echo_cmd(&s, synth);
+	trace_seq_terminate(&s);
+	trace_seq_do_printf(&s);
+	trace_seq_destroy(&s);
+}
+
+int main (int argc, char **argv)
+{
+	make_event();
+
+	if (argc > 1) {
+		if (!strcmp(argv[1], "create")) {
+			/* Create the synthetic event */
+			tracefs_synth_create(synth);
+		} else if (!strcmp(argv[1], "delete")) {
+			/* Delete the synthetic event */
+			tracefs_synth_destroy(synth);
+		} else {
+			printf("usage: %s [create|delete]\n", argv[0]);
+			exit(-1);
+		}
+	} else
+		show_event();
+
+	tracefs_synth_free(synth);
+
+	return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*tracefs_synth_create*(3),
+*tracefs_synth_destroy*(3),
+*tracfes_synth_echo_cmd*(3),
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+*tracefs_hist_alloc*(3),
+*tracefs_hist_alloc_2d*(3),
+*tracefs_hist_alloc_nd*(3),
+*tracefs_hist_free*(3),
+*tracefs_hist_add_key*(3),
+*tracefs_hist_add_value*(3),
+*tracefs_hist_add_name*(3),
+*tracefs_hist_start*(3),
+*tracefs_hist_destory*(3),
+*tracefs_hist_add_sort_key*(3),
+*tracefs_hist_sort_key_direction*(3),
+*tracefs_synth_create*(3),
+*tracefs_synth_destroy*(3),
+*tracefs_synth_complete*(3),
+*tracefs_synth_trace*(3),
+*tracefs_synth_snapshot*(3),
+*tracefs_synth_save*(3),
+*tracefs_synth_echo_cmd*(3),
+*tracefs_synth_get_start_hist*(3),
+*tracefs_synth_get_name*(3),
+*tracefs_synth_raw_fmt*(3),
+*tracefs_synth_show_event*(3),
+*tracefs_synth_show_start_hist*(3),
+*tracefs_synth_show_end_hist*(3),
+*tracefs_synth_get_event*(3),
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-synth2.txt b/Documentation/libtracefs-synth2.txt
new file mode 100644
index 0000000..7e8e6cc
--- /dev/null
+++ b/Documentation/libtracefs-synth2.txt
@@ -0,0 +1,281 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_synth_create, tracefs_synth_destroy, tracefs_synth_complete,
+tracefs_synth_trace, tracefs_synth_snapshot, tracefs_synth_save
+- Creation of synthetic events
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_synth_create*(struct tracefs_synth pass:[*]_synth_);
+int *tracefs_synth_destroy*(struct tracefs_synth pass:[*]_synth_);
+bool *tracefs_synth_complete*(struct tracefs_synth pass:[*]_synth_);
+
+int *tracefs_synth_trace*(struct tracefs_synth pass:[*]_synth_,
+			enum tracefs_synth_handler _type_, const char pass:[*]_var_);
+int *tracefs_synth_snapshot*(struct tracefs_synth pass:[*]_synth_,
+			   enum tracefs_synth_handler _type_, const char pass:[*]_var_);
+int *tracefs_synth_save*(struct tracefs_synth pass:[*]_synth_,
+		       enum tracefs_synth_handler _type_, const char pass:[*]_var_,
+		       char pass:[**]_save_fields_);
+--
+
+DESCRIPTION
+-----------
+Synthetic events are dynamic events that are created by matching
+two other events which triggers a synthetic event. One event is the starting
+event which some field is recorded, and when the second event is executed,
+if it has a field (or fields) that matches the starting event's field (or fields)
+then it will trigger the synthetic event. The field values other than the matching
+fields may be passed from the starting event to the end event to perform calculations
+on, or to simply pass as a parameter to the synthetic event.
+
+One common use case is to set "sched_waking" as the starting event. This event is
+triggered when a process is awoken. Then set "sched_switch" as the ending event.
+This event is triggered when a new task is scheduled on the CPU. By setting
+the "common_pid" of both events as the matching fields, the time between the
+two events is considered the wake up latency of that process. Use *TRACEFS_TIMESTAMP*
+as a field for both events to calculate the delta in nanoseconds, or use
+*TRACEFS_TIMESTAMP_USECS* as the compare fields for both events to calculate the
+delta in microseconds. This is used as the example below.
+
+*tracefs_synth_create*() creates the synthetic event in the system. The synthetic events apply
+across all instances. A synthetic event must be created with *tracefs_synth_alloc*(3) before
+it can be created.
+
+*tracefs_synth_destroy*() destroys the synthetic event. It will attempt to stop the running of it in
+its instance (top by default), but if its running in another instance this may fail as busy.
+
+*tracefs_synth_complete*() returns true if the synthetic event _synth_ has both
+a starting and ending event.
+
+*tracefs_synth_trace*() Instead of doing just a trace on matching of the start and
+end events, do the _type_ handler where *TRACEFS_SYNTH_HANDLE_MAX* will do a trace
+when the given variable _var_ hits a new max for the matching keys. Or
+*TRACEFS_SYNTH_HANDLE_CHANGE* for when the _var_ changes. _var_ must be one of
+the _name_ elements used in *tracefs_synth_add_end_field*(3).
+
+*tracefs_synth_snapshot*() When the given variable _var_ is either a new max if
+_handler_ is *TRACEFS_SYNTH_HANDLE_MAX* or simply changed if *TRACEFS_SYNTH_HANDLE_CHANGE*
+then take a "snapshot" of the buffer. The snapshot moves the normal "trace" buffer
+into a "snapshot" buffer, that can be accessed via the "snapshot" file in the
+top level tracefs directory, or one of the instances.  _var_ changes. _var_ must be one of
+the _name_ elements used in *tracefs_synth_add_end_field*(3).
+
+*tracefs_synth_save*() When the given variable _var_ is either a new max if
+_handler_ is *TRACEFS_SYNTH_HANDLE_MAX* or simpy changed if *TRACEFS_SYNTH_HANDLE_CHANGE*
+then save the given _save_fields_ list. The fields will be stored in the histogram
+"hist" file of the event that can be retrieved with *tracefs_event_file_read*(3).
+_var_ must be one of the _name_ elements used in *tracefs_synth_add_end_field*(3).
+
+RETURN VALUE
+------------
+All functions return zero on success or -1 on error.
+
+ERRORS
+------
+The following errors are for all the above calls:
+
+*EPERM* Not run as root user when required.
+
+*EINVAL* Either a parameter is not valid (NULL when it should not be)
+  or a field that is not compatible for calculations.
+
+*ENODEV* An event or one of its fields is not found.
+
+*EBADE* The fields of the start and end events are not compatible for
+  either matching or comparing.
+
+*ENOMEM* not enough memory is available.
+
+And more errors may have happened from the system calls to the system.
+
+EXAMPLE
+-------
+See *tracefs_sql*(3) for a more indepth use of some of this code.
+
+[source,c]
+--
+#include <stdlib.h>
+#include <tracefs.h>
+
+#define start_event "sched_waking"
+#define start_field "pid"
+
+#define end_event "sched_switch"
+#define end_field "next_pid"
+
+#define match_name "pid"
+
+static struct tracefs_synth *synth;
+
+static void make_event(void)
+{
+	struct tep_handle *tep;
+
+	/* Load all events from the system */
+	tep = tracefs_local_events(NULL);
+
+	/* Initialize the synthetic event */
+	synth = tracefs_synth_alloc(tep, "wakeup_lat",
+				    NULL, start_event,
+				    NULL, end_event,
+				    start_field, end_field,
+				    match_name);
+
+	/* The tep is no longer needed */
+	tep_free(tep);
+
+
+	/* Save the "prio" field as "prio" from the start event */
+	tracefs_synth_add_start_field(synth, "prio", NULL);
+
+	/* Save the "next_comm" as "comm" from the end event */
+	tracefs_synth_add_end_field(synth, "next_comm", "comm");
+
+	/* Save the "prev_prio" as "prev_prio" from the end event */
+	tracefs_synth_add_end_field(synth, "prev_prio", NULL);
+
+	/*
+	 * Take a microsecond time difference between end and start
+	 * and record as "delta"
+	 */
+	tracefs_synth_add_compare_field(synth, TRACEFS_TIMESTAMP_USECS,
+					TRACEFS_TIMESTAMP_USECS,
+					TRACEFS_SYNTH_DELTA_END, "delta");
+
+	/* Only record if start event "prio" is less than 100 */
+	tracefs_synth_append_start_filter(synth, TRACEFS_FILTER_COMPARE,
+					  "prio", TRACEFS_COMPARE_LT, "100");
+
+	/*
+	 * Only record if end event "next_prio" is less than 50
+	 * or the previous task's prio was not greater than or equal to 100.
+	 *   next_prio < 50 || !(prev_prio >= 100)
+	 */
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_COMPARE,
+					"next_prio", TRACEFS_COMPARE_LT, "50");
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_OR, NULL, 0, NULL);
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_NOT, NULL, 0, NULL);
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_OPEN_PAREN, NULL, 0, NULL);
+	tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_COMPARE,
+					"prev_prio", TRACEFS_COMPARE_GE, "100");
+	/*
+	 * Note, the above only added: "next_prio < 50 || !(prev_prio >= 100"
+	 * That's because, when the synth is executed, the remaining close parenthesis
+	 * will be added. That is, the string will end up being:
+	 * "next_prio < 50 || !(prev_prio >= 100)" when one of tracefs_sync_create()
+	 * or tracefs_sync_echo_cmd() is run.
+	 */
+}
+
+/* Display how to create the synthetic event */
+static void show_event(void)
+{
+	struct trace_seq s;
+
+	trace_seq_init(&s);
+
+	tracefs_synth_echo_cmd(&s, synth);
+	trace_seq_terminate(&s);
+	trace_seq_do_printf(&s);
+	trace_seq_destroy(&s);
+}
+
+int main (int argc, char **argv)
+{
+	make_event();
+
+	if (argc > 1) {
+		if (!strcmp(argv[1], "create")) {
+			/* Create the synthetic event */
+			tracefs_synth_create(synth);
+		} else if (!strcmp(argv[1], "delete")) {
+			/* Delete the synthetic event */
+			tracefs_synth_destroy(synth);
+		} else {
+			printf("usage: %s [create|delete]\n", argv[0]);
+			exit(-1);
+		}
+	} else
+		show_event();
+
+	tracefs_synth_free(synth);
+
+	return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1),
+*tracefs_hist_alloc*(3),
+*tracefs_hist_alloc_2d*(3),
+*tracefs_hist_alloc_nd*(3),
+*tracefs_hist_free*(3),
+*tracefs_hist_add_key*(3),
+*tracefs_hist_add_value*(3),
+*tracefs_hist_add_name*(3),
+*tracefs_hist_start*(3),
+*tracefs_hist_destory*(3),
+*tracefs_hist_add_sort_key*(3),
+*tracefs_hist_sort_key_direction*(3),
+*tracefs_synth_alloc*(3),
+*tracefs_synth_add_match_field*(3),
+*tracefs_synth_add_compare_field*(3),
+*tracefs_synth_add_start_field*(3),
+*tracefs_synth_add_end_field*(3),
+*tracefs_synth_append_start_filter*(3),
+*tracefs_synth_append_end_filter*(3),
+*tracefs_synth_free*(3),
+*tracefs_synth_echo_cmd*(3),
+*tracefs_synth_get_start_hist*(3),
+*tracefs_synth_get_name*(3),
+*tracefs_synth_raw_fmt*(3),
+*tracefs_synth_show_event*(3),
+*tracefs_synth_show_start_hist*(3),
+*tracefs_synth_show_end_hist*(3),
+*tracefs_synth_get_event*(3),
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-traceon.txt b/Documentation/libtracefs-traceon.txt
new file mode 100644
index 0000000..28c79ed
--- /dev/null
+++ b/Documentation/libtracefs-traceon.txt
@@ -0,0 +1,151 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_trace_is_on, tracefs_trace_on, tracefs_trace_off, tracefs_trace_on_get_fd,
+tracefs_trace_on_fd, tracefs_trace_off_fd - Functions to enable or disable tracing.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_trace_is_on*(struct tracefs_instance pass:[*]_instance_);
+int *tracefs_trace_on*(struct tracefs_instance pass:[*]_instance_);
+int *tracefs_trace_off*(struct tracefs_instance pass:[*]_instance_);
+int *tracefs_trace_on_get_fd*(struct tracefs_instance pass:[*]_instance_);
+int *tracefs_trace_on_fd*(int _fd_);
+int *tracefs_trace_off_fd*(int _fd_);
+--
+
+DESCRIPTION
+-----------
+This set of functions can be used to check, enable or disable writing to the ring buffer in
+the given trace instance. The tracing is enabled when writing to the ring buffer is enabled.
+
+The *tracefs_trace_is_on()* function checks if tracing is enabled for the given _instance_. If
+_instance_ is NULL, the top instance is used.
+
+The *tracefs_trace_on()* and *tracefs_trace_off()* functions set the tracing in the _instance_
+to enable or disable state. If _instance_ is NULL, the top instance is used.
+
+The *tracefs_trace_on_get_fd()* function returns a file descriptor to the "tracing_on" file from
+the given _instance_. If _instance_ is NULL, the top trace instance is used. The returned descriptor
+can be used for fast enabling or disabling the tracing of the instance.
+
+The *tracefs_trace_on_fd()* and *tracefs_trace_off_fd()* functions set the tracing state to enable
+or disable using the given _fd_. This file descriptor must be opened for writing with
+*tracefs_trace_on_get_fd*(3) of the desired trace instance. These functions are faster than
+*tracefs_trace_on* and *tracefs_trace_off*.
+
+RETURN VALUE
+------------
+The *tracefs_trace_is_on()* function returns 0 if tracing is disable, 1 if it is enabled or
+-1 in case of an error.
+
+The *tracefs_trace_on_get_fd()* function returns a file descriptor to "tracing_on"
+file for reading and writing, which must be closed wuth close(). In case of an error -1 is returned.
+
+The *tracefs_trace_on()*, *tracefs_trace_off()*, *tracefs_trace_on_fd()* and
+*tracefs_trace_off_fd()* functions return -1 in case of an error or 0 otherwise.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+
+	int ret;
+
+	ret = tracefs_trace_is_on(NULL);
+	if (ret == 0) {
+		/* Tracing is disabled in the top instance */
+	} else if (ret == 1) {
+		/* Tracing is enabled in the top instance */
+	} else {
+		/* Error getting tracing state of the top instance */
+	}
+
+	...
+
+	if (!tracefs_trace_on(NULL)) {
+	    /* Enabled tracing in the top instance */
+
+	    ...
+
+	    if (!tracefs_trace_off(NULL)) {
+	    	/* Disabled tracing in the top instance */
+	    } else {
+	    	/* Error disabling tracing in the top instance */
+	    }
+	} else {
+		/* Error enabling tracing in the top instance */
+	}
+
+	...
+
+	int fd = tracefs_trace_on_get_fd(NULL);
+
+	if (fd < 0) {
+		/* Error opening tracing_on file */
+	}
+	...
+	if (!tracefs_trace_on_fd(fd)) {
+	    /* Enabled tracing in the top instance */
+
+	    ...
+
+	    if (!tracefs_trace_off_fd(fd)) {
+	    	/* Disabled tracing in the top instance */
+	    } else {
+	    	/* Error disabling tracing in the top instance */
+	    }
+	} else {
+		/* Error enabling tracing in the top instance */
+	}
+
+	...
+
+	close(fd);
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2021 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-tracer.txt b/Documentation/libtracefs-tracer.txt
new file mode 100644
index 0000000..ea57962
--- /dev/null
+++ b/Documentation/libtracefs-tracer.txt
@@ -0,0 +1,221 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_tracer_set, tracefs_tracer_clear - Enable or disable a tracer in an instance or the top level
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_tracer_set*(struct tracefs_instance pass:[*]_instance_, enum tracefs_tracers _tracer_);
+int *tracefs_tracer_set*(struct tracefs_instance pass:[*]_instance_, enum tracefs_tracers _tracer_, const char pass:[*]_name_);
+int *tracefs_tracer_clear*(struct tracefs_instance pass:[*]_instance_);
+--
+
+DESCRIPTION
+-----------
+*tracefs_tracer_set* enables a tracer in the given instance, defined by the
+_instance_ parameter. If _instance_ is NULL, then the top level instance is
+changed. If _tracer_ is set to *TRACFES_TRACER_CUSTOM* then a _name_
+string must be passed in as the third parameter, and that is written into the
+instance to enable the tracer with that name. This is useful for newer or
+custom kernels that contain tracers that are not yet identified by the
+tracefs_tracers enum.
+
+*tracefs_tracer_clear* disables the tracer for the given instance defined by
+the _instance_ variable, or the top level instance if it is NULL.
+This is the same as calling *tracefs_tracer_set* with TRACEFS_TRACER_NOP as
+the _tracer_ parameter.
+
+TRACEFS_TRACER ENUMS
+--------------------
+
+The currently defined enums that are accepted are:
+
+*TRACEFS_TRACER_NOP* :
+This is the idle tracer, which does nothing and is used to clear any
+active tracer.
+
+*TRACEFS_TRACER_FUNCTION* :
+Enables most functions in the kernel to be traced.
+
+*TRACEFS_TRACER_FUNCTION_GRAPH* :
+Enables most functions in the kernel to be traced as well as the return
+of the function.
+
+*TRACEFS_TRACER_IRQSOFF* :
+Tracers the latency of interrupts disabled.
+
+*TRACEFS_TRACER_PREEMPTOFF* :
+Tracers the latency of preemption disabled (the time in the kernel that
+tasks can not be scheduled from the CPU).
+
+*TRACEFS_TRACER_PREEMPTIRQSOFF* :
+Traces the combined total latency of when interrupts are disabled as well as when
+preemption is disabled.
+
+*TRACEFS_TRACER_WAKEUP* :
+Traces the latency of when the highest priority task takes to wake up.
+
+*TRACEFS_TRACER_WAKEUP_RT* :
+Traces the latency of when the highest priority real-time task takes to wake up.
+All other tasks are ignored.
+
+*TRACEFS_TRACER_WAKEUP_DL* :
+Traces the latency of when the highest priority DEADLINE task takes to wake up.
+All other tasks are ignored.
+
+*TRACEFS_TRACER_MMIOTRACE* :
+Traces the interaction of devices with the kernel.
+
+*TRACEFS_TRACER_HWLAT* :
+Detects latency caused by the hardware that is outside the scope of the kernel.
+
+*TRACEFS_TRACER_BRANCH* :
+Traces when likely or unlikely branches are taken.
+
+*TRACEFS_TRACER_BLOCK* :
+Special tracer for the block devices.
+
+Note that the above tracers may not be available in the kernel and
+*tracefs_tracer_set()* will return an error with errno set to ENODEV,
+if the kernel does not support the _tracer_ option, or the custom one
+if TRACEFS_TRACER_CUSTOM is used.
+
+RETURN VALUE
+------------
+Returns 0 on success, or -1 on error.
+
+ERRORS
+------
+
+*tracefs_tracer_set*() can fail with the following errors:
+
+*EINVAL* The _tracer_ parameter is outside the scope of what is defined.
+
+*ENOMEM* Memory allocation error.
+
+*ENOENT* Tracers are not supported on the running kernel.
+
+*ENODEV* The specified tracer is not supported on the running kernel.
+
+Other errors may also happen caused by internal system calls.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <tracefs.h>
+
+int main(int argc, char *argv[])
+{
+	struct tracefs_instance *inst = NULL;
+	enum tracefs_tracers t = TRACEFS_TRACER_NOP;
+	const char *buf = NULL;
+	const char *cust;
+	int ret;
+	int ch;
+
+	while ((ch = getopt(argc, argv, "nfgiwdc:B:")) > 0) {
+		switch (ch) {
+		case 'f': t = TRACEFS_TRACER_FUNCTION; break;
+		case 'g': t = TRACEFS_TRACER_FUNCTION_GRAPH; break;
+		case 'i': t = TRACEFS_TRACER_PREEMPTIRQSOFF; break;
+		case 'w': t = TRACEFS_TRACER_WAKEUP_RT; break;
+		case 'd': t = TRACEFS_TRACER_WAKEUP_DL; break;
+		case 'c':
+			t = TRACEFS_TRACER_CUSTOM;
+			cust = optarg;
+			break;
+		case 'B':
+			buf = optarg;
+			break;
+		case 'n':
+			/* nop */
+			break;
+		default:
+			printf("Unknow arg %c\n", ch);
+			exit(-1);
+		}
+	}
+
+	if (buf) {
+		inst = tracefs_instance_create(buf);
+		if (!inst) {
+			printf("failed to create instance\n");
+			exit(-1);
+		}
+	}
+
+	if (t == TRACEFS_TRACER_CUSTOM)
+		ret = tracefs_tracer_set(inst, t, cust);
+	else
+		ret = tracefs_tracer_set(inst, t);
+
+	if (ret < 0) {
+		if (inst) {
+			tracefs_instance_destroy(inst);
+			tracefs_instance_free(inst);
+		}
+		if (errno == ENODEV)
+			printf("Tracer not supported by kernel\n");
+		else
+			perror("Error");
+		exit(-1);
+	}
+
+	if (inst)
+		tracefs_instance_free(inst);
+
+	exit(0);
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-uprobes.txt b/Documentation/libtracefs-uprobes.txt
new file mode 100644
index 0000000..1ca80da
--- /dev/null
+++ b/Documentation/libtracefs-uprobes.txt
@@ -0,0 +1,189 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_uprobe_alloc,tracefs_uretprobe_alloc - Allocate new user (return) probe
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_dynevent pass:[*]
+*tracefs_uprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+		     const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_)
+struct tracefs_dynevent pass:[*]
+*tracefs_uretprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+		     const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_)
+--
+
+DESCRIPTION
+-----------
+*tracefs_uprobe_alloc*() allocates a new uprobe context. It will be in the _system_ group
+(or uprobes if _system_ is NULL) and with _event_ name. The uprobe will be attached to _offset_
+within the _file_. The list of arguments, described in _fetchargs_, will be fetched  with the uprobe.
+The returned pointer to the user probe context must be freed with *tracefs_dynevent_free*().
+The ubrobe is not configured in the system, tracefs_dynevent_* set of APIs can be used to configure
+it.
+
+The *tracefs_uretprobe_alloc*() behaves the same as *tracefs_uprobe_alloc*(), the only difference is
+that it allocates context to user return probe (uretprobe).
+
+RETURN VALUE
+------------
+The *tracefs_uprobe_alloc*() and *tracefs_uretprobe_alloc*() APIs return a pointer to an allocated
+tracefs_dynevent structure, describing the user probe. This pointer must be freed with
+*tracefs_dynevent_free*(3). Note, this only allocates a descriptor representing the uprobe. It does
+not modify the running system. On error NULL is returned.
+
+EXAMPLE
+-------
+[source,c]
+--
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <tracefs.h>
+
+static int callback(struct tep_event *event, struct tep_record *record,
+		    int cpu, void *data)
+{
+	struct trace_seq seq;
+
+	trace_seq_init(&seq);
+	tep_print_event(event->tep, &seq, record, "%d-%s: %s",
+			TEP_PRINT_PID, TEP_PRINT_COMM, TEP_PRINT_NAME);
+	trace_seq_puts(&seq, "'\n");
+
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+	trace_seq_destroy(&seq);
+
+	return 0;
+}
+
+static pid_t run_exec(char **argv, char **env)
+{
+	pid_t pid;
+
+	pid = fork();
+	if (pid)
+		return pid;
+
+	execve(argv[0], argv, env);
+	perror("exec");
+	exit(-1);
+}
+
+const char *myprobe = "my_urobes";
+
+int main (int argc, char **argv, char **env)
+{
+	struct tracefs_dynevent *uprobe, *uretprobe;
+	struct tep_handle *tep;
+	struct tracefs_instance *instance;
+	const char *sysnames[] = { myprobe, NULL };
+	long addr;
+	pid_t pid;
+
+	if (argc < 3) {
+		printf("usage: %s file_offset command\n", argv[0]);
+		exit(-1);
+	}
+	addr = strtol(argv[1], NULL, 0);
+
+	instance = tracefs_instance_create("exec_open");
+	if (!instance) {
+		perror("creating instance");
+		exit(-1);
+	}
+
+	tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_UPROBE|TRACEFS_DYNEVENT_URETPROBE, true);
+
+	uprobe = tracefs_uprobe_alloc(myprobe, "user_probe", argv[2], addr, NULL);
+	uretprobe = tracefs_uretprobe_alloc(myprobe, "user_retprobe", argv[2], addr, NULL);
+	if (!uprobe || !uretprobe) {
+		perror("allocating user probes");
+		exit(-1);
+	}
+
+	if (tracefs_dynevent_create(uprobe) ||
+	    tracefs_dynevent_create(uretprobe)) {
+		perror("creating user probes");
+		exit(-1);
+	}
+
+	tep = tracefs_local_events_system(NULL, sysnames);
+	if (!tep) {
+		perror("reading events");
+		exit(-1);
+	}
+
+	tracefs_event_enable(instance, myprobe, "user_probe");
+	tracefs_event_enable(instance, myprobe, "user_retprobe");
+
+	pid = run_exec(&argv[2], env);
+
+	/* Let the child start to run */
+	sched_yield();
+
+	do {
+		tracefs_load_cmdlines(NULL, tep);
+		tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, NULL);
+	} while (waitpid(pid, NULL, WNOHANG) != pid);
+
+	/* disable and destroy the events */
+	tracefs_dynevent_destroy(uprobe, true);
+	tracefs_dynevent_destroy(uretprobe, true);
+	tracefs_dynevent_free(uprobe);
+	tracefs_dynevent_free(uretprobe);
+	tracefs_instance_destroy(instance);
+
+	return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2022 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-utils.txt b/Documentation/libtracefs-utils.txt
new file mode 100644
index 0000000..ab16cc6
--- /dev/null
+++ b/Documentation/libtracefs-utils.txt
@@ -0,0 +1,139 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_tracers, tracefs_tracer_available, tracefs_get_clock, tracefs_list_free,
+tracefs_list_add, tracefs_list_size - Helper functions for working with trace file system.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+char pass:[*]pass:[*]*tracefs_tracers*(const char pass:[*]_tracing_dir_);
+bool *tracefs_tracer_available*(const char pass:[*]_tracing_dir_, const char pass:[*]_tracer_);
+char pass:[*]*tracefs_get_clock*(struct tracefs_instance pass:[*]_instance_);
+void *tracefs_list_free*(char pass:[*]pass:[*]_list_);
+char pass:[**]*tracefs_list_add*(char **_list_, const char *_string_);
+int *tracefs_list_size*(char pass:[**]_list_);
+--
+
+DESCRIPTION
+-----------
+Various useful functions for working with trace file system.
+
+The *tracefs_tracers()* function returns array of strings with the
+names of supported tracer plugins, located in the given _tracing_dir_
+directory. This could be NULL or the location of the tracefs mount point
+for the trace systems of the local machine, or it may be a path to a copy
+of the tracefs directory from another machine. The last entry in the array
+as a NULL pointer. The array must be freed with *tracefs_list_free()* API.
+
+The *tracefs_tracer_available()* returns true if the _tracer_ is available
+in the given _tracing_dir_director_, and false otherwise.
+
+The *tracefs_get_clock()* function returns name of the current trace clock,
+used in the given _instance_. If _instance_ is NULL, the clock of the main
+trace instance is returned. The returned string must be freed with free().
+
+The *tracefs_list_free()* function frees an array of strings, returned by
+*tracefs_event_systems()*, *tracefs_system_events()* and *tracefs_tracers()*
+APIs.
+
+The *tracefs_list_add()* function adds _string_ to the string _list_. If
+_list_ is NULL, then a new list is created with the first element a copy
+of _string_, and the second element will be a NULL pointer.  If _list_ is
+not NULL, then it is reallocated to include a new element and a NULL terminator,
+and the new allocated array is returned. The list passed in should be ignored,
+as it wil be freed if a new one was allocated.
+
+The *tracefs_list_size()* is a fast way to find out the number of elements
+in a string array that is to be freed with *tracefs_list_free()*. Note, this
+depends on meta data that is created for these lists and will not work on
+normal string arrays like argv.
+
+RETURN VALUE
+------------
+The *tracefs_tracers()* returns array of strings. The last element in that
+array is a NULL pointer. The array must be freed with *tracefs_list_free()* API.
+In case of an error, NULL is returned.
+
+The *tracefs_tracer_available()* returns true if the _tracer_ is available,
+and false otherwise.
+
+The *tracefs_get_clock()* returns string, that must be freed with free(), or NULL
+in case of an error.
+
+The *tracefs_list_add()* returns an allocated string array that must be freed
+with *tracefs_list_free()* on success or NULL on error. If NULL is returned,
+then the passed in _list_ is untouched. Thus, *tracefs_list_add()* should be
+treated similarly to *realloc*(3).
+
+The *tracefs_list_size()* returns the number of strings in the _list_. The
+passed in list must be one that is to be freed with *tracefs_list_free()*
+as the list has meta data that is used to determine the size and this does
+not work on any normal string array like argv.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <tracefs.h>
+
+char **tracers = tracefs_tracers(NULL);
+
+	if (tracers) {
+		/* Got tracer plugins from the top trace instance */
+		...
+		tracefs_list_free(tracers);
+	}
+....
+char *clock = tracefs_get_clock(NULL);
+
+	if (clock) {
+		/* Got current trace clock of the top trace instance */
+		...
+		free(clock);
+	}
+--
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt
new file mode 100644
index 0000000..c3f448d
--- /dev/null
+++ b/Documentation/libtracefs.txt
@@ -0,0 +1,344 @@
+libtracefs(3)
+=============
+
+NAME
+----
+libtracefs - Linux kernel trace file system library
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+Locations of tracing files and directories:
+	char pass:[*]*tracefs_get_tracing_file*(const char pass:[*]_name_);
+	void *tracefs_put_tracing_file*(char pass:[*]_name_);
+	const char pass:[*]*tracefs_tracing_dir*(void);
+	const char pass:[*]*tracefs_debug_dir*(void);
+	int *tracefs_set_tracing_dir*(char pass:[*]_tracing_dir_)
+	int *tracefs_tracing_dir_is_mounted*(bool _mount_, const char pass:[**]_path_);
+
+Trace instances:
+	struct tracefs_instance pass:[*]*tracefs_instance_create*(const char pass:[*]_name_);
+	int *tracefs_instance_destroy*(struct tracefs_instance pass:[*]_instance_);
+	struct tracefs_instance pass:[*]*tracefs_instance_alloc*(const char pass:[*]_tracing_dir_, const char pass:[*]_name_);
+	void *tracefs_instance_free*(struct tracefs_instance pass:[*]_instance_);
+	char pass:[**]*tracefs_instances*(const char pass:[*]_regex_);
+	bool *tracefs_instance_is_new*(struct tracefs_instance pass:[*]_instance_);
+	bool *tracefs_file_exists*(struct tracefs_instance pass:[*]_instance_, char pass:[*]_name_);
+	bool *tracefs_dir_exists*(struct tracefs_instance pass:[*]_instance_, char pass:[*]_name_);
+	char pass:[*]*tracefs_instance_get_file*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_);
+	char pass:[*]*tracefs_instance_get_dir*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_instance_file_open*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, int _mode_);
+	int *tracefs_instance_file_write*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, const char pass:[*]_str_);
+	int *tracefs_instance_file_append*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, const char pass:[*]_str_);
+	int *tracefs_instance_file_clear*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_);
+	char pass:[*]*tracefs_instance_file_read*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, int pass:[*]_psize_);
+	int *tracefs_instance_file_read_number*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, long long int pass:[*]_res_);
+	const char pass:[*]*tracefs_instance_get_name*(struct tracefs_instance pass:[*]_instance_);
+	const char pass:[*]*tracefs_instance_get_trace_dir*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_instances_walk*(int (pass:[*]_callback_)(const char pass:[*], void pass:[*]), void pass:[*]_context)_;
+	bool *tracefs_instance_exists*(const char pass:[*]_name_);
+	int *tracefs_instance_set_affinity*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_cpu_str_);
+	int *tracefs_instance_set_affinity_set*(struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_set_, size_t _set_size_);
+	int *tracefs_instance_set_affinity_raw*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_mask_);
+	char pass:[*]*tracefs_instance_get_affinity*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_instance_get_affinity_set*(struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_set_, size_t _set_size_);
+	char pass:[*]*tracefs_instance_get_affinity_raw*(struct tracefs_instance pass:[*]_instance_);
+	size_t *tracefs_instance_get_buffer_size*(struct tracefs_instance pass:[*]_instance_, int _cpu_);
+	int *tracefs_instance_set_buffer_size*(struct tracefs_instance pass:[*]_instance_, size_t _size_, int _cpu_);
+
+Trace events:
+	char pass:[*]pass:[*]*tracefs_event_systems*(const char pass:[*]_tracing_dir_);
+	char pass:[*]pass:[*]*tracefs_system_events*(const char pass:[*]_tracing_dir_, const char pass:[*]_system_);
+	int *tracefs_event_enable*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_,
+                           const char pass:[*]_event_);
+	int *tracefs_event_disable*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_,
+                            const char pass:[*]_event_);
+	enum tracefs_enable_state *tracefs_event_is_enabled*(struct tracefs_instance pass:[*]_instance_,
+				 const char pass:[*]_system_, const char pass:[*]_event_);
+	int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_cpus_, int _cpu_size_, int (pass:[*]_callback_)(struct tep_event pass:[*], struct tep_record pass:[*], int, void pass:[*]), void pass:[*]_callback_context_);
+	void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_follow_event*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
+				  const char pass:[*]_system_, const char pass:[*]_event_name_,
+				  int (pass:[*]_callback_)(struct tep_event pass:[*],
+						  struct tep_record pass:[*],
+						  int, void pass:[*]),
+				  void pass:[*]_callback_data_);
+	int *tracefs_follow_missed_events*(struct tracefs_instance pass:[*]_instance_,
+				  int (pass:[*]_callback_)(struct tep_event pass:[*],
+						  struct tep_record pass:[*],
+						  int, void pass:[*]),
+				  void pass:[*]_callback_data_);
+	struct tep_handle pass:[*]*tracefs_local_events*(const char pass:[*]_tracing_dir_);
+	struct tep_handle pass:[*]*tracefs_local_events_system*(const char pass:[*]_tracing_dir_, const char pass:[*] const pass:[*]_sys_names_);
+	int *tracefs_fill_local_events*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_, int pass:[*]_parsing_failures_);
+	int *tracefs_load_cmdlines*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_);
+	char pass:[*]*tracefs_event_get_file*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+			     const char pass:[*]_file_);
+	char pass:[*]*tracefs_event_file_read*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+			      const char pass:[*]_file_, int pass:[*]_psize_);
+	int *tracefs_event_file_write*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+			     const char pass:[*]_file_, const char pass:[*]_str_);
+	int *tracefs_event_file_append*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+			      const char pass:[*]_file_, const char pass:[*]_str_);
+	int *tracefs_event_file_clear*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+			     const char pass:[*]_file_);
+	bool *tracefs_event_file_exists*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_,
+
+Event filters:
+	int *tracefs_filter_string_append*(struct tep_event pass:[*]_event_, char pass:[**]_filter_,
+					 struct tracefs_filter _type_, const char pass:[*]_field_,
+					 enum tracefs_synth_compare _compare_, const char pass:[*]_val_);
+	int *tracefs_filter_string_verify*(struct tep_event pass:[*]_event_, const char pass:[*]_filter_, char pass:[**]_err_);
+	int *tracefs_event_filter_apply*(struct tracefs_instance pass:[*]_instance_, struct tep_event pass:[*]_event_, const char pass:[*]_filter_);
+	int *tracefs_event_filter_clear*(struct tracefs_instance pass:[*]_instance_, struct tep_event pass:[*]_event_);
+
+Function filters:
+	int *tracefs_function_filter*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_filter_, const char pass:[*]_module_, int _flags_);
+	int *tracefs_function_notrace*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_filter_, const char pass:[*]_module_, int _flags_);
+	int *tracefs_filter_functions*(const char pass:[*]_filter_, const char pass:[*]_module_, char pass:[*]pass:[*]pass:[*]_list_);
+
+Trace helper functions:
+	void *tracefs_list_free*(char pass:[*]pass:[*]_list_);
+	char pass:[**]*tracefs_list_add*(char **_list_, const char *_string_);
+	int *tracefs_list_size*(char pass:[**]_list_);
+	char pass:[*]*tracefs_get_clock*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_trace_is_on*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_trace_on*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_trace_off*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_trace_on_get_fd*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_trace_on_fd*(int _fd_);
+	int *tracefs_trace_off_fd*(int _fd_);
+
+Trace stream:
+	ssize_t *tracefs_trace_pipe_stream*(int _fd_, struct tracefs_instance pass:[*]_instance_, int _flags_);
+	ssize_t *tracefs_trace_pipe_print*(struct tracefs_instance pass:[*]_instance_, int _flags_);
+	void *tracefs_trace_pipe_stop*(struct tracefs_instance pass:[*]_instance_);
+
+Trace options:
+	const struct tracefs_options_mask pass:[*]*tracefs_options_get_supported*(struct tracefs_instance pass:[*]_instance_);
+	bool *tracefs_option_is_supported*(struct tracefs_instance pass:[*]_instance_, enum tracefs_option_id _id_);
+	const struct tracefs_options_mask pass:[*]*tracefs_options_get_enabled*(struct tracefs_instance pass:[*]_instance_);
+	bool *tracefs_option_is_enabled*(struct tracefs_instance pass:[*]_instance_, enum tracefs_option_id _id_);
+	bool *tracefs_option_mask_is_set*(const struct tracefs_options_mask *options, enum tracefs_option_id id);
+	int *tracefs_option_enable*(struct tracefs_instance pass:[*]_instance_, enum tracefs_option_id _id_);
+	int *tracefs_option_disable*(struct tracefs_instance pass:[*]_instance_, enum tracefs_option_id _id_);
+	const char pass:[*]*tracefs_option_name*(enum tracefs_option_id _id_);
+	enum tracefs_option_id *tracefs_option_id*(const char pass:[*]_name_);
+
+Ftrace tracers:
+	char pass:[*]pass:[*]*tracefs_tracers*(const char pass:[*]_tracing_dir_);
+	bool *tracefs_tracer_available*(const char pass:[*]_tracing_dir_, const char pass:[*]_tracer_);
+	int *tracefs_tracer_set*(struct tracefs_instance pass:[*]_instance_, enum tracefs_tracers _tracer_);
+	int *tracefs_tracer_set*(struct tracefs_instance pass:[*]_instance_, enum tracefs_tracers _tracer_, const char pass:[*]_name_);
+	int *tracefs_tracer_clear*(struct tracefs_instance pass:[*]_instance_);
+
+Writing data in the trace buffer:
+	int *tracefs_print_init*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_printf*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_fmt_, _..._);
+	int *tracefs_vprintf*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_fmt_, va_list _ap_);
+	void *tracefs_print_close*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_binary_init*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_binary_write*(struct tracefs_instance pass:[*]_instance_, void pass:[*]_data_, int _len_);
+	void *tracefs_binary_close*(struct tracefs_instance pass:[*]_instance_);
+
+Control library logs:
+	int *tracefs_set_loglevel*(enum tep_loglevel _level_);
+
+Dynamic event generic APIs:
+	struct *tracefs_dynevent*;
+	enum *tracefs_dynevent_type*;
+	int *tracefs_dynevent_create*(struct tracefs_dynevent pass:[*]_devent_);
+	int *tracefs_dynevent_destroy*(struct tracefs_dynevent pass:[*]_devent_, bool _force_);
+	int *tracefs_dynevent_destroy_all*(unsigned int _types_, bool _force_);
+	void *tracefs_dynevent_free*(struct tracefs_dynevent pass:[*]_devent_);
+	void *tracefs_dynevent_list_free*(struct tracefs_dynevent pass:[*]pass:[*]_events_);
+	struct tracefs_dynevent pass:[*]*tracefs_dynevent_get*(enum tracefs_dynevent_type _type_, const char pass:[*]_system_, const char pass:[*]_event_);
+	struct tracefs_dynevent pass:[*]pass:[*]*tracefs_dynevent_get_all*(unsigned int _types_, const char pass:[*]_system_);
+	enum tracefs_dynevent_type *tracefs_dynevent_info*(struct tracefs_dynevent pass:[*]_dynevent_, char pass:[*]pass:[*]_system_, char pass:[*]pass:[*]_event_, char pass:[*]pass:[*]_prefix_, char pass:[*]pass:[*]_addr_, char pass:[*]pass:[*]_format_);
+	struct tep_event pass:[*]*tracefs_dynevent_get_event*(struct tep_handle pass:[*]_tep_, struct tracefs_dynevent pass:[*]_dynevent_);
+
+Even probes (eprobes):
+	struct tracefs_dynevent pass:[*] *tracefs_eprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_target_system_, const char pass:[*]_target_event_, const char pass:[*]_fetchargs_);
+
+Uprobes, Kprobes and Kretprobes:
+	struct tracefs_dynevent pass:[*] *tracefs_kprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_);
+	struct tracefs_dynevent pass:[*] *tracefs_kretprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_, unsigned int _max_);
+	int *tracefs_kprobe_raw*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_);
+	int *tracefs_kretprobe_raw*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_);
+	*tracefs_uprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+		     const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_)
+	*tracefs_uretprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+		     const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_);
+
+Synthetic events:
+	struct tracefs_synth pass:[*]*tracefs_sql*(struct tep_handle pass:[*]_tep_, const char pass:[*]_name_,
+					const char pass:[*]_sql_buffer_, char pass:[**]_err_);
+	struct tracefs_synth pass:[*]*tracefs_synth_alloc*(struct tep_handle pass:[*]_tep_,
+						 const char pass:[*]_name_,
+						 const char pass:[*]_start_system_,
+						 const char pass:[*]_start_event_,
+						 const char pass:[*]_end_system_,
+						 const char pass:[*]_end_event_,
+						 const char pass:[*]_start_match_field_,
+						 const char pass:[*]_end_match_field_,
+						 const char pass:[*]_match_name_);
+	int *tracefs_synth_add_match_field*(struct tracefs_synth pass:[*]_synth_,
+					  const char pass:[*]_start_match_field_,
+					  const char pass:[*]_end_match_field_,
+					  const char pass:[*]_name_);
+	int *tracefs_synth_add_compare_field*(struct tracefs_synth pass:[*]_synth_,
+					    const char pass:[*]_start_compare_field_,
+					    const char pass:[*]_end_compare_field_,
+					    enum tracefs_synth_calc _calc_,
+					    const char pass:[*]_name_);
+	int *tracefs_synth_add_start_field*(struct tracefs_synth pass:[*]_synth_,
+					  const char pass:[*]_start_field_,
+					  const char pass:[*]_name_);
+	int *tracefs_synth_add_end_field*(struct tracefs_synth pass:[*]_synth_,
+					const char pass:[*]_end_field_,
+					const char pass:[*]_name_);
+	int *tracefs_synth_append_start_filter*(struct tracefs_synth pass:[*]_synth_,
+					      struct tracefs_filter _type_,
+					      const char pass:[*]_field_,
+					      enum tracefs_synth_compare _compare_,
+					      const char pass:[*]_val_);
+	int *tracefs_synth_append_end_filter*(struct tracefs_synth pass:[*]_synth_,
+					    struct tracefs_filter _type_,
+					    const char pass:[*]_field_,
+					    enum tracefs_synth_compare _compare_,
+					    const char pass:[*]_val_);
+	void *tracefs_synth_free*(struct tracefs_synth pass:[*]_synth_);
+	int *tracefs_synth_create*(struct tracefs_synth pass:[*]_synth_);
+	int *tracefs_synth_destroy*(struct tracefs_synth pass:[*]_synth_);
+	int *tracefs_synth_echo_cmd*(struct trace_seq pass:[*]_seq_, struct tracefs_synth pass:[*]_synth_);
+	bool *tracefs_synth_complete*(struct tracefs_synth pass:[*]_synth_);
+	struct tracefs_hist pass:[*]*tracefs_synth_get_start_hist*(struct tracefs_synth pass:[*]_synth_);
+	int *tracefs_synth_trace*(struct tracefs_synth pass:[*]_synth_,
+				enum tracefs_synth_handler _type_, const char pass:[*]_var_);
+	int *tracefs_synth_snapshot*(struct tracefs_synth pass:[*]_synth_,
+				   enum tracefs_synth_handler _type_, const char pass:[*]_var_);
+	int *tracefs_synth_save*(struct tracefs_synth pass:[*]_synth_,
+			       enum tracefs_synth_handler _type_, const char pass:[*]_var_,
+			       char pass:[**]_save_fields_);
+	const char pass:[*]*tracefs_synth_get_name*(struct tracefs_synth pass:[*]_synth_);
+	int *tracefs_synth_raw_fmt*(struct trace_seq pass:[*]_seq_, struct tracefs_synth pass:[*]_synth_);
+	const char pass:[*]*tracefs_synth_show_event*(struct tracefs_synth pass:[*]_synth_);
+	const char pass:[*]*tracefs_synth_show_start_hist*(struct tracefs_synth pass:[*]_synth_);
+	const char pass:[*]*tracefs_synth_show_end_hist*(struct tracefs_synth pass:[*]_synth_);
+	struct tep_event pass:[*]*tracefs_synth_get_event*(struct tep_handle pass:[*]_tep_, struct tracefs_synth pass:[*]_synth_);
+
+Ftrace errors reporting:
+	char pass:[*]*tracefs_error_last*(struct tracefs_instance pass:[*]_instance_);
+	char pass:[*]*tracefs_error_all*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_error_clear*(struct tracefs_instance pass:[*]_instance_);
+
+Histograms:
+	struct tracefs_hist pass:[*]*tracefs_hist_alloc*(struct tracefs_tep pass:[*] _tep_,
+				const char pass:[*]_system_, const char pass:[*]_event_,
+				const char pass:[*]_key_, enum tracefs_hist_key_type _type_);
+	struct tracefs_hist pass:[*]*tracefs_hist_alloc_2d*(struct tracefs_tep pass:[*] _tep_,
+				const char pass:[*]_system_, const char pass:[*]_event_,
+				const char pass:[*]_key1_, enum tracefs_hist_key_type _type1_,
+				const char pass:[*]_key2_, enum tracefs_hist_key_type _type2_));
+	struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd*(struct tracefs_tep pass:[*] _tep_,
+				const char pass:[*]_system_, const char pass:[*]_event_,
+				struct tracefs_hist_axis pass:[*]_axes_);
+	struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd_cnt*(struct tep_handle pass:[*]_tep_,
+				  const char pass:[*]_system_, const char pass:[*]_event_name_,
+				  struct tracefs_hist_axis_cnt pass:[*]_axes_);
+	void *tracefs_hist_free*(struct tracefs_hist pass:[*]_hist_);
+	int *tracefs_hist_add_key*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_key_,
+				 enum tracefs_hist_key_type _type_);
+	int *tracefs_hist_add_key_cnt*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_key_,
+				 enum tracefs_hist_key_type _type_, int _cnt_);
+	int *tracefs_hist_add_value*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_value_);
+	int *tracefs_hist_add_sort_key*(struct tracefs_hist pass:[*]_hist_,
+				      const char pass:[*]_sort_key_);
+	int *tracefs_hist_set_sort_key*(struct tracefs_hist pass:[*]_hist_,
+				      const char pass:[*]_sort_key_, _..._);
+	int *tracefs_hist_sort_key_direction*(struct tracefs_hist pass:[*]_hist_,
+					    const char pass:[*]_sort_key_,
+					    enum tracefs_hist_sort_direction _dir_);
+	int *tracefs_hist_add_name*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_name_);
+	int *tracefs_hist_append_filter*(struct tracefs_hist pass:[*]_hist_,
+				       enum tracefs_filter _type_,
+				       const char pass:[*]_field_,
+				       enum tracefs_compare _compare_,
+				       const char pass:[*]_val_);
+	int *tracefs_hist_echo_cmd*(struct trace_seq pass:[*]_s_, struct tracefs_instance pass:[*]_instance_,
+				  struct tracefs_hist pass:[*]_hist_,
+				  enum tracefs_hist_command _command_);
+	int *tracefs_hist_command*(struct tracefs_instance pass:[*]_instance_,
+				 struct tracefs_hist pass:[*]_hist_,
+				 enum tracefs_hist_command _command_);
+	const char pass:[*]*tracefs_hist_get_name*(struct tracefs_hist pass:[*]_hist_);
+	const char pass:[*]*tracefs_hist_get_event*(struct tracefs_hist pass:[*]_hist_);
+	const char pass:[*]*tracefs_hist_get_system*(struct tracefs_hist pass:[*]_hist_);
+	int *tracefs_hist_start*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
+	int *tracefs_hist_destroy*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
+	int *tracefs_hist_pause*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
+	int *tracefs_hist_continue*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
+	int *tracefs_hist_reset*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
+
+Recording of trace_pipe_raw files:
+	struct tracefs_cpu pass:[*]*tracefs_cpu_open*(struct tracefs_instance pass:[*]_instance_,
+					     int _cpu_, bool _nonblock_);
+	struct tracefs_cpu pass:[*]*tracefs_cpu_alloc_fd*(int _fd_, int _subbuf_size_, bool _nonblock_);
+	void *tracefs_cpu_close*(struct tracefs_cpu pass:[*]_tcpu_);
+	void *tracefs_cpu_free_fd*(struct tracefs_cpu pass:[*]_tcpu_);
+	int *tracefs_cpu_read_size*(struct tracefs_cpu pass:[*]_tcpu_);
+	int *tracefs_cpu_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+	int *tracefs_cpu_buffered_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+	int *tracefs_cpu_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_);
+	int *tracefs_cpu_stop*(struct tracefs_cpu pass:[*]_tcpu_);
+	int *tracefs_cpu_flush*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_);
+	int *tracefs_cpu_flush_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_);
+	int *tracefs_cpu_pipe*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_);
+
+--
+
+DESCRIPTION
+-----------
+The libtracefs(3) library provides APIs to access kernel trace file system.
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/manpage-1.72.xsl b/Documentation/manpage-1.72.xsl
new file mode 100644
index 0000000..b4d315c
--- /dev/null
+++ b/Documentation/manpage-1.72.xsl
@@ -0,0 +1,14 @@
+<!-- manpage-1.72.xsl:
+     special settings for manpages rendered from asciidoc+docbook
+     handles peculiarities in docbook-xsl 1.72.0 -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
+
+<xsl:import href="manpage-base.xsl"/>
+
+<!-- these are the special values for the roff control characters
+     needed for docbook-xsl 1.72.0 -->
+<xsl:param name="git.docbook.backslash">&#x2593;</xsl:param>
+<xsl:param name="git.docbook.dot"      >&#x2302;</xsl:param>
+
+</xsl:stylesheet>
diff --git a/Documentation/manpage-base.xsl b/Documentation/manpage-base.xsl
new file mode 100644
index 0000000..a264fa6
--- /dev/null
+++ b/Documentation/manpage-base.xsl
@@ -0,0 +1,35 @@
+<!-- manpage-base.xsl:
+     special formatting for manpages rendered from asciidoc+docbook -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
+
+<!-- these params silence some output from xmlto -->
+<xsl:param name="man.output.quietly" select="1"/>
+<xsl:param name="refentry.meta.get.quietly" select="1"/>
+
+<!-- convert asciidoc callouts to man page format;
+     git.docbook.backslash and git.docbook.dot params
+     must be supplied by another XSL file or other means -->
+<xsl:template match="co">
+	<xsl:value-of select="concat(
+			      $git.docbook.backslash,'fB(',
+			      substring-after(@id,'-'),')',
+			      $git.docbook.backslash,'fR')"/>
+</xsl:template>
+<xsl:template match="calloutlist">
+	<xsl:value-of select="$git.docbook.dot"/>
+	<xsl:text>sp&#10;</xsl:text>
+	<xsl:apply-templates/>
+	<xsl:text>&#10;</xsl:text>
+</xsl:template>
+<xsl:template match="callout">
+	<xsl:value-of select="concat(
+			      $git.docbook.backslash,'fB',
+			      substring-after(@arearefs,'-'),
+			      '. ',$git.docbook.backslash,'fR')"/>
+	<xsl:apply-templates/>
+	<xsl:value-of select="$git.docbook.dot"/>
+	<xsl:text>br&#10;</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/Documentation/manpage-bold-literal.xsl b/Documentation/manpage-bold-literal.xsl
new file mode 100644
index 0000000..608eb5d
--- /dev/null
+++ b/Documentation/manpage-bold-literal.xsl
@@ -0,0 +1,17 @@
+<!-- manpage-bold-literal.xsl:
+     special formatting for manpages rendered from asciidoc+docbook -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
+
+<!-- render literal text as bold (instead of plain or monospace);
+     this makes literal text easier to distinguish in manpages
+     viewed on a tty -->
+<xsl:template match="literal">
+	<xsl:value-of select="$git.docbook.backslash"/>
+	<xsl:text>fB</xsl:text>
+	<xsl:apply-templates/>
+	<xsl:value-of select="$git.docbook.backslash"/>
+	<xsl:text>fR</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/Documentation/manpage-normal.xsl b/Documentation/manpage-normal.xsl
new file mode 100644
index 0000000..a48f5b1
--- /dev/null
+++ b/Documentation/manpage-normal.xsl
@@ -0,0 +1,13 @@
+<!-- manpage-normal.xsl:
+     special settings for manpages rendered from asciidoc+docbook
+     handles anything we want to keep away from docbook-xsl 1.72.0 -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
+
+<xsl:import href="manpage-base.xsl"/>
+
+<!-- these are the normal values for the roff control characters -->
+<xsl:param name="git.docbook.backslash">\</xsl:param>
+<xsl:param name="git.docbook.dot"	>.</xsl:param>
+
+</xsl:stylesheet>
diff --git a/Documentation/manpage-suppress-sp.xsl b/Documentation/manpage-suppress-sp.xsl
new file mode 100644
index 0000000..a63c763
--- /dev/null
+++ b/Documentation/manpage-suppress-sp.xsl
@@ -0,0 +1,21 @@
+<!-- manpage-suppress-sp.xsl:
+     special settings for manpages rendered from asciidoc+docbook
+     handles erroneous, inline .sp in manpage output of some
+     versions of docbook-xsl -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
+
+<!-- attempt to work around spurious .sp at the tail of the line
+     that some versions of docbook stylesheets seem to add -->
+<xsl:template match="simpara">
+  <xsl:variable name="content">
+    <xsl:apply-templates/>
+  </xsl:variable>
+  <xsl:value-of select="normalize-space($content)"/>
+  <xsl:if test="not(ancestor::authorblurb) and
+                not(ancestor::personblurb)">
+    <xsl:text>&#10;&#10;</xsl:text>
+  </xsl:if>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1ec0990
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,1503 @@
+Valid-License-Identifier: GPL-2.0
+Valid-License-Identifier: GPL-2.0-only
+Valid-License-Identifier: GPL-2.0+
+Valid-License-Identifier: GPL-2.0-or-later
+SPDX-URL: https://spdx.org/licenses/GPL-2.0.html
+Usage-Guide:
+  To use this license in source code, put one of the following SPDX
+  tag/value pairs into a comment according to the placement
+  guidelines in the licensing rules documentation.
+  For 'GNU General Public License (GPL) version 2 only' use:
+    SPDX-License-Identifier: GPL-2.0
+  or
+    SPDX-License-Identifier: GPL-2.0-only
+  For 'GNU General Public License (GPL) version 2 or any later version' use:
+    SPDX-License-Identifier: GPL-2.0+
+  or
+    SPDX-License-Identifier: GPL-2.0-or-later
+License-Text:
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+-------------------------------------------------------------------------
+
+Valid-License-Identifier: LGPL-2.1
+Valid-License-Identifier: LGPL-2.1+
+SPDX-URL: https://spdx.org/licenses/LGPL-2.1.html
+Usage-Guide:
+  To use this license in source code, put one of the following SPDX
+  tag/value pairs into a comment according to the placement
+  guidelines in the licensing rules documentation.
+  For 'GNU Lesser General Public License (LGPL) version 2.1 only' use:
+    SPDX-License-Identifier: LGPL-2.1
+  For 'GNU Lesser General Public License (LGPL) version 2.1 or any later
+  version' use:
+    SPDX-License-Identifier: LGPL-2.1+
+License-Text:
+
+GNU LESSER GENERAL PUBLIC LICENSE
+Version 2.1, February 1999
+
+Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts as
+the successor of the GNU Library Public License, version 2, hence the
+version number 2.1.]
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public Licenses are
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users.
+
+This license, the Lesser General Public License, applies to some specially
+designated software packages--typically libraries--of the Free Software
+Foundation and other authors who decide to use it. You can use it too, but
+we suggest you first think carefully about whether this license or the
+ordinary General Public License is the better strategy to use in any
+particular case, based on the explanations below.
+
+When we speak of free software, we are referring to freedom of use, not
+price. Our General Public Licenses are designed to make sure that you have
+the freedom to distribute copies of free software (and charge for this
+service if you wish); that you receive source code or can get it if you
+want it; that you can change the software and use pieces of it in new free
+programs; and that you are informed that you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for you if
+you distribute copies of the library or if you modify it.
+
+For example, if you distribute copies of the library, whether gratis or for
+a fee, you must give the recipients all the rights that we gave you. You
+must make sure that they, too, receive or can get the source code. If you
+link other code with the library, you must provide complete object files to
+the recipients, so that they can relink them with the library after making
+changes to the library and recompiling it. And you must show them these
+terms so they know their rights.
+
+We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+To protect each distributor, we want to make it very clear that there is no
+warranty for the free library. Also, if the library is modified by someone
+else and passed on, the recipients should know that what they have is not
+the original version, so that the original author's reputation will not be
+affected by problems that might be introduced by others.
+
+Finally, software patents pose a constant threat to the existence of any
+free program. We wish to make sure that a company cannot effectively
+restrict the users of a free program by obtaining a restrictive license
+from a patent holder. Therefore, we insist that any patent license obtained
+for a version of the library must be consistent with the full freedom of
+use specified in this license.
+
+Most GNU software, including some libraries, is covered by the ordinary GNU
+General Public License. This license, the GNU Lesser General Public
+License, applies to certain designated libraries, and is quite different
+from the ordinary General Public License. We use this license for certain
+libraries in order to permit linking those libraries into non-free
+programs.
+
+When a program is linked with a library, whether statically or using a
+shared library, the combination of the two is legally speaking a combined
+work, a derivative of the original library. The ordinary General Public
+License therefore permits such linking only if the entire combination fits
+its criteria of freedom. The Lesser General Public License permits more lax
+criteria for linking other code with the library.
+
+We call this license the "Lesser" General Public License because it does
+Less to protect the user's freedom than the ordinary General Public
+License. It also provides other free software developers Less of an
+advantage over competing non-free programs. These disadvantages are the
+reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+For example, on rare occasions, there may be a special need to encourage
+the widest possible use of a certain library, so that it becomes a de-facto
+standard. To achieve this, non-free programs must be allowed to use the
+library. A more frequent case is that a free library does the same job as
+widely used non-free libraries. In this case, there is little to gain by
+limiting the free library to free software only, so we use the Lesser
+General Public License.
+
+In other cases, permission to use a particular library in non-free programs
+enables a greater number of people to use a large body of free
+software. For example, permission to use the GNU C Library in non-free
+programs enables many more people to use the whole GNU operating system, as
+well as its variant, the GNU/Linux operating system.
+
+Although the Lesser General Public License is Less protective of the users'
+freedom, it does ensure that the user of a program that is linked with the
+Library has the freedom and the wherewithal to run that program using a
+modified version of the Library.
+
+The precise terms and conditions for copying, distribution and modification
+follow. Pay close attention to the difference between a "work based on the
+library" and a "work that uses the library". The former contains code
+derived from the library, whereas the latter must be combined with the
+library in order to run.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License Agreement applies to any software library or other program
+   which contains a notice placed by the copyright holder or other
+   authorized party saying it may be distributed under the terms of this
+   Lesser General Public License (also called "this License"). Each
+   licensee is addressed as "you".
+
+   A "library" means a collection of software functions and/or data
+   prepared so as to be conveniently linked with application programs
+   (which use some of those functions and data) to form executables.
+
+   The "Library", below, refers to any such software library or work which
+   has been distributed under these terms. A "work based on the Library"
+   means either the Library or any derivative work under copyright law:
+   that is to say, a work containing the Library or a portion of it, either
+   verbatim or with modifications and/or translated straightforwardly into
+   another language. (Hereinafter, translation is included without
+   limitation in the term "modification".)
+
+   "Source code" for a work means the preferred form of the work for making
+   modifications to it. For a library, complete source code means all the
+   source code for all modules it contains, plus any associated interface
+   definition files, plus the scripts used to control compilation and
+   installation of the library.
+
+    Activities other than copying, distribution and modification are not
+    covered by this License; they are outside its scope. The act of running
+    a program using the Library is not restricted, and output from such a
+    program is covered only if its contents constitute a work based on the
+    Library (independent of the use of the Library in a tool for writing
+    it). Whether that is true depends on what the Library does and what the
+    program that uses the Library does.
+
+1. You may copy and distribute verbatim copies of the Library's complete
+   source code as you receive it, in any medium, provided that you
+   conspicuously and appropriately publish on each copy an appropriate
+   copyright notice and disclaimer of warranty; keep intact all the notices
+   that refer to this License and to the absence of any warranty; and
+   distribute a copy of this License along with the Library.
+
+   You may charge a fee for the physical act of transferring a copy, and
+   you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Library or any portion of it,
+   thus forming a work based on the Library, and copy and distribute such
+   modifications or work under the terms of Section 1 above, provided that
+   you also meet all of these conditions:
+
+   a) The modified work must itself be a software library.
+
+   b) You must cause the files modified to carry prominent notices stating
+      that you changed the files and the date of any change.
+
+   c) You must cause the whole of the work to be licensed at no charge to
+      all third parties under the terms of this License.
+
+   d) If a facility in the modified Library refers to a function or a table
+      of data to be supplied by an application program that uses the
+      facility, other than as an argument passed when the facility is
+      invoked, then you must make a good faith effort to ensure that, in
+      the event an application does not supply such function or table, the
+      facility still operates, and performs whatever part of its purpose
+      remains meaningful.
+
+   (For example, a function in a library to compute square roots has a
+    purpose that is entirely well-defined independent of the
+    application. Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must be
+    optional: if the application does not supply it, the square root
+    function must still compute square roots.)
+
+   These requirements apply to the modified work as a whole. If
+   identifiable sections of that work are not derived from the Library, and
+   can be reasonably considered independent and separate works in
+   themselves, then this License, and its terms, do not apply to those
+   sections when you distribute them as separate works. But when you
+   distribute the same sections as part of a whole which is a work based on
+   the Library, the distribution of the whole must be on the terms of this
+   License, whose permissions for other licensees extend to the entire
+   whole, and thus to each and every part regardless of who wrote it.
+
+   Thus, it is not the intent of this section to claim rights or contest
+   your rights to work written entirely by you; rather, the intent is to
+   exercise the right to control the distribution of derivative or
+   collective works based on the Library.
+
+   In addition, mere aggregation of another work not based on the Library
+   with the Library (or with a work based on the Library) on a volume of a
+   storage or distribution medium does not bring the other work under the
+   scope of this License.
+
+3. You may opt to apply the terms of the ordinary GNU General Public
+   License instead of this License to a given copy of the Library. To do
+   this, you must alter all the notices that refer to this License, so that
+   they refer to the ordinary GNU General Public License, version 2,
+   instead of to this License. (If a newer version than version 2 of the
+   ordinary GNU General Public License has appeared, then you can specify
+   that version instead if you wish.) Do not make any other change in these
+   notices.
+
+   Once this change is made in a given copy, it is irreversible for that
+   copy, so the ordinary GNU General Public License applies to all
+   subsequent copies and derivative works made from that copy.
+
+   This option is useful when you wish to copy part of the code of the
+   Library into a program that is not a library.
+
+4. You may copy and distribute the Library (or a portion or derivative of
+   it, under Section 2) in object code or executable form under the terms
+   of Sections 1 and 2 above provided that you accompany it with the
+   complete corresponding machine-readable source code, which must be
+   distributed under the terms of Sections 1 and 2 above on a medium
+   customarily used for software interchange.
+
+   If distribution of object code is made by offering access to copy from a
+   designated place, then offering equivalent access to copy the source
+   code from the same place satisfies the requirement to distribute the
+   source code, even though third parties are not compelled to copy the
+   source along with the object code.
+
+5. A program that contains no derivative of any portion of the Library, but
+   is designed to work with the Library by being compiled or linked with
+   it, is called a "work that uses the Library". Such a work, in isolation,
+   is not a derivative work of the Library, and therefore falls outside the
+   scope of this License.
+
+   However, linking a "work that uses the Library" with the Library creates
+   an executable that is a derivative of the Library (because it contains
+   portions of the Library), rather than a "work that uses the
+   library". The executable is therefore covered by this License. Section 6
+   states terms for distribution of such executables.
+
+   When a "work that uses the Library" uses material from a header file
+   that is part of the Library, the object code for the work may be a
+   derivative work of the Library even though the source code is
+   not. Whether this is true is especially significant if the work can be
+   linked without the Library, or if the work is itself a library. The
+   threshold for this to be true is not precisely defined by law.
+
+   If such an object file uses only numerical parameters, data structure
+   layouts and accessors, and small macros and small inline functions (ten
+   lines or less in length), then the use of the object file is
+   unrestricted, regardless of whether it is legally a derivative
+   work. (Executables containing this object code plus portions of the
+   Library will still fall under Section 6.)
+
+   Otherwise, if the work is a derivative of the Library, you may
+   distribute the object code for the work under the terms of Section
+   6. Any executables containing that work also fall under Section 6,
+   whether or not they are linked directly with the Library itself.
+
+6. As an exception to the Sections above, you may also combine or link a
+   "work that uses the Library" with the Library to produce a work
+   containing portions of the Library, and distribute that work under terms
+   of your choice, provided that the terms permit modification of the work
+   for the customer's own use and reverse engineering for debugging such
+   modifications.
+
+   You must give prominent notice with each copy of the work that the
+   Library is used in it and that the Library and its use are covered by
+   this License. You must supply a copy of this License. If the work during
+   execution displays copyright notices, you must include the copyright
+   notice for the Library among them, as well as a reference directing the
+   user to the copy of this License. Also, you must do one of these things:
+
+   a) Accompany the work with the complete corresponding machine-readable
+      source code for the Library including whatever changes were used in
+      the work (which must be distributed under Sections 1 and 2 above);
+      and, if the work is an executable linked with the Library, with the
+      complete machine-readable "work that uses the Library", as object
+      code and/or source code, so that the user can modify the Library and
+      then relink to produce a modified executable containing the modified
+      Library. (It is understood that the user who changes the contents of
+      definitions files in the Library will not necessarily be able to
+      recompile the application to use the modified definitions.)
+
+   b) Use a suitable shared library mechanism for linking with the
+      Library. A suitable mechanism is one that (1) uses at run time a copy
+      of the library already present on the user's computer system, rather
+      than copying library functions into the executable, and (2) will
+      operate properly with a modified version of the library, if the user
+      installs one, as long as the modified version is interface-compatible
+      with the version that the work was made with.
+
+   c) Accompany the work with a written offer, valid for at least three
+      years, to give the same user the materials specified in Subsection
+      6a, above, for a charge no more than the cost of performing this
+      distribution.
+
+   d) If distribution of the work is made by offering access to copy from a
+      designated place, offer equivalent access to copy the above specified
+      materials from the same place.
+
+   e) Verify that the user has already received a copy of these materials
+      or that you have already sent this user a copy.
+
+   For an executable, the required form of the "work that uses the Library"
+   must include any data and utility programs needed for reproducing the
+   executable from it. However, as a special exception, the materials to be
+   distributed need not include anything that is normally distributed (in
+   either source or binary form) with the major components (compiler,
+   kernel, and so on) of the operating system on which the executable runs,
+   unless that component itself accompanies the executable.
+
+   It may happen that this requirement contradicts the license restrictions
+   of other proprietary libraries that do not normally accompany the
+   operating system. Such a contradiction means you cannot use both them
+   and the Library together in an executable that you distribute.
+
+7. You may place library facilities that are a work based on the Library
+   side-by-side in a single library together with other library facilities
+   not covered by this License, and distribute such a combined library,
+   provided that the separate distribution of the work based on the Library
+   and of the other library facilities is otherwise permitted, and provided
+   that you do these two things:
+
+   a) Accompany the combined library with a copy of the same work based on
+      the Library, uncombined with any other library facilities. This must
+      be distributed under the terms of the Sections above.
+
+   b) Give prominent notice with the combined library of the fact that part
+      of it is a work based on the Library, and explaining where to find
+      the accompanying uncombined form of the same work.
+
+8. You may not copy, modify, sublicense, link with, or distribute the
+   Library except as expressly provided under this License. Any attempt
+   otherwise to copy, modify, sublicense, link with, or distribute the
+   Library is void, and will automatically terminate your rights under this
+   License. However, parties who have received copies, or rights, from you
+   under this License will not have their licenses terminated so long as
+   such parties remain in full compliance.
+
+9. You are not required to accept this License, since you have not signed
+   it. However, nothing else grants you permission to modify or distribute
+   the Library or its derivative works. These actions are prohibited by law
+   if you do not accept this License. Therefore, by modifying or
+   distributing the Library (or any work based on the Library), you
+   indicate your acceptance of this License to do so, and all its terms and
+   conditions for copying, distributing or modifying the Library or works
+   based on it.
+
+10. Each time you redistribute the Library (or any work based on the
+    Library), the recipient automatically receives a license from the
+    original licensor to copy, distribute, link with or modify the Library
+    subject to these terms and conditions. You may not impose any further
+    restrictions on the recipients' exercise of the rights granted
+    herein. You are not responsible for enforcing compliance by third
+    parties with this License.
+
+11. If, as a consequence of a court judgment or allegation of patent
+    infringement or for any other reason (not limited to patent issues),
+    conditions are imposed on you (whether by court order, agreement or
+    otherwise) that contradict the conditions of this License, they do not
+    excuse you from the conditions of this License. If you cannot
+    distribute so as to satisfy simultaneously your obligations under this
+    License and any other pertinent obligations, then as a consequence you
+    may not distribute the Library at all. For example, if a patent license
+    would not permit royalty-free redistribution of the Library by all
+    those who receive copies directly or indirectly through you, then the
+    only way you could satisfy both it and this License would be to refrain
+    entirely from distribution of the Library.
+
+    If any portion of this section is held invalid or unenforceable under
+    any particular circumstance, the balance of the section is intended to
+    apply, and the section as a whole is intended to apply in other
+    circumstances.
+
+    It is not the purpose of this section to induce you to infringe any
+    patents or other property right claims or to contest validity of any
+    such claims; this section has the sole purpose of protecting the
+    integrity of the free software distribution system which is implemented
+    by public license practices. Many people have made generous
+    contributions to the wide range of software distributed through that
+    system in reliance on consistent application of that system; it is up
+    to the author/donor to decide if he or she is willing to distribute
+    software through any other system and a licensee cannot impose that
+    choice.
+
+    This section is intended to make thoroughly clear what is believed to
+    be a consequence of the rest of this License.
+
+12. If the distribution and/or use of the Library is restricted in certain
+    countries either by patents or by copyrighted interfaces, the original
+    copyright holder who places the Library under this License may add an
+    explicit geographical distribution limitation excluding those
+    countries, so that distribution is permitted only in or among countries
+    not thus excluded. In such case, this License incorporates the
+    limitation as if written in the body of this License.
+
+13. The Free Software Foundation may publish revised and/or new versions of
+    the Lesser General Public License from time to time. Such new versions
+    will be similar in spirit to the present version, but may differ in
+    detail to address new problems or concerns.
+
+    Each version is given a distinguishing version number. If the Library
+    specifies a version number of this License which applies to it and "any
+    later version", you have the option of following the terms and
+    conditions either of that version or of any later version published by
+    the Free Software Foundation. If the Library does not specify a license
+    version number, you may choose any version ever published by the Free
+    Software Foundation.
+
+14. If you wish to incorporate parts of the Library into other free
+    programs whose distribution conditions are incompatible with these,
+    write to the author to ask for permission. For software which is
+    copyrighted by the Free Software Foundation, write to the Free Software
+    Foundation; we sometimes make exceptions for this. Our decision will be
+    guided by the two goals of preserving the free status of all
+    derivatives of our free software and of promoting the sharing and reuse
+    of software generally.
+
+NO WARRANTY
+
+15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+    FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+    OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+    PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+    EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+    ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH
+    YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+    NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+    WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+    REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+    DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+    DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY
+    (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+    INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+    THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR
+    OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Libraries
+
+If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+one line to give the library's name and an idea of what it does.
+Copyright (C) year name of author
+
+This library is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at
+your option) any later version.
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add
+information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in
+the library `Frob' (a library for tweaking knobs) written
+by James Random Hacker.
+
+signature of Ty Coon, 1 April 1990
+Ty Coon, President of Vice
+That's all there is to it!
+
+-------------------------------------------------------------------------
+
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+Bison Exception
+
+As a special exception, you may create a larger work that contains part or all
+of the Bison parser skeleton and distribute that work under terms of your
+choice, so long as that work isn't itself a parser generator using the skeleton
+or a modified version thereof as a parser skeleton.  Alternatively, if you
+modify or redistribute the parser skeleton itself, you may (at your option)
+remove this special exception, which will cause the skeleton and the resulting
+Bison output files to be licensed under the GNU General Public License without
+this special exception.
+
+This special exception was added by the Free Software Foundation in version
+2.2 of Bison.
+
+                     END OF TERMS AND CONDITIONS
diff --git a/LICENSES/GPL-2.0 b/LICENSES/GPL-2.0
new file mode 100644
index 0000000..ff0812f
--- /dev/null
+++ b/LICENSES/GPL-2.0
@@ -0,0 +1,359 @@
+Valid-License-Identifier: GPL-2.0
+Valid-License-Identifier: GPL-2.0-only
+Valid-License-Identifier: GPL-2.0+
+Valid-License-Identifier: GPL-2.0-or-later
+SPDX-URL: https://spdx.org/licenses/GPL-2.0.html
+Usage-Guide:
+  To use this license in source code, put one of the following SPDX
+  tag/value pairs into a comment according to the placement
+  guidelines in the licensing rules documentation.
+  For 'GNU General Public License (GPL) version 2 only' use:
+    SPDX-License-Identifier: GPL-2.0
+  or
+    SPDX-License-Identifier: GPL-2.0-only
+  For 'GNU General Public License (GPL) version 2 or any later version' use:
+    SPDX-License-Identifier: GPL-2.0+
+  or
+    SPDX-License-Identifier: GPL-2.0-or-later
+License-Text:
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/LICENSES/LGPL-2.1 b/LICENSES/LGPL-2.1
new file mode 100644
index 0000000..27bb434
--- /dev/null
+++ b/LICENSES/LGPL-2.1
@@ -0,0 +1,503 @@
+Valid-License-Identifier: LGPL-2.1
+Valid-License-Identifier: LGPL-2.1+
+SPDX-URL: https://spdx.org/licenses/LGPL-2.1.html
+Usage-Guide:
+  To use this license in source code, put one of the following SPDX
+  tag/value pairs into a comment according to the placement
+  guidelines in the licensing rules documentation.
+  For 'GNU Lesser General Public License (LGPL) version 2.1 only' use:
+    SPDX-License-Identifier: LGPL-2.1
+  For 'GNU Lesser General Public License (LGPL) version 2.1 or any later
+  version' use:
+    SPDX-License-Identifier: LGPL-2.1+
+License-Text:
+
+GNU LESSER GENERAL PUBLIC LICENSE
+Version 2.1, February 1999
+
+Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts as
+the successor of the GNU Library Public License, version 2, hence the
+version number 2.1.]
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public Licenses are
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users.
+
+This license, the Lesser General Public License, applies to some specially
+designated software packages--typically libraries--of the Free Software
+Foundation and other authors who decide to use it. You can use it too, but
+we suggest you first think carefully about whether this license or the
+ordinary General Public License is the better strategy to use in any
+particular case, based on the explanations below.
+
+When we speak of free software, we are referring to freedom of use, not
+price. Our General Public Licenses are designed to make sure that you have
+the freedom to distribute copies of free software (and charge for this
+service if you wish); that you receive source code or can get it if you
+want it; that you can change the software and use pieces of it in new free
+programs; and that you are informed that you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for you if
+you distribute copies of the library or if you modify it.
+
+For example, if you distribute copies of the library, whether gratis or for
+a fee, you must give the recipients all the rights that we gave you. You
+must make sure that they, too, receive or can get the source code. If you
+link other code with the library, you must provide complete object files to
+the recipients, so that they can relink them with the library after making
+changes to the library and recompiling it. And you must show them these
+terms so they know their rights.
+
+We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+To protect each distributor, we want to make it very clear that there is no
+warranty for the free library. Also, if the library is modified by someone
+else and passed on, the recipients should know that what they have is not
+the original version, so that the original author's reputation will not be
+affected by problems that might be introduced by others.
+
+Finally, software patents pose a constant threat to the existence of any
+free program. We wish to make sure that a company cannot effectively
+restrict the users of a free program by obtaining a restrictive license
+from a patent holder. Therefore, we insist that any patent license obtained
+for a version of the library must be consistent with the full freedom of
+use specified in this license.
+
+Most GNU software, including some libraries, is covered by the ordinary GNU
+General Public License. This license, the GNU Lesser General Public
+License, applies to certain designated libraries, and is quite different
+from the ordinary General Public License. We use this license for certain
+libraries in order to permit linking those libraries into non-free
+programs.
+
+When a program is linked with a library, whether statically or using a
+shared library, the combination of the two is legally speaking a combined
+work, a derivative of the original library. The ordinary General Public
+License therefore permits such linking only if the entire combination fits
+its criteria of freedom. The Lesser General Public License permits more lax
+criteria for linking other code with the library.
+
+We call this license the "Lesser" General Public License because it does
+Less to protect the user's freedom than the ordinary General Public
+License. It also provides other free software developers Less of an
+advantage over competing non-free programs. These disadvantages are the
+reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+For example, on rare occasions, there may be a special need to encourage
+the widest possible use of a certain library, so that it becomes a de-facto
+standard. To achieve this, non-free programs must be allowed to use the
+library. A more frequent case is that a free library does the same job as
+widely used non-free libraries. In this case, there is little to gain by
+limiting the free library to free software only, so we use the Lesser
+General Public License.
+
+In other cases, permission to use a particular library in non-free programs
+enables a greater number of people to use a large body of free
+software. For example, permission to use the GNU C Library in non-free
+programs enables many more people to use the whole GNU operating system, as
+well as its variant, the GNU/Linux operating system.
+
+Although the Lesser General Public License is Less protective of the users'
+freedom, it does ensure that the user of a program that is linked with the
+Library has the freedom and the wherewithal to run that program using a
+modified version of the Library.
+
+The precise terms and conditions for copying, distribution and modification
+follow. Pay close attention to the difference between a "work based on the
+library" and a "work that uses the library". The former contains code
+derived from the library, whereas the latter must be combined with the
+library in order to run.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License Agreement applies to any software library or other program
+   which contains a notice placed by the copyright holder or other
+   authorized party saying it may be distributed under the terms of this
+   Lesser General Public License (also called "this License"). Each
+   licensee is addressed as "you".
+
+   A "library" means a collection of software functions and/or data
+   prepared so as to be conveniently linked with application programs
+   (which use some of those functions and data) to form executables.
+
+   The "Library", below, refers to any such software library or work which
+   has been distributed under these terms. A "work based on the Library"
+   means either the Library or any derivative work under copyright law:
+   that is to say, a work containing the Library or a portion of it, either
+   verbatim or with modifications and/or translated straightforwardly into
+   another language. (Hereinafter, translation is included without
+   limitation in the term "modification".)
+
+   "Source code" for a work means the preferred form of the work for making
+   modifications to it. For a library, complete source code means all the
+   source code for all modules it contains, plus any associated interface
+   definition files, plus the scripts used to control compilation and
+   installation of the library.
+
+    Activities other than copying, distribution and modification are not
+    covered by this License; they are outside its scope. The act of running
+    a program using the Library is not restricted, and output from such a
+    program is covered only if its contents constitute a work based on the
+    Library (independent of the use of the Library in a tool for writing
+    it). Whether that is true depends on what the Library does and what the
+    program that uses the Library does.
+
+1. You may copy and distribute verbatim copies of the Library's complete
+   source code as you receive it, in any medium, provided that you
+   conspicuously and appropriately publish on each copy an appropriate
+   copyright notice and disclaimer of warranty; keep intact all the notices
+   that refer to this License and to the absence of any warranty; and
+   distribute a copy of this License along with the Library.
+
+   You may charge a fee for the physical act of transferring a copy, and
+   you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Library or any portion of it,
+   thus forming a work based on the Library, and copy and distribute such
+   modifications or work under the terms of Section 1 above, provided that
+   you also meet all of these conditions:
+
+   a) The modified work must itself be a software library.
+
+   b) You must cause the files modified to carry prominent notices stating
+      that you changed the files and the date of any change.
+
+   c) You must cause the whole of the work to be licensed at no charge to
+      all third parties under the terms of this License.
+
+   d) If a facility in the modified Library refers to a function or a table
+      of data to be supplied by an application program that uses the
+      facility, other than as an argument passed when the facility is
+      invoked, then you must make a good faith effort to ensure that, in
+      the event an application does not supply such function or table, the
+      facility still operates, and performs whatever part of its purpose
+      remains meaningful.
+
+   (For example, a function in a library to compute square roots has a
+    purpose that is entirely well-defined independent of the
+    application. Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must be
+    optional: if the application does not supply it, the square root
+    function must still compute square roots.)
+
+   These requirements apply to the modified work as a whole. If
+   identifiable sections of that work are not derived from the Library, and
+   can be reasonably considered independent and separate works in
+   themselves, then this License, and its terms, do not apply to those
+   sections when you distribute them as separate works. But when you
+   distribute the same sections as part of a whole which is a work based on
+   the Library, the distribution of the whole must be on the terms of this
+   License, whose permissions for other licensees extend to the entire
+   whole, and thus to each and every part regardless of who wrote it.
+
+   Thus, it is not the intent of this section to claim rights or contest
+   your rights to work written entirely by you; rather, the intent is to
+   exercise the right to control the distribution of derivative or
+   collective works based on the Library.
+
+   In addition, mere aggregation of another work not based on the Library
+   with the Library (or with a work based on the Library) on a volume of a
+   storage or distribution medium does not bring the other work under the
+   scope of this License.
+
+3. You may opt to apply the terms of the ordinary GNU General Public
+   License instead of this License to a given copy of the Library. To do
+   this, you must alter all the notices that refer to this License, so that
+   they refer to the ordinary GNU General Public License, version 2,
+   instead of to this License. (If a newer version than version 2 of the
+   ordinary GNU General Public License has appeared, then you can specify
+   that version instead if you wish.) Do not make any other change in these
+   notices.
+
+   Once this change is made in a given copy, it is irreversible for that
+   copy, so the ordinary GNU General Public License applies to all
+   subsequent copies and derivative works made from that copy.
+
+   This option is useful when you wish to copy part of the code of the
+   Library into a program that is not a library.
+
+4. You may copy and distribute the Library (or a portion or derivative of
+   it, under Section 2) in object code or executable form under the terms
+   of Sections 1 and 2 above provided that you accompany it with the
+   complete corresponding machine-readable source code, which must be
+   distributed under the terms of Sections 1 and 2 above on a medium
+   customarily used for software interchange.
+
+   If distribution of object code is made by offering access to copy from a
+   designated place, then offering equivalent access to copy the source
+   code from the same place satisfies the requirement to distribute the
+   source code, even though third parties are not compelled to copy the
+   source along with the object code.
+
+5. A program that contains no derivative of any portion of the Library, but
+   is designed to work with the Library by being compiled or linked with
+   it, is called a "work that uses the Library". Such a work, in isolation,
+   is not a derivative work of the Library, and therefore falls outside the
+   scope of this License.
+
+   However, linking a "work that uses the Library" with the Library creates
+   an executable that is a derivative of the Library (because it contains
+   portions of the Library), rather than a "work that uses the
+   library". The executable is therefore covered by this License. Section 6
+   states terms for distribution of such executables.
+
+   When a "work that uses the Library" uses material from a header file
+   that is part of the Library, the object code for the work may be a
+   derivative work of the Library even though the source code is
+   not. Whether this is true is especially significant if the work can be
+   linked without the Library, or if the work is itself a library. The
+   threshold for this to be true is not precisely defined by law.
+
+   If such an object file uses only numerical parameters, data structure
+   layouts and accessors, and small macros and small inline functions (ten
+   lines or less in length), then the use of the object file is
+   unrestricted, regardless of whether it is legally a derivative
+   work. (Executables containing this object code plus portions of the
+   Library will still fall under Section 6.)
+
+   Otherwise, if the work is a derivative of the Library, you may
+   distribute the object code for the work under the terms of Section
+   6. Any executables containing that work also fall under Section 6,
+   whether or not they are linked directly with the Library itself.
+
+6. As an exception to the Sections above, you may also combine or link a
+   "work that uses the Library" with the Library to produce a work
+   containing portions of the Library, and distribute that work under terms
+   of your choice, provided that the terms permit modification of the work
+   for the customer's own use and reverse engineering for debugging such
+   modifications.
+
+   You must give prominent notice with each copy of the work that the
+   Library is used in it and that the Library and its use are covered by
+   this License. You must supply a copy of this License. If the work during
+   execution displays copyright notices, you must include the copyright
+   notice for the Library among them, as well as a reference directing the
+   user to the copy of this License. Also, you must do one of these things:
+
+   a) Accompany the work with the complete corresponding machine-readable
+      source code for the Library including whatever changes were used in
+      the work (which must be distributed under Sections 1 and 2 above);
+      and, if the work is an executable linked with the Library, with the
+      complete machine-readable "work that uses the Library", as object
+      code and/or source code, so that the user can modify the Library and
+      then relink to produce a modified executable containing the modified
+      Library. (It is understood that the user who changes the contents of
+      definitions files in the Library will not necessarily be able to
+      recompile the application to use the modified definitions.)
+
+   b) Use a suitable shared library mechanism for linking with the
+      Library. A suitable mechanism is one that (1) uses at run time a copy
+      of the library already present on the user's computer system, rather
+      than copying library functions into the executable, and (2) will
+      operate properly with a modified version of the library, if the user
+      installs one, as long as the modified version is interface-compatible
+      with the version that the work was made with.
+
+   c) Accompany the work with a written offer, valid for at least three
+      years, to give the same user the materials specified in Subsection
+      6a, above, for a charge no more than the cost of performing this
+      distribution.
+
+   d) If distribution of the work is made by offering access to copy from a
+      designated place, offer equivalent access to copy the above specified
+      materials from the same place.
+
+   e) Verify that the user has already received a copy of these materials
+      or that you have already sent this user a copy.
+
+   For an executable, the required form of the "work that uses the Library"
+   must include any data and utility programs needed for reproducing the
+   executable from it. However, as a special exception, the materials to be
+   distributed need not include anything that is normally distributed (in
+   either source or binary form) with the major components (compiler,
+   kernel, and so on) of the operating system on which the executable runs,
+   unless that component itself accompanies the executable.
+
+   It may happen that this requirement contradicts the license restrictions
+   of other proprietary libraries that do not normally accompany the
+   operating system. Such a contradiction means you cannot use both them
+   and the Library together in an executable that you distribute.
+
+7. You may place library facilities that are a work based on the Library
+   side-by-side in a single library together with other library facilities
+   not covered by this License, and distribute such a combined library,
+   provided that the separate distribution of the work based on the Library
+   and of the other library facilities is otherwise permitted, and provided
+   that you do these two things:
+
+   a) Accompany the combined library with a copy of the same work based on
+      the Library, uncombined with any other library facilities. This must
+      be distributed under the terms of the Sections above.
+
+   b) Give prominent notice with the combined library of the fact that part
+      of it is a work based on the Library, and explaining where to find
+      the accompanying uncombined form of the same work.
+
+8. You may not copy, modify, sublicense, link with, or distribute the
+   Library except as expressly provided under this License. Any attempt
+   otherwise to copy, modify, sublicense, link with, or distribute the
+   Library is void, and will automatically terminate your rights under this
+   License. However, parties who have received copies, or rights, from you
+   under this License will not have their licenses terminated so long as
+   such parties remain in full compliance.
+
+9. You are not required to accept this License, since you have not signed
+   it. However, nothing else grants you permission to modify or distribute
+   the Library or its derivative works. These actions are prohibited by law
+   if you do not accept this License. Therefore, by modifying or
+   distributing the Library (or any work based on the Library), you
+   indicate your acceptance of this License to do so, and all its terms and
+   conditions for copying, distributing or modifying the Library or works
+   based on it.
+
+10. Each time you redistribute the Library (or any work based on the
+    Library), the recipient automatically receives a license from the
+    original licensor to copy, distribute, link with or modify the Library
+    subject to these terms and conditions. You may not impose any further
+    restrictions on the recipients' exercise of the rights granted
+    herein. You are not responsible for enforcing compliance by third
+    parties with this License.
+
+11. If, as a consequence of a court judgment or allegation of patent
+    infringement or for any other reason (not limited to patent issues),
+    conditions are imposed on you (whether by court order, agreement or
+    otherwise) that contradict the conditions of this License, they do not
+    excuse you from the conditions of this License. If you cannot
+    distribute so as to satisfy simultaneously your obligations under this
+    License and any other pertinent obligations, then as a consequence you
+    may not distribute the Library at all. For example, if a patent license
+    would not permit royalty-free redistribution of the Library by all
+    those who receive copies directly or indirectly through you, then the
+    only way you could satisfy both it and this License would be to refrain
+    entirely from distribution of the Library.
+
+    If any portion of this section is held invalid or unenforceable under
+    any particular circumstance, the balance of the section is intended to
+    apply, and the section as a whole is intended to apply in other
+    circumstances.
+
+    It is not the purpose of this section to induce you to infringe any
+    patents or other property right claims or to contest validity of any
+    such claims; this section has the sole purpose of protecting the
+    integrity of the free software distribution system which is implemented
+    by public license practices. Many people have made generous
+    contributions to the wide range of software distributed through that
+    system in reliance on consistent application of that system; it is up
+    to the author/donor to decide if he or she is willing to distribute
+    software through any other system and a licensee cannot impose that
+    choice.
+
+    This section is intended to make thoroughly clear what is believed to
+    be a consequence of the rest of this License.
+
+12. If the distribution and/or use of the Library is restricted in certain
+    countries either by patents or by copyrighted interfaces, the original
+    copyright holder who places the Library under this License may add an
+    explicit geographical distribution limitation excluding those
+    countries, so that distribution is permitted only in or among countries
+    not thus excluded. In such case, this License incorporates the
+    limitation as if written in the body of this License.
+
+13. The Free Software Foundation may publish revised and/or new versions of
+    the Lesser General Public License from time to time. Such new versions
+    will be similar in spirit to the present version, but may differ in
+    detail to address new problems or concerns.
+
+    Each version is given a distinguishing version number. If the Library
+    specifies a version number of this License which applies to it and "any
+    later version", you have the option of following the terms and
+    conditions either of that version or of any later version published by
+    the Free Software Foundation. If the Library does not specify a license
+    version number, you may choose any version ever published by the Free
+    Software Foundation.
+
+14. If you wish to incorporate parts of the Library into other free
+    programs whose distribution conditions are incompatible with these,
+    write to the author to ask for permission. For software which is
+    copyrighted by the Free Software Foundation, write to the Free Software
+    Foundation; we sometimes make exceptions for this. Our decision will be
+    guided by the two goals of preserving the free status of all
+    derivatives of our free software and of promoting the sharing and reuse
+    of software generally.
+
+NO WARRANTY
+
+15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+    FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+    OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+    PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+    EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+    ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH
+    YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+    NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+    WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+    REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+    DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+    DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY
+    (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+    INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+    THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR
+    OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Libraries
+
+If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+one line to give the library's name and an idea of what it does.
+Copyright (C) year name of author
+
+This library is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at
+your option) any later version.
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add
+information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in
+the library `Frob' (a library for tweaking knobs) written
+by James Random Hacker.
+
+signature of Ty Coon, 1 April 1990
+Ty Coon, President of Vice
+That's all there is to it!
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..33cda28
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,19 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update libtracefs
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
+name: "libtracefs"
+description: "libtracefs is a library that provides APIs to access to the Linux kernel tracefs file system"
+third_party {
+  url {
+    type: GIT
+    value: "https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git"
+  }
+  version: "libtracefs-1.6.4"
+  license_type: RESTRICTED
+  last_upgrade_date {
+    year: 2023
+    month: 1
+    day: 18
+  }
+}
diff --git a/MODULE_LICENSE_GPL b/MODULE_LICENSE_GPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_GPL
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..61ed976
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,397 @@
+# SPDX-License-Identifier: LGPL-2.1
+# libtracefs version
+TFS_VERSION = 1
+TFS_PATCHLEVEL = 6
+TFS_EXTRAVERSION = 4
+TRACEFS_VERSION = $(TFS_VERSION).$(TFS_PATCHLEVEL).$(TFS_EXTRAVERSION)
+
+export TFS_VERSION
+export TFS_PATCHLEVEL
+export TFS_EXTRAVERSION
+export TRACEFS_VERSION
+
+LIBTRACEEVENT_MIN_VERSION = 1.3
+
+# taken from trace-cmd
+MAKEFLAGS += --no-print-directory
+
+# 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
+
+# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
+$(call allow-override,CC,$(CROSS_COMPILE)gcc)
+$(call allow-override,AR,$(CROSS_COMPILE)ar)
+$(call allow-override,PKG_CONFIG,pkg-config)
+$(call allow-override,LD_SO_CONF_PATH,/etc/ld.so.conf.d/)
+$(call allow-override,LDCONFIG,ldconfig)
+
+EXT = -std=gnu99
+INSTALL = install
+
+# Use DESTDIR for installing into a different root directory.
+# This is useful for building a package. The program will be
+# installed in this directory as if it was the root directory.
+# Then the build tool can move it later.
+DESTDIR ?=
+DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
+
+LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
+ifeq ($(LP64), 1)
+  libdir_relative_temp = lib64
+else
+  libdir_relative_temp = lib
+endif
+
+libdir_relative ?= $(libdir_relative_temp)
+prefix ?= /usr/local
+man_dir ?= $(prefix)/share/man
+man_dir_SQ = '$(subst ','\'',$(man_dir))'
+libdir ?= $(prefix)/$(libdir_relative)
+libdir_SQ = '$(subst ','\'',$(libdir))'
+includedir_relative ?= include/tracefs
+includedir ?= $(prefix)/$(includedir_relative)
+includedir_SQ = '$(subst ','\'',$(includedir))'
+pkgconfig_dir ?= $(word 1,$(shell $(PKG_CONFIG) 		\
+			--variable pc_path pkg-config | tr ":" " "))
+
+TEST_LIBTRACEEVENT = $(shell sh -c "$(PKG_CONFIG) --atleast-version $(LIBTRACEEVENT_MIN_VERSION) libtraceevent > /dev/null 2>&1 && echo y")
+
+ifeq ("$(TEST_LIBTRACEEVENT)", "y")
+LIBTRACEEVENT_INCLUDES = $(shell sh -c "$(PKG_CONFIG) --cflags libtraceevent")
+LIBTRACEEVENT_LIBS = $(shell sh -c "$(PKG_CONFIG) --libs libtraceevent")
+else
+ ifneq ($(MAKECMDGOALS),clean)
+   $(error libtraceevent.so minimum version of $(LIBTRACEEVENT_MIN_VERSION) not installed)
+ endif
+endif
+
+etcdir ?= /etc
+etcdir_SQ = '$(subst ','\'',$(etcdir))'
+
+export man_dir man_dir_SQ html_install html_install_SQ INSTALL
+export img_install img_install_SQ
+export DESTDIR DESTDIR_SQ
+
+pound := \#
+
+HELP_DIR = -DHELP_DIR=$(html_install)
+HELP_DIR_SQ = '$(subst ','\'',$(HELP_DIR))'
+#' emacs highlighting gets confused by the above escaped quote.
+
+BASH_COMPLETE_DIR ?= $(etcdir)/bash_completion.d
+
+# copy a bit from Linux kbuild
+
+ifeq ("$(origin V)", "command line")
+  VERBOSE = $(V)
+endif
+ifndef VERBOSE
+  VERBOSE = 0
+endif
+
+SILENT := $(if $(findstring s,$(filter-out --%,$(MAKEFLAGS))),1)
+
+# $(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)
+
+ifeq ("$(origin O)", "command line")
+
+  saved-output := $(O)
+  BUILD_OUTPUT := $(shell cd $(O) && /bin/pwd)
+  $(if $(BUILD_OUTPUT),, \
+    $(error output directory "$(saved-output)" does not exist))
+
+else
+  BUILD_OUTPUT = $(CURDIR)
+endif
+
+srctree		:= $(if $(BUILD_SRC),$(BUILD_SRC),$(CURDIR))
+objtree		:= $(BUILD_OUTPUT)
+src		:= $(srctree)
+obj		:= $(objtree)
+bdir		:= $(obj)/lib
+
+export prefix src obj bdir
+
+LIBTRACEFS_STATIC = $(bdir)/libtracefs.a
+LIBTRACEFS_SHARED = $(bdir)/libtracefs.so.$(TRACEFS_VERSION)
+
+LIBTRACEFS_SHARED_SO = $(bdir)/libtracefs.so
+LIBTRACEFS_SHARED_VERSION = $(bdir)/libtracefs.so.$(TFS_VERSION)
+
+PKG_CONFIG_SOURCE_FILE = libtracefs.pc
+PKG_CONFIG_FILE := $(addprefix $(obj)/,$(PKG_CONFIG_SOURCE_FILE))
+
+LPTHREAD ?= -lpthread
+LIBS = $(LIBTRACEEVENT_LIBS) $(LPTHREAD)
+
+export LIBS
+export LIBTRACEFS_STATIC LIBTRACEFS_SHARED
+export LIBTRACEEVENT_LIBS LIBTRACEEVENT_INCLUDES
+export LIBTRACEFS_SHARED_SO LIBTRACEFS_SHARED_VERSION
+
+export Q SILENT VERBOSE EXT
+
+# Include the utils
+include scripts/utils.mk
+
+INCLUDES = -I$(src)/include
+INCLUDES += -I$(src)/include/tracefs
+
+include $(src)/scripts/features.mk
+
+# Set compile option CFLAGS if not set elsewhere
+CFLAGS ?= -g -Wall
+CPPFLAGS ?=
+LDFLAGS ?=
+
+CUNIT_INSTALLED := $(shell if (printf "$(pound)include <CUnit/Basic.h>\n void main(){CU_initialize_registry();}" | $(CC) -x c - -lcunit -o /dev/null >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)
+export CUNIT_INSTALLED
+
+export CFLAGS
+export INCLUDES
+
+# Append required CFLAGS
+override CFLAGS += -D_GNU_SOURCE $(LIBTRACEEVENT_INCLUDES) $(INCLUDES)
+
+# Make sure 32 bit stat() works on large file systems
+override CFLAGS += -D_FILE_OFFSET_BITS=64
+
+all: all_cmd
+
+LIB_TARGET  = libtracefs.a libtracefs.so.$(TRACEFS_VERSION)
+LIB_INSTALL = libtracefs.a libtracefs.so*
+LIB_INSTALL := $(addprefix $(bdir)/,$(LIB_INSTALL))
+
+TARGETS = libtracefs.so libtracefs.a
+
+all_cmd: $(TARGETS) $(PKG_CONFIG_FILE)
+
+libtracefs.a: $(bdir) $(LIBTRACEFS_STATIC)
+libtracefs.so: $(bdir) $(LIBTRACEFS_SHARED)
+
+libs: libtracefs.a libtracefs.so
+
+VALGRIND = $(shell which valgrind)
+UTEST_DIR = utest
+UTEST_BINARY = trace-utest
+
+test: force $(LIBTRACEFS_STATIC)
+ifneq ($(CUNIT_INSTALLED),1)
+	$(error CUnit framework not installed, cannot build unit tests))
+endif
+	$(Q)$(call descend,$(src)/$(UTEST_DIR),$@)
+
+test_mem: test
+ifeq (, $(VALGRIND))
+	$(error "No valgrind in $(PATH), cannot run memory test")
+endif
+ifneq ($(shell id -u), 0)
+	$(error "The unit test should be run as root, as it reuqires full access to tracefs")
+endif
+	CK_FORK=no LD_LIBRARY_PATH=$(bdir) $(VALGRIND) \
+		--show-leak-kinds=all --leak-resolution=high \
+		--leak-check=full --show-possibly-lost=yes \
+		--track-origins=yes -s \
+		$(src)/$(UTEST_DIR)/$(UTEST_BINARY)
+
+define find_tag_files
+	find $(src) -name '\.pc' -prune -o -name '*\.[ch]' -print -o -name '*\.[ch]pp' \
+		! -name '\.#' -print
+endef
+
+define do_make_pkgconfig_file
+	cp -f ${PKG_CONFIG_SOURCE_FILE}.template ${PKG_CONFIG_FILE};	\
+	sed -i "s|INSTALL_PREFIX|${1}|g" ${PKG_CONFIG_FILE}; 		\
+	sed -i "s|LIB_VERSION|${TRACEFS_VERSION}|g" ${PKG_CONFIG_FILE}; \
+	sed -i "s|LIB_DIR|${libdir_relative}|g" ${PKG_CONFIG_FILE}; \
+	sed -i "s|HEADER_DIR|$(includedir_relative)|g" ${PKG_CONFIG_FILE}; \
+	sed -i "s|LIBTRACEEVENT_MIN|$(LIBTRACEEVENT_MIN_VERSION)|g" ${PKG_CONFIG_FILE};
+endef
+
+BUILD_PREFIX := $(BUILD_OUTPUT)/build_prefix
+
+VERSION_FILE = tfs_version.h
+
+$(BUILD_PREFIX): force
+	$(Q)$(call build_prefix,$(prefix))
+
+$(PKG_CONFIG_FILE) : ${PKG_CONFIG_SOURCE_FILE}.template $(BUILD_PREFIX) $(VERSION_FILE)
+	$(Q) $(call do_make_pkgconfig_file,$(prefix))
+
+VIM_TAGS = $(obj)/tags
+EMACS_TAGS = $(obj)/TAGS
+CSCOPE_TAGS = $(obj)/cscope
+
+$(VIM_TAGS): force
+	$(RM) $@
+	$(call find_tag_files) | (cd $(obj) && xargs ctags --extra=+f --c-kinds=+px)
+
+$(EMACS_TAGS): force
+	$(RM) $@
+	$(call find_tag_files) | (cd $(obj) && xargs etags)
+
+$(CSCOPE_TAGS): force
+	$(RM) $(obj)/cscope*
+	$(call find_tag_files) | cscope -b -q
+
+tags: $(VIM_TAGS)
+TAGS: $(EMACS_TAGS)
+cscope: $(CSCOPE_TAGS)
+
+ifeq ("$(DESTDIR)", "")
+# If DESTDIR is not defined, then test if after installing the library
+# and running ldconfig, if the library is visible by ld.so.
+# If not, add the path to /etc/ld.so.conf.d/trace.conf and run ldconfig again.
+define install_ld_config
+	if $(LDCONFIG); then \
+		if ! grep -q "^$(libdir)$$" $(LD_SO_CONF_PATH)/* ; then \
+			$(CC) -o $(objtree)/test $(srctree)/test.c -I $(includedir_SQ) \
+				-L $(libdir_SQ) -ltracefs &> /dev/null; \
+			if ! $(objtree)/test &> /dev/null; then \
+				$(call print_install, trace.conf, $(LD_SO_CONF_PATH)) \
+				echo $(libdir_SQ) >> $(LD_SO_CONF_PATH)/trace.conf; \
+				$(LDCONFIG); \
+			fi; \
+			$(RM) $(objtree)/test; \
+		fi; \
+	fi
+endef
+else
+# If installing to a location for another machine or package, do not bother
+# with running ldconfig.
+define install_ld_config
+endef
+endif # DESTDIR = ""
+
+install_libs: libs install_pkgconfig
+	$(Q)$(call do_install,$(LIBTRACEFS_SHARED),$(libdir_SQ)); \
+		cp -fpR $(LIB_INSTALL) $(DESTDIR)$(libdir_SQ)
+	$(Q)$(call do_install,$(src)/include/tracefs.h,$(includedir_SQ),644)
+	$(Q)$(call install_ld_config)
+
+install: install_libs
+
+install_pkgconfig: $(PKG_CONFIG_FILE)
+	$(Q)$(call , $(PKG_CONFIG_FILE)) \
+		$(call do_install_pkgconfig_file,$(prefix))
+
+doc: check_doc
+	$(Q)$(call descend,$(src)/Documentation,all)
+
+doc_clean:
+	$(Q)$(call descend,$(src)/Documentation,clean)
+
+install_doc:
+	$(Q)$(call descend,$(src)/Documentation,install)
+
+check_doc: force
+	$(Q)$(src)/check-manpages.sh $(src)/Documentation
+
+define build_uninstall_script
+	$(Q)mkdir $(BUILD_OUTPUT)/tmp_build
+	$(Q)$(MAKE) -C $(src) DESTDIR=$(BUILD_OUTPUT)/tmp_build/ O=$(BUILD_OUTPUT) $1 > /dev/null
+	$(Q)find $(BUILD_OUTPUT)/tmp_build ! -type d -printf "%P\n" > $(BUILD_OUTPUT)/build_$2
+	$(Q)$(RM) -rf $(BUILD_OUTPUT)/tmp_build
+endef
+
+build_uninstall: $(BUILD_PREFIX)
+	$(call build_uninstall_script,install,uninstall)
+
+$(BUILD_OUTPUT)/build_uninstall: build_uninstall
+
+define uninstall_file
+	if [ -f $(DESTDIR)/$1 -o -h $(DESTDIR)/$1 ]; then \
+		$(call print_uninstall,$(DESTDIR)/$1)$(RM) $(DESTDIR)/$1; \
+	fi;
+endef
+
+uninstall: $(BUILD_OUTPUT)/build_uninstall
+	@$(foreach file,$(shell cat $(BUILD_OUTPUT)/build_uninstall),$(call uninstall_file,$(file)))
+
+PHONY += force
+force:
+
+# Declare the contents of the .PHONY variable as phony.  We keep that
+# information in a variable so we can use it in if_changed and friends.
+.PHONY: $(PHONY)
+
+DEFAULT_TARGET = $(LIBTRACEFS_STATIC)
+
+OBJS =
+OBJS += tracefs-utils.o
+OBJS += tracefs-instance.o
+OBJS += tracefs-events.o
+
+OBJS := $(OBJS:%.o=$(bdir)/%.o)
+
+all: $(DEFAULT_TARGET)
+
+$(bdir):
+	@mkdir -p $(bdir)
+
+VERSION = $(TFS_VERSION)
+PATCHLEVEL = $(TFS_PATCHLEVEL)
+EXTRAVERSION = $(TFS_EXTRAVERSION)
+
+define make_version.h
+  (echo '/* This file is automatically generated. Do not modify. */';		\
+   echo \#define VERSION_CODE $(shell						\
+   expr $(VERSION) \* 256 + $(PATCHLEVEL));					\
+   echo '#define EXTRAVERSION ' $(EXTRAVERSION);				\
+   echo '#define VERSION_STRING "'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'"';	\
+  ) > $1
+endef
+
+define update_version.h
+  ($(call make_version.h, $@.tmp);		\
+    if [ -r $@ ] && cmp -s $@ $@.tmp; then	\
+      rm -f $@.tmp;				\
+    else					\
+      echo '  UPDATE             $@';		\
+      mv -f $@.tmp $@;				\
+    fi);
+endef
+
+$(VERSION_FILE): force
+	$(Q)$(call update_version.h)
+
+$(LIBTRACEFS_STATIC): force
+	$(Q)$(call descend,$(src)/src,$@)
+
+$(bdir)/libtracefs.so.$(TRACEFS_VERSION): force
+	$(Q)$(call descend,$(src)/src,libtracefs.so)
+
+samples/sqlhist: libtracefs.a
+	$(Q)$(call descend,$(src)/samples,sqlhist)
+
+sqlhist: samples/sqlhist
+
+samples: libtracefs.a force
+	$(Q)$(call descend,$(src)/samples,all)
+
+clean:
+	$(Q)$(call descend_clean,utest)
+	$(Q)$(call descend_clean,src)
+	$(Q)$(call descend_clean,samples)
+	$(Q)$(call do_clean, \
+	  $(TARGETS) $(bdir)/*.a $(bdir)/*.so $(bdir)/*.so.* $(bdir)/*.o $(bdir)/.*.d \
+	  $(PKG_CONFIG_FILE) \
+	  $(VERSION_FILE) \
+	  $(BUILD_PREFIX))
+
+.PHONY: clean
+
+# libtracefs.a and libtracefs.so would concurrently enter the same directory -
+# a recipe for collisions.
+.NOTPARALLEL:
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..7e72f72
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+kaleshsingh@google.com
+namhyung@google.com
diff --git a/README b/README
new file mode 100644
index 0000000..ad95602
--- /dev/null
+++ b/README
@@ -0,0 +1,56 @@
+
+The official repository is here:
+
+  https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+This repository requires libtraceevent to be installed:
+
+  https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/
+
+
+To build:
+
+    make;
+    sudo make install;
+
+To build in a specific directory outside of the source directory:
+
+    make O=/path/to/build; sudo make O=/path/to/build
+
+  Note that the path needs to exist before building.
+
+To set the install path (the expected final location):
+
+    make prefix=/usr; sudo make O=/path/to/build
+
+To install in a directory not for the local system (for use to move
+to another machine):
+
+    make DESTDIR=/path/to/dest/ install
+
+  Note, if you have write permission to the DESTDIR, then there is
+  no reason to use sudo or switch to root.
+
+  Note, DESTDIR must end with '/', otherwise the files will be appended
+  to the path, which will most likely have unwanted results.
+
+Contributions:
+
+  For questions about the use of the library, please send email to:
+
+    linux-trace-users@vger.kernel.org
+
+    Subscribe: http://vger.kernel.org/vger-lists.html#linux-trace-users
+    Archives: https://lore.kernel.org/linux-trace-users/
+
+  For contributions to development, please send patches to:
+
+    linux-trace-devel@vger.kernel.org
+
+    Subscribe: http://vger.kernel.org/vger-lists.html#linux-trace-devel
+    Archives: https://lore.kernel.org/linux-trace-devel/
+
+  Note, this project follows the style of submitting patches as described
+  by the Linux kernel.
+
+     https://www.kernel.org/doc/html/v5.4/process/submitting-patches.html
diff --git a/check-manpages.sh b/check-manpages.sh
new file mode 100755
index 0000000..776365c
--- /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=libtracefs
+MAIN_FILE=${MAIN}.txt
+
+PROCESSED=""
+
+# Ignore man pages that do not contain functions
+IGNORE="libtracefs-options.txt"
+
+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="*tracefs_event_append_filter* *tracefs_event_verify_filter*"
+
+sed -ne 's/^[a-z].*[ \*]\([a-z_][a-z_]*\)(.*/\1/p' -e 's/^\([a-z_][a-z_]*\)(.*/\1/p' ../include/tracefs.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/tracefs-local.h b/include/tracefs-local.h
new file mode 100644
index 0000000..2007d26
--- /dev/null
+++ b/include/tracefs-local.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#ifndef _TRACE_FS_LOCAL_H
+#define _TRACE_FS_LOCAL_H
+
+#include <pthread.h>
+
+#define __hidden __attribute__((visibility ("hidden")))
+#define __weak __attribute__((weak))
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+
+/* Will cause a division by zero warning if cond is true */
+#define BUILD_BUG_ON(cond)			\
+	do { if (!(1/!(cond))) { } } while (0)
+
+#define HASH_BITS 10
+
+struct tracefs_options_mask {
+	unsigned long long	mask;
+};
+
+struct follow_event {
+	struct tep_event	*event;
+	void			*callback_data;
+	int (*callback)(struct tep_event *,
+			struct tep_record *,
+			int, void *);
+};
+
+struct tracefs_instance {
+	struct tracefs_options_mask	supported_opts;
+	struct tracefs_options_mask	enabled_opts;
+	struct follow_event		*followers;
+	struct follow_event		*missed_followers;
+	char				*trace_dir;
+	char				*name;
+	pthread_mutex_t			lock;
+	int				ref;
+	int				flags;
+	int				ftrace_filter_fd;
+	int				ftrace_notrace_fd;
+	int				ftrace_marker_fd;
+	int				ftrace_marker_raw_fd;
+	int				nr_followers;
+	int				nr_missed_followers;
+	bool				pipe_keep_going;
+	bool				iterate_keep_going;
+};
+
+extern pthread_mutex_t toplevel_lock;
+
+static inline pthread_mutex_t *trace_get_lock(struct tracefs_instance *instance)
+{
+	return instance ? &instance->lock : &toplevel_lock;
+}
+
+void trace_put_instance(struct tracefs_instance *instance);
+int trace_get_instance(struct tracefs_instance *instance);
+
+/* Can be overridden */
+void tracefs_warning(const char *fmt, ...);
+
+int str_read_file(const char *file, char **buffer, bool warn);
+char *trace_append_file(const char *dir, const char *name);
+char *trace_find_tracing_dir(bool debugfs);
+
+#ifndef ACCESSPERMS
+#define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) /* 0777 */
+#endif
+
+#ifndef ALLPERMS
+#define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) /* 07777 */
+#endif
+
+#ifndef DEFFILEMODE
+#define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) /* 0666*/
+#endif
+
+struct tracefs_options_mask *
+supported_opts_mask(struct tracefs_instance *instance);
+
+struct tracefs_options_mask *
+enabled_opts_mask(struct tracefs_instance *instance);
+
+char **trace_list_create_empty(void);
+int trace_list_pop(char **list);
+
+char *append_string(char *str, const char *delim, const char *add);
+int trace_test_state(int state);
+bool trace_verify_event_field(struct tep_event *event,
+			      const char *field_name,
+			      const struct tep_format_field **ptr_field);
+int trace_append_filter(char **filter, unsigned int *state,
+			unsigned int *open_parens,
+			struct tep_event *event,
+			enum tracefs_filter type,
+			const char *field_name,
+			enum tracefs_compare compare,
+			 const char *val);
+
+struct tracefs_synth *synth_init_from(struct tep_handle *tep,
+				      const char *start_system,
+				      const char *start_event);
+
+#define HIST_COUNTER_TYPE	(TRACEFS_HIST_KEY_MAX + 100)
+int synth_add_start_field(struct tracefs_synth *synth,
+			  const char *start_field,
+			  const char *name,
+			  enum tracefs_hist_key_type type, int cnt);
+
+/* Internal interface for ftrace dynamic events */
+
+struct tracefs_dynevent {
+	char *trace_file;
+	char *prefix;
+	char *system;
+	char *event;
+	char *address;
+	char *format;
+	enum tracefs_dynevent_type type;
+};
+
+struct tracefs_dynevent *
+dynevent_alloc(enum tracefs_dynevent_type type, const char *system,
+	       const char *event, const char *address, const char *format);
+int dynevent_get_count(unsigned int types, const char *system);
+
+int trace_load_events(struct tep_handle *tep,
+		      const char *tracing_dir, const char *system);
+int trace_rescan_events(struct tep_handle *tep,
+			const char *tracing_dir, const char *system);
+struct tep_event *get_tep_event(struct tep_handle *tep,
+				const char *system, const char *name);
+
+unsigned int quick_hash(const char *str);
+
+#endif /* _TRACE_FS_LOCAL_H */
diff --git a/include/tracefs.h b/include/tracefs.h
new file mode 100644
index 0000000..3547b5a
--- /dev/null
+++ b/include/tracefs.h
@@ -0,0 +1,637 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#ifndef _TRACE_FS_H
+#define _TRACE_FS_H
+
+#include <fcntl.h>
+#include <sched.h>
+#include <event-parse.h>
+
+char *tracefs_get_tracing_file(const char *name);
+void tracefs_put_tracing_file(char *name);
+
+/* the returned string must *not* be freed */
+const char *tracefs_tracing_dir(void);
+const char *tracefs_debug_dir(void);
+int tracefs_set_tracing_dir(char *tracing_dir);
+int tracefs_tracing_dir_is_mounted(bool mount, const char **path);
+
+/* ftrace instances */
+struct tracefs_instance;
+
+void tracefs_instance_free(struct tracefs_instance *instance);
+struct tracefs_instance *tracefs_instance_create(const char *name);
+struct tracefs_instance *tracefs_instance_alloc(const char *tracing_dir,
+						const char *name);
+int tracefs_instance_destroy(struct tracefs_instance *instance);
+bool tracefs_instance_is_new(struct tracefs_instance *instance);
+const char *tracefs_instance_get_name(struct tracefs_instance *instance);
+const char *tracefs_instance_get_trace_dir(struct tracefs_instance *instance);
+char *
+tracefs_instance_get_file(struct tracefs_instance *instance, const char *file);
+char *tracefs_instance_get_dir(struct tracefs_instance *instance);
+int tracefs_instance_file_write(struct tracefs_instance *instance,
+				const char *file, const char *str);
+int tracefs_instance_file_append(struct tracefs_instance *instance,
+				 const char *file, const char *str);
+int tracefs_instance_file_clear(struct tracefs_instance *instance,
+				const char *file);
+char *tracefs_instance_file_read(struct tracefs_instance *instance,
+				 const char *file, int *psize);
+int tracefs_instance_file_read_number(struct tracefs_instance *instance,
+				      const char *file, long long *res);
+int tracefs_instance_file_open(struct tracefs_instance *instance,
+			       const char *file, int mode);
+int tracefs_instances_walk(int (*callback)(const char *, void *), void *context);
+int tracefs_instance_set_affinity_set(struct tracefs_instance *instance,
+				  cpu_set_t *set, size_t set_size);
+int tracefs_instance_set_affinity_raw(struct tracefs_instance *instance,
+				      const char *mask);
+int tracefs_instance_set_affinity(struct tracefs_instance *instance,
+				  const char *cpu_str);
+char *tracefs_instance_get_affinity(struct tracefs_instance *instance);
+char *tracefs_instance_get_affinity_raw(struct tracefs_instance *instance);
+int tracefs_instance_get_affinity_set(struct tracefs_instance *instance,
+				      cpu_set_t *set, size_t set_size);
+ssize_t tracefs_instance_get_buffer_size(struct tracefs_instance *instance, int cpu);
+int tracefs_instance_set_buffer_size(struct tracefs_instance *instance, size_t size, int cpu);
+char **tracefs_instances(const char *regex);
+
+bool tracefs_instance_exists(const char *name);
+bool tracefs_file_exists(struct tracefs_instance *instance, const char *name);
+bool tracefs_dir_exists(struct tracefs_instance *instance, const char *name);
+
+int tracefs_trace_is_on(struct tracefs_instance *instance);
+int tracefs_trace_on(struct tracefs_instance *instance);
+int tracefs_trace_off(struct tracefs_instance *instance);
+int tracefs_trace_on_fd(int fd);
+int tracefs_trace_off_fd(int fd);
+
+enum tracefs_enable_state {
+	TRACEFS_ERROR		= -1,
+	TRACEFS_ALL_DISABLED	= 0,
+	TRACEFS_ALL_ENABLED	= 1,
+	TRACEFS_SOME_ENABLED	= 2,
+};
+
+int tracefs_event_enable(struct tracefs_instance *instance, const char *system, const char *event);
+int tracefs_event_disable(struct tracefs_instance *instance, const char *system, const char *event);
+enum tracefs_enable_state tracefs_event_is_enabled(struct tracefs_instance *instance,
+			 const char *system, const char *event);
+
+char *tracefs_error_last(struct tracefs_instance *instance);
+char *tracefs_error_all(struct tracefs_instance *instance);
+int tracefs_error_clear(struct tracefs_instance *instance);
+
+void tracefs_list_free(char **list);
+char **tracefs_list_add(char **list, const char *string);
+int tracefs_list_size(char **list);
+
+bool tracefs_tracer_available(const char *tracing_dir, const char *tracer);
+
+/**
+ * tracefs_trace_on_get_fd - Get a file descriptor of "tracing_on" in given instance
+ * @instance: ftrace instance, can be NULL for the top instance
+ *
+ * Returns -1 in case of an error, or a valid file descriptor to "tracing_on"
+ * file for reading and writing.The returned FD must be closed with close().
+ */
+static inline int tracefs_trace_on_get_fd(struct tracefs_instance *instance)
+{
+	return tracefs_instance_file_open(instance, "tracing_on", O_RDWR);
+}
+
+/* trace print string*/
+int tracefs_print_init(struct tracefs_instance *instance);
+int tracefs_printf(struct tracefs_instance *instance, const char *fmt, ...);
+int tracefs_vprintf(struct tracefs_instance *instance, const char *fmt, va_list ap);
+void tracefs_print_close(struct tracefs_instance *instance);
+
+/* trace write binary data*/
+int tracefs_binary_init(struct tracefs_instance *instance);
+int tracefs_binary_write(struct tracefs_instance *instance, void *data, int len);
+void tracefs_binary_close(struct tracefs_instance *instance);
+
+/* events */
+char **tracefs_event_systems(const char *tracing_dir);
+char **tracefs_system_events(const char *tracing_dir, const char *system);
+int tracefs_iterate_raw_events(struct tep_handle *tep,
+				struct tracefs_instance *instance,
+				cpu_set_t *cpus, int cpu_size,
+				int (*callback)(struct tep_event *,
+						struct tep_record *,
+						int, void *),
+				void *callback_context);
+void tracefs_iterate_stop(struct tracefs_instance *instance);
+int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instance,
+			  const char *system, const char *event_name,
+			  int (*callback)(struct tep_event *,
+					  struct tep_record *,
+					  int, void *),
+			  void *callback_data);
+int tracefs_follow_missed_events(struct tracefs_instance *instance,
+				 int (*callback)(struct tep_event *,
+						 struct tep_record *,
+						 int, void *),
+				 void *callback_data);
+
+char *tracefs_event_get_file(struct tracefs_instance *instance,
+			     const char *system, const char *event,
+			     const char *file);
+char *tracefs_event_file_read(struct tracefs_instance *instance,
+			      const char *system, const char *event,
+			      const char *file, int *psize);
+int tracefs_event_file_write(struct tracefs_instance *instance,
+			     const char *system, const char *event,
+			     const char *file, const char *str);
+int tracefs_event_file_append(struct tracefs_instance *instance,
+			      const char *system, const char *event,
+			      const char *file, const char *str);
+int tracefs_event_file_clear(struct tracefs_instance *instance,
+			     const char *system, const char *event,
+			     const char *file);
+bool tracefs_event_file_exists(struct tracefs_instance *instance,
+			       const char *system, const char *event,
+			       const char *file);
+
+char **tracefs_tracers(const char *tracing_dir);
+
+struct tep_handle *tracefs_local_events(const char *tracing_dir);
+struct tep_handle *tracefs_local_events_system(const char *tracing_dir,
+					       const char * const *sys_names);
+int tracefs_fill_local_events(const char *tracing_dir,
+			       struct tep_handle *tep, int *parsing_failures);
+
+int tracefs_load_cmdlines(const char *tracing_dir, struct tep_handle *tep);
+
+char *tracefs_get_clock(struct tracefs_instance *instance);
+
+enum tracefs_option_id {
+	TRACEFS_OPTION_INVALID = 0,
+	TRACEFS_OPTION_ANNOTATE,
+	TRACEFS_OPTION_BIN,
+	TRACEFS_OPTION_BLK_CGNAME,
+	TRACEFS_OPTION_BLK_CGROUP,
+	TRACEFS_OPTION_BLK_CLASSIC,
+	TRACEFS_OPTION_BLOCK,
+	TRACEFS_OPTION_CONTEXT_INFO,
+	TRACEFS_OPTION_DISABLE_ON_FREE,
+	TRACEFS_OPTION_DISPLAY_GRAPH,
+	TRACEFS_OPTION_EVENT_FORK,
+	TRACEFS_OPTION_FGRAPH_ABSTIME,
+	TRACEFS_OPTION_FGRAPH_CPU,
+	TRACEFS_OPTION_FGRAPH_DURATION,
+	TRACEFS_OPTION_FGRAPH_IRQS,
+	TRACEFS_OPTION_FGRAPH_OVERHEAD,
+	TRACEFS_OPTION_FGRAPH_OVERRUN,
+	TRACEFS_OPTION_FGRAPH_PROC,
+	TRACEFS_OPTION_FGRAPH_TAIL,
+	TRACEFS_OPTION_FUNC_STACKTRACE,
+	TRACEFS_OPTION_FUNCTION_FORK,
+	TRACEFS_OPTION_FUNCTION_TRACE,
+	TRACEFS_OPTION_GRAPH_TIME,
+	TRACEFS_OPTION_HEX,
+	TRACEFS_OPTION_IRQ_INFO,
+	TRACEFS_OPTION_LATENCY_FORMAT,
+	TRACEFS_OPTION_MARKERS,
+	TRACEFS_OPTION_OVERWRITE,
+	TRACEFS_OPTION_PAUSE_ON_TRACE,
+	TRACEFS_OPTION_PRINTK_MSG_ONLY,
+	TRACEFS_OPTION_PRINT_PARENT,
+	TRACEFS_OPTION_RAW,
+	TRACEFS_OPTION_RECORD_CMD,
+	TRACEFS_OPTION_RECORD_TGID,
+	TRACEFS_OPTION_SLEEP_TIME,
+	TRACEFS_OPTION_STACKTRACE,
+	TRACEFS_OPTION_SYM_ADDR,
+	TRACEFS_OPTION_SYM_OFFSET,
+	TRACEFS_OPTION_SYM_USEROBJ,
+	TRACEFS_OPTION_TRACE_PRINTK,
+	TRACEFS_OPTION_USERSTACKTRACE,
+	TRACEFS_OPTION_VERBOSE,
+};
+#define TRACEFS_OPTION_MAX (TRACEFS_OPTION_VERBOSE + 1)
+
+struct tracefs_options_mask;
+bool tracefs_option_mask_is_set(const struct tracefs_options_mask *options,
+				enum tracefs_option_id id);
+const struct tracefs_options_mask *tracefs_options_get_supported(struct tracefs_instance *instance);
+bool tracefs_option_is_supported(struct tracefs_instance *instance, enum tracefs_option_id id);
+const struct tracefs_options_mask *tracefs_options_get_enabled(struct tracefs_instance *instance);
+bool tracefs_option_is_enabled(struct tracefs_instance *instance, enum tracefs_option_id id);
+int tracefs_option_enable(struct tracefs_instance *instance, enum tracefs_option_id id);
+int tracefs_option_disable(struct tracefs_instance *instance, enum tracefs_option_id id);
+const char *tracefs_option_name(enum tracefs_option_id id);
+enum tracefs_option_id tracefs_option_id(const char *name);
+
+/*
+ * RESET	- Reset on opening filter file (O_TRUNC)
+ * CONTINUE	- Do not close filter file on return.
+ * FUTURE	- For kernels that support this feature, enable filters for
+ *		  a module that has yet to be loaded.
+ */
+enum {
+	TRACEFS_FL_RESET	= (1 << 0),
+	TRACEFS_FL_CONTINUE	= (1 << 1),
+	TRACEFS_FL_FUTURE	= (1 << 2),
+};
+
+int tracefs_function_filter(struct tracefs_instance *instance, const char *filter,
+			    const char *module, unsigned int flags);
+int tracefs_function_notrace(struct tracefs_instance *instance, const char *filter,
+			     const char *module, unsigned int flags);
+int tracefs_filter_functions(const char *filter, const char *module, char ***list);
+
+
+/* Control library logs */
+void tracefs_set_loglevel(enum tep_loglevel level);
+
+enum tracefs_tracers {
+	TRACEFS_TRACER_NOP = 0,
+	TRACEFS_TRACER_CUSTOM,
+	TRACEFS_TRACER_FUNCTION,
+	TRACEFS_TRACER_FUNCTION_GRAPH,
+	TRACEFS_TRACER_IRQSOFF,
+	TRACEFS_TRACER_PREEMPTOFF,
+	TRACEFS_TRACER_PREEMPTIRQSOFF,
+	TRACEFS_TRACER_WAKEUP,
+	TRACEFS_TRACER_WAKEUP_RT,
+	TRACEFS_TRACER_WAKEUP_DL,
+	TRACEFS_TRACER_MMIOTRACE,
+	TRACEFS_TRACER_HWLAT,
+	TRACEFS_TRACER_BRANCH,
+	TRACEFS_TRACER_BLOCK,
+};
+
+int tracefs_tracer_set(struct tracefs_instance *instance, enum tracefs_tracers tracer, ...);
+
+int tracefs_tracer_clear(struct tracefs_instance *instance);
+
+ssize_t tracefs_trace_pipe_stream(int fd, struct tracefs_instance *instance, int flags);
+ssize_t tracefs_trace_pipe_print(struct tracefs_instance *instance, int flags);
+void tracefs_trace_pipe_stop(struct tracefs_instance *instance);
+
+/* Dynamic events */
+struct tracefs_dynevent;
+enum tracefs_dynevent_type {
+	TRACEFS_DYNEVENT_UNKNOWN	= 0,
+	TRACEFS_DYNEVENT_KPROBE		= 1 << 0,
+	TRACEFS_DYNEVENT_KRETPROBE	= 1 << 1,
+	TRACEFS_DYNEVENT_UPROBE		= 1 << 2,
+	TRACEFS_DYNEVENT_URETPROBE	= 1 << 3,
+	TRACEFS_DYNEVENT_EPROBE		= 1 << 4,
+	TRACEFS_DYNEVENT_SYNTH		= 1 << 5,
+	TRACEFS_DYNEVENT_MAX		= 1 << 6,
+};
+
+#define TRACEFS_DYNEVENT_ALL		0xFFFFFFFF
+
+int tracefs_dynevent_create(struct tracefs_dynevent *devent);
+int tracefs_dynevent_destroy(struct tracefs_dynevent *devent, bool force);
+int tracefs_dynevent_destroy_all(unsigned int types, bool force);
+void tracefs_dynevent_free(struct tracefs_dynevent *devent);
+void tracefs_dynevent_list_free(struct tracefs_dynevent **events);
+struct tracefs_dynevent **
+tracefs_dynevent_get_all(unsigned int types, const char *system);
+struct tracefs_dynevent *
+tracefs_dynevent_get(enum tracefs_dynevent_type type, const char *system, const char *event);
+enum tracefs_dynevent_type
+tracefs_dynevent_info(struct tracefs_dynevent *dynevent, char **system,
+		      char **event, char **prefix, char **addr, char **format);
+struct tep_event *
+tracefs_dynevent_get_event(struct tep_handle *tep, struct tracefs_dynevent *dynevent);
+
+struct tracefs_dynevent *
+tracefs_eprobe_alloc(const char *system, const char *event,
+		     const char *target_system, const char *target_event, const char *fetchargs);
+struct tracefs_dynevent *
+tracefs_uprobe_alloc(const char *system, const char *event,
+		     const char *file, unsigned long long offset, const char *fetchargs);
+struct tracefs_dynevent *
+tracefs_uretprobe_alloc(const char *system, const char *event,
+			const char *file, unsigned long long offset, const char *fetchargs);
+
+struct tracefs_dynevent *
+tracefs_kprobe_alloc(const char *system, const char *event, const char *addr, const char *format);
+struct tracefs_dynevent *
+tracefs_kretprobe_alloc(const char *system, const char *event,
+			const char *addr, const char *format, unsigned int max);
+int tracefs_kprobe_raw(const char *system, const char *event,
+		       const char *addr, const char *format);
+int tracefs_kretprobe_raw(const char *system, const char *event,
+			  const char *addr, const char *format);
+
+enum tracefs_hist_key_type {
+	TRACEFS_HIST_KEY_NORMAL = 0,
+	TRACEFS_HIST_KEY_HEX,
+	TRACEFS_HIST_KEY_SYM,
+	TRACEFS_HIST_KEY_SYM_OFFSET,
+	TRACEFS_HIST_KEY_SYSCALL,
+	TRACEFS_HIST_KEY_EXECNAME,
+	TRACEFS_HIST_KEY_LOG,
+	TRACEFS_HIST_KEY_USECS,
+	TRACEFS_HIST_KEY_BUCKETS,
+	TRACEFS_HIST_KEY_MAX
+};
+
+enum tracefs_hist_sort_direction {
+	TRACEFS_HIST_SORT_ASCENDING,
+	TRACEFS_HIST_SORT_DESCENDING,
+};
+
+#define TRACEFS_HIST_TIMESTAMP		"common_timestamp"
+#define TRACEFS_HIST_TIMESTAMP_USECS	"common_timestamp.usecs"
+#define TRACEFS_HIST_CPU		"cpu"
+
+#define TRACEFS_HIST_COUNTER		"__COUNTER__"
+
+#define TRACEFS_HIST_HITCOUNT		"hitcount"
+
+struct tracefs_hist;
+
+enum tracefs_hist_command {
+	TRACEFS_HIST_CMD_START = 0,
+	TRACEFS_HIST_CMD_PAUSE,
+	TRACEFS_HIST_CMD_CONT,
+	TRACEFS_HIST_CMD_CLEAR,
+	TRACEFS_HIST_CMD_DESTROY,
+};
+
+enum tracefs_filter {
+	TRACEFS_FILTER_COMPARE,
+	TRACEFS_FILTER_AND,
+	TRACEFS_FILTER_OR,
+	TRACEFS_FILTER_NOT,
+	TRACEFS_FILTER_OPEN_PAREN,
+	TRACEFS_FILTER_CLOSE_PAREN,
+};
+
+enum tracefs_compare {
+	TRACEFS_COMPARE_EQ,
+	TRACEFS_COMPARE_NE,
+	TRACEFS_COMPARE_GT,
+	TRACEFS_COMPARE_GE,
+	TRACEFS_COMPARE_LT,
+	TRACEFS_COMPARE_LE,
+	TRACEFS_COMPARE_RE,
+	TRACEFS_COMPARE_AND,
+};
+
+void tracefs_hist_free(struct tracefs_hist *hist);
+struct tracefs_hist *
+tracefs_hist_alloc(struct tep_handle *tep,
+		   const char *system, const char *event_name,
+		   const char *key, enum tracefs_hist_key_type type);
+struct tracefs_hist *
+tracefs_hist_alloc_2d(struct tep_handle *tep,
+		      const char *system, const char *event_name,
+		      const char *key1, enum tracefs_hist_key_type type1,
+		      const char *key2, enum tracefs_hist_key_type type2);
+
+struct tracefs_hist_axis {
+	const char *key;
+	enum tracefs_hist_key_type type;
+};
+
+struct tracefs_hist_axis_cnt {
+	const char *key;
+	enum tracefs_hist_key_type type;
+	int cnt;
+};
+
+struct tracefs_hist *
+tracefs_hist_alloc_nd(struct tep_handle *tep,
+		      const char *system, const char *event_name,
+		      struct tracefs_hist_axis *axes);
+struct tracefs_hist *
+tracefs_hist_alloc_nd_cnt(struct tep_handle *tep,
+			  const char *system, const char *event_name,
+			  struct tracefs_hist_axis_cnt *axes);
+const char *tracefs_hist_get_name(struct tracefs_hist *hist);
+const char *tracefs_hist_get_event(struct tracefs_hist *hist);
+const char *tracefs_hist_get_system(struct tracefs_hist *hist);
+int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
+			 enum tracefs_hist_key_type type);
+int tracefs_hist_add_key_cnt(struct tracefs_hist *hist, const char *key,
+			 enum tracefs_hist_key_type type, int cnt);
+int tracefs_hist_add_value(struct tracefs_hist *hist, const char *value);
+int tracefs_hist_add_sort_key(struct tracefs_hist *hist,
+			      const char *sort_key);
+int tracefs_hist_set_sort_key(struct tracefs_hist *hist,
+			      const char *sort_key, ...);
+int tracefs_hist_sort_key_direction(struct tracefs_hist *hist,
+				    const char *sort_key,
+				    enum tracefs_hist_sort_direction dir);
+int tracefs_hist_add_name(struct tracefs_hist *hist, const char *name);
+int tracefs_hist_append_filter(struct tracefs_hist *hist,
+			       enum tracefs_filter type,
+			       const char *field,
+			       enum tracefs_compare compare,
+			       const char *val);
+int tracefs_hist_echo_cmd(struct trace_seq *seq,  struct tracefs_instance *instance,
+			  struct tracefs_hist *hist, enum tracefs_hist_command command);
+int tracefs_hist_command(struct tracefs_instance *instance,
+			 struct tracefs_hist *hist, enum tracefs_hist_command cmd);
+
+/**
+ * tracefs_hist_start - enable a histogram
+ * @instance: The instance the histogram will be in (NULL for toplevel)
+ * @hist: The histogram to start
+ *
+ * Starts executing a histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static inline int tracefs_hist_start(struct tracefs_instance *instance,
+				     struct tracefs_hist *hist)
+{
+	return tracefs_hist_command(instance, hist, 0);
+}
+
+/**
+ * tracefs_hist_pause - pause a histogram
+ * @instance: The instance the histogram is in (NULL for toplevel)
+ * @hist: The histogram to pause
+ *
+ * Pause a histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static inline int tracefs_hist_pause(struct tracefs_instance *instance,
+				     struct tracefs_hist *hist)
+{
+	return tracefs_hist_command(instance, hist, TRACEFS_HIST_CMD_PAUSE);
+}
+
+/**
+ * tracefs_hist_continue - continue a paused histogram
+ * @instance: The instance the histogram is in (NULL for toplevel)
+ * @hist: The histogram to continue
+ *
+ * Continue a histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static inline int tracefs_hist_continue(struct tracefs_instance *instance,
+					struct tracefs_hist *hist)
+{
+	return tracefs_hist_command(instance, hist, TRACEFS_HIST_CMD_CONT);
+}
+
+/**
+ * tracefs_hist_reset - clear a histogram
+ * @instance: The instance the histogram is in (NULL for toplevel)
+ * @hist: The histogram to reset
+ *
+ * Resets a histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static inline int tracefs_hist_reset(struct tracefs_instance *instance,
+				     struct tracefs_hist *hist)
+{
+	return tracefs_hist_command(instance, hist, TRACEFS_HIST_CMD_CLEAR);
+}
+
+/**
+ * tracefs_hist_destroy - deletes a histogram (needs to be enabled again)
+ * @instance: The instance the histogram is in (NULL for toplevel)
+ * @hist: The histogram to delete
+ *
+ * Deletes (removes) a running histogram. This is different than
+ * clear, as clear only clears the data but the histogram still exists.
+ * This deletes the histogram and should be called before
+ * tracefs_hist_free() to clean up properly.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static inline int tracefs_hist_destroy(struct tracefs_instance *instance,
+				       struct tracefs_hist *hist)
+{
+	return tracefs_hist_command(instance, hist, TRACEFS_HIST_CMD_DESTROY);
+}
+
+struct tracefs_synth;
+
+/*
+ * DELTA_END	- end_field - start_field
+ * DELTA_START	- start_field - end_field
+ * ADD		- start_field + end_field
+ */
+enum tracefs_synth_calc {
+	TRACEFS_SYNTH_DELTA_END,
+	TRACEFS_SYNTH_DELTA_START,
+	TRACEFS_SYNTH_ADD,
+};
+
+int tracefs_filter_string_append(struct tep_event *event, char **filter,
+				 enum tracefs_filter type,
+				 const char *field, enum tracefs_compare compare,
+				 const char *val);
+int tracefs_filter_string_verify(struct tep_event *event, const char *filter,
+				 char **err);
+
+int tracefs_event_filter_apply(struct tracefs_instance *instance,
+			       struct tep_event *event, const char *filter);
+
+int tracefs_event_filter_clear(struct tracefs_instance *instance,
+			       struct tep_event *event);
+
+/** Deprecated do not use: Instead use tracefs_filter_string_append() **/
+int tracefs_event_append_filter(struct tep_event *event, char **filter,
+				enum tracefs_filter type,
+				const char *field, enum tracefs_compare compare,
+				const char *val);
+
+/** Deprecated do not use: Instead use tracefs_filter_string_verify()  **/
+int tracefs_event_verify_filter(struct tep_event *event, const char *filter,
+				char **err);
+
+#define TRACEFS_TIMESTAMP "common_timestamp"
+#define TRACEFS_TIMESTAMP_USECS "common_timestamp.usecs"
+
+enum tracefs_synth_handler {
+	TRACEFS_SYNTH_HANDLE_NONE	= 0,
+	TRACEFS_SYNTH_HANDLE_MATCH,
+	TRACEFS_SYNTH_HANDLE_MAX,
+	TRACEFS_SYNTH_HANDLE_CHANGE,
+};
+
+const char *tracefs_synth_get_name(struct tracefs_synth *synth);
+struct tracefs_synth *tracefs_synth_alloc(struct tep_handle *tep,
+					  const char *name,
+					  const char *start_system,
+					  const char *start_event,
+					  const char *end_system,
+					  const char *end_event,
+					  const char *start_match_field,
+					  const char *end_match_field,
+					  const char *match_name);
+int tracefs_synth_add_match_field(struct tracefs_synth *synth,
+				  const char *start_match_field,
+				  const char *end_match_field,
+				  const char *name);
+int tracefs_synth_add_compare_field(struct tracefs_synth *synth,
+				    const char *start_compare_field,
+				    const char *end_compare_field,
+				    enum tracefs_synth_calc calc,
+				    const char *name);
+int tracefs_synth_add_start_field(struct tracefs_synth *synth,
+				  const char *start_field,
+				  const char *name);
+int tracefs_synth_add_end_field(struct tracefs_synth *synth,
+				const char *end_field,
+				const char *name);
+int tracefs_synth_append_start_filter(struct tracefs_synth *synth,
+				      enum tracefs_filter type,
+				      const char *field,
+				      enum tracefs_compare compare,
+				      const char *val);
+int tracefs_synth_append_end_filter(struct tracefs_synth *synth,
+				    enum tracefs_filter type,
+				    const char *field,
+				    enum tracefs_compare compare,
+				    const char *val);
+int tracefs_synth_trace(struct tracefs_synth *synth,
+			enum tracefs_synth_handler type, const char *field);
+int tracefs_synth_snapshot(struct tracefs_synth *synth,
+			   enum tracefs_synth_handler type, const char *field);
+int tracefs_synth_save(struct tracefs_synth *synth,
+		       enum tracefs_synth_handler type, const char *field,
+		       char **save_fields);
+bool tracefs_synth_complete(struct tracefs_synth *synth);
+struct tracefs_hist *tracefs_synth_get_start_hist(struct tracefs_synth *synth);
+int tracefs_synth_create(struct tracefs_synth *synth);
+int tracefs_synth_destroy(struct tracefs_synth *synth);
+void tracefs_synth_free(struct tracefs_synth *synth);
+int tracefs_synth_echo_cmd(struct trace_seq *seq, struct tracefs_synth *synth);
+int tracefs_synth_raw_fmt(struct trace_seq *seq, struct tracefs_synth *synth);
+const char *tracefs_synth_show_event(struct tracefs_synth *synth);
+const char *tracefs_synth_show_start_hist(struct tracefs_synth *synth);
+const char *tracefs_synth_show_end_hist(struct tracefs_synth *synth);
+
+struct tracefs_synth *tracefs_sql(struct tep_handle *tep, const char *name,
+				  const char *sql_buffer, char **err);
+struct tep_event *
+tracefs_synth_get_event(struct tep_handle *tep, struct tracefs_synth *synth);
+
+struct tracefs_cpu;
+
+struct tracefs_cpu *tracefs_cpu_alloc_fd(int fd, int subbuf_size, bool nonblock);
+struct tracefs_cpu *tracefs_cpu_open(struct tracefs_instance *instance,
+				     int cpu, bool nonblock);
+void tracefs_cpu_close(struct tracefs_cpu *tcpu);
+void tracefs_cpu_free_fd(struct tracefs_cpu *tcpu);
+int tracefs_cpu_read_size(struct tracefs_cpu *tcpu);
+int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock);
+int tracefs_cpu_buffered_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock);
+int tracefs_cpu_write(struct tracefs_cpu *tcpu, int wfd, bool nonblock);
+int tracefs_cpu_stop(struct tracefs_cpu *tcpu);
+int tracefs_cpu_flush(struct tracefs_cpu *tcpu, void *buffer);
+int tracefs_cpu_flush_write(struct tracefs_cpu *tcpu, int wfd);
+int tracefs_cpu_pipe(struct tracefs_cpu *tcpu, int wfd, bool nonblock);
+
+#endif /* _TRACE_FS_H */
diff --git a/libtracefs.pc.template b/libtracefs.pc.template
new file mode 100644
index 0000000..09b335b
--- /dev/null
+++ b/libtracefs.pc.template
@@ -0,0 +1,11 @@
+prefix=INSTALL_PREFIX
+libdir=${prefix}/LIB_DIR
+includedir=${prefix}/HEADER_DIR
+
+Name: libtracefs
+URL: https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+Description: Library for accessing ftrace file system
+Version: LIB_VERSION
+Requires: libtraceevent > LIBTRACEEVENT_MIN
+Cflags: -I${includedir}
+Libs: -L${libdir} -ltracefs
diff --git a/samples/Makefile b/samples/Makefile
new file mode 100644
index 0000000..743bddb
--- /dev/null
+++ b/samples/Makefile
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: LGPL-2.1
+
+#
+# The samples are pulled out of the examples used in the man pages
+# that are located in the Documentation directory.
+#
+
+include $(src)/scripts/utils.mk
+
+EXAMPLES :=
+EXAMPLES += dynevents
+EXAMPLES += kprobes
+EXAMPLES += eprobes
+EXAMPLES += uprobes
+EXAMPLES += synth
+EXAMPLES += error
+EXAMPLES += filter
+EXAMPLES += function-filter
+EXAMPLES += hist
+EXAMPLES += hist-cont
+EXAMPLES += tracer
+EXAMPLES += stream
+EXAMPLES += instances-affinity
+EXAMPLES += cpu
+
+TARGETS :=
+TARGETS += sqlhist
+TARGETS += $(EXAMPLES)
+
+sdir := $(obj)/bin
+
+TARGETS := $(patsubst %,$(sdir)/%,$(TARGETS))
+
+all: $(TARGETS)
+
+$(bdir)/sqlhist.c: $(src)/Documentation/libtracefs-sql.txt
+	$(call extract_example,$<,$@)
+
+$(bdir)/%.c: ../Documentation/libtracefs-%.txt
+	$(call extract_example,$<,$@)
+
+$(sdir):
+	@mkdir -p $(sdir)
+
+sqlhist: $(sdir)/sqlhist
+
+$(TARGETS): $(sdir)
+
+# sqlhist is unique and stands on its own
+$(sdir)/sqlhist: $(bdir)/sqlhist.c $(LIBTRACEFS_STATIC)
+	$(call do_sample_build,$@,$<)
+
+$(sdir)/%: $(bdir)/%.o
+	$(call do_sample_build,$@,$<)
+
+$(EXAMPLES): $(patsubst %,$(sdir)/%,$(TARGETS))
+
+## The intermediate files get removed by Make.
+## To examine the .c files created by one of the man pages,
+## uncomment the below, and replace the XX with the exec example
+## name, and the file will not be discarded by make.
+#
+# $(bdir)/XX.o: $(bdir)/XX.c
+#	$(CC) -g -Wall $(CFLAGS) -c -o $@ $^ -I../include/ $(LIBTRACEEVENT_INCLUDES)
+
+$(bdir)/%.o: $(bdir)/%.c
+	$(call do_sample_obj,$@,$^)
+
+$(bdir)/XX.o: $(bdir)/hist.c
+	$(CC) -g -Wall $(CFLAGS) -c -o $@ $^ -I../include/ $(LIBTRACEEVENT_INCLUDES)
+
+clean:
+	$(Q)$(call do_clean,$(sdir)/* $(bdir)/sqlhist.c $(bdir)/sqlhist.o)
+
+.PHONY: sqlhist
diff --git a/scripts/features.mk b/scripts/features.mk
new file mode 100644
index 0000000..9c7f8c3
--- /dev/null
+++ b/scripts/features.mk
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# taken from perf which was based on Linux Kbuild
+# try-cc
+# Usage: option = $(call try-cc, source-to-build, cc-options)
+try-cc = $(shell sh -c							\
+	'TMP="$(BUILD_OUTPUT)$(TMPOUT).$$$$";						\
+	echo "$(1)" |							\
+	$(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y;	\
+	rm -f "$$TMP"')
+
+define SOURCE_PTRACE
+#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;
+}
+endef
diff --git a/scripts/utils.mk b/scripts/utils.mk
new file mode 100644
index 0000000..4d0f8bc
--- /dev/null
+++ b/scripts/utils.mk
@@ -0,0 +1,196 @@
+# SPDX-License-Identifier: LGPL-2.1
+
+# Utils
+
+ PWD		:= $(shell /bin/pwd)
+ GOBJ		= $(notdir $(strip $@))
+ BASE1		= $(notdir $(strip $1))
+ BASE2		= $(notdir $(strip $2))
+ BASEPWD	= $(notdir $(strip $(PWD)))
+
+
+ifeq ($(VERBOSE),1)
+  Q =
+  S =
+else
+  Q = @
+  S = -s
+endif
+
+# Use empty print_* macros if either SILENT or VERBOSE.
+ifeq ($(findstring 1,$(SILENT)$(VERBOSE)),1)
+  print_compile =
+  print_app_build =
+  print_fpic_compile =
+  print_shared_lib_compile =
+  print_plugin_obj_compile =
+  print_plugin_build =
+  print_install =
+  print_uninstall =
+  print_update =
+  print_descend =
+  print_clean =
+  print_extract =
+  print_sample_build =
+  print_sample_obj =
+else
+  print_compile =		echo '  COMPILE            '$(GOBJ);
+  print_app_build =		echo '  BUILD              '$(GOBJ);
+  print_fpic_compile =		echo '  COMPILE FPIC       '$(GOBJ);
+  print_shared_lib_compile =	echo '  COMPILE SHARED LIB '$(GOBJ);
+  print_plugin_obj_compile =	echo '  COMPILE PLUGIN OBJ '$(GOBJ);
+  print_plugin_build =		echo '  BUILD PLUGIN       '$(GOBJ);
+  print_static_lib_build =	echo '  BUILD STATIC LIB   '$(GOBJ);
+  print_install =		echo '  INSTALL     '$1'	to	$(DESTDIR_SQ)$2';
+  print_uninstall =		echo '  UNINSTALL     $(DESTDIR_SQ)$1';
+  print_update =		echo '  UPDATE             '$(GOBJ);
+  print_descend =		echo '  DESCEND            '$(BASE1) $(BASE2);
+  print_clean =			echo '  CLEAN              '$(BASEPWD);
+  print_extract =		echo '  EXTRACT            '$(GOBJ);
+  print_sample_build =		echo '  COMPILE SAMPLE     '$(GOBJ);
+  print_sample_obj =		echo '  COMPILE SAMPLE OBJ '$(GOBJ);
+endif
+
+do_fpic_compile =					\
+	($(print_fpic_compile)				\
+	$(CC) -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ -MP -c $(CPPFLAGS) $(CFLAGS) $(EXT) -fPIC $< -o $@)
+
+do_compile =							\
+	($(if $(GENERATE_PIC), $(do_fpic_compile),		\
+	 $(print_compile)					\
+	 $(CC) -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ -MP -c $(CPPFLAGS) $(CFLAGS) $(EXT) $< -o $@))
+
+do_app_build =						\
+	($(print_app_build)				\
+	$(CC) $^ -rdynamic -o $@ $(LDFLAGS) $(CONFIG_LIBS) $(LIBS))
+
+do_build_static_lib =				\
+	($(print_static_lib_build)		\
+	if [ -f $@ ]; then			\
+	    mv $@ $@.rm; $(RM) $@.rm;		\
+	fi;					\
+	$(AR) rcs $@ $^)
+
+do_compile_shared_library =			\
+	($(print_shared_lib_compile)		\
+	$(CC) --shared $^ '-Wl,-soname,$(1),-rpath=$$ORIGIN' -o $@ $(LDFLAGS) $(LIBS))
+
+do_compile_plugin_obj =				\
+	($(print_plugin_obj_compile)		\
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) -fPIC -o $@ $<)
+
+do_plugin_build =				\
+	($(print_plugin_build)			\
+	$(CC) $(CFLAGS) $(LDFLAGS) -shared -nostartfiles -o $@ $<)
+
+do_compile_python_plugin_obj =			\
+	($(print_plugin_obj_compile)		\
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $(PYTHON_DIR_SQ) $(PYTHON_INCLUDES) -fPIC -o $@ $<)
+
+do_python_plugin_build =			\
+	($(print_plugin_build)			\
+	$(CC) $< -shared $(LDFLAGS) $(PYTHON_LDFLAGS) -o $@)
+
+do_clean =					\
+	($(print_clean)				\
+	$(RM) $1)
+
+extract_example =				\
+	$(Q)($(print_extract)			\
+	cat $1 | sed -ne '/^EXAMPLE/,/FILES/ { /EXAMPLE/,+2d ; /^FILES/d ;  /^--/d ; p}' > $2)
+
+do_sample_build =							\
+	$(Q)($(print_sample_build)					\
+	$(CC) -o $1 $2 $(CFLAGS) $(LIBTRACEFS_STATIC) $(LIBTRACEEVENT_LIBS) -lpthread)
+
+do_sample_obj =									\
+	$(Q)($(print_sample_obj)						\
+	$(CC) -g -Wall -c $(CFLAGS) -o $1 $2 -I../include/ $(LIBTRACEEVENT_INCLUDES))
+
+ifneq ($(findstring $(MAKEFLAGS), w),w)
+PRINT_DIR = --no-print-directory
+else
+NO_SUBDIR = :
+endif
+
+#
+# Define a callable command for descending to a new directory
+#
+# Call by doing: $(call descend,directory[,target])
+#
+descend = \
+	($(print_descend)		\
+	mkdir -p $(obj)/$(BASE1); \
+	$(MAKE) $(PRINT_DIR) bdir=$(obj)/$(BASE1) -C $(1) $(2))
+
+descend_clean = \
+	$(MAKE) $(PRINT_DIR) bdir=$(obj)/$(BASE1) -C $(1) clean
+
+define make_version.h
+	(echo '/* This file is automatically generated. Do not modify. */';		\
+	echo \#define VERSION_CODE $(shell						\
+	expr $(VERSION) \* 256 + $(PATCHLEVEL));					\
+	echo '#define EXTRAVERSION ' $(EXTRAVERSION);					\
+	echo '#define VERSION_STRING "'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'"';	\
+	echo '#define FILE_VERSION '$(FILE_VERSION);					\
+	if [ -d $(src)/.git ]; then							\
+	  d=`git diff`;									\
+	  x="";										\
+	  if [ ! -z "$$d" ]; then x="+"; fi;						\
+	  echo '#define VERSION_GIT "'$(shell 						\
+		git log -1 --pretty=format:"%H" 2>/dev/null)$$x'"';			\
+	else										\
+	  echo '#define VERSION_GIT "not-a-git-repo"';					\
+	fi										\
+	) > $1
+endef
+
+define update_version.h
+	($(call make_version.h, $@.tmp);				\
+	if [ -r $@ ] && cmp -s $@ $@.tmp; then				\
+		rm -f $@.tmp;						\
+	else								\
+		$(print_update)						\
+		mv -f $@.tmp $@;					\
+	fi);
+endef
+
+define update_dir
+	(echo $1 > $@.tmp;	\
+	if [ -r $@ ] && cmp -s $@ $@.tmp; then				\
+		rm -f $@.tmp;						\
+	else								\
+		$(print_update)						\
+		mv -f $@.tmp $@;					\
+	fi);
+endef
+
+define build_prefix
+	(echo $1 > $@.tmp;	\
+	if [ -r $@ ] && cmp -s $@ $@.tmp; then				\
+		rm -f $@.tmp;						\
+	else								\
+		$(print_update)						\
+		mv -f $@.tmp $@;					\
+	fi);
+endef
+
+define do_install_mkdir
+	if [ ! -d '$(DESTDIR_SQ)$1' ]; then		\
+		$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$1';	\
+	fi
+endef
+
+define do_install
+	$(print_install)				\
+	$(call do_install_mkdir,$2);			\
+	$(INSTALL) $(if $3,-m $3,) $1 '$(DESTDIR_SQ)$2'
+endef
+
+define do_install_pkgconfig_file
+	if [ -n "${pkgconfig_dir}" ]; then 					\
+		$(call do_install,$(PKG_CONFIG_FILE),$(pkgconfig_dir),644); 	\
+	else 									\
+		(echo Failed to locate pkg-config directory) 1>&2;		\
+	fi
+endef
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..e2965bc
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: LGPL-2.1
+
+include $(src)/scripts/utils.mk
+
+OBJS =
+OBJS += tracefs-utils.o
+OBJS += tracefs-instance.o
+OBJS += tracefs-events.o
+OBJS += tracefs-tools.o
+OBJS += tracefs-marker.o
+OBJS += tracefs-kprobes.o
+OBJS += tracefs-hist.o
+OBJS += tracefs-filter.o
+OBJS += tracefs-dynevents.o
+OBJS += tracefs-eprobes.o
+OBJS += tracefs-uprobes.o
+OBJS += tracefs-record.o
+
+# Order matters for the the three below
+OBJS += sqlhist-lex.o
+OBJS += sqlhist.tab.o
+OBJS += tracefs-sqlhist.o
+
+OBJS := $(OBJS:%.o=$(bdir)/%.o)
+
+$(LIBTRACEFS_STATIC): $(OBJS)
+	$(Q)$(call do_build_static_lib)
+
+$(LIBTRACEFS_SHARED): $(OBJS)
+	$(Q)$(call do_compile_shared_library,$(notdir $(LIBTRACEFS_SHARED_VERSION)))
+
+$(LIBTRACEFS_SHARED_VERSION): $(LIBTRACEFS_SHARED)
+	@ln -sf $(<F) $@
+
+$(LIBTRACEFS_SHARED_SO): $(LIBTRACEFS_SHARED_VERSION)
+	@ln -sf $(<F) $@
+
+libtracefs.so: $(LIBTRACEFS_SHARED_SO)
+
+# bison will create both sqlhist.tab.c and sqlhist.tab.h
+sqlhist.tab.h:
+sqlhist.tab.c: sqlhist.y sqlhist.tab.h
+	bison --debug -v --report-file=bison.report -d -o $@ $<
+
+sqlhist-lex.c: sqlhist.l sqlhist.tab.c
+	flex -o $@ $<
+
+$(bdir)/%.o: %.c
+	$(Q)$(call do_fpic_compile)
+
+tracefs-sqlhist.o: sqlhist.tab.h
+
+$(OBJS): | $(bdir)
+
+clean:
+	$(Q)$(call do_clean,$(OBJS) .*.d)
+
+-include .*.d
+
+$(bdir)/tracefs-sqlhist.o tracefs-sqlhist.o: sqlhist.tab.h
+
+.PHONY: $(LIBTRACEFS_SHARED_SO) $(LIBTRACEFS_STATIC)
diff --git a/src/sqlhist-lex.c b/src/sqlhist-lex.c
new file mode 100644
index 0000000..5c75280
--- /dev/null
+++ b/src/sqlhist-lex.c
@@ -0,0 +1,2204 @@
+#line 1 "sqlhist-lex.c"
+
+#line 3 "sqlhist-lex.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 4
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+#ifdef yyget_lval
+#define yyget_lval_ALREADY_DEFINED
+#else
+#define yyget_lval yyget_lval
+#endif
+
+#ifdef yyset_lval
+#define yyset_lval_ALREADY_DEFINED
+#else
+#define yyset_lval yyset_lval
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX               (~(size_t)0)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+/* begin standard C++ headers. */
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an
+ *   integer in range [0..255] for use as an array index.
+ */
+#define YY_SC_TO_UI(c) ((YY_CHAR) (c))
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin , yyscanner )
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+    
+    #define YY_LESS_LINENO(n)
+    #define YY_LINENO_REWIND_TO(ptr)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = yyg->yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	int yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+                          ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+                          : NULL)
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void yyrestart ( FILE *input_file , yyscan_t yyscanner );
+void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
+void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+void yypop_buffer_state ( yyscan_t yyscanner );
+
+static void yyensure_buffer_stack ( yyscan_t yyscanner );
+static void yy_load_buffer_state ( yyscan_t yyscanner );
+static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner );
+#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner)
+
+YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
+
+void *yyalloc ( yy_size_t , yyscan_t yyscanner );
+void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
+void yyfree ( void * , yyscan_t yyscanner );
+
+#define yy_new_buffer yy_create_buffer
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+typedef flex_uint8_t YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state ( yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans ( yy_state_type current_state  , yyscan_t yyscanner);
+static int yy_get_next_buffer ( yyscan_t yyscanner );
+static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yyg->yytext_ptr = yy_bp; \
+	yyleng = (int) (yy_cp - yy_bp); \
+	yyg->yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yyg->yy_c_buf_p = yy_cp;
+#define YY_NUM_RULES 24
+#define YY_END_OF_BUFFER 25
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static const flex_int16_t yy_accept[72] =
+    {   0,
+        0,    0,   25,   23,   21,   22,   20,   23,   19,   20,
+       12,   12,   19,   20,   19,   10,   10,   10,   10,   10,
+       10,   10,   10,   10,   23,   23,   19,   13,    0,    9,
+       17,   12,    0,   14,   16,   15,   10,   10,    2,   10,
+       10,   10,    5,   10,   10,   10,   10,   18,   11,   10,
+       10,   10,   10,   10,   10,    7,    3,    4,   10,    0,
+       10,   10,    0,    6,    1,    0,    0,    0,    0,    8,
+        0
+    } ;
+
+static const YY_CHAR yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    4,    5,    1,    1,    1,    6,    1,    7,
+        7,    7,    7,    7,    8,    9,    7,   10,   11,   11,
+       11,   11,   11,   11,   11,   11,   11,    1,    1,   12,
+       13,   14,    1,    1,   15,   16,   17,   16,   18,   19,
+       20,   21,   22,   23,   20,   24,   25,   26,   27,   20,
+       20,   28,   29,   30,   20,   20,   31,   32,   33,   20,
+        1,   34,    1,    1,   20,    1,   35,   16,   36,   16,
+
+       37,   38,   20,   39,   40,   41,   20,   42,   43,   44,
+       45,   20,   20,   46,   47,   48,   20,   20,   49,   50,
+       51,   20,    1,   52,    1,   53,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static const YY_CHAR yy_meta[54] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        3,    1,    1,    1,    4,    4,    4,    4,    4,    5,
+        5,    5,    5,    5,    5,    5,    5,    5,    5,    5,
+        5,    6,    5,    1,    4,    4,    4,    4,    5,    5,
+        5,    5,    5,    5,    5,    5,    5,    5,    5,    6,
+        5,    1,    1
+    } ;
+
+static const flex_int16_t yy_base[77] =
+    {   0,
+        0,    0,   94,  180,  180,  180,   58,   62,   59,  180,
+       22,   23,   51,   50,   49,   64,  102,   41,   31,    0,
+       33,   40,   52,   47,    0,    9,  180,  180,   53,  180,
+      180,   37,    0,  180,  180,  180,    0,    0,    0,   59,
+       63,   69,    0,   68,   71,   79,    0,  180,    0,   74,
+       80,   81,   97,   49,   96,    0,    0,    0,  109,  101,
+      111,  101,  113,    0,    0,  114,  106,  118,  111,  180,
+      180,  159,  163,  168,  171,  175
+    } ;
+
+static const flex_int16_t yy_def[77] =
+    {   0,
+       71,    1,   71,   71,   71,   71,   71,   72,   71,   71,
+       73,   73,   71,   71,   71,   74,   74,   17,   17,   74,
+       74,   74,   74,   74,   75,   71,   71,   71,   72,   71,
+       71,   73,   76,   71,   71,   71,   74,   17,   74,   17,
+       74,   74,   74,   74,   74,   74,   74,   71,   76,   74,
+       74,   74,   74,   74,   74,   74,   74,   74,   74,   71,
+       74,   74,   71,   74,   74,   71,   71,   71,   71,   71,
+        0,   71,   71,   71,   71,   71
+    } ;
+
+static const flex_int16_t yy_nxt[234] =
+    {   0,
+        4,    5,    6,    7,    8,    9,   10,   10,    4,   11,
+       12,   13,   14,   15,   16,   17,   18,   17,   19,   20,
+       20,   20,   21,   20,   20,   20,   22,   20,   23,   20,
+       24,   20,   20,   25,   16,   18,   17,   19,   20,   20,
+       21,   20,   20,   20,   22,   20,   23,   20,   24,   20,
+       20,   26,   27,   33,   71,   40,   60,   30,   41,   42,
+       48,   36,   35,   34,   31,   43,   30,   46,   71,   44,
+       28,   33,   71,   38,   38,   40,   41,   42,   38,   38,
+       38,   38,   38,   43,   45,   46,   71,   50,   44,   51,
+       52,   53,   39,   71,   71,   54,   55,   71,   38,   38,
+
+       38,   38,   45,   56,   57,   50,   58,   51,   52,   53,
+       39,   38,   38,   54,   59,   55,   38,   38,   38,   38,
+       38,   56,   57,   61,   58,   62,   71,   63,   64,   71,
+       65,   66,   67,   59,   68,   69,   38,   38,   38,   38,
+       70,   61,   71,   71,   62,   63,   71,   64,   65,   71,
+       66,   67,   68,   71,   69,   71,   71,   71,   70,   29,
+       29,   29,   29,   29,   29,   32,   32,   71,   32,   37,
+       37,   37,   37,   37,   47,   47,   47,   49,   49,    3,
+       71,   71,   71,   71,   71,   71,   71,   71,   71,   71,
+       71,   71,   71,   71,   71,   71,   71,   71,   71,   71,
+
+       71,   71,   71,   71,   71,   71,   71,   71,   71,   71,
+       71,   71,   71,   71,   71,   71,   71,   71,   71,   71,
+       71,   71,   71,   71,   71,   71,   71,   71,   71,   71,
+       71,   71,   71
+    } ;
+
+static const flex_int16_t yy_chk[234] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,   11,   12,   18,   54,   29,   19,   21,
+       26,   15,   14,   13,    9,   22,    8,   24,   32,   23,
+        7,   11,   12,   16,   16,   18,   19,   21,   16,   16,
+       16,   16,   16,   22,   23,   24,   32,   40,   23,   41,
+       42,   44,   16,    3,    0,   45,   46,    0,   16,   16,
+
+       16,   16,   23,   50,   51,   40,   52,   41,   42,   44,
+       16,   17,   17,   45,   53,   46,   17,   17,   17,   17,
+       17,   50,   51,   55,   52,   59,    0,   60,   61,    0,
+       62,   63,   66,   53,   67,   68,   17,   17,   17,   17,
+       69,   55,    0,    0,   59,   60,    0,   61,   62,    0,
+       63,   66,   67,    0,   68,    0,    0,    0,   69,   72,
+       72,   72,   72,   72,   72,   73,   73,    0,   73,   74,
+       74,   74,   74,   74,   75,   75,   75,   76,   76,   71,
+       71,   71,   71,   71,   71,   71,   71,   71,   71,   71,
+       71,   71,   71,   71,   71,   71,   71,   71,   71,   71,
+
+       71,   71,   71,   71,   71,   71,   71,   71,   71,   71,
+       71,   71,   71,   71,   71,   71,   71,   71,   71,   71,
+       71,   71,   71,   71,   71,   71,   71,   71,   71,   71,
+       71,   71,   71
+    } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "sqlhist.l"
+#line 2 "sqlhist.l"
+/* code here */
+
+#include <stdarg.h>
+#include "sqlhist-parse.h"
+
+extern int my_yyinput(void *extra, char *buf, int max);
+
+#undef YY_INPUT
+#define YY_INPUT(b, r, m) ({r = my_yyinput(yyextra, b, m);})
+
+#define YY_NO_INPUT
+#define YY_NO_UNPUT
+
+#define YY_EXTRA_TYPE struct sqlhist_bison *
+
+#define yytext yyg->yytext_r
+
+#define TRACE_SB	((struct sqlhist_bison *)yyextra)
+#define HANDLE_COLUMN do { TRACE_SB->line_idx += strlen(yytext); } while (0)
+
+#line 527 "sqlhist-lex.c"
+#line 528 "sqlhist-lex.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+    {
+
+    /* User-defined. Not touched by flex. */
+    YY_EXTRA_TYPE yyextra_r;
+
+    /* The rest are the same as the globals declared in the non-reentrant scanner. */
+    FILE *yyin_r, *yyout_r;
+    size_t yy_buffer_stack_top; /**< index of top of stack. */
+    size_t yy_buffer_stack_max; /**< capacity of stack. */
+    YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+    char yy_hold_char;
+    int yy_n_chars;
+    int yyleng_r;
+    char *yy_c_buf_p;
+    int yy_init;
+    int yy_start;
+    int yy_did_buffer_switch_on_eof;
+    int yy_start_stack_ptr;
+    int yy_start_stack_depth;
+    int *yy_start_stack;
+    yy_state_type yy_last_accepting_state;
+    char* yy_last_accepting_cpos;
+
+    int yylineno_r;
+    int yy_flex_debug_r;
+
+    char *yytext_r;
+    int yy_more_flag;
+    int yy_more_len;
+
+    YYSTYPE * yylval_r;
+
+    }; /* end struct yyguts_t */
+
+static int yy_init_globals ( yyscan_t yyscanner );
+
+    /* This must go here because YYSTYPE and YYLTYPE are included
+     * from bison output in section 1.*/
+    #    define yylval yyg->yylval_r
+    
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy ( yyscan_t yyscanner );
+
+int yyget_debug ( yyscan_t yyscanner );
+
+void yyset_debug ( int debug_flag , yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner );
+
+void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
+
+FILE *yyget_in ( yyscan_t yyscanner );
+
+void yyset_in  ( FILE * _in_str , yyscan_t yyscanner );
+
+FILE *yyget_out ( yyscan_t yyscanner );
+
+void yyset_out  ( FILE * _out_str , yyscan_t yyscanner );
+
+			int yyget_leng ( yyscan_t yyscanner );
+
+char *yyget_text ( yyscan_t yyscanner );
+
+int yyget_lineno ( yyscan_t yyscanner );
+
+void yyset_lineno ( int _line_number , yyscan_t yyscanner );
+
+int yyget_column  ( yyscan_t yyscanner );
+
+void yyset_column ( int _column_no , yyscan_t yyscanner );
+
+YYSTYPE * yyget_lval ( yyscan_t yyscanner );
+
+void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap ( yyscan_t yyscanner );
+#else
+extern int yywrap ( yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+    
+    static void yyunput ( int c, char *buf_ptr  , yyscan_t yyscanner);
+    
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput ( yyscan_t yyscanner );
+#else
+static int input ( yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		int n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(yyin); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex \
+               (YYSTYPE * yylval_param , yyscan_t yyscanner);
+
+#define YY_DECL int yylex \
+               (YYSTYPE * yylval_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK /*LINTED*/break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	yy_state_type yy_current_state;
+	char *yy_cp, *yy_bp;
+	int yy_act;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+    yylval = yylval_param;
+
+	if ( !yyg->yy_init )
+		{
+		yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yyg->yy_start )
+			yyg->yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			yyensure_buffer_stack (yyscanner);
+			YY_CURRENT_BUFFER_LVALUE =
+				yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner);
+		}
+
+		yy_load_buffer_state( yyscanner );
+		}
+
+	{
+#line 33 "sqlhist.l"
+
+
+#line 805 "sqlhist-lex.c"
+
+	while ( /*CONSTCOND*/1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yyg->yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yyg->yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yyg->yy_start;
+yy_match:
+		do
+			{
+			YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+			if ( yy_accept[yy_current_state] )
+				{
+				yyg->yy_last_accepting_state = yy_current_state;
+				yyg->yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 72 )
+					yy_c = yy_meta[yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 180 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yyg->yy_hold_char;
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 35 "sqlhist.l"
+{ HANDLE_COLUMN; return SELECT; }
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 36 "sqlhist.l"
+{ HANDLE_COLUMN; return AS; }
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 37 "sqlhist.l"
+{ HANDLE_COLUMN; return FROM; }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 38 "sqlhist.l"
+{ HANDLE_COLUMN; return JOIN; }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 39 "sqlhist.l"
+{ HANDLE_COLUMN; return ON; }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 40 "sqlhist.l"
+{ HANDLE_COLUMN; return WHERE; }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 41 "sqlhist.l"
+{ HANDLE_COLUMN; return CAST; }
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 43 "sqlhist.l"
+{
+	HANDLE_COLUMN;
+	yylval->string = store_str(TRACE_SB, yyg->yytext_r);
+	return FIELD;
+}
+	YY_BREAK
+case 9:
+/* rule 9 can match eol */
+YY_RULE_SETUP
+#line 49 "sqlhist.l"
+{
+	HANDLE_COLUMN;
+	yylval->string = store_str(TRACE_SB, yyg->yytext_r);
+	return STRING;
+}
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 55 "sqlhist.l"
+{
+	const char *str = yyg->yytext_r;
+	HANDLE_COLUMN;
+	if (str[0] == '\\') { str++; };
+	yylval->string = store_str(TRACE_SB, str);
+	return FIELD;
+}
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 63 "sqlhist.l"
+{
+	HANDLE_COLUMN;
+	yylval->number = strtol(yyg->yytext_r, NULL, 0);
+	return NUMBER;
+}
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 69 "sqlhist.l"
+{
+	HANDLE_COLUMN;
+	yylval->number = strtol(yyg->yytext_r, NULL, 0);
+	return NUMBER;
+}
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 75 "sqlhist.l"
+{ HANDLE_COLUMN; return NEQ; }
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 76 "sqlhist.l"
+{ HANDLE_COLUMN; return LE; }
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 77 "sqlhist.l"
+{ HANDLE_COLUMN; return GE; }
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 78 "sqlhist.l"
+{ HANDLE_COLUMN; return EQ; }
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 79 "sqlhist.l"
+{ HANDLE_COLUMN; return AND; }
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 80 "sqlhist.l"
+{ HANDLE_COLUMN; return OR; }
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 81 "sqlhist.l"
+{ HANDLE_COLUMN; return yytext[0]; }
+	YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 83 "sqlhist.l"
+{ HANDLE_COLUMN; return yytext[0]; }
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 85 "sqlhist.l"
+{ HANDLE_COLUMN; }
+	YY_BREAK
+case 22:
+/* rule 22 can match eol */
+YY_RULE_SETUP
+#line 86 "sqlhist.l"
+{ TRACE_SB->line_idx = 0; TRACE_SB->line_no++; }
+	YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 88 "sqlhist.l"
+{ HANDLE_COLUMN; return PARSE_ERROR; }
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 89 "sqlhist.l"
+ECHO;
+	YY_BREAK
+#line 1006 "sqlhist-lex.c"
+case YY_STATE_EOF(INITIAL):
+	yyterminate();
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yyg->yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state( yyscanner );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+			yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yyg->yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yyg->yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer( yyscanner ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yyg->yy_did_buffer_switch_on_eof = 0;
+
+				if ( yywrap( yyscanner ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yyg->yy_c_buf_p =
+					yyg->yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yyg->yy_c_buf_p =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+	} /* end of user's declarations */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	char *source = yyg->yytext_ptr;
+	int number_to_move, i;
+	int ret_val;
+
+	if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1);
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+	else
+		{
+			int num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+			int yy_c_buf_p_offset =
+				(int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yyrealloc( (void *) b->yy_ch_buf,
+							 (yy_size_t) (b->yy_buf_size + 2) , yyscanner );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = NULL;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			yyg->yy_n_chars, num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	if ( yyg->yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart( yyin  , yyscanner);
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc(
+			(void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+		/* "- 2" to take care of EOB's */
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2);
+	}
+
+	yyg->yy_n_chars += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+	yy_state_type yy_current_state;
+	char *yy_cp;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	yy_current_state = yyg->yy_start;
+
+	for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+		{
+		YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			yyg->yy_last_accepting_state = yy_current_state;
+			yyg->yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 72 )
+				yy_c = yy_meta[yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+	int yy_is_jam;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+	char *yy_cp = yyg->yy_c_buf_p;
+
+	YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		yyg->yy_last_accepting_state = yy_current_state;
+		yyg->yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 72 )
+			yy_c = yy_meta[yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+	yy_is_jam = (yy_current_state == 71);
+
+	(void)yyg;
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_UNPUT
+
+    static void yyunput (int c, char * yy_bp , yyscan_t yyscanner)
+{
+	char *yy_cp;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+    yy_cp = yyg->yy_c_buf_p;
+
+	/* undo effects of setting up yytext */
+	*yy_cp = yyg->yy_hold_char;
+
+	if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		int number_to_move = yyg->yy_n_chars + 2;
+		char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+					YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+		char *source =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+		while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+			yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+		if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+	yyg->yytext_ptr = yy_bp;
+	yyg->yy_hold_char = *yy_cp;
+	yyg->yy_c_buf_p = yy_cp;
+}
+
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (yyscan_t yyscanner)
+#else
+    static int input  (yyscan_t yyscanner)
+#endif
+
+{
+	int c;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	*yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+	if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			/* This was really a NUL. */
+			*yyg->yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr);
+			++yyg->yy_c_buf_p;
+
+			switch ( yy_get_next_buffer( yyscanner ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart( yyin , yyscanner);
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap( yyscanner ) )
+						return 0;
+
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput(yyscanner);
+#else
+					return input(yyscanner);
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yyg->yy_c_buf_p;	/* cast for 8-bit char's */
+	*yyg->yy_c_buf_p = '\0';	/* preserve yytext */
+	yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void yyrestart  (FILE * input_file , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! YY_CURRENT_BUFFER ){
+        yyensure_buffer_stack (yyscanner);
+		YY_CURRENT_BUFFER_LVALUE =
+            yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner);
+	}
+
+	yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner);
+	yy_load_buffer_state( yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		yypop_buffer_state();
+	 *		yypush_buffer_state(new_buffer);
+     */
+	yyensure_buffer_stack (yyscanner);
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	yy_load_buffer_state( yyscanner );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void yy_load_buffer_state  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer( b, file , yyscanner);
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+    void yy_delete_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yyfree( (void *) b->yy_ch_buf , yyscanner );
+
+	yyfree( (void *) b , yyscanner );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file , yyscan_t yyscanner)
+
+{
+	int oerrno = errno;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	yy_flush_buffer( b , yyscanner);
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then yy_init_buffer was _probably_
+     * called from yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+    void yy_flush_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		yy_load_buffer_state( yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  @param yyscanner The scanner object.
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (new_buffer == NULL)
+		return;
+
+	yyensure_buffer_stack(yyscanner);
+
+	/* This block is copied from yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		yyg->yy_buffer_stack_top++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from yy_switch_to_buffer. */
+	yy_load_buffer_state( yyscanner );
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  @param yyscanner The scanner object.
+ */
+void yypop_buffer_state (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (!YY_CURRENT_BUFFER)
+		return;
+
+	yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner);
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if (yyg->yy_buffer_stack_top > 0)
+		--yyg->yy_buffer_stack_top;
+
+	if (YY_CURRENT_BUFFER) {
+		yy_load_buffer_state( yyscanner );
+		yyg->yy_did_buffer_switch_on_eof = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (yyscan_t yyscanner)
+{
+	yy_size_t num_to_alloc;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if (!yyg->yy_buffer_stack) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+      num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+		memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+		yyg->yy_buffer_stack_max = num_to_alloc;
+		yyg->yy_buffer_stack_top = 0;
+		return;
+	}
+
+	if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		yy_size_t grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc
+								(yyg->yy_buffer_stack,
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+		yyg->yy_buffer_stack_max = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return NULL;
+
+	b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+	b->yy_buf_size = (int) (size - 2);	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = NULL;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	yy_switch_to_buffer( b , yyscanner );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner)
+{
+    
+	return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes  (const char * yybytes, int  _yybytes_len , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = (yy_size_t) (_yybytes_len + 2);
+	buf = (char *) yyalloc( n , yyscanner );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = yy_scan_buffer( buf, n , yyscanner);
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+	fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = yyg->yy_hold_char; \
+		yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+		yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+		*yyg->yy_c_buf_p = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE yyget_extra  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_lineno  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_column  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_in  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_out  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int yyget_leng  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *yyget_text  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void yyset_extra (YY_EXTRA_TYPE  user_defined , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param _line_number line number
+ * @param yyscanner The scanner object.
+ */
+void yyset_lineno (int  _line_number , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* lineno is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           YY_FATAL_ERROR( "yyset_lineno called with no buffer" );
+    
+    yylineno = _line_number;
+}
+
+/** Set the current column.
+ * @param _column_no column number
+ * @param yyscanner The scanner object.
+ */
+void yyset_column (int  _column_no , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* column is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           YY_FATAL_ERROR( "yyset_column called with no buffer" );
+    
+    yycolumn = _column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param _in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE *  _in_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyin = _in_str ;
+}
+
+void yyset_out (FILE *  _out_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyout = _out_str ;
+}
+
+int yyget_debug  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yy_flex_debug;
+}
+
+void yyset_debug (int  _bdebug , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yy_flex_debug = _bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * yyget_lval  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yylval;
+}
+
+void yyset_lval (YYSTYPE *  yylval_param , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yylval = yylval_param;
+}
+
+/* User-visible API */
+
+/* yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+int yylex_init(yyscan_t* ptr_yy_globals)
+{
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+
+    *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+
+    /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* yylex_init_extra has the same functionality as yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to yyalloc in
+ * the yyextra field.
+ */
+int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals )
+{
+    struct yyguts_t dummy_yyguts;
+
+    yyset_extra (yy_user_defined, &dummy_yyguts);
+
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+
+    *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+
+    /* By setting to 0xAA, we expose bugs in
+    yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+    yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from yylex_destroy(), so don't allocate here.
+     */
+
+    yyg->yy_buffer_stack = NULL;
+    yyg->yy_buffer_stack_top = 0;
+    yyg->yy_buffer_stack_max = 0;
+    yyg->yy_c_buf_p = NULL;
+    yyg->yy_init = 0;
+    yyg->yy_start = 0;
+
+    yyg->yy_start_stack_ptr = 0;
+    yyg->yy_start_stack_depth = 0;
+    yyg->yy_start_stack =  NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = NULL;
+    yyout = NULL;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * yylex_init()
+     */
+    return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		yypop_buffer_state(yyscanner);
+	}
+
+	/* Destroy the stack itself. */
+	yyfree(yyg->yy_buffer_stack , yyscanner);
+	yyg->yy_buffer_stack = NULL;
+
+    /* Destroy the start condition stack. */
+        yyfree( yyg->yy_start_stack , yyscanner );
+        yyg->yy_start_stack = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * yylex() is called, initialization will occur. */
+    yy_init_globals( yyscanner);
+
+    /* Destroy the main struct (reentrant only). */
+    yyfree ( yyscanner , yyscanner );
+    yyscanner = NULL;
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+
+	int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (const char * s , yyscan_t yyscanner)
+{
+	int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *yyalloc (yy_size_t  size , yyscan_t yyscanner)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+	return malloc(size);
+}
+
+void *yyrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return realloc(ptr, size);
+}
+
+void yyfree (void * ptr , yyscan_t yyscanner)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+	free( (char *) ptr );	/* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 89 "sqlhist.l"
+
+
+int yywrap(void *data)
+{
+	return 1;
+}
+
+void yyerror(struct sqlhist_bison *sb, char *fmt, ...)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)sb->scanner;
+	va_list ap;
+
+	va_start(ap, fmt);
+	sql_parse_error(sb, yytext, fmt, ap);
+	va_end(ap);
+}
+
diff --git a/src/sqlhist-parse.h b/src/sqlhist-parse.h
new file mode 100644
index 0000000..7bd2a63
--- /dev/null
+++ b/src/sqlhist-parse.h
@@ -0,0 +1,77 @@
+#ifndef __SQLHIST_PARSE_H
+#define __SQLHIST_PARSE_H
+
+#include <stdarg.h>
+#include <tracefs.h>
+
+#include <tracefs-local.h>
+
+struct str_hash;
+
+struct sql_table;
+
+struct sqlhist_bison {
+	void			*scanner;
+	const char		*buffer;
+	size_t			buffer_size;
+	size_t			buffer_idx;
+	int			line_no;
+	int			line_idx;
+	struct sql_table	*table;
+	char			*parse_error_str;
+	struct str_hash         *str_hash[1 << HASH_BITS];
+};
+
+#include "sqlhist.tab.h"
+
+enum filter_type {
+	FILTER_GROUP,
+	FILTER_NOT_GROUP,
+	FILTER_EQ,
+	FILTER_NE,
+	FILTER_LE,
+	FILTER_LT,
+	FILTER_GE,
+	FILTER_GT,
+	FILTER_BIN_AND,
+	FILTER_STR_CMP,
+	FILTER_AND,
+	FILTER_OR,
+};
+
+enum compare_type {
+	COMPARE_GROUP,
+	COMPARE_ADD,
+	COMPARE_SUB,
+	COMPARE_MUL,
+	COMPARE_DIV,
+	COMPARE_BIN_AND,
+	COMPARE_BIN_OR,
+	COMPARE_AND,
+	COMPARE_OR,
+};
+
+char * store_str(struct sqlhist_bison *sb, const char *str);
+
+int table_start(struct sqlhist_bison *sb);
+
+void *add_field(struct sqlhist_bison *sb, const char *field, const char *label);
+
+void *add_filter(struct sqlhist_bison *sb, void *A, void *B, enum filter_type op);
+
+int add_match(struct sqlhist_bison *sb, void *A, void *B);
+void *add_compare(struct sqlhist_bison *sb, void *A, void *B, enum compare_type type);
+int add_where(struct sqlhist_bison *sb, void *expr);
+
+int add_selection(struct sqlhist_bison *sb, void *item, const char *label);
+int add_from(struct sqlhist_bison *sb, void *item);
+int add_to(struct sqlhist_bison *sb, void *item);
+void *add_cast(struct sqlhist_bison *sb, void *field, const char *type);
+
+void *add_string(struct sqlhist_bison *sb, const char *str);
+void *add_number(struct sqlhist_bison *sb, long val);
+
+extern void sql_parse_error(struct sqlhist_bison *sb, const char *text,
+			    const char *fmt, va_list ap);
+
+#endif
diff --git a/src/sqlhist.l b/src/sqlhist.l
new file mode 100644
index 0000000..4df475a
--- /dev/null
+++ b/src/sqlhist.l
@@ -0,0 +1,104 @@
+%{
+/* code here */
+
+#include <stdarg.h>
+#include "sqlhist-parse.h"
+
+extern int my_yyinput(void *extra, char *buf, int max);
+
+#undef YY_INPUT
+#define YY_INPUT(b, r, m) ({r = my_yyinput(yyextra, b, m);})
+
+#define YY_NO_INPUT
+#define YY_NO_UNPUT
+
+#define YY_EXTRA_TYPE struct sqlhist_bison *
+
+#define yytext yyg->yytext_r
+
+#define TRACE_SB	((struct sqlhist_bison *)yyextra)
+#define HANDLE_COLUMN do { TRACE_SB->line_idx += strlen(yytext); } while (0)
+
+%}
+
+%option caseless
+%option reentrant
+%option bison-bridge
+
+field		\\?[a-z_][a-z0-9_\.]*
+qstring		\"[^\"]*\"
+
+hexnum		0x[0-9a-f]+
+number		[0-9a-f]+
+%%
+
+select { HANDLE_COLUMN; return SELECT; }
+as { HANDLE_COLUMN; return AS; }
+from { HANDLE_COLUMN; return FROM; }
+join { HANDLE_COLUMN; return JOIN; }
+on { HANDLE_COLUMN; return ON; }
+where { HANDLE_COLUMN; return WHERE; }
+cast { HANDLE_COLUMN; return CAST; }
+
+sym-offset {
+	HANDLE_COLUMN;
+	yylval->string = store_str(TRACE_SB, yyg->yytext_r);
+	return FIELD;
+}
+
+{qstring} {
+	HANDLE_COLUMN;
+	yylval->string = store_str(TRACE_SB, yyg->yytext_r);
+	return STRING;
+}
+
+{field} {
+	const char *str = yyg->yytext_r;
+	HANDLE_COLUMN;
+	if (str[0] == '\\') { str++; };
+	yylval->string = store_str(TRACE_SB, str);
+	return FIELD;
+}
+
+{hexnum} {
+	HANDLE_COLUMN;
+	yylval->number = strtol(yyg->yytext_r, NULL, 0);
+	return NUMBER;
+}
+
+{number} {
+	HANDLE_COLUMN;
+	yylval->number = strtol(yyg->yytext_r, NULL, 0);
+	return NUMBER;
+}
+
+\!= { HANDLE_COLUMN; return NEQ; }
+\<= { HANDLE_COLUMN; return LE; }
+\>= { HANDLE_COLUMN; return GE; }
+== { HANDLE_COLUMN; return EQ; }
+&& { HANDLE_COLUMN; return AND; }
+"||" { HANDLE_COLUMN; return OR; }
+[<>&~] { HANDLE_COLUMN; return yytext[0]; }
+
+[\!()\-\+\*/,=] { HANDLE_COLUMN; return yytext[0]; }
+
+[ \t] { HANDLE_COLUMN; }
+\n { TRACE_SB->line_idx = 0; TRACE_SB->line_no++; }
+
+. { HANDLE_COLUMN; return PARSE_ERROR; }
+%%
+
+int yywrap(void *data)
+{
+	return 1;
+}
+
+void yyerror(struct sqlhist_bison *sb, char *fmt, ...)
+{
+	struct yyguts_t * yyg = (struct yyguts_t*)sb->scanner;
+	va_list ap;
+
+	va_start(ap, fmt);
+	sql_parse_error(sb, yytext, fmt, ap);
+	va_end(ap);
+}
diff --git a/src/sqlhist.tab.c b/src/sqlhist.tab.c
new file mode 100644
index 0000000..6393e95
--- /dev/null
+++ b/src/sqlhist.tab.c
@@ -0,0 +1,1755 @@
+/* A Bison parser, made by GNU Bison 3.6.4.  */
+
+/* Bison implementation for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation,
+   Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+   especially those whose name start with YY_ or yy_.  They are
+   private implementation details that can be changed or removed.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "3.6.4"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 1
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+/* Substitute the type names.  */
+#define YYSTYPE         TRACEFS_STYPE
+/* Substitute the variable and function names.  */
+#define yyparse         tracefs_parse
+#define yylex           tracefs_lex
+#define yyerror         tracefs_error
+#define yydebug         tracefs_debug
+#define yynerrs         tracefs_nerrs
+
+/* First part of user prologue.  */
+#line 1 "sqlhist.y"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "sqlhist-parse.h"
+
+#define scanner sb->scanner
+
+extern int yylex(YYSTYPE *yylval, void *);
+extern void yyerror(struct sqlhist_bison *, char *fmt, ...);
+
+#define CHECK_RETURN_PTR(x)					\
+	do {							\
+		if (!(x)) {					\
+			printf("FAILED MEMORY: %s\n", #x);	\
+			return -ENOMEM;				\
+		}						\
+	} while (0)
+
+#define CHECK_RETURN_VAL(x)					\
+	do {							\
+		if ((x) < 0) {					\
+			printf("FAILED MEMORY: %s\n", #x);	\
+			return -ENOMEM;				\
+		}						\
+	} while (0)
+
+
+#line 108 "sqlhist.tab.c"
+
+# ifndef YY_CAST
+#  ifdef __cplusplus
+#   define YY_CAST(Type, Val) static_cast<Type> (Val)
+#   define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val)
+#  else
+#   define YY_CAST(Type, Val) ((Type) (Val))
+#   define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val))
+#  endif
+# endif
+# ifndef YY_NULLPTR
+#  if defined __cplusplus
+#   if 201103L <= __cplusplus
+#    define YY_NULLPTR nullptr
+#   else
+#    define YY_NULLPTR 0
+#   endif
+#  else
+#   define YY_NULLPTR ((void*)0)
+#  endif
+# endif
+
+/* Use api.header.include to #include this header
+   instead of duplicating it here.  */
+#ifndef YY_TRACEFS_SQLHIST_TAB_H_INCLUDED
+# define YY_TRACEFS_SQLHIST_TAB_H_INCLUDED
+/* Debug traces.  */
+#ifndef TRACEFS_DEBUG
+# if defined YYDEBUG
+#if YYDEBUG
+#   define TRACEFS_DEBUG 1
+#  else
+#   define TRACEFS_DEBUG 0
+#  endif
+# else /* ! defined YYDEBUG */
+#  define TRACEFS_DEBUG 1
+# endif /* ! defined YYDEBUG */
+#endif  /* ! defined TRACEFS_DEBUG */
+#if TRACEFS_DEBUG
+extern int tracefs_debug;
+#endif
+
+/* Token kinds.  */
+#ifndef TRACEFS_TOKENTYPE
+# define TRACEFS_TOKENTYPE
+  enum tracefs_tokentype
+  {
+    TRACEFS_EMPTY = -2,
+    TRACEFS_EOF = 0,               /* "end of file"  */
+    TRACEFS_error = 256,           /* error  */
+    TRACEFS_UNDEF = 257,           /* "invalid token"  */
+    AS = 258,                      /* AS  */
+    SELECT = 259,                  /* SELECT  */
+    FROM = 260,                    /* FROM  */
+    JOIN = 261,                    /* JOIN  */
+    ON = 262,                      /* ON  */
+    WHERE = 263,                   /* WHERE  */
+    PARSE_ERROR = 264,             /* PARSE_ERROR  */
+    CAST = 265,                    /* CAST  */
+    NUMBER = 266,                  /* NUMBER  */
+    field_type = 267,              /* field_type  */
+    STRING = 268,                  /* STRING  */
+    FIELD = 269,                   /* FIELD  */
+    LE = 270,                      /* LE  */
+    GE = 271,                      /* GE  */
+    EQ = 272,                      /* EQ  */
+    NEQ = 273,                     /* NEQ  */
+    AND = 274,                     /* AND  */
+    OR = 275                       /* OR  */
+  };
+  typedef enum tracefs_tokentype tracefs_token_kind_t;
+#endif
+
+/* Value type.  */
+#if ! defined TRACEFS_STYPE && ! defined TRACEFS_STYPE_IS_DECLARED
+union TRACEFS_STYPE
+{
+#line 46 "sqlhist.y"
+
+	int	s32;
+	char	*string;
+	long	number;
+	void	*expr;
+
+#line 193 "sqlhist.tab.c"
+
+};
+typedef union TRACEFS_STYPE TRACEFS_STYPE;
+# define TRACEFS_STYPE_IS_TRIVIAL 1
+# define TRACEFS_STYPE_IS_DECLARED 1
+#endif
+
+
+
+int tracefs_parse (struct sqlhist_bison *sb);
+/* "%code provides" blocks.  */
+#line 37 "sqlhist.y"
+
+  #define YYSTYPE TRACEFS_STYPE
+  #define yylex tracefs_lex
+  #define yyerror tracefs_error
+
+#line 211 "sqlhist.tab.c"
+
+#endif /* !YY_TRACEFS_SQLHIST_TAB_H_INCLUDED  */
+/* Symbol kind.  */
+enum yysymbol_kind_t
+{
+  YYSYMBOL_YYEMPTY = -2,
+  YYSYMBOL_YYEOF = 0,                      /* "end of file"  */
+  YYSYMBOL_YYerror = 1,                    /* error  */
+  YYSYMBOL_YYUNDEF = 2,                    /* "invalid token"  */
+  YYSYMBOL_AS = 3,                         /* AS  */
+  YYSYMBOL_SELECT = 4,                     /* SELECT  */
+  YYSYMBOL_FROM = 5,                       /* FROM  */
+  YYSYMBOL_JOIN = 6,                       /* JOIN  */
+  YYSYMBOL_ON = 7,                         /* ON  */
+  YYSYMBOL_WHERE = 8,                      /* WHERE  */
+  YYSYMBOL_PARSE_ERROR = 9,                /* PARSE_ERROR  */
+  YYSYMBOL_CAST = 10,                      /* CAST  */
+  YYSYMBOL_NUMBER = 11,                    /* NUMBER  */
+  YYSYMBOL_field_type = 12,                /* field_type  */
+  YYSYMBOL_STRING = 13,                    /* STRING  */
+  YYSYMBOL_FIELD = 14,                     /* FIELD  */
+  YYSYMBOL_LE = 15,                        /* LE  */
+  YYSYMBOL_GE = 16,                        /* GE  */
+  YYSYMBOL_EQ = 17,                        /* EQ  */
+  YYSYMBOL_NEQ = 18,                       /* NEQ  */
+  YYSYMBOL_AND = 19,                       /* AND  */
+  YYSYMBOL_OR = 20,                        /* OR  */
+  YYSYMBOL_21_ = 21,                       /* '+'  */
+  YYSYMBOL_22_ = 22,                       /* '-'  */
+  YYSYMBOL_23_ = 23,                       /* '*'  */
+  YYSYMBOL_24_ = 24,                       /* '/'  */
+  YYSYMBOL_25_ = 25,                       /* '<'  */
+  YYSYMBOL_26_ = 26,                       /* '>'  */
+  YYSYMBOL_27_ = 27,                       /* ','  */
+  YYSYMBOL_28_ = 28,                       /* '('  */
+  YYSYMBOL_29_ = 29,                       /* ')'  */
+  YYSYMBOL_30_ = 30,                       /* '='  */
+  YYSYMBOL_31_ = 31,                       /* "!="  */
+  YYSYMBOL_32_ = 32,                       /* '&'  */
+  YYSYMBOL_33_ = 33,                       /* '~'  */
+  YYSYMBOL_34_ = 34,                       /* '!'  */
+  YYSYMBOL_YYACCEPT = 35,                  /* $accept  */
+  YYSYMBOL_start = 36,                     /* start  */
+  YYSYMBOL_label = 37,                     /* label  */
+  YYSYMBOL_select = 38,                    /* select  */
+  YYSYMBOL_select_statement = 39,          /* select_statement  */
+  YYSYMBOL_selection_list = 40,            /* selection_list  */
+  YYSYMBOL_selection = 41,                 /* selection  */
+  YYSYMBOL_selection_expr = 42,            /* selection_expr  */
+  YYSYMBOL_selection_addition = 43,        /* selection_addition  */
+  YYSYMBOL_item = 44,                      /* item  */
+  YYSYMBOL_field = 45,                     /* field  */
+  YYSYMBOL_named_field = 46,               /* named_field  */
+  YYSYMBOL_name = 47,                      /* name  */
+  YYSYMBOL_str_val = 48,                   /* str_val  */
+  YYSYMBOL_val = 49,                       /* val  */
+  YYSYMBOL_compare = 50,                   /* compare  */
+  YYSYMBOL_compare_and_or = 51,            /* compare_and_or  */
+  YYSYMBOL_compare_items = 52,             /* compare_items  */
+  YYSYMBOL_compare_cmds = 53,              /* compare_cmds  */
+  YYSYMBOL_compare_list = 54,              /* compare_list  */
+  YYSYMBOL_where_clause = 55,              /* where_clause  */
+  YYSYMBOL_opt_where_clause = 56,          /* opt_where_clause  */
+  YYSYMBOL_opt_join_clause = 57,           /* opt_join_clause  */
+  YYSYMBOL_table_exp = 58,                 /* table_exp  */
+  YYSYMBOL_from_clause = 59,               /* from_clause  */
+  YYSYMBOL_join_clause = 60,               /* join_clause  */
+  YYSYMBOL_match = 61,                     /* match  */
+  YYSYMBOL_match_clause = 62               /* match_clause  */
+};
+typedef enum yysymbol_kind_t yysymbol_kind_t;
+
+
+
+
+#ifdef short
+# undef short
+#endif
+
+/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure
+   <limits.h> and (if available) <stdint.h> are included
+   so that the code can choose integer types of a good width.  */
+
+#ifndef __PTRDIFF_MAX__
+# include <limits.h> /* INFRINGES ON USER NAME SPACE */
+# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
+#  include <stdint.h> /* INFRINGES ON USER NAME SPACE */
+#  define YY_STDINT_H
+# endif
+#endif
+
+/* Narrow types that promote to a signed type and that can represent a
+   signed or unsigned integer of at least N bits.  In tables they can
+   save space and decrease cache pressure.  Promoting to a signed type
+   helps avoid bugs in integer arithmetic.  */
+
+#ifdef __INT_LEAST8_MAX__
+typedef __INT_LEAST8_TYPE__ yytype_int8;
+#elif defined YY_STDINT_H
+typedef int_least8_t yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef __INT_LEAST16_MAX__
+typedef __INT_LEAST16_TYPE__ yytype_int16;
+#elif defined YY_STDINT_H
+typedef int_least16_t yytype_int16;
+#else
+typedef short yytype_int16;
+#endif
+
+#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST8_TYPE__ yytype_uint8;
+#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \
+       && UINT_LEAST8_MAX <= INT_MAX)
+typedef uint_least8_t yytype_uint8;
+#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX
+typedef unsigned char yytype_uint8;
+#else
+typedef short yytype_uint8;
+#endif
+
+#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST16_TYPE__ yytype_uint16;
+#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \
+       && UINT_LEAST16_MAX <= INT_MAX)
+typedef uint_least16_t yytype_uint16;
+#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX
+typedef unsigned short yytype_uint16;
+#else
+typedef int yytype_uint16;
+#endif
+
+#ifndef YYPTRDIFF_T
+# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__
+#  define YYPTRDIFF_T __PTRDIFF_TYPE__
+#  define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__
+# elif defined PTRDIFF_MAX
+#  ifndef ptrdiff_t
+#   include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  endif
+#  define YYPTRDIFF_T ptrdiff_t
+#  define YYPTRDIFF_MAXIMUM PTRDIFF_MAX
+# else
+#  define YYPTRDIFF_T long
+#  define YYPTRDIFF_MAXIMUM LONG_MAX
+# endif
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM                                  \
+  YY_CAST (YYPTRDIFF_T,                                 \
+           (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1)  \
+            ? YYPTRDIFF_MAXIMUM                         \
+            : YY_CAST (YYSIZE_T, -1)))
+
+#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X))
+
+
+/* Stored state numbers (used for stacks). */
+typedef yytype_int8 yy_state_t;
+
+/* State numbers in computations.  */
+typedef int yy_state_fast_t;
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(Msgid) Msgid
+# endif
+#endif
+
+
+#ifndef YY_ATTRIBUTE_PURE
+# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_PURE __attribute__ ((__pure__))
+# else
+#  define YY_ATTRIBUTE_PURE
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+# else
+#  define YY_ATTRIBUTE_UNUSED
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized.  */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN                            \
+    _Pragma ("GCC diagnostic push")                                     \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")              \
+    _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END      \
+    _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__
+# define YY_IGNORE_USELESS_CAST_BEGIN                          \
+    _Pragma ("GCC diagnostic push")                            \
+    _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"")
+# define YY_IGNORE_USELESS_CAST_END            \
+    _Pragma ("GCC diagnostic pop")
+#endif
+#ifndef YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_END
+#endif
+
+
+#define YY_ASSERT(E) ((void) (0 && (E)))
+
+#if !defined yyoverflow
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+      /* Use EXIT_SUCCESS as a witness for stdlib.h.  */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's 'empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+             && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* !defined yyoverflow */
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+         || (defined TRACEFS_STYPE_IS_TRIVIAL && TRACEFS_STYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yy_state_t yyss_alloc;
+  YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)                           \
+    do                                                                  \
+      {                                                                 \
+        YYPTRDIFF_T yynewbytes;                                         \
+        YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
+        Stack = &yyptr->Stack_alloc;                                    \
+        yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \
+        yyptr += yynewbytes / YYSIZEOF (*yyptr);                        \
+      }                                                                 \
+    while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(Dst, Src, Count) \
+      __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src)))
+#  else
+#   define YYCOPY(Dst, Src, Count)              \
+      do                                        \
+        {                                       \
+          YYPTRDIFF_T yyi;                      \
+          for (yyi = 0; yyi < (Count); yyi++)   \
+            (Dst)[yyi] = (Src)[yyi];            \
+        }                                       \
+      while (0)
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  5
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   104
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  35
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  28
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  61
+/* YYNSTATES -- Number of states.  */
+#define YYNSTATES  111
+
+#define YYMAXUTOK   276
+
+
+/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, with out-of-bounds checking.  */
+#define YYTRANSLATE(YYX)                                \
+  (0 <= (YYX) && (YYX) <= YYMAXUTOK                     \
+   ? YY_CAST (yysymbol_kind_t, yytranslate[YYX])        \
+   : YYSYMBOL_YYUNDEF)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex.  */
+static const yytype_int8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,    34,     2,     2,     2,     2,    32,     2,
+      28,    29,    23,    21,    27,    22,     2,    24,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+      25,    30,    26,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,    33,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    31
+};
+
+#if TRACEFS_DEBUG
+  /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
+static const yytype_uint8 yyrline[] =
+{
+       0,    75,    75,    78,    79,    82,    86,    90,    91,    95,
+      99,   106,   107,   108,   109,   110,   117,   122,   130,   131,
+     135,   139,   143,   147,   151,   152,   157,   158,   159,   160,
+     161,   162,   163,   164,   165,   166,   170,   171,   172,   173,
+     174,   178,   179,   180,   181,   182,   186,   195,   196,   197,
+     201,   204,   206,   209,   211,   215,   219,   234,   238,   239,
+     244,   245
+};
+#endif
+
+/** Accessing symbol of state STATE.  */
+#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State])
+
+#if TRACEFS_DEBUG || 0
+/* The user-facing name of the symbol whose (internal) number is
+   YYSYMBOL.  No bounds checking.  */
+static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED;
+
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "\"end of file\"", "error", "\"invalid token\"", "AS", "SELECT", "FROM",
+  "JOIN", "ON", "WHERE", "PARSE_ERROR", "CAST", "NUMBER", "field_type",
+  "STRING", "FIELD", "LE", "GE", "EQ", "NEQ", "AND", "OR", "'+'", "'-'",
+  "'*'", "'/'", "'<'", "'>'", "','", "'('", "')'", "'='", "\"!=\"", "'&'",
+  "'~'", "'!'", "$accept", "start", "label", "select", "select_statement",
+  "selection_list", "selection", "selection_expr", "selection_addition",
+  "item", "field", "named_field", "name", "str_val", "val", "compare",
+  "compare_and_or", "compare_items", "compare_cmds", "compare_list",
+  "where_clause", "opt_where_clause", "opt_join_clause", "table_exp",
+  "from_clause", "join_clause", "match", "match_clause", YY_NULLPTR
+};
+
+static const char *
+yysymbol_name (yysymbol_kind_t yysymbol)
+{
+  return yytname[yysymbol];
+}
+#endif
+
+#ifdef YYPRINT
+/* YYTOKNUM[NUM] -- (External) token number corresponding to the
+   (internal) symbol number NUM (which must be that of a token).  */
+static const yytype_int16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,    43,    45,    42,    47,    60,    62,    44,    40,    41,
+      61,   276,    38,   126,    33
+};
+#endif
+
+#define YYPACT_NINF (-58)
+
+#define yypact_value_is_default(Yyn) \
+  ((Yyn) == YYPACT_NINF)
+
+#define YYTABLE_NINF (-1)
+
+#define yytable_value_is_error(Yyn) \
+  0
+
+  /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+     STATE-NUM.  */
+static const yytype_int8 yypact[] =
+{
+       4,   -58,    13,    11,   -58,   -58,     5,   -58,    43,    53,
+      45,    29,   -58,    19,    43,    44,    32,    60,   -58,    73,
+      11,    75,   -58,   -58,   -58,    43,    43,    87,   -58,   -58,
+      29,   -58,   -58,   -58,    60,    83,   -58,   -58,   -58,   -58,
+     -58,    78,   -58,    86,    14,   -58,   -58,    65,    60,    -4,
+     -12,    34,   -58,    76,   -15,   -58,   -58,   -10,    68,   -58,
+       1,   -58,    18,    -4,   -58,    33,    33,    33,    33,    33,
+      33,    33,    33,    33,    84,    14,    14,    14,    60,    60,
+      60,    -4,   -58,    -4,    -4,   -58,    49,   -58,   -58,   -58,
+     -58,   -58,   -58,   -58,   -58,   -58,   -58,   -58,   -58,   -58,
+     -58,   -58,   -58,   -58,   -58,   -58,    51,   -58,   -58,   -58,
+     -58
+};
+
+  /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+     Performed when YYTABLE does not specify something else to do.  Zero
+     means the default is an error.  */
+static const yytype_int8 yydefact[] =
+{
+       0,     5,     0,     0,     2,     1,     0,    20,     0,     0,
+       7,     9,    13,    11,     0,     0,     0,     0,     6,    53,
+       0,     0,    22,    10,     4,     0,     0,     0,    14,    12,
+      20,    56,    19,    18,     0,    51,    54,     8,     3,    16,
+      17,     0,    21,     0,     0,    52,    55,     0,     0,     0,
+       0,     0,    45,    46,    47,    50,    15,     0,    60,    57,
+       0,    40,     0,     0,    44,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,    39,     0,     0,    42,     0,    25,    23,    24,
+      28,    29,    31,    32,    26,    27,    30,    33,    34,    35,
+      41,    49,    48,    59,    58,    61,     0,    37,    36,    43,
+      38
+};
+
+  /* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+     -58,   -58,    69,   -58,   -58,    80,   -58,   -58,    90,   -16,
+      -3,   -58,    81,    27,    15,   -41,   -57,    28,   -58,   -21,
+     -58,   -58,   -58,   -58,   -58,   -58,   -58,    24
+};
+
+  /* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+      -1,     2,    23,     3,     4,     9,    10,    11,    12,    57,
+      51,    33,    24,    89,    90,    61,    62,    53,    54,    55,
+      45,    46,    35,    18,    19,    36,    58,    59
+};
+
+  /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
+     positive, shift that token.  If negative, reduce the rule whose
+     number is the opposite.  If YYTABLE_NINF, syntax error.  */
+static const yytype_int8 yytable[] =
+{
+      13,    31,     7,    52,    76,    16,    86,    78,     1,    64,
+       7,    27,    77,     5,    32,     7,    63,    13,    43,    82,
+      79,     6,    39,    40,   106,     7,   107,   108,     7,    81,
+      60,    32,    21,    14,    52,    52,    52,    83,    84,     8,
+      25,    26,    49,    22,    87,    32,    88,    85,    50,    65,
+      66,    67,    68,    25,    26,   101,   102,     7,    17,    69,
+      70,    29,   103,   104,    71,    72,    73,    74,    83,    84,
+      83,    84,    20,    28,    30,    32,    32,    32,   109,    34,
+     110,    91,    92,    93,    94,    95,    96,    97,    98,    22,
+      41,    44,    47,    48,    56,    80,    75,    88,    15,    42,
+      37,    99,    38,   100,   105
+};
+
+static const yytype_int8 yycheck[] =
+{
+       3,    17,    14,    44,    19,     8,    63,    17,     4,    50,
+      14,    14,    27,     0,    17,    14,    28,    20,    34,    60,
+      30,    10,    25,    26,    81,    14,    83,    84,    14,    28,
+      34,    34,     3,    28,    75,    76,    77,    19,    20,    28,
+      21,    22,    28,    14,    11,    48,    13,    29,    34,    15,
+      16,    17,    18,    21,    22,    76,    77,    14,     5,    25,
+      26,    29,    78,    79,    30,    31,    32,    33,    19,    20,
+      19,    20,    27,    29,    14,    78,    79,    80,    29,     6,
+      29,    66,    67,    68,    69,    70,    71,    72,    73,    14,
+       3,     8,    14,     7,    29,    27,    20,    13,     8,    30,
+      20,    74,    21,    75,    80
+};
+
+  /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+     symbol of state STATE-NUM.  */
+static const yytype_int8 yystos[] =
+{
+       0,     4,    36,    38,    39,     0,    10,    14,    28,    40,
+      41,    42,    43,    45,    28,    43,    45,     5,    58,    59,
+      27,     3,    14,    37,    47,    21,    22,    45,    29,    29,
+      14,    44,    45,    46,     6,    57,    60,    40,    47,    45,
+      45,     3,    37,    44,     8,    55,    56,    14,     7,    28,
+      34,    45,    50,    52,    53,    54,    29,    44,    61,    62,
+      34,    50,    51,    28,    50,    15,    16,    17,    18,    25,
+      26,    30,    31,    32,    33,    20,    19,    27,    17,    30,
+      27,    28,    50,    19,    20,    29,    51,    11,    13,    48,
+      49,    49,    49,    49,    49,    49,    49,    49,    49,    48,
+      52,    54,    54,    44,    44,    62,    51,    51,    51,    29,
+      29
+};
+
+  /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_int8 yyr1[] =
+{
+       0,    35,    36,    37,    37,    38,    39,    40,    40,    41,
+      41,    42,    42,    42,    42,    42,    43,    43,    44,    44,
+      45,    46,    47,    48,    49,    49,    50,    50,    50,    50,
+      50,    50,    50,    50,    50,    50,    51,    51,    51,    51,
+      51,    52,    52,    52,    52,    52,    53,    54,    54,    54,
+      55,    56,    56,    57,    57,    58,    59,    60,    61,    61,
+      62,    62
+};
+
+  /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN.  */
+static const yytype_int8 yyr2[] =
+{
+       0,     2,     1,     2,     1,     1,     3,     1,     3,     1,
+       2,     1,     3,     1,     3,     6,     3,     3,     1,     1,
+       1,     2,     1,     1,     1,     1,     3,     3,     3,     3,
+       3,     3,     3,     3,     3,     3,     3,     3,     4,     2,
+       1,     3,     3,     4,     2,     1,     1,     1,     3,     3,
+       2,     0,     1,     0,     1,     3,     2,     4,     3,     3,
+       1,     3
+};
+
+
+enum { YYENOMEM = -2 };
+
+#define yyerrok         (yyerrstatus = 0)
+#define yyclearin       (yychar = TRACEFS_EMPTY)
+
+#define YYACCEPT        goto yyacceptlab
+#define YYABORT         goto yyabortlab
+#define YYERROR         goto yyerrorlab
+
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                    \
+  do                                                              \
+    if (yychar == TRACEFS_EMPTY)                                        \
+      {                                                           \
+        yychar = (Token);                                         \
+        yylval = (Value);                                         \
+        YYPOPSTACK (yylen);                                       \
+        yystate = *yyssp;                                         \
+        goto yybackup;                                            \
+      }                                                           \
+    else                                                          \
+      {                                                           \
+        yyerror (sb, YY_("syntax error: cannot back up")); \
+        YYERROR;                                                  \
+      }                                                           \
+  while (0)
+
+/* Backward compatibility with an undocumented macro.
+   Use TRACEFS_error or TRACEFS_UNDEF. */
+#define YYERRCODE TRACEFS_UNDEF
+
+
+/* Enable debugging if requested.  */
+#if TRACEFS_DEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                        \
+do {                                            \
+  if (yydebug)                                  \
+    YYFPRINTF Args;                             \
+} while (0)
+
+/* This macro is provided for backward compatibility. */
+# ifndef YY_LOCATION_PRINT
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+
+
+# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)                    \
+do {                                                                      \
+  if (yydebug)                                                            \
+    {                                                                     \
+      YYFPRINTF (stderr, "%s ", Title);                                   \
+      yy_symbol_print (stderr,                                            \
+                  Kind, Value, sb); \
+      YYFPRINTF (stderr, "\n");                                           \
+    }                                                                     \
+} while (0)
+
+
+/*-----------------------------------.
+| Print this symbol's value on YYO.  |
+`-----------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyo,
+                       yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, struct sqlhist_bison *sb)
+{
+  FILE *yyoutput = yyo;
+  YYUSE (yyoutput);
+  YYUSE (sb);
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yykind < YYNTOKENS)
+    YYPRINT (yyo, yytoknum[yykind], *yyvaluep);
+# endif
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  YYUSE (yykind);
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+/*---------------------------.
+| Print this symbol on YYO.  |
+`---------------------------*/
+
+static void
+yy_symbol_print (FILE *yyo,
+                 yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, struct sqlhist_bison *sb)
+{
+  YYFPRINTF (yyo, "%s %s (",
+             yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind));
+
+  yy_symbol_value_print (yyo, yykind, yyvaluep, sb);
+  YYFPRINTF (yyo, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop)
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                            \
+do {                                                            \
+  if (yydebug)                                                  \
+    yy_stack_print ((Bottom), (Top));                           \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp,
+                 int yyrule, struct sqlhist_bison *sb)
+{
+  int yylno = yyrline[yyrule];
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n",
+             yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr,
+                       YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]),
+                       &yyvsp[(yyi + 1) - (yynrhs)], sb);
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)          \
+do {                                    \
+  if (yydebug)                          \
+    yy_reduce_print (yyssp, yyvsp, Rule, sb); \
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !TRACEFS_DEBUG */
+# define YYDPRINTF(Args) ((void) 0)
+# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !TRACEFS_DEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg,
+            yysymbol_kind_t yykind, YYSTYPE *yyvaluep, struct sqlhist_bison *sb)
+{
+  YYUSE (yyvaluep);
+  YYUSE (sb);
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp);
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  YYUSE (yykind);
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+int
+yyparse (struct sqlhist_bison *sb)
+{
+/* The lookahead symbol.  */
+int yychar;
+
+
+/* The semantic value of the lookahead symbol.  */
+/* Default value used for initialization, for pacifying older GCCs
+   or non-GCC compilers.  */
+YY_INITIAL_VALUE (static YYSTYPE yyval_default;)
+YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default);
+
+    /* Number of syntax errors so far.  */
+    int yynerrs;
+
+    yy_state_fast_t yystate;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus;
+
+    /* The stacks and their tools:
+       'yyss': related to states.
+       'yyvs': related to semantic values.
+
+       Refer to the stacks through separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* Their size.  */
+    YYPTRDIFF_T yystacksize;
+
+    /* The state stack.  */
+    yy_state_t yyssa[YYINITDEPTH];
+    yy_state_t *yyss;
+    yy_state_t *yyssp;
+
+    /* The semantic value stack.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs;
+    YYSTYPE *yyvsp;
+
+  int yyn;
+  /* The return value of yyparse.  */
+  int yyresult;
+  /* Lookahead token as an internal (translated) token number.  */
+  yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  yynerrs = 0;
+  yystate = 0;
+  yyerrstatus = 0;
+
+  yystacksize = YYINITDEPTH;
+  yyssp = yyss = yyssa;
+  yyvsp = yyvs = yyvsa;
+
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yychar = TRACEFS_EMPTY; /* Cause a token to be read.  */
+  goto yysetstate;
+
+
+/*------------------------------------------------------------.
+| yynewstate -- push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+
+/*--------------------------------------------------------------------.
+| yysetstate -- set current state (the top of the stack) to yystate.  |
+`--------------------------------------------------------------------*/
+yysetstate:
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+  YY_ASSERT (0 <= yystate && yystate < YYNSTATES);
+  YY_IGNORE_USELESS_CAST_BEGIN
+  *yyssp = YY_CAST (yy_state_t, yystate);
+  YY_IGNORE_USELESS_CAST_END
+  YY_STACK_PRINT (yyss, yyssp);
+
+  if (yyss + yystacksize - 1 <= yyssp)
+#if !defined yyoverflow && !defined YYSTACK_RELOCATE
+    goto yyexhaustedlab;
+#else
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYPTRDIFF_T yysize = yyssp - yyss + 1;
+
+# if defined yyoverflow
+      {
+        /* Give user a chance to reallocate the stack.  Use copies of
+           these so that the &'s don't force the real ones into
+           memory.  */
+        yy_state_t *yyss1 = yyss;
+        YYSTYPE *yyvs1 = yyvs;
+
+        /* Each stack pointer address is followed by the size of the
+           data in use in that stack, in bytes.  This used to be a
+           conditional around just the two extra args, but that might
+           be undefined if yyoverflow is a macro.  */
+        yyoverflow (YY_("memory exhausted"),
+                    &yyss1, yysize * YYSIZEOF (*yyssp),
+                    &yyvs1, yysize * YYSIZEOF (*yyvsp),
+                    &yystacksize);
+        yyss = yyss1;
+        yyvs = yyvs1;
+      }
+# else /* defined YYSTACK_RELOCATE */
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+        goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+        yystacksize = YYMAXDEPTH;
+
+      {
+        yy_state_t *yyss1 = yyss;
+        union yyalloc *yyptr =
+          YY_CAST (union yyalloc *,
+                   YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize))));
+        if (! yyptr)
+          goto yyexhaustedlab;
+        YYSTACK_RELOCATE (yyss_alloc, yyss);
+        YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+#  undef YYSTACK_RELOCATE
+        if (yyss1 != yyssa)
+          YYSTACK_FREE (yyss1);
+      }
+# endif
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+      YY_IGNORE_USELESS_CAST_BEGIN
+      YYDPRINTF ((stderr, "Stack size increased to %ld\n",
+                  YY_CAST (long, yystacksize)));
+      YY_IGNORE_USELESS_CAST_END
+
+      if (yyss + yystacksize - 1 <= yyssp)
+        YYABORT;
+    }
+#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either empty, or end-of-input, or a valid lookahead.  */
+  if (yychar == TRACEFS_EMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token\n"));
+      yychar = yylex (&yylval, scanner);
+    }
+
+  if (yychar <= TRACEFS_EOF)
+    {
+      yychar = TRACEFS_EOF;
+      yytoken = YYSYMBOL_YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else if (yychar == TRACEFS_error)
+    {
+      /* The scanner already issued an error message, process directly
+         to error recovery.  But do not keep the error token as
+         lookahead, it is too special and may lead us to an endless
+         loop in error recovery. */
+      yychar = TRACEFS_UNDEF;
+      yytoken = YYSYMBOL_YYerror;
+      goto yyerrlab1;
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+  yystate = yyn;
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+  /* Discard the shifted token.  */
+  yychar = TRACEFS_EMPTY;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     '$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+  case 3:
+#line 78 "sqlhist.y"
+                { CHECK_RETURN_PTR((yyval.string) = store_str(sb, (yyvsp[0].string))); }
+#line 1328 "sqlhist.tab.c"
+    break;
+
+  case 4:
+#line 79 "sqlhist.y"
+        { CHECK_RETURN_PTR((yyval.string) = store_str(sb, (yyvsp[0].string))); }
+#line 1334 "sqlhist.tab.c"
+    break;
+
+  case 5:
+#line 82 "sqlhist.y"
+                 { table_start(sb); }
+#line 1340 "sqlhist.tab.c"
+    break;
+
+  case 9:
+#line 96 "sqlhist.y"
+                                {
+					CHECK_RETURN_VAL(add_selection(sb, (yyvsp[0].expr), NULL));
+				}
+#line 1348 "sqlhist.tab.c"
+    break;
+
+  case 10:
+#line 100 "sqlhist.y"
+                                {
+					CHECK_RETURN_VAL(add_selection(sb, (yyvsp[-1].expr), (yyvsp[0].string)));
+				}
+#line 1356 "sqlhist.tab.c"
+    break;
+
+  case 12:
+#line 107 "sqlhist.y"
+                                {  (yyval.expr) = (yyvsp[-1].expr); }
+#line 1362 "sqlhist.tab.c"
+    break;
+
+  case 14:
+#line 109 "sqlhist.y"
+                                {  (yyval.expr) = (yyvsp[-1].expr); }
+#line 1368 "sqlhist.tab.c"
+    break;
+
+  case 15:
+#line 110 "sqlhist.y"
+                                {
+					 (yyval.expr) = add_cast(sb, (yyvsp[-3].expr), (yyvsp[-1].string));
+					 CHECK_RETURN_PTR((yyval.expr));
+				}
+#line 1377 "sqlhist.tab.c"
+    break;
+
+  case 16:
+#line 118 "sqlhist.y"
+                                {
+					(yyval.expr) = add_compare(sb, (yyvsp[-2].expr), (yyvsp[0].expr), COMPARE_ADD);
+					CHECK_RETURN_PTR((yyval.expr));
+				}
+#line 1386 "sqlhist.tab.c"
+    break;
+
+  case 17:
+#line 123 "sqlhist.y"
+                                {
+					(yyval.expr) = add_compare(sb, (yyvsp[-2].expr), (yyvsp[0].expr), COMPARE_SUB);
+					CHECK_RETURN_PTR((yyval.expr));
+				}
+#line 1395 "sqlhist.tab.c"
+    break;
+
+  case 20:
+#line 135 "sqlhist.y"
+                { (yyval.expr) = add_field(sb, (yyvsp[0].string), NULL); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1401 "sqlhist.tab.c"
+    break;
+
+  case 21:
+#line 139 "sqlhist.y"
+               { (yyval.expr) = add_field(sb, (yyvsp[-1].string), (yyvsp[0].string)); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1407 "sqlhist.tab.c"
+    break;
+
+  case 23:
+#line 147 "sqlhist.y"
+                { (yyval.expr) = add_string(sb, (yyvsp[0].string)); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1413 "sqlhist.tab.c"
+    break;
+
+  case 25:
+#line 152 "sqlhist.y"
+                { (yyval.expr) = add_number(sb, (yyvsp[0].number)); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1419 "sqlhist.tab.c"
+    break;
+
+  case 26:
+#line 157 "sqlhist.y"
+                        { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_LT); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1425 "sqlhist.tab.c"
+    break;
+
+  case 27:
+#line 158 "sqlhist.y"
+                        { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_GT); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1431 "sqlhist.tab.c"
+    break;
+
+  case 28:
+#line 159 "sqlhist.y"
+                { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_LE); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1437 "sqlhist.tab.c"
+    break;
+
+  case 29:
+#line 160 "sqlhist.y"
+                { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_GE); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1443 "sqlhist.tab.c"
+    break;
+
+  case 30:
+#line 161 "sqlhist.y"
+                        { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_EQ); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1449 "sqlhist.tab.c"
+    break;
+
+  case 31:
+#line 162 "sqlhist.y"
+                { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_EQ); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1455 "sqlhist.tab.c"
+    break;
+
+  case 32:
+#line 163 "sqlhist.y"
+                        { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_NE); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1461 "sqlhist.tab.c"
+    break;
+
+  case 33:
+#line 164 "sqlhist.y"
+                        { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_NE); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1467 "sqlhist.tab.c"
+    break;
+
+  case 34:
+#line 165 "sqlhist.y"
+                        { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_BIN_AND); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1473 "sqlhist.tab.c"
+    break;
+
+  case 35:
+#line 166 "sqlhist.y"
+                        { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_STR_CMP); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1479 "sqlhist.tab.c"
+    break;
+
+  case 36:
+#line 170 "sqlhist.y"
+                                        { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_OR); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1485 "sqlhist.tab.c"
+    break;
+
+  case 37:
+#line 171 "sqlhist.y"
+                                        { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_AND); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1491 "sqlhist.tab.c"
+    break;
+
+  case 38:
+#line 172 "sqlhist.y"
+                                        { (yyval.expr) = add_filter(sb, (yyvsp[-1].expr), NULL, FILTER_NOT_GROUP); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1497 "sqlhist.tab.c"
+    break;
+
+  case 39:
+#line 173 "sqlhist.y"
+                                        { (yyval.expr) = add_filter(sb, (yyvsp[0].expr), NULL, FILTER_NOT_GROUP); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1503 "sqlhist.tab.c"
+    break;
+
+  case 41:
+#line 178 "sqlhist.y"
+                                        { (yyval.expr) = add_filter(sb, (yyvsp[-2].expr), (yyvsp[0].expr), FILTER_OR); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1509 "sqlhist.tab.c"
+    break;
+
+  case 42:
+#line 179 "sqlhist.y"
+                                        { (yyval.expr) = add_filter(sb, (yyvsp[-1].expr), NULL, FILTER_GROUP); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1515 "sqlhist.tab.c"
+    break;
+
+  case 43:
+#line 180 "sqlhist.y"
+                                        { (yyval.expr) = add_filter(sb, (yyvsp[-1].expr), NULL, FILTER_NOT_GROUP); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1521 "sqlhist.tab.c"
+    break;
+
+  case 44:
+#line 181 "sqlhist.y"
+                                        { (yyval.expr) = add_filter(sb, (yyvsp[0].expr), NULL, FILTER_NOT_GROUP); CHECK_RETURN_PTR((yyval.expr)); }
+#line 1527 "sqlhist.tab.c"
+    break;
+
+  case 46:
+#line 186 "sqlhist.y"
+                                { CHECK_RETURN_VAL(add_where(sb, (yyvsp[0].expr))); }
+#line 1533 "sqlhist.tab.c"
+    break;
+
+  case 56:
+#line 219 "sqlhist.y"
+                        { CHECK_RETURN_VAL(add_from(sb, (yyvsp[0].expr))); }
+#line 1539 "sqlhist.tab.c"
+    break;
+
+  case 57:
+#line 234 "sqlhist.y"
+                                { add_to(sb, (yyvsp[-2].expr)); }
+#line 1545 "sqlhist.tab.c"
+    break;
+
+  case 58:
+#line 238 "sqlhist.y"
+                 { CHECK_RETURN_VAL(add_match(sb, (yyvsp[-2].expr), (yyvsp[0].expr))); }
+#line 1551 "sqlhist.tab.c"
+    break;
+
+  case 59:
+#line 239 "sqlhist.y"
+                { CHECK_RETURN_VAL(add_match(sb, (yyvsp[-2].expr), (yyvsp[0].expr))); }
+#line 1557 "sqlhist.tab.c"
+    break;
+
+
+#line 1561 "sqlhist.tab.c"
+
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+
+  *++yyvsp = yyval;
+
+  /* Now 'shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+  {
+    const int yylhs = yyr1[yyn] - YYNTOKENS;
+    const int yyi = yypgoto[yylhs] + *yyssp;
+    yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp
+               ? yytable[yyi]
+               : yydefgoto[yylhs]);
+  }
+
+  goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error.  |
+`--------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == TRACEFS_EMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar);
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+      yyerror (sb, YY_("syntax error"));
+    }
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+         error, discard it.  */
+
+      if (yychar <= TRACEFS_EOF)
+        {
+          /* Return failure if at end of input.  */
+          if (yychar == TRACEFS_EOF)
+            YYABORT;
+        }
+      else
+        {
+          yydestruct ("Error: discarding",
+                      yytoken, &yylval, sb);
+          yychar = TRACEFS_EMPTY;
+        }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+  /* Pacify compilers when the user code never invokes YYERROR and the
+     label yyerrorlab therefore never appears in user code.  */
+  if (0)
+    YYERROR;
+
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;      /* Each real token shifted decrements this.  */
+
+  /* Pop stack until we find a state that shifts the error token.  */
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+        {
+          yyn += YYSYMBOL_YYerror;
+          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror)
+            {
+              yyn = yytable[yyn];
+              if (0 < yyn)
+                break;
+            }
+        }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+        YYABORT;
+
+
+      yydestruct ("Error: popping",
+                  YY_ACCESSING_SYMBOL (yystate), yyvsp, sb);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+
+#if !defined yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (sb, YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+
+/*-----------------------------------------------------.
+| yyreturn -- parsing is finished, return the result.  |
+`-----------------------------------------------------*/
+yyreturn:
+  if (yychar != TRACEFS_EMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval, sb);
+    }
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                  YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, sb);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+
+  return yyresult;
+}
+
+#line 248 "sqlhist.y"
+
diff --git a/src/sqlhist.tab.h b/src/sqlhist.tab.h
new file mode 100644
index 0000000..b02a782
--- /dev/null
+++ b/src/sqlhist.tab.h
@@ -0,0 +1,118 @@
+/* A Bison parser, made by GNU Bison 3.6.4.  */
+
+/* Bison interface for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation,
+   Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+   especially those whose name start with YY_ or yy_.  They are
+   private implementation details that can be changed or removed.  */
+
+#ifndef YY_TRACEFS_SQLHIST_TAB_H_INCLUDED
+# define YY_TRACEFS_SQLHIST_TAB_H_INCLUDED
+/* Debug traces.  */
+#ifndef TRACEFS_DEBUG
+# if defined YYDEBUG
+#if YYDEBUG
+#   define TRACEFS_DEBUG 1
+#  else
+#   define TRACEFS_DEBUG 0
+#  endif
+# else /* ! defined YYDEBUG */
+#  define TRACEFS_DEBUG 1
+# endif /* ! defined YYDEBUG */
+#endif  /* ! defined TRACEFS_DEBUG */
+#if TRACEFS_DEBUG
+extern int tracefs_debug;
+#endif
+
+/* Token kinds.  */
+#ifndef TRACEFS_TOKENTYPE
+# define TRACEFS_TOKENTYPE
+  enum tracefs_tokentype
+  {
+    TRACEFS_EMPTY = -2,
+    TRACEFS_EOF = 0,               /* "end of file"  */
+    TRACEFS_error = 256,           /* error  */
+    TRACEFS_UNDEF = 257,           /* "invalid token"  */
+    AS = 258,                      /* AS  */
+    SELECT = 259,                  /* SELECT  */
+    FROM = 260,                    /* FROM  */
+    JOIN = 261,                    /* JOIN  */
+    ON = 262,                      /* ON  */
+    WHERE = 263,                   /* WHERE  */
+    PARSE_ERROR = 264,             /* PARSE_ERROR  */
+    CAST = 265,                    /* CAST  */
+    NUMBER = 266,                  /* NUMBER  */
+    field_type = 267,              /* field_type  */
+    STRING = 268,                  /* STRING  */
+    FIELD = 269,                   /* FIELD  */
+    LE = 270,                      /* LE  */
+    GE = 271,                      /* GE  */
+    EQ = 272,                      /* EQ  */
+    NEQ = 273,                     /* NEQ  */
+    AND = 274,                     /* AND  */
+    OR = 275                       /* OR  */
+  };
+  typedef enum tracefs_tokentype tracefs_token_kind_t;
+#endif
+
+/* Value type.  */
+#if ! defined TRACEFS_STYPE && ! defined TRACEFS_STYPE_IS_DECLARED
+union TRACEFS_STYPE
+{
+#line 46 "sqlhist.y"
+
+	int	s32;
+	char	*string;
+	long	number;
+	void	*expr;
+
+#line 99 "sqlhist.tab.h"
+
+};
+typedef union TRACEFS_STYPE TRACEFS_STYPE;
+# define TRACEFS_STYPE_IS_TRIVIAL 1
+# define TRACEFS_STYPE_IS_DECLARED 1
+#endif
+
+
+
+int tracefs_parse (struct sqlhist_bison *sb);
+/* "%code provides" blocks.  */
+#line 37 "sqlhist.y"
+
+  #define YYSTYPE TRACEFS_STYPE
+  #define yylex tracefs_lex
+  #define yyerror tracefs_error
+
+#line 117 "sqlhist.tab.h"
+
+#endif /* !YY_TRACEFS_SQLHIST_TAB_H_INCLUDED  */
diff --git a/src/sqlhist.y b/src/sqlhist.y
new file mode 100644
index 0000000..fade9a4
--- /dev/null
+++ b/src/sqlhist.y
@@ -0,0 +1,248 @@
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "sqlhist-parse.h"
+
+#define scanner sb->scanner
+
+extern int yylex(YYSTYPE *yylval, void *);
+extern void yyerror(struct sqlhist_bison *, char *fmt, ...);
+
+#define CHECK_RETURN_PTR(x)					\
+	do {							\
+		if (!(x)) {					\
+			printf("FAILED MEMORY: %s\n", #x);	\
+			return -ENOMEM;				\
+		}						\
+	} while (0)
+
+#define CHECK_RETURN_VAL(x)					\
+	do {							\
+		if ((x) < 0) {					\
+			printf("FAILED MEMORY: %s\n", #x);	\
+			return -ENOMEM;				\
+		}						\
+	} while (0)
+
+%}
+
+%define api.pure
+
+/* Change the globals to use tracefs_ prefix */
+%define api.prefix{tracefs_}
+%code provides
+{
+  #define YYSTYPE TRACEFS_STYPE
+  #define yylex tracefs_lex
+  #define yyerror tracefs_error
+}
+
+%lex-param {void *scanner}
+%parse-param {struct sqlhist_bison *sb}
+
+%union {
+	int	s32;
+	char	*string;
+	long	number;
+	void	*expr;
+}
+
+%token AS SELECT FROM JOIN ON WHERE PARSE_ERROR CAST
+%token <number> NUMBER field_type
+%token <string> STRING
+%token <string> FIELD
+%token <string> LE GE EQ NEQ AND OR
+
+%left '+' '-'
+%left '*' '/'
+%left '<' '>'
+%left AND OR
+
+%type <string> name label
+
+%type <expr>  selection_expr field item named_field
+%type <expr>  selection_addition
+%type <expr>  compare compare_list compare_cmds compare_items
+%type <expr>  compare_and_or
+%type <expr>  str_val val
+
+%%
+
+start :
+   select_statement
+ ;
+
+label : AS name { CHECK_RETURN_PTR($$ = store_str(sb, $2)); }
+ | name { CHECK_RETURN_PTR($$ = store_str(sb, $1)); }
+ ;
+
+select : SELECT  { table_start(sb); }
+  ;
+
+select_statement :
+    select selection_list table_exp
+  ;
+
+selection_list :
+   selection
+ | selection ',' selection_list
+ ;
+
+selection :
+    selection_expr
+				{
+					CHECK_RETURN_VAL(add_selection(sb, $1, NULL));
+				}
+  | selection_expr label
+				{
+					CHECK_RETURN_VAL(add_selection(sb, $1, $2));
+				}
+  ;
+
+selection_expr :
+   field
+ | '(' field ')'		{  $$ = $2; }
+ | selection_addition
+ | '(' selection_addition ')'	{  $$ = $2; }
+ | CAST '(' field AS FIELD ')'	{
+					 $$ = add_cast(sb, $3, $5);
+					 CHECK_RETURN_PTR($$);
+				}
+ ;
+
+selection_addition :
+   field '+' field
+				{
+					$$ = add_compare(sb, $1, $3, COMPARE_ADD);
+					CHECK_RETURN_PTR($$);
+				}
+ | field '-' field
+				{
+					$$ = add_compare(sb, $1, $3, COMPARE_SUB);
+					CHECK_RETURN_PTR($$);
+				}
+ ;
+
+item :
+   named_field
+ | field
+ ;
+
+field :
+   FIELD	{ $$ = add_field(sb, $1, NULL); CHECK_RETURN_PTR($$); }
+ ;
+
+named_field :
+   FIELD label { $$ = add_field(sb, $1, $2); CHECK_RETURN_PTR($$); }
+ ;
+
+name :
+   FIELD
+ ;
+
+str_val :
+   STRING	{ $$ = add_string(sb, $1); CHECK_RETURN_PTR($$); }
+ ;
+
+val :
+   str_val
+ | NUMBER	{ $$ = add_number(sb, $1); CHECK_RETURN_PTR($$); }
+ ;
+
+
+compare :
+   field '<' val	{ $$ = add_filter(sb, $1, $3, FILTER_LT); CHECK_RETURN_PTR($$); }
+ | field '>' val	{ $$ = add_filter(sb, $1, $3, FILTER_GT); CHECK_RETURN_PTR($$); }
+ | field LE val	{ $$ = add_filter(sb, $1, $3, FILTER_LE); CHECK_RETURN_PTR($$); }
+ | field GE val	{ $$ = add_filter(sb, $1, $3, FILTER_GE); CHECK_RETURN_PTR($$); }
+ | field '=' val	{ $$ = add_filter(sb, $1, $3, FILTER_EQ); CHECK_RETURN_PTR($$); }
+ | field EQ val	{ $$ = add_filter(sb, $1, $3, FILTER_EQ); CHECK_RETURN_PTR($$); }
+ | field NEQ val	{ $$ = add_filter(sb, $1, $3, FILTER_NE); CHECK_RETURN_PTR($$); }
+ | field "!=" val	{ $$ = add_filter(sb, $1, $3, FILTER_NE); CHECK_RETURN_PTR($$); }
+ | field '&' val	{ $$ = add_filter(sb, $1, $3, FILTER_BIN_AND); CHECK_RETURN_PTR($$); }
+ | field '~' str_val	{ $$ = add_filter(sb, $1, $3, FILTER_STR_CMP); CHECK_RETURN_PTR($$); }
+;
+
+compare_and_or :
+   compare_and_or OR compare_and_or	{ $$ = add_filter(sb, $1, $3, FILTER_OR); CHECK_RETURN_PTR($$); }
+ | compare_and_or AND compare_and_or	{ $$ = add_filter(sb, $1, $3, FILTER_AND); CHECK_RETURN_PTR($$); }
+ | '!' '(' compare_and_or ')'		{ $$ = add_filter(sb, $3, NULL, FILTER_NOT_GROUP); CHECK_RETURN_PTR($$); }
+ | '!' compare				{ $$ = add_filter(sb, $2, NULL, FILTER_NOT_GROUP); CHECK_RETURN_PTR($$); }
+ | compare
+ ;
+
+compare_items :
+   compare_items OR compare_items	{ $$ = add_filter(sb, $1, $3, FILTER_OR); CHECK_RETURN_PTR($$); }
+ | '(' compare_and_or ')'		{ $$ = add_filter(sb, $2, NULL, FILTER_GROUP); CHECK_RETURN_PTR($$); }
+ | '!' '(' compare_and_or ')'		{ $$ = add_filter(sb, $3, NULL, FILTER_NOT_GROUP); CHECK_RETURN_PTR($$); }
+ | '!' compare				{ $$ = add_filter(sb, $2, NULL, FILTER_NOT_GROUP); CHECK_RETURN_PTR($$); }
+ | compare
+ ;
+
+compare_cmds :
+   compare_items		{ CHECK_RETURN_VAL(add_where(sb, $1)); }
+ ;
+
+/*
+ * Top level AND is equal to ',' but the compare_cmds in them must
+ * all be of for the same event (start or end exclusive).
+ * That is, OR is not to be used between start and end events.
+ */
+compare_list :
+   compare_cmds
+ | compare_cmds ',' compare_list
+ | compare_cmds AND compare_list
+ ;
+
+where_clause :
+   WHERE compare_list
+ ;
+
+opt_where_clause :
+   /* empty */
+ | where_clause
+;
+
+opt_join_clause :
+  /* empty set */
+  | join_clause
+ ;
+
+table_exp :
+   from_clause opt_join_clause opt_where_clause
+ ;
+
+from_clause :
+   FROM item		{ CHECK_RETURN_VAL(add_from(sb, $2)); }
+
+/*
+ * Select from a from clause confuses the variable parsing.
+ * disable it for now.
+
+   | FROM '(' select_statement ')' label
+				{
+					from_table_end($5);
+					$$ = store_printf("FROM (%s) AS %s", $3, $5);
+				}
+*/
+ ;
+
+join_clause :
+ JOIN item ON match_clause	{ add_to(sb, $2); }
+ ;
+
+match :
+   item '=' item { CHECK_RETURN_VAL(add_match(sb, $1, $3)); }
+ | item EQ item { CHECK_RETURN_VAL(add_match(sb, $1, $3)); }
+
+ ;
+
+match_clause :
+   match
+ | match ',' match_clause
+ ;
+
+%%
diff --git a/src/tracefs-dynevents.c b/src/tracefs-dynevents.c
new file mode 100644
index 0000000..7a3c45c
--- /dev/null
+++ b/src/tracefs-dynevents.c
@@ -0,0 +1,779 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
+ *
+ * Updates:
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+#define DYNEVENTS_EVENTS "dynamic_events"
+#define KPROBE_EVENTS "kprobe_events"
+#define UPROBE_EVENTS "uprobe_events"
+#define SYNTH_EVENTS "synthetic_events"
+#define DYNEVENTS_DEFAULT_GROUP "dynamic"
+
+#define EVENT_INDEX(B)	(ffs(B) - 1)
+
+struct dyn_events_desc;
+static int dyn_generic_parse(struct dyn_events_desc *,
+			     const char *, char *, struct tracefs_dynevent **);
+static int dyn_synth_parse(struct dyn_events_desc *,
+			   const char *, char *, struct tracefs_dynevent **);
+static int dyn_generic_del(struct dyn_events_desc *, struct tracefs_dynevent *);
+static int dyn_synth_del(struct dyn_events_desc *, struct tracefs_dynevent *);
+
+struct dyn_events_desc {
+	enum tracefs_dynevent_type type;
+	const char *file;
+	const char *prefix;
+	int (*del)(struct dyn_events_desc *desc, struct tracefs_dynevent *dyn);
+	int (*parse)(struct dyn_events_desc *desc, const char *group,
+				char *line, struct tracefs_dynevent **ret_dyn);
+} dynevents[] = {
+	{TRACEFS_DYNEVENT_KPROBE, KPROBE_EVENTS, "p", dyn_generic_del, dyn_generic_parse},
+	{TRACEFS_DYNEVENT_KRETPROBE, KPROBE_EVENTS, "r", dyn_generic_del, dyn_generic_parse},
+	{TRACEFS_DYNEVENT_UPROBE, UPROBE_EVENTS, "p", dyn_generic_del, dyn_generic_parse},
+	{TRACEFS_DYNEVENT_URETPROBE, UPROBE_EVENTS, "r", dyn_generic_del, dyn_generic_parse},
+	{TRACEFS_DYNEVENT_EPROBE, "", "e", dyn_generic_del, dyn_generic_parse},
+	{TRACEFS_DYNEVENT_SYNTH, SYNTH_EVENTS, "", dyn_synth_del, dyn_synth_parse},
+};
+
+
+
+static int dyn_generic_del(struct dyn_events_desc *desc, struct tracefs_dynevent *dyn)
+{
+	char *str;
+	int ret;
+
+	if (dyn->system)
+		ret = asprintf(&str, "-:%s/%s", dyn->system, dyn->event);
+	else
+		ret = asprintf(&str, "-:%s", dyn->event);
+
+	if (ret < 0)
+		return -1;
+
+	ret = tracefs_instance_file_append(NULL, desc->file, str);
+	free(str);
+
+	return ret < 0 ? ret : 0;
+}
+
+/**
+ * tracefs_dynevent_free - Free a dynamic event context
+ * @devent: Pointer to a dynamic event context
+ *
+ * The dynamic event, described by this context, is not
+ * removed from the system by this API. It only frees the memory.
+ */
+void tracefs_dynevent_free(struct tracefs_dynevent *devent)
+{
+	if (!devent)
+		return;
+	free(devent->system);
+	free(devent->event);
+	free(devent->address);
+	free(devent->format);
+	free(devent->prefix);
+	free(devent->trace_file);
+	free(devent);
+}
+
+static void parse_prefix(char *word, char **prefix, char **system, char **name)
+{
+	char *sav;
+
+	*prefix = NULL;
+	*system = NULL;
+	*name = NULL;
+
+	*prefix = strtok_r(word, ":", &sav);
+	*system = strtok_r(NULL, "/", &sav);
+	if (!(*system))
+		return;
+
+	*name = strtok_r(NULL, " \t", &sav);
+	if (!(*name)) {
+		*name = *system;
+		*system = NULL;
+	}
+}
+
+/*
+ * Parse lines from dynamic_events, kprobe_events and uprobe_events files
+ * PREFIX[:[SYSTEM/]EVENT] [ADDRSS] [FORMAT]
+ */
+static int dyn_generic_parse(struct dyn_events_desc *desc, const char *group,
+			     char *line, struct tracefs_dynevent **ret_dyn)
+{
+	struct tracefs_dynevent *dyn;
+	char *word;
+	char *format = NULL;
+	char *address = NULL;
+	char *system;
+	char *prefix;
+	char *event;
+	char *sav;
+
+	if (strncmp(line, desc->prefix, strlen(desc->prefix)))
+		return -1;
+
+	word = strtok_r(line, " \t", &sav);
+	if (!word || *word == '\0')
+		return -1;
+
+	parse_prefix(word, &prefix, &system, &event);
+	if (!prefix)
+		return -1;
+
+	if (desc->type != TRACEFS_DYNEVENT_SYNTH) {
+		address = strtok_r(NULL, " \t", &sav);
+		if (!address || *address == '\0')
+			return -1;
+	}
+
+	format = strtok_r(NULL, "", &sav);
+
+	/* KPROBEs and UPROBEs share the same prefix, check the format */
+	if (desc->type & (TRACEFS_DYNEVENT_UPROBE | TRACEFS_DYNEVENT_URETPROBE)) {
+		if (!strchr(address, '/'))
+			return -1;
+	}
+
+	if (group && (!system || strcmp(group, system) != 0))
+		return -1;
+
+	if (!ret_dyn)
+		return 0;
+
+	dyn = calloc(1, sizeof(*dyn));
+	if (!dyn)
+		return -1;
+
+	dyn->type = desc->type;
+	dyn->trace_file = strdup(desc->file);
+	if (!dyn->trace_file)
+		goto error;
+
+	dyn->prefix = strdup(prefix);
+	if (!dyn->prefix)
+		goto error;
+
+	if (system) {
+		dyn->system = strdup(system);
+		if (!dyn->system)
+			goto error;
+	}
+
+	if (event) {
+		dyn->event = strdup(event);
+		if (!dyn->event)
+			goto error;
+	}
+
+	if (address) {
+		dyn->address = strdup(address);
+		if (!dyn->address)
+			goto error;
+	}
+
+	if (format) {
+		dyn->format = strdup(format);
+		if (!dyn->format)
+			goto error;
+	}
+
+	*ret_dyn = dyn;
+	return 0;
+error:
+	tracefs_dynevent_free(dyn);
+	return -1;
+}
+
+static int dyn_synth_del(struct dyn_events_desc *desc, struct tracefs_dynevent *dyn)
+{
+	char *str;
+	int ret;
+
+	if (!strcmp(desc->file, DYNEVENTS_EVENTS))
+		return dyn_generic_del(desc, dyn);
+
+	ret = asprintf(&str, "!%s", dyn->event);
+	if (ret < 0)
+		return -1;
+
+	ret = tracefs_instance_file_append(NULL, desc->file, str);
+	free(str);
+
+	return ret < 0 ? ret : 0;
+}
+
+/*
+ * Parse lines from synthetic_events file
+ * EVENT ARG [ARG]
+ */
+static int dyn_synth_parse(struct dyn_events_desc *desc, const char *group,
+			   char *line, struct tracefs_dynevent **ret_dyn)
+{
+	struct tracefs_dynevent *dyn;
+	char *format;
+	char *event;
+	char *sav;
+
+	if (!strcmp(desc->file, DYNEVENTS_EVENTS))
+		return dyn_generic_parse(desc, group, line, ret_dyn);
+
+	/* synthetic_events file has slightly different syntax */
+	event = strtok_r(line, " \t", &sav);
+	if (!event || *event == '\0')
+		return -1;
+
+	format = strtok_r(NULL, "", &sav);
+	if (!format || *format == '\0')
+		return -1;
+
+	if (!ret_dyn)
+		return 0;
+
+	dyn = calloc(1, sizeof(*dyn));
+	if (!dyn)
+		return -1;
+
+	dyn->type = desc->type;
+	dyn->trace_file = strdup(desc->file);
+	if (!dyn->trace_file)
+		goto error;
+
+	dyn->event = strdup(event);
+	if (!dyn->event)
+		goto error;
+
+	dyn->format = strdup(format+1);
+	if (!dyn->format)
+		goto error;
+
+	*ret_dyn = dyn;
+	return 0;
+error:
+	tracefs_dynevent_free(dyn);
+	return -1;
+}
+
+static void init_devent_desc(void)
+{
+	int i;
+
+	BUILD_BUG_ON(ARRAY_SIZE(dynevents) != EVENT_INDEX(TRACEFS_DYNEVENT_MAX));
+
+	if (!tracefs_file_exists(NULL, DYNEVENTS_EVENTS))
+		return;
+
+	/* Use  ftrace dynamic_events, if available */
+	for (i = 0; i < EVENT_INDEX(TRACEFS_DYNEVENT_MAX); i++)
+		dynevents[i].file = DYNEVENTS_EVENTS;
+
+	dynevents[EVENT_INDEX(TRACEFS_DYNEVENT_SYNTH)].prefix = "s";
+}
+
+static struct dyn_events_desc *get_devent_desc(enum tracefs_dynevent_type type)
+{
+
+	static bool init;
+
+	if (type >= TRACEFS_DYNEVENT_MAX)
+		return NULL;
+
+	if (!init) {
+		init_devent_desc();
+		init = true;
+	}
+
+	return &dynevents[EVENT_INDEX(type)];
+}
+
+/**
+ * dynevent_alloc - Allocate new dynamic event
+ * @type: Type of the dynamic event
+ * @system: The system name (NULL for the default dynamic)
+ * @event: Name of the event
+ * @addr: The function and offset (or address) to insert the probe
+ * @format: The format string to define the probe.
+ *
+ * Allocate a dynamic event context that will be in the @system group
+ * (or dynamic if @system is NULL). Have the name of @event and
+ * will be associated to @addr, if applicable for that event type
+ * (function name, with or without offset, or a address). And the @format will
+ * define the format of the kprobe.
+ * The dynamic event is not created in the system.
+ *
+ * Return a pointer to a dynamic event context on success, or NULL on error.
+ * The returned pointer must be freed with tracefs_dynevent_free()
+ *
+ * errno will be set to EINVAL if event is NULL.
+ */
+__hidden struct tracefs_dynevent *
+dynevent_alloc(enum tracefs_dynevent_type type, const char *system,
+	       const char *event, const char *address, const char *format)
+{
+	struct tracefs_dynevent *devent;
+	struct dyn_events_desc *desc;
+
+	if (!event) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	desc = get_devent_desc(type);
+	if (!desc || !desc->file) {
+		errno = ENOTSUP;
+		return NULL;
+	}
+
+	devent = calloc(1, sizeof(*devent));
+	if (!devent)
+		return NULL;
+
+	devent->type = type;
+	devent->trace_file = strdup(desc->file);
+	if (!devent->trace_file)
+		goto err;
+
+	if (!system)
+		system = DYNEVENTS_DEFAULT_GROUP;
+	devent->system = strdup(system);
+	if (!devent->system)
+		goto err;
+
+	devent->event = strdup(event);
+	if (!devent->event)
+		goto err;
+
+	devent->prefix = strdup(desc->prefix);
+	if (!devent->prefix)
+		goto err;
+
+	if (address) {
+		devent->address = strdup(address);
+		if (!devent->address)
+			goto err;
+	}
+	if (format) {
+		devent->format = strdup(format);
+		if (!devent->format)
+			goto err;
+	}
+
+	return devent;
+err:
+	tracefs_dynevent_free(devent);
+	return NULL;
+}
+
+/**
+ * tracefs_dynevent_create - Create a dynamic event in the system
+ * @devent: Pointer to a dynamic event context, describing the event
+ *
+ * Return 0 on success, or -1 on error.
+ */
+int tracefs_dynevent_create(struct tracefs_dynevent *devent)
+{
+	char *str;
+	int ret;
+
+	if (!devent)
+		return -1;
+
+	if (devent->system && devent->system[0])
+		ret = asprintf(&str, "%s%s%s/%s %s %s\n",
+				devent->prefix, strlen(devent->prefix) ? ":" : "",
+				devent->system, devent->event,
+				devent->address ? devent->address : "",
+				devent->format ? devent->format : "");
+	else
+		ret = asprintf(&str, "%s%s%s %s %s\n",
+				devent->prefix, strlen(devent->prefix) ? ":" : "",
+				devent->event,
+				devent->address ? devent->address : "",
+				devent->format ? devent->format : "");
+	if (ret < 0)
+		return -1;
+
+	ret = tracefs_instance_file_append(NULL, devent->trace_file, str);
+	free(str);
+
+	return ret < 0 ? ret : 0;
+}
+
+static void disable_events(const char *system, const char *event,
+			   char **list)
+{
+	struct tracefs_instance *instance;
+	int i;
+
+	/*
+	 * Note, this will not fail even on error.
+	 * That is because even if something fails, it may still
+	 * work enough to clear the kprobes. If that's the case
+	 * the clearing after the loop will succeed and the function
+	 * is a success, even though other parts had failed. If
+	 * one of the kprobe events is enabled in one of the
+	 * instances that fail, then the clearing will fail too
+	 * and the function will return an error.
+	 */
+
+	tracefs_event_disable(NULL, system, event);
+	/* No need to test results */
+
+	if (!list)
+		return;
+
+	for (i = 0; list[i]; i++) {
+		instance = tracefs_instance_alloc(NULL, list[i]);
+		/* If this fails, try the next one */
+		if (!instance)
+			continue;
+		tracefs_event_disable(instance, system, event);
+		tracefs_instance_free(instance);
+	}
+}
+
+/**
+ * tracefs_dynevent_destroy - Remove a dynamic event from the system
+ * @devent: A dynamic event context, describing the dynamic event that will be deleted.
+ * @force: Will attempt to disable all events before removing them.
+ *
+ * The dynamic event context is not freed by this API. It only removes the event from the system.
+ * If there are any enabled events, and @force is not set, then it will error with -1 and errno
+ * to be EBUSY.
+ *
+ * Return 0 on success, or -1 on error.
+ */
+int tracefs_dynevent_destroy(struct tracefs_dynevent *devent, bool force)
+{
+	struct dyn_events_desc *desc;
+	char **instance_list;
+
+	if (!devent)
+		return -1;
+
+	if (force) {
+		instance_list = tracefs_instances(NULL);
+		disable_events(devent->system, devent->event, instance_list);
+		tracefs_list_free(instance_list);
+	}
+
+	desc = get_devent_desc(devent->type);
+	if (!desc)
+		return -1;
+
+	return desc->del(desc, devent);
+}
+
+static int get_all_dynevents(enum tracefs_dynevent_type type, const char *system,
+			     struct tracefs_dynevent ***ret_all)
+{
+	struct dyn_events_desc *desc;
+	struct tracefs_dynevent *devent, **tmp, **all = NULL;
+	char *content;
+	int count = 0;
+	char *line;
+	char *next;
+	int ret;
+
+	desc = get_devent_desc(type);
+	if (!desc)
+		return -1;
+
+	content = tracefs_instance_file_read(NULL, desc->file, NULL);
+	if (!content)
+		return -1;
+
+	line = content;
+	do {
+		next = strchr(line, '\n');
+		if (next)
+			*next = '\0';
+		ret = desc->parse(desc, system, line, ret_all ? &devent : NULL);
+		if (!ret) {
+			if (ret_all) {
+				tmp = realloc(all, (count + 1) * sizeof(*tmp));
+				if (!tmp)
+					goto error;
+				all = tmp;
+				all[count] = devent;
+			}
+			count++;
+		}
+		line = next + 1;
+	} while (next);
+
+	free(content);
+	if (ret_all)
+		*ret_all = all;
+	return count;
+
+error:
+	free(content);
+	free(all);
+	return -1;
+}
+
+/**
+ * tracefs_dynevent_list_free - Deletes an array of pointers to dynamic event contexts
+ * @events: An array of pointers to dynamic event contexts. The last element of the array
+ *	    must be a NULL pointer.
+ */
+void tracefs_dynevent_list_free(struct tracefs_dynevent **events)
+{
+	int i;
+
+	if (!events)
+		return;
+
+	for (i = 0; events[i]; i++)
+		tracefs_dynevent_free(events[i]);
+
+	free(events);
+}
+
+/**
+ * tracefs_dynevent_get_all - return an array of pointers to dynamic events of given types
+ * @types: Dynamic event type, or bitmask of dynamic event types. If 0 is passed, all types
+ *	   are considered.
+ * @system: Get events from that system only. If @system is NULL, events from all systems
+ *	    are returned.
+ *
+ * Returns an array of pointers to dynamic events of given types that exist in the system.
+ * The array must be freed with tracefs_dynevent_list_free(). If there are no events a NULL
+ * pointer is returned.
+ */
+struct tracefs_dynevent **
+tracefs_dynevent_get_all(unsigned int types, const char *system)
+{
+	struct tracefs_dynevent **events, **tmp, **all_events = NULL;
+	int count, all = 0;
+	int i;
+
+	for (i = 1; i < TRACEFS_DYNEVENT_MAX; i <<= 1) {
+		if (types) {
+			if (i > types)
+				break;
+			if (!(types & i))
+				continue;
+		}
+		count = get_all_dynevents(i, system, &events);
+		if (count > 0) {
+			tmp = realloc(all_events, (all + count + 1) * sizeof(*tmp));
+			if (!tmp)
+				goto error;
+			all_events = tmp;
+			memcpy(all_events + all, events, count * sizeof(*events));
+			all += count;
+			/* Add a NULL pointer at the end */
+			all_events[all] = NULL;
+			free(events);
+		}
+	}
+
+	return all_events;
+
+error:
+	if (all_events) {
+		for (i = 0; i < all; i++)
+			free(all_events[i]);
+		free(all_events);
+	}
+	return NULL;
+}
+
+/**
+ * tracefs_dynevent_get - return a single dynamic event if it exists
+ * @type; Dynamic event type
+ * @system: Get events from that system only. May be NULL.
+ * @event: Get event of the system type (may not be NULL)
+ *
+ * Returns the dynamic event of the given @type and @system for with the @event
+ * name. If @system is NULL, it will return the first dynamic event that it finds
+ * that matches the @event name.
+ *
+ * The returned event must be freed with tracefs_dynevent_free().
+ * NULL is returned if no event match is found, or other error.
+ */
+struct tracefs_dynevent *
+tracefs_dynevent_get(enum tracefs_dynevent_type type, const char *system,
+		     const char *event)
+{
+	struct tracefs_dynevent **events;
+	struct tracefs_dynevent *devent = NULL;
+	int count;
+	int i;
+
+	if (!event) {
+		errno = -EINVAL;
+		return NULL;
+	}
+
+	count = get_all_dynevents(type, system, &events);
+	if (count <= 0)
+		return NULL;
+
+	for (i = 0; i < count; i++) {
+		if (strcmp(events[i]->event, event) == 0)
+			break;
+	}
+	if (i < count) {
+		devent = events[i];
+		events[i] = NULL;
+	}
+
+	tracefs_dynevent_list_free(events);
+
+	return devent;
+}
+
+/**
+ * tracefs_dynevent_destroy_all - removes all dynamic events of given types from the system
+ * @types: Dynamic event type, or bitmask of dynamic event types. If 0 is passed, all types
+ *	   are considered.
+ * @force: Will attempt to disable all events before removing them.
+ *
+ * Will remove all dynamic events of the given types from the system. If there are any enabled
+ * events, and @force is not set, then the removal of these will fail. If @force is set, then
+ * it will attempt to disable all the events in all instances before removing them.
+ *
+ * Returns zero if all requested events are removed successfully, or -1 if some of them are not
+ * removed.
+ */
+int tracefs_dynevent_destroy_all(unsigned int types, bool force)
+{
+	struct tracefs_dynevent **all;
+	int ret = 0;
+	int i;
+
+	all = tracefs_dynevent_get_all(types, NULL);
+	if (!all)
+		return 0;
+
+	for (i = 0; all[i]; i++) {
+		if (tracefs_dynevent_destroy(all[i], force))
+			ret = -1;
+	}
+
+	tracefs_dynevent_list_free(all);
+
+	return ret;
+}
+
+/**
+ * dynevent_get_count - Count dynamic events of given types and system
+ * @types: Dynamic event type, or bitmask of dynamic event types. If 0 is passed, all types
+ *	   are considered.
+ * @system: Count events from that system only. If @system is NULL, events from all systems
+ *	    are counted.
+ *
+ * Return the count of requested dynamic events
+ */
+__hidden int dynevent_get_count(unsigned int types, const char *system)
+{
+	int count, all = 0;
+	int i;
+
+	for (i = 1; i < TRACEFS_DYNEVENT_MAX; i <<= 1) {
+		if (types) {
+			if (i > types)
+				break;
+			if (!(types & i))
+				continue;
+		}
+		count = get_all_dynevents(i, system, NULL);
+		if (count > 0)
+			all += count;
+	}
+
+	return all;
+}
+
+static enum tracefs_dynevent_type
+dynevent_info(struct tracefs_dynevent *dynevent, char **system,
+	      char **event, char **prefix, char **addr, char **format)
+{
+	char **lv[] = { system, event, prefix, addr, format };
+	char **rv[] = { &dynevent->system, &dynevent->event, &dynevent->prefix,
+			&dynevent->address, &dynevent->format };
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(lv); i++) {
+		if (lv[i]) {
+			if (*rv[i]) {
+				*lv[i] = strdup(*rv[i]);
+				if (!*lv[i])
+					goto error;
+			} else {
+				*lv[i] = NULL;
+			}
+		}
+	}
+
+	return dynevent->type;
+
+error:
+	for (i--; i >= 0; i--) {
+		if (lv[i])
+			free(*lv[i]);
+	}
+
+	return TRACEFS_DYNEVENT_UNKNOWN;
+}
+
+/**
+ * tracefs_dynevent_info - return details of a dynamic event
+ * @dynevent: A dynamic event context, describing given dynamic event.
+ * @group: return, group in which the dynamic event is configured
+ * @event: return, name of the dynamic event
+ * @prefix: return, prefix string of the dynamic event
+ * @addr: return, the function and offset (or address) of the dynamic event
+ * @format: return, the format string of the dynamic event
+ *
+ * Returns the type of the dynamic event, or TRACEFS_DYNEVENT_UNKNOWN in case of an error.
+ * Any of the @group, @event, @prefix, @addr and @format parameters are optional.
+ * If a valid pointer is passed, in case of success - a string is allocated and returned.
+ * These strings must be freed with free().
+ */
+enum tracefs_dynevent_type
+tracefs_dynevent_info(struct tracefs_dynevent *dynevent, char **system,
+		      char **event, char **prefix, char **addr, char **format)
+{
+	if (!dynevent)
+		return TRACEFS_DYNEVENT_UNKNOWN;
+
+	return dynevent_info(dynevent, system, event, prefix, addr, format);
+}
+
+/**
+ * tracefs_dynevent_get_event - return tep event representing the given dynamic event
+ * @tep: a handle to the trace event parser context that holds the events
+ * @dynevent: a dynamic event context, describing given dynamic event.
+ *
+ * Returns a pointer to a tep event describing the given dynamic event. The pointer
+ * is managed by the @tep handle and must not be freed. In case of an error, or in case
+ * the requested dynamic event is missing in the @tep handler - NULL is returned.
+ */
+struct tep_event *
+tracefs_dynevent_get_event(struct tep_handle *tep, struct tracefs_dynevent *dynevent)
+{
+	if (!tep || !dynevent || !dynevent->event)
+		return NULL;
+
+	return get_tep_event(tep, dynevent->system, dynevent->event);
+}
diff --git a/src/tracefs-eprobes.c b/src/tracefs-eprobes.c
new file mode 100644
index 0000000..cc25f8e
--- /dev/null
+++ b/src/tracefs-eprobes.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdlib.h>
+#include <errno.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+#define EPROBE_DEFAULT_GROUP "eprobes"
+
+/**
+ * tracefs_eprobe_alloc - Allocate new eprobe
+ * @system: The system name (NULL for the default eprobes)
+ * @event: The name of the event to create
+ * @target_system: The system of the target event
+ * @target_event: The name of the target event
+ * @fetchargs: String with arguments, that will be fetched from @target_event
+ *
+ * Allocate an eprobe context that will be in the @system group (or eprobes if
+ * @system is NULL). Have the name of @event. The new eprobe will be attached to
+ * given @target_event which is in the given @target_system. The arguments
+ * described in @fetchargs will fetched from the @target_event.
+ *
+ * The eprobe is not created in the system.
+ *
+ * Return a pointer to a eprobe context on success, or NULL on error.
+ * The returned pointer must be freed with tracefs_dynevent_free()
+ *
+ */
+struct tracefs_dynevent *
+tracefs_eprobe_alloc(const char *system, const char *event,
+		     const char *target_system, const char *target_event, const char *fetchargs)
+{
+	struct tracefs_dynevent *kp;
+	char *target;
+
+	if (!event || !target_system || !target_event) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if (!system)
+		system = EPROBE_DEFAULT_GROUP;
+
+	if (asprintf(&target, "%s.%s", target_system, target_event) < 0)
+		return NULL;
+
+	kp = dynevent_alloc(TRACEFS_DYNEVENT_EPROBE, system, event, target, fetchargs);
+	free(target);
+
+	return kp;
+}
+
diff --git a/src/tracefs-events.c b/src/tracefs-events.c
new file mode 100644
index 0000000..c2adf41
--- /dev/null
+++ b/src/tracefs-events.c
@@ -0,0 +1,1515 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ * Updates:
+ * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include <kbuffer.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+static struct follow_event *root_followers;
+static int nr_root_followers;
+
+static struct follow_event *root_missed_followers;
+static int nr_root_missed_followers;
+
+struct cpu_iterate {
+	struct tracefs_cpu *tcpu;
+	struct tep_record record;
+	struct tep_event *event;
+	struct kbuffer *kbuf;
+	void *page;
+	int psize;
+	int cpu;
+};
+
+static int read_kbuf_record(struct cpu_iterate *cpu)
+{
+	unsigned long long ts;
+	void *ptr;
+
+	if (!cpu || !cpu->kbuf)
+		return -1;
+	ptr = kbuffer_read_event(cpu->kbuf, &ts);
+	if (!ptr)
+		return -1;
+
+	memset(&cpu->record, 0, sizeof(cpu->record));
+	cpu->record.ts = ts;
+	cpu->record.size = kbuffer_event_size(cpu->kbuf);
+	cpu->record.record_size = kbuffer_curr_size(cpu->kbuf);
+	cpu->record.missed_events = kbuffer_missed_events(cpu->kbuf);
+	cpu->record.cpu = cpu->cpu;
+	cpu->record.data = ptr;
+	cpu->record.ref_count = 1;
+
+	kbuffer_next_event(cpu->kbuf, NULL);
+
+	return 0;
+}
+
+int read_next_page(struct tep_handle *tep, struct cpu_iterate *cpu)
+{
+	enum kbuffer_long_size long_size;
+	enum kbuffer_endian endian;
+	int r;
+
+	if (!cpu->tcpu)
+		return -1;
+
+	r = tracefs_cpu_buffered_read(cpu->tcpu, cpu->page, true);
+	/*
+	 * tracefs_cpu_buffered_read() only reads in full subbuffer size,
+	 * but this wants partial buffers as well. If the function returns
+	 * empty (-1 for EAGAIN), try tracefs_cpu_read() next, as that can
+	 * read partially filled buffers too, but isn't as efficient.
+	 */
+	if (r <= 0)
+		r = tracefs_cpu_read(cpu->tcpu, cpu->page, true);
+	if (r <= 0)
+		return -1;
+
+	if (!cpu->kbuf) {
+		if (tep_is_file_bigendian(tep))
+			endian = KBUFFER_ENDIAN_BIG;
+		else
+			endian = KBUFFER_ENDIAN_LITTLE;
+
+		if (tep_get_header_page_size(tep) == 8)
+			long_size = KBUFFER_LSIZE_8;
+		else
+			long_size = KBUFFER_LSIZE_4;
+
+		cpu->kbuf = kbuffer_alloc(long_size, endian);
+		if (!cpu->kbuf)
+			return -1;
+	}
+
+	kbuffer_load_subbuffer(cpu->kbuf, cpu->page);
+	if (kbuffer_subbuffer_size(cpu->kbuf) > r) {
+		tracefs_warning("%s: page_size > %d", __func__, r);
+		return -1;
+	}
+
+	return 0;
+}
+
+int read_next_record(struct tep_handle *tep, struct cpu_iterate *cpu)
+{
+	int id;
+
+	do {
+		while (!read_kbuf_record(cpu)) {
+			id = tep_data_type(tep, &(cpu->record));
+			cpu->event = tep_find_event(tep, id);
+			if (cpu->event)
+				return 0;
+		}
+	} while (!read_next_page(tep, cpu));
+
+	return -1;
+}
+
+/**
+ * tracefs_follow_missed_events - Add callback for missed events for iterators
+ * @instance: The instance to follow
+ * @callback: The function to call when missed events is detected
+ * @callback_data: The data to pass to @callback
+ *
+ * This attaches a callback to an @instance or the root instance if @instance
+ * is NULL, where if tracefs_iterate_raw_events() is called, that if missed
+ * events are detected, it will call @callback, with the following parameters:
+ *  @event: The event pointer of the record with the missing events
+ *  @record; The event instance of @event.
+ *  @cpu: The cpu that the event happened on.
+ *  @callback_data: The same as @callback_data passed to the function.
+ *
+ * If the count of missing events is available, @record->missed_events
+ * will have a positive number holding the number of missed events since
+ * the last event on the same CPU, or just -1 if that number is unknown
+ * but missed events did happen.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int tracefs_follow_missed_events(struct tracefs_instance *instance,
+				 int (*callback)(struct tep_event *,
+						 struct tep_record *,
+						 int, void *),
+				 void *callback_data)
+{
+	struct follow_event **followers;
+	struct follow_event *follower;
+	struct follow_event follow;
+	int *nr_followers;
+
+	follow.event = NULL;
+	follow.callback = callback;
+	follow.callback_data = callback_data;
+
+	if (instance) {
+		followers = &instance->missed_followers;
+		nr_followers = &instance->nr_missed_followers;
+	} else {
+		followers = &root_missed_followers;
+		nr_followers = &nr_root_missed_followers;
+	}
+	follower = realloc(*followers, sizeof(*follower) *
+			    ((*nr_followers) + 1));
+	if (!follower)
+		return -1;
+
+	*followers = follower;
+	follower[(*nr_followers)++] = follow;
+
+	return 0;
+}
+
+static int call_missed_events(struct tracefs_instance *instance,
+			      struct tep_event *event, struct tep_record *record, int cpu)
+{
+	struct follow_event *followers;
+	int nr_followers;
+	int ret = 0;
+	int i;
+
+	if (instance) {
+		followers = instance->missed_followers;
+		nr_followers = instance->nr_missed_followers;
+	} else {
+		followers = root_missed_followers;
+		nr_followers = nr_root_missed_followers;
+	}
+
+	if (!followers)
+		return 0;
+
+	for (i = 0; i < nr_followers; i++) {
+		ret |= followers[i].callback(event, record,
+					     cpu, followers[i].callback_data);
+	}
+
+	return ret;
+}
+
+static int call_followers(struct tracefs_instance *instance,
+			  struct tep_event *event, struct tep_record *record, int cpu)
+{
+	struct follow_event *followers;
+	int nr_followers;
+	int ret = 0;
+	int i;
+
+	if (record->missed_events)
+		ret = call_missed_events(instance, event, record, cpu);
+	if (ret)
+		return ret;
+
+	if (instance) {
+		followers = instance->followers;
+		nr_followers = instance->nr_followers;
+	} else {
+		followers = root_followers;
+		nr_followers = nr_root_followers;
+	}
+
+	if (!followers)
+		return 0;
+
+	for (i = 0; i < nr_followers; i++) {
+		if (followers[i].event == event)
+			ret |= followers[i].callback(event, record,
+						     cpu, followers[i].callback_data);
+	}
+
+	return ret;
+}
+
+static int read_cpu_pages(struct tep_handle *tep, struct tracefs_instance *instance,
+			  struct cpu_iterate *cpus, int count,
+			  int (*callback)(struct tep_event *,
+					  struct tep_record *,
+					  int, void *),
+			  void *callback_context,
+			  bool *keep_going)
+{
+	bool has_data = false;
+	int ret;
+	int i, j;
+
+	for (i = 0; i < count; i++) {
+		ret = read_next_record(tep, cpus + i);
+		if (!ret)
+			has_data = true;
+	}
+
+	while (has_data && *(volatile bool *)keep_going) {
+		j = count;
+		for (i = 0; i < count; i++) {
+			if (!cpus[i].event)
+				continue;
+			if (j == count || cpus[j].record.ts > cpus[i].record.ts)
+				j = i;
+		}
+		if (j < count) {
+			if (call_followers(instance, cpus[j].event, &cpus[j].record, cpus[j].cpu))
+				break;
+			if (callback &&
+			    callback(cpus[j].event, &cpus[j].record, cpus[j].cpu, callback_context))
+				break;
+			cpus[j].event = NULL;
+			read_next_record(tep, cpus + j);
+		} else {
+			has_data = false;
+		}
+	}
+
+	return 0;
+}
+
+static int open_cpu_files(struct tracefs_instance *instance, cpu_set_t *cpus,
+			  int cpu_size, struct cpu_iterate **all_cpus, int *count)
+{
+	struct tracefs_cpu *tcpu;
+	struct cpu_iterate *tmp;
+	int nr_cpus;
+	int cpu;
+	int i = 0;
+
+	*all_cpus = NULL;
+
+	nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+	for (cpu = 0; cpu < nr_cpus; cpu++) {
+		if (cpus && !CPU_ISSET_S(cpu, cpu_size, cpus))
+			continue;
+		tcpu = tracefs_cpu_open(instance, cpu, true);
+		tmp = realloc(*all_cpus, (i + 1) * sizeof(*tmp));
+		if (!tmp) {
+			i--;
+			goto error;
+		}
+
+		*all_cpus = tmp;
+
+		memset(tmp + i, 0, sizeof(*tmp));
+
+		if (!tcpu)
+			goto error;
+
+		tmp[i].tcpu = tcpu;
+		tmp[i].cpu = cpu;
+		tmp[i].psize = tracefs_cpu_read_size(tcpu);
+		tmp[i].page =  malloc(tmp[i].psize);
+
+		if (!tmp[i++].page)
+			goto error;
+	}
+	*count = i;
+	return 0;
+ error:
+	tmp = *all_cpus;
+	for (; i >= 0; i--) {
+		tracefs_cpu_close(tmp[i].tcpu);
+		free(tmp[i].page);
+	}
+	free(tmp);
+	*all_cpus = NULL;
+	return -1;
+}
+
+/**
+ * tracefs_follow_event - Add callback for specific events for iterators
+ * @tep: a handle to the trace event parser context
+ * @instance: The instance to follow
+ * @system: The system of the event to track
+ * @event_name: The name of the event to track
+ * @callback: The function to call when the event is hit in an iterator
+ * @callback_data: The data to pass to @callback
+ *
+ * This attaches a callback to an @instance or the root instance if @instance
+ * is NULL, where if tracefs_iterate_raw_events() is called, that if the specified
+ * event is hit, it will call @callback, with the following parameters:
+ *  @event: The event pointer that was found by @system and @event_name.
+ *  @record; The event instance of @event.
+ *  @cpu: The cpu that the event happened on.
+ *  @callback_data: The same as @callback_data passed to the function.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instance,
+			  const char *system, const char *event_name,
+			  int (*callback)(struct tep_event *,
+					  struct tep_record *,
+					  int, void *),
+			  void *callback_data)
+{
+	struct follow_event **followers;
+	struct follow_event *follower;
+	struct follow_event follow;
+	int *nr_followers;
+
+	if (!tep) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	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;
+
+	if (instance) {
+		followers = &instance->followers;
+		nr_followers = &instance->nr_followers;
+	} else {
+		followers = &root_followers;
+		nr_followers = &nr_root_followers;
+	}
+	follower = realloc(*followers, sizeof(*follower) *
+			    ((*nr_followers) + 1));
+	if (!follower)
+		return -1;
+
+	*followers = follower;
+	follower[(*nr_followers)++] = follow;
+
+	return 0;
+}
+
+static bool top_iterate_keep_going;
+
+/*
+ * tracefs_iterate_raw_events - Iterate through events in trace_pipe_raw,
+ *				per CPU trace buffers
+ * @tep: a handle to the trace event parser context
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @cpus: Iterate only through the buffers of CPUs, set in the mask.
+ *	  If NULL, iterate through all CPUs.
+ * @cpu_size: size of @cpus set
+ * @callback: A user function, called for each record from the file
+ * @callback_context: A custom context, passed to the user callback function
+ *
+ * If the @callback returns non-zero, the iteration stops - in that case all
+ * records from the current page will be lost from future reads
+ * The events are iterated in sorted order, oldest first.
+ *
+ * Returns -1 in case of an error, or 0 otherwise
+ */
+int tracefs_iterate_raw_events(struct tep_handle *tep,
+				struct tracefs_instance *instance,
+				cpu_set_t *cpus, int cpu_size,
+				int (*callback)(struct tep_event *,
+						struct tep_record *,
+						int, void *),
+				void *callback_context)
+{
+	bool *keep_going = instance ? &instance->iterate_keep_going :
+				      &top_iterate_keep_going;
+	struct follow_event *followers;
+	struct cpu_iterate *all_cpus;
+	int count = 0;
+	int ret;
+	int i;
+
+	(*(volatile bool *)keep_going) = true;
+
+	if (!tep)
+		return -1;
+
+	if (instance)
+		followers = instance->followers;
+	else
+		followers = root_followers;
+	if (!callback && !followers)
+		return -1;
+
+	ret = open_cpu_files(instance, cpus, cpu_size, &all_cpus, &count);
+	if (ret < 0)
+		goto out;
+	ret = read_cpu_pages(tep, instance, all_cpus, count,
+			     callback, callback_context,
+			     keep_going);
+
+out:
+	if (all_cpus) {
+		for (i = 0; i < count; i++) {
+			kbuffer_free(all_cpus[i].kbuf);
+			tracefs_cpu_close(all_cpus[i].tcpu);
+			free(all_cpus[i].page);
+		}
+		free(all_cpus);
+	}
+
+	return ret;
+}
+
+/**
+ * tracefs_iterate_stop - stop the iteration over the raw events.
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ */
+void tracefs_iterate_stop(struct tracefs_instance *instance)
+{
+	if (instance)
+		instance->iterate_keep_going = false;
+	else
+		top_iterate_keep_going = false;
+}
+
+static int add_list_string(char ***list, const char *name)
+{
+	char **tmp;
+
+	tmp = tracefs_list_add(*list, name);
+	if (!tmp) {
+		tracefs_list_free(*list);
+		*list = NULL;
+		return -1;
+	}
+
+	*list = tmp;
+	return 0;
+}
+
+__hidden char *trace_append_file(const char *dir, const char *name)
+{
+	char *file;
+	int ret;
+
+	ret = asprintf(&file, "%s/%s", dir, name);
+
+	return ret < 0 ? NULL : file;
+}
+
+static int event_file(char **path, const char *system,
+		      const char *event, const char *file)
+{
+	if (!system || !event || !file)
+		return -1;
+
+	return asprintf(path, "events/%s/%s/%s",
+			system, event, file);
+}
+
+/**
+ * tracefs_event_get_file - return a file in an event directory
+ * @instance: The instance the event is in (NULL for top level)
+ * @system: The system name that the event file is in
+ * @event: The event name of the event
+ * @file: The name of the file in the event directory.
+ *
+ * Returns a path to a file in the event director.
+ * or NULL on error. The path returned must be freed with
+ * tracefs_put_tracing_file().
+ */
+char *tracefs_event_get_file(struct tracefs_instance *instance,
+			     const char *system, const char *event,
+			     const char *file)
+{
+	char *instance_path;
+	char *path;
+	int ret;
+
+	ret = event_file(&path, system, event, file);
+	if (ret < 0)
+		return NULL;
+
+	instance_path = tracefs_instance_get_file(instance, path);
+	free(path);
+
+	return instance_path;
+}
+
+/**
+ * tracefs_event_file_read - read the content from an event file
+ * @instance: The instance the event is in (NULL for top level)
+ * @system: The system name that the event file is in
+ * @event: The event name of the event
+ * @file: The name of the file in the event directory.
+ * @psize: the size of the content read.
+ *
+ * Reads the content of the event file that is passed via the
+ * arguments and returns the content.
+ *
+ * Return a string containing the content of the file or NULL
+ * on error. The string returned must be freed with free().
+ */
+char *tracefs_event_file_read(struct tracefs_instance *instance,
+			      const char *system, const char *event,
+			      const char *file, int *psize)
+{
+	char *content;
+	char *path;
+	int ret;
+
+	ret = event_file(&path, system, event, file);
+	if (ret < 0)
+		return NULL;
+
+	content = tracefs_instance_file_read(instance, path, psize);
+	free(path);
+	return content;
+}
+
+/**
+ * tracefs_event_file_write - write to an event file
+ * @instance: The instance the event is in (NULL for top level)
+ * @system: The system name that the event file is in
+ * @event: The event name of the event
+ * @file: The name of the file in the event directory.
+ * @str: The string to write into the file
+ *
+ * Writes the content of @str to a file in the instance directory.
+ * The content of the file will be overwritten by @str.
+ *
+ * Return 0 on success, and -1 on error.
+ */
+int tracefs_event_file_write(struct tracefs_instance *instance,
+			     const char *system, const char *event,
+			     const char *file, const char *str)
+{
+	char *path;
+	int ret;
+
+	ret = event_file(&path, system, event, file);
+	if (ret < 0)
+		return -1;
+
+	ret = tracefs_instance_file_write(instance, path, str);
+	free(path);
+	return ret;
+}
+
+/**
+ * tracefs_event_file_append - write to an event file
+ * @instance: The instance the event is in (NULL for top level)
+ * @system: The system name that the event file is in
+ * @event: The event name of the event
+ * @file: The name of the file in the event directory.
+ * @str: The string to write into the file
+ *
+ * Writes the content of @str to a file in the instance directory.
+ * The content of @str will be appended to the content of the file.
+ * The current content should not be lost.
+ *
+ * Return 0 on success, and -1 on error.
+ */
+int tracefs_event_file_append(struct tracefs_instance *instance,
+			      const char *system, const char *event,
+			      const char *file, const char *str)
+{
+	char *path;
+	int ret;
+
+	ret = event_file(&path, system, event, file);
+	if (ret < 0)
+		return -1;
+
+	ret = tracefs_instance_file_append(instance, path, str);
+	free(path);
+	return ret;
+}
+
+/**
+ * tracefs_event_file_clear - clear an event file
+ * @instance: The instance the event is in (NULL for top level)
+ * @system: The system name that the event file is in
+ * @event: The event name of the event
+ * @file: The name of the file in the event directory.
+ *
+ * Clears the content of the event file. That is, it is opened
+ * with O_TRUNC and then closed.
+ *
+ * Return 0 on success, and -1 on error.
+ */
+int tracefs_event_file_clear(struct tracefs_instance *instance,
+			     const char *system, const char *event,
+			     const char *file)
+{
+	char *path;
+	int ret;
+
+	ret = event_file(&path, system, event, file);
+	if (ret < 0)
+		return -1;
+
+	ret = tracefs_instance_file_clear(instance, path);
+	free(path);
+	return ret;
+}
+
+/**
+ * tracefs_event_file_exits - test if a file exists
+ * @instance: The instance the event is in (NULL for top level)
+ * @system: The system name that the event file is in
+ * @event: The event name of the event
+ * @file: The name of the file in the event directory.
+ *
+ * Return true if the file exists, false if it odes not or
+ * an error occurred.
+ */
+bool tracefs_event_file_exists(struct tracefs_instance *instance,
+			       const char *system, const char *event,
+			       const char *file)
+{
+	char *path;
+	bool ret;
+
+	if (event_file(&path, system, event, file) < 0)
+		return false;
+
+	ret = tracefs_file_exists(instance, path);
+	free(path);
+	return ret;
+}
+
+/**
+ * tracefs_event_systems - return list of systems for tracing
+ * @tracing_dir: directory holding the "events" directory
+ *		 if NULL, top tracing directory is used
+ *
+ * Returns an allocated list of system names. Both the names and
+ * the list must be freed with tracefs_list_free()
+ * The list returned ends with a "NULL" pointer
+ */
+char **tracefs_event_systems(const char *tracing_dir)
+{
+	struct dirent *dent;
+	char **systems = NULL;
+	char *events_dir;
+	struct stat st;
+	DIR *dir;
+	int ret;
+
+	if (!tracing_dir)
+		tracing_dir = tracefs_tracing_dir();
+
+	if (!tracing_dir)
+		return NULL;
+
+	events_dir = trace_append_file(tracing_dir, "events");
+	if (!events_dir)
+		return NULL;
+
+	/*
+	 * Search all the directories in the events directory,
+	 * and collect the ones that have the "enable" file.
+	 */
+	ret = stat(events_dir, &st);
+	if (ret < 0 || !S_ISDIR(st.st_mode))
+		goto out_free;
+
+	dir = opendir(events_dir);
+	if (!dir)
+		goto out_free;
+
+	while ((dent = readdir(dir))) {
+		const char *name = dent->d_name;
+		char *enable;
+		char *sys;
+
+		if (strcmp(name, ".") == 0 ||
+		    strcmp(name, "..") == 0)
+			continue;
+
+		sys = trace_append_file(events_dir, name);
+		ret = stat(sys, &st);
+		if (ret < 0 || !S_ISDIR(st.st_mode)) {
+			free(sys);
+			continue;
+		}
+
+		enable = trace_append_file(sys, "enable");
+
+		ret = stat(enable, &st);
+		if (ret >= 0) {
+			if (add_list_string(&systems, name) < 0)
+				goto out_free;
+		}
+		free(enable);
+		free(sys);
+	}
+
+	closedir(dir);
+
+ out_free:
+	free(events_dir);
+	return systems;
+}
+
+/**
+ * tracefs_system_events - return list of events for system
+ * @tracing_dir: directory holding the "events" directory
+ * @system: the system to return the events for
+ *
+ * Returns an allocated list of event names. Both the names and
+ * the list must be freed with tracefs_list_free()
+ * The list returned ends with a "NULL" pointer
+ */
+char **tracefs_system_events(const char *tracing_dir, const char *system)
+{
+	struct dirent *dent;
+	char **events = NULL;
+	char *system_dir = NULL;
+	struct stat st;
+	DIR *dir;
+	int ret;
+
+	if (!tracing_dir)
+		tracing_dir = tracefs_tracing_dir();
+
+	if (!tracing_dir || !system)
+		return NULL;
+
+	asprintf(&system_dir, "%s/events/%s", tracing_dir, system);
+	if (!system_dir)
+		return NULL;
+
+	ret = stat(system_dir, &st);
+	if (ret < 0 || !S_ISDIR(st.st_mode))
+		goto out_free;
+
+	dir = opendir(system_dir);
+	if (!dir)
+		goto out_free;
+
+	while ((dent = readdir(dir))) {
+		const char *name = dent->d_name;
+		char *event;
+
+		if (strcmp(name, ".") == 0 ||
+		    strcmp(name, "..") == 0)
+			continue;
+
+		event = trace_append_file(system_dir, name);
+		ret = stat(event, &st);
+		if (ret < 0 || !S_ISDIR(st.st_mode)) {
+			free(event);
+			continue;
+		}
+
+		if (add_list_string(&events, name) < 0)
+			goto out_free;
+
+		free(event);
+	}
+
+	closedir(dir);
+
+ out_free:
+	free(system_dir);
+
+	return events;
+}
+
+/**
+ * tracefs_tracers - returns an array of available tracers
+ * @tracing_dir: The directory that contains the tracing directory
+ *
+ * Returns an allocate list of plugins. The array ends with NULL
+ * Both the plugin names and array must be freed with tracefs_list_free()
+ */
+char **tracefs_tracers(const char *tracing_dir)
+{
+	char *available_tracers;
+	struct stat st;
+	char **plugins = NULL;
+	char *buf;
+	char *str, *saveptr;
+	char *plugin;
+	int slen;
+	int len;
+	int ret;
+
+	if (!tracing_dir)
+		tracing_dir = tracefs_tracing_dir();
+
+	if (!tracing_dir)
+		return NULL;
+
+	available_tracers = trace_append_file(tracing_dir, "available_tracers");
+	if (!available_tracers)
+		return NULL;
+
+	ret = stat(available_tracers, &st);
+	if (ret < 0)
+		goto out_free;
+
+	len = str_read_file(available_tracers, &buf, true);
+	if (len <= 0)
+		goto out_free;
+
+	for (str = buf; ; str = NULL) {
+		plugin = strtok_r(str, " ", &saveptr);
+		if (!plugin)
+			break;
+		slen = strlen(plugin);
+		if (!slen)
+			continue;
+
+		/* chop off any newlines */
+		if (plugin[slen - 1] == '\n')
+			plugin[slen - 1] = '\0';
+
+		/* Skip the non tracers */
+		if (strcmp(plugin, "nop") == 0 ||
+		    strcmp(plugin, "none") == 0)
+			continue;
+
+		if (add_list_string(&plugins, plugin) < 0)
+			break;
+	}
+	free(buf);
+
+ out_free:
+	free(available_tracers);
+
+	return plugins;
+}
+
+static int load_events(struct tep_handle *tep,
+		       const char *tracing_dir, const char *system, bool check)
+{
+	int ret = 0, failure = 0;
+	char **events = NULL;
+	struct stat st;
+	int len = 0;
+	int i;
+
+	if (!tracing_dir)
+		tracing_dir = tracefs_tracing_dir();
+
+	events = tracefs_system_events(tracing_dir, system);
+	if (!events)
+		return -ENOENT;
+
+	for (i = 0; events[i]; i++) {
+		char *format;
+		char *buf;
+
+		ret = asprintf(&format, "%s/events/%s/%s/format",
+			       tracing_dir, system, events[i]);
+		if (ret < 0) {
+			failure = -ENOMEM;
+			break;
+		}
+
+		ret = stat(format, &st);
+		if (ret < 0)
+			goto next_event;
+
+		/* check if event is already added, to avoid duplicates */
+		if (check && tep_find_event_by_name(tep, system, events[i]))
+			goto next_event;
+
+		len = str_read_file(format, &buf, true);
+		if (len <= 0)
+			goto next_event;
+
+		ret = tep_parse_event(tep, buf, len, system);
+		free(buf);
+next_event:
+		free(format);
+		if (ret)
+			failure = ret;
+	}
+
+	tracefs_list_free(events);
+	return failure;
+}
+
+__hidden int trace_rescan_events(struct tep_handle *tep,
+				const char *tracing_dir, const char *system)
+{
+	/* ToDo: add here logic for deleting removed events from tep handle */
+	return load_events(tep, tracing_dir, system, true);
+}
+
+__hidden int trace_load_events(struct tep_handle *tep,
+			       const char *tracing_dir, const char *system)
+{
+	return load_events(tep, tracing_dir, system, false);
+}
+
+__hidden struct tep_event *get_tep_event(struct tep_handle *tep,
+					 const char *system, const char *name)
+{
+	struct tep_event *event;
+
+	/* Check if event exists in the system */
+	if (!tracefs_event_file_exists(NULL, system, name, "format"))
+		return NULL;
+
+	/* If the event is already loaded in the tep, return it */
+	event = tep_find_event_by_name(tep, system, name);
+	if (event)
+		return event;
+
+	/* Try to load any new events from the given system */
+	if (trace_rescan_events(tep, NULL, system))
+		return NULL;
+
+	return tep_find_event_by_name(tep, system, name);
+}
+
+static int read_header(struct tep_handle *tep, const char *tracing_dir)
+{
+	struct stat st;
+	char *header;
+	char *buf;
+	int len;
+	int ret = -1;
+
+	header = trace_append_file(tracing_dir, "events/header_page");
+
+	ret = stat(header, &st);
+	if (ret < 0)
+		goto out;
+
+	len = str_read_file(header, &buf, true);
+	if (len <= 0)
+		goto out;
+
+	tep_parse_header_page(tep, buf, len, sizeof(long));
+
+	free(buf);
+
+	ret = 0;
+ out:
+	free(header);
+	return ret;
+}
+
+static bool contains(const char *name, const char * const *names)
+{
+	if (!names)
+		return false;
+	for (; *names; names++)
+		if (strcmp(name, *names) == 0)
+			return true;
+	return false;
+}
+
+static void load_kallsyms(struct tep_handle *tep)
+{
+	char *buf;
+
+	if (str_read_file("/proc/kallsyms", &buf, false) <= 0)
+		return;
+
+	tep_parse_kallsyms(tep, buf);
+	free(buf);
+}
+
+static int load_saved_cmdlines(const char *tracing_dir,
+			       struct tep_handle *tep, bool warn)
+{
+	char *path;
+	char *buf;
+	int ret;
+
+	path = trace_append_file(tracing_dir, "saved_cmdlines");
+	if (!path)
+		return -1;
+
+	ret = str_read_file(path, &buf, false);
+	free(path);
+	if (ret <= 0)
+		return -1;
+
+	ret = tep_parse_saved_cmdlines(tep, buf);
+	free(buf);
+
+	return ret;
+}
+
+static void load_printk_formats(const char *tracing_dir,
+				struct tep_handle *tep)
+{
+	char *path;
+	char *buf;
+	int ret;
+
+	path = trace_append_file(tracing_dir, "printk_formats");
+	if (!path)
+		return;
+
+	ret = str_read_file(path, &buf, false);
+	free(path);
+	if (ret <= 0)
+		return;
+
+	tep_parse_printk_formats(tep, buf);
+	free(buf);
+}
+
+/*
+ * Do a best effort attempt to load kallsyms, saved_cmdlines and
+ * printk_formats. If they can not be loaded, then this will not
+ * do the mappings. But this does not fail the loading of events.
+ */
+static void load_mappings(const char *tracing_dir,
+			  struct tep_handle *tep)
+{
+	load_kallsyms(tep);
+
+	/* If there's no tracing_dir no reason to go further */
+	if (!tracing_dir)
+		tracing_dir = tracefs_tracing_dir();
+
+	if (!tracing_dir)
+		return;
+
+	load_saved_cmdlines(tracing_dir, tep, false);
+	load_printk_formats(tracing_dir, tep);
+}
+
+int tracefs_load_cmdlines(const char *tracing_dir, struct tep_handle *tep)
+{
+
+	if (!tracing_dir)
+		tracing_dir = tracefs_tracing_dir();
+
+	if (!tracing_dir)
+		return -1;
+
+	return load_saved_cmdlines(tracing_dir, tep, true);
+}
+
+static int fill_local_events_system(const char *tracing_dir,
+				    struct tep_handle *tep,
+				    const char * const *sys_names,
+				    int *parsing_failures)
+{
+	char **systems = NULL;
+	int ret;
+	int i;
+
+	if (!tracing_dir)
+		tracing_dir = tracefs_tracing_dir();
+	if (!tracing_dir)
+		return -1;
+
+	systems = tracefs_event_systems(tracing_dir);
+	if (!systems)
+		return -1;
+
+	ret = read_header(tep, tracing_dir);
+	if (ret < 0) {
+		ret = -1;
+		goto out;
+	}
+
+	if (parsing_failures)
+		*parsing_failures = 0;
+
+	for (i = 0; systems[i]; i++) {
+		if (sys_names && !contains(systems[i], sys_names))
+			continue;
+		ret = trace_load_events(tep, tracing_dir, systems[i]);
+		if (ret && parsing_failures)
+			(*parsing_failures)++;
+	}
+
+	/* Include ftrace, as it is excluded for not having "enable" file */
+	if (!sys_names || contains("ftrace", sys_names))
+		trace_load_events(tep, tracing_dir, "ftrace");
+
+	load_mappings(tracing_dir, tep);
+
+	/* always succeed because parsing failures are not critical */
+	ret = 0;
+out:
+	tracefs_list_free(systems);
+	return ret;
+}
+
+static void set_tep_cpus(const char *tracing_dir, struct tep_handle *tep)
+{
+	struct stat st;
+	char path[PATH_MAX];
+	int cpus = sysconf(_SC_NPROCESSORS_CONF);
+	int max_cpu = 0;
+	int ret;
+	int i;
+
+	if (!tracing_dir)
+		tracing_dir = tracefs_tracing_dir();
+
+	/*
+	 * Paranoid: in case sysconf() above does not work.
+	 * And we also only care about the number of tracing
+	 * buffers that exist. If cpus is 32, but the top half
+	 * is offline, there may only be 16 tracing buffers.
+	 * That's what we want to know.
+	 */
+	for (i = 0; !cpus || i < cpus; i++) {
+		snprintf(path, PATH_MAX, "%s/per_cpu/cpu%d", tracing_dir, i);
+		ret = stat(path, &st);
+		if (!ret && S_ISDIR(st.st_mode))
+			max_cpu = i + 1;
+		else if (i >= cpus)
+			break;
+	}
+
+	if (!max_cpu)
+		max_cpu = cpus;
+
+	tep_set_cpus(tep, max_cpu);
+}
+
+/**
+ * tracefs_local_events_system - create a tep from the events of the specified subsystem.
+ *
+ * @tracing_dir: The directory that contains the events.
+ * @sys_name: Array of system names, to load the events from.
+ * The last element from the array must be NULL
+ *
+ * Returns a tep structure that contains the tep local to
+ * the system.
+ */
+struct tep_handle *tracefs_local_events_system(const char *tracing_dir,
+					       const char * const *sys_names)
+{
+	struct tep_handle *tep = NULL;
+
+	tep = tep_alloc();
+	if (!tep)
+		return NULL;
+
+	if (fill_local_events_system(tracing_dir, tep, sys_names, NULL)) {
+		tep_free(tep);
+		tep = NULL;
+	}
+
+	set_tep_cpus(tracing_dir, tep);
+
+	/* Set the long size for this tep handle */
+	tep_set_long_size(tep, tep_get_header_page_size(tep));
+
+	return tep;
+}
+
+/**
+ * tracefs_local_events - create a tep from the events on system
+ * @tracing_dir: The directory that contains the events.
+ *
+ * Returns a tep structure that contains the teps local to
+ * the system.
+ */
+struct tep_handle *tracefs_local_events(const char *tracing_dir)
+{
+	return tracefs_local_events_system(tracing_dir, NULL);
+}
+
+/**
+ * tracefs_fill_local_events - Fill a tep with the events on system
+ * @tracing_dir: The directory that contains the events.
+ * @tep: Allocated tep handler which will be filled
+ * @parsing_failures: return number of failures while parsing the event files
+ *
+ * Returns whether the operation succeeded
+ */
+int tracefs_fill_local_events(const char *tracing_dir,
+			       struct tep_handle *tep, int *parsing_failures)
+{
+	return fill_local_events_system(tracing_dir, tep,
+					NULL, parsing_failures);
+}
+
+static bool match(const char *str, regex_t *re)
+{
+	return regexec(re, str, 0, NULL, 0) == 0;
+}
+
+enum event_state {
+	STATE_INIT,
+	STATE_ENABLED,
+	STATE_DISABLED,
+	STATE_MIXED,
+	STATE_ERROR,
+};
+
+static int read_event_state(struct tracefs_instance *instance, const char *file,
+			    enum event_state *state)
+{
+	char *val;
+	int ret = 0;
+
+	if (*state == STATE_ERROR)
+		return -1;
+
+	val = tracefs_instance_file_read(instance, file, NULL);
+	if (!val)
+		return -1;
+
+	switch (val[0]) {
+	case '0':
+		switch (*state) {
+		case STATE_INIT:
+			*state = STATE_DISABLED;
+			break;
+		case STATE_ENABLED:
+			*state = STATE_MIXED;
+			break;
+		default:
+			break;
+		}
+		break;
+	case '1':
+		switch (*state) {
+		case STATE_INIT:
+			*state = STATE_ENABLED;
+			break;
+		case STATE_DISABLED:
+			*state = STATE_MIXED;
+			break;
+		default:
+			break;
+		}
+		break;
+	case 'X':
+		*state = STATE_MIXED;
+		break;
+	default:
+		*state = TRACEFS_ERROR;
+		ret = -1;
+		break;
+	}
+	free(val);
+
+	return ret;
+}
+
+static int enable_disable_event(struct tracefs_instance *instance,
+				const char *system, const char *event,
+				bool enable, enum event_state *state)
+{
+	const char *str = enable ? "1" : "0";
+	char *system_event;
+	int ret;
+
+	ret = asprintf(&system_event, "events/%s/%s/enable", system, event);
+	if (ret < 0)
+		return ret;
+
+	if (state)
+		ret = read_event_state(instance, system_event, state);
+	else
+		ret = tracefs_instance_file_write(instance, system_event, str);
+	free(system_event);
+
+	return ret;
+}
+
+static int enable_disable_system(struct tracefs_instance *instance,
+				 const char *system, bool enable,
+				 enum event_state *state)
+{
+	const char *str = enable ? "1" : "0";
+	char *system_path;
+	int ret;
+
+	ret = asprintf(&system_path, "events/%s/enable", system);
+	if (ret < 0)
+		return ret;
+
+	if (state)
+		ret = read_event_state(instance, system_path, state);
+	else
+		ret = tracefs_instance_file_write(instance, system_path, str);
+	free(system_path);
+
+	return ret;
+}
+
+static int enable_disable_all(struct tracefs_instance *instance,
+			      bool enable)
+{
+	const char *str = enable ? "1" : "0";
+	int ret;
+
+	ret = tracefs_instance_file_write(instance, "events/enable", str);
+	return ret < 0 ? ret : 0;
+}
+
+static int make_regex(regex_t *re, const char *match)
+{
+	int len = strlen(match);
+	char str[len + 3];
+	char *p = &str[0];
+
+	if (!len || match[0] != '^')
+		*(p++) = '^';
+
+	strcpy(p, match);
+	p += len;
+
+	if (!len || match[len-1] != '$')
+		*(p++) = '$';
+
+	*p = '\0';
+
+	return regcomp(re, str, REG_ICASE|REG_NOSUB);
+}
+
+static int event_enable_disable(struct tracefs_instance *instance,
+				const char *system, const char *event,
+				bool enable, enum event_state *state)
+{
+	regex_t system_re, event_re;
+	char **systems;
+	char **events = NULL;
+	int ret = -1;
+	int s, e;
+
+	/* Handle all events first */
+	if (!system && !event)
+		return enable_disable_all(instance, enable);
+
+	systems = tracefs_event_systems(NULL);
+	if (!systems)
+		goto out_free;
+
+	if (system) {
+		ret = make_regex(&system_re, system);
+		if (ret < 0)
+			goto out_free;
+	}
+	if (event) {
+		ret = make_regex(&event_re, event);
+		if (ret < 0) {
+			if (system)
+				regfree(&system_re);
+			goto out_free;
+		}
+	}
+
+	ret = -1;
+	for (s = 0; systems[s]; s++) {
+		if (system && !match(systems[s], &system_re))
+			continue;
+
+		/* Check for the short cut first */
+		if (!event) {
+			ret = enable_disable_system(instance, systems[s], enable, state);
+			if (ret < 0)
+				break;
+			ret = 0;
+			continue;
+		}
+
+		events = tracefs_system_events(NULL, systems[s]);
+		if (!events)
+			continue; /* Error? */
+
+		for (e = 0; events[e]; e++) {
+			if (!match(events[e], &event_re))
+				continue;
+			ret = enable_disable_event(instance, systems[s],
+						   events[e], enable, state);
+			if (ret < 0)
+				break;
+			ret = 0;
+		}
+		tracefs_list_free(events);
+		events = NULL;
+	}
+	if (system)
+		regfree(&system_re);
+	if (event)
+		regfree(&event_re);
+
+ out_free:
+	tracefs_list_free(systems);
+	tracefs_list_free(events);
+	return ret;
+}
+
+/**
+ * tracefs_event_enable - enable specified events
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @system: A regex of a system (NULL to match all systems)
+ * @event: A regex of the event in the system (NULL to match all events)
+ *
+ * This will enable events that match the @system and @event.
+ * If both @system and @event are NULL, then it will enable all events.
+ * If @system is NULL, it will look at all systems for matching events
+ * to @event.
+ * If @event is NULL, then it will enable all events in the systems
+ * that match @system.
+ *
+ * Returns 0 on success, and -1 if it encountered an error,
+ * or if no events matched. If no events matched, then -1 is set
+ * but errno will not be.
+ */
+int tracefs_event_enable(struct tracefs_instance *instance,
+			 const char *system, const char *event)
+{
+	return event_enable_disable(instance, system, event, true, NULL);
+}
+
+int tracefs_event_disable(struct tracefs_instance *instance,
+			  const char *system, const char *event)
+{
+	return event_enable_disable(instance, system, event, false, NULL);
+}
+
+/**
+ * tracefs_event_is_enabled - return if the event is enabled or not
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @system: The name of the system to check
+ * @event: The name of the event to check
+ *
+ * Checks is an event or multiple events are enabled.
+ *
+ * If @system is NULL, then it will check all the systems where @event is
+ * a match.
+ *
+ * If @event is NULL, then it will check all events where @system is a match.
+ *
+ * If both @system and @event are NULL, then it will check all events
+ *
+ * Returns TRACEFS_ALL_ENABLED if all matching are enabled.
+ * Returns TRACEFS_SOME_ENABLED if some are enabled and some are not
+ * Returns TRACEFS_ALL_DISABLED if none of the events are enabled.
+ * Returns TRACEFS_ERROR if there is an error reading the events.
+ */
+enum tracefs_enable_state
+tracefs_event_is_enabled(struct tracefs_instance *instance,
+			 const char *system, const char *event)
+{
+	enum event_state state = STATE_INIT;
+	int ret;
+
+	ret = event_enable_disable(instance, system, event, false, &state);
+
+	if (ret < 0)
+		return TRACEFS_ERROR;
+
+	switch (state) {
+	case STATE_ENABLED:
+		return TRACEFS_ALL_ENABLED;
+	case STATE_DISABLED:
+		return TRACEFS_ALL_DISABLED;
+	case STATE_MIXED:
+		return TRACEFS_SOME_ENABLED;
+	default:
+		return TRACEFS_ERROR;
+	}
+}
diff --git a/src/tracefs-filter.c b/src/tracefs-filter.c
new file mode 100644
index 0000000..a3dd77b
--- /dev/null
+++ b/src/tracefs-filter.c
@@ -0,0 +1,807 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
+ *
+ * Updates:
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <trace-seq.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+enum {
+	S_START,
+	S_COMPARE,
+	S_NOT,
+	S_CONJUNCTION,
+	S_OPEN_PAREN,
+	S_CLOSE_PAREN,
+};
+
+static const struct tep_format_field common_timestamp = {
+	.type			= "u64",
+	.name			= "common_timestamp",
+	.size			= 8,
+};
+
+static const struct tep_format_field common_timestamp_usecs = {
+	.type			= "u64",
+	.name			= "common_timestamp.usecs",
+	.size			= 8,
+};
+
+static const struct tep_format_field common_comm = {
+	.type			= "char *",
+	.name			= "common_comm",
+	.size			= 16,
+};
+
+/*
+ * This also must be able to accept fields that are OK via the histograms,
+ * such as common_timestamp.
+ */
+static const struct tep_format_field *get_event_field(struct tep_event *event,
+					 const char *field_name)
+{
+	const struct tep_format_field *field;
+
+	if (!strcmp(field_name, TRACEFS_TIMESTAMP))
+		return &common_timestamp;
+
+	if (!strcmp(field_name, TRACEFS_TIMESTAMP_USECS))
+		return &common_timestamp_usecs;
+
+	field = tep_find_any_field(event, field_name);
+	if (!field && (!strcmp(field_name, "COMM") || !strcmp(field_name, "comm")))
+		return &common_comm;
+
+	return field;
+}
+
+__hidden bool
+trace_verify_event_field(struct tep_event *event,
+			 const char *field_name,
+			 const struct tep_format_field **ptr_field)
+{
+	const struct tep_format_field *field;
+
+	field = get_event_field(event, field_name);
+	if (!field) {
+		errno = ENODEV;
+		return false;
+	}
+
+	if (ptr_field)
+		*ptr_field = field;
+
+	return true;
+}
+
+__hidden int trace_test_state(int state)
+{
+	switch (state) {
+	case S_START:
+	case S_CLOSE_PAREN:
+	case S_COMPARE:
+		return 0;
+	}
+
+	errno = EBADE;
+	return -1;
+}
+
+static int append_filter(char **filter, unsigned int *state,
+			 unsigned int *open_parens,
+			 struct tep_event *event,
+			 enum tracefs_filter type,
+			 const char *field_name,
+			 enum tracefs_compare compare,
+			 const char *val)
+{
+	const struct tep_format_field *field;
+	bool is_string;
+	char *conj = "||";
+	char *tmp;
+
+	switch (type) {
+	case TRACEFS_FILTER_COMPARE:
+		switch (*state) {
+		case S_START:
+		case S_OPEN_PAREN:
+		case S_CONJUNCTION:
+		case S_NOT:
+			break;
+		default:
+			goto inval;
+		}
+		break;
+
+	case TRACEFS_FILTER_AND:
+		conj = "&&";
+		/* Fall through */
+	case TRACEFS_FILTER_OR:
+		switch (*state) {
+		case S_COMPARE:
+		case S_CLOSE_PAREN:
+			break;
+		default:
+			goto inval;
+		}
+		/* Don't lose old filter on failure */
+		tmp = strdup(*filter);
+		if (!tmp)
+			return -1;
+		tmp = append_string(tmp, NULL, conj);
+		if (!tmp)
+			return -1;
+		free(*filter);
+		*filter = tmp;
+		*state = S_CONJUNCTION;
+		return 0;
+
+	case TRACEFS_FILTER_NOT:
+		switch (*state) {
+		case S_START:
+		case S_OPEN_PAREN:
+		case S_CONJUNCTION:
+		case S_NOT:
+			break;
+		default:
+			goto inval;
+		}
+		if (*filter) {
+			tmp = strdup(*filter);
+			tmp = append_string(tmp, NULL, "!");
+		} else {
+			tmp = strdup("!");
+		}
+		if (!tmp)
+			return -1;
+		free(*filter);
+		*filter = tmp;
+		*state = S_NOT;
+		return 0;
+
+	case TRACEFS_FILTER_OPEN_PAREN:
+		switch (*state) {
+		case S_START:
+		case S_OPEN_PAREN:
+		case S_NOT:
+		case S_CONJUNCTION:
+			break;
+		default:
+			goto inval;
+		}
+		if (*filter) {
+			tmp = strdup(*filter);
+			tmp = append_string(tmp, NULL, "(");
+		} else {
+			tmp = strdup("(");
+		}
+		if (!tmp)
+			return -1;
+		free(*filter);
+		*filter = tmp;
+		*state = S_OPEN_PAREN;
+		(*open_parens)++;
+		return 0;
+
+	case TRACEFS_FILTER_CLOSE_PAREN:
+		switch (*state) {
+		case S_CLOSE_PAREN:
+		case S_COMPARE:
+			break;
+		default:
+			goto inval;
+		}
+		if (!*open_parens)
+			goto inval;
+
+		tmp = strdup(*filter);
+		if (!tmp)
+			return -1;
+		tmp = append_string(tmp, NULL, ")");
+		if (!tmp)
+			return -1;
+		free(*filter);
+		*filter = tmp;
+		*state = S_CLOSE_PAREN;
+		(*open_parens)--;
+		return 0;
+	}
+
+	if (!field_name || !val)
+		goto inval;
+
+	if (!trace_verify_event_field(event, field_name, &field))
+		return -1;
+
+	is_string = field->flags & TEP_FIELD_IS_STRING;
+
+	if (!is_string && (field->flags & TEP_FIELD_IS_ARRAY))
+		goto inval;
+
+	if (*filter) {
+		tmp = strdup(*filter);
+		if (!tmp)
+			return -1;
+		tmp = append_string(tmp, NULL, field_name);
+	} else {
+		tmp = strdup(field_name);
+	}
+
+	switch (compare) {
+	case TRACEFS_COMPARE_EQ: tmp = append_string(tmp, NULL, " == "); break;
+	case TRACEFS_COMPARE_NE: tmp = append_string(tmp, NULL, " != "); break;
+	case TRACEFS_COMPARE_RE:
+		if (!is_string)
+			goto inval;
+		tmp = append_string(tmp, NULL, "~");
+		break;
+	default:
+		if (is_string)
+			goto inval;
+	}
+
+	switch (compare) {
+	case TRACEFS_COMPARE_GT: tmp = append_string(tmp, NULL, " > "); break;
+	case TRACEFS_COMPARE_GE: tmp = append_string(tmp, NULL, " >= "); break;
+	case TRACEFS_COMPARE_LT: tmp = append_string(tmp, NULL, " < "); break;
+	case TRACEFS_COMPARE_LE: tmp = append_string(tmp, NULL, " <= "); break;
+	case TRACEFS_COMPARE_AND: tmp = append_string(tmp, NULL, " & "); break;
+	default: break;
+	}
+
+	tmp = append_string(tmp, NULL, val);
+
+	if (!tmp)
+		return -1;
+
+	free(*filter);
+	*filter = tmp;
+	*state = S_COMPARE;
+
+	return 0;
+inval:
+	errno = EINVAL;
+	return -1;
+}
+
+static int count_parens(char *filter, unsigned int *state)
+{
+	bool backslash = false;
+	int quote = 0;
+	int open = 0;
+	int i;
+
+	if (!filter)
+		return 0;
+
+	for (i = 0; filter[i]; i++) {
+		if (quote) {
+			if (backslash)
+				backslash = false;
+			else if (filter[i] == '\\')
+				backslash = true;
+			else if (quote == filter[i])
+				quote = 0;
+			continue;
+		}
+
+		switch (filter[i]) {
+		case '(':
+			*state = S_OPEN_PAREN;
+			open++;
+			break;
+		case ')':
+			*state = S_CLOSE_PAREN;
+			open--;
+			break;
+		case '\'':
+		case '"':
+			*state = S_COMPARE;
+			quote = filter[i];
+			break;
+		case '!':
+			switch (filter[i+1]) {
+			case '=':
+			case '~':
+				*state = S_COMPARE;
+				i++;
+				break;
+			default:
+				*state = S_NOT;
+			}
+			break;
+		case '&':
+		case '|':
+			if (filter[i] == filter[i+1]) {
+				*state = S_CONJUNCTION;
+				i++;
+				break;
+			}
+			/* Fall through */
+		case '0' ... '9':
+		case 'a' ... 'z':
+		case 'A' ... 'Z':
+		case '_': case '+': case '-': case '*': case '/':
+			*state = S_COMPARE;
+			break;
+		}
+	}
+	return open;
+}
+
+__hidden int trace_append_filter(char **filter, unsigned int *state,
+			 unsigned int *open_parens,
+			 struct tep_event *event,
+			 enum tracefs_filter type,
+			 const char *field_name,
+			 enum tracefs_compare compare,
+			 const char *val)
+{
+	return append_filter(filter, state, open_parens, event, type,
+			     field_name, compare, val);
+}
+
+/**
+ * tracefs_filter_string_append - create or append a filter for an event
+ * @event: tep_event to create / append a filter for
+ * @filter: Pointer to string to append to (pointer to NULL to create)
+ * @type: The type of element to add to the filter
+ * @field: For @type == TRACEFS_FILTER_COMPARE, the field to compare
+ * @compare: For @type == TRACEFS_FILTER_COMPARE, how to compare @field to @val
+ * @val: For @type == TRACEFS_FILTER_COMPARE, what value @field is to be
+ *
+ * This will put together a filter string for the starting event
+ * of @synth. It check to make sure that what is added is correct compared
+ * to the filter that is already built.
+ *
+ * @type can be:
+ *     TRACEFS_FILTER_COMPARE:        See below
+ *     TRACEFS_FILTER_AND:            Append "&&" to the filter
+ *     TRACEFS_FILTER_OR:             Append "||" to the filter
+ *     TRACEFS_FILTER_NOT:            Append "!" to the filter
+ *     TRACEFS_FILTER_OPEN_PAREN:     Append "(" to the filter
+ *     TRACEFS_FILTER_CLOSE_PAREN:    Append ")" to the filter
+ *
+ * For all types except TRACEFS_FILTER_COMPARE, the @field, @compare,
+ * and @val are ignored.
+ *
+ * For @type == TRACEFS_FILTER_COMPARE.
+ *
+ *  @field is the name of the field for the start event to compare.
+ *         If it is not a field for the start event, this return an
+ *         error.
+ *
+ *  @compare can be one of:
+ *     TRACEFS_COMPARE_EQ:       Test @field == @val
+ *     TRACEFS_COMPARE_NE:       Test @field != @val
+ *     TRACEFS_COMPARE_GT:       Test @field > @val
+ *     TRACEFS_COMPARE_GE:       Test @field >= @val
+ *     TRACEFS_COMPARE_LT:       Test @field < @val
+ *     TRACEFS_COMPARE_LE:       Test @field <= @val
+ *     TRACEFS_COMPARE_RE:       Test @field ~ @val
+ *     TRACEFS_COMPARE_AND:      Test @field & @val
+ *
+ * If the @field is of type string, and @compare is not
+ *   TRACEFS_COMPARE_EQ, TRACEFS_COMPARE_NE or TRACEFS_COMPARE_RE,
+ *   then this will return an error.
+ *
+ * Various other checks are made, for instance, if more CLOSE_PARENs
+ * are added than existing OPEN_PARENs. Or if AND is added after an
+ * OPEN_PAREN or another AND or an OR or a NOT.
+ *
+ * Returns 0 on success and -1 on failure.
+ */
+int tracefs_filter_string_append(struct tep_event *event, char **filter,
+				 enum tracefs_filter type,
+				 const char *field, enum tracefs_compare compare,
+				 const char *val)
+{
+	unsigned int open_parens;
+	unsigned int state = 0;
+	char *str = NULL;
+	int open;
+	int ret;
+
+	if (!filter) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	open = count_parens(*filter, &state);
+	if (open < 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (*filter) {
+		/* append_filter() will free filter on error */
+		str = strdup(*filter);
+		if (!str)
+			return -1;
+	}
+	open_parens = open;
+
+	ret = append_filter(&str, &state, &open_parens,
+			    event, type, field, compare, val);
+	if (!ret) {
+		free(*filter);
+		*filter = str;
+	}
+
+	return ret;
+}
+
+static int error_msg(char **err, char *str,
+		     const char *filter, int i, const char *msg)
+{
+	char ws[i+2];
+	char *errmsg;
+
+	free(str);
+
+	/* msg is NULL for parsing append_filter failing */
+	if (!msg) {
+		switch(errno) {
+		case ENODEV:
+			msg = "field not valid";
+			break;
+		default:
+			msg = "Invalid filter";
+
+		}
+	} else
+		errno = EINVAL;
+
+	if (!err)
+		return -1;
+
+	if (!filter) {
+		*err = strdup(msg);
+		return -1;
+	}
+
+	memset(ws, ' ', i);
+	ws[i] = '^';
+	ws[i+1] = '\0';
+
+	errmsg = strdup(filter);
+	errmsg = append_string(errmsg, "\n", ws);
+	errmsg = append_string(errmsg, "\n", msg);
+	errmsg = append_string(errmsg, NULL, "\n");
+
+	*err = errmsg;
+	return -1;
+}
+
+static int get_field_end(const char *filter, int i, int *end)
+{
+	int start_i = i;
+
+	for (; filter[i]; i++) {
+		switch(filter[i]) {
+		case '0' ... '9':
+			if (i == start_i)
+				return 0;
+			/* Fall through */
+		case 'a' ... 'z':
+		case 'A' ... 'Z':
+		case '_':
+			continue;
+		default:
+			*end = i;
+			return i - start_i;
+		}
+	}
+	*end = i;
+	return i - start_i;
+}
+
+static int get_compare(const char *filter, int i, enum tracefs_compare *cmp)
+{
+	int start_i = i;
+
+	for (; filter[i]; i++) {
+		if (!isspace(filter[i]))
+			break;
+	}
+
+	switch(filter[i]) {
+	case '=':
+		if (filter[i+1] != '=')
+			goto err;
+		*cmp = TRACEFS_COMPARE_EQ;
+		i++;
+		break;
+	case '!':
+		if (filter[i+1] == '=') {
+			*cmp = TRACEFS_COMPARE_NE;
+			i++;
+			break;
+		}
+		if (filter[i+1] == '~') {
+			/* todo! */
+		}
+		goto err;
+	case '>':
+		if (filter[i+1] == '=') {
+			*cmp = TRACEFS_COMPARE_GE;
+			i++;
+			break;
+		}
+		*cmp = TRACEFS_COMPARE_GT;
+		break;
+	case '<':
+		if (filter[i+1] == '=') {
+			*cmp = TRACEFS_COMPARE_LE;
+			i++;
+			break;
+		}
+		*cmp = TRACEFS_COMPARE_LT;
+		break;
+	case '~':
+		*cmp = TRACEFS_COMPARE_RE;
+		break;
+	case '&':
+		*cmp = TRACEFS_COMPARE_AND;
+		break;
+	default:
+		goto err;
+	}
+	i++;
+
+	for (; filter[i]; i++) {
+		if (!isspace(filter[i]))
+			break;
+	}
+	return i - start_i;
+ err:
+	return start_i - i; /* negative or zero */
+}
+
+static int get_val_end(const char *filter, int i, int *end)
+{
+	bool backslash = false;
+	int start_i = i;
+	int quote;
+
+	switch (filter[i]) {
+	case '0':
+		i++;
+		if (tolower(filter[i+1]) != 'x' &&
+		    !isdigit(filter[i+1]))
+			break;
+		/* fall through */
+	case '1' ... '9':
+		switch (tolower(filter[i])) {
+		case 'x':
+			for (i++; filter[i]; i++) {
+				if (!isxdigit(filter[i]))
+					break;
+			}
+			break;
+		case '0':
+			for (i++; filter[i]; i++) {
+				if (filter[i] < '0' ||
+				    filter[i] > '7')
+					break;
+			}
+			break;
+		default:
+			for (i++; filter[i]; i++) {
+				if (!isdigit(filter[i]))
+					break;
+			}
+			break;
+		}
+		break;
+	case '"':
+	case '\'':
+		quote = filter[i];
+		for (i++; filter[i]; i++) {
+			if (backslash) {
+				backslash = false;
+				continue;
+			}
+			switch (filter[i]) {
+			case '\\':
+				backslash = true;
+				continue;
+			case '"':
+			case '\'':
+				if (filter[i] == quote)
+					break;
+				continue;
+			default:
+				continue;
+			}
+			break;
+		}
+		if (filter[i])
+			i++;
+		break;
+	default:
+		break;
+	}
+
+	*end = i;
+	return i - start_i;
+}
+
+/**
+ * tracefs_filter_string_verify - verify a given filter works for an event
+ * @event: The event to test the given filter for
+ * @filter: The filter to test
+ * @err: Error message for syntax errors (NULL to ignore)
+ *
+ * Parse the @filter to verify that it is valid for the given @event.
+ *
+ * Returns 0 on succes and -1 on error, and except for memory allocation
+ * errors, @err will be allocated with an error message. It must
+ * be freed with free().
+ */
+int tracefs_filter_string_verify(struct tep_event *event, const char *filter,
+				 char **err)
+{
+	enum tracefs_filter filter_type;
+	enum tracefs_compare compare;
+	char *str = NULL;
+	char buf[(filter ? strlen(filter) : 0) + 1];
+	char *field;
+	char *val;
+	unsigned int state = 0;
+	unsigned int open = 0;
+	int len;
+	int end;
+	int n;
+	int i;
+
+	if (!filter)
+		return error_msg(err, str, NULL, 0, "No filter given");
+
+	len = strlen(filter);
+
+	for (i = 0; i < len; i++) {
+		field = NULL;
+		val = NULL;
+		compare = 0;
+
+		switch (filter[i]) {
+		case '(':
+			filter_type = TRACEFS_FILTER_OPEN_PAREN;
+			break;
+		case ')':
+			filter_type = TRACEFS_FILTER_CLOSE_PAREN;
+			break;
+		case '!':
+			filter_type = TRACEFS_FILTER_NOT;
+			break;
+		case '&':
+		case '|':
+
+			if (filter[i] == filter[i+1]) {
+				i++;
+				if (filter[i] == '&')
+					filter_type = TRACEFS_FILTER_AND;
+				else
+					filter_type = TRACEFS_FILTER_OR;
+				break;
+			}
+			if (filter[i] == '|')
+				return error_msg(err, str, filter, i,
+						 "Invalid op");
+
+			return error_msg(err, str, filter, i,
+					 "Invalid location for '&'");
+		default:
+			if (isspace(filter[i]))
+				continue;
+
+			field = buf;
+
+			n = get_field_end(filter, i, &end);
+			if (!n)
+				return error_msg(err, str, filter, i,
+						 "Invalid field name");
+
+			strncpy(field, filter+i, n);
+
+			i += n;
+			field[n++] = '\0';
+
+			val = field + n;
+
+			n = get_compare(filter, i, &compare);
+			if (n <= 0)
+				return error_msg(err, str, filter, i - n,
+						 "Invalid compare");
+
+			i += n;
+			get_val_end(filter, i, &end);
+			n = end - i;
+			if (!n)
+				return error_msg(err, str, filter, i,
+						 "Invalid value");
+			strncpy(val, filter + i, n);
+			val[n] = '\0';
+			i += n - 1;
+
+			filter_type = TRACEFS_FILTER_COMPARE;
+			break;
+		}
+		n = append_filter(&str, &state, &open,
+				    event, filter_type, field, compare, val);
+
+		if (n < 0)
+			return error_msg(err, str, filter, i, NULL);
+	}
+
+	if (open)
+		return error_msg(err, str, filter, i,
+				 "Not enough closed parenthesis");
+	switch (state) {
+	case S_COMPARE:
+	case S_CLOSE_PAREN:
+		break;
+	default:
+		return error_msg(err, str, filter, i,
+				 "Unfinished filter");
+	}
+
+	free(str);
+	return 0;
+}
+
+/**
+ * tracefs_event_filter_apply - apply given filter on event in given instance
+ * @instance: The instance in which the filter will be applied (NULL for toplevel).
+ * @event: The event to apply the filter on.
+ * @filter: The filter to apply.
+ *
+ * Apply the @filter to given @event in givem @instance. The @filter string
+ * should be created with tracefs_filter_string_append().
+ *
+ * Returns 0 on succes and -1 on error.
+ */
+int tracefs_event_filter_apply(struct tracefs_instance *instance,
+			       struct tep_event *event, const char *filter)
+{
+	return tracefs_event_file_write(instance, event->system, event->name,
+					"filter", filter);
+}
+
+/**
+ * tracefs_event_filter_clear - clear the filter on event in given instance
+ * @instance: The instance in which the filter will be applied (NULL for toplevel).
+ * @event: The event to apply the filter on.
+ *
+ * Returns 0 on succes and -1 on error.
+ */
+int tracefs_event_filter_clear(struct tracefs_instance *instance,
+			       struct tep_event *event)
+{
+	return tracefs_event_file_write(instance, event->system, event->name,
+					"filter", "0");
+}
+
+/** Deprecated **/
+int tracefs_event_append_filter(struct tep_event *event, char **filter,
+				enum tracefs_filter type,
+				const char *field, enum tracefs_compare compare,
+				const char *val)
+{
+	return tracefs_filter_string_append(event, filter, type, field,
+					    compare, val);
+}
+int tracefs_event_verify_filter(struct tep_event *event, const char *filter,
+				char **err)
+{
+	return tracefs_filter_string_verify(event, filter, err);
+}
diff --git a/src/tracefs-hist.c b/src/tracefs-hist.c
new file mode 100644
index 0000000..fb6231e
--- /dev/null
+++ b/src/tracefs-hist.c
@@ -0,0 +1,2401 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
+ *
+ * Updates:
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+#define HIST_FILE "hist"
+
+#define ASCENDING ".ascending"
+#define DESCENDING ".descending"
+
+#define SYNTHETIC_GROUP "synthetic"
+
+struct tracefs_hist {
+	struct tep_handle	*tep;
+	struct tep_event	*event;
+	char			*system;
+	char			*event_name;
+	char			*name;
+	char			**keys;
+	char			**values;
+	char			**sort;
+	char			*filter;
+	int			size;
+	unsigned int		filter_parens;
+	unsigned int		filter_state;
+};
+
+/*
+ * tracefs_hist_get_name - get the name of the histogram
+ * @hist: The histogram to get the name for
+ *
+ * Returns name string owned by @hist on success, or NULL on error.
+ */
+const char *tracefs_hist_get_name(struct tracefs_hist *hist)
+{
+	return hist ? hist->name : NULL;
+}
+
+/*
+ * tracefs_hist_get_event - get the event name of the histogram
+ * @hist: The histogram to get the event name for
+ *
+ * Returns event name string owned by @hist on success, or NULL on error.
+ */
+const char *tracefs_hist_get_event(struct tracefs_hist *hist)
+{
+	return hist ? hist->event_name : NULL;
+}
+
+/*
+ * tracefs_hist_get_system - get the system name of the histogram
+ * @hist: The histogram to get the system name for
+ *
+ * Returns system name string owned by @hist on success, or NULL on error.
+ */
+const char *tracefs_hist_get_system(struct tracefs_hist *hist)
+{
+	return hist ? hist->system : NULL;
+}
+
+static void add_list(struct trace_seq *seq, const char *start,
+		     char **list)
+{
+	int i;
+
+	trace_seq_puts(seq, start);
+	for (i = 0; list[i]; i++) {
+		if (i)
+			trace_seq_putc(seq, ',');
+		trace_seq_puts(seq, list[i]);
+	}
+}
+
+static void add_hist_commands(struct trace_seq *seq, struct tracefs_hist *hist,
+			     enum tracefs_hist_command command)
+{
+	if (command == TRACEFS_HIST_CMD_DESTROY)
+		trace_seq_putc(seq, '!');
+
+	add_list(seq, "hist:keys=", hist->keys);
+
+	if (hist->values)
+		add_list(seq, ":vals=", hist->values);
+
+	if (hist->sort)
+		add_list(seq, ":sort=", hist->sort);
+
+	if (hist->size)
+		trace_seq_printf(seq, ":size=%d", hist->size);
+
+	switch(command) {
+	case TRACEFS_HIST_CMD_START: break;
+	case TRACEFS_HIST_CMD_PAUSE: trace_seq_puts(seq, ":pause"); break;
+	case TRACEFS_HIST_CMD_CONT: trace_seq_puts(seq, ":cont"); break;
+	case TRACEFS_HIST_CMD_CLEAR: trace_seq_puts(seq, ":clear"); break;
+	default: break;
+	}
+
+	if (hist->name)
+		trace_seq_printf(seq, ":name=%s", hist->name);
+
+	if (hist->filter)
+		trace_seq_printf(seq, " if %s", hist->filter);
+
+}
+
+/*
+ * trace_hist_echo_cmd - show how to start the histogram
+ * @seq: A trace_seq to store the commands to create
+ * @hist: The histogram to write into the trigger file
+ * @command: If not zero, can pause, continue or clear the histogram
+ *
+ * This shows the echo commands to create the histogram for an event
+ * with the given fields.
+ *
+ * Returns 0 on succes -1 on error.
+ */
+int
+tracefs_hist_echo_cmd(struct trace_seq *seq, struct tracefs_instance *instance,
+		      struct tracefs_hist *hist,
+		      enum tracefs_hist_command command)
+{
+	const char *system = hist->system;
+	const char *event = hist->event_name;
+	char *path;
+
+	if (!hist->keys) {
+		errno = -EINVAL;
+		return -1;
+	}
+
+	path = tracefs_event_get_file(instance, system, event, "trigger");
+	if (!path)
+		return -1;
+
+	trace_seq_puts(seq, "echo '");
+
+	add_hist_commands(seq, hist, command);
+
+	trace_seq_printf(seq, "' > %s\n", path);
+
+	tracefs_put_tracing_file(path);
+
+	return 0;
+}
+
+/*
+ * tracefs_hist_command - Create, start, pause, destroy a histogram for an event
+ * @instance: The instance the histogram will be in (NULL for toplevel)
+ * @hist: The histogram to write into the trigger file
+ * @command: Command to perform on a histogram.
+ *
+ * Creates, pause, continue, clears, or destroys a histogram.
+ *
+ * Returns 0 on succes -1 on error.
+ */
+int tracefs_hist_command(struct tracefs_instance *instance,
+			 struct tracefs_hist *hist,
+			 enum tracefs_hist_command command)
+{
+	const char *system = hist->system;
+	const char *event = hist->event_name;
+	struct trace_seq seq;
+	int ret;
+
+	if (!tracefs_event_file_exists(instance, system, event, HIST_FILE))
+		return -1;
+
+	errno = -EINVAL;
+	if (!hist->keys)
+		return -1;
+
+	trace_seq_init(&seq);
+
+	add_hist_commands(&seq, hist, command);
+
+	trace_seq_putc(&seq, '\n');
+	trace_seq_terminate(&seq);
+
+	ret = -1;
+	if (seq.state == TRACE_SEQ__GOOD)
+		ret = tracefs_event_file_append(instance, system, event,
+						"trigger", seq.buffer);
+
+	trace_seq_destroy(&seq);
+
+	return ret < 0 ? -1 : 0;
+}
+
+/**
+ * tracefs_hist_free - free a tracefs_hist element
+ * @hist: The histogram to free
+ */
+void tracefs_hist_free(struct tracefs_hist *hist)
+{
+	if (!hist)
+		return;
+
+	tep_unref(hist->tep);
+	free(hist->system);
+	free(hist->event_name);
+	free(hist->name);
+	free(hist->filter);
+	tracefs_list_free(hist->keys);
+	tracefs_list_free(hist->values);
+	tracefs_list_free(hist->sort);
+	free(hist);
+}
+
+/**
+ * tracefs_hist_alloc - Initialize one-dimensional histogram
+ * @tep: The tep handle that has the @system and @event.
+ * @system: The system the histogram event is in.
+ * @event_name: The name of the event that the histogram will be attached to.
+ * @key: The primary key the histogram will use
+ * @type: The format type of the key.
+ *
+ * Will initialize a histogram descriptor that will be attached to
+ * the @system/@event with the given @key as the primary. This only
+ * initializes the descriptor, it does not start the histogram
+ * in the kernel.
+ *
+ * Returns an initialized histogram on success.
+ * NULL on failure.
+ */
+struct tracefs_hist *
+tracefs_hist_alloc(struct tep_handle *tep,
+		   const char *system, const char *event_name,
+		   const char *key, enum tracefs_hist_key_type type)
+{
+	struct tracefs_hist_axis axis[] = {{key, type}, {NULL, 0}};
+
+	return tracefs_hist_alloc_nd(tep, system, event_name, axis);
+}
+
+/**
+ * tracefs_hist_alloc_2d - Initialize two-dimensional histogram
+ * @tep: The tep handle that has the @system and @event.
+ * @system: The system the histogram event is in.
+ * @event: The event that the histogram will be attached to.
+ * @key1: The first primary key the histogram will use
+ * @type1: The format type of the first key.
+ * @key2: The second primary key the histogram will use
+ * @type2: The format type of the second key.
+ *
+ * Will initialize a histogram descriptor that will be attached to
+ * the @system/@event with the given @key1 and @key2 as the primaries.
+ * This only initializes the descriptor, it does not start the histogram
+ * in the kernel.
+ *
+ * Returns an initialized histogram on success.
+ * NULL on failure.
+ */
+struct tracefs_hist *
+tracefs_hist_alloc_2d(struct tep_handle *tep,
+		      const char *system, const char *event_name,
+		      const char *key1, enum tracefs_hist_key_type type1,
+		      const char *key2, enum tracefs_hist_key_type type2)
+{
+	struct tracefs_hist_axis axis[] = {{key1, type1},
+					   {key2, type2},
+					   {NULL, 0}};
+
+	return tracefs_hist_alloc_nd(tep, system, event_name, axis);
+}
+
+static struct tracefs_hist *
+hist_alloc_nd(struct tep_handle *tep,
+	      const char *system, const char *event_name,
+	      struct tracefs_hist_axis *axes,
+	      struct tracefs_hist_axis_cnt *axes_cnt)
+{
+	struct tep_event *event;
+	struct tracefs_hist *hist;
+
+	if (!system || !event_name)
+		return NULL;
+
+	event = tep_find_event_by_name(tep, system, event_name);
+	if (!event)
+		return NULL;
+
+	hist = calloc(1, sizeof(*hist));
+	if (!hist)
+		return NULL;
+
+	tep_ref(tep);
+	hist->tep = tep;
+	hist->event = event;
+	hist->system = strdup(system);
+	hist->event_name = strdup(event_name);
+	if (!hist->system || !hist->event_name)
+		goto fail;
+
+	for (; axes && axes->key; axes++)
+		if (tracefs_hist_add_key(hist, axes->key, axes->type) < 0)
+			goto fail;
+
+	for (; axes_cnt && axes_cnt->key; axes_cnt++)
+		if (tracefs_hist_add_key_cnt(hist, axes_cnt->key, axes_cnt->type, axes_cnt->cnt) < 0)
+			goto fail;
+
+	return hist;
+
+ fail:
+	tracefs_hist_free(hist);
+	return NULL;
+}
+/**
+ * tracefs_hist_alloc_nd - Initialize N-dimensional histogram
+ * @tep: The tep handle that has the @system and @event.
+ * @system: The system the histogram event is in
+ * @event: The event that the histogram will be attached to
+ * @axes: An array of histogram axes, terminated by a {NULL, 0} entry
+ *
+ * Will initialize a histogram descriptor that will be attached to
+ * the @system/@event. This only initializes the descriptor with the given
+ * @axes keys as primaries. This only initializes the descriptor, it does
+ * not start the histogram in the kernel.
+ *
+ * Returns an initialized histogram on success.
+ * NULL on failure.
+ */
+struct tracefs_hist *
+tracefs_hist_alloc_nd(struct tep_handle *tep,
+		      const char *system, const char *event_name,
+		      struct tracefs_hist_axis *axes)
+{
+	return hist_alloc_nd(tep, system, event_name, axes, NULL);
+}
+
+/**
+ * tracefs_hist_alloc_nd_cnt - Initialize N-dimensional histogram
+ * @tep: The tep handle that has the @system and @event.
+ * @system: The system the histogram event is in
+ * @event: The event that the histogram will be attached to
+ * @axes: An array of histogram axes, terminated by a {NULL, 0} entry
+ *
+ * Will initialize a histogram descriptor that will be attached to
+ * the @system/@event. This only initializes the descriptor with the given
+ * @axes keys as primaries. This only initializes the descriptor, it does
+ * not start the histogram in the kernel.
+ *
+ * Returns an initialized histogram on success.
+ * NULL on failure.
+ */
+struct tracefs_hist *
+tracefs_hist_alloc_nd_cnt(struct tep_handle *tep,
+			  const char *system, const char *event_name,
+			  struct tracefs_hist_axis_cnt *axes)
+{
+	return hist_alloc_nd(tep, system, event_name, NULL, axes);
+}
+
+/**
+ * tracefs_hist_add_key - add to a key to a histogram
+ * @hist: The histogram to add the key to.
+ * @key: The name of the key field.
+ * @type: The type of the key format.
+ * @cnt: Some types require a counter, for those, this is used
+ *
+ * This adds a secondary or tertiary key to the histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_add_key_cnt(struct tracefs_hist *hist, const char *key,
+			     enum tracefs_hist_key_type type, int cnt)
+{
+	bool use_key = false;
+	char *key_type = NULL;
+	char **new_list;
+	int ret = -1;
+
+	switch (type) {
+	case TRACEFS_HIST_KEY_NORMAL:
+		use_key = true;
+		ret = 0;
+		break;
+	case TRACEFS_HIST_KEY_HEX:
+		ret = asprintf(&key_type, "%s.hex", key);
+		break;
+	case TRACEFS_HIST_KEY_SYM:
+		ret = asprintf(&key_type, "%s.sym", key);
+		break;
+	case TRACEFS_HIST_KEY_SYM_OFFSET:
+		ret = asprintf(&key_type, "%s.sym-offset", key);
+		break;
+	case TRACEFS_HIST_KEY_SYSCALL:
+		ret = asprintf(&key_type, "%s.syscall", key);
+		break;
+	case TRACEFS_HIST_KEY_EXECNAME:
+		ret = asprintf(&key_type, "%s.execname", key);
+		break;
+	case TRACEFS_HIST_KEY_LOG:
+		ret = asprintf(&key_type, "%s.log2", key);
+		break;
+	case TRACEFS_HIST_KEY_USECS:
+		ret = asprintf(&key_type, "%s.usecs", key);
+		break;
+	case TRACEFS_HIST_KEY_BUCKETS:
+		ret = asprintf(&key_type, "%s.buckets=%d", key, cnt);
+		break;
+	case TRACEFS_HIST_KEY_MAX:
+		/* error */
+		break;
+	}
+
+	if (ret < 0)
+		return -1;
+
+	new_list = tracefs_list_add(hist->keys, use_key ? key : key_type);
+	free(key_type);
+	if (!new_list)
+		return -1;
+
+	hist->keys = new_list;
+
+	return 0;
+}
+
+/**
+ * tracefs_hist_add_key - add to a key to a histogram
+ * @hist: The histogram to add the key to.
+ * @key: The name of the key field.
+ * @type: The type of the key format.
+ *
+ * This adds a secondary or tertiary key to the histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
+			 enum tracefs_hist_key_type type)
+{
+	return tracefs_hist_add_key_cnt(hist, key, type, 0);
+}
+
+/**
+ * tracefs_hist_add_value - add to a value to a histogram
+ * @hist: The histogram to add the value to.
+ * @key: The name of the value field.
+ *
+ * This adds a value field to the histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_add_value(struct tracefs_hist *hist, const char *value)
+{
+	char **new_list;
+
+	new_list = tracefs_list_add(hist->values, value);
+	if (!new_list)
+		return -1;
+
+	hist->values = new_list;
+
+	return 0;
+}
+
+/**
+ * tracefs_hist_add_name - name a histogram
+ * @hist: The histogram to name.
+ * @name: The name of the histogram.
+ *
+ * Adds a name to the histogram. Named histograms will share their
+ * data with other events that have the same name as if it was
+ * a single histogram.
+ *
+ * If the histogram already has a name, this will fail.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_add_name(struct tracefs_hist *hist, const char *name)
+{
+	if (hist->name)
+		return -1;
+
+	hist->name = strdup(name);
+
+	return hist->name ? 0 : -1;
+}
+
+static char **
+add_sort_key(struct tracefs_hist *hist, const char *sort_key, char **list)
+{
+	char **key_list = hist->keys;
+	char **val_list = hist->values;
+	int i;
+
+	if (strcmp(sort_key, TRACEFS_HIST_HITCOUNT) == 0)
+		goto out;
+
+	for (i = 0; key_list[i]; i++) {
+		if (strcmp(key_list[i], sort_key) == 0)
+			break;
+	}
+
+	if (!key_list[i] && val_list) {
+		for (i = 0; val_list[i]; i++) {
+			if (strcmp(val_list[i], sort_key) == 0)
+				break;
+			if (!val_list[i])
+				return NULL;
+		}
+	}
+
+
+ out:
+	return tracefs_list_add(list, sort_key);
+}
+
+/**
+ * tracefs_hist_add_sort_key - add a key for sorting the histogram
+ * @hist: The histogram to add the sort key to
+ * @sort_key: The key to sort
+ *
+ * Call the function to add to the list of sort keys of the histohram in
+ * the order of priority that the keys would be sorted on output.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_add_sort_key(struct tracefs_hist *hist,
+			      const char *sort_key)
+{
+	char **list = hist->sort;
+
+	if (!hist || !sort_key)
+		return -1;
+
+	list = add_sort_key(hist, sort_key, hist->sort);
+	if (!list)
+		return -1;
+
+	hist->sort = list;
+
+	return 0;
+}
+
+/**
+ * tracefs_hist_set_sort_key - set a key for sorting the histogram
+ * @hist: The histogram to set the sort key to
+ * @sort_key: The key to sort (and the strings after it)
+ *  Last one must be NULL.
+ *
+ * Set a list of sort keys in the order of priority that the
+ * keys would be sorted on output. Keys must be added first.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_set_sort_key(struct tracefs_hist *hist,
+			      const char *sort_key, ...)
+{
+	char **list = NULL;
+	char **tmp;
+	va_list ap;
+
+	if (!hist || !sort_key)
+		return -1;
+
+	tmp = add_sort_key(hist, sort_key, list);
+	if (!tmp)
+		goto fail;
+	list = tmp;
+
+	va_start(ap, sort_key);
+	for (;;) {
+		sort_key = va_arg(ap, const char *);
+		if (!sort_key)
+			break;
+		tmp = add_sort_key(hist, sort_key, list);
+		if (!tmp)
+			goto fail;
+		list = tmp;
+	}
+	va_end(ap);
+
+	tracefs_list_free(hist->sort);
+	hist->sort = list;
+
+	return 0;
+ fail:
+	tracefs_list_free(list);
+	return -1;
+}
+
+static int end_match(const char *sort_key, const char *ending)
+{
+	int key_len = strlen(sort_key);
+	int end_len = strlen(ending);
+
+	if (key_len <= end_len)
+		return 0;
+
+	sort_key += key_len - end_len;
+
+	return strcmp(sort_key, ending) == 0 ? key_len - end_len : 0;
+}
+
+/**
+ * tracefs_hist_sort_key_direction - set direction of a sort key
+ * @hist: The histogram to modify.
+ * @sort_str: The sort key to set the direction for
+ * @dir: The direction to set the sort key to.
+ *
+ * Returns 0 on success, and -1 on error;
+ */
+int tracefs_hist_sort_key_direction(struct tracefs_hist *hist,
+				    const char *sort_str,
+				    enum tracefs_hist_sort_direction dir)
+{
+	char **sort = hist->sort;
+	char *sort_key;
+	char *direct;
+	int match;
+	int i;
+
+	if (!sort)
+		return -1;
+
+	for (i = 0; sort[i]; i++) {
+		if (strcmp(sort[i], sort_str) == 0)
+			break;
+	}
+	if (!sort[i])
+		return -1;
+
+	sort_key = sort[i];
+
+	switch (dir) {
+	case TRACEFS_HIST_SORT_ASCENDING:
+		direct = ASCENDING;
+		break;
+	case TRACEFS_HIST_SORT_DESCENDING:
+		direct = DESCENDING;
+		break;
+	default:
+		return -1;
+	}
+
+	match = end_match(sort_key, ASCENDING);
+	if (match) {
+		/* Already match? */
+		if (dir == TRACEFS_HIST_SORT_ASCENDING)
+			return 0;
+	} else {
+		match = end_match(sort_key, DESCENDING);
+		/* Already match? */
+		if (match && dir == TRACEFS_HIST_SORT_DESCENDING)
+			return 0;
+	}
+
+	if (match)
+		/* Clear the original text */
+		sort_key[match] = '\0';
+
+	sort_key = realloc(sort_key, strlen(sort_key) + strlen(direct) + 1);
+	if (!sort_key) {
+		/* Failed to alloc, may need to put back the match */
+		sort_key = sort[i];
+		if (match)
+			sort_key[match] = '.';
+		return -1;
+	}
+
+	strcat(sort_key, direct);
+	sort[i] = sort_key;
+	return 0;
+}
+
+int tracefs_hist_append_filter(struct tracefs_hist *hist,
+			       enum tracefs_filter type,
+			       const char *field,
+			       enum tracefs_compare compare,
+			       const char *val)
+{
+	return trace_append_filter(&hist->filter, &hist->filter_state,
+				   &hist->filter_parens,
+				   hist->event,
+				   type, field, compare, val);
+}
+
+enum action_type {
+	ACTION_NONE,
+	ACTION_TRACE,
+	ACTION_SNAPSHOT,
+	ACTION_SAVE,
+};
+
+struct action {
+	struct action			*next;
+	enum action_type		type;
+	enum tracefs_synth_handler	handler;
+	char				*handle_field;
+	char				*save;
+};
+
+struct name_hash {
+	struct name_hash	*next;
+	char			*name;
+	char			*hash;
+};
+
+/*
+ * @name: name of the synthetic event
+ * @start_system: system of the starting event
+ * @start_event: the starting event
+ * @end_system: system of the ending event
+ * @end_event: the ending event
+ * @actions: List of actions to take
+ * @match_names: If a match set is to be a synthetic field, it has a name
+ * @start_match: list of keys in the start event that matches end event
+ * @end_match: list of keys in the end event that matches the start event
+ * @compare_names: The synthetic field names of the compared fields
+ * @start_compare: A list of compare fields in the start to compare to end
+ * @end_compare: A list of compare fields in the end to compare to start
+ * @compare_ops: The type of operations to perform between the start and end
+ * @start_names: The fields in the start event to record
+ * @end_names: The fields in the end event to record
+ * @start_filters: The fields in the end event to record
+ * @end_filters: The fields in the end event to record
+ * @start_parens: Current parenthesis level for start event
+ * @end_parens: Current parenthesis level for end event
+ * @new_format: onmatch().trace(synth_event,..) or onmatch().synth_event(...)
+ */
+struct tracefs_synth {
+	struct tracefs_instance *instance;
+	struct tep_handle	*tep;
+	struct tep_event	*start_event;
+	struct tep_event	*end_event;
+	struct action		*actions;
+	struct action		**next_action;
+	struct tracefs_dynevent	*dyn_event;
+	struct name_hash	*name_hash[1 << HASH_BITS];
+	char			*start_hist;
+	char			*end_hist;
+	char			*name;
+	char			**synthetic_fields;
+	char			**synthetic_args;
+	char			**start_selection;
+	char			**start_keys;
+	char			**end_keys;
+	char			**start_vars;
+	char			**end_vars;
+	char			*start_filter;
+	char			*end_filter;
+	unsigned int		start_parens;
+	unsigned int		start_state;
+	unsigned int		end_parens;
+	unsigned int		end_state;
+	int			*start_type;
+	char			arg_name[16];
+	int			arg_cnt;
+	bool			new_format;
+};
+
+ /*
+ * tracefs_synth_get_name - get the name of the synthetic event
+ * @synth: The synthetic event to get the name for
+ *
+ * Returns name string owned by @synth on success, or NULL on error.
+ */
+const char *tracefs_synth_get_name(struct tracefs_synth *synth)
+{
+	return synth ? synth->name : NULL;
+}
+
+static void action_free(struct action *action)
+{
+	free(action->handle_field);
+	free(action->save);
+	free(action);
+}
+
+static void free_name_hash(struct name_hash **hash)
+{
+	struct name_hash *item;
+	int i;
+
+	for (i = 0; i < 1 << HASH_BITS; i++) {
+		while ((item = hash[i])) {
+			hash[i] = item->next;
+			free(item->name);
+			free(item->hash);
+			free(item);
+		}
+	}
+}
+
+/**
+ * tracefs_synth_free - free the resources alloced to a synth
+ * @synth: The tracefs_synth descriptor
+ *
+ * Frees the resources allocated for a @synth created with
+ * tracefs_synth_alloc(). It does not touch the system. That is,
+ * any synthetic event created, will not be destroyed by this
+ * function.
+ */
+void tracefs_synth_free(struct tracefs_synth *synth)
+{
+	struct action *action;
+
+	if (!synth)
+		return;
+
+	free(synth->name);
+	free(synth->start_hist);
+	free(synth->end_hist);
+	tracefs_list_free(synth->synthetic_fields);
+	tracefs_list_free(synth->synthetic_args);
+	tracefs_list_free(synth->start_selection);
+	tracefs_list_free(synth->start_keys);
+	tracefs_list_free(synth->end_keys);
+	tracefs_list_free(synth->start_vars);
+	tracefs_list_free(synth->end_vars);
+	free_name_hash(synth->name_hash);
+	free(synth->start_filter);
+	free(synth->end_filter);
+	free(synth->start_type);
+
+	tep_unref(synth->tep);
+
+	while ((action = synth->actions)) {
+		synth->actions = action->next;
+		action_free(action);
+	}
+	tracefs_dynevent_free(synth->dyn_event);
+
+	free(synth);
+}
+
+static bool verify_event_fields(struct tep_event *start_event,
+				struct tep_event *end_event,
+				const char *start_field_name,
+				const char *end_field_name,
+				const struct tep_format_field **ptr_start_field)
+{
+	const struct tep_format_field *start_field;
+	const struct tep_format_field *end_field;
+	int start_flags, end_flags;
+
+	if (!trace_verify_event_field(start_event, start_field_name,
+				      &start_field))
+		return false;
+
+	if (end_event) {
+		if (!trace_verify_event_field(end_event, end_field_name,
+					      &end_field))
+			return false;
+
+		/* A pointer can still match a long */
+		start_flags = start_field->flags & ~TEP_FIELD_IS_POINTER;
+		end_flags = end_field->flags & ~TEP_FIELD_IS_POINTER;
+
+		if (start_flags != end_flags ||
+		    start_field->size != end_field->size) {
+			errno = EBADE;
+			return false;
+		}
+	}
+
+	if (ptr_start_field)
+		*ptr_start_field = start_field;
+
+	return true;
+}
+
+__hidden char *append_string(char *str, const char *space, const char *add)
+{
+	char *new;
+	int len;
+
+	/* String must already be allocated */
+	if (!str)
+		return NULL;
+
+	len = strlen(str) + strlen(add) + 2;
+	if (space)
+		len += strlen(space);
+
+	new = realloc(str, len);
+	if (!new) {
+		free(str);
+		return NULL;
+	}
+	str = new;
+
+	if (space)
+		strcat(str, space);
+	strcat(str, add);
+
+	return str;
+}
+
+static char *add_synth_field(const struct tep_format_field *field,
+			     const char *name)
+{
+	const char *type;
+	char size[64];
+	char *str;
+	bool sign;
+
+	if (field->flags & TEP_FIELD_IS_ARRAY) {
+		str = strdup("char");
+		str = append_string(str, " ", name);
+		str = append_string(str, NULL, "[");
+
+		if (!(field->flags & TEP_FIELD_IS_DYNAMIC)) {
+			snprintf(size, 64, "%d", field->size);
+			str = append_string(str, NULL, size);
+		}
+		return append_string(str, NULL, "];");
+	}
+
+	/* Synthetic events understand pid_t, gfp_t and bool */
+	if (strcmp(field->type, "pid_t") == 0 ||
+	    strcmp(field->type, "gfp_t") == 0 ||
+	    strcmp(field->type, "bool") == 0) {
+		str = strdup(field->type);
+		str = append_string(str, " ", name);
+		return append_string(str, NULL, ";");
+	}
+
+	sign = field->flags & TEP_FIELD_IS_SIGNED;
+
+	switch (field->size) {
+	case 1:
+		if (!sign)
+			type = "unsigned char";
+		else
+			type = "char";
+		break;
+	case 2:
+		if (sign)
+			type = "s16";
+		else
+			type = "u16";
+		break;
+	case 4:
+		if (sign)
+			type = "s32";
+		else
+			type = "u32";
+		break;
+	case 8:
+		if (sign)
+			type = "s64";
+		else
+			type = "u64";
+		break;
+	default:
+		errno = EBADF;
+		return NULL;
+	}
+
+	str = strdup(type);
+	str = append_string(str, " ", name);
+	return append_string(str, NULL, ";");
+}
+
+static int add_var(char ***list, const char *name, const char *var, bool is_var)
+{
+	char **new;
+	char *assign;
+	int ret;
+
+	if (is_var)
+		ret = asprintf(&assign, "%s=$%s", name, var);
+	else
+		ret = asprintf(&assign, "%s=%s", name, var);
+
+	if (ret < 0)
+		return -1;
+
+	new = tracefs_list_add(*list, assign);
+	free(assign);
+
+	if (!new)
+		return -1;
+	*list = new;
+	return 0;
+}
+
+__hidden struct tracefs_synth *
+synth_init_from(struct tep_handle *tep, const char *start_system,
+		const char *start_event_name)
+{
+	struct tep_event *start_event;
+	struct tracefs_synth *synth;
+
+	start_event = tep_find_event_by_name(tep, start_system,
+					     start_event_name);
+	if (!start_event) {
+		errno = ENODEV;
+		return NULL;
+	}
+
+	synth = calloc(1, sizeof(*synth));
+	if (!synth)
+		return NULL;
+
+	synth->start_event = start_event;
+	synth->next_action = &synth->actions;
+
+	/* Hold onto a reference to this handler */
+	tep_ref(tep);
+	synth->tep = tep;
+
+	return synth;
+}
+
+static int alloc_synthetic_event(struct tracefs_synth *synth)
+{
+	char *format;
+	const char *field;
+	int i;
+
+	format = strdup("");
+	if (!format)
+		return -1;
+
+	for (i = 0; synth->synthetic_fields && synth->synthetic_fields[i]; i++) {
+		field = synth->synthetic_fields[i];
+		format = append_string(format, i ? " " : NULL, field);
+	}
+
+	synth->dyn_event = dynevent_alloc(TRACEFS_DYNEVENT_SYNTH, SYNTHETIC_GROUP,
+					  synth->name, NULL, format);
+	free(format);
+
+	return synth->dyn_event ? 0 : -1;
+}
+
+/*
+ * See if it is onmatch().trace(synth_event,...) or
+ *   onmatch().synth_event(...)
+ */
+static bool has_new_format()
+{
+	char *readme;
+	char *p;
+	int size;
+
+	readme = tracefs_instance_file_read(NULL, "README", &size);
+	if (!readme)
+		return false;
+
+	p = strstr(readme, "trace(<synthetic_event>,param list)");
+	free(readme);
+
+	return p != NULL;
+}
+
+/**
+ * tracefs_synth_alloc - create a new tracefs_synth instance
+ * @tep: The tep handle that holds the events to work on
+ * @name: The name of the synthetic event being created
+ * @start_system: The name of the system of the start event (can be NULL)
+ * @start_event_name: The name of the start event
+ * @end_system: The name of the system of the end event (can be NULL)
+ * @end_event_name: The name of the end event
+ * @start_match_field: The name of the field in start event to match @end_match_field
+ * @end_match_field: The name of the field in end event to match @start_match_field
+ * @match_name: Name to call the fields that match (can be NULL)
+ * 
+ * Creates a tracefs_synth instance that has the minimum requirements to
+ * create a synthetic event.
+ *
+ * @name is will be the name of the synthetic event that this can create.
+ *
+ * The start event is found with @start_system and @start_event_name. If
+ * @start_system is NULL, then the first event with @start_event_name will
+ * be used.
+ *
+ * The end event is found with @end_system and @end_event_name. If
+ * @end_system is NULL, then the first event with @end_event_name will
+ * be used.
+ *
+ * The @start_match_field is the field in the start event that will be used
+ * to match the @end_match_field of the end event.
+ *
+ * If @match_name is given, then the field that matched the start and
+ * end events will be passed an a field to the sythetic event with this
+ * as the field name.
+ *
+ * Returns an allocated tracefs_synth descriptor on success and NULL
+ * on error, with the following set in errno.
+ *
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be
+ * ENODEV - could not find an event or field
+ * EBADE - The start and end fields are not compatible to match 
+ * 
+ * Note, this does not modify the system. That is, the synthetic
+ * event on the system is not created. That needs to be done with
+ * tracefs_synth_create().
+ */
+struct tracefs_synth *tracefs_synth_alloc(struct tep_handle *tep,
+					  const char *name,
+					  const char *start_system,
+					  const char *start_event_name,
+					  const char *end_system,
+					  const char *end_event_name,
+					  const char *start_match_field,
+					  const char *end_match_field,
+					  const char *match_name)
+{
+	struct tep_event *end_event;
+	struct tracefs_synth *synth;
+	int ret = 0;
+
+	if (!tep || !name || !start_event_name || !end_event_name ||
+	    !start_match_field || !end_match_field) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	synth = synth_init_from(tep, start_system, start_event_name);
+	if (!synth)
+		return NULL;
+
+	end_event = tep_find_event_by_name(tep, end_system,
+					   end_event_name);
+	if (!end_event) {
+		tep_unref(tep);
+		errno = ENODEV;
+		return NULL;
+	}
+
+	synth->end_event = end_event;
+
+	synth->name = strdup(name);
+
+	ret = tracefs_synth_add_match_field(synth, start_match_field,
+					    end_match_field, match_name);
+
+	if (!synth->name || !synth->start_keys || !synth->end_keys || ret) {
+		tracefs_synth_free(synth);
+		synth = NULL;
+	} else
+		synth->new_format = has_new_format();
+
+	return synth;
+}
+
+static int add_synth_fields(struct tracefs_synth *synth,
+			    const struct tep_format_field *field,
+			    const char *name, const char *var)
+{
+	char **list;
+	char *str;
+	int ret;
+
+	str = add_synth_field(field, name ? : field->name);
+	if (!str)
+		return -1;
+
+	if (!name)
+		name = var;
+
+	list = tracefs_list_add(synth->synthetic_fields, str);
+	free(str);
+	if (!list)
+		return -1;
+	synth->synthetic_fields = list;
+
+	ret = asprintf(&str, "$%s", var ? : name);
+	if (ret < 0) {
+		trace_list_pop(synth->synthetic_fields);
+		return -1;
+	}
+
+	list = tracefs_list_add(synth->synthetic_args, str);
+	free(str);
+	if (!list) {
+		trace_list_pop(synth->synthetic_fields);
+		return -1;
+	}
+
+	synth->synthetic_args = list;
+
+	return 0;
+}
+
+/**
+ * tracefs_synth_add_match_field - add another key to match events
+ * @synth: The tracefs_synth descriptor
+ * @start_match_field: The field of the start event to match the end event
+ * @end_match_field: The field of the end event to match the start event
+ * @name: The name to show in the synthetic event (NULL is allowed)
+ *
+ * This will add another set of keys to use for a match between
+ * the start event and the end event.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be
+ * ENODEV - could not find a field
+ * EBADE - The start and end fields are not compatible to match 
+ */
+int tracefs_synth_add_match_field(struct tracefs_synth *synth,
+				  const char *start_match_field,
+				  const char *end_match_field,
+				  const char *name)
+{
+	const struct tep_format_field *key_field;
+	char **list;
+	int ret;
+
+	if (!synth || !start_match_field || !end_match_field) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!verify_event_fields(synth->start_event, synth->end_event,
+				 start_match_field, end_match_field,
+				 &key_field))
+		return -1;
+
+	list = tracefs_list_add(synth->start_keys, start_match_field);
+	if (!list)
+		return -1;
+
+	synth->start_keys = list;
+
+	list = tracefs_list_add(synth->end_keys, end_match_field);
+	if (!list) {
+		trace_list_pop(synth->start_keys);
+		return -1;
+	}
+	synth->end_keys = list;
+
+	if (!name)
+		return 0;
+
+	ret = add_var(&synth->end_vars, name, end_match_field, false);
+
+	if (ret < 0)
+		goto pop_lists;
+
+	ret = add_synth_fields(synth, key_field, name, NULL);
+	if (ret < 0)
+		goto pop_lists;
+
+	return 0;
+
+ pop_lists:
+	trace_list_pop(synth->start_keys);
+	trace_list_pop(synth->end_keys);
+	return -1;
+}
+
+static unsigned int make_rand(void)
+{
+	struct timeval tv;
+	unsigned long seed;
+
+	gettimeofday(&tv, NULL);
+	seed = (tv.tv_sec + tv.tv_usec) + getpid();
+
+	/* taken from the rand(3) man page */
+	seed = seed * 1103515245 + 12345;
+	return((unsigned)(seed/65536) % 32768);
+}
+
+static char *new_name(struct tracefs_synth *synth, const char *name)
+{
+	int cnt = synth->arg_cnt + 1;
+	char *arg;
+	int ret;
+
+	/* Create a unique argument name */
+	if (!synth->arg_name[0]) {
+		/* make_rand() returns at most 32768 (total 13 bytes in use) */
+		sprintf(synth->arg_name, "%u", make_rand());
+	}
+	ret = asprintf(&arg, "__%s_%s_%d", name, synth->arg_name, cnt);
+	if (ret < 0)
+		return NULL;
+
+	synth->arg_cnt = cnt;
+	return arg;
+}
+
+static struct name_hash *find_name(struct tracefs_synth *synth, const char *name)
+{
+	unsigned int key = quick_hash(name);
+	struct name_hash *hash = synth->name_hash[key];
+
+	for (; hash; hash = hash->next) {
+		if (!strcmp(hash->name, name))
+			return hash;
+	}
+	return NULL;
+}
+
+static const char *hash_name(struct tracefs_synth *synth, const char *name)
+{
+	struct name_hash *hash;
+	int key;
+
+	hash = find_name(synth, name);
+	if (hash)
+		return hash->hash;
+
+	hash = malloc(sizeof(*hash));
+	if (!hash)
+		return name;
+
+	hash->hash = new_name(synth, name);
+	if (!hash->hash) {
+		free(hash);
+		return name;
+	}
+
+	key = quick_hash(name);
+	hash->next = synth->name_hash[key];
+	synth->name_hash[key] = hash;
+
+	hash->name = strdup(name);
+	if (!hash->name) {
+		free(hash->hash);
+		free(hash);
+		return name;
+	}
+	return hash->hash;
+}
+
+static char *new_arg(struct tracefs_synth *synth)
+{
+	return new_name(synth, "arg");
+}
+
+/**
+ * tracefs_synth_add_compare_field - add a comparison between start and end
+ * @synth: The tracefs_synth descriptor
+ * @start_compare_field: The field of the start event to compare to the end
+ * @end_compare_field: The field of the end event to compare to the start
+ * @calc - How to go about the comparing the fields.
+ * @name: The name to show in the synthetic event (must NOT be NULL)
+ *
+ * This will add a way to compare two different fields between the
+ * start end end events.
+ *
+ * The comparing between events is decided by @calc:
+ *    TRACEFS_SYNTH_DELTA_END       - name = end - start
+ *    TRACEFS_SYNTH_DELTA_START     - name = start - end
+ *    TRACEFS_SYNTH_ADD             - name = end + start
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be
+ * ENODEV - could not find a field
+ * EBADE - The start and end fields are not compatible to compare
+ */
+int tracefs_synth_add_compare_field(struct tracefs_synth *synth,
+				    const char *start_compare_field,
+				    const char *end_compare_field,
+				    enum tracefs_synth_calc calc,
+				    const char *name)
+{
+	const struct tep_format_field *start_field;
+	const char *hname;
+	char *start_arg;
+	char *compare;
+	int ret;
+
+	/* Compare fields require a name */
+	if (!name || !start_compare_field || !end_compare_field) {
+		errno = -EINVAL;
+		return -1;
+	}
+
+	if (!verify_event_fields(synth->start_event, synth->end_event,
+				 start_compare_field, end_compare_field,
+				 &start_field))
+		return -1;
+
+	/* Calculations are not allowed on string */
+	if (start_field->flags & (TEP_FIELD_IS_ARRAY |
+				  TEP_FIELD_IS_DYNAMIC)) {
+		errno = -EINVAL;
+		return -1;
+	}
+
+	start_arg = new_arg(synth);
+	if (!start_arg)
+		return -1;
+
+	ret = add_var(&synth->start_vars, start_arg, start_compare_field, false);
+	if (ret < 0) {
+		free(start_arg);
+		return -1;
+	}
+
+	ret = -1;
+	switch (calc) {
+	case TRACEFS_SYNTH_DELTA_END:
+		ret = asprintf(&compare, "%s-$%s", end_compare_field,
+			       start_arg);
+		break;
+	case TRACEFS_SYNTH_DELTA_START:
+		ret = asprintf(&compare, "$%s-%s", start_arg,
+			       end_compare_field);
+		break;
+	case TRACEFS_SYNTH_ADD:
+		ret = asprintf(&compare, "%s+$%s", end_compare_field,
+			       start_arg);
+		break;
+	}
+	free(start_arg);
+	if (ret < 0)
+		return -1;
+
+	hname = hash_name(synth, name);
+	ret = add_var(&synth->end_vars, hname, compare, false);
+	if (ret < 0)
+		goto out_free;
+
+	ret = add_synth_fields(synth, start_field, name, hname);
+	if (ret < 0)
+		goto out_free;
+
+ out_free:
+	free(compare);
+
+	return ret ? -1 : 0;
+}
+
+__hidden int synth_add_start_field(struct tracefs_synth *synth,
+				   const char *start_field,
+				   const char *name,
+				   enum tracefs_hist_key_type type, int count)
+{
+	const struct tep_format_field *field;
+	const char *var;
+	char *start_arg;
+	char **tmp;
+	int *types;
+	int len;
+	int ret;
+
+	if (!synth || !start_field) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!name)
+		name = start_field;
+
+	var = hash_name(synth, name);
+
+	if (!trace_verify_event_field(synth->start_event, start_field, &field))
+		return -1;
+
+	start_arg = new_arg(synth);
+	if (!start_arg)
+		return -1;
+
+	ret = add_var(&synth->start_vars, start_arg, start_field, false);
+	if (ret)
+		goto out_free;
+
+	ret = add_var(&synth->end_vars, var, start_arg, true);
+	if (ret)
+		goto out_free;
+
+	ret = add_synth_fields(synth, field, name, var);
+	if (ret)
+		goto out_free;
+
+	tmp = tracefs_list_add(synth->start_selection, start_field);
+	if (!tmp) {
+		ret = -1;
+		goto out_free;
+	}
+	synth->start_selection = tmp;
+
+	len = tracefs_list_size(tmp);
+	if (len <= 0) { /* ?? */
+		ret = -1;
+		goto out_free;
+	}
+
+	types = realloc(synth->start_type, sizeof(*types) * len);
+	if (!types) {
+		ret = -1;
+		goto out_free;
+	}
+	synth->start_type = types;
+	synth->start_type[len - 1] = type;
+
+ out_free:
+	free(start_arg);
+	return ret;
+}
+
+/**
+ * tracefs_synth_add_start_field - add a start field to save
+ * @synth: The tracefs_synth descriptor
+ * @start_field: The field of the start event to save
+ * @name: The name to show in the synthetic event (if NULL @start_field is used)
+ *
+ * This adds a field named by @start_field of the start event to
+ * record in the synthetic event.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be
+ * ENODEV - could not find a field
+ */
+int tracefs_synth_add_start_field(struct tracefs_synth *synth,
+				  const char *start_field,
+				  const char *name)
+{
+	return synth_add_start_field(synth, start_field, name, 0, 0);
+}
+
+/**
+ * tracefs_synth_add_end_field - add a end field to save
+ * @synth: The tracefs_synth descriptor
+ * @end_field: The field of the end event to save
+ * @name: The name to show in the synthetic event (if NULL @end_field is used)
+ *
+ * This adds a field named by @end_field of the start event to
+ * record in the synthetic event.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be
+ * ENODEV - could not find a field
+ */
+int tracefs_synth_add_end_field(struct tracefs_synth *synth,
+				const char *end_field,
+				const char *name)
+{
+	const struct tep_format_field *field;
+	const char *hname = NULL;
+	char *tmp_var = NULL;
+	int ret;
+
+	if (!synth || !end_field) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (name) {
+		if (strncmp(name, "__arg", 5) != 0)
+			hname = hash_name(synth, name);
+		else
+			hname = name;
+	}
+
+	if (!name)
+		tmp_var = new_arg(synth);
+
+	if (!trace_verify_event_field(synth->end_event, end_field, &field))
+		return -1;
+
+	ret = add_var(&synth->end_vars, name ? hname : tmp_var, end_field, false);
+	if (ret)
+		goto out;
+
+	ret = add_synth_fields(synth, field, name, hname ? : tmp_var);
+	free(tmp_var);
+ out:
+	return ret;
+}
+
+/**
+ * tracefs_synth_append_start_filter - create or append a filter
+ * @synth: The tracefs_synth descriptor
+ * @type: The type of element to add to the filter
+ * @field: For @type == TRACEFS_FILTER_COMPARE, the field to compare
+ * @compare: For @type == TRACEFS_FILTER_COMPARE, how to compare @field to @val
+ * @val: For @type == TRACEFS_FILTER_COMPARE, what value @field is to be
+ *
+ * This will put together a filter string for the starting event
+ * of @synth. It check to make sure that what is added is correct compared
+ * to the filter that is already built.
+ *
+ * @type can be:
+ *     TRACEFS_FILTER_COMPARE:        See below
+ *     TRACEFS_FILTER_AND:            Append "&&" to the filter
+ *     TRACEFS_FILTER_OR:             Append "||" to the filter
+ *     TRACEFS_FILTER_NOT:            Append "!" to the filter
+ *     TRACEFS_FILTER_OPEN_PAREN:     Append "(" to the filter
+ *     TRACEFS_FILTER_CLOSE_PAREN:    Append ")" to the filter
+ *
+ * For all types except TRACEFS_FILTER_COMPARE, the @field, @compare,
+ * and @val are ignored.
+ *
+ * For @type == TRACEFS_FILTER_COMPARE.
+ *
+ *  @field is the name of the field for the start event to compare.
+ *         If it is not a field for the start event, this return an
+ *         error.
+ *
+ *  @compare can be one of:
+ *     TRACEFS_COMPARE_EQ:       Test @field == @val
+ *     TRACEFS_COMPARE_NE:       Test @field != @val
+ *     TRACEFS_COMPARE_GT:       Test @field > @val
+ *     TRACEFS_COMPARE_GE:       Test @field >= @val
+ *     TRACEFS_COMPARE_LT:       Test @field < @val
+ *     TRACEFS_COMPARE_LE:       Test @field <= @val
+ *     TRACEFS_COMPARE_RE:       Test @field ~ @val
+ *     TRACEFS_COMPARE_AND:      Test @field & @val
+ *
+ * If the @field is of type string, and @compare is not
+ *   TRACEFS_COMPARE_EQ, TRACEFS_COMPARE_NE or TRACEFS_COMPARE_RE,
+ *   then this will return an error.
+ *
+ * Various other checks are made, for instance, if more CLOSE_PARENs
+ * are added than existing OPEN_PARENs. Or if AND is added after an
+ * OPEN_PAREN or another AND or an OR or a NOT.
+ *
+ * Returns 0 on success and -1 on failure.
+ */
+int tracefs_synth_append_start_filter(struct tracefs_synth *synth,
+				      enum tracefs_filter type,
+				      const char *field,
+				      enum tracefs_compare compare,
+				      const char *val)
+{
+	return trace_append_filter(&synth->start_filter, &synth->start_state,
+				   &synth->start_parens,
+				   synth->start_event,
+				   type, field, compare, val);
+}
+
+/**
+ * tracefs_synth_append_end_filter - create or append a filter
+ * @synth: The tracefs_synth descriptor
+ * @type: The type of element to add to the filter
+ * @field: For @type == TRACEFS_FILTER_COMPARE, the field to compare
+ * @compare: For @type == TRACEFS_FILTER_COMPARE, how to compare @field to @val
+ * @val: For @type == TRACEFS_FILTER_COMPARE, what value @field is to be
+ *
+ * Performs the same thing as tracefs_synth_append_start_filter() but
+ * for the @synth end event.
+ */
+int tracefs_synth_append_end_filter(struct tracefs_synth *synth,
+				    enum tracefs_filter type,
+				    const char *field,
+				    enum tracefs_compare compare,
+				    const char *val)
+{
+	return trace_append_filter(&synth->end_filter, &synth->end_state,
+				   &synth->end_parens,
+				   synth->end_event,
+				   type, field, compare, val);
+}
+
+static int test_max_var(struct tracefs_synth *synth, const char *var)
+{
+	char **vars = synth->end_vars;
+	char *p;
+	int len;
+	int i;
+
+	len = strlen(var);
+
+	/* Make sure the var is defined for the end event */
+	for (i = 0; vars[i]; i++) {
+		p = strchr(vars[i], '=');
+		if (!p)
+			continue;
+		if (p - vars[i] != len)
+			continue;
+		if (!strncmp(var, vars[i], len))
+			return 0;
+	}
+	errno = ENODEV;
+	return -1;
+}
+
+static struct action *create_action(enum tracefs_synth_handler type,
+				    struct tracefs_synth *synth,
+				    const char *var)
+{
+	struct action *action;
+	int ret;
+
+	switch (type) {
+	case TRACEFS_SYNTH_HANDLE_MAX:
+	case TRACEFS_SYNTH_HANDLE_CHANGE:
+		ret = test_max_var(synth, var);
+		if (ret < 0)
+			return NULL;
+		break;
+	default:
+		break;
+	}
+
+	action = calloc(1, sizeof(*action));
+	if (!action)
+		return NULL;
+
+	if (var) {
+		ret = asprintf(&action->handle_field, "$%s", var);
+		if (!action->handle_field) {
+			free(action);
+			return NULL;
+		}
+	}
+	return action;
+}
+
+static void add_action(struct tracefs_synth *synth, struct action *action)
+{
+	*synth->next_action = action;
+	synth->next_action = &action->next;
+}
+
+/**
+ * tracefs_synth_trace - Execute the trace option
+ * @synth: The tracefs_synth descriptor
+ * @type: The type of handler to attach the trace action with
+ * @field: The field for handlers onmax and onchange (ignored otherwise)
+ *
+ * Add the action 'trace' for handlers onmatch, onmax and onchange.
+ *
+ * Returns 0 on succes, -1 on error.
+ */
+int tracefs_synth_trace(struct tracefs_synth *synth,
+			enum tracefs_synth_handler type, const char *field)
+{
+	struct action *action;
+
+	if (!synth || (!field && (type != TRACEFS_SYNTH_HANDLE_MATCH))) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	action = create_action(type, synth, field);
+	if (!action)
+		return -1;
+
+	action->type = ACTION_TRACE;
+	action->handler = type;
+	add_action(synth, action);
+	return 0;
+}
+
+/**
+ * tracefs_synth_snapshot - create a snapshot command to the histogram
+ * @synth: The tracefs_synth descriptor
+ * @type: The type of handler to attach the snapshot action with
+ * @field: The field for handlers onmax and onchange
+ *
+ * Add the action to do a snapshot for handlers onmax and onchange.
+ *
+ * Returns 0 on succes, -1 on error.
+ */
+int tracefs_synth_snapshot(struct tracefs_synth *synth,
+			   enum tracefs_synth_handler type, const char *field)
+{
+	struct action *action;
+
+	if (!synth || !field || (type == TRACEFS_SYNTH_HANDLE_MATCH)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	action = create_action(type, synth, field);
+	if (!action)
+		return -1;
+
+	action->type = ACTION_SNAPSHOT;
+	action->handler = type;
+	add_action(synth, action);
+	return 0;
+}
+
+/**
+ * tracefs_synth_save - create a save command to the histogram
+ * @synth: The tracefs_synth descriptor
+ * @type: The type of handler to attach the save action
+ * @max_field: The field for handlers onmax and onchange
+ * @fields: The fields to save for the end variable
+ *
+ * Add the action to save fields for handlers onmax and onchange.
+ *
+ * Returns 0 on succes, -1 on error.
+ */
+int tracefs_synth_save(struct tracefs_synth *synth,
+		       enum tracefs_synth_handler type, const char *max_field,
+		       char **fields)
+{
+	struct action *action;
+	char *save;
+	int i;
+
+	if (!synth || !max_field || (type == TRACEFS_SYNTH_HANDLE_MATCH)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	action = create_action(type, synth, max_field);
+	if (!action)
+		return -1;
+
+	action->type = ACTION_SAVE;
+	action->handler = type;
+	*synth->next_action = action;
+	synth->next_action = &action->next;
+
+	save = strdup(".save(");
+	if (!save)
+		goto error;
+
+	for (i = 0; fields[i]; i++) {
+		char *delim = i ? "," : NULL;
+
+		if (!trace_verify_event_field(synth->end_event, fields[i], NULL))
+			goto error;
+		save = append_string(save, delim, fields[i]);
+	}
+	save = append_string(save, NULL, ")");
+	if (!save)
+		goto error;
+
+	action->save = save;
+	add_action(synth, action);
+	return 0;
+ error:
+	action_free(action);
+	free(save);
+	return -1;
+	return 0;
+}
+
+static int remove_hist(struct tracefs_instance *instance,
+		       struct tep_event *event, const char *hist)
+{
+	char *str;
+	int ret;
+
+	ret = asprintf(&str, "!%s", hist);
+	if (ret < 0)
+		return -1;
+
+	ret = tracefs_event_file_append(instance, event->system, event->name,
+				  "trigger", str);
+	free(str);
+	return ret < 0 ? -1 : 0;
+}
+
+static char *create_hist(char **keys, char **vars)
+{
+	char *hist = strdup("hist:keys=");
+	char *name;
+	int i;
+
+	if (!hist)
+		return NULL;
+
+	for (i = 0; keys[i]; i++) {
+		name = keys[i];
+		if (i)
+			hist = append_string(hist, NULL, ",");
+		hist = append_string(hist, NULL, name);
+	}
+
+	if (!vars)
+		return hist;
+
+	hist = append_string(hist, NULL, ":");
+
+	for (i = 0; vars[i]; i++) {
+		name = vars[i];
+		if (i)
+			hist = append_string(hist, NULL, ",");
+		hist = append_string(hist, NULL, name);
+	}
+
+	return hist;
+}
+
+static char *create_onmatch(char *hist, struct tracefs_synth *synth)
+{
+	hist = append_string(hist, NULL, ":onmatch(");
+	hist = append_string(hist, NULL, synth->start_event->system);
+	hist = append_string(hist, NULL, ".");
+	hist = append_string(hist, NULL, synth->start_event->name);
+	return append_string(hist, NULL, ")");
+}
+
+static char *create_trace(char *hist, struct tracefs_synth *synth)
+{
+	char *name;
+	int i;
+
+	if (synth->new_format) {
+		hist = append_string(hist, NULL, ".trace(");
+		hist = append_string(hist, NULL, synth->name);
+		hist = append_string(hist, NULL, ",");
+	} else {
+		hist = append_string(hist, NULL, ".");
+		hist = append_string(hist, NULL, synth->name);
+		hist = append_string(hist, NULL, "(");
+	}
+
+	for (i = 0; synth->synthetic_args && synth->synthetic_args[i]; i++) {
+		name = synth->synthetic_args[i];
+
+		if (i)
+			hist = append_string(hist, NULL, ",");
+		hist = append_string(hist, NULL, name);
+	}
+
+	return append_string(hist, NULL, ")");
+}
+
+static char *create_max(char *hist, struct tracefs_synth *synth, char *field)
+{
+	hist = append_string(hist, NULL, ":onmax(");
+	hist = append_string(hist, NULL, field);
+	return append_string(hist, NULL, ")");
+}
+
+static char *create_change(char *hist, struct tracefs_synth *synth, char *field)
+{
+	hist = append_string(hist, NULL, ":onchange(");
+	hist = append_string(hist, NULL, field);
+	return append_string(hist, NULL, ")");
+}
+
+static char *create_actions(char *hist, struct tracefs_synth *synth)
+{
+	struct action *action;
+
+	if (!synth->actions) {
+		/* Default is "onmatch" and "trace" */
+		hist = create_onmatch(hist, synth);
+		return create_trace(hist, synth);
+	}
+
+	for (action = synth->actions; action; action = action->next) {
+		switch (action->handler) {
+		case TRACEFS_SYNTH_HANDLE_MATCH:
+			hist = create_onmatch(hist, synth);
+			break;
+		case TRACEFS_SYNTH_HANDLE_MAX:
+			hist = create_max(hist, synth, action->handle_field);
+			break;
+		case TRACEFS_SYNTH_HANDLE_CHANGE:
+			hist = create_change(hist, synth, action->handle_field);
+			break;
+		default:
+			continue;
+		}
+		switch (action->type) {
+		case ACTION_TRACE:
+			hist = create_trace(hist, synth);
+			break;
+		case ACTION_SNAPSHOT:
+			hist = append_string(hist, NULL, ".snapshot()");
+			break;
+		case ACTION_SAVE:
+			hist = append_string(hist, NULL, action->save);
+			break;
+		default:
+			continue;
+		}
+	}
+	return hist;
+}
+
+static char *create_end_hist(struct tracefs_synth *synth)
+{
+	char *end_hist;
+
+	end_hist = create_hist(synth->end_keys, synth->end_vars);
+	return create_actions(end_hist, synth);
+}
+
+/*
+ * tracefs_synth_raw_fmt - show the raw format of a synthetic event
+ * @seq: A trace_seq to store the format string
+ * @synth: The synthetic event to read format from
+ *
+ * This shows the raw format that describes the synthetic event, including
+ * the format of the dynamic event and the start / end histograms.
+ *
+ * Returns 0 on succes -1 on error.
+ */
+int tracefs_synth_raw_fmt(struct trace_seq *seq, struct tracefs_synth *synth)
+{
+	if (!synth->dyn_event)
+		return -1;
+
+	trace_seq_printf(seq, "%s", synth->dyn_event->format);
+	trace_seq_printf(seq, "\n%s", synth->start_hist);
+	trace_seq_printf(seq, "\n%s", synth->end_hist);
+
+	return 0;
+}
+
+/*
+ * tracefs_synth_show_event - show the dynamic event used by a synth event
+ * @synth: The synthetic event to read format from
+ *
+ * This shows the raw format of the dynamic event used by the synthetic event.
+ *
+ * Returns format string owned by @synth on success, or NULL on error.
+ */
+const char *tracefs_synth_show_event(struct tracefs_synth *synth)
+{
+	return synth->dyn_event ? synth->dyn_event->format : NULL;
+}
+
+/*
+ * tracefs_synth_show_start_hist - show the start histogram used by a synth event
+ * @synth: The synthetic event to read format from
+ *
+ * This shows the raw format of the start histogram used by the synthetic event.
+ *
+ * Returns format string owned by @synth on success, or NULL on error.
+ */
+const char *tracefs_synth_show_start_hist(struct tracefs_synth *synth)
+{
+	return synth->start_hist;
+}
+
+/*
+ * tracefs_synth_show_end_hist - show the end histogram used by a synth event
+ * @synth: The synthetic event to read format from
+ *
+ * This shows the raw format of the end histogram used by the synthetic event.
+ *
+ * Returns format string owned by @synth on success, or NULL on error.
+ */
+const char *tracefs_synth_show_end_hist(struct tracefs_synth *synth)
+{
+	return synth->end_hist;
+}
+
+static char *append_filter(char *hist, char *filter, unsigned int parens)
+{
+	int i;
+
+	if (!filter)
+		return hist;
+
+	hist = append_string(hist, NULL, " if ");
+	hist = append_string(hist, NULL, filter);
+	for (i = 0; i < parens; i++)
+		hist = append_string(hist, NULL, ")");
+	return hist;
+}
+
+static int verify_state(struct tracefs_synth *synth)
+{
+	if (trace_test_state(synth->start_state) < 0 ||
+	    trace_test_state(synth->end_state) < 0)
+		return -1;
+	return 0;
+}
+
+/**
+ * tracefs_synth_complete - tell if the tracefs_synth is complete or not
+ * @synth: The synthetic event to get the start hist from.
+ *
+ * Retruns true if the synthetic event @synth has both a start and
+ * end event (ie. a synthetic event, or just a histogram), and
+ * false otherwise.
+ */
+bool tracefs_synth_complete(struct tracefs_synth *synth)
+{
+	return synth && synth->start_event && synth->end_event;
+}
+
+/**
+ * tracefs_synth_get_start_hist - Return the histogram of the start event
+ * @synth: The synthetic event to get the start hist from.
+ *
+ * On success, returns a tracefs_hist descriptor that holds the
+ * histogram information of the start_event of the synthetic event
+ * structure. Returns NULL on failure.
+ */
+struct tracefs_hist *
+tracefs_synth_get_start_hist(struct tracefs_synth *synth)
+{
+	struct tracefs_hist *hist = NULL;
+	struct tep_handle *tep;
+	const char *system;
+	const char *event;
+	const char *key;
+	char **keys;
+	int *types;
+	int ret;
+	int i;
+
+	if (!synth) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	system = synth->start_event->system;
+	event = synth->start_event->name;
+	types = synth->start_type;
+	keys = synth->start_keys;
+	tep = synth->tep;
+
+	if (!keys)
+		keys = synth->start_selection;
+
+	if (!keys)
+		return NULL;
+
+	for (i = 0; keys[i]; i++) {
+		int type = types ? types[i] : 0;
+
+		if (type == HIST_COUNTER_TYPE)
+			continue;
+
+		key = keys[i];
+
+		if (i) {
+			ret = tracefs_hist_add_key(hist, key, type);
+			if (ret < 0) {
+				tracefs_hist_free(hist);
+				return NULL;
+			}
+		} else {
+			hist = tracefs_hist_alloc(tep, system, event,
+						  key, type);
+			if (!hist)
+				return NULL;
+		}
+	}
+
+	if (!hist)
+		return NULL;
+
+	for (i = 0; keys[i]; i++) {
+		int type = types ? types[i] : 0;
+
+		if (type != HIST_COUNTER_TYPE)
+			continue;
+
+		key = keys[i];
+
+		ret = tracefs_hist_add_value(hist, key);
+		if (ret < 0) {
+			tracefs_hist_free(hist);
+			return NULL;
+		}
+	}
+
+	if (synth->start_filter) {
+		hist->filter = strdup(synth->start_filter);
+		if (!hist->filter) {
+			tracefs_hist_free(hist);
+			return NULL;
+		}
+	}
+
+	return hist;
+}
+
+/**
+ * tracefs_synth_create - creates the synthetic event on the system
+ * @synth: The tracefs_synth descriptor
+ *
+ * This creates the synthetic events.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be or a problem
+ *   writing into the system.
+ */
+int tracefs_synth_create(struct tracefs_synth *synth)
+{
+	int ret;
+
+	if (!synth) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!synth->name || !synth->end_event) {
+		errno = EUNATCH;
+		return -1;
+	}
+
+	if (verify_state(synth) < 0)
+		return -1;
+
+	if (!synth->dyn_event && alloc_synthetic_event(synth))
+		return -1;
+	if (tracefs_dynevent_create(synth->dyn_event))
+		return -1;
+
+	synth->start_hist = create_hist(synth->start_keys, synth->start_vars);
+	synth->start_hist = append_filter(synth->start_hist, synth->start_filter,
+					  synth->start_parens);
+	if (!synth->start_hist)
+		goto remove_synthetic;
+
+	synth->end_hist = create_end_hist(synth);
+	synth->end_hist = append_filter(synth->end_hist, synth->end_filter,
+					synth->end_parens);
+	if (!synth->end_hist)
+		goto remove_synthetic;
+
+	ret = tracefs_event_file_append(synth->instance, synth->start_event->system,
+					synth->start_event->name,
+					"trigger", synth->start_hist);
+	if (ret < 0)
+		goto remove_synthetic;
+
+	ret = tracefs_event_file_append(synth->instance, synth->end_event->system,
+					synth->end_event->name,
+					"trigger", synth->end_hist);
+	if (ret < 0)
+		goto remove_start_hist;
+
+	return 0;
+
+ remove_start_hist:
+	remove_hist(synth->instance, synth->start_event, synth->start_hist);
+ remove_synthetic:
+	tracefs_dynevent_destroy(synth->dyn_event, false);
+	return -1;
+}
+
+/**
+ * tracefs_synth_destroy - delete the synthetic event from the system
+ * @synth: The tracefs_synth descriptor
+ *
+ * This will destroy a synthetic event created by tracefs_synth_create()
+ * with the same @synth.
+ *
+ * It will attempt to disable the synthetic event in its instance (top by default),
+ * but if other instances have it active, it is likely to fail, which will likely
+ * fail on all other parts of tearing down the synthetic event.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be or a problem
+ *   writing into the system.
+ */
+int tracefs_synth_destroy(struct tracefs_synth *synth)
+{
+	char *hist;
+	int ret;
+
+	if (!synth) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!synth->name || !synth->end_event) {
+		errno = EUNATCH;
+		return -1;
+	}
+
+	/* Try to disable the event if possible */
+	tracefs_event_disable(synth->instance, "synthetic", synth->name);
+
+	hist = create_end_hist(synth);
+	hist = append_filter(hist, synth->end_filter,
+			     synth->end_parens);
+	if (!hist)
+		return -1;
+	ret = remove_hist(synth->instance, synth->end_event, hist);
+	free(hist);
+
+	hist = create_hist(synth->start_keys, synth->start_vars);
+	hist = append_filter(hist, synth->start_filter,
+			     synth->start_parens);
+	if (!hist)
+		return -1;
+
+	ret = remove_hist(synth->instance, synth->start_event, hist);
+	free(hist);
+
+	ret = tracefs_dynevent_destroy(synth->dyn_event, true);
+
+	return ret ? -1 : 0;
+}
+
+/**
+ * tracefs_synth_echo_cmd - show the command lines to create the synthetic event
+ * @seq: The trace_seq to store the command lines in
+ * @synth: The tracefs_synth descriptor
+ *
+ * This will list the "echo" commands that are equivalent to what would
+ * be executed by the tracefs_synth_create() command.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ */
+int tracefs_synth_echo_cmd(struct trace_seq *seq,
+			   struct tracefs_synth *synth)
+{
+	bool new_event = false;
+	char *hist = NULL;
+	char *path;
+	int ret = -1;
+
+	if (!synth) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!synth->name || !synth->end_event) {
+		errno = EUNATCH;
+		return -1;
+	}
+
+	if (!synth->dyn_event) {
+		if (alloc_synthetic_event(synth))
+			return -1;
+		new_event = true;
+	}
+
+	path = trace_find_tracing_dir(false);
+	if (!path)
+		goto out_free;
+
+	trace_seq_printf(seq, "echo '%s%s%s %s' >> %s/%s\n",
+			 synth->dyn_event->prefix,
+			 strlen(synth->dyn_event->prefix) ? ":" : "",
+			 synth->dyn_event->event,
+			 synth->dyn_event->format, path, synth->dyn_event->trace_file);
+
+	tracefs_put_tracing_file(path);
+	path = tracefs_instance_get_dir(synth->instance);
+
+	hist = create_hist(synth->start_keys, synth->start_vars);
+	hist = append_filter(hist, synth->start_filter,
+			     synth->start_parens);
+	if (!hist)
+		goto out_free;
+
+	trace_seq_printf(seq, "echo '%s' >> %s/events/%s/%s/trigger\n",
+			 hist, path, synth->start_event->system,
+			 synth->start_event->name);
+	free(hist);
+	hist = create_end_hist(synth);
+	hist = append_filter(hist, synth->end_filter,
+			     synth->end_parens);
+	if (!hist)
+		goto out_free;
+
+	trace_seq_printf(seq, "echo '%s' >> %s/events/%s/%s/trigger\n",
+			 hist, path, synth->end_event->system,
+			 synth->end_event->name);
+
+	ret = 0;
+ out_free:
+	free(hist);
+	tracefs_put_tracing_file(path);
+	if (new_event) {
+		tracefs_dynevent_free(synth->dyn_event);
+		synth->dyn_event = NULL;
+	}
+	return ret;
+}
+
+/**
+ * tracefs_synth_get_event - return tep event representing the given synthetic event
+ * @tep: a handle to the trace event parser context that holds the events
+ * @synth: a synthetic event context, describing given synthetic event.
+ *
+ * Returns a pointer to a tep event describing the given synthetic event. The pointer
+ * is managed by the @tep handle and must not be freed. In case of an error, or in case
+ * the requested synthetic event is missing in the @tep handler - NULL is returned.
+ */
+struct tep_event *
+tracefs_synth_get_event(struct tep_handle *tep, struct tracefs_synth *synth)
+{
+	if (!tep || !synth || !synth->name)
+		return NULL;
+
+	return get_tep_event(tep, SYNTHETIC_GROUP, synth->name);
+}
diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c
new file mode 100644
index 0000000..57f5c7f
--- /dev/null
+++ b/src/tracefs-instance.c
@@ -0,0 +1,1241 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ * Updates:
+ * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <regex.h>
+#include <limits.h>
+#include <pthread.h>
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+enum {
+	FLAG_INSTANCE_NEWLY_CREATED	= (1 << 0),
+	FLAG_INSTANCE_DELETED		= (1 << 1),
+};
+
+
+struct tracefs_options_mask	toplevel_supported_opts;
+struct tracefs_options_mask	toplevel_enabled_opts;
+
+__hidden inline struct tracefs_options_mask *
+supported_opts_mask(struct tracefs_instance *instance)
+{
+	return instance ? &instance->supported_opts : &toplevel_supported_opts;
+}
+
+__hidden inline struct tracefs_options_mask *
+enabled_opts_mask(struct tracefs_instance *instance)
+{
+	return instance ? &instance->enabled_opts : &toplevel_enabled_opts;
+}
+
+/**
+ * instance_alloc - allocate a new ftrace instance
+ * @trace_dir - Full path to the tracing directory, where the instance is
+ * @name: The name of the instance (instance will point to this)
+ *
+ * Returns a newly allocated instance, or NULL in case of an error.
+ */
+static struct tracefs_instance *instance_alloc(const char *trace_dir, const char *name)
+{
+	struct tracefs_instance *instance;
+
+	instance = calloc(1, sizeof(*instance));
+	if (!instance)
+		goto error;
+	instance->trace_dir = strdup(trace_dir);
+	if (!instance->trace_dir)
+		goto error;
+	if (name) {
+		instance->name = strdup(name);
+		if (!instance->name)
+			goto error;
+	}
+
+	if (pthread_mutex_init(&instance->lock, NULL) < 0)
+		goto error;
+
+	instance->ftrace_filter_fd = -1;
+	instance->ftrace_notrace_fd = -1;
+	instance->ftrace_marker_fd = -1;
+	instance->ftrace_marker_raw_fd = -1;
+
+	return instance;
+
+error:
+	if (instance) {
+		free(instance->name);
+		free(instance->trace_dir);
+		free(instance);
+	}
+	return NULL;
+}
+
+
+__hidden int trace_get_instance(struct tracefs_instance *instance)
+{
+	int ret;
+
+	pthread_mutex_lock(&instance->lock);
+	if (instance->flags & FLAG_INSTANCE_DELETED) {
+		ret = -1;
+	} else {
+		instance->ref++;
+		ret = 0;
+	}
+	pthread_mutex_unlock(&instance->lock);
+	return ret;
+}
+
+__hidden void trace_put_instance(struct tracefs_instance *instance)
+{
+	pthread_mutex_lock(&instance->lock);
+	if (--instance->ref < 0)
+		instance->flags |= FLAG_INSTANCE_DELETED;
+	pthread_mutex_unlock(&instance->lock);
+
+	if (!(instance->flags & FLAG_INSTANCE_DELETED))
+		return;
+
+	if (instance->ftrace_filter_fd >= 0)
+		close(instance->ftrace_filter_fd);
+
+	if (instance->ftrace_notrace_fd >= 0)
+		close(instance->ftrace_notrace_fd);
+
+	if (instance->ftrace_marker_fd >= 0)
+		close(instance->ftrace_marker_fd);
+
+	if (instance->ftrace_marker_raw_fd >= 0)
+		close(instance->ftrace_marker_raw_fd);
+
+	free(instance->trace_dir);
+	free(instance->name);
+	pthread_mutex_destroy(&instance->lock);
+	free(instance);
+}
+
+/**
+ * tracefs_instance_free - Free an instance, previously allocated by
+			   tracefs_instance_create()
+ * @instance: Pointer to the instance to be freed
+ *
+ */
+void tracefs_instance_free(struct tracefs_instance *instance)
+{
+	if (!instance)
+		return;
+
+	trace_put_instance(instance);
+}
+
+static mode_t get_trace_file_permissions(char *name)
+{
+	mode_t rmode = 0;
+	struct stat st;
+	char *path;
+	int ret;
+
+	path = tracefs_get_tracing_file(name);
+	if (!path)
+		return 0;
+	ret = stat(path, &st);
+	if (ret)
+		goto out;
+	rmode = st.st_mode & ACCESSPERMS;
+out:
+	tracefs_put_tracing_file(path);
+	return rmode;
+}
+
+/**
+ * tracefs_instance_is_new - Check if the instance is newly created by the library
+ * @instance: Pointer to an ftrace instance
+ *
+ * Returns true, if the ftrace instance is newly created by the library or
+ * false otherwise.
+ */
+bool tracefs_instance_is_new(struct tracefs_instance *instance)
+{
+	if (instance && (instance->flags & FLAG_INSTANCE_NEWLY_CREATED))
+		return true;
+	return false;
+}
+
+/**
+ * tracefs_instance_create - Create a new ftrace instance
+ * @name: Name of the instance to be created
+ *
+ * Allocates and initializes a new instance structure. If the instance does not
+ * exist in the system, create it.
+ * Returns a pointer to a newly allocated instance, or NULL in case of an error.
+ * The returned instance must be freed by tracefs_instance_free().
+ */
+struct tracefs_instance *tracefs_instance_create(const char *name)
+{
+	struct tracefs_instance *inst = NULL;
+	char *path = NULL;
+	const char *tdir;
+	struct stat st;
+	mode_t mode;
+	int ret;
+
+	tdir = tracefs_tracing_dir();
+	if (!tdir)
+		return NULL;
+	inst = instance_alloc(tdir, name);
+	if (!inst)
+		return NULL;
+
+	path = tracefs_instance_get_dir(inst);
+	ret = stat(path, &st);
+	if (ret < 0) {
+		/* Cannot create the top instance, if it does not exist! */
+		if (!name)
+			goto error;
+		mode = get_trace_file_permissions("instances");
+		if (mkdir(path, mode))
+			goto error;
+		inst->flags |= FLAG_INSTANCE_NEWLY_CREATED;
+	}
+	tracefs_put_tracing_file(path);
+	return inst;
+
+error:
+	tracefs_instance_free(inst);
+	return NULL;
+}
+
+/**
+ * tracefs_instance_alloc - Allocate an instance structure for existing trace instance
+ * @tracing_dir: full path to the system trace directory, where the new instance is
+ *		 if NULL, the default top tracing directory is used.
+ * @name: Name of the instance.
+ *
+ * Allocates and initializes a new instance structure. If the instance does not
+ * exist, do not create it and exit with error.
+ * Returns a pointer to a newly allocated instance, or NULL in case of an error
+ * or the requested instance does not exists.
+ * The returned instance must be freed by tracefs_instance_free().
+ */
+struct tracefs_instance *tracefs_instance_alloc(const char *tracing_dir,
+						const char *name)
+{
+	struct tracefs_instance *inst = NULL;
+	char file[PATH_MAX];
+	const char *tdir;
+	struct stat st;
+	int ret;
+
+	if (tracing_dir) {
+		ret = stat(tracing_dir, &st);
+		if (ret < 0 || !S_ISDIR(st.st_mode))
+			return NULL;
+		tdir = tracing_dir;
+
+	} else
+		tdir = tracefs_tracing_dir();
+	if (!tdir)
+		return NULL;
+
+	if (name) {
+		sprintf(file, "%s/instances/%s", tdir, name);
+		ret = stat(file, &st);
+		if (ret < 0 || !S_ISDIR(st.st_mode))
+			return NULL;
+	}
+	inst = instance_alloc(tdir, name);
+
+	return inst;
+}
+
+/**
+ * tracefs_instance_destroy - Remove a ftrace instance
+ * @instance: Pointer to the instance to be removed
+ *
+ * Returns -1 in case of an error, or 0 otherwise.
+ */
+int tracefs_instance_destroy(struct tracefs_instance *instance)
+{
+	char *path;
+	int ret = -1;
+
+	if (!instance || !instance->name) {
+		tracefs_warning("Cannot remove top instance");
+		return -1;
+	}
+
+	path = tracefs_instance_get_dir(instance);
+	if (path)
+		ret = rmdir(path);
+	tracefs_put_tracing_file(path);
+	if (ret) {
+		pthread_mutex_lock(&instance->lock);
+		instance->flags |= FLAG_INSTANCE_DELETED;
+		pthread_mutex_unlock(&instance->lock);
+	}
+
+	return ret;
+}
+
+/**
+ * tracefs_instance_get_file - return the path to an instance file.
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @file: name of file to return
+ *
+ * Returns the path of the @file for the given @instance, or NULL in
+ * case of an error.
+ *
+ * Must use tracefs_put_tracing_file() to free the returned string.
+ */
+char *
+tracefs_instance_get_file(struct tracefs_instance *instance, const char *file)
+{
+	char *path = NULL;
+	int ret;
+
+	if (!instance)
+		return tracefs_get_tracing_file(file);
+	if (!instance->name)
+		ret = asprintf(&path, "%s/%s", instance->trace_dir, file);
+	else
+		ret = asprintf(&path, "%s/instances/%s/%s",
+			       instance->trace_dir, instance->name, file);
+	if (ret < 0)
+		return NULL;
+
+	return path;
+}
+
+/**
+ * tracefs_instance_get_dir - return the path to the instance directory.
+ * @instance: ftrace instance, can be NULL for the top instance
+ *
+ * Returns the full path to the instance directory
+ *
+ * Must use tracefs_put_tracing_file() to free the returned string.
+ */
+char *tracefs_instance_get_dir(struct tracefs_instance *instance)
+{
+	char *path = NULL;
+	int ret;
+
+	if (!instance) /* Top instance of default system trace directory */
+		return trace_find_tracing_dir(false);
+
+	if (!instance->name)
+		return strdup(instance->trace_dir);
+
+	ret = asprintf(&path, "%s/instances/%s", instance->trace_dir, instance->name);
+	if (ret < 0) {
+		tracefs_warning("Failed to allocate path for instance %s",
+				instance->name);
+		return NULL;
+	}
+
+	return path;
+}
+
+/**
+ * tracefs_instance_get_name - return the name of an instance
+ * @instance: ftrace instance
+ *
+ * Returns the name of the given @instance.
+ * The returned string must *not* be freed.
+ */
+const char *tracefs_instance_get_name(struct tracefs_instance *instance)
+{
+	if (instance)
+		return instance->name;
+	return NULL;
+}
+
+/**
+ * tracefs_instance_get_buffer_size - return the buffer size of the ring buffer
+ * @instance: The instance to get the buffer size from
+ * @cpu: if less that zero, will return the total size, otherwise the cpu size
+ *
+ * Returns the buffer size. If @cpu is less than zero, it returns the total size
+ * of the ring buffer otherwise it returs the size of the buffer for the given
+ * CPU.
+ *
+ * Returns -1 on error.
+ */
+ssize_t tracefs_instance_get_buffer_size(struct tracefs_instance *instance, int cpu)
+{
+	unsigned long long size;
+	char *path;
+	char *val;
+	int ret;
+
+	if (cpu < 0) {
+		val = tracefs_instance_file_read(instance, "buffer_total_size_kb", NULL);
+	} else {
+		ret = asprintf(&path, "per_cpu/cpu%d/buffer_size_kb", cpu);
+		if (ret < 0)
+			return ret;
+
+		val = tracefs_instance_file_read(instance, path, NULL);
+		free(path);
+	}
+
+	if (!val)
+		return -1;
+
+	size = strtoull(val, NULL, 0);
+	free(val);
+	return size;
+}
+
+int tracefs_instance_set_buffer_size(struct tracefs_instance *instance, size_t size, int cpu)
+{
+	char *path;
+	char *val;
+	int ret;
+
+	ret = asprintf(&val, "%zd", size);
+	if (ret < 0)
+		return ret;
+
+	if (cpu < 0) {
+		ret = tracefs_instance_file_write(instance, "buffer_size_kb", val);
+	} else {
+		ret = asprintf(&path, "per_cpu/cpu%d/buffer_size_kb", cpu);
+		if (ret < 0) {
+			free(val);
+			return ret;
+		}
+
+		ret = tracefs_instance_file_write(instance, path, val);
+		free(path);
+	}
+	free(val);
+
+	return ret < 0 ? -1 : 0;
+}
+
+/**
+ * tracefs_instance_get_trace_dir - return the top trace directory, where the instance is confuigred
+ * @instance: ftrace instance
+ *
+ * Returns the top trace directory where the given @instance is configured.
+ * The returned string must *not* be freed.
+ */
+const char *tracefs_instance_get_trace_dir(struct tracefs_instance *instance)
+{
+	if (instance)
+		return instance->trace_dir;
+	return NULL;
+}
+
+static int write_file(const char *file, const char *str, int flags)
+{
+	int ret = 0;
+	int fd;
+
+	fd = open(file, flags);
+	if (fd < 0) {
+		tracefs_warning("Failed to open '%s'", file);
+		return -1;
+	}
+
+	if (str)
+		ret = write(fd, str, strlen(str));
+
+	close(fd);
+	return ret;
+}
+
+static int instance_file_write(struct tracefs_instance *instance,
+			       const char *file, const char *str, int flags)
+{
+	struct stat st;
+	char *path;
+	int ret;
+
+	path = tracefs_instance_get_file(instance, file);
+	if (!path)
+		return -1;
+	ret = stat(path, &st);
+	if (ret == 0)
+		ret = write_file(path, str, flags);
+	tracefs_put_tracing_file(path);
+
+	return ret;
+}
+
+/**
+ * tracefs_instance_file_write - Write in trace file of specific instance.
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @file: name of the file
+ * @str: nul terminated string, that will be written in the file.
+ *
+ * Returns the number of written bytes, or -1 in case of an error
+ */
+int tracefs_instance_file_write(struct tracefs_instance *instance,
+				 const char *file, const char *str)
+{
+	return instance_file_write(instance, file, str, O_WRONLY | O_TRUNC);
+}
+
+/**
+ * tracefs_instance_file_append - Append to a trace file of specific instance.
+ * @instance: ftrace instance, can be NULL for the top instance.
+ * @file: name of the file.
+ * @str: nul terminated string, that will be appended to the file.
+ *
+ * Returns the number of appended bytes, or -1 in case of an error.
+ */
+int tracefs_instance_file_append(struct tracefs_instance *instance,
+				 const char *file, const char *str)
+{
+	return instance_file_write(instance, file, str, O_WRONLY);
+}
+
+/**
+ * tracefs_instance_file_clear - Clear a trace file of specific instance.
+ * Note, it only opens with O_TRUNC and closes the file. If the file has
+ * content that does not get cleared in this way, this will not have any
+ * effect. For example, set_ftrace_filter can have probes that are not
+ * cleared by O_TRUNC:
+ *
+ * echo "schedule:stacktrace" > set_ftrace_filter
+ *
+ * This function will not clear the above "set_ftrace_filter" after that
+ * command.
+ * @instance: ftrace instance, can be NULL for the top instance.
+ * @file: name of the file to clear.
+ *
+ * Returns 0 on success, or -1 in case of an error.
+ */
+int tracefs_instance_file_clear(struct tracefs_instance *instance,
+				const char *file)
+{
+	return instance_file_write(instance, file, NULL, O_WRONLY | O_TRUNC);
+}
+
+/**
+ * tracefs_instance_file_read - Read from a trace file of specific instance.
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @file: name of the file
+ * @psize: returns the number of bytes read
+ *
+ * Returns a pointer to a nul terminated string, read from the file, or NULL in
+ * case of an error.
+ * The return string must be freed by free()
+ */
+char *tracefs_instance_file_read(struct tracefs_instance *instance,
+				 const char *file, int *psize)
+{
+	char *buf = NULL;
+	int size = 0;
+	char *path;
+
+	path = tracefs_instance_get_file(instance, file);
+	if (!path)
+		return NULL;
+
+	size = str_read_file(path, &buf, true);
+
+	tracefs_put_tracing_file(path);
+	if (buf && psize)
+		*psize = size;
+
+	return buf;
+}
+
+/**
+ * tracefs_instance_file_read_number - Read long long integer from a trace file.
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @file: name of the file
+ * @res: The integer from the file.
+ *
+ * Returns 0 if the reading is successful and the result is stored in res, -1
+ * in case of an error.
+ */
+int tracefs_instance_file_read_number(struct tracefs_instance *instance,
+				      const char *file, long long *res)
+{
+	long long num;
+	int ret = -1;
+	int size = 0;
+	char *endptr;
+	char *str;
+
+	str = tracefs_instance_file_read(instance, file, &size);
+	if (size && str) {
+		errno = 0;
+		num = strtoll(str, &endptr, 0);
+		if (errno == 0 && str != endptr) {
+			*res = num;
+			ret = 0;
+		}
+	}
+	free(str);
+	return ret;
+}
+
+/**
+ * tracefs_instance_file_open - Open a trace file for reading and writing
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @file: name of the file
+ * @mode: file open flags, -1 for default O_RDWR
+ *
+ * Returns -1 in case of an error, or a valid file descriptor otherwise.
+ * The returned FD must be closed with close()
+ */
+int tracefs_instance_file_open(struct tracefs_instance *instance,
+			       const char *file, int mode)
+{
+	int flags = O_RDWR;
+	int fd = -1;
+	char *path;
+
+	path = tracefs_instance_get_file(instance, file);
+	if (!path)
+		return -1;
+
+	if (mode >= 0)
+		flags = mode;
+	fd = open(path, flags);
+	tracefs_put_tracing_file(path);
+
+	return fd;
+}
+
+static bool check_file_exists(struct tracefs_instance *instance,
+			      const char *name, bool dir)
+{
+	char file[PATH_MAX];
+	struct stat st;
+	char *path;
+	int ret;
+
+	path = tracefs_instance_get_dir(instance);
+	if (name)
+		snprintf(file, PATH_MAX, "%s/%s", path, name);
+	else
+		snprintf(file, PATH_MAX, "%s", path);
+	tracefs_put_tracing_file(path);
+	ret = stat(file, &st);
+	if (ret < 0)
+		return false;
+
+	return !dir == !S_ISDIR(st.st_mode);
+}
+
+/**
+ * tracefs_instance_exists - Check an instance with given name exists
+ * @name: name of the instance
+ *
+ * Returns true if the instance exists, false otherwise
+ *
+ */
+bool tracefs_instance_exists(const char *name)
+{
+	char file[PATH_MAX];
+
+	if (!name)
+		return false;
+	snprintf(file, PATH_MAX, "instances/%s", name);
+	return check_file_exists(NULL, file, true);
+}
+
+/**
+ * tracefs_file_exists - Check if a file with given name exists in given instance
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @name: name of the file
+ *
+ * Returns true if the file exists, false otherwise
+ *
+ * If a directory with the given name exists, false is returned.
+ */
+bool tracefs_file_exists(struct tracefs_instance *instance, const char *name)
+{
+	return check_file_exists(instance, name, false);
+}
+
+/**
+ * tracefs_dir_exists - Check if a directory with given name exists in given instance
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @name: name of the directory
+ *
+ * Returns true if the directory exists, false otherwise
+ */
+bool tracefs_dir_exists(struct tracefs_instance *instance, const char *name)
+{
+	return check_file_exists(instance, name, true);
+}
+
+/**
+ * tracefs_instances_walk - Iterate through all ftrace instances in the system
+ * @callback: user callback, called for each instance. Instance name is passed
+ *	      as input parameter. If the @callback returns non-zero,
+ *	      the iteration stops.
+ * @context: user context, passed to the @callback.
+ *
+ * Returns -1 in case of an error, 1 if the iteration was stopped because of the
+ * callback return value or 0 otherwise.
+ */
+int tracefs_instances_walk(int (*callback)(const char *, void *), void *context)
+{
+	struct dirent *dent;
+	char *path = NULL;
+	DIR *dir = NULL;
+	struct stat st;
+	int fret = -1;
+	int ret;
+
+	path = tracefs_get_tracing_file("instances");
+	if (!path)
+		return -1;
+	ret = stat(path, &st);
+	if (ret < 0 || !S_ISDIR(st.st_mode))
+		goto out;
+
+	dir = opendir(path);
+	if (!dir)
+		goto out;
+	fret = 0;
+	while ((dent = readdir(dir))) {
+		char *instance;
+
+		if (strcmp(dent->d_name, ".") == 0 ||
+		    strcmp(dent->d_name, "..") == 0)
+			continue;
+		instance = trace_append_file(path, dent->d_name);
+		ret = stat(instance, &st);
+		free(instance);
+		if (ret < 0 || !S_ISDIR(st.st_mode))
+			continue;
+		if (callback(dent->d_name, context)) {
+			fret = 1;
+			break;
+		}
+	}
+
+out:
+	if (dir)
+		closedir(dir);
+	tracefs_put_tracing_file(path);
+	return fret;
+}
+
+static inline bool match(const char *str, regex_t *re)
+{
+	if (!re)
+		return true;
+	return regexec(re, str, 0, NULL, 0) == 0;
+}
+
+struct instance_list {
+	regex_t		*re;
+	char		**list;
+	int		failed;
+};
+
+static int build_list(const char *name, void *data)
+{
+	struct instance_list *list = data;
+	char **instances;
+	int ret = -1;
+
+	if (!match(name, list->re))
+		return 0;
+
+	instances = tracefs_list_add(list->list, name);
+	if (!instances)
+		goto out;
+
+	list->list = instances;
+	ret = 0;
+
+ out:
+	list->failed = ret;
+	return ret;
+}
+
+/**
+ * tracefs_instances - return a list of instance names
+ * @regex: A regex of instances to filter on (NULL to match all)
+ *
+ * Returns a list of names of existing instances, that must be
+ * freed with tracefs_list_free(). Note, if there are no matches
+ * then an empty list will be returned (not NULL).
+ * NULL on error.
+ */
+char **tracefs_instances(const char *regex)
+{
+	struct instance_list list = { .re = NULL, .list = NULL };
+	regex_t re;
+	int ret;
+
+	if (regex) {
+		ret = regcomp(&re, regex, REG_ICASE|REG_NOSUB);
+		if (ret < 0)
+			return NULL;
+		list.re = &re;
+	}
+
+	ret = tracefs_instances_walk(build_list, &list);
+	if (ret < 0 || list.failed) {
+		tracefs_list_free(list.list);
+		list.list = NULL;
+	} else {
+		/* No matches should produce an empty list */
+		if (!list.list)
+			list.list = trace_list_create_empty();
+	}
+	return list.list;
+}
+
+/**
+ * tracefs_get_clock - Get the current trace clock
+ * @instance: ftrace instance, can be NULL for the top instance
+ *
+ * Returns the current trace clock of the given instance, or NULL in
+ * case of an error.
+ * The return string must be freed by free()
+ */
+char *tracefs_get_clock(struct tracefs_instance *instance)
+{
+	char *all_clocks = NULL;
+	char *ret = NULL;
+	int bytes = 0;
+	char *clock;
+	char *cont;
+
+	all_clocks  = tracefs_instance_file_read(instance, "trace_clock", &bytes);
+	if (!all_clocks || !bytes)
+		goto out;
+
+	clock = strstr(all_clocks, "[");
+	if (!clock)
+		goto out;
+	clock++;
+	cont = strstr(clock, "]");
+	if (!cont)
+		goto out;
+	*cont = '\0';
+
+	ret = strdup(clock);
+out:
+	free(all_clocks);
+	return ret;
+}
+
+/**
+ * tracefs_instance_set_affinity_raw - write a hex bitmask into the affinity
+ * @instance: The instance to set affinity to (NULL for top level)
+ * @mask: String containing the hex value to set the tracing affinity to.
+ *
+ * Sets the tracing affinity CPU mask for @instance. The @mask is the raw
+ * value that is used to write into the tracing system.
+ *
+ * Return 0 on success and -1 on error.
+ */
+int tracefs_instance_set_affinity_raw(struct tracefs_instance *instance,
+				      const char *mask)
+{
+	return tracefs_instance_file_write(instance, "tracing_cpumask", mask);
+}
+
+/**
+ * tracefs_instance_set_affinity_set - use a cpu_set to define tracing affinity
+ * @instance: The instance to set affinity to (NULL for top level)
+ * @set: A CPU set that describes the CPU affinity to set tracing to.
+ * @set_size: The size in bytes of @set (use CPU_ALLOC_SIZE() to get this value)
+ *
+ * Sets the tracing affinity CPU mask for @instance. The bits in @set will be
+ * used to set the CPUs to have tracing on.
+ *
+ * If @set is NULL, then all CPUs defined by sysconf(_SC_NPROCESSORS_CONF)
+ * will be set, and @set_size is ignored.
+ *
+ * Return 0 on success and -1 on error.
+ */
+int tracefs_instance_set_affinity_set(struct tracefs_instance *instance,
+				      cpu_set_t *set, size_t set_size)
+{
+	struct trace_seq seq;
+	bool free_set = false;
+	bool hit = false;
+	int nr_cpus;
+	int cpu;
+	int ret = -1;
+	int w, n, i;
+
+	trace_seq_init(&seq);
+
+	/* NULL set means all CPUs to be set */
+	if (!set) {
+		nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+		set = CPU_ALLOC(nr_cpus);
+		if (!set)
+			goto out;
+		set_size = CPU_ALLOC_SIZE(nr_cpus);
+		CPU_ZERO_S(set_size, set);
+		/* Set all CPUS */
+		for (cpu = 0; cpu < nr_cpus; cpu++)
+			CPU_SET_S(cpu, set_size, set);
+		free_set = true;
+	}
+	/* Convert to a bitmask hex string */
+	nr_cpus = (set_size + 1) * 8;
+	if (nr_cpus < 1) {
+		/* Must have at least one bit set */
+		errno = EINVAL;
+		goto out;
+	}
+	/* Start backwards from 32 bits */
+	for (w = ((nr_cpus + 31) / 32) - 1; w >= 0; w--) {
+		/* Now move one nibble at a time */
+		for (n = 7; n >= 0; n--) {
+			int nibble = 0;
+
+			if ((n * 4) + (w * 32) >= nr_cpus)
+				continue;
+
+			/* One bit at a time */
+			for (i = 3; i >= 0; i--) {
+				cpu = (w * 32) + (n * 4) + i;
+				if (cpu >= nr_cpus)
+					continue;
+				if (CPU_ISSET_S(cpu, set_size, set)) {
+					nibble |= 1 << i;
+					hit = true;
+				}
+			}
+			if (hit && trace_seq_printf(&seq, "%x", nibble) < 0)
+				goto out;
+		}
+		if (hit && w)
+			if (trace_seq_putc(&seq, ',') < 0)
+				goto out;
+	}
+	if (!hit) {
+		errno = EINVAL;
+		goto out;
+	}
+	trace_seq_terminate(&seq);
+	ret = tracefs_instance_set_affinity_raw(instance, seq.buffer);
+ out:
+	trace_seq_destroy(&seq);
+	if (free_set)
+		CPU_FREE(set);
+	return ret;
+}
+
+/**
+ * tracefs_instance_set_affinity - Set the affinity defined by CPU values.
+ * @instance: The instance to set affinity to (NULL for top level)
+ * @cpu_str: A string of values that define what CPUs to set.
+ *
+ * Sets the tracing affinity CPU mask for @instance. The @cpu_str is a set
+ * of decimal numbers used to state which CPU should be part of the affinity
+ * mask. A range may also be specified via a hyphen.
+ *
+ * For example, "1,4,6-8"
+ *
+ * The numbers do not need to be in order.
+ *
+ * If @cpu_str is NULL, then all CPUs defined by sysconf(_SC_NPROCESSORS_CONF)
+ * will be set.
+ *
+ * Return 0 on success and -1 on error.
+ */
+int tracefs_instance_set_affinity(struct tracefs_instance *instance,
+				  const char *cpu_str)
+{
+	cpu_set_t *set = NULL;
+	size_t set_size;
+	char *word;
+	char *cpus;
+	char *del;
+	char *c;
+	int max_cpu = 0;
+	int cpu1, cpu2;
+	int len;
+	int ret = -1;
+
+	/* NULL cpu_str means to set all CPUs in the mask */
+	if (!cpu_str)
+		return tracefs_instance_set_affinity_set(instance, NULL, 0);
+
+	/* First, find out how many CPUs are needed */
+	cpus = strdup(cpu_str);
+	if (!cpus)
+		return -1;
+	len = strlen(cpus) + 1;
+	for (word = strtok_r(cpus, ",", &del); word; word = strtok_r(NULL, ",", &del)) {
+		cpu1 = atoi(word);
+		if (cpu1 < 0) {
+			errno = EINVAL;
+			goto out;
+		}
+		if (cpu1 > max_cpu)
+			max_cpu = cpu1;
+		cpu2 = -1;
+		if ((c = strchr(word, '-'))) {
+			c++;
+			cpu2 = atoi(c);
+			if (cpu2 < cpu1) {
+				errno = EINVAL;
+				goto out;
+			}
+			if (cpu2 > max_cpu)
+				max_cpu = cpu2;
+		}
+	}
+	/*
+	 * Now ideally, cpus should fit cpu_str as it was orginally allocated
+	 * by strdup(). But I'm paranoid, and can imagine someone playing tricks
+	 * with threads, and changes cpu_str from another thread and messes
+	 * with this. At least only copy what we know is allocated.
+	 */
+	strncpy(cpus, cpu_str, len);
+
+	set = CPU_ALLOC(max_cpu + 1);
+	if (!set)
+		goto out;
+	set_size = CPU_ALLOC_SIZE(max_cpu + 1);
+	CPU_ZERO_S(set_size, set);
+
+	for (word = strtok_r(cpus, ",", &del); word; word = strtok_r(NULL, ",", &del)) {
+		cpu1 = atoi(word);
+		if (cpu1 < 0 || cpu1 > max_cpu) {
+			/* Someone playing games? */
+			errno = EACCES;
+			goto out;
+		}
+		cpu2 = cpu1;
+		if ((c = strchr(word, '-'))) {
+			c++;
+			cpu2 = atoi(c);
+			if (cpu2 < cpu1 || cpu2 > max_cpu) {
+				errno = EACCES;
+				goto out;
+			}
+		}
+		for ( ; cpu1 <= cpu2; cpu1++)
+			CPU_SET(cpu1, set);
+	}
+	ret = tracefs_instance_set_affinity_set(instance, set, set_size);
+ out:
+	free(cpus);
+	CPU_FREE(set);
+	return ret;
+}
+
+/**
+ * tracefs_instance_get_affinity_raw - read the affinity instance file
+ * @instance: The instance to get affinity of (NULL for top level)
+ *
+ * Reads the affinity file for @instance (or the top level if @instance
+ * is NULL) and returns it. The returned string must be freed with free().
+ *
+ * Returns the affinity mask on success, and must be freed with free()
+ *   or NULL on error.
+ */
+char *tracefs_instance_get_affinity_raw(struct tracefs_instance *instance)
+{
+	return tracefs_instance_file_read(instance, "tracing_cpumask", NULL);
+}
+
+static inline int update_cpu_set(int cpus, int cpu_set, int cpu,
+				 cpu_set_t *set, size_t set_size)
+{
+	int bit = 1 << cpu;
+
+	if (!(cpus & bit))
+		return 0;
+
+	CPU_SET_S(cpu_set + cpu, set_size, set);
+
+	/*
+	 * It is possible that the passed in set_size is not big enough
+	 * to hold the cpu we just set. If that's the case, do not report
+	 * it as being set.
+	 *
+	 * The CPU_ISSET_S() should return false if the CPU given to it
+	 * is bigger than the set itself.
+	 */
+	return CPU_ISSET_S(cpu_set + cpu, set_size, set) ? 1 : 0;
+}
+
+/**
+ * tracefs_instance_get_affinity_set - Retrieve the cpuset of an instance affinity
+ * @instance: The instance to get affinity of (NULL for top level)
+ * @set: A CPU set to put the affinity into.
+ * @set_size: The size in bytes of @set (use CPU_ALLOC_SIZE() to get this value)
+ *
+ * Reads the affinity of a given instance and updates the CPU set by the
+ * instance.
+ *
+ * Returns the number of CPUS that are set, or -1 on error.
+ */
+int tracefs_instance_get_affinity_set(struct tracefs_instance *instance,
+				      cpu_set_t *set, size_t set_size)
+{
+	char *affinity;
+	int cpu_set;
+	int cpus;
+	int cnt = 0;
+	int ch;
+	int i;
+
+	if (!set || !set_size) {
+		errno = -EINVAL;
+		return -1;
+	}
+
+	affinity = tracefs_instance_get_affinity_raw(instance);
+	if (!affinity)
+		return -1;
+
+	/*
+	 * The returned affinity should be a comma delimited
+	 * hex string. Work backwards setting the values.
+	 */
+	cpu_set = 0;
+	i = strlen(affinity);
+	for (i--; i >= 0; i--) {
+		ch = affinity[i];
+		if (isalnum(ch)) {
+			ch = tolower(ch);
+			if (isdigit(ch))
+				cpus = ch - '0';
+			else
+				cpus = ch - 'a' + 10;
+
+			cnt += update_cpu_set(cpus, cpu_set, 0, set, set_size);
+			cnt += update_cpu_set(cpus, cpu_set, 1, set, set_size);
+			cnt += update_cpu_set(cpus, cpu_set, 2, set, set_size);
+			cnt += update_cpu_set(cpus, cpu_set, 3, set, set_size);
+			/* Next nibble */
+			cpu_set += 4;
+		}
+	}
+
+	free(affinity);
+
+	return cnt;
+}
+
+static inline int update_cpu(int cpus, int cpu_set, int cpu, int s, char **set)
+{
+	char *list;
+	int bit = 1 << cpu;
+	int ret;
+
+	if (*set == (char *)-1)
+		return s;
+
+	if (cpus & bit) {
+		/* If the previous CPU is set just return s */
+		if (s >= 0)
+			return s;
+		/* Otherwise, return this cpu */
+		return cpu_set + cpu;
+	}
+
+	/* If the last CPU wasn't set, just return s */
+	if (s < 0)
+		return s;
+
+	/* Update the string */
+	if (s == cpu_set + cpu - 1) {
+		ret = asprintf(&list, "%s%s%d",
+			       *set ? *set : "", *set ? "," : "", s);
+	} else {
+		ret = asprintf(&list, "%s%s%d-%d",
+			       *set ? *set : "", *set ? "," : "",
+			       s, cpu_set + cpu - 1);
+	}
+	free(*set);
+	/* Force *set to be a failure */
+	if (ret < 0)
+		*set = (char *)-1;
+	else
+		*set = list;
+	return -1;
+}
+
+/**
+ * tracefs_instance_get_affinity - Retrieve a string of CPUs for instance affinity
+ * @instance: The instance to get affinity of (NULL for top level)
+ *
+ * Reads the affinity of a given instance and returns a CPU count of the
+ * instance. For example, if it reads "eb" it will return:
+ *      "0-1,3,5-7"
+ *
+ * If no CPUs are set, an empty string is returned "\0", and it too needs
+ * to be freed.
+ *
+ * Returns an allocated string containing the CPU affinity in "human readable"
+ *  format which needs to be freed with free(), or NULL on error.
+ */
+char *tracefs_instance_get_affinity(struct tracefs_instance *instance)
+{
+	char *affinity;
+	char *set = NULL;
+	int cpu_set;
+	int cpus;
+	int ch;
+	int s = -1;
+	int i;
+
+	affinity = tracefs_instance_get_affinity_raw(instance);
+	if (!affinity)
+		return NULL;
+
+	/*
+	 * The returned affinity should be a comma delimited
+	 * hex string. Work backwards setting the values.
+	 */
+	cpu_set = 0;
+	i = strlen(affinity);
+	for (i--; i >= 0; i--) {
+		ch = affinity[i];
+		if (isalnum(ch)) {
+			ch = tolower(ch);
+			if (isdigit(ch))
+				cpus = ch - '0';
+			else
+				cpus = ch - 'a' + 10;
+			s = update_cpu(cpus, cpu_set, 0, s, &set);
+			s = update_cpu(cpus, cpu_set, 1, s, &set);
+			s = update_cpu(cpus, cpu_set, 2, s, &set);
+			s = update_cpu(cpus, cpu_set, 3, s, &set);
+
+			if (set == (char *)-1) {
+				set = NULL;
+				goto out;
+			}
+			/* Next nibble */
+			cpu_set += 4;
+		}
+	}
+	/* Clean up in case the last CPU is set */
+	s = update_cpu(0, cpu_set, 0, s, &set);
+
+	if (!set)
+		set = strdup("");
+ out:
+	free(affinity);
+
+	return set;
+}
diff --git a/src/tracefs-kprobes.c b/src/tracefs-kprobes.c
new file mode 100644
index 0000000..a8c0163
--- /dev/null
+++ b/src/tracefs-kprobes.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
+ *
+ * Updates:
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+#define KPROBE_EVENTS "kprobe_events"
+#define KPROBE_DEFAULT_GROUP "kprobes"
+
+static struct tracefs_dynevent *
+kprobe_alloc(enum tracefs_dynevent_type type, const char *system, const char *event,
+	     const char *addr, const char *format)
+{
+	struct tracefs_dynevent *kp;
+	const char *sys = system;
+	const char *ename = event;
+	char *tmp;
+
+	if (!addr) {
+		errno = EBADMSG;
+		return NULL;
+	}
+	if (!sys)
+		sys = KPROBE_DEFAULT_GROUP;
+
+	if (!event) {
+		ename = strdup(addr);
+		if (!ename)
+			return NULL;
+		tmp = strchr(ename, ':');
+		if (tmp)
+			*tmp = '\0';
+	}
+
+	kp = dynevent_alloc(type, sys, ename, addr, format);
+	if (!event)
+		free((char *)ename);
+
+	return kp;
+}
+
+/**
+ * tracefs_kprobe_alloc - Allocate new kprobe
+ * @system: The system name (NULL for the default kprobes)
+ * @event: The event to create (NULL to use @addr for the event)
+ * @addr: The function and offset (or address) to insert the probe
+ * @format: The format string to define the probe.
+ *
+ * Allocate a kprobe context that will be in the @system group (or kprobes if
+ * @system is NULL). Have the name of @event (or @addr if @event is NULL). Will
+ * be inserted to @addr (function name, with or without offset, or a address).
+ * And the @format will define the format of the kprobe.
+ *
+ * See the Linux documentation file under:
+ *  Documentation/trace/kprobetrace.rst
+ *
+ * The kprobe is not created in the system.
+ *
+ * Return a pointer to a kprobe context on success, or NULL on error.
+ * The returned pointer must be freed with tracefs_dynevent_free()
+ *
+ * errno will be set to EBADMSG if addr is NULL.
+ */
+struct tracefs_dynevent *
+tracefs_kprobe_alloc(const char *system, const char *event, const char *addr, const char *format)
+
+{
+	return kprobe_alloc(TRACEFS_DYNEVENT_KPROBE, system, event, addr, format);
+}
+
+/**
+ * tracefs_kretprobe_alloc - Allocate new kretprobe
+ * @system: The system name (NULL for the default kprobes)
+ * @event: The event to create (NULL to use @addr for the event)
+ * @addr: The function and offset (or address) to insert the retprobe
+ * @format: The format string to define the retprobe.
+ * @max: Maximum number of instances of the specified function that
+ *	 can be probed simultaneously, or 0 for the default value.
+ *
+ * Allocate a kretprobe that will be in the @system group (or kprobes if
+ * @system is NULL). Have the name of @event (or @addr if @event is
+ * NULL). Will be inserted to @addr (function name, with or without
+ * offset, or a address). And the @format will define the raw format
+ * of the kprobe. See the Linux documentation file under:
+ * Documentation/trace/kprobetrace.rst
+ * The kretprobe is not created in the system.
+ *
+ * Return a pointer to a kprobe context on success, or NULL on error.
+ * The returned pointer must be freed with tracefs_dynevent_free()
+ *
+ * errno will be set to EBADMSG if addr is NULL.
+ */
+struct tracefs_dynevent *
+tracefs_kretprobe_alloc(const char *system, const char *event,
+			const char *addr, const char *format, unsigned int max)
+{
+	struct tracefs_dynevent *kp;
+	int ret;
+
+	kp = kprobe_alloc(TRACEFS_DYNEVENT_KRETPROBE, system, event, addr, format);
+	if (!kp)
+		return NULL;
+
+	if (!max)
+		return kp;
+
+	free(kp->prefix);
+	kp->prefix = NULL;
+	ret = asprintf(&kp->prefix, "r%d:", max);
+	if (ret < 0)
+		goto error;
+
+	return kp;
+error:
+	tracefs_dynevent_free(kp);
+	return NULL;
+}
+
+static int kprobe_raw(enum tracefs_dynevent_type type, const char *system,
+		      const char *event, const char *addr, const char *format)
+{
+	static struct tracefs_dynevent *kp;
+	int ret;
+
+	kp = kprobe_alloc(type, system, event, addr, format);
+	if (!kp)
+		return -1;
+
+	ret = tracefs_dynevent_create(kp);
+	tracefs_dynevent_free(kp);
+
+	return ret;
+}
+
+/**
+ * tracefs_kprobe_raw - Create a kprobe using raw format
+ * @system: The system name (NULL for the default kprobes)
+ * @event: The event to create (NULL to use @addr for the event)
+ * @addr: The function and offset (or address) to insert the probe
+ * @format: The raw format string to define the probe.
+ *
+ * Create a kprobe that will be in the @system group (or kprobes if
+ * @system is NULL). Have the name of @event (or @addr if @event is
+ * NULL). Will be inserted to @addr (function name, with or without
+ * offset, or a address). And the @format will define the raw format
+ * of the kprobe. See the Linux documentation file under:
+ * Documentation/trace/kprobetrace.rst
+ *
+ * Return 0 on success, or -1 on error.
+ *   If the syntex of @format was incorrect, running
+ *   tracefs_error_last(NULL) may show what went wrong.
+ *
+ * errno will be set to EBADMSG if addr or format is NULL.
+ */
+int tracefs_kprobe_raw(const char *system, const char *event,
+		       const char *addr, const char *format)
+{
+	return kprobe_raw(TRACEFS_DYNEVENT_KPROBE, system, event, addr, format);
+}
+
+/**
+ * tracefs_kretprobe_raw - Create a kretprobe using raw format
+ * @system: The system name (NULL for the default kprobes)
+ * @event: The event to create (NULL to use @addr for the event)
+ * @addr: The function and offset (or address) to insert the retprobe
+ * @format: The raw format string to define the retprobe.
+ *
+ * Create a kretprobe that will be in the @system group (or kprobes if
+ * @system is NULL). Have the name of @event (or @addr if @event is
+ * NULL). Will be inserted to @addr (function name, with or without
+ * offset, or a address). And the @format will define the raw format
+ * of the kprobe. See the Linux documentation file under:
+ * Documentation/trace/kprobetrace.rst
+ *
+ * Return 0 on success, or -1 on error.
+ *   If the syntex of @format was incorrect, running
+ *   tracefs_error_last(NULL) may show what went wrong.
+ *
+ * errno will be set to EBADMSG if addr or format is NULL.
+ */
+int tracefs_kretprobe_raw(const char *system, const char *event,
+			  const char *addr, const char *format)
+{
+	return kprobe_raw(TRACEFS_DYNEVENT_KRETPROBE, system, event, addr, format);
+}
diff --git a/src/tracefs-marker.c b/src/tracefs-marker.c
new file mode 100644
index 0000000..61a07ab
--- /dev/null
+++ b/src/tracefs-marker.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <pthread.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+/* File descriptors for Top level trace markers */
+static int ftrace_marker_fd = -1;
+static int ftrace_marker_raw_fd = -1;
+
+static inline int *get_marker_fd(struct tracefs_instance *instance, bool raw)
+{
+	if (raw)
+		return instance ? &instance->ftrace_marker_raw_fd : &ftrace_marker_raw_fd;
+	return instance ? &instance->ftrace_marker_fd : &ftrace_marker_fd;
+}
+
+static int marker_init(struct tracefs_instance *instance, bool raw)
+{
+	const char *file = raw ? "trace_marker_raw" : "trace_marker";
+	pthread_mutex_t *lock = trace_get_lock(instance);
+	int *fd = get_marker_fd(instance, raw);
+	int ret;
+
+	if (*fd >= 0)
+		return 0;
+
+	/*
+	 * The mutex is only to hold the integrity of the file descriptor
+	 * to prevent opening it more than once, or closing the same
+	 * file descriptor more than once. It does not protect against
+	 * one thread closing the file descriptor and another thread
+	 * writing to it. That is up to the application to prevent
+	 * from happening.
+	 */
+	pthread_mutex_lock(lock);
+	/* The file could have been opened since we taken the lock */
+	if (*fd < 0)
+		*fd = tracefs_instance_file_open(instance, file, O_WRONLY | O_CLOEXEC);
+
+	ret = *fd < 0 ? -1 : 0;
+	pthread_mutex_unlock(lock);
+
+	return ret;
+}
+
+static void marker_close(struct tracefs_instance *instance, bool raw)
+{
+	pthread_mutex_t *lock = trace_get_lock(instance);
+	int *fd = get_marker_fd(instance, raw);
+
+	pthread_mutex_lock(lock);
+	if (*fd >= 0) {
+		close(*fd);
+		*fd = -1;
+	}
+	pthread_mutex_unlock(lock);
+}
+
+static int marker_write(struct tracefs_instance *instance, bool raw, void *data, int len)
+{
+	int *fd = get_marker_fd(instance, raw);
+	int ret;
+
+	/*
+	 * The lock does not need to be taken for writes. As a write
+	 * does not modify the file descriptor. It's up to the application
+	 * to prevent it from being closed if another thread is doing a write.
+	 */
+	if (!data || len < 1)
+		return -1;
+	if (*fd < 0) {
+		ret = marker_init(instance, raw);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = write(*fd, data, len);
+
+	return ret == len ? 0 : -1;
+}
+
+/**
+ * tracefs_print_init - Open trace marker of selected instance for writing
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ *
+ * Returns 0 if the trace marker is opened successfully, or -1 in case of an error
+ */
+int tracefs_print_init(struct tracefs_instance *instance)
+{
+	return marker_init(instance, false);
+}
+
+/**
+ * tracefs_vprintf - Write a formatted string in the trace marker
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ * @fmt: pritnf formatted string
+ * @ap: list of arguments for the formatted string
+ *
+ * If the trace marker of the desired instance is not open already,
+ * this API will open it for writing. It will stay open until
+ * tracefs_print_close() is called.
+ *
+ * Returns 0 if the string is written correctly, or -1 in case of an error
+ */
+int tracefs_vprintf(struct tracefs_instance *instance, const char *fmt, va_list ap)
+{
+	char *str = NULL;
+	int ret;
+
+	ret = vasprintf(&str, fmt, ap);
+	if (ret < 0)
+		return ret;
+	ret = marker_write(instance, false, str, strlen(str));
+	free(str);
+
+	return ret;
+}
+
+/**
+ * tracefs_printf - Write a formatted string in the trace marker
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ * @fmt: pritnf formatted string with variable arguments ...
+ *
+ * If the trace marker of the desired instance is not open already,
+ * this API will open it for writing. It will stay open until
+ * tracefs_print_close() is called.
+ *
+ * Returns 0 if the string is written correctly, or -1 in case of an error
+ */
+int tracefs_printf(struct tracefs_instance *instance, const char *fmt, ...)
+{
+	va_list ap;
+	int ret;
+
+	va_start(ap, fmt);
+	ret = tracefs_vprintf(instance, fmt, ap);
+	va_end(ap);
+
+	return ret;
+}
+
+/**
+ * tracefs_print_close - Close trace marker of selected instance
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ *
+ * Closes the trace marker, previously opened with any of the other tracefs_print APIs
+ */
+void tracefs_print_close(struct tracefs_instance *instance)
+{
+	marker_close(instance, false);
+}
+
+/**
+ * tracefs_binary_init - Open raw trace marker of selected instance for writing
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ *
+ * Returns 0 if the raw trace marker is opened successfully, or -1 in case of an error
+ */
+int tracefs_binary_init(struct tracefs_instance *instance)
+{
+	return marker_init(instance, true);
+}
+
+/**
+ * tracefs_binary_write - Write binary data in the raw trace marker
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ * @data: binary data, that is going to be written in the trace marker
+ * @len: length of the @data
+ *
+ * If the raw trace marker of the desired instance is not open already,
+ * this API will open it for writing. It will stay open until
+ * tracefs_binary_close() is called.
+ *
+ * Returns 0 if the data is written correctly, or -1 in case of an error
+ */
+int tracefs_binary_write(struct tracefs_instance *instance, void *data, int len)
+{
+	return marker_write(instance, true, data, len);
+}
+
+/**
+ * tracefs_binary_close - Close raw trace marker of selected instance
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ *
+ * Closes the raw trace marker, previously opened with any of the other tracefs_binary APIs
+ */
+void tracefs_binary_close(struct tracefs_instance *instance)
+{
+	marker_close(instance, true);
+}
diff --git a/src/tracefs-record.c b/src/tracefs-record.c
new file mode 100644
index 0000000..b078c86
--- /dev/null
+++ b/src/tracefs-record.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2022 Google Inc, Steven Rostedt <rostedt@goodmis.org>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+
+#include <kbuffer.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+enum {
+	TC_STOP			= 1 << 0,   /* Stop reading */
+	TC_PERM_NONBLOCK	= 1 << 1,   /* read is always non blocking */
+	TC_NONBLOCK		= 1 << 2,   /* read is non blocking */
+};
+
+struct tracefs_cpu {
+	int		fd;
+	int		flags;
+	int		nfds;
+	int		ctrl_pipe[2];
+	int		splice_pipe[2];
+	int		pipe_size;
+	int		subbuf_size;
+	int		buffered;
+	int		splice_read_flags;
+};
+
+/**
+ * tracefs_cpu_alloc_fd - create a tracefs_cpu instance for an existing fd
+ * @fd: The file descriptor to attach the tracefs_cpu to
+ * @subbuf_size: The expected size to read the subbuffer with
+ * @nonblock: If true, the file will be opened in O_NONBLOCK mode
+ *
+ * Return a descriptor that can read the tracefs trace_pipe_raw file
+ * that is associated with the given @fd and must be read in @subbuf_size.
+ *
+ * Returns NULL on error.
+ */
+struct tracefs_cpu *
+tracefs_cpu_alloc_fd(int fd, int subbuf_size, bool nonblock)
+{
+	struct tracefs_cpu *tcpu;
+	int mode = O_RDONLY;
+	int ret;
+
+	tcpu = calloc(1, sizeof(*tcpu));
+	if (!tcpu)
+		return NULL;
+
+	if (nonblock) {
+		mode |= O_NONBLOCK;
+		tcpu->flags |= TC_NONBLOCK | TC_PERM_NONBLOCK;
+	}
+
+	tcpu->splice_pipe[0] = -1;
+	tcpu->splice_pipe[1] = -1;
+
+	tcpu->fd = fd;
+
+	tcpu->subbuf_size = subbuf_size;
+
+	if (tcpu->flags & TC_PERM_NONBLOCK) {
+		tcpu->ctrl_pipe[0] = -1;
+		tcpu->ctrl_pipe[1] = -1;
+	} else {
+		/* ctrl_pipe is used to break out of blocked reads */
+		ret = pipe(tcpu->ctrl_pipe);
+		if (ret < 0)
+			goto fail;
+		if (tcpu->ctrl_pipe[0] > tcpu->fd)
+			tcpu->nfds = tcpu->ctrl_pipe[0] + 1;
+		else
+			tcpu->nfds = tcpu->fd + 1;
+	}
+
+	return tcpu;
+ fail:
+	free(tcpu);
+	return NULL;
+}
+
+/**
+ * tracefs_cpu_open - open an instance raw trace file
+ * @instance: the instance (NULL for toplevel) of the cpu raw file to open
+ * @cpu: The CPU that the raw trace file is associated with
+ * @nonblock: If true, the file will be opened in O_NONBLOCK mode
+ *
+ * Return a descriptor that can read the tracefs trace_pipe_raw file
+ * for a give @cpu in a given @instance.
+ *
+ * Returns NULL on error.
+ */
+struct tracefs_cpu *
+tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock)
+{
+	struct tracefs_cpu *tcpu;
+	struct tep_handle *tep;
+	char path[128];
+	char *buf;
+	int mode = O_RDONLY;
+	int subbuf_size;
+	int len;
+	int ret;
+	int fd;
+
+	if (nonblock)
+		mode |= O_NONBLOCK;
+
+	sprintf(path, "per_cpu/cpu%d/trace_pipe_raw", cpu);
+
+	fd = tracefs_instance_file_open(instance, path, mode);
+	if (fd < 0)
+		return NULL;
+
+	tep = tep_alloc();
+	if (!tep)
+		goto fail;
+
+	/* Get the size of the page */
+	buf = tracefs_instance_file_read(NULL, "events/header_page", &len);
+	if (!buf)
+		goto fail;
+
+	ret = tep_parse_header_page(tep, buf, len, sizeof(long));
+	free(buf);
+	if (ret < 0)
+		goto fail;
+
+	subbuf_size = tep_get_sub_buffer_size(tep);
+	tep_free(tep);
+	tep = NULL;
+
+	tcpu = tracefs_cpu_alloc_fd(fd, subbuf_size, nonblock);
+	if (!tcpu)
+		goto fail;
+
+	return tcpu;
+ fail:
+	tep_free(tep);
+	close(fd);
+	return NULL;
+}
+
+static void close_fd(int fd)
+{
+	if (fd < 0)
+		return;
+	close(fd);
+}
+
+/**
+ * tracefs_cpu_free_fd - clean up the tracefs_cpu descriptor
+ * @tcpu: The descriptor created with tracefs_cpu_alloc_fd()
+ *
+ * Closes all the internal file descriptors that were opened by
+ * tracefs_cpu_alloc_fd(), and frees the descriptor.
+ */
+void tracefs_cpu_free_fd(struct tracefs_cpu *tcpu)
+{
+	close_fd(tcpu->ctrl_pipe[0]);
+	close_fd(tcpu->ctrl_pipe[1]);
+	close_fd(tcpu->splice_pipe[0]);
+	close_fd(tcpu->splice_pipe[1]);
+
+	free(tcpu);
+}
+
+/**
+ * tracefs_cpu_close - clean up and close a raw trace descriptor
+ * @tcpu: The descriptor created with tracefs_cpu_open()
+ *
+ * Closes all the file descriptors associated to the trace_pipe_raw
+ * opened by tracefs_cpu_open().
+ */
+void tracefs_cpu_close(struct tracefs_cpu *tcpu)
+{
+	if (!tcpu)
+		return;
+
+	close(tcpu->fd);
+	tracefs_cpu_free_fd(tcpu);
+}
+
+/**
+ * tracefs_cpu_read_size - Return the size of the sub buffer
+ * @tcpu: The descriptor that holds the size of the sub buffer
+ *
+ * A lot of the functions that read the data from the trace_pipe_raw
+ * expect the caller to have allocated enough space to store a full
+ * subbuffer. Calling this function is a requirement to do so.
+ */
+int tracefs_cpu_read_size(struct tracefs_cpu *tcpu)
+{
+	if (!tcpu)
+		return -1;
+	return tcpu->subbuf_size;
+}
+
+static void set_nonblock(struct tracefs_cpu *tcpu)
+{
+	long flags;
+
+	if (tcpu->flags & TC_NONBLOCK)
+		return;
+
+	flags = fcntl(tcpu->fd, F_GETFL);
+	fcntl(tcpu->fd, F_SETFL, flags | O_NONBLOCK);
+	tcpu->flags |= TC_NONBLOCK;
+}
+
+static void unset_nonblock(struct tracefs_cpu *tcpu)
+{
+	long flags;
+
+	if (!(tcpu->flags & TC_NONBLOCK))
+		return;
+
+	flags = fcntl(tcpu->fd, F_GETFL);
+	flags &= ~O_NONBLOCK;
+	fcntl(tcpu->fd, F_SETFL, flags);
+	tcpu->flags &= ~TC_NONBLOCK;
+}
+
+/*
+ * If set to blocking mode, block until the watermark has been
+ * reached, or the control has said to stop. If the contol is
+ * set, then nonblock will be set to true on the way out.
+ */
+static int wait_on_input(struct tracefs_cpu *tcpu, bool nonblock)
+{
+	fd_set rfds;
+	int ret;
+
+	if (tcpu->flags & TC_PERM_NONBLOCK)
+		return 1;
+
+	if (nonblock) {
+		set_nonblock(tcpu);
+		return 1;
+	} else {
+		unset_nonblock(tcpu);
+	}
+
+	FD_ZERO(&rfds);
+	FD_SET(tcpu->fd, &rfds);
+	FD_SET(tcpu->ctrl_pipe[0], &rfds);
+
+	ret = select(tcpu->nfds, &rfds, NULL, NULL, NULL);
+
+	/* Let the application decide what to do with signals and such */
+	if (ret < 0)
+		return ret;
+
+	if (FD_ISSET(tcpu->ctrl_pipe[0], &rfds)) {
+		/* Flush the ctrl pipe */
+		read(tcpu->ctrl_pipe[0], &ret, 1);
+
+		/* Make nonblock as it is now stopped */
+		set_nonblock(tcpu);
+		/* Permanently set unblock */
+		tcpu->flags |= TC_PERM_NONBLOCK;
+	}
+
+	return FD_ISSET(tcpu->fd, &rfds);
+}
+
+/**
+ * tracefs_cpu_read - read from the raw trace file
+ * @tcpu: The descriptor representing the raw trace file
+ * @buffer: Where to read into (must be at least the size of the subbuffer)
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * Reads the trace_pipe_raw files associated to @tcpu into @buffer.
+ * @buffer must be at least the size of the sub buffer of the ring buffer,
+ * which is returned by tracefs_cpu_read_size().
+ *
+ * If @nonblock is set, and there's no data available, it will return
+ * immediately. Otherwise depending on how @tcpu was opened, it will
+ * block. If @tcpu was opened with nonblock set, then this @nonblock
+ * will make no difference.
+ *
+ * Returns the amount read or -1 on error.
+ */
+int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock)
+{
+	int ret;
+
+	/*
+	 * If nonblock is set, then the wait_on_input() will return
+	 * immediately, if there's nothing in the buffer, with
+	 * ret == 0.
+	 */
+	ret = wait_on_input(tcpu, nonblock);
+	if (ret <= 0)
+		return ret;
+
+	ret = read(tcpu->fd, buffer, tcpu->subbuf_size);
+
+	/* It's OK if there's no data to read */
+	if (ret < 0 && errno == EAGAIN) {
+		/* Reset errno */
+		errno = 0;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int init_splice(struct tracefs_cpu *tcpu)
+{
+	int ret;
+
+	if (tcpu->splice_pipe[0] >= 0)
+		return 0;
+
+	ret = pipe(tcpu->splice_pipe);
+	if (ret < 0)
+		return ret;
+
+	ret = fcntl(tcpu->splice_pipe[0], F_GETPIPE_SZ, &tcpu->pipe_size);
+	/*
+	 * F_GETPIPE_SZ was introduced in 2.6.35, ftrace was introduced
+	 * in 2.6.31. If we are running on an older kernel, just fall
+	 * back to using subbuf_size for splice(). It could also return
+	 * the size of the pipe and not set pipe_size.
+	 */
+	if (ret > 0 && !tcpu->pipe_size)
+		tcpu->pipe_size = ret;
+	else if (ret < 0)
+		tcpu->pipe_size = tcpu->subbuf_size;
+
+	tcpu->splice_read_flags = SPLICE_F_MOVE;
+	if (tcpu->flags & TC_NONBLOCK)
+		tcpu->splice_read_flags |= SPLICE_F_NONBLOCK;
+
+	return 0;
+}
+
+/**
+ * tracefs_cpu_buffered_read - Read the raw trace data buffering through a pipe
+ * @tcpu: The descriptor representing the raw trace file
+ * @buffer: Where to read into (must be at least the size of the subbuffer)
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * This is basically the same as tracefs_cpu_read() except that it uses
+ * a pipe through splice to buffer reads. This will batch reads keeping
+ * the reading from the ring buffer less intrusive to the system, as
+ * just reading all the time can cause quite a disturbance.
+ *
+ * Note, one difference between this and tracefs_cpu_read() is that it
+ * will read only in sub buffer pages. If the ring buffer has not filled
+ * a page, then it will not return anything, even with @nonblock set.
+ * Calls to tracefs_cpu_flush() should be done to read the rest of
+ * the file at the end of the trace.
+ *
+ * Returns the amount read or -1 on error.
+ */
+int tracefs_cpu_buffered_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock)
+{
+	int mode = SPLICE_F_MOVE;
+	int ret;
+
+	if (tcpu->buffered < 0)
+		tcpu->buffered = 0;
+
+	if (tcpu->buffered)
+		goto do_read;
+
+	ret = wait_on_input(tcpu, nonblock);
+	if (ret <= 0)
+		return ret;
+
+	if (tcpu->flags & TC_NONBLOCK)
+		mode |= SPLICE_F_NONBLOCK;
+
+	ret = init_splice(tcpu);
+	if (ret < 0)
+		return ret;
+
+	ret = splice(tcpu->fd, NULL, tcpu->splice_pipe[1], NULL,
+		     tcpu->pipe_size, mode);
+	if (ret <= 0)
+		return ret;
+
+	tcpu->buffered = ret;
+
+ do_read:
+	ret = read(tcpu->splice_pipe[0], buffer, tcpu->subbuf_size);
+	if (ret > 0)
+		tcpu->buffered -= ret;
+	return ret;
+}
+
+/**
+ * tracefs_cpu_stop - Stop a blocked read of the raw tracing file
+ * @tcpu: The descriptor representing the raw trace file
+ *
+ * This will attempt to unblock a task blocked on @tcpu reading it.
+ * On older kernels, it may not do anything for the pipe reads, as
+ * older kernels do not wake up tasks waiting on the ring buffer.
+ *
+ * Returns 0 if the tasks reading the raw tracing file does not
+ * need a nudge.
+ *
+ * Returns 1 if that tasks may need a nudge (send a signal).
+ *
+ * Returns negative on error.
+ */
+int tracefs_cpu_stop(struct tracefs_cpu *tcpu)
+{
+	int ret = 1;
+
+	if (tcpu->flags & TC_PERM_NONBLOCK)
+		return 0;
+
+	ret = write(tcpu->ctrl_pipe[1], &ret, 1);
+	if (ret < 0)
+		return ret;
+
+	/* Calling ioctl() on recent kernels will wake up the waiters */
+	ret = ioctl(tcpu->fd, 0);
+	if (ret < 0)
+		ret = 1;
+	else
+		ret = 0;
+
+	set_nonblock(tcpu);
+
+	return ret;
+}
+
+/**
+ * tracefs_cpu_flush - Finish out and read the rest of the raw tracing file
+ * @tcpu: The descriptor representing the raw trace file
+ * @buffer: Where to read into (must be at least the size of the subbuffer)
+ *
+ * Reads the trace_pipe_raw file associated by the @tcpu and puts it
+ * into @buffer, which must be the size of the sub buffer which is retrieved.
+ * by tracefs_cpu_read_size(). This should be called at the end of tracing
+ * to get the rest of the data.
+ *
+ * This will set the file descriptor for reading to non-blocking mode.
+ *
+ * Returns the number of bytes read, or negative on error.
+ */
+int tracefs_cpu_flush(struct tracefs_cpu *tcpu, void *buffer)
+{
+	int ret;
+
+	/* Make sure that reading is now non blocking */
+	set_nonblock(tcpu);
+
+	if (tcpu->buffered < 0)
+		tcpu->buffered = 0;
+
+	if (tcpu->buffered) {
+		ret = read(tcpu->splice_pipe[0], buffer, tcpu->subbuf_size);
+		if (ret > 0)
+			tcpu->buffered -= ret;
+		return ret;
+	}
+
+	ret = read(tcpu->fd, buffer, tcpu->subbuf_size);
+	if (ret > 0 && tcpu->buffered)
+		tcpu->buffered -= ret;
+
+	/* It's OK if there's no data to read */
+	if (ret < 0 && errno == EAGAIN) {
+		/* Reset errno */
+		errno = 0;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/**
+ * tracefs_cpu_flush_write - Finish out and read the rest of the raw tracing file
+ * @tcpu: The descriptor representing the raw trace file
+ * @wfd: The write file descriptor to write the data to
+ *
+ * Reads the trace_pipe_raw file associated by the @tcpu and writes it to
+ * @wfd. This should be called at the end of tracing to get the rest of the data.
+ *
+ * Returns the number of bytes written, or negative on error.
+ */
+int tracefs_cpu_flush_write(struct tracefs_cpu *tcpu, int wfd)
+{
+	char buffer[tcpu->subbuf_size];
+	int ret;
+
+	ret = tracefs_cpu_flush(tcpu, buffer);
+	if (ret > 0)
+		ret = write(wfd, buffer, ret);
+
+	/* It's OK if there's no data to read */
+	if (ret < 0 && errno == EAGAIN)
+		ret = 0;
+
+	return ret;
+}
+
+/**
+ * tracefs_cpu_write - Write the raw trace file into a file descriptor
+ * @tcpu: The descriptor representing the raw trace file
+ * @wfd: The write file descriptor to write the data to
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * This will pipe the data from the trace_pipe_raw file associated with @tcpu
+ * into the @wfd file descriptor. If @nonblock is set, then it will not
+ * block on if there's nothing to write. Note, it will only write sub buffer
+ * size data to @wfd. Calls to tracefs_cpu_flush_write() are needed to
+ * write out the rest.
+ *
+ * Returns the number of bytes read or negative on error.
+ */
+int tracefs_cpu_write(struct tracefs_cpu *tcpu, int wfd, bool nonblock)
+{
+	char buffer[tcpu->subbuf_size];
+	int mode = SPLICE_F_MOVE;
+	int tot_write = 0;
+	int tot;
+	int ret;
+
+	ret = wait_on_input(tcpu, nonblock);
+	if (ret <= 0)
+		return ret;
+
+	if (tcpu->flags & TC_NONBLOCK)
+		mode |= SPLICE_F_NONBLOCK;
+
+	ret = init_splice(tcpu);
+	if (ret < 0)
+		return ret;
+
+	tot = splice(tcpu->fd, NULL, tcpu->splice_pipe[1], NULL,
+		     tcpu->pipe_size, mode);
+	if (tot < 0)
+		return tot;
+
+	if (tot == 0)
+		return 0;
+
+	ret = splice(tcpu->splice_pipe[0], NULL, wfd, NULL,
+		     tot, SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+
+	if (ret >= 0)
+		return ret;
+
+	/* Some file systems do not allow splicing, try writing instead */
+	do {
+		int r = tcpu->subbuf_size;
+
+		if (r > tot)
+			r = tot;
+
+		ret = read(tcpu->splice_pipe[0], buffer, r);
+		if (ret > 0) {
+			tot -= ret;
+			ret = write(wfd, buffer, ret);
+		}
+		if (ret > 0)
+			tot_write += ret;
+	} while (ret > 0);
+
+	if (ret < 0)
+		return ret;
+
+	return tot_write;
+}
+
+/**
+ * tracefs_cpu_pipe - Write the raw trace file into a pipe descriptor
+ * @tcpu: The descriptor representing the raw trace file
+ * @wfd: The write file descriptor to write the data to (must be a pipe)
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * This will splice directly the file descriptor of the trace_pipe_raw
+ * file to the given @wfd, which must be a pipe. This can also be used
+ * if @tcpu was created with tracefs_cpu_create_fd() where the passed
+ * in @fd there was a pipe, then @wfd does not need to be a pipe.
+ *
+ * Returns the number of bytes read or negative on error.
+ */
+int tracefs_cpu_pipe(struct tracefs_cpu *tcpu, int wfd, bool nonblock)
+{
+	int mode = SPLICE_F_MOVE;
+	int ret;
+
+	ret = wait_on_input(tcpu, nonblock);
+	if (ret <= 0)
+		return ret;
+
+	if (tcpu->flags & TC_NONBLOCK)
+		mode |= SPLICE_F_NONBLOCK;
+
+	ret = splice(tcpu->fd, NULL, wfd, NULL,
+		     tcpu->pipe_size, mode);
+	return ret;
+}
diff --git a/src/tracefs-sqlhist.c b/src/tracefs-sqlhist.c
new file mode 100644
index 0000000..3f571b7
--- /dev/null
+++ b/src/tracefs-sqlhist.c
@@ -0,0 +1,1653 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
+ *
+ * Updates:
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <trace-seq.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+#include "sqlhist-parse.h"
+
+extern int yylex_init(void* ptr_yy_globals);
+extern int yylex_init_extra(struct sqlhist_bison *sb, void* ptr_yy_globals);
+extern int yylex_destroy (void * yyscanner );
+
+struct str_hash {
+	struct str_hash		*next;
+	char			*str;
+};
+
+enum alias_type {
+	ALIAS_EVENT,
+	ALIAS_FIELD,
+};
+
+enum field_type {
+	FIELD_NONE,
+	FIELD_FROM,
+	FIELD_TO,
+};
+
+#define for_each_field(expr, field, table) \
+	for (expr = (table)->fields; expr; expr = (field)->next)
+
+struct field {
+	struct expr		*next;	/* private link list */
+	const char		*system;
+	const char		*event_name;
+	struct tep_event	*event;
+	const char		*raw;
+	const char		*label;
+	const char		*field;
+	const char		*type;
+	enum field_type		ftype;
+};
+
+struct filter {
+	enum filter_type	type;
+	struct expr		*lval;
+	struct expr		*rval;
+};
+
+struct match {
+	struct match		*next;
+	struct expr		*lval;
+	struct expr		*rval;
+};
+
+struct compare {
+	enum compare_type	type;
+	struct expr		*lval;
+	struct expr		*rval;
+	const char		*name;
+};
+
+enum expr_type
+{
+	EXPR_NUMBER,
+	EXPR_STRING,
+	EXPR_FIELD,
+	EXPR_FILTER,
+	EXPR_COMPARE,
+};
+
+struct expr {
+	struct expr		*free_list;
+	struct expr		*next;
+	enum expr_type		type;
+	int			line;
+	int			idx;
+	union {
+		struct field	field;
+		struct filter	filter;
+		struct compare	compare;
+		const char	*string;
+		long		number;
+	};
+};
+
+struct sql_table {
+	struct sqlhist_bison	*sb;
+	const char		*name;
+	struct expr		*exprs;
+	struct expr		*fields;
+	struct expr		*from;
+	struct expr		*to;
+	struct expr		*where;
+	struct expr		**next_where;
+	struct match		*matches;
+	struct match		**next_match;
+	struct expr		*selections;
+	struct expr		**next_selection;
+};
+
+__hidden int my_yyinput(void *extra, char *buf, int max)
+{
+	struct sqlhist_bison *sb = extra;
+
+	if (!sb || !sb->buffer)
+		return -1;
+
+	if (sb->buffer_idx + max > sb->buffer_size)
+		max = sb->buffer_size - sb->buffer_idx;
+
+	if (max)
+		memcpy(buf, sb->buffer + sb->buffer_idx, max);
+
+	sb->buffer_idx += max;
+
+	return max;
+}
+
+__hidden void sql_parse_error(struct sqlhist_bison *sb, const char *text,
+			      const char *fmt, va_list ap)
+{
+	const char *buffer = sb->buffer;
+	struct trace_seq s;
+	int line = sb->line_no;
+	int idx = sb->line_idx - strlen(text);
+	int i;
+
+	if (!buffer)
+		return;
+
+	trace_seq_init(&s);
+	if (!s.buffer) {
+		tracefs_warning("Error allocating internal buffer\n");
+		return;
+	}
+
+	for (i = 0; line && buffer[i]; i++) {
+		if (buffer[i] == '\n')
+			line--;
+	}
+	for (; buffer[i] && buffer[i] != '\n'; i++)
+		trace_seq_putc(&s, buffer[i]);
+	trace_seq_putc(&s, '\n');
+	for (i = idx; i > 0; i--)
+		trace_seq_putc(&s, ' ');
+	trace_seq_puts(&s, "^\n");
+	trace_seq_printf(&s, "ERROR: '%s'\n", text);
+	trace_seq_vprintf(&s, fmt, ap);
+
+	trace_seq_terminate(&s);
+
+	sb->parse_error_str = strdup(s.buffer);
+	trace_seq_destroy(&s);
+}
+
+static void parse_error(struct sqlhist_bison *sb, const char *text,
+			const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	sql_parse_error(sb, text, fmt, ap);
+	va_end(ap);
+}
+
+__hidden unsigned int quick_hash(const char *str)
+{
+	unsigned int val = 0;
+	int len = strlen(str);
+
+	for (; len >= 4; str += 4, len -= 4) {
+		val += str[0];
+		val += str[1] << 8;
+		val += str[2] << 16;
+		val += str[3] << 24;
+	}
+	for (; len > 0; str++, len--)
+		val += str[0] << (len * 8);
+
+        val *= 2654435761;
+
+        return val & ((1 << HASH_BITS) - 1);
+}
+
+
+static struct str_hash *find_string(struct sqlhist_bison *sb, const char *str)
+{
+	unsigned int key = quick_hash(str);
+	struct str_hash *hash = sb->str_hash[key];
+
+	for (; hash; hash = hash->next) {
+		if (!strcmp(hash->str, str))
+			return hash;
+	}
+	return NULL;
+}
+
+/*
+ * If @str is found, then return the hash string.
+ * This lets store_str() know to free str.
+ */
+static char **add_hash(struct sqlhist_bison *sb, const char *str)
+{
+	struct str_hash *hash;
+	unsigned int key;
+
+	if ((hash = find_string(sb, str))) {
+		return &hash->str;
+	}
+
+	hash = malloc(sizeof(*hash));
+	if (!hash)
+		return NULL;
+	key = quick_hash(str);
+	hash->next = sb->str_hash[key];
+	sb->str_hash[key] = hash;
+	hash->str = NULL;
+	return &hash->str;
+}
+
+__hidden char *store_str(struct sqlhist_bison *sb, const char *str)
+{
+	char **pstr = add_hash(sb, str);
+
+	if (!pstr)
+		return NULL;
+
+	if (!(*pstr))
+		*pstr = strdup(str);
+
+	return *pstr;
+}
+
+__hidden void *add_cast(struct sqlhist_bison *sb,
+			void *data, const char *type)
+{
+	struct expr *expr = data;
+	struct field *field = &expr->field;
+
+	field->type = type;
+	return expr;
+}
+
+__hidden int add_selection(struct sqlhist_bison *sb, void *select,
+			   const char *name)
+{
+	struct sql_table *table = sb->table;
+	struct expr *expr = select;
+
+	switch (expr->type) {
+	case EXPR_FIELD:
+		expr->field.label = name;
+		break;
+	case EXPR_COMPARE:
+		expr->compare.name = name;
+		break;
+	case EXPR_NUMBER:
+	case EXPR_STRING:
+	case EXPR_FILTER:
+	default:
+		return -1;
+	}
+
+	if (expr->next)
+		return -1;
+
+	*table->next_selection = expr;
+	table->next_selection = &expr->next;
+
+	return 0;
+}
+
+static struct expr *find_field(struct sqlhist_bison *sb,
+				const char *raw, const char *label)
+{
+	struct field *field;
+	struct expr *expr;
+
+	for_each_field(expr, field, sb->table) {
+		field = &expr->field;
+
+		if (!strcmp(field->raw, raw)) {
+			if (label && !field->label)
+				field->label = label;
+			if (label && strcmp(label, field->label) != 0)
+				continue;
+			return expr;
+		}
+
+		if (label && !strcmp(field->raw, label)) {
+			if (!field->label) {
+				field->label = label;
+				field->raw = raw;
+			}
+			return expr;
+		}
+
+		if (!field->label)
+			continue;
+
+		if (!strcmp(field->label, raw))
+			return expr;
+
+		if (label && !strcmp(field->label, label))
+			return expr;
+	}
+	return NULL;
+}
+
+static void *create_expr(struct sqlhist_bison *sb,
+			 enum expr_type type, struct expr **expr_p)
+{
+	struct expr *expr;
+
+	expr = calloc(1, sizeof(*expr));
+	if (!expr)
+		return NULL;
+
+	if (expr_p)
+		*expr_p = expr;
+
+	expr->free_list = sb->table->exprs;
+	sb->table->exprs = expr;
+
+	expr->type = type;
+	expr->line = sb->line_no;
+	expr->idx = sb->line_idx;
+
+	switch (type) {
+	case EXPR_FIELD:	return &expr->field;
+	case EXPR_COMPARE:	return &expr->compare;
+	case EXPR_NUMBER:	return &expr->number;
+	case EXPR_STRING:	return &expr->string;
+	case EXPR_FILTER:	return &expr->filter;
+	}
+
+	return NULL;
+}
+
+#define __create_expr(var, type, ENUM, expr)			\
+	do {							\
+		var = (type *)create_expr(sb, EXPR_##ENUM, expr);	\
+	} while(0)
+
+#define create_field(var, expr)				\
+	__create_expr(var, struct field, FIELD, expr)
+
+#define create_filter(var, expr)			\
+	__create_expr(var, struct filter, FILTER, expr)
+
+#define create_compare(var, expr)				\
+	__create_expr(var, struct compare, COMPARE, expr)
+
+#define create_string(var, expr)			\
+	__create_expr(var, const char *, STRING, expr)
+
+#define create_number(var, expr)			\
+	__create_expr(var, long, NUMBER, expr)
+
+__hidden void *add_field(struct sqlhist_bison *sb,
+			 const char *field_name, const char *label)
+{
+	struct sql_table *table = sb->table;
+	struct expr *expr;
+	struct field *field;
+
+	expr = find_field(sb, field_name, label);
+	if (expr)
+		return expr;
+
+	create_field(field, &expr);
+
+	field->next = table->fields;
+	table->fields = expr;
+
+	field->raw = field_name;
+	field->label = label;
+
+	return expr;
+}
+
+__hidden void *add_filter(struct sqlhist_bison *sb,
+			  void *A, void *B, enum filter_type op)
+{
+	struct filter *filter;
+	struct expr *expr;
+
+	create_filter(filter, &expr);
+
+	filter->lval = A;
+	filter->rval = B;
+
+	filter->type = op;
+
+	return expr;
+}
+
+__hidden int add_match(struct sqlhist_bison *sb, void *A, void *B)
+{
+	struct sql_table *table = sb->table;
+	struct match *match;
+
+	match = calloc(1, sizeof(*match));
+	if (!match)
+		return -1;
+
+	match->lval = A;
+	match->rval = B;
+
+	*table->next_match = match;
+	table->next_match = &match->next;
+
+	return 0;
+}
+__hidden void *add_compare(struct sqlhist_bison *sb,
+			   void *A, void *B, enum compare_type type)
+{
+	struct compare *compare;
+	struct expr *expr;
+
+	create_compare(compare, &expr);
+
+	compare = &expr->compare;
+	compare->lval = A;
+	compare->rval = B;
+	compare->type = type;
+
+	return expr;
+}
+
+__hidden int add_where(struct sqlhist_bison *sb, void *item)
+{
+	struct expr *expr = item;
+	struct sql_table *table = sb->table;
+
+	if (expr->type != EXPR_FILTER)
+		return -1;
+
+	*table->next_where = expr;
+	table->next_where = &expr->next;
+
+	if (expr->next)
+		return -1;
+
+	return 0;
+}
+
+__hidden int add_from(struct sqlhist_bison *sb, void *item)
+{
+	struct expr *expr = item;
+
+	if (expr->type != EXPR_FIELD)
+		return -1;
+
+	sb->table->from = expr;
+
+	return 0;
+}
+
+__hidden int add_to(struct sqlhist_bison *sb, void *item)
+{
+	struct expr *expr = item;
+
+	if (expr->type != EXPR_FIELD)
+		return -1;
+
+	sb->table->to = expr;
+
+	return 0;
+}
+
+__hidden void *add_string(struct sqlhist_bison *sb, const char *str)
+{
+	struct expr *expr;
+	const char **str_p;
+
+	create_string(str_p, &expr);
+	*str_p = str;
+	return expr;
+}
+
+__hidden void *add_number(struct sqlhist_bison *sb, long val)
+{
+	struct expr *expr;
+	long *num;
+
+	create_number(num, &expr);
+	*num = val;
+	return expr;
+}
+
+__hidden int table_start(struct sqlhist_bison *sb)
+{
+	struct sql_table *table;
+
+	table = calloc(1, sizeof(*table));
+	if (!table)
+		return -ENOMEM;
+
+	table->sb = sb;
+	sb->table = table;
+
+	table->next_where = &table->where;
+	table->next_match = &table->matches;
+	table->next_selection = &table->selections;
+
+	return 0;
+}
+
+static int test_event_exists(struct tep_handle *tep,
+			     struct sqlhist_bison *sb,
+			     struct expr *expr, struct tep_event **pevent)
+{
+	struct field *field = &expr->field;
+	const char *system = field->system;
+	const char *event = field->event_name;
+
+	if (!field->event)
+		field->event = tep_find_event_by_name(tep, system, event);
+	if (pevent)
+		*pevent = field->event;
+
+	if (field->event)
+		return 0;
+
+	sb->line_no = expr->line;
+	sb->line_idx = expr->idx;
+
+	parse_error(sb, field->raw, "event not found\n");
+	return -1;
+}
+
+static int test_field_exists(struct tep_handle *tep,
+			     struct sqlhist_bison *sb,
+			     struct expr *expr)
+{
+	struct field *field = &expr->field;
+	struct tep_format_field *tfield;
+	char *field_name;
+	const char *p;
+
+	if (!field->event) {
+		if (test_event_exists(tep, sb, expr, NULL))
+			return -1;
+	}
+
+	/* The field could have a conversion */
+	p = strchr(field->field, '.');
+	if (p)
+		field_name = strndup(field->field, p - field->field);
+	else
+		field_name = strdup(field->field);
+
+	if (!field_name)
+		return -1;
+
+	if (!strcmp(field_name, TRACEFS_TIMESTAMP) ||
+	    !strcmp(field->field, TRACEFS_TIMESTAMP_USECS))
+		tfield = (void *)1L;
+	else
+		tfield = tep_find_any_field(field->event, field_name);
+	free(field_name);
+
+	if (!tfield && (!strcmp(field->field, "COMM") || !strcmp(field->field, "comm")))
+		tfield = (void *)1L;
+
+	if (tfield)
+		return 0;
+
+	sb->line_no = expr->line;
+	sb->line_idx = expr->idx;
+
+	parse_error(sb, field->raw,
+		    "Field '%s' not part of event %s\n",
+		    field->field, field->event_name);
+	return -1;
+}
+
+static int update_vars(struct tep_handle *tep,
+		       struct sql_table *table,
+		       struct expr *expr)
+{
+	struct sqlhist_bison *sb = table->sb;
+	struct field *event_field = &expr->field;
+	enum field_type ftype = FIELD_NONE;
+	struct tep_event *event;
+	struct field *field;
+	const char *label;
+	const char *raw = event_field->raw;
+	const char *event_name;
+	const char *system;
+	const char *p;
+	int label_len = 0, event_len, system_len;
+
+	if (expr == table->to)
+		ftype = FIELD_TO;
+	else if (expr == table->from)
+		ftype = FIELD_FROM;
+
+	p = strchr(raw, '.');
+	if (p) {
+		char *str;
+
+		str = strndup(raw, p - raw);
+		if (!str)
+			return -1;
+		event_field->system = store_str(sb, str);
+		free(str);
+		if (!event_field->system)
+			return -1;
+		p++;
+	} else {
+		p = raw;
+	}
+
+	event_field->event_name = store_str(sb, p);
+	if (!event_field->event_name)
+		return -1;
+
+	if (test_event_exists(tep, sb, expr, &event))
+		return -1;
+
+	if (!event_field->system)
+		event_field->system = store_str(sb, event->system);
+
+	if (!event_field->system)
+		return -1;
+
+	label = event_field->label;
+	if (label)
+		label_len = strlen(label);
+
+	system = event_field->system;
+	system_len = strlen(system);
+
+	event_name = event_field->event_name;
+	event_len = strlen(event_name);
+
+	for_each_field(expr, field, table) {
+		int len;
+
+		field = &expr->field;
+
+		if (field->event)
+			continue;
+
+		raw = field->raw;
+
+		/*
+		 * The field could be:
+		 *     system.event.field...
+		 *     event.field...
+		 *     label.field...
+		 * We check label first.
+		 */
+
+		len = label_len;
+		if (label && !strncmp(raw, label, len) &&
+		    raw[len] == '.') {
+			/* Label matches and takes precedence */
+			goto found;
+		}
+
+		if (!strncmp(raw, system, system_len) &&
+		    raw[system_len] == '.') {
+			raw += system_len + 1;
+			/* Check the event portion next */
+		}
+
+		len = event_len;
+		if (strncmp(raw, event_name, len) ||
+		    raw[len] != '.') {
+			/* Does not match */
+			continue;
+		}
+ found:
+		field->system = system;
+		field->event_name = event_name;
+		field->event = event;
+		field->field = raw + len + 1;
+		field->ftype = ftype;
+
+		if (!strcmp(field->field, "TIMESTAMP"))
+			field->field = store_str(sb, TRACEFS_TIMESTAMP);
+		if (!strcmp(field->field, "TIMESTAMP_USECS"))
+			field->field = store_str(sb, TRACEFS_TIMESTAMP_USECS);
+		if (test_field_exists(tep, sb, expr))
+			return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Called when there's a FROM but no JOIN(to), which means that the
+ * selections can be fields and not mention the event itself.
+ */
+static int update_fields(struct tep_handle *tep,
+			 struct sql_table *table,
+			 struct expr *expr)
+{
+	struct field *event_field = &expr->field;
+	struct sqlhist_bison *sb = table->sb;
+	struct tep_format_field *tfield;
+	struct tep_event *event;
+	struct field *field;
+	const char *p;
+	int len;
+
+	/* First update fields with aliases an such and add event */
+	update_vars(tep, table, expr);
+
+	/*
+	 * If event is not found, the creation of the synth will
+	 * add a proper error, so return "success".
+	*/
+	if (!event_field->event)
+		return 0;
+
+	event = event_field->event;
+
+	for_each_field(expr, field, table) {
+		const char *field_name;
+
+		field = &expr->field;
+
+		if (field->event)
+			continue;
+
+		field_name = field->raw;
+
+		p = strchr(field_name, '.');
+		if (p) {
+			len = p - field_name;
+			p = strndup(field_name, len);
+			if (!p)
+				return -1;
+			field_name = store_str(sb, p);
+			if (!field_name)
+				return -1;
+			free((char *)p);
+		}
+
+		tfield = tep_find_any_field(event, field_name);
+		/* Let it error properly later */
+		if (!tfield)
+			continue;
+
+		field->system = event_field->system;
+		field->event_name = event_field->event_name;
+		field->event = event;
+		field->field = field_name;
+	}
+
+	return 0;
+}
+
+static int match_error(struct sqlhist_bison *sb, struct match *match,
+		       struct field *lmatch, struct field *rmatch)
+{
+	struct field *lval = &match->lval->field;
+	struct field *rval = &match->rval->field;
+	struct field *field;
+	struct expr *expr;
+
+	if (lval->system != lmatch->system ||
+	    lval->event != lmatch->event) {
+		expr = match->lval;
+		field = lval;
+	} else {
+		expr = match->rval;
+		field = rval;
+	}
+
+	sb->line_no = expr->line;
+	sb->line_idx = expr->idx;
+
+	parse_error(sb, field->raw,
+		    "'%s' and '%s' must be a field for each event: '%s' and '%s'\n",
+		    lval->raw, rval->raw, sb->table->to->field.raw,
+		    sb->table->from->field.raw);
+
+	return -1;
+}
+
+static int test_match(struct sql_table *table, struct match *match)
+{
+	struct field *lval, *rval;
+	struct field *to, *from;
+
+	if (!match->lval || !match->rval)
+		return -1;
+
+	if (match->lval->type != EXPR_FIELD || match->rval->type != EXPR_FIELD)
+		return -1;
+
+	to = &table->to->field;
+	from = &table->from->field;
+
+	lval = &match->lval->field;
+	rval = &match->rval->field;
+
+	/*
+	 * Note, strings are stored in the string store, so all
+	 * duplicate strings are the same value, and we can use
+	 * normal "==" and "!=" instead of strcmp().
+	 *
+	 * Either lval == to and rval == from
+	 * or lval == from and rval == to.
+	 */
+	if ((lval->system != to->system) ||
+	    (lval->event != to->event)) {
+		if ((rval->system != to->system) ||
+		    (rval->event != to->event) ||
+		    (lval->system != from->system) ||
+		    (lval->event != from->event))
+			return match_error(table->sb, match, from, to);
+	} else {
+		if ((rval->system != from->system) ||
+		    (rval->event != from->event) ||
+		    (lval->system != to->system) ||
+		    (lval->event != to->event))
+			return match_error(table->sb, match, to, from);
+	}
+	return 0;
+}
+
+static void assign_match(const char *system, const char *event,
+			 struct match *match,
+			 const char **start_match, const char **end_match)
+{
+	struct field *lval, *rval;
+
+	lval = &match->lval->field;
+	rval = &match->rval->field;
+
+	if (lval->system == system &&
+	    lval->event_name == event) {
+		*start_match = lval->field;
+		*end_match = rval->field;
+	} else {
+		*start_match = rval->field;
+		*end_match = lval->field;
+	}
+}
+
+static int build_compare(struct tracefs_synth *synth,
+			 const char *system, const char *event,
+			 struct compare *compare)
+{
+	const char *start_field;
+	const char *end_field;
+	struct field *lval, *rval;
+	enum tracefs_synth_calc calc;
+	int ret;
+
+	if (!compare->name)
+		return -1;
+
+	lval = &compare->lval->field;
+	rval = &compare->rval->field;
+
+	if (lval->system == system &&
+	    lval->event_name == event) {
+		start_field = lval->field;
+		end_field = rval->field;
+		calc = TRACEFS_SYNTH_DELTA_START;
+	} else {
+		start_field = rval->field;
+		end_field = lval->field;
+		calc = TRACEFS_SYNTH_DELTA_END;
+	}
+
+	if (compare->type == COMPARE_ADD)
+		calc = TRACEFS_SYNTH_ADD;
+
+	ret = tracefs_synth_add_compare_field(synth, start_field,
+					      end_field, calc,
+					      compare->name);
+	return ret;
+}
+
+static int verify_filter_error(struct sqlhist_bison *sb, struct expr *expr,
+			       const char *event)
+{
+	struct field *field = &expr->field;
+
+	sb->line_no = expr->line;
+	sb->line_idx = expr->idx;
+
+	parse_error(sb, field->raw,
+		    "event '%s' can not be grouped or '||' together with '%s'\n"
+		    "All filters between '&&' must be for the same event\n",
+		    field->event, event);
+	return -1;
+}
+
+static int do_verify_filter(struct sqlhist_bison *sb, struct filter *filter,
+			    const char **system, const char **event,
+			    enum field_type *ftype)
+{
+	int ret;
+
+	if (filter->type == FILTER_OR ||
+	    filter->type == FILTER_AND) {
+		ret = do_verify_filter(sb, &filter->lval->filter, system, event, ftype);
+		if (ret)
+			return ret;
+		return do_verify_filter(sb, &filter->rval->filter, system, event, ftype);
+	}
+	if (filter->type == FILTER_GROUP ||
+	    filter->type == FILTER_NOT_GROUP) {
+		return do_verify_filter(sb, &filter->lval->filter, system, event, ftype);
+	}
+
+	/*
+	 * system and event will be NULL until we find the left most
+	 * node. Then assign it, and compare on the way back up.
+	 */
+	if (!*system && !*event) {
+		*system = filter->lval->field.system;
+		*event = filter->lval->field.event_name;
+		*ftype = filter->lval->field.ftype;
+		return 0;
+	}
+
+	if (filter->lval->field.system != *system ||
+	    filter->lval->field.event_name != *event)
+		return verify_filter_error(sb, filter->lval, *event);
+
+	return 0;
+}
+
+static int verify_filter(struct sqlhist_bison *sb, struct filter *filter,
+			 const char **system, const char **event,
+			 enum field_type *ftype)
+{
+	int ret;
+
+	switch (filter->type) {
+	case FILTER_OR:
+	case FILTER_AND:
+	case FILTER_GROUP:
+	case FILTER_NOT_GROUP:
+		break;
+	default:
+		return do_verify_filter(sb, filter, system, event, ftype);
+	}
+
+	ret = do_verify_filter(sb, &filter->lval->filter, system, event, ftype);
+	if (ret)
+		return ret;
+
+	switch (filter->type) {
+	case FILTER_OR:
+	case FILTER_AND:
+		return do_verify_filter(sb, &filter->rval->filter, system, event, ftype);
+	default:
+		return 0;
+	}
+}
+
+static int test_field_exists(struct tep_handle *tep, struct sqlhist_bison *sb,
+			     struct expr *expr);
+
+static void filter_compare_error(struct tep_handle *tep,
+				 struct sqlhist_bison *sb,
+				 struct expr *expr)
+{
+	struct field *field = &expr->field;
+
+	switch (errno) {
+	case ENODEV:
+	case EBADE:
+		break;
+	case EINVAL:
+		parse_error(sb, field->raw, "Invalid compare\n");
+		break;
+	default:
+		parse_error(sb, field->raw, "System error?\n");
+		return;
+	}
+
+	/* ENODEV means that an event or field does not exist */
+	if (errno == ENODEV) {
+		if (test_field_exists(tep, sb, expr))
+			return;
+		if (test_field_exists(tep, sb, expr))
+			return;
+		return;
+	}
+
+	/* fields exist, but values are not compatible */
+	sb->line_no = expr->line;
+	sb->line_idx = expr->idx;
+
+	parse_error(sb, field->raw,
+		    "Field '%s' is not compatible to be compared with the given value\n",
+		    field->field);
+}
+
+static void filter_error(struct tep_handle *tep,
+			 struct sqlhist_bison *sb, struct expr *expr)
+{
+	struct filter *filter = &expr->filter;
+
+	sb->line_no = expr->line;
+	sb->line_idx = expr->idx;
+
+	switch (filter->type) {
+	case FILTER_NOT_GROUP:
+	case FILTER_GROUP:
+	case FILTER_OR:
+	case FILTER_AND:
+		break;
+	default:
+		filter_compare_error(tep, sb, filter->lval);
+		return;
+	}
+
+	sb->line_no = expr->line;
+	sb->line_idx = expr->idx;
+
+	parse_error(sb, "", "Problem with filter entry?\n");
+}
+
+static int build_filter(struct tep_handle *tep, struct sqlhist_bison *sb,
+			struct tracefs_synth *synth,
+			bool start, struct expr *expr, bool *started)
+{
+	int (*append_filter)(struct tracefs_synth *synth,
+			     enum tracefs_filter type,
+			     const char *field,
+			     enum tracefs_compare compare,
+			     const char *val);
+	struct filter *filter = &expr->filter;
+	enum tracefs_compare cmp;
+	const char *val;
+	int and_or = TRACEFS_FILTER_AND;
+	char num[64];
+	int ret;
+
+	if (start)
+		append_filter = tracefs_synth_append_start_filter;
+	else
+		append_filter = tracefs_synth_append_end_filter;
+
+	if (started && *started) {
+		ret = append_filter(synth, and_or, NULL, 0, NULL);
+		ret = append_filter(synth, TRACEFS_FILTER_OPEN_PAREN,
+				    NULL, 0, NULL);
+	}
+
+	switch (filter->type) {
+	case FILTER_NOT_GROUP:
+		ret = append_filter(synth, TRACEFS_FILTER_NOT,
+				    NULL, 0, NULL);
+		if (ret < 0)
+			goto out;
+		/* Fall through */
+	case FILTER_GROUP:
+		ret = append_filter(synth, TRACEFS_FILTER_OPEN_PAREN,
+				    NULL, 0, NULL);
+		if (ret < 0)
+			goto out;
+		ret = build_filter(tep, sb, synth, start, filter->lval, NULL);
+		if (ret < 0)
+			goto out;
+		ret = append_filter(synth, TRACEFS_FILTER_CLOSE_PAREN,
+				    NULL, 0, NULL);
+		goto out;
+
+	case FILTER_OR:
+		and_or = TRACEFS_FILTER_OR;
+		/* Fall through */
+	case FILTER_AND:
+		ret = build_filter(tep, sb, synth, start, filter->lval, NULL);
+		if (ret < 0)
+			goto out;
+		ret = append_filter(synth, and_or, NULL, 0, NULL);
+
+		if (ret)
+			goto out;
+		ret = build_filter(tep, sb, synth, start, filter->rval, NULL);
+		goto out;
+	default:
+		break;
+	}
+
+	switch (filter->rval->type) {
+	case EXPR_NUMBER:
+		sprintf(num, "%ld", filter->rval->number);
+		val = num;
+		break;
+	case EXPR_STRING:
+		val = filter->rval->string;
+		break;
+	default:
+		break;
+	}
+
+	switch (filter->type) {
+	case FILTER_EQ:		cmp = TRACEFS_COMPARE_EQ; break;
+	case FILTER_NE:		cmp = TRACEFS_COMPARE_NE; break;
+	case FILTER_LE:		cmp = TRACEFS_COMPARE_LE; break;
+	case FILTER_LT:		cmp = TRACEFS_COMPARE_LT; break;
+	case FILTER_GE:		cmp = TRACEFS_COMPARE_GE; break;
+	case FILTER_GT:		cmp = TRACEFS_COMPARE_GT; break;
+	case FILTER_BIN_AND:	cmp = TRACEFS_COMPARE_AND; break;
+	case FILTER_STR_CMP:	cmp = TRACEFS_COMPARE_RE; break;
+	default:
+		tracefs_warning("Error invalid filter type '%d'", filter->type);
+		return ERANGE;
+	}
+
+	ret = append_filter(synth, TRACEFS_FILTER_COMPARE,
+			    filter->lval->field.field, cmp, val);
+
+	if (ret)
+		filter_error(tep, sb, expr);
+ out:
+	if (!ret && started) {
+		if (*started)
+			ret = append_filter(synth, TRACEFS_FILTER_CLOSE_PAREN,
+					    NULL, 0, NULL);
+		*started = true;
+	}
+	return ret;
+}
+
+static void *field_match_error(struct tep_handle *tep, struct sqlhist_bison *sb,
+			       struct match *match)
+{
+	switch (errno) {
+	case ENODEV:
+	case EBADE:
+		break;
+	default:
+		/* System error */
+		return NULL;
+	}
+
+	/* ENODEV means that an event or field does not exist */
+	if (errno == ENODEV) {
+		if (test_field_exists(tep, sb, match->lval))
+			return NULL;
+		if (test_field_exists(tep, sb, match->rval))
+			return NULL;
+		return NULL;
+	}
+
+	/* fields exist, but values are not compatible */
+	sb->line_no = match->lval->line;
+	sb->line_idx = match->lval->idx;
+
+	parse_error(sb, match->lval->field.raw,
+		    "Field '%s' is not compatible to match field '%s'\n",
+		    match->lval->field.raw, match->rval->field.raw);
+	return NULL;
+}
+
+static void *synth_init_error(struct tep_handle *tep, struct sql_table *table)
+{
+	struct sqlhist_bison *sb = table->sb;
+	struct match *match = table->matches;
+
+	switch (errno) {
+	case ENODEV:
+	case EBADE:
+		break;
+	default:
+		/* System error */
+		return NULL;
+	}
+
+	/* ENODEV could mean that start or end events do not exist */
+	if (errno == ENODEV) {
+		if (test_event_exists(tep, sb, table->from, NULL))
+			return NULL;
+		if (test_event_exists(tep, sb, table->to, NULL))
+			return NULL;
+	}
+
+	return field_match_error(tep, sb, match);
+}
+
+static void selection_error(struct tep_handle *tep,
+			    struct sqlhist_bison *sb, struct expr *expr)
+{
+	/* We just care about event not existing */
+	if (errno != ENODEV)
+		return;
+
+	test_field_exists(tep, sb, expr);
+}
+
+static void compare_error(struct tep_handle *tep,
+			    struct sqlhist_bison *sb, struct expr *expr)
+{
+	struct compare *compare = &expr->compare;
+
+	if (!compare->name) {
+		sb->line_no = expr->line;
+		sb->line_idx = expr->idx + strlen("no name");
+
+		parse_error(sb, "no name",
+		    "Field calculations must be labeled 'AS name'\n");
+	}
+
+	switch (errno) {
+	case ENODEV:
+	case EBADE:
+		break;
+	default:
+		/* System error */
+		return;
+	}
+
+	/* ENODEV means that an event or field does not exist */
+	if (errno == ENODEV) {
+		if (test_field_exists(tep, sb, compare->lval))
+			return;
+		if (test_field_exists(tep, sb, compare->rval))
+			return;
+		return;
+	}
+
+	/* fields exist, but values are not compatible */
+	sb->line_no = compare->lval->line;
+	sb->line_idx = compare->lval->idx;
+
+	parse_error(sb, compare->lval->field.raw,
+		    "'%s' is not compatible to compare with '%s'\n",
+		    compare->lval->field.raw, compare->rval->field.raw);
+}
+
+static void compare_no_to_error(struct sqlhist_bison *sb, struct expr *expr)
+{
+	struct compare *compare = &expr->compare;
+
+	sb->line_no = compare->lval->line;
+	sb->line_idx = compare->lval->idx;
+
+	parse_error(sb, compare->lval->field.raw,
+		    "Simple SQL (without JOIN/ON) do not allow comparisons\n",
+		    compare->lval->field.raw, compare->rval->field.raw);
+}
+
+static void where_no_to_error(struct sqlhist_bison *sb, struct expr *expr,
+			      const char *from_event, const char *event)
+{
+	while (expr) {
+		switch (expr->filter.type) {
+		case FILTER_OR:
+		case FILTER_AND:
+		case FILTER_GROUP:
+		case FILTER_NOT_GROUP:
+			expr = expr->filter.lval;
+			continue;
+		default:
+			break;
+		}
+		break;
+	}
+	sb->line_no = expr->filter.lval->line;
+	sb->line_idx = expr->filter.lval->idx;
+
+	parse_error(sb, expr->filter.lval->field.raw,
+		    "Event '%s' does not match FROM event '%s'\n",
+		    event, from_event);
+}
+
+static int verify_field_type(struct tep_handle *tep,
+			     struct sqlhist_bison *sb,
+			     struct expr *expr, int *cnt)
+{
+	struct field *field = &expr->field;
+	struct tep_event *event;
+	struct tep_format_field *tfield;
+	char *type;
+	int ret;
+	int i;
+
+	if (!field->type)
+		return 0;
+
+	sb->line_no = expr->line;
+	sb->line_idx = expr->idx;
+
+	event = tep_find_event_by_name(tep, field->system, field->event_name);
+	if (!event) {
+		parse_error(sb, field->raw,
+			    "Event '%s' not found\n",
+			    field->event_name ? : "(null)");
+		return -1;
+	}
+
+	tfield = tep_find_any_field(event, field->field);
+	if (!tfield) {
+		parse_error(sb, field->raw,
+			    "Field '%s' not part of event '%s'\n",
+			    field->field ? : "(null)", field->event);
+		return -1;
+	}
+
+	type = strdup(field->type);
+	if (!type)
+		return -1;
+
+	if (!strcmp(type, TRACEFS_HIST_COUNTER) ||
+	    !strcmp(type, "_COUNTER_")) {
+		ret = HIST_COUNTER_TYPE;
+		if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY)) {
+			parse_error(sb, field->raw,
+				    "'%s' is a string, and counters may only be used with numbers\n");
+			ret = -1;
+		}
+		goto out;
+	}
+
+	for (i = 0; type[i]; i++)
+		type[i] = tolower(type[i]);
+
+	if (!strcmp(type, "hex")) {
+		if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY))
+			goto fail_type;
+		ret = TRACEFS_HIST_KEY_HEX;
+	} else if (!strcmp(type, "sym")) {
+		if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY))
+			goto fail_type;
+		ret = TRACEFS_HIST_KEY_SYM;
+	} else if (!strcmp(type, "sym-offset")) {
+		if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY))
+			goto fail_type;
+		ret = TRACEFS_HIST_KEY_SYM_OFFSET;
+	} else if (!strcmp(type, "syscall")) {
+		if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY))
+			goto fail_type;
+		ret = TRACEFS_HIST_KEY_SYSCALL;
+	} else if (!strcmp(type, "execname") ||
+		   !strcmp(type, "comm")) {
+		ret = TRACEFS_HIST_KEY_EXECNAME;
+		if (strcmp(field->field, "common_pid")) {
+			parse_error(sb, field->raw,
+				    "'%s' is only allowed for common_pid\n",
+				    type);
+			ret = -1;
+		}
+	} else if (!strcmp(type, "log") ||
+		   !strcmp(type, "log2")) {
+		if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY))
+			goto fail_type;
+		ret = TRACEFS_HIST_KEY_LOG;
+	} else if (!strncmp(type, "buckets", 7)) {
+		if (type[7] != '=' || !isdigit(type[8])) {
+			parse_error(sb, field->raw,
+				    "buckets type must have '=[number]' after it\n");
+			ret = -1;
+			goto out;
+		}
+		*cnt = atoi(&type[8]);
+		if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY))
+			goto fail_type;
+		ret = TRACEFS_HIST_KEY_BUCKETS;
+	} else {
+		parse_error(sb, field->raw,
+			    "Cast of '%s' to unknown type '%s'\n",
+			    field->raw, type);
+		ret = -1;
+	}
+ out:
+	free(type);
+	return ret;
+ fail_type:
+	parse_error(sb, field->raw,
+		    "Field '%s' cast to '%s' but is of type %s\n",
+		    field->field, type, tfield->flags & TEP_FIELD_IS_STRING ?
+		    "string" : "array");
+	free(type);
+	return -1;
+}
+
+static struct tracefs_synth *build_synth(struct tep_handle *tep,
+					 const char *name,
+					 struct sql_table *table)
+{
+	struct tracefs_synth *synth;
+	struct field *field;
+	struct match *match;
+	struct expr *expr;
+	const char *start_system;
+	const char *start_event;
+	const char *end_system;
+	const char *end_event;
+	const char *start_match;
+	const char *end_match;
+	bool started_start = false;
+	bool started_end = false;
+	bool non_val = false;
+	int ret;
+
+	if (!table->from)
+		return NULL;
+
+	/* This could be a simple SQL statement to only build a histogram */
+	if (!table->to) {
+		ret = update_fields(tep, table, table->from);
+		if (ret < 0)
+			return NULL;
+
+		start_system = table->from->field.system;
+		start_event = table->from->field.event_name;
+
+		synth = synth_init_from(tep, start_system, start_event);
+		if (!synth)
+			return synth_init_error(tep, table);
+		goto hist_only;
+	}
+
+	ret = update_vars(tep, table, table->from);
+	if (ret < 0)
+		return NULL;
+
+	ret = update_vars(tep, table, table->to);
+	if (ret < 0)
+		return NULL;
+
+	start_system = table->from->field.system;
+	start_event = table->from->field.event_name;
+
+	match = table->matches;
+	if (!match)
+		return NULL;
+
+	ret = test_match(table, match);
+	if (ret < 0)
+		return NULL;
+
+	end_system = table->to->field.system;
+	end_event = table->to->field.event_name;
+
+	assign_match(start_system, start_event, match,
+		     &start_match, &end_match);
+
+	synth = tracefs_synth_alloc(tep, name, start_system,
+				    start_event, end_system, end_event,
+				    start_match, end_match, NULL);
+	if (!synth)
+		return synth_init_error(tep, table);
+
+	for (match = match->next; match; match = match->next) {
+		ret = test_match(table, match);
+		if (ret < 0)
+			goto free;
+
+		assign_match(start_system, start_event, match,
+			     &start_match, &end_match);
+
+		ret = tracefs_synth_add_match_field(synth,
+						    start_match,
+						    end_match, NULL);
+		if (ret < 0) {
+			field_match_error(tep, table->sb, match);
+			goto free;
+		}
+	}
+
+ hist_only:
+	/* table->to may be NULL here */
+
+	for (expr = table->selections; expr; expr = expr->next) {
+		if (expr->type == EXPR_FIELD) {
+			ret = -1;
+			field = &expr->field;
+			if (field->ftype != FIELD_TO &&
+			    field->system == start_system &&
+			    field->event_name == start_event) {
+				int type;
+				int cnt = 0;
+				type = verify_field_type(tep, table->sb, expr, &cnt);
+				if (type < 0)
+					goto free;
+				if (type != HIST_COUNTER_TYPE)
+					non_val = true;
+				ret = synth_add_start_field(synth,
+						field->field, field->label,
+						type, cnt);
+			} else if (table->to) {
+				ret = tracefs_synth_add_end_field(synth,
+						field->field, field->label);
+			}
+			if (ret < 0) {
+				selection_error(tep, table->sb, expr);
+				goto free;
+			}
+			continue;
+		}
+
+		if (!table->to) {
+			compare_no_to_error(table->sb, expr);
+			goto free;
+		}
+
+		if (expr->type != EXPR_COMPARE)
+			goto free;
+
+		ret = build_compare(synth, start_system, end_system,
+				    &expr->compare);
+		if (ret < 0) {
+			compare_error(tep, table->sb, expr);
+			goto free;
+		}
+	}
+
+	if (!non_val && !table->to) {
+		table->sb->line_no = 0;
+		table->sb->line_idx = 10;
+		parse_error(table->sb, "CAST",
+			    "Not all SELECT items can be of type _COUNTER_\n");
+		goto free;
+	}
+
+	for (expr = table->where; expr; expr = expr->next) {
+		const char *filter_system = NULL;
+		const char *filter_event = NULL;
+		enum field_type ftype = FIELD_NONE;
+		bool *started;
+		bool start;
+
+		ret = verify_filter(table->sb, &expr->filter, &filter_system,
+				    &filter_event, &ftype);
+		if (ret < 0)
+			goto free;
+
+		start = filter_system == start_system &&
+			filter_event == start_event &&
+			ftype != FIELD_TO;
+
+		if (start)
+			started = &started_start;
+		else if (!table->to) {
+			where_no_to_error(table->sb, expr, start_event,
+					  filter_event);
+			goto free;
+		} else
+			started = &started_end;
+
+		ret = build_filter(tep, table->sb, synth, start, expr, started);
+		if (ret < 0)
+			goto free;
+	}
+
+	return synth;
+ free:
+	tracefs_synth_free(synth);
+	return NULL;
+}
+
+static void free_sql_table(struct sql_table *table)
+{
+	struct match *match;
+	struct expr *expr;
+
+	if (!table)
+		return;
+
+	while ((expr = table->exprs)) {
+		table->exprs = expr->free_list;
+		free(expr);
+	}
+
+	while ((match = table->matches)) {
+		table->matches = match->next;
+		free(match);
+	}
+
+	free(table);
+}
+
+static void free_str_hash(struct str_hash **hash)
+{
+	struct str_hash *item;
+	int i;
+
+	for (i = 0; i < 1 << HASH_BITS; i++) {
+		while ((item = hash[i])) {
+			hash[i] = item->next;
+			free(item->str);
+			free(item);
+		}
+	}
+}
+
+static void free_sb(struct sqlhist_bison *sb)
+{
+	free_sql_table(sb->table);
+	free_str_hash(sb->str_hash);
+	free(sb->parse_error_str);
+}
+
+struct tracefs_synth *tracefs_sql(struct tep_handle *tep, const char *name,
+				  const char *sql_buffer, char **err)
+{
+	struct tracefs_synth *synth = NULL;
+	struct sqlhist_bison sb;
+	int ret;
+
+	if (!tep || !sql_buffer) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	memset(&sb, 0, sizeof(sb));
+
+	sb.buffer = sql_buffer;
+	sb.buffer_size = strlen(sql_buffer);
+	sb.buffer_idx = 0;
+
+	ret = yylex_init_extra(&sb, &sb.scanner);
+	if (ret < 0) {
+		yylex_destroy(sb.scanner);
+		return NULL;
+	}
+
+	ret = tracefs_parse(&sb);
+	yylex_destroy(sb.scanner);
+
+	if (ret)
+		goto free;
+
+	synth = build_synth(tep, name, sb.table);
+
+ free:
+	if (!synth) {
+		if (sb.parse_error_str && err) {
+			*err = sb.parse_error_str;
+			sb.parse_error_str = NULL;
+		}
+	}
+	free_sb(&sb);
+	return synth;
+}
diff --git a/src/tracefs-tools.c b/src/tracefs-tools.c
new file mode 100644
index 0000000..8e7b46d
--- /dev/null
+++ b/src/tracefs-tools.c
@@ -0,0 +1,1273 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ * Updates:
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <regex.h>
+#include <dirent.h>
+#include <limits.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+__hidden pthread_mutex_t toplevel_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define TRACE_CTRL		"tracing_on"
+#define TRACE_FILTER		"set_ftrace_filter"
+#define TRACE_NOTRACE		"set_ftrace_notrace"
+#define TRACE_FILTER_LIST	"available_filter_functions"
+#define CUR_TRACER		"current_tracer"
+
+#define TRACERS \
+	C(NOP,                  "nop"),			\
+	C(CUSTOM,		"CUSTOM"),		\
+	C(FUNCTION,             "function"),            \
+	C(FUNCTION_GRAPH,       "function_graph"),      \
+	C(IRQSOFF,              "irqsoff"),             \
+	C(PREEMPTOFF,           "preemptoff"),          \
+	C(PREEMPTIRQSOFF,       "preemptirqsoff"),      \
+	C(WAKEUP,               "wakeup"),              \
+	C(WAKEUP_RT,            "wakeup_rt"),	\
+	C(WAKEUP_DL,            "wakeup_dl"),           \
+	C(MMIOTRACE,            "mmiotrace"),           \
+	C(HWLAT,                "hwlat"),               \
+	C(BRANCH,               "branch"),              \
+	C(BLOCK,                "block")
+
+#undef C
+#define C(a, b) b
+const char *tracers[] = { TRACERS };
+
+#undef C
+#define C(a, b) TRACEFS_TRACER_##a
+const int tracer_enums[] = { TRACERS };
+
+/* File descriptor for Top level set_ftrace_filter  */
+static int ftrace_filter_fd = -1;
+static int ftrace_notrace_fd = -1;
+
+static const char * const options_map[] = {
+	"unknown",
+	"annotate",
+	"bin",
+	"blk_cgname",
+	"blk_cgroup",
+	"blk_classic",
+	"block",
+	"context-info",
+	"disable_on_free",
+	"display-graph",
+	"event-fork",
+	"funcgraph-abstime",
+	"funcgraph-cpu",
+	"funcgraph-duration",
+	"funcgraph-irqs",
+	"funcgraph-overhead",
+	"funcgraph-overrun",
+	"funcgraph-proc",
+	"funcgraph-tail",
+	"func_stack_trace",
+	"function-fork",
+	"function-trace",
+	"graph-time",
+	"hex",
+	"irq-info",
+	"latency-format",
+	"markers",
+	"overwrite",
+	"pause-on-trace",
+	"printk-msg-only",
+	"print-parent",
+	"raw",
+	"record-cmd",
+	"record-tgid",
+	"sleep-time",
+	"stacktrace",
+	"sym-addr",
+	"sym-offset",
+	"sym-userobj",
+	"trace_printk",
+	"userstacktrace",
+	"verbose" };
+
+static int trace_on_off(int fd, bool on)
+{
+	const char *val = on ? "1" : "0";
+	int ret;
+
+	ret = write(fd, val, 1);
+	if (ret == 1)
+		return 0;
+
+	return -1;
+}
+
+static int trace_on_off_file(struct tracefs_instance *instance, bool on)
+{
+	int ret;
+	int fd;
+
+	fd = tracefs_instance_file_open(instance, TRACE_CTRL, O_WRONLY);
+	if (fd < 0)
+		return -1;
+	ret = trace_on_off(fd, on);
+	close(fd);
+
+	return ret;
+}
+
+/**
+ * tracefs_trace_is_on - Check if writing traces to the ring buffer is enabled
+ * @instance: ftrace instance, can be NULL for the top instance
+ *
+ * Returns -1 in case of an error, 0 if tracing is disable or 1 if tracing
+ * is enabled.
+ */
+int tracefs_trace_is_on(struct tracefs_instance *instance)
+{
+	long long res;
+
+	if (tracefs_instance_file_read_number(instance, TRACE_CTRL, &res) == 0)
+		return (int)res;
+
+	return -1;
+}
+
+/**
+ * tracefs_trace_on - Enable writing traces to the ring buffer of the given instance
+ * @instance: ftrace instance, can be NULL for the top instance
+ *
+ * Returns -1 in case of an error or 0 otherwise
+ */
+int tracefs_trace_on(struct tracefs_instance *instance)
+{
+	return trace_on_off_file(instance, true);
+}
+
+/**
+ * tracefs_trace_off - Disable writing traces to the ring buffer of the given instance
+ * @instance: ftrace instance, can be NULL for the top instance
+ *
+ * Returns -1 in case of an error or 0 otherwise
+ */
+int tracefs_trace_off(struct tracefs_instance *instance)
+{
+	return trace_on_off_file(instance, false);
+}
+
+/**
+ * tracefs_trace_on_fd - Enable writing traces to the ring buffer
+ * @fd: File descriptor to ftrace tracing_on file, previously opened
+ *	with tracefs_trace_on_get_fd()
+ *
+ * Returns -1 in case of an error or 0 otherwise
+ */
+int tracefs_trace_on_fd(int fd)
+{
+	if (fd < 0)
+		return -1;
+	return trace_on_off(fd, true);
+}
+
+/**
+ * tracefs_trace_off_fd - Disable writing traces to the ring buffer
+ * @fd: File descriptor to ftrace tracing_on file, previously opened
+ *	with tracefs_trace_on_get_fd()
+ *
+ * Returns -1 in case of an error or 0 otherwise
+ */
+int tracefs_trace_off_fd(int fd)
+{
+	if (fd < 0)
+		return -1;
+	return trace_on_off(fd, false);
+}
+
+/**
+ * tracefs_option_name - Get trace option name from id
+ * @id: trace option id
+ *
+ * Returns string with option name, or "unknown" in case of not known option id.
+ * The returned string must *not* be freed.
+ */
+const char *tracefs_option_name(enum tracefs_option_id id)
+{
+	/* Make sure options map contains all the options */
+	BUILD_BUG_ON(ARRAY_SIZE(options_map) != TRACEFS_OPTION_MAX);
+
+	if (id < TRACEFS_OPTION_MAX)
+		return options_map[id];
+
+	return options_map[0];
+}
+
+/**
+ * tracefs_option_id - Get trace option ID from name
+ * @name: trace option name
+ *
+ * Returns trace option ID or TRACEFS_OPTION_INVALID in case of an error or
+ * unknown option name.
+ */
+enum tracefs_option_id tracefs_option_id(const char *name)
+{
+	int i;
+
+	if (!name)
+		return TRACEFS_OPTION_INVALID;
+
+	for (i = 0; i < TRACEFS_OPTION_MAX; i++) {
+		if (strlen(name) == strlen(options_map[i]) &&
+		    !strcmp(options_map[i], name))
+			return i;
+	}
+
+	return TRACEFS_OPTION_INVALID;
+}
+
+const static struct tracefs_options_mask *
+trace_get_options(struct tracefs_instance *instance, bool enabled)
+{
+	pthread_mutex_t *lock = trace_get_lock(instance);
+	struct tracefs_options_mask *bitmask;
+	enum tracefs_option_id id;
+	unsigned long long set;
+	char file[PATH_MAX];
+	struct stat st;
+	long long val;
+	char *path;
+	int ret;
+
+	bitmask = enabled ? enabled_opts_mask(instance) :
+			   supported_opts_mask(instance);
+
+	for (id = 1; id < TRACEFS_OPTION_MAX; id++) {
+		snprintf(file, PATH_MAX, "options/%s", options_map[id]);
+		path = tracefs_instance_get_file(instance, file);
+		if (!path)
+			return NULL;
+
+		set = 1;
+		ret = stat(path, &st);
+		if (ret < 0 || !S_ISREG(st.st_mode)) {
+			set = 0;
+		} else if (enabled) {
+			ret = tracefs_instance_file_read_number(instance, file, &val);
+			if (ret != 0 || val != 1)
+				set = 0;
+		}
+
+		pthread_mutex_lock(lock);
+		bitmask->mask = (bitmask->mask & ~(1ULL << (id - 1))) | (set << (id - 1));
+		pthread_mutex_unlock(lock);
+
+		tracefs_put_tracing_file(path);
+	}
+
+
+	return bitmask;
+}
+
+/**
+ * tracefs_options_get_supported - Get all supported trace options in given instance
+ * @instance: ftrace instance, can be NULL for the top instance
+ *
+ * Returns bitmask structure with all trace options, supported in given instance,
+ * or NULL in case of an error.
+ */
+const struct tracefs_options_mask *
+tracefs_options_get_supported(struct tracefs_instance *instance)
+{
+	return trace_get_options(instance, false);
+}
+
+/**
+ * tracefs_options_get_enabled - Get all currently enabled trace options in given instance
+ * @instance: ftrace instance, can be NULL for the top instance
+ *
+ * Returns bitmask structure with all trace options, enabled in given instance,
+ * or NULL in case of an error.
+ */
+const struct tracefs_options_mask *
+tracefs_options_get_enabled(struct tracefs_instance *instance)
+{
+	return trace_get_options(instance, true);
+}
+
+static int trace_config_option(struct tracefs_instance *instance,
+			       enum tracefs_option_id id, bool set)
+{
+	const char *set_str = set ? "1" : "0";
+	char file[PATH_MAX];
+	const char *name;
+
+	name = tracefs_option_name(id);
+	if (!name)
+		return -1;
+
+	snprintf(file, PATH_MAX, "options/%s", name);
+	if (strlen(set_str) != tracefs_instance_file_write(instance, file, set_str))
+		return -1;
+	return 0;
+}
+
+/**
+ * tracefs_option_enable - Enable trace option
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @id: trace option id
+ *
+ * Returns -1 in case of an error or 0 otherwise
+ */
+int tracefs_option_enable(struct tracefs_instance *instance, enum tracefs_option_id id)
+{
+	return trace_config_option(instance, id, true);
+}
+
+/**
+ * tracefs_option_disable - Disable trace option
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @id: trace option id
+ *
+ * Returns -1 in case of an error or 0 otherwise
+ */
+int tracefs_option_disable(struct tracefs_instance *instance, enum tracefs_option_id id)
+{
+	return trace_config_option(instance, id, false);
+}
+
+/**
+ * tracefs_option_is_supported - Check if an option is supported
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @id: trace option id
+ *
+ * Returns true if an option with given id is supported by the system, false if
+ * it is not supported.
+ */
+bool tracefs_option_is_supported(struct tracefs_instance *instance, enum tracefs_option_id id)
+{
+	const char *name = tracefs_option_name(id);
+	char file[PATH_MAX];
+
+	if (!name)
+		return false;
+	snprintf(file, PATH_MAX, "options/%s", name);
+	return tracefs_file_exists(instance, file);
+}
+
+/**
+ * tracefs_option_is_enabled - Check if an option is enabled in given instance
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @id: trace option id
+ *
+ * Returns true if an option with given id is enabled in the given instance,
+ * false if it is not enabled.
+ */
+bool tracefs_option_is_enabled(struct tracefs_instance *instance, enum tracefs_option_id id)
+{
+	const char *name = tracefs_option_name(id);
+	char file[PATH_MAX];
+	long long res;
+
+	if (!name)
+		return false;
+	snprintf(file, PATH_MAX, "options/%s", name);
+	if (!tracefs_instance_file_read_number(instance, file, &res) && res)
+		return true;
+
+	return false;
+}
+
+/**
+ * tracefs_option_mask_is_set - Check if given option is set in the bitmask
+ * @options: Options bitmask
+ * @id: trace option id
+ *
+ * Returns true if an option with given id is set in the bitmask,
+ * false if it is not set.
+ */
+bool tracefs_option_mask_is_set(const struct tracefs_options_mask *options,
+			   enum tracefs_option_id id)
+{
+	if (id > TRACEFS_OPTION_INVALID)
+		return options->mask & (1ULL << (id - 1));
+	return false;
+}
+
+struct func_list {
+	struct func_list	*next;
+	char			*func;
+	unsigned int		start;
+	unsigned int		end;
+};
+
+struct func_filter {
+	const char		*filter;
+	regex_t			re;
+	bool			set;
+	bool			is_regex;
+};
+
+static bool is_regex(const char *str)
+{
+	int i;
+
+	for (i = 0; str[i]; i++) {
+		switch (str[i]) {
+		case 'a' ... 'z':
+		case 'A'...'Z':
+		case '_':
+		case '0'...'9':
+		case '*':
+		case '.':
+			/* Dots can be part of a function name */
+		case '?':
+			continue;
+		default:
+			return true;
+		}
+	}
+	return false;
+}
+
+static char *update_regex(const char *reg)
+{
+	int len = strlen(reg);
+	char *str;
+
+	if (reg[0] == '^' && reg[len - 1] == '$')
+		return strdup(reg);
+
+	str = malloc(len + 3);
+	if (reg[0] == '^') {
+		strcpy(str, reg);
+	} else {
+		str[0] = '^';
+		strcpy(str + 1, reg);
+		len++; /* add ^ */
+	}
+	if (str[len - 1] != '$')
+		str[len++]= '$';
+	str[len] = '\0';
+	return str;
+}
+
+/*
+ * Convert a glob into a regular expression.
+ */
+static char *make_regex(const char *glob)
+{
+	char *str;
+	int cnt = 0;
+	int i, j;
+
+	for (i = 0; glob[i]; i++) {
+		if (glob[i] == '*'|| glob[i] == '.')
+			cnt++;
+	}
+
+	/* '^' + ('*'->'.*' or '.' -> '\.') + '$' + '\0' */
+	str = malloc(i + cnt + 3);
+	if (!str)
+		return NULL;
+
+	str[0] = '^';
+	for (i = 0, j = 1; glob[i]; i++, j++) {
+		if (glob[i] == '*')
+			str[j++] = '.';
+		/* Dots can be part of a function name */
+		if (glob[i] == '.')
+			str[j++] = '\\';
+		str[j] = glob[i];
+	}
+	str[j++] = '$';
+	str[j] = '\0';
+	return str;
+}
+
+static bool match(const char *str, struct func_filter *func_filter)
+{
+	return regexec(&func_filter->re, str, 0, NULL, 0) == 0;
+}
+
+/*
+ * Return 0 on success, -1 error writing, 1 on other errors.
+ */
+static int write_filter(int fd, const char *filter, const char *module)
+{
+	char *each_str = NULL;
+	int write_size;
+	int size;
+
+	if (module)
+		write_size = asprintf(&each_str, "%s:mod:%s ", filter, module);
+	else
+		write_size = asprintf(&each_str, "%s ", filter);
+
+	if (write_size < 0)
+		return 1;
+
+	size = write(fd, each_str, write_size);
+	free(each_str);
+
+	/* compare written bytes*/
+	if (size < write_size)
+		return -1;
+
+	return 0;
+}
+
+static int add_func(struct func_list ***next_func_ptr, unsigned int index)
+{
+	struct func_list **next_func = *next_func_ptr;
+	struct func_list *func_list = *next_func;
+
+	if (!func_list) {
+		func_list = calloc(1, sizeof(*func_list));
+		if (!func_list)
+			return -1;
+		func_list->start = index;
+		func_list->end = index;
+		*next_func = func_list;
+		return 0;
+	}
+
+	if (index == func_list->end + 1) {
+		func_list->end = index;
+		return 0;
+	}
+	*next_func_ptr = &func_list->next;
+	return add_func(next_func_ptr, index);
+}
+
+static int add_func_str(struct func_list ***next_func_ptr, const char *func)
+{
+	struct func_list **next_func = *next_func_ptr;
+	struct func_list *func_list = *next_func;
+
+	if (!func_list) {
+		func_list = calloc(1, sizeof(*func_list));
+		if (!func_list)
+			return -1;
+		func_list->func = strdup(func);
+		if (!func_list->func)
+			return -1;
+		*next_func = func_list;
+		return 0;
+	}
+	*next_func_ptr = &func_list->next;
+	return add_func_str(next_func_ptr, func);
+}
+
+static void free_func_list(struct func_list *func_list)
+{
+	struct func_list *f;
+
+	while (func_list) {
+		f = func_list;
+		func_list = f->next;
+		free(f->func);
+		free(f);
+	}
+}
+
+enum match_type {
+	FILTER_CHECK	= (1 << 0),
+	FILTER_WRITE	= (1 << 1),
+	FILTER_FUTURE	= (1 << 2),
+	SAVE_STRING	= (1 << 2),
+};
+
+static int match_filters(int fd, struct func_filter *func_filter,
+			 const char *module, struct func_list **func_list,
+			 int flags)
+{
+	enum match_type type = flags & (FILTER_CHECK | FILTER_WRITE);
+	bool save_str = flags & SAVE_STRING;
+	bool future = flags & FILTER_FUTURE;
+	bool mod_match = false;
+	char *line = NULL;
+	size_t size = 0;
+	char *path;
+	FILE *fp;
+	int index = 0;
+	int ret = 1;
+	int mlen;
+
+	path = tracefs_get_tracing_file(TRACE_FILTER_LIST);
+	if (!path)
+		return 1;
+
+	fp = fopen(path, "r");
+	tracefs_put_tracing_file(path);
+
+	if (!fp)
+		return 1;
+
+	if (module)
+		mlen = strlen(module);
+
+	while (getline(&line, &size, fp) >= 0) {
+		char *saveptr = NULL;
+		char *tok, *mtok;
+		int len = strlen(line);
+
+		if (line[len - 1] == '\n')
+			line[len - 1] = '\0';
+		tok = strtok_r(line, " ", &saveptr);
+		if (!tok)
+			goto next;
+
+		index++;
+
+		if (module) {
+			mtok = strtok_r(NULL, " ", &saveptr);
+			if (!mtok)
+				goto next;
+			if ((strncmp(mtok + 1, module, mlen) != 0) ||
+			    (mtok[mlen + 1] != ']'))
+				goto next;
+			if (future)
+				mod_match = true;
+		}
+		switch (type) {
+		case FILTER_CHECK:
+			if (match(tok, func_filter)) {
+				func_filter->set = true;
+				if (save_str)
+					ret = add_func_str(&func_list, tok);
+				else
+					ret = add_func(&func_list, index);
+				if (ret)
+					goto out;
+			}
+			break;
+		case FILTER_WRITE:
+			/* Writes only have one filter */
+			if (match(tok, func_filter)) {
+				ret = write_filter(fd, tok, module);
+				if (ret)
+					goto out;
+			}
+			break;
+		default:
+			/* Should never happen */
+			ret = -1;
+			goto out;
+
+		}
+	next:
+		free(line);
+		line = NULL;
+		len = 0;
+	}
+ out:
+	free(line);
+	fclose(fp);
+
+	/* If there was no matches and future was set, this is a success */
+	if (future && !mod_match)
+		ret = 0;
+
+	return ret;
+}
+
+static int check_available_filters(struct func_filter *func_filter,
+				   const char *module,
+				   struct func_list **func_list,
+				   bool future)
+{
+	int flags = FILTER_CHECK | (future ? FILTER_FUTURE : 0);
+
+	return match_filters(-1, func_filter, module, func_list, flags);
+}
+
+
+static int list_available_filters(struct func_filter *func_filter,
+				   const char *module,
+				   struct func_list **func_list)
+{
+	int flags = FILTER_CHECK | SAVE_STRING;
+
+	return match_filters(-1, func_filter, module, func_list, flags);
+}
+
+static int set_regex_filter(int fd, struct func_filter *func_filter,
+			    const char *module)
+{
+	return match_filters(fd, func_filter, module, NULL, FILTER_WRITE);
+}
+
+static int controlled_write(int fd, struct func_filter *func_filter,
+			    const char *module)
+{
+	const char *filter = func_filter->filter;
+	int ret;
+
+	if (func_filter->is_regex)
+		ret = set_regex_filter(fd, func_filter, module);
+	else
+		ret = write_filter(fd, filter, module);
+
+	return ret;
+}
+
+static int init_func_filter(struct func_filter *func_filter, const char *filter)
+{
+	char *str;
+	int ret;
+
+	if (!(func_filter->is_regex = is_regex(filter)))
+		str = make_regex(filter);
+	else
+		str = update_regex(filter);
+
+	if (!str)
+		return -1;
+
+	ret = regcomp(&func_filter->re, str, REG_ICASE|REG_NOSUB);
+	free(str);
+
+	if (ret < 0)
+		return -1;
+
+	func_filter->filter = filter;
+	return 0;
+}
+
+static int write_number(int fd, unsigned int start, unsigned int end)
+{
+	char buf[64];
+	unsigned int i;
+	int n, ret;
+
+	for (i = start; i <= end; i++) {
+		n = snprintf(buf, 64, "%d ", i);
+		ret = write(fd, buf, n);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * This will try to write the first number, if that fails, it
+ * will assume that it is not supported and return 1.
+ * If the first write succeeds, but a following write fails, then
+ * the kernel does support this, but something else went wrong,
+ * in this case, return -1.
+ */
+static int write_func_list(int fd, struct func_list *list)
+{
+	int ret;
+
+	if (!list)
+		return 0;
+
+	ret = write_number(fd, list->start, list->end);
+	if (ret)
+		return 1; // try a different way
+	list = list->next;
+	while (list) {
+		ret = write_number(fd, list->start, list->end);
+		if (ret)
+			return -1;
+		list = list->next;
+	}
+	return 0;
+}
+
+static int update_filter(const char *filter_path, int *fd,
+			 struct tracefs_instance *instance, const char *filter,
+			 const char *module, unsigned int flags)
+{
+	struct func_filter func_filter;
+	struct func_list *func_list = NULL;
+	bool reset = flags & TRACEFS_FL_RESET;
+	bool cont = flags & TRACEFS_FL_CONTINUE;
+	bool future = flags & TRACEFS_FL_FUTURE;
+	pthread_mutex_t *lock = trace_get_lock(instance);
+	int open_flags;
+	int ret = 1;
+
+	/* future flag is only applicable to modules */
+	if (future && !module) {
+		errno = EINVAL;
+		return 1;
+	}
+
+	pthread_mutex_lock(lock);
+
+	/* RESET is only allowed if the file is not opened yet */
+	if (reset && *fd >= 0) {
+		errno = EBUSY;
+		ret = -1;
+		goto out;
+	}
+
+	/*
+	 * Set EINVAL on no matching filter. But errno may still be modified
+	 * on another type of failure (allocation or opening a file).
+	 */
+	errno = EINVAL;
+
+	/* module set with NULL filter means to enable all functions in a module */
+	if (module && !filter)
+		filter = "*";
+
+	if (!filter) {
+		/* OK to call without filters if this is closing the opened file */
+		if (!cont && *fd >= 0) {
+			errno = 0;
+			ret = 0;
+			close(*fd);
+			*fd = -1;
+		}
+		/* Also OK to call if reset flag is set */
+		if (reset)
+			goto open_file;
+
+		goto out;
+	}
+
+	if (init_func_filter(&func_filter, filter) < 0)
+		goto out;
+
+	ret = check_available_filters(&func_filter, module, &func_list, future);
+	if (ret)
+		goto out_free;
+
+ open_file:
+	ret = 1;
+
+	open_flags = reset ? O_TRUNC : O_APPEND;
+
+	if (*fd < 0)
+		*fd = open(filter_path, O_WRONLY | O_CLOEXEC | open_flags);
+	if (*fd < 0)
+		goto out_free;
+
+	errno = 0;
+	ret = 0;
+
+	if (filter) {
+		/*
+		 * If future is set, and no functions were found, then
+		 * set it directly.
+		 */
+		if (func_list)
+			ret = write_func_list(*fd, func_list);
+		else
+			ret = 1;
+		if (ret > 0)
+			ret = controlled_write(*fd, &func_filter, module);
+	}
+
+	if (!cont) {
+		close(*fd);
+		*fd = -1;
+	}
+
+ out_free:
+	if (filter)
+		regfree(&func_filter.re);
+	free_func_list(func_list);
+ out:
+	pthread_mutex_unlock(lock);
+
+	return ret;
+}
+
+/**
+ * tracefs_function_filter - filter the functions that are traced
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ * @filter: The filter to filter what functions are to be traced
+ * @module: Module to be traced or NULL if all functions are to be examined.
+ * @flags: flags on modifying the filter file
+ *
+ * @filter may be a full function name, a glob, or a regex. It will be
+ * considered a regex, if there's any characters that are not normally in
+ * function names or "*" or "?" for a glob.
+ *
+ * @flags:
+ *   TRACEFS_FL_RESET - will clear the functions in the filter file
+ *          before applying the @filter. This will error with -1
+ *          and errno of EBUSY if this flag is set and a previous
+ *          call had the same instance and TRACEFS_FL_CONTINUE set.
+ *   TRACEFS_FL_CONTINUE - will keep the filter file open on return.
+ *          The filter is updated on closing of the filter file.
+ *          With this flag set, the file is not closed, and more filters
+ *          may be added before they take effect. The last call of this
+ *          function must be called without this flag for the filter
+ *          to take effect.
+ *   TRACEFS_FL_FUTURE - only applicable if "module" is set. If no match
+ *          is made, and the module is not yet loaded, it will still attempt
+ *          to write the filter plus the module; "<filter>:mod:<module>"
+ *          to the filter file. Starting with Linux kernels 4.13, it is possible
+ *          to load the filter file with module functions for a module that
+ *          is not yet loaded, and when the module is loaded, it will then
+ *          activate the module.
+ *
+ * Returns 0 on success, 1 if there was an error but the filtering has not
+ *  yet started, -1 if there was an error but the filtering has started.
+ *  If -1 is returned and TRACEFS_FL_CONTINUE was set, then this function
+ *  needs to be called again without the TRACEFS_FL_CONTINUE flag to commit
+ *  the changes and close the filter file.
+ */
+int tracefs_function_filter(struct tracefs_instance *instance, const char *filter,
+			    const char *module, unsigned int flags)
+{
+	char *filter_path;
+	int *fd;
+	int ret;
+
+	filter_path = tracefs_instance_get_file(instance, TRACE_FILTER);
+	if (!filter_path)
+		return -1;
+
+	if (instance)
+		fd = &instance->ftrace_filter_fd;
+	else
+		fd = &ftrace_filter_fd;
+
+	ret = update_filter(filter_path, fd, instance, filter, module, flags);
+	tracefs_put_tracing_file(filter_path);
+	return ret;
+}
+
+/**
+ * tracefs_function_notrace - filter the functions that are not to be traced
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ * @filter: The filter to filter what functions are not to be traced
+ * @module: Module to be traced or NULL if all functions are to be examined.
+ * @flags: flags on modifying the filter file
+ *
+ * See tracefs_function_filter, as this has the same functionality but
+ * for adding to the "notrace" filter.
+ */
+int tracefs_function_notrace(struct tracefs_instance *instance, const char *filter,
+			     const char *module, unsigned int flags)
+{
+	char *filter_path;
+	int *fd;
+	int ret;
+
+	filter_path = tracefs_instance_get_file(instance, TRACE_NOTRACE);
+	if (!filter_path)
+		return -1;
+
+	if (instance)
+		fd = &instance->ftrace_notrace_fd;
+	else
+		fd = &ftrace_notrace_fd;
+
+	ret = update_filter(filter_path, fd, instance, filter, module, flags);
+	tracefs_put_tracing_file(filter_path);
+	return ret;
+}
+
+int write_tracer(int fd, const char *tracer)
+{
+	int ret;
+
+	ret = write(fd, tracer, strlen(tracer));
+	if (ret < strlen(tracer))
+		return -1;
+	return ret;
+}
+
+/**
+ * tracefs_set_tracer - function to set the tracer
+ * @instance: ftrace instance, can be NULL for top tracing instance
+ * @tracer: The tracer enum that defines the tracer to be set
+ * @t: A tracer name if TRACEFS_TRACER_CUSTOM is passed in for @tracer
+ *
+ * Set the tracer for the instance based on the tracefs_tracer enums.
+ * If the user wishes to enable a tracer that is not defined by
+ * the enum (new or custom kernel), the tracer can be set to
+ * TRACEFS_TRACER_CUSTOM, and pass in a const char * name for
+ * the tracer to set.
+ *
+ * Returns 0 on succes, negative on error.
+ */
+
+int tracefs_tracer_set(struct tracefs_instance *instance,
+		       enum tracefs_tracers tracer, ...)
+{
+	char *tracer_path = NULL;
+	const char *t = NULL;
+	int ret = -1;
+	int fd = -1;
+	int i;
+
+	if (tracer < 0 || tracer > ARRAY_SIZE(tracers)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	tracer_path = tracefs_instance_get_file(instance, CUR_TRACER);
+	if (!tracer_path)
+		return -1;
+
+	fd = open(tracer_path, O_WRONLY);
+	if (fd < 0) {
+		errno = ENOENT;
+		goto out;
+	}
+
+	if (tracer == TRACEFS_TRACER_CUSTOM) {
+		va_list ap;
+
+		va_start(ap, tracer);
+		t = va_arg(ap, const char *);
+		va_end(ap);
+	} else if (tracer == tracer_enums[tracer]) {
+		t = tracers[tracer];
+	} else {
+		for (i = 0; i < ARRAY_SIZE(tracer_enums); i++) {
+			if (tracer == tracer_enums[i]) {
+				t = tracers[i];
+				break;
+			}
+		}
+	}
+	if (!t) {
+		errno = EINVAL;
+		goto out;
+	}
+	ret = write_tracer(fd, t);
+	/*
+	 * If the tracer does not exist, EINVAL is returned,
+	 * but let the user know this as ENODEV.
+	 */
+	if (ret < 0 && errno == EINVAL)
+		errno = ENODEV;
+ out:
+	tracefs_put_tracing_file(tracer_path);
+	close(fd);
+	return ret > 0 ? 0 : ret;
+}
+
+int  tracefs_tracer_clear(struct tracefs_instance *instance)
+{
+	return tracefs_tracer_set(instance, TRACEFS_TRACER_NOP);
+}
+
+static bool splice_safe(int fd, int pfd)
+{
+	int ret;
+
+	errno = 0;
+	ret = splice(pfd, NULL, fd, NULL,
+		     10, SPLICE_F_NONBLOCK | SPLICE_F_MOVE);
+
+	return !ret || (ret < 0 && errno == EAGAIN);
+}
+
+static ssize_t read_trace_pipe(bool *keep_going, int in_fd, int out_fd)
+{
+	char buf[BUFSIZ];
+	ssize_t bread = 0;
+	int ret;
+
+	while (*(volatile bool *)keep_going) {
+		int r;
+		ret = read(in_fd, buf, BUFSIZ);
+		if (ret <= 0)
+			break;
+		r = ret;
+		ret = write(out_fd, buf, r);
+		if (ret < 0)
+			break;
+		bread += ret;
+		/*
+		 * If the write does a partial write, then
+		 * the iteration should stop. This can happen if
+		 * the destination file system ran out of disk space.
+		 * Sure, it probably lost a little from the read
+		 * but there's not much more that can be
+		 * done. Just return what was transferred.
+		 */
+		if (ret < r)
+			break;
+	}
+
+	if (ret < 0 && (errno == EAGAIN || errno == EINTR))
+		ret = 0;
+
+	return ret < 0 ? ret : bread;
+}
+
+static bool top_pipe_keep_going;
+
+/**
+ * tracefs_trace_pipe_stream - redirect the stream of trace data to an output
+ * file. The "splice" system call is used to moves the data without copying
+ * between kernel address space and user address space. The user can interrupt
+ * the streaming of the data by pressing Ctrl-c.
+ * @fd: The file descriptor of the output file.
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ * @flags: flags for opening the trace_pipe file.
+ *
+ * Returns -1 in case of an error or number of bytes transferred otherwise.
+ */
+ssize_t tracefs_trace_pipe_stream(int fd, struct tracefs_instance *instance,
+				 int flags)
+{
+	bool *keep_going = instance ? &instance->pipe_keep_going :
+				      &top_pipe_keep_going;
+	const char *file = "trace_pipe";
+	int brass[2], in_fd, ret = -1;
+	int sflags = flags & O_NONBLOCK ? SPLICE_F_NONBLOCK : 0;
+	off_t data_size;
+	ssize_t bread = 0;
+
+	(*(volatile bool *)keep_going) = true;
+
+	in_fd = tracefs_instance_file_open(instance, file, O_RDONLY | flags);
+	if (in_fd < 0) {
+		tracefs_warning("Failed to open 'trace_pipe'.");
+		return ret;
+	}
+
+	if(pipe(brass) < 0) {
+		tracefs_warning("Failed to open pipe.");
+		goto close_file;
+	}
+
+	data_size = fcntl(brass[0], F_GETPIPE_SZ);
+	if (data_size <= 0) {
+		tracefs_warning("Failed to open pipe (size=0).");
+		goto close_all;
+	}
+
+	/* Test if the output is splice safe */
+	if (!splice_safe(fd, brass[0])) {
+		bread = read_trace_pipe(keep_going, in_fd, fd);
+		ret = 0; /* Force return of bread */
+		goto close_all;
+	}
+
+	errno = 0;
+
+	while (*(volatile bool *)keep_going) {
+		ret = splice(in_fd, NULL,
+			     brass[1], NULL,
+			     data_size, sflags);
+		if (ret < 0)
+			break;
+
+		ret = splice(brass[0], NULL,
+			     fd, NULL,
+			     data_size, sflags);
+		if (ret < 0)
+			break;
+		bread += ret;
+	}
+
+	/*
+	 * Do not return error in the case when the "splice" system call
+	 * was interrupted by the user (pressing Ctrl-c).
+	 * Or if NONBLOCK was specified.
+	 */
+	if (!keep_going || errno == EAGAIN || errno == EINTR)
+		ret = 0;
+
+ close_all:
+	close(brass[0]);
+	close(brass[1]);
+ close_file:
+	close(in_fd);
+
+	return ret ? ret : bread;
+}
+
+/**
+ * tracefs_trace_pipe_print - redirect the stream of trace data to "stdout".
+ * The "splice" system call is used to moves the data without copying
+ * between kernel address space and user address space.
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ * @flags: flags for opening the trace_pipe file.
+ *
+ * Returns -1 in case of an error or number of bytes transferred otherwise.
+ */
+
+ssize_t tracefs_trace_pipe_print(struct tracefs_instance *instance, int flags)
+{
+	return tracefs_trace_pipe_stream(STDOUT_FILENO, instance, flags);
+}
+
+/**
+ * tracefs_trace_pipe_stop - stop the streaming of trace data.
+ * @instance: ftrace instance, can be NULL for top tracing instance.
+ */
+void tracefs_trace_pipe_stop(struct tracefs_instance *instance)
+{
+	if (instance)
+		instance->pipe_keep_going = false;
+	else
+		top_pipe_keep_going = false;
+}
+
+/**
+ * tracefs_filter_functions - return a list of available functons that can be filtered
+ * @filter: The filter to filter what functions to list (can be NULL for all)
+ * @module: Module to be traced or NULL if all functions are to be examined.
+ * @list: The list to return the list from (freed by tracefs_list_free() on success)
+ *
+ * Returns a list of function names that match @filter and @module. If both
+ * @filter and @module is NULL, then all available functions that can be filtered
+ * will be returned. (Note, there can be duplicates, if there are more than
+ * one function with the same name.
+ *
+ * On success, zero is returned, and @list contains a list of functions that were
+ * found, and must be freed with tracefs_list_free().
+ * On failure, a negative number is returned, and @list is ignored.
+ */
+int tracefs_filter_functions(const char *filter, const char *module, char ***list)
+{
+	struct func_filter func_filter;
+	struct func_list *func_list = NULL, *f;
+	char **funcs = NULL;
+	int ret;
+
+	if (!filter)
+		filter = ".*";
+
+	ret = init_func_filter(&func_filter, filter);
+	if (ret < 0)
+		return ret;
+
+	ret = list_available_filters(&func_filter, module, &func_list);
+	if (ret < 0)
+		goto out;
+
+	ret = -1;
+	for (f = func_list; f; f = f->next) {
+		char **tmp;
+
+		tmp = tracefs_list_add(funcs, f->func);
+		if (!tmp) {
+			tracefs_list_free(funcs);
+			goto out;
+		}
+		funcs = tmp;
+	}
+
+	*list = funcs;
+	ret = 0;
+out:
+	regfree(&func_filter.re);
+	free_func_list(func_list);
+	return ret;
+}
diff --git a/src/tracefs-uprobes.c b/src/tracefs-uprobes.c
new file mode 100644
index 0000000..aa39b75
--- /dev/null
+++ b/src/tracefs-uprobes.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2022, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdlib.h>
+#include <errno.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+#define UPROBE_DEFAULT_GROUP "uprobes"
+
+static struct tracefs_dynevent *
+uprobe_alloc(enum tracefs_dynevent_type type, const char *system, const char *event,
+	     const char *file, unsigned long long offset, const char *fetchargs)
+{
+	struct tracefs_dynevent *kp;
+	char *target;
+
+	if (!event || !file) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if (!system)
+		system = UPROBE_DEFAULT_GROUP;
+
+	if (asprintf(&target, "%s:0x%0*llx", file, (int)(sizeof(void *) * 2), offset) < 0)
+		return NULL;
+
+	kp = dynevent_alloc(type, system, event, target, fetchargs);
+	free(target);
+
+	return kp;
+}
+
+/**
+ * tracefs_uprobe_alloc - Allocate new user probe (uprobe)
+ * @system: The system name (NULL for the default uprobes)
+ * @event: The name of the event to create
+ * @file: The full path to the binary file, where the uprobe will be set
+ * @offset: Offset within the @file
+ * @fetchargs: String with arguments, that will be fetched with the uprobe
+ *
+ * Allocate new uprobe context that will be in the @system group
+ * (or uprobes if @system is NULL) and with @event name. The new uprobe will be
+ * attached to @offset within the @file. The arguments described in @fetchargs
+ * will fetched with the uprobe. See linux/Documentation/trace/uprobetracer.rst
+ * for more details.
+ *
+ * The uprobe is not created in the system.
+ *
+ * Return a pointer to a uprobe context on success, or NULL on error.
+ * The returned pointer must be freed with tracefs_dynevent_free()
+ *
+ */
+struct tracefs_dynevent *
+tracefs_uprobe_alloc(const char *system, const char *event,
+		     const char *file, unsigned long long offset, const char *fetchargs)
+{
+	return uprobe_alloc(TRACEFS_DYNEVENT_UPROBE, system, event, file, offset, fetchargs);
+}
+
+/**
+ * tracefs_uretprobe_alloc - Allocate new user return probe (uretprobe)
+ * @system: The system name (NULL for the default uprobes)
+ * @event: The name of the event to create
+ * @file: The full path to the binary file, where the uretprobe will be set
+ * @offset: Offset within the @file
+ * @fetchargs: String with arguments, that will be fetched with the uretprobe
+ *
+ * Allocate mew uretprobe context that will be in the @system group
+ * (or uprobes if @system is NULL) and with @event name. The new uretprobe will
+ * be attached to @offset within the @file. The arguments described in @fetchargs
+ * will fetched with the uprobe. See linux/Documentation/trace/uprobetracer.rst
+ * for more details.
+ *
+ * The uretprobe is not created in the system.
+ *
+ * Return a pointer to a uretprobe context on success, or NULL on error.
+ * The returned pointer must be freed with tracefs_dynevent_free()
+ *
+ */
+struct tracefs_dynevent *
+tracefs_uretprobe_alloc(const char *system, const char *event,
+			const char *file, unsigned long long offset, const char *fetchargs)
+{
+	return uprobe_alloc(TRACEFS_DYNEVENT_URETPROBE, system, event, file, offset, fetchargs);
+}
diff --git a/src/tracefs-utils.c b/src/tracefs-utils.c
new file mode 100644
index 0000000..9acf2ad
--- /dev/null
+++ b/src/tracefs-utils.c
@@ -0,0 +1,624 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ * Updates:
+ * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <linux/limits.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <event-parse.h>
+#include <event-utils.h>
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+#define TRACEFS_PATH "/sys/kernel/tracing"
+#define DEBUGFS_PATH "/sys/kernel/debug"
+
+#define ERROR_LOG "error_log"
+
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+static int log_level = TEP_LOG_CRITICAL;
+static char *custom_tracing_dir;
+
+/**
+ * tracefs_set_loglevel - set log level of the library
+ * @level: desired level of the library messages
+ */
+void tracefs_set_loglevel(enum tep_loglevel level)
+{
+	log_level = level;
+	tep_set_loglevel(level);
+}
+
+void __weak tracefs_warning(const char *fmt, ...)
+{
+	va_list ap;
+
+	if (log_level < TEP_LOG_WARNING)
+		return;
+
+	va_start(ap, fmt);
+	tep_vprint("libtracefs", TEP_LOG_WARNING, true, fmt, ap);
+	va_end(ap);
+}
+
+static int mount_tracefs(void)
+{
+	struct stat st;
+	int ret;
+
+	/* make sure debugfs exists */
+	ret = stat(TRACEFS_PATH, &st);
+	if (ret < 0)
+		return -1;
+
+	ret = mount("nodev", TRACEFS_PATH,
+		    "tracefs", 0, NULL);
+
+	return ret;
+}
+
+static int mount_debugfs(void)
+{
+	struct stat st;
+	int ret;
+
+	/* make sure debugfs exists */
+	ret = stat(DEBUGFS_PATH, &st);
+	if (ret < 0)
+		return -1;
+
+	ret = mount("nodev", DEBUGFS_PATH,
+		    "debugfs", 0, NULL);
+
+	return ret;
+}
+
+/* Exported for testing purpose only */
+__hidden char *find_tracing_dir(bool debugfs, bool mount)
+{
+	char *debug_str = NULL;
+	char fspath[PATH_MAX+1];
+	char *tracing_dir;
+	char type[100];
+	int use_debug = 0;
+	FILE *fp;
+
+	fp = fopen("/proc/mounts", "r");
+	if (!fp) {
+		tracefs_warning("Can't open /proc/mounts for read");
+		return NULL;
+	}
+
+	while (fscanf(fp, "%*s %"
+		      STR(PATH_MAX)
+		      "s %99s %*s %*d %*d\n",
+		      fspath, type) == 2) {
+		if (!debugfs && strcmp(type, "tracefs") == 0)
+			break;
+		if (!debug_str && strcmp(type, "debugfs") == 0) {
+			if (debugfs)
+				break;
+			debug_str = strdup(fspath);
+			if (!debug_str) {
+				fclose(fp);
+				return NULL;
+			}
+		}
+	}
+	fclose(fp);
+
+	if (debugfs) {
+		if (strcmp(type, "debugfs") != 0) {
+			if (!mount || mount_debugfs() < 0)
+				return NULL;
+			strcpy(fspath, DEBUGFS_PATH);
+		}
+	} else if (strcmp(type, "tracefs") != 0) {
+		if (!mount || mount_tracefs() < 0) {
+			if (debug_str) {
+				strncpy(fspath, debug_str, PATH_MAX);
+				fspath[PATH_MAX] = 0;
+			} else {
+				if (!mount || mount_debugfs() < 0) {
+					if (mount)
+						tracefs_warning("debugfs not mounted, please mount");
+					free(debug_str);
+					return NULL;
+				}
+				strcpy(fspath, DEBUGFS_PATH);
+			}
+			use_debug = 1;
+		} else
+			strcpy(fspath, TRACEFS_PATH);
+	}
+	free(debug_str);
+
+	if (use_debug) {
+		int ret;
+
+		ret = asprintf(&tracing_dir, "%s/tracing", fspath);
+		if (ret < 0)
+			return NULL;
+	} else {
+		tracing_dir = strdup(fspath);
+		if (!tracing_dir)
+			return NULL;
+	}
+
+	return tracing_dir;
+}
+
+/**
+ * tracefs_tracing_dir_is_mounted - test if the tracing dir is already mounted
+ * @mount: Mount it if it is not already mounted
+ * @path: the path to the tracing directory if mounted or was mounted
+ *
+ * Returns 1 if the tracing directory is already mounted and 0 if it is not.
+ * If @mount is set and it fails to mount, it returns -1.
+ *
+ * If path is not NULL, and the tracing directory is or was mounted, it holds
+ * the path to the tracing directory. It must not be freed.
+ */
+int tracefs_tracing_dir_is_mounted(bool mount, const char **path)
+{
+	const char *dir;
+
+	dir = find_tracing_dir(false, false);
+	if (dir) {
+		if (path)
+			*path = dir;
+		return 1;
+	}
+	if (!mount)
+		return 0;
+
+	dir = find_tracing_dir(false, mount);
+	if (!dir)
+		return -1;
+	if (path)
+		*path = dir;
+	return 0;
+}
+
+/**
+ * trace_find_tracing_dir - Find tracing directory
+ * @debugfs: Boolean to just return the debugfs directory
+ *
+ * Returns string containing the full path to the system's tracing directory.
+ * The string must be freed by free()
+ */
+__hidden char *trace_find_tracing_dir(bool debugfs)
+{
+	return find_tracing_dir(debugfs, false);
+}
+
+/**
+ * tracefs_set_tracing_dir - Set location of the tracing directory
+ * @tracing_dir: full path to the system's tracing directory mount point.
+ *
+ * Set the location to the system's tracing directory. This API should be used
+ * to set a custom location of the tracing directory. There is no need to call
+ * it if the location is standard, in that case the library will auto detect it.
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+int tracefs_set_tracing_dir(char *tracing_dir)
+{
+	if (custom_tracing_dir) {
+		free(custom_tracing_dir);
+		custom_tracing_dir = NULL;
+	}
+
+	if (tracing_dir) {
+		custom_tracing_dir = strdup(tracing_dir);
+		if (!custom_tracing_dir)
+			return -1;
+	}
+
+	return 0;
+}
+
+/* Used to check if the directory is still mounted */
+static int test_dir(const char *dir, const char *file)
+{
+	char path[strlen(dir) + strlen(file) + 2];
+	struct stat st;
+
+	sprintf(path, "%s/%s", dir, file);
+	return stat(path, &st) < 0 ? 0 : 1;
+}
+
+/**
+ * tracefs_tracing_dir - Get tracing directory
+ *
+ * Returns string containing the full path to the system's tracing directory.
+ * The returned string must *not* be freed.
+ */
+const char *tracefs_tracing_dir(void)
+{
+	static const char *tracing_dir;
+
+	/* Do not check custom_tracing_dir */
+	if (custom_tracing_dir)
+		return custom_tracing_dir;
+
+	if (tracing_dir && test_dir(tracing_dir, "trace"))
+		return tracing_dir;
+
+	tracing_dir = find_tracing_dir(false, true);
+	return tracing_dir;
+}
+
+/**
+ * tracefs_debug_dir - Get debugfs directory path
+ *
+ * Returns string containing the full path to the system's debugfs directory.
+ *
+ * The returned string must *not* be freed.
+ */
+const char *tracefs_debug_dir(void)
+{
+	static const char *debug_dir;
+
+	if (debug_dir && test_dir(debug_dir, "tracing"))
+		return debug_dir;
+
+	debug_dir = find_tracing_dir(true, true);
+	return debug_dir;
+}
+
+/**
+ * tracefs_get_tracing_file - Get tracing file
+ * @name: tracing file name
+ *
+ * Returns string containing the full path to a tracing file in
+ * the system's tracing directory.
+ *
+ * Must use tracefs_put_tracing_file() to free the returned string.
+ */
+char *tracefs_get_tracing_file(const char *name)
+{
+	const char *tracing;
+	char *file;
+	int ret;
+
+	if (!name)
+		return NULL;
+
+	tracing = tracefs_tracing_dir();
+	if (!tracing)
+		return NULL;
+
+	ret = asprintf(&file, "%s/%s", tracing, name);
+	if (ret < 0)
+		return NULL;
+
+	return file;
+}
+
+/**
+ * tracefs_put_tracing_file - Free tracing file or directory name
+ *
+ * Frees tracing file or directory, returned by
+ * tracefs_get_tracing_file()API.
+ */
+void tracefs_put_tracing_file(char *name)
+{
+	free(name);
+}
+
+__hidden int str_read_file(const char *file, char **buffer, bool warn)
+{
+	char stbuf[BUFSIZ];
+	char *buf = NULL;
+	int size = 0;
+	char *nbuf;
+	int fd;
+	int r;
+
+	fd = open(file, O_RDONLY);
+	if (fd < 0) {
+		if (warn)
+			tracefs_warning("File %s not found", file);
+		return -1;
+	}
+
+	do {
+		r = read(fd, stbuf, BUFSIZ);
+		if (r <= 0)
+			continue;
+		nbuf = realloc(buf, size+r+1);
+		if (!nbuf) {
+			if (warn)
+				tracefs_warning("Failed to allocate file buffer");
+			size = -1;
+			break;
+		}
+		buf = nbuf;
+		memcpy(buf+size, stbuf, r);
+		size += r;
+	} while (r > 0);
+
+	close(fd);
+	if (r == 0 && size > 0) {
+		buf[size] = '\0';
+		*buffer = buf;
+	} else
+		free(buf);
+
+	return size;
+}
+
+/**
+ * tracefs_error_all - return the content of the error log
+ * @instance: The instance to read the error log from (NULL for top level)
+ *
+ * Return NULL if the log is empty, or on error (where errno will be
+ * set. Otherwise the content of the entire log is returned in a string
+ * that must be freed with free().
+ */
+char *tracefs_error_all(struct tracefs_instance *instance)
+{
+	char *content;
+	char *path;
+	int size;
+
+	errno = 0;
+
+	path = tracefs_instance_get_file(instance, ERROR_LOG);
+	if (!path)
+		return NULL;
+	size = str_read_file(path, &content, false);
+	tracefs_put_tracing_file(path);
+
+	if (size <= 0)
+		return NULL;
+
+	return content;
+}
+
+enum line_states {
+	START,
+	CARROT,
+};
+
+/**
+ * tracefs_error_last - return the last error logged
+ * @instance: The instance to read the error log from (NULL for top level)
+ *
+ * Return NULL if the log is empty, or on error (where errno will be
+ * set. Otherwise a string containing the content of the last error shown
+* in the log that must be freed with free().
+ */
+char *tracefs_error_last(struct tracefs_instance *instance)
+{
+	enum line_states state = START;
+	char *content;
+	char *ret;
+	bool done = false;
+	int size;
+	int i;
+
+	content = tracefs_error_all(instance);
+	if (!content)
+		return NULL;
+
+	size = strlen(content);
+	if (!size) /* Should never happen */
+		return content;
+
+	for (i = size - 1; i > 0; i--) {
+		switch (state) {
+		case START:
+			if (content[i] == '\n') {
+				/* Remove extra new lines */
+				content[i] = '\0';
+				break;
+			}
+			if (content[i] == '^')
+				state = CARROT;
+			break;
+		case CARROT:
+			if (content[i] == '\n') {
+				/* Remember last new line */
+				size = i;
+				break;
+			}
+			if (content[i] == '^') {
+				/* Go just passed the last newline */
+				i = size + 1;
+				done = true;
+			}
+			break;
+		}
+		if (done)
+			break;
+	}
+
+	if (i) {
+		ret = strdup(content + i);
+		free(content);
+	} else {
+		ret = content;
+	}
+
+	return ret;
+}
+
+/**
+ * tracefs_error_clear - clear the error log of an instance
+ * @instance: The instance to clear (NULL for top level)
+ *
+ * Clear the content of the error log.
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+int tracefs_error_clear(struct tracefs_instance *instance)
+{
+	return tracefs_instance_file_clear(instance, ERROR_LOG);
+}
+
+/**
+ * tracefs_list_free - free list if strings, returned by APIs
+ *			tracefs_event_systems()
+ *			tracefs_system_events()
+ *
+ *@list pointer to a list of strings, the last one must be NULL
+ */
+void tracefs_list_free(char **list)
+{
+	int i;
+
+	if (!list)
+		return;
+
+	for (i = 0; list[i]; i++)
+		free(list[i]);
+
+	/* The allocated list is before the user visible portion */
+	list--;
+	free(list);
+}
+
+
+__hidden char ** trace_list_create_empty(void)
+{
+	char **list;
+
+	list = calloc(2, sizeof(*list));
+
+	return list ? &list[1] : NULL;
+}
+
+/**
+ * tracefs_list_add - create or extend a string list
+ * @list: The list to add to (NULL to create a new one)
+ * @string: The string to append to @list.
+ *
+ * If @list is NULL, a new list is created with the first element
+ * a copy of @string, and the second element is NULL.
+ *
+ * If @list is not NULL, it is then reallocated to include
+ * a new element and a NULL terminator, and will return the new
+ * allocated array on success, and the one passed in should be
+ * ignored.
+ *
+ * Returns an allocated string array that must be freed with
+ * tracefs_list_free() on success. On failure, NULL is returned
+ * and the @list is untouched.
+ */
+char **tracefs_list_add(char **list, const char *string)
+{
+	unsigned long size = 0;
+	char *str = strdup(string);
+	char **new_list;
+
+	if (!str)
+		return NULL;
+
+	/*
+	 * The returned list is really the address of the
+	 * second entry of the list (&list[1]), the first
+	 * entry contains the number of strings in the list.
+	 */
+	if (list) {
+		list--;
+		size = *(unsigned long *)list;
+	}
+
+	new_list = realloc(list, sizeof(*list) * (size + 3));
+	if (!new_list) {
+		free(str);
+		return NULL;
+	}
+
+	list = new_list;
+	list[0] = (char *)(size + 1);
+	list++;
+	list[size++] = str;
+	list[size] = NULL;
+
+	return list;
+}
+
+/*
+ * trace_list_pop - Removes the last string added
+ * @list: The list to remove the last event from
+ *
+ * Returns 0 on success, -1 on error.
+ * Returns 1 if the list is empty or NULL.
+ */
+__hidden int trace_list_pop(char **list)
+{
+	unsigned long size;
+
+	if (!list || list[0])
+		return 1;
+
+	list--;
+	size = *(unsigned long *)list;
+	/* size must be greater than zero */
+	if (!size)
+		return -1;
+	size--;
+	*list = (char *)size;
+	list++;
+	list[size] = NULL;
+	return 0;
+}
+
+/**
+ * tracefs_list_size - Return the number of strings in the list
+ * @list: The list to determine the size.
+ *
+ * Returns the number of elements in the list.
+ * If @list is NULL, then zero is returned.
+ */
+int tracefs_list_size(char **list)
+{
+	if (!list)
+		return 0;
+
+	list--;
+	return (int)*(unsigned long *)list;
+}
+
+/**
+ * tracefs_tracer_available - test if a tracer is available
+ * @tracing_dir: The directory that contains the tracing directory
+ * @tracer: The name of the tracer
+ *
+ * Return true if the tracer is available
+ */
+bool tracefs_tracer_available(const char *tracing_dir, const char *tracer)
+{
+	bool ret = false;
+	char **tracers = NULL;
+	int i;
+
+	tracers = tracefs_tracers(tracing_dir);
+	if (!tracers)
+		return false;
+
+	for (i = 0; tracers[i]; i++) {
+		if (strcmp(tracer, tracers[i]) == 0) {
+			ret = true;
+			break;
+		}
+	}
+
+	tracefs_list_free(tracers);
+	return ret;
+}
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..d38fc92
--- /dev/null
+++ b/test.c
@@ -0,0 +1,7 @@
+#include <tracefs.h>
+
+int main()
+{
+	tracefs_tracing_dir();
+	return 0;
+}
diff --git a/utest/Makefile b/utest/Makefile
new file mode 100644
index 0000000..f10b709
--- /dev/null
+++ b/utest/Makefile
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: LGPL-2.1
+
+include $(src)/scripts/utils.mk
+
+bdir:=$(obj)/utest
+
+TARGETS = $(bdir)/trace-utest
+
+OBJS =
+OBJS += trace-utest.o
+OBJS += tracefs-utest.o
+
+LIBS += -lcunit				\
+	-ldl				\
+	$(obj)/lib/libtracefs.a
+
+OBJS := $(OBJS:%.o=$(bdir)/%.o)
+
+$(bdir):
+	@mkdir -p $(bdir)
+
+$(OBJS): | $(bdir)
+
+$(bdir)/trace-utest: $(OBJS) $(obj)/lib/libtracefs.a
+	$(Q)$(do_app_build)
+
+$(bdir)/%.o: %.c
+	$(Q)$(call do_fpic_compile)
+
+-include .*.d
+
+test: $(TARGETS)
+
+clean:
+	$(Q)$(call do_clean,$(TARGETS) $(bdir)/*.o $(bdir)/.*.d)
diff --git a/utest/README b/utest/README
new file mode 100644
index 0000000..647e460
--- /dev/null
+++ b/utest/README
@@ -0,0 +1,18 @@
+
+Unit tests for tracefs library. The tests use CUnit framework:
+ http://cunit.sourceforge.net/
+which must be pre installed on the system, before building the unit tests.
+The framework can be downloaded, compiled and installed manually, or
+using a precompiled distro package:
+
+ Fedora:
+	 CUnit
+	 CUnit-devel
+
+ Ubuntu and Debian:
+	libcunit1
+	libcunit1-doc
+	libcunit1-dev
+
+ openSUSE and SLE:
+	cunit-devel
diff --git a/utest/trace-utest.c b/utest/trace-utest.c
new file mode 100644
index 0000000..58d4d4e
--- /dev/null
+++ b/utest/trace-utest.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+#include <CUnit/Basic.h>
+
+#include "trace-utest.h"
+
+enum unit_tests {
+	RUN_NONE	= 0,
+	RUN_TRACEFS	= (1 << 0),
+	RUN_ALL		= 0xFFFF
+};
+
+static void print_help(char **argv)
+{
+	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-h, --help\tPrint usage information\n");
+	exit(0);
+}
+
+int main(int argc, char **argv)
+{
+	CU_BasicRunMode verbose = CU_BRM_VERBOSE;
+	enum unit_tests tests = RUN_NONE;
+
+	for (;;) {
+		int c;
+		int index = 0;
+		const char *opts = "+hsr:";
+		static struct option long_options[] = {
+			{"silent", no_argument, NULL, 's'},
+			{"run", required_argument, NULL, 'r'},
+			{"help", no_argument, NULL, 'h'},
+			{NULL, 0, NULL, 0}
+		};
+
+		c = getopt_long (argc, argv, opts, long_options, &index);
+		if (c == -1)
+			break;
+		switch (c) {
+		case 'r':
+			if (strcmp(optarg, "tracefs") == 0)
+				tests |= RUN_TRACEFS;
+			else
+				print_help(argv);
+			break;
+		case 's':
+			verbose = CU_BRM_SILENT;
+			break;
+		case 'h':
+		default:
+			print_help(argv);
+			break;
+		}
+	}
+
+	if (tests == RUN_NONE)
+		tests = RUN_ALL;
+
+	if (CU_initialize_registry() != CUE_SUCCESS) {
+		printf("Test registry cannot be initialized\n");
+		return -1;
+	}
+
+	if (tests & RUN_TRACEFS)
+		test_tracefs_lib();
+
+	CU_basic_set_mode(verbose);
+	CU_basic_run_tests();
+	CU_cleanup_registry();
+	return 0;
+}
diff --git a/utest/trace-utest.h b/utest/trace-utest.h
new file mode 100644
index 0000000..917c0e7
--- /dev/null
+++ b/utest/trace-utest.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#ifndef _TRACE_UTEST_H_
+#define _TRACE_UTEST_H_
+
+void test_tracefs_lib(void);
+
+#endif /* _TRACE_UTEST_H_ */
diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c
new file mode 100644
index 0000000..e0e3c07
--- /dev/null
+++ b/utest/tracefs-utest.c
@@ -0,0 +1,2385 @@
+// 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 <ftw.h>
+#include <libgen.h>
+#include <kbuffer.h>
+#include <pthread.h>
+
+#include <sys/mount.h>
+
+#include <CUnit/CUnit.h>
+#include <CUnit/Basic.h>
+
+#include "tracefs.h"
+
+#define TRACEFS_SUITE		"tracefs library"
+#define TEST_INSTANCE_NAME	"cunit_test_iter"
+#define TEST_TRACE_DIR		"/tmp/trace_utest.XXXXXX"
+#define TEST_ARRAY_SIZE		5000
+
+#define ALL_TRACERS	"available_tracers"
+#define CUR_TRACER	"current_tracer"
+#define PER_CPU		"per_cpu"
+#define TRACE_ON	"tracing_on"
+#define TRACE_CLOCK	"trace_clock"
+
+#define SQL_1_EVENT	"wakeup_1"
+#define SQL_1_SQL	"select sched_switch.next_pid as woke_pid, sched_waking.common_pid as waking_pid from sched_waking join sched_switch on sched_switch.next_pid = sched_waking.pid"
+
+#define SQL_2_EVENT	"wakeup_2"
+#define SQL_2_SQL	"select woke.next_pid as woke_pid, wake.common_pid as waking_pid from sched_waking as wake join sched_switch as woke on woke.next_pid = wake.pid"
+
+#define SQL_3_EVENT	"wakeup_lat"
+#define SQL_3_SQL	"select sched_switch.next_prio as prio, end.prev_prio as pprio, (sched.sched_waking.common_timestamp.usecs - end.TIMESTAMP_USECS) as lat from sched_waking as start join sched_switch as end on start.pid = end.next_pid"
+
+#define SQL_4_EVENT	"wakeup_lat_2"
+#define SQL_4_SQL	"select start.pid, end.next_prio as prio, (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 (start.prio >= 1 && start.prio < 100) || !(start.pid >= 0 && start.pid <= 1) && end.prev_pid != 0"
+
+#define SQL_5_EVENT	"irq_lat"
+#define SQL_5_SQL	"select end.common_pid as pid, (end.common_timestamp.usecs - start.common_timestamp.usecs) as irq_lat from irq_disable as start join irq_enable as end on start.common_pid = end.common_pid, start.parent_offs == end.parent_offs where start.common_pid != 0"
+#define SQL_5_START	"irq_disable"
+
+#define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug"
+#define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing"
+#define TRACEFS_DEFAULT2_PATH "/sys/kernel/debug/tracing"
+
+static struct tracefs_instance *test_instance;
+static struct tep_handle *test_tep;
+struct test_sample {
+	int cpu;
+	int value;
+};
+static struct test_sample test_array[TEST_ARRAY_SIZE];
+static int test_found;
+static unsigned long long last_ts;
+
+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;
+
+	CU_TEST(last_ts <= record->ts);
+	last_ts = record->ts;
+
+	if (cpu_test && *cpu_test >= 0) {
+		CU_TEST(*cpu_test == cpu);
+	}
+	CU_TEST(cpu == record->cpu);
+
+	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 cpu_set_t *cpuset_save;
+static cpu_set_t *cpuset;
+static int cpu_size;
+
+static void save_affinity(void)
+{
+	int cpus;
+
+	cpus = sysconf(_SC_NPROCESSORS_CONF);
+	cpuset_save = CPU_ALLOC(cpus);
+	cpuset = CPU_ALLOC(cpus);
+	cpu_size = CPU_ALLOC_SIZE(cpus);
+	CU_TEST(cpuset_save != NULL && cpuset != NULL);
+	CU_TEST(sched_getaffinity(0, cpu_size, cpuset_save) == 0);
+}
+
+static void thread_affinity(void)
+{
+	sched_setaffinity(0, cpu_size, cpuset_save);
+}
+
+static void reset_affinity(void)
+{
+	sched_setaffinity(0, cpu_size, cpuset_save);
+	CPU_FREE(cpuset_save);
+	CPU_FREE(cpuset);
+}
+
+static void set_affinity(int cpu)
+{
+	CPU_ZERO_S(cpu_size, cpuset);
+	CPU_SET_S(cpu, cpu_size, cpuset);
+	CU_TEST(sched_setaffinity(0, cpu_size, cpuset) == 0);
+	sched_yield(); /* Force schedule */
+}
+
+static void test_iter_write(struct tracefs_instance *instance)
+{
+	char *path;
+	int i, fd;
+	int cpus;
+	int ret;
+
+	cpus = sysconf(_SC_NPROCESSORS_CONF);
+	save_affinity();
+
+	path = tracefs_instance_get_file(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);
+		set_affinity(test_array[i].cpu);
+		ret = write(fd, test_array + i, sizeof(struct test_sample));
+		CU_TEST(ret == sizeof(struct test_sample));
+	}
+
+	reset_affinity();
+	close(fd);
+}
+
+
+static void iter_raw_events_on_cpu(struct tracefs_instance *instance, int cpu)
+{
+	int cpus = sysconf(_SC_NPROCESSORS_CONF);
+	cpu_set_t *cpuset = NULL;
+	int cpu_size = 0;
+	int check = 0;
+	int ret;
+	int i;
+
+	if (cpu >= 0) {
+		cpuset = CPU_ALLOC(cpus);
+		cpu_size = CPU_ALLOC_SIZE(cpus);
+		CPU_ZERO_S(cpu_size, cpuset);
+		CPU_SET(cpu, cpuset);
+	}
+	test_found = 0;
+	last_ts = 0;
+	test_iter_write(instance);
+	ret = tracefs_iterate_raw_events(test_tep, instance, cpuset, cpu_size,
+					 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);
+	}
+
+	if (cpuset)
+		CPU_FREE(cpuset);
+}
+
+static void test_instance_iter_raw_events(struct tracefs_instance *instance)
+{
+	int cpus = sysconf(_SC_NPROCESSORS_CONF);
+	int ret;
+	int i;
+
+	ret = tracefs_iterate_raw_events(NULL, instance, NULL, 0, test_callback, NULL);
+	CU_TEST(ret < 0);
+	last_ts = 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, instance, NULL, 0, NULL, NULL);
+	CU_TEST(ret < 0);
+
+	iter_raw_events_on_cpu(instance, -1);
+	for (i = 0; i < cpus; i++)
+		iter_raw_events_on_cpu(instance, i);
+}
+
+static void test_iter_raw_events(void)
+{
+	test_instance_iter_raw_events(test_instance);
+}
+
+#define RAND_STR_SIZE 20
+#define RAND_ASCII "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+static const char *get_rand_str(void)
+{
+	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;
+}
+
+struct marker_find {
+	int data_offset;
+	int event_id;
+	int count;
+	int len;
+	void *data;
+};
+
+static int test_marker_callback(struct tep_event *event, struct tep_record *record,
+				int cpu, void *context)
+{
+	struct marker_find *walk = context;
+
+	if (!walk)
+		return -1;
+	if (event->id != walk->event_id)
+		return 0;
+	if (record->size < (walk->data_offset + walk->len))
+		return 0;
+
+	if (memcmp(walk->data, record->data + walk->data_offset, walk->len) == 0)
+		walk->count++;
+
+	return 0;
+}
+
+static bool find_test_marker(struct tracefs_instance *instance,
+			     void *data, int len, int expected, bool raw)
+{
+	struct tep_format_field *field;
+	struct tep_event *event;
+	struct marker_find walk;
+	int ret;
+
+	if (raw) {
+		event = tep_find_event_by_name(test_tep, "ftrace", "raw_data");
+		if (event)
+			field = tep_find_field(event, "id");
+
+	} else {
+		event = tep_find_event_by_name(test_tep, "ftrace", "print");
+		if (event)
+			field = tep_find_field(event, "buf");
+	}
+
+	if (!event || !field)
+		return false;
+
+	walk.data = data;
+	walk.len = len;
+	walk.count = 0;
+	walk.event_id = event->id;
+	walk.data_offset = field->offset;
+	ret = tracefs_iterate_raw_events(test_tep, instance, NULL, 0,
+					 test_marker_callback, &walk);
+	CU_TEST(ret == 0);
+
+	return walk.count == expected;
+}
+
+static int marker_vprint(struct tracefs_instance *instance, char *fmt, ...)
+{
+	va_list ap;
+	int ret;
+
+	va_start(ap, fmt);
+	ret = tracefs_vprintf(instance, fmt, ap);
+	va_end(ap);
+
+	return ret;
+}
+
+#define MARKERS_WRITE_COUNT	100
+static void test_instance_ftrace_marker(struct tracefs_instance *instance)
+{
+	const char *string = get_rand_str();
+	unsigned int data = 0xdeadbeef;
+	char *str;
+	int i;
+
+	CU_TEST(tracefs_print_init(instance) == 0);
+	tracefs_print_close(instance);
+
+	CU_TEST(tracefs_binary_init(instance) == 0);
+	tracefs_binary_close(instance);
+
+	for (i = 0; i < MARKERS_WRITE_COUNT; i++) {
+		CU_TEST(tracefs_binary_write(instance, &data, sizeof(data)) == 0);
+	}
+	CU_TEST(find_test_marker(instance, &data, sizeof(data), MARKERS_WRITE_COUNT, true));
+
+	for (i = 0; i < MARKERS_WRITE_COUNT; i++) {
+		CU_TEST(tracefs_printf(instance, "Test marker: %s 0x%X", string, data) == 0);
+	}
+	asprintf(&str, "Test marker: %s 0x%X", string, data);
+	CU_TEST(find_test_marker(instance, str, strlen(str), MARKERS_WRITE_COUNT, false));
+	free(str);
+
+	for (i = 0; i < MARKERS_WRITE_COUNT; i++) {
+		CU_TEST(marker_vprint(instance, "Test marker V: %s 0x%X", string, data) == 0);
+	}
+	asprintf(&str, "Test marker V: %s 0x%X", string, data);
+	CU_TEST(find_test_marker(instance, str, strlen(str), MARKERS_WRITE_COUNT, false));
+	free(str);
+
+	tracefs_print_close(instance);
+	tracefs_binary_close(instance);
+}
+
+static void test_ftrace_marker(void)
+{
+	test_instance_ftrace_marker(test_instance);
+}
+
+static void test_instance_trace_sql(struct tracefs_instance *instance)
+{
+	struct tracefs_synth *synth;
+	struct trace_seq seq;
+	struct tep_handle *tep;
+	struct tep_event *event;
+	int ret;
+
+	tep = tracefs_local_events(NULL);
+	CU_TEST(tep != NULL);
+
+	trace_seq_init(&seq);
+
+	synth = tracefs_sql(tep, SQL_1_EVENT, SQL_1_SQL, NULL);
+	CU_TEST(synth != NULL);
+	ret = tracefs_synth_echo_cmd(&seq, synth);
+	CU_TEST(ret == 0);
+	tracefs_synth_free(synth);
+	trace_seq_reset(&seq);
+
+	synth = tracefs_sql(tep, SQL_2_EVENT, SQL_2_SQL, NULL);
+	CU_TEST(synth != NULL);
+	ret = tracefs_synth_echo_cmd(&seq, synth);
+	CU_TEST(ret == 0);
+	tracefs_synth_free(synth);
+	trace_seq_reset(&seq);
+
+	synth = tracefs_sql(tep, SQL_3_EVENT, SQL_3_SQL, NULL);
+	CU_TEST(synth != NULL);
+	ret = tracefs_synth_echo_cmd(&seq, synth);
+	CU_TEST(ret == 0);
+	tracefs_synth_free(synth);
+	trace_seq_reset(&seq);
+
+	synth = tracefs_sql(tep, SQL_4_EVENT, SQL_4_SQL, NULL);
+	CU_TEST(synth != NULL);
+	ret = tracefs_synth_echo_cmd(&seq, synth);
+	CU_TEST(ret == 0);
+	tracefs_synth_free(synth);
+	trace_seq_reset(&seq);
+
+	event = tep_find_event_by_name(tep, NULL, SQL_5_START);
+	if (event) {
+		synth = tracefs_sql(tep, SQL_5_EVENT, SQL_5_SQL, NULL);
+		CU_TEST(synth != NULL);
+		ret = tracefs_synth_echo_cmd(&seq, synth);
+		CU_TEST(ret == 0);
+		tracefs_synth_free(synth);
+		trace_seq_reset(&seq);
+	}
+
+	tep_free(tep);
+	trace_seq_destroy(&seq);
+}
+
+static void test_trace_sql(void)
+{
+	test_instance_trace_sql(test_instance);
+}
+
+struct test_cpu_data {
+	struct tracefs_instance		*instance;
+	struct tracefs_cpu		*tcpu;
+	struct kbuffer			*kbuf;
+	struct tep_handle		*tep;
+	unsigned long long		missed_events;
+	void				*buf;
+	int				events_per_buf;
+	int				bufsize;
+	int				data_size;
+	int				this_pid;
+	int				fd;
+	bool				done;
+};
+
+static void cleanup_trace_cpu(struct test_cpu_data *data)
+{
+	close(data->fd);
+	tep_free(data->tep);
+	tracefs_cpu_close(data->tcpu);
+	free(data->buf);
+	kbuffer_free(data->kbuf);
+}
+
+#define EVENT_SYSTEM "syscalls"
+#define EVENT_NAME  "sys_enter_getppid"
+
+static int setup_trace_cpu(struct tracefs_instance *instance, struct test_cpu_data *data)
+{
+	struct tep_format_field **fields;
+	struct tep_event *event;
+	char tmpfile[] = "/tmp/utest-libtracefsXXXXXX";
+	int max = 0;
+	int ret;
+	int i;
+
+	/* Make sure tracing is on */
+	tracefs_trace_on(instance);
+
+	memset (data, 0, sizeof(*data));
+
+	data->instance = instance;
+
+	data->fd = mkstemp(tmpfile);
+	CU_TEST(data->fd >= 0);
+	unlink(tmpfile);
+	if (data->fd < 0)
+		return -1;
+
+	data->tep = tracefs_local_events(NULL);
+	CU_TEST(data->tep != NULL);
+	if (!data->tep)
+		goto fail;
+
+	data->tcpu = tracefs_cpu_open(instance, 0, true);
+	CU_TEST(data->tcpu != NULL);
+	if (!data->tcpu)
+		goto fail;
+
+	data->bufsize = tracefs_cpu_read_size(data->tcpu);
+
+	data->buf = calloc(1, data->bufsize);
+	CU_TEST(data->buf != NULL);
+	if (!data->buf)
+		goto fail;
+
+	data->kbuf = kbuffer_alloc(sizeof(long) == 8, !tep_is_bigendian());
+	CU_TEST(data->kbuf != NULL);
+	if (!data->kbuf)
+		goto fail;
+
+	data->data_size = data->bufsize - kbuffer_start_of_data(data->kbuf);
+
+	tracefs_instance_file_clear(instance, "trace");
+
+	event = tep_find_event_by_name(data->tep, EVENT_SYSTEM, EVENT_NAME);
+	CU_TEST(event != NULL);
+	if (!event)
+		goto fail;
+
+	fields = tep_event_fields(event);
+	CU_TEST(fields != NULL);
+	if (!fields)
+		goto fail;
+
+	for (i = 0; fields[i]; i++) {
+		int end = fields[i]->offset + fields[i]->size;
+		if (end > max)
+			max = end;
+	}
+	free(fields);
+
+	CU_TEST(max != 0);
+	if (!max)
+		goto fail;
+
+	data->events_per_buf = data->data_size / max;
+
+	data->this_pid = getpid();
+	ret = tracefs_event_enable(instance, EVENT_SYSTEM, EVENT_NAME);
+	CU_TEST(ret == 0);
+	if (ret)
+		goto fail;
+
+
+	save_affinity();
+	set_affinity(0);
+
+	return 0;
+ fail:
+	cleanup_trace_cpu(data);
+	return -1;
+}
+
+static void shutdown_trace_cpu(struct test_cpu_data *data)
+{
+	struct tracefs_instance *instance = data->instance;
+	int ret;
+
+	reset_affinity();
+
+	ret = tracefs_event_disable(instance, EVENT_SYSTEM, EVENT_NAME);
+	CU_TEST(ret == 0);
+
+	cleanup_trace_cpu(data);
+}
+
+static void call_getppid(int cnt)
+{
+	int i;
+
+	for (i = 0; i < cnt; i++)
+		getppid();
+}
+
+static void test_cpu_read(struct test_cpu_data *data, int expect)
+{
+	struct tracefs_cpu *tcpu = data->tcpu;
+	struct kbuffer *kbuf = data->kbuf;
+	struct tep_record record;
+	void *buf = data->buf;
+	unsigned long long ts;
+	bool first = true;
+	int pid;
+	int ret;
+	int cnt = 0;
+
+	call_getppid(expect);
+
+	for (;;) {
+		ret = tracefs_cpu_read(tcpu, buf, false);
+		CU_TEST(ret > 0 || !first);
+		if (ret <= 0)
+			break;
+		first = false;
+		ret = kbuffer_load_subbuffer(kbuf, buf);
+		CU_TEST(ret == 0);
+		for (;;) {
+			record.data = kbuffer_read_event(kbuf, &ts);
+			if (!record.data)
+				break;
+			record.ts = ts;
+			pid = tep_data_pid(data->tep, &record);
+			if (pid == data->this_pid)
+				cnt++;
+			kbuffer_next_event(kbuf, NULL);
+		}
+	}
+	CU_TEST(cnt == expect);
+}
+
+static void test_instance_trace_cpu_read(struct tracefs_instance *instance)
+{
+	struct test_cpu_data data;
+
+	if (setup_trace_cpu(instance, &data))
+		return;
+
+	test_cpu_read(&data, 1);
+	test_cpu_read(&data, data.events_per_buf / 2);
+	test_cpu_read(&data, data.events_per_buf);
+	test_cpu_read(&data, data.events_per_buf + 1);
+	test_cpu_read(&data, data.events_per_buf * 50);
+
+	shutdown_trace_cpu(&data);
+}
+
+static void test_trace_cpu_read(void)
+{
+	test_instance_trace_cpu_read(NULL);
+	test_instance_trace_cpu_read(test_instance);
+}
+
+struct follow_data {
+	struct tep_event *sched_switch;
+	struct tep_event *sched_waking;
+	struct tep_event *function;
+	int missed;
+};
+
+static int switch_callback(struct tep_event *event, struct tep_record *record,
+			   int cpu, void *data)
+{
+	struct follow_data *fdata = data;
+
+	CU_TEST(cpu == record->cpu);
+	CU_TEST(event->id == fdata->sched_switch->id);
+	return 0;
+}
+
+static int waking_callback(struct tep_event *event, struct tep_record *record,
+			   int cpu, void *data)
+{
+	struct follow_data *fdata = data;
+
+	CU_TEST(cpu == record->cpu);
+	CU_TEST(event->id == fdata->sched_waking->id);
+	return 0;
+}
+
+static int function_callback(struct tep_event *event, struct tep_record *record,
+			     int cpu, void *data)
+{
+	struct follow_data *fdata = data;
+
+	CU_TEST(cpu == record->cpu);
+	CU_TEST(event->id == fdata->function->id);
+	return 0;
+}
+
+static int missed_callback(struct tep_event *event, struct tep_record *record,
+			     int cpu, void *data)
+{
+	struct follow_data *fdata = data;
+
+	fdata->missed = record->missed_events;
+	return 0;
+}
+
+static int all_callback(struct tep_event *event, struct tep_record *record,
+			int cpu, void *data)
+{
+	struct follow_data *fdata = data;
+
+	CU_TEST(fdata->missed == record->missed_events);
+	fdata->missed = 0;
+	return 0;
+}
+
+static void *stop_thread(void *arg)
+{
+	struct tracefs_instance *instance = arg;
+
+	sleep(1);
+	tracefs_iterate_stop(instance);
+	return NULL;
+}
+
+static void test_instance_follow_events(struct tracefs_instance *instance)
+{
+	struct follow_data fdata;
+	struct tep_handle *tep;
+	pthread_t thread;
+	int ret;
+
+	memset(&fdata, 0, sizeof(fdata));
+
+	tep = tracefs_local_events(NULL);
+	CU_TEST(tep != NULL);
+	if (!tep)
+		return;
+
+	fdata.sched_switch = tep_find_event_by_name(tep, "sched", "sched_switch");
+	CU_TEST(fdata.sched_switch != NULL);
+	if (!fdata.sched_switch)
+		return;
+
+	fdata.sched_waking = tep_find_event_by_name(tep, "sched", "sched_waking");
+	CU_TEST(fdata.sched_waking != NULL);
+	if (!fdata.sched_waking)
+		return;
+
+	fdata.function = tep_find_event_by_name(tep, "ftrace", "function");
+	CU_TEST(fdata.function != NULL);
+	if (!fdata.function)
+		return;
+
+	ret = tracefs_follow_event(tep, instance, "sched", "sched_switch",
+				   switch_callback, &fdata);
+	CU_TEST(ret == 0);
+
+	ret = tracefs_follow_event(tep, instance, "sched", "sched_waking",
+				   waking_callback, &fdata);
+	CU_TEST(ret == 0);
+
+	ret = tracefs_follow_event(tep, instance, "ftrace", "function",
+				   function_callback, &fdata);
+	CU_TEST(ret == 0);
+
+	ret = tracefs_follow_missed_events(instance, missed_callback, &fdata);
+	CU_TEST(ret == 0);
+
+	ret = tracefs_event_enable(instance, "sched", "sched_switch");
+	CU_TEST(ret == 0);
+
+	ret = tracefs_event_enable(instance, "sched", "sched_waking");
+	CU_TEST(ret == 0);
+
+	ret = tracefs_tracer_set(instance, TRACEFS_TRACER_FUNCTION);
+	CU_TEST(ret == 0);
+
+	pthread_create(&thread, NULL, stop_thread, instance);
+
+	ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, all_callback, &fdata);
+	CU_TEST(ret == 0);
+
+	pthread_join(thread, NULL);
+
+	tracefs_tracer_clear(instance);
+	tracefs_event_disable(instance, NULL, NULL);
+}
+
+static void test_follow_events(void)
+{
+	test_instance_follow_events(NULL);
+	test_instance_follow_events(test_instance);
+}
+
+extern char *find_tracing_dir(bool debugfs, bool mount);
+static void test_mounting(void)
+{
+	const char *tracing_dir;
+	const char *debug_dir;
+	struct stat st;
+	char *save_tracing = NULL;
+	char *save_debug = NULL;
+	char *path;
+	char *dir;
+	int ret;
+
+	/* First, unmount all instances of debugfs */
+	do {
+		dir = find_tracing_dir(true, false);
+		if (dir) {
+			ret = umount(dir);
+			CU_TEST(ret == 0);
+			if (ret < 0)
+				return;
+			/* Save the first instance that's not /sys/kernel/debug */
+			if (!save_debug && strcmp(dir, DEBUGFS_DEFAULT_PATH) != 0)
+				save_debug = dir;
+			else
+				free(dir);
+		}
+	} while (dir);
+
+	/* Next, unmount all instances of tracefs */
+	do {
+		dir = find_tracing_dir(false, false);
+		if (dir) {
+			ret = umount(dir);
+			CU_TEST(ret == 0);
+			if (ret < 0)
+				return;
+			/* Save the first instance that's not in /sys/kernel/ */
+			if (!save_tracing && strncmp(dir, "/sys/kernel/", 12) != 0)
+				save_tracing = dir;
+			else
+				free(dir);
+		}
+	} while (dir);
+
+	/* Mount first the tracing dir (which should mount at /sys/kernel/tracing */
+	tracing_dir = tracefs_tracing_dir();
+	CU_TEST(tracing_dir != NULL);
+	if (tracing_dir != NULL) {
+		CU_TEST(strcmp(tracing_dir, TRACEFS_DEFAULT_PATH) == 0 ||
+			strcmp(tracing_dir, TRACEFS_DEFAULT2_PATH) == 0);
+		if (strncmp(tracing_dir, "/sys/kernel/", 12) != 0)
+			printf("Tracing directory mounted at '%s'\n",
+			       tracing_dir);
+
+		/* Make sure the directory has content.*/
+		asprintf(&path, "%s/trace", tracing_dir);
+		CU_TEST(stat(path, &st) == 0);
+		free(path);
+	}
+
+	/* Now mount debugfs dir, which should mount at /sys/kernel/debug */
+	debug_dir = tracefs_debug_dir();
+	CU_TEST(debug_dir != NULL);
+	if (debug_dir != NULL) {
+		CU_TEST(strcmp(debug_dir, DEBUGFS_DEFAULT_PATH) == 0);
+		if (strcmp(debug_dir, DEBUGFS_DEFAULT_PATH) != 0)
+			printf("debug directory mounted at '%s'\n",
+			       debug_dir);
+
+		/* Make sure the directory has content.*/
+		asprintf(&path, "%s/tracing", debug_dir);
+		CU_TEST(stat(path, &st) == 0);
+		free(path);
+	}
+
+	if (save_debug)
+		mount("debugfs", save_debug, "debugfs", 0, NULL);
+
+	if (save_tracing &&
+	    (!save_debug || strncmp(save_debug, save_tracing, strlen(save_debug)) != 0))
+		mount("tracefs", save_tracing, "tracefs", 0, NULL);
+
+	free(save_debug);
+	free(save_tracing);
+}
+
+static int read_trace_cpu_file(struct test_cpu_data *data)
+{
+	unsigned long long ts;
+	struct tep_record record;
+	struct kbuffer *kbuf = data->kbuf;
+	void *buf = data->buf;
+	bool first = true;
+	int bufsize = data->bufsize;
+	int fd = data->fd;
+	int missed;
+	int pid;
+	int ret;
+	int cnt = 0;
+
+	ret = lseek64(fd, 0, SEEK_SET);
+	CU_TEST(ret == 0);
+	if (ret)
+		return -1;
+
+	for (;;) {
+		ret = read(fd, buf, bufsize);
+		CU_TEST(ret > 0 || !first);
+		if (ret <= 0)
+			break;
+		first = false;
+
+		ret = kbuffer_load_subbuffer(kbuf, buf);
+		CU_TEST(ret == 0);
+		missed = kbuffer_missed_events(kbuf);
+		if (missed)
+			printf("missed events %d\n", missed);
+		for (;;) {
+			record.data = kbuffer_read_event(kbuf, &ts);
+			if (!record.data)
+				break;
+			record.ts = ts;
+			pid = tep_data_pid(data->tep, &record);
+			if (pid == data->this_pid)
+				cnt++;
+			kbuffer_next_event(kbuf, NULL);
+		}
+	}
+	return ret == 0 ? cnt : ret;
+}
+
+static void *trace_cpu_thread(void *arg)
+{
+	struct test_cpu_data *data = arg;
+	struct tracefs_cpu *tcpu = data->tcpu;
+	int fd = data->fd;
+	long ret = 0;
+
+	thread_affinity();
+
+	while (!data->done && ret >= 0) {
+		ret = tracefs_cpu_write(tcpu, fd, false);
+		if (ret < 0 && errno == EAGAIN)
+			ret = 0;
+	}
+	if (ret >= 0 || errno == EAGAIN) {
+		do {
+			ret = tracefs_cpu_flush_write(tcpu, fd);
+		} while (ret > 0);
+	}
+
+	return (void *)ret;
+}
+
+static void test_cpu_pipe(struct test_cpu_data *data, int expect)
+{
+	pthread_t thread;
+	void *retval;
+	long ret;
+	int cnt;
+
+	tracefs_instance_file_clear(data->instance, "trace");
+	ftruncate(data->fd, 0);
+
+	data->done = false;
+
+	pthread_create(&thread, NULL, trace_cpu_thread, data);
+	sleep(1);
+
+	call_getppid(expect);
+
+	data->done = true;
+	tracefs_cpu_stop(data->tcpu);
+	pthread_join(thread, &retval);
+	ret = (long)retval;
+	CU_TEST(ret >= 0);
+
+	cnt = read_trace_cpu_file(data);
+
+	CU_TEST(cnt == expect);
+}
+
+static void test_instance_trace_cpu_pipe(struct tracefs_instance *instance)
+{
+	struct test_cpu_data data;
+
+	if (setup_trace_cpu(instance, &data))
+		return;
+
+	test_cpu_pipe(&data, 1);
+	test_cpu_pipe(&data, data.events_per_buf / 2);
+	test_cpu_pipe(&data, data.events_per_buf);
+	test_cpu_pipe(&data, data.events_per_buf + 1);
+	test_cpu_pipe(&data, data.events_per_buf * 1000);
+
+	shutdown_trace_cpu(&data);
+}
+
+static void test_trace_cpu_pipe(void)
+{
+	test_instance_trace_cpu_pipe(NULL);
+	test_instance_trace_cpu_pipe(test_instance);
+}
+
+static struct tracefs_dynevent **get_dynevents_check(enum tracefs_dynevent_type types, int count)
+{
+	struct tracefs_dynevent **devents;
+	int i;
+
+	devents = tracefs_dynevent_get_all(types, NULL);
+	if (count) {
+		CU_TEST(devents != NULL);
+		if (!devents)
+			return NULL;
+		i = 0;
+		while (devents[i])
+			i++;
+		CU_TEST(i == count);
+	} else {
+		CU_TEST(devents == NULL);
+	}
+
+	return devents;
+}
+
+
+struct test_synth {
+	char *name;
+	char *start_system;
+	char *start_event;
+	char *end_system;
+	char *end_event;
+	char *start_match_field;
+	char *end_match_field;
+	char *match_name;
+};
+
+static void test_synth_compare(struct test_synth *synth, struct tracefs_dynevent **devents)
+{
+	enum tracefs_dynevent_type stype;
+	char *format;
+	char *event;
+	int i;
+
+	for (i = 0; devents && devents[i]; i++) {
+		stype = tracefs_dynevent_info(devents[i], NULL,
+					      &event, NULL, NULL, &format);
+		CU_TEST(stype == TRACEFS_DYNEVENT_SYNTH);
+		CU_TEST(strcmp(event, synth[i].name) == 0);
+		if (synth[i].match_name) {
+			CU_TEST(strstr(format, synth[i].match_name) != NULL);
+		}
+		free(event);
+		free(format);
+	}
+	CU_TEST(devents == NULL || devents[i] == NULL);
+}
+
+static void test_instance_synthetic(struct tracefs_instance *instance)
+{
+	struct test_synth sevents[] = {
+		{"synth_1", "sched", "sched_waking", "sched", "sched_switch", "pid", "next_pid", "pid_match"},
+		{"synth_2", "syscalls", "sys_enter_openat2", "syscalls", "sys_exit_openat2", "__syscall_nr", "__syscall_nr", "nr_match"},
+	};
+	int sevents_count = sizeof(sevents) / sizeof((sevents)[0]);
+	struct tracefs_dynevent **devents;
+	struct tracefs_synth **synth;
+	struct tep_handle *tep;
+	int ret;
+	int i;
+
+	synth = calloc(sevents_count + 1, sizeof(*synth));
+
+	tep = tracefs_local_events(NULL);
+	CU_TEST(tep != NULL);
+
+	/* kprobes APIs */
+	ret = tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_SYNTH, true);
+	CU_TEST(ret == 0);
+	get_dynevents_check(TRACEFS_DYNEVENT_SYNTH, 0);
+
+	for (i = 0; i < sevents_count; i++) {
+		synth[i] = tracefs_synth_alloc(tep,  sevents[i].name,
+					       sevents[i].start_system, sevents[i].start_event,
+					       sevents[i].end_system, sevents[i].end_event,
+					       sevents[i].start_match_field, sevents[i].end_match_field,
+					       sevents[i].match_name);
+		CU_TEST(synth[i] != NULL);
+	}
+
+	get_dynevents_check(TRACEFS_DYNEVENT_SYNTH, 0);
+
+	for (i = 0; i < sevents_count; i++) {
+		ret = tracefs_synth_create(synth[i]);
+		CU_TEST(ret == 0);
+	}
+
+	devents = get_dynevents_check(TRACEFS_DYNEVENT_SYNTH, sevents_count);
+	CU_TEST(devents != NULL);
+	test_synth_compare(sevents, devents);
+	tracefs_dynevent_list_free(devents);
+
+	for (i = 0; i < sevents_count; i++) {
+		ret = tracefs_synth_destroy(synth[i]);
+		CU_TEST(ret == 0);
+	}
+
+	get_dynevents_check(TRACEFS_DYNEVENT_SYNTH, 0);
+
+	for (i = 0; i < sevents_count; i++)
+		tracefs_synth_free(synth[i]);
+
+	tep_free(tep);
+	free(synth);
+}
+
+static void test_synthetic(void)
+{
+	test_instance_synthetic(test_instance);
+}
+
+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, const 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);
+}
+
+struct probe_test {
+	enum tracefs_dynevent_type type;
+	char *prefix;
+	char *system;
+	char *event;
+	char *address;
+	char *format;
+};
+
+static bool check_probes(struct probe_test *probes, int count,
+			 struct tracefs_dynevent **devents, bool in_system,
+			 struct tracefs_instance *instance, struct tep_handle *tep)
+{
+	enum tracefs_dynevent_type type;
+	struct tep_event *tevent;
+	char *ename;
+	char *address;
+	char *event;
+	char *system;
+	char *format;
+	char *prefix;
+	int found = 0;
+	int ret;
+	int i, j;
+
+	for (i = 0; devents && devents[i]; i++) {
+		type = tracefs_dynevent_info(devents[i], &system,
+					     &event, &prefix, &address, &format);
+		for (j = 0; j < count; j++) {
+			if (type != probes[j].type)
+				continue;
+			if (probes[j].event)
+				ename = probes[j].event;
+			else
+				ename = probes[j].address;
+			if (strcmp(ename, event))
+				continue;
+			if (probes[j].system) {
+				CU_TEST(strcmp(probes[j].system, system) == 0);
+			}
+			CU_TEST(strcmp(probes[j].address, address) == 0);
+			if (probes[j].format) {
+				CU_TEST(strcmp(probes[j].format, format) == 0);
+			}
+			if (probes[j].prefix) {
+				CU_TEST(strcmp(probes[j].prefix, prefix) == 0);
+			}
+			ret = tracefs_event_enable(instance, system, event);
+			if (in_system) {
+				CU_TEST(ret == 0);
+			} else {
+				CU_TEST(ret != 0);
+			}
+			ret = tracefs_event_disable(instance, system, event);
+			if (in_system) {
+				CU_TEST(ret == 0);
+			} else {
+				CU_TEST(ret != 0);
+			}
+
+			tevent =  tracefs_dynevent_get_event(tep, devents[i]);
+			if (in_system) {
+				CU_TEST(tevent != NULL);
+				if (tevent) {
+					CU_TEST(strcmp(tevent->name, event) == 0);
+					CU_TEST(strcmp(tevent->system, system) == 0);
+				}
+			} else {
+				CU_TEST(tevent == NULL);
+			}
+
+			found++;
+			break;
+		}
+		free(system);
+		free(event);
+		free(prefix);
+		free(address);
+		free(format);
+	}
+
+	CU_TEST(found == count);
+	if (found != count)
+		return false;
+
+	return true;
+}
+
+static void test_kprobes_instance(struct tracefs_instance *instance)
+{
+	struct probe_test ktests[] = {
+		{ TRACEFS_DYNEVENT_KPROBE, "p", NULL, "mkdir", "do_mkdirat", "path=+u0($arg2):ustring" },
+		{ TRACEFS_DYNEVENT_KPROBE, "p", NULL, "close", "close_fd", NULL },
+		{ TRACEFS_DYNEVENT_KPROBE, "p", "ptest", "open2", "do_sys_openat2",
+				  "file=+u0($arg2):ustring flags=+0($arg3):x64" },
+	};
+	struct probe_test kretests[] = {
+		{ TRACEFS_DYNEVENT_KRETPROBE, NULL, NULL, "retopen", "do_sys_openat2", "ret=$retval" },
+		{ TRACEFS_DYNEVENT_KRETPROBE, NULL, NULL, NULL, "do_sys_open", "ret=$retval" },
+	};
+	int kretprobe_count = sizeof(kretests) / sizeof((kretests)[0]);
+	int kprobe_count = sizeof(ktests) / sizeof((ktests)[0]);
+	struct tracefs_dynevent **dkretprobe;
+	struct tracefs_dynevent **dkprobe;
+	struct tracefs_dynevent **devents;
+	struct tep_handle *tep;
+	char *tmp;
+	int ret;
+	int i;
+
+	tep = tep_alloc();
+	CU_TEST(tep != NULL);
+
+	dkprobe = calloc(kprobe_count + 1, sizeof(*dkprobe));
+	dkretprobe = calloc(kretprobe_count + 1, sizeof(*dkretprobe));
+
+	/* Invalid parameters */
+	CU_TEST(tracefs_kprobe_alloc("test", NULL, NULL, "test") == NULL);
+	CU_TEST(tracefs_kretprobe_alloc("test", NULL, NULL, "test", 0) == NULL);
+	CU_TEST(tracefs_dynevent_create(NULL) != 0);
+	CU_TEST(tracefs_dynevent_info(NULL, &tmp, &tmp, &tmp, &tmp, &tmp) == TRACEFS_DYNEVENT_UNKNOWN);
+	CU_TEST(tracefs_kprobe_raw("test", "test", NULL, "test") != 0);
+	CU_TEST(tracefs_kretprobe_raw("test", "test", NULL, "test") != 0);
+
+	/* kprobes APIs */
+	ret = tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, true);
+	CU_TEST(ret == 0);
+	get_dynevents_check(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, 0);
+
+	for (i = 0; i < kprobe_count; i++) {
+		dkprobe[i] = tracefs_kprobe_alloc(ktests[i].system, ktests[i].event,
+						  ktests[i].address, ktests[i].format);
+		CU_TEST(dkprobe[i] != NULL);
+	}
+	dkprobe[i] = NULL;
+	get_dynevents_check(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, 0);
+	CU_TEST(check_probes(ktests, kprobe_count, dkprobe, false, instance, tep));
+
+	for (i = 0; i < kretprobe_count; i++) {
+		dkretprobe[i] = tracefs_kretprobe_alloc(kretests[i].system, kretests[i].event,
+							kretests[i].address, kretests[i].format, 0);
+		CU_TEST(dkretprobe[i] != NULL);
+	}
+	dkretprobe[i] = NULL;
+	get_dynevents_check(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, 0);
+	CU_TEST(check_probes(kretests, kretprobe_count, dkretprobe, false, instance, tep));
+
+	for (i = 0; i < kprobe_count; i++) {
+		CU_TEST(tracefs_dynevent_create(dkprobe[i]) == 0);
+	}
+	devents = get_dynevents_check(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE,
+				    kprobe_count);
+	CU_TEST(check_probes(ktests, kprobe_count, devents, true, instance, tep));
+	CU_TEST(check_probes(kretests, kretprobe_count, dkretprobe, false, instance, tep));
+	tracefs_dynevent_list_free(devents);
+	devents = NULL;
+
+	for (i = 0; i < kretprobe_count; i++) {
+		CU_TEST(tracefs_dynevent_create(dkretprobe[i]) == 0);
+	}
+	devents = get_dynevents_check(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE,
+				    kprobe_count + kretprobe_count);
+	CU_TEST(check_probes(ktests, kprobe_count, devents, true, instance, tep));
+	CU_TEST(check_probes(kretests, kretprobe_count, devents, true, instance, tep));
+	tracefs_dynevent_list_free(devents);
+	devents = NULL;
+
+	for (i = 0; i < kretprobe_count; i++) {
+		CU_TEST(tracefs_dynevent_destroy(dkretprobe[i], false) == 0);
+	}
+	devents = get_dynevents_check(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE,
+				    kprobe_count);
+	CU_TEST(check_probes(ktests, kprobe_count, devents, true, instance, tep));
+	CU_TEST(check_probes(kretests, kretprobe_count, dkretprobe, false, instance, tep));
+	tracefs_dynevent_list_free(devents);
+	devents = NULL;
+
+	for (i = 0; i < kprobe_count; i++) {
+		CU_TEST(tracefs_dynevent_destroy(dkprobe[i], false) == 0);
+	}
+	get_dynevents_check(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, 0);
+	CU_TEST(check_probes(ktests, kprobe_count, dkprobe, false, instance, tep));
+	CU_TEST(check_probes(kretests, kretprobe_count, dkretprobe, false, instance, tep));
+	tracefs_dynevent_list_free(devents);
+	devents = NULL;
+
+	for (i = 0; i < kprobe_count; i++)
+		tracefs_dynevent_free(dkprobe[i]);
+	for (i = 0; i < kretprobe_count; i++)
+		tracefs_dynevent_free(dkretprobe[i]);
+
+	/* kprobes raw APIs */
+	ret = tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, true);
+	CU_TEST(ret == 0);
+	get_dynevents_check(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, 0);
+
+	for (i = 0; i < kprobe_count; i++) {
+		ret = tracefs_kprobe_raw(ktests[i].system, ktests[i].event,
+					 ktests[i].address, ktests[i].format);
+		CU_TEST(ret == 0);
+	}
+
+	devents = get_dynevents_check(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, kprobe_count);
+	CU_TEST(check_probes(ktests, kprobe_count, devents, true, instance, tep));
+	tracefs_dynevent_list_free(devents);
+	devents = NULL;
+
+	for (i = 0; i < kretprobe_count; i++) {
+		ret = tracefs_kretprobe_raw(kretests[i].system, kretests[i].event,
+					    kretests[i].address, kretests[i].format);
+		CU_TEST(ret == 0);
+	}
+
+	devents = get_dynevents_check(TRACEFS_DYNEVENT_KPROBE, kprobe_count);
+	CU_TEST(check_probes(ktests, kprobe_count, devents, true, instance, tep));
+	tracefs_dynevent_list_free(devents);
+	devents = NULL;
+
+	devents = get_dynevents_check(TRACEFS_DYNEVENT_KRETPROBE, kretprobe_count);
+	CU_TEST(check_probes(kretests, kretprobe_count, devents, true, instance, tep));
+	tracefs_dynevent_list_free(devents);
+	devents = NULL;
+
+	devents = get_dynevents_check(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE,
+				    kprobe_count + kretprobe_count);
+	CU_TEST(check_probes(ktests, kprobe_count, devents, true, instance, tep));
+	CU_TEST(check_probes(kretests, kretprobe_count, devents, true, instance, tep));
+	tracefs_dynevent_list_free(devents);
+	devents = NULL;
+
+	ret = tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, true);
+	CU_TEST(ret == 0);
+	get_dynevents_check(TRACEFS_DYNEVENT_KPROBE | TRACEFS_DYNEVENT_KRETPROBE, 0);
+	free(dkretprobe);
+	free(dkprobe);
+	tep_free(tep);
+}
+
+static void test_kprobes(void)
+{
+	test_kprobes_instance(test_instance);
+}
+
+static void test_eprobes_instance(struct tracefs_instance *instance)
+{
+	struct probe_test etests[] = {
+		{ TRACEFS_DYNEVENT_EPROBE, "e", NULL, "sopen_in", "syscalls.sys_enter_openat",
+					   "file=+0($filename):ustring" },
+		{ TRACEFS_DYNEVENT_EPROBE, "e", "etest", "sopen_out", "syscalls.sys_exit_openat",
+					   "res=$ret:u64" },
+	};
+	int count = sizeof(etests) / sizeof((etests)[0]);
+	struct tracefs_dynevent **deprobes;
+	struct tracefs_dynevent **devents;
+	struct tep_handle *tep;
+	char *tsys, *tevent;
+	char *tmp, *sav;
+	int ret;
+	int i;
+
+	tep = tep_alloc();
+	CU_TEST(tep != NULL);
+
+	deprobes = calloc(count + 1, sizeof(*deprobes));
+
+	/* Invalid parameters */
+	CU_TEST(tracefs_eprobe_alloc("test", NULL, "test", "test", "test") == NULL);
+	CU_TEST(tracefs_eprobe_alloc("test", "test", NULL, "test", "test") == NULL);
+	CU_TEST(tracefs_eprobe_alloc("test", "test", "test", NULL, "test") == NULL);
+
+	ret = tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_EPROBE, true);
+	CU_TEST(ret == 0);
+	get_dynevents_check(TRACEFS_DYNEVENT_EPROBE, 0);
+
+	for (i = 0; i < count; i++) {
+		tmp = strdup(etests[i].address);
+		tsys = strtok_r(tmp, "./", &sav);
+		tevent = strtok_r(NULL, "", &sav);
+		deprobes[i] = tracefs_eprobe_alloc(etests[i].system, etests[i].event,
+						   tsys, tevent, etests[i].format);
+		free(tmp);
+		CU_TEST(deprobes[i] != NULL);
+	}
+	deprobes[i] = NULL;
+
+	get_dynevents_check(TRACEFS_DYNEVENT_EPROBE, 0);
+	CU_TEST(check_probes(etests, count, deprobes, false, instance, tep));
+
+	for (i = 0; i < count; i++) {
+		CU_TEST(tracefs_dynevent_create(deprobes[i]) == 0);
+	}
+
+	devents = get_dynevents_check(TRACEFS_DYNEVENT_EPROBE, count);
+	CU_TEST(check_probes(etests, count, devents, true, instance, tep));
+	tracefs_dynevent_list_free(devents);
+	devents = NULL;
+
+	for (i = 0; i < count; i++) {
+		CU_TEST(tracefs_dynevent_destroy(deprobes[i], false) == 0);
+	}
+	get_dynevents_check(TRACEFS_DYNEVENT_EPROBE, 0);
+	CU_TEST(check_probes(etests, count, deprobes, false, instance, tep));
+
+	for (i = 0; i < count; i++)
+		tracefs_dynevent_free(deprobes[i]);
+
+	free(deprobes);
+	tep_free(tep);
+}
+
+static void test_eprobes(void)
+{
+	test_eprobes_instance(test_instance);
+}
+
+#define FOFFSET 1000ll
+static void test_uprobes_instance(struct tracefs_instance *instance)
+{
+	struct probe_test utests[] = {
+		{ TRACEFS_DYNEVENT_UPROBE, "p", "utest", "utest_u", NULL, "arg1=$stack2" },
+		{ TRACEFS_DYNEVENT_URETPROBE, "r", "utest", "utest_r", NULL, "arg1=$retval" },
+	};
+	int count = sizeof(utests) / sizeof((utests)[0]);
+	struct tracefs_dynevent **duprobes;
+	struct tracefs_dynevent **duvents;
+	char self[PATH_MAX] = { 0 };
+	struct tep_handle *tep;
+	char *target = NULL;
+	int i;
+
+	tep = tep_alloc();
+	CU_TEST(tep != NULL);
+
+	duprobes = calloc(count + 1, sizeof(*duvents));
+	CU_TEST(duprobes != NULL);
+	CU_TEST(readlink("/proc/self/exe", self, sizeof(self)) > 0);
+	CU_TEST(asprintf(&target, "%s:0x%0*llx", self, (int)(sizeof(void *) * 2), FOFFSET) > 0);
+
+	for (i = 0; i < count; i++)
+		utests[i].address = target;
+
+	/* Invalid parameters */
+	CU_TEST(tracefs_uprobe_alloc(NULL, NULL, self, 0, NULL) == NULL);
+	CU_TEST(tracefs_uprobe_alloc(NULL, "test", NULL, 0, NULL) == NULL);
+	CU_TEST(tracefs_uretprobe_alloc(NULL, NULL, self, 0, NULL) == NULL);
+	CU_TEST(tracefs_uretprobe_alloc(NULL, "test", NULL, 0, NULL) == NULL);
+
+	for (i = 0; i < count; i++) {
+		if (utests[i].type == TRACEFS_DYNEVENT_UPROBE)
+			duprobes[i] = tracefs_uprobe_alloc(utests[i].system, utests[i].event,
+							   self, FOFFSET, utests[i].format);
+		else
+			duprobes[i] = tracefs_uretprobe_alloc(utests[i].system, utests[i].event,
+							      self, FOFFSET, utests[i].format);
+		CU_TEST(duprobes[i] != NULL);
+	}
+	duprobes[i] = NULL;
+
+	get_dynevents_check(TRACEFS_DYNEVENT_UPROBE | TRACEFS_DYNEVENT_URETPROBE, 0);
+	CU_TEST(check_probes(utests, count, duprobes, false, instance, tep));
+
+	for (i = 0; i < count; i++) {
+		CU_TEST(tracefs_dynevent_create(duprobes[i]) == 0);
+	}
+
+	duvents = get_dynevents_check(TRACEFS_DYNEVENT_UPROBE | TRACEFS_DYNEVENT_URETPROBE, count);
+	CU_TEST(check_probes(utests, count, duvents, true, instance, tep));
+	tracefs_dynevent_list_free(duvents);
+
+	for (i = 0; i < count; i++) {
+		CU_TEST(tracefs_dynevent_destroy(duprobes[i], false) == 0);
+	}
+	get_dynevents_check(TRACEFS_DYNEVENT_UPROBE | TRACEFS_DYNEVENT_URETPROBE, 0);
+	CU_TEST(check_probes(utests, count, duprobes, false, instance, tep));
+
+	for (i = 0; i < count; i++)
+		tracefs_dynevent_free(duprobes[i]);
+
+	free(duprobes);
+	free(target);
+	tep_free(tep);
+}
+
+static void test_uprobes(void)
+{
+	test_uprobes_instance(test_instance);
+}
+
+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 *file1;
+	char *file2;
+	char *tracer;
+	char *fname;
+	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 bool check_fd_name(int fd, const char *dir, const char *name)
+{
+	char link[PATH_MAX + 1];
+	char path[PATH_MAX + 1];
+	struct stat st;
+	char *file;
+	int ret;
+
+	snprintf(link, PATH_MAX, "/proc/self/fd/%d", fd);
+	ret = lstat(link, &st);
+	CU_TEST(ret == 0);
+	if (ret < 0)
+		return false;
+	CU_TEST(S_ISLNK(st.st_mode));
+	if (!S_ISLNK(st.st_mode))
+		return false;
+	ret = readlink(link, path, PATH_MAX);
+	CU_TEST(ret > 0);
+	if (ret > PATH_MAX || ret < 0)
+		return false;
+	path[ret] = 0;
+	ret = strncmp(dir, path, strlen(dir));
+	CU_TEST(ret == 0);
+	if (ret)
+		return false;
+	file = basename(path);
+	CU_TEST(file != NULL);
+	if (!file)
+		return false;
+	ret = strcmp(file, name);
+	CU_TEST(ret == 0);
+	if (ret)
+		return false;
+	return true;
+}
+
+#define FLAGS_STR	"flags:"
+static bool check_fd_mode(int fd, int mode)
+{
+	char path[PATH_MAX + 1];
+	long fmode = -1;
+	char *line = NULL;
+	struct stat st;
+	size_t len = 0;
+	ssize_t size;
+	FILE *file;
+	int ret;
+
+	snprintf(path, PATH_MAX, "/proc/self/fdinfo/%d", fd);
+	ret = stat(path, &st);
+	CU_TEST(ret == 0);
+	if (ret < 0)
+		return false;
+	file = fopen(path, "r");
+	if (!file)
+		return false;
+	while ((size = getline(&line, &len, file)) > 0) {
+		if (strncmp(line, FLAGS_STR, strlen(FLAGS_STR)))
+			continue;
+		fmode = strtol(line + strlen(FLAGS_STR), NULL, 8);
+		break;
+	}
+	free(line);
+	fclose(file);
+	if (fmode < 0 ||
+	    (O_ACCMODE & fmode) != (O_ACCMODE & mode))
+		return false;
+	return true;
+}
+
+static void test_instance_file_fd(struct tracefs_instance *instance)
+{
+	const char *name = get_rand_str();
+	const char *tdir = tracefs_instance_get_trace_dir(instance);
+	long long res = -1;
+	char rd[2];
+	int fd;
+
+	CU_TEST(tdir != NULL);
+	fd = tracefs_instance_file_open(instance, name, -1);
+	CU_TEST(fd == -1);
+	fd = tracefs_instance_file_open(instance, TRACE_ON, O_RDONLY);
+	CU_TEST(fd >= 0);
+
+	CU_TEST(check_fd_name(fd, tdir, TRACE_ON));
+	CU_TEST(check_fd_mode(fd, O_RDONLY));
+
+	CU_TEST(tracefs_instance_file_read_number(instance, ALL_TRACERS, &res) != 0);
+	CU_TEST(tracefs_instance_file_read_number(instance, name, &res) != 0);
+	CU_TEST(tracefs_instance_file_read_number(instance, TRACE_ON, &res) == 0);
+	CU_TEST((res == 0 || res == 1));
+	CU_TEST(read(fd, &rd, 1) == 1);
+	rd[1] = 0;
+	CU_TEST(res == atoi(rd));
+
+	close(fd);
+}
+
+static void test_file_fd(void)
+{
+	test_instance_file_fd(test_instance);
+}
+
+static void test_instance_tracing_onoff(struct tracefs_instance *instance)
+{
+	const char *tdir = tracefs_instance_get_trace_dir(instance);
+	long long res = -1;
+	int fd;
+
+	CU_TEST(tdir != NULL);
+	fd = tracefs_trace_on_get_fd(instance);
+	CU_TEST(fd >= 0);
+	CU_TEST(check_fd_name(fd, tdir, TRACE_ON));
+	CU_TEST(check_fd_mode(fd, O_RDWR));
+	CU_TEST(tracefs_instance_file_read_number(instance, TRACE_ON, &res) == 0);
+	if (res == 1) {
+		CU_TEST(tracefs_trace_is_on(instance) == 1);
+		CU_TEST(tracefs_trace_off(instance) == 0);
+		CU_TEST(tracefs_trace_is_on(instance) == 0);
+		CU_TEST(tracefs_trace_on(instance) == 0);
+		CU_TEST(tracefs_trace_is_on(instance) == 1);
+
+		CU_TEST(tracefs_trace_off_fd(fd) == 0);
+		CU_TEST(tracefs_trace_is_on(instance) == 0);
+		CU_TEST(tracefs_trace_on_fd(fd) == 0);
+		CU_TEST(tracefs_trace_is_on(instance) == 1);
+	} else {
+		CU_TEST(tracefs_trace_is_on(instance) == 0);
+		CU_TEST(tracefs_trace_on(instance) == 0);
+		CU_TEST(tracefs_trace_is_on(instance) == 1);
+		CU_TEST(tracefs_trace_off(instance) == 0);
+		CU_TEST(tracefs_trace_is_on(instance) == 0);
+
+		CU_TEST(tracefs_trace_on_fd(fd) == 0);
+		CU_TEST(tracefs_trace_is_on(instance) == 1);
+		CU_TEST(tracefs_trace_off_fd(fd) == 0);
+		CU_TEST(tracefs_trace_is_on(instance) == 0);
+	}
+
+	if (fd >= 0)
+		close(fd);
+}
+
+static void test_tracing_onoff(void)
+{
+	test_instance_tracing_onoff(test_instance);
+}
+
+static bool check_option(struct tracefs_instance *instance,
+			 enum tracefs_option_id id, bool exist, int enabled)
+{
+	const char *name = tracefs_option_name(id);
+	char file[PATH_MAX];
+	char *path = NULL;
+	bool ret = false;
+	bool supported;
+	struct stat st;
+	char buf[10];
+	int fd = 0;
+	int r;
+	int rstat;
+
+	CU_TEST(name != NULL);
+	supported = tracefs_option_is_supported(instance, id);
+	CU_TEST(supported == exist);
+	if (supported != exist)
+		goto out;
+	snprintf(file, PATH_MAX, "options/%s", name);
+	path = tracefs_instance_get_file(instance, file);
+	CU_TEST(path != NULL);
+	rstat = stat(path, &st);
+	if (exist) {
+		CU_TEST(rstat == 0);
+		if (rstat != 0)
+			goto out;
+	} else {
+		CU_TEST(stat(path, &st) == -1);
+		if (rstat != -1)
+			goto out;
+	}
+
+	fd = open(path, O_RDONLY);
+	if (exist) {
+		CU_TEST(fd >= 0);
+		if (fd < 0)
+			goto out;
+	} else {
+		CU_TEST(fd < 0);
+		if (fd >= 0)
+			goto out;
+	}
+
+	if (exist && enabled >= 0) {
+		int val = enabled ? '1' : '0';
+
+		r = read(fd, buf, 10);
+		CU_TEST(r >= 1);
+		CU_TEST(buf[0] == val);
+		if (buf[0] != val)
+			goto out;
+	}
+
+	ret = true;
+out:
+	tracefs_put_tracing_file(path);
+	if (fd >= 0)
+		close(fd);
+	return ret;
+}
+
+static void test_instance_tracing_options(struct tracefs_instance *instance)
+{
+	const struct tracefs_options_mask *enabled;
+	const struct tracefs_options_mask *all_copy;
+	const struct tracefs_options_mask *all;
+	enum tracefs_option_id i = 1;
+	char file[PATH_MAX];
+	const char *name;
+
+	all = tracefs_options_get_supported(instance);
+	all_copy = tracefs_options_get_supported(instance);
+	enabled = tracefs_options_get_enabled(instance);
+	CU_TEST(all != NULL);
+
+	/* Invalid parameters test */
+	CU_TEST(!tracefs_option_is_supported(instance, TRACEFS_OPTION_INVALID));
+	CU_TEST(!tracefs_option_is_enabled(instance, TRACEFS_OPTION_INVALID));
+	CU_TEST(tracefs_option_enable(instance, TRACEFS_OPTION_INVALID) == -1);
+	CU_TEST(tracefs_option_disable(instance, TRACEFS_OPTION_INVALID) == -1);
+	name = tracefs_option_name(TRACEFS_OPTION_INVALID);
+	CU_TEST(!strcmp(name, "unknown"));
+	/* Test all valid options */
+	for (i = 1; i < TRACEFS_OPTION_MAX; i++) {
+		name = tracefs_option_name(i);
+		CU_TEST(name != NULL);
+		CU_TEST(strcmp(name, "unknown"));
+		snprintf(file, PATH_MAX, "options/%s", name);
+
+		if (tracefs_option_mask_is_set(all, i)) {
+			CU_TEST(check_option(instance, i, true, -1));
+			CU_TEST(tracefs_option_is_supported(instance, i));
+		} else {
+			CU_TEST(check_option(instance, i, false, -1));
+			CU_TEST(!tracefs_option_is_supported(instance, i));
+		}
+
+		if (tracefs_option_mask_is_set(enabled, i)) {
+			CU_TEST(check_option(instance, i, true, 1));
+			CU_TEST(tracefs_option_is_supported(instance, i));
+			CU_TEST(tracefs_option_is_enabled(instance, i));
+			CU_TEST(tracefs_option_disable(instance, i) == 0);
+			CU_TEST(check_option(instance, i, true, 0));
+			CU_TEST(tracefs_option_enable(instance, i) == 0);
+			CU_TEST(check_option(instance, i, true, 1));
+		} else if (tracefs_option_mask_is_set(all_copy, i)) {
+			CU_TEST(check_option(instance, i, true, 0));
+			CU_TEST(tracefs_option_is_supported(instance, i));
+			CU_TEST(!tracefs_option_is_enabled(instance, i));
+			CU_TEST(tracefs_option_enable(instance, i) == 0);
+			CU_TEST(check_option(instance, i, true, 1));
+			CU_TEST(tracefs_option_disable(instance, i) == 0);
+			CU_TEST(check_option(instance, i, true, 0));
+		}
+	}
+}
+
+static void test_tracing_options(void)
+{
+	test_instance_tracing_options(test_instance);
+}
+
+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 system_event(const char *tdir)
+{
+
+	char **systems;
+	char **events;
+	char *sdir = 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_system_event(void)
+{
+	const char *tdir;
+
+	tdir  = tracefs_tracing_dir();
+	CU_TEST(tdir != NULL);
+	system_event(tdir);
+}
+
+static void test_instance_tracers(struct tracefs_instance *instance)
+{
+	const char *tdir;
+	char **tracers;
+	char *tfile;
+	char *tracer;
+	int i;
+
+	tdir  = tracefs_instance_get_trace_dir(instance);
+	CU_TEST(tdir != NULL);
+
+	tracers = tracefs_tracers(tdir);
+	CU_TEST(tracers != NULL);
+
+	for (i = 0; tracers[i]; i++)
+		CU_TEST(tracefs_tracer_available(tdir, tracers[i]));
+
+	tfile = tracefs_instance_file_read(NULL, ALL_TRACERS, NULL);
+
+	tracer = strtok(tfile, " ");
+	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_tracers(void)
+{
+	test_instance_tracers(test_instance);
+}
+
+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 local_events(const char *tdir)
+{
+	struct tep_handle *tep;
+	char **systems;
+	char *lsystems[3];
+	int i;
+
+	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);
+}
+
+static void test_local_events(void)
+{
+	const char *tdir;
+
+	tdir  = tracefs_tracing_dir();
+	CU_TEST(tdir != NULL);
+	local_events(tdir);
+}
+
+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(struct tracefs_instance *instance, const char *clock)
+{
+	int size = 0;
+	char *clocks;
+	char *str;
+
+	clocks = tracefs_instance_file_read(instance, TRACE_CLOCK, &size);
+	CU_TEST_FATAL(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_instance_get_clock(struct tracefs_instance *instance)
+{
+	const char *clock;
+
+	clock = tracefs_get_clock(instance);
+	CU_TEST_FATAL(clock != NULL);
+	current_clock_check(instance, clock);
+	free((char *)clock);
+}
+
+static void test_get_clock(void)
+{
+	test_instance_get_clock(test_instance);
+}
+
+static void copy_trace_file(const char *from, char *to)
+{
+	int fd_from = -1;
+	int fd_to = -1;
+	char buf[512];
+	int ret;
+
+	fd_from = open(from, O_RDONLY);
+	if (fd_from < 0)
+		goto out;
+	fd_to = open(to, O_WRONLY | O_TRUNC | O_CREAT, S_IRWXU | S_IRWXG);
+	if (fd_to < 0)
+		goto out;
+
+	while ((ret = read(fd_from, buf, 512)) > 0) {
+		if (write(fd_to, buf, ret) == -1)
+			break;
+	}
+
+out:
+	if (fd_to >= 0)
+		close(fd_to);
+	if (fd_from >= 0)
+		close(fd_from);
+}
+
+static int trace_dir_base;
+static char *trace_tmp_dir;
+static int copy_trace_walk(const char *fpath, const struct stat *sb,
+			   int typeflag, struct FTW *ftwbuf)
+{
+	char path[PATH_MAX];
+
+	sprintf(path, "%s%s", trace_tmp_dir, fpath + trace_dir_base);
+
+	switch (typeflag) {
+	case FTW_D:
+		mkdir(path, 0750);
+		break;
+	case FTW_F:
+		copy_trace_file(fpath, path);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static void dup_trace_dir(char *to, char *dir)
+{
+	const char *trace_dir = tracefs_tracing_dir();
+	char file_from[PATH_MAX];
+	char file_to[PATH_MAX];
+
+	sprintf(file_from, "%s/%s", trace_dir, dir);
+	sprintf(file_to, "%s/%s", to, dir);
+	trace_tmp_dir = file_to;
+	trace_dir_base = strlen(file_from);
+	nftw(file_from, copy_trace_walk, 20, 0);
+}
+
+static void dup_trace_file(char *to, char *file)
+{
+	const char *trace_dir = tracefs_tracing_dir();
+	char file_from[PATH_MAX];
+	char file_to[PATH_MAX];
+
+	sprintf(file_from, "%s/%s", trace_dir, file);
+	sprintf(file_to, "%s/%s", to, file);
+	copy_trace_file(file_from, file_to);
+}
+
+static char *copy_trace_dir(void)
+{
+	char template[] = TEST_TRACE_DIR;
+	char *dname = mkdtemp(template);
+
+	dup_trace_dir(dname, "events");
+	dup_trace_dir(dname, "options");
+	dup_trace_file(dname, TRACE_ON);
+	dup_trace_file(dname, CUR_TRACER);
+	dup_trace_file(dname, TRACE_CLOCK);
+	dup_trace_file(dname, ALL_TRACERS);
+
+	return strdup(dname);
+}
+
+static int del_trace_walk(const char *fpath, const struct stat *sb,
+			  int typeflag, struct FTW *ftwbuf)
+{
+	remove(fpath);
+	return 0;
+}
+
+void del_trace_dir(char *dir)
+{
+	nftw(dir, del_trace_walk, 20, FTW_DEPTH);
+}
+
+static void test_custom_trace_dir(void)
+{
+	char *tdir = "/tmp/custom_tracefs";
+	struct tracefs_instance *instance;
+	char *dname = copy_trace_dir();
+	const char *trace_dir;
+	char *tfile;
+
+	instance = tracefs_instance_alloc(dname, NULL);
+	CU_TEST(instance != NULL);
+
+	system_event(dname);
+	local_events(dname);
+	test_instance_tracing_options(instance);
+	test_instance_get_clock(instance);
+	test_instance_file_fd(instance);
+	test_instance_tracers(instance);
+
+	tracefs_instance_free(instance);
+	del_trace_dir(dname);
+	free(dname);
+
+	trace_dir = tracefs_tracing_dir();
+	CU_TEST(trace_dir != NULL);
+	CU_TEST(tracefs_set_tracing_dir(tdir) == 0);
+	CU_TEST(strcmp(tdir, tracefs_tracing_dir()) == 0);
+	tfile = tracefs_get_tracing_file("trace");
+	CU_TEST(tfile != NULL);
+	CU_TEST(strcmp(tdir, dirname(tfile)) == 0);
+	free(tfile);
+
+	CU_TEST(tracefs_set_tracing_dir(NULL) == 0);
+	CU_TEST(strcmp(trace_dir, tracefs_tracing_dir()) == 0);
+	tfile = tracefs_get_tracing_file("trace");
+	CU_TEST(tfile != NULL);
+	CU_TEST(strcmp(trace_dir, dirname(tfile)) == 0);
+	free(tfile);
+}
+
+static int test_suite_destroy(void)
+{
+	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, "Test tracefs/debugfs mounting", test_mounting);
+	CU_add_test(suite, "trace cpu read",
+		    test_trace_cpu_read);
+	CU_add_test(suite, "trace cpu pipe",
+		    test_trace_cpu_pipe);
+	CU_add_test(suite, "trace sql",
+		    test_trace_sql);
+	CU_add_test(suite, "tracing file / directory APIs",
+		    test_trace_file);
+	CU_add_test(suite, "instance file / directory APIs",
+		    test_file_fd);
+	CU_add_test(suite, "instance file descriptor",
+		    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);
+
+	/* Follow events test must be after the iterate raw events above */
+	CU_add_test(suite, "Follow events", test_follow_events);
+
+	CU_add_test(suite, "tracefs_tracers API",
+		    test_tracers);
+	CU_add_test(suite, "tracefs_local events API",
+		    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);
+	CU_add_test(suite, "tracing on / off",
+		    test_tracing_onoff);
+	CU_add_test(suite, "tracing options",
+		    test_tracing_options);
+	CU_add_test(suite, "custom system directory",
+		    test_custom_trace_dir);
+	CU_add_test(suite, "ftrace marker",
+		    test_ftrace_marker);
+	CU_add_test(suite, "kprobes", test_kprobes);
+	CU_add_test(suite, "synthetic events", test_synthetic);
+	CU_add_test(suite, "eprobes", test_eprobes);
+	CU_add_test(suite, "uprobes", test_uprobes);
+}