aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:04:00 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:04:00 +0000
commit4ae53abbd632f824f0e2f4735f7c2858a44f9563 (patch)
tree21be18ffc6c7cbb651dcc56278ff076a3f297bcf
parent88c6000bd334a5c18705d96c129d9b4750c23872 (diff)
parent287e80b3a36113050663245e7f2c00d274188f18 (diff)
downloadlibtracefs-android14-mainline-permission-release.tar.gz
Change-Id: I8b0376dcc54f2f97b8aa753eb4b128fa08d2e104
-rw-r--r--.gitignore15
-rw-r--r--Android.bp69
-rw-r--r--Documentation/.gitignore5
-rw-r--r--Documentation/Makefile247
-rw-r--r--Documentation/asciidoc.conf120
-rw-r--r--Documentation/libtracefs-cpu-open.txt100
-rw-r--r--Documentation/libtracefs-cpu.txt240
-rw-r--r--Documentation/libtracefs-dynevents.txt283
-rw-r--r--Documentation/libtracefs-eprobes.txt187
-rw-r--r--Documentation/libtracefs-error.txt137
-rw-r--r--Documentation/libtracefs-events-file.txt217
-rw-r--r--Documentation/libtracefs-events-tep.txt148
-rw-r--r--Documentation/libtracefs-events.txt195
-rw-r--r--Documentation/libtracefs-files.txt131
-rw-r--r--Documentation/libtracefs-filter.txt345
-rw-r--r--Documentation/libtracefs-function-filter.txt237
-rw-r--r--Documentation/libtracefs-hist-cont.txt222
-rw-r--r--Documentation/libtracefs-hist-mod.txt540
-rw-r--r--Documentation/libtracefs-hist.txt531
-rw-r--r--Documentation/libtracefs-instances-affinity.txt200
-rw-r--r--Documentation/libtracefs-instances-file-manip.txt199
-rw-r--r--Documentation/libtracefs-instances-files.txt173
-rw-r--r--Documentation/libtracefs-instances-manage.txt150
-rw-r--r--Documentation/libtracefs-instances-utils.txt141
-rw-r--r--Documentation/libtracefs-iterator.txt229
-rw-r--r--Documentation/libtracefs-kprobes.txt273
-rw-r--r--Documentation/libtracefs-log.txt76
-rw-r--r--Documentation/libtracefs-marker.txt116
-rw-r--r--Documentation/libtracefs-marker_raw.txt102
-rw-r--r--Documentation/libtracefs-option-get.txt141
-rw-r--r--Documentation/libtracefs-option-misc.txt100
-rw-r--r--Documentation/libtracefs-options.txt159
-rw-r--r--Documentation/libtracefs-sql.txt628
-rw-r--r--Documentation/libtracefs-sqlhist.txt.1356
-rw-r--r--Documentation/libtracefs-stream.txt126
-rw-r--r--Documentation/libtracefs-synth-info.txt298
-rw-r--r--Documentation/libtracefs-synth.txt368
-rw-r--r--Documentation/libtracefs-synth2.txt281
-rw-r--r--Documentation/libtracefs-traceon.txt151
-rw-r--r--Documentation/libtracefs-tracer.txt221
-rw-r--r--Documentation/libtracefs-uprobes.txt189
-rw-r--r--Documentation/libtracefs-utils.txt139
-rw-r--r--Documentation/libtracefs.txt344
-rw-r--r--Documentation/manpage-1.72.xsl14
-rw-r--r--Documentation/manpage-base.xsl35
-rw-r--r--Documentation/manpage-bold-literal.xsl17
-rw-r--r--Documentation/manpage-normal.xsl13
-rw-r--r--Documentation/manpage-suppress-sp.xsl21
-rw-r--r--LICENSE1503
-rw-r--r--LICENSES/GPL-2.0359
-rw-r--r--LICENSES/LGPL-2.1503
-rw-r--r--METADATA19
-rw-r--r--MODULE_LICENSE_GPL0
-rw-r--r--Makefile397
-rw-r--r--OWNERS2
-rw-r--r--README56
-rwxr-xr-xcheck-manpages.sh61
-rw-r--r--include/tracefs-local.h141
-rw-r--r--include/tracefs.h637
-rw-r--r--libtracefs.pc.template11
-rw-r--r--samples/Makefile75
-rw-r--r--scripts/features.mk37
-rw-r--r--scripts/utils.mk196
-rw-r--r--src/Makefile62
-rw-r--r--src/sqlhist-lex.c2204
-rw-r--r--src/sqlhist-parse.h77
-rw-r--r--src/sqlhist.l104
-rw-r--r--src/sqlhist.tab.c1755
-rw-r--r--src/sqlhist.tab.h118
-rw-r--r--src/sqlhist.y248
-rw-r--r--src/tracefs-dynevents.c779
-rw-r--r--src/tracefs-eprobes.c56
-rw-r--r--src/tracefs-events.c1515
-rw-r--r--src/tracefs-filter.c807
-rw-r--r--src/tracefs-hist.c2401
-rw-r--r--src/tracefs-instance.c1241
-rw-r--r--src/tracefs-kprobes.c198
-rw-r--r--src/tracefs-marker.c199
-rw-r--r--src/tracefs-record.c611
-rw-r--r--src/tracefs-sqlhist.c1653
-rw-r--r--src/tracefs-tools.c1273
-rw-r--r--src/tracefs-uprobes.c90
-rw-r--r--src/tracefs-utils.c624
-rw-r--r--test.c7
-rw-r--r--utest/Makefile35
-rw-r--r--utest/README18
-rw-r--r--utest/trace-utest.c83
-rw-r--r--utest/trace-utest.h11
-rw-r--r--utest/tracefs-utest.c2385
89 files changed, 31780 insertions, 0 deletions
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);
+}