diff options
Diffstat (limited to 'Documentation')
46 files changed, 9145 insertions, 0 deletions
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=* +plus=+ +caret=^ +startsb=[ +endsb=] +tilde=~ + +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[] + .ft C +endif::doctype-manpage[] +| +ifdef::doctype-manpage[] + .ft +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">▓</xsl:param> +<xsl:param name="git.docbook.dot" >⌂</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 </xsl:text> + <xsl:apply-templates/> + <xsl:text> </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 </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> </xsl:text> + </xsl:if> +</xsl:template> + +</xsl:stylesheet> |