perf/core improvements and fixes:

perf.data:
 
   Alexey Budankov:
 
   - Streaming compression of perf ring buffer into PERF_RECORD_COMPRESSED
     user space records, resulting in ~3-5x perf.data file size reduction
     on variety of tested workloads what saves storage space on larger
     server systems where perf.data size can easily reach several tens or
     even hundreds of GiBs, especially when profiling with DWARF-based
     stacks and tracing of context switches.
 
 perf record:
 
   Arnaldo Carvalho de Melo
 
   - Improve -user-regs/intr-regs suggestions to overcome errors.
 
 perf annotate:
 
   Jin Yao:
 
   - Remove hist__account_cycles() from callback, speeding up branch processing
     (perf record -b).
 
 perf stat:
 
   - Add a 'percore' event qualifier, e.g.: -e cpu/event=0,umask=0x3,percore=1/,
     that sums up the event counts for both hardware threads in a core.
 
     We can already do this with --per-core, but it's often useful to do
     this together with other metrics that are collected per hardware thread.
 
     I.e. now its possible to do this per-event, and have it mixed with other
     events not aggregated by core.
 
 core libraries:
 
   Donald Yandt:
 
   - Check for errors when doing fgets(/proc/version).
 
   Jiri Olsa:
 
   - Speed up report for perf compiled with linbunwind.
 
 tools headers:
 
   Arnaldo Carvalho de Melo
 
   - Update memcpy_64.S, x86's kvm.h and pt_regs.h.
 
 arm64:
 
   Florian Fainelli:
 
   - Map Brahma-B53 CPUID to cortex-a53 events.
 
   - Add Cortex-A57 and Cortex-A72 events.
 
 csky:
 
   Mao Han:
 
   - Add DWARF register mappings for libdw, allowing --call-graph=dwarf to work
     on the C-SKY arch.
 
 x86:
 
   Andi Kleen/Kan Liang:
 
   - Add support for recording and printing XMM registers, available, for
     instance, on Icelake.
 
   Kan Liang:
 
   - Add uncore_upi (Intel's "Ultra Path Interconnect" events) JSON support.
     UPI replaced the Intel QuickPath Interconnect (QPI) in Xeon Skylake-SP.
 
 Intel PT:
 
   Adrian Hunter
 
   . Fix instructions sampling rate.
 
   . Timestamp fixes.
 
   . Improve exported-sql-viewer GUI, allowing, for instance, to copy'n'paste
     the trees, useful for e-mailing.
 
 Documentation:
 
   Thomas Richter:
 
   - Add description for 'perf --debug stderr=1', which redirects stderr to stdout.
 
 libtraceevent:
 
   Tzvetomir Stoyanov:
 
   - Add man pages for the various APIs.
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQR2GiIUctdOfX2qHhGyPKLppCJ+JwUCXN8KEwAKCRCyPKLppCJ+
 JxbAAPsE4bDD7UyTJgVWVXFyqMkaFURv9eGtmhbH/EhnZ/ADfwEAoUG6C+G2DLre
 IMtgTXSlRShQucQbum5Rcp9lcXGL+gY=
 =mGtw
 -----END PGP SIGNATURE-----

Merge tag 'perf-core-for-mingo-5.2-20190517' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

perf.data:

  Alexey Budankov:

  - Streaming compression of perf ring buffer into PERF_RECORD_COMPRESSED
    user space records, resulting in ~3-5x perf.data file size reduction
    on variety of tested workloads what saves storage space on larger
    server systems where perf.data size can easily reach several tens or
    even hundreds of GiBs, especially when profiling with DWARF-based
    stacks and tracing of context switches.

perf record:

  Arnaldo Carvalho de Melo

  - Improve -user-regs/intr-regs suggestions to overcome errors.

perf annotate:

  Jin Yao:

  - Remove hist__account_cycles() from callback, speeding up branch processing
    (perf record -b).

perf stat:

  - Add a 'percore' event qualifier, e.g.: -e cpu/event=0,umask=0x3,percore=1/,
    that sums up the event counts for both hardware threads in a core.

    We can already do this with --per-core, but it's often useful to do
    this together with other metrics that are collected per hardware thread.

    I.e. now its possible to do this per-event, and have it mixed with other
    events not aggregated by core.

core libraries:

  Donald Yandt:

  - Check for errors when doing fgets(/proc/version).

  Jiri Olsa:

  - Speed up report for perf compiled with linbunwind.

tools headers:

  Arnaldo Carvalho de Melo

  - Update memcpy_64.S, x86's kvm.h and pt_regs.h.

arm64:

  Florian Fainelli:

  - Map Brahma-B53 CPUID to cortex-a53 events.

  - Add Cortex-A57 and Cortex-A72 events.

csky:

  Mao Han:

  - Add DWARF register mappings for libdw, allowing --call-graph=dwarf to work
    on the C-SKY arch.

x86:

  Andi Kleen/Kan Liang:

  - Add support for recording and printing XMM registers, available, for
    instance, on Icelake.

  Kan Liang:

  - Add uncore_upi (Intel's "Ultra Path Interconnect" events) JSON support.
    UPI replaced the Intel QuickPath Interconnect (QPI) in Xeon Skylake-SP.

Intel PT:

  Adrian Hunter

  . Fix instructions sampling rate.

  . Timestamp fixes.

  . Improve exported-sql-viewer GUI, allowing, for instance, to copy'n'paste
    the trees, useful for e-mailing.

Documentation:

  Thomas Richter:

  - Add description for 'perf --debug stderr=1', which redirects stderr to stdout.

libtraceevent:

  Tzvetomir Stoyanov:

  - Add man pages for the various APIs.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2019-05-18 10:24:43 +02:00
commit 62e1c09418
94 changed files with 5414 additions and 214 deletions

View File

@ -381,6 +381,7 @@ struct kvm_sync_regs {
#define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0)
#define KVM_X86_QUIRK_CD_NW_CLEARED (1 << 1)
#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE (1 << 2)
#define KVM_X86_QUIRK_OUT_7E_INC_RIP (1 << 3)
#define KVM_STATE_NESTED_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_RUN_PENDING 0x00000002

View File

@ -27,8 +27,29 @@ enum perf_event_x86_regs {
PERF_REG_X86_R13,
PERF_REG_X86_R14,
PERF_REG_X86_R15,
/* These are the limits for the GPRs. */
PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1,
PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1,
/* These all need two bits set because they are 128bit */
PERF_REG_X86_XMM0 = 32,
PERF_REG_X86_XMM1 = 34,
PERF_REG_X86_XMM2 = 36,
PERF_REG_X86_XMM3 = 38,
PERF_REG_X86_XMM4 = 40,
PERF_REG_X86_XMM5 = 42,
PERF_REG_X86_XMM6 = 44,
PERF_REG_X86_XMM7 = 46,
PERF_REG_X86_XMM8 = 48,
PERF_REG_X86_XMM9 = 50,
PERF_REG_X86_XMM10 = 52,
PERF_REG_X86_XMM11 = 54,
PERF_REG_X86_XMM12 = 56,
PERF_REG_X86_XMM13 = 58,
PERF_REG_X86_XMM14 = 60,
PERF_REG_X86_XMM15 = 62,
/* These include both GPRs and XMMX registers */
PERF_REG_X86_XMM_MAX = PERF_REG_X86_XMM15 + 2,
};
#endif /* _ASM_X86_PERF_REGS_H */

View File

@ -257,6 +257,7 @@ ENTRY(__memcpy_mcsafe)
/* Copy successful. Return zero */
.L_done_memcpy_trap:
xorl %eax, %eax
.L_done:
ret
ENDPROC(__memcpy_mcsafe)
EXPORT_SYMBOL_GPL(__memcpy_mcsafe)
@ -273,7 +274,7 @@ EXPORT_SYMBOL_GPL(__memcpy_mcsafe)
addl %edx, %ecx
.E_trailing_bytes:
mov %ecx, %eax
ret
jmp .L_done
/*
* For write fault handling, given the destination is unaligned,

View File

@ -0,0 +1,207 @@
include ../../../scripts/Makefile.include
include ../../../scripts/utilities.mak
# This Makefile and manpage XSL files were taken from tools/perf/Documentation
# and modified for libtraceevent.
MAN3_TXT= \
$(wildcard libtraceevent-*.txt) \
libtraceevent.txt
MAN_TXT = $(MAN3_TXT)
_MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
_MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT))
_DOC_MAN3=$(patsubst %.txt,%.3,$(MAN3_TXT))
MAN_XML=$(addprefix $(OUTPUT),$(_MAN_XML))
MAN_HTML=$(addprefix $(OUTPUT),$(_MAN_HTML))
DOC_MAN3=$(addprefix $(OUTPUT),$(_DOC_MAN3))
# Make the path relative to DESTDIR, not prefix
ifndef DESTDIR
prefix?=$(HOME)
endif
bindir?=$(prefix)/bin
htmldir?=$(prefix)/share/doc/libtraceevent-doc
pdfdir?=$(prefix)/share/doc/libtraceevent-doc
mandir?=$(prefix)/share/man
man3dir=$(mandir)/man3
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="libtraceevent" -a manmanual="libtraceevent Manual"
ASCIIDOC_HTML = xhtml5
endif
XMLTO=xmlto
_tmp_tool_path := $(call get-executable,$(ASCIIDOC))
ifeq ($(_tmp_tool_path),)
missing_tools = $(ASCIIDOC)
endif
ifndef USE_ASCIIDOCTOR
_tmp_tool_path := $(call get-executable,$(XMLTO))
ifeq ($(_tmp_tool_path),)
missing_tools += $(XMLTO)
endif
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 libtraceevent-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 libtraceevent-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
#
# Please note that there is a minor bug in asciidoc.
# The version after 6.0.3 _will_ include the patch found here:
# http://marc.theaimsgroup.com/?l=libtraceevent&m=111558757202243&w=2
#
# Until that version is released you may have to apply the patch
# yourself - yes, all 6 characters of it!
#
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: html man
man: man3
man3: $(DOC_MAN3)
html: $(MAN_HTML)
$(MAN_HTML) $(DOC_MAN3): asciidoc.conf
install: install-man
check-man-tools:
ifdef missing_tools
$(error "You need to install $(missing_tools) for man pages")
endif
do-install-man: man
$(call QUIET_INSTALL, Documentation-man) \
$(INSTALL) -d -m 755 $(DESTDIR)$(man3dir); \
$(INSTALL) -m 644 $(DOC_MAN3) $(DESTDIR)$(man3dir);
install-man: check-man-tools man do-install-man
uninstall: uninstall-man
uninstall-man:
$(call QUIET_UNINST, Documentation-man) \
$(Q)$(RM) $(addprefix $(DESTDIR)$(man3dir)/,$(DOC_MAN3))
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)) \
$(DOC_MAN3) *.3
clean:
$(call QUIET_CLEAN, Documentation) $(RM) $(CLEAN_FILES)
ifdef USE_ASCIIDOCTOR
$(OUTPUT)%.3 : $(OUTPUT)%.txt
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
$(ASCIIDOC) -b manpage -d manpage \
$(ASCIIDOC_EXTRA) -alibtraceevent_version=$(EVENT_PARSE_VERSION) -o $@+ $< && \
mv $@+ $@
endif
$(OUTPUT)%.3 : $(OUTPUT)%.xml
$(QUIET_XMLTO)$(RM) $@ && \
$(XMLTO) -o $(OUTPUT). -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
$(OUTPUT)%.xml : %.txt
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
$(ASCIIDOC) -b docbook -d manpage \
$(ASCIIDOC_EXTRA) -alibtraceevent_version=$(EVENT_PARSE_VERSION) -o $@+ $< && \
mv $@+ $@
$(MAN_HTML): $(OUTPUT)%.html : %.txt
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
$(ASCIIDOC) -b $(ASCIIDOC_HTML) -d manpage \
$(ASCIIDOC_EXTRA) -aperf_version=$(EVENT_PARSE_VERSION) -o $@+ $< && \
mv $@+ $@

View File

@ -0,0 +1,120 @@
## linktep: macro
#
# Usage: linktep:command[manpage-section]
#
# Note, {0} is the manpage section, while {target} is the command.
#
# Show TEP link as: <command>(<section>); if section is defined, else just show
# the command.
[macros]
(?su)[\\]?(?P<name>linktep):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
[attributes]
asterisk=&#42;
plus=&#43;
caret=&#94;
startsb=&#91;
endsb=&#93;
tilde=&#126;
ifdef::backend-docbook[]
[linktep-inlinemacro]
{0%{target}}
{0#<citerefentry>}
{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>}
{0#</citerefentry>}
endif::backend-docbook[]
ifdef::backend-docbook[]
ifndef::tep-asciidoc-no-roff[]
# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
# v1.72 breaks with this because it replaces dots not in roff requests.
[listingblock]
<example><title>{title}</title>
<literallayout>
ifdef::doctype-manpage[]
&#10;.ft C&#10;
endif::doctype-manpage[]
|
ifdef::doctype-manpage[]
&#10;.ft&#10;
endif::doctype-manpage[]
</literallayout>
{title#}</example>
endif::tep-asciidoc-no-roff[]
ifdef::tep-asciidoc-no-roff[]
ifdef::doctype-manpage[]
# The following two small workarounds insert a simple paragraph after screen
[listingblock]
<example><title>{title}</title>
<literallayout>
|
</literallayout><simpara></simpara>
{title#}</example>
[verseblock]
<formalpara{id? id="{id}"}><title>{title}</title><para>
{title%}<literallayout{id? id="{id}"}>
{title#}<literallayout>
|
</literallayout>
{title#}</para></formalpara>
{title%}<simpara></simpara>
endif::doctype-manpage[]
endif::tep-asciidoc-no-roff[]
endif::backend-docbook[]
ifdef::doctype-manpage[]
ifdef::backend-docbook[]
[header]
template::[header-declarations]
<refentry>
<refmeta>
<refentrytitle>{mantitle}</refentrytitle>
<manvolnum>{manvolnum}</manvolnum>
<refmiscinfo class="source">libtraceevent</refmiscinfo>
<refmiscinfo class="version">{libtraceevent_version}</refmiscinfo>
<refmiscinfo class="manual">libtraceevent 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[]

View File

@ -0,0 +1,153 @@
libtraceevent(3)
================
NAME
----
tep_register_comm, tep_override_comm, tep_pid_is_registered,
tep_data_comm_from_pid, tep_data_pid_from_comm, tep_cmdline_pid -
Manage pid to process name mappings.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
int *tep_register_comm*(struct tep_handle pass:[*]_tep_, const char pass:[*]_comm_, int _pid_);
int *tep_override_comm*(struct tep_handle pass:[*]_tep_, const char pass:[*]_comm_, int _pid_);
bool *tep_is_pid_registered*(struct tep_handle pass:[*]_tep_, int _pid_);
const char pass:[*]*tep_data_comm_from_pid*(struct tep_handle pass:[*]_pevent_, int _pid_);
struct cmdline pass:[*]*tep_data_pid_from_comm*(struct tep_handle pass:[*]_pevent_, const char pass:[*]_comm_, struct cmdline pass:[*]_next_);
int *tep_cmdline_pid*(struct tep_handle pass:[*]_pevent_, struct cmdline pass:[*]_cmdline_);
--
DESCRIPTION
-----------
These functions can be used to handle the mapping between pid and process name.
The library builds a cache of these mappings, which is used to display the name
of the process, instead of its pid. This information can be retrieved from
tracefs/saved_cmdlines file.
The _tep_register_comm()_ function registers a _pid_ / process name mapping.
If a command with the same _pid_ is already registered, an error is returned.
The _pid_ argument is the process ID, the _comm_ argument is the process name,
_tep_ is the event context. The _comm_ is duplicated internally.
The _tep_override_comm()_ function registers a _pid_ / process name mapping.
If a process with the same pid is already registered, the process name string is
udapted with the new one. The _pid_ argument is the process ID, the _comm_
argument is the process name, _tep_ is the event context. The _comm_ is
duplicated internally.
The _tep_is_pid_registered()_ function checks if a pid has a process name
mapping registered. The _pid_ argument is the process ID, _tep_ is the event
context.
The _tep_data_comm_from_pid()_ function returns the process name for a given
pid. The _pid_ argument is the process ID, _tep_ is the event context.
The returned string should not be freed, but will be freed when the _tep_
handler is closed.
The _tep_data_pid_from_comm()_ function returns a pid for a given process name.
The _comm_ argument is the process name, _tep_ is the event context.
The argument _next_ is the cmdline structure to search for the next pid.
As there may be more than one pid for a given process, the result of this call
can be passed back into a recurring call in the _next_ parameter, to search for
the next pid. If _next_ is NULL, it will return the first pid associated with
the _comm_. The function performs a linear search, so it may be slow.
The _tep_cmdline_pid()_ function returns the pid associated with a given
_cmdline_. The _tep_ argument is the event context.
RETURN VALUE
------------
_tep_register_comm()_ function returns 0 on success. In case of an error -1 is
returned and errno is set to indicate the cause of the problem: ENOMEM, if there
is not enough memory to duplicate the _comm_ or EEXIST if a mapping for this
_pid_ is already registered.
_tep_override_comm()_ function returns 0 on success. In case of an error -1 is
returned and errno is set to indicate the cause of the problem: ENOMEM, if there
is not enough memory to duplicate the _comm_.
_tep_is_pid_registered()_ function returns true if the _pid_ has a process name
mapped to it, false otherwise.
_tep_data_comm_from_pid()_ function returns the process name as string, or the
string "<...>" if there is no mapping for the given pid.
_tep_data_pid_from_comm()_ function returns a pointer to a struct cmdline, that
holds a pid for a given process, or NULL if none is found. This result can be
passed back into a recurring call as the _next_ parameter of the function.
_tep_cmdline_pid()_ functions returns the pid for the give cmdline. If _cmdline_
is NULL, then -1 is returned.
EXAMPLE
-------
The following example registers pid for command "ls", in context of event _tep_
and performs various searches for pid / process name mappings:
[source,c]
--
#include <event-parse.h>
...
int ret;
int ls_pid = 1021;
struct tep_handle *tep = tep_alloc();
...
ret = tep_register_comm(tep, "ls", ls_pid);
if (ret != 0 && errno == EEXIST)
ret = tep_override_comm(tep, "ls", ls_pid);
if (ret != 0) {
/* Failed to register pid / command mapping */
}
...
if (tep_is_pid_registered(tep, ls_pid) == 0) {
/* Command mapping for ls_pid is not registered */
}
...
const char *comm = tep_data_comm_from_pid(tep, ls_pid);
if (comm) {
/* Found process name for ls_pid */
}
...
int pid;
struct cmdline *cmd = tep_data_pid_from_comm(tep, "ls", NULL);
while (cmd) {
pid = tep_cmdline_pid(tep, cmd);
/* Found pid for process "ls" */
cmd = tep_data_pid_from_comm(tep, "ls", cmd);
}
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,77 @@
libtraceevent(3)
================
NAME
----
tep_get_cpus, tep_set_cpus - Get / set the number of CPUs, which have a tracing
buffer representing it. Note, the buffer may be empty.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
int *tep_get_cpus*(struct tep_handle pass:[*]_tep_);
void *tep_set_cpus*(struct tep_handle pass:[*]_tep_, int _cpus_);
--
DESCRIPTION
-----------
The _tep_get_cpus()_ function gets the number of CPUs, which have a tracing
buffer representing it. The _tep_ argument is trace event parser context.
The _tep_set_cpus()_ function sets the number of CPUs, which have a tracing
buffer representing it. The _tep_ argument is trace event parser context.
The _cpu_ argument is the number of CPUs with tracing data.
RETURN VALUE
------------
The _tep_get_cpus()_ functions returns the number of CPUs, which have tracing
data recorded.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
tep_set_cpus(tep, 5);
...
printf("We have tracing data for %d CPUs", tep_get_cpus(tep));
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,78 @@
libtraceevent(3)
================
NAME
----
tep_read_number - Reads a number from raw data.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
unsigned long long *tep_read_number*(struct tep_handle pass:[*]_tep_, const void pass:[*]_ptr_, int _size_);
--
DESCRIPTION
-----------
The _tep_read_number()_ function reads an integer from raw data, taking into
account the endianness of the raw data and the current host. The _tep_ argument
is the trace event parser context. The _ptr_ is a pointer to the raw data, where
the integer is, and the _size_ is the size of the integer.
RETURN VALUE
------------
The _tep_read_number()_ function returns the integer in the byte order of
the current host. In case of an error, 0 is returned.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
void process_record(struct tep_record *record)
{
int offset = 24;
int data = tep_read_number(tep, record->data + offset, 4);
/* Read the 4 bytes at the offset 24 of data as an integer */
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,103 @@
libtraceevent(3)
================
NAME
----
tep_find_event,tep_find_event_by_name,tep_find_event_by_record -
Find events by given key.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
struct tep_event pass:[*]*tep_find_event*(struct tep_handle pass:[*]_tep_, int _id_);
struct tep_event pass:[*]*tep_find_event_by_name*(struct tep_handle pass:[*]_tep_, const char pass:[*]_sys_, const char pass:[*]_name_);
struct tep_event pass:[*]*tep_find_event_by_record*(struct tep_handle pass:[*]_tep_, struct tep_record pass:[*]_record_);
--
DESCRIPTION
-----------
This set of functions can be used to search for an event, based on a given
criteria. All functions require a pointer to a _tep_, trace event parser
context.
The _tep_find_event()_ function searches for an event by given event _id_. The
event ID is assigned dynamically and can be viewed in event's format file,
"ID" field.
The tep_find_event_by_name()_ function searches for an event by given
event _name_, under the system _sys_. If the _sys_ is NULL (not specified),
the first event with _name_ is returned.
The tep_find_event_by_record()_ function searches for an event from a given
_record_.
RETURN VALUE
------------
All these functions return a pointer to the found event, or NULL if there is no
such event.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
struct tep_event *event;
event = tep_find_event(tep, 1857);
if (event == NULL) {
/* There is no event with ID 1857 */
}
event = tep_find_event_by_name(tep, "kvm", "kvm_exit");
if (event == NULL) {
/* There is no kvm_exit event, from kvm system */
}
void event_from_record(struct tep_record *record)
{
struct tep_event *event = tep_find_event_by_record(tep, record);
if (event == NULL) {
/* There is no event from given record */
}
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,99 @@
libtraceevent(3)
================
NAME
----
tep_get_event, tep_get_first_event, tep_get_events_count - Access events.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
struct tep_event pass:[*]*tep_get_event*(struct tep_handle pass:[*]_tep_, int _index_);
struct tep_event pass:[*]*tep_get_first_event*(struct tep_handle pass:[*]_tep_);
int *tep_get_events_count*(struct tep_handle pass:[*]_tep_);
--
DESCRIPTION
-----------
The _tep_get_event()_ function returns a pointer to event at the given _index_.
The _tep_ argument is trace event parser context, the _index_ is the index of
the requested event.
The _tep_get_first_event()_ function returns a pointer to the first event.
As events are stored in an array, this function returns the pointer to the
beginning of the array. The _tep_ argument is trace event parser context.
The _tep_get_events_count()_ function returns the number of the events
in the array. The _tep_ argument is trace event parser context.
RETURN VALUE
------------
The _tep_get_event()_ returns a pointer to the event located at _index_.
NULL is returned in case of error, in case there are no events or _index_ is
out of range.
The _tep_get_first_event()_ returns a pointer to the first event. NULL is
returned in case of error, or in case there are no events.
The _tep_get_events_count()_ returns the number of the events. 0 is
returned in case of error, or in case there are no events.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
int i,count = tep_get_events_count(tep);
struct tep_event *event, *events = tep_get_first_event(tep);
if (events == NULL) {
/* There are no events */
} else {
for (i = 0; i < count; i++) {
event = (events+i);
/* process events[i] */
}
/* Get the last event */
event = tep_get_event(tep, count-1);
}
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,122 @@
libtraceevent(3)
================
NAME
----
tep_list_events, tep_list_events_copy -
Get list of events, sorted by given criteria.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
enum *tep_event_sort_type* {
_TEP_EVENT_SORT_ID_,
_TEP_EVENT_SORT_NAME_,
_TEP_EVENT_SORT_SYSTEM_,
};
struct tep_event pass:[*]pass:[*]*tep_list_events*(struct tep_handle pass:[*]_tep_, enum tep_event_sort_type _sort_type_);
struct tep_event pass:[*]pass:[*]*tep_list_events_copy*(struct tep_handle pass:[*]_tep_, enum tep_event_sort_type _sort_type_);
--
DESCRIPTION
-----------
The _tep_list_events()_ function returns an array of pointers to the events,
sorted by the _sort_type_ criteria. The last element of the array is NULL.
The returned memory must not be freed, it is managed by the library.
The function is not thread safe. The _tep_ argument is trace event parser
context. The _sort_type_ argument is the required sort criteria:
[verse]
--
_TEP_EVENT_SORT_ID_ - sort by the event ID.
_TEP_EVENT_SORT_NAME_ - sort by the event (name, system, id) triplet.
_TEP_EVENT_SORT_SYSTEM_ - sort by the event (system, name, id) triplet.
--
The _tep_list_events_copy()_ is a thread safe version of _tep_list_events()_.
It has the same behavior, but the returned array is allocated internally and
must be freed by the caller. Note that the content of the array must not be
freed (see the EXAMPLE below).
RETURN VALUE
------------
The _tep_list_events()_ function returns an array of pointers to events.
In case of an error, NULL is returned. The returned array must not be freed,
it is managed by the library.
The _tep_list_events_copy()_ function returns an array of pointers to events.
In case of an error, NULL is returned. The returned array must be freed by
the caller.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
int i;
struct tep_event_format **events;
i=0;
events = tep_list_events(tep, TEP_EVENT_SORT_ID);
if (events == NULL) {
/* Failed to get the events, sorted by ID */
} else {
while(events[i]) {
/* walk through the list of the events, sorted by ID */
i++;
}
}
i=0;
events = tep_list_events_copy(tep, TEP_EVENT_SORT_NAME);
if (events == NULL) {
/* Failed to get the events, sorted by name */
} else {
while(events[i]) {
/* walk through the list of the events, sorted by name */
i++;
}
free(events);
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,118 @@
libtraceevent(3)
================
NAME
----
tep_find_common_field, tep_find_field, tep_find_any_field -
Search for a field in an event.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
struct tep_format_field pass:[*]*tep_find_common_field*(struct tep_event pass:[*]_event_, const char pass:[*]_name_);
struct tep_format_field pass:[*]*tep_find_field*(struct tep_event_ormat pass:[*]_event_, const char pass:[*]_name_);
struct tep_format_field pass:[*]*tep_find_any_field*(struct tep_event pass:[*]_event_, const char pass:[*]_name_);
--
DESCRIPTION
-----------
These functions search for a field with given name in an event. The field
returned can be used to find the field content from within a data record.
The _tep_find_common_field()_ function searches for a common field with _name_
in the _event_.
The _tep_find_field()_ function searches for an event specific field with
_name_ in the _event_.
The _tep_find_any_field()_ function searches for any field with _name_ in the
_event_.
RETURN VALUE
------------
The _tep_find_common_field(), _tep_find_field()_ and _tep_find_any_field()_
functions return a pointer to the found field, or NULL in case there is no field
with the requested name.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
void get_htimer_info(struct tep_handle *tep, struct tep_record *record)
{
struct tep_format_field *field;
struct tep_event *event;
long long softexpires;
int mode;
int pid;
event = tep_find_event_by_name(tep, "timer", "hrtimer_start");
field = tep_find_common_field(event, "common_pid");
if (field == NULL) {
/* Cannot find "common_pid" field in the event */
} else {
/* Get pid from the data record */
pid = tep_read_number(tep, record->data + field->offset,
field->size);
}
field = tep_find_field(event, "softexpires");
if (field == NULL) {
/* Cannot find "softexpires" event specific field in the event */
} else {
/* Get softexpires parameter from the data record */
softexpires = tep_read_number(tep, record->data + field->offset,
field->size);
}
field = tep_find_any_field(event, "mode");
if (field == NULL) {
/* Cannot find "mode" field in the event */
} else
{
/* Get mode parameter from the data record */
mode = tep_read_number(tep, record->data + field->offset,
field->size);
}
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,122 @@
libtraceevent(3)
================
NAME
----
tep_get_any_field_val, tep_get_common_field_val, tep_get_field_val,
tep_get_field_raw - Get value of a field.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
*#include <trace-seq.h>*
int *tep_get_any_field_val*(struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, unsigned long long pass:[*]_val_, int _err_);
int *tep_get_common_field_val*(struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, unsigned long long pass:[*]_val_, int _err_);
int *tep_get_field_val*(struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, unsigned long long pass:[*]_val_, int _err_);
void pass:[*]*tep_get_field_raw*(struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, int pass:[*]_len_, int _err_);
--
DESCRIPTION
-----------
These functions can be used to find a field and retrieve its value.
The _tep_get_any_field_val()_ function searches in the _record_ for a field
with _name_, part of the _event_. If the field is found, its value is stored in
_val_. If there is an error and _err_ is not zero, then an error string is
written into _s_.
The _tep_get_common_field_val()_ function does the same as
_tep_get_any_field_val()_, but searches only in the common fields. This works
for any event as all events include the common fields.
The _tep_get_field_val()_ function does the same as _tep_get_any_field_val()_,
but searches only in the event specific fields.
The _tep_get_field_raw()_ function searches in the _record_ for a field with
_name_, part of the _event_. If the field is found, a pointer to where the field
exists in the record's raw data is returned. The size of the data is stored in
_len_. If there is an error and _err_ is not zero, then an error string is
written into _s_.
RETURN VALUE
------------
The _tep_get_any_field_val()_, _tep_get_common_field_val()_ and
_tep_get_field_val()_ functions return 0 on success, or -1 in case of an error.
The _tep_get_field_raw()_ function returns a pointer to field's raw data, and
places the length of this data in _len_. In case of an error NULL is returned.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
#include <trace-seq.h>
...
struct tep_handle *tep = tep_alloc();
...
struct tep_event *event = tep_find_event_by_name(tep, "kvm", "kvm_exit");
...
void process_record(struct tep_record *record)
{
int len;
char *comm;
struct tep_event_format *event;
unsigned long long val;
event = tep_find_event_by_record(pevent, record);
if (event != NULL) {
if (tep_get_common_field_val(NULL, event, "common_type",
record, &val, 0) == 0) {
/* Got the value of common type field */
}
if (tep_get_field_val(NULL, event, "pid", record, &val, 0) == 0) {
/* Got the value of pid specific field */
}
comm = tep_get_field_raw(NULL, event, "comm", record, &len, 0);
if (comm != NULL) {
/* Got a pointer to the comm event specific field */
}
}
}
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*trace-seq.h*
Header file to include in order to have access to trace sequences
related APIs. Trace sequences are used to allow a function to call
several other functions to create a string of data to use.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,126 @@
libtraceevent(3)
================
NAME
----
tep_print_field, tep_print_fields, tep_print_num_field, tep_print_func_field -
Print the field content.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
*#include <trace-seq.h>*
void *tep_print_field*(struct trace_seq pass:[*]_s_, void pass:[*]_data_, struct tep_format_field pass:[*]_field_);
void *tep_print_fields*(struct trace_seq pass:[*]_s_, void pass:[*]_data_, int _size_, struct tep_event pass:[*]_event_);
int *tep_print_num_field*(struct trace_seq pass:[*]_s_, const char pass:[*]_fmt_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, int _err_);
int *tep_print_func_field*(struct trace_seq pass:[*]_s_, const char pass:[*]_fmt_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, int _err_);
--
DESCRIPTION
-----------
These functions print recorded field's data, according to the field's type.
The _tep_print_field()_ function extracts from the recorded raw _data_ value of
the _field_ and prints it into _s_, according to the field type.
The _tep_print_fields()_ prints each field name followed by the record's field
value according to the field's type:
[verse]
--
"field1_name=field1_value field2_name=field2_value ..."
--
It iterates all fields of the _event_, and calls _tep_print_field()_ for each of
them.
The _tep_print_num_field()_ function prints a numeric field with given format
string. A search is performed in the _event_ for a field with _name_. If such
field is found, its value is extracted from the _record_ and is printed in the
_s_, according to the given format string _fmt_. If the argument _err_ is
non-zero, and an error occures - it is printed in the _s_.
The _tep_print_func_field()_ function prints a function field with given format
string. A search is performed in the _event_ for a field with _name_. If such
field is found, its value is extracted from the _record_. The value is assumed
to be a function address, and a search is perform to find the name of this
function. The function name (if found) and its address are printed in the _s_,
according to the given format string _fmt_. If the argument _err_ is non-zero,
and an error occures - it is printed in _s_.
RETURN VALUE
------------
The _tep_print_num_field()_ and _tep_print_func_field()_ functions return 1
on success, -1 in case of an error or 0 if the print buffer _s_ is full.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
#include <trace-seq.h>
...
struct tep_handle *tep = tep_alloc();
...
struct trace_seq seq;
trace_seq_init(&seq);
struct tep_event *event = tep_find_event_by_name(tep, "timer", "hrtimer_start");
...
void process_record(struct tep_record *record)
{
struct tep_format_field *field_pid = tep_find_common_field(event, "common_pid");
trace_seq_reset(&seq);
/* Print the value of "common_pid" */
tep_print_field(&seq, record->data, field_pid);
/* Print all fields of the "hrtimer_start" event */
tep_print_fields(&seq, record->data, record->size, event);
/* Print the value of "expires" field with custom format string */
tep_print_num_field(&seq, " timer expires in %llu ", event, "expires", record, 0);
/* Print the address and the name of "function" field with custom format string */
tep_print_func_field(&seq, " timer function is %s ", event, "function", record, 0);
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*trace-seq.h*
Header file to include in order to have access to trace sequences related APIs.
Trace sequences are used to allow a function to call several other functions
to create a string of data to use.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,81 @@
libtraceevent(3)
================
NAME
----
tep_read_number_field - Reads a number from raw data.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
int *tep_read_number_field*(struct tep_format_field pass:[*]_field_, const void pass:[*]_data_, unsigned long long pass:[*]_value_);
--
DESCRIPTION
-----------
The _tep_read_number_field()_ function reads the value of the _field_ from the
raw _data_ and stores it in the _value_. The function sets the _value_ according
to the endianness of the raw data and the current machine and stores it in
_value_.
RETURN VALUE
------------
The _tep_read_number_field()_ function retunrs 0 in case of success, or -1 in
case of an error.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
struct tep_event *event = tep_find_event_by_name(tep, "timer", "hrtimer_start");
...
void process_record(struct tep_record *record)
{
unsigned long long pid;
struct tep_format_field *field_pid = tep_find_common_field(event, "common_pid");
if (tep_read_number_field(field_pid, record->data, &pid) != 0) {
/* Failed to get "common_pid" value */
}
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,105 @@
libtraceevent(3)
================
NAME
----
tep_event_common_fields, tep_event_fields - Get a list of fields for an event.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
struct tep_format_field pass:[*]pass:[*]*tep_event_common_fields*(struct tep_event pass:[*]_event_);
struct tep_format_field pass:[*]pass:[*]*tep_event_fields*(struct tep_event pass:[*]_event_);
--
DESCRIPTION
-----------
The _tep_event_common_fields()_ function returns an array of pointers to common
fields for the _event_. The array is allocated in the function and must be freed
by free(). The last element of the array is NULL.
The _tep_event_fields()_ function returns an array of pointers to event specific
fields for the _event_. The array is allocated in the function and must be freed
by free(). The last element of the array is NULL.
RETURN VALUE
------------
Both _tep_event_common_fields()_ and _tep_event_fields()_ functions return
an array of pointers to tep_format_field structures in case of success, or
NULL in case of an error.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
int i;
struct tep_format_field **fields;
struct tep_event *event = tep_find_event_by_name(tep, "kvm", "kvm_exit");
if (event != NULL) {
fields = tep_event_common_fields(event);
if (fields != NULL) {
i = 0;
while (fields[i]) {
/*
walk through the list of the common fields
of the kvm_exit event
*/
i++;
}
free(fields);
}
fields = tep_event_fields(event);
if (fields != NULL) {
i = 0;
while (fields[i]) {
/*
walk through the list of the event specific
fields of the kvm_exit event
*/
i++;
}
free(fields);
}
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,91 @@
libtraceevent(3)
================
NAME
----
tep_is_file_bigendian, tep_set_file_bigendian - Get / set the endianness of the
raw data being accessed by the tep handler.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
enum *tep_endian* {
TEP_LITTLE_ENDIAN = 0,
TEP_BIG_ENDIAN
};
bool *tep_is_file_bigendian*(struct tep_handle pass:[*]_tep_);
void *tep_set_file_bigendian*(struct tep_handle pass:[*]_tep_, enum tep_endian _endian_);
--
DESCRIPTION
-----------
The _tep_is_file_bigendian()_ function gets the endianness of the raw data,
being accessed by the tep handler. The _tep_ argument is trace event parser
context.
The _tep_set_file_bigendian()_ function sets the endianness of raw data being
accessed by the tep handler. The _tep_ argument is trace event parser context.
[verse]
--
The _endian_ argument is the endianness:
_TEP_LITTLE_ENDIAN_ - the raw data is in little endian format,
_TEP_BIG_ENDIAN_ - the raw data is in big endian format.
--
RETURN VALUE
------------
The _tep_is_file_bigendian()_ function returns true if the data is in bigendian
format, false otherwise.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
tep_set_file_bigendian(tep, TEP_LITTLE_ENDIAN);
...
if (tep_is_file_bigendian(tep)) {
/* The raw data is in big endian */
} else {
/* The raw data is in little endian */
}
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,209 @@
libtraceevent(3)
================
NAME
----
tep_filter_alloc, tep_filter_free, tep_filter_reset, tep_filter_make_string,
tep_filter_copy, tep_filter_compare, tep_filter_match, tep_event_filtered,
tep_filter_remove_event, tep_filter_strerror, tep_filter_add_filter_str -
Event filter related APIs.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
struct tep_event_filter pass:[*]*tep_filter_alloc*(struct tep_handle pass:[*]_tep_);
void *tep_filter_free*(struct tep_event_filter pass:[*]_filter_);
void *tep_filter_reset*(struct tep_event_filter pass:[*]_filter_);
enum tep_errno *tep_filter_add_filter_str*(struct tep_event_filter pass:[*]_filter_, const char pass:[*]_filter_str_);
int *tep_event_filtered*(struct tep_event_filter pass:[*]_filter_, int _event_id_);
int *tep_filter_remove_event*(struct tep_event_filter pass:[*]_filter_, int _event_id_);
enum tep_errno *tep_filter_match*(struct tep_event_filter pass:[*]_filter_, struct tep_record pass:[*]_record_);
int *tep_filter_copy*(struct tep_event_filter pass:[*]_dest_, struct tep_event_filter pass:[*]_source_);
int *tep_filter_compare*(struct tep_event_filter pass:[*]_filter1_, struct tep_event_filter pass:[*]_filter2_);
char pass:[*]*tep_filter_make_string*(struct tep_event_filter pass:[*]_filter_, int _event_id_);
int *tep_filter_strerror*(struct tep_event_filter pass:[*]_filter_, enum tep_errno _err_, char pass:[*]buf, size_t _buflen_);
--
DESCRIPTION
-----------
Filters can be attached to traced events. They can be used to filter out various
events when outputting them. Each event can be filtered based on its parameters,
described in the event's format file. This set of functions can be used to
create, delete, modify and attach event filters.
The _tep_filter_alloc()_ function creates a new event filter. The _tep_ argument
is the trace event parser context.
The _tep_filter_free()_ function frees an event filter and all resources that it
had used.
The _tep_filter_reset()_ function removes all rules from an event filter and
resets it.
The _tep_filter_add_filter_str()_ function adds a new rule to the _filter_. The
_filter_str_ argument is the filter string, that contains the rule.
The _tep_event_filtered()_ function checks if the event with _event_id_ has
_filter_.
The _tep_filter_remove_event()_ function removes a _filter_ for an event with
_event_id_.
The _tep_filter_match()_ function tests if a _record_ matches given _filter_.
The _tep_filter_copy()_ function copies a _source_ filter into a _dest_ filter.
The _tep_filter_compare()_ function compares two filers - _filter1_ and _filter2_.
The _tep_filter_make_string()_ function constructs a string, displaying
the _filter_ contents for given _event_id_.
The _tep_filter_strerror()_ function copies the _filter_ error buffer into the
given _buf_ with the size _buflen_. If the error buffer is empty, in the _buf_
is copied a string, describing the error _err_.
RETURN VALUE
------------
The _tep_filter_alloc()_ function returns a pointer to the newly created event
filter, or NULL in case of an error.
The _tep_filter_add_filter_str()_ function returns 0 if the rule was
successfully added or a negative error code. Use _tep_filter_strerror()_ to see
actual error message in case of an error.
The _tep_event_filtered()_ function returns 1 if the filter is found for given
event, or 0 otherwise.
The _tep_filter_remove_event()_ function returns 1 if the vent was removed, or
0 if the event was not found.
The _tep_filter_match()_ function returns _tep_errno_, according to the result:
[verse]
--
_pass:[TEP_ERRNO__FILTER_MATCH]_ - filter found for event, the record matches.
_pass:[TEP_ERRNO__FILTER_MISS]_ - filter found for event, the record does not match.
_pass:[TEP_ERRNO__FILTER_NOT_FOUND]_ - no filter found for record's event.
_pass:[TEP_ERRNO__NO_FILTER]_ - no rules in the filter.
--
or any other _tep_errno_, if an error occurred during the test.
The _tep_filter_copy()_ function returns 0 on success or -1 if not all rules
were copied.
The _tep_filter_compare()_ function returns 1 if the two filters hold the same
content, or 0 if they do not.
The _tep_filter_make_string()_ function returns a string, which must be freed
with free(), or NULL in case of an error.
The _tep_filter_strerror()_ function returns 0 if message was filled
successfully, or -1 in case of an error.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
char errstr[200];
int ret;
struct tep_event_filter *filter = tep_filter_alloc(tep);
struct tep_event_filter *filter1 = tep_filter_alloc(tep);
ret = tep_filter_add_filter_str(filter, "sched/sched_wakeup:target_cpu==1");
if(ret < 0) {
tep_filter_strerror(filter, ret, errstr, sizeof(errstr));
/* Failed to add a new rule to the filter, the error string is in errstr */
}
if (tep_filter_copy(filter1, filter) != 0) {
/* Failed to copy filter in filter1 */
}
...
if (tep_filter_compare(filter, filter1) != 1) {
/* Both filters are different */
}
...
void process_record(struct tep_handle *tep, struct tep_record *record)
{
struct tep_event *event;
char *fstring;
event = tep_find_event_by_record(tep, record);
if (tep_event_filtered(filter, event->id) == 1) {
/* The event has filter */
fstring = tep_filter_make_string(filter, event->id);
if (fstring != NULL) {
/* The filter for the event is in fstring */
free(fstring);
}
}
switch (tep_filter_match(filter, record)) {
case TEP_ERRNO__FILTER_MATCH:
/* The filter matches the record */
break;
case TEP_ERRNO__FILTER_MISS:
/* The filter does not match the record */
break;
case TEP_ERRNO__FILTER_NOT_FOUND:
/* No filter found for record's event */
break;
case TEP_ERRNO__NO_FILTER:
/* There are no rules in the filter */
break
default:
/* An error occurred during the test */
break;
}
if (tep_filter_remove_event(filter, event->id) == 1) {
/* The event was removed from the filter */
}
}
...
tep_filter_reset(filter);
...
tep_filter_free(filter);
tep_filter_free(filter1);
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,183 @@
libtraceevent(3)
================
NAME
----
tep_find_function, tep_find_function_address, tep_set_function_resolver,
tep_reset_function_resolver, tep_register_function, tep_register_print_string -
function related tep APIs
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
typedef char pass:[*](*tep_func_resolver_t*)(void pass:[*]_priv_, unsigned long long pass:[*]_addrp_, char pass:[**]_modp_);
int *tep_set_function_resolver*(struct tep_handle pass:[*]_tep_, tep_func_resolver_t pass:[*]_func_, void pass:[*]_priv_);
void *tep_reset_function_resolver*(struct tep_handle pass:[*]_tep_);
const char pass:[*]*tep_find_function*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
unsigned long long *tep_find_function_address*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
int *tep_register_function*(struct tep_handle pass:[*]_tep_, char pass:[*]_name_, unsigned long long _addr_, char pass:[*]_mod_);
int *tep_register_print_string*(struct tep_handle pass:[*]_tep_, const char pass:[*]_fmt_, unsigned long long _addr_);
--
DESCRIPTION
-----------
Some tools may have already a way to resolve the kernel functions. These APIs
allow them to keep using it instead of duplicating all the entries inside.
The _tep_func_resolver_t_ type is the prototype of the alternative kernel
functions resolver. This function receives a pointer to its custom context
(set with the _tep_set_function_resolver()_ call ) and the address of a kernel
function, which has to be resolved. In case of success, it should return
the name of the function and its module (if any) in _modp_.
The _tep_set_function_resolver()_ function registers _func_ as an alternative
kernel functions resolver. The _tep_ argument is trace event parser context.
The _priv_ argument is a custom context of the _func_ function. The function
resolver is used by the APIs _tep_find_function()_,
_tep_find_function_address()_, and _tep_print_func_field()_ to resolve
a function address to a function name.
The _tep_reset_function_resolver()_ function resets the kernel functions
resolver to the default function. The _tep_ argument is trace event parser
context.
These APIs can be used to find function name and start address, by given
address. The given address does not have to be exact, it will select
the function that would contain it.
The _tep_find_function()_ function returns the function name, which contains the
given address _addr_. The _tep_ argument is the trace event parser context.
The _tep_find_function_address()_ function returns the function start address,
by given address _addr_. The _addr_ does not have to be exact, it will select
the function that would contain it. The _tep_ argument is the trace event
parser context.
The _tep_register_function()_ function registers a function name mapped to an
address and (optional) module. This mapping is used in case the function tracer
or events have "%pF" or "%pS" parameter in its format string. It is common to
pass in the kallsyms function names with their corresponding addresses with this
function. The _tep_ argument is the trace event parser context. The _name_ is
the name of the function, the string is copied internally. The _addr_ is
the start address of the function. The _mod_ is the kernel module
the function may be in (NULL for none).
The _tep_register_print_string()_ function registers a string by the address
it was stored in the kernel. Some strings internal to the kernel with static
address are passed to certain events. The "%s" in the event's format field
which has an address needs to know what string would be at that address. The
tep_register_print_string() supplies the parsing with the mapping between kernel
addresses and those strings. The _tep_ argument is the trace event parser
context. The _fmt_ is the string to register, it is copied internally.
The _addr_ is the address the string was located at.
RETURN VALUE
------------
The _tep_set_function_resolver()_ function returns 0 in case of success, or -1
in case of an error.
The _tep_find_function()_ function returns the function name, or NULL in case
it cannot be found.
The _tep_find_function_address()_ function returns the function start address,
or 0 in case it cannot be found.
The _tep_register_function()_ function returns 0 in case of success. In case of
an error -1 is returned, and errno is set to the appropriate error number.
The _tep_register_print_string()_ function returns 0 in case of success. In case
of an error -1 is returned, and errno is set to the appropriate error number.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
char *my_resolve_kernel_addr(void *context,
unsigned long long *addrp, char **modp)
{
struct db *function_database = context;
struct symbol *sym = sql_lookup(function_database, *addrp);
if (!sym)
return NULL;
*modp = sym->module_name;
return sym->name;
}
void show_function( unsigned long long addr)
{
unsigned long long fstart;
const char *fname;
if (tep_set_function_resolver(tep, my_resolve_kernel_addr,
function_database) != 0) {
/* failed to register my_resolve_kernel_addr */
}
/* These APIs use my_resolve_kernel_addr() to resolve the addr */
fname = tep_find_function(tep, addr);
fstart = tep_find_function_address(tep, addr);
/*
addr is in function named fname, starting at fstart address,
at offset (addr - fstart)
*/
tep_reset_function_resolver(tep);
}
...
if (tep_register_function(tep, "kvm_exit",
(unsigned long long) 0x12345678, "kvm") != 0) {
/* Failed to register kvm_exit address mapping */
}
...
if (tep_register_print_string(tep, "print string",
(unsigned long long) 0x87654321, NULL) != 0) {
/* Failed to register "print string" address mapping */
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,88 @@
libtraceevent(3)
================
NAME
----
tep_find_function,tep_find_function_address - Find function name / start address.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
const char pass:[*]*tep_find_function*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
unsigned long long *tep_find_function_address*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
--
DESCRIPTION
-----------
These functions can be used to find function name and start address, by given
address. The given address does not have to be exact, it will select the function
that would contain it.
The _tep_find_function()_ function returns the function name, which contains the
given address _addr_. The _tep_ argument is the trace event parser context.
The _tep_find_function_address()_ function returns the function start address,
by given address _addr_. The _addr_ does not have to be exact, it will select the
function that would contain it. The _tep_ argument is the trace event parser context.
RETURN VALUE
------------
The _tep_find_function()_ function returns the function name, or NULL in case
it cannot be found.
The _tep_find_function_address()_ function returns the function start address,
or 0 in case it cannot be found.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
void show_function( unsigned long long addr)
{
const char *fname = tep_find_function(tep, addr);
unsigned long long fstart = tep_find_function_address(tep, addr);
/* addr is in function named fname, starting at fstart address, at offset (addr - fstart) */
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,101 @@
libtraceevent(3)
================
NAME
----
tep_alloc, tep_free,tep_ref, tep_unref,tep_ref_get - Create, destroy, manage
references of trace event parser context.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
struct tep_handle pass:[*]*tep_alloc*(void);
void *tep_free*(struct tep_handle pass:[*]_tep_);
void *tep_ref*(struct tep_handle pass:[*]_tep_);
void *tep_unref*(struct tep_handle pass:[*]_tep_);
int *tep_ref_get*(struct tep_handle pass:[*]_tep_);
--
DESCRIPTION
-----------
These are the main functions to create and destroy tep_handle - the main
structure, representing the trace event parser context. This context is used as
the input parameter of most library APIs.
The _tep_alloc()_ function allocates and initializes the tep context.
The _tep_free()_ function will decrement the reference of the _tep_ handler.
When there is no more references, then it will free the handler, as well
as clean up all its resources that it had used. The argument _tep_ is
the pointer to the trace event parser context.
The _tep_ref()_ function adds a reference to the _tep_ handler.
The _tep_unref()_ function removes a reference from the _tep_ handler. When
the last reference is removed, the _tep_ is destroyed, and all resources that
it had used are cleaned up.
The _tep_ref_get()_ functions gets the current references of the _tep_ handler.
RETURN VALUE
------------
_tep_alloc()_ returns a pointer to a newly created tep_handle structure.
NULL is returned in case there is not enough free memory to allocate it.
_tep_ref_get()_ returns the current references of _tep_.
If _tep_ is NULL, 0 is returned.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
int ref = tep_ref_get(tep);
tep_ref(tep);
if ( (ref+1) != tep_ref_get(tep)) {
/* Something wrong happened, the counter is not incremented by 1 */
}
tep_unref(tep);
...
tep_free(tep);
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,102 @@
libtraceevent(3)
================
NAME
----
tep_get_header_page_size, tep_get_header_timestamp_size, tep_is_old_format -
Get the data stored in the header page, in kernel context.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
int *tep_get_header_page_size*(struct tep_handle pass:[*]_tep_);
int *tep_get_header_timestamp_size*(struct tep_handle pass:[*]_tep_);
bool *tep_is_old_format*(struct tep_handle pass:[*]_tep_);
--
DESCRIPTION
-----------
These functions retrieve information from kernel context, stored in tracefs
events/header_page. Old kernels do not have header page info, so default values
from user space context are used.
The _tep_get_header_page_size()_ function returns the size of a long integer,
in kernel context. The _tep_ argument is trace event parser context.
This information is retrieved from tracefs events/header_page, "commit" field.
The _tep_get_header_timestamp_size()_ function returns the size of timestamps,
in kernel context. The _tep_ argument is trace event parser context. This
information is retrieved from tracefs events/header_page, "timestamp" field.
The _tep_is_old_format()_ function returns true if the kernel predates
the addition of events/header_page, otherwise it returns false.
RETURN VALUE
------------
The _tep_get_header_page_size()_ function returns the size of a long integer,
in bytes.
The _tep_get_header_timestamp_size()_ function returns the size of timestamps,
in bytes.
The _tep_is_old_format()_ function returns true, if an old kernel is used to
generate the tracing data, which has no event/header_page. If the kernel is new,
or _tep_ is NULL, false is returned.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
int longsize;
int timesize;
bool old;
longsize = tep_get_header_page_size(tep);
timesize = tep_get_header_timestamp_size(tep);
old = tep_is_old_format(tep);
printf ("%s kernel is used to generate the tracing data.\n",
old?"Old":"New");
printf("The size of a long integer is %d bytes.\n", longsize);
printf("The timestamps size is %d bytes.\n", timesize);
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,104 @@
libtraceevent(3)
================
NAME
----
tep_is_bigendian, tep_is_local_bigendian, tep_set_local_bigendian - Get / set
the endianness of the local machine.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
enum *tep_endian* {
TEP_LITTLE_ENDIAN = 0,
TEP_BIG_ENDIAN
};
int *tep_is_bigendian*(void);
bool *tep_is_local_bigendian*(struct tep_handle pass:[*]_tep_);
void *tep_set_local_bigendian*(struct tep_handle pass:[*]_tep_, enum tep_endian _endian_);
--
DESCRIPTION
-----------
The _tep_is_bigendian()_ gets the endianness of the machine, executing
the function.
The _tep_is_local_bigendian()_ function gets the endianness of the local
machine, saved in the _tep_ handler. The _tep_ argument is the trace event
parser context. This API is a bit faster than _tep_is_bigendian()_, as it
returns cached endianness of the local machine instead of checking it each time.
The _tep_set_local_bigendian()_ function sets the endianness of the local
machine in the _tep_ handler. The _tep_ argument is trace event parser context.
The _endian_ argument is the endianness:
[verse]
--
_TEP_LITTLE_ENDIAN_ - the machine is little endian,
_TEP_BIG_ENDIAN_ - the machine is big endian.
--
RETURN VALUE
------------
The _tep_is_bigendian()_ function returns non zero if the endianness of the
machine, executing the code, is big endian and zero otherwise.
The _tep_is_local_bigendian()_ function returns true, if the endianness of the
local machine, saved in the _tep_ handler, is big endian, or false otherwise.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
if (tep_is_bigendian())
tep_set_local_bigendian(tep, TEP_BIG_ENDIAN);
else
tep_set_local_bigendian(tep, TEP_LITTLE_ENDIAN);
...
if (tep_is_local_bigendian(tep))
printf("This machine you are running on is bigendian\n");
else
printf("This machine you are running on is little endian\n");
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,78 @@
libtraceevent(3)
================
NAME
----
tep_get_long_size, tep_set_long_size - Get / set the size of a long integer on
the machine, where the trace is generated, in bytes
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
int *tep_get_long_size*(strucqt tep_handle pass:[*]_tep_);
void *tep_set_long_size*(struct tep_handle pass:[*]_tep_, int _long_size_);
--
DESCRIPTION
-----------
The _tep_get_long_size()_ function returns the size of a long integer on the machine,
where the trace is generated. The _tep_ argument is trace event parser context.
The _tep_set_long_size()_ function sets the size of a long integer on the machine,
where the trace is generated. The _tep_ argument is trace event parser context.
The _long_size_ is the size of a long integer, in bytes.
RETURN VALUE
------------
The _tep_get_long_size()_ function returns the size of a long integer on the machine,
where the trace is generated, in bytes.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
tep_set_long_size(tep, 4);
...
int long_size = tep_get_long_size(tep);
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,82 @@
libtraceevent(3)
================
NAME
----
tep_get_page_size, tep_set_page_size - Get / set the size of a memory page on
the machine, where the trace is generated
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
int *tep_get_page_size*(struct tep_handle pass:[*]_tep_);
void *tep_set_page_size*(struct tep_handle pass:[*]_tep_, int _page_size_);
--
DESCRIPTION
-----------
The _tep_get_page_size()_ function returns the size of a memory page on
the machine, where the trace is generated. The _tep_ argument is trace
event parser context.
The _tep_set_page_size()_ function stores in the _tep_ context the size of a
memory page on the machine, where the trace is generated.
The _tep_ argument is trace event parser context.
The _page_size_ argument is the size of a memory page, in bytes.
RETURN VALUE
------------
The _tep_get_page_size()_ function returns size of the memory page, in bytes.
EXAMPLE
-------
[source,c]
--
#include <unistd.h>
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
int page_size = getpagesize();
tep_set_page_size(tep, page_size);
printf("The page size for this machine is %d\n", tep_get_page_size(tep));
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,90 @@
libtraceevent(3)
================
NAME
----
tep_parse_event, tep_parse_format - Parse the event format information
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
enum tep_errno *tep_parse_event*(struct tep_handle pass:[*]_tep_, const char pass:[*]_buf_, unsigned long _size_, const char pass:[*]_sys_);
enum tep_errno *tep_parse_format*(struct tep_handle pass:[*]_tep_, struct tep_event pass:[*]pass:[*]_eventp_, const char pass:[*]_buf_, unsigned long _size_, const char pass:[*]_sys_);
--
DESCRIPTION
-----------
The _tep_parse_event()_ function parses the event format and creates an event
structure to quickly parse raw data for a given event. The _tep_ argument is
the trace event parser context. The created event structure is stored in the
_tep_ context. The _buf_ argument is a buffer with _size_, where the event
format data is. The event format data can be taken from
tracefs/events/.../.../format files. The _sys_ argument is the system of
the event.
The _tep_parse_format()_ function does the same as _tep_parse_event()_. The only
difference is in the extra _eventp_ argument, where the newly created event
structure is returned.
RETURN VALUE
------------
Both _tep_parse_event()_ and _tep_parse_format()_ functions return 0 on success,
or TEP_ERRNO__... in case of an error.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
char *buf;
int size;
struct tep_event *event = NULL;
buf = read_file("/sys/kernel/tracing/events/ftrace/print/format", &size);
if (tep_parse_event(tep, buf, size, "ftrace") != 0) {
/* Failed to parse the ftrace print format */
}
if (tep_parse_format(tep, &event, buf, size, "ftrace") != 0) {
/* Failed to parse the ftrace print format */
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,82 @@
libtraceevent(3)
================
NAME
----
tep_parse_header_page - Parses the data stored in the header page.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
int *tep_parse_header_page*(struct tep_handle pass:[*]_tep_, char pass:[*]_buf_, unsigned long _size_, int _long_size_);
--
DESCRIPTION
-----------
The _tep_parse_header_page()_ function parses the header page data from _buf_,
and initializes the _tep_, trace event parser context, with it. The buffer
_buf_ is with _size_, and is supposed to be copied from
tracefs/events/header_page.
Some old kernels do not have header page info, in this case the
_tep_parse_header_page()_ function can be called with _size_ equal to 0. The
_tep_ context is initialized with default values. The _long_size_ can be used in
this use case, to set the size of a long integer to be used.
RETURN VALUE
------------
The _tep_parse_header_page()_ function returns 0 in case of success, or -1
in case of an error.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
char *buf;
int size;
buf = read_file("/sys/kernel/tracing/events/header_page", &size);
if (tep_parse_header_page(tep, buf, size, sizeof(unsigned long)) != 0) {
/* Failed to parse the header page */
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,137 @@
libtraceevent(3)
================
NAME
----
tep_data_type, tep_data_pid,tep_data_preempt_count, tep_data_flags -
Extract common fields from a record.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
enum *trace_flag_type* {
_TRACE_FLAG_IRQS_OFF_,
_TRACE_FLAG_IRQS_NOSUPPORT_,
_TRACE_FLAG_NEED_RESCHED_,
_TRACE_FLAG_HARDIRQ_,
_TRACE_FLAG_SOFTIRQ_,
};
int *tep_data_type*(struct tep_handle pass:[*]_tep_, struct tep_record pass:[*]_rec_);
int *tep_data_pid*(struct tep_handle pass:[*]_tep_, struct tep_record pass:[*]_rec_);
int *tep_data_preempt_count*(struct tep_handle pass:[*]_tep_, struct tep_record pass:[*]_rec_);
int *tep_data_flags*(struct tep_handle pass:[*]_tep_, struct tep_record pass:[*]_rec_);
--
DESCRIPTION
-----------
This set of functions can be used to extract common fields from a record.
The _tep_data_type()_ function gets the event id from the record _rec_.
It reads the "common_type" field. The _tep_ argument is the trace event parser
context.
The _tep_data_pid()_ function gets the process id from the record _rec_.
It reads the "common_pid" field. The _tep_ argument is the trace event parser
context.
The _tep_data_preempt_count()_ function gets the preemption count from the
record _rec_. It reads the "common_preempt_count" field. The _tep_ argument is
the trace event parser context.
The _tep_data_flags()_ function gets the latency flags from the record _rec_.
It reads the "common_flags" field. The _tep_ argument is the trace event parser
context. Supported latency flags are:
[verse]
--
_TRACE_FLAG_IRQS_OFF_, Interrupts are disabled.
_TRACE_FLAG_IRQS_NOSUPPORT_, Reading IRQ flag is not supported by the architecture.
_TRACE_FLAG_NEED_RESCHED_, Task needs rescheduling.
_TRACE_FLAG_HARDIRQ_, Hard IRQ is running.
_TRACE_FLAG_SOFTIRQ_, Soft IRQ is running.
--
RETURN VALUE
------------
The _tep_data_type()_ function returns an integer, representing the event id.
The _tep_data_pid()_ function returns an integer, representing the process id
The _tep_data_preempt_count()_ function returns an integer, representing the
preemption count.
The _tep_data_flags()_ function returns an integer, representing the latency
flags. Look at the _trace_flag_type_ enum for supported flags.
All these functions in case of an error return a negative integer.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
void process_record(struct tep_record *record)
{
int data;
data = tep_data_type(tep, record);
if (data >= 0) {
/* Got the ID of the event */
}
data = tep_data_pid(tep, record);
if (data >= 0) {
/* Got the process ID */
}
data = tep_data_preempt_count(tep, record);
if (data >= 0) {
/* Got the preemption count */
}
data = tep_data_flags(tep, record);
if (data >= 0) {
/* Got the latency flags */
}
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,156 @@
libtraceevent(3)
================
NAME
----
tep_register_event_handler, tep_unregister_event_handler - Register /
unregisters a callback function to parse an event information.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
enum *tep_reg_handler* {
_TEP_REGISTER_SUCCESS_,
_TEP_REGISTER_SUCCESS_OVERWRITE_,
};
int *tep_register_event_handler*(struct tep_handle pass:[*]_tep_, int _id_, const char pass:[*]_sys_name_, const char pass:[*]_event_name_, tep_event_handler_func _func_, void pass:[*]_context_);
int *tep_unregister_event_handler*(struct tep_handle pass:[*]tep, int id, const char pass:[*]sys_name, const char pass:[*]event_name, tep_event_handler_func func, void pass:[*]_context_);
typedef int (*pass:[*]tep_event_handler_func*)(struct trace_seq pass:[*]s, struct tep_record pass:[*]record, struct tep_event pass:[*]event, void pass:[*]context);
--
DESCRIPTION
-----------
The _tep_register_event_handler()_ function registers a handler function,
which is going to be called to parse the information for a given event.
The _tep_ argument is the trace event parser context. The _id_ argument is
the id of the event. The _sys_name_ argument is the name of the system,
the event belongs to. The _event_name_ argument is the name of the event.
If _id_ is >= 0, it is used to find the event, otherwise _sys_name_ and
_event_name_ are used. The _func_ is a pointer to the function, which is going
to be called to parse the event information. The _context_ argument is a pointer
to the context data, which will be passed to the _func_. If a handler function
for the same event is already registered, it will be overridden with the new
one. This mechanism allows a developer to override the parsing of a given event.
If for some reason the default print format is not sufficient, the developer
can register a function for an event to be used to parse the data instead.
The _tep_unregister_event_handler()_ function unregisters the handler function,
previously registered with _tep_register_event_handler()_. The _tep_ argument
is the trace event parser context. The _id_, _sys_name_, _event_name_, _func_,
and _context_ are the same arguments, as when the callback function _func_ was
registered.
The _tep_event_handler_func_ is the type of the custom event handler
function. The _s_ argument is the trace sequence, it can be used to create a
custom string, describing the event. A _record_ to get the event from is passed
as input parameter and also the _event_ - the handle to the record's event. The
_context_ is custom context, set when the custom event handler is registered.
RETURN VALUE
------------
The _tep_register_event_handler()_ function returns _TEP_REGISTER_SUCCESS_
if the new handler is registered successfully or
_TEP_REGISTER_SUCCESS_OVERWRITE_ if an existing handler is overwritten.
If there is not enough memory to complete the registration,
TEP_ERRNO__MEM_ALLOC_FAILED is returned.
The _tep_unregister_event_handler()_ function returns 0 if _func_ was removed
successful or, -1 if the event was not found.
The _tep_event_handler_func_ should return -1 in case of an error,
or 0 otherwise.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
#include <trace-seq.h>
...
struct tep_handle *tep = tep_alloc();
...
int timer_expire_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
trace_seq_printf(s, "hrtimer=");
if (tep_print_num_field(s, "0x%llx", event, "timer", record, 0) == -1)
tep_print_num_field(s, "0x%llx", event, "hrtimer", record, 1);
trace_seq_printf(s, " now=");
tep_print_num_field(s, "%llu", event, "now", record, 1);
tep_print_func_field(s, " function=%s", event, "function", record, 0);
return 0;
}
...
int ret;
ret = tep_register_event_handler(tep, -1, "timer", "hrtimer_expire_entry",
timer_expire_handler, NULL);
if (ret < 0) {
char buf[32];
tep_strerror(tep, ret, buf, 32)
printf("Failed to register handler for hrtimer_expire_entry: %s\n", buf);
} else {
switch (ret) {
case TEP_REGISTER_SUCCESS:
printf ("Registered handler for hrtimer_expire_entry\n");
break;
case TEP_REGISTER_SUCCESS_OVERWRITE:
printf ("Overwrote handler for hrtimer_expire_entry\n");
break;
}
}
...
ret = tep_unregister_event_handler(tep, -1, "timer", "hrtimer_expire_entry",
timer_expire_handler, NULL);
if ( ret )
printf ("Failed to unregister handler for hrtimer_expire_entry\n");
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*trace-seq.h*
Header file to include in order to have access to trace sequences
related APIs. Trace sequences are used to allow a function to call
several other functions to create a string of data to use.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,155 @@
libtraceevent(3)
================
NAME
----
tep_register_print_function,tep_unregister_print_function -
Registers / Unregisters a helper function.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
enum *tep_func_arg_type* {
TEP_FUNC_ARG_VOID,
TEP_FUNC_ARG_INT,
TEP_FUNC_ARG_LONG,
TEP_FUNC_ARG_STRING,
TEP_FUNC_ARG_PTR,
TEP_FUNC_ARG_MAX_TYPES
};
typedef unsigned long long (*pass:[*]tep_func_handler*)(struct trace_seq pass:[*]s, unsigned long long pass:[*]args);
int *tep_register_print_function*(struct tep_handle pass:[*]_tep_, tep_func_handler _func_, enum tep_func_arg_type _ret_type_, char pass:[*]_name_, _..._);
int *tep_unregister_print_function*(struct tep_handle pass:[*]_tep_, tep_func_handler _func_, char pass:[*]_name_);
--
DESCRIPTION
-----------
Some events may have helper functions in the print format arguments.
This allows a plugin to dynamically create a way to process one of
these functions.
The _tep_register_print_function()_ registers such helper function. The _tep_
argument is the trace event parser context. The _func_ argument is a pointer
to the helper function. The _ret_type_ argument is the return type of the
helper function, value from the _tep_func_arg_type_ enum. The _name_ is the name
of the helper function, as seen in the print format arguments. The _..._ is a
variable list of _tep_func_arg_type_ enums, the _func_ function arguments.
This list must end with _TEP_FUNC_ARG_VOID_. See 'EXAMPLE' section.
The _tep_unregister_print_function()_ unregisters a helper function, previously
registered with _tep_register_print_function()_. The _tep_ argument is the
trace event parser context. The _func_ and _name_ arguments are the same, used
when the helper function was registered.
The _tep_func_handler_ is the type of the helper function. The _s_ argument is
the trace sequence, it can be used to create a custom string.
The _args_ is a list of arguments, defined when the helper function was
registered.
RETURN VALUE
------------
The _tep_register_print_function()_ function returns 0 in case of success.
In case of an error, TEP_ERRNO_... code is returned.
The _tep_unregister_print_function()_ returns 0 in case of success, or -1 in
case of an error.
EXAMPLE
-------
Some events have internal functions calls, that appear in the print format
output. For example "tracefs/events/i915/g4x_wm/format" has:
[source,c]
--
print fmt: "pipe %c, frame=%u, scanline=%u, wm %d/%d/%d, sr %s/%d/%d/%d, hpll %s/%d/%d/%d, fbc %s",
((REC->pipe) + 'A'), REC->frame, REC->scanline, REC->primary,
REC->sprite, REC->cursor, yesno(REC->cxsr), REC->sr_plane,
REC->sr_cursor, REC->sr_fbc, yesno(REC->hpll), REC->hpll_plane,
REC->hpll_cursor, REC->hpll_fbc, yesno(REC->fbc)
--
Notice the call to function _yesno()_ in the print arguments. In the kernel
context, this function has the following implementation:
[source,c]
--
static const char *yesno(int x)
{
static const char *yes = "yes";
static const char *no = "no";
return x ? yes : no;
}
--
The user space event parser has no idea how to handle this _yesno()_ function.
The _tep_register_print_function()_ API can be used to register a user space
helper function, mapped to the kernel's _yesno()_:
[source,c]
--
#include <event-parse.h>
#include <trace-seq.h>
...
struct tep_handle *tep = tep_alloc();
...
static const char *yes_no_helper(int x)
{
return x ? "yes" : "no";
}
...
if ( tep_register_print_function(tep,
yes_no_helper,
TEP_FUNC_ARG_STRING,
"yesno",
TEP_FUNC_ARG_INT,
TEP_FUNC_ARG_VOID) != 0) {
/* Failed to register yes_no_helper function */
}
/*
Now, when the event parser encounters this yesno() function, it will know
how to handle it.
*/
...
if (tep_unregister_print_function(tep, yes_no_helper, "yesno") != 0) {
/* Failed to unregister yes_no_helper function */
}
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*trace-seq.h*
Header file to include in order to have access to trace sequences
related APIs. Trace sequences are used to allow a function to call
several other functions to create a string of data to use.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,104 @@
libtraceevent(3)
================
NAME
----
tep_set_flag, tep_clear_flag, tep_test_flag -
Manage flags of trace event parser context.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
enum *tep_flag* {
_TEP_NSEC_OUTPUT_,
_TEP_DISABLE_SYS_PLUGINS_,
_TEP_DISABLE_PLUGINS_
};
void *tep_set_flag*(struct tep_handle pass:[*]_tep_, enum tep_flag _flag_);
void *tep_clear_flag*(struct tep_handle pass:[*]_tep_, enum tep_flag _flag_);
bool *tep_test_flag*(struct tep_handle pass:[*]_tep_, enum tep_flag _flag_);
--
DESCRIPTION
-----------
Trace event parser context flags are defined in *enum tep_flag*:
[verse]
--
_TEP_NSEC_OUTPUT_ - print event's timestamp in nano seconds, instead of micro seconds.
_TEP_DISABLE_SYS_PLUGINS_ - disable plugins, located in system's plugin
directory. This directory is defined at library compile
time, and usually depends on library installation
prefix: (install_preffix)/lib/traceevent/plugins
_TEP_DISABLE_PLUGINS_ - disable all library plugins:
- in system's plugin directory
- in directory, defined by the environment variable _TRACEEVENT_PLUGIN_DIR_
- in user's home directory, _~/.traceevent/plugins_
--
Note: plugin related flags must me set before calling _tep_load_plugins()_ API.
The _tep_set_flag()_ function sets _flag_ to _tep_ context.
The _tep_clear_flag()_ function clears _flag_ from _tep_ context.
The _tep_test_flag()_ function tests if _flag_ is set to _tep_ context.
RETURN VALUE
------------
_tep_test_flag()_ function returns true if _flag_ is set, false otherwise.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
/* Print timestamps in nanoseconds */
tep_set_flag(tep, TEP_NSEC_OUTPUT);
...
if (tep_test_flag(tep, TEP_NSEC_OUTPUT)) {
/* print timestamps in nanoseconds */
} else {
/* print timestamps in microseconds */
}
...
/* Print timestamps in microseconds */
tep_clear_flag(tep, TEP_NSEC_OUTPUT);
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,85 @@
libtraceevent(3)
================
NAME
----
tep_strerror - Returns a string describing regular errno and tep error number.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
int *tep_strerror*(struct tep_handle pass:[*]_tep_, enum tep_errno _errnum_, char pass:[*]_buf_, size_t _buflen_);
--
DESCRIPTION
-----------
The _tep_strerror()_ function converts tep error number into a human
readable string.
The _tep_ argument is trace event parser context. The _errnum_ is a regular
errno, defined in errno.h, or a tep error number. The string, describing this
error number is copied in the _buf_ argument. The _buflen_ argument is
the size of the _buf_.
It as a thread safe wrapper around strerror_r(). The library function has two
different behaviors - POSIX and GNU specific. The _tep_strerror()_ API always
behaves as the POSIX version - the error string is copied in the user supplied
buffer.
RETURN VALUE
------------
The _tep_strerror()_ function returns 0, if a valid _errnum_ is passed and the
string is copied into _buf_. If _errnum_ is not a valid error number,
-1 is returned and _buf_ is not modified.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
...
struct tep_handle *tep = tep_alloc();
...
char buf[32];
char *pool = calloc(1, 128);
if (tep == NULL) {
tep_strerror(tep, TEP_ERRNO__MEM_ALLOC_FAILED, buf, 32);
printf ("The pool is not initialized, %s", buf);
}
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,158 @@
libtraceevent(3)
================
NAME
----
trace_seq_init, trace_seq_destroy, trace_seq_reset, trace_seq_terminate,
trace_seq_putc, trace_seq_puts, trace_seq_printf, trace_seq_vprintf,
trace_seq_do_fprintf, trace_seq_do_printf -
Initialize / destroy a trace sequence.
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
*#include <trace-seq.h>*
void *trace_seq_init*(struct trace_seq pass:[*]_s_);
void *trace_seq_destroy*(struct trace_seq pass:[*]_s_);
void *trace_seq_reset*(struct trace_seq pass:[*]_s_);
void *trace_seq_terminate*(struct trace_seq pass:[*]_s_);
int *trace_seq_putc*(struct trace_seq pass:[*]_s_, unsigned char _c_);
int *trace_seq_puts*(struct trace_seq pass:[*]_s_, const char pass:[*]_str_);
int *trace_seq_printf*(struct trace_seq pass:[*]_s_, const char pass:[*]_fmt_, _..._);
int *trace_seq_vprintf*(struct trace_seq pass:[*]_s_, const char pass:[*]_fmt_, va_list _args_);
int *trace_seq_do_printf*(struct trace_seq pass:[*]_s_);
int *trace_seq_do_fprintf*(struct trace_seq pass:[*]_s_, FILE pass:[*]_fp_);
--
DESCRIPTION
-----------
Trace sequences are used to allow a function to call several other functions
to create a string of data to use.
The _trace_seq_init()_ function initializes the trace sequence _s_.
The _trace_seq_destroy()_ function destroys the trace sequence _s_ and frees
all its resources that it had used.
The _trace_seq_reset()_ function re-initializes the trace sequence _s_. All
characters already written in _s_ will be deleted.
The _trace_seq_terminate()_ function terminates the trace sequence _s_. It puts
the null character pass:['\0'] at the end of the buffer.
The _trace_seq_putc()_ function puts a single character _c_ in the trace
sequence _s_.
The _trace_seq_puts()_ function puts a NULL terminated string _str_ in the
trace sequence _s_.
The _trace_seq_printf()_ function puts a formated string _fmt _with
variable arguments _..._ in the trace sequence _s_.
The _trace_seq_vprintf()_ function puts a formated string _fmt _with
list of arguments _args_ in the trace sequence _s_.
The _trace_seq_do_printf()_ function prints the buffer of trace sequence _s_ to
the standard output stdout.
The _trace_seq_do_fprintf()_ function prints the buffer of trace sequence _s_
to the given file _fp_.
RETURN VALUE
------------
Both _trace_seq_putc()_ and _trace_seq_puts()_ functions return the number of
characters put in the trace sequence, or 0 in case of an error
Both _trace_seq_printf()_ and _trace_seq_vprintf()_ functions return 0 if the
trace oversizes the buffer's free space, the number of characters printed, or
a negative value in case of an error.
Both _trace_seq_do_printf()_ and _trace_seq_do_fprintf()_ functions return the
number of printed characters, or -1 in case of an error.
EXAMPLE
-------
[source,c]
--
#include <event-parse.h>
#include <trace-seq.h>
...
struct trace_seq seq;
trace_seq_init(&seq);
...
void foo_seq_print(struct trace_seq *tseq, char *format, ...)
{
va_list ap;
va_start(ap, format);
if (trace_seq_vprintf(tseq, format, ap) <= 0) {
/* Failed to print in the trace sequence */
}
va_end(ap);
}
trace_seq_reset(&seq);
char *str = " MAN page example";
if (trace_seq_puts(&seq, str) != strlen(str)) {
/* Failed to put str in the trace sequence */
}
if (trace_seq_putc(&seq, ':') != 1) {
/* Failed to put ':' in the trace sequence */
}
if (trace_seq_printf(&seq, " trace sequence: %d", 1) <= 0) {
/* Failed to print in the trace sequence */
}
foo_seq_print( &seq, " %d\n", 2);
trace_seq_terminate(&seq);
...
if (trace_seq_do_printf(&seq) < 0 ) {
/* Failed to print the sequence buffer to the standard output */
}
FILE *fp = fopen("trace.txt", "w");
if (trace_seq_do_fprintf(&seq, fp) < 0 ) [
/* Failed to print the sequence buffer to the trace.txt file */
}
trace_seq_destroy(&seq);
...
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*trace-seq.h*
Header file to include in order to have access to trace sequences related APIs.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_libtraceevent(3)_, _trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,203 @@
libtraceevent(3)
================
NAME
----
libtraceevent - Linux kernel trace event library
SYNOPSIS
--------
[verse]
--
*#include <event-parse.h>*
Management of tep handler data structure and access of its members:
struct tep_handle pass:[*]*tep_alloc*(void);
void *tep_free*(struct tep_handle pass:[*]_tep_);
void *tep_ref*(struct tep_handle pass:[*]_tep_);
void *tep_unref*(struct tep_handle pass:[*]_tep_);
int *tep_ref_get*(struct tep_handle pass:[*]_tep_);
void *tep_set_flag*(struct tep_handle pass:[*]_tep_, enum tep_flag _flag_);
void *tep_clear_flag*(struct tep_handle pass:[*]_tep_, enum tep_flag _flag_);
bool *tep_test_flag*(struct tep_handle pass:[*]_tep_, enum tep_flag _flags_);
int *tep_get_cpus*(struct tep_handle pass:[*]_tep_);
void *tep_set_cpus*(struct tep_handle pass:[*]_tep_, int _cpus_);
int *tep_get_long_size*(strucqt tep_handle pass:[*]_tep_);
void *tep_set_long_size*(struct tep_handle pass:[*]_tep_, int _long_size_);
int *tep_get_page_size*(struct tep_handle pass:[*]_tep_);
void *tep_set_page_size*(struct tep_handle pass:[*]_tep_, int _page_size_);
bool *tep_is_latency_format*(struct tep_handle pass:[*]_tep_);
void *tep_set_latency_format*(struct tep_handle pass:[*]_tep_, int _lat_);
int *tep_get_header_page_size*(struct tep_handle pass:[*]_tep_);
int *tep_get_header_timestamp_size*(struct tep_handle pass:[*]_tep_);
bool *tep_is_old_format*(struct tep_handle pass:[*]_tep_);
int *tep_strerror*(struct tep_handle pass:[*]_tep_, enum tep_errno _errnum_, char pass:[*]_buf_, size_t _buflen_);
Register / unregister APIs:
int *tep_register_trace_clock*(struct tep_handle pass:[*]_tep_, const char pass:[*]_trace_clock_);
int *tep_register_function*(struct tep_handle pass:[*]_tep_, char pass:[*]_name_, unsigned long long _addr_, char pass:[*]_mod_);
int *tep_register_event_handler*(struct tep_handle pass:[*]_tep_, int _id_, const char pass:[*]_sys_name_, const char pass:[*]_event_name_, tep_event_handler_func _func_, void pass:[*]_context_);
int *tep_unregister_event_handler*(struct tep_handle pass:[*]tep, int id, const char pass:[*]sys_name, const char pass:[*]event_name, tep_event_handler_func func, void pass:[*]_context_);
int *tep_register_print_string*(struct tep_handle pass:[*]_tep_, const char pass:[*]_fmt_, unsigned long long _addr_);
int *tep_register_print_function*(struct tep_handle pass:[*]_tep_, tep_func_handler _func_, enum tep_func_arg_type _ret_type_, char pass:[*]_name_, _..._);
int *tep_unregister_print_function*(struct tep_handle pass:[*]_tep_, tep_func_handler _func_, char pass:[*]_name_);
Plugins management:
struct tep_plugin_list pass:[*]*tep_load_plugins*(struct tep_handle pass:[*]_tep_);
void *tep_unload_plugins*(struct tep_plugin_list pass:[*]_plugin_list_, struct tep_handle pass:[*]_tep_);
char pass:[*]pass:[*]*tep_plugin_list_options*(void);
void *tep_plugin_free_options_list*(char pass:[*]pass:[*]_list_);
int *tep_plugin_add_options*(const char pass:[*]_name_, struct tep_plugin_option pass:[*]_options_);
void *tep_plugin_remove_options*(struct tep_plugin_option pass:[*]_options_);
void *tep_print_plugins*(struct trace_seq pass:[*]_s_, const char pass:[*]_prefix_, const char pass:[*]_suffix_, const struct tep_plugin_list pass:[*]_list_);
Event related APIs:
struct tep_event pass:[*]*tep_get_event*(struct tep_handle pass:[*]_tep_, int _index_);
struct tep_event pass:[*]*tep_get_first_event*(struct tep_handle pass:[*]_tep_);
int *tep_get_events_count*(struct tep_handle pass:[*]_tep_);
struct tep_event pass:[*]pass:[*]*tep_list_events*(struct tep_handle pass:[*]_tep_, enum tep_event_sort_type _sort_type_);
struct tep_event pass:[*]pass:[*]*tep_list_events_copy*(struct tep_handle pass:[*]_tep_, enum tep_event_sort_type _sort_type_);
Event printing:
void *tep_print_event*(struct tep_handle pass:[*]_tep_, struct trace_seq pass:[*]_s_, struct tep_record pass:[*]_record_, bool _use_trace_clock_);
void *tep_print_event_data*(struct tep_handle pass:[*]_tep_, struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, struct tep_record pass:[*]_record_);
void *tep_event_info*(struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, struct tep_record pass:[*]_record_);
void *tep_print_event_task*(struct tep_handle pass:[*]_tep_, struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, struct tep_record pass:[*]_record_);
void *tep_print_event_time*(struct tep_handle pass:[*]_tep_, struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, struct tep_record pass:[*]record, bool _use_trace_clock_);
void *tep_set_print_raw*(struct tep_handle pass:[*]_tep_, int _print_raw_);
Event finding:
struct tep_event pass:[*]*tep_find_event*(struct tep_handle pass:[*]_tep_, int _id_);
struct tep_event pass:[*]*tep_find_event_by_name*(struct tep_handle pass:[*]_tep_, const char pass:[*]_sys_, const char pass:[*]_name_);
struct tep_event pass:[*]*tep_find_event_by_record*(struct tep_handle pass:[*]_tep_, struct tep_record pass:[*]_record_);
Parsing of event files:
int *tep_parse_header_page*(struct tep_handle pass:[*]_tep_, char pass:[*]_buf_, unsigned long _size_, int _long_size_);
enum tep_errno *tep_parse_event*(struct tep_handle pass:[*]_tep_, const char pass:[*]_buf_, unsigned long _size_, const char pass:[*]_sys_);
enum tep_errno *tep_parse_format*(struct tep_handle pass:[*]_tep_, struct tep_event pass:[*]pass:[*]_eventp_, const char pass:[*]_buf_, unsigned long _size_, const char pass:[*]_sys_);
APIs related to fields from event's format files:
struct tep_format_field pass:[*]pass:[*]*tep_event_common_fields*(struct tep_event pass:[*]_event_);
struct tep_format_field pass:[*]pass:[*]*tep_event_fields*(struct tep_event pass:[*]_event_);
void pass:[*]*tep_get_field_raw*(struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, int pass:[*]_len_, int _err_);
int *tep_get_field_val*(struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, unsigned long long pass:[*]_val_, int _err_);
int *tep_get_common_field_val*(struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, unsigned long long pass:[*]_val_, int _err_);
int *tep_get_any_field_val*(struct trace_seq pass:[*]_s_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, unsigned long long pass:[*]_val_, int _err_);
int *tep_read_number_field*(struct tep_format_field pass:[*]_field_, const void pass:[*]_data_, unsigned long long pass:[*]_value_);
Event fields printing:
void *tep_print_field*(struct trace_seq pass:[*]_s_, void pass:[*]_data_, struct tep_format_field pass:[*]_field_);
void *tep_print_fields*(struct trace_seq pass:[*]_s_, void pass:[*]_data_, int _size_, struct tep_event pass:[*]_event_);
int *tep_print_num_field*(struct trace_seq pass:[*]_s_, const char pass:[*]_fmt_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, int _err_);
int *tep_print_func_field*(struct trace_seq pass:[*]_s_, const char pass:[*]_fmt_, struct tep_event pass:[*]_event_, const char pass:[*]_name_, struct tep_record pass:[*]_record_, int _err_);
Event fields finding:
struct tep_format_field pass:[*]*tep_find_common_field*(struct tep_event pass:[*]_event_, const char pass:[*]_name_);
struct tep_format_field pass:[*]*tep_find_field*(struct tep_event_ormat pass:[*]_event_, const char pass:[*]_name_);
struct tep_format_field pass:[*]*tep_find_any_field*(struct tep_event pass:[*]_event_, const char pass:[*]_name_);
Functions resolver:
int *tep_set_function_resolver*(struct tep_handle pass:[*]_tep_, tep_func_resolver_t pass:[*]_func_, void pass:[*]_priv_);
void *tep_reset_function_resolver*(struct tep_handle pass:[*]_tep_);
const char pass:[*]*tep_find_function*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
unsigned long long *tep_find_function_address*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
Filter management:
struct tep_event_filter pass:[*]*tep_filter_alloc*(struct tep_handle pass:[*]_tep_);
enum tep_errno *tep_filter_add_filter_str*(struct tep_event_filter pass:[*]_filter_, const char pass:[*]_filter_str_);
enum tep_errno *tep_filter_match*(struct tep_event_filter pass:[*]_filter_, struct tep_record pass:[*]_record_);
int *tep_filter_strerror*(struct tep_event_filter pass:[*]_filter_, enum tep_errno _err_, char pass:[*]buf, size_t _buflen_);
int *tep_event_filtered*(struct tep_event_filter pass:[*]_filter_, int _event_id_);
void *tep_filter_reset*(struct tep_event_filter pass:[*]_filter_);
void *tep_filter_free*(struct tep_event_filter pass:[*]_filter_);
char pass:[*]*tep_filter_make_string*(struct tep_event_filter pass:[*]_filter_, int _event_id_);
int *tep_filter_remove_event*(struct tep_event_filter pass:[*]_filter_, int _event_id_);
int *tep_filter_copy*(struct tep_event_filter pass:[*]_dest_, struct tep_event_filter pass:[*]_source_);
int *tep_filter_compare*(struct tep_event_filter pass:[*]_filter1_, struct tep_event_filter pass:[*]_filter2_);
Parsing various data from the records:
void *tep_data_latency_format*(struct tep_handle pass:[*]_tep_, struct trace_seq pass:[*]_s_, struct tep_record pass:[*]_record_);
int *tep_data_type*(struct tep_handle pass:[*]_tep_, struct tep_record pass:[*]_rec_);
int *tep_data_pid*(struct tep_handle pass:[*]_tep_, struct tep_record pass:[*]_rec_);
int *tep_data_preempt_count*(struct tep_handle pass:[*]_tep_, struct tep_record pass:[*]_rec_);
int *tep_data_flags*(struct tep_handle pass:[*]_tep_, struct tep_record pass:[*]_rec_);
Command and task related APIs:
const char pass:[*]*tep_data_comm_from_pid*(struct tep_handle pass:[*]_tep_, int _pid_);
struct cmdline pass:[*]*tep_data_pid_from_comm*(struct tep_handle pass:[*]_tep_, const char pass:[*]_comm_, struct cmdline pass:[*]_next_);
int *tep_register_comm*(struct tep_handle pass:[*]_tep_, const char pass:[*]_comm_, int _pid_);
int *tep_override_comm*(struct tep_handle pass:[*]_tep_, const char pass:[*]_comm_, int _pid_);
bool *tep_is_pid_registered*(struct tep_handle pass:[*]_tep_, int _pid_);
int *tep_cmdline_pid*(struct tep_handle pass:[*]_tep_, struct cmdline pass:[*]_cmdline_);
Endian related APIs:
int *tep_is_bigendian*(void);
unsigned long long *tep_read_number*(struct tep_handle pass:[*]_tep_, const void pass:[*]_ptr_, int _size_);
bool *tep_is_file_bigendian*(struct tep_handle pass:[*]_tep_);
void *tep_set_file_bigendian*(struct tep_handle pass:[*]_tep_, enum tep_endian _endian_);
bool *tep_is_local_bigendian*(struct tep_handle pass:[*]_tep_);
void *tep_set_local_bigendian*(struct tep_handle pass:[*]_tep_, enum tep_endian _endian_);
Trace sequences:
*#include <trace-seq.h>*
void *trace_seq_init*(struct trace_seq pass:[*]_s_);
void *trace_seq_reset*(struct trace_seq pass:[*]_s_);
void *trace_seq_destroy*(struct trace_seq pass:[*]_s_);
int *trace_seq_printf*(struct trace_seq pass:[*]_s_, const char pass:[*]_fmt_, ...);
int *trace_seq_vprintf*(struct trace_seq pass:[*]_s_, const char pass:[*]_fmt_, va_list _args_);
int *trace_seq_puts*(struct trace_seq pass:[*]_s_, const char pass:[*]_str_);
int *trace_seq_putc*(struct trace_seq pass:[*]_s_, unsigned char _c_);
void *trace_seq_terminate*(struct trace_seq pass:[*]_s_);
int *trace_seq_do_fprintf*(struct trace_seq pass:[*]_s_, FILE pass:[*]_fp_);
int *trace_seq_do_printf*(struct trace_seq pass:[*]_s_);
--
DESCRIPTION
-----------
The libtraceevent(3) library provides APIs to access kernel tracepoint events,
located in the tracefs file system under the events directory.
ENVIRONMENT
-----------
[verse]
--
TRACEEVENT_PLUGIN_DIR
Additional plugin directory. All shared object files, located in this directory will be loaded as traceevent plugins.
--
FILES
-----
[verse]
--
*event-parse.h*
Header file to include in order to have access to the library APIs.
*trace-seq.h*
Header file to include in order to have access to trace sequences related APIs.
Trace sequences are used to allow a function to call several other functions
to create a string of data to use.
*-ltraceevent*
Linker switch to add when building a program that uses the library.
--
SEE ALSO
--------
_trace-cmd(1)_
AUTHOR
------
[verse]
--
*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
--
REPORTING BUGS
--------------
Report bugs to <linux-trace-devel@vger.kernel.org>
LICENSE
-------
libtraceevent is Free Software licensed under the GNU LGPL 2.1
RESOURCES
---------
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -0,0 +1,14 @@
<!-- manpage-1.72.xsl:
special settings for manpages rendered from asciidoc+docbook
handles peculiarities in docbook-xsl 1.72.0 -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="manpage-base.xsl"/>
<!-- these are the special values for the roff control characters
needed for docbook-xsl 1.72.0 -->
<xsl:param name="git.docbook.backslash">&#x2593;</xsl:param>
<xsl:param name="git.docbook.dot" >&#x2302;</xsl:param>
</xsl:stylesheet>

View File

@ -0,0 +1,35 @@
<!-- manpage-base.xsl:
special formatting for manpages rendered from asciidoc+docbook -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<!-- these params silence some output from xmlto -->
<xsl:param name="man.output.quietly" select="1"/>
<xsl:param name="refentry.meta.get.quietly" select="1"/>
<!-- convert asciidoc callouts to man page format;
git.docbook.backslash and git.docbook.dot params
must be supplied by another XSL file or other means -->
<xsl:template match="co">
<xsl:value-of select="concat(
$git.docbook.backslash,'fB(',
substring-after(@id,'-'),')',
$git.docbook.backslash,'fR')"/>
</xsl:template>
<xsl:template match="calloutlist">
<xsl:value-of select="$git.docbook.dot"/>
<xsl:text>sp&#10;</xsl:text>
<xsl:apply-templates/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="callout">
<xsl:value-of select="concat(
$git.docbook.backslash,'fB',
substring-after(@arearefs,'-'),
'. ',$git.docbook.backslash,'fR')"/>
<xsl:apply-templates/>
<xsl:value-of select="$git.docbook.dot"/>
<xsl:text>br&#10;</xsl:text>
</xsl:template>
</xsl:stylesheet>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,21 @@
<!-- manpage-suppress-sp.xsl:
special settings for manpages rendered from asciidoc+docbook
handles erroneous, inline .sp in manpage output of some
versions of docbook-xsl -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<!-- attempt to work around spurious .sp at the tail of the line
that some versions of docbook stylesheets seem to add -->
<xsl:template match="simpara">
<xsl:variable name="content">
<xsl:apply-templates/>
</xsl:variable>
<xsl:value-of select="normalize-space($content)"/>
<xsl:if test="not(ancestor::authorblurb) and
not(ancestor::personblurb)">
<xsl:text>&#10;&#10;</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

View File

@ -50,9 +50,13 @@ man_dir = $(prefix)/share/man
man_dir_SQ = '$(subst ','\'',$(man_dir))'
pkgconfig_dir ?= $(word 1,$(shell $(PKG_CONFIG) \
--variable pc_path pkg-config | tr ":" " "))
includedir_relative = traceevent
includedir = $(prefix)/include/$(includedir_relative)
includedir_SQ = '$(subst ','\'',$(includedir))'
export man_dir man_dir_SQ INSTALL
export DESTDIR DESTDIR_SQ
export EVENT_PARSE_VERSION
set_plugin_dir := 1
@ -279,6 +283,8 @@ define do_install_pkgconfig_file
cp -f ${PKG_CONFIG_FILE}.template ${PKG_CONFIG_FILE}; \
sed -i "s|INSTALL_PREFIX|${1}|g" ${PKG_CONFIG_FILE}; \
sed -i "s|LIB_VERSION|${EVENT_PARSE_VERSION}|g" ${PKG_CONFIG_FILE}; \
sed -i "s|LIB_DIR|${libdir}|g" ${PKG_CONFIG_FILE}; \
sed -i "s|HEADER_DIR|$(includedir)|g" ${PKG_CONFIG_FILE}; \
$(call do_install,$(PKG_CONFIG_FILE),$(pkgconfig_dir),644); \
else \
(echo Failed to locate pkg-config directory) 1>&2; \
@ -300,10 +306,10 @@ install_pkgconfig:
install_headers:
$(call QUIET_INSTALL, headers) \
$(call do_install,event-parse.h,$(prefix)/include/traceevent,644); \
$(call do_install,event-utils.h,$(prefix)/include/traceevent,644); \
$(call do_install,trace-seq.h,$(prefix)/include/traceevent,644); \
$(call do_install,kbuffer.h,$(prefix)/include/traceevent,644)
$(call do_install,event-parse.h,$(DESTDIR)$(includedir_SQ),644); \
$(call do_install,event-utils.h,$(DESTDIR)$(includedir_SQ),644); \
$(call do_install,trace-seq.h,$(DESTDIR)$(includedir_SQ),644); \
$(call do_install,kbuffer.h,$(DESTDIR)$(includedir_SQ),644)
install: install_lib
@ -313,6 +319,38 @@ clean:
$(RM) TRACEEVENT-CFLAGS tags TAGS; \
$(RM) $(PKG_CONFIG_FILE)
PHONY += doc
doc:
$(call descend,Documentation)
PHONY += doc-clean
doc-clean:
$(call descend,Documentation,clean)
PHONY += doc-install
doc-install:
$(call descend,Documentation,install)
PHONY += doc-uninstall
doc-uninstall:
$(call descend,Documentation,uninstall)
PHONY += help
help:
@echo 'Possible targets:'
@echo''
@echo ' all - default, compile the library and the'\
'plugins'
@echo ' plugins - compile the plugins'
@echo ' install - install the library, the plugins,'\
'the header and pkgconfig files'
@echo ' clean - clean the library and the plugins object files'
@echo ' doc - compile the documentation files - man'\
'and html pages, in the Documentation directory'
@echo ' doc-clean - clean the documentation files'
@echo ' doc-install - install the man pages'
@echo ' doc-uninstall - uninstall the man pages'
@echo''
PHONY += force plugins
force:

View File

@ -1,6 +1,6 @@
prefix=INSTALL_PREFIX
libdir=${prefix}/lib64
includedir=${prefix}/include/traceevent
libdir=LIB_DIR
includedir=HEADER_DIR
Name: libtraceevent
URL: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

View File

@ -47,7 +47,7 @@ clean:
install: $(ALL_PROGRAMS)
install -d -m 755 $(DESTDIR)$(bindir); \
for program in $(ALL_PROGRAMS); do \
for program in $(ALL_PROGRAMS) pcitest.sh; do \
install $$program $(DESTDIR)$(bindir); \
done; \
for script in $(ALL_SCRIPTS); do \

View File

@ -199,6 +199,18 @@ also be supplied. For example:
perf stat -C 0 -e 'hv_gpci/dtbp_ptitc,phys_processor_idx=0x2/' ...
EVENT QUALIFIERS:
It is also possible to add extra qualifiers to an event:
percore:
Sums up the event counts for all hardware threads in a core, e.g.:
perf stat -e cpu/event=0,umask=0x3,percore=1/
EVENT GROUPS
------------

View File

@ -406,7 +406,8 @@ symbolic names, e.g. on x86, ax, si. To list the available registers use
--intr-regs=ax,bx. The list of register is architecture dependent.
--user-regs::
Capture user registers at sample time. Same arguments as -I.
Similar to -I, but capture user registers at sample time. To list the available
user registers use --user-regs=\?.
--running-time::
Record running and enabled time for read events (:S)
@ -478,6 +479,11 @@ Also at some cases executing less output write syscalls with bigger data size
can take less time than executing more output write syscalls with smaller data
size thus lowering runtime profiling overhead.
-z::
--compression-level[=n]::
Produce compressed trace using specified level n (default: 1 - fastest compression,
22 - smallest trace)
--all-kernel::
Configure all used events to run in kernel space.

View File

@ -43,6 +43,10 @@ report::
param1 and param2 are defined as formats for the PMU in
/sys/bus/event_source/devices/<pmu>/format/*
'percore' is a event qualifier that sums up the event counts for both
hardware threads in a core. For example:
perf stat -A -a -e cpu/event,percore=1/,otherevent ...
- a symbolically formed event like 'pmu/config=M,config1=N,config2=K/'
where M, N, K are numbers (in decimal, hex, octal format).
Acceptable values for each of 'config', 'config1' and 'config2'

View File

@ -272,6 +272,19 @@ struct {
Two uint64_t for the time of first sample and the time of last sample.
HEADER_COMPRESSED = 27,
struct {
u32 version;
u32 type;
u32 level;
u32 ratio;
u32 mmap_len;
};
Indicates that trace contains records of PERF_RECORD_COMPRESSED type
that have perf_events records in compressed form.
other bits are reserved and should ignored for now
HEADER_FEAT_BITS = 256,
@ -437,6 +450,17 @@ struct auxtrace_error_event {
Describes a header feature. These are records used in pipe-mode that
contain information that otherwise would be in perf.data file's header.
PERF_RECORD_COMPRESSED = 81,
struct compressed_event {
struct perf_event_header header;
char data[];
};
The header is followed by compressed data frame that can be decompressed
into array of perf trace records. The size of the entire compressed event
record including the header is limited by the max value of header.size.
Event types
Define the event attributes with their IDs.

View File

@ -22,6 +22,8 @@ OPTIONS
verbose - general debug messages
ordered-events - ordered events object debug messages
data-convert - data convert command debug messages
stderr - write debug output (option -v) to stderr
in browser mode
--buildid-dir::
Setup buildid cache directory. It has higher priority than

View File

@ -8,9 +8,10 @@
void perf_regs_load(u64 *regs);
#define PERF_REGS_MAX PERF_REG_X86_XMM_MAX
#define PERF_XMM_REGS_MASK (~((1ULL << PERF_REG_X86_XMM0) - 1))
#ifndef HAVE_ARCH_X86_64_SUPPORT
#define PERF_REGS_MASK ((1ULL << PERF_REG_X86_32_MAX) - 1)
#define PERF_REGS_MAX PERF_REG_X86_32_MAX
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32
#else
#define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \
@ -18,7 +19,6 @@ void perf_regs_load(u64 *regs);
(1ULL << PERF_REG_X86_FS) | \
(1ULL << PERF_REG_X86_GS))
#define PERF_REGS_MASK (((1ULL << PERF_REG_X86_64_MAX) - 1) & ~REG_NOSUPPORT)
#define PERF_REGS_MAX PERF_REG_X86_64_MAX
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64
#endif
#define PERF_REG_IP PERF_REG_X86_IP
@ -77,6 +77,28 @@ static inline const char *perf_reg_name(int id)
case PERF_REG_X86_R15:
return "R15";
#endif /* HAVE_ARCH_X86_64_SUPPORT */
#define XMM(x) \
case PERF_REG_X86_XMM ## x: \
case PERF_REG_X86_XMM ## x + 1: \
return "XMM" #x;
XMM(0)
XMM(1)
XMM(2)
XMM(3)
XMM(4)
XMM(5)
XMM(6)
XMM(7)
XMM(8)
XMM(9)
XMM(10)
XMM(11)
XMM(12)
XMM(13)
XMM(14)
XMM(15)
#undef XMM
default:
return NULL;
}

View File

@ -31,6 +31,22 @@ const struct sample_reg sample_reg_masks[] = {
SMPL_REG(R14, PERF_REG_X86_R14),
SMPL_REG(R15, PERF_REG_X86_R15),
#endif
SMPL_REG2(XMM0, PERF_REG_X86_XMM0),
SMPL_REG2(XMM1, PERF_REG_X86_XMM1),
SMPL_REG2(XMM2, PERF_REG_X86_XMM2),
SMPL_REG2(XMM3, PERF_REG_X86_XMM3),
SMPL_REG2(XMM4, PERF_REG_X86_XMM4),
SMPL_REG2(XMM5, PERF_REG_X86_XMM5),
SMPL_REG2(XMM6, PERF_REG_X86_XMM6),
SMPL_REG2(XMM7, PERF_REG_X86_XMM7),
SMPL_REG2(XMM8, PERF_REG_X86_XMM8),
SMPL_REG2(XMM9, PERF_REG_X86_XMM9),
SMPL_REG2(XMM10, PERF_REG_X86_XMM10),
SMPL_REG2(XMM11, PERF_REG_X86_XMM11),
SMPL_REG2(XMM12, PERF_REG_X86_XMM12),
SMPL_REG2(XMM13, PERF_REG_X86_XMM13),
SMPL_REG2(XMM14, PERF_REG_X86_XMM14),
SMPL_REG2(XMM15, PERF_REG_X86_XMM15),
SMPL_REG_END
};
@ -254,3 +270,31 @@ int arch_sdt_arg_parse_op(char *old_op, char **new_op)
return SDT_ARG_VALID;
}
uint64_t arch__intr_reg_mask(void)
{
struct perf_event_attr attr = {
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES,
.sample_type = PERF_SAMPLE_REGS_INTR,
.sample_regs_intr = PERF_XMM_REGS_MASK,
.precise_ip = 1,
.disabled = 1,
.exclude_kernel = 1,
};
int fd;
/*
* In an unnamed union, init it here to build on older gcc versions
*/
attr.sample_period = 1;
event_attr_init(&attr);
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
if (fd != -1) {
close(fd);
return (PERF_XMM_REGS_MASK | PERF_REGS_MASK);
}
return PERF_REGS_MASK;
}

View File

@ -159,8 +159,6 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter,
struct perf_evsel *evsel = iter->evsel;
int err;
hist__account_cycles(sample->branch_stack, al, sample, false);
bi = he->branch_info;
err = addr_map_symbol__inc_samples(&bi->from, sample, evsel);
@ -199,6 +197,8 @@ static int process_branch_callback(struct perf_evsel *evsel,
if (a.map != NULL)
a.map->dso->hit = 1;
hist__account_cycles(sample->branch_stack, al, sample, false);
ret = hist_entry_iter__add(&iter, &a, PERF_MAX_STACK_DEPTH, ann);
return ret;
}

View File

@ -837,6 +837,9 @@ int cmd_inject(int argc, const char **argv)
if (inject.session == NULL)
return -1;
if (zstd_init(&(inject.session->zstd_data), 0) < 0)
pr_warning("Decompression initialization failed.\n");
if (inject.build_ids) {
/*
* to make sure the mmap records are ordered correctly
@ -867,6 +870,7 @@ int cmd_inject(int argc, const char **argv)
ret = __cmd_inject(&inject);
out_delete:
zstd_fini(&(inject.session->zstd_data));
perf_session__delete(inject.session);
return ret;
}

View File

@ -133,6 +133,11 @@ static int record__write(struct record *rec, struct perf_mmap *map __maybe_unuse
return 0;
}
static int record__aio_enabled(struct record *rec);
static int record__comp_enabled(struct record *rec);
static size_t zstd_compress(struct perf_session *session, void *dst, size_t dst_size,
void *src, size_t src_size);
#ifdef HAVE_AIO_SUPPORT
static int record__aio_write(struct aiocb *cblock, int trace_fd,
void *buf, size_t size, off_t off)
@ -183,9 +188,9 @@ static int record__aio_complete(struct perf_mmap *md, struct aiocb *cblock)
if (rem_size == 0) {
cblock->aio_fildes = -1;
/*
* md->refcount is incremented in perf_mmap__push() for
* every enqueued aio write request so decrement it because
* the request is now complete.
* md->refcount is incremented in record__aio_pushfn() for
* every aio write request started in record__aio_push() so
* decrement it because the request is now complete.
*/
perf_mmap__put(md);
rc = 1;
@ -240,18 +245,89 @@ static int record__aio_sync(struct perf_mmap *md, bool sync_all)
} while (1);
}
static int record__aio_pushfn(void *to, struct aiocb *cblock, void *bf, size_t size, off_t off)
struct record_aio {
struct record *rec;
void *data;
size_t size;
};
static int record__aio_pushfn(struct perf_mmap *map, void *to, void *buf, size_t size)
{
struct record *rec = to;
int ret, trace_fd = rec->session->data->file.fd;
struct record_aio *aio = to;
/*
* map->base data pointed by buf is copied into free map->aio.data[] buffer
* to release space in the kernel buffer as fast as possible, calling
* perf_mmap__consume() from perf_mmap__push() function.
*
* That lets the kernel to proceed with storing more profiling data into
* the kernel buffer earlier than other per-cpu kernel buffers are handled.
*
* Coping can be done in two steps in case the chunk of profiling data
* crosses the upper bound of the kernel buffer. In this case we first move
* part of data from map->start till the upper bound and then the reminder
* from the beginning of the kernel buffer till the end of the data chunk.
*/
if (record__comp_enabled(aio->rec)) {
size = zstd_compress(aio->rec->session, aio->data + aio->size,
perf_mmap__mmap_len(map) - aio->size,
buf, size);
} else {
memcpy(aio->data + aio->size, buf, size);
}
if (!aio->size) {
/*
* Increment map->refcount to guard map->aio.data[] buffer
* from premature deallocation because map object can be
* released earlier than aio write request started on
* map->aio.data[] buffer is complete.
*
* perf_mmap__put() is done at record__aio_complete()
* after started aio request completion or at record__aio_push()
* if the request failed to start.
*/
perf_mmap__get(map);
}
aio->size += size;
return size;
}
static int record__aio_push(struct record *rec, struct perf_mmap *map, off_t *off)
{
int ret, idx;
int trace_fd = rec->session->data->file.fd;
struct record_aio aio = { .rec = rec, .size = 0 };
/*
* Call record__aio_sync() to wait till map->aio.data[] buffer
* becomes available after previous aio write operation.
*/
idx = record__aio_sync(map, false);
aio.data = map->aio.data[idx];
ret = perf_mmap__push(map, &aio, record__aio_pushfn);
if (ret != 0) /* ret > 0 - no data, ret < 0 - error */
return ret;
rec->samples++;
ret = record__aio_write(cblock, trace_fd, bf, size, off);
ret = record__aio_write(&(map->aio.cblocks[idx]), trace_fd, aio.data, aio.size, *off);
if (!ret) {
rec->bytes_written += size;
*off += aio.size;
rec->bytes_written += aio.size;
if (switch_output_size(rec))
trigger_hit(&switch_output_trigger);
} else {
/*
* Decrement map->refcount incremented in record__aio_pushfn()
* back if record__aio_write() operation failed to start, otherwise
* map->refcount is decremented in record__aio_complete() after
* aio write operation finishes successfully.
*/
perf_mmap__put(map);
}
return ret;
@ -273,7 +349,7 @@ static void record__aio_mmap_read_sync(struct record *rec)
struct perf_evlist *evlist = rec->evlist;
struct perf_mmap *maps = evlist->mmap;
if (!rec->opts.nr_cblocks)
if (!record__aio_enabled(rec))
return;
for (i = 0; i < evlist->nr_mmaps; i++) {
@ -307,13 +383,8 @@ static int record__aio_parse(const struct option *opt,
#else /* HAVE_AIO_SUPPORT */
static int nr_cblocks_max = 0;
static int record__aio_sync(struct perf_mmap *md __maybe_unused, bool sync_all __maybe_unused)
{
return -1;
}
static int record__aio_pushfn(void *to __maybe_unused, struct aiocb *cblock __maybe_unused,
void *bf __maybe_unused, size_t size __maybe_unused, off_t off __maybe_unused)
static int record__aio_push(struct record *rec __maybe_unused, struct perf_mmap *map __maybe_unused,
off_t *off __maybe_unused)
{
return -1;
}
@ -372,6 +443,32 @@ static int record__mmap_flush_parse(const struct option *opt,
return 0;
}
#ifdef HAVE_ZSTD_SUPPORT
static unsigned int comp_level_default = 1;
static int record__parse_comp_level(const struct option *opt, const char *str, int unset)
{
struct record_opts *opts = opt->value;
if (unset) {
opts->comp_level = 0;
} else {
if (str)
opts->comp_level = strtol(str, NULL, 0);
if (!opts->comp_level)
opts->comp_level = comp_level_default;
}
return 0;
}
#endif
static unsigned int comp_level_max = 22;
static int record__comp_enabled(struct record *rec)
{
return rec->opts.comp_level > 0;
}
static int process_synthesized_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
@ -385,6 +482,11 @@ static int record__pushfn(struct perf_mmap *map, void *to, void *bf, size_t size
{
struct record *rec = to;
if (record__comp_enabled(rec)) {
size = zstd_compress(rec->session, map->data, perf_mmap__mmap_len(map), bf, size);
bf = map->data;
}
rec->samples++;
return record__write(rec, map, bf, size);
}
@ -582,7 +684,7 @@ static int record__mmap_evlist(struct record *rec,
opts->auxtrace_mmap_pages,
opts->auxtrace_snapshot_mode,
opts->nr_cblocks, opts->affinity,
opts->mmap_flush) < 0) {
opts->mmap_flush, opts->comp_level) < 0) {
if (errno == EPERM) {
pr_err("Permission error mapping pages.\n"
"Consider increasing "
@ -771,6 +873,37 @@ static void record__adjust_affinity(struct record *rec, struct perf_mmap *map)
}
}
static size_t process_comp_header(void *record, size_t increment)
{
struct compressed_event *event = record;
size_t size = sizeof(*event);
if (increment) {
event->header.size += increment;
return increment;
}
event->header.type = PERF_RECORD_COMPRESSED;
event->header.size = size;
return size;
}
static size_t zstd_compress(struct perf_session *session, void *dst, size_t dst_size,
void *src, size_t src_size)
{
size_t compressed;
size_t max_record_size = PERF_SAMPLE_MAX_SIZE - sizeof(struct compressed_event) - 1;
compressed = zstd_compress_stream_to_records(&session->zstd_data, dst, dst_size, src, src_size,
max_record_size, process_comp_header);
session->bytes_transferred += src_size;
session->bytes_compressed += compressed;
return compressed;
}
static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evlist,
bool overwrite, bool synch)
{
@ -779,7 +912,7 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli
int rc = 0;
struct perf_mmap *maps;
int trace_fd = rec->data.file.fd;
off_t off;
off_t off = 0;
if (!evlist)
return 0;
@ -805,20 +938,14 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli
map->flush = 1;
}
if (!record__aio_enabled(rec)) {
if (perf_mmap__push(map, rec, record__pushfn) != 0) {
if (perf_mmap__push(map, rec, record__pushfn) < 0) {
if (synch)
map->flush = flush;
rc = -1;
goto out;
}
} else {
int idx;
/*
* Call record__aio_sync() to wait till map->data buffer
* becomes available after previous aio write request.
*/
idx = record__aio_sync(map, false);
if (perf_mmap__aio_push(map, rec, idx, record__aio_pushfn, &off) != 0) {
if (record__aio_push(rec, map, &off) < 0) {
record__aio_set_pos(trace_fd, off);
if (synch)
map->flush = flush;
@ -888,6 +1015,8 @@ static void record__init_features(struct record *rec)
perf_header__clear_feat(&session->header, HEADER_CLOCKID);
perf_header__clear_feat(&session->header, HEADER_DIR_FORMAT);
if (!record__comp_enabled(rec))
perf_header__clear_feat(&session->header, HEADER_COMPRESSED);
perf_header__clear_feat(&session->header, HEADER_STAT);
}
@ -1186,6 +1315,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
bool disabled = false, draining = false;
struct perf_evlist *sb_evlist = NULL;
int fd;
float ratio = 0;
atexit(record__sig_exit);
signal(SIGCHLD, sig_handler);
@ -1215,6 +1345,14 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
fd = perf_data__fd(data);
rec->session = session;
if (zstd_init(&session->zstd_data, rec->opts.comp_level) < 0) {
pr_err("Compression initialization failed.\n");
return -1;
}
session->header.env.comp_type = PERF_COMP_ZSTD;
session->header.env.comp_level = rec->opts.comp_level;
record__init_features(rec);
if (rec->opts.use_clockid && rec->opts.clockid_res_ns)
@ -1244,6 +1382,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
err = -1;
goto out_child;
}
session->header.env.comp_mmap_len = session->evlist->mmap_len;
err = bpf__apply_obj_config();
if (err) {
@ -1491,6 +1630,11 @@ out_child:
record__mmap_read_all(rec, true);
record__aio_mmap_read_sync(rec);
if (rec->session->bytes_transferred && rec->session->bytes_compressed) {
ratio = (float)rec->session->bytes_transferred/(float)rec->session->bytes_compressed;
session->header.env.comp_ratio = ratio + 0.5;
}
if (forks) {
int exit_status;
@ -1537,12 +1681,19 @@ out_child:
else
samples[0] = '\0';
fprintf(stderr, "[ perf record: Captured and wrote %.3f MB %s%s%s ]\n",
fprintf(stderr, "[ perf record: Captured and wrote %.3f MB %s%s%s",
perf_data__size(data) / 1024.0 / 1024.0,
data->path, postfix, samples);
if (ratio) {
fprintf(stderr, ", compressed (original %.3f MB, ratio is %.3f)",
rec->session->bytes_transferred / 1024.0 / 1024.0,
ratio);
}
fprintf(stderr, " ]\n");
}
out_delete_session:
zstd_fini(&session->zstd_data);
perf_session__delete(session);
if (!opts->no_bpf_event)
@ -2017,10 +2168,10 @@ static struct option __record_options[] = {
"use per-thread mmaps"),
OPT_CALLBACK_OPTARG('I', "intr-regs", &record.opts.sample_intr_regs, NULL, "any register",
"sample selected machine registers on interrupt,"
" use -I ? to list register names", parse_regs),
" use '-I?' to list register names", parse_intr_regs),
OPT_CALLBACK_OPTARG(0, "user-regs", &record.opts.sample_user_regs, NULL, "any register",
"sample selected machine registers on interrupt,"
" use -I ? to list register names", parse_regs),
" use '--user-regs=?' to list register names", parse_user_regs),
OPT_BOOLEAN(0, "running-time", &record.opts.running_time,
"Record running/enabled time of read (:S) events"),
OPT_CALLBACK('k', "clockid", &record.opts,
@ -2068,6 +2219,11 @@ static struct option __record_options[] = {
OPT_CALLBACK(0, "affinity", &record.opts, "node|cpu",
"Set affinity mask of trace reading thread to NUMA node cpu mask or cpu of processed mmap buffer",
record__parse_affinity),
#ifdef HAVE_ZSTD_SUPPORT
OPT_CALLBACK_OPTARG('z', "compression-level", &record.opts, &comp_level_default,
"n", "Compressed records using specified level (default: 1 - fastest compression, 22 - greatest compression)",
record__parse_comp_level),
#endif
OPT_END()
};
@ -2127,6 +2283,12 @@ int cmd_record(int argc, const char **argv)
"cgroup monitoring only available in system-wide mode");
}
if (rec->opts.comp_level != 0) {
pr_debug("Compression enabled, disabling build id collection at the end of the session.\n");
rec->no_buildid = true;
}
if (rec->opts.record_switch_events &&
!perf_can_record_switch_events()) {
ui__error("kernel does not support recording context switch events\n");
@ -2272,12 +2434,15 @@ int cmd_record(int argc, const char **argv)
if (rec->opts.nr_cblocks > nr_cblocks_max)
rec->opts.nr_cblocks = nr_cblocks_max;
if (verbose > 0)
pr_info("nr_cblocks: %d\n", rec->opts.nr_cblocks);
pr_debug("nr_cblocks: %d\n", rec->opts.nr_cblocks);
pr_debug("affinity: %s\n", affinity_tags[rec->opts.affinity]);
pr_debug("mmap flush: %d\n", rec->opts.mmap_flush);
if (rec->opts.comp_level > comp_level_max)
rec->opts.comp_level = comp_level_max;
pr_debug("comp level: %d\n", rec->opts.comp_level);
err = __cmd_record(&record, argc, argv);
out:
perf_evlist__delete(rec->evlist);

View File

@ -136,9 +136,6 @@ static int hist_iter__report_callback(struct hist_entry_iter *iter,
if (!ui__has_annotation() && !rep->symbol_ipc)
return 0;
hist__account_cycles(sample->branch_stack, al, sample,
rep->nonany_branch_mode);
if (sort__mode == SORT_MODE__BRANCH) {
bi = he->branch_info;
err = addr_map_symbol__inc_samples(&bi->from, sample, evsel);
@ -181,9 +178,6 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter,
if (!ui__has_annotation() && !rep->symbol_ipc)
return 0;
hist__account_cycles(sample->branch_stack, al, sample,
rep->nonany_branch_mode);
bi = he->branch_info;
err = addr_map_symbol__inc_samples(&bi->from, sample, evsel);
if (err)
@ -282,6 +276,11 @@ static int process_sample_event(struct perf_tool *tool,
if (al.map != NULL)
al.map->dso->hit = 1;
if (ui__has_annotation() || rep->symbol_ipc) {
hist__account_cycles(sample->branch_stack, &al, sample,
rep->nonany_branch_mode);
}
ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep);
if (ret < 0)
pr_debug("problem adding hist entry, skipping event\n");
@ -1259,6 +1258,9 @@ repeat:
if (session == NULL)
return -1;
if (zstd_init(&(session->zstd_data), 0) < 0)
pr_warning("Decompression initialization failed. Reported data may be incomplete.\n");
if (report.queue_size) {
ordered_events__set_alloc_size(&session->ordered_events,
report.queue_size);
@ -1449,7 +1451,7 @@ repeat:
error:
if (report.ptime_range)
zfree(&report.ptime_range);
zstd_fini(&(session->zstd_data));
perf_session__delete(session);
return ret;
}

View File

@ -847,6 +847,18 @@ static int perf_stat__get_core_cached(struct perf_stat_config *config,
return perf_stat__get_aggr(config, perf_stat__get_core, map, idx);
}
static bool term_percore_set(void)
{
struct perf_evsel *counter;
evlist__for_each_entry(evsel_list, counter) {
if (counter->percore)
return true;
}
return false;
}
static int perf_stat_init_aggr_mode(void)
{
int nr;
@ -867,6 +879,15 @@ static int perf_stat_init_aggr_mode(void)
stat_config.aggr_get_id = perf_stat__get_core_cached;
break;
case AGGR_NONE:
if (term_percore_set()) {
if (cpu_map__build_core_map(evsel_list->cpus,
&stat_config.aggr_map)) {
perror("cannot build core map");
return -1;
}
stat_config.aggr_get_id = perf_stat__get_core_cached;
}
break;
case AGGR_GLOBAL:
case AGGR_THREAD:
case AGGR_UNSET:

View File

@ -86,6 +86,7 @@ struct record_opts {
int nr_cblocks;
int affinity;
int mmap_flush;
unsigned int comp_level;
};
enum perf_affinity {

View File

@ -0,0 +1,179 @@
[
{
"ArchStdEvent": "L1D_CACHE_RD",
},
{
"ArchStdEvent": "L1D_CACHE_WR",
},
{
"ArchStdEvent": "L1D_CACHE_REFILL_RD",
},
{
"ArchStdEvent": "L1D_CACHE_REFILL_WR",
},
{
"ArchStdEvent": "L1D_CACHE_WB_VICTIM",
},
{
"ArchStdEvent": "L1D_CACHE_WB_CLEAN",
},
{
"ArchStdEvent": "L1D_CACHE_INVAL",
},
{
"ArchStdEvent": "L1D_TLB_REFILL_RD",
},
{
"ArchStdEvent": "L1D_TLB_REFILL_WR",
},
{
"ArchStdEvent": "L2D_CACHE_RD",
},
{
"ArchStdEvent": "L2D_CACHE_WR",
},
{
"ArchStdEvent": "L2D_CACHE_REFILL_RD",
},
{
"ArchStdEvent": "L2D_CACHE_REFILL_WR",
},
{
"ArchStdEvent": "L2D_CACHE_WB_VICTIM",
},
{
"ArchStdEvent": "L2D_CACHE_WB_CLEAN",
},
{
"ArchStdEvent": "L2D_CACHE_INVAL",
},
{
"ArchStdEvent": "BUS_ACCESS_RD",
},
{
"ArchStdEvent": "BUS_ACCESS_WR",
},
{
"ArchStdEvent": "BUS_ACCESS_SHARED",
},
{
"ArchStdEvent": "BUS_ACCESS_NOT_SHARED",
},
{
"ArchStdEvent": "BUS_ACCESS_NORMAL",
},
{
"ArchStdEvent": "BUS_ACCESS_PERIPH",
},
{
"ArchStdEvent": "MEM_ACCESS_RD",
},
{
"ArchStdEvent": "MEM_ACCESS_WR",
},
{
"ArchStdEvent": "UNALIGNED_LD_SPEC",
},
{
"ArchStdEvent": "UNALIGNED_ST_SPEC",
},
{
"ArchStdEvent": "UNALIGNED_LDST_SPEC",
},
{
"ArchStdEvent": "LDREX_SPEC",
},
{
"ArchStdEvent": "STREX_PASS_SPEC",
},
{
"ArchStdEvent": "STREX_FAIL_SPEC",
},
{
"ArchStdEvent": "LD_SPEC",
},
{
"ArchStdEvent": "ST_SPEC",
},
{
"ArchStdEvent": "LDST_SPEC",
},
{
"ArchStdEvent": "DP_SPEC",
},
{
"ArchStdEvent": "ASE_SPEC",
},
{
"ArchStdEvent": "VFP_SPEC",
},
{
"ArchStdEvent": "PC_WRITE_SPEC",
},
{
"ArchStdEvent": "CRYPTO_SPEC",
},
{
"ArchStdEvent": "BR_IMMED_SPEC",
},
{
"ArchStdEvent": "BR_RETURN_SPEC",
},
{
"ArchStdEvent": "BR_INDIRECT_SPEC",
},
{
"ArchStdEvent": "ISB_SPEC",
},
{
"ArchStdEvent": "DSB_SPEC",
},
{
"ArchStdEvent": "DMB_SPEC",
},
{
"ArchStdEvent": "EXC_UNDEF",
},
{
"ArchStdEvent": "EXC_SVC",
},
{
"ArchStdEvent": "EXC_PABORT",
},
{
"ArchStdEvent": "EXC_DABORT",
},
{
"ArchStdEvent": "EXC_IRQ",
},
{
"ArchStdEvent": "EXC_FIQ",
},
{
"ArchStdEvent": "EXC_SMC",
},
{
"ArchStdEvent": "EXC_HVC",
},
{
"ArchStdEvent": "EXC_TRAP_PABORT",
},
{
"ArchStdEvent": "EXC_TRAP_DABORT",
},
{
"ArchStdEvent": "EXC_TRAP_OTHER",
},
{
"ArchStdEvent": "EXC_TRAP_IRQ",
},
{
"ArchStdEvent": "EXC_TRAP_FIQ",
},
{
"ArchStdEvent": "RC_LD_SPEC",
},
{
"ArchStdEvent": "RC_ST_SPEC",
},
]

View File

@ -12,7 +12,10 @@
#
#
#Family-model,Version,Filename,EventType
0x00000000410fd03[[:xdigit:]],v1,arm/cortex-a53,core
0x00000000410fd030,v1,arm/cortex-a53,core
0x00000000420f1000,v1,arm/cortex-a53,core
0x00000000410fd070,v1,arm/cortex-a57-a72,core
0x00000000410fd080,v1,arm/cortex-a57-a72,core
0x00000000420f5160,v1,cavium/thunderx2,core
0x00000000430f0af0,v1,cavium/thunderx2,core
0x00000000480fd010,v1,hisilicon/hip08,core

1 # Format:
12 #
13 #
14 #Family-model,Version,Filename,EventType
15 0x00000000410fd03[[:xdigit:]],v1,arm/cortex-a53,core 0x00000000410fd030,v1,arm/cortex-a53,core
16 0x00000000420f1000,v1,arm/cortex-a53,core
17 0x00000000410fd070,v1,arm/cortex-a57-a72,core
18 0x00000000410fd080,v1,arm/cortex-a57-a72,core
19 0x00000000420f5160,v1,cavium/thunderx2,core
20 0x00000000430f0af0,v1,cavium/thunderx2,core
21 0x00000000480fd010,v1,hisilicon/hip08,core

View File

@ -235,6 +235,7 @@ static struct map {
{ "iMPH-U", "uncore_arb" },
{ "CPU-M-CF", "cpum_cf" },
{ "CPU-M-SF", "cpum_sf" },
{ "UPI LL", "uncore_upi" },
{}
};
@ -414,7 +415,6 @@ static int save_arch_std_events(void *data, char *name, char *event,
char *metric_name, char *metric_group)
{
struct event_struct *es;
struct stat *sb = data;
es = malloc(sizeof(*es));
if (!es)

View File

@ -456,6 +456,10 @@ class CallGraphLevelItemBase(object):
self.query_done = False;
self.child_count = 0
self.child_items = []
if parent_item:
self.level = parent_item.level + 1
else:
self.level = 0
def getChildItem(self, row):
return self.child_items[row]
@ -877,9 +881,14 @@ class TreeWindowBase(QMdiSubWindow):
super(TreeWindowBase, self).__init__(parent)
self.model = None
self.view = None
self.find_bar = None
self.view = QTreeView()
self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
self.context_menu = TreeContextMenu(self.view)
def DisplayFound(self, ids):
if not len(ids):
return False
@ -921,7 +930,6 @@ class CallGraphWindow(TreeWindowBase):
self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
self.view = QTreeView()
self.view.setModel(self.model)
for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
@ -944,7 +952,6 @@ class CallTreeWindow(TreeWindowBase):
self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x))
self.view = QTreeView()
self.view.setModel(self.model)
for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)):
@ -1649,10 +1656,14 @@ class BranchWindow(QMdiSubWindow):
self.view = QTreeView()
self.view.setUniformRowHeights(True)
self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
self.view.setModel(self.model)
self.ResizeColumnsToContents()
self.context_menu = TreeContextMenu(self.view)
self.find_bar = FindBar(self, self, True)
self.finder = ChildDataItemFinder(self.model.root)
@ -2261,6 +2272,240 @@ class ResizeColumnsToContentsBase(QObject):
self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
self.ResizeColumnsToContents()
# Convert value to CSV
def ToCSValue(val):
if '"' in val:
val = val.replace('"', '""')
if "," in val or '"' in val:
val = '"' + val + '"'
return val
# Key to sort table model indexes by row / column, assuming fewer than 1000 columns
glb_max_cols = 1000
def RowColumnKey(a):
return a.row() * glb_max_cols + a.column()
# Copy selected table cells to clipboard
def CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False):
indexes = sorted(view.selectedIndexes(), key=RowColumnKey)
idx_cnt = len(indexes)
if not idx_cnt:
return
if idx_cnt == 1:
with_hdr=False
min_row = indexes[0].row()
max_row = indexes[0].row()
min_col = indexes[0].column()
max_col = indexes[0].column()
for i in indexes:
min_row = min(min_row, i.row())
max_row = max(max_row, i.row())
min_col = min(min_col, i.column())
max_col = max(max_col, i.column())
if max_col > glb_max_cols:
raise RuntimeError("glb_max_cols is too low")
max_width = [0] * (1 + max_col - min_col)
for i in indexes:
c = i.column() - min_col
max_width[c] = max(max_width[c], len(str(i.data())))
text = ""
pad = ""
sep = ""
if with_hdr:
model = indexes[0].model()
for col in range(min_col, max_col + 1):
val = model.headerData(col, Qt.Horizontal)
if as_csv:
text += sep + ToCSValue(val)
sep = ","
else:
c = col - min_col
max_width[c] = max(max_width[c], len(val))
width = max_width[c]
align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole)
if align & Qt.AlignRight:
val = val.rjust(width)
text += pad + sep + val
pad = " " * (width - len(val))
sep = " "
text += "\n"
pad = ""
sep = ""
last_row = min_row
for i in indexes:
if i.row() > last_row:
last_row = i.row()
text += "\n"
pad = ""
sep = ""
if as_csv:
text += sep + ToCSValue(str(i.data()))
sep = ","
else:
width = max_width[i.column() - min_col]
if i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
val = str(i.data()).rjust(width)
else:
val = str(i.data())
text += pad + sep + val
pad = " " * (width - len(val))
sep = " "
QApplication.clipboard().setText(text)
def CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False):
indexes = view.selectedIndexes()
if not len(indexes):
return
selection = view.selectionModel()
first = None
for i in indexes:
above = view.indexAbove(i)
if not selection.isSelected(above):
first = i
break
if first is None:
raise RuntimeError("CopyTreeCellsToClipboard internal error")
model = first.model()
row_cnt = 0
col_cnt = model.columnCount(first)
max_width = [0] * col_cnt
indent_sz = 2
indent_str = " " * indent_sz
expanded_mark_sz = 2
if sys.version_info[0] == 3:
expanded_mark = "\u25BC "
not_expanded_mark = "\u25B6 "
else:
expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8")
not_expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8")
leaf_mark = " "
if not as_csv:
pos = first
while True:
row_cnt += 1
row = pos.row()
for c in range(col_cnt):
i = pos.sibling(row, c)
if c:
n = len(str(i.data()))
else:
n = len(str(i.data()).strip())
n += (i.internalPointer().level - 1) * indent_sz
n += expanded_mark_sz
max_width[c] = max(max_width[c], n)
pos = view.indexBelow(pos)
if not selection.isSelected(pos):
break
text = ""
pad = ""
sep = ""
if with_hdr:
for c in range(col_cnt):
val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip()
if as_csv:
text += sep + ToCSValue(val)
sep = ","
else:
max_width[c] = max(max_width[c], len(val))
width = max_width[c]
align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole)
if align & Qt.AlignRight:
val = val.rjust(width)
text += pad + sep + val
pad = " " * (width - len(val))
sep = " "
text += "\n"
pad = ""
sep = ""
pos = first
while True:
row = pos.row()
for c in range(col_cnt):
i = pos.sibling(row, c)
val = str(i.data())
if not c:
if model.hasChildren(i):
if view.isExpanded(i):
mark = expanded_mark
else:
mark = not_expanded_mark
else:
mark = leaf_mark
val = indent_str * (i.internalPointer().level - 1) + mark + val.strip()
if as_csv:
text += sep + ToCSValue(val)
sep = ","
else:
width = max_width[c]
if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
val = val.rjust(width)
text += pad + sep + val
pad = " " * (width - len(val))
sep = " "
pos = view.indexBelow(pos)
if not selection.isSelected(pos):
break
text = text.rstrip() + "\n"
pad = ""
sep = ""
QApplication.clipboard().setText(text)
def CopyCellsToClipboard(view, as_csv=False, with_hdr=False):
view.CopyCellsToClipboard(view, as_csv, with_hdr)
def CopyCellsToClipboardHdr(view):
CopyCellsToClipboard(view, False, True)
def CopyCellsToClipboardCSV(view):
CopyCellsToClipboard(view, True, True)
# Context menu
class ContextMenu(object):
def __init__(self, view):
self.view = view
self.view.setContextMenuPolicy(Qt.CustomContextMenu)
self.view.customContextMenuRequested.connect(self.ShowContextMenu)
def ShowContextMenu(self, pos):
menu = QMenu(self.view)
self.AddActions(menu)
menu.exec_(self.view.mapToGlobal(pos))
def AddCopy(self, menu):
menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view))
menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view))
def AddActions(self, menu):
self.AddCopy(menu)
class TreeContextMenu(ContextMenu):
def __init__(self, view):
super(TreeContextMenu, self).__init__(view)
def AddActions(self, menu):
i = self.view.currentIndex()
text = str(i.data()).strip()
if len(text):
menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view))
self.AddCopy(menu)
# Table window
class TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
@ -2279,9 +2524,13 @@ class TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
self.view.verticalHeader().setVisible(False)
self.view.sortByColumn(-1, Qt.AscendingOrder)
self.view.setSortingEnabled(True)
self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
self.ResizeColumnsToContents()
self.context_menu = ContextMenu(self.view)
self.find_bar = FindBar(self, self, True)
self.finder = ChildDataItemFinder(self.data_model)
@ -2395,6 +2644,10 @@ class TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
self.view.setModel(self.model)
self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.view.verticalHeader().setVisible(False)
self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
self.context_menu = ContextMenu(self.view)
self.ResizeColumnsToContents()
@ -2660,6 +2913,60 @@ class HelpOnlyWindow(QMainWindow):
self.setCentralWidget(self.text)
# PostqreSQL server version
def PostqreSQLServerVersion(db):
query = QSqlQuery(db)
QueryExec(query, "SELECT VERSION()")
if query.next():
v_str = query.value(0)
v_list = v_str.strip().split(" ")
if v_list[0] == "PostgreSQL" and v_list[2] == "on":
return v_list[1]
return v_str
return "Unknown"
# SQLite version
def SQLiteVersion(db):
query = QSqlQuery(db)
QueryExec(query, "SELECT sqlite_version()")
if query.next():
return query.value(0)
return "Unknown"
# About dialog
class AboutDialog(QDialog):
def __init__(self, glb, parent=None):
super(AboutDialog, self).__init__(parent)
self.setWindowTitle("About Exported SQL Viewer")
self.setMinimumWidth(300)
pyside_version = "1" if pyside_version_1 else "2"
text = "<pre>"
text += "Python version: " + sys.version.split(" ")[0] + "\n"
text += "PySide version: " + pyside_version + "\n"
text += "Qt version: " + qVersion() + "\n"
if glb.dbref.is_sqlite3:
text += "SQLite version: " + SQLiteVersion(glb.db) + "\n"
else:
text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n"
text += "</pre>"
self.text = QTextBrowser()
self.text.setHtml(text)
self.text.setReadOnly(True)
self.text.setOpenExternalLinks(True)
self.vbox = QVBoxLayout()
self.vbox.addWidget(self.text)
self.setLayout(self.vbox);
# Font resize
def ResizeFont(widget, diff):
@ -2732,6 +3039,8 @@ class MainWindow(QMainWindow):
file_menu.addAction(CreateExitAction(glb.app, self))
edit_menu = menu.addMenu("&Edit")
edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy))
edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self))
edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
@ -2755,6 +3064,21 @@ class MainWindow(QMainWindow):
help_menu = menu.addMenu("&Help")
help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self))
def Try(self, fn):
win = self.mdi_area.activeSubWindow()
if win:
try:
fn(win.view)
except:
pass
def CopyToClipboard(self):
self.Try(CopyCellsToClipboardHdr)
def CopyToClipboardCSV(self):
self.Try(CopyCellsToClipboardCSV)
def Find(self):
win = self.mdi_area.activeSubWindow()
@ -2773,12 +3097,10 @@ class MainWindow(QMainWindow):
pass
def ShrinkFont(self):
win = self.mdi_area.activeSubWindow()
ShrinkFont(win.view)
self.Try(ShrinkFont)
def EnlargeFont(self):
win = self.mdi_area.activeSubWindow()
EnlargeFont(win.view)
self.Try(EnlargeFont)
def EventMenu(self, events, reports_menu):
branches_events = 0
@ -2828,6 +3150,10 @@ class MainWindow(QMainWindow):
def Help(self):
HelpWindow(self.glb, self)
def About(self):
dialog = AboutDialog(self.glb, self)
dialog.exec_()
# XED Disassembler
class xed_state_t(Structure):

View File

@ -304,7 +304,7 @@ int test__dso_data_cache(struct test *test __maybe_unused, int subtest __maybe_u
/* Make sure we did not leak any file descriptor. */
nr_end = open_files_cnt();
pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end);
TEST_ASSERT_VAL("failed leadking files", nr == nr_end);
TEST_ASSERT_VAL("failed leaking files", nr == nr_end);
return 0;
}
@ -380,6 +380,6 @@ int test__dso_data_reopen(struct test *test __maybe_unused, int subtest __maybe_
/* Make sure we did not leak any file descriptor. */
nr_end = open_files_cnt();
pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end);
TEST_ASSERT_VAL("failed leadking files", nr == nr_end);
TEST_ASSERT_VAL("failed leaking files", nr == nr_end);
return 0;
}

View File

@ -107,7 +107,7 @@ make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1
make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1
make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1
make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1
make_minimal += NO_LIBCRYPTO=1 NO_SDT=1 NO_JVMTI=1
make_minimal += NO_LIBCRYPTO=1 NO_SDT=1 NO_JVMTI=1 NO_LIBZSTD=1
# $(run) contains all available tests
run := make_pure

View File

@ -0,0 +1,34 @@
#!/bin/sh
# Zstd perf.data compression/decompression
trace_file=$(mktemp /tmp/perf.data.XXX)
perf_tool=perf
skip_if_no_z_record() {
$perf_tool record -h 2>&1 | grep -q '\-z, \-\-compression\-level'
}
collect_z_record() {
echo "Collecting compressed record file:"
$perf_tool record -o $trace_file -g -z -F 5000 -- \
dd count=500 if=/dev/random of=/dev/null
}
check_compressed_stats() {
echo "Checking compressed events stats:"
$perf_tool report -i $trace_file --header --stats | \
grep -E "(# compressed : Zstd,)|(COMPRESSED events:)"
}
check_compressed_output() {
$perf_tool inject -i $trace_file -o $trace_file.decomp &&
$perf_tool report -i $trace_file --stdio | head -n -3 > $trace_file.comp.output &&
$perf_tool report -i $trace_file.decomp --stdio | head -n -3 > $trace_file.decomp.output &&
diff $trace_file.comp.output $trace_file.decomp.output
}
skip_if_no_z_record || exit 2
collect_z_record && check_compressed_stats && check_compressed_output
err=$?
rm -f $trace_file*
exit $err

View File

@ -145,6 +145,8 @@ perf-y += scripting-engines/
perf-$(CONFIG_ZLIB) += zlib.o
perf-$(CONFIG_LZMA) += lzma.o
perf-$(CONFIG_ZSTD) += zstd.o
perf-y += demangle-java.o
perf-y += demangle-rust.o

View File

@ -1021,7 +1021,7 @@ static void annotation__count_and_fill(struct annotation *notes, u64 start, u64
float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
/* Hide data when there are too many overlaps. */
if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
if (ch->reset >= 0x7fff)
return;
for (offset = start; offset <= end; offset++) {

View File

@ -2,6 +2,11 @@
#ifndef PERF_COMPRESS_H
#define PERF_COMPRESS_H
#include <stdbool.h>
#ifdef HAVE_ZSTD_SUPPORT
#include <zstd.h>
#endif
#ifdef HAVE_ZLIB_SUPPORT
int gzip_decompress_to_file(const char *input, int output_fd);
bool gzip_is_compressed(const char *input);
@ -12,4 +17,52 @@ int lzma_decompress_to_file(const char *input, int output_fd);
bool lzma_is_compressed(const char *input);
#endif
struct zstd_data {
#ifdef HAVE_ZSTD_SUPPORT
ZSTD_CStream *cstream;
ZSTD_DStream *dstream;
#endif
};
#ifdef HAVE_ZSTD_SUPPORT
int zstd_init(struct zstd_data *data, int level);
int zstd_fini(struct zstd_data *data);
size_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_t dst_size,
void *src, size_t src_size, size_t max_record_size,
size_t process_header(void *record, size_t increment));
size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size,
void *dst, size_t dst_size);
#else /* !HAVE_ZSTD_SUPPORT */
static inline int zstd_init(struct zstd_data *data __maybe_unused, int level __maybe_unused)
{
return 0;
}
static inline int zstd_fini(struct zstd_data *data __maybe_unused)
{
return 0;
}
static inline
size_t zstd_compress_stream_to_records(struct zstd_data *data __maybe_unused,
void *dst __maybe_unused, size_t dst_size __maybe_unused,
void *src __maybe_unused, size_t src_size __maybe_unused,
size_t max_record_size __maybe_unused,
size_t process_header(void *record, size_t increment) __maybe_unused)
{
return 0;
}
static inline size_t zstd_decompress_stream(struct zstd_data *data __maybe_unused, void *src __maybe_unused,
size_t src_size __maybe_unused, void *dst __maybe_unused,
size_t dst_size __maybe_unused)
{
return 0;
}
#endif
#endif /* PERF_COMPRESS_H */

View File

@ -62,6 +62,11 @@ struct perf_env {
struct cpu_topology_map *cpu;
struct cpu_cache_level *caches;
int caches_cnt;
u32 comp_ratio;
u32 comp_ver;
u32 comp_type;
u32 comp_level;
u32 comp_mmap_len;
struct numa_node *numa_nodes;
struct memory_node *memory_nodes;
unsigned long long memory_bsize;
@ -80,6 +85,12 @@ struct perf_env {
} bpf_progs;
};
enum perf_compress_type {
PERF_COMP_NONE = 0,
PERF_COMP_ZSTD,
PERF_COMP_MAX
};
struct bpf_prog_info_node;
struct btf_node;

View File

@ -68,6 +68,7 @@ static const char *perf_event__names[] = {
[PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE",
[PERF_RECORD_TIME_CONV] = "TIME_CONV",
[PERF_RECORD_HEADER_FEATURE] = "FEATURE",
[PERF_RECORD_COMPRESSED] = "COMPRESSED",
};
static const char *perf_ns__names[] = {

View File

@ -255,6 +255,7 @@ enum perf_user_event_type { /* above any possible kernel type */
PERF_RECORD_EVENT_UPDATE = 78,
PERF_RECORD_TIME_CONV = 79,
PERF_RECORD_HEADER_FEATURE = 80,
PERF_RECORD_COMPRESSED = 81,
PERF_RECORD_HEADER_MAX
};
@ -627,6 +628,11 @@ struct feature_event {
char data[];
};
struct compressed_event {
struct perf_event_header header;
char data[];
};
union perf_event {
struct perf_event_header header;
struct mmap_event mmap;
@ -660,6 +666,7 @@ union perf_event {
struct feature_event feat;
struct ksymbol_event ksymbol_event;
struct bpf_event bpf_event;
struct compressed_event pack;
};
void perf_event__print_totals(void);

View File

@ -1009,7 +1009,8 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,
*/
int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
unsigned int auxtrace_pages,
bool auxtrace_overwrite, int nr_cblocks, int affinity, int flush)
bool auxtrace_overwrite, int nr_cblocks, int affinity, int flush,
int comp_level)
{
struct perf_evsel *evsel;
const struct cpu_map *cpus = evlist->cpus;
@ -1019,7 +1020,8 @@ int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
* Its value is decided by evsel's write_backward.
* So &mp should not be passed through const pointer.
*/
struct mmap_params mp = { .nr_cblocks = nr_cblocks, .affinity = affinity, .flush = flush };
struct mmap_params mp = { .nr_cblocks = nr_cblocks, .affinity = affinity, .flush = flush,
.comp_level = comp_level };
if (!evlist->mmap)
evlist->mmap = perf_evlist__alloc_mmap(evlist, false);
@ -1051,7 +1053,7 @@ int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages)
{
return perf_evlist__mmap_ex(evlist, pages, 0, false, 0, PERF_AFFINITY_SYS, 1);
return perf_evlist__mmap_ex(evlist, pages, 0, false, 0, PERF_AFFINITY_SYS, 1, 0);
}
int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)

View File

@ -178,7 +178,7 @@ unsigned long perf_event_mlock_kb_in_pages(void);
int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
unsigned int auxtrace_pages,
bool auxtrace_overwrite, int nr_cblocks,
int affinity, int flush);
int affinity, int flush, int comp_level);
int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages);
void perf_evlist__munmap(struct perf_evlist *evlist);

View File

@ -813,6 +813,8 @@ static void apply_config_terms(struct perf_evsel *evsel,
break;
case PERF_EVSEL__CONFIG_TERM_DRV_CFG:
break;
case PERF_EVSEL__CONFIG_TERM_PERCORE:
break;
default:
break;
}

View File

@ -50,6 +50,7 @@ enum term_type {
PERF_EVSEL__CONFIG_TERM_OVERWRITE,
PERF_EVSEL__CONFIG_TERM_DRV_CFG,
PERF_EVSEL__CONFIG_TERM_BRANCH,
PERF_EVSEL__CONFIG_TERM_PERCORE,
};
struct perf_evsel_config_term {
@ -67,6 +68,7 @@ struct perf_evsel_config_term {
bool overwrite;
char *branch;
unsigned long max_events;
bool percore;
} val;
bool weak;
};
@ -158,6 +160,7 @@ struct perf_evsel {
struct perf_evsel **metric_events;
bool collect_stat;
bool weak_group;
bool percore;
const char *pmu_name;
struct {
perf_evsel__sb_cb_t *cb;

View File

@ -1344,6 +1344,30 @@ out:
return ret;
}
static int write_compressed(struct feat_fd *ff __maybe_unused,
struct perf_evlist *evlist __maybe_unused)
{
int ret;
ret = do_write(ff, &(ff->ph->env.comp_ver), sizeof(ff->ph->env.comp_ver));
if (ret)
return ret;
ret = do_write(ff, &(ff->ph->env.comp_type), sizeof(ff->ph->env.comp_type));
if (ret)
return ret;
ret = do_write(ff, &(ff->ph->env.comp_level), sizeof(ff->ph->env.comp_level));
if (ret)
return ret;
ret = do_write(ff, &(ff->ph->env.comp_ratio), sizeof(ff->ph->env.comp_ratio));
if (ret)
return ret;
return do_write(ff, &(ff->ph->env.comp_mmap_len), sizeof(ff->ph->env.comp_mmap_len));
}
static void print_hostname(struct feat_fd *ff, FILE *fp)
{
fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
@ -1688,6 +1712,13 @@ static void print_cache(struct feat_fd *ff, FILE *fp __maybe_unused)
}
}
static void print_compressed(struct feat_fd *ff, FILE *fp)
{
fprintf(fp, "# compressed : %s, level = %d, ratio = %d\n",
ff->ph->env.comp_type == PERF_COMP_ZSTD ? "Zstd" : "Unknown",
ff->ph->env.comp_level, ff->ph->env.comp_ratio);
}
static void print_pmu_mappings(struct feat_fd *ff, FILE *fp)
{
const char *delimiter = "# pmu mappings: ";
@ -2667,6 +2698,27 @@ out:
return err;
}
static int process_compressed(struct feat_fd *ff,
void *data __maybe_unused)
{
if (do_read_u32(ff, &(ff->ph->env.comp_ver)))
return -1;
if (do_read_u32(ff, &(ff->ph->env.comp_type)))
return -1;
if (do_read_u32(ff, &(ff->ph->env.comp_level)))
return -1;
if (do_read_u32(ff, &(ff->ph->env.comp_ratio)))
return -1;
if (do_read_u32(ff, &(ff->ph->env.comp_mmap_len)))
return -1;
return 0;
}
struct feature_ops {
int (*write)(struct feat_fd *ff, struct perf_evlist *evlist);
void (*print)(struct feat_fd *ff, FILE *fp);
@ -2730,6 +2782,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPN(DIR_FORMAT, dir_format, false),
FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false),
FEAT_OPR(BPF_BTF, bpf_btf, false),
FEAT_OPR(COMPRESSED, compressed, false),
};
struct header_print_data {

View File

@ -42,6 +42,7 @@ enum {
HEADER_DIR_FORMAT,
HEADER_BPF_PROG_INFO,
HEADER_BPF_BTF,
HEADER_COMPRESSED,
HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256,
};

View File

@ -58,6 +58,7 @@ enum intel_pt_pkt_state {
INTEL_PT_STATE_NO_IP,
INTEL_PT_STATE_ERR_RESYNC,
INTEL_PT_STATE_IN_SYNC,
INTEL_PT_STATE_TNT_CONT,
INTEL_PT_STATE_TNT,
INTEL_PT_STATE_TIP,
INTEL_PT_STATE_TIP_PGD,
@ -72,8 +73,9 @@ static inline bool intel_pt_sample_time(enum intel_pt_pkt_state pkt_state)
case INTEL_PT_STATE_NO_IP:
case INTEL_PT_STATE_ERR_RESYNC:
case INTEL_PT_STATE_IN_SYNC:
case INTEL_PT_STATE_TNT:
case INTEL_PT_STATE_TNT_CONT:
return true;
case INTEL_PT_STATE_TNT:
case INTEL_PT_STATE_TIP:
case INTEL_PT_STATE_TIP_PGD:
case INTEL_PT_STATE_FUP:
@ -888,16 +890,20 @@ static uint64_t intel_pt_next_period(struct intel_pt_decoder *decoder)
timestamp = decoder->timestamp + decoder->timestamp_insn_cnt;
masked_timestamp = timestamp & decoder->period_mask;
if (decoder->continuous_period) {
if (masked_timestamp != decoder->last_masked_timestamp)
if (masked_timestamp > decoder->last_masked_timestamp)
return 1;
} else {
timestamp += 1;
masked_timestamp = timestamp & decoder->period_mask;
if (masked_timestamp != decoder->last_masked_timestamp) {
if (masked_timestamp > decoder->last_masked_timestamp) {
decoder->last_masked_timestamp = masked_timestamp;
decoder->continuous_period = true;
}
}
if (masked_timestamp < decoder->last_masked_timestamp)
return decoder->period_ticks;
return decoder->period_ticks - (timestamp - masked_timestamp);
}
@ -926,7 +932,10 @@ static void intel_pt_sample_insn(struct intel_pt_decoder *decoder)
case INTEL_PT_PERIOD_TICKS:
timestamp = decoder->timestamp + decoder->timestamp_insn_cnt;
masked_timestamp = timestamp & decoder->period_mask;
decoder->last_masked_timestamp = masked_timestamp;
if (masked_timestamp > decoder->last_masked_timestamp)
decoder->last_masked_timestamp = masked_timestamp;
else
decoder->last_masked_timestamp += decoder->period_ticks;
break;
case INTEL_PT_PERIOD_NONE:
case INTEL_PT_PERIOD_MTC:
@ -1254,7 +1263,9 @@ static int intel_pt_walk_tnt(struct intel_pt_decoder *decoder)
return -ENOENT;
}
decoder->tnt.count -= 1;
if (!decoder->tnt.count)
if (decoder->tnt.count)
decoder->pkt_state = INTEL_PT_STATE_TNT_CONT;
else
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
decoder->tnt.payload <<= 1;
decoder->state.from_ip = decoder->ip;
@ -1285,7 +1296,9 @@ static int intel_pt_walk_tnt(struct intel_pt_decoder *decoder)
if (intel_pt_insn.branch == INTEL_PT_BR_CONDITIONAL) {
decoder->tnt.count -= 1;
if (!decoder->tnt.count)
if (decoder->tnt.count)
decoder->pkt_state = INTEL_PT_STATE_TNT_CONT;
else
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
if (decoder->tnt.payload & BIT63) {
decoder->tnt.payload <<= 1;
@ -1305,8 +1318,11 @@ static int intel_pt_walk_tnt(struct intel_pt_decoder *decoder)
return 0;
}
decoder->ip += intel_pt_insn.length;
if (!decoder->tnt.count)
if (!decoder->tnt.count) {
decoder->sample_timestamp = decoder->timestamp;
decoder->sample_insn_cnt = decoder->timestamp_insn_cnt;
return -EAGAIN;
}
decoder->tnt.payload <<= 1;
continue;
}
@ -2365,6 +2381,7 @@ const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder)
err = intel_pt_walk_trace(decoder);
break;
case INTEL_PT_STATE_TNT:
case INTEL_PT_STATE_TNT_CONT:
err = intel_pt_walk_tnt(decoder);
if (err == -EAGAIN)
err = intel_pt_walk_trace(decoder);

View File

@ -1234,8 +1234,9 @@ static char *get_kernel_version(const char *root_dir)
if (!file)
return NULL;
version[0] = '\0';
tmp = fgets(version, sizeof(version), file);
if (!tmp)
*version = '\0';
fclose(file);
name = strstr(version, prefix);

View File

@ -157,6 +157,10 @@ void __weak auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp __mayb
}
#ifdef HAVE_AIO_SUPPORT
static int perf_mmap__aio_enabled(struct perf_mmap *map)
{
return map->aio.nr_cblocks > 0;
}
#ifdef HAVE_LIBNUMA_SUPPORT
static int perf_mmap__aio_alloc(struct perf_mmap *map, int idx)
@ -198,7 +202,7 @@ static int perf_mmap__aio_bind(struct perf_mmap *map, int idx, int cpu, int affi
return 0;
}
#else
#else /* !HAVE_LIBNUMA_SUPPORT */
static int perf_mmap__aio_alloc(struct perf_mmap *map, int idx)
{
map->aio.data[idx] = malloc(perf_mmap__mmap_len(map));
@ -285,81 +289,12 @@ static void perf_mmap__aio_munmap(struct perf_mmap *map)
zfree(&map->aio.cblocks);
zfree(&map->aio.aiocb);
}
int perf_mmap__aio_push(struct perf_mmap *md, void *to, int idx,
int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off),
off_t *off)
#else /* !HAVE_AIO_SUPPORT */
static int perf_mmap__aio_enabled(struct perf_mmap *map __maybe_unused)
{
u64 head = perf_mmap__read_head(md);
unsigned char *data = md->base + page_size;
unsigned long size, size0 = 0;
void *buf;
int rc = 0;
rc = perf_mmap__read_init(md);
if (rc < 0)
return (rc == -EAGAIN) ? 0 : -1;
/*
* md->base data is copied into md->data[idx] buffer to
* release space in the kernel buffer as fast as possible,
* thru perf_mmap__consume() below.
*
* That lets the kernel to proceed with storing more
* profiling data into the kernel buffer earlier than other
* per-cpu kernel buffers are handled.
*
* Coping can be done in two steps in case the chunk of
* profiling data crosses the upper bound of the kernel buffer.
* In this case we first move part of data from md->start
* till the upper bound and then the reminder from the
* beginning of the kernel buffer till the end of
* the data chunk.
*/
size = md->end - md->start;
if ((md->start & md->mask) + size != (md->end & md->mask)) {
buf = &data[md->start & md->mask];
size = md->mask + 1 - (md->start & md->mask);
md->start += size;
memcpy(md->aio.data[idx], buf, size);
size0 = size;
}
buf = &data[md->start & md->mask];
size = md->end - md->start;
md->start += size;
memcpy(md->aio.data[idx] + size0, buf, size);
/*
* Increment md->refcount to guard md->data[idx] buffer
* from premature deallocation because md object can be
* released earlier than aio write request started
* on mmap->data[idx] is complete.
*
* perf_mmap__put() is done at record__aio_complete()
* after started request completion.
*/
perf_mmap__get(md);
md->prev = head;
perf_mmap__consume(md);
rc = push(to, &md->aio.cblocks[idx], md->aio.data[idx], size0 + size, *off);
if (!rc) {
*off += size0 + size;
} else {
/*
* Decrement md->refcount back if aio write
* operation failed to start.
*/
perf_mmap__put(md);
}
return rc;
return 0;
}
#else
static int perf_mmap__aio_mmap(struct perf_mmap *map __maybe_unused,
struct mmap_params *mp __maybe_unused)
{
@ -374,6 +309,10 @@ static void perf_mmap__aio_munmap(struct perf_mmap *map __maybe_unused)
void perf_mmap__munmap(struct perf_mmap *map)
{
perf_mmap__aio_munmap(map);
if (map->data != NULL) {
munmap(map->data, perf_mmap__mmap_len(map));
map->data = NULL;
}
if (map->base != NULL) {
munmap(map->base, perf_mmap__mmap_len(map));
map->base = NULL;
@ -442,6 +381,19 @@ int perf_mmap__mmap(struct perf_mmap *map, struct mmap_params *mp, int fd, int c
map->flush = mp->flush;
map->comp_level = mp->comp_level;
if (map->comp_level && !perf_mmap__aio_enabled(map)) {
map->data = mmap(NULL, perf_mmap__mmap_len(map), PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
if (map->data == MAP_FAILED) {
pr_debug2("failed to mmap data buffer, error %d\n",
errno);
map->data = NULL;
return -1;
}
}
if (auxtrace_mmap__mmap(&map->auxtrace_mmap,
&mp->auxtrace_mp, map->base, fd))
return -1;
@ -540,7 +492,7 @@ int perf_mmap__push(struct perf_mmap *md, void *to,
rc = perf_mmap__read_init(md);
if (rc < 0)
return (rc == -EAGAIN) ? 0 : -1;
return (rc == -EAGAIN) ? 1 : -1;
size = md->end - md->start;

View File

@ -40,6 +40,8 @@ struct perf_mmap {
#endif
cpu_set_t affinity_mask;
u64 flush;
void *data;
int comp_level;
};
/*
@ -71,7 +73,7 @@ enum bkw_mmap_state {
};
struct mmap_params {
int prot, mask, nr_cblocks, affinity, flush;
int prot, mask, nr_cblocks, affinity, flush, comp_level;
struct auxtrace_mmap_params auxtrace_mp;
};
@ -99,18 +101,6 @@ union perf_event *perf_mmap__read_event(struct perf_mmap *map);
int perf_mmap__push(struct perf_mmap *md, void *to,
int push(struct perf_mmap *map, void *to, void *buf, size_t size));
#ifdef HAVE_AIO_SUPPORT
int perf_mmap__aio_push(struct perf_mmap *md, void *to, int idx,
int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off),
off_t *off);
#else
static inline int perf_mmap__aio_push(struct perf_mmap *md __maybe_unused, void *to __maybe_unused, int idx __maybe_unused,
int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off) __maybe_unused,
off_t *off __maybe_unused)
{
return 0;
}
#endif
size_t perf_mmap__mmap_len(struct perf_mmap *map);

View File

@ -950,6 +950,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = {
[PARSE_EVENTS__TERM_TYPE_OVERWRITE] = "overwrite",
[PARSE_EVENTS__TERM_TYPE_NOOVERWRITE] = "no-overwrite",
[PARSE_EVENTS__TERM_TYPE_DRV_CFG] = "driver-config",
[PARSE_EVENTS__TERM_TYPE_PERCORE] = "percore",
};
static bool config_term_shrinked;
@ -970,6 +971,7 @@ config_term_avail(int term_type, struct parse_events_error *err)
case PARSE_EVENTS__TERM_TYPE_CONFIG2:
case PARSE_EVENTS__TERM_TYPE_NAME:
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
case PARSE_EVENTS__TERM_TYPE_PERCORE:
return true;
default:
if (!err)
@ -1061,6 +1063,14 @@ do { \
case PARSE_EVENTS__TERM_TYPE_MAX_EVENTS:
CHECK_TYPE_VAL(NUM);
break;
case PARSE_EVENTS__TERM_TYPE_PERCORE:
CHECK_TYPE_VAL(NUM);
if ((unsigned int)term->val.num > 1) {
err->str = strdup("expected 0 or 1");
err->idx = term->err_val;
return -EINVAL;
}
break;
default:
err->str = strdup("unknown term");
err->idx = term->err_term;
@ -1199,6 +1209,10 @@ do { \
case PARSE_EVENTS__TERM_TYPE_DRV_CFG:
ADD_CONFIG_TERM(DRV_CFG, drv_cfg, term->val.str);
break;
case PARSE_EVENTS__TERM_TYPE_PERCORE:
ADD_CONFIG_TERM(PERCORE, percore,
term->val.num ? true : false);
break;
default:
break;
}
@ -1260,6 +1274,18 @@ int parse_events_add_tool(struct parse_events_state *parse_state,
return add_event_tool(list, &parse_state->idx, tool_event);
}
static bool config_term_percore(struct list_head *config_terms)
{
struct perf_evsel_config_term *term;
list_for_each_entry(term, config_terms, list) {
if (term->type == PERF_EVSEL__CONFIG_TERM_PERCORE)
return term->val.percore;
}
return false;
}
int parse_events_add_pmu(struct parse_events_state *parse_state,
struct list_head *list, char *name,
struct list_head *head_config,
@ -1333,6 +1359,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
evsel->metric_name = info.metric_name;
evsel->pmu_name = name;
evsel->use_uncore_alias = use_uncore_alias;
evsel->percore = config_term_percore(&evsel->config_terms);
}
return evsel ? 0 : -ENOMEM;

View File

@ -75,6 +75,7 @@ enum {
PARSE_EVENTS__TERM_TYPE_NOOVERWRITE,
PARSE_EVENTS__TERM_TYPE_OVERWRITE,
PARSE_EVENTS__TERM_TYPE_DRV_CFG,
PARSE_EVENTS__TERM_TYPE_PERCORE,
__PARSE_EVENTS__TERM_TYPE_NR,
};

View File

@ -283,6 +283,7 @@ inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_INHERIT); }
no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); }
overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_OVERWRITE); }
no-overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOOVERWRITE); }
percore { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE); }
, { return ','; }
"/" { BEGIN(INITIAL); return '/'; }
{name_minus} { return str(yyscanner, PE_NAME); }

View File

@ -5,13 +5,14 @@
#include <subcmd/parse-options.h>
#include "util/parse-regs-options.h"
int
parse_regs(const struct option *opt, const char *str, int unset)
static int
__parse_regs(const struct option *opt, const char *str, int unset, bool intr)
{
uint64_t *mode = (uint64_t *)opt->value;
const struct sample_reg *r;
char *s, *os = NULL, *p;
int ret = -1;
uint64_t mask;
if (unset)
return 0;
@ -22,6 +23,11 @@ parse_regs(const struct option *opt, const char *str, int unset)
if (*mode)
return -1;
if (intr)
mask = arch__intr_reg_mask();
else
mask = arch__user_reg_mask();
/* str may be NULL in case no arg is passed to -I */
if (str) {
/* because str is read-only */
@ -37,19 +43,20 @@ parse_regs(const struct option *opt, const char *str, int unset)
if (!strcmp(s, "?")) {
fprintf(stderr, "available registers: ");
for (r = sample_reg_masks; r->name; r++) {
fprintf(stderr, "%s ", r->name);
if (r->mask & mask)
fprintf(stderr, "%s ", r->name);
}
fputc('\n', stderr);
/* just printing available regs */
return -1;
}
for (r = sample_reg_masks; r->name; r++) {
if (!strcasecmp(s, r->name))
if ((r->mask & mask) && !strcasecmp(s, r->name))
break;
}
if (!r->name) {
ui__warning("unknown register %s,"
" check man page\n", s);
ui__warning("Unknown register \"%s\", check man page or run \"perf record %s?\"\n",
s, intr ? "-I" : "--user-regs=");
goto error;
}
@ -65,8 +72,20 @@ parse_regs(const struct option *opt, const char *str, int unset)
/* default to all possible regs */
if (*mode == 0)
*mode = PERF_REGS_MASK;
*mode = mask;
error:
free(os);
return ret;
}
int
parse_user_regs(const struct option *opt, const char *str, int unset)
{
return __parse_regs(opt, str, unset, false);
}
int
parse_intr_regs(const struct option *opt, const char *str, int unset)
{
return __parse_regs(opt, str, unset, true);
}

View File

@ -2,5 +2,6 @@
#ifndef _PERF_PARSE_REGS_OPTIONS_H
#define _PERF_PARSE_REGS_OPTIONS_H 1
struct option;
int parse_regs(const struct option *opt, const char *str, int unset);
int parse_user_regs(const struct option *opt, const char *str, int unset);
int parse_intr_regs(const struct option *opt, const char *str, int unset);
#endif /* _PERF_PARSE_REGS_OPTIONS_H */

View File

@ -13,6 +13,16 @@ int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused,
return SDT_ARG_SKIP;
}
uint64_t __weak arch__intr_reg_mask(void)
{
return PERF_REGS_MASK;
}
uint64_t __weak arch__user_reg_mask(void)
{
return PERF_REGS_MASK;
}
#ifdef HAVE_PERF_REGS_SUPPORT
int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
{

View File

@ -12,6 +12,7 @@ struct sample_reg {
uint64_t mask;
};
#define SMPL_REG(n, b) { .name = #n, .mask = 1ULL << (b) }
#define SMPL_REG2(n, b) { .name = #n, .mask = 3ULL << (b) }
#define SMPL_REG_END { .name = NULL }
extern const struct sample_reg sample_reg_masks[];
@ -22,6 +23,8 @@ enum {
};
int arch_sdt_arg_parse_op(char *old_op, char **new_op);
uint64_t arch__intr_reg_mask(void);
uint64_t arch__user_reg_mask(void);
#ifdef HAVE_PERF_REGS_SUPPORT
#include <perf_regs.h>

View File

@ -29,6 +29,61 @@
#include "stat.h"
#include "arch/common.h"
#ifdef HAVE_ZSTD_SUPPORT
static int perf_session__process_compressed_event(struct perf_session *session,
union perf_event *event, u64 file_offset)
{
void *src;
size_t decomp_size, src_size;
u64 decomp_last_rem = 0;
size_t decomp_len = session->header.env.comp_mmap_len;
struct decomp *decomp, *decomp_last = session->decomp_last;
decomp = mmap(NULL, sizeof(struct decomp) + decomp_len, PROT_READ|PROT_WRITE,
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if (decomp == MAP_FAILED) {
pr_err("Couldn't allocate memory for decompression\n");
return -1;
}
decomp->file_pos = file_offset;
decomp->head = 0;
if (decomp_last) {
decomp_last_rem = decomp_last->size - decomp_last->head;
memcpy(decomp->data, &(decomp_last->data[decomp_last->head]), decomp_last_rem);
decomp->size = decomp_last_rem;
}
src = (void *)event + sizeof(struct compressed_event);
src_size = event->pack.header.size - sizeof(struct compressed_event);
decomp_size = zstd_decompress_stream(&(session->zstd_data), src, src_size,
&(decomp->data[decomp_last_rem]), decomp_len - decomp_last_rem);
if (!decomp_size) {
munmap(decomp, sizeof(struct decomp) + decomp_len);
pr_err("Couldn't decompress data\n");
return -1;
}
decomp->size += decomp_size;
if (session->decomp == NULL) {
session->decomp = decomp;
session->decomp_last = decomp;
} else {
session->decomp_last->next = decomp;
session->decomp_last = decomp;
}
pr_debug("decomp (B): %ld to %ld\n", src_size, decomp_size);
return 0;
}
#else /* !HAVE_ZSTD_SUPPORT */
#define perf_session__process_compressed_event perf_session__process_compressed_event_stub
#endif
static int perf_session__deliver_event(struct perf_session *session,
union perf_event *event,
struct perf_tool *tool,
@ -197,6 +252,21 @@ static void perf_session__delete_threads(struct perf_session *session)
machine__delete_threads(&session->machines.host);
}
static void perf_session__release_decomp_events(struct perf_session *session)
{
struct decomp *next, *decomp;
size_t decomp_len;
next = session->decomp;
decomp_len = session->header.env.comp_mmap_len;
do {
decomp = next;
if (decomp == NULL)
break;
next = decomp->next;
munmap(decomp, decomp_len + sizeof(struct decomp));
} while (1);
}
void perf_session__delete(struct perf_session *session)
{
if (session == NULL)
@ -205,6 +275,7 @@ void perf_session__delete(struct perf_session *session)
auxtrace_index__free(&session->auxtrace_index);
perf_session__destroy_kernel_maps(session);
perf_session__delete_threads(session);
perf_session__release_decomp_events(session);
perf_env__exit(&session->header.env);
machines__exit(&session->machines);
if (session->data)
@ -358,6 +429,14 @@ static int process_stat_round_stub(struct perf_session *perf_session __maybe_unu
return 0;
}
static int perf_session__process_compressed_event_stub(struct perf_session *session __maybe_unused,
union perf_event *event __maybe_unused,
u64 file_offset __maybe_unused)
{
dump_printf(": unhandled!\n");
return 0;
}
void perf_tool__fill_defaults(struct perf_tool *tool)
{
if (tool->sample == NULL)
@ -430,6 +509,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
tool->time_conv = process_event_op2_stub;
if (tool->feature == NULL)
tool->feature = process_event_op2_stub;
if (tool->compressed == NULL)
tool->compressed = perf_session__process_compressed_event;
}
static void swap_sample_id_all(union perf_event *event, void *data)
@ -1373,7 +1454,9 @@ static s64 perf_session__process_user_event(struct perf_session *session,
int fd = perf_data__fd(session->data);
int err;
dump_event(session->evlist, event, file_offset, &sample);
if (event->header.type != PERF_RECORD_COMPRESSED ||
tool->compressed == perf_session__process_compressed_event_stub)
dump_event(session->evlist, event, file_offset, &sample);
/* These events are processed right away */
switch (event->header.type) {
@ -1426,6 +1509,11 @@ static s64 perf_session__process_user_event(struct perf_session *session,
return tool->time_conv(session, event);
case PERF_RECORD_HEADER_FEATURE:
return tool->feature(session, event);
case PERF_RECORD_COMPRESSED:
err = tool->compressed(session, event, file_offset);
if (err)
dump_event(session->evlist, event, file_offset, &sample);
return err;
default:
return -EINVAL;
}
@ -1708,6 +1796,8 @@ static int perf_session__flush_thread_stacks(struct perf_session *session)
volatile int session_done;
static int __perf_session__process_decomp_events(struct perf_session *session);
static int __perf_session__process_pipe_events(struct perf_session *session)
{
struct ordered_events *oe = &session->ordered_events;
@ -1788,6 +1878,10 @@ more:
if (skip > 0)
head += skip;
err = __perf_session__process_decomp_events(session);
if (err)
goto out_err;
if (!session_done())
goto more;
done:
@ -1836,6 +1930,39 @@ fetch_mmaped_event(struct perf_session *session,
return event;
}
static int __perf_session__process_decomp_events(struct perf_session *session)
{
s64 skip;
u64 size, file_pos = 0;
struct decomp *decomp = session->decomp_last;
if (!decomp)
return 0;
while (decomp->head < decomp->size && !session_done()) {
union perf_event *event = fetch_mmaped_event(session, decomp->head, decomp->size, decomp->data);
if (!event)
break;
size = event->header.size;
if (size < sizeof(struct perf_event_header) ||
(skip = perf_session__process_event(session, event, file_pos)) < 0) {
pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n",
decomp->file_pos + decomp->head, event->header.size, event->header.type);
return -EINVAL;
}
if (skip)
size += skip;
decomp->head += size;
}
return 0;
}
/*
* On 64bit we can mmap the data file in one go. No need for tiny mmap
* slices. On 32bit we use 32MB.
@ -1945,6 +2072,10 @@ more:
head += size;
file_pos += size;
err = __perf_session__process_decomp_events(session);
if (err)
goto out;
ui_progress__update(prog, size);
if (session_done())

View File

@ -8,6 +8,7 @@
#include "machine.h"
#include "data.h"
#include "ordered-events.h"
#include "util/compress.h"
#include <linux/kernel.h>
#include <linux/rbtree.h>
#include <linux/perf_event.h>
@ -35,6 +36,19 @@ struct perf_session {
struct ordered_events ordered_events;
struct perf_data *data;
struct perf_tool *tool;
u64 bytes_transferred;
u64 bytes_compressed;
struct zstd_data zstd_data;
struct decomp *decomp;
struct decomp *decomp_last;
};
struct decomp {
struct decomp *next;
u64 file_pos;
u64 head;
size_t size;
char data[];
};
struct perf_tool;

View File

@ -88,9 +88,17 @@ static void aggr_printout(struct perf_stat_config *config,
config->csv_sep);
break;
case AGGR_NONE:
fprintf(config->output, "CPU%*d%s",
config->csv_output ? 0 : -4,
perf_evsel__cpus(evsel)->map[id], config->csv_sep);
if (evsel->percore) {
fprintf(config->output, "S%d-C%*d%s",
cpu_map__id_to_socket(id),
config->csv_output ? 0 : -5,
cpu_map__id_to_cpu(id), config->csv_sep);
} else {
fprintf(config->output, "CPU%*d%s ",
config->csv_output ? 0 : -5,
perf_evsel__cpus(evsel)->map[id],
config->csv_sep);
}
break;
case AGGR_THREAD:
fprintf(config->output, "%*s-%*d%s",
@ -594,6 +602,41 @@ static void aggr_cb(struct perf_stat_config *config,
}
}
static void print_counter_aggrdata(struct perf_stat_config *config,
struct perf_evsel *counter, int s,
char *prefix, bool metric_only,
bool *first)
{
struct aggr_data ad;
FILE *output = config->output;
u64 ena, run, val;
int id, nr;
double uval;
ad.id = id = config->aggr_map->map[s];
ad.val = ad.ena = ad.run = 0;
ad.nr = 0;
if (!collect_data(config, counter, aggr_cb, &ad))
return;
nr = ad.nr;
ena = ad.ena;
run = ad.run;
val = ad.val;
if (*first && metric_only) {
*first = false;
aggr_printout(config, counter, id, nr);
}
if (prefix && !metric_only)
fprintf(output, "%s", prefix);
uval = val * counter->scale;
printout(config, id, nr, counter, uval, prefix,
run, ena, 1.0, &rt_stat);
if (!metric_only)
fputc('\n', output);
}
static void print_aggr(struct perf_stat_config *config,
struct perf_evlist *evlist,
char *prefix)
@ -601,9 +644,7 @@ static void print_aggr(struct perf_stat_config *config,
bool metric_only = config->metric_only;
FILE *output = config->output;
struct perf_evsel *counter;
int s, id, nr;
double uval;
u64 ena, run, val;
int s;
bool first;
if (!(config->aggr_map || config->aggr_get_id))
@ -616,33 +657,14 @@ static void print_aggr(struct perf_stat_config *config,
* Without each counter has its own line.
*/
for (s = 0; s < config->aggr_map->nr; s++) {
struct aggr_data ad;
if (prefix && metric_only)
fprintf(output, "%s", prefix);
ad.id = id = config->aggr_map->map[s];
first = true;
evlist__for_each_entry(evlist, counter) {
ad.val = ad.ena = ad.run = 0;
ad.nr = 0;
if (!collect_data(config, counter, aggr_cb, &ad))
continue;
nr = ad.nr;
ena = ad.ena;
run = ad.run;
val = ad.val;
if (first && metric_only) {
first = false;
aggr_printout(config, counter, id, nr);
}
if (prefix && !metric_only)
fprintf(output, "%s", prefix);
uval = val * counter->scale;
printout(config, id, nr, counter, uval, prefix,
run, ena, 1.0, &rt_stat);
if (!metric_only)
fputc('\n', output);
print_counter_aggrdata(config, counter, s,
prefix, metric_only,
&first);
}
if (metric_only)
fputc('\n', output);
@ -1089,6 +1111,30 @@ static void print_footer(struct perf_stat_config *config)
"the same PMU. Try reorganizing the group.\n");
}
static void print_percore(struct perf_stat_config *config,
struct perf_evsel *counter, char *prefix)
{
bool metric_only = config->metric_only;
FILE *output = config->output;
int s;
bool first = true;
if (!(config->aggr_map || config->aggr_get_id))
return;
for (s = 0; s < config->aggr_map->nr; s++) {
if (prefix && metric_only)
fprintf(output, "%s", prefix);
print_counter_aggrdata(config, counter, s,
prefix, metric_only,
&first);
}
if (metric_only)
fputc('\n', output);
}
void
perf_evlist__print_counters(struct perf_evlist *evlist,
struct perf_stat_config *config,
@ -1139,7 +1185,10 @@ perf_evlist__print_counters(struct perf_evlist *evlist,
print_no_aggr_metric(config, evlist, prefix);
else {
evlist__for_each_entry(evlist, counter) {
print_counter(config, counter, prefix);
if (counter->percore)
print_percore(config, counter, prefix);
else
print_counter(config, counter, prefix);
}
}
break;

View File

@ -277,9 +277,11 @@ process_counter_values(struct perf_stat_config *config, struct perf_evsel *evsel
if (!evsel->snapshot)
perf_evsel__compute_deltas(evsel, cpu, thread, count);
perf_counts_values__scale(count, config->scale, NULL);
if (config->aggr_mode == AGGR_NONE)
perf_stat__update_shadow_stats(evsel, count->val, cpu,
&rt_stat);
if ((config->aggr_mode == AGGR_NONE) && (!evsel->percore)) {
perf_stat__update_shadow_stats(evsel, count->val,
cpu, &rt_stat);
}
if (config->aggr_mode == AGGR_THREAD) {
if (config->stats)
perf_stat__update_shadow_stats(evsel,

View File

@ -15,6 +15,7 @@
#include "map.h"
#include "symbol.h"
#include "unwind.h"
#include "callchain.h"
#include <api/fs/fs.h>
@ -327,7 +328,7 @@ static int thread__prepare_access(struct thread *thread)
{
int err = 0;
if (symbol_conf.use_callchain)
if (dwarf_callchain_users)
err = __thread__prepare_access(thread);
return err;

View File

@ -28,6 +28,7 @@ typedef int (*event_attr_op)(struct perf_tool *tool,
typedef int (*event_op2)(struct perf_session *session, union perf_event *event);
typedef s64 (*event_op3)(struct perf_session *session, union perf_event *event);
typedef int (*event_op4)(struct perf_session *session, union perf_event *event, u64 data);
typedef int (*event_oe)(struct perf_tool *tool, union perf_event *event,
struct ordered_events *oe);
@ -72,6 +73,7 @@ struct perf_tool {
stat,
stat_round,
feature;
event_op4 compressed;
event_op3 auxtrace;
bool ordered_events;
bool ordering_requires_timestamps;

View File

@ -617,8 +617,6 @@ static unw_accessors_t accessors = {
static int _unwind__prepare_access(struct thread *thread)
{
if (!dwarf_callchain_users)
return 0;
thread->addr_space = unw_create_addr_space(&accessors, 0);
if (!thread->addr_space) {
pr_err("unwind: Can't create unwind address space.\n");
@ -631,15 +629,11 @@ static int _unwind__prepare_access(struct thread *thread)
static void _unwind__flush_access(struct thread *thread)
{
if (!dwarf_callchain_users)
return;
unw_flush_cache(thread->addr_space, 0, 0);
}
static void _unwind__finish_access(struct thread *thread)
{
if (!dwarf_callchain_users)
return;
unw_destroy_addr_space(thread->addr_space);
}

View File

@ -5,6 +5,7 @@
#include "session.h"
#include "debug.h"
#include "env.h"
#include "callchain.h"
struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;
@ -24,6 +25,9 @@ int unwind__prepare_access(struct thread *thread, struct map *map,
struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
int err;
if (!dwarf_callchain_users)
return 0;
if (thread->addr_space) {
pr_debug("unwind: thread map already set, dso=%s\n",
map->dso->name);
@ -65,12 +69,18 @@ out_register:
void unwind__flush_access(struct thread *thread)
{
if (!dwarf_callchain_users)
return;
if (thread->unwind_libunwind_ops)
thread->unwind_libunwind_ops->flush_access(thread);
}
void unwind__finish_access(struct thread *thread)
{
if (!dwarf_callchain_users)
return;
if (thread->unwind_libunwind_ops)
thread->unwind_libunwind_ops->finish_access(thread);
}

111
tools/perf/util/zstd.c Normal file
View File

@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-2.0
#include <string.h>
#include "util/compress.h"
#include "util/debug.h"
int zstd_init(struct zstd_data *data, int level)
{
size_t ret;
data->dstream = ZSTD_createDStream();
if (data->dstream == NULL) {
pr_err("Couldn't create decompression stream.\n");
return -1;
}
ret = ZSTD_initDStream(data->dstream);
if (ZSTD_isError(ret)) {
pr_err("Failed to initialize decompression stream: %s\n", ZSTD_getErrorName(ret));
return -1;
}
if (!level)
return 0;
data->cstream = ZSTD_createCStream();
if (data->cstream == NULL) {
pr_err("Couldn't create compression stream.\n");
return -1;
}
ret = ZSTD_initCStream(data->cstream, level);
if (ZSTD_isError(ret)) {
pr_err("Failed to initialize compression stream: %s\n", ZSTD_getErrorName(ret));
return -1;
}
return 0;
}
int zstd_fini(struct zstd_data *data)
{
if (data->dstream) {
ZSTD_freeDStream(data->dstream);
data->dstream = NULL;
}
if (data->cstream) {
ZSTD_freeCStream(data->cstream);
data->cstream = NULL;
}
return 0;
}
size_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_t dst_size,
void *src, size_t src_size, size_t max_record_size,
size_t process_header(void *record, size_t increment))
{
size_t ret, size, compressed = 0;
ZSTD_inBuffer input = { src, src_size, 0 };
ZSTD_outBuffer output;
void *record;
while (input.pos < input.size) {
record = dst;
size = process_header(record, 0);
compressed += size;
dst += size;
dst_size -= size;
output = (ZSTD_outBuffer){ dst, (dst_size > max_record_size) ?
max_record_size : dst_size, 0 };
ret = ZSTD_compressStream(data->cstream, &output, &input);
ZSTD_flushStream(data->cstream, &output);
if (ZSTD_isError(ret)) {
pr_err("failed to compress %ld bytes: %s\n",
(long)src_size, ZSTD_getErrorName(ret));
memcpy(dst, src, src_size);
return src_size;
}
size = output.pos;
size = process_header(record, size);
compressed += size;
dst += size;
dst_size -= size;
}
return compressed;
}
size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size,
void *dst, size_t dst_size)
{
size_t ret;
ZSTD_inBuffer input = { src, src_size, 0 };
ZSTD_outBuffer output = { dst, dst_size, 0 };
while (input.pos < input.size) {
ret = ZSTD_decompressStream(data->dstream, &output, &input);
if (ZSTD_isError(ret)) {
pr_err("failed to decompress (B): %ld -> %ld : %s\n",
src_size, output.size, ZSTD_getErrorName(ret));
break;
}
output.dst = dst + output.pos;
output.size = dst_size - output.pos;
}
return output.pos;
}