* [PATCH v2 0/5] libtraceeval: Add man pages
@ 2023-10-06 19:55 Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 1/5] libtraceeval: Start creating the documentation " Steven Rostedt
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Steven Rostedt @ 2023-10-06 19:55 UTC (permalink / raw)
To: linux-trace-devel; +Cc: Ross Zwisler, Steven Rostedt (Google)
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
This series adds the man pages for all the currently defined APIs in
traceeval.h.
Changes since v1: https://lore.kernel.org/all/20231005232450.1311519-1-rostedt@goodmis.org/
- Made one of the examples a bit smaller (was too big, although it's still pretty big)
- Fixed up missing traceeval_iterator_put() in the examples.
Steven Rostedt (Google) (5):
libtraceeval: Start creating the documentation man pages
libtraceeval: Add man pages for creating and releasing the traceeval
descriptor
libtraceeval: Add man pages for insertion and removal of elements in a
traceeval
libtraceeval: Add man pages for the traceeval iterator functions
libtraceeval: Add man pages for traceeval statistics
Documentation/.gitignore | 4 +
Documentation/Makefile | 247 +++++++
Documentation/asciidoc.conf | 120 +++
Documentation/libtraceeval-init.txt | 395 ++++++++++
Documentation/libtraceeval-insert.txt | 390 ++++++++++
Documentation/libtraceeval-iterator.txt | 921 ++++++++++++++++++++++++
Documentation/libtraceeval-stat.txt | 308 ++++++++
Documentation/libtraceeval.txt | 118 +++
Documentation/manpage-1.72.xsl | 14 +
Documentation/manpage-base.xsl | 35 +
Documentation/manpage-bold-literal.xsl | 17 +
Documentation/manpage-normal.xsl | 13 +
Documentation/manpage-suppress-sp.xsl | 21 +
check-manpages.sh | 63 ++
14 files changed, 2666 insertions(+)
create mode 100644 Documentation/.gitignore
create mode 100644 Documentation/Makefile
create mode 100644 Documentation/asciidoc.conf
create mode 100644 Documentation/libtraceeval-init.txt
create mode 100644 Documentation/libtraceeval-insert.txt
create mode 100644 Documentation/libtraceeval-iterator.txt
create mode 100644 Documentation/libtraceeval-stat.txt
create mode 100644 Documentation/libtraceeval.txt
create mode 100644 Documentation/manpage-1.72.xsl
create mode 100644 Documentation/manpage-base.xsl
create mode 100644 Documentation/manpage-bold-literal.xsl
create mode 100644 Documentation/manpage-normal.xsl
create mode 100644 Documentation/manpage-suppress-sp.xsl
create mode 100755 check-manpages.sh
--
2.40.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 1/5] libtraceeval: Start creating the documentation man pages
2023-10-06 19:55 [PATCH v2 0/5] libtraceeval: Add man pages Steven Rostedt
@ 2023-10-06 19:55 ` Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 2/5] libtraceeval: Add man pages for creating and releasing the traceeval descriptor Steven Rostedt
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Steven Rostedt @ 2023-10-06 19:55 UTC (permalink / raw)
To: linux-trace-devel; +Cc: Ross Zwisler, Steven Rostedt (Google)
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
Create a Documentation directory and start adding man pages for the
libtraceeval functions.
This just adds the infrastructure for the man pages. The man pages
themselves will come later.
Link: https://lore.kernel.org/linux-trace-devel/20231005232450.1311519-2-rostedt@goodmis.org
Cc: Ross Zwisler <zwisler@google.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Documentation/.gitignore | 4 +
Documentation/Makefile | 245 +++++++++++++++++++++++++
Documentation/asciidoc.conf | 120 ++++++++++++
Documentation/libtraceeval.txt | 55 ++++++
Documentation/manpage-1.72.xsl | 14 ++
Documentation/manpage-base.xsl | 35 ++++
Documentation/manpage-bold-literal.xsl | 17 ++
Documentation/manpage-normal.xsl | 13 ++
Documentation/manpage-suppress-sp.xsl | 21 +++
check-manpages.sh | 63 +++++++
10 files changed, 587 insertions(+)
create mode 100644 Documentation/.gitignore
create mode 100644 Documentation/Makefile
create mode 100644 Documentation/asciidoc.conf
create mode 100644 Documentation/libtraceeval.txt
create mode 100644 Documentation/manpage-1.72.xsl
create mode 100644 Documentation/manpage-base.xsl
create mode 100644 Documentation/manpage-bold-literal.xsl
create mode 100644 Documentation/manpage-normal.xsl
create mode 100644 Documentation/manpage-suppress-sp.xsl
create mode 100755 check-manpages.sh
diff --git a/Documentation/.gitignore b/Documentation/.gitignore
new file mode 100644
index 000000000000..747b2584d55b
--- /dev/null
+++ b/Documentation/.gitignore
@@ -0,0 +1,4 @@
+*.3
+*.m
+*.xml
+*.html
diff --git a/Documentation/Makefile b/Documentation/Makefile
new file mode 100644
index 000000000000..3540440e1dae
--- /dev/null
+++ b/Documentation/Makefile
@@ -0,0 +1,245 @@
+# SPDX-License-Identifier: LGPL-2.1
+
+include $(src)/scripts/utils.mk
+
+
+# This Makefile and manpage XSL files were taken from libtracefs
+# and modified for libtraceeval
+
+MAN3_TXT= \
+ libtraceeval.txt
+
+MAN1_TEXT=
+
+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/libtraceeval-doc
+pdfdir?=$(prefix)/share/doc/libtraceeval-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="libtraceeval" -a manmanual="libtraceeval 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 libtraceeval-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 libtraceeval-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) -alibtraceeval_version=$(TRACEEVAL_VERSION) -o $@+ $< && \
+ mv $@+ $@
+$(OUTPUT)%.1 : $(OUTPUT)%.txt.1
+ $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+ $(ASCIIDOC) -b manpage -d manpage \
+ $(ASCIIDOC_EXTRA) -alibtraceeval_version=$(TRACEEVAL_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) -alibtraceeval_version=$(TRACEEVAL_VERSION) -o $@+ $< && \
+ mv $@+ $@
+
+$(OUTPUT)%.xml : %.txt.1
+ $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+ $(ASCIIDOC) -b docbook -d manpage \
+ $(ASCIIDOC_EXTRA) -alibtraceeval_version=$(TRACEEVAL_VERSION) -o $@+ $< && \
+ mv $@+ $@
+
+$(MAN_HTML): $(OUTPUT)%.html : %.txt
+ $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+ $(ASCIIDOC) -b $(ASCIIDOC_HTML) -d manpage \
+ $(ASCIIDOC_EXTRA) -alibtraceeval_version=$(TRACEEVAL_VERSION) -o $@+ $< && \
+ mv $@+ $@
+
+$(MAN1_HTML): $(OUTPUT)%.html : %.txt.1
+ $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+ $(ASCIIDOC) -b $(ASCIIDOC_HTML) -d manpage \
+ $(ASCIIDOC_EXTRA) -alibtraceeval_version=$(TRACEEVAL_VERSION) -o $@+ $< && \
+ mv $@+ $@
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
new file mode 100644
index 000000000000..c15aa13b21ef
--- /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/libtraceeval.txt b/Documentation/libtraceeval.txt
new file mode 100644
index 000000000000..d3b97805d6ca
--- /dev/null
+++ b/Documentation/libtraceeval.txt
@@ -0,0 +1,55 @@
+libtraceeval(3)
+===============
+
+NAME
+----
+libtraceeval - A trace evaluation helper library
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <traceeval.h>*
+
+--
+
+DESCRIPTION
+-----------
+The libtracefs(3) library provides APIs to access kernel trace file system.
+
+FILES
+-----
+[verse]
+--
+*traceeval.h*
+ Header file to include in order to have access to the library APIs.
+*-ltraceeval*
+ Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtracecmd*
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+--
+
+REPORTING BUGS
+--------------
+Report bugs to <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+TBD
+
+RESOURCES
+---------
+TBD
+
+COPYING
+-------
+Copyright \(C) 2023 Google, llc.
diff --git a/Documentation/manpage-1.72.xsl b/Documentation/manpage-1.72.xsl
new file mode 100644
index 000000000000..b4d315cb8c47
--- /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 000000000000..a264fa616093
--- /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 000000000000..608eb5df6281
--- /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 000000000000..a48f5b11f3dc
--- /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 000000000000..a63c7632a87d
--- /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>
diff --git a/check-manpages.sh b/check-manpages.sh
new file mode 100755
index 000000000000..07acc12ecbc4
--- /dev/null
+++ b/check-manpages.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1
+# Copyright (C) 2022, Google Inc, Steven Rostedt <rostedt@goodmis.org>
+#
+# This checks if any function is listed in a man page that is not listed
+# in the main man page.
+
+if [ $# -lt 1 ]; then
+ echo "usage: check-manpages man-page-path"
+ exit 1
+fi
+
+cd $1
+
+MAIN=libtraceeval
+MAIN_FILE=${MAIN}.txt
+
+PROCESSED=""
+
+# Ignore man pages that do not contain functions
+IGNORE=""
+
+for man in ${MAIN}-*.txt; do
+
+ for a in `sed -ne '/^NAME/,/^SYNOP/{/^[a-z]/{s/, *$//;s/,/\n/g;s/ //g;s/-.*$/-/;/-/{s/-//p;q};p}}' $man`; do
+ if [ "${PROCESSED/:${a} /}" != "${PROCESSED}" ]; then
+ P="${PROCESSED/:${a} */}"
+ echo "Found ${a} in ${man} and in ${P/* /}"
+ fi
+ PROCESSED="${man}:${a} ${PROCESSED}"
+ if [ "${IGNORE/$man/}" != "${IGNORE}" ]; then
+ continue
+ fi
+ if ! grep -q '\*'${a}'\*' $MAIN_FILE; then
+ if [ "$last" == "" ]; then
+ echo
+ fi
+ if [ "$last" != "$man" ]; then
+ echo "Missing functions from $MAIN_FILE that are in $man"
+ last=$man
+ fi
+ echo " ${a}"
+ fi
+ done
+done
+
+# traceeval_init_data_size is not deprecated, but users shouldn't be using it directly.
+DEPRECATED="*traceeval_init_data_size*"
+
+last=""
+sed -ne 's/^[a-z].*[ \*]\([a-z_][a-z_]*\)(.*/\1/p' -e 's/^\([a-z_][a-z_]*\)(.*/\1/p' ../include/traceeval.h | while read f; do
+ if ! grep -q '\*'${f}'\*' $MAIN_FILE; then
+ if [ "${DEPRECATED/\*$f\*/}" != "${DEPRECATED}" ]; then
+ continue;
+ fi
+ if [ "$last" == "" ]; then
+ echo
+ echo "Missing functions from $MAIN_FILE that are in traceeval.h"
+ last=$f
+ fi
+ echo " ${f}"
+ fi
+done
--
2.40.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 2/5] libtraceeval: Add man pages for creating and releasing the traceeval descriptor
2023-10-06 19:55 [PATCH v2 0/5] libtraceeval: Add man pages Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 1/5] libtraceeval: Start creating the documentation " Steven Rostedt
@ 2023-10-06 19:55 ` Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 3/5] libtraceeval: Add man pages for insertion and removal of elements in a traceeval Steven Rostedt
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Steven Rostedt @ 2023-10-06 19:55 UTC (permalink / raw)
To: linux-trace-devel; +Cc: Ross Zwisler, Steven Rostedt (Google)
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
Add man pages for:
traceeval_init()
traceeval_init_size()
traceeval_release()
Link: https://lore.kernel.org/linux-trace-devel/20231005232450.1311519-3-rostedt@goodmis.org
Cc: Ross Zwisler <zwisler@google.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Documentation/Makefile | 4 +-
Documentation/libtraceeval-init.txt | 395 ++++++++++++++++++++++++++++
Documentation/libtraceeval.txt | 5 +
3 files changed, 403 insertions(+), 1 deletion(-)
create mode 100644 Documentation/libtraceeval-init.txt
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 3540440e1dae..d6c0c05fcc99 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -7,9 +7,11 @@ include $(src)/scripts/utils.mk
# and modified for libtraceeval
MAN3_TXT= \
+ $(wildcard libtraceeval-*.txt) \
libtraceeval.txt
-MAN1_TEXT=
+MAN1_TEXT= \
+ $(wildcard libtraceeval-*.txt.1)
MAN_TXT = $(MAN3_TXT)
_MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
diff --git a/Documentation/libtraceeval-init.txt b/Documentation/libtraceeval-init.txt
new file mode 100644
index 000000000000..ed505d11713c
--- /dev/null
+++ b/Documentation/libtraceeval-init.txt
@@ -0,0 +1,395 @@
+libtraceeval(3)
+===============
+
+NAME
+----
+traceeval_init, traceeval_init_size, traceeval_release - Create a trace evaluation helper mechanism.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <traceeval.h>*
+
+struct traceeval pass:[*]*traceeval_init*(struct traceeval_type pass:[*]_keys_, struct traceeval_type pass:[*]_vals_);
+struct traceeval pass:[*]*traceeval_init_size*(struct traceeval_type pass:[*]_keys_, struct traceeval_type pass:[*]_vals_,
+ int _nr_keys_, int _nr_vals_);
+void *traceeval_release*(struct traceeval pass:[*]_teval_);
+--
+
+DESCRIPTION
+-----------
+The traceeval utility is to help simplify analysis of tracing data by doing
+most of the tedious work that is usually done. For example, if one had a trace
+of the all the times all tasks were woken up and then later the time for those
+tasks when they were scheduled on the CPU, the traceeval tooling would allow
+the program to record the time of the wake up at the wake up event, then on the
+scheduling event the tool could query for the last event that hold where the
+given task woke up, take the delta from the current timestamp, subtract it from
+the wake up timestamp, and then record the delta (wake up latency) into anther
+traceeval instance. At the end of the program, the traceeval instance can be
+iterated over and report the maximum and minimum deltas (with the timestamps
+where they occurred), along with the total of all deltas and the number of
+times it work up.
+
+The *traceeval_init()* function will take a static array of _keys_ to use as
+how to query for the elements in the traceeval. It also takes an option static
+array of _vals_ that are associated to each of the elements in the traceveal.
+The _keys_ are mandatory as they are used to find and differentiate the
+elements in the traceeval.
+
+Both the _keys_ and the _vals_ are defined by the *struct traceeval_data*
+structure.
+
+[verse]
+--
+struct traceeval_type {
+ char pass:[*]name;
+ enum traceeval_data_type type;
+ size_t flags;
+ size_t index;
+ size_t id;
+ traceeval_data_release_fn release;
+ traceeval_data_cmp_fn cmp;
+ traceeval_data_copy_fn copy;
+ traceeval_data_hash_fn hash;
+};
+--
+
+The _name_ is the name of the field. For example it could be "PID" or "Timestamp".
+
+The _type_ is an enum that defines what the content of this field will be. See *TYPE ENUMS* below.
+
+The _flags_ defines characteristics of the field. See *FLAGS* below.
+
+The _index_ is the index into the array passed to *traceeval_init()*. The user does not need
+to updated this. This will be updated by *traceeval_init()*, so the array passed in must not be
+a constant. The index is updated so that the type can be used by other functions where a lookup
+into the internal array can be quicker than searching for matching _name_s.
+
+The _id_ is free for the application to define.
+
+The next fields are function pointers:
+
+The _release_() function is called when the data is being replaced or removed from the trace_eval.
+This will be called by any type, but it comes in handy with the _POINTER_ type, if the pointer
+needs to clean up anything allocated before being inserted.
+
+The _cmp_() function pointer is required for the _POINTER_ type, but also may be used
+to override any of the other types. It is used for sorting of the data in the traceeval for
+iterating over the elements.
+
+The _copy_() function pointer is required for the _POINTER_ type, but also may be used
+to override any of the other types. This is used to copy a field when updating an existing
+element.
+
+The _hash_() function pointer is required for the _POINTER_ key type, but may also be used
+to override the default of other types. The _hash_() function is used to hash the field
+of the element for look ups.
+
+*TYPE ENUMS*:
+
+[verse]
+--
+enum traceeval_data_type {
+ TRACEEVAL_TYPE_NONE,
+ TRACEEVAL_TYPE_NUMBER_8,
+ TRACEEVAL_TYPE_NUMBER_16,
+ TRACEEVAL_TYPE_NUMBER_32,
+ TRACEEVAL_TYPE_NUMBER_64,
+ TRACEEVAL_TYPE_NUMBER,
+ TRACEEVAL_TYPE_POINTER,
+ TRACEEVAL_TYPE_STRING,
+};
+--
+
+Where _NONE_ is just a place holder.
+
+_NUMBER_8_ means the type is just 8 bits in size (for example, *char*).
+
+_NUMBER_16_ means the type is 16 bits in size (*short*).
+
+_NUMBER_32_ means the type is 32 bits in size (*int*)
+
+_NUMBER_64_ means the type is 64 bits in size (*unsigned long long*).
+
+_NUMBER_ means the type is of the natural word length (*size_t*).
+
+_POINTER_ means that the type points to something.
+
+_STRING_ meants that the type is a character array with an nul ('\0') terminating byte.
+
+
+*FLAGS*:
+
+[verse]
+--
+enum traceeval_flags {
+ TRACEEVAL_FL_KEY = (1 << 0),
+ TRACEEVAL_FL_VALUE = (1 << 1),
+ TRACEEVAL_FL_SIGNED = (1 << 2),
+ TRACEEVAL_FL_TIMESTAMP = (1 << 3),
+ TRACEEVAL_FL_STAT = (1 << 4),
+};
+--
+
+The _KEY_ and _VALUE_ flags will be set by *traceeval_init()* for the types that associate
+to _keys_ or _vals_, and does not need to be set by the user.
+
+The _SIGNED_ flag is for the user to denote that the type is signed. This is useful for
+the compare operations in the sorts.
+
+The _TIMESTAMP_ flag denotes that the field is a timestamp. These have some meaning in
+which the statitics will record this field if defined. Note the TIMESTAMP and STAT flags
+are mutually exclusive. The _TIMESTAMP_ flag is ignored for _KEYS_.
+
+The _STAT_ flag denotes that the field should have its statistics recorded (maximum, minimum, etc).
+This is only valid for one of the _NUMBER_pass:[*]_ types.
+
+
+As the *traceeval_init()* requires that the passed in _keys_ and _vals_ must be a static
+array, if an dynamic array needs to be used, then *traceeval_init_size()* can be used.
+This function takes in two new parameters, _nr_keys_ and _nr_vals_. These must mach the
+size of the _keys_ and _vals_ arrays respectively. The reason that the *traceeval_init()*
+requires static size arrays is because it is really just a macro that calles
+*traceeval_init_size()*.
+
+[verse]
+--
+#define TRACEEVAL_ARRAY_SIZE(data) (sizeof(data) / sizeof(data[0]))
+#define traceeval_init(keys, vals) \
+ traceeval_init_size(keys, vals, \
+ TRACEEVAL_ARRAY_SIZE(keys), \
+ (void *)vals == NULL ? 0 : TRACEEVAL_ARRAY_SIZE(vals))
+--
+
+The *traceeval_release()* function releases and frees all the resources of
+a traceeval returned by *traceeval_init()* and *traceeval_init_size()*.
+
+RETURN VALUE
+------------
+The *traceeval_init()* and *traceeval_init_size()* both return a descriptor
+to the traceeval or NULL on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <signal.h>
+#include <errno.h>
+#include <tracefs.h>
+#include <traceeval.h>
+
+static bool done;
+
+static void pdie(const char *str)
+{
+ perror(str);
+ exit(-1);
+
+}
+
+struct traceeval_type sched_keys[] = {
+ {
+ .type = TRACEEVAL_TYPE_STRING,
+ .name = "COMM",
+ },
+ {
+ .type = TRACEEVAL_TYPE_NUMBER,
+ .name = "PID",
+ },
+ {
+ .type = TRACEEVAL_TYPE_NUMBER,
+ .name = "State",
+ },
+};
+
+
+struct traceeval_type sched_vals[] = {
+ {
+ .type = TRACEEVAL_TYPE_NUMBER_64,
+ .name = "Hitcount",
+ },
+};
+
+static int sched_callback(struct tep_event *event, struct tep_record *record,
+ int cpu, void *data)
+{
+ static struct tep_format_field *pid_field;
+ static struct tep_format_field *comm_field;
+ static struct tep_format_field *state_field;
+ const struct traceeval_data *results;
+ struct traceeval_data keys[3];
+ struct traceeval_data vals[1];
+ struct traceeval *teval = data;
+ unsigned long long hits = 0;
+ unsigned long long val;
+ char *comm;
+ int state;
+ int pid;
+ int ret;
+
+ if (!pid_field) {
+ pid_field = tep_find_field(event, "prev_pid");
+ comm_field = tep_find_field(event, "prev_comm");
+ state_field = tep_find_field(event, "prev_state");
+
+ if (!pid_field || !comm_field || !state_field)
+ pdie("Could not find sched_switch required fields");
+ }
+
+ tep_read_number_field(pid_field, record->data, &val);
+ pid = val;
+
+ tep_read_number_field(state_field, record->data, &val);
+ state = val;
+
+ comm = record->data + comm_field->offset;
+
+ TRACEEVAL_SET_STRING(keys[0], comm);
+ TRACEEVAL_SET_NUMBER(keys[1], pid);
+ TRACEEVAL_SET_NUMBER(keys[2], state);
+
+ ret = traceeval_query(teval, keys, &results);
+ if (ret < 0)
+ pdie("Failed to query keys");
+
+ if (ret) {
+ hits = results[0].number_64;
+ traceeval_results_release(teval, results);
+ }
+
+ hits++;
+
+ TRACEEVAL_SET_NUMBER_64(vals[0], hits);
+
+ traceeval_insert(teval, keys, vals);
+
+ return 0;
+}
+
+static char *get_state(int state)
+{
+ switch (state & 7) {
+ case 0:
+ return "R";
+ case 1:
+ return "S";
+ case 2:
+ return "D";
+ }
+ return "X";
+}
+
+static void display_teval(struct traceeval *teval)
+{
+ struct traceeval_iterator *iter = traceeval_iterator_get(teval);
+ const struct traceeval_data *results;
+ const struct traceeval_data *keys;
+
+ /* Sort comms first. */
+ traceeval_iterator_sort(iter, sched_keys[0].name, 0, true);
+ /* Sort pids next */
+ traceeval_iterator_sort(iter, sched_keys[1].name, 1, true);
+ /* Sort state last */
+ traceeval_iterator_sort(iter, sched_keys[2].name, 2, true);
+
+ while (traceeval_iterator_next(iter, &keys) > 0) {
+ traceeval_iterator_query(iter, &results);
+
+ printf("%s [%ld] %s: %lld\n",
+ keys[0].string, keys[1].number, get_state(keys[2].number),
+ results[0].number_64);
+ }
+ traceeval_iterator_put(iter);
+}
+
+static void stop(int s)
+{
+ done = true;
+}
+
+int main (int argc, char **argv)
+{
+ struct tracefs_instance *instance;
+ struct tep_handle *tep;
+ const char *systems[] = { "sched", NULL };
+ struct traceeval *teval;
+ bool finished = false;
+ int ret;
+
+ teval = traceeval_init(sched_keys, sched_vals);
+ if (!teval)
+ pdie("Creating traceeval");
+
+ instance = tracefs_instance_create("sched-counter");
+ if (!instance)
+ pdie("Could not create instance");
+
+ tep = tracefs_local_events_system(NULL, systems);
+ if (!tep)
+ pdie("Could not read system events");
+
+ tracefs_trace_off(instance);
+
+ tracefs_event_enable(instance, "sched", "sched_switch");
+
+ ret = tracefs_follow_event(tep, instance, "sched", "sched_switch",
+ sched_callback, teval);
+ if (ret < 0)
+ pdie("Could not follow sched event");
+
+ signal(SIGTERM, stop);
+ signal(SIGINT, stop);
+
+ printf("Hit Ctrl^C to stop\n");
+
+ tracefs_trace_on(instance);
+ do {
+ if (done) {
+ tracefs_trace_off(instance);
+ finished = true;
+ }
+
+ tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, NULL);
+
+ } while (!finished);
+
+ tracefs_event_disable(instance, NULL, NULL);
+ tracefs_instance_destroy(instance);
+ tracefs_instance_free(instance);
+
+ display_teval(teval);
+
+ return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*traceval.h*
+ Header file to include in order to have access to the library APIs.
+*-ltraceeval*
+ Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtraceeval*(3)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceeval*.
+--
+REPORTING BUGS
+--------------
+Report bugs to <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtraceeval is licensed under MIT.
+
diff --git a/Documentation/libtraceeval.txt b/Documentation/libtraceeval.txt
index d3b97805d6ca..83d69b6eeda1 100644
--- a/Documentation/libtraceeval.txt
+++ b/Documentation/libtraceeval.txt
@@ -11,6 +11,11 @@ SYNOPSIS
--
*#include <traceeval.h>*
+Creating and releasing the traceeval resources:
+ struct traceeval pass:[*]*traceeval_init*(struct traceeval_type pass:[*]_keys_, struct traceeval_type pass:[*]_vals_);
+ struct traceeval pass:[*]*traceeval_init_size*(struct traceeval_type pass:[*]_keys_, struct traceeval_type pass:[*]_vals_,
+ int _nr_keys_, int _nr_vals_);
+ void *traceeval_release*(struct traceeval pass:[*]_teval_);
--
DESCRIPTION
--
2.40.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 3/5] libtraceeval: Add man pages for insertion and removal of elements in a traceeval
2023-10-06 19:55 [PATCH v2 0/5] libtraceeval: Add man pages Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 1/5] libtraceeval: Start creating the documentation " Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 2/5] libtraceeval: Add man pages for creating and releasing the traceeval descriptor Steven Rostedt
@ 2023-10-06 19:55 ` Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 4/5] libtraceeval: Add man pages for the traceeval iterator functions Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 5/5] libtraceeval: Add man pages for traceeval statistics Steven Rostedt
4 siblings, 0 replies; 6+ messages in thread
From: Steven Rostedt @ 2023-10-06 19:55 UTC (permalink / raw)
To: linux-trace-devel; +Cc: Ross Zwisler, Steven Rostedt (Google)
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
Add man pages for:
traceeval_insert()
traceeval_insert_size()
traceeval_remove()
traceeval_remove_size()
traceeval_query()
traceeval_query_size()
traceeval_results_release()
traceeval_count()
Link: https://lore.kernel.org/linux-trace-devel/20231005232450.1311519-4-rostedt@goodmis.org
Cc: Ross Zwisler <zwisler@google.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Documentation/libtraceeval-insert.txt | 390 ++++++++++++++++++++++++++
Documentation/libtraceeval.txt | 25 ++
2 files changed, 415 insertions(+)
create mode 100644 Documentation/libtraceeval-insert.txt
diff --git a/Documentation/libtraceeval-insert.txt b/Documentation/libtraceeval-insert.txt
new file mode 100644
index 000000000000..138c3d2fd0b8
--- /dev/null
+++ b/Documentation/libtraceeval-insert.txt
@@ -0,0 +1,390 @@
+libtraceeval(3)
+===============
+
+NAME
+----
+traceeval_insert, traceeval_insert_size, traceeval_remove, traceeval_remove_size,
+traceeval_query, traceeval_query_size, traceeval_results_release, traceeval_count - Insert, remove and query traceeval elements
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <traceeval.h>*
+
+int *traceeval_insert*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[*]_keys_,
+ const struct traceeval_data pass:[*]_vals_);
+
+int *traceeval_insert_size*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[*]_keys_, size_t _nr_keys_,
+ const struct traceeval_data pass:[*]_vals_, size_t _nr_vals_);
+
+int *traceeval_remove*(struct traceeval pass:[*]_teval_, const struct traceeval_data pass:[*]_keys_);
+
+int *traceeval_remove_size*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[*]_keys_, size_t _nr_keys_);
+
+int *traceeval_query*(struct traceeval pass:[*]_teval_, const struct traceeval_data pass:[*]_keys_,
+ const struct traceeval_data pass:[**]_results_);
+
+int *traceeval_query_size*(struct traceeval pass:[*]_teval_, const struct traceeval_data pass:[*]_keys_,
+ size_t _nr_keys_, const struct traceeval_data pass:[**]_results_);
+
+void *traceeval_results_release*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[**]_results_);
+
+size_t *traceeval_count*(struct traceeval pass:[*]_teval_);
+--
+
+DESCRIPTION
+-----------
+These functions deal with inserting, finding and removing elements from the
+traceeval descriptor.
+
+The *traceeval_insert()* will add a new or update a current element into the
+given _teval_. The _keys_ must be a static array that is the same size as the
+key types given to *traceeval_init*(3). The same is true for the _vals_, as
+they must be an array of the same size as the val type array given. Both _keys_
+and _vals_ are of type struct traceeval_data.
+
+[verse]
+--
+struct traceeval_data {
+ enum traceeval_data_type type;
+ union {
+ char *string;
+ const char *cstring;
+ void *pointer;
+ unsigned long number;
+ unsigned long long number_64;
+ unsigned int number_32;
+ unsigned short number_16;
+ unsigned char number_8;
+ };
+};
+--
+
+For each _keys_ and _vals_ their types must match the keys and vals of the
+types passed to *traceeval_init*(3).
+
+The struct traceeval_data then has a union that represent each of the types.
+For example, a data field with a key type defined as TRACEEVAL_TYPE_POINTER,
+must use the _pointer_ union field.
+
+*traceveal_insert()* will make a copy of each of the _keys_ and _vals_ passed
+in. As to pass in a constant string, the field _cstring_ was added to allow
+constants to be passed to the function.
+
+Like *traceeval_init*(3), elements can also be inserted or updated updated with
+dynamically allocatend _keys_ and _vals_ with *traceeval_insert_size()*. This
+takes two more values: _nr_keys_ and _nr_vals_ to denote the size. This is to
+test against the key and val types registered in *traceeval_init*(3) to make
+sure they match. Once again, *traceeval_insert()* is really just a macro
+defined to pass in the array sizes to *traceeval_insert_size()*.
+
+In order to facilitate assgining the struct traceeval_data to the proper types,
+macros should be used.
+
+For initializing data:
+
+[verse]
+--
+*DEFINE_TRACEEVAL_NUMBER*(_data_) - assign a natural word size (size_t).
+*DEFINE_TRACEEVAL_NUMBER_8*(_data_) - assign an 8 bit value (char).
+*DEFINE_TRACEEVAL_NUMBER_16*(_data_) - assign a 16 bit value (short)
+*DEFINE_TRACEEVAL_NUMBER_32*(_data_) - assign a 32 bit value (int)
+*DEFINE_TRACEEVAL_NUMBER_64*(_data_) - assign a 64 bit value (long long).
+*DEFINE_TRACEEVAL_STRING*(_data_) - assign a nul terminated string value.
+*DEFINE_TRACEEVAL_CSTRING*(_data_) - assign a constant nul terminated string value.
+*DEFINE_TRACEEVAL_POINTER*(_data_) - assign a pointer value.
+
+static int foo(const char *str, int val)
+{
+ struct traceeval_data keys[] = {
+ DEFINE_TRACEEVAL_CSTRING( str ),
+ DEFINE_TRACEEVAL_NUMBER( val ),
+ };
+ [..]
+--
+
+There are also macros to set the data later:
+
+[verse]
+--
+TRACEEVAL_SET_NUMBER(_data_, _val_) - assign a natural word size (size_t)
+TRACEEVAL_SET_NUMBER_8(_data_, _val_) - assign an 8 bit value (char).
+TRACEEVAL_SET_NUMBER_16(_data_, _val_) - assign an 16 bit value (short).
+TRACEEVAL_SET_NUMBER_32(_data_, _val_) - assign an 32 bit value (int).
+TRACEEVAL_SET_NUMBER_64(_data_, _val_) - assign an 64 bit value (long long).
+--
+
+
+The *traceeval_query()* will return the element that matches the _keys_. The
+_keys_ must be a static array that is the same size as the key types defined by
+*traceeval_init()*. If an element is found, it will fill in the _results_
+pointer to point to the content of the values for the given element. The
+results must be released with *traceeval_results_release()*.
+
+Similar to *traceeval_init*(3) and *traceeval_insert()*, there's a
+*traceeval_queury_size()* that takes the size of the key array and allows for
+dynamic arrays to be passed to it.
+
+The *traceeval_results_release()* will release any necessary resources that a
+*traceeval_query()* may have added to return the _results_.
+
+The *traceeval_count()* will return the number of elements in the _teval_.
+
+RETURN VALUE
+------------
+The *traceeval_insert()* and *traceeval_insert_size()* return 0 on succes and -1 on error.
+
+The *traceeval_remove()* and *traceeval_remove_size()* returns 1 if the item was found and removed,
+0 if the item was not found, and -1 on an error (like invalid keys).
+
+The *traceeval_query()* and *traceveal_query_size()* return 1 if the item is found that matches
+the _keys_ and _results_ will contain the values of the last values of that time. It will return
+0 if not found, and -1 on error (like invalid keys).
+
+The *traceeval_count()* returns the number of elements currently in the _teval_.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <unistd.h>
+#include <sys/wait.h>
+#include <tracefs.h>
+#include <traceeval.h>
+
+struct data {
+ struct traceeval *teval_wakeup;
+ struct traceeval *teval_sched;
+};
+
+struct traceeval_type wakeup_keys[] = {
+ {
+ .name = "PID",
+ .type = TRACEEVAL_TYPE_NUMBER,
+ }
+};
+
+struct traceeval_type wakeup_vals[] = {
+ {
+ .name = "timestamp",
+ .flags = TRACEEVAL_FL_TIMESTAMP,
+ .type = TRACEEVAL_TYPE_NUMBER_64,
+ }
+};
+
+struct traceeval_type sched_keys[] = {
+ {
+ .name = "COMM",
+ .type = TRACEEVAL_TYPE_STRING,
+ },
+ {
+ .name = "PID",
+ .type = TRACEEVAL_TYPE_NUMBER,
+ }
+};
+
+struct traceeval_type sched_vals[] = {
+ {
+ .name = "timestamp",
+ .flags = TRACEEVAL_FL_TIMESTAMP,
+ .type = TRACEEVAL_TYPE_NUMBER_64,
+ },
+ {
+ .name = "delta",
+ .flags = TRACEEVAL_FL_STAT,
+ .type = TRACEEVAL_TYPE_NUMBER_64,
+ }
+};
+
+static int wakeup_callback(struct tep_event *event, struct tep_record *record, int cpu, void *d)
+{
+ static struct tep_format_field *pid_field;
+ struct data *data = d;
+ unsigned long long val;
+ long pid;
+ struct traceeval_data keys[1];
+ struct traceeval_data vals[1];
+
+ if (!pid_field)
+ pid_field = tep_find_field(event, "pid");
+
+ tep_read_number_field(pid_field, record->data, &val);
+ pid = val;
+
+ TRACEEVAL_SET_NUMBER(keys[0], pid);
+ TRACEEVAL_SET_NUMBER_64(vals[0], record->ts);
+
+ traceeval_insert(data->teval_wakeup, keys, vals);
+
+ return 0;
+}
+
+static int sched_callback(struct tep_event *event, struct tep_record *record, int cpu, void *d)
+{
+ static struct tep_format_field *next_pid_field;
+ static struct tep_format_field *next_comm_field;
+ struct data *data = d;
+ unsigned long long delta;
+ unsigned long long val;
+ long pid;
+ struct traceeval_data wakeup_keys[1];
+ struct traceeval_data keys[2];
+ struct traceeval_data vals[2];
+ const struct traceeval_data *results;
+
+ if (!next_pid_field) {
+ next_pid_field = tep_find_field(event, "next_pid");
+ next_comm_field = tep_find_field(event, "next_comm");
+ }
+
+ tep_read_number_field(next_pid_field, record->data, &val);
+ pid = val;
+
+ TRACEEVAL_SET_NUMBER(wakeup_keys[0], pid);
+
+ if (traceeval_query(data->teval_wakeup, wakeup_keys, &results) <= 0)
+ return 0;
+
+ delta = record->ts - results[0].number_64;
+ traceeval_results_release(data->teval_wakeup, results);
+
+ TRACEEVAL_SET_CSTRING(keys[0], record->data + next_comm_field->offset);
+ TRACEEVAL_SET_NUMBER(keys[1], pid);
+
+ TRACEEVAL_SET_NUMBER_64(vals[0], record->ts);
+ TRACEEVAL_SET_NUMBER_64(vals[1], delta);
+
+ traceeval_insert(data->teval_sched, keys, vals);
+
+ return 0;
+}
+
+static pid_t call_code(struct tracefs_instance *instance, int argc, char **argv)
+{
+ char pid_str[64];
+ pid_t pid;
+
+ pid = fork();
+
+ if (pid)
+ return pid;
+
+ sprintf(pid_str, "%d", getpid());
+
+ tracefs_instance_file_write(instance, "set_event_pid", pid_str);
+ tracefs_trace_on(instance);
+
+ execvp(argv[0], argv);
+ exit(-1);
+}
+
+static void show_latency(struct data *data)
+{
+ struct traceeval_iterator *iter = traceeval_iterator_get(data->teval_sched);
+ const struct traceeval_data *keys;
+
+ printf("\n");
+ while (traceeval_iterator_next(iter, &keys) > 0) {
+ struct traceeval_stat *stat;
+
+ stat = traceeval_iterator_stat(iter, sched_vals[1].name);
+ if (!stat)
+ continue;
+
+ printf("%s-%ld max:%lld min:%lld total:%lld count:%lld\n",
+ keys[0].string, keys[1].number,
+ traceeval_stat_max(stat),
+ traceeval_stat_min(stat),
+ traceeval_stat_total(stat),
+ traceeval_stat_count(stat));
+ }
+ traceeval_iterator_put(iter);
+}
+
+static pid_t wait_pid;
+static bool done;
+
+static void sig(int sig)
+{
+ pid_t ret;
+
+ ret = waitpid(wait_pid, NULL, WNOHANG);
+ if (ret == wait_pid)
+ done = true;
+}
+
+int main (int argc, char **argv)
+{
+ struct tracefs_instance *instance;
+ struct tep_handle *tep;
+ struct data data;
+
+ if (argc < 2) {
+ printf("usage: wakeup_latency exec\n");
+ exit(-1);
+ }
+
+ data.teval_wakeup = traceeval_init(wakeup_keys, wakeup_vals);
+ data.teval_sched = traceeval_init(sched_keys, sched_vals);
+
+ tep = tracefs_local_events(NULL);
+
+ instance = tracefs_instance_create("wakeup_latency");
+ tracefs_trace_off(instance);
+ tracefs_event_enable(instance, "sched", "sched_waking");
+ tracefs_event_enable(instance, "sched", "sched_switch");
+
+ tracefs_follow_event(tep, instance, "sched", "sched_waking", wakeup_callback, &data);
+ tracefs_follow_event(tep, instance, "sched", "sched_switch", sched_callback, &data);
+
+ signal(SIGCHLD, sig);
+
+ wait_pid = call_code(instance, argc - 1, argv + 1);
+
+ do {
+ tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, NULL);
+ } while (!done);
+
+ tracefs_event_disable(instance, NULL, NULL);
+ tracefs_instance_destroy(instance);
+ tracefs_instance_free(instance);
+
+ show_latency(&data);
+
+ return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*traceval.h*
+ Header file to include in order to have access to the library APIs.
+*-ltraceeval*
+ Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtraceeval*(3)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceeval*.
+--
+REPORTING BUGS
+--------------
+Report bugs to <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtraceeval is licensed under MIT.
+
diff --git a/Documentation/libtraceeval.txt b/Documentation/libtraceeval.txt
index 83d69b6eeda1..9f753aaadf12 100644
--- a/Documentation/libtraceeval.txt
+++ b/Documentation/libtraceeval.txt
@@ -16,6 +16,31 @@ Creating and releasing the traceeval resources:
struct traceeval pass:[*]*traceeval_init_size*(struct traceeval_type pass:[*]_keys_, struct traceeval_type pass:[*]_vals_,
int _nr_keys_, int _nr_vals_);
void *traceeval_release*(struct traceeval pass:[*]_teval_);
+
+Inserting and removing elements from the traceeval:
+ int *traceeval_insert*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[*]_keys_,
+ const struct traceeval_data pass:[*]_vals_);
+
+ int *traceeval_insert_size*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[*]_keys_, size_t _nr_keys_,
+ const struct traceeval_data pass:[*]_vals_, size_t _nr_vals_);
+
+ int *traceeval_remove*(struct traceeval pass:[*]_teval_, const struct traceeval_data pass:[*]_keys_);
+
+ int *traceeval_remove_size*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[*]_keys_, size_t _nr_keys_);
+
+ int *traceeval_query*(struct traceeval pass:[*]_teval_, const struct traceeval_data pass:[*]_keys_,
+ const struct traceeval_data pass:[**]_results_);
+
+ int *traceeval_query_size*(struct traceeval pass:[*]_teval_, const struct traceeval_data pass:[*]_keys_,
+ size_t _nr_keys_, const struct traceeval_data pass:[**]_results_);
+
+ void *traceeval_results_release*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[**]_results_);
+
+ size_t *traceeval_count*(struct traceeval pass:[*]_teval_);
--
DESCRIPTION
--
2.40.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 4/5] libtraceeval: Add man pages for the traceeval iterator functions
2023-10-06 19:55 [PATCH v2 0/5] libtraceeval: Add man pages Steven Rostedt
` (2 preceding siblings ...)
2023-10-06 19:55 ` [PATCH v2 3/5] libtraceeval: Add man pages for insertion and removal of elements in a traceeval Steven Rostedt
@ 2023-10-06 19:55 ` Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 5/5] libtraceeval: Add man pages for traceeval statistics Steven Rostedt
4 siblings, 0 replies; 6+ messages in thread
From: Steven Rostedt @ 2023-10-06 19:55 UTC (permalink / raw)
To: linux-trace-devel; +Cc: Ross Zwisler, Steven Rostedt (Google)
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
Add man pages for:
traceeval_iterator_get()
traceeval_iterator_put()
traceeval_iterator_sort()
traceeval_iterator_sort_custom()
traceeval_iterator_next()
traceeval_iterator_query()
traceeval_iterator_results_release()
traceeval_iterator_stat()
traceeval_iterator_remove()
Link: https://lore.kernel.org/linux-trace-devel/20231005232450.1311519-5-rostedt@goodmis.org
Cc: Ross Zwisler <zwisler@google.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Documentation/libtraceeval-iterator.txt | 921 ++++++++++++++++++++++++
Documentation/libtraceeval.txt | 17 +
2 files changed, 938 insertions(+)
create mode 100644 Documentation/libtraceeval-iterator.txt
diff --git a/Documentation/libtraceeval-iterator.txt b/Documentation/libtraceeval-iterator.txt
new file mode 100644
index 000000000000..9c03b5855749
--- /dev/null
+++ b/Documentation/libtraceeval-iterator.txt
@@ -0,0 +1,921 @@
+libtraceeval(3)
+===============
+
+NAME
+----
+traceeval_iterator_get, traceeval_iterator_put, traceeval_iterator_sort, traceeval_iterator_sort_custom,
+traceeval_iterator_next, traceeval_iterator_query, traceeval_iterator_results_release, traceeval_iterator_stat,
+traceeval_iterator_remove - Operations to iterate over the elements of a traceeval
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <traceeval.h>*
+
+struct traceeval_iterator pass:[*]*traceeval_iterator_get*(struct traceeval pass:[*]_teval_);
+void *traceeval_iterator_put*(struct traceeval_iterator pass:[*]_iter_);
+int *traceeval_iterator_sort*(struct traceeval_iterator pass:[*]_iter_, const char pass:[*]_sort_field_,
+ int _level_, bool _ascending_);
+int *traceeval_iterator_sort_custom*(struct traceeval_iterator pass:[*]_iter_,
+ traceeval_cmp_fn _sort_fn_, void pass:[*]_data_);
+int *traceeval_iterator_next*(struct traceeval_iterator pass:[*]_iter_,
+ const struct traceeval_data pass:[**]_keys_);
+int *traceeval_iterator_query*(struct traceeval_iterator pass:[*]_iter_,
+ const struct traceeval_data pass:[**]_results_);
+void *traceeval_iterator_results_release*(struct traceeval_iterator pass:[*]_iter_,
+ const struct traceeval_data pass:[*]_results_);
+struct traceeval_stat pass:[*]*traceeval_iterator_stat*(struct traceeval_iterator pass:[*]_iter_,
+ struct traceeval_type pass:[*]_type_);
+int *traceeval_iterator_remove*(struct traceeval_iterator pass:[*]_iter_);
+--
+
+DESCRIPTION
+-----------
+The traceeval utility facilitates the collection of data as a tool iterates over
+trace events. The traceeval_iterator is a means to read the data that was collected.
+
+The *traceeval_iterator_get()* takes a _teval_ descriptor of the traceeval that is
+to be extracted and returns a struct traceeval_iterator descriptor that can be
+used to iterate over the events of the traceeval.
+
+When the iterator is no longer required, *traceeval_iterator_put()* is used to
+release any of the allocated memory of the _iter_ traceeval_iterator.
+
+Before iterating, the data may be sorted using *traceeval_iterator_sort()*.
+This will sort how the elements will be iterated over via the given _iter_ iterator.
+The _sort_field_ is a string that matches the .name element of a struct traceeval_type
+for either keys or vals that were passed into the *traceeval_init*(3) for the given
+traceeval passed to *traceeval_iterator_get()*. The _level_ is the sorting priority
+of this field, where 0 is the highest priority (sort this field first), 1 is the
+next priority (sort this field for elements that have matching fields of level 0),
+2 is the next priority and so on. This function should be called for each level.
+If a level is skipped, it may cause an error with *traceeval_iterator_next()*.
+
+If a custom sort needs to be done (one that is not simply following the fields)
+then *traceeval_iterator_sort_custom()* may be used. This takes a parameter
+_sort_fn_ that is a function of type traceeval_cmp_fn to sort. The _data_ parameter
+is used to pass data to the _sort_fn_.
+
+[verse]
+--
+typedef int (*traceeval_cmp_fn)(struct traceeval *teval,
+ const struct traceeval_data *Akeys,
+ const struct traceeval_data *Avals,
+ const struct traceeval_data *Bkeys,
+ const struct traceeval_data *Bvals,
+ void *data);
+--
+
+The compare function passed to *traceeval_iterator_sort_custom()* takes a _teval_ that
+holds the traceeval passed to *traceeval_iterator_get()*. Then the _Akeys_ and _Avals_ of one element
+and the _Bkeys_ and _Bvals_ of another element to use for comapring the two. The _data_ is a pointer
+to the data that was passed into *traceeval_iterator_sort_custom()*. This function should return
+less than zero if Akeys and Avals is less than Bkeys and Bvals, 0 if they are equal, and greater than zero
+if greater than.
+
+The *traceeval_iterator_next()* is used to do the iteration over the traceeval passed
+to *traceeval_iterator_get()* in the sorted order defined with *traceeval_iterator_sort()*.
+If the _iter_ was not sorted, the order will be somewhat random, but all the elements
+will still be convered just once. For each iteration, _keys_ will be assigned the
+struct traceeval_data keys of the next element. If an element is found, this will return
+1.
+
+Inside the *traceeval_iterator_next()* loop, the values of the element returned by
+the keys can be quickly retrieved with *traceeval_iterator_query()*. This will
+place the values in _results_, which when finished with should call *traceeval_iterator_results_release()*
+on.
+
+
+The *traceeval_iterator_stat()* will quickly return the stats of one of the current
+element's fields (if it as desginated as a stat field). See *traceeval_stat*(3).
+
+The *traceeval_iterator_remove()* is a safe way to remove an element from the traceeval
+that was passed to *traceeval_iterator_get()*. It will remove the current element
+returned by *traceeveal_iterator_next()* from the traceeval of _iter_.
+
+RETURN VALUE
+------------
+The *traceeval_iterator_get()* returns a traceeval_iterator descriptor that will iterate
+over the given _teval_ on success, and NULL on error.
+
+The *traceeval_iterator_sort()* and traceeval_iterator_sort_custom()* return 0 on success and -1 or error.
+
+The *traceeval_iterator_next()* returns 1 when it reads a new element from the traceeval and places the element's
+keys into _keys_. It returns 0 when there's no more elements to read and -1 on error.
+
+The *traceeval_iterator_query()* returns 1 if it successfully reads the current element from the
+*traceeval_iterator_next()* and places the values in _results_. It returns 0 if there are no more elements,
+and -1 on error.
+
+The *traceeval_iterator_stat()* returns a descriptor for the current element's given _field_ on success and
+NULL if there are no current elements or the _field_ is not a valid stat type.
+
+The *traceeval_iterator_remove()* returns 1 if the current element was successfully removed, or 0
+if there was no element (called before *traceeval_iterator_next()*).
+
+EXAMPLE
+-------
+This example takes a trace.dat file created by *trace-cmd*(1) with the following command:
+
+[verse]
+--
+ trace-cmd record -e sched_switch
+--
+
+And will report the times that all the tasks were preempted, running, sleeping or blocked and
+break it up between tasks with the same name. It also shows how long the CPUs were running
+and idle.
+
+[source,c]
+--
+#include <trace-cmd.h>
+#include <traceeval.h>
+
+static struct traceeval_type cpu_keys[] = {
+ {
+ .type = TRACEEVAL_TYPE_NUMBER,
+ .name = "CPU",
+ },
+ {
+ .type = TRACEEVAL_TYPE_NUMBER,
+ .name = "Schedule state",
+ },
+};
+
+static struct traceeval_type process_keys[] = {
+ {
+ .type = TRACEEVAL_TYPE_STRING,
+ .name = "COMM"
+ },
+ {
+ .type = TRACEEVAL_TYPE_NUMBER,
+ .name = "Schedule state"
+ },
+};
+
+static struct traceeval_type process_data_vals[] = {
+ {
+ .type = TRACEEVAL_TYPE_POINTER,
+ .name = "data",
+ },
+};
+
+static struct traceeval_type thread_keys[] = {
+ {
+ .type = TRACEEVAL_TYPE_NUMBER,
+ .name = "TID",
+ },
+ {
+ .type = TRACEEVAL_TYPE_NUMBER,
+ .name = "Schedule state",
+ },
+};
+
+static struct traceeval_type timestamp_vals[] = {
+ {
+ .type = TRACEEVAL_TYPE_NUMBER_64,
+ .name = "Timestamp",
+ .flags = TRACEEVAL_FL_TIMESTAMP,
+ },
+};
+
+static struct traceeval_type delta_vals[] = {
+ {
+ .type = TRACEEVAL_TYPE_NUMBER_64,
+ .name = "delta",
+ .flags = TRACEEVAL_FL_STAT,
+ },
+};
+
+enum sched_state {
+ RUNNING,
+ BLOCKED,
+ PREEMPT,
+ SLEEP,
+ IDLE,
+ OTHER
+};
+
+struct teval_pair {
+ struct traceeval *start;
+ struct traceeval *stop;
+};
+
+struct process_data {
+ struct teval_pair teval_cpus;
+ struct teval_pair teval_threads;
+ char *comm;
+ int state;
+};
+
+struct task_data {
+ struct teval_pair teval_cpus;
+ struct teval_pair teval_processes;
+ struct traceeval *teval_processes_data;
+ char *comm;
+};
+
+enum command {
+ START,
+ STOP
+};
+
+static void update_process(struct task_data *tdata, const char *comm,
+ enum sched_state state, enum command cmd,
+ unsigned long long ts)
+{
+ struct traceeval_data keys[] = {
+ DEFINE_TRACEEVAL_CSTRING( comm ),
+ DEFINE_TRACEEVAL_NUMBER( state ),
+ };
+ struct traceeval_data vals[] = {
+ DEFINE_TRACEEVAL_NUMBER_64( ts ),
+ };
+ struct traceeval_data new_vals[1] = { };
+ const struct traceeval_data *results;
+ int ret;
+
+ switch (cmd) {
+ case START:
+ ret = traceeval_insert(tdata->teval_processes.start, keys, vals);
+ return;
+ case STOP:
+ ret = traceeval_query(tdata->teval_processes.start, keys, &results);
+ if (ret == 0)
+ return;
+ if (!results[0].number_64)
+ break;
+
+ TRACEEVAL_SET_NUMBER_64(new_vals[0], ts - results[0].number_64);
+
+ ret = traceeval_insert(tdata->teval_processes.stop, keys, new_vals);
+
+ /* Reset the start */
+ TRACEEVAL_SET_NUMBER_64(new_vals[0], 0);
+
+ ret = traceeval_insert(tdata->teval_processes.start, keys, new_vals);
+ break;
+ }
+ traceeval_results_release(tdata->teval_processes.start, results);
+}
+
+static void start_process(struct task_data *tdata, const char *comm,
+ enum sched_state state, unsigned long long ts)
+{
+ update_process(tdata, comm, state, START, ts);
+}
+
+static void stop_process(struct task_data *tdata, const char *comm,
+ enum sched_state state, unsigned long long ts)
+{
+ update_process(tdata, comm, state, STOP, ts);
+}
+
+static struct process_data *
+get_process_data(struct task_data *tdata, const char *comm)
+{
+ struct traceeval_data keys[] = {
+ DEFINE_TRACEEVAL_CSTRING( comm ),
+ DEFINE_TRACEEVAL_NUMBER( RUNNING ),
+ };
+ const struct traceeval_data *results;
+ void *data;
+ int ret;
+
+ ret = traceeval_query(tdata->teval_processes_data, keys, &results);
+ if (ret == 0)
+ return NULL;
+
+ data = results[0].pointer;
+ traceeval_results_release(tdata->teval_processes_data, results);
+ return data;
+}
+
+void set_process_data(struct task_data *tdata, const char *comm, void *data)
+{
+ struct traceeval_data keys[] = {
+ DEFINE_TRACEEVAL_CSTRING( comm ),
+ DEFINE_TRACEEVAL_NUMBER( RUNNING ),
+ };
+ struct traceeval_data new_vals[1] = { };
+ const struct traceeval_data *results;
+ int ret;
+
+ ret = traceeval_query(tdata->teval_processes_data, keys, &results);
+ if (ret > 0)
+ goto out; /* It already exists ? */
+
+ TRACEEVAL_SET_POINTER(new_vals[0], data);
+ ret = traceeval_insert(tdata->teval_processes_data, keys, new_vals);
+
+ out:
+ traceeval_results_release(tdata->teval_processes_data, results);
+}
+
+static void update_cpu(struct teval_pair *teval_pair, int cpu,
+ enum sched_state state, enum command cmd,
+ unsigned long long ts)
+{
+ const struct traceeval_data *results;
+ struct traceeval_data keys[] = {
+ DEFINE_TRACEEVAL_NUMBER( cpu ),
+ DEFINE_TRACEEVAL_NUMBER( state ),
+ };
+ struct traceeval_data vals[] = {
+ DEFINE_TRACEEVAL_NUMBER_64( ts ),
+ };
+ struct traceeval_data new_vals[1] = { };
+ int ret;
+
+ switch (cmd) {
+ case START:
+ /* Only set if the timestamp is zero (or doesn't exist) */
+ ret = traceeval_query(teval_pair->start, keys, &results);
+ if (ret > 0) {
+ if (results[0].number_64)
+ break;
+ }
+ ret = traceeval_insert(teval_pair->start, keys, vals);
+ break;
+ case STOP:
+ ret = traceeval_query(teval_pair->start, keys, &results);
+ if (ret == 0)
+ return;
+
+ if (!results[0].number_64)
+ break;
+
+ TRACEEVAL_SET_NUMBER_64(new_vals[0], ts - results[0].number_64);
+
+ ret = traceeval_insert(teval_pair->stop, keys, new_vals);
+
+ /* Reset the start */
+ TRACEEVAL_SET_NUMBER_64(new_vals[0], 0);
+ ret = traceeval_insert(teval_pair->start, keys, new_vals);
+
+ break;
+ default:
+ return;
+ }
+ traceeval_results_release(teval_pair->start, results);
+}
+
+static void start_cpu(struct teval_pair *teval_pair, int cpu,
+ enum sched_state state, unsigned long long ts)
+{
+ update_cpu(teval_pair, cpu, state, START, ts);
+}
+
+static void stop_cpu(struct teval_pair *teval_pair, int cpu,
+ enum sched_state state, unsigned long long ts)
+{
+ update_cpu(teval_pair, cpu, state, STOP, ts);
+}
+
+static void update_thread(struct process_data *pdata, int tid,
+ enum sched_state state, enum command cmd,
+ unsigned long long ts)
+{
+ const struct traceeval_data *results;
+ struct traceeval_data keys[] = {
+ DEFINE_TRACEEVAL_NUMBER( tid ),
+ DEFINE_TRACEEVAL_NUMBER( state ),
+ };
+ struct traceeval_data vals[] = {
+ DEFINE_TRACEEVAL_NUMBER_64( ts ),
+ };
+ struct traceeval_data new_vals[1] = { };
+ int ret;
+
+ switch (cmd) {
+ case START:
+ ret = traceeval_insert(pdata->teval_threads.start, keys, vals);
+ return;
+ case STOP:
+ ret = traceeval_query(pdata->teval_threads.start, keys, &results);
+ if (ret == 0)
+ return;
+
+ TRACEEVAL_SET_NUMBER_64(new_vals[0], ts - results[0].number_64);
+
+ ret = traceeval_insert(pdata->teval_threads.stop, keys, new_vals);
+ traceeval_results_release(pdata->teval_threads.start, results);
+ return;
+ }
+}
+
+static void start_thread(struct process_data *pdata, int tid,
+ enum sched_state state, unsigned long long ts)
+{
+ update_thread(pdata, tid, state, START, ts);
+}
+
+static void stop_thread(struct process_data *pdata, int tid,
+ enum sched_state state, unsigned long long ts)
+{
+ update_thread(pdata, tid, state, STOP, ts);
+}
+
+static struct tep_format_field *get_field(struct tep_event *event, const char *name)
+{
+ static struct tep_format_field *field;
+
+ field = tep_find_field(event, name);
+ if (!field) {
+ fprintf(stderr, "Could not find field %s for %s",
+ name, event->name);
+ exit(-1);
+ }
+
+ return field;
+}
+
+static void init_process_data(struct process_data *pdata)
+{
+
+ pdata->teval_cpus.start = traceeval_init(cpu_keys, timestamp_vals);
+ pdata->teval_cpus.stop = traceeval_init(cpu_keys, delta_vals);
+
+ pdata->teval_threads.start = traceeval_init(thread_keys, timestamp_vals);
+
+ pdata->teval_threads.stop = traceeval_init(thread_keys, delta_vals);
+}
+
+static struct process_data *alloc_pdata(struct task_data *tdata, const char *comm)
+{
+ struct process_data *pdata;
+
+ pdata = calloc(1, sizeof(*pdata));
+ init_process_data(pdata);
+ set_process_data(tdata, comm, pdata);
+
+ return pdata;
+}
+
+static void sched_out(struct task_data *tdata, const char *comm,
+ struct tep_event *event,
+ struct tep_record *record, struct tep_format_field *prev_pid,
+ struct tep_format_field *prev_state)
+{
+ struct process_data *pdata;
+ unsigned long long val;
+ int pid;
+
+ tep_read_number_field(prev_pid, record->data, &val);
+
+ /* Ignore the idle task */
+ pid = val;
+ if (!pid) {
+ /* Record the runtime for the process CPUs */
+ stop_cpu(&tdata->teval_cpus, record->cpu, IDLE, record->ts);
+ return;
+ }
+
+ /* The process is scheduling out. Stop the run time. */
+ update_process(tdata, comm, RUNNING, STOP, record->ts);
+
+ /* Get the process data from the process running state */
+ pdata = get_process_data(tdata, comm);
+ if (!pdata)
+ pdata = alloc_pdata(tdata, comm);
+
+ tep_read_number_field(prev_state, record->data, &val);
+ val &= 3;
+ /*
+ * Save the state the process is exiting with. Will need this
+ * when scheduled back in.
+ */
+ if (!val)
+ pdata->state = PREEMPT;
+ else if (val & 1)
+ pdata->state = SLEEP;
+ else if (val & 2)
+ pdata->state = BLOCKED;
+
+ /* Record the state timings for the process */
+ start_process(tdata, comm, pdata->state, record->ts);
+
+ /* Record the state timings for the individual thread */
+ stop_thread(pdata, pid, RUNNING, record->ts);
+
+ /* Record the state timings for the individual thread */
+ start_thread(pdata, pid, pdata->state, record->ts);
+
+ /* Record the runtime for the process CPUs */
+ stop_cpu(&pdata->teval_cpus, record->cpu, RUNNING, record->ts);
+
+ /* Record the runtime for the all CPUs */
+ stop_cpu(&tdata->teval_cpus, record->cpu, RUNNING, record->ts);
+}
+
+static void sched_in(struct task_data *tdata, const char *comm,
+ struct tep_event *event,
+ struct tep_record *record, struct tep_format_field *next_pid)
+{
+ struct process_data *pdata;
+ unsigned long long val;
+ bool is_new = false;
+ int pid;
+
+ tep_read_number_field(next_pid, record->data, &val);
+ pid = val;
+
+ /* Ignore the idle task */
+ if (!pid) {
+ /* Record the runtime for the process CPUs */
+ start_cpu(&tdata->teval_cpus, record->cpu, IDLE, record->ts);
+ return;
+ }
+
+ /* Start recording the running time of this process */
+ start_process(tdata, comm, RUNNING, record->ts);
+
+ pdata = get_process_data(tdata, comm);
+
+ /* Start recording the running time of process CPUs */
+ start_cpu(&tdata->teval_cpus, record->cpu, RUNNING, record->ts);
+
+ /* If there was no pdata, then this process did not go through sched out */
+ if (!pdata) {
+ pdata = alloc_pdata(tdata, comm);
+ is_new = true;
+ }
+
+ /* Record the state timings for the individual thread */
+ start_thread(pdata, pid, RUNNING, record->ts);
+
+ /* Start recording the running time of process CPUs */
+ start_cpu(&pdata->teval_cpus, record->cpu, RUNNING, record->ts);
+
+ /* If it was just created, there's nothing to stop */
+ if (is_new)
+ return;
+
+ /* Stop recording the thread time for its scheduled out state */
+ stop_thread(pdata, val, pdata->state, record->ts);
+
+ /* Stop recording the process time for its scheduled out state */
+ stop_process(tdata, comm, pdata->state, record->ts);
+}
+
+static int switch_func(struct tracecmd_input *handle, struct tep_event *event,
+ struct tep_record *record, int cpu, void *data)
+{
+ static struct tep_format_field *prev_comm;
+ static struct tep_format_field *prev_pid;
+ static struct tep_format_field *prev_state;
+ static struct tep_format_field *next_comm;
+ static struct tep_format_field *next_pid;
+ struct task_data *tdata = data;
+ const char *comm;
+
+ if (!next_comm) {
+ prev_comm = get_field(event, "prev_comm");
+ prev_pid = get_field(event, "prev_pid");
+ prev_state = get_field(event, "prev_state");
+
+ next_comm = get_field(event, "next_comm");
+ next_pid = get_field(event, "next_pid");
+ }
+
+ comm = record->data + prev_comm->offset;
+ if (!tdata->comm || strcmp(comm, tdata->comm) == 0)
+ sched_out(tdata, comm, event, record, prev_pid, prev_state);
+
+ comm = record->data + next_comm->offset;
+ if (!tdata->comm || strcmp(comm, tdata->comm) == 0)
+ sched_in(tdata, comm, event, record, next_pid);
+
+ return 0;
+}
+
+static void print_microseconds(int idx, unsigned long long nsecs)
+{
+ unsigned long long usecs;
+
+ usecs = nsecs / 1000;
+ if (!nsecs || usecs)
+ printf("%*lld\n", idx, usecs);
+ else
+ printf("%*d.%03lld\n", idx, 0, nsecs);
+}
+
+/*
+ * Sort all the processes by the RUNNING state.
+ * If A and B have the same COMM, then sort by state.
+ * else
+ * Find the RUNNNIG state for A and B
+ * If the RUNNING state does not exist, it's considered -1
+ * If RUNNING is equal, then sort by COMM.
+ */
+static int compare_pdata(struct traceeval *teval_data,
+ const struct traceeval_data *Akeys,
+ const struct traceeval_data *Avals,
+ const struct traceeval_data *Bkeys,
+ const struct traceeval_data *Bvals,
+ void *data)
+{
+ struct traceeval *teval = data; /* The deltas are here */
+ struct traceeval_data keysA[] = {
+ DEFINE_TRACEEVAL_CSTRING( Akeys[0].cstring ),
+ DEFINE_TRACEEVAL_NUMBER( RUNNING ), };
+ struct traceeval_data keysB[] = {
+ DEFINE_TRACEEVAL_CSTRING( Bkeys[0].cstring ),
+ DEFINE_TRACEEVAL_NUMBER( RUNNING ), };
+ struct traceeval_stat *statA;
+ struct traceeval_stat *statB;
+ unsigned long long totalA = -1;
+ unsigned long long totalB = -1;
+
+ /* First check if we are on the same task */
+ if (strcmp(Akeys[0].cstring, Bkeys[0].cstring) == 0) {
+ /* Sort decending */
+ if (Bkeys[1].number > Akeys[1].number)
+ return -1;
+ return Bkeys[1].number != Akeys[1].number;
+ }
+
+ /* Get the RUNNING values for both processes */
+ statA = traceeval_stat(teval, keysA, delta_vals[0].name);
+ if (statA)
+ totalA = traceeval_stat_total(statA);
+
+ statB = traceeval_stat(teval, keysB, delta_vals[0].name);
+ if (statB)
+ totalB = traceeval_stat_total(statB);
+
+ if (totalB < totalA)
+ return -1;
+ if (totalB > totalA)
+ return 1;
+
+ return strcmp(Bkeys[0].cstring, Akeys[0].cstring);
+}
+
+static void display_cpus(struct traceeval *teval)
+{
+ struct traceeval_iterator *iter = traceeval_iterator_get(teval);
+ const struct traceeval_data *keys;
+ struct traceeval_stat *stat;
+ int last_cpu = -1;
+
+ printf("\n");
+
+ traceeval_iterator_sort(iter, cpu_keys[0].name, 0, true);
+ traceeval_iterator_sort(iter, cpu_keys[1].name, 1, true);
+
+ while (traceeval_iterator_next(iter, &keys) > 0) {
+ int state = keys[1].number;
+ int cpu = keys[0].number;
+
+ stat = traceeval_iterator_stat(iter, delta_vals[0].name);
+ if (!stat)
+ continue; // die?
+
+ if (last_cpu != cpu)
+ printf(" CPU [%d]:\n", cpu);
+
+ switch (state) {
+ case RUNNING:
+ printf(" Running: ");
+ break;
+ case IDLE:
+ printf(" Idle: ");
+ break;
+ case BLOCKED:
+ case PREEMPT:
+ case SLEEP:
+ case OTHER:
+ printf(" \?\?(%d): ", state);
+ break;
+ }
+ printf(" time (us):");
+ print_microseconds(12, traceeval_stat_total(stat));
+
+ last_cpu = cpu;
+ }
+ traceeval_iterator_put(iter);
+}
+
+static void display_state_times(int state, unsigned long long total)
+{
+ switch (state) {
+ case RUNNING:
+ printf(" Total run time (us):");
+ print_microseconds(14, total);
+ break;
+ case BLOCKED:
+ printf(" Total blocked time (us):");
+ print_microseconds(10, total);
+ break;
+ case PREEMPT:
+ printf(" Total preempt time (us):");
+ print_microseconds(10, total);
+ break;
+ case SLEEP:
+ printf(" Total sleep time (us):");
+ print_microseconds(12, total);
+ }
+}
+
+static void display_threads(struct traceeval *teval)
+{
+ struct traceeval_iterator *iter = traceeval_iterator_get(teval);
+ const struct traceeval_data *keys;
+ struct traceeval_stat *stat;
+ int last_tid = -1;
+
+ traceeval_iterator_sort(iter, thread_keys[0].name, 0, true);
+ traceeval_iterator_sort(iter, thread_keys[1].name, 1, true);
+
+ while (traceeval_iterator_next(iter, &keys) > 0) {
+ int state = keys[1].number;
+ int tid = keys[0].number;
+
+ stat = traceeval_iterator_stat(iter, delta_vals[0].name);
+
+ if (last_tid != keys[0].number)
+ printf("\n thread id: %d\n", tid);
+
+ last_tid = tid;
+
+ display_state_times(state, traceeval_stat_total(stat));
+ }
+ traceeval_iterator_put(iter);
+}
+
+static void display_process(struct process_data *pdata)
+{
+ display_threads(pdata->teval_threads.stop);
+ display_cpus(pdata->teval_cpus.stop);
+ printf("\n");
+}
+
+static void display_process_stats(struct traceeval *teval,
+ struct process_data *pdata, const char *comm)
+{
+ struct traceeval_stat *stat;
+ unsigned long long delta;
+ struct traceeval_data keys[] = {
+ DEFINE_TRACEEVAL_CSTRING( comm ),
+ DEFINE_TRACEEVAL_NUMBER( RUNNING ),
+ };
+
+ for (int i = 0; i < OTHER; i++) {
+ TRACEEVAL_SET_NUMBER(keys[1], i);
+
+ delta = 0;
+ stat = traceeval_stat(teval, keys, delta_vals[0].name);
+ if (stat)
+ delta = traceeval_stat_total(stat);
+ display_state_times(i, delta);
+ }
+}
+
+static void display_processes(struct traceeval *teval,
+ struct traceeval *teval_data)
+{
+ struct traceeval_iterator *iter = traceeval_iterator_get(teval_data);
+ const struct traceeval_data *keys;
+
+ traceeval_iterator_sort_custom(iter, compare_pdata, teval);
+
+ while (traceeval_iterator_next(iter, &keys) > 0) {
+ const struct traceeval_data *results;
+ struct process_data *pdata = NULL;
+ const char *comm = keys[0].cstring;
+
+ traceeval_iterator_query(iter, &results);
+ pdata = results[0].pointer;
+ traceeval_results_release(teval_data, results);
+
+ if (!pdata)
+ continue;
+
+ printf("Task: %s\n", comm);
+
+ display_process_stats(teval, pdata, comm);
+ if (pdata)
+ display_process(pdata);
+ }
+ traceeval_iterator_put(iter);
+}
+
+static void display(struct task_data *tdata)
+{
+ struct traceeval *teval = tdata->teval_cpus.stop;
+ struct traceeval_iterator *iter = traceeval_iterator_get(teval);
+ const struct traceeval_data *keys;
+ struct traceeval_stat *stat;
+ unsigned long long total_time = 0;
+ unsigned long long idle_time = 0;
+
+ if (tdata->comm) {
+ return display_processes(tdata->teval_processes.stop,
+ tdata->teval_processes_data);
+ }
+
+ printf("Total:\n");
+
+ while (traceeval_iterator_next(iter, &keys) > 0) {
+ int state = keys[1].number;
+
+ stat = traceeval_iterator_stat(iter, delta_vals[0].name);
+ if (!stat)
+ continue;
+
+ switch (state) {
+ case RUNNING:
+ total_time += traceeval_stat_total(stat);
+ break;
+ case IDLE:
+ idle_time += traceeval_stat_total(stat);
+ break;
+ default:
+ return;
+ }
+ }
+ traceeval_iterator_put(iter);
+
+ printf(" Total run time (us):");
+ print_microseconds(16, total_time);
+ printf(" Total idle time (us):");
+ print_microseconds(16, idle_time);
+
+ display_cpus(tdata->teval_cpus.stop);
+
+ printf("\n");
+ display_processes(tdata->teval_processes.stop, tdata->teval_processes_data);
+}
+
+static void free_tdata(struct task_data *tdata)
+{
+}
+
+int main (int argc, char **argv)
+{
+ struct tracecmd_input *handle;
+ struct task_data data;
+
+ memset(&data, 0, sizeof(data));
+
+ if (argc < 2) {
+ printf("Pass in trace.dat file\n");
+ exit(-1);
+ }
+
+ handle = tracecmd_open(argv[1], TRACECMD_FL_LOAD_NO_PLUGINS);
+ if (!handle) {
+ perror(argv[1]);
+ exit(-1);
+ }
+
+ data.teval_processes.start = traceeval_init(process_keys, timestamp_vals);
+ data.teval_processes_data = traceeval_init(process_keys, process_data_vals);
+ data.teval_processes.stop = traceeval_init(process_keys, delta_vals);
+
+
+ data.teval_cpus.start = traceeval_init(cpu_keys, timestamp_vals);
+ data.teval_cpus.stop = traceeval_init(cpu_keys, delta_vals);
+
+ tracecmd_follow_event(handle, "sched", "sched_switch", switch_func, &data);
+
+ tracecmd_iterate_events(handle, NULL, 0, NULL, NULL);
+
+ display(&data);
+
+ free_tdata(&data);
+
+ return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*traceval.h*
+ Header file to include in order to have access to the library APIs.
+*-ltraceeval*
+ Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtraceeval*(3)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceeval*.
+--
+REPORTING BUGS
+--------------
+Report bugs to <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtraceeval is licensed under MIT.
+
diff --git a/Documentation/libtraceeval.txt b/Documentation/libtraceeval.txt
index 9f753aaadf12..fc7c621878ac 100644
--- a/Documentation/libtraceeval.txt
+++ b/Documentation/libtraceeval.txt
@@ -41,6 +41,23 @@ Inserting and removing elements from the traceeval:
const struct traceeval_data pass:[**]_results_);
size_t *traceeval_count*(struct traceeval pass:[*]_teval_);
+
+Functions for iterating over the elements of the libtraceeval:
+ struct traceeval_iterator pass:[*]*traceeval_iterator_get*(struct traceeval pass:[*]_teval_);
+ void *traceeval_iterator_put*(struct traceeval_iterator pass:[*]_iter_);
+ int *traceeval_iterator_sort*(struct traceeval_iterator pass:[*]_iter_, const char pass:[*]_sort_field_,
+ int _level_, bool _ascending_);
+ int *traceeval_iterator_sort_custom*(struct traceeval_iterator pass:[*]_iter_,
+ traceeval_cmp_fn _sort_fn_, void pass:[*]_data_);
+ int *traceeval_iterator_next*(struct traceeval_iterator pass:[*]_iter_,
+ const struct traceeval_data pass:[**]_keys_);
+ int *traceeval_iterator_query*(struct traceeval_iterator pass:[*]_iter_,
+ const struct traceeval_data pass:[**]_results_);
+ void *traceeval_iterator_results_release*(struct traceeval_iterator pass:[*]_iter_,
+ const struct traceeval_data pass:[*]_results_);
+ struct traceeval_stat pass:[*]*traceeval_iterator_stat*(struct traceeval_iterator pass:[*]_iter_,
+ struct traceeval_type pass:[*]_type_);
+ int *traceeval_iterator_remove*(struct traceeval_iterator pass:[*]_iter_);
--
DESCRIPTION
--
2.40.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 5/5] libtraceeval: Add man pages for traceeval statistics
2023-10-06 19:55 [PATCH v2 0/5] libtraceeval: Add man pages Steven Rostedt
` (3 preceding siblings ...)
2023-10-06 19:55 ` [PATCH v2 4/5] libtraceeval: Add man pages for the traceeval iterator functions Steven Rostedt
@ 2023-10-06 19:55 ` Steven Rostedt
4 siblings, 0 replies; 6+ messages in thread
From: Steven Rostedt @ 2023-10-06 19:55 UTC (permalink / raw)
To: linux-trace-devel; +Cc: Ross Zwisler, Steven Rostedt (Google)
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
Add man pages for:
traceeval_stat()
traceeval_stat_size()
traceeval_stat_max()
traceeval_stat_min()
traceeval_stat_max_timestamp()
traceeval_stat_min_timestamp()
traceeval_stat_total()
traceeval_stat_count()
Link: https://lore.kernel.org/linux-trace-devel/20231005232450.1311519-6-rostedt@goodmis.org
Cc: Ross Zwisler <zwisler@google.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Documentation/libtraceeval-stat.txt | 308 ++++++++++++++++++++++++++++
Documentation/libtraceeval.txt | 16 ++
2 files changed, 324 insertions(+)
create mode 100644 Documentation/libtraceeval-stat.txt
diff --git a/Documentation/libtraceeval-stat.txt b/Documentation/libtraceeval-stat.txt
new file mode 100644
index 000000000000..f44c94ad17f6
--- /dev/null
+++ b/Documentation/libtraceeval-stat.txt
@@ -0,0 +1,308 @@
+libtraceeval(3)
+===============
+
+NAME
+----
+traceeval_stat, traceeval_stat_size, traceeval_stat_max, traceeval_stat_min, traceeval_stat_max_timestamp,
+traceeval_stat_min_timestamp, traceeval_stat_total, traceeval_stat_count - Get statistics of an elements value/key in a traceeval.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <traceeval.h>*
+
+struct traceeval_stat pass:[*]*traceeval_stat*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[*]_keys_,
+ const char pass:[*]_val_name_);
+struct traceeval_stat pass:[*]*traceeval_stat_size*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[*]_keys_,
+ size_t _nr_keys_,
+ const char pass:[*]_val_name_);
+
+unsigned long long *traceeval_stat_max*(struct traceeval_stat pass:[*]_stat_);
+unsigned long long *traceeval_stat_min*(struct traceeval_stat pass:[*]_stat_);
+unsigned long long *traceeval_stat_max_timestamp*(struct traceeval_stat pass:[*]_stat_, unsigned long long pass:[*]ts);
+unsigned long long *traceeval_stat_min_timestamp*(struct traceeval_stat pass:[*]_stat_, unsigned long long pass:[*]ts);
+unsigned long long *traceeval_stat_total*(struct traceeval_stat pass:[*]_stat_);
+unsigned long long *traceeval_stat_count*(struct traceeval_stat pass:[*]_stat_);
+--
+
+DESCRIPTION
+-----------
+If a type of a value is numeric (*TRACEEVAL_TYPE_NUMBERpass:[*]*) and has the
+*TRACEEVAL_FL_STAT* flag set, then every instance added will save the maximum,
+minimum, count and total for that value. These functions are used to retrieve
+that information.
+
+If one of the values has *TRACEEVAL_FL_TIMESTAMP* set, that value will also be used
+to record that value for when a maximum or minimum stat value is hit. Note that
+a value can not have both the *TRACEEVAL_FL_TIMESTAMP* and *TRACEEVAL_FL_STAT* flags set.
+That will cause an error when creating the traceeval via *traceeval_init*(3).
+Note that fields marked with *TRACEEVAL_FL_TIMESTAMP* must also be of the
+*TRACEEVAL_TYPE_NUMBERpass:[*]* types.
+
+The *traceeval_stat()* will return a struct traceeval_stat descriptor for the
+given field that is numeric and is marked as a stat type. The _teval_ is the
+traceeval descriptor that contains the elements, the _keys_ are the keys to find
+the element to get the stats of the vals, and _val_name_ is the name of the value
+field to retrieve the stats from (this is the same name for the field passed to
+*traceeval_init*(3)). Note that the _keys_ passed in must be a static array.
+If only a dynamic array (pointer) is available, then *traceeval_stat_size()* needs to
+be used instead. This is because *traceeval_stat()* is a macro that will calculate
+the size of the array.
+
+Once a traceeval_stat descriptor is retrieved, then it can be used to extract the
+statistics for that give value field whith the below functions.
+
+The *traceeval_stat_max()* will return the maximum value for the value represented by
+the _stat_ descriptor passed in.
+
+The *traceeval_stat_min()* will return the minimum value for the value represented by
+the _stat_ descriptor passed in.
+
+The *traceeval_stat_max_timestamp()* and *traceeval_stat_min_timestamp()* functions
+return the same result as the *traceeval_stat_max()* and *traceeval_stat_min()* functions
+respectively, but they take another parameter. The _ts_ is a pointer to a unsigend long long that
+if not NULL, will be used to return the timestamp of when the maximum or minimum values
+respectively were retrieved. This only works if another field of the traceeval descriptor
+values was marked as *TRACEEVAL_FL_TIMESTAMP*. That field is saved when a new maximum or
+minimum is found.
+
+The *traceeval_stat_total()* returns the sum of all the values of the field that the _stat_
+represents.
+
+The *traceeval_stat_count()* returns the number of times the value was calculated. Note that
+this may not be the same as *traceveal_count*(3) as that function returns the number
+of instances currently in the traceeval. If *traceeval_remove*(3) is called on the traceeval
+descriptor to remove an element, the *traceeval_count*(3) will return one less. The removal
+of elements does not affect the count of the traceeval_stat, and the same goes for
+the total count. The number returned from this function can safely be used against the number
+returned by *traceeval_stat_total()* to calculate the average.
+
+RETURN VALUE
+------------
+The *traceeval_stat()* and *traceeval_stat_size()* both return a descriptor to a traceeval_stat
+on success, and NULL on error.
+
+The *traceeval_stat_max()* and *traceeval_stat_max_timestamp()* both return the maximum value that
+the traceeval_stat represtents. The *traceeval_stat_max_timestamp()* also returns the timestamp
+that was recorded when the maximum was found. Note, if no other value field was marked with
+*TRACEEVAL_FL_TIMESTAMP* then the timestamp _ts_ will contain zero.
+
+The *traceeval_stat_min()* and *traceeval_stat_min_timestamp()* both return the minimum value that
+the traceeval_stat represtents. The *traceeval_stat_min_timestamp()* also returns the timestamp
+that was recorded when the minimum was found. Note, if no other value field was marked with
+*TRACEEVAL_FL_TIMESTAMP* then the timestamp _ts_ will contain zero.
+
+The *traceeval_stat_total()* returns the total sum of all the values that the traceeval_stat
+represents.
+
+The *traceeval_stat_count()* returns the number of times the traceeval_stat total was updated.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <unistd.h>
+#include <sys/wait.h>
+#include <trace-cmd.h>
+#include <traceeval.h>
+
+struct data {
+ struct traceeval *teval_wakeup;
+ struct traceeval *teval_sched;
+};
+
+struct traceeval_type wakeup_keys[] = {
+ {
+ .name = "PID",
+ .type = TRACEEVAL_TYPE_NUMBER,
+ }
+};
+
+struct traceeval_type wakeup_vals[] = {
+ {
+ .name = "timestamp",
+ .flags = TRACEEVAL_FL_TIMESTAMP,
+ .type = TRACEEVAL_TYPE_NUMBER_64,
+ }
+};
+
+struct traceeval_type sched_keys[] = {
+ {
+ .name = "COMM",
+ .type = TRACEEVAL_TYPE_STRING,
+ },
+ {
+ .name = "PID",
+ .type = TRACEEVAL_TYPE_NUMBER,
+ }
+};
+
+struct traceeval_type sched_vals[] = {
+ {
+ .name = "timestamp",
+ .flags = TRACEEVAL_FL_TIMESTAMP,
+ .type = TRACEEVAL_TYPE_NUMBER_64,
+ },
+ {
+ .name = "delta",
+ .flags = TRACEEVAL_FL_STAT,
+ .type = TRACEEVAL_TYPE_NUMBER_64,
+ }
+};
+
+static int wakeup_callback(struct tracecmd_input *handle, struct tep_event *event,
+ struct tep_record *record, int cpu, void *d)
+{
+ static struct tep_format_field *pid_field;
+ struct data *data = d;
+ unsigned long long val;
+ long pid;
+ struct traceeval_data keys[1];
+ struct traceeval_data vals[1];
+
+ if (!pid_field)
+ pid_field = tep_find_field(event, "pid");
+
+ tep_read_number_field(pid_field, record->data, &val);
+ pid = val;
+
+ TRACEEVAL_SET_NUMBER(keys[0], pid);
+ TRACEEVAL_SET_NUMBER_64(vals[0], record->ts);
+
+ traceeval_insert(data->teval_wakeup, keys, vals);
+
+ return 0;
+}
+
+static int sched_callback(struct tracecmd_input *handle, struct tep_event *event,
+ struct tep_record *record, int cpu, void *d)
+{
+ static struct tep_format_field *next_pid_field;
+ static struct tep_format_field *next_comm_field;
+ struct data *data = d;
+ unsigned long long delta;
+ unsigned long long val;
+ long pid;
+ struct traceeval_data wakeup_keys[1];
+ struct traceeval_data keys[2];
+ struct traceeval_data vals[2];
+ const struct traceeval_data *results;
+
+ if (!next_pid_field) {
+ next_pid_field = tep_find_field(event, "next_pid");
+ next_comm_field = tep_find_field(event, "next_comm");
+ }
+
+ tep_read_number_field(next_pid_field, record->data, &val);
+ pid = val;
+
+ TRACEEVAL_SET_NUMBER(wakeup_keys[0], pid);
+
+ if (traceeval_query(data->teval_wakeup, wakeup_keys, &results) <= 0)
+ return 0;
+
+ delta = record->ts - results[0].number_64;
+ traceeval_results_release(data->teval_wakeup, results);
+
+ TRACEEVAL_SET_CSTRING(keys[0], record->data + next_comm_field->offset);
+ TRACEEVAL_SET_NUMBER(keys[1], pid);
+
+ TRACEEVAL_SET_NUMBER_64(vals[0], record->ts);
+ TRACEEVAL_SET_NUMBER_64(vals[1], delta);
+
+ traceeval_insert(data->teval_sched, keys, vals);
+
+ return 0;
+}
+
+static void show_latency(struct data *data)
+{
+ struct traceeval_iterator *iter = traceeval_iterator_get(data->teval_sched);
+ const struct traceeval_data *keys;
+
+ printf("\n");
+ while (traceeval_iterator_next(iter, &keys) > 0) {
+ struct traceeval_stat *stat;
+ unsigned long long val;
+ unsigned long long ts;
+
+ stat = traceeval_iterator_stat(iter, sched_vals[1].name);
+ if (!stat)
+ continue;
+
+ printf("%s-%ld\n", keys[0].string, keys[1].number);
+
+ val = traceeval_stat_max_timestamp(stat, &ts),
+
+ printf("\tmax:%lld at %lld\n", val, ts);
+
+ val = traceeval_stat_min_timestamp(stat, &ts);
+ printf("\tmin:%lld at %lld\n", val, ts);
+ printf("\ttotal:%lld count:%lld\n",
+ traceeval_stat_total(stat),
+ traceeval_stat_count(stat));
+ }
+ traceeval_iterator_put(iter);
+}
+
+int main (int argc, char **argv)
+{
+ struct tracecmd_input *handle;
+ struct data data;
+
+ if (argc < 2) {
+ printf("usage: wake-lat trace.dat\n");
+ exit(-1);
+ }
+
+ data.teval_wakeup = traceeval_init(wakeup_keys, wakeup_vals);
+ data.teval_sched = traceeval_init(sched_keys, sched_vals);
+
+ handle = tracecmd_open(argv[1], TRACECMD_FL_LOAD_NO_PLUGINS);
+ if (!handle) {
+ perror(argv[0]);
+ exit(-1);
+ }
+
+ tracecmd_follow_event(handle, "sched", "sched_waking", wakeup_callback, &data);
+ tracecmd_follow_event(handle, "sched", "sched_switch", sched_callback, &data);
+
+ tracecmd_iterate_events(handle, NULL, 0, NULL, NULL);
+
+ show_latency(&data);
+
+ return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*traceval.h*
+ Header file to include in order to have access to the library APIs.
+*-ltraceeval*
+ Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtraceeval*(3)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceeval*.
+--
+REPORTING BUGS
+--------------
+Report bugs to <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtraceeval is licensed under MIT.
+
diff --git a/Documentation/libtraceeval.txt b/Documentation/libtraceeval.txt
index fc7c621878ac..173b1ddb3df8 100644
--- a/Documentation/libtraceeval.txt
+++ b/Documentation/libtraceeval.txt
@@ -58,6 +58,22 @@ Functions for iterating over the elements of the libtraceeval:
struct traceeval_stat pass:[*]*traceeval_iterator_stat*(struct traceeval_iterator pass:[*]_iter_,
struct traceeval_type pass:[*]_type_);
int *traceeval_iterator_remove*(struct traceeval_iterator pass:[*]_iter_);
+
+Functions to manage statistics of values of a traceeval:
+ struct traceeval_stat pass:[*]*traceeval_stat*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[*]_keys_,
+ const char pass:[*]_val_name_);
+ struct traceeval_stat pass:[*]*traceeval_stat_size*(struct traceeval pass:[*]_teval_,
+ const struct traceeval_data pass:[*]_keys_,
+ size_t _nr_keys_,
+ const char pass:[*]_val_name_);
+
+ unsigned long long *traceeval_stat_max*(struct traceeval_stat pass:[*]_stat_);
+ unsigned long long *traceeval_stat_min*(struct traceeval_stat pass:[*]_stat_);
+ unsigned long long *traceeval_stat_max_timestamp*(struct traceeval_stat pass:[*]_stat_, unsigned long long pass:[*]ts);
+ unsigned long long *traceeval_stat_min_timestamp*(struct traceeval_stat pass:[*]_stat_, unsigned long long pass:[*]ts);
+ unsigned long long *traceeval_stat_total*(struct traceeval_stat pass:[*]_stat_);
+ unsigned long long *traceeval_stat_count*(struct traceeval_stat pass:[*]_stat_);
--
DESCRIPTION
--
2.40.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2023-10-06 19:54 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-10-06 19:55 [PATCH v2 0/5] libtraceeval: Add man pages Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 1/5] libtraceeval: Start creating the documentation " Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 2/5] libtraceeval: Add man pages for creating and releasing the traceeval descriptor Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 3/5] libtraceeval: Add man pages for insertion and removal of elements in a traceeval Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 4/5] libtraceeval: Add man pages for the traceeval iterator functions Steven Rostedt
2023-10-06 19:55 ` [PATCH v2 5/5] libtraceeval: Add man pages for traceeval statistics Steven Rostedt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).