mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
30
tools/perf/CREDITS
Normal file
30
tools/perf/CREDITS
Normal file
|
@ -0,0 +1,30 @@
|
|||
Most of the infrastructure that 'perf' uses here has been reused
|
||||
from the Git project, as of version:
|
||||
|
||||
66996ec: Sync with 1.6.2.4
|
||||
|
||||
Here is an (incomplete!) list of main contributors to those files
|
||||
in util/* and elsewhere:
|
||||
|
||||
Alex Riesen
|
||||
Christian Couder
|
||||
Dmitry Potapov
|
||||
Jeff King
|
||||
Johannes Schindelin
|
||||
Johannes Sixt
|
||||
Junio C Hamano
|
||||
Linus Torvalds
|
||||
Matthias Kestenholz
|
||||
Michal Ostrowski
|
||||
Miklos Vajna
|
||||
Petr Baudis
|
||||
Pierre Habouzit
|
||||
René Scharfe
|
||||
Samuel Tardieu
|
||||
Shawn O. Pearce
|
||||
Steffen Prohaska
|
||||
Steve Haslam
|
||||
|
||||
Thanks guys!
|
||||
|
||||
The full history of the files can be found in the upstream Git commits.
|
343
tools/perf/Documentation/Makefile
Normal file
343
tools/perf/Documentation/Makefile
Normal file
|
@ -0,0 +1,343 @@
|
|||
include ../../scripts/Makefile.include
|
||||
include ../config/utilities.mak
|
||||
|
||||
MAN1_TXT= \
|
||||
$(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
|
||||
$(wildcard perf-*.txt)) \
|
||||
perf.txt
|
||||
MAN5_TXT=
|
||||
MAN7_TXT=
|
||||
|
||||
MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
|
||||
_MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
|
||||
_MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT))
|
||||
|
||||
MAN_XML=$(addprefix $(OUTPUT),$(_MAN_XML))
|
||||
MAN_HTML=$(addprefix $(OUTPUT),$(_MAN_HTML))
|
||||
|
||||
ARTICLES =
|
||||
# with their own formatting rules.
|
||||
SP_ARTICLES =
|
||||
API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt)))
|
||||
SP_ARTICLES += $(API_DOCS)
|
||||
SP_ARTICLES += technical/api-index
|
||||
|
||||
_DOC_HTML = $(_MAN_HTML)
|
||||
_DOC_HTML+=$(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
|
||||
DOC_HTML=$(addprefix $(OUTPUT),$(_DOC_HTML))
|
||||
|
||||
_DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
|
||||
_DOC_MAN5=$(patsubst %.txt,%.5,$(MAN5_TXT))
|
||||
_DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
|
||||
|
||||
DOC_MAN1=$(addprefix $(OUTPUT),$(_DOC_MAN1))
|
||||
DOC_MAN5=$(addprefix $(OUTPUT),$(_DOC_MAN5))
|
||||
DOC_MAN7=$(addprefix $(OUTPUT),$(_DOC_MAN7))
|
||||
|
||||
# Make the path relative to DESTDIR, not prefix
|
||||
ifndef DESTDIR
|
||||
prefix?=$(HOME)
|
||||
endif
|
||||
bindir?=$(prefix)/bin
|
||||
htmldir?=$(prefix)/share/doc/perf-doc
|
||||
pdfdir?=$(prefix)/share/doc/perf-doc
|
||||
mandir?=$(prefix)/share/man
|
||||
man1dir=$(mandir)/man1
|
||||
man5dir=$(mandir)/man5
|
||||
man7dir=$(mandir)/man7
|
||||
|
||||
ASCIIDOC=asciidoc
|
||||
ASCIIDOC_EXTRA = --unsafe
|
||||
MANPAGE_XSL = manpage-normal.xsl
|
||||
XMLTO_EXTRA =
|
||||
INSTALL?=install
|
||||
RM ?= rm -f
|
||||
DOC_REF = origin/man
|
||||
HTML_REF = origin/html
|
||||
|
||||
infodir?=$(prefix)/share/info
|
||||
MAKEINFO=makeinfo
|
||||
INSTALL_INFO=install-info
|
||||
DOCBOOK2X_TEXI=docbook2x-texi
|
||||
DBLATEX=dblatex
|
||||
XMLTO=xmlto
|
||||
ifndef PERL_PATH
|
||||
PERL_PATH = /usr/bin/perl
|
||||
endif
|
||||
|
||||
-include ../config.mak.autogen
|
||||
-include ../config.mak
|
||||
|
||||
_tmp_tool_path := $(call get-executable,$(ASCIIDOC))
|
||||
ifeq ($(_tmp_tool_path),)
|
||||
missing_tools = $(ASCIIDOC)
|
||||
endif
|
||||
|
||||
_tmp_tool_path := $(call get-executable,$(XMLTO))
|
||||
ifeq ($(_tmp_tool_path),)
|
||||
missing_tools += $(XMLTO)
|
||||
endif
|
||||
|
||||
#
|
||||
# For asciidoc ...
|
||||
# -7.1.2, no extra settings are needed.
|
||||
# 8.0-, set ASCIIDOC8.
|
||||
#
|
||||
|
||||
#
|
||||
# For docbook-xsl ...
|
||||
# -1.68.1, set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0)
|
||||
# 1.69.0, no extra settings are needed?
|
||||
# 1.69.1-1.71.0, set DOCBOOK_SUPPRESS_SP?
|
||||
# 1.71.1, no extra settings are needed?
|
||||
# 1.72.0, set DOCBOOK_XSL_172.
|
||||
# 1.73.0-, set ASCIIDOC_NO_ROFF
|
||||
#
|
||||
|
||||
#
|
||||
# If you had been using DOCBOOK_XSL_172 in an attempt to get rid
|
||||
# of 'the ".ft C" problem' in your generated manpages, and you
|
||||
# instead ended up with weird characters around callouts, try
|
||||
# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8).
|
||||
#
|
||||
|
||||
ifdef ASCIIDOC8
|
||||
ASCIIDOC_EXTRA += -a asciidoc7compatible
|
||||
endif
|
||||
ifdef DOCBOOK_XSL_172
|
||||
ASCIIDOC_EXTRA += -a perf-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 perf-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))
|
||||
|
||||
#
|
||||
# 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=perf&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_DB2TEXI = @echo ' DB2TEXI '$@;
|
||||
QUIET_MAKEINFO = @echo ' MAKEINFO '$@;
|
||||
QUIET_DBLATEX = @echo ' DBLATEX '$@;
|
||||
QUIET_XSLTPROC = @echo ' XSLTPROC '$@;
|
||||
QUIET_GEN = @echo ' GEN '$@;
|
||||
QUIET_STDERR = 2> /dev/null
|
||||
QUIET_SUBDIR0 = +@subdir=
|
||||
QUIET_SUBDIR1 = ;$(NO_SUBDIR) \
|
||||
echo ' SUBDIR ' $$subdir; \
|
||||
$(MAKE) $(PRINT_DIR) -C $$subdir
|
||||
export V
|
||||
endif
|
||||
endif
|
||||
|
||||
all: html man
|
||||
|
||||
html: $(DOC_HTML)
|
||||
|
||||
$(DOC_HTML) $(DOC_MAN1) $(DOC_MAN5) $(DOC_MAN7): asciidoc.conf
|
||||
|
||||
man: man1 man5 man7
|
||||
man1: $(DOC_MAN1)
|
||||
man5: $(DOC_MAN5)
|
||||
man7: $(DOC_MAN7)
|
||||
|
||||
info: $(OUTPUT)perf.info $(OUTPUT)perfman.info
|
||||
|
||||
pdf: $(OUTPUT)user-manual.pdf
|
||||
|
||||
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)$(man1dir); \
|
||||
# $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir); \
|
||||
# $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir); \
|
||||
$(INSTALL) -m 644 $(DOC_MAN1) $(DESTDIR)$(man1dir); \
|
||||
# $(INSTALL) -m 644 $(DOC_MAN5) $(DESTDIR)$(man5dir); \
|
||||
# $(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
|
||||
|
||||
install-man: check-man-tools man
|
||||
|
||||
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
|
||||
|
||||
try-install-man: $(DO_INSTALL_MAN)
|
||||
|
||||
install-info: info
|
||||
$(call QUIET_INSTALL, Documentation-info) \
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(infodir); \
|
||||
$(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir); \
|
||||
if test -r $(DESTDIR)$(infodir)/dir; then \
|
||||
$(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perf.info ;\
|
||||
$(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perfman.info ;\
|
||||
else \
|
||||
echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \
|
||||
fi
|
||||
|
||||
install-pdf: pdf
|
||||
$(call QUIET_INSTALL, Documentation-pdf) \
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir); \
|
||||
$(INSTALL) -m 644 $(OUTPUT)user-manual.pdf $(DESTDIR)$(pdfdir)
|
||||
|
||||
#install-html: html
|
||||
# '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
|
||||
|
||||
|
||||
#
|
||||
# Determine "include::" file references in asciidoc files.
|
||||
#
|
||||
$(OUTPUT)doc.dep : $(wildcard *.txt) build-docdep.perl
|
||||
$(QUIET_GEN)$(RM) $@+ $@ && \
|
||||
$(PERL_PATH) ./build-docdep.perl >$@+ $(QUIET_STDERR) && \
|
||||
mv $@+ $@
|
||||
|
||||
-include $(OUPTUT)doc.dep
|
||||
|
||||
_cmds_txt = cmds-ancillaryinterrogators.txt \
|
||||
cmds-ancillarymanipulators.txt \
|
||||
cmds-mainporcelain.txt \
|
||||
cmds-plumbinginterrogators.txt \
|
||||
cmds-plumbingmanipulators.txt \
|
||||
cmds-synchingrepositories.txt \
|
||||
cmds-synchelpers.txt \
|
||||
cmds-purehelpers.txt \
|
||||
cmds-foreignscminterface.txt
|
||||
cmds_txt=$(addprefix $(OUTPUT),$(_cmds_txt))
|
||||
|
||||
$(cmds_txt): $(OUTPUT)cmd-list.made
|
||||
|
||||
$(OUTPUT)cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
|
||||
$(QUIET_GEN)$(RM) $@ && \
|
||||
$(PERL_PATH) ./cmd-list.perl ../command-list.txt $(QUIET_STDERR) && \
|
||||
date >$@
|
||||
|
||||
CLEAN_FILES = \
|
||||
$(MAN_XML) $(addsuffix +,$(MAN_XML)) \
|
||||
$(MAN_HTML) $(addsuffix +,$(MAN_HTML)) \
|
||||
$(DOC_HTML) $(DOC_MAN1) $(DOC_MAN5) $(DOC_MAN7) \
|
||||
$(OUTPUT)*.texi $(OUTPUT)*.texi+ $(OUTPUT)*.texi++ \
|
||||
$(OUTPUT)perf.info $(OUTPUT)perfman.info \
|
||||
$(OUTPUT)howto-index.txt $(OUTPUT)howto/*.html $(OUTPUT)doc.dep \
|
||||
$(OUTPUT)technical/api-*.html $(OUTPUT)technical/api-index.txt \
|
||||
$(cmds_txt) $(OUTPUT)*.made
|
||||
clean:
|
||||
$(call QUIET_CLEAN, Documentation) $(RM) $(CLEAN_FILES)
|
||||
|
||||
$(MAN_HTML): $(OUTPUT)%.html : %.txt
|
||||
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
|
||||
$(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \
|
||||
$(ASCIIDOC_EXTRA) -aperf_version=$(PERF_VERSION) -o $@+ $< && \
|
||||
mv $@+ $@
|
||||
|
||||
$(OUTPUT)%.1 $(OUTPUT)%.5 $(OUTPUT)%.7 : $(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 -f asciidoc.conf \
|
||||
$(ASCIIDOC_EXTRA) -aperf_version=$(PERF_VERSION) -o $@+ $< && \
|
||||
mv $@+ $@
|
||||
|
||||
XSLT = docbook.xsl
|
||||
XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
|
||||
|
||||
$(OUTPUT)user-manual.html: $(OUTPUT)user-manual.xml
|
||||
$(QUIET_XSLTPROC)xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
|
||||
|
||||
$(OUTPUT)perf.info: $(OUTPUT)user-manual.texi
|
||||
$(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ $(OUTPUT)user-manual.texi
|
||||
|
||||
$(OUTPUT)user-manual.texi: $(OUTPUT)user-manual.xml
|
||||
$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
|
||||
$(DOCBOOK2X_TEXI) $(OUTPUT)user-manual.xml --encoding=UTF-8 --to-stdout >$@++ && \
|
||||
$(PERL_PATH) fix-texi.perl <$@++ >$@+ && \
|
||||
rm $@++ && \
|
||||
mv $@+ $@
|
||||
|
||||
$(OUTPUT)user-manual.pdf: $(OUTPUT)user-manual.xml
|
||||
$(QUIET_DBLATEX)$(RM) $@+ $@ && \
|
||||
$(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< && \
|
||||
mv $@+ $@
|
||||
|
||||
$(OUTPUT)perfman.texi: $(MAN_XML) cat-texi.perl
|
||||
$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
|
||||
($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \
|
||||
--to-stdout $(xml) &&) true) > $@++ && \
|
||||
$(PERL_PATH) cat-texi.perl $@ <$@++ >$@+ && \
|
||||
rm $@++ && \
|
||||
mv $@+ $@
|
||||
|
||||
$(OUTPUT)perfman.info: $(OUTPUT)perfman.texi
|
||||
$(QUIET_MAKEINFO)$(MAKEINFO) --no-split --no-validate $*.texi
|
||||
|
||||
$(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
|
||||
$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
|
||||
$(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+ && \
|
||||
mv $@+ $@
|
||||
|
||||
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
|
||||
$(QUIET_GEN)$(RM) $@+ $@ && \
|
||||
'$(SHELL_PATH_SQ)' ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \
|
||||
mv $@+ $@
|
||||
|
||||
$(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
|
||||
$(QUIET_ASCIIDOC)$(ASCIIDOC) -b xhtml11 $*.txt
|
||||
|
||||
WEBDOC_DEST = /pub/software/tools/perf/docs
|
||||
|
||||
$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
|
||||
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
|
||||
sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+ && \
|
||||
mv $@+ $@
|
||||
|
||||
# UNIMPLEMENTED
|
||||
#install-webdoc : html
|
||||
# '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(WEBDOC_DEST)
|
||||
|
||||
# quick-install: quick-install-man
|
||||
|
||||
# quick-install-man:
|
||||
# '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
|
||||
|
||||
#quick-install-html:
|
||||
# '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
|
78
tools/perf/Documentation/android.txt
Normal file
78
tools/perf/Documentation/android.txt
Normal file
|
@ -0,0 +1,78 @@
|
|||
How to compile perf for Android
|
||||
=========================================
|
||||
|
||||
I. Set the Android NDK environment
|
||||
------------------------------------------------
|
||||
|
||||
(a). Use the Android NDK
|
||||
------------------------------------------------
|
||||
1. You need to download and install the Android Native Development Kit (NDK).
|
||||
Set the NDK variable to point to the path where you installed the NDK:
|
||||
export NDK=/path/to/android-ndk
|
||||
|
||||
2. Set cross-compiling environment variables for NDK toolchain and sysroot.
|
||||
For arm:
|
||||
export NDK_TOOLCHAIN=${NDK}/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/arm-linux-androideabi-
|
||||
export NDK_SYSROOT=${NDK}/platforms/android-9/arch-arm
|
||||
For x86:
|
||||
export NDK_TOOLCHAIN=${NDK}/toolchains/x86-4.6/prebuilt/linux-x86/bin/i686-linux-android-
|
||||
export NDK_SYSROOT=${NDK}/platforms/android-9/arch-x86
|
||||
|
||||
This method is not working for Android NDK versions up to Revision 8b.
|
||||
perf uses some bionic enhancements that are not included in these NDK versions.
|
||||
You can use method (b) described below instead.
|
||||
|
||||
(b). Use the Android source tree
|
||||
-----------------------------------------------
|
||||
1. Download the master branch of the Android source tree.
|
||||
Set the environment for the target you want using:
|
||||
source build/envsetup.sh
|
||||
lunch
|
||||
|
||||
2. Build your own NDK sysroot to contain latest bionic changes and set the
|
||||
NDK sysroot environment variable.
|
||||
cd ${ANDROID_BUILD_TOP}/ndk
|
||||
For arm:
|
||||
./build/tools/build-ndk-sysroot.sh --abi=arm
|
||||
export NDK_SYSROOT=${ANDROID_BUILD_TOP}/ndk/build/platforms/android-3/arch-arm
|
||||
For x86:
|
||||
./build/tools/build-ndk-sysroot.sh --abi=x86
|
||||
export NDK_SYSROOT=${ANDROID_BUILD_TOP}/ndk/build/platforms/android-3/arch-x86
|
||||
|
||||
3. Set the NDK toolchain environment variable.
|
||||
For arm:
|
||||
export NDK_TOOLCHAIN=${ANDROID_TOOLCHAIN}/arm-linux-androideabi-
|
||||
For x86:
|
||||
export NDK_TOOLCHAIN=${ANDROID_TOOLCHAIN}/i686-linux-android-
|
||||
|
||||
II. Compile perf for Android
|
||||
------------------------------------------------
|
||||
You need to run make with the NDK toolchain and sysroot defined above:
|
||||
For arm:
|
||||
make ARCH=arm CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}"
|
||||
For x86:
|
||||
make ARCH=x86 CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}"
|
||||
|
||||
III. Install perf
|
||||
-----------------------------------------------
|
||||
You need to connect to your Android device/emulator using adb.
|
||||
Install perf using:
|
||||
adb push perf /data/perf
|
||||
|
||||
If you also want to use perf-archive you need busybox tools for Android.
|
||||
For installing perf-archive, you first need to replace #!/bin/bash with #!/system/bin/sh:
|
||||
sed 's/#!\/bin\/bash/#!\/system\/bin\/sh/g' perf-archive >> /tmp/perf-archive
|
||||
chmod +x /tmp/perf-archive
|
||||
adb push /tmp/perf-archive /data/perf-archive
|
||||
|
||||
IV. Environment settings for running perf
|
||||
------------------------------------------------
|
||||
Some perf features need environment variables to run properly.
|
||||
You need to set these before running perf on the target:
|
||||
adb shell
|
||||
# PERF_PAGER=cat
|
||||
|
||||
IV. Run perf
|
||||
------------------------------------------------
|
||||
Run perf on your device/emulator to which you previously connected using adb:
|
||||
# ./data/perf
|
91
tools/perf/Documentation/asciidoc.conf
Normal file
91
tools/perf/Documentation/asciidoc.conf
Normal file
|
@ -0,0 +1,91 @@
|
|||
## linkperf: macro
|
||||
#
|
||||
# Usage: linkperf:command[manpage-section]
|
||||
#
|
||||
# Note, {0} is the manpage section, while {target} is the command.
|
||||
#
|
||||
# Show PERF link as: <command>(<section>); if section is defined, else just show
|
||||
# the command.
|
||||
|
||||
[macros]
|
||||
(?su)[\\]?(?P<name>linkperf):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
|
||||
|
||||
[attributes]
|
||||
asterisk=*
|
||||
plus=+
|
||||
caret=^
|
||||
startsb=[
|
||||
endsb=]
|
||||
tilde=~
|
||||
|
||||
ifdef::backend-docbook[]
|
||||
[linkperf-inlinemacro]
|
||||
{0%{target}}
|
||||
{0#<citerefentry>}
|
||||
{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>}
|
||||
{0#</citerefentry>}
|
||||
endif::backend-docbook[]
|
||||
|
||||
ifdef::backend-docbook[]
|
||||
ifndef::perf-asciidoc-no-roff[]
|
||||
# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
|
||||
# v1.72 breaks with this because it replaces dots not in roff requests.
|
||||
[listingblock]
|
||||
<example><title>{title}</title>
|
||||
<literallayout>
|
||||
ifdef::doctype-manpage[]
|
||||
.ft C
|
||||
endif::doctype-manpage[]
|
||||
|
|
||||
ifdef::doctype-manpage[]
|
||||
.ft
|
||||
endif::doctype-manpage[]
|
||||
</literallayout>
|
||||
{title#}</example>
|
||||
endif::perf-asciidoc-no-roff[]
|
||||
|
||||
ifdef::perf-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::perf-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">perf</refmiscinfo>
|
||||
<refmiscinfo class="version">{perf_version}</refmiscinfo>
|
||||
<refmiscinfo class="manual">perf Manual</refmiscinfo>
|
||||
</refmeta>
|
||||
<refnamediv>
|
||||
<refname>{manname}</refname>
|
||||
<refpurpose>{manpurpose}</refpurpose>
|
||||
</refnamediv>
|
||||
endif::backend-docbook[]
|
||||
endif::doctype-manpage[]
|
||||
|
||||
ifdef::backend-xhtml11[]
|
||||
[linkperf-inlinemacro]
|
||||
<a href="{target}.html">{target}{0?({0})}</a>
|
||||
endif::backend-xhtml11[]
|
225
tools/perf/Documentation/examples.txt
Normal file
225
tools/perf/Documentation/examples.txt
Normal file
|
@ -0,0 +1,225 @@
|
|||
|
||||
------------------------------
|
||||
****** perf by examples ******
|
||||
------------------------------
|
||||
|
||||
[ From an e-mail by Ingo Molnar, http://lkml.org/lkml/2009/8/4/346 ]
|
||||
|
||||
|
||||
First, discovery/enumeration of available counters can be done via
|
||||
'perf list':
|
||||
|
||||
titan:~> perf list
|
||||
[...]
|
||||
kmem:kmalloc [Tracepoint event]
|
||||
kmem:kmem_cache_alloc [Tracepoint event]
|
||||
kmem:kmalloc_node [Tracepoint event]
|
||||
kmem:kmem_cache_alloc_node [Tracepoint event]
|
||||
kmem:kfree [Tracepoint event]
|
||||
kmem:kmem_cache_free [Tracepoint event]
|
||||
kmem:mm_page_free [Tracepoint event]
|
||||
kmem:mm_page_free_batched [Tracepoint event]
|
||||
kmem:mm_page_alloc [Tracepoint event]
|
||||
kmem:mm_page_alloc_zone_locked [Tracepoint event]
|
||||
kmem:mm_page_pcpu_drain [Tracepoint event]
|
||||
kmem:mm_page_alloc_extfrag [Tracepoint event]
|
||||
|
||||
Then any (or all) of the above event sources can be activated and
|
||||
measured. For example the page alloc/free properties of a 'hackbench
|
||||
run' are:
|
||||
|
||||
titan:~> perf stat -e kmem:mm_page_pcpu_drain -e kmem:mm_page_alloc
|
||||
-e kmem:mm_page_free_batched -e kmem:mm_page_free ./hackbench 10
|
||||
Time: 0.575
|
||||
|
||||
Performance counter stats for './hackbench 10':
|
||||
|
||||
13857 kmem:mm_page_pcpu_drain
|
||||
27576 kmem:mm_page_alloc
|
||||
6025 kmem:mm_page_free_batched
|
||||
20934 kmem:mm_page_free
|
||||
|
||||
0.613972165 seconds time elapsed
|
||||
|
||||
You can observe the statistical properties as well, by using the
|
||||
'repeat the workload N times' feature of perf stat:
|
||||
|
||||
titan:~> perf stat --repeat 5 -e kmem:mm_page_pcpu_drain -e
|
||||
kmem:mm_page_alloc -e kmem:mm_page_free_batched -e
|
||||
kmem:mm_page_free ./hackbench 10
|
||||
Time: 0.627
|
||||
Time: 0.644
|
||||
Time: 0.564
|
||||
Time: 0.559
|
||||
Time: 0.626
|
||||
|
||||
Performance counter stats for './hackbench 10' (5 runs):
|
||||
|
||||
12920 kmem:mm_page_pcpu_drain ( +- 3.359% )
|
||||
25035 kmem:mm_page_alloc ( +- 3.783% )
|
||||
6104 kmem:mm_page_free_batched ( +- 0.934% )
|
||||
18376 kmem:mm_page_free ( +- 4.941% )
|
||||
|
||||
0.643954516 seconds time elapsed ( +- 2.363% )
|
||||
|
||||
Furthermore, these tracepoints can be used to sample the workload as
|
||||
well. For example the page allocations done by a 'git gc' can be
|
||||
captured the following way:
|
||||
|
||||
titan:~/git> perf record -e kmem:mm_page_alloc -c 1 ./git gc
|
||||
Counting objects: 1148, done.
|
||||
Delta compression using up to 2 threads.
|
||||
Compressing objects: 100% (450/450), done.
|
||||
Writing objects: 100% (1148/1148), done.
|
||||
Total 1148 (delta 690), reused 1148 (delta 690)
|
||||
[ perf record: Captured and wrote 0.267 MB perf.data (~11679 samples) ]
|
||||
|
||||
To check which functions generated page allocations:
|
||||
|
||||
titan:~/git> perf report
|
||||
# Samples: 10646
|
||||
#
|
||||
# Overhead Command Shared Object
|
||||
# ........ ............... ..........................
|
||||
#
|
||||
23.57% git-repack /lib64/libc-2.5.so
|
||||
21.81% git /lib64/libc-2.5.so
|
||||
14.59% git ./git
|
||||
11.79% git-repack ./git
|
||||
7.12% git /lib64/ld-2.5.so
|
||||
3.16% git-repack /lib64/libpthread-2.5.so
|
||||
2.09% git-repack /bin/bash
|
||||
1.97% rm /lib64/libc-2.5.so
|
||||
1.39% mv /lib64/ld-2.5.so
|
||||
1.37% mv /lib64/libc-2.5.so
|
||||
1.12% git-repack /lib64/ld-2.5.so
|
||||
0.95% rm /lib64/ld-2.5.so
|
||||
0.90% git-update-serv /lib64/libc-2.5.so
|
||||
0.73% git-update-serv /lib64/ld-2.5.so
|
||||
0.68% perf /lib64/libpthread-2.5.so
|
||||
0.64% git-repack /usr/lib64/libz.so.1.2.3
|
||||
|
||||
Or to see it on a more finegrained level:
|
||||
|
||||
titan:~/git> perf report --sort comm,dso,symbol
|
||||
# Samples: 10646
|
||||
#
|
||||
# Overhead Command Shared Object Symbol
|
||||
# ........ ............... .......................... ......
|
||||
#
|
||||
9.35% git-repack ./git [.] insert_obj_hash
|
||||
9.12% git ./git [.] insert_obj_hash
|
||||
7.31% git /lib64/libc-2.5.so [.] memcpy
|
||||
6.34% git-repack /lib64/libc-2.5.so [.] _int_malloc
|
||||
6.24% git-repack /lib64/libc-2.5.so [.] memcpy
|
||||
5.82% git-repack /lib64/libc-2.5.so [.] __GI___fork
|
||||
5.47% git /lib64/libc-2.5.so [.] _int_malloc
|
||||
2.99% git /lib64/libc-2.5.so [.] memset
|
||||
|
||||
Furthermore, call-graph sampling can be done too, of page
|
||||
allocations - to see precisely what kind of page allocations there
|
||||
are:
|
||||
|
||||
titan:~/git> perf record -g -e kmem:mm_page_alloc -c 1 ./git gc
|
||||
Counting objects: 1148, done.
|
||||
Delta compression using up to 2 threads.
|
||||
Compressing objects: 100% (450/450), done.
|
||||
Writing objects: 100% (1148/1148), done.
|
||||
Total 1148 (delta 690), reused 1148 (delta 690)
|
||||
[ perf record: Captured and wrote 0.963 MB perf.data (~42069 samples) ]
|
||||
|
||||
titan:~/git> perf report -g
|
||||
# Samples: 10686
|
||||
#
|
||||
# Overhead Command Shared Object
|
||||
# ........ ............... ..........................
|
||||
#
|
||||
23.25% git-repack /lib64/libc-2.5.so
|
||||
|
|
||||
|--50.00%-- _int_free
|
||||
|
|
||||
|--37.50%-- __GI___fork
|
||||
| make_child
|
||||
|
|
||||
|--12.50%-- ptmalloc_unlock_all2
|
||||
| make_child
|
||||
|
|
||||
--6.25%-- __GI_strcpy
|
||||
21.61% git /lib64/libc-2.5.so
|
||||
|
|
||||
|--30.00%-- __GI_read
|
||||
| |
|
||||
| --83.33%-- git_config_from_file
|
||||
| git_config
|
||||
| |
|
||||
[...]
|
||||
|
||||
Or you can observe the whole system's page allocations for 10
|
||||
seconds:
|
||||
|
||||
titan:~/git> perf stat -a -e kmem:mm_page_pcpu_drain -e
|
||||
kmem:mm_page_alloc -e kmem:mm_page_free_batched -e
|
||||
kmem:mm_page_free sleep 10
|
||||
|
||||
Performance counter stats for 'sleep 10':
|
||||
|
||||
171585 kmem:mm_page_pcpu_drain
|
||||
322114 kmem:mm_page_alloc
|
||||
73623 kmem:mm_page_free_batched
|
||||
254115 kmem:mm_page_free
|
||||
|
||||
10.000591410 seconds time elapsed
|
||||
|
||||
Or observe how fluctuating the page allocations are, via statistical
|
||||
analysis done over ten 1-second intervals:
|
||||
|
||||
titan:~/git> perf stat --repeat 10 -a -e kmem:mm_page_pcpu_drain -e
|
||||
kmem:mm_page_alloc -e kmem:mm_page_free_batched -e
|
||||
kmem:mm_page_free sleep 1
|
||||
|
||||
Performance counter stats for 'sleep 1' (10 runs):
|
||||
|
||||
17254 kmem:mm_page_pcpu_drain ( +- 3.709% )
|
||||
34394 kmem:mm_page_alloc ( +- 4.617% )
|
||||
7509 kmem:mm_page_free_batched ( +- 4.820% )
|
||||
25653 kmem:mm_page_free ( +- 3.672% )
|
||||
|
||||
1.058135029 seconds time elapsed ( +- 3.089% )
|
||||
|
||||
Or you can annotate the recorded 'git gc' run on a per symbol basis
|
||||
and check which instructions/source-code generated page allocations:
|
||||
|
||||
titan:~/git> perf annotate __GI___fork
|
||||
------------------------------------------------
|
||||
Percent | Source code & Disassembly of libc-2.5.so
|
||||
------------------------------------------------
|
||||
:
|
||||
:
|
||||
: Disassembly of section .plt:
|
||||
: Disassembly of section .text:
|
||||
:
|
||||
: 00000031a2e95560 <__fork>:
|
||||
[...]
|
||||
0.00 : 31a2e95602: b8 38 00 00 00 mov $0x38,%eax
|
||||
0.00 : 31a2e95607: 0f 05 syscall
|
||||
83.42 : 31a2e95609: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax
|
||||
0.00 : 31a2e9560f: 0f 87 4d 01 00 00 ja 31a2e95762 <__fork+0x202>
|
||||
0.00 : 31a2e95615: 85 c0 test %eax,%eax
|
||||
|
||||
( this shows that 83.42% of __GI___fork's page allocations come from
|
||||
the 0x38 system call it performs. )
|
||||
|
||||
etc. etc. - a lot more is possible. I could list a dozen of
|
||||
other different usecases straight away - neither of which is
|
||||
possible via /proc/vmstat.
|
||||
|
||||
/proc/vmstat is not in the same league really, in terms of
|
||||
expressive power of system analysis and performance
|
||||
analysis.
|
||||
|
||||
All that the above results needed were those new tracepoints
|
||||
in include/tracing/events/kmem.h.
|
||||
|
||||
Ingo
|
||||
|
||||
|
15
tools/perf/Documentation/jit-interface.txt
Normal file
15
tools/perf/Documentation/jit-interface.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
perf supports a simple JIT interface to resolve symbols for dynamic code generated
|
||||
by a JIT.
|
||||
|
||||
The JIT has to write a /tmp/perf-%d.map (%d = pid of process) file
|
||||
|
||||
This is a text file.
|
||||
|
||||
Each line has the following format, fields separated with spaces:
|
||||
|
||||
START SIZE symbolname
|
||||
|
||||
START and SIZE are hex numbers without 0x.
|
||||
symbolname is the rest of the line, so it could contain special characters.
|
||||
|
||||
The ownership of the file has to match the process.
|
14
tools/perf/Documentation/manpage-1.72.xsl
Normal file
14
tools/perf/Documentation/manpage-1.72.xsl
Normal 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">▓</xsl:param>
|
||||
<xsl:param name="git.docbook.dot" >⌂</xsl:param>
|
||||
|
||||
</xsl:stylesheet>
|
35
tools/perf/Documentation/manpage-base.xsl
Normal file
35
tools/perf/Documentation/manpage-base.xsl
Normal 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 </xsl:text>
|
||||
<xsl:apply-templates/>
|
||||
<xsl:text> </xsl:text>
|
||||
</xsl:template>
|
||||
<xsl:template match="callout">
|
||||
<xsl:value-of select="concat(
|
||||
$git.docbook.backslash,'fB',
|
||||
substring-after(@arearefs,'-'),
|
||||
'. ',$git.docbook.backslash,'fR')"/>
|
||||
<xsl:apply-templates/>
|
||||
<xsl:value-of select="$git.docbook.dot"/>
|
||||
<xsl:text>br </xsl:text>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
17
tools/perf/Documentation/manpage-bold-literal.xsl
Normal file
17
tools/perf/Documentation/manpage-bold-literal.xsl
Normal 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>
|
13
tools/perf/Documentation/manpage-normal.xsl
Normal file
13
tools/perf/Documentation/manpage-normal.xsl
Normal 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>
|
21
tools/perf/Documentation/manpage-suppress-sp.xsl
Normal file
21
tools/perf/Documentation/manpage-suppress-sp.xsl
Normal 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> </xsl:text>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
101
tools/perf/Documentation/perf-annotate.txt
Normal file
101
tools/perf/Documentation/perf-annotate.txt
Normal file
|
@ -0,0 +1,101 @@
|
|||
perf-annotate(1)
|
||||
================
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-annotate - Read perf.data (created by perf record) and display annotated code
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf annotate' [-i <file> | --input=file] [symbol_name]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command reads the input file and displays an annotated version of the
|
||||
code. If the object file has debug symbols then the source code will be
|
||||
displayed alongside assembly code.
|
||||
|
||||
If there is no debug info in the object, then annotated assembly is displayed.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-i::
|
||||
--input=::
|
||||
Input file name. (default: perf.data unless stdin is a fifo)
|
||||
|
||||
-d::
|
||||
--dsos=<dso[,dso...]>::
|
||||
Only consider symbols in these dsos.
|
||||
-s::
|
||||
--symbol=<symbol>::
|
||||
Symbol to annotate.
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Don't complain, do it.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose. (Show symbol address, etc)
|
||||
|
||||
-D::
|
||||
--dump-raw-trace::
|
||||
Dump raw trace in ASCII.
|
||||
|
||||
-k::
|
||||
--vmlinux=<file>::
|
||||
vmlinux pathname.
|
||||
|
||||
-m::
|
||||
--modules::
|
||||
Load module symbols. WARNING: use only with -k and LIVE kernel.
|
||||
|
||||
-l::
|
||||
--print-line::
|
||||
Print matching source lines (may be slow).
|
||||
|
||||
-P::
|
||||
--full-paths::
|
||||
Don't shorten the displayed pathnames.
|
||||
|
||||
--stdio:: Use the stdio interface.
|
||||
|
||||
--tui:: Use the TUI interface. Use of --tui requires a tty, if one is not
|
||||
present, as when piping to other commands, the stdio interface is
|
||||
used. This interfaces starts by centering on the line with more
|
||||
samples, TAB/UNTAB cycles through the lines with more samples.
|
||||
|
||||
--gtk:: Use the GTK interface.
|
||||
|
||||
-C::
|
||||
--cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
|
||||
be provided as a comma-separated list with no space: 0,1. Ranges of
|
||||
CPUs are specified with -: 0-2. Default is to report samples on all
|
||||
CPUs.
|
||||
|
||||
--asm-raw::
|
||||
Show raw instruction encoding of assembly instructions.
|
||||
|
||||
--source::
|
||||
Interleave source code with assembly code. Enabled by default,
|
||||
disable with --no-source.
|
||||
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
|
||||
-M::
|
||||
--disassembler-style=:: Set disassembler style for objdump.
|
||||
|
||||
--objdump=<path>::
|
||||
Path to objdump binary.
|
||||
|
||||
--skip-missing::
|
||||
Skip symbols that cannot be annotated.
|
||||
|
||||
--group::
|
||||
Show event group information together
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1]
|
22
tools/perf/Documentation/perf-archive.txt
Normal file
22
tools/perf/Documentation/perf-archive.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
perf-archive(1)
|
||||
===============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-archive - Create archive with object files with build-ids found in perf.data file
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf archive' [file]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command runs perf-buildid-list --with-hits, and collects the files with the
|
||||
buildids found so that analysis of perf.data contents can be possible on another
|
||||
machine.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-buildid-list[1], linkperf:perf-report[1]
|
218
tools/perf/Documentation/perf-bench.txt
Normal file
218
tools/perf/Documentation/perf-bench.txt
Normal file
|
@ -0,0 +1,218 @@
|
|||
perf-bench(1)
|
||||
=============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-bench - General framework for benchmark suites
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf bench' [<common options>] <subsystem> <suite> [<options>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This 'perf bench' command is a general framework for benchmark suites.
|
||||
|
||||
COMMON OPTIONS
|
||||
--------------
|
||||
-r::
|
||||
--repeat=::
|
||||
Specify amount of times to repeat the run (default 10).
|
||||
|
||||
-f::
|
||||
--format=::
|
||||
Specify format style.
|
||||
Current available format styles are:
|
||||
|
||||
'default'::
|
||||
Default style. This is mainly for human reading.
|
||||
---------------------
|
||||
% perf bench sched pipe # with no style specified
|
||||
(executing 1000000 pipe operations between two tasks)
|
||||
Total time:5.855 sec
|
||||
5.855061 usecs/op
|
||||
170792 ops/sec
|
||||
---------------------
|
||||
|
||||
'simple'::
|
||||
This simple style is friendly for automated
|
||||
processing by scripts.
|
||||
---------------------
|
||||
% perf bench --format=simple sched pipe # specified simple
|
||||
5.988
|
||||
---------------------
|
||||
|
||||
SUBSYSTEM
|
||||
---------
|
||||
|
||||
'sched'::
|
||||
Scheduler and IPC mechanisms.
|
||||
|
||||
'mem'::
|
||||
Memory access performance.
|
||||
|
||||
'numa'::
|
||||
NUMA scheduling and MM benchmarks.
|
||||
|
||||
'futex'::
|
||||
Futex stressing benchmarks.
|
||||
|
||||
'all'::
|
||||
All benchmark subsystems.
|
||||
|
||||
SUITES FOR 'sched'
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
*messaging*::
|
||||
Suite for evaluating performance of scheduler and IPC mechanisms.
|
||||
Based on hackbench by Rusty Russell.
|
||||
|
||||
Options of *messaging*
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
-p::
|
||||
--pipe::
|
||||
Use pipe() instead of socketpair()
|
||||
|
||||
-t::
|
||||
--thread::
|
||||
Be multi thread instead of multi process
|
||||
|
||||
-g::
|
||||
--group=::
|
||||
Specify number of groups
|
||||
|
||||
-l::
|
||||
--loop=::
|
||||
Specify number of loops
|
||||
|
||||
Example of *messaging*
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
---------------------
|
||||
% perf bench sched messaging # run with default
|
||||
options (20 sender and receiver processes per group)
|
||||
(10 groups == 400 processes run)
|
||||
|
||||
Total time:0.308 sec
|
||||
|
||||
% perf bench sched messaging -t -g 20 # be multi-thread, with 20 groups
|
||||
(20 sender and receiver threads per group)
|
||||
(20 groups == 800 threads run)
|
||||
|
||||
Total time:0.582 sec
|
||||
---------------------
|
||||
|
||||
*pipe*::
|
||||
Suite for pipe() system call.
|
||||
Based on pipe-test-1m.c by Ingo Molnar.
|
||||
|
||||
Options of *pipe*
|
||||
^^^^^^^^^^^^^^^^^
|
||||
-l::
|
||||
--loop=::
|
||||
Specify number of loops.
|
||||
|
||||
Example of *pipe*
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
---------------------
|
||||
% perf bench sched pipe
|
||||
(executing 1000000 pipe operations between two tasks)
|
||||
|
||||
Total time:8.091 sec
|
||||
8.091833 usecs/op
|
||||
123581 ops/sec
|
||||
|
||||
% perf bench sched pipe -l 1000 # loop 1000
|
||||
(executing 1000 pipe operations between two tasks)
|
||||
|
||||
Total time:0.016 sec
|
||||
16.948000 usecs/op
|
||||
59004 ops/sec
|
||||
---------------------
|
||||
|
||||
SUITES FOR 'mem'
|
||||
~~~~~~~~~~~~~~~~
|
||||
*memcpy*::
|
||||
Suite for evaluating performance of simple memory copy in various ways.
|
||||
|
||||
Options of *memcpy*
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
-l::
|
||||
--length::
|
||||
Specify length of memory to copy (default: 1MB).
|
||||
Available units are B, KB, MB, GB and TB (case insensitive).
|
||||
|
||||
-r::
|
||||
--routine::
|
||||
Specify routine to copy (default: default).
|
||||
Available routines are depend on the architecture.
|
||||
On x86-64, x86-64-unrolled, x86-64-movsq and x86-64-movsb are supported.
|
||||
|
||||
-i::
|
||||
--iterations::
|
||||
Repeat memcpy invocation this number of times.
|
||||
|
||||
-c::
|
||||
--cycle::
|
||||
Use perf's cpu-cycles event instead of gettimeofday syscall.
|
||||
|
||||
-o::
|
||||
--only-prefault::
|
||||
Show only the result with page faults before memcpy.
|
||||
|
||||
-n::
|
||||
--no-prefault::
|
||||
Show only the result without page faults before memcpy.
|
||||
|
||||
*memset*::
|
||||
Suite for evaluating performance of simple memory set in various ways.
|
||||
|
||||
Options of *memset*
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
-l::
|
||||
--length::
|
||||
Specify length of memory to set (default: 1MB).
|
||||
Available units are B, KB, MB, GB and TB (case insensitive).
|
||||
|
||||
-r::
|
||||
--routine::
|
||||
Specify routine to set (default: default).
|
||||
Available routines are depend on the architecture.
|
||||
On x86-64, x86-64-unrolled, x86-64-stosq and x86-64-stosb are supported.
|
||||
|
||||
-i::
|
||||
--iterations::
|
||||
Repeat memset invocation this number of times.
|
||||
|
||||
-c::
|
||||
--cycle::
|
||||
Use perf's cpu-cycles event instead of gettimeofday syscall.
|
||||
|
||||
-o::
|
||||
--only-prefault::
|
||||
Show only the result with page faults before memset.
|
||||
|
||||
-n::
|
||||
--no-prefault::
|
||||
Show only the result without page faults before memset.
|
||||
|
||||
SUITES FOR 'numa'
|
||||
~~~~~~~~~~~~~~~~~
|
||||
*mem*::
|
||||
Suite for evaluating NUMA workloads.
|
||||
|
||||
SUITES FOR 'futex'
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
*hash*::
|
||||
Suite for evaluating hash tables.
|
||||
|
||||
*wake*::
|
||||
Suite for evaluating wake calls.
|
||||
|
||||
*requeue*::
|
||||
Suite for evaluating requeue calls.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf[1]
|
53
tools/perf/Documentation/perf-buildid-cache.txt
Normal file
53
tools/perf/Documentation/perf-buildid-cache.txt
Normal file
|
@ -0,0 +1,53 @@
|
|||
perf-buildid-cache(1)
|
||||
=====================
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-buildid-cache - Manage build-id cache.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf buildid-cache <options>'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command manages the build-id cache. It can add and remove files to/from
|
||||
the cache. In the future it should as well purge older entries, set upper
|
||||
limits for the space used by the cache, etc.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-a::
|
||||
--add=::
|
||||
Add specified file to the cache.
|
||||
-k::
|
||||
--kcore::
|
||||
Add specified kcore file to the cache. For the current host that is
|
||||
/proc/kcore which requires root permissions to read. Be aware that
|
||||
running 'perf buildid-cache' as root may update root's build-id cache
|
||||
not the user's. Use the -v option to see where the file is created.
|
||||
Note that the copied file contains only code sections not the whole core
|
||||
image. Note also that files "kallsyms" and "modules" must also be in the
|
||||
same directory and are also copied. All 3 files are created with read
|
||||
permissions for root only. kcore will not be added if there is already a
|
||||
kcore in the cache (with the same build-id) that has the same modules at
|
||||
the same addresses. Use the -v option to see if a copy of kcore is
|
||||
actually made.
|
||||
-r::
|
||||
--remove=::
|
||||
Remove specified file from the cache.
|
||||
-M::
|
||||
--missing=::
|
||||
List missing build ids in the cache for the specified file.
|
||||
-u::
|
||||
--update::
|
||||
Update specified file of the cache. It can be used to update kallsyms
|
||||
kernel dso to vmlinux in order to support annotation.
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-buildid-list[1]
|
43
tools/perf/Documentation/perf-buildid-list.txt
Normal file
43
tools/perf/Documentation/perf-buildid-list.txt
Normal file
|
@ -0,0 +1,43 @@
|
|||
perf-buildid-list(1)
|
||||
====================
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-buildid-list - List the buildids in a perf.data file
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf buildid-list <options>'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command displays the buildids found in a perf.data file, so that other
|
||||
tools can be used to fetch packages with matching symbol tables for use by
|
||||
perf report.
|
||||
|
||||
It can also be used to show the build id of the running kernel or in an ELF
|
||||
file using -i/--input.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-H::
|
||||
--with-hits::
|
||||
Show only DSOs with hits.
|
||||
-i::
|
||||
--input=::
|
||||
Input file name. (default: perf.data unless stdin is a fifo)
|
||||
-f::
|
||||
--force::
|
||||
Don't do ownership validation.
|
||||
-k::
|
||||
--kernel::
|
||||
Show running kernel build id.
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-top[1],
|
||||
linkperf:perf-report[1]
|
206
tools/perf/Documentation/perf-diff.txt
Normal file
206
tools/perf/Documentation/perf-diff.txt
Normal file
|
@ -0,0 +1,206 @@
|
|||
perf-diff(1)
|
||||
============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-diff - Read perf.data files and display the differential profile
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf diff' [baseline file] [data file1] [[data file2] ... ]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command displays the performance difference amongst two or more perf.data
|
||||
files captured via perf record.
|
||||
|
||||
If no parameters are passed it will assume perf.data.old and perf.data.
|
||||
|
||||
The differential profile is displayed only for events matching both
|
||||
specified perf.data files.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-D::
|
||||
--dump-raw-trace::
|
||||
Dump raw trace in ASCII.
|
||||
|
||||
-m::
|
||||
--modules::
|
||||
Load module symbols. WARNING: use only with -k and LIVE kernel
|
||||
|
||||
-d::
|
||||
--dsos=::
|
||||
Only consider symbols in these dsos. CSV that understands
|
||||
file://filename entries. This option will affect the percentage
|
||||
of the Baseline/Delta column. See --percentage for more info.
|
||||
|
||||
-C::
|
||||
--comms=::
|
||||
Only consider symbols in these comms. CSV that understands
|
||||
file://filename entries. This option will affect the percentage
|
||||
of the Baseline/Delta column. See --percentage for more info.
|
||||
|
||||
-S::
|
||||
--symbols=::
|
||||
Only consider these symbols. CSV that understands
|
||||
file://filename entries. This option will affect the percentage
|
||||
of the Baseline/Delta column. See --percentage for more info.
|
||||
|
||||
-s::
|
||||
--sort=::
|
||||
Sort by key(s): pid, comm, dso, symbol, cpu, parent, srcline.
|
||||
Please see description of --sort in the perf-report man page.
|
||||
|
||||
-t::
|
||||
--field-separator=::
|
||||
|
||||
Use a special separator character and don't pad with spaces, replacing
|
||||
all occurrences of this separator in symbol names (and other output)
|
||||
with a '.' character, that thus it's the only non valid separator.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be verbose, for instance, show the raw counts in addition to the
|
||||
diff.
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Don't complain, do it.
|
||||
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
|
||||
-b::
|
||||
--baseline-only::
|
||||
Show only items with match in baseline.
|
||||
|
||||
-c::
|
||||
--compute::
|
||||
Differential computation selection - delta,ratio,wdiff (default is delta).
|
||||
See COMPARISON METHODS section for more info.
|
||||
|
||||
-p::
|
||||
--period::
|
||||
Show period values for both compared hist entries.
|
||||
|
||||
-F::
|
||||
--formula::
|
||||
Show formula for given computation.
|
||||
|
||||
-o::
|
||||
--order::
|
||||
Specify compute sorting column number.
|
||||
|
||||
--percentage::
|
||||
Determine how to display the overhead percentage of filtered entries.
|
||||
Filters can be applied by --comms, --dsos and/or --symbols options.
|
||||
|
||||
"relative" means it's relative to filtered entries only so that the
|
||||
sum of shown entries will be always 100%. "absolute" means it retains
|
||||
the original value before and after the filter is applied.
|
||||
|
||||
COMPARISON
|
||||
----------
|
||||
The comparison is governed by the baseline file. The baseline perf.data
|
||||
file is iterated for samples. All other perf.data files specified on
|
||||
the command line are searched for the baseline sample pair. If the pair
|
||||
is found, specified computation is made and result is displayed.
|
||||
|
||||
All samples from non-baseline perf.data files, that do not match any
|
||||
baseline entry, are displayed with empty space within baseline column
|
||||
and possible computation results (delta) in their related column.
|
||||
|
||||
Example files samples:
|
||||
- file A with samples f1, f2, f3, f4, f6
|
||||
- file B with samples f2, f4, f5
|
||||
- file C with samples f1, f2, f5
|
||||
|
||||
Example output:
|
||||
x - computation takes place for pair
|
||||
b - baseline sample percentage
|
||||
|
||||
- perf diff A B C
|
||||
|
||||
baseline/A compute/B compute/C samples
|
||||
---------------------------------------
|
||||
b x f1
|
||||
b x x f2
|
||||
b f3
|
||||
b x f4
|
||||
b f6
|
||||
x x f5
|
||||
|
||||
- perf diff B A C
|
||||
|
||||
baseline/B compute/A compute/C samples
|
||||
---------------------------------------
|
||||
b x x f2
|
||||
b x f4
|
||||
b x f5
|
||||
x x f1
|
||||
x f3
|
||||
x f6
|
||||
|
||||
- perf diff C B A
|
||||
|
||||
baseline/C compute/B compute/A samples
|
||||
---------------------------------------
|
||||
b x f1
|
||||
b x x f2
|
||||
b x f5
|
||||
x f3
|
||||
x x f4
|
||||
x f6
|
||||
|
||||
COMPARISON METHODS
|
||||
------------------
|
||||
delta
|
||||
~~~~~
|
||||
If specified the 'Delta' column is displayed with value 'd' computed as:
|
||||
|
||||
d = A->period_percent - B->period_percent
|
||||
|
||||
with:
|
||||
- A/B being matching hist entry from data/baseline file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period_percent being the % of the hist entry period value within
|
||||
single data file
|
||||
|
||||
- with filtering by -C, -d and/or -S, period_percent might be changed
|
||||
relative to how entries are filtered. Use --percentage=absolute to
|
||||
prevent such fluctuation.
|
||||
|
||||
ratio
|
||||
~~~~~
|
||||
If specified the 'Ratio' column is displayed with value 'r' computed as:
|
||||
|
||||
r = A->period / B->period
|
||||
|
||||
with:
|
||||
- A/B being matching hist entry from data/baseline file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period being the hist entry period value
|
||||
|
||||
wdiff:WEIGHT-B,WEIGHT-A
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
If specified the 'Weighted diff' column is displayed with value 'd' computed as:
|
||||
|
||||
d = B->period * WEIGHT-A - A->period * WEIGHT-B
|
||||
|
||||
- A/B being matching hist entry from data/baseline file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period being the hist entry period value
|
||||
|
||||
- WEIGHT-A/WEIGHT-B being user supplied weights in the the '-c' option
|
||||
behind ':' separator like '-c wdiff:1,2'.
|
||||
- WEIGHT-A being the weight of the data file
|
||||
- WEIGHT-B being the weight of the baseline data file
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1]
|
38
tools/perf/Documentation/perf-evlist.txt
Normal file
38
tools/perf/Documentation/perf-evlist.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
perf-evlist(1)
|
||||
==============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-evlist - List the event names in a perf.data file
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf evlist <options>'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command displays the names of events sampled in a perf.data file.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-i::
|
||||
--input=::
|
||||
Input file name. (default: perf.data unless stdin is a fifo)
|
||||
|
||||
-F::
|
||||
--freq=::
|
||||
Show just the sample frequency used for each event.
|
||||
|
||||
-v::
|
||||
--verbose=::
|
||||
Show all fields.
|
||||
|
||||
-g::
|
||||
--group::
|
||||
Show event group information.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-list[1],
|
||||
linkperf:perf-report[1]
|
38
tools/perf/Documentation/perf-help.txt
Normal file
38
tools/perf/Documentation/perf-help.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
perf-help(1)
|
||||
============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-help - display help information about perf
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'perf help' [-a|--all] [COMMAND]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
With no options and no COMMAND given, the synopsis of the 'perf'
|
||||
command and a list of the most commonly used perf commands are printed
|
||||
on the standard output.
|
||||
|
||||
If the option '--all' or '-a' is given, then all available commands are
|
||||
printed on the standard output.
|
||||
|
||||
If a perf command is named, a manual page for that command is brought
|
||||
up. The 'man' program is used by default for this purpose, but this
|
||||
can be overridden by other options or configuration variables.
|
||||
|
||||
Note that `perf --help ...` is identical to `perf help ...` because the
|
||||
former is internally converted into the latter.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-a::
|
||||
--all::
|
||||
Prints all the available commands on the standard output. This
|
||||
option supersedes any other option.
|
||||
|
||||
PERF
|
||||
----
|
||||
Part of the linkperf:perf[1] suite
|
49
tools/perf/Documentation/perf-inject.txt
Normal file
49
tools/perf/Documentation/perf-inject.txt
Normal file
|
@ -0,0 +1,49 @@
|
|||
perf-inject(1)
|
||||
==============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-inject - Filter to augment the events stream with additional information
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf inject <options>'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
perf-inject reads a perf-record event stream and repipes it to stdout. At any
|
||||
point the processing code can inject other events into the event stream - in
|
||||
this case build-ids (-b option) are read and injected as needed into the event
|
||||
stream.
|
||||
|
||||
Build-ids are just the first user of perf-inject - potentially anything that
|
||||
needs userspace processing to augment the events stream with additional
|
||||
information could make use of this facility.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-b::
|
||||
--build-ids=::
|
||||
Inject build-ids into the output stream
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose.
|
||||
-i::
|
||||
--input=::
|
||||
Input file name. (default: stdin)
|
||||
-o::
|
||||
--output=::
|
||||
Output file name. (default: stdout)
|
||||
-s::
|
||||
--sched-stat::
|
||||
Merge sched_stat and sched_switch for getting events where and how long
|
||||
tasks slept. sched_switch contains a callchain where a task slept and
|
||||
sched_stat contains a timeslice how long a task slept.
|
||||
|
||||
--kallsyms=<file>::
|
||||
kallsyms pathname
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1]
|
47
tools/perf/Documentation/perf-kmem.txt
Normal file
47
tools/perf/Documentation/perf-kmem.txt
Normal file
|
@ -0,0 +1,47 @@
|
|||
perf-kmem(1)
|
||||
============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-kmem - Tool to trace/measure kernel memory(slab) properties
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf kmem' {record|stat} [<options>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
There are two variants of perf kmem:
|
||||
|
||||
'perf kmem record <command>' to record the kmem events
|
||||
of an arbitrary workload.
|
||||
|
||||
'perf kmem stat' to report kernel memory statistics.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-i <file>::
|
||||
--input=<file>::
|
||||
Select the input file (default: perf.data unless stdin is a fifo)
|
||||
|
||||
--caller::
|
||||
Show per-callsite statistics
|
||||
|
||||
--alloc::
|
||||
Show per-allocation statistics
|
||||
|
||||
-s <key[,key2...]>::
|
||||
--sort=<key[,key2...]>::
|
||||
Sort the output (default: frag,hit,bytes)
|
||||
|
||||
-l <num>::
|
||||
--line=<num>::
|
||||
Print n lines only
|
||||
|
||||
--raw-ip::
|
||||
Print raw ip instead of symbol
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1]
|
158
tools/perf/Documentation/perf-kvm.txt
Normal file
158
tools/perf/Documentation/perf-kvm.txt
Normal file
|
@ -0,0 +1,158 @@
|
|||
perf-kvm(1)
|
||||
===========
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-kvm - Tool to trace/measure kvm guest os
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf kvm' [--host] [--guest] [--guestmount=<path>
|
||||
[--guestkallsyms=<path> --guestmodules=<path> | --guestvmlinux=<path>]]
|
||||
{top|record|report|diff|buildid-list} [<options>]
|
||||
'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path>
|
||||
| --guestvmlinux=<path>] {top|record|report|diff|buildid-list|stat} [<options>]
|
||||
'perf kvm stat [record|report|live] [<options>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
There are a couple of variants of perf kvm:
|
||||
|
||||
'perf kvm [options] top <command>' to generates and displays
|
||||
a performance counter profile of guest os in realtime
|
||||
of an arbitrary workload.
|
||||
|
||||
'perf kvm record <command>' to record the performance counter profile
|
||||
of an arbitrary workload and save it into a perf data file. We set the
|
||||
default behavior of perf kvm as --guest, so if neither --host nor --guest
|
||||
is input, the perf data file name is perf.data.guest. If --host is input,
|
||||
the perf data file name is perf.data.kvm. If you want to record data into
|
||||
perf.data.host, please input --host --no-guest. The behaviors are shown as
|
||||
following:
|
||||
Default('') -> perf.data.guest
|
||||
--host -> perf.data.kvm
|
||||
--guest -> perf.data.guest
|
||||
--host --guest -> perf.data.kvm
|
||||
--host --no-guest -> perf.data.host
|
||||
|
||||
'perf kvm report' to display the performance counter profile information
|
||||
recorded via perf kvm record.
|
||||
|
||||
'perf kvm diff' to displays the performance difference amongst two perf.data
|
||||
files captured via perf record.
|
||||
|
||||
'perf kvm buildid-list' to display the buildids found in a perf data file,
|
||||
so that other tools can be used to fetch packages with matching symbol tables
|
||||
for use by perf report. As buildid is read from /sys/kernel/notes in os, then
|
||||
if you want to list the buildid for guest, please make sure your perf data file
|
||||
was captured with --guestmount in perf kvm record.
|
||||
|
||||
'perf kvm stat <command>' to run a command and gather performance counter
|
||||
statistics.
|
||||
Especially, perf 'kvm stat record/report' generates a statistical analysis
|
||||
of KVM events. Currently, vmexit, mmio (x86 only) and ioport (x86 only)
|
||||
events are supported. 'perf kvm stat record <command>' records kvm events
|
||||
and the events between start and end <command>.
|
||||
And this command produces a file which contains tracing results of kvm
|
||||
events.
|
||||
|
||||
'perf kvm stat report' reports statistical data which includes events
|
||||
handled time, samples, and so on.
|
||||
|
||||
'perf kvm stat live' reports statistical data in a live mode (similar to
|
||||
record + report but with statistical data updated live at a given display
|
||||
rate).
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-i::
|
||||
--input=<path>::
|
||||
Input file name.
|
||||
-o::
|
||||
--output=<path>::
|
||||
Output file name.
|
||||
--host::
|
||||
Collect host side performance profile.
|
||||
--guest::
|
||||
Collect guest side performance profile.
|
||||
--guestmount=<path>::
|
||||
Guest os root file system mount directory. Users mounts guest os
|
||||
root directories under <path> by a specific filesystem access method,
|
||||
typically, sshfs. For example, start 2 guest os. The one's pid is 8888
|
||||
and the other's is 9999.
|
||||
#mkdir ~/guestmount; cd ~/guestmount
|
||||
#sshfs -o allow_other,direct_io -p 5551 localhost:/ 8888/
|
||||
#sshfs -o allow_other,direct_io -p 5552 localhost:/ 9999/
|
||||
#perf kvm --host --guest --guestmount=~/guestmount top
|
||||
--guestkallsyms=<path>::
|
||||
Guest os /proc/kallsyms file copy. 'perf' kvm' reads it to get guest
|
||||
kernel symbols. Users copy it out from guest os.
|
||||
--guestmodules=<path>::
|
||||
Guest os /proc/modules file copy. 'perf' kvm' reads it to get guest
|
||||
kernel module information. Users copy it out from guest os.
|
||||
--guestvmlinux=<path>::
|
||||
Guest os kernel vmlinux.
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose (show counter open errors, etc).
|
||||
|
||||
STAT REPORT OPTIONS
|
||||
-------------------
|
||||
--vcpu=<value>::
|
||||
analyze events which occur on this vcpu. (default: all vcpus)
|
||||
|
||||
--event=<value>::
|
||||
event to be analyzed. Possible values: vmexit, mmio (x86 only),
|
||||
ioport (x86 only). (default: vmexit)
|
||||
-k::
|
||||
--key=<value>::
|
||||
Sorting key. Possible values: sample (default, sort by samples
|
||||
number), time (sort by average time).
|
||||
-p::
|
||||
--pid=::
|
||||
Analyze events only for given process ID(s) (comma separated list).
|
||||
|
||||
STAT LIVE OPTIONS
|
||||
-----------------
|
||||
-d::
|
||||
--display::
|
||||
Time in seconds between display updates
|
||||
|
||||
-m::
|
||||
--mmap-pages=::
|
||||
Number of mmap data pages (must be a power of two) or size
|
||||
specification with appended unit character - B/K/M/G. The
|
||||
size is rounded up to have nearest pages power of two value.
|
||||
|
||||
-a::
|
||||
--all-cpus::
|
||||
System-wide collection from all CPUs.
|
||||
|
||||
-p::
|
||||
--pid=::
|
||||
Analyze events only for given process ID(s) (comma separated list).
|
||||
|
||||
--vcpu=<value>::
|
||||
analyze events which occur on this vcpu. (default: all vcpus)
|
||||
|
||||
|
||||
--event=<value>::
|
||||
event to be analyzed. Possible values: vmexit,
|
||||
mmio (x86 only), ioport (x86 only).
|
||||
(default: vmexit)
|
||||
|
||||
-k::
|
||||
--key=<value>::
|
||||
Sorting key. Possible values: sample (default, sort by samples
|
||||
number), time (sort by average time).
|
||||
|
||||
--duration=<value>::
|
||||
Show events other than HLT (x86 only) or Wait state (s390 only)
|
||||
that take longer than duration usecs.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-top[1], linkperf:perf-record[1], linkperf:perf-report[1],
|
||||
linkperf:perf-diff[1], linkperf:perf-buildid-list[1],
|
||||
linkperf:perf-stat[1]
|
122
tools/perf/Documentation/perf-list.txt
Normal file
122
tools/perf/Documentation/perf-list.txt
Normal file
|
@ -0,0 +1,122 @@
|
|||
perf-list(1)
|
||||
============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-list - List all symbolic event types
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf list' [hw|sw|cache|tracepoint|pmu|event_glob]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command displays the symbolic event types which can be selected in the
|
||||
various perf commands with the -e option.
|
||||
|
||||
[[EVENT_MODIFIERS]]
|
||||
EVENT MODIFIERS
|
||||
---------------
|
||||
|
||||
Events can optionally have a modifier by appending a colon and one or
|
||||
more modifiers. Modifiers allow the user to restrict the events to be
|
||||
counted. The following modifiers exist:
|
||||
|
||||
u - user-space counting
|
||||
k - kernel counting
|
||||
h - hypervisor counting
|
||||
G - guest counting (in KVM guests)
|
||||
H - host counting (not in KVM guests)
|
||||
p - precise level
|
||||
S - read sample value (PERF_SAMPLE_READ)
|
||||
D - pin the event to the PMU
|
||||
|
||||
The 'p' modifier can be used for specifying how precise the instruction
|
||||
address should be. The 'p' modifier can be specified multiple times:
|
||||
|
||||
0 - SAMPLE_IP can have arbitrary skid
|
||||
1 - SAMPLE_IP must have constant skid
|
||||
2 - SAMPLE_IP requested to have 0 skid
|
||||
3 - SAMPLE_IP must have 0 skid
|
||||
|
||||
For Intel systems precise event sampling is implemented with PEBS
|
||||
which supports up to precise-level 2.
|
||||
|
||||
On AMD systems it is implemented using IBS (up to precise-level 2).
|
||||
The precise modifier works with event types 0x76 (cpu-cycles, CPU
|
||||
clocks not halted) and 0xC1 (micro-ops retired). Both events map to
|
||||
IBS execution sampling (IBS op) with the IBS Op Counter Control bit
|
||||
(IbsOpCntCtl) set respectively (see AMD64 Architecture Programmer’s
|
||||
Manual Volume 2: System Programming, 13.3 Instruction-Based
|
||||
Sampling). Examples to use IBS:
|
||||
|
||||
perf record -a -e cpu-cycles:p ... # use ibs op counting cycles
|
||||
perf record -a -e r076:p ... # same as -e cpu-cycles:p
|
||||
perf record -a -e r0C1:p ... # use ibs op counting micro-ops
|
||||
|
||||
RAW HARDWARE EVENT DESCRIPTOR
|
||||
-----------------------------
|
||||
Even when an event is not available in a symbolic form within perf right now,
|
||||
it can be encoded in a per processor specific way.
|
||||
|
||||
For instance For x86 CPUs NNN represents the raw register encoding with the
|
||||
layout of IA32_PERFEVTSELx MSRs (see [Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide] Figure 30-1 Layout
|
||||
of IA32_PERFEVTSELx MSRs) or AMD's PerfEvtSeln (see [AMD64 Architecture Programmer’s Manual Volume 2: System Programming], Page 344,
|
||||
Figure 13-7 Performance Event-Select Register (PerfEvtSeln)).
|
||||
|
||||
Note: Only the following bit fields can be set in x86 counter
|
||||
registers: event, umask, edge, inv, cmask. Esp. guest/host only and
|
||||
OS/user mode flags must be setup using <<EVENT_MODIFIERS, EVENT
|
||||
MODIFIERS>>.
|
||||
|
||||
Example:
|
||||
|
||||
If the Intel docs for a QM720 Core i7 describe an event as:
|
||||
|
||||
Event Umask Event Mask
|
||||
Num. Value Mnemonic Description Comment
|
||||
|
||||
A8H 01H LSD.UOPS Counts the number of micro-ops Use cmask=1 and
|
||||
delivered by loop stream detector invert to count
|
||||
cycles
|
||||
|
||||
raw encoding of 0x1A8 can be used:
|
||||
|
||||
perf stat -e r1a8 -a sleep 1
|
||||
perf record -e r1a8 ...
|
||||
|
||||
You should refer to the processor specific documentation for getting these
|
||||
details. Some of them are referenced in the SEE ALSO section below.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
Without options all known events will be listed.
|
||||
|
||||
To limit the list use:
|
||||
|
||||
. 'hw' or 'hardware' to list hardware events such as cache-misses, etc.
|
||||
|
||||
. 'sw' or 'software' to list software events such as context switches, etc.
|
||||
|
||||
. 'cache' or 'hwcache' to list hardware cache events such as L1-dcache-loads, etc.
|
||||
|
||||
. 'tracepoint' to list all tracepoint events, alternatively use
|
||||
'subsys_glob:event_glob' to filter by tracepoint subsystems such as sched,
|
||||
block, etc.
|
||||
|
||||
. 'pmu' to print the kernel supplied PMU events.
|
||||
|
||||
. If none of the above is matched, it will apply the supplied glob to all
|
||||
events, printing the ones that match.
|
||||
|
||||
One or more types can be used at the same time, listing the events for the
|
||||
types specified.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-top[1],
|
||||
linkperf:perf-record[1],
|
||||
http://www.intel.com/Assets/PDF/manual/253669.pdf[Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide],
|
||||
http://support.amd.com/us/Processor_TechDocs/24593_APM_v2.pdf[AMD64 Architecture Programmer’s Manual Volume 2: System Programming]
|
66
tools/perf/Documentation/perf-lock.txt
Normal file
66
tools/perf/Documentation/perf-lock.txt
Normal file
|
@ -0,0 +1,66 @@
|
|||
perf-lock(1)
|
||||
============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-lock - Analyze lock events
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf lock' {record|report|script|info}
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
You can analyze various lock behaviours
|
||||
and statistics with this 'perf lock' command.
|
||||
|
||||
'perf lock record <command>' records lock events
|
||||
between start and end <command>. And this command
|
||||
produces the file "perf.data" which contains tracing
|
||||
results of lock events.
|
||||
|
||||
'perf lock report' reports statistical data.
|
||||
|
||||
'perf lock script' shows raw lock events.
|
||||
|
||||
'perf lock info' shows metadata like threads or addresses
|
||||
of lock instances.
|
||||
|
||||
COMMON OPTIONS
|
||||
--------------
|
||||
|
||||
-i::
|
||||
--input=<file>::
|
||||
Input file name. (default: perf.data unless stdin is a fifo)
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose (show symbol address, etc).
|
||||
|
||||
-D::
|
||||
--dump-raw-trace::
|
||||
Dump raw trace in ASCII.
|
||||
|
||||
REPORT OPTIONS
|
||||
--------------
|
||||
|
||||
-k::
|
||||
--key=<value>::
|
||||
Sorting key. Possible values: acquired (default), contended,
|
||||
avg_wait, wait_total, wait_max, wait_min.
|
||||
|
||||
INFO OPTIONS
|
||||
------------
|
||||
|
||||
-t::
|
||||
--threads::
|
||||
dump thread list in perf.data
|
||||
|
||||
-m::
|
||||
--map::
|
||||
dump map of lock instances (address:name table)
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf[1]
|
52
tools/perf/Documentation/perf-mem.txt
Normal file
52
tools/perf/Documentation/perf-mem.txt
Normal file
|
@ -0,0 +1,52 @@
|
|||
perf-mem(1)
|
||||
===========
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-mem - Profile memory accesses
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf mem' [<options>] (record [<command>] | report)
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
"perf mem -t <TYPE> record" runs a command and gathers memory operation data
|
||||
from it, into perf.data. Perf record options are accepted and are passed through.
|
||||
|
||||
"perf mem -t <TYPE> report" displays the result. It invokes perf report with the
|
||||
right set of options to display a memory access profile.
|
||||
|
||||
Note that on Intel systems the memory latency reported is the use-latency,
|
||||
not the pure load (or store latency). Use latency includes any pipeline
|
||||
queueing delays in addition to the memory subsystem latency.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
<command>...::
|
||||
Any command you can specify in a shell.
|
||||
|
||||
-t::
|
||||
--type=::
|
||||
Select the memory operation type: load or store (default: load)
|
||||
|
||||
-D::
|
||||
--dump-raw-samples=::
|
||||
Dump the raw decoded samples on the screen in a format that is easy to parse with
|
||||
one sample per line.
|
||||
|
||||
-x::
|
||||
--field-separator::
|
||||
Specify the field separator used when dump raw samples (-D option). By default,
|
||||
The separator is the space character.
|
||||
|
||||
-C::
|
||||
--cpu-list::
|
||||
Restrict dump of raw samples to those provided via this option. Note that the same
|
||||
option can be passed in record mode. It will be interpreted the same way as perf
|
||||
record.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1]
|
210
tools/perf/Documentation/perf-probe.txt
Normal file
210
tools/perf/Documentation/perf-probe.txt
Normal file
|
@ -0,0 +1,210 @@
|
|||
perf-probe(1)
|
||||
=============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-probe - Define new dynamic tracepoints
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf probe' [options] --add='PROBE' [...]
|
||||
or
|
||||
'perf probe' [options] PROBE
|
||||
or
|
||||
'perf probe' [options] --del='[GROUP:]EVENT' [...]
|
||||
or
|
||||
'perf probe' --list
|
||||
or
|
||||
'perf probe' [options] --line='LINE'
|
||||
or
|
||||
'perf probe' [options] --vars='PROBEPOINT'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command defines dynamic tracepoint events, by symbol and registers
|
||||
without debuginfo, or by C expressions (C line numbers, C function names,
|
||||
and C local variables) with debuginfo.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-k::
|
||||
--vmlinux=PATH::
|
||||
Specify vmlinux path which has debuginfo (Dwarf binary).
|
||||
|
||||
-m::
|
||||
--module=MODNAME|PATH::
|
||||
Specify module name in which perf-probe searches probe points
|
||||
or lines. If a path of module file is passed, perf-probe
|
||||
treat it as an offline module (this means you can add a probe on
|
||||
a module which has not been loaded yet).
|
||||
|
||||
-s::
|
||||
--source=PATH::
|
||||
Specify path to kernel source.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose (show parsed arguments, etc).
|
||||
|
||||
-a::
|
||||
--add=::
|
||||
Define a probe event (see PROBE SYNTAX for detail).
|
||||
|
||||
-d::
|
||||
--del=::
|
||||
Delete probe events. This accepts glob wildcards('*', '?') and character
|
||||
classes(e.g. [a-z], [!A-Z]).
|
||||
|
||||
-l::
|
||||
--list::
|
||||
List up current probe events.
|
||||
|
||||
-L::
|
||||
--line=::
|
||||
Show source code lines which can be probed. This needs an argument
|
||||
which specifies a range of the source code. (see LINE SYNTAX for detail)
|
||||
|
||||
-V::
|
||||
--vars=::
|
||||
Show available local variables at given probe point. The argument
|
||||
syntax is same as PROBE SYNTAX, but NO ARGs.
|
||||
|
||||
--externs::
|
||||
(Only for --vars) Show external defined variables in addition to local
|
||||
variables.
|
||||
|
||||
-F::
|
||||
--funcs::
|
||||
Show available functions in given module or kernel. With -x/--exec,
|
||||
can also list functions in a user space executable / shared library.
|
||||
|
||||
--filter=FILTER::
|
||||
(Only for --vars and --funcs) Set filter. FILTER is a combination of glob
|
||||
pattern, see FILTER PATTERN for detail.
|
||||
Default FILTER is "!__k???tab_* & !__crc_*" for --vars, and "!_*"
|
||||
for --funcs.
|
||||
If several filters are specified, only the last filter is used.
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Forcibly add events with existing name.
|
||||
|
||||
-n::
|
||||
--dry-run::
|
||||
Dry run. With this option, --add and --del doesn't execute actual
|
||||
adding and removal operations.
|
||||
|
||||
--max-probes::
|
||||
Set the maximum number of probe points for an event. Default is 128.
|
||||
|
||||
-x::
|
||||
--exec=PATH::
|
||||
Specify path to the executable or shared library file for user
|
||||
space tracing. Can also be used with --funcs option.
|
||||
|
||||
--demangle-kernel::
|
||||
Demangle kernel symbols.
|
||||
|
||||
In absence of -m/-x options, perf probe checks if the first argument after
|
||||
the options is an absolute path name. If its an absolute path, perf probe
|
||||
uses it as a target module/target user space binary to probe.
|
||||
|
||||
PROBE SYNTAX
|
||||
------------
|
||||
Probe points are defined by following syntax.
|
||||
|
||||
1) Define event based on function name
|
||||
[EVENT=]FUNC[@SRC][:RLN|+OFFS|%return|;PTN] [ARG ...]
|
||||
|
||||
2) Define event based on source file with line number
|
||||
[EVENT=]SRC:ALN [ARG ...]
|
||||
|
||||
3) Define event based on source file with lazy pattern
|
||||
[EVENT=]SRC;PTN [ARG ...]
|
||||
|
||||
|
||||
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
|
||||
'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
|
||||
It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
|
||||
'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
|
||||
|
||||
PROBE ARGUMENT
|
||||
--------------
|
||||
Each probe argument follows below syntax.
|
||||
|
||||
[NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
|
||||
|
||||
'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), local array with fixed index (e.g. array[1], var->array[0], var->pointer[2]), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
|
||||
'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type.
|
||||
|
||||
On x86 systems %REG is always the short form of the register: for example %AX. %RAX or %EAX is not valid.
|
||||
|
||||
LINE SYNTAX
|
||||
-----------
|
||||
Line range is described by following syntax.
|
||||
|
||||
"FUNC[@SRC][:RLN[+NUM|-RLN2]]|SRC[:ALN[+NUM|-ALN2]]"
|
||||
|
||||
FUNC specifies the function name of showing lines. 'RLN' is the start line
|
||||
number from function entry line, and 'RLN2' is the end line number. As same as
|
||||
probe syntax, 'SRC' means the source file path, 'ALN' is start line number,
|
||||
and 'ALN2' is end line number in the file. It is also possible to specify how
|
||||
many lines to show by using 'NUM'. Moreover, 'FUNC@SRC' combination is good
|
||||
for searching a specific function when several functions share same name.
|
||||
So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function.
|
||||
|
||||
LAZY MATCHING
|
||||
-------------
|
||||
The lazy line matching is similar to glob matching but ignoring spaces in both of pattern and target. So this accepts wildcards('*', '?') and character classes(e.g. [a-z], [!A-Z]).
|
||||
|
||||
e.g.
|
||||
'a=*' can matches 'a=b', 'a = b', 'a == b' and so on.
|
||||
|
||||
This provides some sort of flexibility and robustness to probe point definitions against minor code changes. For example, actual 10th line of schedule() can be moved easily by modifying schedule(), but the same line matching 'rq=cpu_rq*' may still exist in the function.)
|
||||
|
||||
FILTER PATTERN
|
||||
--------------
|
||||
The filter pattern is a glob matching pattern(s) to filter variables.
|
||||
In addition, you can use "!" for specifying filter-out rule. You also can give several rules combined with "&" or "|", and fold those rules as one rule by using "(" ")".
|
||||
|
||||
e.g.
|
||||
With --filter "foo* | bar*", perf probe -V shows variables which start with "foo" or "bar".
|
||||
With --filter "!foo* & *bar", perf probe -V shows variables which don't start with "foo" and end with "bar", like "fizzbar". But "foobar" is filtered out.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
Display which lines in schedule() can be probed:
|
||||
|
||||
./perf probe --line schedule
|
||||
|
||||
Add a probe on schedule() function 12th line with recording cpu local variable:
|
||||
|
||||
./perf probe schedule:12 cpu
|
||||
or
|
||||
./perf probe --add='schedule:12 cpu'
|
||||
|
||||
this will add one or more probes which has the name start with "schedule".
|
||||
|
||||
Add probes on lines in schedule() function which calls update_rq_clock().
|
||||
|
||||
./perf probe 'schedule;update_rq_clock*'
|
||||
or
|
||||
./perf probe --add='schedule;update_rq_clock*'
|
||||
|
||||
Delete all probes on schedule().
|
||||
|
||||
./perf probe --del='schedule*'
|
||||
|
||||
Add probes at zfree() function on /bin/zsh
|
||||
|
||||
./perf probe -x /bin/zsh zfree or ./perf probe /bin/zsh zfree
|
||||
|
||||
Add probes at malloc() function on libc
|
||||
|
||||
./perf probe -x /lib/libc.so.6 malloc or ./perf probe /lib/libc.so.6 malloc
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-trace[1], linkperf:perf-record[1]
|
219
tools/perf/Documentation/perf-record.txt
Normal file
219
tools/perf/Documentation/perf-record.txt
Normal file
|
@ -0,0 +1,219 @@
|
|||
perf-record(1)
|
||||
==============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-record - Run a command and record its profile into perf.data
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf record' [-e <EVENT> | --event=EVENT] [-l] [-a] <command>
|
||||
'perf record' [-e <EVENT> | --event=EVENT] [-l] [-a] -- <command> [<options>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command runs a command and gathers a performance counter profile
|
||||
from it, into perf.data - without displaying anything.
|
||||
|
||||
This file can then be inspected later on, using 'perf report'.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
<command>...::
|
||||
Any command you can specify in a shell.
|
||||
|
||||
-e::
|
||||
--event=::
|
||||
Select the PMU event. Selection can be:
|
||||
|
||||
- a symbolic event name (use 'perf list' to list all events)
|
||||
|
||||
- a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a
|
||||
hexadecimal event descriptor.
|
||||
|
||||
- a hardware breakpoint event in the form of '\mem:addr[:access]'
|
||||
where addr is the address in memory you want to break in.
|
||||
Access is the memory access type (read, write, execute) it can
|
||||
be passed as follows: '\mem:addr[:[r][w][x]]'.
|
||||
If you want to profile read-write accesses in 0x1000, just set
|
||||
'mem:0x1000:rw'.
|
||||
|
||||
--filter=<filter>::
|
||||
Event filter.
|
||||
|
||||
-a::
|
||||
--all-cpus::
|
||||
System-wide collection from all CPUs.
|
||||
|
||||
-l::
|
||||
Scale counter values.
|
||||
|
||||
-p::
|
||||
--pid=::
|
||||
Record events on existing process ID (comma separated list).
|
||||
|
||||
-t::
|
||||
--tid=::
|
||||
Record events on existing thread ID (comma separated list).
|
||||
This option also disables inheritance by default. Enable it by adding
|
||||
--inherit.
|
||||
|
||||
-u::
|
||||
--uid=::
|
||||
Record events in threads owned by uid. Name or number.
|
||||
|
||||
-r::
|
||||
--realtime=::
|
||||
Collect data with this RT SCHED_FIFO priority.
|
||||
|
||||
--no-buffering::
|
||||
Collect data without buffering.
|
||||
|
||||
-c::
|
||||
--count=::
|
||||
Event period to sample.
|
||||
|
||||
-o::
|
||||
--output=::
|
||||
Output file name.
|
||||
|
||||
-i::
|
||||
--no-inherit::
|
||||
Child tasks do not inherit counters.
|
||||
-F::
|
||||
--freq=::
|
||||
Profile at this frequency.
|
||||
|
||||
-m::
|
||||
--mmap-pages=::
|
||||
Number of mmap data pages (must be a power of two) or size
|
||||
specification with appended unit character - B/K/M/G. The
|
||||
size is rounded up to have nearest pages power of two value.
|
||||
|
||||
-g::
|
||||
Enables call-graph (stack chain/backtrace) recording.
|
||||
|
||||
--call-graph::
|
||||
Setup and enable call-graph (stack chain/backtrace) recording,
|
||||
implies -g.
|
||||
|
||||
Allows specifying "fp" (frame pointer) or "dwarf"
|
||||
(DWARF's CFI - Call Frame Information) as the method to collect
|
||||
the information used to show the call graphs.
|
||||
|
||||
In some systems, where binaries are build with gcc
|
||||
--fomit-frame-pointer, using the "fp" method will produce bogus
|
||||
call graphs, using "dwarf", if available (perf tools linked to
|
||||
the libunwind library) should be used instead.
|
||||
|
||||
-q::
|
||||
--quiet::
|
||||
Don't print any message, useful for scripting.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose (show counter open errors, etc).
|
||||
|
||||
-s::
|
||||
--stat::
|
||||
Per thread counts.
|
||||
|
||||
-d::
|
||||
--data::
|
||||
Sample addresses.
|
||||
|
||||
-T::
|
||||
--timestamp::
|
||||
Sample timestamps. Use it with 'perf report -D' to see the timestamps,
|
||||
for instance.
|
||||
|
||||
-n::
|
||||
--no-samples::
|
||||
Don't sample.
|
||||
|
||||
-R::
|
||||
--raw-samples::
|
||||
Collect raw sample records from all opened counters (default for tracepoint counters).
|
||||
|
||||
-C::
|
||||
--cpu::
|
||||
Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a
|
||||
comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
|
||||
In per-thread mode with inheritance mode on (default), samples are captured only when
|
||||
the thread executes on the designated CPUs. Default is to monitor all CPUs.
|
||||
|
||||
-N::
|
||||
--no-buildid-cache::
|
||||
Do not update the buildid cache. This saves some overhead in situations
|
||||
where the information in the perf.data file (which includes buildids)
|
||||
is sufficient.
|
||||
|
||||
-G name,...::
|
||||
--cgroup name,...::
|
||||
monitor only in the container (cgroup) called "name". This option is available only
|
||||
in per-cpu mode. The cgroup filesystem must be mounted. All threads belonging to
|
||||
container "name" are monitored when they run on the monitored CPUs. Multiple cgroups
|
||||
can be provided. Each cgroup is applied to the corresponding event, i.e., first cgroup
|
||||
to first event, second cgroup to second event and so on. It is possible to provide
|
||||
an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must have
|
||||
corresponding events, i.e., they always refer to events defined earlier on the command
|
||||
line.
|
||||
|
||||
-b::
|
||||
--branch-any::
|
||||
Enable taken branch stack sampling. Any type of taken branch may be sampled.
|
||||
This is a shortcut for --branch-filter any. See --branch-filter for more infos.
|
||||
|
||||
-j::
|
||||
--branch-filter::
|
||||
Enable taken branch stack sampling. Each sample captures a series of consecutive
|
||||
taken branches. The number of branches captured with each sample depends on the
|
||||
underlying hardware, the type of branches of interest, and the executed code.
|
||||
It is possible to select the types of branches captured by enabling filters. The
|
||||
following filters are defined:
|
||||
|
||||
- any: any type of branches
|
||||
- any_call: any function call or system call
|
||||
- any_ret: any function return or system call return
|
||||
- ind_call: any indirect branch
|
||||
- u: only when the branch target is at the user level
|
||||
- k: only when the branch target is in the kernel
|
||||
- hv: only when the target is at the hypervisor level
|
||||
- in_tx: only when the target is in a hardware transaction
|
||||
- no_tx: only when the target is not in a hardware transaction
|
||||
- abort_tx: only when the target is a hardware transaction abort
|
||||
- cond: conditional branches
|
||||
|
||||
+
|
||||
The option requires at least one branch type among any, any_call, any_ret, ind_call, cond.
|
||||
The privilege levels may be omitted, in which case, the privilege levels of the associated
|
||||
event are applied to the branch filter. Both kernel (k) and hypervisor (hv) privilege
|
||||
levels are subject to permissions. When sampling on multiple events, branch stack sampling
|
||||
is enabled for all the sampling events. The sampled branch type is the same for all events.
|
||||
The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k
|
||||
Note that this feature may not be available on all processors.
|
||||
|
||||
--weight::
|
||||
Enable weightened sampling. An additional weight is recorded per sample and can be
|
||||
displayed with the weight and local_weight sort keys. This currently works for TSX
|
||||
abort events and some memory events in precise mode on modern Intel CPUs.
|
||||
|
||||
--transaction::
|
||||
Record transaction flags for transaction related events.
|
||||
|
||||
--per-thread::
|
||||
Use per-thread mmaps. By default per-cpu mmaps are created. This option
|
||||
overrides that and uses per-thread mmaps. A side-effect of that is that
|
||||
inheritance is automatically disabled. --per-thread is ignored with a warning
|
||||
if combined with -a or -C options.
|
||||
|
||||
-D::
|
||||
--delay=::
|
||||
After starting the program, wait msecs before measuring. This is useful to
|
||||
filter out the startup phase of the program, which is often very different.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-list[1]
|
313
tools/perf/Documentation/perf-report.txt
Normal file
313
tools/perf/Documentation/perf-report.txt
Normal file
|
@ -0,0 +1,313 @@
|
|||
perf-report(1)
|
||||
==============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-report - Read perf.data (created by perf record) and display the profile
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf report' [-i <file> | --input=file]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command displays the performance counter profile information recorded
|
||||
via perf record.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-i::
|
||||
--input=::
|
||||
Input file name. (default: perf.data unless stdin is a fifo)
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose. (show symbol address, etc)
|
||||
|
||||
-n::
|
||||
--show-nr-samples::
|
||||
Show the number of samples for each symbol
|
||||
|
||||
--showcpuutilization::
|
||||
Show sample percentage for different cpu modes.
|
||||
|
||||
-T::
|
||||
--threads::
|
||||
Show per-thread event counters
|
||||
-c::
|
||||
--comms=::
|
||||
Only consider symbols in these comms. CSV that understands
|
||||
file://filename entries. This option will affect the percentage of
|
||||
the overhead column. See --percentage for more info.
|
||||
-d::
|
||||
--dsos=::
|
||||
Only consider symbols in these dsos. CSV that understands
|
||||
file://filename entries. This option will affect the percentage of
|
||||
the overhead column. See --percentage for more info.
|
||||
-S::
|
||||
--symbols=::
|
||||
Only consider these symbols. CSV that understands
|
||||
file://filename entries. This option will affect the percentage of
|
||||
the overhead column. See --percentage for more info.
|
||||
|
||||
--symbol-filter=::
|
||||
Only show symbols that match (partially) with this filter.
|
||||
|
||||
-U::
|
||||
--hide-unresolved::
|
||||
Only display entries resolved to a symbol.
|
||||
|
||||
-s::
|
||||
--sort=::
|
||||
Sort histogram entries by given key(s) - multiple keys can be specified
|
||||
in CSV format. Following sort keys are available:
|
||||
pid, comm, dso, symbol, parent, cpu, srcline, weight, local_weight.
|
||||
|
||||
Each key has following meaning:
|
||||
|
||||
- comm: command (name) of the task which can be read via /proc/<pid>/comm
|
||||
- pid: command and tid of the task
|
||||
- dso: name of library or module executed at the time of sample
|
||||
- symbol: name of function executed at the time of sample
|
||||
- parent: name of function matched to the parent regex filter. Unmatched
|
||||
entries are displayed as "[other]".
|
||||
- cpu: cpu number the task ran at the time of sample
|
||||
- srcline: filename and line number executed at the time of sample. The
|
||||
DWARF debugging info must be provided.
|
||||
- weight: Event specific weight, e.g. memory latency or transaction
|
||||
abort cost. This is the global weight.
|
||||
- local_weight: Local weight version of the weight above.
|
||||
- transaction: Transaction abort flags.
|
||||
- overhead: Overhead percentage of sample
|
||||
- overhead_sys: Overhead percentage of sample running in system mode
|
||||
- overhead_us: Overhead percentage of sample running in user mode
|
||||
- overhead_guest_sys: Overhead percentage of sample running in system mode
|
||||
on guest machine
|
||||
- overhead_guest_us: Overhead percentage of sample running in user mode on
|
||||
guest machine
|
||||
- sample: Number of sample
|
||||
- period: Raw number of event count of sample
|
||||
|
||||
By default, comm, dso and symbol keys are used.
|
||||
(i.e. --sort comm,dso,symbol)
|
||||
|
||||
If --branch-stack option is used, following sort keys are also
|
||||
available:
|
||||
dso_from, dso_to, symbol_from, symbol_to, mispredict.
|
||||
|
||||
- dso_from: name of library or module branched from
|
||||
- dso_to: name of library or module branched to
|
||||
- symbol_from: name of function branched from
|
||||
- symbol_to: name of function branched to
|
||||
- mispredict: "N" for predicted branch, "Y" for mispredicted branch
|
||||
- in_tx: branch in TSX transaction
|
||||
- abort: TSX transaction abort.
|
||||
|
||||
And default sort keys are changed to comm, dso_from, symbol_from, dso_to
|
||||
and symbol_to, see '--branch-stack'.
|
||||
|
||||
-F::
|
||||
--fields=::
|
||||
Specify output field - multiple keys can be specified in CSV format.
|
||||
Following fields are available:
|
||||
overhead, overhead_sys, overhead_us, overhead_children, sample and period.
|
||||
Also it can contain any sort key(s).
|
||||
|
||||
By default, every sort keys not specified in -F will be appended
|
||||
automatically.
|
||||
|
||||
If --mem-mode option is used, following sort keys are also available
|
||||
(incompatible with --branch-stack):
|
||||
symbol_daddr, dso_daddr, locked, tlb, mem, snoop, dcacheline.
|
||||
|
||||
- symbol_daddr: name of data symbol being executed on at the time of sample
|
||||
- dso_daddr: name of library or module containing the data being executed
|
||||
on at the time of sample
|
||||
- locked: whether the bus was locked at the time of sample
|
||||
- tlb: type of tlb access for the data at the time of sample
|
||||
- mem: type of memory access for the data at the time of sample
|
||||
- snoop: type of snoop (if any) for the data at the time of sample
|
||||
- dcacheline: the cacheline the data address is on at the time of sample
|
||||
|
||||
And default sort keys are changed to local_weight, mem, sym, dso,
|
||||
symbol_daddr, dso_daddr, snoop, tlb, locked, see '--mem-mode'.
|
||||
|
||||
-p::
|
||||
--parent=<regex>::
|
||||
A regex filter to identify parent. The parent is a caller of this
|
||||
function and searched through the callchain, thus it requires callchain
|
||||
information recorded. The pattern is in the exteneded regex format and
|
||||
defaults to "\^sys_|^do_page_fault", see '--sort parent'.
|
||||
|
||||
-x::
|
||||
--exclude-other::
|
||||
Only display entries with parent-match.
|
||||
|
||||
-w::
|
||||
--column-widths=<width[,width...]>::
|
||||
Force each column width to the provided list, for large terminal
|
||||
readability. 0 means no limit (default behavior).
|
||||
|
||||
-t::
|
||||
--field-separator=::
|
||||
Use a special separator character and don't pad with spaces, replacing
|
||||
all occurrences of this separator in symbol names (and other output)
|
||||
with a '.' character, that thus it's the only non valid separator.
|
||||
|
||||
-D::
|
||||
--dump-raw-trace::
|
||||
Dump raw trace in ASCII.
|
||||
|
||||
-g [type,min[,limit],order[,key]]::
|
||||
--call-graph::
|
||||
Display call chains using type, min percent threshold, optional print
|
||||
limit and order.
|
||||
type can be either:
|
||||
- flat: single column, linear exposure of call chains.
|
||||
- graph: use a graph tree, displaying absolute overhead rates.
|
||||
- fractal: like graph, but displays relative rates. Each branch of
|
||||
the tree is considered as a new profiled object. +
|
||||
|
||||
order can be either:
|
||||
- callee: callee based call graph.
|
||||
- caller: inverted caller based call graph.
|
||||
|
||||
key can be:
|
||||
- function: compare on functions
|
||||
- address: compare on individual code addresses
|
||||
|
||||
Default: fractal,0.5,callee,function.
|
||||
|
||||
--children::
|
||||
Accumulate callchain of children to parent entry so that then can
|
||||
show up in the output. The output will have a new "Children" column
|
||||
and will be sorted on the data. It requires callchains are recorded.
|
||||
|
||||
--max-stack::
|
||||
Set the stack depth limit when parsing the callchain, anything
|
||||
beyond the specified depth will be ignored. This is a trade-off
|
||||
between information loss and faster processing especially for
|
||||
workloads that can have a very long callchain stack.
|
||||
|
||||
Default: 127
|
||||
|
||||
-G::
|
||||
--inverted::
|
||||
alias for inverted caller based call graph.
|
||||
|
||||
--ignore-callees=<regex>::
|
||||
Ignore callees of the function(s) matching the given regex.
|
||||
This has the effect of collecting the callers of each such
|
||||
function into one place in the call-graph tree.
|
||||
|
||||
--pretty=<key>::
|
||||
Pretty printing style. key: normal, raw
|
||||
|
||||
--stdio:: Use the stdio interface.
|
||||
|
||||
--tui:: Use the TUI interface, that is integrated with annotate and allows
|
||||
zooming into DSOs or threads, among other features. Use of --tui
|
||||
requires a tty, if one is not present, as when piping to other
|
||||
commands, the stdio interface is used.
|
||||
|
||||
--gtk:: Use the GTK2 interface.
|
||||
|
||||
-k::
|
||||
--vmlinux=<file>::
|
||||
vmlinux pathname
|
||||
|
||||
--kallsyms=<file>::
|
||||
kallsyms pathname
|
||||
|
||||
-m::
|
||||
--modules::
|
||||
Load module symbols. WARNING: This should only be used with -k and
|
||||
a LIVE kernel.
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Don't complain, do it.
|
||||
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
|
||||
-C::
|
||||
--cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
|
||||
be provided as a comma-separated list with no space: 0,1. Ranges of
|
||||
CPUs are specified with -: 0-2. Default is to report samples on all
|
||||
CPUs.
|
||||
|
||||
-M::
|
||||
--disassembler-style=:: Set disassembler style for objdump.
|
||||
|
||||
--source::
|
||||
Interleave source code with assembly code. Enabled by default,
|
||||
disable with --no-source.
|
||||
|
||||
--asm-raw::
|
||||
Show raw instruction encoding of assembly instructions.
|
||||
|
||||
--show-total-period:: Show a column with the sum of periods.
|
||||
|
||||
-I::
|
||||
--show-info::
|
||||
Display extended information about the perf.data file. This adds
|
||||
information which may be very large and thus may clutter the display.
|
||||
It currently includes: cpu and numa topology of the host system.
|
||||
|
||||
-b::
|
||||
--branch-stack::
|
||||
Use the addresses of sampled taken branches instead of the instruction
|
||||
address to build the histograms. To generate meaningful output, the
|
||||
perf.data file must have been obtained using perf record -b or
|
||||
perf record --branch-filter xxx where xxx is a branch filter option.
|
||||
perf report is able to auto-detect whether a perf.data file contains
|
||||
branch stacks and it will automatically switch to the branch view mode,
|
||||
unless --no-branch-stack is used.
|
||||
|
||||
--objdump=<path>::
|
||||
Path to objdump binary.
|
||||
|
||||
--group::
|
||||
Show event group information together.
|
||||
|
||||
--demangle::
|
||||
Demangle symbol names to human readable form. It's enabled by default,
|
||||
disable with --no-demangle.
|
||||
|
||||
--demangle-kernel::
|
||||
Demangle kernel symbol names to human readable form (for C++ kernels).
|
||||
|
||||
--mem-mode::
|
||||
Use the data addresses of samples in addition to instruction addresses
|
||||
to build the histograms. To generate meaningful output, the perf.data
|
||||
file must have been obtained using perf record -d -W and using a
|
||||
special event -e cpu/mem-loads/ or -e cpu/mem-stores/. See
|
||||
'perf mem' for simpler access.
|
||||
|
||||
--percent-limit::
|
||||
Do not show entries which have an overhead under that percent.
|
||||
(Default: 0).
|
||||
|
||||
--percentage::
|
||||
Determine how to display the overhead percentage of filtered entries.
|
||||
Filters can be applied by --comms, --dsos and/or --symbols options and
|
||||
Zoom operations on the TUI (thread, dso, etc).
|
||||
|
||||
"relative" means it's relative to filtered entries only so that the
|
||||
sum of shown entries will be always 100%. "absolute" means it retains
|
||||
the original value before and after the filter is applied.
|
||||
|
||||
--header::
|
||||
Show header information in the perf.data file. This includes
|
||||
various information like hostname, OS and perf version, cpu/mem
|
||||
info, perf command line, event list and so on. Currently only
|
||||
--stdio output supports this feature.
|
||||
|
||||
--header-only::
|
||||
Show only perf.data header (forces --stdio).
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-annotate[1]
|
55
tools/perf/Documentation/perf-sched.txt
Normal file
55
tools/perf/Documentation/perf-sched.txt
Normal file
|
@ -0,0 +1,55 @@
|
|||
perf-sched(1)
|
||||
==============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-sched - Tool to trace/measure scheduler properties (latencies)
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf sched' {record|latency|map|replay|script}
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
There are five variants of perf sched:
|
||||
|
||||
'perf sched record <command>' to record the scheduling events
|
||||
of an arbitrary workload.
|
||||
|
||||
'perf sched latency' to report the per task scheduling latencies
|
||||
and other scheduling properties of the workload.
|
||||
|
||||
'perf sched script' to see a detailed trace of the workload that
|
||||
was recorded (aliased to 'perf script' for now).
|
||||
|
||||
'perf sched replay' to simulate the workload that was recorded
|
||||
via perf sched record. (this is done by starting up mockup threads
|
||||
that mimic the workload based on the events in the trace. These
|
||||
threads can then replay the timings (CPU runtime and sleep patterns)
|
||||
of the workload as it occurred when it was recorded - and can repeat
|
||||
it a number of times, measuring its performance.)
|
||||
|
||||
'perf sched map' to print a textual context-switching outline of
|
||||
workload captured via perf sched record. Columns stand for
|
||||
individual CPUs, and the two-letter shortcuts stand for tasks that
|
||||
are running on a CPU. A '*' denotes the CPU that had the event, and
|
||||
a dot signals an idle CPU.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-i::
|
||||
--input=<file>::
|
||||
Input file name. (default: perf.data unless stdin is a fifo)
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose. (show symbol address, etc)
|
||||
|
||||
-D::
|
||||
--dump-raw-trace=::
|
||||
Display verbose dump of the sched data.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1]
|
216
tools/perf/Documentation/perf-script-perl.txt
Normal file
216
tools/perf/Documentation/perf-script-perl.txt
Normal file
|
@ -0,0 +1,216 @@
|
|||
perf-script-perl(1)
|
||||
==================
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-script-perl - Process trace data with a Perl script
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf script' [-s [Perl]:script[.pl] ]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
This perf script option is used to process perf script data using perf's
|
||||
built-in Perl interpreter. It reads and processes the input file and
|
||||
displays the results of the trace analysis implemented in the given
|
||||
Perl script, if any.
|
||||
|
||||
STARTER SCRIPTS
|
||||
---------------
|
||||
|
||||
You can avoid reading the rest of this document by running 'perf script
|
||||
-g perl' in the same directory as an existing perf.data trace file.
|
||||
That will generate a starter script containing a handler for each of
|
||||
the event types in the trace file; it simply prints every available
|
||||
field for each event in the trace file.
|
||||
|
||||
You can also look at the existing scripts in
|
||||
~/libexec/perf-core/scripts/perl for typical examples showing how to
|
||||
do basic things like aggregate event data, print results, etc. Also,
|
||||
the check-perf-script.pl script, while not interesting for its results,
|
||||
attempts to exercise all of the main scripting features.
|
||||
|
||||
EVENT HANDLERS
|
||||
--------------
|
||||
|
||||
When perf script is invoked using a trace script, a user-defined
|
||||
'handler function' is called for each event in the trace. If there's
|
||||
no handler function defined for a given event type, the event is
|
||||
ignored (or passed to a 'trace_handled' function, see below) and the
|
||||
next event is processed.
|
||||
|
||||
Most of the event's field values are passed as arguments to the
|
||||
handler function; some of the less common ones aren't - those are
|
||||
available as calls back into the perf executable (see below).
|
||||
|
||||
As an example, the following perf record command can be used to record
|
||||
all sched_wakeup events in the system:
|
||||
|
||||
# perf record -a -e sched:sched_wakeup
|
||||
|
||||
Traces meant to be processed using a script should be recorded with
|
||||
the above option: -a to enable system-wide collection.
|
||||
|
||||
The format file for the sched_wakep event defines the following fields
|
||||
(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
|
||||
|
||||
----
|
||||
format:
|
||||
field:unsigned short common_type;
|
||||
field:unsigned char common_flags;
|
||||
field:unsigned char common_preempt_count;
|
||||
field:int common_pid;
|
||||
|
||||
field:char comm[TASK_COMM_LEN];
|
||||
field:pid_t pid;
|
||||
field:int prio;
|
||||
field:int success;
|
||||
field:int target_cpu;
|
||||
----
|
||||
|
||||
The handler function for this event would be defined as:
|
||||
|
||||
----
|
||||
sub sched::sched_wakeup
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs,
|
||||
$common_nsecs, $common_pid, $common_comm,
|
||||
$comm, $pid, $prio, $success, $target_cpu) = @_;
|
||||
}
|
||||
----
|
||||
|
||||
The handler function takes the form subsystem::event_name.
|
||||
|
||||
The $common_* arguments in the handler's argument list are the set of
|
||||
arguments passed to all event handlers; some of the fields correspond
|
||||
to the common_* fields in the format file, but some are synthesized,
|
||||
and some of the common_* fields aren't common enough to to be passed
|
||||
to every event as arguments but are available as library functions.
|
||||
|
||||
Here's a brief description of each of the invariant event args:
|
||||
|
||||
$event_name the name of the event as text
|
||||
$context an opaque 'cookie' used in calls back into perf
|
||||
$common_cpu the cpu the event occurred on
|
||||
$common_secs the secs portion of the event timestamp
|
||||
$common_nsecs the nsecs portion of the event timestamp
|
||||
$common_pid the pid of the current task
|
||||
$common_comm the name of the current process
|
||||
|
||||
All of the remaining fields in the event's format file have
|
||||
counterparts as handler function arguments of the same name, as can be
|
||||
seen in the example above.
|
||||
|
||||
The above provides the basics needed to directly access every field of
|
||||
every event in a trace, which covers 90% of what you need to know to
|
||||
write a useful trace script. The sections below cover the rest.
|
||||
|
||||
SCRIPT LAYOUT
|
||||
-------------
|
||||
|
||||
Every perf script Perl script should start by setting up a Perl module
|
||||
search path and 'use'ing a few support modules (see module
|
||||
descriptions below):
|
||||
|
||||
----
|
||||
use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
|
||||
use lib "./Perf-Trace-Util/lib";
|
||||
use Perf::Trace::Core;
|
||||
use Perf::Trace::Context;
|
||||
use Perf::Trace::Util;
|
||||
----
|
||||
|
||||
The rest of the script can contain handler functions and support
|
||||
functions in any order.
|
||||
|
||||
Aside from the event handler functions discussed above, every script
|
||||
can implement a set of optional functions:
|
||||
|
||||
*trace_begin*, if defined, is called before any event is processed and
|
||||
gives scripts a chance to do setup tasks:
|
||||
|
||||
----
|
||||
sub trace_begin
|
||||
{
|
||||
}
|
||||
----
|
||||
|
||||
*trace_end*, if defined, is called after all events have been
|
||||
processed and gives scripts a chance to do end-of-script tasks, such
|
||||
as display results:
|
||||
|
||||
----
|
||||
sub trace_end
|
||||
{
|
||||
}
|
||||
----
|
||||
|
||||
*trace_unhandled*, if defined, is called after for any event that
|
||||
doesn't have a handler explicitly defined for it. The standard set
|
||||
of common arguments are passed into it:
|
||||
|
||||
----
|
||||
sub trace_unhandled
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs,
|
||||
$common_nsecs, $common_pid, $common_comm) = @_;
|
||||
}
|
||||
----
|
||||
|
||||
The remaining sections provide descriptions of each of the available
|
||||
built-in perf script Perl modules and their associated functions.
|
||||
|
||||
AVAILABLE MODULES AND FUNCTIONS
|
||||
-------------------------------
|
||||
|
||||
The following sections describe the functions and variables available
|
||||
via the various Perf::Trace::* Perl modules. To use the functions and
|
||||
variables from the given module, add the corresponding 'use
|
||||
Perf::Trace::XXX' line to your perf script script.
|
||||
|
||||
Perf::Trace::Core Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These functions provide some essential functions to user scripts.
|
||||
|
||||
The *flag_str* and *symbol_str* functions provide human-readable
|
||||
strings for flag and symbolic fields. These correspond to the strings
|
||||
and values parsed from the 'print fmt' fields of the event format
|
||||
files:
|
||||
|
||||
flag_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the flag field $field_name of event $event_name
|
||||
symbol_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the symbolic field $field_name of event $event_name
|
||||
|
||||
Perf::Trace::Context Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Some of the 'common' fields in the event format file aren't all that
|
||||
common, but need to be made accessible to user scripts nonetheless.
|
||||
|
||||
Perf::Trace::Context defines a set of functions that can be used to
|
||||
access this data in the context of the current event. Each of these
|
||||
functions expects a $context variable, which is the same as the
|
||||
$context variable passed into every event handler as the second
|
||||
argument.
|
||||
|
||||
common_pc($context) - returns common_preempt count for the current event
|
||||
common_flags($context) - returns common_flags for the current event
|
||||
common_lock_depth($context) - returns common_lock_depth for the current event
|
||||
|
||||
Perf::Trace::Util Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Various utility functions for use with perf script:
|
||||
|
||||
nsecs($secs, $nsecs) - returns total nsecs given secs/nsecs pair
|
||||
nsecs_secs($nsecs) - returns whole secs portion given nsecs
|
||||
nsecs_nsecs($nsecs) - returns nsecs remainder given nsecs
|
||||
nsecs_str($nsecs) - returns printable string in the form secs.nsecs
|
||||
avg($total, $n) - returns average given a sum and a total number of values
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-script[1]
|
620
tools/perf/Documentation/perf-script-python.txt
Normal file
620
tools/perf/Documentation/perf-script-python.txt
Normal file
|
@ -0,0 +1,620 @@
|
|||
perf-script-python(1)
|
||||
====================
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-script-python - Process trace data with a Python script
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf script' [-s [Python]:script[.py] ]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
This perf script option is used to process perf script data using perf's
|
||||
built-in Python interpreter. It reads and processes the input file and
|
||||
displays the results of the trace analysis implemented in the given
|
||||
Python script, if any.
|
||||
|
||||
A QUICK EXAMPLE
|
||||
---------------
|
||||
|
||||
This section shows the process, start to finish, of creating a working
|
||||
Python script that aggregates and extracts useful information from a
|
||||
raw perf script stream. You can avoid reading the rest of this
|
||||
document if an example is enough for you; the rest of the document
|
||||
provides more details on each step and lists the library functions
|
||||
available to script writers.
|
||||
|
||||
This example actually details the steps that were used to create the
|
||||
'syscall-counts' script you see when you list the available perf script
|
||||
scripts via 'perf script -l'. As such, this script also shows how to
|
||||
integrate your script into the list of general-purpose 'perf script'
|
||||
scripts listed by that command.
|
||||
|
||||
The syscall-counts script is a simple script, but demonstrates all the
|
||||
basic ideas necessary to create a useful script. Here's an example
|
||||
of its output (syscall names are not yet supported, they will appear
|
||||
as numbers):
|
||||
|
||||
----
|
||||
syscall events:
|
||||
|
||||
event count
|
||||
---------------------------------------- -----------
|
||||
sys_write 455067
|
||||
sys_getdents 4072
|
||||
sys_close 3037
|
||||
sys_swapoff 1769
|
||||
sys_read 923
|
||||
sys_sched_setparam 826
|
||||
sys_open 331
|
||||
sys_newfstat 326
|
||||
sys_mmap 217
|
||||
sys_munmap 216
|
||||
sys_futex 141
|
||||
sys_select 102
|
||||
sys_poll 84
|
||||
sys_setitimer 12
|
||||
sys_writev 8
|
||||
15 8
|
||||
sys_lseek 7
|
||||
sys_rt_sigprocmask 6
|
||||
sys_wait4 3
|
||||
sys_ioctl 3
|
||||
sys_set_robust_list 1
|
||||
sys_exit 1
|
||||
56 1
|
||||
sys_access 1
|
||||
----
|
||||
|
||||
Basically our task is to keep a per-syscall tally that gets updated
|
||||
every time a system call occurs in the system. Our script will do
|
||||
that, but first we need to record the data that will be processed by
|
||||
that script. Theoretically, there are a couple of ways we could do
|
||||
that:
|
||||
|
||||
- we could enable every event under the tracing/events/syscalls
|
||||
directory, but this is over 600 syscalls, well beyond the number
|
||||
allowable by perf. These individual syscall events will however be
|
||||
useful if we want to later use the guidance we get from the
|
||||
general-purpose scripts to drill down and get more detail about
|
||||
individual syscalls of interest.
|
||||
|
||||
- we can enable the sys_enter and/or sys_exit syscalls found under
|
||||
tracing/events/raw_syscalls. These are called for all syscalls; the
|
||||
'id' field can be used to distinguish between individual syscall
|
||||
numbers.
|
||||
|
||||
For this script, we only need to know that a syscall was entered; we
|
||||
don't care how it exited, so we'll use 'perf record' to record only
|
||||
the sys_enter events:
|
||||
|
||||
----
|
||||
# perf record -a -e raw_syscalls:sys_enter
|
||||
|
||||
^C[ perf record: Woken up 1 times to write data ]
|
||||
[ perf record: Captured and wrote 56.545 MB perf.data (~2470503 samples) ]
|
||||
----
|
||||
|
||||
The options basically say to collect data for every syscall event
|
||||
system-wide and multiplex the per-cpu output into a single stream.
|
||||
That single stream will be recorded in a file in the current directory
|
||||
called perf.data.
|
||||
|
||||
Once we have a perf.data file containing our data, we can use the -g
|
||||
'perf script' option to generate a Python script that will contain a
|
||||
callback handler for each event type found in the perf.data trace
|
||||
stream (for more details, see the STARTER SCRIPTS section).
|
||||
|
||||
----
|
||||
# perf script -g python
|
||||
generated Python script: perf-script.py
|
||||
|
||||
The output file created also in the current directory is named
|
||||
perf-script.py. Here's the file in its entirety:
|
||||
|
||||
# perf script event handlers, generated by perf script -g python
|
||||
# Licensed under the terms of the GNU GPL License version 2
|
||||
|
||||
# The common_* event handler fields are the most useful fields common to
|
||||
# all events. They don't necessarily correspond to the 'common_*' fields
|
||||
# in the format files. Those fields not available as handler params can
|
||||
# be retrieved using Python functions of the form common_*(context).
|
||||
# See the perf-script-python Documentation for the list of available functions.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
|
||||
|
||||
from perf_trace_context import *
|
||||
from Core import *
|
||||
|
||||
def trace_begin():
|
||||
print "in trace_begin"
|
||||
|
||||
def trace_end():
|
||||
print "in trace_end"
|
||||
|
||||
def raw_syscalls__sys_enter(event_name, context, common_cpu,
|
||||
common_secs, common_nsecs, common_pid, common_comm,
|
||||
id, args):
|
||||
print_header(event_name, common_cpu, common_secs, common_nsecs,
|
||||
common_pid, common_comm)
|
||||
|
||||
print "id=%d, args=%s\n" % \
|
||||
(id, args),
|
||||
|
||||
def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs,
|
||||
common_pid, common_comm):
|
||||
print_header(event_name, common_cpu, common_secs, common_nsecs,
|
||||
common_pid, common_comm)
|
||||
|
||||
def print_header(event_name, cpu, secs, nsecs, pid, comm):
|
||||
print "%-20s %5u %05u.%09u %8u %-20s " % \
|
||||
(event_name, cpu, secs, nsecs, pid, comm),
|
||||
----
|
||||
|
||||
At the top is a comment block followed by some import statements and a
|
||||
path append which every perf script script should include.
|
||||
|
||||
Following that are a couple generated functions, trace_begin() and
|
||||
trace_end(), which are called at the beginning and the end of the
|
||||
script respectively (for more details, see the SCRIPT_LAYOUT section
|
||||
below).
|
||||
|
||||
Following those are the 'event handler' functions generated one for
|
||||
every event in the 'perf record' output. The handler functions take
|
||||
the form subsystem__event_name, and contain named parameters, one for
|
||||
each field in the event; in this case, there's only one event,
|
||||
raw_syscalls__sys_enter(). (see the EVENT HANDLERS section below for
|
||||
more info on event handlers).
|
||||
|
||||
The final couple of functions are, like the begin and end functions,
|
||||
generated for every script. The first, trace_unhandled(), is called
|
||||
every time the script finds an event in the perf.data file that
|
||||
doesn't correspond to any event handler in the script. This could
|
||||
mean either that the record step recorded event types that it wasn't
|
||||
really interested in, or the script was run against a trace file that
|
||||
doesn't correspond to the script.
|
||||
|
||||
The script generated by -g option simply prints a line for each
|
||||
event found in the trace stream i.e. it basically just dumps the event
|
||||
and its parameter values to stdout. The print_header() function is
|
||||
simply a utility function used for that purpose. Let's rename the
|
||||
script and run it to see the default output:
|
||||
|
||||
----
|
||||
# mv perf-script.py syscall-counts.py
|
||||
# perf script -s syscall-counts.py
|
||||
|
||||
raw_syscalls__sys_enter 1 00840.847582083 7506 perf id=1, args=
|
||||
raw_syscalls__sys_enter 1 00840.847595764 7506 perf id=1, args=
|
||||
raw_syscalls__sys_enter 1 00840.847620860 7506 perf id=1, args=
|
||||
raw_syscalls__sys_enter 1 00840.847710478 6533 npviewer.bin id=78, args=
|
||||
raw_syscalls__sys_enter 1 00840.847719204 6533 npviewer.bin id=142, args=
|
||||
raw_syscalls__sys_enter 1 00840.847755445 6533 npviewer.bin id=3, args=
|
||||
raw_syscalls__sys_enter 1 00840.847775601 6533 npviewer.bin id=3, args=
|
||||
raw_syscalls__sys_enter 1 00840.847781820 6533 npviewer.bin id=3, args=
|
||||
.
|
||||
.
|
||||
.
|
||||
----
|
||||
|
||||
Of course, for this script, we're not interested in printing every
|
||||
trace event, but rather aggregating it in a useful way. So we'll get
|
||||
rid of everything to do with printing as well as the trace_begin() and
|
||||
trace_unhandled() functions, which we won't be using. That leaves us
|
||||
with this minimalistic skeleton:
|
||||
|
||||
----
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
|
||||
|
||||
from perf_trace_context import *
|
||||
from Core import *
|
||||
|
||||
def trace_end():
|
||||
print "in trace_end"
|
||||
|
||||
def raw_syscalls__sys_enter(event_name, context, common_cpu,
|
||||
common_secs, common_nsecs, common_pid, common_comm,
|
||||
id, args):
|
||||
----
|
||||
|
||||
In trace_end(), we'll simply print the results, but first we need to
|
||||
generate some results to print. To do that we need to have our
|
||||
sys_enter() handler do the necessary tallying until all events have
|
||||
been counted. A hash table indexed by syscall id is a good way to
|
||||
store that information; every time the sys_enter() handler is called,
|
||||
we simply increment a count associated with that hash entry indexed by
|
||||
that syscall id:
|
||||
|
||||
----
|
||||
syscalls = autodict()
|
||||
|
||||
try:
|
||||
syscalls[id] += 1
|
||||
except TypeError:
|
||||
syscalls[id] = 1
|
||||
----
|
||||
|
||||
The syscalls 'autodict' object is a special kind of Python dictionary
|
||||
(implemented in Core.py) that implements Perl's 'autovivifying' hashes
|
||||
in Python i.e. with autovivifying hashes, you can assign nested hash
|
||||
values without having to go to the trouble of creating intermediate
|
||||
levels if they don't exist e.g syscalls[comm][pid][id] = 1 will create
|
||||
the intermediate hash levels and finally assign the value 1 to the
|
||||
hash entry for 'id' (because the value being assigned isn't a hash
|
||||
object itself, the initial value is assigned in the TypeError
|
||||
exception. Well, there may be a better way to do this in Python but
|
||||
that's what works for now).
|
||||
|
||||
Putting that code into the raw_syscalls__sys_enter() handler, we
|
||||
effectively end up with a single-level dictionary keyed on syscall id
|
||||
and having the counts we've tallied as values.
|
||||
|
||||
The print_syscall_totals() function iterates over the entries in the
|
||||
dictionary and displays a line for each entry containing the syscall
|
||||
name (the dictionary keys contain the syscall ids, which are passed to
|
||||
the Util function syscall_name(), which translates the raw syscall
|
||||
numbers to the corresponding syscall name strings). The output is
|
||||
displayed after all the events in the trace have been processed, by
|
||||
calling the print_syscall_totals() function from the trace_end()
|
||||
handler called at the end of script processing.
|
||||
|
||||
The final script producing the output shown above is shown in its
|
||||
entirety below (syscall_name() helper is not yet available, you can
|
||||
only deal with id's for now):
|
||||
|
||||
----
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
|
||||
|
||||
from perf_trace_context import *
|
||||
from Core import *
|
||||
from Util import *
|
||||
|
||||
syscalls = autodict()
|
||||
|
||||
def trace_end():
|
||||
print_syscall_totals()
|
||||
|
||||
def raw_syscalls__sys_enter(event_name, context, common_cpu,
|
||||
common_secs, common_nsecs, common_pid, common_comm,
|
||||
id, args):
|
||||
try:
|
||||
syscalls[id] += 1
|
||||
except TypeError:
|
||||
syscalls[id] = 1
|
||||
|
||||
def print_syscall_totals():
|
||||
if for_comm is not None:
|
||||
print "\nsyscall events for %s:\n\n" % (for_comm),
|
||||
else:
|
||||
print "\nsyscall events:\n\n",
|
||||
|
||||
print "%-40s %10s\n" % ("event", "count"),
|
||||
print "%-40s %10s\n" % ("----------------------------------------", \
|
||||
"-----------"),
|
||||
|
||||
for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
|
||||
reverse = True):
|
||||
print "%-40s %10d\n" % (syscall_name(id), val),
|
||||
----
|
||||
|
||||
The script can be run just as before:
|
||||
|
||||
# perf script -s syscall-counts.py
|
||||
|
||||
So those are the essential steps in writing and running a script. The
|
||||
process can be generalized to any tracepoint or set of tracepoints
|
||||
you're interested in - basically find the tracepoint(s) you're
|
||||
interested in by looking at the list of available events shown by
|
||||
'perf list' and/or look in /sys/kernel/debug/tracing events for
|
||||
detailed event and field info, record the corresponding trace data
|
||||
using 'perf record', passing it the list of interesting events,
|
||||
generate a skeleton script using 'perf script -g python' and modify the
|
||||
code to aggregate and display it for your particular needs.
|
||||
|
||||
After you've done that you may end up with a general-purpose script
|
||||
that you want to keep around and have available for future use. By
|
||||
writing a couple of very simple shell scripts and putting them in the
|
||||
right place, you can have your script listed alongside the other
|
||||
scripts listed by the 'perf script -l' command e.g.:
|
||||
|
||||
----
|
||||
root@tropicana:~# perf script -l
|
||||
List of available trace scripts:
|
||||
wakeup-latency system-wide min/max/avg wakeup latency
|
||||
rw-by-file <comm> r/w activity for a program, by file
|
||||
rw-by-pid system-wide r/w activity
|
||||
----
|
||||
|
||||
A nice side effect of doing this is that you also then capture the
|
||||
probably lengthy 'perf record' command needed to record the events for
|
||||
the script.
|
||||
|
||||
To have the script appear as a 'built-in' script, you write two simple
|
||||
scripts, one for recording and one for 'reporting'.
|
||||
|
||||
The 'record' script is a shell script with the same base name as your
|
||||
script, but with -record appended. The shell script should be put
|
||||
into the perf/scripts/python/bin directory in the kernel source tree.
|
||||
In that script, you write the 'perf record' command-line needed for
|
||||
your script:
|
||||
|
||||
----
|
||||
# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-record
|
||||
|
||||
#!/bin/bash
|
||||
perf record -a -e raw_syscalls:sys_enter
|
||||
----
|
||||
|
||||
The 'report' script is also a shell script with the same base name as
|
||||
your script, but with -report appended. It should also be located in
|
||||
the perf/scripts/python/bin directory. In that script, you write the
|
||||
'perf script -s' command-line needed for running your script:
|
||||
|
||||
----
|
||||
# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-report
|
||||
|
||||
#!/bin/bash
|
||||
# description: system-wide syscall counts
|
||||
perf script -s ~/libexec/perf-core/scripts/python/syscall-counts.py
|
||||
----
|
||||
|
||||
Note that the location of the Python script given in the shell script
|
||||
is in the libexec/perf-core/scripts/python directory - this is where
|
||||
the script will be copied by 'make install' when you install perf.
|
||||
For the installation to install your script there, your script needs
|
||||
to be located in the perf/scripts/python directory in the kernel
|
||||
source tree:
|
||||
|
||||
----
|
||||
# ls -al kernel-source/tools/perf/scripts/python
|
||||
|
||||
root@tropicana:/home/trz/src/tip# ls -al tools/perf/scripts/python
|
||||
total 32
|
||||
drwxr-xr-x 4 trz trz 4096 2010-01-26 22:30 .
|
||||
drwxr-xr-x 4 trz trz 4096 2010-01-26 22:29 ..
|
||||
drwxr-xr-x 2 trz trz 4096 2010-01-26 22:29 bin
|
||||
-rw-r--r-- 1 trz trz 2548 2010-01-26 22:29 check-perf-script.py
|
||||
drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 Perf-Trace-Util
|
||||
-rw-r--r-- 1 trz trz 1462 2010-01-26 22:30 syscall-counts.py
|
||||
----
|
||||
|
||||
Once you've done that (don't forget to do a new 'make install',
|
||||
otherwise your script won't show up at run-time), 'perf script -l'
|
||||
should show a new entry for your script:
|
||||
|
||||
----
|
||||
root@tropicana:~# perf script -l
|
||||
List of available trace scripts:
|
||||
wakeup-latency system-wide min/max/avg wakeup latency
|
||||
rw-by-file <comm> r/w activity for a program, by file
|
||||
rw-by-pid system-wide r/w activity
|
||||
syscall-counts system-wide syscall counts
|
||||
----
|
||||
|
||||
You can now perform the record step via 'perf script record':
|
||||
|
||||
# perf script record syscall-counts
|
||||
|
||||
and display the output using 'perf script report':
|
||||
|
||||
# perf script report syscall-counts
|
||||
|
||||
STARTER SCRIPTS
|
||||
---------------
|
||||
|
||||
You can quickly get started writing a script for a particular set of
|
||||
trace data by generating a skeleton script using 'perf script -g
|
||||
python' in the same directory as an existing perf.data trace file.
|
||||
That will generate a starter script containing a handler for each of
|
||||
the event types in the trace file; it simply prints every available
|
||||
field for each event in the trace file.
|
||||
|
||||
You can also look at the existing scripts in
|
||||
~/libexec/perf-core/scripts/python for typical examples showing how to
|
||||
do basic things like aggregate event data, print results, etc. Also,
|
||||
the check-perf-script.py script, while not interesting for its results,
|
||||
attempts to exercise all of the main scripting features.
|
||||
|
||||
EVENT HANDLERS
|
||||
--------------
|
||||
|
||||
When perf script is invoked using a trace script, a user-defined
|
||||
'handler function' is called for each event in the trace. If there's
|
||||
no handler function defined for a given event type, the event is
|
||||
ignored (or passed to a 'trace_handled' function, see below) and the
|
||||
next event is processed.
|
||||
|
||||
Most of the event's field values are passed as arguments to the
|
||||
handler function; some of the less common ones aren't - those are
|
||||
available as calls back into the perf executable (see below).
|
||||
|
||||
As an example, the following perf record command can be used to record
|
||||
all sched_wakeup events in the system:
|
||||
|
||||
# perf record -a -e sched:sched_wakeup
|
||||
|
||||
Traces meant to be processed using a script should be recorded with
|
||||
the above option: -a to enable system-wide collection.
|
||||
|
||||
The format file for the sched_wakep event defines the following fields
|
||||
(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
|
||||
|
||||
----
|
||||
format:
|
||||
field:unsigned short common_type;
|
||||
field:unsigned char common_flags;
|
||||
field:unsigned char common_preempt_count;
|
||||
field:int common_pid;
|
||||
|
||||
field:char comm[TASK_COMM_LEN];
|
||||
field:pid_t pid;
|
||||
field:int prio;
|
||||
field:int success;
|
||||
field:int target_cpu;
|
||||
----
|
||||
|
||||
The handler function for this event would be defined as:
|
||||
|
||||
----
|
||||
def sched__sched_wakeup(event_name, context, common_cpu, common_secs,
|
||||
common_nsecs, common_pid, common_comm,
|
||||
comm, pid, prio, success, target_cpu):
|
||||
pass
|
||||
----
|
||||
|
||||
The handler function takes the form subsystem__event_name.
|
||||
|
||||
The common_* arguments in the handler's argument list are the set of
|
||||
arguments passed to all event handlers; some of the fields correspond
|
||||
to the common_* fields in the format file, but some are synthesized,
|
||||
and some of the common_* fields aren't common enough to to be passed
|
||||
to every event as arguments but are available as library functions.
|
||||
|
||||
Here's a brief description of each of the invariant event args:
|
||||
|
||||
event_name the name of the event as text
|
||||
context an opaque 'cookie' used in calls back into perf
|
||||
common_cpu the cpu the event occurred on
|
||||
common_secs the secs portion of the event timestamp
|
||||
common_nsecs the nsecs portion of the event timestamp
|
||||
common_pid the pid of the current task
|
||||
common_comm the name of the current process
|
||||
|
||||
All of the remaining fields in the event's format file have
|
||||
counterparts as handler function arguments of the same name, as can be
|
||||
seen in the example above.
|
||||
|
||||
The above provides the basics needed to directly access every field of
|
||||
every event in a trace, which covers 90% of what you need to know to
|
||||
write a useful trace script. The sections below cover the rest.
|
||||
|
||||
SCRIPT LAYOUT
|
||||
-------------
|
||||
|
||||
Every perf script Python script should start by setting up a Python
|
||||
module search path and 'import'ing a few support modules (see module
|
||||
descriptions below):
|
||||
|
||||
----
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
|
||||
|
||||
from perf_trace_context import *
|
||||
from Core import *
|
||||
----
|
||||
|
||||
The rest of the script can contain handler functions and support
|
||||
functions in any order.
|
||||
|
||||
Aside from the event handler functions discussed above, every script
|
||||
can implement a set of optional functions:
|
||||
|
||||
*trace_begin*, if defined, is called before any event is processed and
|
||||
gives scripts a chance to do setup tasks:
|
||||
|
||||
----
|
||||
def trace_begin:
|
||||
pass
|
||||
----
|
||||
|
||||
*trace_end*, if defined, is called after all events have been
|
||||
processed and gives scripts a chance to do end-of-script tasks, such
|
||||
as display results:
|
||||
|
||||
----
|
||||
def trace_end:
|
||||
pass
|
||||
----
|
||||
|
||||
*trace_unhandled*, if defined, is called after for any event that
|
||||
doesn't have a handler explicitly defined for it. The standard set
|
||||
of common arguments are passed into it:
|
||||
|
||||
----
|
||||
def trace_unhandled(event_name, context, common_cpu, common_secs,
|
||||
common_nsecs, common_pid, common_comm):
|
||||
pass
|
||||
----
|
||||
|
||||
The remaining sections provide descriptions of each of the available
|
||||
built-in perf script Python modules and their associated functions.
|
||||
|
||||
AVAILABLE MODULES AND FUNCTIONS
|
||||
-------------------------------
|
||||
|
||||
The following sections describe the functions and variables available
|
||||
via the various perf script Python modules. To use the functions and
|
||||
variables from the given module, add the corresponding 'from XXXX
|
||||
import' line to your perf script script.
|
||||
|
||||
Core.py Module
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
These functions provide some essential functions to user scripts.
|
||||
|
||||
The *flag_str* and *symbol_str* functions provide human-readable
|
||||
strings for flag and symbolic fields. These correspond to the strings
|
||||
and values parsed from the 'print fmt' fields of the event format
|
||||
files:
|
||||
|
||||
flag_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the flag field field_name of event event_name
|
||||
symbol_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the symbolic field field_name of event event_name
|
||||
|
||||
The *autodict* function returns a special kind of Python
|
||||
dictionary that implements Perl's 'autovivifying' hashes in Python
|
||||
i.e. with autovivifying hashes, you can assign nested hash values
|
||||
without having to go to the trouble of creating intermediate levels if
|
||||
they don't exist.
|
||||
|
||||
autodict() - returns an autovivifying dictionary instance
|
||||
|
||||
|
||||
perf_trace_context Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Some of the 'common' fields in the event format file aren't all that
|
||||
common, but need to be made accessible to user scripts nonetheless.
|
||||
|
||||
perf_trace_context defines a set of functions that can be used to
|
||||
access this data in the context of the current event. Each of these
|
||||
functions expects a context variable, which is the same as the
|
||||
context variable passed into every event handler as the second
|
||||
argument.
|
||||
|
||||
common_pc(context) - returns common_preempt count for the current event
|
||||
common_flags(context) - returns common_flags for the current event
|
||||
common_lock_depth(context) - returns common_lock_depth for the current event
|
||||
|
||||
Util.py Module
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Various utility functions for use with perf script:
|
||||
|
||||
nsecs(secs, nsecs) - returns total nsecs given secs/nsecs pair
|
||||
nsecs_secs(nsecs) - returns whole secs portion given nsecs
|
||||
nsecs_nsecs(nsecs) - returns nsecs remainder given nsecs
|
||||
nsecs_str(nsecs) - returns printable string in the form secs.nsecs
|
||||
avg(total, n) - returns average given a sum and a total number of values
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-script[1]
|
221
tools/perf/Documentation/perf-script.txt
Normal file
221
tools/perf/Documentation/perf-script.txt
Normal file
|
@ -0,0 +1,221 @@
|
|||
perf-script(1)
|
||||
=============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-script - Read perf.data (created by perf record) and display trace output
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf script' [<options>]
|
||||
'perf script' [<options>] record <script> [<record-options>] <command>
|
||||
'perf script' [<options>] report <script> [script-args]
|
||||
'perf script' [<options>] <script> <required-script-args> [<record-options>] <command>
|
||||
'perf script' [<options>] <top-script> [script-args]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command reads the input file and displays the trace recorded.
|
||||
|
||||
There are several variants of perf script:
|
||||
|
||||
'perf script' to see a detailed trace of the workload that was
|
||||
recorded.
|
||||
|
||||
You can also run a set of pre-canned scripts that aggregate and
|
||||
summarize the raw trace data in various ways (the list of scripts is
|
||||
available via 'perf script -l'). The following variants allow you to
|
||||
record and run those scripts:
|
||||
|
||||
'perf script record <script> <command>' to record the events required
|
||||
for 'perf script report'. <script> is the name displayed in the
|
||||
output of 'perf script --list' i.e. the actual script name minus any
|
||||
language extension. If <command> is not specified, the events are
|
||||
recorded using the -a (system-wide) 'perf record' option.
|
||||
|
||||
'perf script report <script> [args]' to run and display the results
|
||||
of <script>. <script> is the name displayed in the output of 'perf
|
||||
trace --list' i.e. the actual script name minus any language
|
||||
extension. The perf.data output from a previous run of 'perf script
|
||||
record <script>' is used and should be present for this command to
|
||||
succeed. [args] refers to the (mainly optional) args expected by
|
||||
the script.
|
||||
|
||||
'perf script <script> <required-script-args> <command>' to both
|
||||
record the events required for <script> and to run the <script>
|
||||
using 'live-mode' i.e. without writing anything to disk. <script>
|
||||
is the name displayed in the output of 'perf script --list' i.e. the
|
||||
actual script name minus any language extension. If <command> is
|
||||
not specified, the events are recorded using the -a (system-wide)
|
||||
'perf record' option. If <script> has any required args, they
|
||||
should be specified before <command>. This mode doesn't allow for
|
||||
optional script args to be specified; if optional script args are
|
||||
desired, they can be specified using separate 'perf script record'
|
||||
and 'perf script report' commands, with the stdout of the record step
|
||||
piped to the stdin of the report script, using the '-o -' and '-i -'
|
||||
options of the corresponding commands.
|
||||
|
||||
'perf script <top-script>' to both record the events required for
|
||||
<top-script> and to run the <top-script> using 'live-mode'
|
||||
i.e. without writing anything to disk. <top-script> is the name
|
||||
displayed in the output of 'perf script --list' i.e. the actual
|
||||
script name minus any language extension; a <top-script> is defined
|
||||
as any script name ending with the string 'top'.
|
||||
|
||||
[<record-options>] can be passed to the record steps of 'perf script
|
||||
record' and 'live-mode' variants; this isn't possible however for
|
||||
<top-script> 'live-mode' or 'perf script report' variants.
|
||||
|
||||
See the 'SEE ALSO' section for links to language-specific
|
||||
information on how to write and run your own trace scripts.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
<command>...::
|
||||
Any command you can specify in a shell.
|
||||
|
||||
-D::
|
||||
--dump-raw-script=::
|
||||
Display verbose dump of the trace data.
|
||||
|
||||
-L::
|
||||
--Latency=::
|
||||
Show latency attributes (irqs/preemption disabled, etc).
|
||||
|
||||
-l::
|
||||
--list=::
|
||||
Display a list of available trace scripts.
|
||||
|
||||
-s ['lang']::
|
||||
--script=::
|
||||
Process trace data with the given script ([lang]:script[.ext]).
|
||||
If the string 'lang' is specified in place of a script name, a
|
||||
list of supported languages will be displayed instead.
|
||||
|
||||
-g::
|
||||
--gen-script=::
|
||||
Generate perf-script.[ext] starter script for given language,
|
||||
using current perf.data.
|
||||
|
||||
-a::
|
||||
Force system-wide collection. Scripts run without a <command>
|
||||
normally use -a by default, while scripts run with a <command>
|
||||
normally don't - this option allows the latter to be run in
|
||||
system-wide mode.
|
||||
|
||||
-i::
|
||||
--input=::
|
||||
Input file name. (default: perf.data unless stdin is a fifo)
|
||||
|
||||
-d::
|
||||
--debug-mode::
|
||||
Do various checks like samples ordering and lost events.
|
||||
|
||||
-f::
|
||||
--fields::
|
||||
Comma separated list of fields to print. Options are:
|
||||
comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline, period.
|
||||
Field list can be prepended with the type, trace, sw or hw,
|
||||
to indicate to which event type the field list applies.
|
||||
e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace
|
||||
|
||||
perf script -f <fields>
|
||||
|
||||
is equivalent to:
|
||||
|
||||
perf script -f trace:<fields> -f sw:<fields> -f hw:<fields>
|
||||
|
||||
i.e., the specified fields apply to all event types if the type string
|
||||
is not given.
|
||||
|
||||
The arguments are processed in the order received. A later usage can
|
||||
reset a prior request. e.g.:
|
||||
|
||||
-f trace: -f comm,tid,time,ip,sym
|
||||
|
||||
The first -f suppresses trace events (field list is ""), but then the
|
||||
second invocation sets the fields to comm,tid,time,ip,sym. In this case a
|
||||
warning is given to the user:
|
||||
|
||||
"Overriding previous field request for all events."
|
||||
|
||||
Alternatively, consider the order:
|
||||
|
||||
-f comm,tid,time,ip,sym -f trace:
|
||||
|
||||
The first -f sets the fields for all events and the second -f
|
||||
suppresses trace events. The user is given a warning message about
|
||||
the override, and the result of the above is that only S/W and H/W
|
||||
events are displayed with the given fields.
|
||||
|
||||
For the 'wildcard' option if a user selected field is invalid for an
|
||||
event type, a message is displayed to the user that the option is
|
||||
ignored for that type. For example:
|
||||
|
||||
$ perf script -f comm,tid,trace
|
||||
'trace' not valid for hardware events. Ignoring.
|
||||
'trace' not valid for software events. Ignoring.
|
||||
|
||||
Alternatively, if the type is given an invalid field is specified it
|
||||
is an error. For example:
|
||||
|
||||
perf script -v -f sw:comm,tid,trace
|
||||
'trace' not valid for software events.
|
||||
|
||||
At this point usage is displayed, and perf-script exits.
|
||||
|
||||
Finally, a user may not set fields to none for all event types.
|
||||
i.e., -f "" is not allowed.
|
||||
|
||||
-k::
|
||||
--vmlinux=<file>::
|
||||
vmlinux pathname
|
||||
|
||||
--kallsyms=<file>::
|
||||
kallsyms pathname
|
||||
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
|
||||
-G::
|
||||
--hide-call-graph::
|
||||
When printing symbols do not display call chain.
|
||||
|
||||
-C::
|
||||
--cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
|
||||
be provided as a comma-separated list with no space: 0,1. Ranges of
|
||||
CPUs are specified with -: 0-2. Default is to report samples on all
|
||||
CPUs.
|
||||
|
||||
-c::
|
||||
--comms=::
|
||||
Only display events for these comms. CSV that understands
|
||||
file://filename entries.
|
||||
|
||||
-I::
|
||||
--show-info::
|
||||
Display extended information about the perf.data file. This adds
|
||||
information which may be very large and thus may clutter the display.
|
||||
It currently includes: cpu and numa topology of the host system.
|
||||
It can only be used with the perf script report mode.
|
||||
|
||||
--show-kernel-path::
|
||||
Try to resolve the path of [kernel.kallsyms]
|
||||
|
||||
--show-task-events
|
||||
Display task related events (e.g. FORK, COMM, EXIT).
|
||||
|
||||
--show-mmap-events
|
||||
Display mmap related events (e.g. MMAP, MMAP2).
|
||||
|
||||
--header
|
||||
Show perf.data header.
|
||||
|
||||
--header-only
|
||||
Show only perf.data header.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-script-perl[1],
|
||||
linkperf:perf-script-python[1]
|
165
tools/perf/Documentation/perf-stat.txt
Normal file
165
tools/perf/Documentation/perf-stat.txt
Normal file
|
@ -0,0 +1,165 @@
|
|||
perf-stat(1)
|
||||
============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-stat - Run a command and gather performance counter statistics
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf stat' [-e <EVENT> | --event=EVENT] [-a] <command>
|
||||
'perf stat' [-e <EVENT> | --event=EVENT] [-a] -- <command> [<options>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command runs a command and gathers performance counter statistics
|
||||
from it.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
<command>...::
|
||||
Any command you can specify in a shell.
|
||||
|
||||
|
||||
-e::
|
||||
--event=::
|
||||
Select the PMU event. Selection can be a symbolic event name
|
||||
(use 'perf list' to list all events) or a raw PMU
|
||||
event (eventsel+umask) in the form of rNNN where NNN is a
|
||||
hexadecimal event descriptor.
|
||||
|
||||
-i::
|
||||
--no-inherit::
|
||||
child tasks do not inherit counters
|
||||
-p::
|
||||
--pid=<pid>::
|
||||
stat events on existing process id (comma separated list)
|
||||
|
||||
-t::
|
||||
--tid=<tid>::
|
||||
stat events on existing thread id (comma separated list)
|
||||
|
||||
|
||||
-a::
|
||||
--all-cpus::
|
||||
system-wide collection from all CPUs
|
||||
|
||||
-c::
|
||||
--scale::
|
||||
scale/normalize counter values
|
||||
|
||||
-r::
|
||||
--repeat=<n>::
|
||||
repeat command and print average + stddev (max: 100). 0 means forever.
|
||||
|
||||
-B::
|
||||
--big-num::
|
||||
print large numbers with thousands' separators according to locale
|
||||
|
||||
-C::
|
||||
--cpu=::
|
||||
Count only on the list of CPUs provided. Multiple CPUs can be provided as a
|
||||
comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
|
||||
In per-thread mode, this option is ignored. The -a option is still necessary
|
||||
to activate system-wide monitoring. Default is to count on all CPUs.
|
||||
|
||||
-A::
|
||||
--no-aggr::
|
||||
Do not aggregate counts across all monitored CPUs in system-wide mode (-a).
|
||||
This option is only valid in system-wide mode.
|
||||
|
||||
-n::
|
||||
--null::
|
||||
null run - don't start any counters
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
be more verbose (show counter open errors, etc)
|
||||
|
||||
-x SEP::
|
||||
--field-separator SEP::
|
||||
print counts using a CSV-style output to make it easy to import directly into
|
||||
spreadsheets. Columns are separated by the string specified in SEP.
|
||||
|
||||
-G name::
|
||||
--cgroup name::
|
||||
monitor only in the container (cgroup) called "name". This option is available only
|
||||
in per-cpu mode. The cgroup filesystem must be mounted. All threads belonging to
|
||||
container "name" are monitored when they run on the monitored CPUs. Multiple cgroups
|
||||
can be provided. Each cgroup is applied to the corresponding event, i.e., first cgroup
|
||||
to first event, second cgroup to second event and so on. It is possible to provide
|
||||
an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must have
|
||||
corresponding events, i.e., they always refer to events defined earlier on the command
|
||||
line.
|
||||
|
||||
-o file::
|
||||
--output file::
|
||||
Print the output into the designated file.
|
||||
|
||||
--append::
|
||||
Append to the output file designated with the -o option. Ignored if -o is not specified.
|
||||
|
||||
--log-fd::
|
||||
|
||||
Log output to fd, instead of stderr. Complementary to --output, and mutually exclusive
|
||||
with it. --append may be used here. Examples:
|
||||
3>results perf stat --log-fd 3 -- $cmd
|
||||
3>>results perf stat --log-fd 3 --append -- $cmd
|
||||
|
||||
--pre::
|
||||
--post::
|
||||
Pre and post measurement hooks, e.g.:
|
||||
|
||||
perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- make -s -j64 O=defconfig-build/ bzImage
|
||||
|
||||
-I msecs::
|
||||
--interval-print msecs::
|
||||
Print count deltas every N milliseconds (minimum: 100ms)
|
||||
example: perf stat -I 1000 -e cycles -a sleep 5
|
||||
|
||||
--per-socket::
|
||||
Aggregate counts per processor socket for system-wide mode measurements. This
|
||||
is a useful mode to detect imbalance between sockets. To enable this mode,
|
||||
use --per-socket in addition to -a. (system-wide). The output includes the
|
||||
socket number and the number of online processors on that socket. This is
|
||||
useful to gauge the amount of aggregation.
|
||||
|
||||
--per-core::
|
||||
Aggregate counts per physical processor for system-wide mode measurements. This
|
||||
is a useful mode to detect imbalance between physical cores. To enable this mode,
|
||||
use --per-core in addition to -a. (system-wide). The output includes the
|
||||
core number and the number of online logical processors on that physical processor.
|
||||
|
||||
-D msecs::
|
||||
--delay msecs::
|
||||
After starting the program, wait msecs before measuring. This is useful to
|
||||
filter out the startup phase of the program, which is often very different.
|
||||
|
||||
-T::
|
||||
--transaction::
|
||||
|
||||
Print statistics of transactional execution if supported.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
$ perf stat -- make -j
|
||||
|
||||
Performance counter stats for 'make -j':
|
||||
|
||||
8117.370256 task clock ticks # 11.281 CPU utilization factor
|
||||
678 context switches # 0.000 M/sec
|
||||
133 CPU migrations # 0.000 M/sec
|
||||
235724 pagefaults # 0.029 M/sec
|
||||
24821162526 CPU cycles # 3057.784 M/sec
|
||||
18687303457 instructions # 2302.138 M/sec
|
||||
172158895 cache references # 21.209 M/sec
|
||||
27075259 cache misses # 3.335 M/sec
|
||||
|
||||
Wall-clock time elapsed: 719.554352 msecs
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-top[1], linkperf:perf-list[1]
|
32
tools/perf/Documentation/perf-test.txt
Normal file
32
tools/perf/Documentation/perf-test.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
perf-test(1)
|
||||
============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-test - Runs sanity tests.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command does assorted sanity tests, initially through linked routines but
|
||||
also will look for a directory with more tests in the form of scripts.
|
||||
|
||||
To get a list of available tests use 'perf test list', specifying a test name
|
||||
fragment will show all tests that have it.
|
||||
|
||||
To run just specific tests, inform test name fragments or the numbers obtained
|
||||
from 'perf test list'.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-s::
|
||||
--skip::
|
||||
Tests to skip (comma separated numeric list).
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose.
|
126
tools/perf/Documentation/perf-timechart.txt
Normal file
126
tools/perf/Documentation/perf-timechart.txt
Normal file
|
@ -0,0 +1,126 @@
|
|||
perf-timechart(1)
|
||||
=================
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-timechart - Tool to visualize total system behavior during a workload
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf timechart' [<timechart options>] {record} [<record options>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
There are two variants of perf timechart:
|
||||
|
||||
'perf timechart record <command>' to record the system level events
|
||||
of an arbitrary workload. By default timechart records only scheduler
|
||||
and CPU events (task switches, running times, CPU power states, etc),
|
||||
but it's possible to record IO (disk, network) activity using -I argument.
|
||||
|
||||
'perf timechart' to turn a trace into a Scalable Vector Graphics file,
|
||||
that can be viewed with popular SVG viewers such as 'Inkscape'. Depending
|
||||
on the events in the perf.data file, timechart will contain scheduler/cpu
|
||||
events or IO events.
|
||||
|
||||
In IO mode, every bar has two charts: upper and lower.
|
||||
Upper bar shows incoming events (disk reads, ingress network packets).
|
||||
Lower bar shows outgoing events (disk writes, egress network packets).
|
||||
There are also poll bars which show how much time application spent
|
||||
in poll/epoll/select syscalls.
|
||||
|
||||
TIMECHART OPTIONS
|
||||
-----------------
|
||||
-o::
|
||||
--output=::
|
||||
Select the output file (default: output.svg)
|
||||
-i::
|
||||
--input=::
|
||||
Select the input file (default: perf.data unless stdin is a fifo)
|
||||
-w::
|
||||
--width=::
|
||||
Select the width of the SVG file (default: 1000)
|
||||
-P::
|
||||
--power-only::
|
||||
Only output the CPU power section of the diagram
|
||||
-T::
|
||||
--tasks-only::
|
||||
Don't output processor state transitions
|
||||
-p::
|
||||
--process::
|
||||
Select the processes to display, by name or PID
|
||||
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
-n::
|
||||
--proc-num::
|
||||
Print task info for at least given number of tasks.
|
||||
-t::
|
||||
--topology::
|
||||
Sort CPUs according to topology.
|
||||
--highlight=<duration_nsecs|task_name>::
|
||||
Highlight tasks (using different color) that run more than given
|
||||
duration or tasks with given name. If number is given it's interpreted
|
||||
as number of nanoseconds. If non-numeric string is given it's
|
||||
interpreted as task name.
|
||||
--io-skip-eagain::
|
||||
Don't draw EAGAIN IO events.
|
||||
--io-min-time=<nsecs>::
|
||||
Draw small events as if they lasted min-time. Useful when you need
|
||||
to see very small and fast IO. It's possible to specify ms or us
|
||||
suffix to specify time in milliseconds or microseconds.
|
||||
Default value is 1ms.
|
||||
--io-merge-dist=<nsecs>::
|
||||
Merge events that are merge-dist nanoseconds apart.
|
||||
Reduces number of figures on the SVG and makes it more render-friendly.
|
||||
It's possible to specify ms or us suffix to specify time in
|
||||
milliseconds or microseconds.
|
||||
Default value is 1us.
|
||||
|
||||
RECORD OPTIONS
|
||||
--------------
|
||||
-P::
|
||||
--power-only::
|
||||
Record only power-related events
|
||||
-T::
|
||||
--tasks-only::
|
||||
Record only tasks-related events
|
||||
-I::
|
||||
--io-only::
|
||||
Record only io-related events
|
||||
-g::
|
||||
--callchain::
|
||||
Do call-graph (stack chain/backtrace) recording
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
$ perf timechart record git pull
|
||||
|
||||
[ perf record: Woken up 13 times to write data ]
|
||||
[ perf record: Captured and wrote 4.253 MB perf.data (~185801 samples) ]
|
||||
|
||||
$ perf timechart
|
||||
|
||||
Written 10.2 seconds of trace to output.svg.
|
||||
|
||||
Record system-wide timechart:
|
||||
|
||||
$ perf timechart record
|
||||
|
||||
then generate timechart and highlight 'gcc' tasks:
|
||||
|
||||
$ perf timechart --highlight gcc
|
||||
|
||||
Record system-wide IO events:
|
||||
|
||||
$ perf timechart record -I
|
||||
|
||||
then generate timechart:
|
||||
|
||||
$ perf timechart
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1]
|
240
tools/perf/Documentation/perf-top.txt
Normal file
240
tools/perf/Documentation/perf-top.txt
Normal file
|
@ -0,0 +1,240 @@
|
|||
perf-top(1)
|
||||
===========
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-top - System profiling tool.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf top' [-e <EVENT> | --event=EVENT] [<options>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command generates and displays a performance counter profile in real time.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-a::
|
||||
--all-cpus::
|
||||
System-wide collection. (default)
|
||||
|
||||
-c <count>::
|
||||
--count=<count>::
|
||||
Event period to sample.
|
||||
|
||||
-C <cpu-list>::
|
||||
--cpu=<cpu>::
|
||||
Monitor only on the list of CPUs provided. Multiple CPUs can be provided as a
|
||||
comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
|
||||
Default is to monitor all CPUS.
|
||||
|
||||
-d <seconds>::
|
||||
--delay=<seconds>::
|
||||
Number of seconds to delay between refreshes.
|
||||
|
||||
-e <event>::
|
||||
--event=<event>::
|
||||
Select the PMU event. Selection can be a symbolic event name
|
||||
(use 'perf list' to list all events) or a raw PMU
|
||||
event (eventsel+umask) in the form of rNNN where NNN is a
|
||||
hexadecimal event descriptor.
|
||||
|
||||
-E <entries>::
|
||||
--entries=<entries>::
|
||||
Display this many functions.
|
||||
|
||||
-f <count>::
|
||||
--count-filter=<count>::
|
||||
Only display functions with more events than this.
|
||||
|
||||
--group::
|
||||
Put the counters into a counter group.
|
||||
|
||||
-F <freq>::
|
||||
--freq=<freq>::
|
||||
Profile at this frequency.
|
||||
|
||||
-i::
|
||||
--inherit::
|
||||
Child tasks do not inherit counters.
|
||||
|
||||
-k <path>::
|
||||
--vmlinux=<path>::
|
||||
Path to vmlinux. Required for annotation functionality.
|
||||
|
||||
-m <pages>::
|
||||
--mmap-pages=<pages>::
|
||||
Number of mmap data pages (must be a power of two) or size
|
||||
specification with appended unit character - B/K/M/G. The
|
||||
size is rounded up to have nearest pages power of two value.
|
||||
|
||||
-p <pid>::
|
||||
--pid=<pid>::
|
||||
Profile events on existing Process ID (comma separated list).
|
||||
|
||||
-t <tid>::
|
||||
--tid=<tid>::
|
||||
Profile events on existing thread ID (comma separated list).
|
||||
|
||||
-u::
|
||||
--uid=::
|
||||
Record events in threads owned by uid. Name or number.
|
||||
|
||||
-r <priority>::
|
||||
--realtime=<priority>::
|
||||
Collect data with this RT SCHED_FIFO priority.
|
||||
|
||||
--sym-annotate=<symbol>::
|
||||
Annotate this symbol.
|
||||
|
||||
-K::
|
||||
--hide_kernel_symbols::
|
||||
Hide kernel symbols.
|
||||
|
||||
-U::
|
||||
--hide_user_symbols::
|
||||
Hide user symbols.
|
||||
|
||||
--demangle-kernel::
|
||||
Demangle kernel symbols.
|
||||
|
||||
-D::
|
||||
--dump-symtab::
|
||||
Dump the symbol table used for profiling.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose (show counter open errors, etc).
|
||||
|
||||
-z::
|
||||
--zero::
|
||||
Zero history across display updates.
|
||||
|
||||
-s::
|
||||
--sort::
|
||||
Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight,
|
||||
local_weight, abort, in_tx, transaction, overhead, sample, period.
|
||||
Please see description of --sort in the perf-report man page.
|
||||
|
||||
--fields=::
|
||||
Specify output field - multiple keys can be specified in CSV format.
|
||||
Following fields are available:
|
||||
overhead, overhead_sys, overhead_us, overhead_children, sample and period.
|
||||
Also it can contain any sort key(s).
|
||||
|
||||
By default, every sort keys not specified in --field will be appended
|
||||
automatically.
|
||||
|
||||
-n::
|
||||
--show-nr-samples::
|
||||
Show a column with the number of samples.
|
||||
|
||||
--show-total-period::
|
||||
Show a column with the sum of periods.
|
||||
|
||||
--dsos::
|
||||
Only consider symbols in these dsos. This option will affect the
|
||||
percentage of the overhead column. See --percentage for more info.
|
||||
|
||||
--comms::
|
||||
Only consider symbols in these comms. This option will affect the
|
||||
percentage of the overhead column. See --percentage for more info.
|
||||
|
||||
--symbols::
|
||||
Only consider these symbols. This option will affect the
|
||||
percentage of the overhead column. See --percentage for more info.
|
||||
|
||||
-M::
|
||||
--disassembler-style=:: Set disassembler style for objdump.
|
||||
|
||||
--source::
|
||||
Interleave source code with assembly code. Enabled by default,
|
||||
disable with --no-source.
|
||||
|
||||
--asm-raw::
|
||||
Show raw instruction encoding of assembly instructions.
|
||||
|
||||
-g::
|
||||
Enables call-graph (stack chain/backtrace) recording.
|
||||
|
||||
--call-graph::
|
||||
Setup and enable call-graph (stack chain/backtrace) recording,
|
||||
implies -g.
|
||||
|
||||
--children::
|
||||
Accumulate callchain of children to parent entry so that then can
|
||||
show up in the output. The output will have a new "Children" column
|
||||
and will be sorted on the data. It requires -g/--call-graph option
|
||||
enabled.
|
||||
|
||||
--max-stack::
|
||||
Set the stack depth limit when parsing the callchain, anything
|
||||
beyond the specified depth will be ignored. This is a trade-off
|
||||
between information loss and faster processing especially for
|
||||
workloads that can have a very long callchain stack.
|
||||
|
||||
Default: 127
|
||||
|
||||
--ignore-callees=<regex>::
|
||||
Ignore callees of the function(s) matching the given regex.
|
||||
This has the effect of collecting the callers of each such
|
||||
function into one place in the call-graph tree.
|
||||
|
||||
--percent-limit::
|
||||
Do not show entries which have an overhead under that percent.
|
||||
(Default: 0).
|
||||
|
||||
--percentage::
|
||||
Determine how to display the overhead percentage of filtered entries.
|
||||
Filters can be applied by --comms, --dsos and/or --symbols options and
|
||||
Zoom operations on the TUI (thread, dso, etc).
|
||||
|
||||
"relative" means it's relative to filtered entries only so that the
|
||||
sum of shown entries will be always 100%. "absolute" means it retains
|
||||
the original value before and after the filter is applied.
|
||||
|
||||
-w::
|
||||
--column-widths=<width[,width...]>::
|
||||
Force each column width to the provided list, for large terminal
|
||||
readability. 0 means no limit (default behavior).
|
||||
|
||||
|
||||
INTERACTIVE PROMPTING KEYS
|
||||
--------------------------
|
||||
|
||||
[d]::
|
||||
Display refresh delay.
|
||||
|
||||
[e]::
|
||||
Number of entries to display.
|
||||
|
||||
[E]::
|
||||
Event to display when multiple counters are active.
|
||||
|
||||
[f]::
|
||||
Profile display filter (>= hit count).
|
||||
|
||||
[F]::
|
||||
Annotation display filter (>= % of total).
|
||||
|
||||
[s]::
|
||||
Annotate symbol.
|
||||
|
||||
[S]::
|
||||
Stop annotation, return to full profile display.
|
||||
|
||||
[z]::
|
||||
Toggle event count zeroing across display updates.
|
||||
|
||||
[qQ]::
|
||||
Quit.
|
||||
|
||||
Pressing any unmapped key displays a menu, and prompts for input.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-report[1]
|
158
tools/perf/Documentation/perf-trace.txt
Normal file
158
tools/perf/Documentation/perf-trace.txt
Normal file
|
@ -0,0 +1,158 @@
|
|||
perf-trace(1)
|
||||
=============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-trace - strace inspired tool
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf trace'
|
||||
'perf trace record'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command will show the events associated with the target, initially
|
||||
syscalls, but other system events like pagefaults, task lifetime events,
|
||||
scheduling events, etc.
|
||||
|
||||
This is a live mode tool in addition to working with perf.data files like
|
||||
the other perf tools. Files can be generated using the 'perf record' command
|
||||
but the session needs to include the raw_syscalls events (-e 'raw_syscalls:*').
|
||||
Alternatively, 'perf trace record' can be used as a shortcut to
|
||||
automatically include the raw_syscalls events when writing events to a file.
|
||||
|
||||
The following options apply to perf trace; options to perf trace record are
|
||||
found in the perf record man page.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
-a::
|
||||
--all-cpus::
|
||||
System-wide collection from all CPUs.
|
||||
|
||||
-e::
|
||||
--expr::
|
||||
List of events to show, currently only syscall names.
|
||||
Prefixing with ! shows all syscalls but the ones specified. You may
|
||||
need to escape it.
|
||||
|
||||
-o::
|
||||
--output=::
|
||||
Output file name.
|
||||
|
||||
-p::
|
||||
--pid=::
|
||||
Record events on existing process ID (comma separated list).
|
||||
|
||||
-t::
|
||||
--tid=::
|
||||
Record events on existing thread ID (comma separated list).
|
||||
|
||||
-u::
|
||||
--uid=::
|
||||
Record events in threads owned by uid. Name or number.
|
||||
|
||||
-v::
|
||||
--verbose=::
|
||||
Verbosity level.
|
||||
|
||||
-i::
|
||||
--no-inherit::
|
||||
Child tasks do not inherit counters.
|
||||
|
||||
-m::
|
||||
--mmap-pages=::
|
||||
Number of mmap data pages (must be a power of two) or size
|
||||
specification with appended unit character - B/K/M/G. The
|
||||
size is rounded up to have nearest pages power of two value.
|
||||
|
||||
-C::
|
||||
--cpu::
|
||||
Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a
|
||||
comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
|
||||
In per-thread mode with inheritance mode on (default), Events are captured only when
|
||||
the thread executes on the designated CPUs. Default is to monitor all CPUs.
|
||||
|
||||
--duration:
|
||||
Show only events that had a duration greater than N.M ms.
|
||||
|
||||
--sched:
|
||||
Accrue thread runtime and provide a summary at the end of the session.
|
||||
|
||||
-i
|
||||
--input
|
||||
Process events from a given perf data file.
|
||||
|
||||
-T
|
||||
--time
|
||||
Print full timestamp rather time relative to first sample.
|
||||
|
||||
--comm::
|
||||
Show process COMM right beside its ID, on by default, disable with --no-comm.
|
||||
|
||||
-s::
|
||||
--summary::
|
||||
Show only a summary of syscalls by thread with min, max, and average times
|
||||
(in msec) and relative stddev.
|
||||
|
||||
-S::
|
||||
--with-summary::
|
||||
Show all syscalls followed by a summary by thread with min, max, and
|
||||
average times (in msec) and relative stddev.
|
||||
|
||||
--tool_stats::
|
||||
Show tool stats such as number of times fd->pathname was discovered thru
|
||||
hooking the open syscall return + vfs_getname or via reading /proc/pid/fd, etc.
|
||||
|
||||
-F=[all|min|maj]::
|
||||
--pf=[all|min|maj]::
|
||||
Trace pagefaults. Optionally, you can specify whether you want minor,
|
||||
major or all pagefaults. Default value is maj.
|
||||
|
||||
--syscalls::
|
||||
Trace system calls. This options is enabled by default.
|
||||
|
||||
PAGEFAULTS
|
||||
----------
|
||||
|
||||
When tracing pagefaults, the format of the trace is as follows:
|
||||
|
||||
<min|maj>fault [<ip.symbol>+<ip.offset>] => <addr.dso@addr.offset> (<map type><addr level>).
|
||||
|
||||
- min/maj indicates whether fault event is minor or major;
|
||||
- ip.symbol shows symbol for instruction pointer (the code that generated the
|
||||
fault); if no debug symbols available, perf trace will print raw IP;
|
||||
- addr.dso shows DSO for the faulted address;
|
||||
- map type is either 'd' for non-executable maps or 'x' for executable maps;
|
||||
- addr level is either 'k' for kernel dso or '.' for user dso.
|
||||
|
||||
For symbols resolution you may need to install debugging symbols.
|
||||
|
||||
Please be aware that duration is currently always 0 and doesn't reflect actual
|
||||
time it took for fault to be handled!
|
||||
|
||||
When --verbose specified, perf trace tries to print all available information
|
||||
for both IP and fault address in the form of dso@symbol+offset.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
Trace only major pagefaults:
|
||||
|
||||
$ perf trace --no-syscalls -F
|
||||
|
||||
Trace syscalls, major and minor pagefaults:
|
||||
|
||||
$ perf trace -F all
|
||||
|
||||
1416.547 ( 0.000 ms): python/20235 majfault [CRYPTO_push_info_+0x0] => /lib/x86_64-linux-gnu/libcrypto.so.1.0.0@0x61be0 (x.)
|
||||
|
||||
As you can see, there was major pagefault in python process, from
|
||||
CRYPTO_push_info_ routine which faulted somewhere in libcrypto.so.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-script[1]
|
32
tools/perf/Documentation/perf.txt
Normal file
32
tools/perf/Documentation/perf.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
perf(1)
|
||||
=======
|
||||
|
||||
NAME
|
||||
----
|
||||
perf - Performance analysis tools for Linux
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf' [--version] [--help] [OPTIONS] COMMAND [ARGS]
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
--debug::
|
||||
Setup debug variable (just verbose for now) in value
|
||||
range (0, 10). Use like:
|
||||
--debug verbose # sets verbose = 1
|
||||
--debug verbose=2 # sets verbose = 2
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Performance counters for Linux are a new kernel-based subsystem
|
||||
that provide a framework for all things performance analysis. It
|
||||
covers hardware level (CPU/PMU, Performance Monitoring Unit) features
|
||||
and software features (software counters, tracepoints) as well.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-top[1],
|
||||
linkperf:perf-record[1], linkperf:perf-report[1],
|
||||
linkperf:perf-list[1]
|
29
tools/perf/Documentation/perfconfig.example
Normal file
29
tools/perf/Documentation/perfconfig.example
Normal file
|
@ -0,0 +1,29 @@
|
|||
[colors]
|
||||
|
||||
# These were the old defaults
|
||||
top = red, lightgray
|
||||
medium = green, lightgray
|
||||
normal = black, lightgray
|
||||
selected = lightgray, magenta
|
||||
code = blue, lightgray
|
||||
addr = magenta, lightgray
|
||||
|
||||
[tui]
|
||||
|
||||
# Defaults if linked with libslang
|
||||
report = on
|
||||
annotate = on
|
||||
top = on
|
||||
|
||||
[buildid]
|
||||
|
||||
# Default, disable using /dev/null
|
||||
dir = /root/.debug
|
||||
|
||||
[annotate]
|
||||
|
||||
# Defaults
|
||||
hide_src_code = false
|
||||
use_offset = true
|
||||
jump_arrows = true
|
||||
show_nr_jumps = false
|
42
tools/perf/MANIFEST
Normal file
42
tools/perf/MANIFEST
Normal file
|
@ -0,0 +1,42 @@
|
|||
tools/perf
|
||||
tools/scripts
|
||||
tools/lib/traceevent
|
||||
tools/lib/api
|
||||
tools/lib/symbol/kallsyms.c
|
||||
tools/lib/symbol/kallsyms.h
|
||||
tools/include/asm/bug.h
|
||||
tools/include/linux/compiler.h
|
||||
tools/include/linux/hash.h
|
||||
tools/include/linux/export.h
|
||||
tools/include/linux/types.h
|
||||
include/linux/const.h
|
||||
include/linux/perf_event.h
|
||||
include/linux/rbtree.h
|
||||
include/linux/list.h
|
||||
include/linux/hash.h
|
||||
include/linux/stringify.h
|
||||
lib/rbtree.c
|
||||
include/linux/swab.h
|
||||
arch/*/include/asm/unistd*.h
|
||||
arch/*/include/asm/perf_regs.h
|
||||
arch/*/include/uapi/asm/unistd*.h
|
||||
arch/*/include/uapi/asm/perf_regs.h
|
||||
arch/*/lib/memcpy*.S
|
||||
arch/*/lib/memset*.S
|
||||
include/linux/poison.h
|
||||
include/linux/magic.h
|
||||
include/linux/hw_breakpoint.h
|
||||
include/linux/rbtree_augmented.h
|
||||
include/uapi/linux/perf_event.h
|
||||
include/uapi/linux/const.h
|
||||
include/uapi/linux/swab.h
|
||||
include/uapi/linux/hw_breakpoint.h
|
||||
arch/x86/include/asm/svm.h
|
||||
arch/x86/include/asm/vmx.h
|
||||
arch/x86/include/asm/kvm_host.h
|
||||
arch/x86/include/uapi/asm/svm.h
|
||||
arch/x86/include/uapi/asm/vmx.h
|
||||
arch/x86/include/uapi/asm/kvm.h
|
||||
arch/x86/include/uapi/asm/kvm_perf.h
|
||||
arch/s390/include/uapi/asm/sie.h
|
||||
arch/s390/include/uapi/asm/kvm_perf.h
|
90
tools/perf/Makefile
Normal file
90
tools/perf/Makefile
Normal file
|
@ -0,0 +1,90 @@
|
|||
#
|
||||
# This is a simple wrapper Makefile that calls the main Makefile.perf
|
||||
# with a -j option to do parallel builds
|
||||
#
|
||||
# If you want to invoke the perf build in some non-standard way then
|
||||
# you can use the 'make -f Makefile.perf' method to invoke it.
|
||||
#
|
||||
|
||||
#
|
||||
# Clear out the built-in rules GNU make defines by default (such as .o targets),
|
||||
# so that we pass through all targets to Makefile.perf:
|
||||
#
|
||||
.SUFFIXES:
|
||||
|
||||
#
|
||||
# We don't want to pass along options like -j:
|
||||
#
|
||||
unexport MAKEFLAGS
|
||||
|
||||
#
|
||||
# Do a parallel build with multiple jobs, based on the number of CPUs online
|
||||
# in this system: 'make -j8' on a 8-CPU system, etc.
|
||||
#
|
||||
# (To override it, run 'make JOBS=1' and similar.)
|
||||
#
|
||||
ifeq ($(JOBS),)
|
||||
JOBS := $(shell grep -c ^processor /proc/cpuinfo 2>/dev/null)
|
||||
ifeq ($(JOBS),)
|
||||
JOBS := 1
|
||||
endif
|
||||
endif
|
||||
|
||||
#
|
||||
# Only pass canonical directory names as the output directory:
|
||||
#
|
||||
ifneq ($(O),)
|
||||
FULL_O := $(shell readlink -f $(O) || echo $(O))
|
||||
endif
|
||||
|
||||
#
|
||||
# Only accept the 'DEBUG' variable from the command line:
|
||||
#
|
||||
ifeq ("$(origin DEBUG)", "command line")
|
||||
ifeq ($(DEBUG),)
|
||||
override DEBUG = 0
|
||||
else
|
||||
SET_DEBUG = "DEBUG=$(DEBUG)"
|
||||
endif
|
||||
else
|
||||
override DEBUG = 0
|
||||
endif
|
||||
|
||||
define print_msg
|
||||
@printf ' BUILD: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build\n'
|
||||
endef
|
||||
|
||||
define make
|
||||
@$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) O=$(FULL_O) $(SET_DEBUG) $@
|
||||
endef
|
||||
|
||||
#
|
||||
# Needed if no target specified:
|
||||
# (Except for tags and TAGS targets. The reason is that the
|
||||
# Makefile does not treat tags/TAGS as targets but as files
|
||||
# and thus won't rebuilt them once they are in place.)
|
||||
#
|
||||
all tags TAGS:
|
||||
$(print_msg)
|
||||
$(make)
|
||||
|
||||
#
|
||||
# The clean target is not really parallel, don't print the jobs info:
|
||||
#
|
||||
clean:
|
||||
$(make)
|
||||
|
||||
#
|
||||
# The build-test target is not really parallel, don't print the jobs info:
|
||||
#
|
||||
build-test:
|
||||
@$(MAKE) -f tests/make --no-print-directory
|
||||
|
||||
#
|
||||
# All other targets get passed through:
|
||||
#
|
||||
%:
|
||||
$(print_msg)
|
||||
$(make)
|
||||
|
||||
.PHONY: tags TAGS
|
949
tools/perf/Makefile.perf
Normal file
949
tools/perf/Makefile.perf
Normal file
|
@ -0,0 +1,949 @@
|
|||
include ../scripts/Makefile.include
|
||||
|
||||
# The default target of this Makefile is...
|
||||
all:
|
||||
|
||||
include config/utilities.mak
|
||||
|
||||
# Define V to have a more verbose compile.
|
||||
#
|
||||
# Define VF to have a more verbose feature check output.
|
||||
#
|
||||
# Define O to save output files in a separate directory.
|
||||
#
|
||||
# Define ARCH as name of target architecture if you want cross-builds.
|
||||
#
|
||||
# Define CROSS_COMPILE as prefix name of compiler if you want cross-builds.
|
||||
#
|
||||
# Define NO_LIBPERL to disable perl script extension.
|
||||
#
|
||||
# Define NO_LIBPYTHON to disable python script extension.
|
||||
#
|
||||
# Define PYTHON to point to the python binary if the default
|
||||
# `python' is not correct; for example: PYTHON=python2
|
||||
#
|
||||
# Define PYTHON_CONFIG to point to the python-config binary if
|
||||
# the default `$(PYTHON)-config' is not correct.
|
||||
#
|
||||
# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
|
||||
#
|
||||
# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
|
||||
#
|
||||
# Define LDFLAGS=-static to build a static binary.
|
||||
#
|
||||
# Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds.
|
||||
#
|
||||
# Define NO_DWARF if you do not want debug-info analysis feature at all.
|
||||
#
|
||||
# Define WERROR=0 to disable treating any warnings as errors.
|
||||
#
|
||||
# Define NO_NEWT if you do not want TUI support. (deprecated)
|
||||
#
|
||||
# Define NO_SLANG if you do not want TUI support.
|
||||
#
|
||||
# Define NO_GTK2 if you do not want GTK+ GUI support.
|
||||
#
|
||||
# Define NO_DEMANGLE if you do not want C++ symbol demangling.
|
||||
#
|
||||
# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds)
|
||||
#
|
||||
# Define NO_LIBUNWIND if you do not want libunwind dependency for dwarf
|
||||
# backtrace post unwind.
|
||||
#
|
||||
# Define NO_BACKTRACE if you do not want stack backtrace debug feature
|
||||
#
|
||||
# Define NO_LIBNUMA if you do not want numa perf benchmark
|
||||
#
|
||||
# Define NO_LIBAUDIT if you do not want libaudit support
|
||||
#
|
||||
# Define NO_LIBBIONIC if you do not want bionic support
|
||||
#
|
||||
# Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support
|
||||
# for dwarf backtrace post unwind.
|
||||
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
#$(info Determined 'srctree' to be $(srctree))
|
||||
endif
|
||||
|
||||
ifneq ($(objtree),)
|
||||
#$(info Determined 'objtree' to be $(objtree))
|
||||
endif
|
||||
|
||||
ifneq ($(OUTPUT),)
|
||||
#$(info Determined 'OUTPUT' to be $(OUTPUT))
|
||||
endif
|
||||
|
||||
$(OUTPUT)PERF-VERSION-FILE: ../../.git/HEAD
|
||||
@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
|
||||
@touch $(OUTPUT)PERF-VERSION-FILE
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
AR = $(CROSS_COMPILE)ar
|
||||
PKG_CONFIG = $(CROSS_COMPILE)pkg-config
|
||||
|
||||
RM = rm -f
|
||||
LN = ln -f
|
||||
MKDIR = mkdir
|
||||
FIND = find
|
||||
INSTALL = install
|
||||
FLEX = flex
|
||||
BISON = bison
|
||||
STRIP = strip
|
||||
|
||||
LIB_DIR = $(srctree)/tools/lib/api/
|
||||
TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/
|
||||
|
||||
# include config/Makefile by default and rule out
|
||||
# non-config cases
|
||||
config := 1
|
||||
|
||||
NON_CONFIG_TARGETS := clean TAGS tags cscope help
|
||||
|
||||
ifdef MAKECMDGOALS
|
||||
ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),)
|
||||
config := 0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(config),1)
|
||||
include config/Makefile
|
||||
endif
|
||||
|
||||
export prefix bindir sharedir sysconfdir DESTDIR
|
||||
|
||||
# sparse is architecture-neutral, which means that we need to tell it
|
||||
# explicitly what architecture to check for. Fix this up for yours..
|
||||
SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
|
||||
|
||||
# Guard against environment variables
|
||||
BUILTIN_OBJS =
|
||||
LIB_H =
|
||||
LIB_OBJS =
|
||||
GTK_OBJS =
|
||||
PYRF_OBJS =
|
||||
SCRIPT_SH =
|
||||
|
||||
SCRIPT_SH += perf-archive.sh
|
||||
SCRIPT_SH += perf-with-kcore.sh
|
||||
|
||||
grep-libs = $(filter -l%,$(1))
|
||||
strip-libs = $(filter-out -l%,$(1))
|
||||
|
||||
ifneq ($(OUTPUT),)
|
||||
TE_PATH=$(OUTPUT)
|
||||
ifneq ($(subdir),)
|
||||
LIB_PATH=$(OUTPUT)/../lib/api/
|
||||
else
|
||||
LIB_PATH=$(OUTPUT)
|
||||
endif
|
||||
else
|
||||
TE_PATH=$(TRACE_EVENT_DIR)
|
||||
LIB_PATH=$(LIB_DIR)
|
||||
endif
|
||||
|
||||
LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
|
||||
export LIBTRACEEVENT
|
||||
|
||||
LIBAPIKFS = $(LIB_PATH)libapikfs.a
|
||||
export LIBAPIKFS
|
||||
|
||||
# python extension build directories
|
||||
PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/
|
||||
PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
|
||||
PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/
|
||||
export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP
|
||||
|
||||
python-clean := $(call QUIET_CLEAN, python) $(RM) -r $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so
|
||||
|
||||
PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources)
|
||||
PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBAPIKFS)
|
||||
|
||||
$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
|
||||
$(QUIET_GEN)CFLAGS='$(CFLAGS)' $(PYTHON_WORD) util/setup.py \
|
||||
--quiet build_ext; \
|
||||
mkdir -p $(OUTPUT)python && \
|
||||
cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/
|
||||
#
|
||||
# No Perl scripts right now:
|
||||
#
|
||||
|
||||
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH))
|
||||
|
||||
#
|
||||
# Single 'perf' binary right now:
|
||||
#
|
||||
PROGRAMS += $(OUTPUT)perf
|
||||
|
||||
# what 'all' will build and 'install' will install, in perfexecdir
|
||||
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
|
||||
|
||||
# what 'all' will build but not install in perfexecdir
|
||||
OTHER_PROGRAMS = $(OUTPUT)perf
|
||||
|
||||
# Set paths to tools early so that they can be used for version tests.
|
||||
ifndef SHELL_PATH
|
||||
SHELL_PATH = /bin/sh
|
||||
endif
|
||||
ifndef PERL_PATH
|
||||
PERL_PATH = /usr/bin/perl
|
||||
endif
|
||||
|
||||
export PERL_PATH
|
||||
|
||||
$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
|
||||
$(QUIET_FLEX)$(FLEX) -o $@ --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) util/parse-events.l
|
||||
|
||||
$(OUTPUT)util/parse-events-bison.c: util/parse-events.y
|
||||
$(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c -p parse_events_
|
||||
|
||||
$(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c
|
||||
$(QUIET_FLEX)$(FLEX) -o $@ --header-file=$(OUTPUT)util/pmu-flex.h util/pmu.l
|
||||
|
||||
$(OUTPUT)util/pmu-bison.c: util/pmu.y
|
||||
$(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c -p perf_pmu_
|
||||
|
||||
$(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c
|
||||
$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
|
||||
|
||||
LIB_FILE=$(OUTPUT)libperf.a
|
||||
|
||||
LIB_H += ../lib/symbol/kallsyms.h
|
||||
LIB_H += ../../include/uapi/linux/perf_event.h
|
||||
LIB_H += ../../include/linux/rbtree.h
|
||||
LIB_H += ../../include/linux/list.h
|
||||
LIB_H += ../../include/uapi/linux/const.h
|
||||
LIB_H += ../include/linux/hash.h
|
||||
LIB_H += ../../include/linux/stringify.h
|
||||
LIB_H += util/include/linux/bitmap.h
|
||||
LIB_H += util/include/linux/bitops.h
|
||||
LIB_H += ../include/linux/compiler.h
|
||||
LIB_H += util/include/linux/const.h
|
||||
LIB_H += util/include/linux/ctype.h
|
||||
LIB_H += util/include/linux/kernel.h
|
||||
LIB_H += util/include/linux/list.h
|
||||
LIB_H += ../include/linux/export.h
|
||||
LIB_H += util/include/linux/poison.h
|
||||
LIB_H += util/include/linux/rbtree.h
|
||||
LIB_H += util/include/linux/rbtree_augmented.h
|
||||
LIB_H += util/include/linux/string.h
|
||||
LIB_H += ../include/linux/types.h
|
||||
LIB_H += util/include/linux/linkage.h
|
||||
LIB_H += util/include/asm/asm-offsets.h
|
||||
LIB_H += ../include/asm/bug.h
|
||||
LIB_H += util/include/asm/byteorder.h
|
||||
LIB_H += util/include/asm/hweight.h
|
||||
LIB_H += util/include/asm/swab.h
|
||||
LIB_H += util/include/asm/system.h
|
||||
LIB_H += util/include/asm/uaccess.h
|
||||
LIB_H += util/include/dwarf-regs.h
|
||||
LIB_H += util/include/asm/dwarf2.h
|
||||
LIB_H += util/include/asm/cpufeature.h
|
||||
LIB_H += util/include/asm/unistd_32.h
|
||||
LIB_H += util/include/asm/unistd_64.h
|
||||
LIB_H += perf.h
|
||||
LIB_H += util/annotate.h
|
||||
LIB_H += util/cache.h
|
||||
LIB_H += util/callchain.h
|
||||
LIB_H += util/build-id.h
|
||||
LIB_H += util/debug.h
|
||||
LIB_H += util/pmu.h
|
||||
LIB_H += util/event.h
|
||||
LIB_H += util/evsel.h
|
||||
LIB_H += util/evlist.h
|
||||
LIB_H += util/exec_cmd.h
|
||||
LIB_H += util/levenshtein.h
|
||||
LIB_H += util/machine.h
|
||||
LIB_H += util/map.h
|
||||
LIB_H += util/parse-options.h
|
||||
LIB_H += util/parse-events.h
|
||||
LIB_H += util/quote.h
|
||||
LIB_H += util/util.h
|
||||
LIB_H += util/xyarray.h
|
||||
LIB_H += util/header.h
|
||||
LIB_H += util/help.h
|
||||
LIB_H += util/session.h
|
||||
LIB_H += util/ordered-events.h
|
||||
LIB_H += util/strbuf.h
|
||||
LIB_H += util/strlist.h
|
||||
LIB_H += util/strfilter.h
|
||||
LIB_H += util/svghelper.h
|
||||
LIB_H += util/tool.h
|
||||
LIB_H += util/run-command.h
|
||||
LIB_H += util/sigchain.h
|
||||
LIB_H += util/dso.h
|
||||
LIB_H += util/symbol.h
|
||||
LIB_H += util/color.h
|
||||
LIB_H += util/values.h
|
||||
LIB_H += util/sort.h
|
||||
LIB_H += util/hist.h
|
||||
LIB_H += util/comm.h
|
||||
LIB_H += util/thread.h
|
||||
LIB_H += util/thread_map.h
|
||||
LIB_H += util/trace-event.h
|
||||
LIB_H += util/probe-finder.h
|
||||
LIB_H += util/dwarf-aux.h
|
||||
LIB_H += util/probe-event.h
|
||||
LIB_H += util/pstack.h
|
||||
LIB_H += util/cpumap.h
|
||||
LIB_H += util/top.h
|
||||
LIB_H += $(ARCH_INCLUDE)
|
||||
LIB_H += util/cgroup.h
|
||||
LIB_H += $(LIB_INCLUDE)traceevent/event-parse.h
|
||||
LIB_H += util/target.h
|
||||
LIB_H += util/rblist.h
|
||||
LIB_H += util/intlist.h
|
||||
LIB_H += util/perf_regs.h
|
||||
LIB_H += util/unwind.h
|
||||
LIB_H += util/vdso.h
|
||||
LIB_H += util/tsc.h
|
||||
LIB_H += ui/helpline.h
|
||||
LIB_H += ui/progress.h
|
||||
LIB_H += ui/util.h
|
||||
LIB_H += ui/ui.h
|
||||
LIB_H += util/data.h
|
||||
LIB_H += util/kvm-stat.h
|
||||
|
||||
LIB_OBJS += $(OUTPUT)util/abspath.o
|
||||
LIB_OBJS += $(OUTPUT)util/alias.o
|
||||
LIB_OBJS += $(OUTPUT)util/annotate.o
|
||||
LIB_OBJS += $(OUTPUT)util/build-id.o
|
||||
LIB_OBJS += $(OUTPUT)util/config.o
|
||||
LIB_OBJS += $(OUTPUT)util/ctype.o
|
||||
LIB_OBJS += $(OUTPUT)util/pmu.o
|
||||
LIB_OBJS += $(OUTPUT)util/environment.o
|
||||
LIB_OBJS += $(OUTPUT)util/event.o
|
||||
LIB_OBJS += $(OUTPUT)util/evlist.o
|
||||
LIB_OBJS += $(OUTPUT)util/evsel.o
|
||||
LIB_OBJS += $(OUTPUT)util/exec_cmd.o
|
||||
LIB_OBJS += $(OUTPUT)util/help.o
|
||||
LIB_OBJS += $(OUTPUT)util/kallsyms.o
|
||||
LIB_OBJS += $(OUTPUT)util/levenshtein.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-options.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-events.o
|
||||
LIB_OBJS += $(OUTPUT)util/path.o
|
||||
LIB_OBJS += $(OUTPUT)util/rbtree.o
|
||||
LIB_OBJS += $(OUTPUT)util/bitmap.o
|
||||
LIB_OBJS += $(OUTPUT)util/hweight.o
|
||||
LIB_OBJS += $(OUTPUT)util/run-command.o
|
||||
LIB_OBJS += $(OUTPUT)util/quote.o
|
||||
LIB_OBJS += $(OUTPUT)util/strbuf.o
|
||||
LIB_OBJS += $(OUTPUT)util/string.o
|
||||
LIB_OBJS += $(OUTPUT)util/strlist.o
|
||||
LIB_OBJS += $(OUTPUT)util/strfilter.o
|
||||
LIB_OBJS += $(OUTPUT)util/top.o
|
||||
LIB_OBJS += $(OUTPUT)util/usage.o
|
||||
LIB_OBJS += $(OUTPUT)util/wrapper.o
|
||||
LIB_OBJS += $(OUTPUT)util/sigchain.o
|
||||
LIB_OBJS += $(OUTPUT)util/dso.o
|
||||
LIB_OBJS += $(OUTPUT)util/symbol.o
|
||||
LIB_OBJS += $(OUTPUT)util/symbol-elf.o
|
||||
LIB_OBJS += $(OUTPUT)util/color.o
|
||||
LIB_OBJS += $(OUTPUT)util/pager.o
|
||||
LIB_OBJS += $(OUTPUT)util/header.o
|
||||
LIB_OBJS += $(OUTPUT)util/callchain.o
|
||||
LIB_OBJS += $(OUTPUT)util/values.o
|
||||
LIB_OBJS += $(OUTPUT)util/debug.o
|
||||
LIB_OBJS += $(OUTPUT)util/machine.o
|
||||
LIB_OBJS += $(OUTPUT)util/map.o
|
||||
LIB_OBJS += $(OUTPUT)util/pstack.o
|
||||
LIB_OBJS += $(OUTPUT)util/session.o
|
||||
LIB_OBJS += $(OUTPUT)util/ordered-events.o
|
||||
LIB_OBJS += $(OUTPUT)util/comm.o
|
||||
LIB_OBJS += $(OUTPUT)util/thread.o
|
||||
LIB_OBJS += $(OUTPUT)util/thread_map.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-events-flex.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-events-bison.o
|
||||
LIB_OBJS += $(OUTPUT)util/pmu-flex.o
|
||||
LIB_OBJS += $(OUTPUT)util/pmu-bison.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-read.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-info.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event.o
|
||||
LIB_OBJS += $(OUTPUT)util/svghelper.o
|
||||
LIB_OBJS += $(OUTPUT)util/sort.o
|
||||
LIB_OBJS += $(OUTPUT)util/hist.o
|
||||
LIB_OBJS += $(OUTPUT)util/probe-event.o
|
||||
LIB_OBJS += $(OUTPUT)util/util.o
|
||||
LIB_OBJS += $(OUTPUT)util/xyarray.o
|
||||
LIB_OBJS += $(OUTPUT)util/cpumap.o
|
||||
LIB_OBJS += $(OUTPUT)util/cgroup.o
|
||||
LIB_OBJS += $(OUTPUT)util/target.o
|
||||
LIB_OBJS += $(OUTPUT)util/rblist.o
|
||||
LIB_OBJS += $(OUTPUT)util/intlist.o
|
||||
LIB_OBJS += $(OUTPUT)util/vdso.o
|
||||
LIB_OBJS += $(OUTPUT)util/stat.o
|
||||
LIB_OBJS += $(OUTPUT)util/record.o
|
||||
LIB_OBJS += $(OUTPUT)util/srcline.o
|
||||
LIB_OBJS += $(OUTPUT)util/data.o
|
||||
LIB_OBJS += $(OUTPUT)util/tsc.o
|
||||
LIB_OBJS += $(OUTPUT)util/cloexec.o
|
||||
|
||||
LIB_OBJS += $(OUTPUT)ui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/helpline.o
|
||||
LIB_OBJS += $(OUTPUT)ui/progress.o
|
||||
LIB_OBJS += $(OUTPUT)ui/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/hist.o
|
||||
LIB_OBJS += $(OUTPUT)ui/stdio/hist.o
|
||||
|
||||
LIB_OBJS += $(OUTPUT)arch/common.o
|
||||
|
||||
LIB_OBJS += $(OUTPUT)tests/parse-events.o
|
||||
LIB_OBJS += $(OUTPUT)tests/dso-data.o
|
||||
LIB_OBJS += $(OUTPUT)tests/attr.o
|
||||
LIB_OBJS += $(OUTPUT)tests/vmlinux-kallsyms.o
|
||||
LIB_OBJS += $(OUTPUT)tests/open-syscall.o
|
||||
LIB_OBJS += $(OUTPUT)tests/open-syscall-all-cpus.o
|
||||
LIB_OBJS += $(OUTPUT)tests/open-syscall-tp-fields.o
|
||||
LIB_OBJS += $(OUTPUT)tests/mmap-basic.o
|
||||
LIB_OBJS += $(OUTPUT)tests/perf-record.o
|
||||
LIB_OBJS += $(OUTPUT)tests/rdpmc.o
|
||||
LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o
|
||||
LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o
|
||||
LIB_OBJS += $(OUTPUT)tests/fdarray.o
|
||||
LIB_OBJS += $(OUTPUT)tests/pmu.o
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_common.o
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_link.o
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_filter.o
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_output.o
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_cumulate.o
|
||||
LIB_OBJS += $(OUTPUT)tests/python-use.o
|
||||
LIB_OBJS += $(OUTPUT)tests/bp_signal.o
|
||||
LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o
|
||||
LIB_OBJS += $(OUTPUT)tests/task-exit.o
|
||||
LIB_OBJS += $(OUTPUT)tests/sw-clock.o
|
||||
ifeq ($(ARCH),x86)
|
||||
LIB_OBJS += $(OUTPUT)tests/perf-time-to-tsc.o
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)tests/code-reading.o
|
||||
LIB_OBJS += $(OUTPUT)tests/sample-parsing.o
|
||||
LIB_OBJS += $(OUTPUT)tests/parse-no-sample-id-all.o
|
||||
ifndef NO_DWARF_UNWIND
|
||||
ifeq ($(ARCH),$(filter $(ARCH),x86 arm))
|
||||
LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o
|
||||
endif
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o
|
||||
LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o
|
||||
LIB_OBJS += $(OUTPUT)tests/switch-tracking.o
|
||||
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
|
||||
# Benchmark modules
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o
|
||||
ifeq ($(RAW_ARCH),x86_64)
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/mem-memset-x86-64-asm.o
|
||||
endif
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/mem-memset.o
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/futex-hash.o
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/futex-wake.o
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/futex-requeue.o
|
||||
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-diff.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-help.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-sched.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-buildid-cache.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-list.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-record.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-report.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-stat.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-top.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-script.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-probe.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
|
||||
BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-mem.o
|
||||
|
||||
PERFLIBS = $(LIB_FILE) $(LIBAPIKFS) $(LIBTRACEEVENT)
|
||||
|
||||
# We choose to avoid "if .. else if .. else .. endif endif"
|
||||
# because maintaining the nesting to match is a pain. If
|
||||
# we had "elif" things would have been much nicer...
|
||||
|
||||
-include arch/$(ARCH)/Makefile
|
||||
|
||||
ifneq ($(OUTPUT),)
|
||||
CFLAGS += -I$(OUTPUT)
|
||||
endif
|
||||
|
||||
ifdef NO_LIBELF
|
||||
EXTLIBS := $(filter-out -lelf,$(EXTLIBS))
|
||||
|
||||
# Remove ELF/DWARF dependent codes
|
||||
LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS))
|
||||
LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS))
|
||||
LIB_OBJS := $(filter-out $(OUTPUT)util/probe-event.o,$(LIB_OBJS))
|
||||
LIB_OBJS := $(filter-out $(OUTPUT)util/probe-finder.o,$(LIB_OBJS))
|
||||
|
||||
BUILTIN_OBJS := $(filter-out $(OUTPUT)builtin-probe.o,$(BUILTIN_OBJS))
|
||||
|
||||
# Use minimal symbol handling
|
||||
LIB_OBJS += $(OUTPUT)util/symbol-minimal.o
|
||||
|
||||
else # NO_LIBELF
|
||||
ifndef NO_DWARF
|
||||
LIB_OBJS += $(OUTPUT)util/probe-finder.o
|
||||
LIB_OBJS += $(OUTPUT)util/dwarf-aux.o
|
||||
endif # NO_DWARF
|
||||
endif # NO_LIBELF
|
||||
|
||||
ifndef NO_LIBDW_DWARF_UNWIND
|
||||
LIB_OBJS += $(OUTPUT)util/unwind-libdw.o
|
||||
LIB_H += util/unwind-libdw.h
|
||||
endif
|
||||
|
||||
ifndef NO_LIBUNWIND
|
||||
LIB_OBJS += $(OUTPUT)util/unwind-libunwind.o
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)tests/keep-tracking.o
|
||||
|
||||
ifndef NO_LIBAUDIT
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-trace.o
|
||||
endif
|
||||
|
||||
ifndef NO_SLANG
|
||||
LIB_OBJS += $(OUTPUT)ui/browser.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/hists.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/map.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/scripts.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/header.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/helpline.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/progress.o
|
||||
LIB_H += ui/tui/tui.h
|
||||
LIB_H += ui/browser.h
|
||||
LIB_H += ui/browsers/map.h
|
||||
LIB_H += ui/keysyms.h
|
||||
LIB_H += ui/libslang.h
|
||||
endif
|
||||
|
||||
ifndef NO_GTK2
|
||||
ALL_PROGRAMS += $(OUTPUT)libperf-gtk.so
|
||||
|
||||
GTK_OBJS += $(OUTPUT)ui/gtk/browser.o
|
||||
GTK_OBJS += $(OUTPUT)ui/gtk/hists.o
|
||||
GTK_OBJS += $(OUTPUT)ui/gtk/setup.o
|
||||
GTK_OBJS += $(OUTPUT)ui/gtk/util.o
|
||||
GTK_OBJS += $(OUTPUT)ui/gtk/helpline.o
|
||||
GTK_OBJS += $(OUTPUT)ui/gtk/progress.o
|
||||
GTK_OBJS += $(OUTPUT)ui/gtk/annotate.o
|
||||
|
||||
install-gtk: $(OUTPUT)libperf-gtk.so
|
||||
$(call QUIET_INSTALL, 'GTK UI') \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \
|
||||
$(INSTALL) $(OUTPUT)libperf-gtk.so '$(DESTDIR_SQ)$(libdir_SQ)'
|
||||
endif
|
||||
|
||||
ifndef NO_LIBPERL
|
||||
LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o
|
||||
LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o
|
||||
endif
|
||||
|
||||
ifndef NO_LIBPYTHON
|
||||
LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o
|
||||
LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o
|
||||
endif
|
||||
|
||||
ifeq ($(NO_PERF_REGS),0)
|
||||
ifeq ($(ARCH),x86)
|
||||
LIB_H += arch/x86/include/perf_regs.h
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)util/perf_regs.o
|
||||
endif
|
||||
|
||||
ifndef NO_LIBNUMA
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/numa.o
|
||||
endif
|
||||
|
||||
ifdef ASCIIDOC8
|
||||
export ASCIIDOC8
|
||||
endif
|
||||
|
||||
LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive -Wl,--start-group $(EXTLIBS) -Wl,--end-group
|
||||
|
||||
export INSTALL SHELL_PATH
|
||||
|
||||
### Build rules
|
||||
|
||||
SHELL = $(SHELL_PATH)
|
||||
|
||||
all: shell_compatibility_test $(ALL_PROGRAMS) $(LANG_BINDINGS) $(OTHER_PROGRAMS)
|
||||
|
||||
please_set_SHELL_PATH_to_a_more_modern_shell:
|
||||
@$$(:)
|
||||
|
||||
shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
|
||||
|
||||
strip: $(PROGRAMS) $(OUTPUT)perf
|
||||
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf
|
||||
|
||||
$(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -include $(OUTPUT)PERF-VERSION-FILE \
|
||||
'-DPERF_HTML_PATH="$(htmldir_SQ)"' \
|
||||
$(CFLAGS) -c $(filter %.c,$^) -o $@
|
||||
|
||||
$(OUTPUT)perf: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS)
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(OUTPUT)perf.o \
|
||||
$(BUILTIN_OBJS) $(LIBS) -o $@
|
||||
|
||||
$(GTK_OBJS): $(OUTPUT)%.o: %.c $(LIB_H)
|
||||
$(QUIET_CC)$(CC) -o $@ -c -fPIC $(CFLAGS) $(GTK_CFLAGS) $<
|
||||
|
||||
$(OUTPUT)libperf-gtk.so: $(GTK_OBJS) $(PERFLIBS)
|
||||
$(QUIET_LINK)$(CC) -o $@ -shared $(LDFLAGS) $(filter %.o,$^) $(GTK_LIBS)
|
||||
|
||||
$(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \
|
||||
'-DPERF_HTML_PATH="$(htmldir_SQ)"' \
|
||||
'-DPERF_MAN_PATH="$(mandir_SQ)"' \
|
||||
'-DPERF_INFO_PATH="$(infodir_SQ)"' $<
|
||||
|
||||
$(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \
|
||||
'-DPERF_HTML_PATH="$(htmldir_SQ)"' \
|
||||
'-DPERF_MAN_PATH="$(mandir_SQ)"' \
|
||||
'-DPERF_INFO_PATH="$(infodir_SQ)"' $<
|
||||
|
||||
$(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt
|
||||
|
||||
$(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt)
|
||||
$(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@
|
||||
|
||||
$(SCRIPTS) : % : %.sh
|
||||
$(QUIET_GEN)$(INSTALL) '$@.sh' '$(OUTPUT)$@'
|
||||
|
||||
# These can record PERF_VERSION
|
||||
$(OUTPUT)perf.o perf.spec \
|
||||
$(SCRIPTS) \
|
||||
: $(OUTPUT)PERF-VERSION-FILE
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
#
|
||||
# If a target does not match any of the later rules then prefix it by $(OUTPUT)
|
||||
# This makes targets like 'make O=/tmp/perf perf.o' work in a natural way.
|
||||
#
|
||||
ifneq ($(OUTPUT),)
|
||||
%.o: $(OUTPUT)%.o
|
||||
@echo " # Redirected target $@ => $(OUTPUT)$@"
|
||||
util/%.o: $(OUTPUT)util/%.o
|
||||
@echo " # Redirected target $@ => $(OUTPUT)$@"
|
||||
bench/%.o: $(OUTPUT)bench/%.o
|
||||
@echo " # Redirected target $@ => $(OUTPUT)$@"
|
||||
tests/%.o: $(OUTPUT)tests/%.o
|
||||
@echo " # Redirected target $@ => $(OUTPUT)$@"
|
||||
endif
|
||||
|
||||
# These two need to be here so that when O= is not used they take precedence
|
||||
# over the general rule for .o
|
||||
|
||||
$(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(CFLAGS) -w $<
|
||||
|
||||
$(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w $<
|
||||
|
||||
$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $<
|
||||
$(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -E $(CFLAGS) $<
|
||||
$(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -S $(CFLAGS) $<
|
||||
$(OUTPUT)%.o: %.S
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $<
|
||||
$(OUTPUT)%.s: %.S
|
||||
$(QUIET_CC)$(CC) -o $@ -E $(CFLAGS) $<
|
||||
|
||||
$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \
|
||||
'-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \
|
||||
'-DPREFIX="$(prefix_SQ)"' \
|
||||
$<
|
||||
|
||||
$(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \
|
||||
'-DBINDIR="$(bindir_SQ)"' -DPYTHON='"$(PYTHON_WORD)"' \
|
||||
$<
|
||||
|
||||
$(OUTPUT)tests/python-use.o: tests/python-use.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \
|
||||
-DPYTHONPATH='"$(OUTPUT)python"' \
|
||||
-DPYTHON='"$(PYTHON_WORD)"' \
|
||||
$<
|
||||
|
||||
$(OUTPUT)tests/dwarf-unwind.o: tests/dwarf-unwind.c
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -fno-optimize-sibling-calls $<
|
||||
|
||||
$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
|
||||
$(OUTPUT)ui/setup.o: ui/setup.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DLIBDIR='"$(libdir_SQ)"' $<
|
||||
|
||||
$(OUTPUT)ui/browser.o: ui/browser.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $<
|
||||
|
||||
$(OUTPUT)ui/browsers/annotate.o: ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $<
|
||||
|
||||
$(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $<
|
||||
|
||||
$(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $<
|
||||
|
||||
$(OUTPUT)ui/browsers/scripts.o: ui/browsers/scripts.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $<
|
||||
|
||||
$(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $<
|
||||
|
||||
$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
|
||||
$(OUTPUT)util/parse-events.o: util/parse-events.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-redundant-decls $<
|
||||
|
||||
$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-undef -Wno-switch-default $<
|
||||
|
||||
$(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs -Wno-undef -Wno-switch-default $<
|
||||
|
||||
$(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
|
||||
|
||||
$(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $<
|
||||
|
||||
$(OUTPUT)perf-%: %.o $(PERFLIBS)
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
|
||||
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
|
||||
$(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
|
||||
|
||||
# we compile into subdirectories. if the target directory is not the source directory, they might not exists. So
|
||||
# we depend the various files onto their directories.
|
||||
DIRECTORY_DEPS = $(LIB_OBJS) $(BUILTIN_OBJS) $(GTK_OBJS)
|
||||
DIRECTORY_DEPS += $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h
|
||||
# no need to add flex objects, because they depend on bison ones
|
||||
DIRECTORY_DEPS += $(OUTPUT)util/parse-events-bison.c
|
||||
DIRECTORY_DEPS += $(OUTPUT)util/pmu-bison.c
|
||||
|
||||
OUTPUT_DIRECTORIES := $(sort $(dir $(DIRECTORY_DEPS)))
|
||||
|
||||
$(DIRECTORY_DEPS): | $(OUTPUT_DIRECTORIES)
|
||||
# In the second step, we make a rule to actually create these directories
|
||||
$(OUTPUT_DIRECTORIES):
|
||||
$(QUIET_MKDIR)$(MKDIR) -p $@ 2>/dev/null
|
||||
|
||||
$(LIB_FILE): $(LIB_OBJS)
|
||||
$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
|
||||
|
||||
# libtraceevent.a
|
||||
TE_SOURCES = $(wildcard $(TRACE_EVENT_DIR)*.[ch])
|
||||
|
||||
LIBTRACEEVENT_FLAGS = $(QUIET_SUBDIR1) O=$(OUTPUT)
|
||||
LIBTRACEEVENT_FLAGS += CFLAGS="-g -Wall $(EXTRA_CFLAGS)"
|
||||
LIBTRACEEVENT_FLAGS += plugin_dir=$(plugindir_SQ)
|
||||
|
||||
$(LIBTRACEEVENT): $(TE_SOURCES) $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) libtraceevent.a plugins
|
||||
|
||||
$(LIBTRACEEVENT)-clean:
|
||||
$(call QUIET_CLEAN, libtraceevent)
|
||||
@$(MAKE) -C $(TRACE_EVENT_DIR) O=$(OUTPUT) clean >/dev/null
|
||||
|
||||
install-traceevent-plugins: $(LIBTRACEEVENT)
|
||||
$(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) install_plugins
|
||||
|
||||
LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch] $(LIB_PATH)fd/*.[ch])
|
||||
|
||||
# if subdir is set, we've been called from above so target has been built
|
||||
# already
|
||||
$(LIBAPIKFS): $(LIBAPIKFS_SOURCES)
|
||||
ifeq ($(subdir),)
|
||||
$(QUIET_SUBDIR0)$(LIB_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libapikfs.a
|
||||
endif
|
||||
|
||||
$(LIBAPIKFS)-clean:
|
||||
ifeq ($(subdir),)
|
||||
$(call QUIET_CLEAN, libapikfs)
|
||||
@$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null
|
||||
endif
|
||||
|
||||
help:
|
||||
@echo 'Perf make targets:'
|
||||
@echo ' doc - make *all* documentation (see below)'
|
||||
@echo ' man - make manpage documentation (access with man <foo>)'
|
||||
@echo ' html - make html documentation'
|
||||
@echo ' info - make GNU info documentation (access with info <foo>)'
|
||||
@echo ' pdf - make pdf documentation'
|
||||
@echo ' TAGS - use etags to make tag information for source browsing'
|
||||
@echo ' tags - use ctags to make tag information for source browsing'
|
||||
@echo ' cscope - use cscope to make interactive browsing database'
|
||||
@echo ''
|
||||
@echo 'Perf install targets:'
|
||||
@echo ' NOTE: documentation build requires asciidoc, xmlto packages to be installed'
|
||||
@echo ' HINT: use "prefix" or "DESTDIR" to install to a particular'
|
||||
@echo ' path like "make prefix=/usr/local install install-doc"'
|
||||
@echo ' install - install compiled binaries'
|
||||
@echo ' install-doc - install *all* documentation'
|
||||
@echo ' install-man - install manpage documentation'
|
||||
@echo ' install-html - install html documentation'
|
||||
@echo ' install-info - install GNU info documentation'
|
||||
@echo ' install-pdf - install pdf documentation'
|
||||
@echo ''
|
||||
@echo ' quick-install-doc - alias for quick-install-man'
|
||||
@echo ' quick-install-man - install the documentation quickly'
|
||||
@echo ' quick-install-html - install the html documentation quickly'
|
||||
@echo ''
|
||||
@echo 'Perf maintainer targets:'
|
||||
@echo ' clean - clean all binary objects and build output'
|
||||
|
||||
|
||||
DOC_TARGETS := doc man html info pdf
|
||||
|
||||
INSTALL_DOC_TARGETS := $(patsubst %,install-%,$(DOC_TARGETS)) try-install-man
|
||||
INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
|
||||
|
||||
# 'make doc' should call 'make -C Documentation all'
|
||||
$(DOC_TARGETS):
|
||||
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all)
|
||||
|
||||
TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol
|
||||
TAG_FILES= ../../include/uapi/linux/perf_event.h
|
||||
|
||||
TAGS:
|
||||
$(QUIET_GEN)$(RM) TAGS; \
|
||||
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs etags -a $(TAG_FILES)
|
||||
|
||||
tags:
|
||||
$(QUIET_GEN)$(RM) tags; \
|
||||
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs ctags -a $(TAG_FILES)
|
||||
|
||||
cscope:
|
||||
$(QUIET_GEN)$(RM) cscope*; \
|
||||
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs cscope -b $(TAG_FILES)
|
||||
|
||||
### Detect prefix changes
|
||||
TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):\
|
||||
$(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ):$(plugindir_SQ)
|
||||
|
||||
$(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS
|
||||
@FLAGS='$(TRACK_CFLAGS)'; \
|
||||
if test x"$$FLAGS" != x"`cat $(OUTPUT)PERF-CFLAGS 2>/dev/null`" ; then \
|
||||
echo 1>&2 " FLAGS: * new build flags or prefix"; \
|
||||
echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \
|
||||
fi
|
||||
|
||||
### Testing rules
|
||||
|
||||
# GNU make supports exporting all variables by "export" without parameters.
|
||||
# However, the environment gets quite big, and some programs have problems
|
||||
# with that.
|
||||
|
||||
check: $(OUTPUT)common-cmds.h
|
||||
if sparse; \
|
||||
then \
|
||||
for i in *.c */*.c; \
|
||||
do \
|
||||
sparse $(CFLAGS) $(SPARSE_FLAGS) $$i || exit; \
|
||||
done; \
|
||||
else \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
### Installation rules
|
||||
|
||||
install-gtk:
|
||||
|
||||
install-bin: all install-gtk
|
||||
$(call QUIET_INSTALL, binaries) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \
|
||||
$(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \
|
||||
$(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace'
|
||||
$(call QUIET_INSTALL, libexec) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
$(call QUIET_INSTALL, perf-archive) \
|
||||
$(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
$(call QUIET_INSTALL, perf-with-kcore) \
|
||||
$(INSTALL) $(OUTPUT)perf-with-kcore -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
ifndef NO_LIBPERL
|
||||
$(call QUIET_INSTALL, perl-scripts) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \
|
||||
$(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \
|
||||
$(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl'; \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'; \
|
||||
$(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
|
||||
endif
|
||||
ifndef NO_LIBPYTHON
|
||||
$(call QUIET_INSTALL, python-scripts) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace'; \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'; \
|
||||
$(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace'; \
|
||||
$(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'; \
|
||||
$(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
|
||||
endif
|
||||
$(call QUIET_INSTALL, perf_completion-script) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'; \
|
||||
$(INSTALL) perf-completion.sh '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf'
|
||||
$(call QUIET_INSTALL, tests) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
|
||||
$(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \
|
||||
$(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'
|
||||
|
||||
install: install-bin try-install-man install-traceevent-plugins
|
||||
|
||||
install-python_ext:
|
||||
$(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)'
|
||||
|
||||
# 'make install-doc' should call 'make -C Documentation install'
|
||||
$(INSTALL_DOC_TARGETS):
|
||||
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:-doc=)
|
||||
|
||||
### Cleaning rules
|
||||
|
||||
#
|
||||
# This is here, not in config/Makefile, because config/Makefile does
|
||||
# not get included for the clean target:
|
||||
#
|
||||
config-clean:
|
||||
$(call QUIET_CLEAN, config)
|
||||
@$(MAKE) -C config/feature-checks clean >/dev/null
|
||||
|
||||
clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean
|
||||
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS)
|
||||
$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf
|
||||
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
|
||||
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
|
||||
$(python-clean)
|
||||
|
||||
#
|
||||
# Trick: if ../../.git does not exist - we are building out of tree for example,
|
||||
# then force version regeneration:
|
||||
#
|
||||
ifeq ($(wildcard ../../.git/HEAD),)
|
||||
GIT-HEAD-PHONY = ../../.git/HEAD
|
||||
else
|
||||
GIT-HEAD-PHONY =
|
||||
endif
|
||||
|
||||
.PHONY: all install clean config-clean strip install-gtk
|
||||
.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
|
||||
.PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope .FORCE-PERF-CFLAGS
|
||||
|
14
tools/perf/arch/arm/Makefile
Normal file
14
tools/perf/arch/arm/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||
endif
|
||||
ifndef NO_LIBUNWIND
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libunwind.o
|
||||
endif
|
||||
ifndef NO_LIBDW_DWARF_UNWIND
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libdw.o
|
||||
endif
|
||||
ifndef NO_DWARF_UNWIND
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/regs_load.o
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/dwarf-unwind.o
|
||||
endif
|
59
tools/perf/arch/arm/include/perf_regs.h
Normal file
59
tools/perf/arch/arm/include/perf_regs.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
#ifndef ARCH_PERF_REGS_H
|
||||
#define ARCH_PERF_REGS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/perf_regs.h>
|
||||
|
||||
void perf_regs_load(u64 *regs);
|
||||
|
||||
#define PERF_REGS_MASK ((1ULL << PERF_REG_ARM_MAX) - 1)
|
||||
#define PERF_REGS_MAX PERF_REG_ARM_MAX
|
||||
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32
|
||||
|
||||
#define PERF_REG_IP PERF_REG_ARM_PC
|
||||
#define PERF_REG_SP PERF_REG_ARM_SP
|
||||
|
||||
static inline const char *perf_reg_name(int id)
|
||||
{
|
||||
switch (id) {
|
||||
case PERF_REG_ARM_R0:
|
||||
return "r0";
|
||||
case PERF_REG_ARM_R1:
|
||||
return "r1";
|
||||
case PERF_REG_ARM_R2:
|
||||
return "r2";
|
||||
case PERF_REG_ARM_R3:
|
||||
return "r3";
|
||||
case PERF_REG_ARM_R4:
|
||||
return "r4";
|
||||
case PERF_REG_ARM_R5:
|
||||
return "r5";
|
||||
case PERF_REG_ARM_R6:
|
||||
return "r6";
|
||||
case PERF_REG_ARM_R7:
|
||||
return "r7";
|
||||
case PERF_REG_ARM_R8:
|
||||
return "r8";
|
||||
case PERF_REG_ARM_R9:
|
||||
return "r9";
|
||||
case PERF_REG_ARM_R10:
|
||||
return "r10";
|
||||
case PERF_REG_ARM_FP:
|
||||
return "fp";
|
||||
case PERF_REG_ARM_IP:
|
||||
return "ip";
|
||||
case PERF_REG_ARM_SP:
|
||||
return "sp";
|
||||
case PERF_REG_ARM_LR:
|
||||
return "lr";
|
||||
case PERF_REG_ARM_PC:
|
||||
return "pc";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* ARCH_PERF_REGS_H */
|
61
tools/perf/arch/arm/tests/dwarf-unwind.c
Normal file
61
tools/perf/arch/arm/tests/dwarf-unwind.c
Normal file
|
@ -0,0 +1,61 @@
|
|||
#include <string.h>
|
||||
#include "perf_regs.h"
|
||||
#include "thread.h"
|
||||
#include "map.h"
|
||||
#include "event.h"
|
||||
#include "debug.h"
|
||||
#include "tests/tests.h"
|
||||
|
||||
#define STACK_SIZE 8192
|
||||
|
||||
static int sample_ustack(struct perf_sample *sample,
|
||||
struct thread *thread, u64 *regs)
|
||||
{
|
||||
struct stack_dump *stack = &sample->user_stack;
|
||||
struct map *map;
|
||||
unsigned long sp;
|
||||
u64 stack_size, *buf;
|
||||
|
||||
buf = malloc(STACK_SIZE);
|
||||
if (!buf) {
|
||||
pr_debug("failed to allocate sample uregs data\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sp = (unsigned long) regs[PERF_REG_ARM_SP];
|
||||
|
||||
map = map_groups__find(thread->mg, MAP__VARIABLE, (u64) sp);
|
||||
if (!map) {
|
||||
pr_debug("failed to get stack map\n");
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stack_size = map->end - sp;
|
||||
stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size;
|
||||
|
||||
memcpy(buf, (void *) sp, stack_size);
|
||||
stack->data = (char *) buf;
|
||||
stack->size = stack_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test__arch_unwind_sample(struct perf_sample *sample,
|
||||
struct thread *thread)
|
||||
{
|
||||
struct regs_dump *regs = &sample->user_regs;
|
||||
u64 *buf;
|
||||
|
||||
buf = calloc(1, sizeof(u64) * PERF_REGS_MAX);
|
||||
if (!buf) {
|
||||
pr_debug("failed to allocate sample uregs data\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
perf_regs_load(buf);
|
||||
regs->abi = PERF_SAMPLE_REGS_ABI;
|
||||
regs->regs = buf;
|
||||
regs->mask = PERF_REGS_MASK;
|
||||
|
||||
return sample_ustack(sample, thread, buf);
|
||||
}
|
58
tools/perf/arch/arm/tests/regs_load.S
Normal file
58
tools/perf/arch/arm/tests/regs_load.S
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include <linux/linkage.h>
|
||||
|
||||
#define R0 0x00
|
||||
#define R1 0x08
|
||||
#define R2 0x10
|
||||
#define R3 0x18
|
||||
#define R4 0x20
|
||||
#define R5 0x28
|
||||
#define R6 0x30
|
||||
#define R7 0x38
|
||||
#define R8 0x40
|
||||
#define R9 0x48
|
||||
#define SL 0x50
|
||||
#define FP 0x58
|
||||
#define IP 0x60
|
||||
#define SP 0x68
|
||||
#define LR 0x70
|
||||
#define PC 0x78
|
||||
|
||||
/*
|
||||
* Implementation of void perf_regs_load(u64 *regs);
|
||||
*
|
||||
* This functions fills in the 'regs' buffer from the actual registers values,
|
||||
* in the way the perf built-in unwinding test expects them:
|
||||
* - the PC at the time at the call to this function. Since this function
|
||||
* is called using a bl instruction, the PC value is taken from LR.
|
||||
* The built-in unwinding test then unwinds the call stack from the dwarf
|
||||
* information in unwind__get_entries.
|
||||
*
|
||||
* Notes:
|
||||
* - the 8 bytes stride in the registers offsets comes from the fact
|
||||
* that the registers are stored in an u64 array (u64 *regs),
|
||||
* - the regs buffer needs to be zeroed before the call to this function,
|
||||
* in this case using a calloc in dwarf-unwind.c.
|
||||
*/
|
||||
|
||||
.text
|
||||
.type perf_regs_load,%function
|
||||
ENTRY(perf_regs_load)
|
||||
str r0, [r0, #R0]
|
||||
str r1, [r0, #R1]
|
||||
str r2, [r0, #R2]
|
||||
str r3, [r0, #R3]
|
||||
str r4, [r0, #R4]
|
||||
str r5, [r0, #R5]
|
||||
str r6, [r0, #R6]
|
||||
str r7, [r0, #R7]
|
||||
str r8, [r0, #R8]
|
||||
str r9, [r0, #R9]
|
||||
str sl, [r0, #SL]
|
||||
str fp, [r0, #FP]
|
||||
str ip, [r0, #IP]
|
||||
str sp, [r0, #SP]
|
||||
str lr, [r0, #LR]
|
||||
str lr, [r0, #PC] // store pc as lr in order to skip the call
|
||||
// to this function
|
||||
mov pc, lr
|
||||
ENDPROC(perf_regs_load)
|
64
tools/perf/arch/arm/util/dwarf-regs.c
Normal file
64
tools/perf/arch/arm/util/dwarf-regs.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2010 Will Deacon, ARM Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
struct pt_regs_dwarfnum {
|
||||
const char *name;
|
||||
unsigned int dwarfnum;
|
||||
};
|
||||
|
||||
#define STR(s) #s
|
||||
#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
|
||||
#define GPR_DWARFNUM_NAME(num) \
|
||||
{.name = STR(%r##num), .dwarfnum = num}
|
||||
#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
|
||||
|
||||
/*
|
||||
* Reference:
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040a/IHI0040A_aadwarf.pdf
|
||||
*/
|
||||
static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
|
||||
GPR_DWARFNUM_NAME(0),
|
||||
GPR_DWARFNUM_NAME(1),
|
||||
GPR_DWARFNUM_NAME(2),
|
||||
GPR_DWARFNUM_NAME(3),
|
||||
GPR_DWARFNUM_NAME(4),
|
||||
GPR_DWARFNUM_NAME(5),
|
||||
GPR_DWARFNUM_NAME(6),
|
||||
GPR_DWARFNUM_NAME(7),
|
||||
GPR_DWARFNUM_NAME(8),
|
||||
GPR_DWARFNUM_NAME(9),
|
||||
GPR_DWARFNUM_NAME(10),
|
||||
REG_DWARFNUM_NAME("%fp", 11),
|
||||
REG_DWARFNUM_NAME("%ip", 12),
|
||||
REG_DWARFNUM_NAME("%sp", 13),
|
||||
REG_DWARFNUM_NAME("%lr", 14),
|
||||
REG_DWARFNUM_NAME("%pc", 15),
|
||||
REG_DWARFNUM_END,
|
||||
};
|
||||
|
||||
/**
|
||||
* get_arch_regstr() - lookup register name from it's DWARF register number
|
||||
* @n: the DWARF register number
|
||||
*
|
||||
* get_arch_regstr() returns the name of the register in struct
|
||||
* regdwarfnum_table from it's DWARF register number. If the register is not
|
||||
* found in the table, this returns NULL;
|
||||
*/
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
for (roff = regdwarfnum_table; roff->name != NULL; roff++)
|
||||
if (roff->dwarfnum == n)
|
||||
return roff->name;
|
||||
return NULL;
|
||||
}
|
36
tools/perf/arch/arm/util/unwind-libdw.c
Normal file
36
tools/perf/arch/arm/util/unwind-libdw.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include <elfutils/libdwfl.h>
|
||||
#include "../../util/unwind-libdw.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = &ui->sample->user_regs;
|
||||
Dwarf_Word dwarf_regs[PERF_REG_ARM_MAX];
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_ARM_##r); \
|
||||
val; \
|
||||
})
|
||||
|
||||
dwarf_regs[0] = REG(R0);
|
||||
dwarf_regs[1] = REG(R1);
|
||||
dwarf_regs[2] = REG(R2);
|
||||
dwarf_regs[3] = REG(R3);
|
||||
dwarf_regs[4] = REG(R4);
|
||||
dwarf_regs[5] = REG(R5);
|
||||
dwarf_regs[6] = REG(R6);
|
||||
dwarf_regs[7] = REG(R7);
|
||||
dwarf_regs[8] = REG(R8);
|
||||
dwarf_regs[9] = REG(R9);
|
||||
dwarf_regs[10] = REG(R10);
|
||||
dwarf_regs[11] = REG(FP);
|
||||
dwarf_regs[12] = REG(IP);
|
||||
dwarf_regs[13] = REG(SP);
|
||||
dwarf_regs[14] = REG(LR);
|
||||
dwarf_regs[15] = REG(PC);
|
||||
|
||||
return dwfl_thread_state_registers(thread, 0, PERF_REG_ARM_MAX,
|
||||
dwarf_regs);
|
||||
}
|
49
tools/perf/arch/arm/util/unwind-libunwind.c
Normal file
49
tools/perf/arch/arm/util/unwind-libunwind.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <libunwind.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/unwind.h"
|
||||
#include "../../util/debug.h"
|
||||
|
||||
int libunwind__arch_reg_id(int regnum)
|
||||
{
|
||||
switch (regnum) {
|
||||
case UNW_ARM_R0:
|
||||
return PERF_REG_ARM_R0;
|
||||
case UNW_ARM_R1:
|
||||
return PERF_REG_ARM_R1;
|
||||
case UNW_ARM_R2:
|
||||
return PERF_REG_ARM_R2;
|
||||
case UNW_ARM_R3:
|
||||
return PERF_REG_ARM_R3;
|
||||
case UNW_ARM_R4:
|
||||
return PERF_REG_ARM_R4;
|
||||
case UNW_ARM_R5:
|
||||
return PERF_REG_ARM_R5;
|
||||
case UNW_ARM_R6:
|
||||
return PERF_REG_ARM_R6;
|
||||
case UNW_ARM_R7:
|
||||
return PERF_REG_ARM_R7;
|
||||
case UNW_ARM_R8:
|
||||
return PERF_REG_ARM_R8;
|
||||
case UNW_ARM_R9:
|
||||
return PERF_REG_ARM_R9;
|
||||
case UNW_ARM_R10:
|
||||
return PERF_REG_ARM_R10;
|
||||
case UNW_ARM_R11:
|
||||
return PERF_REG_ARM_FP;
|
||||
case UNW_ARM_R12:
|
||||
return PERF_REG_ARM_IP;
|
||||
case UNW_ARM_R13:
|
||||
return PERF_REG_ARM_SP;
|
||||
case UNW_ARM_R14:
|
||||
return PERF_REG_ARM_LR;
|
||||
case UNW_ARM_R15:
|
||||
return PERF_REG_ARM_PC;
|
||||
default:
|
||||
pr_err("unwind: invalid reg id %d\n", regnum);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
7
tools/perf/arch/arm64/Makefile
Normal file
7
tools/perf/arch/arm64/Makefile
Normal file
|
@ -0,0 +1,7 @@
|
|||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||
endif
|
||||
ifndef NO_LIBUNWIND
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libunwind.o
|
||||
endif
|
90
tools/perf/arch/arm64/include/perf_regs.h
Normal file
90
tools/perf/arch/arm64/include/perf_regs.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
#ifndef ARCH_PERF_REGS_H
|
||||
#define ARCH_PERF_REGS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/perf_regs.h>
|
||||
|
||||
#define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1)
|
||||
#define PERF_REGS_MAX PERF_REG_ARM64_MAX
|
||||
|
||||
#define PERF_REG_IP PERF_REG_ARM64_PC
|
||||
#define PERF_REG_SP PERF_REG_ARM64_SP
|
||||
|
||||
static inline const char *perf_reg_name(int id)
|
||||
{
|
||||
switch (id) {
|
||||
case PERF_REG_ARM64_X0:
|
||||
return "x0";
|
||||
case PERF_REG_ARM64_X1:
|
||||
return "x1";
|
||||
case PERF_REG_ARM64_X2:
|
||||
return "x2";
|
||||
case PERF_REG_ARM64_X3:
|
||||
return "x3";
|
||||
case PERF_REG_ARM64_X4:
|
||||
return "x4";
|
||||
case PERF_REG_ARM64_X5:
|
||||
return "x5";
|
||||
case PERF_REG_ARM64_X6:
|
||||
return "x6";
|
||||
case PERF_REG_ARM64_X7:
|
||||
return "x7";
|
||||
case PERF_REG_ARM64_X8:
|
||||
return "x8";
|
||||
case PERF_REG_ARM64_X9:
|
||||
return "x9";
|
||||
case PERF_REG_ARM64_X10:
|
||||
return "x10";
|
||||
case PERF_REG_ARM64_X11:
|
||||
return "x11";
|
||||
case PERF_REG_ARM64_X12:
|
||||
return "x12";
|
||||
case PERF_REG_ARM64_X13:
|
||||
return "x13";
|
||||
case PERF_REG_ARM64_X14:
|
||||
return "x14";
|
||||
case PERF_REG_ARM64_X15:
|
||||
return "x15";
|
||||
case PERF_REG_ARM64_X16:
|
||||
return "x16";
|
||||
case PERF_REG_ARM64_X17:
|
||||
return "x17";
|
||||
case PERF_REG_ARM64_X18:
|
||||
return "x18";
|
||||
case PERF_REG_ARM64_X19:
|
||||
return "x19";
|
||||
case PERF_REG_ARM64_X20:
|
||||
return "x20";
|
||||
case PERF_REG_ARM64_X21:
|
||||
return "x21";
|
||||
case PERF_REG_ARM64_X22:
|
||||
return "x22";
|
||||
case PERF_REG_ARM64_X23:
|
||||
return "x23";
|
||||
case PERF_REG_ARM64_X24:
|
||||
return "x24";
|
||||
case PERF_REG_ARM64_X25:
|
||||
return "x25";
|
||||
case PERF_REG_ARM64_X26:
|
||||
return "x26";
|
||||
case PERF_REG_ARM64_X27:
|
||||
return "x27";
|
||||
case PERF_REG_ARM64_X28:
|
||||
return "x28";
|
||||
case PERF_REG_ARM64_X29:
|
||||
return "x29";
|
||||
case PERF_REG_ARM64_SP:
|
||||
return "sp";
|
||||
case PERF_REG_ARM64_LR:
|
||||
return "lr";
|
||||
case PERF_REG_ARM64_PC:
|
||||
return "pc";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* ARCH_PERF_REGS_H */
|
80
tools/perf/arch/arm64/util/dwarf-regs.c
Normal file
80
tools/perf/arch/arm64/util/dwarf-regs.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2010 Will Deacon, ARM Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
struct pt_regs_dwarfnum {
|
||||
const char *name;
|
||||
unsigned int dwarfnum;
|
||||
};
|
||||
|
||||
#define STR(s) #s
|
||||
#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
|
||||
#define GPR_DWARFNUM_NAME(num) \
|
||||
{.name = STR(%x##num), .dwarfnum = num}
|
||||
#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
|
||||
|
||||
/*
|
||||
* Reference:
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.ihi0057b/IHI0057B_aadwarf64.pdf
|
||||
*/
|
||||
static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
|
||||
GPR_DWARFNUM_NAME(0),
|
||||
GPR_DWARFNUM_NAME(1),
|
||||
GPR_DWARFNUM_NAME(2),
|
||||
GPR_DWARFNUM_NAME(3),
|
||||
GPR_DWARFNUM_NAME(4),
|
||||
GPR_DWARFNUM_NAME(5),
|
||||
GPR_DWARFNUM_NAME(6),
|
||||
GPR_DWARFNUM_NAME(7),
|
||||
GPR_DWARFNUM_NAME(8),
|
||||
GPR_DWARFNUM_NAME(9),
|
||||
GPR_DWARFNUM_NAME(10),
|
||||
GPR_DWARFNUM_NAME(11),
|
||||
GPR_DWARFNUM_NAME(12),
|
||||
GPR_DWARFNUM_NAME(13),
|
||||
GPR_DWARFNUM_NAME(14),
|
||||
GPR_DWARFNUM_NAME(15),
|
||||
GPR_DWARFNUM_NAME(16),
|
||||
GPR_DWARFNUM_NAME(17),
|
||||
GPR_DWARFNUM_NAME(18),
|
||||
GPR_DWARFNUM_NAME(19),
|
||||
GPR_DWARFNUM_NAME(20),
|
||||
GPR_DWARFNUM_NAME(21),
|
||||
GPR_DWARFNUM_NAME(22),
|
||||
GPR_DWARFNUM_NAME(23),
|
||||
GPR_DWARFNUM_NAME(24),
|
||||
GPR_DWARFNUM_NAME(25),
|
||||
GPR_DWARFNUM_NAME(26),
|
||||
GPR_DWARFNUM_NAME(27),
|
||||
GPR_DWARFNUM_NAME(28),
|
||||
GPR_DWARFNUM_NAME(29),
|
||||
REG_DWARFNUM_NAME("%lr", 30),
|
||||
REG_DWARFNUM_NAME("%sp", 31),
|
||||
REG_DWARFNUM_END,
|
||||
};
|
||||
|
||||
/**
|
||||
* get_arch_regstr() - lookup register name from it's DWARF register number
|
||||
* @n: the DWARF register number
|
||||
*
|
||||
* get_arch_regstr() returns the name of the register in struct
|
||||
* regdwarfnum_table from it's DWARF register number. If the register is not
|
||||
* found in the table, this returns NULL;
|
||||
*/
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
for (roff = regdwarfnum_table; roff->name != NULL; roff++)
|
||||
if (roff->dwarfnum == n)
|
||||
return roff->name;
|
||||
return NULL;
|
||||
}
|
83
tools/perf/arch/arm64/util/unwind-libunwind.c
Normal file
83
tools/perf/arch/arm64/util/unwind-libunwind.c
Normal file
|
@ -0,0 +1,83 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <libunwind.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/unwind.h"
|
||||
#include "../../util/debug.h"
|
||||
|
||||
int libunwind__arch_reg_id(int regnum)
|
||||
{
|
||||
switch (regnum) {
|
||||
case UNW_AARCH64_X0:
|
||||
return PERF_REG_ARM64_X0;
|
||||
case UNW_AARCH64_X1:
|
||||
return PERF_REG_ARM64_X1;
|
||||
case UNW_AARCH64_X2:
|
||||
return PERF_REG_ARM64_X2;
|
||||
case UNW_AARCH64_X3:
|
||||
return PERF_REG_ARM64_X3;
|
||||
case UNW_AARCH64_X4:
|
||||
return PERF_REG_ARM64_X4;
|
||||
case UNW_AARCH64_X5:
|
||||
return PERF_REG_ARM64_X5;
|
||||
case UNW_AARCH64_X6:
|
||||
return PERF_REG_ARM64_X6;
|
||||
case UNW_AARCH64_X7:
|
||||
return PERF_REG_ARM64_X7;
|
||||
case UNW_AARCH64_X8:
|
||||
return PERF_REG_ARM64_X8;
|
||||
case UNW_AARCH64_X9:
|
||||
return PERF_REG_ARM64_X9;
|
||||
case UNW_AARCH64_X10:
|
||||
return PERF_REG_ARM64_X10;
|
||||
case UNW_AARCH64_X11:
|
||||
return PERF_REG_ARM64_X11;
|
||||
case UNW_AARCH64_X12:
|
||||
return PERF_REG_ARM64_X12;
|
||||
case UNW_AARCH64_X13:
|
||||
return PERF_REG_ARM64_X13;
|
||||
case UNW_AARCH64_X14:
|
||||
return PERF_REG_ARM64_X14;
|
||||
case UNW_AARCH64_X15:
|
||||
return PERF_REG_ARM64_X15;
|
||||
case UNW_AARCH64_X16:
|
||||
return PERF_REG_ARM64_X16;
|
||||
case UNW_AARCH64_X17:
|
||||
return PERF_REG_ARM64_X17;
|
||||
case UNW_AARCH64_X18:
|
||||
return PERF_REG_ARM64_X18;
|
||||
case UNW_AARCH64_X19:
|
||||
return PERF_REG_ARM64_X19;
|
||||
case UNW_AARCH64_X20:
|
||||
return PERF_REG_ARM64_X20;
|
||||
case UNW_AARCH64_X21:
|
||||
return PERF_REG_ARM64_X21;
|
||||
case UNW_AARCH64_X22:
|
||||
return PERF_REG_ARM64_X22;
|
||||
case UNW_AARCH64_X23:
|
||||
return PERF_REG_ARM64_X23;
|
||||
case UNW_AARCH64_X24:
|
||||
return PERF_REG_ARM64_X24;
|
||||
case UNW_AARCH64_X25:
|
||||
return PERF_REG_ARM64_X25;
|
||||
case UNW_AARCH64_X26:
|
||||
return PERF_REG_ARM64_X26;
|
||||
case UNW_AARCH64_X27:
|
||||
return PERF_REG_ARM64_X27;
|
||||
case UNW_AARCH64_X28:
|
||||
return PERF_REG_ARM64_X28;
|
||||
case UNW_AARCH64_X29:
|
||||
return PERF_REG_ARM64_X29;
|
||||
case UNW_AARCH64_X30:
|
||||
return PERF_REG_ARM64_LR;
|
||||
case UNW_AARCH64_SP:
|
||||
return PERF_REG_ARM64_SP;
|
||||
case UNW_AARCH64_PC:
|
||||
return PERF_REG_ARM64_PC;
|
||||
default:
|
||||
pr_err("unwind: invalid reg id %d\n", regnum);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
220
tools/perf/arch/common.c
Normal file
220
tools/perf/arch/common.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
#include <stdio.h>
|
||||
#include <sys/utsname.h>
|
||||
#include "common.h"
|
||||
#include "../util/debug.h"
|
||||
|
||||
const char *const arm_triplets[] = {
|
||||
"arm-eabi-",
|
||||
"arm-linux-androideabi-",
|
||||
"arm-unknown-linux-",
|
||||
"arm-unknown-linux-gnu-",
|
||||
"arm-unknown-linux-gnueabi-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const arm64_triplets[] = {
|
||||
"aarch64-linux-android-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const powerpc_triplets[] = {
|
||||
"powerpc-unknown-linux-gnu-",
|
||||
"powerpc64-unknown-linux-gnu-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const s390_triplets[] = {
|
||||
"s390-ibm-linux-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const sh_triplets[] = {
|
||||
"sh-unknown-linux-gnu-",
|
||||
"sh64-unknown-linux-gnu-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const sparc_triplets[] = {
|
||||
"sparc-unknown-linux-gnu-",
|
||||
"sparc64-unknown-linux-gnu-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const x86_triplets[] = {
|
||||
"x86_64-pc-linux-gnu-",
|
||||
"x86_64-unknown-linux-gnu-",
|
||||
"i686-pc-linux-gnu-",
|
||||
"i586-pc-linux-gnu-",
|
||||
"i486-pc-linux-gnu-",
|
||||
"i386-pc-linux-gnu-",
|
||||
"i686-linux-android-",
|
||||
"i686-android-linux-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const mips_triplets[] = {
|
||||
"mips-unknown-linux-gnu-",
|
||||
"mipsel-linux-android-",
|
||||
NULL
|
||||
};
|
||||
|
||||
static bool lookup_path(char *name)
|
||||
{
|
||||
bool found = false;
|
||||
char *path, *tmp;
|
||||
char buf[PATH_MAX];
|
||||
char *env = getenv("PATH");
|
||||
|
||||
if (!env)
|
||||
return false;
|
||||
|
||||
env = strdup(env);
|
||||
if (!env)
|
||||
return false;
|
||||
|
||||
path = strtok_r(env, ":", &tmp);
|
||||
while (path) {
|
||||
scnprintf(buf, sizeof(buf), "%s/%s", path, name);
|
||||
if (access(buf, F_OK) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
path = strtok_r(NULL, ":", &tmp);
|
||||
}
|
||||
free(env);
|
||||
return found;
|
||||
}
|
||||
|
||||
static int lookup_triplets(const char *const *triplets, const char *name)
|
||||
{
|
||||
int i;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
for (i = 0; triplets[i] != NULL; i++) {
|
||||
scnprintf(buf, sizeof(buf), "%s%s", triplets[i], name);
|
||||
if (lookup_path(buf))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return architecture name in a normalized form.
|
||||
* The conversion logic comes from the Makefile.
|
||||
*/
|
||||
static const char *normalize_arch(char *arch)
|
||||
{
|
||||
if (!strcmp(arch, "x86_64"))
|
||||
return "x86";
|
||||
if (arch[0] == 'i' && arch[2] == '8' && arch[3] == '6')
|
||||
return "x86";
|
||||
if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5))
|
||||
return "sparc";
|
||||
if (!strcmp(arch, "aarch64") || !strcmp(arch, "arm64"))
|
||||
return "arm64";
|
||||
if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110"))
|
||||
return "arm";
|
||||
if (!strncmp(arch, "s390", 4))
|
||||
return "s390";
|
||||
if (!strncmp(arch, "parisc", 6))
|
||||
return "parisc";
|
||||
if (!strncmp(arch, "powerpc", 7) || !strncmp(arch, "ppc", 3))
|
||||
return "powerpc";
|
||||
if (!strncmp(arch, "mips", 4))
|
||||
return "mips";
|
||||
if (!strncmp(arch, "sh", 2) && isdigit(arch[2]))
|
||||
return "sh";
|
||||
|
||||
return arch;
|
||||
}
|
||||
|
||||
static int perf_session_env__lookup_binutils_path(struct perf_session_env *env,
|
||||
const char *name,
|
||||
const char **path)
|
||||
{
|
||||
int idx;
|
||||
const char *arch, *cross_env;
|
||||
struct utsname uts;
|
||||
const char *const *path_list;
|
||||
char *buf = NULL;
|
||||
|
||||
arch = normalize_arch(env->arch);
|
||||
|
||||
if (uname(&uts) < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We don't need to try to find objdump path for native system.
|
||||
* Just use default binutils path (e.g.: "objdump").
|
||||
*/
|
||||
if (!strcmp(normalize_arch(uts.machine), arch))
|
||||
goto out;
|
||||
|
||||
cross_env = getenv("CROSS_COMPILE");
|
||||
if (cross_env) {
|
||||
if (asprintf(&buf, "%s%s", cross_env, name) < 0)
|
||||
goto out_error;
|
||||
if (buf[0] == '/') {
|
||||
if (access(buf, F_OK) == 0)
|
||||
goto out;
|
||||
goto out_error;
|
||||
}
|
||||
if (lookup_path(buf))
|
||||
goto out;
|
||||
zfree(&buf);
|
||||
}
|
||||
|
||||
if (!strcmp(arch, "arm"))
|
||||
path_list = arm_triplets;
|
||||
else if (!strcmp(arch, "arm64"))
|
||||
path_list = arm64_triplets;
|
||||
else if (!strcmp(arch, "powerpc"))
|
||||
path_list = powerpc_triplets;
|
||||
else if (!strcmp(arch, "sh"))
|
||||
path_list = sh_triplets;
|
||||
else if (!strcmp(arch, "s390"))
|
||||
path_list = s390_triplets;
|
||||
else if (!strcmp(arch, "sparc"))
|
||||
path_list = sparc_triplets;
|
||||
else if (!strcmp(arch, "x86"))
|
||||
path_list = x86_triplets;
|
||||
else if (!strcmp(arch, "mips"))
|
||||
path_list = mips_triplets;
|
||||
else {
|
||||
ui__error("binutils for %s not supported.\n", arch);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
idx = lookup_triplets(path_list, name);
|
||||
if (idx < 0) {
|
||||
ui__error("Please install %s for %s.\n"
|
||||
"You can add it to PATH, set CROSS_COMPILE or "
|
||||
"override the default using --%s.\n",
|
||||
name, arch, name);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (asprintf(&buf, "%s%s", path_list[idx], name) < 0)
|
||||
goto out_error;
|
||||
|
||||
out:
|
||||
*path = buf;
|
||||
return 0;
|
||||
out_error:
|
||||
free(buf);
|
||||
*path = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int perf_session_env__lookup_objdump(struct perf_session_env *env)
|
||||
{
|
||||
/*
|
||||
* For live mode, env->arch will be NULL and we can use
|
||||
* the native objdump tool.
|
||||
*/
|
||||
if (env->arch == NULL)
|
||||
return 0;
|
||||
|
||||
return perf_session_env__lookup_binutils_path(env, "objdump",
|
||||
&objdump_path);
|
||||
}
|
10
tools/perf/arch/common.h
Normal file
10
tools/perf/arch/common.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#ifndef ARCH_PERF_COMMON_H
|
||||
#define ARCH_PERF_COMMON_H
|
||||
|
||||
#include "../util/session.h"
|
||||
|
||||
extern const char *objdump_path;
|
||||
|
||||
int perf_session_env__lookup_objdump(struct perf_session_env *env);
|
||||
|
||||
#endif /* ARCH_PERF_COMMON_H */
|
6
tools/perf/arch/powerpc/Makefile
Normal file
6
tools/perf/arch/powerpc/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/skip-callchain-idx.o
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
|
88
tools/perf/arch/powerpc/util/dwarf-regs.c
Normal file
88
tools/perf/arch/powerpc/util/dwarf-regs.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2010 Ian Munsie, IBM Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
|
||||
struct pt_regs_dwarfnum {
|
||||
const char *name;
|
||||
unsigned int dwarfnum;
|
||||
};
|
||||
|
||||
#define STR(s) #s
|
||||
#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
|
||||
#define GPR_DWARFNUM_NAME(num) \
|
||||
{.name = STR(%gpr##num), .dwarfnum = num}
|
||||
#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
|
||||
|
||||
/*
|
||||
* Reference:
|
||||
* http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html
|
||||
*/
|
||||
static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
|
||||
GPR_DWARFNUM_NAME(0),
|
||||
GPR_DWARFNUM_NAME(1),
|
||||
GPR_DWARFNUM_NAME(2),
|
||||
GPR_DWARFNUM_NAME(3),
|
||||
GPR_DWARFNUM_NAME(4),
|
||||
GPR_DWARFNUM_NAME(5),
|
||||
GPR_DWARFNUM_NAME(6),
|
||||
GPR_DWARFNUM_NAME(7),
|
||||
GPR_DWARFNUM_NAME(8),
|
||||
GPR_DWARFNUM_NAME(9),
|
||||
GPR_DWARFNUM_NAME(10),
|
||||
GPR_DWARFNUM_NAME(11),
|
||||
GPR_DWARFNUM_NAME(12),
|
||||
GPR_DWARFNUM_NAME(13),
|
||||
GPR_DWARFNUM_NAME(14),
|
||||
GPR_DWARFNUM_NAME(15),
|
||||
GPR_DWARFNUM_NAME(16),
|
||||
GPR_DWARFNUM_NAME(17),
|
||||
GPR_DWARFNUM_NAME(18),
|
||||
GPR_DWARFNUM_NAME(19),
|
||||
GPR_DWARFNUM_NAME(20),
|
||||
GPR_DWARFNUM_NAME(21),
|
||||
GPR_DWARFNUM_NAME(22),
|
||||
GPR_DWARFNUM_NAME(23),
|
||||
GPR_DWARFNUM_NAME(24),
|
||||
GPR_DWARFNUM_NAME(25),
|
||||
GPR_DWARFNUM_NAME(26),
|
||||
GPR_DWARFNUM_NAME(27),
|
||||
GPR_DWARFNUM_NAME(28),
|
||||
GPR_DWARFNUM_NAME(29),
|
||||
GPR_DWARFNUM_NAME(30),
|
||||
GPR_DWARFNUM_NAME(31),
|
||||
REG_DWARFNUM_NAME("%msr", 66),
|
||||
REG_DWARFNUM_NAME("%ctr", 109),
|
||||
REG_DWARFNUM_NAME("%link", 108),
|
||||
REG_DWARFNUM_NAME("%xer", 101),
|
||||
REG_DWARFNUM_NAME("%dar", 119),
|
||||
REG_DWARFNUM_NAME("%dsisr", 118),
|
||||
REG_DWARFNUM_END,
|
||||
};
|
||||
|
||||
/**
|
||||
* get_arch_regstr() - lookup register name from it's DWARF register number
|
||||
* @n: the DWARF register number
|
||||
*
|
||||
* get_arch_regstr() returns the name of the register in struct
|
||||
* regdwarfnum_table from it's DWARF register number. If the register is not
|
||||
* found in the table, this returns NULL;
|
||||
*/
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
for (roff = regdwarfnum_table; roff->name != NULL; roff++)
|
||||
if (roff->dwarfnum == n)
|
||||
return roff->name;
|
||||
return NULL;
|
||||
}
|
34
tools/perf/arch/powerpc/util/header.c
Normal file
34
tools/perf/arch/powerpc/util/header.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../../util/header.h"
|
||||
#include "../../util/util.h"
|
||||
|
||||
#define mfspr(rn) ({unsigned long rval; \
|
||||
asm volatile("mfspr %0," __stringify(rn) \
|
||||
: "=r" (rval)); rval; })
|
||||
|
||||
#define SPRN_PVR 0x11F /* Processor Version Register */
|
||||
#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */
|
||||
#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */
|
||||
|
||||
int
|
||||
get_cpuid(char *buffer, size_t sz)
|
||||
{
|
||||
unsigned long pvr;
|
||||
int nb;
|
||||
|
||||
pvr = mfspr(SPRN_PVR);
|
||||
|
||||
nb = scnprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr));
|
||||
|
||||
/* look for end marker to ensure the entire data fit */
|
||||
if (strchr(buffer, '$')) {
|
||||
buffer[nb-1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
267
tools/perf/arch/powerpc/util/skip-callchain-idx.c
Normal file
267
tools/perf/arch/powerpc/util/skip-callchain-idx.c
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* Use DWARF Debug information to skip unnecessary callchain entries.
|
||||
*
|
||||
* Copyright (C) 2014 Sukadev Bhattiprolu, IBM Corporation.
|
||||
* Copyright (C) 2014 Ulrich Weigand, IBM Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
#include <dwarf.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
#include "util/thread.h"
|
||||
#include "util/callchain.h"
|
||||
#include "util/debug.h"
|
||||
|
||||
/*
|
||||
* When saving the callchain on Power, the kernel conservatively saves
|
||||
* excess entries in the callchain. A few of these entries are needed
|
||||
* in some cases but not others. If the unnecessary entries are not
|
||||
* ignored, we end up with duplicate arcs in the call-graphs. Use
|
||||
* DWARF debug information to skip over any unnecessary callchain
|
||||
* entries.
|
||||
*
|
||||
* See function header for arch_adjust_callchain() below for more details.
|
||||
*
|
||||
* The libdwfl code in this file is based on code from elfutils
|
||||
* (libdwfl/argp-std.c, libdwfl/tests/addrcfi.c, etc).
|
||||
*/
|
||||
static char *debuginfo_path;
|
||||
|
||||
static const Dwfl_Callbacks offline_callbacks = {
|
||||
.debuginfo_path = &debuginfo_path,
|
||||
.find_debuginfo = dwfl_standard_find_debuginfo,
|
||||
.section_address = dwfl_offline_section_address,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Use the DWARF expression for the Call-frame-address and determine
|
||||
* if return address is in LR and if a new frame was allocated.
|
||||
*/
|
||||
static int check_return_reg(int ra_regno, Dwarf_Frame *frame)
|
||||
{
|
||||
Dwarf_Op ops_mem[2];
|
||||
Dwarf_Op dummy;
|
||||
Dwarf_Op *ops = &dummy;
|
||||
size_t nops;
|
||||
int result;
|
||||
|
||||
result = dwarf_frame_register(frame, ra_regno, ops_mem, &ops, &nops);
|
||||
if (result < 0) {
|
||||
pr_debug("dwarf_frame_register() %s\n", dwarf_errmsg(-1));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if return address is on the stack.
|
||||
*/
|
||||
if (nops != 0 || ops != NULL)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Return address is in LR. Check if a frame was allocated
|
||||
* but not-yet used.
|
||||
*/
|
||||
result = dwarf_frame_cfa(frame, &ops, &nops);
|
||||
if (result < 0) {
|
||||
pr_debug("dwarf_frame_cfa() returns %d, %s\n", result,
|
||||
dwarf_errmsg(-1));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If call frame address is in r1, no new frame was allocated.
|
||||
*/
|
||||
if (nops == 1 && ops[0].atom == DW_OP_bregx && ops[0].number == 1 &&
|
||||
ops[0].number2 == 0)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* A new frame was allocated but has not yet been used.
|
||||
*/
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the DWARF frame from the .eh_frame section.
|
||||
*/
|
||||
static Dwarf_Frame *get_eh_frame(Dwfl_Module *mod, Dwarf_Addr pc)
|
||||
{
|
||||
int result;
|
||||
Dwarf_Addr bias;
|
||||
Dwarf_CFI *cfi;
|
||||
Dwarf_Frame *frame;
|
||||
|
||||
cfi = dwfl_module_eh_cfi(mod, &bias);
|
||||
if (!cfi) {
|
||||
pr_debug("%s(): no CFI - %s\n", __func__, dwfl_errmsg(-1));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = dwarf_cfi_addrframe(cfi, pc, &frame);
|
||||
if (result) {
|
||||
pr_debug("%s(): %s\n", __func__, dwfl_errmsg(-1));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the DWARF frame from the .debug_frame section.
|
||||
*/
|
||||
static Dwarf_Frame *get_dwarf_frame(Dwfl_Module *mod, Dwarf_Addr pc)
|
||||
{
|
||||
Dwarf_CFI *cfi;
|
||||
Dwarf_Addr bias;
|
||||
Dwarf_Frame *frame;
|
||||
int result;
|
||||
|
||||
cfi = dwfl_module_dwarf_cfi(mod, &bias);
|
||||
if (!cfi) {
|
||||
pr_debug("%s(): no CFI - %s\n", __func__, dwfl_errmsg(-1));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = dwarf_cfi_addrframe(cfi, pc, &frame);
|
||||
if (result) {
|
||||
pr_debug("%s(): %s\n", __func__, dwfl_errmsg(-1));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return:
|
||||
* 0 if return address for the program counter @pc is on stack
|
||||
* 1 if return address is in LR and no new stack frame was allocated
|
||||
* 2 if return address is in LR and a new frame was allocated (but not
|
||||
* yet used)
|
||||
* -1 in case of errors
|
||||
*/
|
||||
static int check_return_addr(const char *exec_file, Dwarf_Addr pc)
|
||||
{
|
||||
int rc = -1;
|
||||
Dwfl *dwfl;
|
||||
Dwfl_Module *mod;
|
||||
Dwarf_Frame *frame;
|
||||
int ra_regno;
|
||||
Dwarf_Addr start = pc;
|
||||
Dwarf_Addr end = pc;
|
||||
bool signalp;
|
||||
|
||||
dwfl = dwfl_begin(&offline_callbacks);
|
||||
if (!dwfl) {
|
||||
pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dwfl_report_offline(dwfl, "", exec_file, -1) == NULL) {
|
||||
pr_debug("dwfl_report_offline() failed %s\n", dwarf_errmsg(-1));
|
||||
goto out;
|
||||
}
|
||||
|
||||
mod = dwfl_addrmodule(dwfl, pc);
|
||||
if (!mod) {
|
||||
pr_debug("dwfl_addrmodule() failed, %s\n", dwarf_errmsg(-1));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* To work with split debug info files (eg: glibc), check both
|
||||
* .eh_frame and .debug_frame sections of the ELF header.
|
||||
*/
|
||||
frame = get_eh_frame(mod, pc);
|
||||
if (!frame) {
|
||||
frame = get_dwarf_frame(mod, pc);
|
||||
if (!frame)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ra_regno = dwarf_frame_info(frame, &start, &end, &signalp);
|
||||
if (ra_regno < 0) {
|
||||
pr_debug("Return address register unavailable: %s\n",
|
||||
dwarf_errmsg(-1));
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = check_return_reg(ra_regno, frame);
|
||||
|
||||
out:
|
||||
dwfl_end(dwfl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* The callchain saved by the kernel always includes the link register (LR).
|
||||
*
|
||||
* 0: PERF_CONTEXT_USER
|
||||
* 1: Program counter (Next instruction pointer)
|
||||
* 2: LR value
|
||||
* 3: Caller's caller
|
||||
* 4: ...
|
||||
*
|
||||
* The value in LR is only needed when it holds a return address. If the
|
||||
* return address is on the stack, we should ignore the LR value.
|
||||
*
|
||||
* Further, when the return address is in the LR, if a new frame was just
|
||||
* allocated but the LR was not saved into it, then the LR contains the
|
||||
* caller, slot 4: contains the caller's caller and the contents of slot 3:
|
||||
* (chain->ips[3]) is undefined and must be ignored.
|
||||
*
|
||||
* Use DWARF debug information to determine if any entries need to be skipped.
|
||||
*
|
||||
* Return:
|
||||
* index: of callchain entry that needs to be ignored (if any)
|
||||
* -1 if no entry needs to be ignored or in case of errors
|
||||
*/
|
||||
int arch_skip_callchain_idx(struct machine *machine, struct thread *thread,
|
||||
struct ip_callchain *chain)
|
||||
{
|
||||
struct addr_location al;
|
||||
struct dso *dso = NULL;
|
||||
int rc;
|
||||
u64 ip;
|
||||
u64 skip_slot = -1;
|
||||
|
||||
if (chain->nr < 3)
|
||||
return skip_slot;
|
||||
|
||||
ip = chain->ips[2];
|
||||
|
||||
thread__find_addr_location(thread, machine, PERF_RECORD_MISC_USER,
|
||||
MAP__FUNCTION, ip, &al);
|
||||
|
||||
if (al.map)
|
||||
dso = al.map->dso;
|
||||
|
||||
if (!dso) {
|
||||
pr_debug("%" PRIx64 " dso is NULL\n", ip);
|
||||
return skip_slot;
|
||||
}
|
||||
|
||||
rc = check_return_addr(dso->long_name, ip);
|
||||
|
||||
pr_debug("DSO %s, nr %" PRIx64 ", ip 0x%" PRIx64 "rc %d\n",
|
||||
dso->long_name, chain->nr, ip, rc);
|
||||
|
||||
if (rc == 0) {
|
||||
/*
|
||||
* Return address on stack. Ignore LR value in callchain
|
||||
*/
|
||||
skip_slot = 2;
|
||||
} else if (rc == 2) {
|
||||
/*
|
||||
* New frame allocated but return address still in LR.
|
||||
* Ignore the caller's caller entry in callchain.
|
||||
*/
|
||||
skip_slot = 3;
|
||||
}
|
||||
return skip_slot;
|
||||
}
|
7
tools/perf/arch/s390/Makefile
Normal file
7
tools/perf/arch/s390/Makefile
Normal file
|
@ -0,0 +1,7 @@
|
|||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/kvm-stat.o
|
22
tools/perf/arch/s390/util/dwarf-regs.c
Normal file
22
tools/perf/arch/s390/util/dwarf-regs.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright IBM Corp. 2010
|
||||
* Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
#define NUM_GPRS 16
|
||||
|
||||
static const char *gpr_names[NUM_GPRS] = {
|
||||
"%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7",
|
||||
"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
|
||||
};
|
||||
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n >= NUM_GPRS) ? NULL : gpr_names[n];
|
||||
}
|
28
tools/perf/arch/s390/util/header.c
Normal file
28
tools/perf/arch/s390/util/header.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Implementation of get_cpuid().
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License (version 2 only)
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../../util/header.h"
|
||||
|
||||
int get_cpuid(char *buffer, size_t sz)
|
||||
{
|
||||
const char *cpuid = "IBM/S390";
|
||||
|
||||
if (strlen(cpuid) + 1 > sz)
|
||||
return -1;
|
||||
|
||||
strcpy(buffer, cpuid);
|
||||
return 0;
|
||||
}
|
105
tools/perf/arch/s390/util/kvm-stat.c
Normal file
105
tools/perf/arch/s390/util/kvm-stat.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Arch specific functions for perf kvm stat.
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License (version 2 only)
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "../../util/kvm-stat.h"
|
||||
#include <asm/kvm_perf.h>
|
||||
|
||||
define_exit_reasons_table(sie_exit_reasons, sie_intercept_code);
|
||||
define_exit_reasons_table(sie_icpt_insn_codes, icpt_insn_codes);
|
||||
define_exit_reasons_table(sie_sigp_order_codes, sigp_order_codes);
|
||||
define_exit_reasons_table(sie_diagnose_codes, diagnose_codes);
|
||||
define_exit_reasons_table(sie_icpt_prog_codes, icpt_prog_codes);
|
||||
|
||||
static void event_icpt_insn_get_key(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
unsigned long insn;
|
||||
|
||||
insn = perf_evsel__intval(evsel, sample, "instruction");
|
||||
key->key = icpt_insn_decoder(insn);
|
||||
key->exit_reasons = sie_icpt_insn_codes;
|
||||
}
|
||||
|
||||
static void event_sigp_get_key(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
key->key = perf_evsel__intval(evsel, sample, "order_code");
|
||||
key->exit_reasons = sie_sigp_order_codes;
|
||||
}
|
||||
|
||||
static void event_diag_get_key(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
key->key = perf_evsel__intval(evsel, sample, "code");
|
||||
key->exit_reasons = sie_diagnose_codes;
|
||||
}
|
||||
|
||||
static void event_icpt_prog_get_key(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
key->key = perf_evsel__intval(evsel, sample, "code");
|
||||
key->exit_reasons = sie_icpt_prog_codes;
|
||||
}
|
||||
|
||||
static struct child_event_ops child_events[] = {
|
||||
{ .name = "kvm:kvm_s390_intercept_instruction",
|
||||
.get_key = event_icpt_insn_get_key },
|
||||
{ .name = "kvm:kvm_s390_handle_sigp",
|
||||
.get_key = event_sigp_get_key },
|
||||
{ .name = "kvm:kvm_s390_handle_diag",
|
||||
.get_key = event_diag_get_key },
|
||||
{ .name = "kvm:kvm_s390_intercept_prog",
|
||||
.get_key = event_icpt_prog_get_key },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static struct kvm_events_ops exit_events = {
|
||||
.is_begin_event = exit_event_begin,
|
||||
.is_end_event = exit_event_end,
|
||||
.child_ops = child_events,
|
||||
.decode_key = exit_event_decode_key,
|
||||
.name = "VM-EXIT"
|
||||
};
|
||||
|
||||
const char * const kvm_events_tp[] = {
|
||||
"kvm:kvm_s390_sie_enter",
|
||||
"kvm:kvm_s390_sie_exit",
|
||||
"kvm:kvm_s390_intercept_instruction",
|
||||
"kvm:kvm_s390_handle_sigp",
|
||||
"kvm:kvm_s390_handle_diag",
|
||||
"kvm:kvm_s390_intercept_prog",
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct kvm_reg_events_ops kvm_reg_events_ops[] = {
|
||||
{ .name = "vmexit", .ops = &exit_events },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
const char * const kvm_skip_events[] = {
|
||||
"Wait state",
|
||||
NULL,
|
||||
};
|
||||
|
||||
int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid)
|
||||
{
|
||||
if (strstr(cpuid, "IBM/S390")) {
|
||||
kvm->exit_reasons = sie_exit_reasons;
|
||||
kvm->exit_reasons_isa = "SIE";
|
||||
} else
|
||||
return -ENOTSUP;
|
||||
|
||||
return 0;
|
||||
}
|
4
tools/perf/arch/sh/Makefile
Normal file
4
tools/perf/arch/sh/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||
endif
|
55
tools/perf/arch/sh/util/dwarf-regs.c
Normal file
55
tools/perf/arch/sh/util/dwarf-regs.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2010 Matt Fleming <matt@console-pimps.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
/*
|
||||
* Generic dwarf analysis helpers
|
||||
*/
|
||||
|
||||
#define SH_MAX_REGS 18
|
||||
const char *sh_regs_table[SH_MAX_REGS] = {
|
||||
"r0",
|
||||
"r1",
|
||||
"r2",
|
||||
"r3",
|
||||
"r4",
|
||||
"r5",
|
||||
"r6",
|
||||
"r7",
|
||||
"r8",
|
||||
"r9",
|
||||
"r10",
|
||||
"r11",
|
||||
"r12",
|
||||
"r13",
|
||||
"r14",
|
||||
"r15",
|
||||
"pc",
|
||||
"pr",
|
||||
};
|
||||
|
||||
/* Return architecture dependent register string (for kprobe-tracer) */
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n <= SH_MAX_REGS) ? sh_regs_table[n] : NULL;
|
||||
}
|
4
tools/perf/arch/sparc/Makefile
Normal file
4
tools/perf/arch/sparc/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||
endif
|
43
tools/perf/arch/sparc/util/dwarf-regs.c
Normal file
43
tools/perf/arch/sparc/util/dwarf-regs.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2010 David S. Miller <davem@davemloft.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
#define SPARC_MAX_REGS 96
|
||||
|
||||
const char *sparc_regs_table[SPARC_MAX_REGS] = {
|
||||
"%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7",
|
||||
"%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7",
|
||||
"%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7",
|
||||
"%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7",
|
||||
"%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
|
||||
"%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15",
|
||||
"%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23",
|
||||
"%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31",
|
||||
"%f32", "%f33", "%f34", "%f35", "%f36", "%f37", "%f38", "%f39",
|
||||
"%f40", "%f41", "%f42", "%f43", "%f44", "%f45", "%f46", "%f47",
|
||||
"%f48", "%f49", "%f50", "%f51", "%f52", "%f53", "%f54", "%f55",
|
||||
"%f56", "%f57", "%f58", "%f59", "%f60", "%f61", "%f62", "%f63",
|
||||
};
|
||||
|
||||
/**
|
||||
* get_arch_regstr() - lookup register name from it's DWARF register number
|
||||
* @n: the DWARF register number
|
||||
*
|
||||
* get_arch_regstr() returns the name of the register in struct
|
||||
* regdwarfnum_table from it's DWARF register number. If the register is not
|
||||
* found in the table, this returns NULL;
|
||||
*/
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n <= SPARC_MAX_REGS) ? sparc_regs_table[n] : NULL;
|
||||
}
|
19
tools/perf/arch/x86/Makefile
Normal file
19
tools/perf/arch/x86/Makefile
Normal file
|
@ -0,0 +1,19 @@
|
|||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||
endif
|
||||
ifndef NO_LIBUNWIND
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libunwind.o
|
||||
endif
|
||||
ifndef NO_LIBDW_DWARF_UNWIND
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libdw.o
|
||||
endif
|
||||
ifndef NO_DWARF_UNWIND
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/regs_load.o
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/dwarf-unwind.o
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/tsc.o
|
||||
LIB_H += arch/$(ARCH)/util/tsc.h
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/kvm-stat.o
|
86
tools/perf/arch/x86/include/perf_regs.h
Normal file
86
tools/perf/arch/x86/include/perf_regs.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
#ifndef ARCH_PERF_REGS_H
|
||||
#define ARCH_PERF_REGS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/perf_regs.h>
|
||||
|
||||
void perf_regs_load(u64 *regs);
|
||||
|
||||
#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) | \
|
||||
(1ULL << PERF_REG_X86_ES) | \
|
||||
(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
|
||||
#define PERF_REG_SP PERF_REG_X86_SP
|
||||
|
||||
static inline const char *perf_reg_name(int id)
|
||||
{
|
||||
switch (id) {
|
||||
case PERF_REG_X86_AX:
|
||||
return "AX";
|
||||
case PERF_REG_X86_BX:
|
||||
return "BX";
|
||||
case PERF_REG_X86_CX:
|
||||
return "CX";
|
||||
case PERF_REG_X86_DX:
|
||||
return "DX";
|
||||
case PERF_REG_X86_SI:
|
||||
return "SI";
|
||||
case PERF_REG_X86_DI:
|
||||
return "DI";
|
||||
case PERF_REG_X86_BP:
|
||||
return "BP";
|
||||
case PERF_REG_X86_SP:
|
||||
return "SP";
|
||||
case PERF_REG_X86_IP:
|
||||
return "IP";
|
||||
case PERF_REG_X86_FLAGS:
|
||||
return "FLAGS";
|
||||
case PERF_REG_X86_CS:
|
||||
return "CS";
|
||||
case PERF_REG_X86_SS:
|
||||
return "SS";
|
||||
case PERF_REG_X86_DS:
|
||||
return "DS";
|
||||
case PERF_REG_X86_ES:
|
||||
return "ES";
|
||||
case PERF_REG_X86_FS:
|
||||
return "FS";
|
||||
case PERF_REG_X86_GS:
|
||||
return "GS";
|
||||
#ifdef HAVE_ARCH_X86_64_SUPPORT
|
||||
case PERF_REG_X86_R8:
|
||||
return "R8";
|
||||
case PERF_REG_X86_R9:
|
||||
return "R9";
|
||||
case PERF_REG_X86_R10:
|
||||
return "R10";
|
||||
case PERF_REG_X86_R11:
|
||||
return "R11";
|
||||
case PERF_REG_X86_R12:
|
||||
return "R12";
|
||||
case PERF_REG_X86_R13:
|
||||
return "R13";
|
||||
case PERF_REG_X86_R14:
|
||||
return "R14";
|
||||
case PERF_REG_X86_R15:
|
||||
return "R15";
|
||||
#endif /* HAVE_ARCH_X86_64_SUPPORT */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* ARCH_PERF_REGS_H */
|
61
tools/perf/arch/x86/tests/dwarf-unwind.c
Normal file
61
tools/perf/arch/x86/tests/dwarf-unwind.c
Normal file
|
@ -0,0 +1,61 @@
|
|||
#include <string.h>
|
||||
#include "perf_regs.h"
|
||||
#include "thread.h"
|
||||
#include "map.h"
|
||||
#include "event.h"
|
||||
#include "debug.h"
|
||||
#include "tests/tests.h"
|
||||
|
||||
#define STACK_SIZE 8192
|
||||
|
||||
static int sample_ustack(struct perf_sample *sample,
|
||||
struct thread *thread, u64 *regs)
|
||||
{
|
||||
struct stack_dump *stack = &sample->user_stack;
|
||||
struct map *map;
|
||||
unsigned long sp;
|
||||
u64 stack_size, *buf;
|
||||
|
||||
buf = malloc(STACK_SIZE);
|
||||
if (!buf) {
|
||||
pr_debug("failed to allocate sample uregs data\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sp = (unsigned long) regs[PERF_REG_X86_SP];
|
||||
|
||||
map = map_groups__find(thread->mg, MAP__VARIABLE, (u64) sp);
|
||||
if (!map) {
|
||||
pr_debug("failed to get stack map\n");
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stack_size = map->end - sp;
|
||||
stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size;
|
||||
|
||||
memcpy(buf, (void *) sp, stack_size);
|
||||
stack->data = (char *) buf;
|
||||
stack->size = stack_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test__arch_unwind_sample(struct perf_sample *sample,
|
||||
struct thread *thread)
|
||||
{
|
||||
struct regs_dump *regs = &sample->user_regs;
|
||||
u64 *buf;
|
||||
|
||||
buf = malloc(sizeof(u64) * PERF_REGS_MAX);
|
||||
if (!buf) {
|
||||
pr_debug("failed to allocate sample uregs data\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
perf_regs_load(buf);
|
||||
regs->abi = PERF_SAMPLE_REGS_ABI;
|
||||
regs->regs = buf;
|
||||
regs->mask = PERF_REGS_MASK;
|
||||
|
||||
return sample_ustack(sample, thread, buf);
|
||||
}
|
98
tools/perf/arch/x86/tests/regs_load.S
Normal file
98
tools/perf/arch/x86/tests/regs_load.S
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include <linux/linkage.h>
|
||||
|
||||
#define AX 0
|
||||
#define BX 1 * 8
|
||||
#define CX 2 * 8
|
||||
#define DX 3 * 8
|
||||
#define SI 4 * 8
|
||||
#define DI 5 * 8
|
||||
#define BP 6 * 8
|
||||
#define SP 7 * 8
|
||||
#define IP 8 * 8
|
||||
#define FLAGS 9 * 8
|
||||
#define CS 10 * 8
|
||||
#define SS 11 * 8
|
||||
#define DS 12 * 8
|
||||
#define ES 13 * 8
|
||||
#define FS 14 * 8
|
||||
#define GS 15 * 8
|
||||
#define R8 16 * 8
|
||||
#define R9 17 * 8
|
||||
#define R10 18 * 8
|
||||
#define R11 19 * 8
|
||||
#define R12 20 * 8
|
||||
#define R13 21 * 8
|
||||
#define R14 22 * 8
|
||||
#define R15 23 * 8
|
||||
|
||||
.text
|
||||
#ifdef HAVE_ARCH_X86_64_SUPPORT
|
||||
ENTRY(perf_regs_load)
|
||||
movq %rax, AX(%rdi)
|
||||
movq %rbx, BX(%rdi)
|
||||
movq %rcx, CX(%rdi)
|
||||
movq %rdx, DX(%rdi)
|
||||
movq %rsi, SI(%rdi)
|
||||
movq %rdi, DI(%rdi)
|
||||
movq %rbp, BP(%rdi)
|
||||
|
||||
leaq 8(%rsp), %rax /* exclude this call. */
|
||||
movq %rax, SP(%rdi)
|
||||
|
||||
movq 0(%rsp), %rax
|
||||
movq %rax, IP(%rdi)
|
||||
|
||||
movq $0, FLAGS(%rdi)
|
||||
movq $0, CS(%rdi)
|
||||
movq $0, SS(%rdi)
|
||||
movq $0, DS(%rdi)
|
||||
movq $0, ES(%rdi)
|
||||
movq $0, FS(%rdi)
|
||||
movq $0, GS(%rdi)
|
||||
|
||||
movq %r8, R8(%rdi)
|
||||
movq %r9, R9(%rdi)
|
||||
movq %r10, R10(%rdi)
|
||||
movq %r11, R11(%rdi)
|
||||
movq %r12, R12(%rdi)
|
||||
movq %r13, R13(%rdi)
|
||||
movq %r14, R14(%rdi)
|
||||
movq %r15, R15(%rdi)
|
||||
ret
|
||||
ENDPROC(perf_regs_load)
|
||||
#else
|
||||
ENTRY(perf_regs_load)
|
||||
push %edi
|
||||
movl 8(%esp), %edi
|
||||
movl %eax, AX(%edi)
|
||||
movl %ebx, BX(%edi)
|
||||
movl %ecx, CX(%edi)
|
||||
movl %edx, DX(%edi)
|
||||
movl %esi, SI(%edi)
|
||||
pop %eax
|
||||
movl %eax, DI(%edi)
|
||||
movl %ebp, BP(%edi)
|
||||
|
||||
leal 4(%esp), %eax /* exclude this call. */
|
||||
movl %eax, SP(%edi)
|
||||
|
||||
movl 0(%esp), %eax
|
||||
movl %eax, IP(%edi)
|
||||
|
||||
movl $0, FLAGS(%edi)
|
||||
movl $0, CS(%edi)
|
||||
movl $0, SS(%edi)
|
||||
movl $0, DS(%edi)
|
||||
movl $0, ES(%edi)
|
||||
movl $0, FS(%edi)
|
||||
movl $0, GS(%edi)
|
||||
ret
|
||||
ENDPROC(perf_regs_load)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We need to provide note.GNU-stack section, saying that we want
|
||||
* NOT executable stack. Otherwise the final linking will assume that
|
||||
* the ELF stack should not be restricted at all and set it RWX.
|
||||
*/
|
||||
.section .note.GNU-stack,"",@progbits
|
75
tools/perf/arch/x86/util/dwarf-regs.c
Normal file
75
tools/perf/arch/x86/util/dwarf-regs.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* dwarf-regs.c : Mapping of DWARF debug register numbers into register names.
|
||||
* Extracted from probe-finder.c
|
||||
*
|
||||
* Written by Masami Hiramatsu <mhiramat@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
/*
|
||||
* Generic dwarf analysis helpers
|
||||
*/
|
||||
|
||||
#define X86_32_MAX_REGS 8
|
||||
const char *x86_32_regs_table[X86_32_MAX_REGS] = {
|
||||
"%ax",
|
||||
"%cx",
|
||||
"%dx",
|
||||
"%bx",
|
||||
"$stack", /* Stack address instead of %sp */
|
||||
"%bp",
|
||||
"%si",
|
||||
"%di",
|
||||
};
|
||||
|
||||
#define X86_64_MAX_REGS 16
|
||||
const char *x86_64_regs_table[X86_64_MAX_REGS] = {
|
||||
"%ax",
|
||||
"%dx",
|
||||
"%cx",
|
||||
"%bx",
|
||||
"%si",
|
||||
"%di",
|
||||
"%bp",
|
||||
"%sp",
|
||||
"%r8",
|
||||
"%r9",
|
||||
"%r10",
|
||||
"%r11",
|
||||
"%r12",
|
||||
"%r13",
|
||||
"%r14",
|
||||
"%r15",
|
||||
};
|
||||
|
||||
/* TODO: switching by dwarf address size */
|
||||
#ifdef __x86_64__
|
||||
#define ARCH_MAX_REGS X86_64_MAX_REGS
|
||||
#define arch_regs_table x86_64_regs_table
|
||||
#else
|
||||
#define ARCH_MAX_REGS X86_32_MAX_REGS
|
||||
#define arch_regs_table x86_32_regs_table
|
||||
#endif
|
||||
|
||||
/* Return architecture dependent register string (for kprobe-tracer) */
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
|
||||
}
|
59
tools/perf/arch/x86/util/header.c
Normal file
59
tools/perf/arch/x86/util/header.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../../util/header.h"
|
||||
|
||||
static inline void
|
||||
cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c,
|
||||
unsigned int *d)
|
||||
{
|
||||
__asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t"
|
||||
"movl %%ebx, %%esi\n\t.byte 0x5b"
|
||||
: "=a" (*a),
|
||||
"=S" (*b),
|
||||
"=c" (*c),
|
||||
"=d" (*d)
|
||||
: "a" (op));
|
||||
}
|
||||
|
||||
int
|
||||
get_cpuid(char *buffer, size_t sz)
|
||||
{
|
||||
unsigned int a, b, c, d, lvl;
|
||||
int family = -1, model = -1, step = -1;
|
||||
int nb;
|
||||
char vendor[16];
|
||||
|
||||
cpuid(0, &lvl, &b, &c, &d);
|
||||
strncpy(&vendor[0], (char *)(&b), 4);
|
||||
strncpy(&vendor[4], (char *)(&d), 4);
|
||||
strncpy(&vendor[8], (char *)(&c), 4);
|
||||
vendor[12] = '\0';
|
||||
|
||||
if (lvl >= 1) {
|
||||
cpuid(1, &a, &b, &c, &d);
|
||||
|
||||
family = (a >> 8) & 0xf; /* bits 11 - 8 */
|
||||
model = (a >> 4) & 0xf; /* Bits 7 - 4 */
|
||||
step = a & 0xf;
|
||||
|
||||
/* extended family */
|
||||
if (family == 0xf)
|
||||
family += (a >> 20) & 0xff;
|
||||
|
||||
/* extended model */
|
||||
if (family >= 0x6)
|
||||
model += ((a >> 16) & 0xf) << 4;
|
||||
}
|
||||
nb = scnprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step);
|
||||
|
||||
/* look for end marker to ensure the entire data fit */
|
||||
if (strchr(buffer, '$')) {
|
||||
buffer[nb-1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
156
tools/perf/arch/x86/util/kvm-stat.c
Normal file
156
tools/perf/arch/x86/util/kvm-stat.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
#include "../../util/kvm-stat.h"
|
||||
#include <asm/kvm_perf.h>
|
||||
|
||||
define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS);
|
||||
define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS);
|
||||
|
||||
static struct kvm_events_ops exit_events = {
|
||||
.is_begin_event = exit_event_begin,
|
||||
.is_end_event = exit_event_end,
|
||||
.decode_key = exit_event_decode_key,
|
||||
.name = "VM-EXIT"
|
||||
};
|
||||
|
||||
/*
|
||||
* For the mmio events, we treat:
|
||||
* the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
|
||||
* the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
|
||||
*/
|
||||
static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
key->key = perf_evsel__intval(evsel, sample, "gpa");
|
||||
key->info = perf_evsel__intval(evsel, sample, "type");
|
||||
}
|
||||
|
||||
#define KVM_TRACE_MMIO_READ_UNSATISFIED 0
|
||||
#define KVM_TRACE_MMIO_READ 1
|
||||
#define KVM_TRACE_MMIO_WRITE 2
|
||||
|
||||
static bool mmio_event_begin(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample, struct event_key *key)
|
||||
{
|
||||
/* MMIO read begin event in kernel. */
|
||||
if (kvm_exit_event(evsel))
|
||||
return true;
|
||||
|
||||
/* MMIO write begin event in kernel. */
|
||||
if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
|
||||
perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) {
|
||||
mmio_event_get_key(evsel, sample, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
/* MMIO write end event in kernel. */
|
||||
if (kvm_entry_event(evsel))
|
||||
return true;
|
||||
|
||||
/* MMIO read end event in kernel.*/
|
||||
if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
|
||||
perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) {
|
||||
mmio_event_get_key(evsel, sample, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
|
||||
struct event_key *key,
|
||||
char *decode)
|
||||
{
|
||||
scnprintf(decode, DECODE_STR_LEN, "%#lx:%s",
|
||||
(unsigned long)key->key,
|
||||
key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R");
|
||||
}
|
||||
|
||||
static struct kvm_events_ops mmio_events = {
|
||||
.is_begin_event = mmio_event_begin,
|
||||
.is_end_event = mmio_event_end,
|
||||
.decode_key = mmio_event_decode_key,
|
||||
.name = "MMIO Access"
|
||||
};
|
||||
|
||||
/* The time of emulation pio access is from kvm_pio to kvm_entry. */
|
||||
static void ioport_event_get_key(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
key->key = perf_evsel__intval(evsel, sample, "port");
|
||||
key->info = perf_evsel__intval(evsel, sample, "rw");
|
||||
}
|
||||
|
||||
static bool ioport_event_begin(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
if (!strcmp(evsel->name, "kvm:kvm_pio")) {
|
||||
ioport_event_get_key(evsel, sample, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ioport_event_end(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct event_key *key __maybe_unused)
|
||||
{
|
||||
return kvm_entry_event(evsel);
|
||||
}
|
||||
|
||||
static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
|
||||
struct event_key *key,
|
||||
char *decode)
|
||||
{
|
||||
scnprintf(decode, DECODE_STR_LEN, "%#llx:%s",
|
||||
(unsigned long long)key->key,
|
||||
key->info ? "POUT" : "PIN");
|
||||
}
|
||||
|
||||
static struct kvm_events_ops ioport_events = {
|
||||
.is_begin_event = ioport_event_begin,
|
||||
.is_end_event = ioport_event_end,
|
||||
.decode_key = ioport_event_decode_key,
|
||||
.name = "IO Port Access"
|
||||
};
|
||||
|
||||
const char * const kvm_events_tp[] = {
|
||||
"kvm:kvm_entry",
|
||||
"kvm:kvm_exit",
|
||||
"kvm:kvm_mmio",
|
||||
"kvm:kvm_pio",
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct kvm_reg_events_ops kvm_reg_events_ops[] = {
|
||||
{ .name = "vmexit", .ops = &exit_events },
|
||||
{ .name = "mmio", .ops = &mmio_events },
|
||||
{ .name = "ioport", .ops = &ioport_events },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
const char * const kvm_skip_events[] = {
|
||||
"HLT",
|
||||
NULL,
|
||||
};
|
||||
|
||||
int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid)
|
||||
{
|
||||
if (strstr(cpuid, "Intel")) {
|
||||
kvm->exit_reasons = vmx_exit_reasons;
|
||||
kvm->exit_reasons_isa = "VMX";
|
||||
} else if (strstr(cpuid, "AMD")) {
|
||||
kvm->exit_reasons = svm_exit_reasons;
|
||||
kvm->exit_reasons_isa = "SVM";
|
||||
} else
|
||||
return -ENOTSUP;
|
||||
|
||||
return 0;
|
||||
}
|
48
tools/perf/arch/x86/util/tsc.c
Normal file
48
tools/perf/arch/x86/util/tsc.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/perf_event.h>
|
||||
|
||||
#include "../../perf.h"
|
||||
#include <linux/types.h>
|
||||
#include "../../util/debug.h"
|
||||
#include "../../util/tsc.h"
|
||||
#include "tsc.h"
|
||||
|
||||
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
|
||||
struct perf_tsc_conversion *tc)
|
||||
{
|
||||
bool cap_user_time_zero;
|
||||
u32 seq;
|
||||
int i = 0;
|
||||
|
||||
while (1) {
|
||||
seq = pc->lock;
|
||||
rmb();
|
||||
tc->time_mult = pc->time_mult;
|
||||
tc->time_shift = pc->time_shift;
|
||||
tc->time_zero = pc->time_zero;
|
||||
cap_user_time_zero = pc->cap_user_time_zero;
|
||||
rmb();
|
||||
if (pc->lock == seq && !(seq & 1))
|
||||
break;
|
||||
if (++i > 10000) {
|
||||
pr_debug("failed to get perf_event_mmap_page lock\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cap_user_time_zero)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 rdtsc(void)
|
||||
{
|
||||
unsigned int low, high;
|
||||
|
||||
asm volatile("rdtsc" : "=a" (low), "=d" (high));
|
||||
|
||||
return low | ((u64)high) << 32;
|
||||
}
|
17
tools/perf/arch/x86/util/tsc.h
Normal file
17
tools/perf/arch/x86/util/tsc.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
|
||||
#define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct perf_tsc_conversion {
|
||||
u16 time_shift;
|
||||
u32 time_mult;
|
||||
u64 time_zero;
|
||||
};
|
||||
|
||||
struct perf_event_mmap_page;
|
||||
|
||||
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
|
||||
struct perf_tsc_conversion *tc);
|
||||
|
||||
#endif /* TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ */
|
51
tools/perf/arch/x86/util/unwind-libdw.c
Normal file
51
tools/perf/arch/x86/util/unwind-libdw.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include <elfutils/libdwfl.h>
|
||||
#include "../../util/unwind-libdw.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = &ui->sample->user_regs;
|
||||
Dwarf_Word dwarf_regs[17];
|
||||
unsigned nregs;
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_X86_##r); \
|
||||
val; \
|
||||
})
|
||||
|
||||
if (user_regs->abi == PERF_SAMPLE_REGS_ABI_32) {
|
||||
dwarf_regs[0] = REG(AX);
|
||||
dwarf_regs[1] = REG(CX);
|
||||
dwarf_regs[2] = REG(DX);
|
||||
dwarf_regs[3] = REG(BX);
|
||||
dwarf_regs[4] = REG(SP);
|
||||
dwarf_regs[5] = REG(BP);
|
||||
dwarf_regs[6] = REG(SI);
|
||||
dwarf_regs[7] = REG(DI);
|
||||
dwarf_regs[8] = REG(IP);
|
||||
nregs = 9;
|
||||
} else {
|
||||
dwarf_regs[0] = REG(AX);
|
||||
dwarf_regs[1] = REG(DX);
|
||||
dwarf_regs[2] = REG(CX);
|
||||
dwarf_regs[3] = REG(BX);
|
||||
dwarf_regs[4] = REG(SI);
|
||||
dwarf_regs[5] = REG(DI);
|
||||
dwarf_regs[6] = REG(BP);
|
||||
dwarf_regs[7] = REG(SP);
|
||||
dwarf_regs[8] = REG(R8);
|
||||
dwarf_regs[9] = REG(R9);
|
||||
dwarf_regs[10] = REG(R10);
|
||||
dwarf_regs[11] = REG(R11);
|
||||
dwarf_regs[12] = REG(R12);
|
||||
dwarf_regs[13] = REG(R13);
|
||||
dwarf_regs[14] = REG(R14);
|
||||
dwarf_regs[15] = REG(R15);
|
||||
dwarf_regs[16] = REG(IP);
|
||||
nregs = 17;
|
||||
}
|
||||
|
||||
return dwfl_thread_state_registers(thread, 0, nregs, dwarf_regs);
|
||||
}
|
112
tools/perf/arch/x86/util/unwind-libunwind.c
Normal file
112
tools/perf/arch/x86/util/unwind-libunwind.c
Normal file
|
@ -0,0 +1,112 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <libunwind.h>
|
||||
#include "perf_regs.h"
|
||||
#include "../../util/unwind.h"
|
||||
#include "../../util/debug.h"
|
||||
|
||||
#ifdef HAVE_ARCH_X86_64_SUPPORT
|
||||
int libunwind__arch_reg_id(int regnum)
|
||||
{
|
||||
int id;
|
||||
|
||||
switch (regnum) {
|
||||
case UNW_X86_64_RAX:
|
||||
id = PERF_REG_X86_AX;
|
||||
break;
|
||||
case UNW_X86_64_RDX:
|
||||
id = PERF_REG_X86_DX;
|
||||
break;
|
||||
case UNW_X86_64_RCX:
|
||||
id = PERF_REG_X86_CX;
|
||||
break;
|
||||
case UNW_X86_64_RBX:
|
||||
id = PERF_REG_X86_BX;
|
||||
break;
|
||||
case UNW_X86_64_RSI:
|
||||
id = PERF_REG_X86_SI;
|
||||
break;
|
||||
case UNW_X86_64_RDI:
|
||||
id = PERF_REG_X86_DI;
|
||||
break;
|
||||
case UNW_X86_64_RBP:
|
||||
id = PERF_REG_X86_BP;
|
||||
break;
|
||||
case UNW_X86_64_RSP:
|
||||
id = PERF_REG_X86_SP;
|
||||
break;
|
||||
case UNW_X86_64_R8:
|
||||
id = PERF_REG_X86_R8;
|
||||
break;
|
||||
case UNW_X86_64_R9:
|
||||
id = PERF_REG_X86_R9;
|
||||
break;
|
||||
case UNW_X86_64_R10:
|
||||
id = PERF_REG_X86_R10;
|
||||
break;
|
||||
case UNW_X86_64_R11:
|
||||
id = PERF_REG_X86_R11;
|
||||
break;
|
||||
case UNW_X86_64_R12:
|
||||
id = PERF_REG_X86_R12;
|
||||
break;
|
||||
case UNW_X86_64_R13:
|
||||
id = PERF_REG_X86_R13;
|
||||
break;
|
||||
case UNW_X86_64_R14:
|
||||
id = PERF_REG_X86_R14;
|
||||
break;
|
||||
case UNW_X86_64_R15:
|
||||
id = PERF_REG_X86_R15;
|
||||
break;
|
||||
case UNW_X86_64_RIP:
|
||||
id = PERF_REG_X86_IP;
|
||||
break;
|
||||
default:
|
||||
pr_err("unwind: invalid reg id %d\n", regnum);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
#else
|
||||
int libunwind__arch_reg_id(int regnum)
|
||||
{
|
||||
int id;
|
||||
|
||||
switch (regnum) {
|
||||
case UNW_X86_EAX:
|
||||
id = PERF_REG_X86_AX;
|
||||
break;
|
||||
case UNW_X86_EDX:
|
||||
id = PERF_REG_X86_DX;
|
||||
break;
|
||||
case UNW_X86_ECX:
|
||||
id = PERF_REG_X86_CX;
|
||||
break;
|
||||
case UNW_X86_EBX:
|
||||
id = PERF_REG_X86_BX;
|
||||
break;
|
||||
case UNW_X86_ESI:
|
||||
id = PERF_REG_X86_SI;
|
||||
break;
|
||||
case UNW_X86_EDI:
|
||||
id = PERF_REG_X86_DI;
|
||||
break;
|
||||
case UNW_X86_EBP:
|
||||
id = PERF_REG_X86_BP;
|
||||
break;
|
||||
case UNW_X86_ESP:
|
||||
id = PERF_REG_X86_SP;
|
||||
break;
|
||||
case UNW_X86_EIP:
|
||||
id = PERF_REG_X86_IP;
|
||||
break;
|
||||
default:
|
||||
pr_err("unwind: invalid reg id %d\n", regnum);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
#endif /* HAVE_ARCH_X86_64_SUPPORT */
|
48
tools/perf/bench/bench.h
Normal file
48
tools/perf/bench/bench.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
#ifndef BENCH_H
|
||||
#define BENCH_H
|
||||
|
||||
/*
|
||||
* The madvise transparent hugepage constants were added in glibc
|
||||
* 2.13. For compatibility with older versions of glibc, define these
|
||||
* tokens if they are not already defined.
|
||||
*
|
||||
* PA-RISC uses different madvise values from other architectures and
|
||||
* needs to be special-cased.
|
||||
*/
|
||||
#ifdef __hppa__
|
||||
# ifndef MADV_HUGEPAGE
|
||||
# define MADV_HUGEPAGE 67
|
||||
# endif
|
||||
# ifndef MADV_NOHUGEPAGE
|
||||
# define MADV_NOHUGEPAGE 68
|
||||
# endif
|
||||
#else
|
||||
# ifndef MADV_HUGEPAGE
|
||||
# define MADV_HUGEPAGE 14
|
||||
# endif
|
||||
# ifndef MADV_NOHUGEPAGE
|
||||
# define MADV_NOHUGEPAGE 15
|
||||
# endif
|
||||
#endif
|
||||
|
||||
extern int bench_numa(int argc, const char **argv, const char *prefix);
|
||||
extern int bench_sched_messaging(int argc, const char **argv, const char *prefix);
|
||||
extern int bench_sched_pipe(int argc, const char **argv, const char *prefix);
|
||||
extern int bench_mem_memcpy(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused);
|
||||
extern int bench_mem_memset(int argc, const char **argv, const char *prefix);
|
||||
extern int bench_futex_hash(int argc, const char **argv, const char *prefix);
|
||||
extern int bench_futex_wake(int argc, const char **argv, const char *prefix);
|
||||
extern int bench_futex_requeue(int argc, const char **argv, const char *prefix);
|
||||
|
||||
#define BENCH_FORMAT_DEFAULT_STR "default"
|
||||
#define BENCH_FORMAT_DEFAULT 0
|
||||
#define BENCH_FORMAT_SIMPLE_STR "simple"
|
||||
#define BENCH_FORMAT_SIMPLE 1
|
||||
|
||||
#define BENCH_FORMAT_UNKNOWN -1
|
||||
|
||||
extern int bench_format;
|
||||
extern unsigned int bench_repeat;
|
||||
|
||||
#endif
|
215
tools/perf/bench/futex-hash.c
Normal file
215
tools/perf/bench/futex-hash.c
Normal file
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Davidlohr Bueso <davidlohr@hp.com>
|
||||
*
|
||||
* futex-hash: Stress the hell out of the Linux kernel futex uaddr hashing.
|
||||
*
|
||||
* This program is particularly useful for measuring the kernel's futex hash
|
||||
* table/function implementation. In order for it to make sense, use with as
|
||||
* many threads and futexes as possible.
|
||||
*/
|
||||
|
||||
#include "../perf.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/stat.h"
|
||||
#include "../util/parse-options.h"
|
||||
#include "../util/header.h"
|
||||
#include "bench.h"
|
||||
#include "futex.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static unsigned int nthreads = 0;
|
||||
static unsigned int nsecs = 10;
|
||||
/* amount of futexes per thread */
|
||||
static unsigned int nfutexes = 1024;
|
||||
static bool fshared = false, done = false, silent = false;
|
||||
static int futex_flag = 0;
|
||||
|
||||
struct timeval start, end, runtime;
|
||||
static pthread_mutex_t thread_lock;
|
||||
static unsigned int threads_starting;
|
||||
static struct stats throughput_stats;
|
||||
static pthread_cond_t thread_parent, thread_worker;
|
||||
|
||||
struct worker {
|
||||
int tid;
|
||||
u_int32_t *futex;
|
||||
pthread_t thread;
|
||||
unsigned long ops;
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"),
|
||||
OPT_UINTEGER('r', "runtime", &nsecs, "Specify runtime (in seconds)"),
|
||||
OPT_UINTEGER('f', "futexes", &nfutexes, "Specify amount of futexes per threads"),
|
||||
OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"),
|
||||
OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char * const bench_futex_hash_usage[] = {
|
||||
"perf bench futex hash <options>",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void *workerfn(void *arg)
|
||||
{
|
||||
int ret;
|
||||
unsigned int i;
|
||||
struct worker *w = (struct worker *) arg;
|
||||
|
||||
pthread_mutex_lock(&thread_lock);
|
||||
threads_starting--;
|
||||
if (!threads_starting)
|
||||
pthread_cond_signal(&thread_parent);
|
||||
pthread_cond_wait(&thread_worker, &thread_lock);
|
||||
pthread_mutex_unlock(&thread_lock);
|
||||
|
||||
do {
|
||||
for (i = 0; i < nfutexes; i++, w->ops++) {
|
||||
/*
|
||||
* We want the futex calls to fail in order to stress
|
||||
* the hashing of uaddr and not measure other steps,
|
||||
* such as internal waitqueue handling, thus enlarging
|
||||
* the critical region protected by hb->lock.
|
||||
*/
|
||||
ret = futex_wait(&w->futex[i], 1234, NULL, futex_flag);
|
||||
if (!silent &&
|
||||
(!ret || errno != EAGAIN || errno != EWOULDBLOCK))
|
||||
warn("Non-expected futex return call");
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void toggle_done(int sig __maybe_unused,
|
||||
siginfo_t *info __maybe_unused,
|
||||
void *uc __maybe_unused)
|
||||
{
|
||||
/* inform all threads that we're done for the day */
|
||||
done = true;
|
||||
gettimeofday(&end, NULL);
|
||||
timersub(&end, &start, &runtime);
|
||||
}
|
||||
|
||||
static void print_summary(void)
|
||||
{
|
||||
unsigned long avg = avg_stats(&throughput_stats);
|
||||
double stddev = stddev_stats(&throughput_stats);
|
||||
|
||||
printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
|
||||
!silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
|
||||
(int) runtime.tv_sec);
|
||||
}
|
||||
|
||||
int bench_futex_hash(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
int ret = 0;
|
||||
cpu_set_t cpu;
|
||||
struct sigaction act;
|
||||
unsigned int i, ncpus;
|
||||
pthread_attr_t thread_attr;
|
||||
struct worker *worker = NULL;
|
||||
|
||||
argc = parse_options(argc, argv, options, bench_futex_hash_usage, 0);
|
||||
if (argc) {
|
||||
usage_with_options(bench_futex_hash_usage, options);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_sigaction = toggle_done;
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
|
||||
if (!nthreads) /* default to the number of CPUs */
|
||||
nthreads = ncpus;
|
||||
|
||||
worker = calloc(nthreads, sizeof(*worker));
|
||||
if (!worker)
|
||||
goto errmem;
|
||||
|
||||
if (!fshared)
|
||||
futex_flag = FUTEX_PRIVATE_FLAG;
|
||||
|
||||
printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n",
|
||||
getpid(), nthreads, nfutexes, fshared ? "shared":"private", nsecs);
|
||||
|
||||
init_stats(&throughput_stats);
|
||||
pthread_mutex_init(&thread_lock, NULL);
|
||||
pthread_cond_init(&thread_parent, NULL);
|
||||
pthread_cond_init(&thread_worker, NULL);
|
||||
|
||||
threads_starting = nthreads;
|
||||
pthread_attr_init(&thread_attr);
|
||||
gettimeofday(&start, NULL);
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
worker[i].tid = i;
|
||||
worker[i].futex = calloc(nfutexes, sizeof(*worker[i].futex));
|
||||
if (!worker[i].futex)
|
||||
goto errmem;
|
||||
|
||||
CPU_ZERO(&cpu);
|
||||
CPU_SET(i % ncpus, &cpu);
|
||||
|
||||
ret = pthread_attr_setaffinity_np(&thread_attr, sizeof(cpu_set_t), &cpu);
|
||||
if (ret)
|
||||
err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
|
||||
|
||||
ret = pthread_create(&worker[i].thread, &thread_attr, workerfn,
|
||||
(void *)(struct worker *) &worker[i]);
|
||||
if (ret)
|
||||
err(EXIT_FAILURE, "pthread_create");
|
||||
|
||||
}
|
||||
pthread_attr_destroy(&thread_attr);
|
||||
|
||||
pthread_mutex_lock(&thread_lock);
|
||||
while (threads_starting)
|
||||
pthread_cond_wait(&thread_parent, &thread_lock);
|
||||
pthread_cond_broadcast(&thread_worker);
|
||||
pthread_mutex_unlock(&thread_lock);
|
||||
|
||||
sleep(nsecs);
|
||||
toggle_done(0, NULL, NULL);
|
||||
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
ret = pthread_join(worker[i].thread, NULL);
|
||||
if (ret)
|
||||
err(EXIT_FAILURE, "pthread_join");
|
||||
}
|
||||
|
||||
/* cleanup & report results */
|
||||
pthread_cond_destroy(&thread_parent);
|
||||
pthread_cond_destroy(&thread_worker);
|
||||
pthread_mutex_destroy(&thread_lock);
|
||||
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
unsigned long t = worker[i].ops/runtime.tv_sec;
|
||||
update_stats(&throughput_stats, t);
|
||||
if (!silent) {
|
||||
if (nfutexes == 1)
|
||||
printf("[thread %2d] futex: %p [ %ld ops/sec ]\n",
|
||||
worker[i].tid, &worker[i].futex[0], t);
|
||||
else
|
||||
printf("[thread %2d] futexes: %p ... %p [ %ld ops/sec ]\n",
|
||||
worker[i].tid, &worker[i].futex[0],
|
||||
&worker[i].futex[nfutexes-1], t);
|
||||
}
|
||||
|
||||
free(worker[i].futex);
|
||||
}
|
||||
|
||||
print_summary();
|
||||
|
||||
free(worker);
|
||||
return ret;
|
||||
errmem:
|
||||
err(EXIT_FAILURE, "calloc");
|
||||
}
|
211
tools/perf/bench/futex-requeue.c
Normal file
211
tools/perf/bench/futex-requeue.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Davidlohr Bueso <davidlohr@hp.com>
|
||||
*
|
||||
* futex-requeue: Block a bunch of threads on futex1 and requeue them
|
||||
* on futex2, N at a time.
|
||||
*
|
||||
* This program is particularly useful to measure the latency of nthread
|
||||
* requeues without waking up any tasks -- thus mimicking a regular futex_wait.
|
||||
*/
|
||||
|
||||
#include "../perf.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/stat.h"
|
||||
#include "../util/parse-options.h"
|
||||
#include "../util/header.h"
|
||||
#include "bench.h"
|
||||
#include "futex.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static u_int32_t futex1 = 0, futex2 = 0;
|
||||
|
||||
/*
|
||||
* How many tasks to requeue at a time.
|
||||
* Default to 1 in order to make the kernel work more.
|
||||
*/
|
||||
static unsigned int nrequeue = 1;
|
||||
|
||||
static pthread_t *worker;
|
||||
static bool done = false, silent = false, fshared = false;
|
||||
static pthread_mutex_t thread_lock;
|
||||
static pthread_cond_t thread_parent, thread_worker;
|
||||
static struct stats requeuetime_stats, requeued_stats;
|
||||
static unsigned int ncpus, threads_starting, nthreads = 0;
|
||||
static int futex_flag = 0;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"),
|
||||
OPT_UINTEGER('q', "nrequeue", &nrequeue, "Specify amount of threads to requeue at once"),
|
||||
OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"),
|
||||
OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char * const bench_futex_requeue_usage[] = {
|
||||
"perf bench futex requeue <options>",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void print_summary(void)
|
||||
{
|
||||
double requeuetime_avg = avg_stats(&requeuetime_stats);
|
||||
double requeuetime_stddev = stddev_stats(&requeuetime_stats);
|
||||
unsigned int requeued_avg = avg_stats(&requeued_stats);
|
||||
|
||||
printf("Requeued %d of %d threads in %.4f ms (+-%.2f%%)\n",
|
||||
requeued_avg,
|
||||
nthreads,
|
||||
requeuetime_avg/1e3,
|
||||
rel_stddev_stats(requeuetime_stddev, requeuetime_avg));
|
||||
}
|
||||
|
||||
static void *workerfn(void *arg __maybe_unused)
|
||||
{
|
||||
pthread_mutex_lock(&thread_lock);
|
||||
threads_starting--;
|
||||
if (!threads_starting)
|
||||
pthread_cond_signal(&thread_parent);
|
||||
pthread_cond_wait(&thread_worker, &thread_lock);
|
||||
pthread_mutex_unlock(&thread_lock);
|
||||
|
||||
futex_wait(&futex1, 0, NULL, futex_flag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void block_threads(pthread_t *w,
|
||||
pthread_attr_t thread_attr)
|
||||
{
|
||||
cpu_set_t cpu;
|
||||
unsigned int i;
|
||||
|
||||
threads_starting = nthreads;
|
||||
|
||||
/* create and block all threads */
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
CPU_ZERO(&cpu);
|
||||
CPU_SET(i % ncpus, &cpu);
|
||||
|
||||
if (pthread_attr_setaffinity_np(&thread_attr, sizeof(cpu_set_t), &cpu))
|
||||
err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
|
||||
|
||||
if (pthread_create(&w[i], &thread_attr, workerfn, NULL))
|
||||
err(EXIT_FAILURE, "pthread_create");
|
||||
}
|
||||
}
|
||||
|
||||
static void toggle_done(int sig __maybe_unused,
|
||||
siginfo_t *info __maybe_unused,
|
||||
void *uc __maybe_unused)
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
|
||||
int bench_futex_requeue(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int i, j;
|
||||
struct sigaction act;
|
||||
pthread_attr_t thread_attr;
|
||||
|
||||
argc = parse_options(argc, argv, options, bench_futex_requeue_usage, 0);
|
||||
if (argc)
|
||||
goto err;
|
||||
|
||||
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_sigaction = toggle_done;
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
|
||||
if (!nthreads)
|
||||
nthreads = ncpus;
|
||||
|
||||
worker = calloc(nthreads, sizeof(*worker));
|
||||
if (!worker)
|
||||
err(EXIT_FAILURE, "calloc");
|
||||
|
||||
if (!fshared)
|
||||
futex_flag = FUTEX_PRIVATE_FLAG;
|
||||
|
||||
printf("Run summary [PID %d]: Requeuing %d threads (from [%s] %p to %p), "
|
||||
"%d at a time.\n\n", getpid(), nthreads,
|
||||
fshared ? "shared":"private", &futex1, &futex2, nrequeue);
|
||||
|
||||
init_stats(&requeued_stats);
|
||||
init_stats(&requeuetime_stats);
|
||||
pthread_attr_init(&thread_attr);
|
||||
pthread_mutex_init(&thread_lock, NULL);
|
||||
pthread_cond_init(&thread_parent, NULL);
|
||||
pthread_cond_init(&thread_worker, NULL);
|
||||
|
||||
for (j = 0; j < bench_repeat && !done; j++) {
|
||||
unsigned int nrequeued = 0;
|
||||
struct timeval start, end, runtime;
|
||||
|
||||
/* create, launch & block all threads */
|
||||
block_threads(worker, thread_attr);
|
||||
|
||||
/* make sure all threads are already blocked */
|
||||
pthread_mutex_lock(&thread_lock);
|
||||
while (threads_starting)
|
||||
pthread_cond_wait(&thread_parent, &thread_lock);
|
||||
pthread_cond_broadcast(&thread_worker);
|
||||
pthread_mutex_unlock(&thread_lock);
|
||||
|
||||
usleep(100000);
|
||||
|
||||
/* Ok, all threads are patiently blocked, start requeueing */
|
||||
gettimeofday(&start, NULL);
|
||||
for (nrequeued = 0; nrequeued < nthreads; nrequeued += nrequeue) {
|
||||
/*
|
||||
* Do not wakeup any tasks blocked on futex1, allowing
|
||||
* us to really measure futex_wait functionality.
|
||||
*/
|
||||
futex_cmp_requeue(&futex1, 0, &futex2, 0,
|
||||
nrequeue, futex_flag);
|
||||
}
|
||||
gettimeofday(&end, NULL);
|
||||
timersub(&end, &start, &runtime);
|
||||
|
||||
if (nrequeued > nthreads)
|
||||
nrequeued = nthreads;
|
||||
|
||||
update_stats(&requeued_stats, nrequeued);
|
||||
update_stats(&requeuetime_stats, runtime.tv_usec);
|
||||
|
||||
if (!silent) {
|
||||
printf("[Run %d]: Requeued %d of %d threads in %.4f ms\n",
|
||||
j + 1, nrequeued, nthreads, runtime.tv_usec/1e3);
|
||||
}
|
||||
|
||||
/* everybody should be blocked on futex2, wake'em up */
|
||||
nrequeued = futex_wake(&futex2, nthreads, futex_flag);
|
||||
if (nthreads != nrequeued)
|
||||
warnx("couldn't wakeup all tasks (%d/%d)", nrequeued, nthreads);
|
||||
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
ret = pthread_join(worker[i], NULL);
|
||||
if (ret)
|
||||
err(EXIT_FAILURE, "pthread_join");
|
||||
}
|
||||
}
|
||||
|
||||
/* cleanup & report results */
|
||||
pthread_cond_destroy(&thread_parent);
|
||||
pthread_cond_destroy(&thread_worker);
|
||||
pthread_mutex_destroy(&thread_lock);
|
||||
pthread_attr_destroy(&thread_attr);
|
||||
|
||||
print_summary();
|
||||
|
||||
free(worker);
|
||||
return ret;
|
||||
err:
|
||||
usage_with_options(bench_futex_requeue_usage, options);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
198
tools/perf/bench/futex-wake.c
Normal file
198
tools/perf/bench/futex-wake.c
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Davidlohr Bueso <davidlohr@hp.com>
|
||||
*
|
||||
* futex-wake: Block a bunch of threads on a futex and wake'em up, N at a time.
|
||||
*
|
||||
* This program is particularly useful to measure the latency of nthread wakeups
|
||||
* in non-error situations: all waiters are queued and all wake calls wakeup
|
||||
* one or more tasks, and thus the waitqueue is never empty.
|
||||
*/
|
||||
|
||||
#include "../perf.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/stat.h"
|
||||
#include "../util/parse-options.h"
|
||||
#include "../util/header.h"
|
||||
#include "bench.h"
|
||||
#include "futex.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/* all threads will block on the same futex */
|
||||
static u_int32_t futex1 = 0;
|
||||
|
||||
/*
|
||||
* How many wakeups to do at a time.
|
||||
* Default to 1 in order to make the kernel work more.
|
||||
*/
|
||||
static unsigned int nwakes = 1;
|
||||
|
||||
pthread_t *worker;
|
||||
static bool done = false, silent = false, fshared = false;
|
||||
static pthread_mutex_t thread_lock;
|
||||
static pthread_cond_t thread_parent, thread_worker;
|
||||
static struct stats waketime_stats, wakeup_stats;
|
||||
static unsigned int ncpus, threads_starting, nthreads = 0;
|
||||
static int futex_flag = 0;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"),
|
||||
OPT_UINTEGER('w', "nwakes", &nwakes, "Specify amount of threads to wake at once"),
|
||||
OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"),
|
||||
OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char * const bench_futex_wake_usage[] = {
|
||||
"perf bench futex wake <options>",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void *workerfn(void *arg __maybe_unused)
|
||||
{
|
||||
pthread_mutex_lock(&thread_lock);
|
||||
threads_starting--;
|
||||
if (!threads_starting)
|
||||
pthread_cond_signal(&thread_parent);
|
||||
pthread_cond_wait(&thread_worker, &thread_lock);
|
||||
pthread_mutex_unlock(&thread_lock);
|
||||
|
||||
futex_wait(&futex1, 0, NULL, futex_flag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void print_summary(void)
|
||||
{
|
||||
double waketime_avg = avg_stats(&waketime_stats);
|
||||
double waketime_stddev = stddev_stats(&waketime_stats);
|
||||
unsigned int wakeup_avg = avg_stats(&wakeup_stats);
|
||||
|
||||
printf("Wokeup %d of %d threads in %.4f ms (+-%.2f%%)\n",
|
||||
wakeup_avg,
|
||||
nthreads,
|
||||
waketime_avg/1e3,
|
||||
rel_stddev_stats(waketime_stddev, waketime_avg));
|
||||
}
|
||||
|
||||
static void block_threads(pthread_t *w,
|
||||
pthread_attr_t thread_attr)
|
||||
{
|
||||
cpu_set_t cpu;
|
||||
unsigned int i;
|
||||
|
||||
threads_starting = nthreads;
|
||||
|
||||
/* create and block all threads */
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
CPU_ZERO(&cpu);
|
||||
CPU_SET(i % ncpus, &cpu);
|
||||
|
||||
if (pthread_attr_setaffinity_np(&thread_attr, sizeof(cpu_set_t), &cpu))
|
||||
err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
|
||||
|
||||
if (pthread_create(&w[i], &thread_attr, workerfn, NULL))
|
||||
err(EXIT_FAILURE, "pthread_create");
|
||||
}
|
||||
}
|
||||
|
||||
static void toggle_done(int sig __maybe_unused,
|
||||
siginfo_t *info __maybe_unused,
|
||||
void *uc __maybe_unused)
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
|
||||
int bench_futex_wake(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int i, j;
|
||||
struct sigaction act;
|
||||
pthread_attr_t thread_attr;
|
||||
|
||||
argc = parse_options(argc, argv, options, bench_futex_wake_usage, 0);
|
||||
if (argc) {
|
||||
usage_with_options(bench_futex_wake_usage, options);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_sigaction = toggle_done;
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
|
||||
if (!nthreads)
|
||||
nthreads = ncpus;
|
||||
|
||||
worker = calloc(nthreads, sizeof(*worker));
|
||||
if (!worker)
|
||||
err(EXIT_FAILURE, "calloc");
|
||||
|
||||
if (!fshared)
|
||||
futex_flag = FUTEX_PRIVATE_FLAG;
|
||||
|
||||
printf("Run summary [PID %d]: blocking on %d threads (at [%s] futex %p), "
|
||||
"waking up %d at a time.\n\n",
|
||||
getpid(), nthreads, fshared ? "shared":"private", &futex1, nwakes);
|
||||
|
||||
init_stats(&wakeup_stats);
|
||||
init_stats(&waketime_stats);
|
||||
pthread_attr_init(&thread_attr);
|
||||
pthread_mutex_init(&thread_lock, NULL);
|
||||
pthread_cond_init(&thread_parent, NULL);
|
||||
pthread_cond_init(&thread_worker, NULL);
|
||||
|
||||
for (j = 0; j < bench_repeat && !done; j++) {
|
||||
unsigned int nwoken = 0;
|
||||
struct timeval start, end, runtime;
|
||||
|
||||
/* create, launch & block all threads */
|
||||
block_threads(worker, thread_attr);
|
||||
|
||||
/* make sure all threads are already blocked */
|
||||
pthread_mutex_lock(&thread_lock);
|
||||
while (threads_starting)
|
||||
pthread_cond_wait(&thread_parent, &thread_lock);
|
||||
pthread_cond_broadcast(&thread_worker);
|
||||
pthread_mutex_unlock(&thread_lock);
|
||||
|
||||
usleep(100000);
|
||||
|
||||
/* Ok, all threads are patiently blocked, start waking folks up */
|
||||
gettimeofday(&start, NULL);
|
||||
while (nwoken != nthreads)
|
||||
nwoken += futex_wake(&futex1, nwakes, futex_flag);
|
||||
gettimeofday(&end, NULL);
|
||||
timersub(&end, &start, &runtime);
|
||||
|
||||
update_stats(&wakeup_stats, nwoken);
|
||||
update_stats(&waketime_stats, runtime.tv_usec);
|
||||
|
||||
if (!silent) {
|
||||
printf("[Run %d]: Wokeup %d of %d threads in %.4f ms\n",
|
||||
j + 1, nwoken, nthreads, runtime.tv_usec/1e3);
|
||||
}
|
||||
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
ret = pthread_join(worker[i], NULL);
|
||||
if (ret)
|
||||
err(EXIT_FAILURE, "pthread_join");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* cleanup & report results */
|
||||
pthread_cond_destroy(&thread_parent);
|
||||
pthread_cond_destroy(&thread_worker);
|
||||
pthread_mutex_destroy(&thread_lock);
|
||||
pthread_attr_destroy(&thread_attr);
|
||||
|
||||
print_summary();
|
||||
|
||||
free(worker);
|
||||
return ret;
|
||||
}
|
71
tools/perf/bench/futex.h
Normal file
71
tools/perf/bench/futex.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Glibc independent futex library for testing kernel functionality.
|
||||
* Shamelessly stolen from Darren Hart <dvhltc@us.ibm.com>
|
||||
* http://git.kernel.org/cgit/linux/kernel/git/dvhart/futextest.git/
|
||||
*/
|
||||
|
||||
#ifndef _FUTEX_H
|
||||
#define _FUTEX_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/futex.h>
|
||||
|
||||
/**
|
||||
* futex() - SYS_futex syscall wrapper
|
||||
* @uaddr: address of first futex
|
||||
* @op: futex op code
|
||||
* @val: typically expected value of uaddr, but varies by op
|
||||
* @timeout: typically an absolute struct timespec (except where noted
|
||||
* otherwise). Overloaded by some ops
|
||||
* @uaddr2: address of second futex for some ops\
|
||||
* @val3: varies by op
|
||||
* @opflags: flags to be bitwise OR'd with op, such as FUTEX_PRIVATE_FLAG
|
||||
*
|
||||
* futex() is used by all the following futex op wrappers. It can also be
|
||||
* used for misuse and abuse testing. Generally, the specific op wrappers
|
||||
* should be used instead. It is a macro instead of an static inline function as
|
||||
* some of the types over overloaded (timeout is used for nr_requeue for
|
||||
* example).
|
||||
*
|
||||
* These argument descriptions are the defaults for all
|
||||
* like-named arguments in the following wrappers except where noted below.
|
||||
*/
|
||||
#define futex(uaddr, op, val, timeout, uaddr2, val3, opflags) \
|
||||
syscall(SYS_futex, uaddr, op | opflags, val, timeout, uaddr2, val3)
|
||||
|
||||
/**
|
||||
* futex_wait() - block on uaddr with optional timeout
|
||||
* @timeout: relative timeout
|
||||
*/
|
||||
static inline int
|
||||
futex_wait(u_int32_t *uaddr, u_int32_t val, struct timespec *timeout, int opflags)
|
||||
{
|
||||
return futex(uaddr, FUTEX_WAIT, val, timeout, NULL, 0, opflags);
|
||||
}
|
||||
|
||||
/**
|
||||
* futex_wake() - wake one or more tasks blocked on uaddr
|
||||
* @nr_wake: wake up to this many tasks
|
||||
*/
|
||||
static inline int
|
||||
futex_wake(u_int32_t *uaddr, int nr_wake, int opflags)
|
||||
{
|
||||
return futex(uaddr, FUTEX_WAKE, nr_wake, NULL, NULL, 0, opflags);
|
||||
}
|
||||
|
||||
/**
|
||||
* futex_cmp_requeue() - requeue tasks from uaddr to uaddr2
|
||||
* @nr_wake: wake up to this many tasks
|
||||
* @nr_requeue: requeue up to this many tasks
|
||||
*/
|
||||
static inline int
|
||||
futex_cmp_requeue(u_int32_t *uaddr, u_int32_t val, u_int32_t *uaddr2, int nr_wake,
|
||||
int nr_requeue, int opflags)
|
||||
{
|
||||
return futex(uaddr, FUTEX_CMP_REQUEUE, nr_wake, nr_requeue, uaddr2,
|
||||
val, opflags);
|
||||
}
|
||||
|
||||
#endif /* _FUTEX_H */
|
12
tools/perf/bench/mem-memcpy-arch.h
Normal file
12
tools/perf/bench/mem-memcpy-arch.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
#ifdef HAVE_ARCH_X86_64_SUPPORT
|
||||
|
||||
#define MEMCPY_FN(fn, name, desc) \
|
||||
extern void *fn(void *, const void *, size_t);
|
||||
|
||||
#include "mem-memcpy-x86-64-asm-def.h"
|
||||
|
||||
#undef MEMCPY_FN
|
||||
|
||||
#endif
|
||||
|
12
tools/perf/bench/mem-memcpy-x86-64-asm-def.h
Normal file
12
tools/perf/bench/mem-memcpy-x86-64-asm-def.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
MEMCPY_FN(__memcpy,
|
||||
"x86-64-unrolled",
|
||||
"unrolled memcpy() in arch/x86/lib/memcpy_64.S")
|
||||
|
||||
MEMCPY_FN(memcpy_c,
|
||||
"x86-64-movsq",
|
||||
"movsq-based memcpy() in arch/x86/lib/memcpy_64.S")
|
||||
|
||||
MEMCPY_FN(memcpy_c_e,
|
||||
"x86-64-movsb",
|
||||
"movsb-based memcpy() in arch/x86/lib/memcpy_64.S")
|
12
tools/perf/bench/mem-memcpy-x86-64-asm.S
Normal file
12
tools/perf/bench/mem-memcpy-x86-64-asm.S
Normal file
|
@ -0,0 +1,12 @@
|
|||
#define memcpy MEMCPY /* don't hide glibc's memcpy() */
|
||||
#define altinstr_replacement text
|
||||
#define globl p2align 4; .globl
|
||||
#define Lmemcpy_c globl memcpy_c; memcpy_c
|
||||
#define Lmemcpy_c_e globl memcpy_c_e; memcpy_c_e
|
||||
#include "../../../arch/x86/lib/memcpy_64.S"
|
||||
/*
|
||||
* We need to provide note.GNU-stack section, saying that we want
|
||||
* NOT executable stack. Otherwise the final linking will assume that
|
||||
* the ELF stack should not be restricted at all and set it RWX.
|
||||
*/
|
||||
.section .note.GNU-stack,"",@progbits
|
312
tools/perf/bench/mem-memcpy.c
Normal file
312
tools/perf/bench/mem-memcpy.c
Normal file
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* mem-memcpy.c
|
||||
*
|
||||
* memcpy: Simple memory copy in various ways
|
||||
*
|
||||
* Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
|
||||
*/
|
||||
|
||||
#include "../perf.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/parse-options.h"
|
||||
#include "../util/header.h"
|
||||
#include "../util/cloexec.h"
|
||||
#include "bench.h"
|
||||
#include "mem-memcpy-arch.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define K 1024
|
||||
|
||||
static const char *length_str = "1MB";
|
||||
static const char *routine = "default";
|
||||
static int iterations = 1;
|
||||
static bool use_cycle;
|
||||
static int cycle_fd;
|
||||
static bool only_prefault;
|
||||
static bool no_prefault;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_STRING('l', "length", &length_str, "1MB",
|
||||
"Specify length of memory to copy. "
|
||||
"Available units: B, KB, MB, GB and TB (upper and lower)"),
|
||||
OPT_STRING('r', "routine", &routine, "default",
|
||||
"Specify routine to copy"),
|
||||
OPT_INTEGER('i', "iterations", &iterations,
|
||||
"repeat memcpy() invocation this number of times"),
|
||||
OPT_BOOLEAN('c', "cycle", &use_cycle,
|
||||
"Use cycles event instead of gettimeofday() for measuring"),
|
||||
OPT_BOOLEAN('o', "only-prefault", &only_prefault,
|
||||
"Show only the result with page faults before memcpy()"),
|
||||
OPT_BOOLEAN('n', "no-prefault", &no_prefault,
|
||||
"Show only the result without page faults before memcpy()"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
typedef void *(*memcpy_t)(void *, const void *, size_t);
|
||||
|
||||
struct routine {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
memcpy_t fn;
|
||||
};
|
||||
|
||||
struct routine routines[] = {
|
||||
{ "default",
|
||||
"Default memcpy() provided by glibc",
|
||||
memcpy },
|
||||
#ifdef HAVE_ARCH_X86_64_SUPPORT
|
||||
|
||||
#define MEMCPY_FN(fn, name, desc) { name, desc, fn },
|
||||
#include "mem-memcpy-x86-64-asm-def.h"
|
||||
#undef MEMCPY_FN
|
||||
|
||||
#endif
|
||||
|
||||
{ NULL,
|
||||
NULL,
|
||||
NULL }
|
||||
};
|
||||
|
||||
static const char * const bench_mem_memcpy_usage[] = {
|
||||
"perf bench mem memcpy <options>",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct perf_event_attr cycle_attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES
|
||||
};
|
||||
|
||||
static void init_cycle(void)
|
||||
{
|
||||
cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1,
|
||||
perf_event_open_cloexec_flag());
|
||||
|
||||
if (cycle_fd < 0 && errno == ENOSYS)
|
||||
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
|
||||
else
|
||||
BUG_ON(cycle_fd < 0);
|
||||
}
|
||||
|
||||
static u64 get_cycle(void)
|
||||
{
|
||||
int ret;
|
||||
u64 clk;
|
||||
|
||||
ret = read(cycle_fd, &clk, sizeof(u64));
|
||||
BUG_ON(ret != sizeof(u64));
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static double timeval2double(struct timeval *ts)
|
||||
{
|
||||
return (double)ts->tv_sec +
|
||||
(double)ts->tv_usec / (double)1000000;
|
||||
}
|
||||
|
||||
static void alloc_mem(void **dst, void **src, size_t length)
|
||||
{
|
||||
*dst = zalloc(length);
|
||||
if (!*dst)
|
||||
die("memory allocation failed - maybe length is too large?\n");
|
||||
|
||||
*src = zalloc(length);
|
||||
if (!*src)
|
||||
die("memory allocation failed - maybe length is too large?\n");
|
||||
/* Make sure to always replace the zero pages even if MMAP_THRESH is crossed */
|
||||
memset(*src, 0, length);
|
||||
}
|
||||
|
||||
static u64 do_memcpy_cycle(memcpy_t fn, size_t len, bool prefault)
|
||||
{
|
||||
u64 cycle_start = 0ULL, cycle_end = 0ULL;
|
||||
void *src = NULL, *dst = NULL;
|
||||
int i;
|
||||
|
||||
alloc_mem(&src, &dst, len);
|
||||
|
||||
if (prefault)
|
||||
fn(dst, src, len);
|
||||
|
||||
cycle_start = get_cycle();
|
||||
for (i = 0; i < iterations; ++i)
|
||||
fn(dst, src, len);
|
||||
cycle_end = get_cycle();
|
||||
|
||||
free(src);
|
||||
free(dst);
|
||||
return cycle_end - cycle_start;
|
||||
}
|
||||
|
||||
static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault)
|
||||
{
|
||||
struct timeval tv_start, tv_end, tv_diff;
|
||||
void *src = NULL, *dst = NULL;
|
||||
int i;
|
||||
|
||||
alloc_mem(&src, &dst, len);
|
||||
|
||||
if (prefault)
|
||||
fn(dst, src, len);
|
||||
|
||||
BUG_ON(gettimeofday(&tv_start, NULL));
|
||||
for (i = 0; i < iterations; ++i)
|
||||
fn(dst, src, len);
|
||||
BUG_ON(gettimeofday(&tv_end, NULL));
|
||||
|
||||
timersub(&tv_end, &tv_start, &tv_diff);
|
||||
|
||||
free(src);
|
||||
free(dst);
|
||||
return (double)((double)len / timeval2double(&tv_diff));
|
||||
}
|
||||
|
||||
#define pf (no_prefault ? 0 : 1)
|
||||
|
||||
#define print_bps(x) do { \
|
||||
if (x < K) \
|
||||
printf(" %14lf B/Sec", x); \
|
||||
else if (x < K * K) \
|
||||
printf(" %14lfd KB/Sec", x / K); \
|
||||
else if (x < K * K * K) \
|
||||
printf(" %14lf MB/Sec", x / K / K); \
|
||||
else \
|
||||
printf(" %14lf GB/Sec", x / K / K / K); \
|
||||
} while (0)
|
||||
|
||||
int bench_mem_memcpy(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
int i;
|
||||
size_t len;
|
||||
double result_bps[2];
|
||||
u64 result_cycle[2];
|
||||
|
||||
argc = parse_options(argc, argv, options,
|
||||
bench_mem_memcpy_usage, 0);
|
||||
|
||||
if (no_prefault && only_prefault) {
|
||||
fprintf(stderr, "Invalid options: -o and -n are mutually exclusive\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (use_cycle)
|
||||
init_cycle();
|
||||
|
||||
len = (size_t)perf_atoll((char *)length_str);
|
||||
|
||||
result_cycle[0] = result_cycle[1] = 0ULL;
|
||||
result_bps[0] = result_bps[1] = 0.0;
|
||||
|
||||
if ((s64)len <= 0) {
|
||||
fprintf(stderr, "Invalid length:%s\n", length_str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* same to without specifying either of prefault and no-prefault */
|
||||
if (only_prefault && no_prefault)
|
||||
only_prefault = no_prefault = false;
|
||||
|
||||
for (i = 0; routines[i].name; i++) {
|
||||
if (!strcmp(routines[i].name, routine))
|
||||
break;
|
||||
}
|
||||
if (!routines[i].name) {
|
||||
printf("Unknown routine:%s\n", routine);
|
||||
printf("Available routines...\n");
|
||||
for (i = 0; routines[i].name; i++) {
|
||||
printf("\t%s ... %s\n",
|
||||
routines[i].name, routines[i].desc);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (bench_format == BENCH_FORMAT_DEFAULT)
|
||||
printf("# Copying %s Bytes ...\n\n", length_str);
|
||||
|
||||
if (!only_prefault && !no_prefault) {
|
||||
/* show both of results */
|
||||
if (use_cycle) {
|
||||
result_cycle[0] =
|
||||
do_memcpy_cycle(routines[i].fn, len, false);
|
||||
result_cycle[1] =
|
||||
do_memcpy_cycle(routines[i].fn, len, true);
|
||||
} else {
|
||||
result_bps[0] =
|
||||
do_memcpy_gettimeofday(routines[i].fn,
|
||||
len, false);
|
||||
result_bps[1] =
|
||||
do_memcpy_gettimeofday(routines[i].fn,
|
||||
len, true);
|
||||
}
|
||||
} else {
|
||||
if (use_cycle) {
|
||||
result_cycle[pf] =
|
||||
do_memcpy_cycle(routines[i].fn,
|
||||
len, only_prefault);
|
||||
} else {
|
||||
result_bps[pf] =
|
||||
do_memcpy_gettimeofday(routines[i].fn,
|
||||
len, only_prefault);
|
||||
}
|
||||
}
|
||||
|
||||
switch (bench_format) {
|
||||
case BENCH_FORMAT_DEFAULT:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte\n",
|
||||
(double)result_cycle[0]
|
||||
/ (double)len);
|
||||
printf(" %14lf Cycle/Byte (with prefault)\n",
|
||||
(double)result_cycle[1]
|
||||
/ (double)len);
|
||||
} else {
|
||||
print_bps(result_bps[0]);
|
||||
printf("\n");
|
||||
print_bps(result_bps[1]);
|
||||
printf(" (with prefault)\n");
|
||||
}
|
||||
} else {
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte",
|
||||
(double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
print_bps(result_bps[pf]);
|
||||
|
||||
printf("%s\n", only_prefault ? " (with prefault)" : "");
|
||||
}
|
||||
break;
|
||||
case BENCH_FORMAT_SIMPLE:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_cycle) {
|
||||
printf("%lf %lf\n",
|
||||
(double)result_cycle[0] / (double)len,
|
||||
(double)result_cycle[1] / (double)len);
|
||||
} else {
|
||||
printf("%lf %lf\n",
|
||||
result_bps[0], result_bps[1]);
|
||||
}
|
||||
} else {
|
||||
if (use_cycle) {
|
||||
printf("%lf\n", (double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
printf("%lf\n", result_bps[pf]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* reaching this means there's some disaster: */
|
||||
die("unknown format: %d\n", bench_format);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
12
tools/perf/bench/mem-memset-arch.h
Normal file
12
tools/perf/bench/mem-memset-arch.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
#ifdef HAVE_ARCH_X86_64_SUPPORT
|
||||
|
||||
#define MEMSET_FN(fn, name, desc) \
|
||||
extern void *fn(void *, int, size_t);
|
||||
|
||||
#include "mem-memset-x86-64-asm-def.h"
|
||||
|
||||
#undef MEMSET_FN
|
||||
|
||||
#endif
|
||||
|
12
tools/perf/bench/mem-memset-x86-64-asm-def.h
Normal file
12
tools/perf/bench/mem-memset-x86-64-asm-def.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
MEMSET_FN(__memset,
|
||||
"x86-64-unrolled",
|
||||
"unrolled memset() in arch/x86/lib/memset_64.S")
|
||||
|
||||
MEMSET_FN(memset_c,
|
||||
"x86-64-stosq",
|
||||
"movsq-based memset() in arch/x86/lib/memset_64.S")
|
||||
|
||||
MEMSET_FN(memset_c_e,
|
||||
"x86-64-stosb",
|
||||
"movsb-based memset() in arch/x86/lib/memset_64.S")
|
13
tools/perf/bench/mem-memset-x86-64-asm.S
Normal file
13
tools/perf/bench/mem-memset-x86-64-asm.S
Normal file
|
@ -0,0 +1,13 @@
|
|||
#define memset MEMSET /* don't hide glibc's memset() */
|
||||
#define altinstr_replacement text
|
||||
#define globl p2align 4; .globl
|
||||
#define Lmemset_c globl memset_c; memset_c
|
||||
#define Lmemset_c_e globl memset_c_e; memset_c_e
|
||||
#include "../../../arch/x86/lib/memset_64.S"
|
||||
|
||||
/*
|
||||
* We need to provide note.GNU-stack section, saying that we want
|
||||
* NOT executable stack. Otherwise the final linking will assume that
|
||||
* the ELF stack should not be restricted at all and set it RWX.
|
||||
*/
|
||||
.section .note.GNU-stack,"",@progbits
|
304
tools/perf/bench/mem-memset.c
Normal file
304
tools/perf/bench/mem-memset.c
Normal file
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
* mem-memset.c
|
||||
*
|
||||
* memset: Simple memory set in various ways
|
||||
*
|
||||
* Trivial clone of mem-memcpy.c.
|
||||
*/
|
||||
|
||||
#include "../perf.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/parse-options.h"
|
||||
#include "../util/header.h"
|
||||
#include "../util/cloexec.h"
|
||||
#include "bench.h"
|
||||
#include "mem-memset-arch.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define K 1024
|
||||
|
||||
static const char *length_str = "1MB";
|
||||
static const char *routine = "default";
|
||||
static int iterations = 1;
|
||||
static bool use_cycle;
|
||||
static int cycle_fd;
|
||||
static bool only_prefault;
|
||||
static bool no_prefault;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_STRING('l', "length", &length_str, "1MB",
|
||||
"Specify length of memory to set. "
|
||||
"Available units: B, KB, MB, GB and TB (upper and lower)"),
|
||||
OPT_STRING('r', "routine", &routine, "default",
|
||||
"Specify routine to set"),
|
||||
OPT_INTEGER('i', "iterations", &iterations,
|
||||
"repeat memset() invocation this number of times"),
|
||||
OPT_BOOLEAN('c', "cycle", &use_cycle,
|
||||
"Use cycles event instead of gettimeofday() for measuring"),
|
||||
OPT_BOOLEAN('o', "only-prefault", &only_prefault,
|
||||
"Show only the result with page faults before memset()"),
|
||||
OPT_BOOLEAN('n', "no-prefault", &no_prefault,
|
||||
"Show only the result without page faults before memset()"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
typedef void *(*memset_t)(void *, int, size_t);
|
||||
|
||||
struct routine {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
memset_t fn;
|
||||
};
|
||||
|
||||
static const struct routine routines[] = {
|
||||
{ "default",
|
||||
"Default memset() provided by glibc",
|
||||
memset },
|
||||
#ifdef HAVE_ARCH_X86_64_SUPPORT
|
||||
|
||||
#define MEMSET_FN(fn, name, desc) { name, desc, fn },
|
||||
#include "mem-memset-x86-64-asm-def.h"
|
||||
#undef MEMSET_FN
|
||||
|
||||
#endif
|
||||
|
||||
{ NULL,
|
||||
NULL,
|
||||
NULL }
|
||||
};
|
||||
|
||||
static const char * const bench_mem_memset_usage[] = {
|
||||
"perf bench mem memset <options>",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct perf_event_attr cycle_attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES
|
||||
};
|
||||
|
||||
static void init_cycle(void)
|
||||
{
|
||||
cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1,
|
||||
perf_event_open_cloexec_flag());
|
||||
|
||||
if (cycle_fd < 0 && errno == ENOSYS)
|
||||
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
|
||||
else
|
||||
BUG_ON(cycle_fd < 0);
|
||||
}
|
||||
|
||||
static u64 get_cycle(void)
|
||||
{
|
||||
int ret;
|
||||
u64 clk;
|
||||
|
||||
ret = read(cycle_fd, &clk, sizeof(u64));
|
||||
BUG_ON(ret != sizeof(u64));
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static double timeval2double(struct timeval *ts)
|
||||
{
|
||||
return (double)ts->tv_sec +
|
||||
(double)ts->tv_usec / (double)1000000;
|
||||
}
|
||||
|
||||
static void alloc_mem(void **dst, size_t length)
|
||||
{
|
||||
*dst = zalloc(length);
|
||||
if (!*dst)
|
||||
die("memory allocation failed - maybe length is too large?\n");
|
||||
}
|
||||
|
||||
static u64 do_memset_cycle(memset_t fn, size_t len, bool prefault)
|
||||
{
|
||||
u64 cycle_start = 0ULL, cycle_end = 0ULL;
|
||||
void *dst = NULL;
|
||||
int i;
|
||||
|
||||
alloc_mem(&dst, len);
|
||||
|
||||
if (prefault)
|
||||
fn(dst, -1, len);
|
||||
|
||||
cycle_start = get_cycle();
|
||||
for (i = 0; i < iterations; ++i)
|
||||
fn(dst, i, len);
|
||||
cycle_end = get_cycle();
|
||||
|
||||
free(dst);
|
||||
return cycle_end - cycle_start;
|
||||
}
|
||||
|
||||
static double do_memset_gettimeofday(memset_t fn, size_t len, bool prefault)
|
||||
{
|
||||
struct timeval tv_start, tv_end, tv_diff;
|
||||
void *dst = NULL;
|
||||
int i;
|
||||
|
||||
alloc_mem(&dst, len);
|
||||
|
||||
if (prefault)
|
||||
fn(dst, -1, len);
|
||||
|
||||
BUG_ON(gettimeofday(&tv_start, NULL));
|
||||
for (i = 0; i < iterations; ++i)
|
||||
fn(dst, i, len);
|
||||
BUG_ON(gettimeofday(&tv_end, NULL));
|
||||
|
||||
timersub(&tv_end, &tv_start, &tv_diff);
|
||||
|
||||
free(dst);
|
||||
return (double)((double)len / timeval2double(&tv_diff));
|
||||
}
|
||||
|
||||
#define pf (no_prefault ? 0 : 1)
|
||||
|
||||
#define print_bps(x) do { \
|
||||
if (x < K) \
|
||||
printf(" %14lf B/Sec", x); \
|
||||
else if (x < K * K) \
|
||||
printf(" %14lfd KB/Sec", x / K); \
|
||||
else if (x < K * K * K) \
|
||||
printf(" %14lf MB/Sec", x / K / K); \
|
||||
else \
|
||||
printf(" %14lf GB/Sec", x / K / K / K); \
|
||||
} while (0)
|
||||
|
||||
int bench_mem_memset(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
int i;
|
||||
size_t len;
|
||||
double result_bps[2];
|
||||
u64 result_cycle[2];
|
||||
|
||||
argc = parse_options(argc, argv, options,
|
||||
bench_mem_memset_usage, 0);
|
||||
|
||||
if (no_prefault && only_prefault) {
|
||||
fprintf(stderr, "Invalid options: -o and -n are mutually exclusive\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (use_cycle)
|
||||
init_cycle();
|
||||
|
||||
len = (size_t)perf_atoll((char *)length_str);
|
||||
|
||||
result_cycle[0] = result_cycle[1] = 0ULL;
|
||||
result_bps[0] = result_bps[1] = 0.0;
|
||||
|
||||
if ((s64)len <= 0) {
|
||||
fprintf(stderr, "Invalid length:%s\n", length_str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* same to without specifying either of prefault and no-prefault */
|
||||
if (only_prefault && no_prefault)
|
||||
only_prefault = no_prefault = false;
|
||||
|
||||
for (i = 0; routines[i].name; i++) {
|
||||
if (!strcmp(routines[i].name, routine))
|
||||
break;
|
||||
}
|
||||
if (!routines[i].name) {
|
||||
printf("Unknown routine:%s\n", routine);
|
||||
printf("Available routines...\n");
|
||||
for (i = 0; routines[i].name; i++) {
|
||||
printf("\t%s ... %s\n",
|
||||
routines[i].name, routines[i].desc);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (bench_format == BENCH_FORMAT_DEFAULT)
|
||||
printf("# Copying %s Bytes ...\n\n", length_str);
|
||||
|
||||
if (!only_prefault && !no_prefault) {
|
||||
/* show both of results */
|
||||
if (use_cycle) {
|
||||
result_cycle[0] =
|
||||
do_memset_cycle(routines[i].fn, len, false);
|
||||
result_cycle[1] =
|
||||
do_memset_cycle(routines[i].fn, len, true);
|
||||
} else {
|
||||
result_bps[0] =
|
||||
do_memset_gettimeofday(routines[i].fn,
|
||||
len, false);
|
||||
result_bps[1] =
|
||||
do_memset_gettimeofday(routines[i].fn,
|
||||
len, true);
|
||||
}
|
||||
} else {
|
||||
if (use_cycle) {
|
||||
result_cycle[pf] =
|
||||
do_memset_cycle(routines[i].fn,
|
||||
len, only_prefault);
|
||||
} else {
|
||||
result_bps[pf] =
|
||||
do_memset_gettimeofday(routines[i].fn,
|
||||
len, only_prefault);
|
||||
}
|
||||
}
|
||||
|
||||
switch (bench_format) {
|
||||
case BENCH_FORMAT_DEFAULT:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte\n",
|
||||
(double)result_cycle[0]
|
||||
/ (double)len);
|
||||
printf(" %14lf Cycle/Byte (with prefault)\n ",
|
||||
(double)result_cycle[1]
|
||||
/ (double)len);
|
||||
} else {
|
||||
print_bps(result_bps[0]);
|
||||
printf("\n");
|
||||
print_bps(result_bps[1]);
|
||||
printf(" (with prefault)\n");
|
||||
}
|
||||
} else {
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte",
|
||||
(double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
print_bps(result_bps[pf]);
|
||||
|
||||
printf("%s\n", only_prefault ? " (with prefault)" : "");
|
||||
}
|
||||
break;
|
||||
case BENCH_FORMAT_SIMPLE:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_cycle) {
|
||||
printf("%lf %lf\n",
|
||||
(double)result_cycle[0] / (double)len,
|
||||
(double)result_cycle[1] / (double)len);
|
||||
} else {
|
||||
printf("%lf %lf\n",
|
||||
result_bps[0], result_bps[1]);
|
||||
}
|
||||
} else {
|
||||
if (use_cycle) {
|
||||
printf("%lf\n", (double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
printf("%lf\n", result_bps[pf]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* reaching this means there's some disaster: */
|
||||
die("unknown format: %d\n", bench_format);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
1744
tools/perf/bench/numa.c
Normal file
1744
tools/perf/bench/numa.c
Normal file
File diff suppressed because it is too large
Load diff
331
tools/perf/bench/sched-messaging.c
Normal file
331
tools/perf/bench/sched-messaging.c
Normal file
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
*
|
||||
* sched-messaging.c
|
||||
*
|
||||
* messaging: Benchmark for scheduler and IPC mechanisms
|
||||
*
|
||||
* Based on hackbench by Rusty Russell <rusty@rustcorp.com.au>
|
||||
* Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../perf.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/parse-options.h"
|
||||
#include "../builtin.h"
|
||||
#include "bench.h"
|
||||
|
||||
/* Test groups of 20 processes spraying to 20 receivers */
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
#include <poll.h>
|
||||
#include <limits.h>
|
||||
#include <err.h>
|
||||
|
||||
#define DATASIZE 100
|
||||
|
||||
static bool use_pipes = false;
|
||||
static unsigned int loops = 100;
|
||||
static bool thread_mode = false;
|
||||
static unsigned int num_groups = 10;
|
||||
|
||||
struct sender_context {
|
||||
unsigned int num_fds;
|
||||
int ready_out;
|
||||
int wakefd;
|
||||
int out_fds[0];
|
||||
};
|
||||
|
||||
struct receiver_context {
|
||||
unsigned int num_packets;
|
||||
int in_fds[2];
|
||||
int ready_out;
|
||||
int wakefd;
|
||||
};
|
||||
|
||||
static void fdpair(int fds[2])
|
||||
{
|
||||
if (use_pipes) {
|
||||
if (pipe(fds) == 0)
|
||||
return;
|
||||
} else {
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
err(EXIT_FAILURE, use_pipes ? "pipe()" : "socketpair()");
|
||||
}
|
||||
|
||||
/* Block until we're ready to go */
|
||||
static void ready(int ready_out, int wakefd)
|
||||
{
|
||||
char dummy;
|
||||
struct pollfd pollfd = { .fd = wakefd, .events = POLLIN };
|
||||
|
||||
/* Tell them we're ready. */
|
||||
if (write(ready_out, &dummy, 1) != 1)
|
||||
err(EXIT_FAILURE, "CLIENT: ready write");
|
||||
|
||||
/* Wait for "GO" signal */
|
||||
if (poll(&pollfd, 1, -1) != 1)
|
||||
err(EXIT_FAILURE, "poll");
|
||||
}
|
||||
|
||||
/* Sender sprays loops messages down each file descriptor */
|
||||
static void *sender(struct sender_context *ctx)
|
||||
{
|
||||
char data[DATASIZE];
|
||||
unsigned int i, j;
|
||||
|
||||
ready(ctx->ready_out, ctx->wakefd);
|
||||
|
||||
/* Now pump to every receiver. */
|
||||
for (i = 0; i < loops; i++) {
|
||||
for (j = 0; j < ctx->num_fds; j++) {
|
||||
int ret, done = 0;
|
||||
|
||||
again:
|
||||
ret = write(ctx->out_fds[j], data + done,
|
||||
sizeof(data)-done);
|
||||
if (ret < 0)
|
||||
err(EXIT_FAILURE, "SENDER: write");
|
||||
done += ret;
|
||||
if (done < DATASIZE)
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* One receiver per fd */
|
||||
static void *receiver(struct receiver_context* ctx)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!thread_mode)
|
||||
close(ctx->in_fds[1]);
|
||||
|
||||
/* Wait for start... */
|
||||
ready(ctx->ready_out, ctx->wakefd);
|
||||
|
||||
/* Receive them all */
|
||||
for (i = 0; i < ctx->num_packets; i++) {
|
||||
char data[DATASIZE];
|
||||
int ret, done = 0;
|
||||
|
||||
again:
|
||||
ret = read(ctx->in_fds[0], data + done, DATASIZE - done);
|
||||
if (ret < 0)
|
||||
err(EXIT_FAILURE, "SERVER: read");
|
||||
done += ret;
|
||||
if (done < DATASIZE)
|
||||
goto again;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static pthread_t create_worker(void *ctx, void *(*func)(void *))
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
pthread_t childid;
|
||||
int ret;
|
||||
|
||||
if (!thread_mode) {
|
||||
/* process mode */
|
||||
/* Fork the receiver. */
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
err(EXIT_FAILURE, "fork()");
|
||||
break;
|
||||
case 0:
|
||||
(*func) (ctx);
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return (pthread_t)0;
|
||||
}
|
||||
|
||||
if (pthread_attr_init(&attr) != 0)
|
||||
err(EXIT_FAILURE, "pthread_attr_init:");
|
||||
|
||||
#ifndef __ia64__
|
||||
if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
|
||||
err(EXIT_FAILURE, "pthread_attr_setstacksize");
|
||||
#endif
|
||||
|
||||
ret = pthread_create(&childid, &attr, func, ctx);
|
||||
if (ret != 0)
|
||||
err(EXIT_FAILURE, "pthread_create failed");
|
||||
|
||||
return childid;
|
||||
}
|
||||
|
||||
static void reap_worker(pthread_t id)
|
||||
{
|
||||
int proc_status;
|
||||
void *thread_status;
|
||||
|
||||
if (!thread_mode) {
|
||||
/* process mode */
|
||||
wait(&proc_status);
|
||||
if (!WIFEXITED(proc_status))
|
||||
exit(1);
|
||||
} else {
|
||||
pthread_join(id, &thread_status);
|
||||
}
|
||||
}
|
||||
|
||||
/* One group of senders and receivers */
|
||||
static unsigned int group(pthread_t *pth,
|
||||
unsigned int num_fds,
|
||||
int ready_out,
|
||||
int wakefd)
|
||||
{
|
||||
unsigned int i;
|
||||
struct sender_context *snd_ctx = malloc(sizeof(struct sender_context)
|
||||
+ num_fds * sizeof(int));
|
||||
|
||||
if (!snd_ctx)
|
||||
err(EXIT_FAILURE, "malloc()");
|
||||
|
||||
for (i = 0; i < num_fds; i++) {
|
||||
int fds[2];
|
||||
struct receiver_context *ctx = malloc(sizeof(*ctx));
|
||||
|
||||
if (!ctx)
|
||||
err(EXIT_FAILURE, "malloc()");
|
||||
|
||||
|
||||
/* Create the pipe between client and server */
|
||||
fdpair(fds);
|
||||
|
||||
ctx->num_packets = num_fds * loops;
|
||||
ctx->in_fds[0] = fds[0];
|
||||
ctx->in_fds[1] = fds[1];
|
||||
ctx->ready_out = ready_out;
|
||||
ctx->wakefd = wakefd;
|
||||
|
||||
pth[i] = create_worker(ctx, (void *)receiver);
|
||||
|
||||
snd_ctx->out_fds[i] = fds[1];
|
||||
if (!thread_mode)
|
||||
close(fds[0]);
|
||||
}
|
||||
|
||||
/* Now we have all the fds, fork the senders */
|
||||
for (i = 0; i < num_fds; i++) {
|
||||
snd_ctx->ready_out = ready_out;
|
||||
snd_ctx->wakefd = wakefd;
|
||||
snd_ctx->num_fds = num_fds;
|
||||
|
||||
pth[num_fds+i] = create_worker(snd_ctx, (void *)sender);
|
||||
}
|
||||
|
||||
/* Close the fds we have left */
|
||||
if (!thread_mode)
|
||||
for (i = 0; i < num_fds; i++)
|
||||
close(snd_ctx->out_fds[i]);
|
||||
|
||||
/* Return number of children to reap */
|
||||
return num_fds * 2;
|
||||
}
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_BOOLEAN('p', "pipe", &use_pipes,
|
||||
"Use pipe() instead of socketpair()"),
|
||||
OPT_BOOLEAN('t', "thread", &thread_mode,
|
||||
"Be multi thread instead of multi process"),
|
||||
OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"),
|
||||
OPT_UINTEGER('l', "loop", &loops, "Specify number of loops"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char * const bench_sched_message_usage[] = {
|
||||
"perf bench sched messaging <options>",
|
||||
NULL
|
||||
};
|
||||
|
||||
int bench_sched_messaging(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
unsigned int i, total_children;
|
||||
struct timeval start, stop, diff;
|
||||
unsigned int num_fds = 20;
|
||||
int readyfds[2], wakefds[2];
|
||||
char dummy;
|
||||
pthread_t *pth_tab;
|
||||
|
||||
argc = parse_options(argc, argv, options,
|
||||
bench_sched_message_usage, 0);
|
||||
|
||||
pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t));
|
||||
if (!pth_tab)
|
||||
err(EXIT_FAILURE, "main:malloc()");
|
||||
|
||||
fdpair(readyfds);
|
||||
fdpair(wakefds);
|
||||
|
||||
total_children = 0;
|
||||
for (i = 0; i < num_groups; i++)
|
||||
total_children += group(pth_tab+total_children, num_fds,
|
||||
readyfds[1], wakefds[0]);
|
||||
|
||||
/* Wait for everyone to be ready */
|
||||
for (i = 0; i < total_children; i++)
|
||||
if (read(readyfds[0], &dummy, 1) != 1)
|
||||
err(EXIT_FAILURE, "Reading for readyfds");
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
/* Kick them off */
|
||||
if (write(wakefds[1], &dummy, 1) != 1)
|
||||
err(EXIT_FAILURE, "Writing to start them");
|
||||
|
||||
/* Reap them all */
|
||||
for (i = 0; i < total_children; i++)
|
||||
reap_worker(pth_tab[i]);
|
||||
|
||||
gettimeofday(&stop, NULL);
|
||||
|
||||
timersub(&stop, &start, &diff);
|
||||
|
||||
switch (bench_format) {
|
||||
case BENCH_FORMAT_DEFAULT:
|
||||
printf("# %d sender and receiver %s per group\n",
|
||||
num_fds, thread_mode ? "threads" : "processes");
|
||||
printf("# %d groups == %d %s run\n\n",
|
||||
num_groups, num_groups * 2 * num_fds,
|
||||
thread_mode ? "threads" : "processes");
|
||||
printf(" %14s: %lu.%03lu [sec]\n", "Total time",
|
||||
diff.tv_sec,
|
||||
(unsigned long) (diff.tv_usec/1000));
|
||||
break;
|
||||
case BENCH_FORMAT_SIMPLE:
|
||||
printf("%lu.%03lu\n", diff.tv_sec,
|
||||
(unsigned long) (diff.tv_usec/1000));
|
||||
break;
|
||||
default:
|
||||
/* reaching here is something disaster */
|
||||
fprintf(stderr, "Unknown format:%d\n", bench_format);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
|
||||
free(pth_tab);
|
||||
|
||||
return 0;
|
||||
}
|
184
tools/perf/bench/sched-pipe.c
Normal file
184
tools/perf/bench/sched-pipe.c
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
*
|
||||
* sched-pipe.c
|
||||
*
|
||||
* pipe: Benchmark for pipe()
|
||||
*
|
||||
* Based on pipe-test-1m.c by Ingo Molnar <mingo@redhat.com>
|
||||
* http://people.redhat.com/mingo/cfs-scheduler/tools/pipe-test-1m.c
|
||||
* Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
|
||||
*/
|
||||
#include "../perf.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/parse-options.h"
|
||||
#include "../builtin.h"
|
||||
#include "bench.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
struct thread_data {
|
||||
int nr;
|
||||
int pipe_read;
|
||||
int pipe_write;
|
||||
pthread_t pthread;
|
||||
};
|
||||
|
||||
#define LOOPS_DEFAULT 1000000
|
||||
static int loops = LOOPS_DEFAULT;
|
||||
|
||||
/* Use processes by default: */
|
||||
static bool threaded;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_INTEGER('l', "loop", &loops, "Specify number of loops"),
|
||||
OPT_BOOLEAN('T', "threaded", &threaded, "Specify threads/process based task setup"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char * const bench_sched_pipe_usage[] = {
|
||||
"perf bench sched pipe <options>",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void *worker_thread(void *__tdata)
|
||||
{
|
||||
struct thread_data *td = __tdata;
|
||||
int m = 0, i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < loops; i++) {
|
||||
if (!td->nr) {
|
||||
ret = read(td->pipe_read, &m, sizeof(int));
|
||||
BUG_ON(ret != sizeof(int));
|
||||
ret = write(td->pipe_write, &m, sizeof(int));
|
||||
BUG_ON(ret != sizeof(int));
|
||||
} else {
|
||||
ret = write(td->pipe_write, &m, sizeof(int));
|
||||
BUG_ON(ret != sizeof(int));
|
||||
ret = read(td->pipe_read, &m, sizeof(int));
|
||||
BUG_ON(ret != sizeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bench_sched_pipe(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
struct thread_data threads[2], *td;
|
||||
int pipe_1[2], pipe_2[2];
|
||||
struct timeval start, stop, diff;
|
||||
unsigned long long result_usec = 0;
|
||||
int nr_threads = 2;
|
||||
int t;
|
||||
|
||||
/*
|
||||
* why does "ret" exist?
|
||||
* discarding returned value of read(), write()
|
||||
* causes error in building environment for perf
|
||||
*/
|
||||
int __maybe_unused ret, wait_stat;
|
||||
pid_t pid, retpid __maybe_unused;
|
||||
|
||||
argc = parse_options(argc, argv, options, bench_sched_pipe_usage, 0);
|
||||
|
||||
BUG_ON(pipe(pipe_1));
|
||||
BUG_ON(pipe(pipe_2));
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
for (t = 0; t < nr_threads; t++) {
|
||||
td = threads + t;
|
||||
|
||||
td->nr = t;
|
||||
|
||||
if (t == 0) {
|
||||
td->pipe_read = pipe_1[0];
|
||||
td->pipe_write = pipe_2[1];
|
||||
} else {
|
||||
td->pipe_write = pipe_1[1];
|
||||
td->pipe_read = pipe_2[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (threaded) {
|
||||
|
||||
for (t = 0; t < nr_threads; t++) {
|
||||
td = threads + t;
|
||||
|
||||
ret = pthread_create(&td->pthread, NULL, worker_thread, td);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
|
||||
for (t = 0; t < nr_threads; t++) {
|
||||
td = threads + t;
|
||||
|
||||
ret = pthread_join(td->pthread, NULL);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
|
||||
} else {
|
||||
pid = fork();
|
||||
assert(pid >= 0);
|
||||
|
||||
if (!pid) {
|
||||
worker_thread(threads + 0);
|
||||
exit(0);
|
||||
} else {
|
||||
worker_thread(threads + 1);
|
||||
}
|
||||
|
||||
retpid = waitpid(pid, &wait_stat, 0);
|
||||
assert((retpid == pid) && WIFEXITED(wait_stat));
|
||||
}
|
||||
|
||||
gettimeofday(&stop, NULL);
|
||||
timersub(&stop, &start, &diff);
|
||||
|
||||
switch (bench_format) {
|
||||
case BENCH_FORMAT_DEFAULT:
|
||||
printf("# Executed %d pipe operations between two %s\n\n",
|
||||
loops, threaded ? "threads" : "processes");
|
||||
|
||||
result_usec = diff.tv_sec * 1000000;
|
||||
result_usec += diff.tv_usec;
|
||||
|
||||
printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
|
||||
diff.tv_sec,
|
||||
(unsigned long) (diff.tv_usec/1000));
|
||||
|
||||
printf(" %14lf usecs/op\n",
|
||||
(double)result_usec / (double)loops);
|
||||
printf(" %14d ops/sec\n",
|
||||
(int)((double)loops /
|
||||
((double)result_usec / (double)1000000)));
|
||||
break;
|
||||
|
||||
case BENCH_FORMAT_SIMPLE:
|
||||
printf("%lu.%03lu\n",
|
||||
diff.tv_sec,
|
||||
(unsigned long) (diff.tv_usec / 1000));
|
||||
break;
|
||||
|
||||
default:
|
||||
/* reaching here is something disaster */
|
||||
fprintf(stderr, "Unknown format:%d\n", bench_format);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
386
tools/perf/builtin-annotate.c
Normal file
386
tools/perf/builtin-annotate.c
Normal file
|
@ -0,0 +1,386 @@
|
|||
/*
|
||||
* builtin-annotate.c
|
||||
*
|
||||
* Builtin annotate command: Analyze the perf.data input file,
|
||||
* look up and read DSOs and symbol information and display
|
||||
* a histogram of results, along various sorting keys.
|
||||
*/
|
||||
#include "builtin.h"
|
||||
|
||||
#include "util/util.h"
|
||||
#include "util/color.h"
|
||||
#include <linux/list.h>
|
||||
#include "util/cache.h"
|
||||
#include <linux/rbtree.h>
|
||||
#include "util/symbol.h"
|
||||
|
||||
#include "perf.h"
|
||||
#include "util/debug.h"
|
||||
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/annotate.h"
|
||||
#include "util/event.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/parse-events.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/sort.h"
|
||||
#include "util/hist.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/data.h"
|
||||
#include "arch/common.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
struct perf_annotate {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
bool use_tui, use_stdio, use_gtk;
|
||||
bool full_paths;
|
||||
bool print_line;
|
||||
bool skip_missing;
|
||||
const char *sym_hist_filter;
|
||||
const char *cpu_list;
|
||||
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
};
|
||||
|
||||
static int perf_evsel__add_sample(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct addr_location *al,
|
||||
struct perf_annotate *ann)
|
||||
{
|
||||
struct hists *hists = evsel__hists(evsel);
|
||||
struct hist_entry *he;
|
||||
int ret;
|
||||
|
||||
if (ann->sym_hist_filter != NULL &&
|
||||
(al->sym == NULL ||
|
||||
strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
|
||||
/* We're only interested in a symbol named sym_hist_filter */
|
||||
if (al->sym != NULL) {
|
||||
rb_erase(&al->sym->rb_node,
|
||||
&al->map->dso->symbols[al->map->type]);
|
||||
symbol__delete(al->sym);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
he = __hists__add_entry(hists, al, NULL, NULL, NULL, 1, 1, 0, true);
|
||||
if (he == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
|
||||
hists__inc_nr_samples(hists, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool);
|
||||
struct addr_location al;
|
||||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
|
||||
pr_warning("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap))
|
||||
return 0;
|
||||
|
||||
if (!al.filtered && perf_evsel__add_sample(evsel, sample, &al, ann)) {
|
||||
pr_warning("problem incrementing symbol count, "
|
||||
"skipping event\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hist_entry__tty_annotate(struct hist_entry *he,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_annotate *ann)
|
||||
{
|
||||
return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel,
|
||||
ann->print_line, ann->full_paths, 0, 0);
|
||||
}
|
||||
|
||||
static void hists__find_annotations(struct hists *hists,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_annotate *ann)
|
||||
{
|
||||
struct rb_node *nd = rb_first(&hists->entries), *next;
|
||||
int key = K_RIGHT;
|
||||
|
||||
while (nd) {
|
||||
struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
|
||||
struct annotation *notes;
|
||||
|
||||
if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
|
||||
goto find_next;
|
||||
|
||||
notes = symbol__annotation(he->ms.sym);
|
||||
if (notes->src == NULL) {
|
||||
find_next:
|
||||
if (key == K_LEFT)
|
||||
nd = rb_prev(nd);
|
||||
else
|
||||
nd = rb_next(nd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (use_browser == 2) {
|
||||
int ret;
|
||||
int (*annotate)(struct hist_entry *he,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt);
|
||||
|
||||
annotate = dlsym(perf_gtk_handle,
|
||||
"hist_entry__gtk_annotate");
|
||||
if (annotate == NULL) {
|
||||
ui__error("GTK browser not found!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = annotate(he, evsel, NULL);
|
||||
if (!ret || !ann->skip_missing)
|
||||
return;
|
||||
|
||||
/* skip missing symbols */
|
||||
nd = rb_next(nd);
|
||||
} else if (use_browser == 1) {
|
||||
key = hist_entry__tui_annotate(he, evsel, NULL);
|
||||
switch (key) {
|
||||
case -1:
|
||||
if (!ann->skip_missing)
|
||||
return;
|
||||
/* fall through */
|
||||
case K_RIGHT:
|
||||
next = rb_next(nd);
|
||||
break;
|
||||
case K_LEFT:
|
||||
next = rb_prev(nd);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (next != NULL)
|
||||
nd = next;
|
||||
} else {
|
||||
hist_entry__tty_annotate(he, evsel, ann);
|
||||
nd = rb_next(nd);
|
||||
/*
|
||||
* Since we have a hist_entry per IP for the same
|
||||
* symbol, free he->ms.sym->src to signal we already
|
||||
* processed this symbol.
|
||||
*/
|
||||
zfree(¬es->src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int __cmd_annotate(struct perf_annotate *ann)
|
||||
{
|
||||
int ret;
|
||||
struct perf_session *session = ann->session;
|
||||
struct perf_evsel *pos;
|
||||
u64 total_nr_samples;
|
||||
|
||||
machines__set_symbol_filter(&session->machines, symbol__annotate_init);
|
||||
|
||||
if (ann->cpu_list) {
|
||||
ret = perf_session__cpu_bitmap(session, ann->cpu_list,
|
||||
ann->cpu_bitmap);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!objdump_path) {
|
||||
ret = perf_session_env__lookup_objdump(&session->header.env);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = perf_session__process_events(session, &ann->tool);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (dump_trace) {
|
||||
perf_session__fprintf_nr_events(session, stdout);
|
||||
perf_evlist__fprintf_nr_events(session->evlist, stdout);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (verbose > 3)
|
||||
perf_session__fprintf(session, stdout);
|
||||
|
||||
if (verbose > 2)
|
||||
perf_session__fprintf_dsos(session, stdout);
|
||||
|
||||
total_nr_samples = 0;
|
||||
evlist__for_each(session->evlist, pos) {
|
||||
struct hists *hists = evsel__hists(pos);
|
||||
u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
|
||||
|
||||
if (nr_samples > 0) {
|
||||
total_nr_samples += nr_samples;
|
||||
hists__collapse_resort(hists, NULL);
|
||||
hists__output_resort(hists);
|
||||
|
||||
if (symbol_conf.event_group &&
|
||||
!perf_evsel__is_group_leader(pos))
|
||||
continue;
|
||||
|
||||
hists__find_annotations(hists, pos, ann);
|
||||
}
|
||||
}
|
||||
|
||||
if (total_nr_samples == 0) {
|
||||
ui__error("The %s file has no samples!\n", session->file->path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (use_browser == 2) {
|
||||
void (*show_annotations)(void);
|
||||
|
||||
show_annotations = dlsym(perf_gtk_handle,
|
||||
"perf_gtk__show_annotations");
|
||||
if (show_annotations == NULL) {
|
||||
ui__error("GTK browser not found!\n");
|
||||
goto out;
|
||||
}
|
||||
show_annotations();
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const annotate_usage[] = {
|
||||
"perf annotate [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
struct perf_annotate annotate = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.mmap2 = perf_event__process_mmap2,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
};
|
||||
struct perf_data_file file = {
|
||||
.path = input_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
|
||||
"only consider symbols in these dsos"),
|
||||
OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol",
|
||||
"symbol to annotate"),
|
||||
OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
||||
"dump raw trace in ASCII"),
|
||||
OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"),
|
||||
OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"),
|
||||
OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"),
|
||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||
"file", "vmlinux pathname"),
|
||||
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
|
||||
"load module symbols - WARNING: use only with -k and LIVE kernel"),
|
||||
OPT_BOOLEAN('l', "print-line", &annotate.print_line,
|
||||
"print matching source lines (may be slow)"),
|
||||
OPT_BOOLEAN('P', "full-paths", &annotate.full_paths,
|
||||
"Don't shorten the displayed pathnames"),
|
||||
OPT_BOOLEAN(0, "skip-missing", &annotate.skip_missing,
|
||||
"Skip symbols that cannot be annotated"),
|
||||
OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"),
|
||||
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
|
||||
"Look for files with symbols relative to this directory"),
|
||||
OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
|
||||
"Interleave source code with assembly code (default)"),
|
||||
OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
|
||||
"Display raw encoding of assembly instructions (default)"),
|
||||
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
|
||||
"Specify disassembler style (e.g. -M intel for intel syntax)"),
|
||||
OPT_STRING(0, "objdump", &objdump_path, "path",
|
||||
"objdump binary to use for disassembly and annotations"),
|
||||
OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
|
||||
"Show event group information together"),
|
||||
OPT_END()
|
||||
};
|
||||
int ret = hists__init();
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
argc = parse_options(argc, argv, options, annotate_usage, 0);
|
||||
|
||||
if (annotate.use_stdio)
|
||||
use_browser = 0;
|
||||
else if (annotate.use_tui)
|
||||
use_browser = 1;
|
||||
else if (annotate.use_gtk)
|
||||
use_browser = 2;
|
||||
|
||||
setup_browser(true);
|
||||
|
||||
annotate.session = perf_session__new(&file, false, &annotate.tool);
|
||||
if (annotate.session == NULL)
|
||||
return -1;
|
||||
|
||||
symbol_conf.priv_size = sizeof(struct annotation);
|
||||
symbol_conf.try_vmlinux_path = true;
|
||||
|
||||
ret = symbol__init(&annotate.session->header.env);
|
||||
if (ret < 0)
|
||||
goto out_delete;
|
||||
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(annotate_usage, options);
|
||||
|
||||
if (argc) {
|
||||
/*
|
||||
* Special case: if there's an argument left then assume that
|
||||
* it's a symbol filter:
|
||||
*/
|
||||
if (argc > 1)
|
||||
usage_with_options(annotate_usage, options);
|
||||
|
||||
annotate.sym_hist_filter = argv[0];
|
||||
}
|
||||
|
||||
ret = __cmd_annotate(&annotate);
|
||||
|
||||
out_delete:
|
||||
/*
|
||||
* Speed up the exit process, for large files this can
|
||||
* take quite a while.
|
||||
*
|
||||
* XXX Enable this when using valgrind or if we ever
|
||||
* librarize this command.
|
||||
*
|
||||
* Also experiment with obstacks to see how much speed
|
||||
* up we'll get here.
|
||||
*
|
||||
* perf_session__delete(session);
|
||||
*/
|
||||
return ret;
|
||||
}
|
289
tools/perf/builtin-bench.c
Normal file
289
tools/perf/builtin-bench.c
Normal file
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* builtin-bench.c
|
||||
*
|
||||
* General benchmarking collections provided by perf
|
||||
*
|
||||
* Copyright (C) 2009, Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Available benchmark collection list:
|
||||
*
|
||||
* sched ... scheduler and IPC performance
|
||||
* mem ... memory access performance
|
||||
* numa ... NUMA scheduling and MM performance
|
||||
* futex ... Futex performance
|
||||
*/
|
||||
#include "perf.h"
|
||||
#include "util/util.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "builtin.h"
|
||||
#include "bench/bench.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
typedef int (*bench_fn_t)(int argc, const char **argv, const char *prefix);
|
||||
|
||||
struct bench {
|
||||
const char *name;
|
||||
const char *summary;
|
||||
bench_fn_t fn;
|
||||
};
|
||||
|
||||
#ifdef HAVE_LIBNUMA_SUPPORT
|
||||
static struct bench numa_benchmarks[] = {
|
||||
{ "mem", "Benchmark for NUMA workloads", bench_numa },
|
||||
{ "all", "Test all NUMA benchmarks", NULL },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct bench sched_benchmarks[] = {
|
||||
{ "messaging", "Benchmark for scheduling and IPC", bench_sched_messaging },
|
||||
{ "pipe", "Benchmark for pipe() between two processes", bench_sched_pipe },
|
||||
{ "all", "Test all scheduler benchmarks", NULL },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static struct bench mem_benchmarks[] = {
|
||||
{ "memcpy", "Benchmark for memcpy()", bench_mem_memcpy },
|
||||
{ "memset", "Benchmark for memset() tests", bench_mem_memset },
|
||||
{ "all", "Test all memory benchmarks", NULL },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static struct bench futex_benchmarks[] = {
|
||||
{ "hash", "Benchmark for futex hash table", bench_futex_hash },
|
||||
{ "wake", "Benchmark for futex wake calls", bench_futex_wake },
|
||||
{ "requeue", "Benchmark for futex requeue calls", bench_futex_requeue },
|
||||
{ "all", "Test all futex benchmarks", NULL },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
struct collection {
|
||||
const char *name;
|
||||
const char *summary;
|
||||
struct bench *benchmarks;
|
||||
};
|
||||
|
||||
static struct collection collections[] = {
|
||||
{ "sched", "Scheduler and IPC benchmarks", sched_benchmarks },
|
||||
{ "mem", "Memory access benchmarks", mem_benchmarks },
|
||||
#ifdef HAVE_LIBNUMA_SUPPORT
|
||||
{ "numa", "NUMA scheduling and MM benchmarks", numa_benchmarks },
|
||||
#endif
|
||||
{"futex", "Futex stressing benchmarks", futex_benchmarks },
|
||||
{ "all", "All benchmarks", NULL },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
/* Iterate over all benchmark collections: */
|
||||
#define for_each_collection(coll) \
|
||||
for (coll = collections; coll->name; coll++)
|
||||
|
||||
/* Iterate over all benchmarks within a collection: */
|
||||
#define for_each_bench(coll, bench) \
|
||||
for (bench = coll->benchmarks; bench && bench->name; bench++)
|
||||
|
||||
static void dump_benchmarks(struct collection *coll)
|
||||
{
|
||||
struct bench *bench;
|
||||
|
||||
printf("\n # List of available benchmarks for collection '%s':\n\n", coll->name);
|
||||
|
||||
for_each_bench(coll, bench)
|
||||
printf("%14s: %s\n", bench->name, bench->summary);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static const char *bench_format_str;
|
||||
|
||||
/* Output/formatting style, exported to benchmark modules: */
|
||||
int bench_format = BENCH_FORMAT_DEFAULT;
|
||||
unsigned int bench_repeat = 10; /* default number of times to repeat the run */
|
||||
|
||||
static const struct option bench_options[] = {
|
||||
OPT_STRING('f', "format", &bench_format_str, "default", "Specify format style"),
|
||||
OPT_UINTEGER('r', "repeat", &bench_repeat, "Specify amount of times to repeat the run"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char * const bench_usage[] = {
|
||||
"perf bench [<common options>] <collection> <benchmark> [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
struct collection *coll;
|
||||
int i;
|
||||
|
||||
printf("Usage: \n");
|
||||
for (i = 0; bench_usage[i]; i++)
|
||||
printf("\t%s\n", bench_usage[i]);
|
||||
printf("\n");
|
||||
|
||||
printf(" # List of all available benchmark collections:\n\n");
|
||||
|
||||
for_each_collection(coll)
|
||||
printf("%14s: %s\n", coll->name, coll->summary);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int bench_str2int(const char *str)
|
||||
{
|
||||
if (!str)
|
||||
return BENCH_FORMAT_DEFAULT;
|
||||
|
||||
if (!strcmp(str, BENCH_FORMAT_DEFAULT_STR))
|
||||
return BENCH_FORMAT_DEFAULT;
|
||||
else if (!strcmp(str, BENCH_FORMAT_SIMPLE_STR))
|
||||
return BENCH_FORMAT_SIMPLE;
|
||||
|
||||
return BENCH_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run a specific benchmark but first rename the running task's ->comm[]
|
||||
* to something meaningful:
|
||||
*/
|
||||
static int run_bench(const char *coll_name, const char *bench_name, bench_fn_t fn,
|
||||
int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int size;
|
||||
char *name;
|
||||
int ret;
|
||||
|
||||
size = strlen(coll_name) + 1 + strlen(bench_name) + 1;
|
||||
|
||||
name = zalloc(size);
|
||||
BUG_ON(!name);
|
||||
|
||||
scnprintf(name, size, "%s-%s", coll_name, bench_name);
|
||||
|
||||
prctl(PR_SET_NAME, name);
|
||||
argv[0] = name;
|
||||
|
||||
ret = fn(argc, argv, prefix);
|
||||
|
||||
free(name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void run_collection(struct collection *coll)
|
||||
{
|
||||
struct bench *bench;
|
||||
const char *argv[2];
|
||||
|
||||
argv[1] = NULL;
|
||||
/*
|
||||
* TODO:
|
||||
*
|
||||
* Preparing preset parameters for
|
||||
* embedded, ordinary PC, HPC, etc...
|
||||
* would be helpful.
|
||||
*/
|
||||
for_each_bench(coll, bench) {
|
||||
if (!bench->fn)
|
||||
break;
|
||||
printf("# Running %s/%s benchmark...\n", coll->name, bench->name);
|
||||
fflush(stdout);
|
||||
|
||||
argv[1] = bench->name;
|
||||
run_bench(coll->name, bench->name, bench->fn, 1, argv, NULL);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void run_all_collections(void)
|
||||
{
|
||||
struct collection *coll;
|
||||
|
||||
for_each_collection(coll)
|
||||
run_collection(coll);
|
||||
}
|
||||
|
||||
int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
struct collection *coll;
|
||||
int ret = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
/* No collection specified. */
|
||||
print_usage();
|
||||
goto end;
|
||||
}
|
||||
|
||||
argc = parse_options(argc, argv, bench_options, bench_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
bench_format = bench_str2int(bench_format_str);
|
||||
if (bench_format == BENCH_FORMAT_UNKNOWN) {
|
||||
printf("Unknown format descriptor: '%s'\n", bench_format_str);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (bench_repeat == 0) {
|
||||
printf("Invalid repeat option: Must specify a positive value\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (argc < 1) {
|
||||
print_usage();
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[0], "all")) {
|
||||
run_all_collections();
|
||||
goto end;
|
||||
}
|
||||
|
||||
for_each_collection(coll) {
|
||||
struct bench *bench;
|
||||
|
||||
if (strcmp(coll->name, argv[0]))
|
||||
continue;
|
||||
|
||||
if (argc < 2) {
|
||||
/* No bench specified. */
|
||||
dump_benchmarks(coll);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "all")) {
|
||||
run_collection(coll);
|
||||
goto end;
|
||||
}
|
||||
|
||||
for_each_bench(coll, bench) {
|
||||
if (strcmp(bench->name, argv[1]))
|
||||
continue;
|
||||
|
||||
if (bench_format == BENCH_FORMAT_DEFAULT)
|
||||
printf("# Running '%s/%s' benchmark:\n", coll->name, bench->name);
|
||||
fflush(stdout);
|
||||
ret = run_bench(coll->name, bench->name, bench->fn, argc-1, argv+1, prefix);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
|
||||
dump_benchmarks(coll);
|
||||
goto end;
|
||||
}
|
||||
|
||||
printf("Unknown benchmark: '%s' for collection '%s'\n", argv[1], argv[0]);
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
printf("Unknown collection: '%s'\n", argv[0]);
|
||||
ret = 1;
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
406
tools/perf/builtin-buildid-cache.c
Normal file
406
tools/perf/builtin-buildid-cache.c
Normal file
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* builtin-buildid-cache.c
|
||||
*
|
||||
* Builtin buildid-cache command: Manages build-id cache
|
||||
*
|
||||
* Copyright (C) 2010, Red Hat Inc.
|
||||
* Copyright (C) 2010, Arnaldo Carvalho de Melo <acme@redhat.com>
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include "builtin.h"
|
||||
#include "perf.h"
|
||||
#include "util/cache.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/header.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/strlist.h"
|
||||
#include "util/build-id.h"
|
||||
#include "util/session.h"
|
||||
#include "util/symbol.h"
|
||||
|
||||
static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid)
|
||||
{
|
||||
char root_dir[PATH_MAX];
|
||||
char notes[PATH_MAX];
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
char *p;
|
||||
|
||||
strlcpy(root_dir, proc_dir, sizeof(root_dir));
|
||||
|
||||
p = strrchr(root_dir, '/');
|
||||
if (!p)
|
||||
return -1;
|
||||
*p = '\0';
|
||||
|
||||
scnprintf(notes, sizeof(notes), "%s/sys/kernel/notes", root_dir);
|
||||
|
||||
if (sysfs__read_build_id(notes, build_id, sizeof(build_id)))
|
||||
return -1;
|
||||
|
||||
build_id__sprintf(build_id, sizeof(build_id), sbuildid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int build_id_cache__kcore_dir(char *dir, size_t sz)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tm tm;
|
||||
char dt[32];
|
||||
|
||||
if (gettimeofday(&tv, NULL) || !localtime_r(&tv.tv_sec, &tm))
|
||||
return -1;
|
||||
|
||||
if (!strftime(dt, sizeof(dt), "%Y%m%d%H%M%S", &tm))
|
||||
return -1;
|
||||
|
||||
scnprintf(dir, sz, "%s%02u", dt, (unsigned)tv.tv_usec / 10000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool same_kallsyms_reloc(const char *from_dir, char *to_dir)
|
||||
{
|
||||
char from[PATH_MAX];
|
||||
char to[PATH_MAX];
|
||||
const char *name;
|
||||
u64 addr1 = 0, addr2 = 0;
|
||||
int i;
|
||||
|
||||
scnprintf(from, sizeof(from), "%s/kallsyms", from_dir);
|
||||
scnprintf(to, sizeof(to), "%s/kallsyms", to_dir);
|
||||
|
||||
for (i = 0; (name = ref_reloc_sym_names[i]) != NULL; i++) {
|
||||
addr1 = kallsyms__get_function_start(from, name);
|
||||
if (addr1)
|
||||
break;
|
||||
}
|
||||
|
||||
if (name)
|
||||
addr2 = kallsyms__get_function_start(to, name);
|
||||
|
||||
return addr1 == addr2;
|
||||
}
|
||||
|
||||
static int build_id_cache__kcore_existing(const char *from_dir, char *to_dir,
|
||||
size_t to_dir_sz)
|
||||
{
|
||||
char from[PATH_MAX];
|
||||
char to[PATH_MAX];
|
||||
char to_subdir[PATH_MAX];
|
||||
struct dirent *dent;
|
||||
int ret = -1;
|
||||
DIR *d;
|
||||
|
||||
d = opendir(to_dir);
|
||||
if (!d)
|
||||
return -1;
|
||||
|
||||
scnprintf(from, sizeof(from), "%s/modules", from_dir);
|
||||
|
||||
while (1) {
|
||||
dent = readdir(d);
|
||||
if (!dent)
|
||||
break;
|
||||
if (dent->d_type != DT_DIR)
|
||||
continue;
|
||||
scnprintf(to, sizeof(to), "%s/%s/modules", to_dir,
|
||||
dent->d_name);
|
||||
scnprintf(to_subdir, sizeof(to_subdir), "%s/%s",
|
||||
to_dir, dent->d_name);
|
||||
if (!compare_proc_modules(from, to) &&
|
||||
same_kallsyms_reloc(from_dir, to_subdir)) {
|
||||
strlcpy(to_dir, to_subdir, to_dir_sz);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int build_id_cache__add_kcore(const char *filename, const char *debugdir,
|
||||
bool force)
|
||||
{
|
||||
char dir[32], sbuildid[BUILD_ID_SIZE * 2 + 1];
|
||||
char from_dir[PATH_MAX], to_dir[PATH_MAX];
|
||||
char *p;
|
||||
|
||||
strlcpy(from_dir, filename, sizeof(from_dir));
|
||||
|
||||
p = strrchr(from_dir, '/');
|
||||
if (!p || strcmp(p + 1, "kcore"))
|
||||
return -1;
|
||||
*p = '\0';
|
||||
|
||||
if (build_id_cache__kcore_buildid(from_dir, sbuildid))
|
||||
return -1;
|
||||
|
||||
scnprintf(to_dir, sizeof(to_dir), "%s/[kernel.kcore]/%s",
|
||||
debugdir, sbuildid);
|
||||
|
||||
if (!force &&
|
||||
!build_id_cache__kcore_existing(from_dir, to_dir, sizeof(to_dir))) {
|
||||
pr_debug("same kcore found in %s\n", to_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (build_id_cache__kcore_dir(dir, sizeof(dir)))
|
||||
return -1;
|
||||
|
||||
scnprintf(to_dir, sizeof(to_dir), "%s/[kernel.kcore]/%s/%s",
|
||||
debugdir, sbuildid, dir);
|
||||
|
||||
if (mkdir_p(to_dir, 0755))
|
||||
return -1;
|
||||
|
||||
if (kcore_copy(from_dir, to_dir)) {
|
||||
/* Remove YYYYmmddHHMMSShh directory */
|
||||
if (!rmdir(to_dir)) {
|
||||
p = strrchr(to_dir, '/');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
/* Try to remove buildid directory */
|
||||
if (!rmdir(to_dir)) {
|
||||
p = strrchr(to_dir, '/');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
/* Try to remove [kernel.kcore] directory */
|
||||
rmdir(to_dir);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
pr_debug("kcore added to build-id cache directory %s\n", to_dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int build_id_cache__add_file(const char *filename, const char *debugdir)
|
||||
{
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
int err;
|
||||
|
||||
if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
|
||||
pr_debug("Couldn't read a build-id in %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
|
||||
err = build_id_cache__add_s(sbuild_id, debugdir, filename,
|
||||
false, false);
|
||||
if (verbose)
|
||||
pr_info("Adding %s %s: %s\n", sbuild_id, filename,
|
||||
err ? "FAIL" : "Ok");
|
||||
return err;
|
||||
}
|
||||
|
||||
static int build_id_cache__remove_file(const char *filename,
|
||||
const char *debugdir)
|
||||
{
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
|
||||
int err;
|
||||
|
||||
if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
|
||||
pr_debug("Couldn't read a build-id in %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
|
||||
err = build_id_cache__remove_s(sbuild_id, debugdir);
|
||||
if (verbose)
|
||||
pr_info("Removing %s %s: %s\n", sbuild_id, filename,
|
||||
err ? "FAIL" : "Ok");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
|
||||
if (dso__build_id_filename(dso, filename, sizeof(filename)) &&
|
||||
filename__read_build_id(filename, build_id,
|
||||
sizeof(build_id)) != sizeof(build_id)) {
|
||||
if (errno == ENOENT)
|
||||
return false;
|
||||
|
||||
pr_warning("Problems with %s file, consider removing it from the cache\n",
|
||||
filename);
|
||||
} else if (memcmp(dso->build_id, build_id, sizeof(dso->build_id))) {
|
||||
pr_warning("Problems with %s file, consider removing it from the cache\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *fp)
|
||||
{
|
||||
perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int build_id_cache__update_file(const char *filename,
|
||||
const char *debugdir)
|
||||
{
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
|
||||
int err;
|
||||
|
||||
if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
|
||||
pr_debug("Couldn't read a build-id in %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
|
||||
err = build_id_cache__remove_s(sbuild_id, debugdir);
|
||||
if (!err) {
|
||||
err = build_id_cache__add_s(sbuild_id, debugdir, filename,
|
||||
false, false);
|
||||
}
|
||||
if (verbose)
|
||||
pr_info("Updating %s %s: %s\n", sbuild_id, filename,
|
||||
err ? "FAIL" : "Ok");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cmd_buildid_cache(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
struct strlist *list;
|
||||
struct str_node *pos;
|
||||
int ret = 0;
|
||||
bool force = false;
|
||||
char debugdir[PATH_MAX];
|
||||
char const *add_name_list_str = NULL,
|
||||
*remove_name_list_str = NULL,
|
||||
*missing_filename = NULL,
|
||||
*update_name_list_str = NULL,
|
||||
*kcore_filename;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
struct perf_data_file file = {
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
struct perf_session *session = NULL;
|
||||
|
||||
const struct option buildid_cache_options[] = {
|
||||
OPT_STRING('a', "add", &add_name_list_str,
|
||||
"file list", "file(s) to add"),
|
||||
OPT_STRING('k', "kcore", &kcore_filename,
|
||||
"file", "kcore file to add"),
|
||||
OPT_STRING('r', "remove", &remove_name_list_str, "file list",
|
||||
"file(s) to remove"),
|
||||
OPT_STRING('M', "missing", &missing_filename, "file",
|
||||
"to find missing build ids in the cache"),
|
||||
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
|
||||
OPT_STRING('u', "update", &update_name_list_str, "file list",
|
||||
"file(s) to update"),
|
||||
OPT_INCR('v', "verbose", &verbose, "be more verbose"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const buildid_cache_usage[] = {
|
||||
"perf buildid-cache [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, buildid_cache_options,
|
||||
buildid_cache_usage, 0);
|
||||
|
||||
if (missing_filename) {
|
||||
file.path = missing_filename;
|
||||
file.force = force;
|
||||
|
||||
session = perf_session__new(&file, false, NULL);
|
||||
if (session == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (symbol__init(session ? &session->header.env : NULL) < 0)
|
||||
goto out;
|
||||
|
||||
setup_pager();
|
||||
|
||||
snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
|
||||
|
||||
if (add_name_list_str) {
|
||||
list = strlist__new(true, add_name_list_str);
|
||||
if (list) {
|
||||
strlist__for_each(pos, list)
|
||||
if (build_id_cache__add_file(pos->s, debugdir)) {
|
||||
if (errno == EEXIST) {
|
||||
pr_debug("%s already in the cache\n",
|
||||
pos->s);
|
||||
continue;
|
||||
}
|
||||
pr_warning("Couldn't add %s: %s\n",
|
||||
pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
|
||||
strlist__delete(list);
|
||||
}
|
||||
}
|
||||
|
||||
if (remove_name_list_str) {
|
||||
list = strlist__new(true, remove_name_list_str);
|
||||
if (list) {
|
||||
strlist__for_each(pos, list)
|
||||
if (build_id_cache__remove_file(pos->s, debugdir)) {
|
||||
if (errno == ENOENT) {
|
||||
pr_debug("%s wasn't in the cache\n",
|
||||
pos->s);
|
||||
continue;
|
||||
}
|
||||
pr_warning("Couldn't remove %s: %s\n",
|
||||
pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
|
||||
strlist__delete(list);
|
||||
}
|
||||
}
|
||||
|
||||
if (missing_filename)
|
||||
ret = build_id_cache__fprintf_missing(session, stdout);
|
||||
|
||||
if (update_name_list_str) {
|
||||
list = strlist__new(true, update_name_list_str);
|
||||
if (list) {
|
||||
strlist__for_each(pos, list)
|
||||
if (build_id_cache__update_file(pos->s, debugdir)) {
|
||||
if (errno == ENOENT) {
|
||||
pr_debug("%s wasn't in the cache\n",
|
||||
pos->s);
|
||||
continue;
|
||||
}
|
||||
pr_warning("Couldn't update %s: %s\n",
|
||||
pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
|
||||
strlist__delete(list);
|
||||
}
|
||||
}
|
||||
|
||||
if (kcore_filename &&
|
||||
build_id_cache__add_kcore(kcore_filename, debugdir, force))
|
||||
pr_warning("Couldn't add %s\n", kcore_filename);
|
||||
|
||||
out:
|
||||
if (session)
|
||||
perf_session__delete(session);
|
||||
|
||||
return ret;
|
||||
}
|
111
tools/perf/builtin-buildid-list.c
Normal file
111
tools/perf/builtin-buildid-list.c
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* builtin-buildid-list.c
|
||||
*
|
||||
* Builtin buildid-list command: list buildids in perf.data, in the running
|
||||
* kernel and in ELF files.
|
||||
*
|
||||
* Copyright (C) 2009, Red Hat Inc.
|
||||
* Copyright (C) 2009, Arnaldo Carvalho de Melo <acme@redhat.com>
|
||||
*/
|
||||
#include "builtin.h"
|
||||
#include "perf.h"
|
||||
#include "util/build-id.h"
|
||||
#include "util/cache.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/session.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/data.h"
|
||||
|
||||
static int sysfs__fprintf_build_id(FILE *fp)
|
||||
{
|
||||
u8 kallsyms_build_id[BUILD_ID_SIZE];
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
|
||||
if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id,
|
||||
sizeof(kallsyms_build_id)) != 0)
|
||||
return -1;
|
||||
|
||||
build_id__sprintf(kallsyms_build_id, sizeof(kallsyms_build_id),
|
||||
sbuild_id);
|
||||
fprintf(fp, "%s\n", sbuild_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int filename__fprintf_build_id(const char *name, FILE *fp)
|
||||
{
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
|
||||
if (filename__read_build_id(name, build_id,
|
||||
sizeof(build_id)) != sizeof(build_id))
|
||||
return 0;
|
||||
|
||||
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
|
||||
return fprintf(fp, "%s\n", sbuild_id);
|
||||
}
|
||||
|
||||
static bool dso__skip_buildid(struct dso *dso, int with_hits)
|
||||
{
|
||||
return with_hits && !dso->hit;
|
||||
}
|
||||
|
||||
static int perf_session__list_build_ids(bool force, bool with_hits)
|
||||
{
|
||||
struct perf_session *session;
|
||||
struct perf_data_file file = {
|
||||
.path = input_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
.force = force,
|
||||
};
|
||||
|
||||
symbol__elf_init();
|
||||
/*
|
||||
* See if this is an ELF file first:
|
||||
*/
|
||||
if (filename__fprintf_build_id(input_name, stdout))
|
||||
goto out;
|
||||
|
||||
session = perf_session__new(&file, false, &build_id__mark_dso_hit_ops);
|
||||
if (session == NULL)
|
||||
return -1;
|
||||
/*
|
||||
* in pipe-mode, the only way to get the buildids is to parse
|
||||
* the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID
|
||||
*/
|
||||
if (with_hits || perf_data_file__is_pipe(&file))
|
||||
perf_session__process_events(session, &build_id__mark_dso_hit_ops);
|
||||
|
||||
perf_session__fprintf_dsos_buildid(session, stdout, dso__skip_buildid, with_hits);
|
||||
perf_session__delete(session);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_buildid_list(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
bool show_kernel = false;
|
||||
bool with_hits = false;
|
||||
bool force = false;
|
||||
const struct option options[] = {
|
||||
OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"),
|
||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
|
||||
OPT_BOOLEAN('k', "kernel", &show_kernel, "Show current kernel build id"),
|
||||
OPT_INCR('v', "verbose", &verbose, "be more verbose"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const buildid_list_usage[] = {
|
||||
"perf buildid-list [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, buildid_list_usage, 0);
|
||||
setup_pager();
|
||||
|
||||
if (show_kernel)
|
||||
return sysfs__fprintf_build_id(stdout);
|
||||
|
||||
return perf_session__list_build_ids(force, with_hits);
|
||||
}
|
1172
tools/perf/builtin-diff.c
Normal file
1172
tools/perf/builtin-diff.c
Normal file
File diff suppressed because it is too large
Load diff
67
tools/perf/builtin-evlist.c
Normal file
67
tools/perf/builtin-evlist.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Builtin evlist command: Show the list of event selectors present
|
||||
* in a perf.data file.
|
||||
*/
|
||||
#include "builtin.h"
|
||||
|
||||
#include "util/util.h"
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "perf.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/parse-events.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/session.h"
|
||||
#include "util/data.h"
|
||||
#include "util/debug.h"
|
||||
|
||||
static int __cmd_evlist(const char *file_name, struct perf_attr_details *details)
|
||||
{
|
||||
struct perf_session *session;
|
||||
struct perf_evsel *pos;
|
||||
struct perf_data_file file = {
|
||||
.path = file_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
|
||||
session = perf_session__new(&file, 0, NULL);
|
||||
if (session == NULL)
|
||||
return -1;
|
||||
|
||||
evlist__for_each(session->evlist, pos)
|
||||
perf_evsel__fprintf(pos, details, stdout);
|
||||
|
||||
perf_session__delete(session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
struct perf_attr_details details = { .verbose = false, };
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file", "Input file name"),
|
||||
OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"),
|
||||
OPT_BOOLEAN('v', "verbose", &details.verbose,
|
||||
"Show all event attr details"),
|
||||
OPT_BOOLEAN('g', "group", &details.event_group,
|
||||
"Show event group information"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const evlist_usage[] = {
|
||||
"perf evlist [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, evlist_usage, 0);
|
||||
if (argc)
|
||||
usage_with_options(evlist_usage, options);
|
||||
|
||||
if (details.event_group && (details.verbose || details.freq)) {
|
||||
pr_err("--group option is not compatible with other options\n");
|
||||
usage_with_options(evlist_usage, options);
|
||||
}
|
||||
|
||||
return __cmd_evlist(input_name, &details);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue