From patchwork Thu Oct 5 23:24:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13410879 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CE3BE43ABB for ; Thu, 5 Oct 2023 23:23:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=none Received: by smtp.kernel.org (Postfix) with ESMTPSA id 46C6FC433CB; Thu, 5 Oct 2023 23:23:41 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qoXi7-005VCR-2e; Thu, 05 Oct 2023 19:24:51 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , "Steven Rostedt (Google)" Subject: [PATCH 1/5] libtraceeval: Start creating the documentation man pages Date: Thu, 5 Oct 2023 19:24:46 -0400 Message-Id: <20231005232450.1311519-2-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20231005232450.1311519-1-rostedt@goodmis.org> References: <20231005232450.1311519-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" 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. Signed-off-by: Steven Rostedt (Google) --- 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: (
); if section is defined, else just show +# the command. + +[macros] +(?su)[\\]?(?Plinktep):(?P\S*?)\[(?P.*?)\]= + +[attributes] +asterisk=* +plus=+ +caret=^ +startsb=[ +endsb=] +tilde=~ + +ifdef::backend-docbook[] +[linktep-inlinemacro] +{0%{target}} +{0#} +{0#{target}{0}} +{0#} +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] +{title} + +ifdef::doctype-manpage[] + .ft C +endif::doctype-manpage[] +| +ifdef::doctype-manpage[] + .ft +endif::doctype-manpage[] + +{title#} +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] +{title} + +| + +{title#} + +[verseblock] +{title} +{title%} +{title#} +| + +{title#} +{title%} +endif::doctype-manpage[] +endif::tep-asciidoc-no-roff[] +endif::backend-docbook[] + +ifdef::doctype-manpage[] +ifdef::backend-docbook[] +[header] +template::[header-declarations] + + +{mantitle} +{manvolnum} +libtracefs +{libtracefs_version} +libtracefs Manual + + + {manname1} + {manname2} + {manname3} + {manname4} + {manname5} + {manname6} + {manname7} + {manname8} + {manname9} + {manname10} + {manname11} + {manname12} + {manname13} + {manname14} + {manname15} + {manname16} + {manname17} + {manname18} + {manname19} + {manname20} + {manname21} + {manname22} + {manname23} + {manname24} + {manname25} + {manname26} + {manname27} + {manname28} + {manname29} + {manname30} + {manpurpose} + +endif::backend-docbook[] +endif::doctype-manpage[] + +ifdef::backend-xhtml11[] +[linktep-inlinemacro] +{target}{0?({0})} +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 * + +-- + +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* +-- + +REPORTING BUGS +-------------- +Report bugs to + +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 @@ + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + sp + + + + + + + + br + + + 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 @@ + + + + + + + fB + + + fR + + + 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 @@ + + + + + + +\ +. + + 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 @@ + + + + + + + + + + + + + + + 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 +# +# 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 From patchwork Thu Oct 5 23:24:47 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13410875 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A14F942BEE for ; Thu, 5 Oct 2023 23:23:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=none Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4B47FC433CA; Thu, 5 Oct 2023 23:23:41 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qoXi7-005VCU-2k; Thu, 05 Oct 2023 19:24:51 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , "Steven Rostedt (Google)" Subject: [PATCH 2/5] libtraceeval: Add man pages for creating and releasing the traceeval descriptor Date: Thu, 5 Oct 2023 19:24:47 -0400 Message-Id: <20231005232450.1311519-3-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20231005232450.1311519-1-rostedt@goodmis.org> References: <20231005232450.1311519-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add man pages for: traceeval_init() traceeval_init_size() traceeval_release() Signed-off-by: Steven Rostedt (Google) --- Documentation/Makefile | 4 +- Documentation/libtraceeval-init.txt | 394 ++++++++++++++++++++++++++++ Documentation/libtraceeval.txt | 5 + 3 files changed, 402 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..3578ac063916 --- /dev/null +++ b/Documentation/libtraceeval-init.txt @@ -0,0 +1,394 @@ +libtraceeval(3) +=============== + +NAME +---- +traceeval_init, traceeval_init_size, traceeval_release - Create a trace evaluation helper mechanism. + +SYNOPSIS +-------- +[verse] +-- +*#include * + +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 +#include +#include +#include + +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); + } +} + +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* , author of *libtraceeval*. +-- +REPORTING BUGS +-------------- +Report bugs to + +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 * +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 From patchwork Thu Oct 5 23:24:48 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13410877 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A442B43AAD for ; Thu, 5 Oct 2023 23:23:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=none Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3F642C433C8; Thu, 5 Oct 2023 23:23:41 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qoXi7-005VCX-2p; Thu, 05 Oct 2023 19:24:51 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , "Steven Rostedt (Google)" Subject: [PATCH 3/5] libtraceeval: Add man pages for insertion and removal of elements in a traceeval Date: Thu, 5 Oct 2023 19:24:48 -0400 Message-Id: <20231005232450.1311519-4-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20231005232450.1311519-1-rostedt@goodmis.org> References: <20231005232450.1311519-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add man pages for: traceeval_insert() traceeval_insert_size() traceeval_remove() traceeval_remove_size() traceeval_query() traceeval_query_size() traceeval_results_release() traceeval_count() Signed-off-by: Steven Rostedt (Google) --- Documentation/libtraceeval-insert.txt | 389 ++++++++++++++++++++++++++ Documentation/libtraceeval.txt | 25 ++ 2 files changed, 414 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..84b4f07af539 --- /dev/null +++ b/Documentation/libtraceeval-insert.txt @@ -0,0 +1,389 @@ +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 * + +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 +#include +#include +#include + +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)); + } +} + +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* , author of *libtraceeval*. +-- +REPORTING BUGS +-------------- +Report bugs to + +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 From patchwork Thu Oct 5 23:24:49 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13410878 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B7A6243AB0 for ; Thu, 5 Oct 2023 23:23:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=none Received: by smtp.kernel.org (Postfix) with ESMTPSA id 43BAAC433C7; Thu, 5 Oct 2023 23:23:41 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qoXi7-005VCa-2v; Thu, 05 Oct 2023 19:24:51 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , "Steven Rostedt (Google)" Subject: [PATCH 4/5] libtraceeval: Add man pages for the traceeval iterator functions Date: Thu, 5 Oct 2023 19:24:49 -0400 Message-Id: <20231005232450.1311519-5-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20231005232450.1311519-1-rostedt@goodmis.org> References: <20231005232450.1311519-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" 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() Signed-off-by: Steven Rostedt (Google) --- Documentation/libtraceeval-iterator.txt | 1072 +++++++++++++++++++++++ Documentation/libtraceeval.txt | 17 + 2 files changed, 1089 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..8be1eb368365 --- /dev/null +++ b/Documentation/libtraceeval-iterator.txt @@ -0,0 +1,1072 @@ +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 * + +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 +------- +[source,c] +-- +#include +#include +#include +#include +#include +#include +#include +#include + +static char *argv0; + +static char *get_this_name(void) +{ + static char *this_name; + char *arg; + char *p; + + if (this_name) + return this_name; + + arg = argv0; + p = arg+strlen(arg); + + while (p >= arg && *p != '/') + p--; + p++; + + this_name = p; + return p; +} + +static void usage(void) +{ + char *p = get_this_name(); + + printf("usage: %s [-c comm] trace.dat\n" + "\n" + " Run this after running: trace-cmd record -e sched\n" + "\n" + " Do some work and then hit Ctrl^C to stop the recording.\n" + " Run this on the resulting trace.dat file\n" + "\n" + "-c comm - to look at only a specific process called 'comm'\n" + "\n",p); + exit(-1); +} + +static void __vdie(const char *fmt, va_list ap, int err) +{ + int ret = errno; + char *p = get_this_name(); + + if (err && errno) + perror(p); + else + ret = -1; + + fprintf(stderr, " "); + vfprintf(stderr, fmt, ap); + + fprintf(stderr, "\n"); + exit(ret); +} + +void die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __vdie(fmt, ap, 0); + va_end(ap); +} + +void pdie(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __vdie(fmt, ap, 1); + va_end(ap); +} + +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); + if (ret < 0) + pdie("Could not start process"); + return; + case STOP: + ret = traceeval_query(tdata->teval_processes.start, keys, &results); + if (ret < 0) + pdie("Could not query start process"); + 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); + if (ret < 0) + pdie("Could not stop process"); + + /* Reset the start */ + TRACEEVAL_SET_NUMBER_64(new_vals[0], 0); + + ret = traceeval_insert(tdata->teval_processes.start, keys, new_vals); + if (ret < 0) + pdie("Could not start CPU"); + 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) + pdie("Could not query process data"); + 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 ? */ + if (ret < 0) + pdie("Could not query process data"); + + TRACEEVAL_SET_POINTER(new_vals[0], data); + ret = traceeval_insert(tdata->teval_processes_data, keys, new_vals); + if (ret < 0) + pdie("Failed to set process data"); + + 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; + } + if (ret < 0) + pdie("Could not query cpu start data"); + ret = traceeval_insert(teval_pair->start, keys, vals); + if (ret < 0) + pdie("Could not start CPU"); + break; + case STOP: + ret = traceeval_query(teval_pair->start, keys, &results); + if (ret < 0) + pdie("Could not query cpu stop data"); + 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); + if (ret < 0) + pdie("Could not stop CPU"); + + /* Reset the start */ + TRACEEVAL_SET_NUMBER_64(new_vals[0], 0); + ret = traceeval_insert(teval_pair->start, keys, new_vals); + if (ret < 0) + pdie("Could not start CPU"); + + 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); + if (ret < 0) + pdie("Could not start thread"); + return; + case STOP: + ret = traceeval_query(pdata->teval_threads.start, keys, &results); + if (ret < 0) + pdie("Could not query thread start"); + 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); + if (ret < 0) + pdie("Could not stop thread"); + 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) + die("Could not find field %s for %s", + name, event->name); + + return field; +} + +static void init_process_data(struct process_data *pdata) +{ + + pdata->teval_cpus.start = traceeval_init(cpu_keys, timestamp_vals); + if (!pdata->teval_cpus.start) + pdie("Creating trace eval cpus start"); + pdata->teval_cpus.stop = traceeval_init(cpu_keys, delta_vals); + if (!pdata->teval_cpus.stop) + pdie("Creating trace eval cpus"); + + pdata->teval_threads.start = traceeval_init(thread_keys, timestamp_vals); + if (!pdata->teval_threads.start) + pdie("Creating trace eval threads start"); + + pdata->teval_threads.stop = traceeval_init(thread_keys, delta_vals); + if (!pdata->teval_threads.stop) + pdie("Creating trace eval threads"); +} + +static struct process_data *alloc_pdata(struct task_data *tdata, const char *comm) +{ + struct process_data *pdata; + + pdata = calloc(1, sizeof(*pdata)); + if (!pdata) + pdie("Allocating process data"); + 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; + int ret; + + ret = tep_read_number_field(prev_pid, record->data, &val); + if (ret < 0) + die("Could not read sched_switch next_pid for record"); + + /* 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); + + ret = tep_read_number_field(prev_state, record->data, &val); + if (ret < 0) + die("Could not read sched_switch next_pid for record"); + 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 ret; + int pid; + + ret = tep_read_number_field(next_pid, record->data, &val); + if (ret < 0) + die("Could not read sched_switch next_pid for record"); + 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; + + if (!iter) + pdie("Could not get iterator?"); + + 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; + } + + if (last_cpu < 0) + die("No result for CPUs\n"); + +} + +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 (!stat) + continue; // die? + + if (last_tid != keys[0].number) + printf("\n thread id: %d\n", tid); + + last_tid = tid; + + display_state_times(state, traceeval_stat_total(stat)); + } + + if (last_tid < 0) + die("No result for threads\n"); + +} + +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; + int ret; + + 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; + + ret = traceeval_iterator_query(iter, &results); + if (ret < 0) + pdie("Could not query iterator"); + if (ret < 1) + continue; /* ?? */ + + pdata = results[0].pointer; + traceeval_results_release(teval_data, results); + + printf("Task: %s\n", comm); + + display_process_stats(teval, pdata, comm); + if (pdata) + display_process(pdata); + } +} + +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"); + + if (!iter) + pdie("No cpus?"); + + 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: + die("Invalid CPU state: %d\n", state); + } + } + + 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; + int c; + + memset(&data, 0, sizeof(data)); + + argv0 = argv[0]; + + while ((c = getopt(argc, argv, "c:h")) >= 0) { + switch (c) { + case 'c': + data.comm = optarg; + break; + case 'h': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + handle = tracecmd_open(argv[0], TRACECMD_FL_LOAD_NO_PLUGINS); + if (!handle) + pdie("Error opening %s", argv[0]); + + data.teval_processes.start = traceeval_init(process_keys, timestamp_vals); + if (!data.teval_processes.start) + pdie("Creating trace eval start"); + data.teval_processes_data = traceeval_init(process_keys, process_data_vals); + if (!data.teval_processes_data) + pdie("Creating trace eval data"); + data.teval_processes.stop = traceeval_init(process_keys, delta_vals); + if (!data.teval_processes.stop) + pdie("Creating trace eval"); + + data.teval_cpus.start = traceeval_init(cpu_keys, timestamp_vals); + if (!data.teval_cpus.start) + pdie("Creating trace eval"); + data.teval_cpus.stop = traceeval_init(cpu_keys, delta_vals); + if (!data.teval_cpus.stop) + pdie("Creating trace eval"); + + 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* , author of *libtraceeval*. +-- +REPORTING BUGS +-------------- +Report bugs to + +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 From patchwork Thu Oct 5 23:24:50 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13410876 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A156243AA1 for ; Thu, 5 Oct 2023 23:23:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=none Received: by smtp.kernel.org (Postfix) with ESMTPSA id 41CFFC433C9; Thu, 5 Oct 2023 23:23:41 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qoXi7-005VCd-31; Thu, 05 Oct 2023 19:24:51 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , "Steven Rostedt (Google)" Subject: [PATCH 5/5] libtraceeval: Add man pages for traceeval statistics Date: Thu, 5 Oct 2023 19:24:50 -0400 Message-Id: <20231005232450.1311519-6-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20231005232450.1311519-1-rostedt@goodmis.org> References: <20231005232450.1311519-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" 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() Signed-off-by: Steven Rostedt (Google) --- Documentation/libtraceeval-stat.txt | 307 ++++++++++++++++++++++++++++ Documentation/libtraceeval.txt | 16 ++ 2 files changed, 323 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..6e9866f4daf2 --- /dev/null +++ b/Documentation/libtraceeval-stat.txt @@ -0,0 +1,307 @@ +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 * + +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 +#include +#include +#include + +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)); + } +} + +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* , author of *libtraceeval*. +-- +REPORTING BUGS +-------------- +Report bugs to + +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