ktrace: Initial commit

Initial commit of the shell like utility to access Linux tracing.

Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..878caad
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.d
+*~
+*.o
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ff0812f
--- /dev/null
+++ b/LICENSE
@@ -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/Makefile b/Makefile
new file mode 100644
index 0000000..b188ff9
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,272 @@
+# SPDX-License-Identifier: LGPL-2.1
+# ktrace version:
+KT_VERSION = 0
+KT_PATCHLEVEL = 0
+KT_EXTRAVERSION = dev
+KTRACE_VERSION = $(KT_VERSION).$(KT_PATCHLEVEL).$(KT_EXTRAVERSION)
+
+export KT_VERSION
+export KT_PATCHLEVEL
+export KT_EXTRAVERSION
+export KTRACE_VERSION
+
+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
+includedir = $(prefix)/$(includedir_relative)
+includedir_SQ = '$(subst ','\'',$(includedir))'
+pkgconfig_dir ?= $(word 1,$(shell $(PKG_CONFIG) 		\
+			--variable pc_path pkg-config | tr ":" " "))
+
+
+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)
+
+LIBTRACEFS_MIN_VERSION = 1.2
+LIBCCLI_MIN_VERSION = 1.1
+
+# Test for necessary libraries
+TEST_LIBTRACEFS = $(shell sh -c "$(PKG_CONFIG) --atleast-version $(LIBTRACEFS_MIN_VERSION) libtracefs > /dev/null 2>&1 && echo y")
+
+ifeq ("$(TEST_LIBTRACEFS)", "y")
+LIBTRACEFS_INCLUDES = $(shell sh -c "$(PKG_CONFIG) --cflags libtracefs")
+LIBTRACEFS_LIBS = $(shell sh -c "$(PKG_CONFIG) --libs libtracefs")
+else
+ ifneq ($(MAKECMDGOALS),clean)
+   $(error libtracefs.so minimum version of $(LIBTRACEFS_MIN_VERSION) not installed)
+ endif
+endif
+
+# Test for necessary libraries
+TEST_LIBCCLI = $(shell sh -c "$(PKG_CONFIG) --atleast-version $(LIBCCLI_MIN_VERSION) libccli > /dev/null 2>&1 && echo y")
+
+ifeq ("$(TEST_LIBCCLI)", "y")
+LIBCCLI_INCLUDES = $(shell sh -c "$(PKG_CONFIG) --cflags libccli")
+LIBCCLI_LIBS = $(shell sh -c "$(PKG_CONFIG) --libs libccli")
+else
+ ifneq ($(MAKECMDGOALS),clean)
+   $(error libccli.so minimum version of $(LIBCCLI_MIN_VERSION) not installed)
+ endif
+endif
+
+# $(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)/src
+
+export prefix src obj bdir
+
+LIBS =$(LIBCCLI_LIBS) $(LIBTRACEFS_LIBS)
+
+export LIBS
+
+export Q SILENT VERBOSE EXT
+
+# Include the utils
+include scripts/utils.mk
+
+# Set compile option CFLAGS if not set elsewhere
+CFLAGS ?= -g -Wall
+CPPFLAGS ?=
+LDFLAGS ?=
+
+CFLAGS += $(LIBCCLI_INCLUDES) $(LIBTRACEFS_INCLUDES)
+
+export CFLAGS
+export INCLUDES
+
+# Append required CFLAGS
+override CFLAGS += -D_GNU_SOURCE $(INCLUDES)
+
+all: all_cmd
+
+TARGETS = ktrace
+
+all_cmd: $(TARGETS)
+
+ktrace: force
+	make -C src $@
+
+VERSION_FILE = ktest_version.h
+
+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)
+
+install:
+
+doc:
+	$(Q)$(call descend,$(src)/Documentation,all)
+
+doc_clean:
+	$(Q)$(call descend,$(src)/Documentation,clean)
+
+install_doc:
+	$(Q)$(call descend,$(src)/Documentation,install)
+
+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)
+
+OBJS := $(OBJS:%.o=$(bdir)/%.o)
+DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
+
+all: $(DEFAULT_TARGET)
+
+$(bdir):
+	@mkdir -p $(bdir)
+
+VERSION = $(KT_VERSION)
+PATCHLEVEL = $(KT_PATCHLEVEL)
+EXTRAVERSION = $(KT_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)
+
+clean:
+	$(Q)$(call descend_clean,src)
+	$(Q)$(call do_clean, \
+	  $(TARGETS) $(bdir)/*.a $(bdir)/*.so $(bdir)/*.so.* $(bdir)/*.o $(bdir)/.*.d \
+	  $(VERSION_FILE))
+
+.PHONY: clean
diff --git a/scripts/utils.mk b/scripts/utils.mk
new file mode 100644
index 0000000..af21322
--- /dev/null
+++ b/scripts/utils.mk
@@ -0,0 +1,226 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# 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_asciidoc =
+  print_xsltproc =
+  print_install =
+  hide_xsltproc_output =
+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     '$(GSPACE)$1'	to	$(DESTDIR_SQ)$2';
+  print_update =		echo '  UPDATE             '$(GOBJ);
+  print_descend =		echo '  DESCEND            '$(BASE1) $(BASE2);
+  print_clean =			echo '  CLEAN              '$(BASEPWD);
+  print_uninstall =		echo '  UNINSTALLING $(DESTDIR_SQ)$1';
+  print_asciidoc =		echo '  ASCIIDOC            '`basename $@`;
+  print_xsltproc =		echo '  XSLTPROC            '`basename $@`;
+  print_install =		echo '  INSTALL             '`basename $1`'	to	$(DESTDIR_SQ)'$2;
+  hide_xsltproc_output = 2> /dev/null
+endif
+
+do_fpic_compile =					\
+	($(print_fpic_compile)				\
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $(EXT) -fPIC $< -o $@)
+
+do_compile =							\
+	($(if $(GENERATE_PIC), $(do_fpic_compile),		\
+	 $(print_compile)					\
+	 $(CC) -c $(CPPFLAGS) $(CFLAGS) $(EXT) $< -o $@))
+
+do_app_build =						\
+	($(print_app_build)				\
+	$(CC) $^ -rdynamic -Wl,-rpath=$(libdir) -o $@ $(LDFLAGS) $(CONFIG_LIBS) $(LIBS))
+
+do_build_static_lib =				\
+	($(print_static_lib_build)		\
+	$(RM) $@;  $(AR) rcs $@ $^)
+
+do_compile_shared_library =			\
+	($(print_shared_lib_compile)		\
+	$(CC) --shared $^ '-Wl,-soname,$(@F),-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_clean =					\
+	($(print_clean)				\
+	$(RM) $1)
+
+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 $@)
+
+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
+	$(print_install)				\
+	if [ ! -d '$(DESTDIR_SQ)$2' ]; then		\
+		$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2';	\
+	fi;						\
+	$(INSTALL) $(if $3,-m $3,) $1 '$(DESTDIR_SQ)$2'
+endef
+
+define do_install_data
+	$(print_install)				\
+	if [ ! -d '$(DESTDIR_SQ)$2' ]; then		\
+		$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2';	\
+	fi;						\
+	$(INSTALL) -m 644 $1 '$(DESTDIR_SQ)$2'
+endef
+
+#
+# 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 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
+
+define do_make_pkgconfig_file
+	$(print_app_build)
+	$(Q)cp -f $(srctree)/${PKG_CONFIG_SOURCE_FILE}.template ${PKG_CONFIG_FILE};	\
+	sed -i "s|INSTALL_PREFIX|${1}|g" ${PKG_CONFIG_FILE}; 		\
+	sed -i "s|LIB_VERSION|${LIBTRACECMD_VERSION}|g" ${PKG_CONFIG_FILE}; \
+	sed -i "s|LIB_DIR|$(libdir)|g" ${PKG_CONFIG_FILE}; \
+	sed -i "s|LIBTRACEFS_MIN_VERSION|$(LIBTRACEFS_MIN_VERSION)|g" ${PKG_CONFIG_FILE}; \
+	sed -i "s|HEADER_DIR|$(includedir)/trace-cmd|g" ${PKG_CONFIG_FILE};
+endef
+
+do_asciidoc_build =				\
+	($(print_asciidoc)			\
+	 asciidoc -d manpage -b docbook -o $@ $<)
+
+do_xsltproc_build =				\
+	($(print_xsltproc)			\
+	 xsltproc --nonet -o $@ ${MANPAGE_DOCBOOK_XSL} $< $(hide_xsltproc_output))
+
+#
+# asciidoc requires a synopsis, but file format man pages (5) do
+# not require them. This removes it from the file in the final step.
+define remove_synopsis
+	(sed -e '/^\.SH "SYNOPSIS"/,/ignore/d' $1 > $1.tmp;\
+	 mv $1.tmp $1)
+endef
+
+define do_install_docs
+	$(print_install)				\
+	if [ ! -d '$(DESTDIR_SQ)$2' ]; then		\
+		$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2';	\
+	fi;						\
+	$(INSTALL) -m 644 $1 '$(DESTDIR_SQ)$2'
+endef
+
+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
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..a828635
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1 @@
+ktrace
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..51c7de8
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: LGPL-2.1
+
+include $(src)/scripts/utils.mk
+
+OBJS =
+OBJS += ktrace.o
+OBJS += create.o
+
+OBJS := $(OBJS:%.o=$(bdir)/%.o)
+DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
+
+$(bdir)/%.o: %.c
+	$(Q)$(call do_compile)
+
+$(DEPS): $(bdir)/.%.d: %.c
+	$(Q)$(CC) -M -MT $(bdir)/$*.o $(CPPFLAGS) $(CFLAGS) $< > $@
+
+$(OBJS): $(bdir)/%.o : $(bdir)/.%.d
+
+$(OBJS): | $(bdir)
+$(DEPS): | $(bdir)
+
+$(bdir)/ktrace: $(OBJS)
+	$(Q)$(do_app_build)
+
+ktrace: $(bdir)/ktrace
+
+clean:
+	$(Q)$(call do_clean,$(OBJS) .*.d)
+
+dep_includes := $(wildcard $(DEPS))
+
+ifneq ($(dep_includes),)
+  include $(dep_includes)
+endif
+
+.PHONY: ktrace clean
diff --git a/src/create.c b/src/create.c
new file mode 100644
index 0000000..ce0753c
--- /dev/null
+++ b/src/create.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Google Inc, Steven Rostedt <rostedt@goodmis.org>
+ */
+#include "ktrace.h"
+
+
+static void create_usage(struct ccli *ccli)
+{
+	ccli_printf(ccli, "usage: create <type> <type-command>\n"
+		    "  <type> : kprobe, eprobe, synthetic_event\n");
+}
+
+static int create_kprobe(struct ccli *ccli, void *data,
+			 int argc, char **argv)
+{
+	return 0;
+}
+
+static int add_event_var(struct ccli *ccli, struct tep_handle *tep,
+			 char **command, struct tep_event *event,
+			 char *line)
+{
+	struct tep_format_field *field;
+	long long offset;
+	bool neg;
+	char *fname;
+	char *type;
+	char *var;
+	char *tmp;
+	char *end;
+	char *p;
+	char ch;
+	int ret;
+
+	p = strchr(line, '=');
+	if (!p) {
+		ccli_printf(ccli, "Invalid variable '%s'\n", line);
+		return -1;
+	}
+
+	*p = '\0';
+	fname = p + 1;
+	for (p++; *p; p++) {
+		if (*p == '.' || *p == '-')
+			break;
+	}
+	ch = *p;
+	*p = '\0';
+
+	field = tep_find_any_field(event, fname);
+	if (!field) {
+		ccli_printf(ccli, "# Cannot find field '%s' for event '%s'\n",
+			    fname, event->name);
+		return -1;
+	}
+
+	ret = asprintf(&var, "$%s", fname);
+	if (ret < 0)
+		return -1;
+
+	*p = ch;
+	for (; *p; p++) {
+		switch(*p) {
+		case '.':
+			p++;
+			type = p;
+			for (; *p; p++) {
+				if (*p == '.' || *p == '-')
+					break;
+			}
+			if (*p == '.') {
+				ccli_printf(ccli, "# Two types can not be togethe '%s'\n",
+					    line);
+				goto out;
+			}
+			if (strncmp(type, "string", 6) == 0 ||
+			    strncmp(type, "ustring", 7) == 0) {
+				if (*p) {
+					ccli_printf(ccli, "# Strings can not be deferenced '%s'\n",
+						type);
+					goto out;
+				}
+				ret = asprintf(&tmp, "+0(%s):%.*s",
+					       var, (int)(p - type),
+					       type);
+			} else {
+				ret = asprintf(&tmp, "%s:%.*s",
+					       var, (int)(p - type),
+					       type);
+			}
+			if (ret < 0)
+				goto out;
+			free(var);
+			var = tmp;
+			/* We need to compare current p again */
+			p--;
+			break;
+		case '-':
+			p++;
+			if (*p != '>') {
+				ccli_printf(ccli, "# Invalid variable '%s'\n",
+					    type);
+				goto out;
+			}
+			p++;
+			if (*p == '-') {
+				p++;
+				neg = true;
+			}
+			offset = strtoll(p, &end, 0);
+			ret = asprintf(&tmp, "%s%llu(%s)",
+				       neg ? "-" : "+", offset, var);
+			if (ret < 0)
+				goto out;
+			free(var);
+			var = tmp;
+			break;
+		}
+	}
+	/* Finished */
+	ret = asprintf(&tmp, "%s %s=%s", *command, line, var);
+ out:
+	free(var);
+	if (ret < 0)
+		return -1;
+	free(*command);
+	*command = tmp;
+	return 0;
+}
+
+static int create_eprobe(struct ccli *ccli, void *data,
+			 int argc, char **argv)
+{
+	struct tep_handle *tep = data;
+	struct tep_event *event;
+	char *command;
+	char *system;
+	char *ename;
+	char *name;
+	char *sav;
+	int ret;
+	int i;
+
+	if (argc < 3) {
+		ccli_printf(ccli, "# usage: create eprobe name system/event fields\n");
+		return 0;
+	}
+
+	name = argv[0];
+
+	system = strtok_r(argv[1], "/", &sav);
+	ename = strtok_r(NULL, "/", &sav);
+	if (!ename) {
+		event = tep_find_event_by_name(tep, NULL, system);
+		if (!event) {
+			ccli_printf(ccli, "# Event %s not found\n", system);
+			return 0;
+		}
+		system = event->system;
+	} else {
+		event = tep_find_event_by_name(tep, system, ename);
+		if (!event) {
+			ccli_printf(ccli, "# Event %s/%s not found\n",
+				    system, ename);
+			return 0;
+		}
+	}
+
+	ret = asprintf(&command, "e:%s %s/%s", name, system, ename);
+	if (ret < 0)
+		return 0;
+
+	for (i = 2 ; i < argc; i++ ) {
+		ret = add_event_var(ccli, tep, &command, event, argv[i]);
+		if (ret < 0)
+			goto out;
+	}
+	ccli_printf(ccli, "# echo '%s' >> %s/dynamic_events\n",
+		    command, tracefs_tracing_dir());
+ out:
+	free(command);
+	return 0;
+}
+
+int cmd_create(struct ccli *ccli, const char *command, const char *line,
+	       void *data, int argc, char **argv)
+{
+	if (argc < 2) {
+		create_usage(ccli);
+		return 0;
+	}
+
+	if (strcmp(argv[1], "kprobe") == 0)
+		return create_kprobe(ccli, data, argc - 2, argv + 2);
+
+	if (strcmp(argv[1], "eprobe") == 0)
+		return create_eprobe(ccli, data, argc - 2, argv + 2);
+
+	return 0;
+}
+
+static int kprobe_completion(struct ccli *ccli, void *data,
+			     int argc, char **argv,
+			     char ***list, int word, char *match)
+{
+	return 0;
+}
+
+static int eprobe_completion(struct ccli *ccli, void *data,
+			     int argc, char **argv,
+			     char ***list, int word, char *match)
+{
+	struct tep_handle *tep = data;
+	struct tep_format_field **common_fields;
+	struct tep_format_field **fields;
+	struct tep_event *event;
+	static char *types[] = {"string" , "ustring", "x8", "x16", "x32", "x64",
+		"u8", "u16", "u32", "u64", "s8", "s16", "s32", "s64" };
+	char **systems;
+	char **events;
+	char **words;
+	char **tmp;
+	char *system;
+	char *name;
+	char *sav;
+	char *p, *m;
+	int len;
+	int i, x;
+
+	switch (word) {
+	case 0:
+		ccli_printf(ccli, "\n# Name the event probe\n");
+		ccli_line_refresh(ccli);
+		return 0;
+	case 1:
+		p = strchr(match, '/');
+		if (p) {
+			system = strdup(match);
+			if (!system)
+				return 0;
+			system[p - match] = '\0';
+			events = tracefs_system_events(NULL, system);
+			if (!events) {
+				free(system);
+				return 0;
+			}
+			words = calloc(tracefs_list_size(events), sizeof(char *));
+			i = 0;
+			if (words) {
+				for (; events[i]; i++)
+					asprintf(words + i, "%s/%s",
+						 system, events[i]);
+			}
+			tracefs_list_free(events);
+		} else {
+			systems = tracefs_event_systems(NULL);
+			if (!systems)
+				return 0;
+			words = calloc(tracefs_list_size(systems), sizeof(char *));
+			i = 0;
+			if (words) {
+				for (; systems[i]; i++)
+					words[i] = strdup(systems[i]);
+			}
+			tracefs_list_free(systems);
+			/* Use '/' as a delim */
+			match[strlen(match)] = '/';
+		}
+		*list = words;
+		return i;
+	default:
+		system = strtok_r(argv[1], "/", &sav);
+		name = strtok_r(NULL, "/", &sav);
+		if (!system || !name)
+			return 0;
+		event = tep_find_event_by_name(tep, system, name);
+		if (!event) {
+			ccli_printf(ccli, "\n# Event %s/%s not found\n",
+				    system, name);
+			return 0;
+		}
+		p = strchr(match, '=');
+		if (!p) {
+			ccli_printf(ccli, "\n# var=field[.type][->offset[.type]\n");
+			ccli_line_refresh(ccli);
+			return 0;
+		}
+		m = p;
+		while (*p) {
+			if (*p == '.' || *p == '-')
+				m = p;
+			p++;
+		}
+
+		len = m - match;
+
+		switch (*m) {
+		default:
+			common_fields = tep_event_common_fields(event);
+			fields = tep_event_fields(event);
+			words = NULL;
+			x = 0;
+			for (i = 0; common_fields && common_fields[i]; i++) {
+				tmp = realloc(words, sizeof(char *) * (x + 2));
+				if (!tmp) {
+					ccli_argv_free(words);
+					return 0;
+				}
+				words = tmp;
+				asprintf(&words[x++], "%.*s=%s",
+					 len, match,
+					 common_fields[i]->name);
+				words[x] = NULL;
+			}
+			for (i = 0; fields && fields[i]; i++) {
+				tmp = realloc(words, sizeof(char *) * (x + 2));
+				if (!tmp) {
+					ccli_argv_free(words);
+					return 0;
+				}
+				words = tmp;
+				asprintf(&words[x++], "%.*s=%s",
+					 len, match,
+					 fields[i]->name);
+				words[x] = NULL;
+			}
+			free(common_fields);
+			free(fields);
+			*list = words;
+			match[strlen(match)] = CCLI_NOSPACE;
+			return x;
+		case '.':
+			x = ARRAY_SIZE(types);
+			words = calloc(x, sizeof(char *));
+			if (!words)
+				return 0;
+			for (i = 0; i < x; i++) {
+				asprintf(&words[i], "%.*s.%s",
+					 len, match, types[i]);
+			}
+			*list = words;
+			match[strlen(match)] = CCLI_NOSPACE;
+			return x;
+		case '-':
+			if (!m[1]) {
+				words = calloc(1, sizeof(char *));
+				if (!words)
+					return 0;
+				asprintf(&words[0], "%.*s->",
+					 len, match);
+				*list = words;
+				match[strlen(match)] = CCLI_NOSPACE;
+				return 1;
+			}
+			return 0;
+		}
+	}
+	printf("\neprobe word=%d match=%s\n", word, match);
+	return 0;
+}
+
+int create_completion(struct ccli *ccli, const char *command,
+		      const char *line, int word,
+		      char *match, char ***list, void *data)
+{
+	char *types[] = { "kprobe", "eprobe", "synthetic" };
+	char **words;
+	char **argv;
+	int argc;
+	int ret = 0;
+	int i;
+
+	if (word == 1) {
+		words = calloc(ARRAY_SIZE(types), sizeof(char *));
+		if (!words)
+			return 0;
+		for (i = 0; i < ARRAY_SIZE(types); i++)
+			words[i] = strdup(types[i]);
+		*list = words;
+		return i;
+	}
+
+	argc = ccli_line_parse(line, &argv);
+	if (argc < 0)
+		return 0;
+
+	if (strcmp(argv[1], "kprobe") == 0)
+		ret = kprobe_completion(ccli, data, argc - 2, argv + 2,
+					list, word - 2, match);
+
+	if (strcmp(argv[1], "eprobe") == 0)
+		ret = eprobe_completion(ccli, data, argc - 2, argv + 2,
+					list, word - 2, match);
+
+	ccli_argv_free(argv);
+
+	return ret;
+}
diff --git a/src/ktrace.c b/src/ktrace.c
new file mode 100644
index 0000000..db4b560
--- /dev/null
+++ b/src/ktrace.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Google Inc, Steven Rostedt <rostedt@goodmis.org>
+ */
+#include "ktrace.h"
+
+#define EXEC_NAME	"ktrace"
+
+static void usage(char **argv)
+{
+	printf("usage: %s\n"
+	       "\n", EXEC_NAME);
+	exit(-1);
+}
+
+static void __vdie(const char *fmt, va_list ap, int err)
+{
+	int ret = errno;
+
+	if (err && errno)
+		perror(EXEC_NAME);
+	else
+		ret = -1;
+
+	fprintf(stderr, "  ");
+	vfprintf(stderr, fmt, ap);
+
+	fprintf(stderr, "\n");
+	exit(ret);
+}
+
+void pdie(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	__vdie(fmt, ap, 1);
+	va_end(ap);
+}
+
+int main (int argc, char **argv)
+{
+	struct ccli *ccli;
+	struct tep_handle *tep;
+
+	tep = tracefs_local_events(NULL);
+	if (!tep) {
+		if (errno == EPERM)
+			pdie("Failed to initialize tracing directory.\n"
+			     "   You do not have permission to read it");
+		pdie("Failed to initialize tracing directory");
+	}
+	ccli = ccli_alloc("ktrace> ", STDIN_FILENO, STDOUT_FILENO);
+	if (!ccli)
+		pdie("ccli initialization");
+
+	ccli_register_command(ccli, "create", cmd_create, tep);
+	ccli_register_completion(ccli, "create", create_completion);
+
+	ccli_loop(ccli);
+	ccli_free(ccli);
+
+	tep_free(tep);
+
+	return 0;
+}
diff --git a/src/ktrace.h b/src/ktrace.h
new file mode 100644
index 0000000..1b8853c
--- /dev/null
+++ b/src/ktrace.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __KTRACE__H
+#define __KTRACE__H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <tracefs.h>
+#include <ccli.h>
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+
+int create_completion(struct ccli *ccli, const char *command,
+		      const char *line, int word,
+		      char *match, char ***list, void *data);
+int cmd_create(struct ccli *ccli, const char *command, const char *line,
+	       void *data, int argc, char **argv);
+
+#endif