diff options
Diffstat (limited to 'tools/perf')
294 files changed, 21798 insertions, 8053 deletions
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 8f8fbc227a4..782d86e961b 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore @@ -13,6 +13,7 @@ perf*.html  common-cmds.h  perf.data  perf.data.old +output.svg  perf-archive  tags  TAGS diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index 5a37a7c84e6..3ba1c0b0990 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile @@ -145,16 +145,17 @@ 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_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; \ +	QUIET_SUBDIR1	= ;$(NO_SUBDIR) \ +			   echo '  SUBDIR   ' $$subdir; \  			  $(MAKE) $(PRINT_DIR) -C $$subdir  	export V  endif @@ -183,47 +184,43 @@ ifdef missing_tools  endif  do-install-man: 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) +	$(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 -try-install-man:  ifdef missing_tools -	$(warning Please install $(missing_tools) to have the man pages installed) +  DO_INSTALL_MAN = $(warning Please install $(missing_tools) to have the man pages installed)  else -	$(MAKE) do-install-man +  DO_INSTALL_MAN = do-install-man  endif +try-install-man: $(DO_INSTALL_MAN) +  install-info: info -	$(INSTALL) -d -m 755 $(DESTDIR)$(infodir) -	$(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir) +	$(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 ;\ +		$(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 -	$(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir) -	$(INSTALL) -m 644 $(OUTPUT)user-manual.pdf $(DESTDIR)$(pdfdir) +	$(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) -ifneq ($(MAKECMDGOALS),clean) -ifneq ($(MAKECMDGOALS),tags) -$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE -	$(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) $(OUTPUT)PERF-VERSION-FILE - --include $(OUTPUT)PERF-VERSION-FILE -endif -endif  #  # Determine "include::" file references in asciidoc files. @@ -253,15 +250,17 @@ $(OUTPUT)cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)  	$(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: -	$(RM) $(MAN_XML) $(addsuffix +,$(MAN_XML)) -	$(RM) $(MAN_HTML) $(addsuffix +,$(MAN_HTML)) -	$(RM) $(DOC_HTML) $(DOC_MAN1) $(DOC_MAN5) $(DOC_MAN7) -	$(RM) $(OUTPUT)*.texi $(OUTPUT)*.texi+ $(OUTPUT)*.texi++ -	$(RM) $(OUTPUT)perf.info $(OUTPUT)perfman.info -	$(RM) $(OUTPUT)howto-index.txt $(OUTPUT)howto/*.html $(OUTPUT)doc.dep -	$(RM) $(OUTPUT)technical/api-*.html $(OUTPUT)technical/api-index.txt -	$(RM) $(cmds_txt) $(OUTPUT)*.made +	$(call QUIET_CLEAN, Documentation) $(RM) $(CLEAN_FILES)  $(MAN_HTML): $(OUTPUT)%.html : %.txt  	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ @@ -342,5 +341,3 @@ $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt  #quick-install-html:  #	'$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir) - -.PHONY: .FORCE-PERF-VERSION-FILE diff --git a/tools/perf/Documentation/perf-archive.txt b/tools/perf/Documentation/perf-archive.txt index 5032a142853..ac6ecbb3e66 100644 --- a/tools/perf/Documentation/perf-archive.txt +++ b/tools/perf/Documentation/perf-archive.txt @@ -12,9 +12,9 @@ SYNOPSIS  DESCRIPTION  ----------- -This command runs 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. +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 diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt index 7065cd6fbdf..4464ad770d5 100644 --- a/tools/perf/Documentation/perf-bench.txt +++ b/tools/perf/Documentation/perf-bench.txt @@ -48,6 +48,12 @@ SUBSYSTEM  'mem'::  	Memory access performance. +'numa':: +	NUMA scheduling and MM benchmarks. + +'futex':: +	Futex stressing benchmarks. +  'all'::  	All benchmark subsystems. @@ -187,6 +193,22 @@ Show only the result with page faults before memset.  --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] diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt index e9a8349a717..fd77d81ea74 100644 --- a/tools/perf/Documentation/perf-buildid-cache.txt +++ b/tools/perf/Documentation/perf-buildid-cache.txt @@ -21,6 +21,19 @@ 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. diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index fdfceee0ffd..b3b8abae62b 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt @@ -33,21 +33,25 @@ OPTIONS  -d::  --dsos=::  	Only consider symbols in these dsos. CSV that understands -	file://filename entries. +	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. +	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. +	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. +	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=:: @@ -89,6 +93,14 @@ OPTIONS  --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 @@ -157,6 +169,10 @@ with:    - 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: @@ -187,4 +203,4 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as:  SEE ALSO  -------- -linkperf:perf-record[1] +linkperf:perf-record[1], linkperf:perf-report[1] diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index ac84db2d233..52276a6d2b7 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt @@ -10,9 +10,9 @@ SYNOPSIS  [verse]  'perf kvm' [--host] [--guest] [--guestmount=<path>  	[--guestkallsyms=<path> --guestmodules=<path> | --guestvmlinux=<path>]] -	{top|record|report|diff|buildid-list} +	{top|record|report|diff|buildid-list} [<options>]  'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path> -	| --guestvmlinux=<path>] {top|record|report|diff|buildid-list|stat} +	| --guestvmlinux=<path>] {top|record|report|diff|buildid-list|stat} [<options>]  'perf kvm stat [record|report|live] [<options>]  DESCRIPTION @@ -24,10 +24,17 @@ There are a couple of variants of perf kvm:    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. If both -  --host and --guest are input, the perf data file name is perf.data.kvm. -  If there is  no --host but --guest, the file name is perf.data.guest. -  If there is no --guest but --host, the file name is perf.data.host. +  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. @@ -37,7 +44,9 @@ There are a couple of variants of perf kvm:    '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. +  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. @@ -58,14 +67,14 @@ There are a couple of variants of perf kvm:  OPTIONS  -------  -i:: ---input=:: +--input=<path>::          Input file name.  -o:: ---output:: +--output=<path>::          Output file name. ---host=:: +--host::          Collect host side performance profile. ---guest=:: +--guest::          Collect guest side performance profile.  --guestmount=<path>::  	Guest os root file system mount directory. Users mounts guest os @@ -84,6 +93,9 @@ OPTIONS  	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  ------------------- @@ -109,7 +121,9 @@ STAT LIVE OPTIONS  -m::  --mmap-pages=:: -    Number of mmap data pages. Must be a power of two. +    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:: diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt index c7f5f55634a..ab25be28c9d 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt @@ -48,7 +48,7 @@ REPORT OPTIONS  -k::  --key=<value>::          Sorting key. Possible values: acquired (default), contended, -        wait_total, wait_max, wait_min. +	avg_wait, wait_total, wait_max, wait_min.  INFO OPTIONS  ------------ diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt index 888d51137fb..1d78a4064da 100644 --- a/tools/perf/Documentation/perf-mem.txt +++ b/tools/perf/Documentation/perf-mem.txt @@ -18,6 +18,10 @@ 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>...:: diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index b715cb71592..1513935c399 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -136,6 +136,8 @@ Each probe argument follows below syntax.  '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. diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index e297b74471b..d460049cae8 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -57,6 +57,8 @@ OPTIONS  -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=:: @@ -66,8 +68,7 @@ OPTIONS  --realtime=::  	Collect data with this RT SCHED_FIFO priority. --D:: ---no-delay:: +--no-buffering::  	Collect data without buffering.  -c:: @@ -87,11 +88,25 @@ OPTIONS  -m::  --mmap-pages=:: -	Number of mmap data pages. Must be a power of two. +	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:: -	Do call-graph (stack chain/backtrace) recording. +	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:: @@ -166,9 +181,13 @@ following filters are defined:          - 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. +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 @@ -176,12 +195,25 @@ is enabled for all the sampling events. The sampled branch type is the same for  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. --W::  --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] diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 2b8097ee39d..d2b59af62bc 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -25,10 +25,6 @@ OPTIONS  --verbose::          Be more verbose. (show symbol address, etc) --d:: ---dsos=:: -	Only consider symbols in these dsos. CSV that understands -	file://filename entries.  -n::  --show-nr-samples::  	Show the number of samples for each symbol @@ -42,11 +38,18 @@ OPTIONS  -c::  --comms=::  	Only consider symbols in these comms. CSV that understands -	file://filename entries. +	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. +	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. @@ -71,7 +74,20 @@ OPTIONS  	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 debuggin info must be provided. +	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) @@ -85,10 +101,38 @@ OPTIONS  	- 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 @@ -135,6 +179,19 @@ OPTIONS  	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. @@ -219,10 +276,35 @@ OPTIONS  	Demangle symbol names to human readable form. It's enabled by default,  	disable with --no-demangle. +--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] diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index e9cbfcddfa3..05f9a0a6784 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -115,7 +115,7 @@ OPTIONS  -f::  --fields::          Comma separated list of fields to print. Options are: -        comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff. +        comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline.          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 @@ -203,6 +203,18 @@ OPTIONS  --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], diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 73c9759005a..29ee857c09c 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -133,10 +133,15 @@ 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:: ---initial-delay 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  -------- diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt index 1632b0efc75..5e0f986dff3 100644 --- a/tools/perf/Documentation/perf-timechart.txt +++ b/tools/perf/Documentation/perf-timechart.txt @@ -8,7 +8,7 @@ perf-timechart - Tool to visualize total system behavior during a workload  SYNOPSIS  --------  [verse] -'perf timechart' {record} +'perf timechart' [<timechart options>] {record} [<record options>]  DESCRIPTION  ----------- @@ -20,8 +20,8 @@ There are two variants of perf timechart:    'perf timechart' to turn a trace into a Scalable Vector Graphics file,    that can be viewed with popular SVG viewers such as 'Inkscape'. -OPTIONS -------- +TIMECHART OPTIONS +-----------------  -o::  --output=::          Select the output file (default: output.svg) @@ -34,12 +34,58 @@ OPTIONS  -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. + +RECORD OPTIONS +-------------- +-P:: +--power-only:: +        Record only power-related events +-T:: +--tasks-only:: +        Record only tasks-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  SEE ALSO  -------- diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 58d6598a968..180ae02137a 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -50,7 +50,6 @@ Default is to monitor all CPUS.  --count-filter=<count>::  	Only display functions with more events than this. --g::  --group::          Put the counters into a counter group. @@ -68,7 +67,9 @@ Default is to monitor all CPUS.  -m <pages>::  --mmap-pages=<pages>:: -	Number of mmapped data 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>:: @@ -86,7 +87,6 @@ Default is to monitor all CPUS.  --realtime=<priority>::  	Collect data with this RT SCHED_FIFO priority. --s <symbol>::  --sym-annotate=<symbol>::          Annotate this symbol. @@ -112,7 +112,18 @@ Default is to monitor all CPUS.  -s::  --sort:: -	Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, local_weight. +	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:: @@ -122,13 +133,16 @@ Default is to monitor all CPUS.  	Show a column with the sum of periods.  --dsos:: -	Only consider symbols in these 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. +	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. +	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. @@ -140,20 +154,26 @@ Default is to monitor all CPUS.  --asm-raw::  	Show raw instruction encoding of assembly instructions. --G [type,min,order]:: +-g:: +	Enables call-graph (stack chain/backtrace) recording. +  --call-graph:: -        Display call chains using type, min percent threshold 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. +	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. -	order can be either: -	- callee: callee based call graph. -	- caller: inverted caller based call graph. +--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: fractal,0.5,callee. +	Default: 127  --ignore-callees=<regex>::          Ignore callees of the function(s) matching the given regex. @@ -164,6 +184,15 @@ Default is to monitor all CPUS.  	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. +  INTERACTIVE PROMPTING KEYS  -------------------------- @@ -199,4 +228,4 @@ Pressing any unmapped key displays a menu, and prompts for input.  SEE ALSO  -------- -linkperf:perf-stat[1], linkperf:perf-list[1] +linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-report[1] diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index daccd2c0a48..fae38d9a44a 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -9,6 +9,7 @@ SYNOPSIS  --------  [verse]  'perf trace' +'perf trace record'  DESCRIPTION  ----------- @@ -16,9 +17,14 @@ This command will show the events associated with the target, initially  syscalls, but other system events like pagefaults, task lifetime events,  scheduling events, etc. -Initially this is a live mode only tool, but eventually will work with -perf.data files like the other tools, allowing a detached 'record' from -analysis phases. +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:*'). +Alernatively, the '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  ------- @@ -59,7 +65,9 @@ OPTIONS  -m::  --mmap-pages=:: -	Number of mmap data pages. Must be a power of two. +	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:: @@ -78,6 +86,27 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.  --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. +  SEE ALSO  --------  linkperf:perf-record[1], linkperf:perf-script[1] diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 025de796067..45da209b6ed 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -1,7 +1,14 @@  tools/perf  tools/scripts  tools/lib/traceevent -tools/lib/lk +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 diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 3a0ff7fb71b..cb2e5868c8e 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -1,818 +1,90 @@ -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 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. +# This is a simple wrapper Makefile that calls the main Makefile.perf +# with a -j option to do parallel builds  # -# Define LDFLAGS=-static to build a static binary. +# 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.  # -# 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. +# 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:  # -# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds) +.SUFFIXES: +  # -# Define NO_LIBUNWIND if you do not want libunwind dependency for dwarf -# backtrace post unwind. +# We don't want to pass along options like -j:  # -# Define NO_BACKTRACE if you do not want stack backtrace debug feature +unexport MAKEFLAGS +  # -# Define NO_LIBNUMA if you do not want numa perf benchmark +# 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.  # -# Define NO_LIBAUDIT if you do not want libaudit support +# (To override it, run 'make JOBS=1' and similar.)  # -# Define NO_LIBBIONIC if you do not want bionic support - -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: .FORCE-PERF-VERSION-FILE -	@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) - -CC = $(CROSS_COMPILE)gcc -AR = $(CROSS_COMPILE)ar - -RM      = rm -f -MKDIR   = mkdir -FIND    = find -INSTALL = install -FLEX    = flex -BISON   = bison -STRIP   = strip - -LK_DIR          = $(srctree)/tools/lib/lk/ -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 +ifeq ($(JOBS),) +  JOBS := $(shell grep -c ^processor /proc/cpuinfo 2>/dev/null) +  ifeq ($(JOBS),) +    JOBS := 1 +  endif  endif -ifeq ($(config),1) -include config/Makefile +# +# Only pass canonical directory names as the output directory: +# +ifneq ($(O),) +  FULL_O := $(shell readlink -f $(O) || echo $(O))  endif -export prefix bindir sharedir sysconfdir - -# 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 = -PYRF_OBJS = -SCRIPT_SH = - -SCRIPT_SH += perf-archive.sh - -grep-libs = $(filter -l%,$(1)) -strip-libs = $(filter-out -l%,$(1)) - -ifneq ($(OUTPUT),) -  TE_PATH=$(OUTPUT) -ifneq ($(subdir),) -  LK_PATH=$(OUTPUT)/../lib/lk/ -else -  LK_PATH=$(OUTPUT) -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 -  TE_PATH=$(TRACE_EVENT_DIR) -  LK_PATH=$(LK_DIR) +  override DEBUG = 0  endif -LIBTRACEEVENT = $(TE_PATH)libtraceevent.a -export LIBTRACEEVENT - -LIBLK = $(LK_PATH)liblk.a -export LIBLK - -# 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 +define print_msg +  @printf '  BUILD:   Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build\n' +endef -python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so +define make +  @$(MAKE) -f Makefile.perf --no-print-directory -j$(JOBS) O=$(FULL_O) $(SET_DEBUG) $@ +endef -PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) -PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBLK) - -$(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: +# 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.)  # - -SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) +all tags TAGS: +	$(print_msg) +	$(make)  # -# Single 'perf' binary right now: +# The clean target is not really parallel, don't print the jobs info:  # -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) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c - -$(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) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c - -$(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 += ../../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 += util/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 += util/include/linux/export.h -LIB_H += util/include/linux/magic.h -LIB_H += util/include/linux/poison.h -LIB_H += util/include/linux/prefetch.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 += util/include/linux/types.h -LIB_H += util/include/linux/linkage.h -LIB_H += util/include/asm/asm-offsets.h -LIB_H += util/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/sysfs.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/types.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/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/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 += ui/helpline.h -LIB_H += ui/progress.h -LIB_H += ui/util.h -LIB_H += ui/ui.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/sysfs.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/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/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/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)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/pmu.o -LIB_OBJS += $(OUTPUT)tests/hists_link.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 - -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)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) $(LIBLK) $(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_LIBUNWIND -  LIB_OBJS += $(OUTPUT)util/unwind.o -endif -LIB_OBJS += $(OUTPUT)tests/keep-tracking.o - -ifndef NO_LIBAUDIT -  BUILTIN_OBJS += $(OUTPUT)builtin-trace.o -endif +clean: +	$(make) -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/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/browser.h -  LIB_H += ui/browsers/map.h -  LIB_H += ui/keysyms.h -  LIB_H += ui/libslang.h -endif - -ifndef NO_GTK2 -  LIB_OBJS += $(OUTPUT)ui/gtk/browser.o -  LIB_OBJS += $(OUTPUT)ui/gtk/hists.o -  LIB_OBJS += $(OUTPUT)ui/gtk/setup.o -  LIB_OBJS += $(OUTPUT)ui/gtk/util.o -  LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o -  LIB_OBJS += $(OUTPUT)ui/gtk/progress.o -  LIB_OBJS += $(OUTPUT)ui/gtk/annotate.o -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 -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 $@ - -$(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: -.SUFFIXES: .o .c .S .s - -# 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)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS -	$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_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/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) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h -$(DIRECTORY_DEPS): | $(sort $(dir $(DIRECTORY_DEPS))) -# In the second step, we make a rule to actually create these directories -$(sort $(dir $(DIRECTORY_DEPS))): -	$(QUIET_MKDIR)$(MKDIR) -p $@ 2>/dev/null - -$(LIB_FILE): $(LIB_OBJS) -	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) - -# libtraceevent.a -$(LIBTRACEEVENT): -	$(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libtraceevent.a - -$(LIBTRACEEVENT)-clean: -	$(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean - -# if subdir is set, we've been called from above so target has been built -# already -$(LIBLK): -ifeq ($(subdir),) -	$(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) liblk.a -endif - -$(LIBLK)-clean: -ifeq ($(subdir),) -	$(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean -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 "make prefix=<path> <install target>" 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) - -TAGS: -	$(RM) TAGS -	$(FIND) . -name '*.[hcS]' -print | xargs etags -a - -tags: -	$(RM) tags -	$(FIND) . -name '*.[hcS]' -print | xargs ctags -a - -cscope: -	$(RM) cscope* -	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b - -### Detect prefix changes -TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):\ -             $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_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 "    * 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-bin: all -	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' -	$(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' -	$(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' -ifndef NO_LIBPERL -	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' -	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' -	$(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) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' -endif -ifndef NO_LIBPYTHON -	$(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 -	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' -	$(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' -	$(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-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 +# +# The build-test target is not really parallel, don't print the jobs info: +# +build-test: +	@$(MAKE) -f tests/make --no-print-directory -clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean -	$(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) -	$(RM) $(ALL_PROGRAMS) perf -	$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* -	$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean -	$(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS -	$(RM) $(OUTPUT)util/*-bison* -	$(RM) $(OUTPUT)util/*-flex* -	$(python-clean) +# +# All other targets get passed through: +# +%: +	$(print_msg) +	$(make) -.PHONY: all install clean strip $(LIBTRACEEVENT) $(LIBLK) -.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell -.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS +.PHONY: tags TAGS diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf new file mode 100644 index 00000000000..9670a16fa57 --- /dev/null +++ b/tools/perf/Makefile.perf @@ -0,0 +1,938 @@ +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 + +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/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 += ui/helpline.h +LIB_H += ui/progress.h +LIB_H += ui/util.h +LIB_H += ui/ui.h +LIB_H += util/data.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/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)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/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 + +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]) + +# 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)' +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.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 + diff --git a/tools/perf/arch/arm/Makefile b/tools/perf/arch/arm/Makefile index 15130b50dfe..09d62153d38 100644 --- a/tools/perf/arch/arm/Makefile +++ b/tools/perf/arch/arm/Makefile @@ -2,3 +2,13 @@ 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 diff --git a/tools/perf/arch/arm/include/perf_regs.h b/tools/perf/arch/arm/include/perf_regs.h new file mode 100644 index 00000000000..f619c9c5a4b --- /dev/null +++ b/tools/perf/arch/arm/include/perf_regs.h @@ -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 */ diff --git a/tools/perf/arch/arm/tests/dwarf-unwind.c b/tools/perf/arch/arm/tests/dwarf-unwind.c new file mode 100644 index 00000000000..9f870d27cb3 --- /dev/null +++ b/tools/perf/arch/arm/tests/dwarf-unwind.c @@ -0,0 +1,60 @@ +#include <string.h> +#include "perf_regs.h" +#include "thread.h" +#include "map.h" +#include "event.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); +} diff --git a/tools/perf/arch/arm/tests/regs_load.S b/tools/perf/arch/arm/tests/regs_load.S new file mode 100644 index 00000000000..e09e983946f --- /dev/null +++ b/tools/perf/arch/arm/tests/regs_load.S @@ -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) diff --git a/tools/perf/arch/arm/util/unwind-libdw.c b/tools/perf/arch/arm/util/unwind-libdw.c new file mode 100644 index 00000000000..b4176c60117 --- /dev/null +++ b/tools/perf/arch/arm/util/unwind-libdw.c @@ -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); +} diff --git a/tools/perf/arch/arm/util/unwind-libunwind.c b/tools/perf/arch/arm/util/unwind-libunwind.c new file mode 100644 index 00000000000..729ed69a666 --- /dev/null +++ b/tools/perf/arch/arm/util/unwind-libunwind.c @@ -0,0 +1,48 @@ + +#include <errno.h> +#include <libunwind.h> +#include "perf_regs.h" +#include "../../util/unwind.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; +} diff --git a/tools/perf/arch/arm64/Makefile b/tools/perf/arch/arm64/Makefile new file mode 100644 index 00000000000..67e9b3d38e8 --- /dev/null +++ b/tools/perf/arch/arm64/Makefile @@ -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 diff --git a/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h new file mode 100644 index 00000000000..e9441b9e2a3 --- /dev/null +++ b/tools/perf/arch/arm64/include/perf_regs.h @@ -0,0 +1,88 @@ +#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_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 */ diff --git a/tools/perf/arch/arm64/util/dwarf-regs.c b/tools/perf/arch/arm64/util/dwarf-regs.c new file mode 100644 index 00000000000..d49efeb8172 --- /dev/null +++ b/tools/perf/arch/arm64/util/dwarf-regs.c @@ -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; +} diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c new file mode 100644 index 00000000000..436ee43859d --- /dev/null +++ b/tools/perf/arch/arm64/util/unwind-libunwind.c @@ -0,0 +1,82 @@ + +#include <errno.h> +#include <libunwind.h> +#include "perf_regs.h" +#include "../../util/unwind.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; +} diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index aacef07ebf3..42faf369211 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c @@ -154,8 +154,7 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env,  		}  		if (lookup_path(buf))  			goto out; -		free(buf); -		buf = NULL; +		zfree(&buf);  	}  	if (!strcmp(arch, "arm")) diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile index 8801fe02f20..1641542e363 100644 --- a/tools/perf/arch/x86/Makefile +++ b/tools/perf/arch/x86/Makefile @@ -3,7 +3,14 @@ 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.o +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 diff --git a/tools/perf/arch/x86/include/perf_regs.h b/tools/perf/arch/x86/include/perf_regs.h index 7fcdcdbee91..7df517acfef 100644 --- a/tools/perf/arch/x86/include/perf_regs.h +++ b/tools/perf/arch/x86/include/perf_regs.h @@ -2,17 +2,23 @@  #define ARCH_PERF_REGS_H  #include <stdlib.h> -#include "../../util/types.h" +#include <linux/types.h>  #include <asm/perf_regs.h> -#ifndef ARCH_X86_64 +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 @@ -52,7 +58,7 @@ static inline const char *perf_reg_name(int id)  		return "FS";  	case PERF_REG_X86_GS:  		return "GS"; -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT  	case PERF_REG_X86_R8:  		return "R8";  	case PERF_REG_X86_R9: @@ -69,7 +75,7 @@ static inline const char *perf_reg_name(int id)  		return "R14";  	case PERF_REG_X86_R15:  		return "R15"; -#endif /* ARCH_X86_64 */ +#endif /* HAVE_ARCH_X86_64_SUPPORT */  	default:  		return NULL;  	} diff --git a/tools/perf/arch/x86/tests/dwarf-unwind.c b/tools/perf/arch/x86/tests/dwarf-unwind.c new file mode 100644 index 00000000000..9f89f899ccc --- /dev/null +++ b/tools/perf/arch/x86/tests/dwarf-unwind.c @@ -0,0 +1,60 @@ +#include <string.h> +#include "perf_regs.h" +#include "thread.h" +#include "map.h" +#include "event.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); +} diff --git a/tools/perf/arch/x86/tests/regs_load.S b/tools/perf/arch/x86/tests/regs_load.S new file mode 100644 index 00000000000..60875d5c556 --- /dev/null +++ b/tools/perf/arch/x86/tests/regs_load.S @@ -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 diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c index b2519e49424..40021fa3129 100644 --- a/tools/perf/arch/x86/util/tsc.c +++ b/tools/perf/arch/x86/util/tsc.c @@ -4,7 +4,7 @@  #include <linux/perf_event.h>  #include "../../perf.h" -#include "../../util/types.h" +#include <linux/types.h>  #include "../../util/debug.h"  #include "tsc.h" diff --git a/tools/perf/arch/x86/util/tsc.h b/tools/perf/arch/x86/util/tsc.h index a24dec81c79..2affe0366b5 100644 --- a/tools/perf/arch/x86/util/tsc.h +++ b/tools/perf/arch/x86/util/tsc.h @@ -1,7 +1,7 @@  #ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__  #define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ -#include "../../util/types.h" +#include <linux/types.h>  struct perf_tsc_conversion {  	u16 time_shift; diff --git a/tools/perf/arch/x86/util/unwind-libdw.c b/tools/perf/arch/x86/util/unwind-libdw.c new file mode 100644 index 00000000000..c4b72176ca8 --- /dev/null +++ b/tools/perf/arch/x86/util/unwind-libdw.c @@ -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); +} diff --git a/tools/perf/arch/x86/util/unwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c index 78d956eff96..3261f68c6a7 100644 --- a/tools/perf/arch/x86/util/unwind.c +++ b/tools/perf/arch/x86/util/unwind-libunwind.c @@ -4,8 +4,8 @@  #include "perf_regs.h"  #include "../../util/unwind.h" -#ifdef ARCH_X86_64 -int unwind__arch_reg_id(int regnum) +#ifdef HAVE_ARCH_X86_64_SUPPORT +int libunwind__arch_reg_id(int regnum)  {  	int id; @@ -69,7 +69,7 @@ int unwind__arch_reg_id(int regnum)  	return id;  }  #else -int unwind__arch_reg_id(int regnum) +int libunwind__arch_reg_id(int regnum)  {  	int id; @@ -108,4 +108,4 @@ int unwind__arch_reg_id(int regnum)  	return id;  } -#endif /* ARCH_X86_64 */ +#endif /* HAVE_ARCH_X86_64_SUPPORT */ diff --git a/tools/perf/bash_completion b/tools/perf/bash_completion deleted file mode 100644 index 56e6a12aab5..00000000000 --- a/tools/perf/bash_completion +++ /dev/null @@ -1,62 +0,0 @@ -# perf completion - -function_exists() -{ -	declare -F $1 > /dev/null -	return $? -} - -function_exists __ltrim_colon_completions || -__ltrim_colon_completions() -{ -	if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then -		# Remove colon-word prefix from COMPREPLY items -		local colon_word=${1%${1##*:}} -		local i=${#COMPREPLY[*]} -		while [[ $((--i)) -ge 0 ]]; do -			COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} -		done -	fi -} - -have perf && -_perf() -{ -	local cur prev cmd - -	COMPREPLY=() -	if function_exists _get_comp_words_by_ref; then -		_get_comp_words_by_ref -n : cur prev -	else -		cur=$(_get_cword :) -		prev=${COMP_WORDS[COMP_CWORD-1]} -	fi - -	cmd=${COMP_WORDS[0]} - -	# List perf subcommands or long options -	if [ $COMP_CWORD -eq 1 ]; then -		if [[ $cur == --* ]]; then -			COMPREPLY=( $( compgen -W '--help --version \ -			--exec-path --html-path --paginate --no-pager \ -			--perf-dir --work-tree --debugfs-dir' -- "$cur" ) ) -		else -			cmds=$($cmd --list-cmds) -			COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) ) -		fi -	# List possible events for -e option -	elif [[ $prev == "-e" && "${COMP_WORDS[1]}" == @(record|stat|top) ]]; then -		evts=$($cmd list --raw-dump) -		COMPREPLY=( $( compgen -W '$evts' -- "$cur" ) ) -		__ltrim_colon_completions $cur -	# List long option names -	elif [[ $cur == --* ]];  then -		subcmd=${COMP_WORDS[1]} -		opts=$($cmd $subcmd --list-opts) -		COMPREPLY=( $( compgen -W '$opts' -- "$cur" ) ) -	# Fall down to list regular files -	else -		_filedir -	fi -} && -complete -F _perf perf diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h index 0fdc85269c4..eba46709b27 100644 --- a/tools/perf/bench/bench.h +++ b/tools/perf/bench/bench.h @@ -31,6 +31,9 @@ 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 diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c new file mode 100644 index 00000000000..a84206e9c4a --- /dev/null +++ b/tools/perf/bench/futex-hash.c @@ -0,0 +1,212 @@ +/* + * 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; + +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, +					 fshared ? 0 : FUTEX_PRIVATE_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; + +	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"); +} diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c new file mode 100644 index 00000000000..a16255876f1 --- /dev/null +++ b/tools/perf/bench/futex-requeue.c @@ -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; + +/* + * There can be significant variance from run to run, + * the more repeats, the more exact the overall avg and + * the better idea of the futex latency. + */ +static unsigned int repeat = 10; + +static pthread_t *worker; +static bool done = 0, silent = 0; +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 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_UINTEGER('r', "repeat",   &repeat,   "Specify amount of times to repeat the run"), +	OPT_BOOLEAN( 's', "silent",   &silent,   "Silent mode: do not display data/details"), +	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_PRIVATE_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"); + +	printf("Run summary [PID %d]: Requeuing %d threads (from %p to %p), " +	       "%d at a time.\n\n", +	       getpid(), nthreads, &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 < 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_PRIVATE_FLAG); +		gettimeofday(&end, NULL); +		timersub(&end, &start, &runtime); + +		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_PRIVATE_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); +} diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c new file mode 100644 index 00000000000..d096169b161 --- /dev/null +++ b/tools/perf/bench/futex-wake.c @@ -0,0 +1,201 @@ +/* + * 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; + +/* + * There can be significant variance from run to run, + * the more repeats, the more exact the overall avg and + * the better idea of the futex latency. + */ +static unsigned int repeat = 10; + +pthread_t *worker; +static bool done = 0, silent = 0; +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 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_UINTEGER('r', "repeat",  &repeat,   "Specify amount of times to repeat the run"), +	OPT_BOOLEAN( 's', "silent",  &silent,   "Silent mode: do not display data/details"), +	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_PRIVATE_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"); + +	printf("Run summary [PID %d]: blocking on %d threads (at futex %p), " +	       "waking up %d at a time.\n\n", +	       getpid(), nthreads, &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 < 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_PRIVATE_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; +} diff --git a/tools/perf/bench/futex.h b/tools/perf/bench/futex.h new file mode 100644 index 00000000000..71f2844cf97 --- /dev/null +++ b/tools/perf/bench/futex.h @@ -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 */ diff --git a/tools/perf/bench/mem-memcpy-arch.h b/tools/perf/bench/mem-memcpy-arch.h index a72e36cb539..57b4ed87145 100644 --- a/tools/perf/bench/mem-memcpy-arch.h +++ b/tools/perf/bench/mem-memcpy-arch.h @@ -1,5 +1,5 @@ -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT  #define MEMCPY_FN(fn, name, desc)		\  	extern void *fn(void *, const void *, size_t); diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index 8cdca43016b..5ce71d3b72c 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c @@ -58,7 +58,7 @@ struct routine routines[] = {  	{ "default",  	  "Default memcpy() provided by glibc",  	  memcpy }, -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT  #define MEMCPY_FN(fn, name, desc) { name, desc, fn },  #include "mem-memcpy-x86-64-asm-def.h" diff --git a/tools/perf/bench/mem-memset-arch.h b/tools/perf/bench/mem-memset-arch.h index a040fa77665..633800cb0dc 100644 --- a/tools/perf/bench/mem-memset-arch.h +++ b/tools/perf/bench/mem-memset-arch.h @@ -1,5 +1,5 @@ -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT  #define MEMSET_FN(fn, name, desc)		\  	extern void *fn(void *, int, size_t); diff --git a/tools/perf/bench/mem-memset.c b/tools/perf/bench/mem-memset.c index 4a2f1208196..9af79d2b18e 100644 --- a/tools/perf/bench/mem-memset.c +++ b/tools/perf/bench/mem-memset.c @@ -58,7 +58,7 @@ static const struct routine routines[] = {  	{ "default",  	  "Default memset() provided by glibc",  	  memset }, -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT  #define MEMSET_FN(fn, name, desc) { name, desc, fn },  #include "mem-memset-x86-64-asm-def.h" diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 30d1c3225b4..ebfa163b80b 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -429,14 +429,14 @@ static int parse_cpu_list(const char *arg)  	return 0;  } -static void parse_setup_cpu_list(void) +static int parse_setup_cpu_list(void)  {  	struct thread_data *td;  	char *str0, *str;  	int t;  	if (!g->p.cpu_list_str) -		return; +		return 0;  	dprintf("g->p.nr_tasks: %d\n", g->p.nr_tasks); @@ -500,8 +500,12 @@ static void parse_setup_cpu_list(void)  		dprintf("CPUs: %d_%d-%d#%dx%d\n", bind_cpu_0, bind_len, bind_cpu_1, step, mul); -		BUG_ON(bind_cpu_0 < 0 || bind_cpu_0 >= g->p.nr_cpus); -		BUG_ON(bind_cpu_1 < 0 || bind_cpu_1 >= g->p.nr_cpus); +		if (bind_cpu_0 >= g->p.nr_cpus || bind_cpu_1 >= g->p.nr_cpus) { +			printf("\nTest not applicable, system has only %d CPUs.\n", g->p.nr_cpus); +			return -1; +		} + +		BUG_ON(bind_cpu_0 < 0 || bind_cpu_1 < 0);  		BUG_ON(bind_cpu_0 > bind_cpu_1);  		for (bind_cpu = bind_cpu_0; bind_cpu <= bind_cpu_1; bind_cpu += step) { @@ -541,6 +545,7 @@ out:  		printf("# NOTE: %d tasks bound, %d tasks unbound\n", t, g->p.nr_tasks - t);  	free(str0); +	return 0;  }  static int parse_cpus_opt(const struct option *opt __maybe_unused, @@ -561,14 +566,14 @@ static int parse_node_list(const char *arg)  	return 0;  } -static void parse_setup_node_list(void) +static int parse_setup_node_list(void)  {  	struct thread_data *td;  	char *str0, *str;  	int t;  	if (!g->p.node_list_str) -		return; +		return 0;  	dprintf("g->p.nr_tasks: %d\n", g->p.nr_tasks); @@ -619,8 +624,12 @@ static void parse_setup_node_list(void)  		dprintf("NODEs: %d-%d #%d\n", bind_node_0, bind_node_1, step); -		BUG_ON(bind_node_0 < 0 || bind_node_0 >= g->p.nr_nodes); -		BUG_ON(bind_node_1 < 0 || bind_node_1 >= g->p.nr_nodes); +		if (bind_node_0 >= g->p.nr_nodes || bind_node_1 >= g->p.nr_nodes) { +			printf("\nTest not applicable, system has only %d nodes.\n", g->p.nr_nodes); +			return -1; +		} + +		BUG_ON(bind_node_0 < 0 || bind_node_1 < 0);  		BUG_ON(bind_node_0 > bind_node_1);  		for (bind_node = bind_node_0; bind_node <= bind_node_1; bind_node += step) { @@ -651,6 +660,7 @@ out:  		printf("# NOTE: %d tasks mem-bound, %d tasks unbound\n", t, g->p.nr_tasks - t);  	free(str0); +	return 0;  }  static int parse_nodes_opt(const struct option *opt __maybe_unused, @@ -1110,7 +1120,7 @@ static void *worker_thread(void *__tdata)  		/* Check whether our max runtime timed out: */  		if (g->p.nr_secs) {  			timersub(&stop, &start0, &diff); -			if (diff.tv_sec >= g->p.nr_secs) { +			if ((u32)diff.tv_sec >= g->p.nr_secs) {  				g->stop_work = true;  				break;  			} @@ -1157,7 +1167,7 @@ static void *worker_thread(void *__tdata)  			runtime_ns_max += diff.tv_usec * 1000;  			if (details >= 0) { -				printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016lx]\n", +				printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016"PRIx64"]\n",  					process_nr, thread_nr, runtime_ns_max / bytes_done, val);  			}  			fflush(stdout); @@ -1356,8 +1366,8 @@ static int init(void)  	init_thread_data();  	tprintf("#\n"); -	parse_setup_cpu_list(); -	parse_setup_node_list(); +	if (parse_setup_cpu_list() || parse_setup_node_list()) +		return -1;  	tprintf("#\n");  	print_summary(); @@ -1583,6 +1593,11 @@ static void init_params(struct params *p, const char *name, int argc, const char  	p->data_rand_walk		= true;  	p->nr_loops			= -1;  	p->init_random			= true; +	p->mb_global_str		= "1"; +	p->nr_proc			= 1; +	p->nr_threads			= 1; +	p->nr_secs			= 5; +	p->run_all			= argc == 1;  }  static int run_bench_numa(const char *name, const char **argv) @@ -1600,7 +1615,6 @@ static int run_bench_numa(const char *name, const char **argv)  	return 0;  err: -	usage_with_options(numa_usage, options);  	return -1;  } @@ -1701,8 +1715,7 @@ static int bench_all(void)  	BUG_ON(ret < 0);  	for (i = 0; i < nr; i++) { -		if (run_bench_numa(tests[i][0], tests[i] + 1)) -			return -1; +		run_bench_numa(tests[i][0], tests[i] + 1);  	}  	printf("\n"); diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c index 69cfba8d4c6..07a8d7646a1 100644 --- a/tools/perf/bench/sched-pipe.c +++ b/tools/perf/bench/sched-pipe.c @@ -7,9 +7,7 @@   * 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" @@ -28,12 +26,24 @@  #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; +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_INTEGER('l', "loop",	&loops,		"Specify number of loops"), +	OPT_BOOLEAN('T', "threaded",	&threaded,	"Specify threads/process based task setup"),  	OPT_END()  }; @@ -42,13 +52,37 @@ static const char * const bench_sched_pipe_usage[] = {  	NULL  }; -int bench_sched_pipe(int argc, const char **argv, -		     const char *prefix __maybe_unused) +static void *worker_thread(void *__tdata)  { -	int pipe_1[2], pipe_2[2]; +	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? @@ -58,43 +92,66 @@ int bench_sched_pipe(int argc, const char **argv,  	int __maybe_unused ret, wait_stat;  	pid_t pid, retpid __maybe_unused; -	argc = parse_options(argc, argv, options, -			     bench_sched_pipe_usage, 0); +	argc = parse_options(argc, argv, options, bench_sched_pipe_usage, 0);  	BUG_ON(pipe(pipe_1));  	BUG_ON(pipe(pipe_2)); -	pid = fork(); -	assert(pid >= 0); -  	gettimeofday(&start, NULL); -	if (!pid) { -		for (i = 0; i < loops; i++) { -			ret = read(pipe_1[0], &m, sizeof(int)); -			ret = write(pipe_2[1], &m, sizeof(int)); -		} -	} else { -		for (i = 0; i < loops; i++) { -			ret = write(pipe_1[1], &m, sizeof(int)); -			ret = read(pipe_2[0], &m, sizeof(int)); +	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];  		}  	} -	gettimeofday(&stop, NULL); -	timersub(&stop, &start, &diff); -	if (pid) { +	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)); -	} else { -		exit(0);  	} +	gettimeofday(&stop, NULL); +	timersub(&stop, &start, &diff); +  	switch (bench_format) {  	case BENCH_FORMAT_DEFAULT: -		printf("# Executed %d pipe operations between two tasks\n\n", -			loops); +		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; diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 5ebd0c3b71b..1ec429fef2b 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -28,8 +28,10 @@  #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 { @@ -44,7 +46,7 @@ struct perf_annotate {  };  static int perf_evsel__add_sample(struct perf_evsel *evsel, -				  struct perf_sample *sample, +				  struct perf_sample *sample __maybe_unused,  				  struct addr_location *al,  				  struct perf_annotate *ann)  { @@ -63,21 +65,13 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,  		return 0;  	} -	he = __hists__add_entry(&evsel->hists, al, NULL, 1, 1); +	he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0, +				true);  	if (he == NULL)  		return -ENOMEM; -	ret = 0; -	if (he->ms.sym != NULL) { -		struct annotation *notes = symbol__annotation(he->ms.sym); -		if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0) -			return -ENOMEM; - -		ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); -	} - -	evsel->hists.stats.total_period += sample->period; -	hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); +	ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); +	hists__inc_nr_samples(&evsel->hists, true);  	return ret;  } @@ -116,11 +110,11 @@ static int hist_entry__tty_annotate(struct hist_entry *he,  				    ann->print_line, ann->full_paths, 0, 0);  } -static void hists__find_annotations(struct hists *self, +static void hists__find_annotations(struct hists *hists,  				    struct perf_evsel *evsel,  				    struct perf_annotate *ann)  { -	struct rb_node *nd = rb_first(&self->entries), *next; +	struct rb_node *nd = rb_first(&hists->entries), *next;  	int key = K_RIGHT;  	while (nd) { @@ -142,8 +136,18 @@ find_next:  		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 = hist_entry__gtk_annotate(he, evsel, NULL); +			ret = annotate(he, evsel, NULL);  			if (!ret || !ann->skip_missing)  				return; @@ -176,8 +180,7 @@ find_next:  			 * symbol, free he->ms.sym->src to signal we already  			 * processed this symbol.  			 */ -			free(notes->src); -			notes->src = NULL; +			zfree(¬es->src);  		}  	}  } @@ -188,9 +191,13 @@ static int __cmd_annotate(struct perf_annotate *ann)  	struct perf_session *session;  	struct perf_evsel *pos;  	u64 total_nr_samples; +	struct perf_data_file file = { +		.path  = input_name, +		.mode  = PERF_DATA_MODE_READ, +		.force = ann->force, +	}; -	session = perf_session__new(input_name, O_RDONLY, -				    ann->force, false, &ann->tool); +	session = perf_session__new(&file, false, &ann->tool);  	if (session == NULL)  		return -ENOMEM; @@ -225,13 +232,13 @@ static int __cmd_annotate(struct perf_annotate *ann)  		perf_session__fprintf_dsos(session, stdout);  	total_nr_samples = 0; -	list_for_each_entry(pos, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, pos) {  		struct hists *hists = &pos->hists;  		u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];  		if (nr_samples > 0) {  			total_nr_samples += nr_samples; -			hists__collapse_resort(hists); +			hists__collapse_resort(hists, NULL);  			hists__output_resort(hists);  			if (symbol_conf.event_group && @@ -243,12 +250,21 @@ static int __cmd_annotate(struct perf_annotate *ann)  	}  	if (total_nr_samples == 0) { -		ui__error("The %s file has no samples!\n", session->filename); +		ui__error("The %s file has no samples!\n", file.path);  		goto out_delete;  	} -	if (use_browser == 2) -		perf_gtk__show_annotations(); +	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_delete; +		} +		show_annotations(); +	}  out_delete:  	/* @@ -348,7 +364,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)  	if (argc) {  		/* -		 * Special case: if there's an argument left then assume tha +		 * Special case: if there's an argument left then assume that  		 * it's a symbol filter:  		 */  		if (argc > 1) diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index 77298bf892b..1e6e7771054 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -1,21 +1,19 @@  /* - *   * builtin-bench.c   * - * General benchmarking subsystem provided by perf + * General benchmarking collections provided by perf   *   * Copyright (C) 2009, Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> - *   */  /* + * Available benchmark collection list:   * - * Available subsystem list: - *  sched ... scheduler and IPC mechanism + *  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" @@ -25,112 +23,101 @@  #include <stdio.h>  #include <stdlib.h>  #include <string.h> +#include <sys/prctl.h> -struct bench_suite { -	const char *name; -	const char *summary; -	int (*fn)(int, const char **, const char *); +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;  }; -						\ -/* sentinel: easy for help */ -#define suite_all { "all", "Test all benchmark suites", NULL } - -#ifdef LIBNUMA_SUPPORT -static struct bench_suite numa_suites[] = { -	{ "mem", -	  "Benchmark for NUMA workloads", -	  bench_numa }, -	suite_all, -	{ NULL, -	  NULL, -	  NULL                  } + +#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_suite sched_suites[] = { -	{ "messaging", -	  "Benchmark for scheduler and IPC mechanisms", -	  bench_sched_messaging }, -	{ "pipe", -	  "Flood of communication over pipe() between two processes", -	  bench_sched_pipe      }, -	suite_all, -	{ NULL, -	  NULL, -	  NULL                  } +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_suite mem_suites[] = { -	{ "memcpy", -	  "Simple memory copy in various ways", -	  bench_mem_memcpy }, -	{ "memset", -	  "Simple memory set in various ways", -	  bench_mem_memset }, -	suite_all, -	{ 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 bench_subsys { -	const char *name; -	const char *summary; -	struct bench_suite *suites; +struct collection { +	const char	*name; +	const char	*summary; +	struct bench	*benchmarks;  }; -static struct bench_subsys subsystems[] = { -#ifdef LIBNUMA_SUPPORT -	{ "numa", -	  "NUMA scheduling and MM behavior", -	  numa_suites }, +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 -	{ "sched", -	  "scheduler and IPC mechanism", -	  sched_suites }, -	{ "mem", -	  "memory access performance", -	  mem_suites }, -	{ "all",		/* sentinel: easy for help */ -	  "all benchmark subsystem", -	  NULL }, -	{ NULL, -	  NULL, -	  NULL       } +	{"futex",       "Futex stressing benchmarks",                   futex_benchmarks        }, +	{ "all",	"All benchmarks",				NULL			}, +	{ NULL,		NULL,						NULL			}  }; -static void dump_suites(int subsys_index) +/* 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)  { -	int i; +	struct bench *bench; -	printf("# List of available suites for %s...\n\n", -	       subsystems[subsys_index].name); +	printf("\n        # List of available benchmarks for collection '%s':\n\n", coll->name); -	for (i = 0; subsystems[subsys_index].suites[i].name; i++) -		printf("%14s: %s\n", -		       subsystems[subsys_index].suites[i].name, -		       subsystems[subsys_index].suites[i].summary); +	for_each_bench(coll, bench) +		printf("%14s: %s\n", bench->name, bench->summary);  	printf("\n"); -	return;  }  static const char *bench_format_str; + +/* Output/formatting style, exported to benchmark modules: */  int bench_format = BENCH_FORMAT_DEFAULT;  static const struct option bench_options[] = { -	OPT_STRING('f', "format", &bench_format_str, "default", -		    "Specify format style"), +	OPT_STRING('f', "format", &bench_format_str, "default", "Specify format style"),  	OPT_END()  };  static const char * const bench_usage[] = { -	"perf bench [<common options>] <subsystem> <suite> [<options>]", +	"perf bench [<common options>] <collection> <benchmark> [<options>]",  	NULL  };  static void print_usage(void)  { +	struct collection *coll;  	int i;  	printf("Usage: \n"); @@ -138,11 +125,10 @@ static void print_usage(void)  		printf("\t%s\n", bench_usage[i]);  	printf("\n"); -	printf("# List of available subsystems...\n\n"); +	printf("        # List of all available benchmark collections:\n\n"); -	for (i = 0; subsystems[i].name; i++) -		printf("%14s: %s\n", -		       subsystems[i].name, subsystems[i].summary); +	for_each_collection(coll) +		printf("%14s: %s\n", coll->name, coll->summary);  	printf("\n");  } @@ -159,44 +145,74 @@ static int bench_str2int(const char *str)  	return BENCH_FORMAT_UNKNOWN;  } -static void all_suite(struct bench_subsys *subsys)	  /* FROM HERE */ +/* + * 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)  { -	int i; +	struct bench *bench;  	const char *argv[2]; -	struct bench_suite *suites = subsys->suites;  	argv[1] = NULL;  	/*  	 * TODO: -	 * preparing preset parameters for +	 * +	 * Preparing preset parameters for  	 * embedded, ordinary PC, HPC, etc... -	 * will be helpful +	 * would be helpful.  	 */ -	for (i = 0; suites[i].fn; i++) { -		printf("# Running %s/%s benchmark...\n", -		       subsys->name, -		       suites[i].name); +	for_each_bench(coll, bench) { +		if (!bench->fn) +			break; +		printf("# Running %s/%s benchmark...\n", coll->name, bench->name);  		fflush(stdout); -		argv[1] = suites[i].name; -		suites[i].fn(1, argv, NULL); +		argv[1] = bench->name; +		run_bench(coll->name, bench->name, bench->fn, 1, argv, NULL);  		printf("\n");  	}  } -static void all_subsystem(void) +static void run_all_collections(void)  { -	int i; -	for (i = 0; subsystems[i].suites; i++) -		all_suite(&subsystems[i]); +	struct collection *coll; + +	for_each_collection(coll) +		run_collection(coll);  }  int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused)  { -	int i, j, status = 0; +	struct collection *coll; +	int ret = 0;  	if (argc < 2) { -		/* No subsystem specified. */ +		/* No collection specified. */  		print_usage();  		goto end;  	} @@ -206,7 +222,7 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused)  	bench_format = bench_str2int(bench_format_str);  	if (bench_format == BENCH_FORMAT_UNKNOWN) { -		printf("Unknown format descriptor:%s\n", bench_format_str); +		printf("Unknown format descriptor: '%s'\n", bench_format_str);  		goto end;  	} @@ -216,52 +232,51 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused)  	}  	if (!strcmp(argv[0], "all")) { -		all_subsystem(); +		run_all_collections();  		goto end;  	} -	for (i = 0; subsystems[i].name; i++) { -		if (strcmp(subsystems[i].name, argv[0])) +	for_each_collection(coll) { +		struct bench *bench; + +		if (strcmp(coll->name, argv[0]))  			continue;  		if (argc < 2) { -			/* No suite specified. */ -			dump_suites(i); +			/* No bench specified. */ +			dump_benchmarks(coll);  			goto end;  		}  		if (!strcmp(argv[1], "all")) { -			all_suite(&subsystems[i]); +			run_collection(coll);  			goto end;  		} -		for (j = 0; subsystems[i].suites[j].name; j++) { -			if (strcmp(subsystems[i].suites[j].name, argv[1])) +		for_each_bench(coll, bench) { +			if (strcmp(bench->name, argv[1]))  				continue;  			if (bench_format == BENCH_FORMAT_DEFAULT) -				printf("# Running %s/%s benchmark...\n", -				       subsystems[i].name, -				       subsystems[i].suites[j].name); +				printf("# Running '%s/%s' benchmark:\n", coll->name, bench->name);  			fflush(stdout); -			status = subsystems[i].suites[j].fn(argc - 1, -							    argv + 1, prefix); +			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_suites(i); +			dump_benchmarks(coll);  			goto end;  		} -		printf("Unknown suite:%s for %s\n", argv[1], argv[0]); -		status = 1; +		printf("Unknown benchmark: '%s' for collection '%s'\n", argv[1], argv[0]); +		ret = 1;  		goto end;  	} -	printf("Unknown subsystem:%s\n", argv[0]); -	status = 1; +	printf("Unknown collection: '%s'\n", argv[0]); +	ret = 1;  end: -	return status; +	return ret;  } diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index c96c8fa3824..b22dbb16f87 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -6,6 +6,11 @@   * 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" @@ -17,6 +22,165 @@  #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) +{ +	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 (!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]; @@ -82,8 +246,12 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)  static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp)  { -	struct perf_session *session = perf_session__new(filename, O_RDONLY, -							 force, false, NULL); +	struct perf_data_file file = { +		.path  = filename, +		.mode  = PERF_DATA_MODE_READ, +		.force = force, +	}; +	struct perf_session *session = perf_session__new(&file, false, NULL);  	if (session == NULL)  		return -1; @@ -130,11 +298,14 @@ int cmd_buildid_cache(int argc, const char **argv,  	char const *add_name_list_str = NULL,  		   *remove_name_list_str = NULL,  		   *missing_filename = NULL, -		   *update_name_list_str = NULL; +		   *update_name_list_str = NULL, +		   *kcore_filename;  	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", @@ -217,5 +388,9 @@ int cmd_buildid_cache(int argc, const char **argv,  		}  	} +	if (kcore_filename && +	    build_id_cache__add_kcore(kcore_filename, debugdir)) +		pr_warning("Couldn't add %s\n", kcore_filename); +  	return ret;  } diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index e74366a1321..ed3873b3e23 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -15,6 +15,7 @@  #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)  { @@ -52,6 +53,11 @@ static bool dso__skip_buildid(struct dso *dso, int with_hits)  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();  	/* @@ -60,15 +66,14 @@ static int perf_session__list_build_ids(bool force, bool with_hits)  	if (filename__fprintf_build_id(input_name, stdout))  		goto out; -	session = perf_session__new(input_name, O_RDONLY, force, false, -				    &build_id__mark_dso_hit_ops); +	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 || session->fd_pipe) +	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); diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index f28799e94f2..9a5a035cb42 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -16,6 +16,7 @@  #include "util/sort.h"  #include "util/symbol.h"  #include "util/util.h" +#include "util/data.h"  #include <stdlib.h>  #include <math.h> @@ -42,7 +43,7 @@ struct diff_hpp_fmt {  struct data__file {  	struct perf_session	*session; -	const char		*file; +	struct perf_data_file	file;  	int			 idx;  	struct hists		*hists;  	struct diff_hpp_fmt	 fmt[PERF_HPP_DIFF__MAX_INDEX]; @@ -59,7 +60,6 @@ static int data__files_cnt;  #define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)  #define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1) -static char diff__default_sort_order[] = "dso,symbol";  static bool force;  static bool show_period;  static bool show_formula; @@ -219,7 +219,8 @@ static int setup_compute(const struct option *opt, const char *str,  static double period_percent(struct hist_entry *he, u64 period)  { -	u64 total = he->hists->stats.total_period; +	u64 total = hists__total_period(he->hists); +  	return (period * 100.0) / total;  } @@ -258,11 +259,18 @@ static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair)  static int formula_delta(struct hist_entry *he, struct hist_entry *pair,  			 char *buf, size_t size)  { +	u64 he_total = he->hists->stats.total_period; +	u64 pair_total = pair->hists->stats.total_period; + +	if (symbol_conf.filter_relative) { +		he_total = he->hists->stats.total_non_filtered_period; +		pair_total = pair->hists->stats.total_non_filtered_period; +	}  	return scnprintf(buf, size,  			 "(%" PRIu64 " * 100 / %" PRIu64 ") - "  			 "(%" PRIu64 " * 100 / %" PRIu64 ")", -			  pair->stat.period, pair->hists->stats.total_period, -			  he->stat.period, he->hists->stats.total_period); +			 pair->stat.period, pair_total, +			 he->stat.period, he_total);  }  static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, @@ -302,11 +310,12 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,  	return -1;  } -static int hists__add_entry(struct hists *self, +static int hists__add_entry(struct hists *hists,  			    struct addr_location *al, u64 period, -			    u64 weight) +			    u64 weight, u64 transaction)  { -	if (__hists__add_entry(self, al, NULL, period, weight) != NULL) +	if (__hists__add_entry(hists, al, NULL, NULL, NULL, period, weight, +			       transaction, true) != NULL)  		return 0;  	return -ENOMEM;  } @@ -325,15 +334,22 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,  		return -1;  	} -	if (al.filtered) -		return 0; - -	if (hists__add_entry(&evsel->hists, &al, sample->period, sample->weight)) { +	if (hists__add_entry(&evsel->hists, &al, sample->period, +			     sample->weight, sample->transaction)) {  		pr_warning("problem incrementing symbol period, skipping event\n");  		return -1;  	} +	/* +	 * The total_period is updated here before going to the output +	 * tree since normally only the baseline hists will call +	 * hists__output_resort() and precompute needs the total +	 * period in order to sort entries by percentage delta. +	 */  	evsel->hists.stats.total_period += sample->period; +	if (!al.filtered) +		evsel->hists.stats.total_non_filtered_period += sample->period; +  	return 0;  } @@ -353,9 +369,10 @@ static struct perf_evsel *evsel_match(struct perf_evsel *evsel,  {  	struct perf_evsel *e; -	list_for_each_entry(e, &evlist->entries, node) +	evlist__for_each(evlist, e) {  		if (perf_evsel__match2(evsel, e))  			return e; +	}  	return NULL;  } @@ -364,10 +381,10 @@ static void perf_evlist__collapse_resort(struct perf_evlist *evlist)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		struct hists *hists = &evsel->hists; -		hists__collapse_resort(hists); +		hists__collapse_resort(hists, NULL);  	}  } @@ -560,8 +577,7 @@ static void hists__compute_resort(struct hists *hists)  	hists->entries = RB_ROOT;  	next = rb_first(root); -	hists->nr_entries = 0; -	hists->stats.total_period = 0; +	hists__reset_stats(hists);  	hists__reset_col_len(hists);  	while (next != NULL) { @@ -571,7 +587,10 @@ static void hists__compute_resort(struct hists *hists)  		next = rb_next(&he->rb_node_in);  		insert_hist_entry_by_compute(&hists->entries, he, compute); -		hists__inc_nr_entries(hists, he); +		hists__inc_stats(hists, he); + +		if (!he->filtered) +			hists__calc_col_len(hists, he);  	}  } @@ -599,7 +618,7 @@ static void data__fprintf(void)  	data__for_each_file(i, d)  		fprintf(stdout, "#  [%d] %s %s\n", -			d->idx, d->file, +			d->idx, d->file.path,  			!d->idx ? "(Baseline)" : "");  	fprintf(stdout, "#\n"); @@ -611,7 +630,7 @@ static void data_process(void)  	struct perf_evsel *evsel_base;  	bool first = true; -	list_for_each_entry(evsel_base, &evlist_base->entries, node) { +	evlist__for_each(evlist_base, evsel_base) {  		struct data__file *d;  		int i; @@ -651,7 +670,7 @@ static void data__free(struct data__file *d)  	for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {  		struct diff_hpp_fmt *fmt = &d->fmt[col]; -		free(fmt->header); +		zfree(&fmt->header);  	}  } @@ -661,17 +680,16 @@ static int __cmd_diff(void)  	int ret = -EINVAL, i;  	data__for_each_file(i, d) { -		d->session = perf_session__new(d->file, O_RDONLY, force, -					       false, &tool); +		d->session = perf_session__new(&d->file, false, &tool);  		if (!d->session) { -			pr_err("Failed to open %s\n", d->file); +			pr_err("Failed to open %s\n", d->file.path);  			ret = -ENOMEM;  			goto out_delete;  		}  		ret = perf_session__process_events(d->session, &tool);  		if (ret) { -			pr_err("Failed to process %s\n", d->file); +			pr_err("Failed to process %s\n", d->file.path);  			goto out_delete;  		} @@ -722,20 +740,24 @@ static const struct option options[] = {  	OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",  		   "only consider these symbols"),  	OPT_STRING('s', "sort", &sort_order, "key[,key2...]", -		   "sort by key(s): pid, comm, dso, symbol, parent"), +		   "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." +		   " Please refer the man page for the complete list."),  	OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",  		   "separator for columns, no spaces will be added between "  		   "columns '.' is reserved."),  	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",  		    "Look for files with symbols relative to this directory"),  	OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."), +	OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", +		     "How to display percentage of filtered entries", parse_filter_percentage),  	OPT_END()  };  static double baseline_percent(struct hist_entry *he)  { -	struct hists *hists = he->hists; -	return 100.0 * he->stat.period / hists->stats.total_period; +	u64 total = hists__total_period(he->hists); + +	return 100.0 * he->stat.period / total;  }  static int hpp__color_baseline(struct perf_hpp_fmt *fmt, @@ -767,6 +789,81 @@ static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)  	return ret;  } +static int __hpp__color_compare(struct perf_hpp_fmt *fmt, +				struct perf_hpp *hpp, struct hist_entry *he, +				int comparison_method) +{ +	struct diff_hpp_fmt *dfmt = +		container_of(fmt, struct diff_hpp_fmt, fmt); +	struct hist_entry *pair = get_pair_fmt(he, dfmt); +	double diff; +	s64 wdiff; +	char pfmt[20] = " "; + +	if (!pair) +		goto dummy_print; + +	switch (comparison_method) { +	case COMPUTE_DELTA: +		if (pair->diff.computed) +			diff = pair->diff.period_ratio_delta; +		else +			diff = compute_delta(he, pair); + +		if (fabs(diff) < 0.01) +			goto dummy_print; +		scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1); +		return percent_color_snprintf(hpp->buf, hpp->size, +					pfmt, diff); +	case COMPUTE_RATIO: +		if (he->dummy) +			goto dummy_print; +		if (pair->diff.computed) +			diff = pair->diff.period_ratio; +		else +			diff = compute_ratio(he, pair); + +		scnprintf(pfmt, 20, "%%%d.6f", dfmt->header_width); +		return value_color_snprintf(hpp->buf, hpp->size, +					pfmt, diff); +	case COMPUTE_WEIGHTED_DIFF: +		if (he->dummy) +			goto dummy_print; +		if (pair->diff.computed) +			wdiff = pair->diff.wdiff; +		else +			wdiff = compute_wdiff(he, pair); + +		scnprintf(pfmt, 20, "%%14ld", dfmt->header_width); +		return color_snprintf(hpp->buf, hpp->size, +				get_percent_color(wdiff), +				pfmt, wdiff); +	default: +		BUG_ON(1); +	} +dummy_print: +	return scnprintf(hpp->buf, hpp->size, "%*s", +			dfmt->header_width, pfmt); +} + +static int hpp__color_delta(struct perf_hpp_fmt *fmt, +			struct perf_hpp *hpp, struct hist_entry *he) +{ +	return __hpp__color_compare(fmt, hpp, he, COMPUTE_DELTA); +} + +static int hpp__color_ratio(struct perf_hpp_fmt *fmt, +			struct perf_hpp *hpp, struct hist_entry *he) +{ +	return __hpp__color_compare(fmt, hpp, he, COMPUTE_RATIO); +} + +static int hpp__color_wdiff(struct perf_hpp_fmt *fmt, +			struct perf_hpp *hpp, struct hist_entry *he) +{ +	return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF); +} +  static void  hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)  { @@ -874,8 +971,8 @@ static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp,  				 dfmt->header_width, buf);  } -static int hpp__header(struct perf_hpp_fmt *fmt, -		       struct perf_hpp *hpp) +static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, +		       struct perf_evsel *evsel __maybe_unused)  {  	struct diff_hpp_fmt *dfmt =  		container_of(fmt, struct diff_hpp_fmt, fmt); @@ -885,7 +982,8 @@ static int hpp__header(struct perf_hpp_fmt *fmt,  }  static int hpp__width(struct perf_hpp_fmt *fmt, -		      struct perf_hpp *hpp __maybe_unused) +		      struct perf_hpp *hpp __maybe_unused, +		      struct perf_evsel *evsel __maybe_unused)  {  	struct diff_hpp_fmt *dfmt =  		container_of(fmt, struct diff_hpp_fmt, fmt); @@ -938,8 +1036,22 @@ static void data__hpp_register(struct data__file *d, int idx)  	fmt->entry  = hpp__entry_global;  	/* TODO more colors */ -	if (idx == PERF_HPP_DIFF__BASELINE) +	switch (idx) { +	case PERF_HPP_DIFF__BASELINE:  		fmt->color = hpp__color_baseline; +		break; +	case PERF_HPP_DIFF__DELTA: +		fmt->color = hpp__color_delta; +		break; +	case PERF_HPP_DIFF__RATIO: +		fmt->color = hpp__color_ratio; +		break; +	case PERF_HPP_DIFF__WEIGHTED_DIFF: +		fmt->color = hpp__color_wdiff; +		break; +	default: +		break; +	}  	init_header(d, dfmt);  	perf_hpp__column_register(fmt); @@ -998,8 +1110,7 @@ static int data_init(int argc, const char **argv)  			data__files_cnt = argc;  			use_default = false;  		} -	} else if (symbol_conf.default_guest_vmlinux_name || -		   symbol_conf.default_guest_kallsyms) { +	} else if (perf_guest) {  		defaults[0] = "perf.data.host";  		defaults[1] = "perf.data.guest";  	} @@ -1014,7 +1125,12 @@ static int data_init(int argc, const char **argv)  		return -ENOMEM;  	data__for_each_file(i, d) { -		d->file = use_default ? defaults[i] : argv[i]; +		struct perf_data_file *file = &d->file; + +		file->path  = use_default ? defaults[i] : argv[i]; +		file->mode  = PERF_DATA_MODE_READ, +		file->force = force, +  		d->idx  = i;  	} @@ -1023,7 +1139,8 @@ static int data_init(int argc, const char **argv)  int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)  { -	sort_order = diff__default_sort_order; +	perf_config(perf_default_config, NULL); +  	argc = parse_options(argc, argv, options, diff_usage, 0);  	if (symbol__init() < 0) @@ -1034,6 +1151,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)  	ui_init(); +	sort__mode = SORT_MODE__DIFF; +  	if (setup_sorting() < 0)  		usage_with_options(diff_usage, options); diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 05bd9dfe875..c99e0de7e54 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -14,17 +14,22 @@  #include "util/parse-events.h"  #include "util/parse-options.h"  #include "util/session.h" +#include "util/data.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_name, O_RDONLY, 0, false, NULL); +	session = perf_session__new(&file, 0, NULL);  	if (session == NULL)  		return -ENOMEM; -	list_for_each_entry(pos, &session->evlist->entries, node) +	evlist__for_each(session->evlist, pos)  		perf_evsel__fprintf(pos, details, stdout);  	perf_session__delete(session); diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index afe377b2884..16c7c11ad06 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -15,20 +15,20 @@  #include "util/tool.h"  #include "util/debug.h"  #include "util/build-id.h" +#include "util/data.h"  #include "util/parse-options.h"  #include <linux/list.h>  struct perf_inject { -	struct perf_tool tool; -	bool		 build_ids; -	bool		 sched_stat; -	const char	 *input_name; -	int		 pipe_output, -			 output; -	u64		 bytes_written; -	struct list_head samples; +	struct perf_tool	tool; +	bool			build_ids; +	bool			sched_stat; +	const char		*input_name; +	struct perf_data_file	output; +	u64			bytes_written; +	struct list_head	samples;  };  struct event_entry { @@ -41,21 +41,14 @@ static int perf_event__repipe_synth(struct perf_tool *tool,  				    union perf_event *event)  {  	struct perf_inject *inject = container_of(tool, struct perf_inject, tool); -	uint32_t size; -	void *buf = event; +	ssize_t size; -	size = event->header.size; - -	while (size) { -		int ret = write(inject->output, buf, size); -		if (ret < 0) -			return -errno; - -		size -= ret; -		buf += ret; -		inject->bytes_written += ret; -	} +	size = perf_data_file__write(&inject->output, event, +				     event->header.size); +	if (size < 0) +		return -errno; +	inject->bytes_written += size;  	return 0;  } @@ -71,12 +64,17 @@ static int perf_event__repipe_attr(struct perf_tool *tool,  				   union perf_event *event,  				   struct perf_evlist **pevlist)  { +	struct perf_inject *inject = container_of(tool, struct perf_inject, +						  tool);  	int ret;  	ret = perf_event__process_attr(tool, event, pevlist);  	if (ret)  		return ret; +	if (!inject->output.is_pipe) +		return 0; +  	return perf_event__repipe_synth(tool, event);  } @@ -100,8 +98,8 @@ static int perf_event__repipe_sample(struct perf_tool *tool,  				     struct perf_evsel *evsel,  				     struct machine *machine)  { -	if (evsel->handler.func) { -		inject_handler f = evsel->handler.func; +	if (evsel->handler) { +		inject_handler f = evsel->handler;  		return f(tool, event, sample, evsel, machine);  	} @@ -161,38 +159,38 @@ static int perf_event__repipe_tracing_data(struct perf_tool *tool,  	return err;  } -static int dso__read_build_id(struct dso *self) +static int dso__read_build_id(struct dso *dso)  { -	if (self->has_build_id) +	if (dso->has_build_id)  		return 0; -	if (filename__read_build_id(self->long_name, self->build_id, -				    sizeof(self->build_id)) > 0) { -		self->has_build_id = true; +	if (filename__read_build_id(dso->long_name, dso->build_id, +				    sizeof(dso->build_id)) > 0) { +		dso->has_build_id = true;  		return 0;  	}  	return -1;  } -static int dso__inject_build_id(struct dso *self, struct perf_tool *tool, +static int dso__inject_build_id(struct dso *dso, struct perf_tool *tool,  				struct machine *machine)  {  	u16 misc = PERF_RECORD_MISC_USER;  	int err; -	if (dso__read_build_id(self) < 0) { -		pr_debug("no build_id found for %s\n", self->long_name); +	if (dso__read_build_id(dso) < 0) { +		pr_debug("no build_id found for %s\n", dso->long_name);  		return -1;  	} -	if (self->kernel) +	if (dso->kernel)  		misc = PERF_RECORD_MISC_KERNEL; -	err = perf_event__synthesize_build_id(tool, self, misc, perf_event__repipe, +	err = perf_event__synthesize_build_id(tool, dso, misc, perf_event__repipe,  					      machine);  	if (err) { -		pr_err("Can't synthesize build_id event for %s\n", self->long_name); +		pr_err("Can't synthesize build_id event for %s\n", dso->long_name);  		return -1;  	} @@ -211,7 +209,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool,  	cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; -	thread = machine__findnew_thread(machine, sample->pid, sample->pid); +	thread = machine__findnew_thread(machine, sample->pid, sample->tid);  	if (thread == NULL) {  		pr_err("problem processing %d event, skipping it.\n",  		       event->header.type); @@ -231,7 +229,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool,  				 * account this as unresolved.  				 */  			} else { -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT  				pr_warning("no symbols found in %s, maybe "  					   "install a debug package?\n",  					   al.map->dso->long_name); @@ -314,7 +312,6 @@ found:  	sample_sw.period = sample->period;  	sample_sw.time	 = sample->time;  	perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, -				      evsel->attr.sample_regs_user,  				      evsel->attr.read_format, &sample_sw,  				      false);  	build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); @@ -345,6 +342,11 @@ static int __cmd_inject(struct perf_inject *inject)  {  	struct perf_session *session;  	int ret = -EINVAL; +	struct perf_data_file file = { +		.path = inject->input_name, +		.mode = PERF_DATA_MODE_READ, +	}; +	struct perf_data_file *file_out = &inject->output;  	signal(SIGINT, sig_handler); @@ -355,7 +357,7 @@ static int __cmd_inject(struct perf_inject *inject)  		inject->tool.tracing_data = perf_event__repipe_tracing_data;  	} -	session = perf_session__new(inject->input_name, O_RDONLY, false, true, &inject->tool); +	session = perf_session__new(&file, true, &inject->tool);  	if (session == NULL)  		return -ENOMEM; @@ -366,29 +368,29 @@ static int __cmd_inject(struct perf_inject *inject)  		inject->tool.ordered_samples = true; -		list_for_each_entry(evsel, &session->evlist->entries, node) { +		evlist__for_each(session->evlist, evsel) {  			const char *name = perf_evsel__name(evsel);  			if (!strcmp(name, "sched:sched_switch")) {  				if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID"))  					return -EINVAL; -				evsel->handler.func = perf_inject__sched_switch; +				evsel->handler = perf_inject__sched_switch;  			} else if (!strcmp(name, "sched:sched_process_exit")) -				evsel->handler.func = perf_inject__sched_process_exit; +				evsel->handler = perf_inject__sched_process_exit;  			else if (!strncmp(name, "sched:sched_stat_", 17)) -				evsel->handler.func = perf_inject__sched_stat; +				evsel->handler = perf_inject__sched_stat;  		}  	} -	if (!inject->pipe_output) -		lseek(inject->output, session->header.data_offset, SEEK_SET); +	if (!file_out->is_pipe) +		lseek(file_out->fd, session->header.data_offset, SEEK_SET);  	ret = perf_session__process_events(session, &inject->tool); -	if (!inject->pipe_output) { +	if (!file_out->is_pipe) {  		session->header.data_size = inject->bytes_written; -		perf_session__write_header(session, session->evlist, inject->output, true); +		perf_session__write_header(session, session->evlist, file_out->fd, true);  	}  	perf_session__delete(session); @@ -417,14 +419,17 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)  		},  		.input_name  = "-",  		.samples = LIST_HEAD_INIT(inject.samples), +		.output = { +			.path = "-", +			.mode = PERF_DATA_MODE_WRITE, +		},  	}; -	const char *output_name = "-";  	const struct option options[] = {  		OPT_BOOLEAN('b', "build-ids", &inject.build_ids,  			    "Inject build-ids into the output stream"),  		OPT_STRING('i', "input", &inject.input_name, "file",  			   "input file name"), -		OPT_STRING('o', "output", &output_name, "file", +		OPT_STRING('o', "output", &inject.output.path, "file",  			   "output file name"),  		OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat,  			    "Merge sched-stat and sched-switch for getting events " @@ -446,16 +451,9 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)  	if (argc)  		usage_with_options(inject_usage, options); -	if (!strcmp(output_name, "-")) { -		inject.pipe_output = 1; -		inject.output = STDOUT_FILENO; -	} else { -		inject.output = open(output_name, O_CREAT | O_WRONLY | O_TRUNC, -						  S_IRUSR | S_IWUSR); -		if (inject.output < 0) { -			perror("failed to create output file"); -			return -1; -		} +	if (perf_data_file__open(&inject.output)) { +		perror("failed to create output file"); +		return -1;  	}  	if (symbol__init() < 0) diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 9b5f077fee5..bef3376bfaf 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -13,6 +13,8 @@  #include "util/parse-options.h"  #include "util/trace-event.h" +#include "util/data.h" +#include "util/cpumap.h"  #include "util/debug.h" @@ -30,9 +32,6 @@ static int			caller_lines = -1;  static bool			raw_ip; -static int			*cpunode_map; -static int			max_cpu_num; -  struct alloc_stat {  	u64	call_site;  	u64	ptr; @@ -54,76 +53,6 @@ static struct rb_root root_caller_sorted;  static unsigned long total_requested, total_allocated;  static unsigned long nr_allocs, nr_cross_allocs; -#define PATH_SYS_NODE	"/sys/devices/system/node" - -static int init_cpunode_map(void) -{ -	FILE *fp; -	int i, err = -1; - -	fp = fopen("/sys/devices/system/cpu/kernel_max", "r"); -	if (!fp) { -		max_cpu_num = 4096; -		return 0; -	} - -	if (fscanf(fp, "%d", &max_cpu_num) < 1) { -		pr_err("Failed to read 'kernel_max' from sysfs"); -		goto out_close; -	} - -	max_cpu_num++; - -	cpunode_map = calloc(max_cpu_num, sizeof(int)); -	if (!cpunode_map) { -		pr_err("%s: calloc failed\n", __func__); -		goto out_close; -	} - -	for (i = 0; i < max_cpu_num; i++) -		cpunode_map[i] = -1; - -	err = 0; -out_close: -	fclose(fp); -	return err; -} - -static int setup_cpunode_map(void) -{ -	struct dirent *dent1, *dent2; -	DIR *dir1, *dir2; -	unsigned int cpu, mem; -	char buf[PATH_MAX]; - -	if (init_cpunode_map()) -		return -1; - -	dir1 = opendir(PATH_SYS_NODE); -	if (!dir1) -		return 0; - -	while ((dent1 = readdir(dir1)) != NULL) { -		if (dent1->d_type != DT_DIR || -		    sscanf(dent1->d_name, "node%u", &mem) < 1) -			continue; - -		snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name); -		dir2 = opendir(buf); -		if (!dir2) -			continue; -		while ((dent2 = readdir(dir2)) != NULL) { -			if (dent2->d_type != DT_LNK || -			    sscanf(dent2->d_name, "cpu%u", &cpu) < 1) -				continue; -			cpunode_map[cpu] = mem; -		} -		closedir(dir2); -	} -	closedir(dir1); -	return 0; -} -  static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,  			     int bytes_req, int bytes_alloc, int cpu)  { @@ -234,7 +163,7 @@ static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel,  	int ret = perf_evsel__process_alloc_event(evsel, sample);  	if (!ret) { -		int node1 = cpunode_map[sample->cpu], +		int node1 = cpu__get_node(sample->cpu),  		    node2 = perf_evsel__intval(evsel, sample, "node");  		if (node1 != node2) @@ -306,7 +235,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,  				struct machine *machine)  {  	struct thread *thread = machine__findnew_thread(machine, sample->pid, -							sample->pid); +							sample->tid);  	if (thread == NULL) {  		pr_debug("problem processing %d event, skipping it.\n", @@ -314,10 +243,10 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,  		return -1;  	} -	dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid); +	dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); -	if (evsel->handler.func != NULL) { -		tracepoint_handler f = evsel->handler.func; +	if (evsel->handler != NULL) { +		tracepoint_handler f = evsel->handler;  		return f(evsel, sample);  	} @@ -486,8 +415,12 @@ static int __cmd_kmem(void)  		{ "kmem:kfree",			perf_evsel__process_free_event, },      		{ "kmem:kmem_cache_free",	perf_evsel__process_free_event, },  	}; +	struct perf_data_file file = { +		.path = input_name, +		.mode = PERF_DATA_MODE_READ, +	}; -	session = perf_session__new(input_name, O_RDONLY, 0, false, &perf_kmem); +	session = perf_session__new(&file, false, &perf_kmem);  	if (session == NULL)  		return -ENOMEM; @@ -751,11 +684,13 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)  	OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),  	OPT_END()  	}; -	const char * const kmem_usage[] = { -		"perf kmem [<options>] {record|stat}", +	const char *const kmem_subcommands[] = { "record", "stat", NULL }; +	const char *kmem_usage[] = { +		NULL,  		NULL  	}; -	argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); +	argc = parse_options_subcommand(argc, argv, kmem_options, +					kmem_subcommands, kmem_usage, 0);  	if (!argc)  		usage_with_options(kmem_usage, kmem_options); @@ -765,7 +700,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)  	if (!strncmp(argv[0], "rec", 3)) {  		return __cmd_record(argc, argv);  	} else if (!strcmp(argv[0], "stat")) { -		if (setup_cpunode_map()) +		if (cpu__setup_cpunode_map())  			return -1;  		if (list_empty(&caller_sort)) diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 935d52216c8..0f1e5a2f6ad 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -13,13 +13,16 @@  #include "util/parse-options.h"  #include "util/trace-event.h"  #include "util/debug.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include "util/tool.h"  #include "util/stat.h"  #include "util/top.h" +#include "util/data.h"  #include <sys/prctl.h> +#ifdef HAVE_TIMERFD_SUPPORT  #include <sys/timerfd.h> +#endif  #include <termios.h>  #include <semaphore.h> @@ -86,7 +89,7 @@ struct exit_reasons_table {  struct perf_kvm_stat {  	struct perf_tool    tool; -	struct perf_record_opts opts; +	struct record_opts  opts;  	struct perf_evlist  *evlist;  	struct perf_session *session; @@ -336,6 +339,7 @@ static void init_kvm_event_record(struct perf_kvm_stat *kvm)  		INIT_LIST_HEAD(&kvm->kvm_events_cache[i]);  } +#ifdef HAVE_TIMERFD_SUPPORT  static void clear_events_cache_stats(struct list_head *kvm_events_cache)  {  	struct list_head *head; @@ -357,6 +361,7 @@ static void clear_events_cache_stats(struct list_head *kvm_events_cache)  		}  	}  } +#endif  static int kvm_events_hash_fn(u64 key)  { @@ -399,6 +404,7 @@ static struct kvm_event *kvm_alloc_init_event(struct event_key *key)  	}  	event->key = *key; +	init_stats(&event->total.stats);  	return event;  } @@ -782,6 +788,7 @@ static void print_result(struct perf_kvm_stat *kvm)  		pr_info("\nLost events: %" PRIu64 "\n\n", kvm->lost_events);  } +#ifdef HAVE_TIMERFD_SUPPORT  static int process_lost_event(struct perf_tool *tool,  			      union perf_event *event __maybe_unused,  			      struct perf_sample *sample __maybe_unused, @@ -792,6 +799,7 @@ static int process_lost_event(struct perf_tool *tool,  	kvm->lost_events++;  	return 0;  } +#endif  static bool skip_sample(struct perf_kvm_stat *kvm,  			struct perf_sample *sample) @@ -871,6 +879,7 @@ static bool verify_vcpu(int vcpu)  	return true;  } +#ifdef HAVE_TIMERFD_SUPPORT  /* keeping the max events to a modest level to keep   * the processing of samples per mmap smooth.   */ @@ -888,11 +897,18 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx,  	while ((event = perf_evlist__mmap_read(kvm->evlist, idx)) != NULL) {  		err = perf_evlist__parse_sample(kvm->evlist, event, &sample);  		if (err) { +			perf_evlist__mmap_consume(kvm->evlist, idx);  			pr_err("Failed to parse sample\n");  			return -1;  		}  		err = perf_session_queue_event(kvm->session, event, &sample, 0); +		/* +		 * FIXME: Here we can't consume the event, as perf_session_queue_event will +		 *        point to it, and it'll get possibly overwritten by the kernel. +		 */ +		perf_evlist__mmap_consume(kvm->evlist, idx); +  		if (err) {  			pr_err("Failed to enqueue sample: %d\n", err);  			return -1; @@ -1143,9 +1159,7 @@ out:  	if (kvm->timerfd >= 0)  		close(kvm->timerfd); -	if (pollfds) -		free(pollfds); - +	free(pollfds);  	return err;  } @@ -1161,7 +1175,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)  	 * Note: exclude_{guest,host} do not apply here.  	 *       This command processes KVM tracepoints from host only  	 */ -	list_for_each_entry(pos, &evlist->entries, node) { +	evlist__for_each(evlist, pos) {  		struct perf_event_attr *attr = &pos->attr;  		/* make sure these *are* set */ @@ -1205,6 +1219,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)  out:  	return rc;  } +#endif  static int read_events(struct perf_kvm_stat *kvm)  { @@ -1215,10 +1230,13 @@ static int read_events(struct perf_kvm_stat *kvm)  		.comm			= perf_event__process_comm,  		.ordered_samples	= true,  	}; +	struct perf_data_file file = { +		.path = kvm->file_name, +		.mode = PERF_DATA_MODE_READ, +	};  	kvm->tool = eops; -	kvm->session = perf_session__new(kvm->file_name, O_RDONLY, 0, false, -					 &kvm->tool); +	kvm->session = perf_session__new(&file, false, &kvm->tool);  	if (!kvm->session) {  		pr_err("Initializing perf session failed\n");  		return -EINVAL; @@ -1368,6 +1386,7 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)  	return kvm_events_report_vcpu(kvm);  } +#ifdef HAVE_TIMERFD_SUPPORT  static struct perf_evlist *kvm_live_event_list(void)  {  	struct perf_evlist *evlist; @@ -1426,8 +1445,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,  	const struct option live_options[] = {  		OPT_STRING('p', "pid", &kvm->opts.target.pid, "pid",  			"record events on existing process id"), -		OPT_UINTEGER('m', "mmap-pages", &kvm->opts.mmap_pages, -			"number of mmap data pages"), +		OPT_CALLBACK('m', "mmap-pages", &kvm->opts.mmap_pages, "pages", +			"number of mmap data pages", +			perf_evlist__parse_mmap_pages),  		OPT_INCR('v', "verbose", &verbose,  			"be more verbose (show counter open errors, etc)"),  		OPT_BOOLEAN('a', "all-cpus", &kvm->opts.target.system_wide, @@ -1449,6 +1469,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,  		"perf kvm stat live [<options>]",  		NULL  	}; +	struct perf_data_file file = { +		.mode = PERF_DATA_MODE_WRITE, +	};  	/* event handling */ @@ -1486,13 +1509,13 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,  	/*  	 * target related setups  	 */ -	err = perf_target__validate(&kvm->opts.target); +	err = target__validate(&kvm->opts.target);  	if (err) { -		perf_target__strerror(&kvm->opts.target, err, errbuf, BUFSIZ); +		target__strerror(&kvm->opts.target, err, errbuf, BUFSIZ);  		ui__warning("%s", errbuf);  	} -	if (perf_target__none(&kvm->opts.target)) +	if (target__none(&kvm->opts.target))  		kvm->opts.target.system_wide = true; @@ -1513,25 +1536,15 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,  	/*  	 * perf session  	 */ -	kvm->session = perf_session__new(NULL, O_WRONLY, false, false, &kvm->tool); +	kvm->session = perf_session__new(&file, false, &kvm->tool);  	if (kvm->session == NULL) {  		err = -ENOMEM;  		goto out;  	}  	kvm->session->evlist = kvm->evlist;  	perf_session__set_id_hdr_size(kvm->session); - - -	if (perf_target__has_task(&kvm->opts.target)) -		perf_event__synthesize_thread_map(&kvm->tool, -						  kvm->evlist->threads, -						  perf_event__process, -						  &kvm->session->machines.host); -	else -		perf_event__synthesize_threads(&kvm->tool, perf_event__process, -					       &kvm->session->machines.host); - - +	machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target, +				    kvm->evlist->threads, false);  	err = kvm_live_open_events(kvm);  	if (err)  		goto out; @@ -1544,13 +1557,12 @@ out:  	if (kvm->session)  		perf_session__delete(kvm->session);  	kvm->session = NULL; -	if (kvm->evlist) { -		perf_evlist__delete_maps(kvm->evlist); +	if (kvm->evlist)  		perf_evlist__delete(kvm->evlist); -	}  	return err;  } +#endif  static void print_kvm_stat_usage(void)  { @@ -1589,8 +1601,10 @@ static int kvm_cmd_stat(const char *file_name, int argc, const char **argv)  	if (!strncmp(argv[1], "rep", 3))  		return kvm_events_report(&kvm, argc - 1 , argv + 1); +#ifdef HAVE_TIMERFD_SUPPORT  	if (!strncmp(argv[1], "live", 4))  		return kvm_events_live(&kvm, argc - 1 , argv + 1); +#endif  perf_stat:  	return cmd_stat(argc, argv, NULL); @@ -1673,20 +1687,20 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused)  			   "file", "file saving guest os /proc/kallsyms"),  		OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules,  			   "file", "file saving guest os /proc/modules"), +		OPT_INCR('v', "verbose", &verbose, +			    "be more verbose (show counter open errors, etc)"),  		OPT_END()  	}; - -	const char * const kvm_usage[] = { -		"perf kvm [<options>] {top|record|report|diff|buildid-list|stat}", -		NULL -	}; +	const char *const kvm_subcommands[] = { "top", "record", "report", "diff", +						"buildid-list", "stat", NULL }; +	const char *kvm_usage[] = { NULL, NULL };  	perf_host  = 0;  	perf_guest = 1; -	argc = parse_options(argc, argv, kvm_options, kvm_usage, -			PARSE_OPT_STOP_AT_NON_OPTION); +	argc = parse_options_subcommand(argc, argv, kvm_options, kvm_subcommands, kvm_usage, +					PARSE_OPT_STOP_AT_NON_OPTION);  	if (!argc)  		usage_with_options(kvm_usage, kvm_options); @@ -1694,12 +1708,7 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused)  		perf_guest = 1;  	if (!file_name) { -		if (perf_host && !perf_guest) -			file_name = strdup("perf.data.host"); -		else if (!perf_host && perf_guest) -			file_name = strdup("perf.data.guest"); -		else -			file_name = strdup("perf.data.kvm"); +		file_name = get_filename_for_perf_kvm();  		if (!file_name) {  			pr_err("Failed to allocate memory for filename\n"); diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index e79f423cc30..011195e38f2 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -14,51 +14,63 @@  #include "util/parse-events.h"  #include "util/cache.h"  #include "util/pmu.h" +#include "util/parse-options.h"  int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)  { +	int i; +	const struct option list_options[] = { +		OPT_END() +	}; +	const char * const list_usage[] = { +		"perf list [hw|sw|cache|tracepoint|pmu|event_glob]", +		NULL +	}; + +	argc = parse_options(argc, argv, list_options, list_usage, +			     PARSE_OPT_STOP_AT_NON_OPTION); +  	setup_pager(); -	if (argc == 1) +	if (argc == 0) {  		print_events(NULL, false); -	else { -		int i; - -		for (i = 1; i < argc; ++i) { -			if (i > 2) -				putchar('\n'); -			if (strncmp(argv[i], "tracepoint", 10) == 0) -				print_tracepoint_events(NULL, NULL, false); -			else if (strcmp(argv[i], "hw") == 0 || -				 strcmp(argv[i], "hardware") == 0) -				print_events_type(PERF_TYPE_HARDWARE); -			else if (strcmp(argv[i], "sw") == 0 || -				 strcmp(argv[i], "software") == 0) -				print_events_type(PERF_TYPE_SOFTWARE); -			else if (strcmp(argv[i], "cache") == 0 || -				 strcmp(argv[i], "hwcache") == 0) -				print_hwcache_events(NULL, false); -			else if (strcmp(argv[i], "pmu") == 0) -				print_pmu_events(NULL, false); -			else if (strcmp(argv[i], "--raw-dump") == 0) -				print_events(NULL, true); -			else { -				char *sep = strchr(argv[i], ':'), *s; -				int sep_idx; +		return 0; +	} -				if (sep == NULL) { -					print_events(argv[i], false); -					continue; -				} -				sep_idx = sep - argv[i]; -				s = strdup(argv[i]); -				if (s == NULL) -					return -1; +	for (i = 0; i < argc; ++i) { +		if (i) +			putchar('\n'); +		if (strncmp(argv[i], "tracepoint", 10) == 0) +			print_tracepoint_events(NULL, NULL, false); +		else if (strcmp(argv[i], "hw") == 0 || +			 strcmp(argv[i], "hardware") == 0) +			print_events_type(PERF_TYPE_HARDWARE); +		else if (strcmp(argv[i], "sw") == 0 || +			 strcmp(argv[i], "software") == 0) +			print_events_type(PERF_TYPE_SOFTWARE); +		else if (strcmp(argv[i], "cache") == 0 || +			 strcmp(argv[i], "hwcache") == 0) +			print_hwcache_events(NULL, false); +		else if (strcmp(argv[i], "pmu") == 0) +			print_pmu_events(NULL, false); +		else if (strcmp(argv[i], "--raw-dump") == 0) +			print_events(NULL, true); +		else { +			char *sep = strchr(argv[i], ':'), *s; +			int sep_idx; -				s[sep_idx] = '\0'; -				print_tracepoint_events(s, s + sep_idx + 1, false); -				free(s); +			if (sep == NULL) { +				print_events(argv[i], false); +				continue;  			} +			sep_idx = sep - argv[i]; +			s = strdup(argv[i]); +			if (s == NULL) +				return -1; + +			s[sep_idx] = '\0'; +			print_tracepoint_events(s, s + sep_idx + 1, false); +			free(s);  		}  	}  	return 0; diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index ee33ba2f05d..6148afc995c 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -15,6 +15,7 @@  #include "util/debug.h"  #include "util/session.h"  #include "util/tool.h" +#include "util/data.h"  #include <sys/types.h>  #include <sys/prctl.h> @@ -56,7 +57,9 @@ struct lock_stat {  	unsigned int		nr_readlock;  	unsigned int		nr_trylock; +  	/* these times are in nano sec. */ +	u64                     avg_wait_time;  	u64			wait_time_total;  	u64			wait_time_min;  	u64			wait_time_max; @@ -208,6 +211,7 @@ static struct thread_stat *thread_stat_findnew_first(u32 tid)  SINGLE_KEY(nr_acquired)  SINGLE_KEY(nr_contended) +SINGLE_KEY(avg_wait_time)  SINGLE_KEY(wait_time_total)  SINGLE_KEY(wait_time_max) @@ -244,6 +248,7 @@ static struct rb_root		result;	/* place to store sorted data */  struct lock_key keys[] = {  	DEF_KEY_LOCK(acquired, nr_acquired),  	DEF_KEY_LOCK(contended, nr_contended), +	DEF_KEY_LOCK(avg_wait, avg_wait_time),  	DEF_KEY_LOCK(wait_total, wait_time_total),  	DEF_KEY_LOCK(wait_min, wait_time_min),  	DEF_KEY_LOCK(wait_max, wait_time_max), @@ -321,10 +326,12 @@ static struct lock_stat *lock_stat_findnew(void *addr, const char *name)  	new->addr = addr;  	new->name = zalloc(sizeof(char) * strlen(name) + 1); -	if (!new->name) +	if (!new->name) { +		free(new);  		goto alloc_failed; -	strcpy(new->name, name); +	} +	strcpy(new->name, name);  	new->wait_time_min = ULLONG_MAX;  	list_add(&new->hash_entry, entry); @@ -400,17 +407,17 @@ static int report_lock_acquire_event(struct perf_evsel *evsel,  	ls = lock_stat_findnew(addr, name);  	if (!ls) -		return -1; +		return -ENOMEM;  	if (ls->discard)  		return 0;  	ts = thread_stat_findnew(sample->tid);  	if (!ts) -		return -1; +		return -ENOMEM;  	seq = get_seq(ts, addr);  	if (!seq) -		return -1; +		return -ENOMEM;  	switch (seq->state) {  	case SEQ_STATE_UNINITIALIZED: @@ -446,7 +453,6 @@ broken:  		list_del(&seq->list);  		free(seq);  		goto end; -		break;  	default:  		BUG_ON("Unknown state of lock sequence found!\n");  		break; @@ -473,17 +479,17 @@ static int report_lock_acquired_event(struct perf_evsel *evsel,  	ls = lock_stat_findnew(addr, name);  	if (!ls) -		return -1; +		return -ENOMEM;  	if (ls->discard)  		return 0;  	ts = thread_stat_findnew(sample->tid);  	if (!ts) -		return -1; +		return -ENOMEM;  	seq = get_seq(ts, addr);  	if (!seq) -		return -1; +		return -ENOMEM;  	switch (seq->state) {  	case SEQ_STATE_UNINITIALIZED: @@ -508,8 +514,6 @@ static int report_lock_acquired_event(struct perf_evsel *evsel,  		list_del(&seq->list);  		free(seq);  		goto end; -		break; -  	default:  		BUG_ON("Unknown state of lock sequence found!\n");  		break; @@ -517,6 +521,7 @@ static int report_lock_acquired_event(struct perf_evsel *evsel,  	seq->state = SEQ_STATE_ACQUIRED;  	ls->nr_acquired++; +	ls->avg_wait_time = ls->nr_contended ? ls->wait_time_total/ls->nr_contended : 0;  	seq->prev_event_time = sample->time;  end:  	return 0; @@ -536,17 +541,17 @@ static int report_lock_contended_event(struct perf_evsel *evsel,  	ls = lock_stat_findnew(addr, name);  	if (!ls) -		return -1; +		return -ENOMEM;  	if (ls->discard)  		return 0;  	ts = thread_stat_findnew(sample->tid);  	if (!ts) -		return -1; +		return -ENOMEM;  	seq = get_seq(ts, addr);  	if (!seq) -		return -1; +		return -ENOMEM;  	switch (seq->state) {  	case SEQ_STATE_UNINITIALIZED: @@ -564,7 +569,6 @@ static int report_lock_contended_event(struct perf_evsel *evsel,  		list_del(&seq->list);  		free(seq);  		goto end; -		break;  	default:  		BUG_ON("Unknown state of lock sequence found!\n");  		break; @@ -572,6 +576,7 @@ static int report_lock_contended_event(struct perf_evsel *evsel,  	seq->state = SEQ_STATE_CONTENDED;  	ls->nr_contended++; +	ls->avg_wait_time = ls->wait_time_total/ls->nr_contended;  	seq->prev_event_time = sample->time;  end:  	return 0; @@ -591,22 +596,21 @@ static int report_lock_release_event(struct perf_evsel *evsel,  	ls = lock_stat_findnew(addr, name);  	if (!ls) -		return -1; +		return -ENOMEM;  	if (ls->discard)  		return 0;  	ts = thread_stat_findnew(sample->tid);  	if (!ts) -		return -1; +		return -ENOMEM;  	seq = get_seq(ts, addr);  	if (!seq) -		return -1; +		return -ENOMEM;  	switch (seq->state) {  	case SEQ_STATE_UNINITIALIZED:  		goto end; -		break;  	case SEQ_STATE_ACQUIRED:  		break;  	case SEQ_STATE_READ_ACQUIRED: @@ -624,7 +628,6 @@ static int report_lock_release_event(struct perf_evsel *evsel,  		ls->discard = 1;  		bad_hist[BROKEN_RELEASE]++;  		goto free_seq; -		break;  	default:  		BUG_ON("Unknown state of lock sequence found!\n");  		break; @@ -690,7 +693,7 @@ static void print_bad_events(int bad, int total)  	pr_info("\n=== output for debug===\n\n");  	pr_info("bad: %d, total: %d\n", bad, total); -	pr_info("bad rate: %f %%\n", (double)bad / (double)total * 100); +	pr_info("bad rate: %.2f %%\n", (double)bad / (double)total * 100);  	pr_info("histogram of events caused bad sequence\n");  	for (i = 0; i < BROKEN_MAX; i++)  		pr_info(" %10s: %d\n", name[i], bad_hist[i]); @@ -707,6 +710,7 @@ static void print_result(void)  	pr_info("%10s ", "acquired");  	pr_info("%10s ", "contended"); +	pr_info("%15s ", "avg wait (ns)");  	pr_info("%15s ", "total wait (ns)");  	pr_info("%15s ", "max wait (ns)");  	pr_info("%15s ", "min wait (ns)"); @@ -738,6 +742,7 @@ static void print_result(void)  		pr_info("%10u ", st->nr_acquired);  		pr_info("%10u ", st->nr_contended); +		pr_info("%15" PRIu64 " ", st->avg_wait_time);  		pr_info("%15" PRIu64 " ", st->wait_time_total);  		pr_info("%15" PRIu64 " ", st->wait_time_max);  		pr_info("%15" PRIu64 " ", st->wait_time_min == ULLONG_MAX ? @@ -762,7 +767,7 @@ static void dump_threads(void)  	while (node) {  		st = container_of(node, struct thread_stat, rb);  		t = perf_session__findnew(session, st->tid); -		pr_info("%10d: %s\n", st->tid, t->comm); +		pr_info("%10d: %s\n", st->tid, thread__comm_str(t));  		node = rb_next(node);  	};  } @@ -814,14 +819,26 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,  		return -1;  	} -	if (evsel->handler.func != NULL) { -		tracepoint_handler f = evsel->handler.func; +	if (evsel->handler != NULL) { +		tracepoint_handler f = evsel->handler;  		return f(evsel, sample);  	}  	return 0;  } +static void sort_result(void) +{ +	unsigned int i; +	struct lock_stat *st; + +	for (i = 0; i < LOCKHASH_SIZE; i++) { +		list_for_each_entry(st, &lockhash_table[i], hash_entry) { +			insert_to_result(st, compare); +		} +	} +} +  static const struct perf_evsel_str_handler lock_tracepoints[] = {  	{ "lock:lock_acquire",	 perf_evsel__process_lock_acquire,   }, /* CONFIG_LOCKDEP */  	{ "lock:lock_acquired",	 perf_evsel__process_lock_acquired,  }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */ @@ -829,51 +846,51 @@ static const struct perf_evsel_str_handler lock_tracepoints[] = {  	{ "lock:lock_release",	 perf_evsel__process_lock_release,   }, /* CONFIG_LOCKDEP */  }; -static int read_events(void) +static int __cmd_report(bool display_info)  { +	int err = -EINVAL;  	struct perf_tool eops = {  		.sample		 = process_sample_event,  		.comm		 = perf_event__process_comm,  		.ordered_samples = true,  	}; -	session = perf_session__new(input_name, O_RDONLY, 0, false, &eops); +	struct perf_data_file file = { +		.path = input_name, +		.mode = PERF_DATA_MODE_READ, +	}; + +	session = perf_session__new(&file, false, &eops);  	if (!session) {  		pr_err("Initializing perf session failed\n"); -		return -1; +		return -ENOMEM;  	} +	if (!perf_session__has_traces(session, "lock record")) +		goto out_delete; +  	if (perf_session__set_tracepoints_handlers(session, lock_tracepoints)) {  		pr_err("Initializing perf session tracepoint handlers failed\n"); -		return -1; +		goto out_delete;  	} -	return perf_session__process_events(session, &eops); -} - -static void sort_result(void) -{ -	unsigned int i; -	struct lock_stat *st; +	if (select_key()) +		goto out_delete; -	for (i = 0; i < LOCKHASH_SIZE; i++) { -		list_for_each_entry(st, &lockhash_table[i], hash_entry) { -			insert_to_result(st, compare); -		} -	} -} +	err = perf_session__process_events(session, &eops); +	if (err) +		goto out_delete; -static int __cmd_report(void) -{  	setup_pager(); +	if (display_info) /* used for info subcommand */ +		err = dump_info(); +	else { +		sort_result(); +		print_result(); +	} -	if ((select_key() != 0) || -	    (read_events() != 0)) -		return -1; - -	sort_result(); -	print_result(); - -	return 0; +out_delete: +	perf_session__delete(session); +	return err;  }  static int __cmd_record(int argc, const char **argv) @@ -881,7 +898,7 @@ static int __cmd_record(int argc, const char **argv)  	const char *record_args[] = {  		"record", "-R", "-m", "1024", "-c", "1",  	}; -	unsigned int rec_argc, i, j; +	unsigned int rec_argc, i, j, ret;  	const char **rec_argv;  	for (i = 0; i < ARRAY_SIZE(lock_tracepoints); i++) { @@ -898,7 +915,7 @@ static int __cmd_record(int argc, const char **argv)  	rec_argc += 2 * ARRAY_SIZE(lock_tracepoints);  	rec_argv = calloc(rec_argc + 1, sizeof(char *)); -	if (rec_argv == NULL) +	if (!rec_argv)  		return -ENOMEM;  	for (i = 0; i < ARRAY_SIZE(record_args); i++) @@ -914,7 +931,9 @@ static int __cmd_record(int argc, const char **argv)  	BUG_ON(i != rec_argc); -	return cmd_record(i, rec_argv, NULL); +	ret = cmd_record(i, rec_argv, NULL); +	free(rec_argv); +	return ret;  }  int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused) @@ -934,7 +953,7 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)  	};  	const struct option report_options[] = {  	OPT_STRING('k', "key", &sort_key, "acquired", -		    "key for sorting (acquired / contended / wait_total / wait_max / wait_min)"), +		    "key for sorting (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"),  	/* TODO: type */  	OPT_END()  	}; @@ -942,8 +961,10 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)  		"perf lock info [<options>]",  		NULL  	}; -	const char * const lock_usage[] = { -		"perf lock [<options>] {record|report|script|info}", +	const char *const lock_subcommands[] = { "record", "report", "script", +						 "info", NULL }; +	const char *lock_usage[] = { +		NULL,  		NULL  	};  	const char * const report_usage[] = { @@ -957,8 +978,8 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)  	for (i = 0; i < LOCKHASH_SIZE; i++)  		INIT_LIST_HEAD(lockhash_table + i); -	argc = parse_options(argc, argv, lock_options, lock_usage, -			     PARSE_OPT_STOP_AT_NON_OPTION); +	argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands, +					lock_usage, PARSE_OPT_STOP_AT_NON_OPTION);  	if (!argc)  		usage_with_options(lock_usage, lock_options); @@ -972,7 +993,7 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)  			if (argc)  				usage_with_options(report_usage, report_options);  		} -		__cmd_report(); +		rc = __cmd_report(false);  	} else if (!strcmp(argv[0], "script")) {  		/* Aliased to 'perf script' */  		return cmd_script(argc, argv, prefix); @@ -985,11 +1006,7 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)  		}  		/* recycling report_lock_ops */  		trace_handler = &report_lock_ops; -		setup_pager(); -		if (read_events() != 0) -			rc = -1; -		else -			rc = dump_info(); +		rc = __cmd_report(true);  	} else {  		usage_with_options(lock_usage, lock_options);  	} diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 253133a6251..4a1a6c94a5e 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -5,6 +5,7 @@  #include "util/trace-event.h"  #include "util/tool.h"  #include "util/session.h" +#include "util/data.h"  #define MEM_OPERATION_LOAD	"load"  #define MEM_OPERATION_STORE	"store" @@ -20,11 +21,6 @@ struct perf_mem {  	DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);  }; -static const char * const mem_usage[] = { -	"perf mem [<options>] {record <command> |report}", -	NULL -}; -  static int __cmd_record(int argc, const char **argv)  {  	int rec_argc, i = 0, j; @@ -61,7 +57,6 @@ static int  dump_raw_samples(struct perf_tool *tool,  		 union perf_event *event,  		 struct perf_sample *sample, -		 struct perf_evsel *evsel __maybe_unused,  		 struct machine *machine)  {  	struct perf_mem *mem = container_of(tool, struct perf_mem, tool); @@ -111,18 +106,22 @@ dump_raw_samples(struct perf_tool *tool,  static int process_sample_event(struct perf_tool *tool,  				union perf_event *event,  				struct perf_sample *sample, -				struct perf_evsel *evsel, +				struct perf_evsel *evsel __maybe_unused,  				struct machine *machine)  { -	return dump_raw_samples(tool, event, sample, evsel, machine); +	return dump_raw_samples(tool, event, sample, machine);  }  static int report_raw_events(struct perf_mem *mem)  { +	struct perf_data_file file = { +		.path = input_name, +		.mode = PERF_DATA_MODE_READ, +	};  	int err = -EINVAL;  	int ret; -	struct perf_session *session = perf_session__new(input_name, O_RDONLY, -							 0, false, &mem->tool); +	struct perf_session *session = perf_session__new(&file, false, +							 &mem->tool);  	if (session == NULL)  		return -ENOMEM; @@ -216,9 +215,15 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)  		   " between columns '.' is reserved."),  	OPT_END()  	}; +	const char *const mem_subcommands[] = { "record", "report", NULL }; +	const char *mem_usage[] = { +		NULL, +		NULL +	}; + -	argc = parse_options(argc, argv, mem_options, mem_usage, -			     PARSE_OPT_STOP_AT_NON_OPTION); +	argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands, +					mem_usage, PARSE_OPT_STOP_AT_NON_OPTION);  	if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation))  		usage_with_options(mem_usage, mem_options); diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index e8a66f9a671..c63fa292507 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -37,7 +37,7 @@  #include "util/strfilter.h"  #include "util/symbol.h"  #include "util/debug.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include "util/parse-options.h"  #include "util/probe-finder.h"  #include "util/probe-event.h" @@ -59,7 +59,7 @@ static struct {  	struct perf_probe_event events[MAX_PROBES];  	struct strlist *dellist;  	struct line_range line_range; -	const char *target; +	char *target;  	int max_probe_points;  	struct strfilter *filter;  } params; @@ -98,7 +98,10 @@ static int set_target(const char *ptr)  	 * short module name.  	 */  	if (!params.target && ptr && *ptr == '/') { -		params.target = ptr; +		params.target = strdup(ptr); +		if (!params.target) +			return -ENOMEM; +  		found = 1;  		buf = ptr + (strlen(ptr) - 3); @@ -116,6 +119,9 @@ static int parse_probe_event_argv(int argc, const char **argv)  	char *buf;  	found_target = set_target(argv[0]); +	if (found_target < 0) +		return found_target; +  	if (found_target && argc == 1)  		return 0; @@ -169,25 +175,38 @@ static int opt_set_target(const struct option *opt, const char *str,  			int unset __maybe_unused)  {  	int ret = -ENOENT; +	char *tmp;  	if  (str && !params.target) {  		if (!strcmp(opt->long_name, "exec"))  			params.uprobes = true; -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT  		else if (!strcmp(opt->long_name, "module"))  			params.uprobes = false;  #endif  		else  			return ret; -		params.target = str; +		/* Expand given path to absolute path, except for modulename */ +		if (params.uprobes || strchr(str, '/')) { +			tmp = realpath(str, NULL); +			if (!tmp) { +				pr_warning("Failed to get the absolute path of %s: %m\n", str); +				return ret; +			} +		} else { +			tmp = strdup(str); +			if (!tmp) +				return -ENOMEM; +		} +		params.target = tmp;  		ret = 0;  	}  	return ret;  } -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT  static int opt_show_lines(const struct option *opt __maybe_unused,  			  const char *str, int unset __maybe_unused)  { @@ -204,7 +223,6 @@ static int opt_show_lines(const struct option *opt __maybe_unused,  	params.show_lines = true;  	ret = parse_line_range_desc(str, ¶ms.line_range); -	INIT_LIST_HEAD(¶ms.line_range.line_list);  	return ret;  } @@ -250,14 +268,42 @@ static int opt_set_filter(const struct option *opt __maybe_unused,  	return 0;  } -int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) +static int init_params(void) +{ +	return line_range__init(¶ms.line_range); +} + +static void cleanup_params(void) +{ +	int i; + +	for (i = 0; i < params.nevents; i++) +		clear_perf_probe_event(params.events + i); +	if (params.dellist) +		strlist__delete(params.dellist); +	line_range__clear(¶ms.line_range); +	free(params.target); +	if (params.filter) +		strfilter__delete(params.filter); +	memset(¶ms, 0, sizeof(params)); +} + +static void pr_err_with_code(const char *msg, int err) +{ +	pr_err("%s", msg); +	pr_debug(" Reason: %s (Code: %d)", strerror(-err), err); +	pr_err("\n"); +} + +static int +__cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)  {  	const char * const probe_usage[] = {  		"perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",  		"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",  		"perf probe [<options>] --del '[GROUP:]EVENT' ...",  		"perf probe --list", -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT  		"perf probe [<options>] --line 'LINEDESC'",  		"perf probe [<options>] --vars 'PROBEPOINT'",  #endif @@ -271,7 +317,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)  	OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",  		opt_del_probe_event),  	OPT_CALLBACK('a', "add", NULL, -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT  		"[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"  		" [[NAME=]ARG ...]",  #else @@ -283,7 +329,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)  		"\t\tFUNC:\tFunction name\n"  		"\t\tOFF:\tOffset from function entry (in byte)\n"  		"\t\t%return:\tPut the probe at function return\n" -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT  		"\t\tSRC:\tSource code path\n"  		"\t\tRL:\tRelative line number from function entry.\n"  		"\t\tAL:\tAbsolute line number in file.\n" @@ -296,7 +342,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)  		opt_add_probe_event),  	OPT_BOOLEAN('f', "force", ¶ms.force_add, "forcibly add events"  		    " with existing name"), -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT  	OPT_CALLBACK('L', "line", NULL,  		     "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",  		     "Show source code lines.", opt_show_lines), @@ -325,6 +371,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)  		     opt_set_filter),  	OPT_CALLBACK('x', "exec", NULL, "executable|path",  			"target executable name or path", opt_set_target), +	OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, +		    "Disable symbol demangling"),  	OPT_END()  	};  	int ret; @@ -338,7 +386,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)  		}  		ret = parse_probe_event_argv(argc, argv);  		if (ret < 0) { -			pr_err("  Error: Parse Error.  (%d)\n", ret); +			pr_err_with_code("  Error: Command Parse Error.", ret);  			return ret;  		}  	} @@ -378,8 +426,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)  		}  		ret = show_perf_probe_events();  		if (ret < 0) -			pr_err("  Error: Failed to show event list. (%d)\n", -			       ret); +			pr_err_with_code("  Error: Failed to show event list.", ret);  		return ret;  	}  	if (params.show_funcs) { @@ -402,14 +449,14 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)  		ret = show_available_funcs(params.target, params.filter,  					params.uprobes);  		strfilter__delete(params.filter); +		params.filter = NULL;  		if (ret < 0) -			pr_err("  Error: Failed to show functions." -			       " (%d)\n", ret); +			pr_err_with_code("  Error: Failed to show functions.", ret);  		return ret;  	} -#ifdef DWARF_SUPPORT -	if (params.show_lines && !params.uprobes) { +#ifdef HAVE_DWARF_SUPPORT +	if (params.show_lines) {  		if (params.mod_events) {  			pr_err("  Error: Don't use --line with"  			       " --add/--del.\n"); @@ -422,7 +469,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)  		ret = show_line_range(¶ms.line_range, params.target);  		if (ret < 0) -			pr_err("  Error: Failed to show lines. (%d)\n", ret); +			pr_err_with_code("  Error: Failed to show lines.", ret);  		return ret;  	}  	if (params.show_vars) { @@ -441,17 +488,17 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)  					  params.filter,  					  params.show_ext_vars);  		strfilter__delete(params.filter); +		params.filter = NULL;  		if (ret < 0) -			pr_err("  Error: Failed to show vars. (%d)\n", ret); +			pr_err_with_code("  Error: Failed to show vars.", ret);  		return ret;  	}  #endif  	if (params.dellist) {  		ret = del_perf_probe_events(params.dellist); -		strlist__delete(params.dellist);  		if (ret < 0) { -			pr_err("  Error: Failed to delete events. (%d)\n", ret); +			pr_err_with_code("  Error: Failed to delete events.", ret);  			return ret;  		}  	} @@ -462,9 +509,22 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)  					    params.target,  					    params.force_add);  		if (ret < 0) { -			pr_err("  Error: Failed to add events. (%d)\n", ret); +			pr_err_with_code("  Error: Failed to add events.", ret);  			return ret;  		}  	}  	return 0;  } + +int cmd_probe(int argc, const char **argv, const char *prefix) +{ +	int ret; + +	ret = init_params(); +	if (!ret) { +		ret = __cmd_probe(argc, argv, prefix); +		cleanup_params(); +	} + +	return ret; +} diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a41ac41546c..378b85b731a 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -24,81 +24,35 @@  #include "util/symbol.h"  #include "util/cpumap.h"  #include "util/thread_map.h" +#include "util/data.h"  #include <unistd.h>  #include <sched.h>  #include <sys/mman.h> -#ifndef HAVE_ON_EXIT -#ifndef ATEXIT_MAX -#define ATEXIT_MAX 32 -#endif -static int __on_exit_count = 0; -typedef void (*on_exit_func_t) (int, void *); -static on_exit_func_t __on_exit_funcs[ATEXIT_MAX]; -static void *__on_exit_args[ATEXIT_MAX]; -static int __exitcode = 0; -static void __handle_on_exit_funcs(void); -static int on_exit(on_exit_func_t function, void *arg); -#define exit(x) (exit)(__exitcode = (x)) - -static int on_exit(on_exit_func_t function, void *arg) -{ -	if (__on_exit_count == ATEXIT_MAX) -		return -ENOMEM; -	else if (__on_exit_count == 0) -		atexit(__handle_on_exit_funcs); -	__on_exit_funcs[__on_exit_count] = function; -	__on_exit_args[__on_exit_count++] = arg; -	return 0; -} -static void __handle_on_exit_funcs(void) -{ -	int i; -	for (i = 0; i < __on_exit_count; i++) -		__on_exit_funcs[i] (__exitcode, __on_exit_args[i]); -} -#endif - -struct perf_record { +struct record {  	struct perf_tool	tool; -	struct perf_record_opts	opts; +	struct record_opts	opts;  	u64			bytes_written; -	const char		*output_name; +	struct perf_data_file	file;  	struct perf_evlist	*evlist;  	struct perf_session	*session;  	const char		*progname; -	int			output; -	unsigned int		page_size;  	int			realtime_prio;  	bool			no_buildid;  	bool			no_buildid_cache;  	long			samples; -	off_t			post_processing_offset;  }; -static void advance_output(struct perf_record *rec, size_t size) +static int record__write(struct record *rec, void *bf, size_t size)  { -	rec->bytes_written += size; -} - -static int write_output(struct perf_record *rec, void *buf, size_t size) -{ -	while (size) { -		int ret = write(rec->output, buf, size); - -		if (ret < 0) { -			pr_err("failed to write\n"); -			return -1; -		} - -		size -= ret; -		buf += ret; - -		rec->bytes_written += ret; +	if (perf_data_file__write(rec->session->file, bf, size) < 0) { +		pr_err("failed to write perf data, error: %m\n"); +		return -1;  	} +	rec->bytes_written += size;  	return 0;  } @@ -107,19 +61,15 @@ static int process_synthesized_event(struct perf_tool *tool,  				     struct perf_sample *sample __maybe_unused,  				     struct machine *machine __maybe_unused)  { -	struct perf_record *rec = container_of(tool, struct perf_record, tool); -	if (write_output(rec, event, event->header.size) < 0) -		return -1; - -	return 0; +	struct record *rec = container_of(tool, struct record, tool); +	return record__write(rec, event, event->header.size);  } -static int perf_record__mmap_read(struct perf_record *rec, -				   struct perf_mmap *md) +static int record__mmap_read(struct record *rec, struct perf_mmap *md)  {  	unsigned int head = perf_mmap__read_head(md);  	unsigned int old = md->prev; -	unsigned char *data = md->base + rec->page_size; +	unsigned char *data = md->base + page_size;  	unsigned long size;  	void *buf;  	int rc = 0; @@ -136,7 +86,7 @@ static int perf_record__mmap_read(struct perf_record *rec,  		size = md->mask + 1 - (old & md->mask);  		old += size; -		if (write_output(rec, buf, size) < 0) { +		if (record__write(rec, buf, size) < 0) {  			rc = -1;  			goto out;  		} @@ -146,7 +96,7 @@ static int perf_record__mmap_read(struct perf_record *rec,  	size = head - old;  	old += size; -	if (write_output(rec, buf, size) < 0) { +	if (record__write(rec, buf, size) < 0) {  		rc = -1;  		goto out;  	} @@ -166,43 +116,33 @@ static void sig_handler(int sig)  {  	if (sig == SIGCHLD)  		child_finished = 1; +	else +		signr = sig;  	done = 1; -	signr = sig;  } -static void perf_record__sig_exit(int exit_status __maybe_unused, void *arg) +static void record__sig_exit(void)  { -	struct perf_record *rec = arg; -	int status; - -	if (rec->evlist->workload.pid > 0) { -		if (!child_finished) -			kill(rec->evlist->workload.pid, SIGTERM); - -		wait(&status); -		if (WIFSIGNALED(status)) -			psignal(WTERMSIG(status), rec->progname); -	} - -	if (signr == -1 || signr == SIGUSR1) +	if (signr == -1)  		return;  	signal(signr, SIG_DFL); +	raise(signr);  } -static int perf_record__open(struct perf_record *rec) +static int record__open(struct record *rec)  {  	char msg[512];  	struct perf_evsel *pos;  	struct perf_evlist *evlist = rec->evlist;  	struct perf_session *session = rec->session; -	struct perf_record_opts *opts = &rec->opts; +	struct record_opts *opts = &rec->opts;  	int rc = 0;  	perf_evlist__config(evlist, opts); -	list_for_each_entry(pos, &evlist->entries, node) { +	evlist__for_each(evlist, pos) {  try_again:  		if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) {  			if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) { @@ -232,12 +172,8 @@ try_again:  			       "Consider increasing "  			       "/proc/sys/kernel/perf_event_mlock_kb,\n"  			       "or try again with a smaller value of -m/--mmap_pages.\n" -			       "(current value: %d)\n", opts->mmap_pages); +			       "(current value: %u)\n", opts->mmap_pages);  			rc = -errno; -		} else if (!is_power_of_2(opts->mmap_pages) && -			   (opts->mmap_pages != UINT_MAX)) { -			pr_err("--mmap_pages/-m value must be a power of two."); -			rc = -EINVAL;  		} else {  			pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno));  			rc = -errno; @@ -251,39 +187,21 @@ out:  	return rc;  } -static int process_buildids(struct perf_record *rec) +static int process_buildids(struct record *rec)  { -	u64 size = lseek(rec->output, 0, SEEK_CUR); +	struct perf_data_file *file  = &rec->file; +	struct perf_session *session = rec->session; +	u64 start = session->header.data_offset; +	u64 size = lseek(file->fd, 0, SEEK_CUR);  	if (size == 0)  		return 0; -	rec->session->fd = rec->output; -	return __perf_session__process_events(rec->session, rec->post_processing_offset, -					      size - rec->post_processing_offset, +	return __perf_session__process_events(session, start, +					      size - start,  					      size, &build_id__mark_dso_hit_ops);  } -static void perf_record__exit(int status, void *arg) -{ -	struct perf_record *rec = arg; - -	if (status != 0) -		return; - -	if (!rec->opts.pipe_output) { -		rec->session->header.data_size += rec->bytes_written; - -		if (!rec->no_buildid) -			process_buildids(rec); -		perf_session__write_header(rec->session, rec->evlist, -					   rec->output, true); -		perf_session__delete(rec->session); -		perf_evlist__delete(rec->evlist); -		symbol__exit(); -	} -} -  static void perf_event__synthesize_guest_os(struct machine *machine, void *data)  {  	int err; @@ -307,10 +225,7 @@ static void perf_event__synthesize_guest_os(struct machine *machine, void *data)  	 * have no _text sometimes.  	 */  	err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event, -						 machine, "_text"); -	if (err < 0) -		err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event, -							 machine, "_stext"); +						 machine);  	if (err < 0)  		pr_err("Couldn't record guest kernel [%d]'s reference"  		       " relocation symbol.\n", machine->pid); @@ -321,14 +236,14 @@ static struct perf_event_header finished_round_event = {  	.type = PERF_RECORD_FINISHED_ROUND,  }; -static int perf_record__mmap_read_all(struct perf_record *rec) +static int record__mmap_read_all(struct record *rec)  {  	int i;  	int rc = 0;  	for (i = 0; i < rec->evlist->nr_mmaps; i++) {  		if (rec->evlist->mmap[i].base) { -			if (perf_record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) { +			if (record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) {  				rc = -1;  				goto out;  			} @@ -336,122 +251,104 @@ static int perf_record__mmap_read_all(struct perf_record *rec)  	}  	if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA)) -		rc = write_output(rec, &finished_round_event, -				  sizeof(finished_round_event)); +		rc = record__write(rec, &finished_round_event, sizeof(finished_round_event));  out:  	return rc;  } -static int __cmd_record(struct perf_record *rec, int argc, const char **argv) +static void record__init_features(struct record *rec) +{ +	struct perf_session *session = rec->session; +	int feat; + +	for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) +		perf_header__set_feat(&session->header, feat); + +	if (rec->no_buildid) +		perf_header__clear_feat(&session->header, HEADER_BUILD_ID); + +	if (!have_tracepoints(&rec->evlist->entries)) +		perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); + +	if (!rec->opts.branch_stack) +		perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); +} + +static volatile int workload_exec_errno; + +/* + * perf_evlist__prepare_workload will send a SIGUSR1 + * if the fork fails, since we asked by setting its + * want_signal to true. + */ +static void workload_exec_failed_signal(int signo __maybe_unused, +					siginfo_t *info, +					void *ucontext __maybe_unused) +{ +	workload_exec_errno = info->si_value.sival_int; +	done = 1; +	child_finished = 1; +} + +static int __cmd_record(struct record *rec, int argc, const char **argv)  { -	struct stat st; -	int flags; -	int err, output, feat; +	int err; +	int status = 0;  	unsigned long waking = 0;  	const bool forks = argc > 0;  	struct machine *machine;  	struct perf_tool *tool = &rec->tool; -	struct perf_record_opts *opts = &rec->opts; -	struct perf_evlist *evsel_list = rec->evlist; -	const char *output_name = rec->output_name; +	struct record_opts *opts = &rec->opts; +	struct perf_data_file *file = &rec->file;  	struct perf_session *session;  	bool disabled = false;  	rec->progname = argv[0]; -	rec->page_size = sysconf(_SC_PAGE_SIZE); - -	on_exit(perf_record__sig_exit, rec); +	atexit(record__sig_exit);  	signal(SIGCHLD, sig_handler);  	signal(SIGINT, sig_handler); -	signal(SIGUSR1, sig_handler);  	signal(SIGTERM, sig_handler); -	if (!output_name) { -		if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) -			opts->pipe_output = true; -		else -			rec->output_name = output_name = "perf.data"; -	} -	if (output_name) { -		if (!strcmp(output_name, "-")) -			opts->pipe_output = true; -		else if (!stat(output_name, &st) && st.st_size) { -			char oldname[PATH_MAX]; -			snprintf(oldname, sizeof(oldname), "%s.old", -				 output_name); -			unlink(oldname); -			rename(output_name, oldname); -		} -	} - -	flags = O_CREAT|O_RDWR|O_TRUNC; - -	if (opts->pipe_output) -		output = STDOUT_FILENO; -	else -		output = open(output_name, flags, S_IRUSR | S_IWUSR); -	if (output < 0) { -		perror("failed to create output file"); -		return -1; -	} - -	rec->output = output; - -	session = perf_session__new(output_name, O_WRONLY, -				    true, false, NULL); +	session = perf_session__new(file, false, NULL);  	if (session == NULL) { -		pr_err("Not enough memory for reading perf file header\n"); +		pr_err("Perf session creation failed.\n");  		return -1;  	}  	rec->session = session; -	for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) -		perf_header__set_feat(&session->header, feat); - -	if (rec->no_buildid) -		perf_header__clear_feat(&session->header, HEADER_BUILD_ID); - -	if (!have_tracepoints(&evsel_list->entries)) -		perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); - -	if (!rec->opts.branch_stack) -		perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); +	record__init_features(rec);  	if (forks) { -		err = perf_evlist__prepare_workload(evsel_list, &opts->target, -						    argv, opts->pipe_output, -						    true); +		err = perf_evlist__prepare_workload(rec->evlist, &opts->target, +						    argv, file->is_pipe, +						    workload_exec_failed_signal);  		if (err < 0) {  			pr_err("Couldn't run the workload!\n"); +			status = err;  			goto out_delete_session;  		}  	} -	if (perf_record__open(rec) != 0) { +	if (record__open(rec) != 0) {  		err = -1; -		goto out_delete_session; +		goto out_child;  	} -	if (!evsel_list->nr_groups) +	if (!rec->evlist->nr_groups)  		perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); -	/* -	 * perf_session__delete(session) will be called at perf_record__exit() -	 */ -	on_exit(perf_record__exit, rec); - -	if (opts->pipe_output) { -		err = perf_header__write_pipe(output); +	if (file->is_pipe) { +		err = perf_header__write_pipe(file->fd);  		if (err < 0) -			goto out_delete_session; +			goto out_child;  	} else { -		err = perf_session__write_header(session, evsel_list, -						 output, false); +		err = perf_session__write_header(session, rec->evlist, +						 file->fd, false);  		if (err < 0) -			goto out_delete_session; +			goto out_child;  	}  	if (!rec->no_buildid @@ -459,22 +356,20 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)  		pr_err("Couldn't generate buildids. "  		       "Use --no-buildid to profile anyway.\n");  		err = -1; -		goto out_delete_session; +		goto out_child;  	} -	rec->post_processing_offset = lseek(output, 0, SEEK_CUR); -  	machine = &session->machines.host; -	if (opts->pipe_output) { +	if (file->is_pipe) {  		err = perf_event__synthesize_attrs(tool, session,  						   process_synthesized_event);  		if (err < 0) {  			pr_err("Couldn't synthesize attrs.\n"); -			goto out_delete_session; +			goto out_child;  		} -		if (have_tracepoints(&evsel_list->entries)) { +		if (have_tracepoints(&rec->evlist->entries)) {  			/*  			 * FIXME err <= 0 here actually means that  			 * there were no tracepoints so its not really @@ -483,21 +378,18 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)  			 * return this more properly and also  			 * propagate errors that now are calling die()  			 */ -			err = perf_event__synthesize_tracing_data(tool, output, evsel_list, +			err = perf_event__synthesize_tracing_data(tool, file->fd, rec->evlist,  								  process_synthesized_event);  			if (err <= 0) {  				pr_err("Couldn't record tracing data.\n"); -				goto out_delete_session; +				goto out_child;  			} -			advance_output(rec, err); +			rec->bytes_written += err;  		}  	}  	err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event, -						 machine, "_text"); -	if (err < 0) -		err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event, -							 machine, "_stext"); +						 machine);  	if (err < 0)  		pr_err("Couldn't record kernel reference relocation symbol\n"  		       "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" @@ -515,18 +407,10 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)  					 perf_event__synthesize_guest_os, tool);  	} -	if (perf_target__has_task(&opts->target)) -		err = perf_event__synthesize_thread_map(tool, evsel_list->threads, -						  process_synthesized_event, -						  machine); -	else if (perf_target__has_cpu(&opts->target)) -		err = perf_event__synthesize_threads(tool, process_synthesized_event, -					       machine); -	else /* command specified */ -		err = 0; - +	err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads, +					    process_synthesized_event, opts->sample_address);  	if (err != 0) -		goto out_delete_session; +		goto out_child;  	if (rec->realtime_prio) {  		struct sched_param param; @@ -535,7 +419,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)  		if (sched_setscheduler(0, SCHED_FIFO, ¶m)) {  			pr_err("Could not set realtime priority.\n");  			err = -1; -			goto out_delete_session; +			goto out_child;  		}  	} @@ -544,27 +428,38 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)  	 * (apart from group members) have enable_on_exec=1 set,  	 * so don't spoil it by prematurely enabling them.  	 */ -	if (!perf_target__none(&opts->target)) -		perf_evlist__enable(evsel_list); +	if (!target__none(&opts->target) && !opts->initial_delay) +		perf_evlist__enable(rec->evlist);  	/*  	 * Let the child rip  	 */  	if (forks) -		perf_evlist__start_workload(evsel_list); +		perf_evlist__start_workload(rec->evlist); + +	if (opts->initial_delay) { +		usleep(opts->initial_delay * 1000); +		perf_evlist__enable(rec->evlist); +	}  	for (;;) {  		int hits = rec->samples; -		if (perf_record__mmap_read_all(rec) < 0) { +		if (record__mmap_read_all(rec) < 0) {  			err = -1; -			goto out_delete_session; +			goto out_child;  		}  		if (hits == rec->samples) {  			if (done)  				break; -			err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1); +			err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1); +			/* +			 * Propagate error, only if there's any. Ignore positive +			 * number of returned events and interrupt error. +			 */ +			if (err > 0 || (err < 0 && errno == EINTR)) +				err = 0;  			waking++;  		} @@ -573,31 +468,63 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)  		 * die with the process and we wait for that. Thus no need to  		 * disable events in this case.  		 */ -		if (done && !disabled && !perf_target__none(&opts->target)) { -			perf_evlist__disable(evsel_list); +		if (done && !disabled && !target__none(&opts->target)) { +			perf_evlist__disable(rec->evlist);  			disabled = true;  		}  	} -	if (quiet || signr == SIGUSR1) -		return 0; +	if (forks && workload_exec_errno) { +		char msg[512]; +		const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); +		pr_err("Workload failed: %s\n", emsg); +		err = -1; +		goto out_child; +	} -	fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); +	if (!quiet) { +		fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); -	/* -	 * Approximate RIP event size: 24 bytes. -	 */ -	fprintf(stderr, -		"[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", -		(double)rec->bytes_written / 1024.0 / 1024.0, -		output_name, -		rec->bytes_written / 24); +		/* +		 * Approximate RIP event size: 24 bytes. +		 */ +		fprintf(stderr, +			"[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", +			(double)rec->bytes_written / 1024.0 / 1024.0, +			file->path, +			rec->bytes_written / 24); +	} -	return 0; +out_child: +	if (forks) { +		int exit_status; + +		if (!child_finished) +			kill(rec->evlist->workload.pid, SIGTERM); + +		wait(&exit_status); + +		if (err < 0) +			status = err; +		else if (WIFEXITED(exit_status)) +			status = WEXITSTATUS(exit_status); +		else if (WIFSIGNALED(exit_status)) +			signr = WTERMSIG(exit_status); +	} else +		status = err; + +	if (!err && !file->is_pipe) { +		rec->session->header.data_size += rec->bytes_written; + +		if (!rec->no_buildid) +			process_buildids(rec); +		perf_session__write_header(rec->session, rec->evlist, +					   file->fd, true); +	}  out_delete_session:  	perf_session__delete(session); -	return err; +	return status;  }  #define BRANCH_OPT(n, m) \ @@ -618,6 +545,10 @@ static const struct branch_mode branch_modes[] = {  	BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL),  	BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN),  	BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL), +	BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX), +	BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX), +	BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX), +	BRANCH_OPT("cond", PERF_SAMPLE_BRANCH_COND),  	BRANCH_END  }; @@ -684,7 +615,7 @@ error:  	return ret;  } -#ifdef LIBUNWIND_SUPPORT +#ifdef HAVE_DWARF_UNWIND_SUPPORT  static int get_stack_size(char *str, unsigned long *_size)  {  	char *endptr; @@ -710,23 +641,14 @@ static int get_stack_size(char *str, unsigned long *_size)  	       max_size, str);  	return -1;  } -#endif /* LIBUNWIND_SUPPORT */ +#endif /* HAVE_DWARF_UNWIND_SUPPORT */ -int record_parse_callchain_opt(const struct option *opt, -			       const char *arg, int unset) +int record_parse_callchain(const char *arg, struct record_opts *opts)  { -	struct perf_record_opts *opts = opt->value;  	char *tok, *name, *saveptr = NULL;  	char *buf;  	int ret = -1; -	/* --no-call-graph */ -	if (unset) -		return 0; - -	/* We specified default option if none is provided. */ -	BUG_ON(!arg); -  	/* We need buffer that we know we can write to. */  	buf = malloc(strlen(arg) + 1);  	if (!buf) @@ -748,7 +670,7 @@ int record_parse_callchain_opt(const struct option *opt,  				       "needed for -g fp\n");  			break; -#ifdef LIBUNWIND_SUPPORT +#ifdef HAVE_DWARF_UNWIND_SUPPORT  		/* Dwarf style */  		} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {  			const unsigned long default_stack_dump_size = 8192; @@ -764,13 +686,9 @@ int record_parse_callchain_opt(const struct option *opt,  				ret = get_stack_size(tok, &size);  				opts->stack_dump_size = size;  			} - -			if (!ret) -				pr_debug("callchain: stack dump size %d\n", -					 opts->stack_dump_size); -#endif /* LIBUNWIND_SUPPORT */ +#endif /* HAVE_DWARF_UNWIND_SUPPORT */  		} else { -			pr_err("callchain: Unknown -g option " +			pr_err("callchain: Unknown --call-graph option "  			       "value: %s\n", arg);  			break;  		} @@ -778,13 +696,68 @@ int record_parse_callchain_opt(const struct option *opt,  	} while (0);  	free(buf); +	return ret; +} + +static void callchain_debug(struct record_opts *opts) +{ +	static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" }; + +	pr_debug("callchain: type %s\n", str[opts->call_graph]); + +	if (opts->call_graph == CALLCHAIN_DWARF) +		pr_debug("callchain: stack dump size %d\n", +			 opts->stack_dump_size); +} + +int record_parse_callchain_opt(const struct option *opt, +			       const char *arg, +			       int unset) +{ +	struct record_opts *opts = opt->value; +	int ret; + +	opts->call_graph_enabled = !unset; + +	/* --no-call-graph */ +	if (unset) { +		opts->call_graph = CALLCHAIN_NONE; +		pr_debug("callchain: disabled\n"); +		return 0; +	} +	ret = record_parse_callchain(arg, opts);  	if (!ret) -		pr_debug("callchain: type %d\n", opts->call_graph); +		callchain_debug(opts);  	return ret;  } +int record_callchain_opt(const struct option *opt, +			 const char *arg __maybe_unused, +			 int unset __maybe_unused) +{ +	struct record_opts *opts = opt->value; + +	opts->call_graph_enabled = !unset; + +	if (opts->call_graph == CALLCHAIN_NONE) +		opts->call_graph = CALLCHAIN_FP; + +	callchain_debug(opts); +	return 0; +} + +static int perf_record_config(const char *var, const char *value, void *cb) +{ +	struct record *rec = cb; + +	if (!strcmp(var, "record.call-graph")) +		return record_parse_callchain(value, &rec->opts); + +	return perf_default_config(var, value, cb); +} +  static const char * const record_usage[] = {  	"perf record [<options>] [<command>]",  	"perf record [<options>] -- <command> [<options>]", @@ -792,8 +765,8 @@ static const char * const record_usage[] = {  };  /* - * XXX Ideally would be local to cmd_record() and passed to a perf_record__new - * because we need to have access to it in perf_record__exit, that is called + * XXX Ideally would be local to cmd_record() and passed to a record__new + * because we need to have access to it in record__exit, that is called   * after cmd_record() exits, but since record_options need to be accessible to   * builtin-script, leave it here.   * @@ -801,7 +774,7 @@ static const char * const record_usage[] = {   *   * Just say no to tons of global variables, sigh.   */ -static struct perf_record record = { +static struct record record = {  	.opts = {  		.mmap_pages	     = UINT_MAX,  		.user_freq	     = UINT_MAX, @@ -809,22 +782,23 @@ static struct perf_record record = {  		.freq		     = 4000,  		.target		     = {  			.uses_mmap   = true, +			.default_per_cpu = true,  		},  	},  }; -#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: " +#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: " -#ifdef LIBUNWIND_SUPPORT -const char record_callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; +#ifdef HAVE_DWARF_UNWIND_SUPPORT +const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf";  #else -const char record_callchain_help[] = CALLCHAIN_HELP "[fp]"; +const char record_callchain_help[] = CALLCHAIN_HELP "fp";  #endif  /*   * XXX Will stay a global variable till we fix builtin-script.c to stop messing   * with it and switch to use the library functions in perf_evlist that came - * from builtin-record.c, i.e. use perf_record_opts, + * from builtin-record.c, i.e. use record_opts,   * perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record',   * using pipes, etc.   */ @@ -840,7 +814,7 @@ const struct option record_options[] = {  		    "record events on existing thread id"),  	OPT_INTEGER('r', "realtime", &record.realtime_prio,  		    "collect data with this RT SCHED_FIFO priority"), -	OPT_BOOLEAN('D', "no-delay", &record.opts.no_delay, +	OPT_BOOLEAN(0, "no-buffering", &record.opts.no_buffering,  		    "collect data without buffering"),  	OPT_BOOLEAN('R', "raw-samples", &record.opts.raw_samples,  		    "collect raw sample records from all opened counters"), @@ -849,18 +823,23 @@ const struct option record_options[] = {  	OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu",  		    "list of cpus to monitor"),  	OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"), -	OPT_STRING('o', "output", &record.output_name, "file", +	OPT_STRING('o', "output", &record.file.path, "file",  		    "output file name"), -	OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, -		    "child tasks do not inherit counters"), +	OPT_BOOLEAN_SET('i', "no-inherit", &record.opts.no_inherit, +			&record.opts.no_inherit_set, +			"child tasks do not inherit counters"),  	OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), -	OPT_UINTEGER('m', "mmap-pages", &record.opts.mmap_pages, -		     "number of mmap data pages"), +	OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages", +		     "number of mmap data pages", +		     perf_evlist__parse_mmap_pages),  	OPT_BOOLEAN(0, "group", &record.opts.group,  		    "put the counters into a counter group"), -	OPT_CALLBACK_DEFAULT('g', "call-graph", &record.opts, -			     "mode[,dump_size]", record_callchain_help, -			     &record_parse_callchain_opt, "fp"), +	OPT_CALLBACK_NOOPT('g', NULL, &record.opts, +			   NULL, "enables call-graph recording" , +			   &record_callchain_opt), +	OPT_CALLBACK(0, "call-graph", &record.opts, +		     "mode[,dump_size]", record_callchain_help, +		     &record_parse_callchain_opt),  	OPT_INCR('v', "verbose", &verbose,  		    "be more verbose (show counter open errors, etc)"),  	OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), @@ -879,6 +858,8 @@ const struct option record_options[] = {  	OPT_CALLBACK('G', "cgroup", &record.evlist, "name",  		     "monitor event in cgroup name only",  		     parse_cgroups), +	OPT_UINTEGER('D', "delay", &record.opts.initial_delay, +		  "ms to wait before starting measurement after program start"),  	OPT_STRING('u', "uid", &record.opts.target.uid_str, "user",  		   "user to profile"), @@ -891,25 +872,28 @@ const struct option record_options[] = {  		     parse_branch_stack),  	OPT_BOOLEAN('W', "weight", &record.opts.sample_weight,  		    "sample by weight (on special events only)"), +	OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction, +		    "sample transaction flags (special events only)"), +	OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread, +		    "use per-thread mmaps"),  	OPT_END()  };  int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)  {  	int err = -ENOMEM; -	struct perf_evlist *evsel_list; -	struct perf_record *rec = &record; +	struct record *rec = &record;  	char errbuf[BUFSIZ]; -	evsel_list = perf_evlist__new(); -	if (evsel_list == NULL) +	rec->evlist = perf_evlist__new(); +	if (rec->evlist == NULL)  		return -ENOMEM; -	rec->evlist = evsel_list; +	perf_config(perf_record_config, rec);  	argc = parse_options(argc, argv, record_options, record_usage,  			    PARSE_OPT_STOP_AT_NON_OPTION); -	if (!argc && perf_target__none(&rec->opts.target)) +	if (!argc && target__none(&rec->opts.target))  		usage_with_options(record_usage, record_options);  	if (nr_cgroups && !rec->opts.target.system_wide) { @@ -933,23 +917,26 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)  	if (rec->no_buildid_cache || rec->no_buildid)  		disable_buildid_cache(); -	if (evsel_list->nr_entries == 0 && -	    perf_evlist__add_default(evsel_list) < 0) { +	if (rec->evlist->nr_entries == 0 && +	    perf_evlist__add_default(rec->evlist) < 0) {  		pr_err("Not enough memory for event selector list\n");  		goto out_symbol_exit;  	} -	err = perf_target__validate(&rec->opts.target); +	if (rec->opts.target.tid && !rec->opts.no_inherit_set) +		rec->opts.no_inherit = true; + +	err = target__validate(&rec->opts.target);  	if (err) { -		perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); +		target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);  		ui__warning("%s", errbuf);  	} -	err = perf_target__parse_uid(&rec->opts.target); +	err = target__parse_uid(&rec->opts.target);  	if (err) {  		int saved_errno = errno; -		perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); +		target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);  		ui__error("%s", errbuf);  		err = -saved_errno; @@ -957,34 +944,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)  	}  	err = -ENOMEM; -	if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) +	if (perf_evlist__create_maps(rec->evlist, &rec->opts.target) < 0)  		usage_with_options(record_usage, record_options); -	if (rec->opts.user_interval != ULLONG_MAX) -		rec->opts.default_interval = rec->opts.user_interval; -	if (rec->opts.user_freq != UINT_MAX) -		rec->opts.freq = rec->opts.user_freq; - -	/* -	 * User specified count overrides default frequency. -	 */ -	if (rec->opts.default_interval) -		rec->opts.freq = 0; -	else if (rec->opts.freq) { -		rec->opts.default_interval = rec->opts.freq; -	} else { -		ui__error("frequency and count are zero, aborting\n"); +	if (record_opts__config(&rec->opts)) {  		err = -EINVAL; -		goto out_free_fd; +		goto out_symbol_exit;  	}  	err = __cmd_record(&record, argc, argv); - -	perf_evlist__munmap(evsel_list); -	perf_evlist__close(evsel_list); -out_free_fd: -	perf_evlist__delete_maps(evsel_list);  out_symbol_exit: +	perf_evlist__delete(rec->evlist);  	symbol__exit();  	return err;  } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 72eae7498c0..21d830bafff 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -33,11 +33,13 @@  #include "util/thread.h"  #include "util/sort.h"  #include "util/hist.h" +#include "util/data.h"  #include "arch/common.h" +#include <dlfcn.h>  #include <linux/bitmap.h> -struct perf_report { +struct report {  	struct perf_tool	tool;  	struct perf_session	*session;  	bool			force, use_tui, use_gtk, use_stdio; @@ -47,293 +49,135 @@ struct perf_report {  	bool			show_threads;  	bool			inverted_callchain;  	bool			mem_mode; +	bool			header; +	bool			header_only; +	int			max_stack;  	struct perf_read_values	show_threads_values;  	const char		*pretty_printing_style;  	const char		*cpu_list;  	const char		*symbol_filter_str;  	float			min_percent; +	u64			nr_entries;  	DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);  }; -static int perf_report_config(const char *var, const char *value, void *cb) +static int report__config(const char *var, const char *value, void *cb)  {  	if (!strcmp(var, "report.group")) {  		symbol_conf.event_group = perf_config_bool(var, value);  		return 0;  	}  	if (!strcmp(var, "report.percent-limit")) { -		struct perf_report *rep = cb; +		struct report *rep = cb;  		rep->min_percent = strtof(value, NULL);  		return 0;  	} +	if (!strcmp(var, "report.children")) { +		symbol_conf.cumulate_callchain = perf_config_bool(var, value); +		return 0; +	}  	return perf_default_config(var, value, cb);  } -static int perf_report__add_mem_hist_entry(struct perf_tool *tool, -					   struct addr_location *al, -					   struct perf_sample *sample, -					   struct perf_evsel *evsel, -					   struct machine *machine, -					   union perf_event *event) +static void report__inc_stats(struct report *rep, struct hist_entry *he)  { -	struct perf_report *rep = container_of(tool, struct perf_report, tool); -	struct symbol *parent = NULL; -	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; -	int err = 0; -	struct hist_entry *he; -	struct mem_info *mi, *mx; -	uint64_t cost; - -	if ((sort__has_parent || symbol_conf.use_callchain) && -	    sample->callchain) { -		err = machine__resolve_callchain(machine, evsel, al->thread, -						 sample, &parent, al); -		if (err) -			return err; -	} - -	mi = machine__resolve_mem(machine, al->thread, sample, cpumode); -	if (!mi) -		return -ENOMEM; - -	if (rep->hide_unresolved && !al->sym) -		return 0; - -	cost = sample->weight; -	if (!cost) -		cost = 1; - -	/* -	 * must pass period=weight in order to get the correct -	 * sorting from hists__collapse_resort() which is solely -	 * based on periods. We want sorting be done on nr_events * weight -	 * and this is indirectly achieved by passing period=weight here -	 * and the he_stat__add_period() function. -	 */ -	he = __hists__add_mem_entry(&evsel->hists, al, parent, mi, cost, cost); -	if (!he) -		return -ENOMEM; -  	/* -	 * In the TUI browser, we are doing integrated annotation, -	 * so we don't allocate the extra space needed because the stdio -	 * code will not use it. +	 * The @he is either of a newly created one or an existing one +	 * merging current sample.  We only want to count a new one so +	 * checking ->nr_events being 1.  	 */ -	if (sort__has_sym && he->ms.sym && use_browser > 0) { -		struct annotation *notes = symbol__annotation(he->ms.sym); - -		assert(evsel != NULL); - -		if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0) -			goto out; - -		err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); -		if (err) -			goto out; -	} - -	if (sort__has_sym && he->mem_info->daddr.sym && use_browser > 0) { -		struct annotation *notes; - -		mx = he->mem_info; - -		notes = symbol__annotation(mx->daddr.sym); -		if (notes->src == NULL && symbol__alloc_hist(mx->daddr.sym) < 0) -			goto out; - -		err = symbol__inc_addr_samples(mx->daddr.sym, -					       mx->daddr.map, -					       evsel->idx, -					       mx->daddr.al_addr); -		if (err) -			goto out; -	} - -	evsel->hists.stats.total_period += cost; -	hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); -	err = 0; - -	if (symbol_conf.use_callchain) { -		err = callchain_append(he->callchain, -				       &callchain_cursor, -				       sample->period); -	} -out: -	return err; +	if (he->stat.nr_events == 1) +		rep->nr_entries++;  } -static int perf_report__add_branch_hist_entry(struct perf_tool *tool, -					struct addr_location *al, -					struct perf_sample *sample, -					struct perf_evsel *evsel, -				      struct machine *machine) +static int hist_iter__report_callback(struct hist_entry_iter *iter, +				      struct addr_location *al, bool single, +				      void *arg)  { -	struct perf_report *rep = container_of(tool, struct perf_report, tool); -	struct symbol *parent = NULL;  	int err = 0; -	unsigned i; -	struct hist_entry *he; -	struct branch_info *bi, *bx; - -	if ((sort__has_parent || symbol_conf.use_callchain) -	    && sample->callchain) { -		err = machine__resolve_callchain(machine, evsel, al->thread, -						 sample, &parent, al); -		if (err) -			return err; -	} - -	bi = machine__resolve_bstack(machine, al->thread, -				     sample->branch_stack); -	if (!bi) -		return -ENOMEM; - -	for (i = 0; i < sample->branch_stack->nr; i++) { -		if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym)) -			continue; - -		err = -ENOMEM; +	struct report *rep = arg; +	struct hist_entry *he = iter->he; +	struct perf_evsel *evsel = iter->evsel; +	struct mem_info *mi; +	struct branch_info *bi; -		/* -		 * The report shows the percentage of total branches captured -		 * and not events sampled. Thus we use a pseudo period of 1. -		 */ -		he = __hists__add_branch_entry(&evsel->hists, al, parent, -				&bi[i], 1, 1); -		if (he) { -			struct annotation *notes; -			bx = he->branch_info; -			if (bx->from.sym && use_browser == 1 && sort__has_sym) { -				notes = symbol__annotation(bx->from.sym); -				if (!notes->src -				    && symbol__alloc_hist(bx->from.sym) < 0) -					goto out; - -				err = symbol__inc_addr_samples(bx->from.sym, -							       bx->from.map, -							       evsel->idx, -							       bx->from.al_addr); -				if (err) -					goto out; -			} - -			if (bx->to.sym && use_browser == 1 && sort__has_sym) { -				notes = symbol__annotation(bx->to.sym); -				if (!notes->src -				    && symbol__alloc_hist(bx->to.sym) < 0) -					goto out; - -				err = symbol__inc_addr_samples(bx->to.sym, -							       bx->to.map, -							       evsel->idx, -							       bx->to.al_addr); -				if (err) -					goto out; -			} -			evsel->hists.stats.total_period += 1; -			hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); -		} else -			goto out; -	} -	err = 0; -out: -	free(bi); -	return err; -} +	report__inc_stats(rep, he); -static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, -				      struct addr_location *al, -				      struct perf_sample *sample, -				      struct machine *machine) -{ -	struct symbol *parent = NULL; -	int err = 0; -	struct hist_entry *he; +	if (!ui__has_annotation()) +		return 0; -	if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { -		err = machine__resolve_callchain(machine, evsel, al->thread, -						 sample, &parent, al); +	if (sort__mode == SORT_MODE__BRANCH) { +		bi = he->branch_info; +		err = addr_map_symbol__inc_samples(&bi->from, evsel->idx);  		if (err) -			return err; -	} +			goto out; -	he = __hists__add_entry(&evsel->hists, al, parent, sample->period, -					sample->weight); -	if (he == NULL) -		return -ENOMEM; +		err = addr_map_symbol__inc_samples(&bi->to, evsel->idx); -	if (symbol_conf.use_callchain) { -		err = callchain_append(he->callchain, -				       &callchain_cursor, -				       sample->period); +	} else if (rep->mem_mode) { +		mi = he->mem_info; +		err = addr_map_symbol__inc_samples(&mi->daddr, evsel->idx);  		if (err) -			return err; -	} -	/* -	 * Only in the TUI browser we are doing integrated annotation, -	 * so we don't allocated the extra space needed because the stdio -	 * code will not use it. -	 */ -	if (he->ms.sym != NULL && use_browser == 1 && sort__has_sym) { -		struct annotation *notes = symbol__annotation(he->ms.sym); - -		assert(evsel != NULL); - -		err = -ENOMEM; -		if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0)  			goto out;  		err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); + +	} else if (symbol_conf.cumulate_callchain) { +		if (single) +			err = hist_entry__inc_addr_samples(he, evsel->idx, +							   al->addr); +	} else { +		err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);  	} -	evsel->hists.stats.total_period += sample->period; -	hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);  out:  	return err;  } -  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_report *rep = container_of(tool, struct perf_report, tool); +	struct report *rep = container_of(tool, struct report, tool);  	struct addr_location al; +	struct hist_entry_iter iter = { +		.hide_unresolved = rep->hide_unresolved, +		.add_entry_cb = hist_iter__report_callback, +	};  	int ret;  	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { -		fprintf(stderr, "problem processing %d event, skipping it.\n", -			event->header.type); +		pr_debug("problem processing %d event, skipping it.\n", +			 event->header.type);  		return -1;  	} -	if (al.filtered || (rep->hide_unresolved && al.sym == NULL)) +	if (rep->hide_unresolved && al.sym == NULL)  		return 0;  	if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))  		return 0; -	if (sort__mode == SORT_MODE__BRANCH) { -		ret = perf_report__add_branch_hist_entry(tool, &al, sample, -							 evsel, machine); -		if (ret < 0) -			pr_debug("problem adding lbr entry, skipping event\n"); -	} else if (rep->mem_mode == 1) { -		ret = perf_report__add_mem_hist_entry(tool, &al, sample, -						      evsel, machine, event); -		if (ret < 0) -			pr_debug("problem adding mem entry, skipping event\n"); -	} else { -		if (al.map != NULL) -			al.map->dso->hit = 1; +	if (sort__mode == SORT_MODE__BRANCH) +		iter.ops = &hist_iter_branch; +	else if (rep->mem_mode) +		iter.ops = &hist_iter_mem; +	else if (symbol_conf.cumulate_callchain) +		iter.ops = &hist_iter_cumulative; +	else +		iter.ops = &hist_iter_normal; + +	if (al.map != NULL) +		al.map->dso->hit = 1; + +	ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack, +				   rep); +	if (ret < 0) +		pr_debug("problem adding hist entry, skipping event\n"); -		ret = perf_evsel__add_hist_entry(evsel, &al, sample, machine); -		if (ret < 0) -			pr_debug("problem incrementing symbol period, skipping event\n"); -	}  	return ret;  } @@ -343,7 +187,7 @@ static int process_read_event(struct perf_tool *tool,  			      struct perf_evsel *evsel,  			      struct machine *machine __maybe_unused)  { -	struct perf_report *rep = container_of(tool, struct perf_report, tool); +	struct report *rep = container_of(tool, struct report, tool);  	if (rep->show_threads) {  		const char *name = evsel ? perf_evsel__name(evsel) : "unknown"; @@ -362,12 +206,13 @@ static int process_read_event(struct perf_tool *tool,  }  /* For pipe mode, sample_type is not currently set */ -static int perf_report__setup_sample_type(struct perf_report *rep) +static int report__setup_sample_type(struct report *rep)  { -	struct perf_session *self = rep->session; -	u64 sample_type = perf_evlist__combined_sample_type(self->evlist); +	struct perf_session *session = rep->session; +	u64 sample_type = perf_evlist__combined_sample_type(session->evlist); +	bool is_pipe = perf_data_file__is_pipe(session->file); -	if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { +	if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {  		if (sort__has_parent) {  			ui__error("Selected --sort parent, but no "  				    "callchain data. Did you call " @@ -389,8 +234,16 @@ static int perf_report__setup_sample_type(struct perf_report *rep)  			}  	} +	if (symbol_conf.cumulate_callchain) { +		/* Silently ignore if callchain is missing */ +		if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) { +			symbol_conf.cumulate_callchain = false; +			perf_hpp__cancel_cumulate(); +		} +	} +  	if (sort__mode == SORT_MODE__BRANCH) { -		if (!self->fd_pipe && +		if (!is_pipe &&  		    !(sample_type & PERF_SAMPLE_BRANCH_STACK)) {  			ui__error("Selected -b but no branch data. "  				  "Did you call perf record without -b?\n"); @@ -406,18 +259,22 @@ static void sig_handler(int sig __maybe_unused)  	session_done = 1;  } -static size_t hists__fprintf_nr_sample_events(struct perf_report *rep, -					      struct hists *self, +static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report *rep,  					      const char *evname, FILE *fp)  {  	size_t ret;  	char unit; -	unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; -	u64 nr_events = self->stats.total_period; -	struct perf_evsel *evsel = hists_to_evsel(self); +	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; +	u64 nr_events = hists->stats.total_period; +	struct perf_evsel *evsel = hists_to_evsel(hists);  	char buf[512];  	size_t size = sizeof(buf); +	if (symbol_conf.filter_relative) { +		nr_samples = hists->stats.nr_non_filtered_samples; +		nr_events = hists->stats.total_non_filtered_period; +	} +  	if (perf_evsel__is_group_event(evsel)) {  		struct perf_evsel *pos; @@ -425,8 +282,13 @@ static size_t hists__fprintf_nr_sample_events(struct perf_report *rep,  		evname = buf;  		for_each_group_member(pos, evsel) { -			nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; -			nr_events += pos->hists.stats.total_period; +			if (symbol_conf.filter_relative) { +				nr_samples += pos->hists.stats.nr_non_filtered_samples; +				nr_events += pos->hists.stats.total_non_filtered_period; +			} else { +				nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; +				nr_events += pos->hists.stats.total_period; +			}  		}  	} @@ -444,12 +306,12 @@ static size_t hists__fprintf_nr_sample_events(struct perf_report *rep,  }  static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, -					 struct perf_report *rep, +					 struct report *rep,  					 const char *help)  {  	struct perf_evsel *pos; -	list_for_each_entry(pos, &evlist->entries, node) { +	evlist__for_each(evlist, pos) {  		struct hists *hists = &pos->hists;  		const char *evname = perf_evsel__name(pos); @@ -457,7 +319,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,  		    !perf_evsel__is_group_leader(pos))  			continue; -		hists__fprintf_nr_sample_events(rep, hists, evname, stdout); +		hists__fprintf_nr_sample_events(hists, rep, evname, stdout);  		hists__fprintf(hists, true, 0, 0, rep->min_percent, stdout);  		fprintf(stdout, "\n\n");  	} @@ -477,41 +339,11 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,  	return 0;  } -static int __cmd_report(struct perf_report *rep) +static void report__warn_kptr_restrict(const struct report *rep)  { -	int ret = -EINVAL; -	u64 nr_samples; -	struct perf_session *session = rep->session; -	struct perf_evsel *pos; -	struct map *kernel_map; -	struct kmap *kernel_kmap; -	const char *help = "For a higher level overview, try: perf report --sort comm,dso"; - -	signal(SIGINT, sig_handler); - -	if (rep->cpu_list) { -		ret = perf_session__cpu_bitmap(session, rep->cpu_list, -					       rep->cpu_bitmap); -		if (ret) -			return ret; -	} - -	if (use_browser <= 0) -		perf_session__fprintf_info(session, stdout, rep->show_full_info); - -	if (rep->show_threads) -		perf_read_values_init(&rep->show_threads_values); - -	ret = perf_report__setup_sample_type(rep); -	if (ret) -		return ret; - -	ret = perf_session__process_events(session, &rep->tool); -	if (ret) -		return ret; +	struct map *kernel_map = rep->session->machines.host.vmlinux_maps[MAP__FUNCTION]; +	struct kmap *kernel_kmap = map__kmap(kernel_map); -	kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION]; -	kernel_kmap = map__kmap(kernel_map);  	if (kernel_map == NULL ||  	    (kernel_map->dso->hit &&  	     (kernel_kmap->ref_reloc_sym == NULL || @@ -534,27 +366,67 @@ static int __cmd_report(struct perf_report *rep)  "Samples in kernel modules can't be resolved as well.\n\n",  		desc);  	} +} -	if (verbose > 3) -		perf_session__fprintf(session, stdout); +static int report__gtk_browse_hists(struct report *rep, const char *help) +{ +	int (*hist_browser)(struct perf_evlist *evlist, const char *help, +			    struct hist_browser_timer *timer, float min_pcnt); -	if (verbose > 2) -		perf_session__fprintf_dsos(session, stdout); +	hist_browser = dlsym(perf_gtk_handle, "perf_evlist__gtk_browse_hists"); -	if (dump_trace) { -		perf_session__fprintf_nr_events(session, stdout); -		return 0; +	if (hist_browser == NULL) { +		ui__error("GTK browser not found!\n"); +		return -1;  	} -	nr_samples = 0; -	list_for_each_entry(pos, &session->evlist->entries, node) { +	return hist_browser(rep->session->evlist, help, NULL, rep->min_percent); +} + +static int report__browse_hists(struct report *rep) +{ +	int ret; +	struct perf_session *session = rep->session; +	struct perf_evlist *evlist = session->evlist; +	const char *help = "For a higher level overview, try: perf report --sort comm,dso"; + +	switch (use_browser) { +	case 1: +		ret = perf_evlist__tui_browse_hists(evlist, help, NULL, +						    rep->min_percent, +						    &session->header.env); +		/* +		 * Usually "ret" is the last pressed key, and we only +		 * care if the key notifies us to switch data file. +		 */ +		if (ret != K_SWITCH_INPUT_DATA) +			ret = 0; +		break; +	case 2: +		ret = report__gtk_browse_hists(rep, help); +		break; +	default: +		ret = perf_evlist__tty_browse_hists(evlist, rep, help); +		break; +	} + +	return ret; +} + +static void report__collapse_hists(struct report *rep) +{ +	struct ui_progress prog; +	struct perf_evsel *pos; + +	ui_progress__init(&prog, rep->nr_entries, "Merging related events..."); + +	evlist__for_each(rep->session->evlist, pos) {  		struct hists *hists = &pos->hists;  		if (pos->idx == 0)  			hists->symbol_filter_str = rep->symbol_filter_str; -		hists__collapse_resort(hists); -		nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; +		hists__collapse_resort(hists, &prog);  		/* Non-group events are considered as leader */  		if (symbol_conf.event_group && @@ -566,129 +438,81 @@ static int __cmd_report(struct perf_report *rep)  		}  	} -	if (session_done()) -		return 0; - -	if (nr_samples == 0) { -		ui__error("The %s file has no samples!\n", session->filename); -		return 0; -	} - -	list_for_each_entry(pos, &session->evlist->entries, node) -		hists__output_resort(&pos->hists); - -	if (use_browser > 0) { -		if (use_browser == 1) { -			ret = perf_evlist__tui_browse_hists(session->evlist, -							help, NULL, -							rep->min_percent, -							&session->header.env); -			/* -			 * Usually "ret" is the last pressed key, and we only -			 * care if the key notifies us to switch data file. -			 */ -			if (ret != K_SWITCH_INPUT_DATA) -				ret = 0; - -		} else if (use_browser == 2) { -			perf_evlist__gtk_browse_hists(session->evlist, help, -						      NULL, rep->min_percent); -		} -	} else -		perf_evlist__tty_browse_hists(session->evlist, rep, help); - -	return ret; +	ui_progress__finish();  } -static int -parse_callchain_opt(const struct option *opt, const char *arg, int unset) +static int __cmd_report(struct report *rep)  { -	struct perf_report *rep = (struct perf_report *)opt->value; -	char *tok, *tok2; -	char *endptr; +	int ret; +	struct perf_session *session = rep->session; +	struct perf_evsel *pos; +	struct perf_data_file *file = session->file; -	/* -	 * --no-call-graph -	 */ -	if (unset) { -		rep->dont_use_callchains = true; -		return 0; -	} +	signal(SIGINT, sig_handler); -	symbol_conf.use_callchain = true; +	if (rep->cpu_list) { +		ret = perf_session__cpu_bitmap(session, rep->cpu_list, +					       rep->cpu_bitmap); +		if (ret) +			return ret; +	} -	if (!arg) -		return 0; +	if (rep->show_threads) +		perf_read_values_init(&rep->show_threads_values); -	tok = strtok((char *)arg, ","); -	if (!tok) -		return -1; +	ret = report__setup_sample_type(rep); +	if (ret) +		return ret; -	/* get the output mode */ -	if (!strncmp(tok, "graph", strlen(arg))) -		callchain_param.mode = CHAIN_GRAPH_ABS; +	ret = perf_session__process_events(session, &rep->tool); +	if (ret) +		return ret; -	else if (!strncmp(tok, "flat", strlen(arg))) -		callchain_param.mode = CHAIN_FLAT; +	report__warn_kptr_restrict(rep); -	else if (!strncmp(tok, "fractal", strlen(arg))) -		callchain_param.mode = CHAIN_GRAPH_REL; +	if (use_browser == 0) { +		if (verbose > 3) +			perf_session__fprintf(session, stdout); -	else if (!strncmp(tok, "none", strlen(arg))) { -		callchain_param.mode = CHAIN_NONE; -		symbol_conf.use_callchain = false; +		if (verbose > 2) +			perf_session__fprintf_dsos(session, stdout); -		return 0; +		if (dump_trace) { +			perf_session__fprintf_nr_events(session, stdout); +			return 0; +		}  	} -	else -		return -1; +	report__collapse_hists(rep); -	/* get the min percentage */ -	tok = strtok(NULL, ","); -	if (!tok) -		goto setup; +	if (session_done()) +		return 0; -	callchain_param.min_percent = strtod(tok, &endptr); -	if (tok == endptr) -		return -1; +	if (rep->nr_entries == 0) { +		ui__error("The %s file has no samples!\n", file->path); +		return 0; +	} -	/* get the print limit */ -	tok2 = strtok(NULL, ","); -	if (!tok2) -		goto setup; +	evlist__for_each(session->evlist, pos) +		hists__output_resort(&pos->hists); -	if (tok2[0] != 'c') { -		callchain_param.print_limit = strtoul(tok2, &endptr, 0); -		tok2 = strtok(NULL, ","); -		if (!tok2) -			goto setup; -	} +	return report__browse_hists(rep); +} -	/* get the call chain order */ -	if (!strncmp(tok2, "caller", strlen("caller"))) -		callchain_param.order = ORDER_CALLER; -	else if (!strncmp(tok2, "callee", strlen("callee"))) -		callchain_param.order = ORDER_CALLEE; -	else -		return -1; +static int +report_parse_callchain_opt(const struct option *opt, const char *arg, int unset) +{ +	struct report *rep = (struct report *)opt->value; -	/* Get the sort key */ -	tok2 = strtok(NULL, ","); -	if (!tok2) -		goto setup; -	if (!strncmp(tok2, "function", strlen("function"))) -		callchain_param.key = CCKEY_FUNCTION; -	else if (!strncmp(tok2, "address", strlen("address"))) -		callchain_param.key = CCKEY_ADDRESS; -	else -		return -1; -setup: -	if (callchain_register_param(&callchain_param) < 0) { -		fprintf(stderr, "Can't register callchain params\n"); -		return -1; +	/* +	 * --no-call-graph +	 */ +	if (unset) { +		rep->dont_use_callchains = true; +		return 0;  	} -	return 0; + +	return parse_callchain_report_opt(arg);  }  int @@ -723,7 +547,7 @@ static int  parse_percent_limit(const struct option *opt, const char *str,  		    int unset __maybe_unused)  { -	struct perf_report *rep = opt->value; +	struct report *rep = opt->value;  	rep->min_percent = strtof(str, NULL);  	return 0; @@ -741,7 +565,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)  		"perf report [<options>]",  		NULL  	}; -	struct perf_report report = { +	struct report report = {  		.tool = {  			.sample		 = process_sample_event,  			.mmap		 = perf_event__process_mmap, @@ -757,6 +581,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)  			.ordered_samples = true,  			.ordering_requires_timestamps = true,  		}, +		.max_stack		 = PERF_MAX_STACK_DEPTH,  		.pretty_printing_style	 = "normal",  	};  	const struct option options[] = { @@ -783,11 +608,14 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)  	OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"),  	OPT_BOOLEAN(0, "stdio", &report.use_stdio,  		    "Use the stdio interface"), +	OPT_BOOLEAN(0, "header", &report.header, "Show data header."), +	OPT_BOOLEAN(0, "header-only", &report.header_only, +		    "Show only data header."),  	OPT_STRING('s', "sort", &sort_order, "key[,key2...]", -		   "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," -		   " dso_to, dso_from, symbol_to, symbol_from, mispredict," -		   " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " -		   "snoop, locked"), +		   "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." +		   " Please refer the man page for the complete list."), +	OPT_STRING('F', "fields", &field_order, "key[,keys...]", +		   "output field(s): overhead, period, sample plus all of sort keys"),  	OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,  		    "Show sample percentage for different cpu modes"),  	OPT_STRING('p', "parent", &parent_pattern, "regex", @@ -796,7 +624,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)  		    "Only display entries with parent-match"),  	OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",  		     "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " -		     "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), +		     "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt), +	OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain, +		    "Accumulate callchains of children and show total overhead as well"), +	OPT_INTEGER(0, "max-stack", &report.max_stack, +		    "Set the maximum stack depth when parsing the callchain, " +		    "anything beyond the specified depth will be ignored. " +		    "Default: " __stringify(PERF_MAX_STACK_DEPTH)),  	OPT_BOOLEAN('G', "inverted", &report.inverted_callchain,  		    "alias for inverted call graph"),  	OPT_CALLBACK(0, "ignore-callees", NULL, "regex", @@ -843,10 +677,15 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)  	OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),  	OPT_CALLBACK(0, "percent-limit", &report, "percent",  		     "Don't show entries under that percent", parse_percent_limit), +	OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", +		     "how to display percentage of filtered entries", parse_filter_percentage),  	OPT_END()  	}; +	struct perf_data_file file = { +		.mode  = PERF_DATA_MODE_READ, +	}; -	perf_config(perf_report_config, &report); +	perf_config(report__config, &report);  	argc = parse_options(argc, argv, options, report_usage, 0); @@ -867,16 +706,11 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)  			input_name = "perf.data";  	} -	if (strcmp(input_name, "-") != 0) -		setup_browser(true); -	else { -		use_browser = 0; -		perf_hpp__init(); -	} +	file.path  = input_name; +	file.force = report.force;  repeat: -	session = perf_session__new(input_name, O_RDONLY, -				    report.force, false, &report.tool); +	session = perf_session__new(&file, false, &report.tool);  	if (session == NULL)  		return -ENOMEM; @@ -885,44 +719,54 @@ repeat:  	has_br_stack = perf_header__has_feat(&session->header,  					     HEADER_BRANCH_STACK); -	if (branch_mode == -1 && has_br_stack) +	if (branch_mode == -1 && has_br_stack) {  		sort__mode = SORT_MODE__BRANCH; - -	/* sort__mode could be NORMAL if --no-branch-stack */ -	if (sort__mode == SORT_MODE__BRANCH) { -		/* -		 * if no sort_order is provided, then specify -		 * branch-mode specific order -		 */ -		if (sort_order == default_sort_order) -			sort_order = "comm,dso_from,symbol_from," -				     "dso_to,symbol_to"; - +		symbol_conf.cumulate_callchain = false;  	} +  	if (report.mem_mode) {  		if (sort__mode == SORT_MODE__BRANCH) { -			fprintf(stderr, "branch and mem mode incompatible\n"); +			pr_err("branch and mem mode incompatible\n");  			goto error;  		}  		sort__mode = SORT_MODE__MEMORY; +		symbol_conf.cumulate_callchain = false; +	} -		/* -		 * if no sort_order is provided, then specify -		 * branch-mode specific order -		 */ -		if (sort_order == default_sort_order) -			sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; +	if (setup_sorting() < 0) { +		if (sort_order) +			parse_options_usage(report_usage, options, "s", 1); +		if (field_order) +			parse_options_usage(sort_order ? NULL : report_usage, +					    options, "F", 1); +		goto error;  	} -	if (setup_sorting() < 0) -		usage_with_options(report_usage, options); +	/* Force tty output for header output. */ +	if (report.header || report.header_only) +		use_browser = 0; + +	if (strcmp(input_name, "-") != 0) +		setup_browser(true); +	else +		use_browser = 0; + +	if (report.header || report.header_only) { +		perf_session__fprintf_info(session, stdout, +					   report.show_full_info); +		if (report.header_only) +			return 0; +	} else if (use_browser == 0) { +		fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n", +		      stdout); +	}  	/*  	 * Only in the TUI browser we are doing integrated annotation,  	 * so don't allocate extra space that won't be used in the stdio  	 * implementation.  	 */ -	if (use_browser == 1 && sort__has_sym) { +	if (ui__has_annotation()) {  		symbol_conf.priv_size = sizeof(struct annotation);  		machines__set_symbol_filter(&session->machines,  					    symbol__annotate_init); @@ -946,11 +790,6 @@ repeat:  	if (symbol__init() < 0)  		goto error; -	if (parent_pattern != default_parent_pattern) { -		if (sort_dimension__add("parent") < 0) -			goto error; -	} -  	if (argc) {  		/*  		 * Special case: if there's an argument left then assume that diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index d8c51b2f263..c38d06c0477 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -66,7 +66,7 @@ struct sched_atom {  	struct task_desc	*wakee;  }; -#define TASK_STATE_TO_CHAR_STR "RSDTtZX" +#define TASK_STATE_TO_CHAR_STR "RSDTtZXxKWP"  enum thread_state {  	THREAD_SLEEPING = 0, @@ -149,7 +149,6 @@ struct perf_sched {  	unsigned long	 nr_runs;  	unsigned long	 nr_timestamps;  	unsigned long	 nr_unordered_timestamps; -	unsigned long	 nr_state_machine_bugs;  	unsigned long	 nr_context_switch_bugs;  	unsigned long	 nr_events;  	unsigned long	 nr_lost_chunks; @@ -469,7 +468,7 @@ static void *thread_func(void *ctx)  	char comm2[22];  	int fd; -	free(parms); +	zfree(&parms);  	sprintf(comm2, ":%s", this_task->comm);  	prctl(PR_SET_NAME, comm2); @@ -737,12 +736,12 @@ static int replay_fork_event(struct perf_sched *sched,  	if (verbose) {  		printf("fork event\n"); -		printf("... parent: %s/%d\n", parent->comm, parent->tid); -		printf("...  child: %s/%d\n", child->comm, child->tid); +		printf("... parent: %s/%d\n", thread__comm_str(parent), parent->tid); +		printf("...  child: %s/%d\n", thread__comm_str(child), child->tid);  	} -	register_pid(sched, parent->tid, parent->comm); -	register_pid(sched, child->tid, child->comm); +	register_pid(sched, parent->tid, thread__comm_str(parent)); +	register_pid(sched, child->tid, thread__comm_str(child));  	return 0;  } @@ -1007,17 +1006,12 @@ static int latency_wakeup_event(struct perf_sched *sched,  				struct perf_sample *sample,  				struct machine *machine)  { -	const u32 pid	  = perf_evsel__intval(evsel, sample, "pid"), -		  success = perf_evsel__intval(evsel, sample, "success"); +	const u32 pid	  = perf_evsel__intval(evsel, sample, "pid");  	struct work_atoms *atoms;  	struct work_atom *atom;  	struct thread *wakee;  	u64 timestamp = sample->time; -	/* Note for later, it may be interesting to observe the failing cases */ -	if (!success) -		return 0; -  	wakee = machine__findnew_thread(machine, 0, pid);  	atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);  	if (!atoms) { @@ -1037,12 +1031,18 @@ static int latency_wakeup_event(struct perf_sched *sched,  	atom = list_entry(atoms->work_list.prev, struct work_atom, list);  	/* +	 * As we do not guarantee the wakeup event happens when +	 * task is out of run queue, also may happen when task is +	 * on run queue and wakeup only change ->state to TASK_RUNNING, +	 * then we should not set the ->wake_up_time when wake up a +	 * task which is on run queue. +	 *  	 * You WILL be missing events if you've recorded only  	 * one CPU, or are only looking at only one, so don't -	 * make useless noise. +	 * skip in this case.  	 */  	if (sched->profile_cpu == -1 && atom->state != THREAD_SLEEPING) -		sched->nr_state_machine_bugs++; +		return 0;  	sched->nr_timestamps++;  	if (atom->sched_out_time > timestamp) { @@ -1077,7 +1077,7 @@ static int latency_migrate_task_event(struct perf_sched *sched,  	if (!atoms) {  		if (thread_atoms_insert(sched, migrant))  			return -1; -		register_pid(sched, migrant->tid, migrant->comm); +		register_pid(sched, migrant->tid, thread__comm_str(migrant));  		atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);  		if (!atoms) {  			pr_err("migration-event: Internal tree error"); @@ -1111,20 +1111,20 @@ static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_  	/*  	 * Ignore idle threads:  	 */ -	if (!strcmp(work_list->thread->comm, "swapper")) +	if (!strcmp(thread__comm_str(work_list->thread), "swapper"))  		return;  	sched->all_runtime += work_list->total_runtime;  	sched->all_count   += work_list->nb_atoms; -	ret = printf("  %s:%d ", work_list->thread->comm, work_list->thread->tid); +	ret = printf("  %s:%d ", thread__comm_str(work_list->thread), work_list->thread->tid);  	for (i = 0; i < 24 - ret; i++)  		printf(" ");  	avg = work_list->total_lat / work_list->nb_atoms; -	printf("|%11.3f ms |%9" PRIu64 " | avg:%9.3f ms | max:%9.3f ms | max at: %9.6f s\n", +	printf("|%11.3f ms |%9" PRIu64 " | avg:%9.3f ms | max:%9.3f ms | max at: %13.6f s\n",  	      (double)work_list->total_runtime / 1e6,  		 work_list->nb_atoms, (double)avg / 1e6,  		 (double)work_list->max_lat / 1e6, @@ -1266,9 +1266,8 @@ static int process_sched_wakeup_event(struct perf_tool *tool,  static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,  			    struct perf_sample *sample, struct machine *machine)  { -	const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"), -		  next_pid = perf_evsel__intval(evsel, sample, "next_pid"); -	struct thread *sched_out __maybe_unused, *sched_in; +	const u32 next_pid = perf_evsel__intval(evsel, sample, "next_pid"); +	struct thread *sched_in;  	int new_shortname;  	u64 timestamp0, timestamp = sample->time;  	s64 delta; @@ -1291,7 +1290,6 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,  		return -1;  	} -	sched_out = machine__findnew_thread(machine, 0, prev_pid);  	sched_in = machine__findnew_thread(machine, 0, next_pid);  	sched->curr_thread[this_cpu] = sched_in; @@ -1300,17 +1298,25 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,  	new_shortname = 0;  	if (!sched_in->shortname[0]) { -		sched_in->shortname[0] = sched->next_shortname1; -		sched_in->shortname[1] = sched->next_shortname2; - -		if (sched->next_shortname1 < 'Z') { -			sched->next_shortname1++; +		if (!strcmp(thread__comm_str(sched_in), "swapper")) { +			/* +			 * Don't allocate a letter-number for swapper:0 +			 * as a shortname. Instead, we use '.' for it. +			 */ +			sched_in->shortname[0] = '.'; +			sched_in->shortname[1] = ' ';  		} else { -			sched->next_shortname1='A'; -			if (sched->next_shortname2 < '9') { -				sched->next_shortname2++; +			sched_in->shortname[0] = sched->next_shortname1; +			sched_in->shortname[1] = sched->next_shortname2; + +			if (sched->next_shortname1 < 'Z') { +				sched->next_shortname1++;  			} else { -				sched->next_shortname2='0'; +				sched->next_shortname1 = 'A'; +				if (sched->next_shortname2 < '9') +					sched->next_shortname2++; +				else +					sched->next_shortname2 = '0';  			}  		}  		new_shortname = 1; @@ -1322,19 +1328,16 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,  		else  			printf("*"); -		if (sched->curr_thread[cpu]) { -			if (sched->curr_thread[cpu]->tid) -				printf("%2s ", sched->curr_thread[cpu]->shortname); -			else -				printf(".  "); -		} else +		if (sched->curr_thread[cpu]) +			printf("%2s ", sched->curr_thread[cpu]->shortname); +		else  			printf("   ");  	}  	printf("  %12.6f secs ", (double)timestamp/1e9);  	if (new_shortname) {  		printf("%s => %s:%d\n", -			sched_in->shortname, sched_in->comm, sched_in->tid); +		       sched_in->shortname, thread__comm_str(sched_in), sched_in->tid);  	} else {  		printf("\n");  	} @@ -1425,10 +1428,10 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_  	int err = 0;  	evsel->hists.stats.total_period += sample->period; -	hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); +	hists__inc_nr_samples(&evsel->hists, true); -	if (evsel->handler.func != NULL) { -		tracepoint_handler f = evsel->handler.func; +	if (evsel->handler != NULL) { +		tracepoint_handler f = evsel->handler;  		err = f(tool, evsel, sample, machine);  	} @@ -1446,8 +1449,12 @@ static int perf_sched__read_events(struct perf_sched *sched,  		{ "sched:sched_migrate_task", process_sched_migrate_task_event, },  	};  	struct perf_session *session; +	struct perf_data_file file = { +		.path = input_name, +		.mode = PERF_DATA_MODE_READ, +	}; -	session = perf_session__new(input_name, O_RDONLY, 0, false, &sched->tool); +	session = perf_session__new(&file, false, &sched->tool);  	if (session == NULL) {  		pr_debug("No Memory for session\n");  		return -1; @@ -1492,14 +1499,6 @@ static void print_bad_events(struct perf_sched *sched)  			(double)sched->nr_lost_events/(double)sched->nr_events * 100.0,  			sched->nr_lost_events, sched->nr_events, sched->nr_lost_chunks);  	} -	if (sched->nr_state_machine_bugs && sched->nr_timestamps) { -		printf("  INFO: %.3f%% state machine bugs (%ld out of %ld)", -			(double)sched->nr_state_machine_bugs/(double)sched->nr_timestamps*100.0, -			sched->nr_state_machine_bugs, sched->nr_timestamps); -		if (sched->nr_lost_events) -			printf(" (due to lost events?)"); -		printf("\n"); -	}  	if (sched->nr_context_switch_bugs && sched->nr_timestamps) {  		printf("  INFO: %.3f%% context switch bugs (%ld out of %ld)",  			(double)sched->nr_context_switch_bugs/(double)sched->nr_timestamps*100.0, @@ -1523,9 +1522,9 @@ static int perf_sched__lat(struct perf_sched *sched)  	perf_sched__sort_lat(sched); -	printf("\n ---------------------------------------------------------------------------------------------------------------\n"); -	printf("  Task                  |   Runtime ms  | Switches | Average delay ms | Maximum delay ms | Maximum delay at     |\n"); -	printf(" ---------------------------------------------------------------------------------------------------------------\n"); +	printf("\n -----------------------------------------------------------------------------------------------------------------\n"); +	printf("  Task                  |   Runtime ms  | Switches | Average delay ms | Maximum delay ms | Maximum delay at       |\n"); +	printf(" -----------------------------------------------------------------------------------------------------------------\n");  	next = rb_first(&sched->sorted_atom_root); @@ -1537,7 +1536,7 @@ static int perf_sched__lat(struct perf_sched *sched)  		next = rb_next(next);  	} -	printf(" -----------------------------------------------------------------------------------------\n"); +	printf(" -----------------------------------------------------------------------------------------------------------------\n");  	printf("  TOTAL:                |%11.3f ms |%9" PRIu64 " |\n",  		(double)sched->all_runtime / 1e6, sched->all_count); @@ -1631,6 +1630,7 @@ static int __cmd_record(int argc, const char **argv)  		"-e", "sched:sched_stat_runtime",  		"-e", "sched:sched_process_fork",  		"-e", "sched:sched_wakeup", +		"-e", "sched:sched_wakeup_new",  		"-e", "sched:sched_migrate_task",  	}; @@ -1651,29 +1651,27 @@ static int __cmd_record(int argc, const char **argv)  	return cmd_record(i, rec_argv, NULL);  } -static const char default_sort_order[] = "avg, max, switch, runtime"; -static struct perf_sched sched = { -	.tool = { -		.sample		 = perf_sched__process_tracepoint_sample, -		.comm		 = perf_event__process_comm, -		.lost		 = perf_event__process_lost, -		.fork		 = perf_sched__process_fork_event, -		.ordered_samples = true, -	}, -	.cmp_pid	      = LIST_HEAD_INIT(sched.cmp_pid), -	.sort_list	      = LIST_HEAD_INIT(sched.sort_list), -	.start_work_mutex     = PTHREAD_MUTEX_INITIALIZER, -	.work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER, -	.curr_pid	      = { [0 ... MAX_CPUS - 1] = -1 }, -	.sort_order	      = default_sort_order, -	.replay_repeat	      = 10, -	.profile_cpu	      = -1, -	.next_shortname1      = 'A', -	.next_shortname2      = '0', -}; -  int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)  { +	const char default_sort_order[] = "avg, max, switch, runtime"; +	struct perf_sched sched = { +		.tool = { +			.sample		 = perf_sched__process_tracepoint_sample, +			.comm		 = perf_event__process_comm, +			.lost		 = perf_event__process_lost, +			.fork		 = perf_sched__process_fork_event, +			.ordered_samples = true, +		}, +		.cmp_pid	      = LIST_HEAD_INIT(sched.cmp_pid), +		.sort_list	      = LIST_HEAD_INIT(sched.sort_list), +		.start_work_mutex     = PTHREAD_MUTEX_INITIALIZER, +		.work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER, +		.sort_order	      = default_sort_order, +		.replay_repeat	      = 10, +		.profile_cpu	      = -1, +		.next_shortname1      = 'A', +		.next_shortname2      = '0', +	};  	const struct option latency_options[] = {  	OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]",  		   "sort by key(s): runtime, switch, avg, max"), @@ -1711,8 +1709,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)  		"perf sched replay [<options>]",  		NULL  	}; -	const char * const sched_usage[] = { -		"perf sched [<options>] {record|latency|map|replay|script}", +	const char *const sched_subcommands[] = { "record", "latency", "map", +						  "replay", "script", NULL }; +	const char *sched_usage[] = { +		NULL,  		NULL  	};  	struct trace_sched_handler lat_ops  = { @@ -1729,9 +1729,13 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)  		.switch_event	    = replay_switch_event,  		.fork_event	    = replay_fork_event,  	}; +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++) +		sched.curr_pid[i] = -1; -	argc = parse_options(argc, argv, sched_options, sched_usage, -			     PARSE_OPT_STOP_AT_NON_OPTION); +	argc = parse_options_subcommand(argc, argv, sched_options, sched_subcommands, +					sched_usage, PARSE_OPT_STOP_AT_NON_OPTION);  	if (!argc)  		usage_with_options(sched_usage, sched_options); diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 9c333ff3dfe..9e9c91f5b7f 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -15,6 +15,7 @@  #include "util/evlist.h"  #include "util/evsel.h"  #include "util/sort.h" +#include "util/data.h"  #include <linux/bitmap.h>  static char const		*script_name; @@ -42,6 +43,7 @@ enum perf_output_field {  	PERF_OUTPUT_DSO             = 1U << 9,  	PERF_OUTPUT_ADDR            = 1U << 10,  	PERF_OUTPUT_SYMOFFSET       = 1U << 11, +	PERF_OUTPUT_SRCLINE         = 1U << 12,  };  struct output_option { @@ -60,6 +62,7 @@ struct output_option {  	{.str = "dso",   .field = PERF_OUTPUT_DSO},  	{.str = "addr",  .field = PERF_OUTPUT_ADDR},  	{.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET}, +	{.str = "srcline", .field = PERF_OUTPUT_SRCLINE},  };  /* default set to maintain compatibility with current format */ @@ -209,6 +212,11 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,  		       "to DSO.\n");  		return -EINVAL;  	} +	if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) { +		pr_err("Display of source line number requested but sample IP is not\n" +		       "selected. Hence, no address to lookup the source line number.\n"); +		return -EINVAL; +	}  	if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) &&  		perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID", @@ -228,6 +236,27 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,  	return 0;  } +static void set_print_ip_opts(struct perf_event_attr *attr) +{ +	unsigned int type = attr->type; + +	output[type].print_ip_opts = 0; +	if (PRINT_FIELD(IP)) +		output[type].print_ip_opts |= PRINT_IP_OPT_IP; + +	if (PRINT_FIELD(SYM)) +		output[type].print_ip_opts |= PRINT_IP_OPT_SYM; + +	if (PRINT_FIELD(DSO)) +		output[type].print_ip_opts |= PRINT_IP_OPT_DSO; + +	if (PRINT_FIELD(SYMOFFSET)) +		output[type].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET; + +	if (PRINT_FIELD(SRCLINE)) +		output[type].print_ip_opts |= PRINT_IP_OPT_SRCLINE; +} +  /*   * verify all user requested events exist and the samples   * have the expected data @@ -236,7 +265,6 @@ static int perf_session__check_output_opt(struct perf_session *session)  {  	int j;  	struct perf_evsel *evsel; -	struct perf_event_attr *attr;  	for (j = 0; j < PERF_TYPE_MAX; ++j) {  		evsel = perf_session__find_first_evtype(session, j); @@ -259,22 +287,33 @@ static int perf_session__check_output_opt(struct perf_session *session)  		if (evsel == NULL)  			continue; -		attr = &evsel->attr; +		set_print_ip_opts(&evsel->attr); +	} -		output[j].print_ip_opts = 0; -		if (PRINT_FIELD(IP)) -			output[j].print_ip_opts |= PRINT_IP_OPT_IP; +	/* +	 * set default for tracepoints to print symbols only +	 * if callchains are present +	 */ +	if (symbol_conf.use_callchain && +	    !output[PERF_TYPE_TRACEPOINT].user_set) { +		struct perf_event_attr *attr; -		if (PRINT_FIELD(SYM)) -			output[j].print_ip_opts |= PRINT_IP_OPT_SYM; +		j = PERF_TYPE_TRACEPOINT; +		evsel = perf_session__find_first_evtype(session, j); +		if (evsel == NULL) +			goto out; -		if (PRINT_FIELD(DSO)) -			output[j].print_ip_opts |= PRINT_IP_OPT_DSO; +		attr = &evsel->attr; -		if (PRINT_FIELD(SYMOFFSET)) -			output[j].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET; +		if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) { +			output[j].fields |= PERF_OUTPUT_IP; +			output[j].fields |= PERF_OUTPUT_SYM; +			output[j].fields |= PERF_OUTPUT_DSO; +			set_print_ip_opts(attr); +		}  	} +out:  	return 0;  } @@ -283,18 +322,17 @@ static void print_sample_start(struct perf_sample *sample,  			       struct perf_evsel *evsel)  {  	struct perf_event_attr *attr = &evsel->attr; -	const char *evname = NULL;  	unsigned long secs;  	unsigned long usecs;  	unsigned long long nsecs;  	if (PRINT_FIELD(COMM)) {  		if (latency_format) -			printf("%8.8s ", thread->comm); +			printf("%8.8s ", thread__comm_str(thread));  		else if (PRINT_FIELD(IP) && symbol_conf.use_callchain) -			printf("%s ", thread->comm); +			printf("%s ", thread__comm_str(thread));  		else -			printf("%16s ", thread->comm); +			printf("%16s ", thread__comm_str(thread));  	}  	if (PRINT_FIELD(PID) && PRINT_FIELD(TID)) @@ -318,11 +356,6 @@ static void print_sample_start(struct perf_sample *sample,  		usecs = nsecs / NSECS_PER_USEC;  		printf("%5lu.%06lu: ", secs, usecs);  	} - -	if (PRINT_FIELD(EVNAME)) { -		evname = perf_evsel__name(evsel); -		printf("%s: ", evname ? evname : "[unknown]"); -	}  }  static bool is_bts_event(struct perf_event_attr *attr) @@ -390,8 +423,8 @@ static void print_sample_addr(union perf_event *event,  static void print_sample_bts(union perf_event *event,  			     struct perf_sample *sample,  			     struct perf_evsel *evsel, -			     struct machine *machine, -			     struct thread *thread) +			     struct thread *thread, +			     struct addr_location *al)  {  	struct perf_event_attr *attr = &evsel->attr; @@ -401,7 +434,7 @@ static void print_sample_bts(union perf_event *event,  			printf(" ");  		else  			printf("\n"); -		perf_evsel__print_ip(evsel, event, sample, machine, +		perf_evsel__print_ip(evsel, sample, al,  				     output[attr->type].print_ip_opts,  				     PERF_MAX_STACK_DEPTH);  	} @@ -409,16 +442,17 @@ static void print_sample_bts(union perf_event *event,  	printf(" => ");  	/* print branch_to information */ -	if (PRINT_FIELD(ADDR)) -		print_sample_addr(event, sample, machine, thread, attr); +	if (PRINT_FIELD(ADDR) || +	    ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && +	     !output[attr->type].user_set)) +		print_sample_addr(event, sample, al->machine, thread, attr);  	printf("\n");  }  static void process_event(union perf_event *event, struct perf_sample *sample, -			  struct perf_evsel *evsel, struct machine *machine, -			  struct thread *thread, -			  struct addr_location *al __maybe_unused) +			  struct perf_evsel *evsel, struct thread *thread, +			  struct addr_location *al)  {  	struct perf_event_attr *attr = &evsel->attr; @@ -427,8 +461,13 @@ static void process_event(union perf_event *event, struct perf_sample *sample,  	print_sample_start(sample, thread, evsel); +	if (PRINT_FIELD(EVNAME)) { +		const char *evname = perf_evsel__name(evsel); +		printf("%s: ", evname ? evname : "[unknown]"); +	} +  	if (is_bts_event(attr)) { -		print_sample_bts(event, sample, evsel, machine, thread); +		print_sample_bts(event, sample, evsel, thread, al);  		return;  	} @@ -436,7 +475,7 @@ static void process_event(union perf_event *event, struct perf_sample *sample,  		event_format__print(evsel->tp_format, sample->cpu,  				    sample->raw_data, sample->raw_size);  	if (PRINT_FIELD(ADDR)) -		print_sample_addr(event, sample, machine, thread, attr); +		print_sample_addr(event, sample, al->machine, thread, attr);  	if (PRINT_FIELD(IP)) {  		if (!symbol_conf.use_callchain) @@ -444,7 +483,7 @@ static void process_event(union perf_event *event, struct perf_sample *sample,  		else  			printf("\n"); -		perf_evsel__print_ip(evsel, event, sample, machine, +		perf_evsel__print_ip(evsel, sample, al,  				     output[attr->type].print_ip_opts,  				     PERF_MAX_STACK_DEPTH);  	} @@ -533,38 +572,227 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,  	if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))  		return 0; -	scripting_ops->process_event(event, sample, evsel, machine, thread, &al); +	scripting_ops->process_event(event, sample, evsel, thread, &al);  	evsel->hists.stats.total_period += sample->period;  	return 0;  } -static struct perf_tool perf_script = { -	.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, -	.attr		 = perf_event__process_attr, -	.tracing_data	 = perf_event__process_tracing_data, -	.build_id	 = perf_event__process_build_id, -	.ordered_samples = true, -	.ordering_requires_timestamps = true, +struct perf_script { +	struct perf_tool	tool; +	struct perf_session	*session; +	bool			show_task_events; +	bool			show_mmap_events;  }; +static int process_attr(struct perf_tool *tool, union perf_event *event, +			struct perf_evlist **pevlist) +{ +	struct perf_script *scr = container_of(tool, struct perf_script, tool); +	struct perf_evlist *evlist; +	struct perf_evsel *evsel, *pos; +	int err; + +	err = perf_event__process_attr(tool, event, pevlist); +	if (err) +		return err; + +	evlist = *pevlist; +	evsel = perf_evlist__last(*pevlist); + +	if (evsel->attr.type >= PERF_TYPE_MAX) +		return 0; + +	evlist__for_each(evlist, pos) { +		if (pos->attr.type == evsel->attr.type && pos != evsel) +			return 0; +	} + +	set_print_ip_opts(&evsel->attr); + +	return perf_evsel__check_attr(evsel, scr->session); +} + +static int process_comm_event(struct perf_tool *tool, +			      union perf_event *event, +			      struct perf_sample *sample, +			      struct machine *machine) +{ +	struct thread *thread; +	struct perf_script *script = container_of(tool, struct perf_script, tool); +	struct perf_session *session = script->session; +	struct perf_evsel *evsel = perf_evlist__first(session->evlist); +	int ret = -1; + +	thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid); +	if (thread == NULL) { +		pr_debug("problem processing COMM event, skipping it.\n"); +		return -1; +	} + +	if (perf_event__process_comm(tool, event, sample, machine) < 0) +		goto out; + +	if (!evsel->attr.sample_id_all) { +		sample->cpu = 0; +		sample->time = 0; +		sample->tid = event->comm.tid; +		sample->pid = event->comm.pid; +	} +	print_sample_start(sample, thread, evsel); +	perf_event__fprintf(event, stdout); +	ret = 0; + +out: +	return ret; +} + +static int process_fork_event(struct perf_tool *tool, +			      union perf_event *event, +			      struct perf_sample *sample, +			      struct machine *machine) +{ +	struct thread *thread; +	struct perf_script *script = container_of(tool, struct perf_script, tool); +	struct perf_session *session = script->session; +	struct perf_evsel *evsel = perf_evlist__first(session->evlist); + +	if (perf_event__process_fork(tool, event, sample, machine) < 0) +		return -1; + +	thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid); +	if (thread == NULL) { +		pr_debug("problem processing FORK event, skipping it.\n"); +		return -1; +	} + +	if (!evsel->attr.sample_id_all) { +		sample->cpu = 0; +		sample->time = event->fork.time; +		sample->tid = event->fork.tid; +		sample->pid = event->fork.pid; +	} +	print_sample_start(sample, thread, evsel); +	perf_event__fprintf(event, stdout); + +	return 0; +} +static int process_exit_event(struct perf_tool *tool, +			      union perf_event *event, +			      struct perf_sample *sample, +			      struct machine *machine) +{ +	struct thread *thread; +	struct perf_script *script = container_of(tool, struct perf_script, tool); +	struct perf_session *session = script->session; +	struct perf_evsel *evsel = perf_evlist__first(session->evlist); + +	thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid); +	if (thread == NULL) { +		pr_debug("problem processing EXIT event, skipping it.\n"); +		return -1; +	} + +	if (!evsel->attr.sample_id_all) { +		sample->cpu = 0; +		sample->time = 0; +		sample->tid = event->comm.tid; +		sample->pid = event->comm.pid; +	} +	print_sample_start(sample, thread, evsel); +	perf_event__fprintf(event, stdout); + +	if (perf_event__process_exit(tool, event, sample, machine) < 0) +		return -1; + +	return 0; +} + +static int process_mmap_event(struct perf_tool *tool, +			      union perf_event *event, +			      struct perf_sample *sample, +			      struct machine *machine) +{ +	struct thread *thread; +	struct perf_script *script = container_of(tool, struct perf_script, tool); +	struct perf_session *session = script->session; +	struct perf_evsel *evsel = perf_evlist__first(session->evlist); + +	if (perf_event__process_mmap(tool, event, sample, machine) < 0) +		return -1; + +	thread = machine__findnew_thread(machine, event->mmap.pid, event->mmap.tid); +	if (thread == NULL) { +		pr_debug("problem processing MMAP event, skipping it.\n"); +		return -1; +	} + +	if (!evsel->attr.sample_id_all) { +		sample->cpu = 0; +		sample->time = 0; +		sample->tid = event->mmap.tid; +		sample->pid = event->mmap.pid; +	} +	print_sample_start(sample, thread, evsel); +	perf_event__fprintf(event, stdout); + +	return 0; +} + +static int process_mmap2_event(struct perf_tool *tool, +			      union perf_event *event, +			      struct perf_sample *sample, +			      struct machine *machine) +{ +	struct thread *thread; +	struct perf_script *script = container_of(tool, struct perf_script, tool); +	struct perf_session *session = script->session; +	struct perf_evsel *evsel = perf_evlist__first(session->evlist); + +	if (perf_event__process_mmap2(tool, event, sample, machine) < 0) +		return -1; + +	thread = machine__findnew_thread(machine, event->mmap2.pid, event->mmap2.tid); +	if (thread == NULL) { +		pr_debug("problem processing MMAP2 event, skipping it.\n"); +		return -1; +	} + +	if (!evsel->attr.sample_id_all) { +		sample->cpu = 0; +		sample->time = 0; +		sample->tid = event->mmap2.tid; +		sample->pid = event->mmap2.pid; +	} +	print_sample_start(sample, thread, evsel); +	perf_event__fprintf(event, stdout); + +	return 0; +} +  static void sig_handler(int sig __maybe_unused)  {  	session_done = 1;  } -static int __cmd_script(struct perf_session *session) +static int __cmd_script(struct perf_script *script)  {  	int ret;  	signal(SIGINT, sig_handler); -	ret = perf_session__process_events(session, &perf_script); +	/* override event processing functions */ +	if (script->show_task_events) { +		script->tool.comm = process_comm_event; +		script->tool.fork = process_fork_event; +		script->tool.exit = process_exit_event; +	} +	if (script->show_mmap_events) { +		script->tool.mmap = process_mmap_event; +		script->tool.mmap2 = process_mmap2_event; +	} + +	ret = perf_session__process_events(script->session, &script->tool);  	if (debug_mode)  		pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered); @@ -874,9 +1102,9 @@ static struct script_desc *script_desc__new(const char *name)  static void script_desc__delete(struct script_desc *s)  { -	free(s->name); -	free(s->half_liner); -	free(s->args); +	zfree(&s->name); +	zfree(&s->half_liner); +	zfree(&s->args);  	free(s);  } @@ -1081,8 +1309,7 @@ static int check_ev_match(char *dir_name, char *scriptname,  			snprintf(evname, len + 1, "%s", p);  			match = 0; -			list_for_each_entry(pos, -					&session->evlist->entries, node) { +			evlist__for_each(session->evlist, pos) {  				if (!strcmp(perf_evsel__name(pos), evname)) {  					match = 1;  					break; @@ -1113,10 +1340,14 @@ int find_scripts(char **scripts_array, char **scripts_path_array)  	char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN];  	DIR *scripts_dir, *lang_dir;  	struct perf_session *session; +	struct perf_data_file file = { +		.path = input_name, +		.mode = PERF_DATA_MODE_READ, +	};  	char *temp;  	int i = 0; -	session = perf_session__new(input_name, O_RDONLY, 0, false, NULL); +	session = perf_session__new(&file, false, NULL);  	if (!session)  		return -1; @@ -1260,12 +1491,29 @@ static int have_cmd(int argc, const char **argv)  int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  {  	bool show_full_info = false; +	bool header = false; +	bool header_only = false;  	char *rec_script_path = NULL;  	char *rep_script_path = NULL;  	struct perf_session *session;  	char *script_path = NULL;  	const char **__argv;  	int i, j, err; +	struct perf_script script = { +		.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, +			.attr		 = process_attr, +			.tracing_data	 = perf_event__process_tracing_data, +			.build_id	 = perf_event__process_build_id, +			.ordered_samples = true, +			.ordering_requires_timestamps = true, +		}, +	};  	const struct option options[] = {  	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,  		    "dump raw trace in ASCII"), @@ -1283,6 +1531,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  	OPT_STRING('i', "input", &input_name, "file", "input file name"),  	OPT_BOOLEAN('d', "debug-mode", &debug_mode,  		   "do various checks like samples ordering and lost events"), +	OPT_BOOLEAN(0, "header", &header, "Show data header."), +	OPT_BOOLEAN(0, "header-only", &header_only, "Show only data header."),  	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,  		   "file", "vmlinux pathname"),  	OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, @@ -1307,6 +1557,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  		    "display extended information from perf.data file"),  	OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,  		    "Show the path of [kernel.kallsyms]"), +	OPT_BOOLEAN('\0', "show-task-events", &script.show_task_events, +		    "Show the fork/comm/exit events"), +	OPT_BOOLEAN('\0', "show-mmap-events", &script.show_mmap_events, +		    "Show the mmap events"),  	OPT_END()  	};  	const char * const script_usage[] = { @@ -1317,12 +1571,17 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  		"perf script [<options>] <top-script> [script-args]",  		NULL  	}; +	struct perf_data_file file = { +		.mode = PERF_DATA_MODE_READ, +	};  	setup_scripting();  	argc = parse_options(argc, argv, options, script_usage,  			     PARSE_OPT_STOP_AT_NON_OPTION); +	file.path = input_name; +  	if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) {  		rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);  		if (!rec_script_path) @@ -1486,19 +1745,23 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  	if (!script_name)  		setup_pager(); -	session = perf_session__new(input_name, O_RDONLY, 0, false, -				    &perf_script); +	session = perf_session__new(&file, false, &script.tool);  	if (session == NULL)  		return -ENOMEM; +	if (header || header_only) { +		perf_session__fprintf_info(session, stdout, show_full_info); +		if (header_only) +			return 0; +	} + +	script.session = session; +  	if (cpu_list) {  		if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap))  			return -1;  	} -	if (!script_name && !generate_script_lang) -		perf_session__fprintf_info(session, stdout, show_full_info); -  	if (!no_callchain)  		symbol_conf.use_callchain = true;  	else @@ -1514,7 +1777,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  			return -1;  		} -		input = open(session->filename, O_RDONLY);	/* input_name */ +		input = open(file.path, O_RDONLY);	/* input_name */  		if (input < 0) {  			perror("failed to open file");  			return -1; @@ -1537,7 +1800,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  			return -1;  		} -		err = scripting_ops->generate_script(session->pevent, +		err = scripting_ops->generate_script(session->tevent.pevent,  						     "perf-script");  		goto out;  	} @@ -1554,7 +1817,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  	if (err < 0)  		goto out; -	err = __cmd_script(session); +	err = __cmd_script(&script);  	perf_session__delete(session);  	cleanup_scripting(); diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index f686d5ff594..65a151e3606 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -46,6 +46,7 @@  #include "util/util.h"  #include "util/parse-options.h"  #include "util/parse-events.h" +#include "util/pmu.h"  #include "util/event.h"  #include "util/evlist.h"  #include "util/evsel.h" @@ -70,9 +71,44 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix);  static void print_counter(struct perf_evsel *counter, char *prefix);  static void print_aggr(char *prefix); +/* Default events used for perf stat -T */ +static const char * const transaction_attrs[] = { +	"task-clock", +	"{" +	"instructions," +	"cycles," +	"cpu/cycles-t/," +	"cpu/tx-start/," +	"cpu/el-start/," +	"cpu/cycles-ct/" +	"}" +}; + +/* More limited version when the CPU does not have all events. */ +static const char * const transaction_limited_attrs[] = { +	"task-clock", +	"{" +	"instructions," +	"cycles," +	"cpu/cycles-t/," +	"cpu/tx-start/" +	"}" +}; + +/* must match transaction_attrs and the beginning limited_attrs */ +enum { +	T_TASK_CLOCK, +	T_INSTRUCTIONS, +	T_CYCLES, +	T_CYCLES_IN_TX, +	T_TRANSACTION_START, +	T_ELISION_START, +	T_CYCLES_IN_TX_CP, +}; +  static struct perf_evlist	*evsel_list; -static struct perf_target	target = { +static struct target target = {  	.uid	= UINT_MAX,  }; @@ -90,6 +126,7 @@ static enum aggr_mode		aggr_mode			= AGGR_GLOBAL;  static volatile pid_t		child_pid			= -1;  static bool			null_run			=  false;  static int			detailed_run			=  0; +static bool			transaction_run;  static bool			big_num				=  true;  static int			big_num_opt			=  -1;  static const char		*csv_sep			= NULL; @@ -101,6 +138,7 @@ static const char		*post_cmd			= NULL;  static bool			sync_run			= false;  static unsigned int		interval			= 0;  static unsigned int		initial_delay			= 0; +static unsigned int		unit_width			= 4; /* strlen("unit") */  static bool			forever				= false;  static struct timespec		ref_time;  static struct cpu_map		*aggr_map; @@ -136,19 +174,25 @@ static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel)  static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)  { -	memset(evsel->priv, 0, sizeof(struct perf_stat)); +	int i; +	struct perf_stat *ps = evsel->priv; + +	for (i = 0; i < 3; i++) +		init_stats(&ps->res_stats[i]);  }  static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)  {  	evsel->priv = zalloc(sizeof(struct perf_stat)); -	return evsel->priv == NULL ? -ENOMEM : 0; +	if (evsel == NULL) +		return -ENOMEM; +	perf_evsel__reset_stat_priv(evsel); +	return 0;  }  static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)  { -	free(evsel->priv); -	evsel->priv = NULL; +	zfree(&evsel->priv);  }  static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel) @@ -170,15 +214,14 @@ static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel)  static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel)  { -	free(evsel->prev_raw_counts); -	evsel->prev_raw_counts = NULL; +	zfree(&evsel->prev_raw_counts);  }  static void perf_evlist__free_stats(struct perf_evlist *evlist)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		perf_evsel__free_stat_priv(evsel);  		perf_evsel__free_counts(evsel);  		perf_evsel__free_prev_raw_counts(evsel); @@ -189,7 +232,7 @@ static int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if (perf_evsel__alloc_stat_priv(evsel) < 0 ||  		    perf_evsel__alloc_counts(evsel, perf_evsel__nr_cpus(evsel)) < 0 ||  		    (alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel) < 0)) @@ -214,13 +257,16 @@ static struct stats runtime_l1_icache_stats[MAX_NR_CPUS];  static struct stats runtime_ll_cache_stats[MAX_NR_CPUS];  static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS];  static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; +static struct stats runtime_cycles_in_tx_stats[MAX_NR_CPUS];  static struct stats walltime_nsecs_stats; +static struct stats runtime_transaction_stats[MAX_NR_CPUS]; +static struct stats runtime_elision_stats[MAX_NR_CPUS];  static void perf_stat__reset_stats(struct perf_evlist *evlist)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		perf_evsel__reset_stat_priv(evsel);  		perf_evsel__reset_counts(evsel, perf_evsel__nr_cpus(evsel));  	} @@ -236,6 +282,11 @@ static void perf_stat__reset_stats(struct perf_evlist *evlist)  	memset(runtime_ll_cache_stats, 0, sizeof(runtime_ll_cache_stats));  	memset(runtime_itlb_cache_stats, 0, sizeof(runtime_itlb_cache_stats));  	memset(runtime_dtlb_cache_stats, 0, sizeof(runtime_dtlb_cache_stats)); +	memset(runtime_cycles_in_tx_stats, 0, +			sizeof(runtime_cycles_in_tx_stats)); +	memset(runtime_transaction_stats, 0, +		sizeof(runtime_transaction_stats)); +	memset(runtime_elision_stats, 0, sizeof(runtime_elision_stats));  	memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats));  } @@ -249,11 +300,10 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)  	attr->inherit = !no_inherit; -	if (perf_target__has_cpu(&target)) +	if (target__has_cpu(&target))  		return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); -	if (!perf_target__has_task(&target) && -	    perf_evsel__is_group_leader(evsel)) { +	if (!target__has_task(&target) && perf_evsel__is_group_leader(evsel)) {  		attr->disabled = 1;  		if (!initial_delay)  			attr->enable_on_exec = 1; @@ -274,6 +324,29 @@ static inline int nsec_counter(struct perf_evsel *evsel)  	return 0;  } +static struct perf_evsel *nth_evsel(int n) +{ +	static struct perf_evsel **array; +	static int array_len; +	struct perf_evsel *ev; +	int j; + +	/* Assumes this only called when evsel_list does not change anymore. */ +	if (!array) { +		evlist__for_each(evsel_list, ev) +			array_len++; +		array = malloc(array_len * sizeof(void *)); +		if (!array) +			exit(ENOMEM); +		j = 0; +		evlist__for_each(evsel_list, ev) +			array[j++] = ev; +	} +	if (n < array_len) +		return array[n]; +	return NULL; +} +  /*   * Update various tracking values we maintain to print   * more semantic information such as miss/hit ratios, @@ -285,6 +358,15 @@ static void update_shadow_stats(struct perf_evsel *counter, u64 *count)  		update_stats(&runtime_nsecs_stats[0], count[0]);  	else if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES))  		update_stats(&runtime_cycles_stats[0], count[0]); +	else if (transaction_run && +		 perf_evsel__cmp(counter, nth_evsel(T_CYCLES_IN_TX))) +		update_stats(&runtime_cycles_in_tx_stats[0], count[0]); +	else if (transaction_run && +		 perf_evsel__cmp(counter, nth_evsel(T_TRANSACTION_START))) +		update_stats(&runtime_transaction_stats[0], count[0]); +	else if (transaction_run && +		 perf_evsel__cmp(counter, nth_evsel(T_ELISION_START))) +		update_stats(&runtime_elision_stats[0], count[0]);  	else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND))  		update_stats(&runtime_stalled_cycles_front_stats[0], count[0]);  	else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND)) @@ -365,13 +447,13 @@ static void print_interval(void)  	char prefix[64];  	if (aggr_mode == AGGR_GLOBAL) { -		list_for_each_entry(counter, &evsel_list->entries, node) { +		evlist__for_each(evsel_list, counter) {  			ps = counter->priv;  			memset(ps->res_stats, 0, sizeof(ps->res_stats));  			read_counter_aggr(counter);  		}  	} else	{ -		list_for_each_entry(counter, &evsel_list->entries, node) { +		evlist__for_each(evsel_list, counter) {  			ps = counter->priv;  			memset(ps->res_stats, 0, sizeof(ps->res_stats));  			read_counter(counter); @@ -385,17 +467,17 @@ static void print_interval(void)  	if (num_print_interval == 0 && !csv_output) {  		switch (aggr_mode) {  		case AGGR_SOCKET: -			fprintf(output, "#           time socket cpus             counts events\n"); +			fprintf(output, "#           time socket cpus             counts %*s events\n", unit_width, "unit");  			break;  		case AGGR_CORE: -			fprintf(output, "#           time core         cpus             counts events\n"); +			fprintf(output, "#           time core         cpus             counts %*s events\n", unit_width, "unit");  			break;  		case AGGR_NONE: -			fprintf(output, "#           time CPU                 counts events\n"); +			fprintf(output, "#           time CPU                counts %*s events\n", unit_width, "unit");  			break;  		case AGGR_GLOBAL:  		default: -			fprintf(output, "#           time             counts events\n"); +			fprintf(output, "#           time             counts %*s events\n", unit_width, "unit");  		}  	} @@ -408,12 +490,12 @@ static void print_interval(void)  		print_aggr(prefix);  		break;  	case AGGR_NONE: -		list_for_each_entry(counter, &evsel_list->entries, node) +		evlist__for_each(evsel_list, counter)  			print_counter(counter, prefix);  		break;  	case AGGR_GLOBAL:  	default: -		list_for_each_entry(counter, &evsel_list->entries, node) +		evlist__for_each(evsel_list, counter)  			print_counter_aggr(counter, prefix);  	} @@ -429,17 +511,31 @@ static void handle_initial_delay(void)  			nthreads = thread_map__nr(evsel_list->threads);  		usleep(initial_delay * 1000); -		list_for_each_entry(counter, &evsel_list->entries, node) +		evlist__for_each(evsel_list, counter)  			perf_evsel__enable(counter, ncpus, nthreads);  	}  } +static volatile int workload_exec_errno; + +/* + * perf_evlist__prepare_workload will send a SIGUSR1 + * if the fork fails, since we asked by setting its + * want_signal to true. + */ +static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *info, +					void *ucontext __maybe_unused) +{ +	workload_exec_errno = info->si_value.sival_int; +} +  static int __run_perf_stat(int argc, const char **argv)  {  	char msg[512];  	unsigned long long t0, t1;  	struct perf_evsel *counter;  	struct timespec ts; +	size_t l;  	int status = 0;  	const bool forks = (argc > 0); @@ -452,17 +548,18 @@ static int __run_perf_stat(int argc, const char **argv)  	}  	if (forks) { -		if (perf_evlist__prepare_workload(evsel_list, &target, argv, -						  false, false) < 0) { +		if (perf_evlist__prepare_workload(evsel_list, &target, argv, false, +						  workload_exec_failed_signal) < 0) {  			perror("failed to prepare workload");  			return -1;  		} +		child_pid = evsel_list->workload.pid;  	}  	if (group)  		perf_evlist__set_leader(evsel_list); -	list_for_each_entry(counter, &evsel_list->entries, node) { +	evlist__for_each(evsel_list, counter) {  		if (create_perf_stat_counter(counter) < 0) {  			/*  			 * PPC returns ENXIO for HW counters until 2.6.37 @@ -488,6 +585,10 @@ static int __run_perf_stat(int argc, const char **argv)  			return -1;  		}  		counter->supported = true; + +		l = strlen(counter->unit); +		if (l > unit_width) +			unit_width = l;  	}  	if (perf_evlist__apply_filters(evsel_list)) { @@ -513,6 +614,13 @@ static int __run_perf_stat(int argc, const char **argv)  			}  		}  		wait(&status); + +		if (workload_exec_errno) { +			const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); +			pr_err("Workload failed: %s\n", emsg); +			return -1; +		} +  		if (WIFSIGNALED(status))  			psignal(WTERMSIG(status), argv[0]);  	} else { @@ -529,13 +637,13 @@ static int __run_perf_stat(int argc, const char **argv)  	update_stats(&walltime_nsecs_stats, t1 - t0);  	if (aggr_mode == AGGR_GLOBAL) { -		list_for_each_entry(counter, &evsel_list->entries, node) { +		evlist__for_each(evsel_list, counter) {  			read_counter_aggr(counter);  			perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter),  					     thread_map__nr(evsel_list->threads));  		}  	} else { -		list_for_each_entry(counter, &evsel_list->entries, node) { +		evlist__for_each(evsel_list, counter) {  			read_counter(counter);  			perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1);  		} @@ -544,7 +652,7 @@ static int __run_perf_stat(int argc, const char **argv)  	return WEXITSTATUS(status);  } -static int run_perf_stat(int argc __maybe_unused, const char **argv) +static int run_perf_stat(int argc, const char **argv)  {  	int ret; @@ -627,11 +735,25 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr)  static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)  {  	double msecs = avg / 1e6; -	const char *fmt = csv_output ? "%.6f%s%s" : "%18.6f%s%-25s"; +	const char *fmt_v, *fmt_n; +	char name[25]; + +	fmt_v = csv_output ? "%.6f%s" : "%18.6f%s"; +	fmt_n = csv_output ? "%s" : "%-25s";  	aggr_printout(evsel, cpu, nr); -	fprintf(output, fmt, msecs, csv_sep, perf_evsel__name(evsel)); +	scnprintf(name, sizeof(name), "%s%s", +		  perf_evsel__name(evsel), csv_output ? "" : " (msec)"); + +	fprintf(output, fmt_v, msecs, csv_sep); + +	if (csv_output) +		fprintf(output, "%s%s", evsel->unit, csv_sep); +	else +		fprintf(output, "%-*s%s", unit_width, evsel->unit, csv_sep); + +	fprintf(output, fmt_n, name);  	if (evsel->cgrp)  		fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); @@ -827,22 +949,32 @@ static void print_ll_cache_misses(int cpu,  static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)  { -	double total, ratio = 0.0; +	double total, ratio = 0.0, total2; +	double sc =  evsel->scale;  	const char *fmt; -	if (csv_output) -		fmt = "%.0f%s%s"; -	else if (big_num) -		fmt = "%'18.0f%s%-25s"; -	else -		fmt = "%18.0f%s%-25s"; +	if (csv_output) { +		fmt = sc != 1.0 ?  "%.2f%s" : "%.0f%s"; +	} else { +		if (big_num) +			fmt = sc != 1.0 ? "%'18.2f%s" : "%'18.0f%s"; +		else +			fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s"; +	}  	aggr_printout(evsel, cpu, nr);  	if (aggr_mode == AGGR_GLOBAL)  		cpu = 0; -	fprintf(output, fmt, avg, csv_sep, perf_evsel__name(evsel)); +	fprintf(output, fmt, avg, csv_sep); + +	if (evsel->unit) +		fprintf(output, "%-*s%s", +			csv_output ? 0 : unit_width, +			evsel->unit, csv_sep); + +	fprintf(output, "%-*s", csv_output ? 0 : 25, perf_evsel__name(evsel));  	if (evsel->cgrp)  		fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); @@ -852,17 +984,19 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)  	if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {  		total = avg_stats(&runtime_cycles_stats[cpu]); -		if (total) +		if (total) {  			ratio = avg / total; - -		fprintf(output, " #   %5.2f  insns per cycle        ", ratio); - +			fprintf(output, " #   %5.2f  insns per cycle        ", ratio); +		}  		total = avg_stats(&runtime_stalled_cycles_front_stats[cpu]);  		total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[cpu]));  		if (total && avg) {  			ratio = total / avg; -			fprintf(output, "\n                                             #   %5.2f  stalled cycles per insn", ratio); +			fprintf(output, "\n"); +			if (aggr_mode == AGGR_NONE) +				fprintf(output, "        "); +			fprintf(output, "                                                  #   %5.2f  stalled cycles per insn", ratio);  		}  	} else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) && @@ -919,10 +1053,47 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)  	} else if (perf_evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) {  		total = avg_stats(&runtime_nsecs_stats[cpu]); +		if (total) { +			ratio = avg / total; +			fprintf(output, " # %8.3f GHz                    ", ratio); +		} +	} else if (transaction_run && +		   perf_evsel__cmp(evsel, nth_evsel(T_CYCLES_IN_TX))) { +		total = avg_stats(&runtime_cycles_stats[cpu]); +		if (total) +			fprintf(output, +				" #   %5.2f%% transactional cycles   ", +				100.0 * (avg / total)); +	} else if (transaction_run && +		   perf_evsel__cmp(evsel, nth_evsel(T_CYCLES_IN_TX_CP))) { +		total = avg_stats(&runtime_cycles_stats[cpu]); +		total2 = avg_stats(&runtime_cycles_in_tx_stats[cpu]); +		if (total2 < avg) +			total2 = avg; +		if (total) +			fprintf(output, +				" #   %5.2f%% aborted cycles         ", +				100.0 * ((total2-avg) / total)); +	} else if (transaction_run && +		   perf_evsel__cmp(evsel, nth_evsel(T_TRANSACTION_START)) && +		   avg > 0 && +		   runtime_cycles_in_tx_stats[cpu].n != 0) { +		total = avg_stats(&runtime_cycles_in_tx_stats[cpu]); +  		if (total) -			ratio = 1.0 * avg / total; +			ratio = total / avg; + +		fprintf(output, " # %8.0f cycles / transaction   ", ratio); +	} else if (transaction_run && +		   perf_evsel__cmp(evsel, nth_evsel(T_ELISION_START)) && +		   avg > 0 && +		   runtime_cycles_in_tx_stats[cpu].n != 0) { +		total = avg_stats(&runtime_cycles_in_tx_stats[cpu]); -		fprintf(output, " # %8.3f GHz                    ", ratio); +		if (total) +			ratio = total / avg; + +		fprintf(output, " # %8.0f cycles / elision       ", ratio);  	} else if (runtime_nsecs_stats[cpu].n != 0) {  		char unit = 'M'; @@ -945,6 +1116,7 @@ static void print_aggr(char *prefix)  {  	struct perf_evsel *counter;  	int cpu, cpu2, s, s2, id, nr; +	double uval;  	u64 ena, run, val;  	if (!(aggr_map || aggr_get_id)) @@ -952,7 +1124,7 @@ static void print_aggr(char *prefix)  	for (s = 0; s < aggr_map->nr; s++) {  		id = aggr_map->map[s]; -		list_for_each_entry(counter, &evsel_list->entries, node) { +		evlist__for_each(evsel_list, counter) {  			val = ena = run = 0;  			nr = 0;  			for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { @@ -971,11 +1143,17 @@ static void print_aggr(char *prefix)  			if (run == 0 || ena == 0) {  				aggr_printout(counter, id, nr); -				fprintf(output, "%*s%s%*s", +				fprintf(output, "%*s%s",  					csv_output ? 0 : 18,  					counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, -					csv_sep, -					csv_output ? 0 : -24, +					csv_sep); + +				fprintf(output, "%-*s%s", +					csv_output ? 0 : unit_width, +					counter->unit, csv_sep); + +				fprintf(output, "%*s", +					csv_output ? 0 : -25,  					perf_evsel__name(counter));  				if (counter->cgrp) @@ -985,11 +1163,12 @@ static void print_aggr(char *prefix)  				fputc('\n', output);  				continue;  			} +			uval = val * counter->scale;  			if (nsec_counter(counter)) -				nsec_printout(id, nr, counter, val); +				nsec_printout(id, nr, counter, uval);  			else -				abs_printout(id, nr, counter, val); +				abs_printout(id, nr, counter, uval);  			if (!csv_output) {  				print_noise(counter, 1.0); @@ -1012,16 +1191,21 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)  	struct perf_stat *ps = counter->priv;  	double avg = avg_stats(&ps->res_stats[0]);  	int scaled = counter->counts->scaled; +	double uval;  	if (prefix)  		fprintf(output, "%s", prefix);  	if (scaled == -1) { -		fprintf(output, "%*s%s%*s", +		fprintf(output, "%*s%s",  			csv_output ? 0 : 18,  			counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, -			csv_sep, -			csv_output ? 0 : -24, +			csv_sep); +		fprintf(output, "%-*s%s", +			csv_output ? 0 : unit_width, +			counter->unit, csv_sep); +		fprintf(output, "%*s", +			csv_output ? 0 : -25,  			perf_evsel__name(counter));  		if (counter->cgrp) @@ -1031,10 +1215,12 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)  		return;  	} +	uval = avg * counter->scale; +  	if (nsec_counter(counter)) -		nsec_printout(-1, 0, counter, avg); +		nsec_printout(-1, 0, counter, uval);  	else -		abs_printout(-1, 0, counter, avg); +		abs_printout(-1, 0, counter, uval);  	print_noise(counter, avg); @@ -1061,6 +1247,7 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)  static void print_counter(struct perf_evsel *counter, char *prefix)  {  	u64 ena, run, val; +	double uval;  	int cpu;  	for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { @@ -1072,14 +1259,20 @@ static void print_counter(struct perf_evsel *counter, char *prefix)  			fprintf(output, "%s", prefix);  		if (run == 0 || ena == 0) { -			fprintf(output, "CPU%*d%s%*s%s%*s", +			fprintf(output, "CPU%*d%s%*s%s",  				csv_output ? 0 : -4,  				perf_evsel__cpus(counter)->map[cpu], csv_sep,  				csv_output ? 0 : 18,  				counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, -				csv_sep, -				csv_output ? 0 : -24, -				perf_evsel__name(counter)); +				csv_sep); + +				fprintf(output, "%-*s%s", +					csv_output ? 0 : unit_width, +					counter->unit, csv_sep); + +				fprintf(output, "%*s", +					csv_output ? 0 : -25, +					perf_evsel__name(counter));  			if (counter->cgrp)  				fprintf(output, "%s%s", @@ -1089,10 +1282,12 @@ static void print_counter(struct perf_evsel *counter, char *prefix)  			continue;  		} +		uval = val * counter->scale; +  		if (nsec_counter(counter)) -			nsec_printout(cpu, 0, counter, val); +			nsec_printout(cpu, 0, counter, uval);  		else -			abs_printout(cpu, 0, counter, val); +			abs_printout(cpu, 0, counter, uval);  		if (!csv_output) {  			print_noise(counter, 1.0); @@ -1115,7 +1310,11 @@ static void print_stat(int argc, const char **argv)  	if (!csv_output) {  		fprintf(output, "\n");  		fprintf(output, " Performance counter stats for "); -		if (!perf_target__has_task(&target)) { +		if (target.system_wide) +			fprintf(output, "\'system wide"); +		else if (target.cpu_list) +			fprintf(output, "\'CPU(s) %s", target.cpu_list); +		else if (!target__has_task(&target)) {  			fprintf(output, "\'%s", argv[0]);  			for (i = 1; i < argc; i++)  				fprintf(output, " %s", argv[i]); @@ -1136,11 +1335,11 @@ static void print_stat(int argc, const char **argv)  		print_aggr(NULL);  		break;  	case AGGR_GLOBAL: -		list_for_each_entry(counter, &evsel_list->entries, node) +		evlist__for_each(evsel_list, counter)  			print_counter_aggr(counter, NULL);  		break;  	case AGGR_NONE: -		list_for_each_entry(counter, &evsel_list->entries, node) +		evlist__for_each(evsel_list, counter)  			print_counter(counter, NULL);  		break;  	default: @@ -1236,6 +1435,16 @@ static int perf_stat_init_aggr_mode(void)  	return 0;  } +static int setup_events(const char * const *attrs, unsigned len) +{ +	unsigned i; + +	for (i = 0; i < len; i++) { +		if (parse_events(evsel_list, attrs[i])) +			return -1; +	} +	return 0; +}  /*   * Add default attributes, if there were no attributes specified or @@ -1354,6 +1563,22 @@ static int add_default_attributes(void)  	if (null_run)  		return 0; +	if (transaction_run) { +		int err; +		if (pmu_have_event("cpu", "cycles-ct") && +		    pmu_have_event("cpu", "el-start")) +			err = setup_events(transaction_attrs, +					ARRAY_SIZE(transaction_attrs)); +		else +			err = setup_events(transaction_limited_attrs, +				 ARRAY_SIZE(transaction_limited_attrs)); +		if (err < 0) { +			fprintf(stderr, "Cannot set up transaction events\n"); +			return -1; +		} +		return 0; +	} +  	if (!evsel_list->nr_entries) {  		if (perf_evlist__add_default_attrs(evsel_list, default_attrs) < 0)  			return -1; @@ -1388,6 +1613,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)  	int output_fd = 0;  	const char *output_name	= NULL;  	const struct option options[] = { +	OPT_BOOLEAN('T', "transaction", &transaction_run, +		    "hardware transaction statistics"),  	OPT_CALLBACK('e', "event", &evsel_list, "event",  		     "event selector. use 'perf list' to list available events",  		     parse_events_option), @@ -1447,7 +1674,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)  		"perf stat [<options>] [<command>]",  		NULL  	}; -	int status = -ENOMEM, run_idx; +	int status = -EINVAL, run_idx;  	const char *mode;  	setlocale(LC_ALL, ""); @@ -1465,12 +1692,15 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)  	if (output_name && output_fd) {  		fprintf(stderr, "cannot use both --output and --log-fd\n"); -		usage_with_options(stat_usage, options); +		parse_options_usage(stat_usage, options, "o", 1); +		parse_options_usage(NULL, options, "log-fd", 0); +		goto out;  	}  	if (output_fd < 0) {  		fprintf(stderr, "argument to --log-fd must be a > 0\n"); -		usage_with_options(stat_usage, options); +		parse_options_usage(stat_usage, options, "log-fd", 0); +		goto out;  	}  	if (!output) { @@ -1507,53 +1737,63 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)  		/* User explicitly passed -B? */  		if (big_num_opt == 1) {  			fprintf(stderr, "-B option not supported with -x\n"); -			usage_with_options(stat_usage, options); +			parse_options_usage(stat_usage, options, "B", 1); +			parse_options_usage(NULL, options, "x", 1); +			goto out;  		} else /* Nope, so disable big number formatting */  			big_num = false;  	} else if (big_num_opt == 0) /* User passed --no-big-num */  		big_num = false; -	if (!argc && !perf_target__has_task(&target)) +	if (!argc && target__none(&target))  		usage_with_options(stat_usage, options); +  	if (run_count < 0) { -		usage_with_options(stat_usage, options); +		pr_err("Run count must be a positive number\n"); +		parse_options_usage(stat_usage, options, "r", 1); +		goto out;  	} else if (run_count == 0) {  		forever = true;  		run_count = 1;  	}  	/* no_aggr, cgroup are for system-wide only */ -	if ((aggr_mode != AGGR_GLOBAL || nr_cgroups) -	     && !perf_target__has_cpu(&target)) { +	if ((aggr_mode != AGGR_GLOBAL || nr_cgroups) && +	    !target__has_cpu(&target)) {  		fprintf(stderr, "both cgroup and no-aggregation "  			"modes only available in system-wide mode\n"); -		usage_with_options(stat_usage, options); -		return -1; +		parse_options_usage(stat_usage, options, "G", 1); +		parse_options_usage(NULL, options, "A", 1); +		parse_options_usage(NULL, options, "a", 1); +		goto out;  	}  	if (add_default_attributes())  		goto out; -	perf_target__validate(&target); +	target__validate(&target);  	if (perf_evlist__create_maps(evsel_list, &target) < 0) { -		if (perf_target__has_task(&target)) +		if (target__has_task(&target)) {  			pr_err("Problems finding threads of monitor\n"); -		if (perf_target__has_cpu(&target)) +			parse_options_usage(stat_usage, options, "p", 1); +			parse_options_usage(NULL, options, "t", 1); +		} else if (target__has_cpu(&target)) {  			perror("failed to parse CPUs map"); - -		usage_with_options(stat_usage, options); -		return -1; +			parse_options_usage(stat_usage, options, "C", 1); +			parse_options_usage(NULL, options, "a", 1); +		} +		goto out;  	}  	if (interval && interval < 100) {  		pr_err("print interval must be >= 100ms\n"); -		usage_with_options(stat_usage, options); -		return -1; +		parse_options_usage(stat_usage, options, "I", 1); +		goto out;  	}  	if (perf_evlist__alloc_stats(evsel_list, interval)) -		goto out_free_maps; +		goto out;  	if (perf_stat_init_aggr_mode())  		goto out; @@ -1588,8 +1828,6 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)  		print_stat(argc, argv);  	perf_evlist__free_stats(evsel_list); -out_free_maps: -	perf_evlist__delete_maps(evsel_list);  out:  	perf_evlist__delete(evsel_list);  	return status; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index c2e02319347..74db2568b86 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -36,29 +36,34 @@  #include "util/session.h"  #include "util/svghelper.h"  #include "util/tool.h" +#include "util/data.h"  #define SUPPORT_OLD_POWER_EVENTS 1  #define PWR_EVENT_EXIT -1 - -static unsigned int	numcpus; -static u64		min_freq;	/* Lowest CPU frequency seen */ -static u64		max_freq;	/* Highest CPU frequency seen */ -static u64		turbo_frequency; - -static u64		first_time, last_time; - -static bool		power_only; - -  struct per_pid; -struct per_pidcomm; - -struct cpu_sample;  struct power_event;  struct wake_event; -struct sample_wrapper; +struct timechart { +	struct perf_tool	tool; +	struct per_pid		*all_data; +	struct power_event	*power_events; +	struct wake_event	*wake_events; +	int			proc_num; +	unsigned int		numcpus; +	u64			min_freq,	/* Lowest CPU frequency seen */ +				max_freq,	/* Highest CPU frequency seen */ +				turbo_frequency, +				first_time, last_time; +	bool			power_only, +				tasks_only, +				with_backtrace, +				topology; +}; + +struct per_pidcomm; +struct cpu_sample;  /*   * Datastructure layout: @@ -123,10 +128,9 @@ struct cpu_sample {  	u64 end_time;  	int type;  	int cpu; +	const char *backtrace;  }; -static struct per_pid *all_data; -  #define CSTATE 1  #define PSTATE 2 @@ -144,12 +148,9 @@ struct wake_event {  	int waker;  	int wakee;  	u64 time; +	const char *backtrace;  }; -static struct power_event    *power_events; -static struct wake_event     *wake_events; - -struct process_filter;  struct process_filter {  	char			*name;  	int			pid; @@ -159,9 +160,9 @@ struct process_filter {  static struct process_filter *process_filter; -static struct per_pid *find_create_pid(int pid) +static struct per_pid *find_create_pid(struct timechart *tchart, int pid)  { -	struct per_pid *cursor = all_data; +	struct per_pid *cursor = tchart->all_data;  	while (cursor) {  		if (cursor->pid == pid) @@ -171,16 +172,16 @@ static struct per_pid *find_create_pid(int pid)  	cursor = zalloc(sizeof(*cursor));  	assert(cursor != NULL);  	cursor->pid = pid; -	cursor->next = all_data; -	all_data = cursor; +	cursor->next = tchart->all_data; +	tchart->all_data = cursor;  	return cursor;  } -static void pid_set_comm(int pid, char *comm) +static void pid_set_comm(struct timechart *tchart, int pid, char *comm)  {  	struct per_pid *p;  	struct per_pidcomm *c; -	p = find_create_pid(pid); +	p = find_create_pid(tchart, pid);  	c = p->all;  	while (c) {  		if (c->comm && strcmp(c->comm, comm) == 0) { @@ -202,14 +203,14 @@ static void pid_set_comm(int pid, char *comm)  	p->all = c;  } -static void pid_fork(int pid, int ppid, u64 timestamp) +static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp)  {  	struct per_pid *p, *pp; -	p = find_create_pid(pid); -	pp = find_create_pid(ppid); +	p = find_create_pid(tchart, pid); +	pp = find_create_pid(tchart, ppid);  	p->ppid = ppid;  	if (pp->current && pp->current->comm && !p->current) -		pid_set_comm(pid, pp->current->comm); +		pid_set_comm(tchart, pid, pp->current->comm);  	p->start_time = timestamp;  	if (p->current) { @@ -218,23 +219,24 @@ static void pid_fork(int pid, int ppid, u64 timestamp)  	}  } -static void pid_exit(int pid, u64 timestamp) +static void pid_exit(struct timechart *tchart, int pid, u64 timestamp)  {  	struct per_pid *p; -	p = find_create_pid(pid); +	p = find_create_pid(tchart, pid);  	p->end_time = timestamp;  	if (p->current)  		p->current->end_time = timestamp;  } -static void -pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end) +static void pid_put_sample(struct timechart *tchart, int pid, int type, +			   unsigned int cpu, u64 start, u64 end, +			   const char *backtrace)  {  	struct per_pid *p;  	struct per_pidcomm *c;  	struct cpu_sample *sample; -	p = find_create_pid(pid); +	p = find_create_pid(tchart, pid);  	c = p->current;  	if (!c) {  		c = zalloc(sizeof(*c)); @@ -251,6 +253,7 @@ pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)  	sample->type = type;  	sample->next = c->samples;  	sample->cpu = cpu; +	sample->backtrace = backtrace;  	c->samples = sample;  	if (sample->type == TYPE_RUNNING && end > start && start > 0) { @@ -271,84 +274,47 @@ static int cpus_cstate_state[MAX_CPUS];  static u64 cpus_pstate_start_times[MAX_CPUS];  static u64 cpus_pstate_state[MAX_CPUS]; -static int process_comm_event(struct perf_tool *tool __maybe_unused, +static int process_comm_event(struct perf_tool *tool,  			      union perf_event *event,  			      struct perf_sample *sample __maybe_unused,  			      struct machine *machine __maybe_unused)  { -	pid_set_comm(event->comm.tid, event->comm.comm); +	struct timechart *tchart = container_of(tool, struct timechart, tool); +	pid_set_comm(tchart, event->comm.tid, event->comm.comm);  	return 0;  } -static int process_fork_event(struct perf_tool *tool __maybe_unused, +static int process_fork_event(struct perf_tool *tool,  			      union perf_event *event,  			      struct perf_sample *sample __maybe_unused,  			      struct machine *machine __maybe_unused)  { -	pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); +	struct timechart *tchart = container_of(tool, struct timechart, tool); +	pid_fork(tchart, event->fork.pid, event->fork.ppid, event->fork.time);  	return 0;  } -static int process_exit_event(struct perf_tool *tool __maybe_unused, +static int process_exit_event(struct perf_tool *tool,  			      union perf_event *event,  			      struct perf_sample *sample __maybe_unused,  			      struct machine *machine __maybe_unused)  { -	pid_exit(event->fork.pid, event->fork.time); +	struct timechart *tchart = container_of(tool, struct timechart, tool); +	pid_exit(tchart, event->fork.pid, event->fork.time);  	return 0;  } -struct trace_entry { -	unsigned short		type; -	unsigned char		flags; -	unsigned char		preempt_count; -	int			pid; -	int			lock_depth; -}; -  #ifdef SUPPORT_OLD_POWER_EVENTS  static int use_old_power_events; -struct power_entry_old { -	struct trace_entry te; -	u64	type; -	u64	value; -	u64	cpu_id; -};  #endif -struct power_processor_entry { -	struct trace_entry te; -	u32	state; -	u32	cpu_id; -}; - -#define TASK_COMM_LEN 16 -struct wakeup_entry { -	struct trace_entry te; -	char comm[TASK_COMM_LEN]; -	int   pid; -	int   prio; -	int   success; -}; - -struct sched_switch { -	struct trace_entry te; -	char prev_comm[TASK_COMM_LEN]; -	int  prev_pid; -	int  prev_prio; -	long prev_state; /* Arjan weeps. */ -	char next_comm[TASK_COMM_LEN]; -	int  next_pid; -	int  next_prio; -}; -  static void c_state_start(int cpu, u64 timestamp, int state)  {  	cpus_cstate_start_times[cpu] = timestamp;  	cpus_cstate_state[cpu] = state;  } -static void c_state_end(int cpu, u64 timestamp) +static void c_state_end(struct timechart *tchart, int cpu, u64 timestamp)  {  	struct power_event *pwr = zalloc(sizeof(*pwr)); @@ -360,12 +326,12 @@ static void c_state_end(int cpu, u64 timestamp)  	pwr->end_time = timestamp;  	pwr->cpu = cpu;  	pwr->type = CSTATE; -	pwr->next = power_events; +	pwr->next = tchart->power_events; -	power_events = pwr; +	tchart->power_events = pwr;  } -static void p_state_change(int cpu, u64 timestamp, u64 new_freq) +static void p_state_change(struct timechart *tchart, int cpu, u64 timestamp, u64 new_freq)  {  	struct power_event *pwr; @@ -381,73 +347,78 @@ static void p_state_change(int cpu, u64 timestamp, u64 new_freq)  	pwr->end_time = timestamp;  	pwr->cpu = cpu;  	pwr->type = PSTATE; -	pwr->next = power_events; +	pwr->next = tchart->power_events;  	if (!pwr->start_time) -		pwr->start_time = first_time; +		pwr->start_time = tchart->first_time; -	power_events = pwr; +	tchart->power_events = pwr;  	cpus_pstate_state[cpu] = new_freq;  	cpus_pstate_start_times[cpu] = timestamp; -	if ((u64)new_freq > max_freq) -		max_freq = new_freq; +	if ((u64)new_freq > tchart->max_freq) +		tchart->max_freq = new_freq; -	if (new_freq < min_freq || min_freq == 0) -		min_freq = new_freq; +	if (new_freq < tchart->min_freq || tchart->min_freq == 0) +		tchart->min_freq = new_freq; -	if (new_freq == max_freq - 1000) -			turbo_frequency = max_freq; +	if (new_freq == tchart->max_freq - 1000) +		tchart->turbo_frequency = tchart->max_freq;  } -static void -sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te) +static void sched_wakeup(struct timechart *tchart, int cpu, u64 timestamp, +			 int waker, int wakee, u8 flags, const char *backtrace)  {  	struct per_pid *p; -	struct wakeup_entry *wake = (void *)te;  	struct wake_event *we = zalloc(sizeof(*we));  	if (!we)  		return;  	we->time = timestamp; -	we->waker = pid; +	we->waker = waker; +	we->backtrace = backtrace; -	if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ)) +	if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ))  		we->waker = -1; -	we->wakee = wake->pid; -	we->next = wake_events; -	wake_events = we; -	p = find_create_pid(we->wakee); +	we->wakee = wakee; +	we->next = tchart->wake_events; +	tchart->wake_events = we; +	p = find_create_pid(tchart, we->wakee);  	if (p && p->current && p->current->state == TYPE_NONE) {  		p->current->state_since = timestamp;  		p->current->state = TYPE_WAITING;  	}  	if (p && p->current && p->current->state == TYPE_BLOCKED) { -		pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp); +		pid_put_sample(tchart, p->pid, p->current->state, cpu, +			       p->current->state_since, timestamp, NULL);  		p->current->state_since = timestamp;  		p->current->state = TYPE_WAITING;  	}  } -static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) +static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp, +			 int prev_pid, int next_pid, u64 prev_state, +			 const char *backtrace)  {  	struct per_pid *p = NULL, *prev_p; -	struct sched_switch *sw = (void *)te; - -	prev_p = find_create_pid(sw->prev_pid); +	prev_p = find_create_pid(tchart, prev_pid); -	p = find_create_pid(sw->next_pid); +	p = find_create_pid(tchart, next_pid);  	if (prev_p->current && prev_p->current->state != TYPE_NONE) -		pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp); +		pid_put_sample(tchart, prev_pid, TYPE_RUNNING, cpu, +			       prev_p->current->state_since, timestamp, +			       backtrace);  	if (p && p->current) {  		if (p->current->state != TYPE_NONE) -			pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp); +			pid_put_sample(tchart, next_pid, p->current->state, cpu, +				       p->current->state_since, timestamp, +				       backtrace);  		p->current->state_since = timestamp;  		p->current->state = TYPE_RUNNING; @@ -456,109 +427,211 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)  	if (prev_p->current) {  		prev_p->current->state = TYPE_NONE;  		prev_p->current->state_since = timestamp; -		if (sw->prev_state & 2) +		if (prev_state & 2)  			prev_p->current->state = TYPE_BLOCKED; -		if (sw->prev_state == 0) +		if (prev_state == 0)  			prev_p->current->state = TYPE_WAITING;  	}  } -typedef int (*tracepoint_handler)(struct perf_evsel *evsel, -				  struct perf_sample *sample); +static const char *cat_backtrace(union perf_event *event, +				 struct perf_sample *sample, +				 struct machine *machine) +{ +	struct addr_location al; +	unsigned int i; +	char *p = NULL; +	size_t p_len; +	u8 cpumode = PERF_RECORD_MISC_USER; +	struct addr_location tal; +	struct ip_callchain *chain = sample->callchain; +	FILE *f = open_memstream(&p, &p_len); + +	if (!f) { +		perror("open_memstream error"); +		return NULL; +	} + +	if (!chain) +		goto exit; -static int process_sample_event(struct perf_tool *tool __maybe_unused, -				union perf_event *event __maybe_unused, +	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { +		fprintf(stderr, "problem processing %d event, skipping it.\n", +			event->header.type); +		goto exit; +	} + +	for (i = 0; i < chain->nr; i++) { +		u64 ip; + +		if (callchain_param.order == ORDER_CALLEE) +			ip = chain->ips[i]; +		else +			ip = chain->ips[chain->nr - i - 1]; + +		if (ip >= PERF_CONTEXT_MAX) { +			switch (ip) { +			case PERF_CONTEXT_HV: +				cpumode = PERF_RECORD_MISC_HYPERVISOR; +				break; +			case PERF_CONTEXT_KERNEL: +				cpumode = PERF_RECORD_MISC_KERNEL; +				break; +			case PERF_CONTEXT_USER: +				cpumode = PERF_RECORD_MISC_USER; +				break; +			default: +				pr_debug("invalid callchain context: " +					 "%"PRId64"\n", (s64) ip); + +				/* +				 * It seems the callchain is corrupted. +				 * Discard all. +				 */ +				zfree(&p); +				goto exit; +			} +			continue; +		} + +		tal.filtered = 0; +		thread__find_addr_location(al.thread, machine, cpumode, +					   MAP__FUNCTION, ip, &tal); + +		if (tal.sym) +			fprintf(f, "..... %016" PRIx64 " %s\n", ip, +				tal.sym->name); +		else +			fprintf(f, "..... %016" PRIx64 "\n", ip); +	} + +exit: +	fclose(f); + +	return p; +} + +typedef int (*tracepoint_handler)(struct timechart *tchart, +				  struct perf_evsel *evsel, +				  struct perf_sample *sample, +				  const char *backtrace); + +static int process_sample_event(struct perf_tool *tool, +				union perf_event *event,  				struct perf_sample *sample,  				struct perf_evsel *evsel, -				struct machine *machine __maybe_unused) +				struct machine *machine)  { +	struct timechart *tchart = container_of(tool, struct timechart, tool); +  	if (evsel->attr.sample_type & PERF_SAMPLE_TIME) { -		if (!first_time || first_time > sample->time) -			first_time = sample->time; -		if (last_time < sample->time) -			last_time = sample->time; +		if (!tchart->first_time || tchart->first_time > sample->time) +			tchart->first_time = sample->time; +		if (tchart->last_time < sample->time) +			tchart->last_time = sample->time;  	} -	if (sample->cpu > numcpus) -		numcpus = sample->cpu; - -	if (evsel->handler.func != NULL) { -		tracepoint_handler f = evsel->handler.func; -		return f(evsel, sample); +	if (evsel->handler != NULL) { +		tracepoint_handler f = evsel->handler; +		return f(tchart, evsel, sample, +			 cat_backtrace(event, sample, machine));  	}  	return 0;  }  static int -process_sample_cpu_idle(struct perf_evsel *evsel __maybe_unused, -			struct perf_sample *sample) +process_sample_cpu_idle(struct timechart *tchart __maybe_unused, +			struct perf_evsel *evsel, +			struct perf_sample *sample, +			const char *backtrace __maybe_unused)  { -	struct power_processor_entry *ppe = sample->raw_data; +	u32 state = perf_evsel__intval(evsel, sample, "state"); +	u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); -	if (ppe->state == (u32) PWR_EVENT_EXIT) -		c_state_end(ppe->cpu_id, sample->time); +	if (state == (u32)PWR_EVENT_EXIT) +		c_state_end(tchart, cpu_id, sample->time);  	else -		c_state_start(ppe->cpu_id, sample->time, ppe->state); +		c_state_start(cpu_id, sample->time, state);  	return 0;  }  static int -process_sample_cpu_frequency(struct perf_evsel *evsel __maybe_unused, -			     struct perf_sample *sample) +process_sample_cpu_frequency(struct timechart *tchart, +			     struct perf_evsel *evsel, +			     struct perf_sample *sample, +			     const char *backtrace __maybe_unused)  { -	struct power_processor_entry *ppe = sample->raw_data; +	u32 state = perf_evsel__intval(evsel, sample, "state"); +	u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); -	p_state_change(ppe->cpu_id, sample->time, ppe->state); +	p_state_change(tchart, cpu_id, sample->time, state);  	return 0;  }  static int -process_sample_sched_wakeup(struct perf_evsel *evsel __maybe_unused, -			    struct perf_sample *sample) +process_sample_sched_wakeup(struct timechart *tchart, +			    struct perf_evsel *evsel, +			    struct perf_sample *sample, +			    const char *backtrace)  { -	struct trace_entry *te = sample->raw_data; +	u8 flags = perf_evsel__intval(evsel, sample, "common_flags"); +	int waker = perf_evsel__intval(evsel, sample, "common_pid"); +	int wakee = perf_evsel__intval(evsel, sample, "pid"); -	sched_wakeup(sample->cpu, sample->time, sample->pid, te); +	sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace);  	return 0;  }  static int -process_sample_sched_switch(struct perf_evsel *evsel __maybe_unused, -			    struct perf_sample *sample) +process_sample_sched_switch(struct timechart *tchart, +			    struct perf_evsel *evsel, +			    struct perf_sample *sample, +			    const char *backtrace)  { -	struct trace_entry *te = sample->raw_data; +	int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"); +	int next_pid = perf_evsel__intval(evsel, sample, "next_pid"); +	u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state"); -	sched_switch(sample->cpu, sample->time, te); +	sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid, +		     prev_state, backtrace);  	return 0;  }  #ifdef SUPPORT_OLD_POWER_EVENTS  static int -process_sample_power_start(struct perf_evsel *evsel __maybe_unused, -			   struct perf_sample *sample) +process_sample_power_start(struct timechart *tchart __maybe_unused, +			   struct perf_evsel *evsel, +			   struct perf_sample *sample, +			   const char *backtrace __maybe_unused)  { -	struct power_entry_old *peo = sample->raw_data; +	u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); +	u64 value = perf_evsel__intval(evsel, sample, "value"); -	c_state_start(peo->cpu_id, sample->time, peo->value); +	c_state_start(cpu_id, sample->time, value);  	return 0;  }  static int -process_sample_power_end(struct perf_evsel *evsel __maybe_unused, -			 struct perf_sample *sample) +process_sample_power_end(struct timechart *tchart, +			 struct perf_evsel *evsel __maybe_unused, +			 struct perf_sample *sample, +			 const char *backtrace __maybe_unused)  { -	c_state_end(sample->cpu, sample->time); +	c_state_end(tchart, sample->cpu, sample->time);  	return 0;  }  static int -process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused, -			       struct perf_sample *sample) +process_sample_power_frequency(struct timechart *tchart, +			       struct perf_evsel *evsel, +			       struct perf_sample *sample, +			       const char *backtrace __maybe_unused)  { -	struct power_entry_old *peo = sample->raw_data; +	u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); +	u64 value = perf_evsel__intval(evsel, sample, "value"); -	p_state_change(peo->cpu_id, sample->time, peo->value); +	p_state_change(tchart, cpu_id, sample->time, value);  	return 0;  }  #endif /* SUPPORT_OLD_POWER_EVENTS */ @@ -567,12 +640,12 @@ process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused,   * After the last sample we need to wrap up the current C/P state   * and close out each CPU for these.   */ -static void end_sample_processing(void) +static void end_sample_processing(struct timechart *tchart)  {  	u64 cpu;  	struct power_event *pwr; -	for (cpu = 0; cpu <= numcpus; cpu++) { +	for (cpu = 0; cpu <= tchart->numcpus; cpu++) {  		/* C state */  #if 0  		pwr = zalloc(sizeof(*pwr)); @@ -581,12 +654,12 @@ static void end_sample_processing(void)  		pwr->state = cpus_cstate_state[cpu];  		pwr->start_time = cpus_cstate_start_times[cpu]; -		pwr->end_time = last_time; +		pwr->end_time = tchart->last_time;  		pwr->cpu = cpu;  		pwr->type = CSTATE; -		pwr->next = power_events; +		pwr->next = tchart->power_events; -		power_events = pwr; +		tchart->power_events = pwr;  #endif  		/* P state */ @@ -596,32 +669,32 @@ static void end_sample_processing(void)  		pwr->state = cpus_pstate_state[cpu];  		pwr->start_time = cpus_pstate_start_times[cpu]; -		pwr->end_time = last_time; +		pwr->end_time = tchart->last_time;  		pwr->cpu = cpu;  		pwr->type = PSTATE; -		pwr->next = power_events; +		pwr->next = tchart->power_events;  		if (!pwr->start_time) -			pwr->start_time = first_time; +			pwr->start_time = tchart->first_time;  		if (!pwr->state) -			pwr->state = min_freq; -		power_events = pwr; +			pwr->state = tchart->min_freq; +		tchart->power_events = pwr;  	}  }  /*   * Sort the pid datastructure   */ -static void sort_pids(void) +static void sort_pids(struct timechart *tchart)  {  	struct per_pid *new_list, *p, *cursor, *prev;  	/* sort by ppid first, then by pid, lowest to highest */  	new_list = NULL; -	while (all_data) { -		p = all_data; -		all_data = p->next; +	while (tchart->all_data) { +		p = tchart->all_data; +		tchart->all_data = p->next;  		p->next = NULL;  		if (new_list == NULL) { @@ -654,14 +727,14 @@ static void sort_pids(void)  				prev->next = p;  		}  	} -	all_data = new_list; +	tchart->all_data = new_list;  } -static void draw_c_p_states(void) +static void draw_c_p_states(struct timechart *tchart)  {  	struct power_event *pwr; -	pwr = power_events; +	pwr = tchart->power_events;  	/*  	 * two pass drawing so that the P state bars are on top of the C state blocks @@ -672,30 +745,30 @@ static void draw_c_p_states(void)  		pwr = pwr->next;  	} -	pwr = power_events; +	pwr = tchart->power_events;  	while (pwr) {  		if (pwr->type == PSTATE) {  			if (!pwr->state) -				pwr->state = min_freq; +				pwr->state = tchart->min_freq;  			svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);  		}  		pwr = pwr->next;  	}  } -static void draw_wakeups(void) +static void draw_wakeups(struct timechart *tchart)  {  	struct wake_event *we;  	struct per_pid *p;  	struct per_pidcomm *c; -	we = wake_events; +	we = tchart->wake_events;  	while (we) {  		int from = 0, to = 0;  		char *task_from = NULL, *task_to = NULL;  		/* locate the column of the waker and wakee */ -		p = all_data; +		p = tchart->all_data;  		while (p) {  			if (p->pid == we->waker || p->pid == we->wakee) {  				c = p->all; @@ -738,11 +811,12 @@ static void draw_wakeups(void)  		}  		if (we->waker == -1) -			svg_interrupt(we->time, to); +			svg_interrupt(we->time, to, we->backtrace);  		else if (from && to && abs(from - to) == 1) -			svg_wakeline(we->time, from, to); +			svg_wakeline(we->time, from, to, we->backtrace);  		else -			svg_partial_wakeline(we->time, from, task_from, to, task_to); +			svg_partial_wakeline(we->time, from, task_from, to, +					     task_to, we->backtrace);  		we = we->next;  		free(task_from); @@ -750,19 +824,25 @@ static void draw_wakeups(void)  	}  } -static void draw_cpu_usage(void) +static void draw_cpu_usage(struct timechart *tchart)  {  	struct per_pid *p;  	struct per_pidcomm *c;  	struct cpu_sample *sample; -	p = all_data; +	p = tchart->all_data;  	while (p) {  		c = p->all;  		while (c) {  			sample = c->samples;  			while (sample) { -				if (sample->type == TYPE_RUNNING) -					svg_process(sample->cpu, sample->start_time, sample->end_time, "sample", c->comm); +				if (sample->type == TYPE_RUNNING) { +					svg_process(sample->cpu, +						    sample->start_time, +						    sample->end_time, +						    p->pid, +						    c->comm, +						    sample->backtrace); +				}  				sample = sample->next;  			} @@ -772,16 +852,16 @@ static void draw_cpu_usage(void)  	}  } -static void draw_process_bars(void) +static void draw_process_bars(struct timechart *tchart)  {  	struct per_pid *p;  	struct per_pidcomm *c;  	struct cpu_sample *sample;  	int Y = 0; -	Y = 2 * numcpus + 2; +	Y = 2 * tchart->numcpus + 2; -	p = all_data; +	p = tchart->all_data;  	while (p) {  		c = p->all;  		while (c) { @@ -795,11 +875,20 @@ static void draw_process_bars(void)  			sample = c->samples;  			while (sample) {  				if (sample->type == TYPE_RUNNING) -					svg_sample(Y, sample->cpu, sample->start_time, sample->end_time); +					svg_running(Y, sample->cpu, +						    sample->start_time, +						    sample->end_time, +						    sample->backtrace);  				if (sample->type == TYPE_BLOCKED) -					svg_box(Y, sample->start_time, sample->end_time, "blocked"); +					svg_blocked(Y, sample->cpu, +						    sample->start_time, +						    sample->end_time, +						    sample->backtrace);  				if (sample->type == TYPE_WAITING) -					svg_waiting(Y, sample->start_time, sample->end_time); +					svg_waiting(Y, sample->cpu, +						    sample->start_time, +						    sample->end_time, +						    sample->backtrace);  				sample = sample->next;  			} @@ -852,21 +941,21 @@ static int passes_filter(struct per_pid *p, struct per_pidcomm *c)  	return 0;  } -static int determine_display_tasks_filtered(void) +static int determine_display_tasks_filtered(struct timechart *tchart)  {  	struct per_pid *p;  	struct per_pidcomm *c;  	int count = 0; -	p = all_data; +	p = tchart->all_data;  	while (p) {  		p->display = 0;  		if (p->start_time == 1) -			p->start_time = first_time; +			p->start_time = tchart->first_time;  		/* no exit marker, task kept running to the end */  		if (p->end_time == 0) -			p->end_time = last_time; +			p->end_time = tchart->last_time;  		c = p->all; @@ -874,7 +963,7 @@ static int determine_display_tasks_filtered(void)  			c->display = 0;  			if (c->start_time == 1) -				c->start_time = first_time; +				c->start_time = tchart->first_time;  			if (passes_filter(p, c)) {  				c->display = 1; @@ -883,7 +972,7 @@ static int determine_display_tasks_filtered(void)  			}  			if (c->end_time == 0) -				c->end_time = last_time; +				c->end_time = tchart->last_time;  			c = c->next;  		} @@ -892,25 +981,25 @@ static int determine_display_tasks_filtered(void)  	return count;  } -static int determine_display_tasks(u64 threshold) +static int determine_display_tasks(struct timechart *tchart, u64 threshold)  {  	struct per_pid *p;  	struct per_pidcomm *c;  	int count = 0;  	if (process_filter) -		return determine_display_tasks_filtered(); +		return determine_display_tasks_filtered(tchart); -	p = all_data; +	p = tchart->all_data;  	while (p) {  		p->display = 0;  		if (p->start_time == 1) -			p->start_time = first_time; +			p->start_time = tchart->first_time;  		/* no exit marker, task kept running to the end */  		if (p->end_time == 0) -			p->end_time = last_time; -		if (p->total_time >= threshold && !power_only) +			p->end_time = tchart->last_time; +		if (p->total_time >= threshold)  			p->display = 1;  		c = p->all; @@ -919,15 +1008,15 @@ static int determine_display_tasks(u64 threshold)  			c->display = 0;  			if (c->start_time == 1) -				c->start_time = first_time; +				c->start_time = tchart->first_time; -			if (c->total_time >= threshold && !power_only) { +			if (c->total_time >= threshold) {  				c->display = 1;  				count++;  			}  			if (c->end_time == 0) -				c->end_time = last_time; +				c->end_time = tchart->last_time;  			c = c->next;  		} @@ -940,45 +1029,77 @@ static int determine_display_tasks(u64 threshold)  #define TIME_THRESH 10000000 -static void write_svg_file(const char *filename) +static void write_svg_file(struct timechart *tchart, const char *filename)  {  	u64 i;  	int count; +	int thresh = TIME_THRESH; -	numcpus++; - +	if (tchart->power_only) +		tchart->proc_num = 0; -	count = determine_display_tasks(TIME_THRESH); +	/* We'd like to show at least proc_num tasks; +	 * be less picky if we have fewer */ +	do { +		count = determine_display_tasks(tchart, thresh); +		thresh /= 10; +	} while (!process_filter && thresh && count < tchart->proc_num); -	/* We'd like to show at least 15 tasks; be less picky if we have fewer */ -	if (count < 15) -		count = determine_display_tasks(TIME_THRESH / 10); +	if (!tchart->proc_num) +		count = 0; -	open_svg(filename, numcpus, count, first_time, last_time); +	open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time);  	svg_time_grid();  	svg_legenda(); -	for (i = 0; i < numcpus; i++) -		svg_cpu_box(i, max_freq, turbo_frequency); +	for (i = 0; i < tchart->numcpus; i++) +		svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency); -	draw_cpu_usage(); -	draw_process_bars(); -	draw_c_p_states(); -	draw_wakeups(); +	draw_cpu_usage(tchart); +	if (tchart->proc_num) +		draw_process_bars(tchart); +	if (!tchart->tasks_only) +		draw_c_p_states(tchart); +	if (tchart->proc_num) +		draw_wakeups(tchart);  	svg_close();  } -static int __cmd_timechart(const char *output_name) +static int process_header(struct perf_file_section *section __maybe_unused, +			  struct perf_header *ph, +			  int feat, +			  int fd __maybe_unused, +			  void *data) +{ +	struct timechart *tchart = data; + +	switch (feat) { +	case HEADER_NRCPUS: +		tchart->numcpus = ph->env.nr_cpus_avail; +		break; + +	case HEADER_CPU_TOPOLOGY: +		if (!tchart->topology) +			break; + +		if (svg_build_topology_map(ph->env.sibling_cores, +					   ph->env.nr_sibling_cores, +					   ph->env.sibling_threads, +					   ph->env.nr_sibling_threads)) +			fprintf(stderr, "problem building topology\n"); +		break; + +	default: +		break; +	} + +	return 0; +} + +static int __cmd_timechart(struct timechart *tchart, const char *output_name)  { -	struct perf_tool perf_timechart = { -		.comm		 = process_comm_event, -		.fork		 = process_fork_event, -		.exit		 = process_exit_event, -		.sample		 = process_sample_event, -		.ordered_samples = true, -	};  	const struct perf_evsel_str_handler power_tracepoints[] = {  		{ "power:cpu_idle",		process_sample_cpu_idle },  		{ "power:cpu_frequency",	process_sample_cpu_frequency }, @@ -990,13 +1111,23 @@ static int __cmd_timechart(const char *output_name)  		{ "power:power_frequency",	process_sample_power_frequency },  #endif  	}; -	struct perf_session *session = perf_session__new(input_name, O_RDONLY, -							 0, false, &perf_timechart); +	struct perf_data_file file = { +		.path = input_name, +		.mode = PERF_DATA_MODE_READ, +	}; + +	struct perf_session *session = perf_session__new(&file, false, +							 &tchart->tool);  	int ret = -EINVAL;  	if (session == NULL)  		return -ENOMEM; +	(void)perf_header__process_sections(&session->header, +					    perf_data_file__fd(session->file), +					    tchart, +					    process_header); +  	if (!perf_session__has_traces(session, "timechart record"))  		goto out_delete; @@ -1006,69 +1137,111 @@ static int __cmd_timechart(const char *output_name)  		goto out_delete;  	} -	ret = perf_session__process_events(session, &perf_timechart); +	ret = perf_session__process_events(session, &tchart->tool);  	if (ret)  		goto out_delete; -	end_sample_processing(); +	end_sample_processing(tchart); -	sort_pids(); +	sort_pids(tchart); -	write_svg_file(output_name); +	write_svg_file(tchart, output_name);  	pr_info("Written %2.1f seconds of trace to %s.\n", -		(last_time - first_time) / 1000000000.0, output_name); +		(tchart->last_time - tchart->first_time) / 1000000000.0, output_name);  out_delete:  	perf_session__delete(session);  	return ret;  } -static int __cmd_record(int argc, const char **argv) +static int timechart__record(struct timechart *tchart, int argc, const char **argv)  { -#ifdef SUPPORT_OLD_POWER_EVENTS -	const char * const record_old_args[] = { +	unsigned int rec_argc, i, j; +	const char **rec_argv; +	const char **p; +	unsigned int record_elems; + +	const char * const common_args[] = {  		"record", "-a", "-R", "-c", "1", +	}; +	unsigned int common_args_nr = ARRAY_SIZE(common_args); + +	const char * const backtrace_args[] = { +		"-g", +	}; +	unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args); + +	const char * const power_args[] = { +		"-e", "power:cpu_frequency", +		"-e", "power:cpu_idle", +	}; +	unsigned int power_args_nr = ARRAY_SIZE(power_args); + +	const char * const old_power_args[] = { +#ifdef SUPPORT_OLD_POWER_EVENTS  		"-e", "power:power_start",  		"-e", "power:power_end",  		"-e", "power:power_frequency", -		"-e", "sched:sched_wakeup", -		"-e", "sched:sched_switch", -	};  #endif -	const char * const record_new_args[] = { -		"record", "-a", "-R", "-c", "1", -		"-e", "power:cpu_frequency", -		"-e", "power:cpu_idle", +	}; +	unsigned int old_power_args_nr = ARRAY_SIZE(old_power_args); + +	const char * const tasks_args[] = {  		"-e", "sched:sched_wakeup",  		"-e", "sched:sched_switch",  	}; -	unsigned int rec_argc, i, j; -	const char **rec_argv; -	const char * const *record_args = record_new_args; -	unsigned int record_elems = ARRAY_SIZE(record_new_args); +	unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args);  #ifdef SUPPORT_OLD_POWER_EVENTS  	if (!is_valid_tracepoint("power:cpu_idle") &&  	    is_valid_tracepoint("power:power_start")) {  		use_old_power_events = 1; -		record_args = record_old_args; -		record_elems = ARRAY_SIZE(record_old_args); +		power_args_nr = 0; +	} else { +		old_power_args_nr = 0;  	}  #endif -	rec_argc = record_elems + argc - 1; +	if (tchart->power_only) +		tasks_args_nr = 0; + +	if (tchart->tasks_only) { +		power_args_nr = 0; +		old_power_args_nr = 0; +	} + +	if (!tchart->with_backtrace) +		backtrace_args_no = 0; + +	record_elems = common_args_nr + tasks_args_nr + +		power_args_nr + old_power_args_nr + backtrace_args_no; + +	rec_argc = record_elems + argc;  	rec_argv = calloc(rec_argc + 1, sizeof(char *));  	if (rec_argv == NULL)  		return -ENOMEM; -	for (i = 0; i < record_elems; i++) -		rec_argv[i] = strdup(record_args[i]); +	p = rec_argv; +	for (i = 0; i < common_args_nr; i++) +		*p++ = strdup(common_args[i]); + +	for (i = 0; i < backtrace_args_no; i++) +		*p++ = strdup(backtrace_args[i]); + +	for (i = 0; i < tasks_args_nr; i++) +		*p++ = strdup(tasks_args[i]); + +	for (i = 0; i < power_args_nr; i++) +		*p++ = strdup(power_args[i]); -	for (j = 1; j < (unsigned int)argc; j++, i++) -		rec_argv[i] = argv[j]; +	for (i = 0; i < old_power_args_nr; i++) +		*p++ = strdup(old_power_args[i]); -	return cmd_record(i, rec_argv, NULL); +	for (j = 0; j < (unsigned int)argc; j++) +		*p++ = argv[j]; + +	return cmd_record(rec_argc, rec_argv, NULL);  }  static int @@ -1080,20 +1253,56 @@ parse_process(const struct option *opt __maybe_unused, const char *arg,  	return 0;  } +static int +parse_highlight(const struct option *opt __maybe_unused, const char *arg, +		int __maybe_unused unset) +{ +	unsigned long duration = strtoul(arg, NULL, 0); + +	if (svg_highlight || svg_highlight_name) +		return -1; + +	if (duration) +		svg_highlight = duration; +	else +		svg_highlight_name = strdup(arg); + +	return 0; +} +  int cmd_timechart(int argc, const char **argv,  		  const char *prefix __maybe_unused)  { +	struct timechart tchart = { +		.tool = { +			.comm		 = process_comm_event, +			.fork		 = process_fork_event, +			.exit		 = process_exit_event, +			.sample		 = process_sample_event, +			.ordered_samples = true, +		}, +		.proc_num = 15, +	};  	const char *output_name = "output.svg"; -	const struct option options[] = { +	const struct option timechart_options[] = {  	OPT_STRING('i', "input", &input_name, "file", "input file name"),  	OPT_STRING('o', "output", &output_name, "file", "output file name"),  	OPT_INTEGER('w', "width", &svg_page_width, "page width"), -	OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"), +	OPT_CALLBACK(0, "highlight", NULL, "duration or task name", +		      "highlight tasks. Pass duration in ns or process name.", +		       parse_highlight), +	OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), +	OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, +		    "output processes data only"),  	OPT_CALLBACK('p', "process", NULL, "process",  		      "process selector. Pass a pid or process name.",  		       parse_process),  	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",  		    "Look for files with symbols relative to this directory"), +	OPT_INTEGER('n', "proc-num", &tchart.proc_num, +		    "min. number of tasks to print"), +	OPT_BOOLEAN('t', "topology", &tchart.topology, +		    "sort CPUs according to topology"),  	OPT_END()  	};  	const char * const timechart_usage[] = { @@ -1101,17 +1310,41 @@ int cmd_timechart(int argc, const char **argv,  		NULL  	}; -	argc = parse_options(argc, argv, options, timechart_usage, +	const struct option record_options[] = { +	OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), +	OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, +		    "output processes data only"), +	OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), +	OPT_END() +	}; +	const char * const record_usage[] = { +		"perf timechart record [<options>]", +		NULL +	}; +	argc = parse_options(argc, argv, timechart_options, timechart_usage,  			PARSE_OPT_STOP_AT_NON_OPTION); +	if (tchart.power_only && tchart.tasks_only) { +		pr_err("-P and -T options cannot be used at the same time.\n"); +		return -1; +	} +  	symbol__init(); -	if (argc && !strncmp(argv[0], "rec", 3)) -		return __cmd_record(argc, argv); -	else if (argc) -		usage_with_options(timechart_usage, options); +	if (argc && !strncmp(argv[0], "rec", 3)) { +		argc = parse_options(argc, argv, record_options, record_usage, +				     PARSE_OPT_STOP_AT_NON_OPTION); + +		if (tchart.power_only && tchart.tasks_only) { +			pr_err("-P and -T options cannot be used at the same time.\n"); +			return -1; +		} + +		return timechart__record(&tchart, argc, argv); +	} else if (argc) +		usage_with_options(timechart_usage, timechart_options);  	setup_pager(); -	return __cmd_timechart(output_name); +	return __cmd_timechart(&tchart, output_name);  } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 212214162bb..377971dc89a 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -176,7 +176,7 @@ static void perf_top__record_precise_ip(struct perf_top *top,  {  	struct annotation *notes;  	struct symbol *sym; -	int err; +	int err = 0;  	if (he == NULL || he->ms.sym == NULL ||  	    ((top->sym_filter_entry == NULL || @@ -189,21 +189,28 @@ static void perf_top__record_precise_ip(struct perf_top *top,  	if (pthread_mutex_trylock(¬es->lock))  		return; -	if (notes->src == NULL && symbol__alloc_hist(sym) < 0) { -		pthread_mutex_unlock(¬es->lock); -		pr_err("Not enough memory for annotating '%s' symbol!\n", -		       sym->name); -		sleep(1); -		return; -	} -  	ip = he->ms.map->map_ip(he->ms.map, ip); -	err = symbol__inc_addr_samples(sym, he->ms.map, counter, ip); + +	if (ui__has_annotation()) +		err = hist_entry__inc_addr_samples(he, counter, ip);  	pthread_mutex_unlock(¬es->lock); +	/* +	 * This function is now called with he->hists->lock held. +	 * Release it before going to sleep. +	 */ +	pthread_mutex_unlock(&he->hists->lock); +  	if (err == -ERANGE && !he->ms.map->erange_warned)  		ui__warn_map_erange(he->ms.map, sym, ip); +	else if (err == -ENOMEM) { +		pr_err("Not enough memory for annotating '%s' symbol!\n", +		       sym->name); +		sleep(1); +	} + +	pthread_mutex_lock(&he->hists->lock);  }  static void perf_top__show_details(struct perf_top *top) @@ -239,24 +246,6 @@ out_unlock:  	pthread_mutex_unlock(¬es->lock);  } -static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, -						     struct addr_location *al, -						     struct perf_sample *sample) -{ -	struct hist_entry *he; - -	pthread_mutex_lock(&evsel->hists.lock); -	he = __hists__add_entry(&evsel->hists, al, NULL, sample->period, -				sample->weight); -	pthread_mutex_unlock(&evsel->hists.lock); - -	if (he == NULL) -		return NULL; - -	hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); -	return he; -} -  static void perf_top__print_sym_table(struct perf_top *top)  {  	char bf[160]; @@ -287,7 +276,7 @@ static void perf_top__print_sym_table(struct perf_top *top)  		return;  	} -	hists__collapse_resort(&top->sym_evsel->hists); +	hists__collapse_resort(&top->sym_evsel->hists, NULL);  	hists__output_resort(&top->sym_evsel->hists);  	hists__decay_entries(&top->sym_evsel->hists,  			     top->hide_user_symbols, @@ -485,7 +474,7 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)  				fprintf(stderr, "\nAvailable events:"); -				list_for_each_entry(top->sym_evsel, &top->evlist->entries, node) +				evlist__for_each(top->evlist, top->sym_evsel)  					fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, perf_evsel__name(top->sym_evsel));  				prompt_integer(&counter, "Enter details event counter"); @@ -496,7 +485,7 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)  					sleep(1);  					break;  				} -				list_for_each_entry(top->sym_evsel, &top->evlist->entries, node) +				evlist__for_each(top->evlist, top->sym_evsel)  					if (top->sym_evsel->idx == counter)  						break;  			} else @@ -553,7 +542,7 @@ static void perf_top__sort_new_samples(void *arg)  	if (t->evlist->selected != NULL)  		t->sym_evsel = t->evlist->selected; -	hists__collapse_resort(&t->sym_evsel->hists); +	hists__collapse_resort(&t->sym_evsel->hists, NULL);  	hists__output_resort(&t->sym_evsel->hists);  	hists__decay_entries(&t->sym_evsel->hists,  			     t->hide_user_symbols, @@ -578,7 +567,7 @@ static void *display_thread_tui(void *arg)  	 * Zooming in/out UIDs. For now juse use whatever the user passed  	 * via --uid.  	 */ -	list_for_each_entry(pos, &top->evlist->entries, node) +	evlist__for_each(top->evlist, pos)  		pos->hists.uid_filter_str = top->record_opts.target.uid_str;  	perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent, @@ -634,26 +623,9 @@ repeat:  	return NULL;  } -/* Tag samples to be skipped. */ -static const char *skip_symbols[] = { -	"intel_idle", -	"default_idle", -	"native_safe_halt", -	"cpu_idle", -	"enter_idle", -	"exit_idle", -	"mwait_idle", -	"mwait_idle_with_hints", -	"poll_idle", -	"ppc64_runlatch_off", -	"pseries_dedicated_idle_sleep", -	NULL -}; -  static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)  {  	const char *name = sym->name; -	int i;  	/*  	 * ppc64 uses function descriptors and appends a '.' to the @@ -671,11 +643,27 @@ static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)  	    strstr(name, "_text_end"))  		return 1; -	for (i = 0; skip_symbols[i]; i++) { -		if (!strcmp(skip_symbols[i], name)) { -			sym->ignore = true; -			break; -		} +	if (symbol__is_idle(sym)) +		sym->ignore = true; + +	return 0; +} + +static int hist_iter__top_callback(struct hist_entry_iter *iter, +				   struct addr_location *al, bool single, +				   void *arg) +{ +	struct perf_top *top = arg; +	struct hist_entry *he = iter->he; +	struct perf_evsel *evsel = iter->evsel; + +	if (sort__has_sym && single) { +		u64 ip = al->addr; + +		if (al->map) +			ip = al->map->unmap_ip(al->map, ip); + +		perf_top__record_precise_ip(top, he, evsel->idx, ip);  	}  	return 0; @@ -688,8 +676,6 @@ static void perf_event__process_sample(struct perf_tool *tool,  				       struct machine *machine)  {  	struct perf_top *top = container_of(tool, struct perf_top, tool); -	struct symbol *parent = NULL; -	u64 ip = sample->ip;  	struct addr_location al;  	int err; @@ -716,8 +702,7 @@ static void perf_event__process_sample(struct perf_tool *tool,  	if (event->header.misc & PERF_RECORD_MISC_EXACT_IP)  		top->exact_samples++; -	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0 || -	    al.filtered) +	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0)  		return;  	if (!top->kptr_restrict_warned && @@ -765,32 +750,23 @@ static void perf_event__process_sample(struct perf_tool *tool,  	}  	if (al.sym == NULL || !al.sym->ignore) { -		struct hist_entry *he; - -		if ((sort__has_parent || symbol_conf.use_callchain) && -		    sample->callchain) { -			err = machine__resolve_callchain(machine, evsel, -							 al.thread, sample, -							 &parent, &al); -			if (err) -				return; -		} +		struct hist_entry_iter iter = { +			.add_entry_cb = hist_iter__top_callback, +		}; -		he = perf_evsel__add_hist_entry(evsel, &al, sample); -		if (he == NULL) { -			pr_err("Problem incrementing symbol period, skipping event\n"); -			return; -		} +		if (symbol_conf.cumulate_callchain) +			iter.ops = &hist_iter_cumulative; +		else +			iter.ops = &hist_iter_normal; -		if (symbol_conf.use_callchain) { -			err = callchain_append(he->callchain, &callchain_cursor, -					       sample->period); -			if (err) -				return; -		} +		pthread_mutex_lock(&evsel->hists.lock); -		if (sort__has_sym) -			perf_top__record_precise_ip(top, he, evsel->idx, ip); +		err = hist_entry_iter__add(&iter, &al, evsel, sample, +					   top->max_stack, top); +		if (err < 0) +			pr_err("Problem incrementing symbol period, skipping event\n"); + +		pthread_mutex_unlock(&evsel->hists.lock);  	}  	return; @@ -810,7 +786,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)  		ret = perf_evlist__parse_sample(top->evlist, event, &sample);  		if (ret) {  			pr_err("Can't parse sample, err = %d\n", ret); -			continue; +			goto next_event;  		}  		evsel = perf_evlist__id2evsel(session->evlist, sample.id); @@ -825,13 +801,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)  		case PERF_RECORD_MISC_USER:  			++top->us_samples;  			if (top->hide_user_symbols) -				continue; +				goto next_event;  			machine = &session->machines.host;  			break;  		case PERF_RECORD_MISC_KERNEL:  			++top->kernel_samples;  			if (top->hide_kernel_symbols) -				continue; +				goto next_event;  			machine = &session->machines.host;  			break;  		case PERF_RECORD_MISC_GUEST_KERNEL: @@ -847,7 +823,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)  			 */  			/* Fall thru */  		default: -			continue; +			goto next_event;  		} @@ -856,9 +832,11 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)  						   &sample, machine);  		} else if (event->header.type < PERF_RECORD_MAX) {  			hists__inc_nr_events(&evsel->hists, event->header.type); -			machine__process_event(machine, event); +			machine__process_event(machine, event, &sample);  		} else  			++session->stats.nr_unknown_events; +next_event: +		perf_evlist__mmap_consume(top->evlist, idx);  	}  } @@ -875,11 +853,11 @@ static int perf_top__start_counters(struct perf_top *top)  	char msg[512];  	struct perf_evsel *counter;  	struct perf_evlist *evlist = top->evlist; -	struct perf_record_opts *opts = &top->record_opts; +	struct record_opts *opts = &top->record_opts;  	perf_evlist__config(evlist, opts); -	list_for_each_entry(counter, &evlist->entries, node) { +	evlist__for_each(evlist, counter) {  try_again:  		if (perf_evsel__open(counter, top->evlist->cpus,  				     top->evlist->threads) < 0) { @@ -927,14 +905,11 @@ static int perf_top__setup_sample_type(struct perf_top *top __maybe_unused)  static int __cmd_top(struct perf_top *top)  { -	struct perf_record_opts *opts = &top->record_opts; +	struct record_opts *opts = &top->record_opts;  	pthread_t thread;  	int ret; -	/* -	 * FIXME: perf_session__new should allow passing a O_MMAP, so that all this -	 * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. -	 */ -	top->session = perf_session__new(NULL, O_WRONLY, false, false, NULL); + +	top->session = perf_session__new(NULL, false, NULL);  	if (top->session == NULL)  		return -ENOMEM; @@ -950,14 +925,8 @@ static int __cmd_top(struct perf_top *top)  	if (ret)  		goto out_delete; -	if (perf_target__has_task(&opts->target)) -		perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, -						  perf_event__process, -						  &top->session->machines.host); -	else -		perf_event__synthesize_threads(&top->tool, perf_event__process, -					       &top->session->machines.host); - +	machine__synthesize_threads(&top->session->machines.host, &opts->target, +				    top->evlist->threads, false);  	ret = perf_top__start_counters(top);  	if (ret)  		goto out_delete; @@ -973,7 +942,7 @@ static int __cmd_top(struct perf_top *top)  	 * XXX 'top' still doesn't start workloads like record, trace, but should,  	 * so leave the check here.  	 */ -        if (!perf_target__none(&opts->target)) +        if (!target__none(&opts->target))                  perf_evlist__enable(top->evlist);  	/* Wait for a minimal set of events before starting the snapshot */ @@ -1016,19 +985,33 @@ out_delete:  }  static int -parse_callchain_opt(const struct option *opt, const char *arg, int unset) +callchain_opt(const struct option *opt, const char *arg, int unset)  { -	/* -	 * --no-call-graph -	 */ -	if (unset) -		return 0; -  	symbol_conf.use_callchain = true; +	return record_callchain_opt(opt, arg, unset); +} +static int +parse_callchain_opt(const struct option *opt, const char *arg, int unset) +{ +	symbol_conf.use_callchain = true;  	return record_parse_callchain_opt(opt, arg, unset);  } +static int perf_top_config(const char *var, const char *value, void *cb) +{ +	struct perf_top *top = cb; + +	if (!strcmp(var, "top.call-graph")) +		return record_parse_callchain(value, &top->record_opts); +	if (!strcmp(var, "top.children")) { +		symbol_conf.cumulate_callchain = perf_config_bool(var, value); +		return 0; +	} + +	return perf_default_config(var, value, cb); +} +  static int  parse_percent_limit(const struct option *opt, const char *arg,  		    int unset __maybe_unused) @@ -1041,7 +1024,7 @@ parse_percent_limit(const struct option *opt, const char *arg,  int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)  { -	int status; +	int status = -1;  	char errbuf[BUFSIZ];  	struct perf_top top = {  		.count_filter	     = 5, @@ -1051,14 +1034,15 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)  			.user_freq	= UINT_MAX,  			.user_interval	= ULLONG_MAX,  			.freq		= 4000, /* 4 KHz */ -			.target		     = { +			.target		= {  				.uses_mmap   = true,  			},  		}, +		.max_stack	     = PERF_MAX_STACK_DEPTH,  		.sym_pcnt_filter     = 5,  	}; -	struct perf_record_opts *opts = &top.record_opts; -	struct perf_target *target = &opts->target; +	struct record_opts *opts = &top.record_opts; +	struct target *target = &opts->target;  	const struct option options[] = {  	OPT_CALLBACK('e', "event", &top.evlist, "event",  		     "event selector. use 'perf list' to list available events", @@ -1074,10 +1058,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)  		    "list of cpus to monitor"),  	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,  		   "file", "vmlinux pathname"), +	OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux, +		    "don't load vmlinux even if found"),  	OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols,  		    "hide kernel symbols"), -	OPT_UINTEGER('m', "mmap-pages", &opts->mmap_pages, -		     "number of mmap data pages"), +	OPT_CALLBACK('m', "mmap-pages", &opts->mmap_pages, "pages", +		     "number of mmap data pages", +		     perf_evlist__parse_mmap_pages),  	OPT_INTEGER('r', "realtime", &top.realtime_prio,  		    "collect data with this RT SCHED_FIFO priority"),  	OPT_INTEGER('d', "delay", &top.delay_secs, @@ -1086,7 +1073,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)  			    "dump the symbol table used for profiling"),  	OPT_INTEGER('f', "count-filter", &top.count_filter,  		    "only display functions with more events than this"), -	OPT_BOOLEAN('g', "group", &opts->group, +	OPT_BOOLEAN(0, "group", &opts->group,  			    "put the counters into a counter group"),  	OPT_BOOLEAN('i', "no-inherit", &opts->no_inherit,  		    "child tasks do not inherit counters"), @@ -1103,12 +1090,23 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)  	OPT_INCR('v', "verbose", &verbose,  		    "be more verbose (show counter open errors, etc)"),  	OPT_STRING('s', "sort", &sort_order, "key[,key2...]", -		   "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight"), +		   "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." +		   " Please refer the man page for the complete list."), +	OPT_STRING(0, "fields", &field_order, "key[,keys...]", +		   "output field(s): overhead, period, sample plus all of sort keys"),  	OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,  		    "Show a column with the number of samples"), -	OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, -			     "mode[,dump_size]", record_callchain_help, -			     &parse_callchain_opt, "fp"), +	OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts, +			   NULL, "enables call-graph recording", +			   &callchain_opt), +	OPT_CALLBACK(0, "call-graph", &top.record_opts, +		     "mode[,dump_size]", record_callchain_help, +		     &parse_callchain_opt), +	OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain, +		    "Accumulate callchains of children and show total overhead as well"), +	OPT_INTEGER(0, "max-stack", &top.max_stack, +		    "Set the maximum stack depth when parsing the callchain. " +		    "Default: " __stringify(PERF_MAX_STACK_DEPTH)),  	OPT_CALLBACK(0, "ignore-callees", NULL, "regex",  		   "ignore callees of these functions in call graphs",  		   report_parse_ignore_callees_opt), @@ -1131,6 +1129,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)  	OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"),  	OPT_CALLBACK(0, "percent-limit", &top, "percent",  		     "Don't show entries under that percent", parse_percent_limit), +	OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", +		     "How to display percentage of filtered entries", parse_filter_percentage),  	OPT_END()  	};  	const char * const top_usage[] = { @@ -1142,19 +1142,25 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)  	if (top.evlist == NULL)  		return -ENOMEM; +	perf_config(perf_top_config, &top); +  	argc = parse_options(argc, argv, options, top_usage, 0);  	if (argc)  		usage_with_options(top_usage, options); -	if (sort_order == default_sort_order) -		sort_order = "dso,symbol"; - -	if (setup_sorting() < 0) -		usage_with_options(top_usage, options); - +	sort__mode = SORT_MODE__TOP;  	/* display thread wants entries to be collapsed in a different tree */  	sort__need_collapse = 1; +	if (setup_sorting() < 0) { +		if (sort_order) +			parse_options_usage(top_usage, options, "s", 1); +		if (field_order) +			parse_options_usage(sort_order ? NULL : top_usage, +					    options, "fields", 0); +		goto out_delete_evlist; +	} +  	if (top.use_stdio)  		use_browser = 0;  	else if (top.use_tui) @@ -1162,24 +1168,24 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)  	setup_browser(false); -	status = perf_target__validate(target); +	status = target__validate(target);  	if (status) { -		perf_target__strerror(target, status, errbuf, BUFSIZ); -		ui__warning("%s", errbuf); +		target__strerror(target, status, errbuf, BUFSIZ); +		ui__warning("%s\n", errbuf);  	} -	status = perf_target__parse_uid(target); +	status = target__parse_uid(target);  	if (status) {  		int saved_errno = errno; -		perf_target__strerror(target, status, errbuf, BUFSIZ); -		ui__error("%s", errbuf); +		target__strerror(target, status, errbuf, BUFSIZ); +		ui__error("%s\n", errbuf);  		status = -saved_errno;  		goto out_delete_evlist;  	} -	if (perf_target__none(target)) +	if (target__none(target))  		target->system_wide = true;  	if (perf_evlist__create_maps(top.evlist, target) < 0) @@ -1188,7 +1194,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)  	if (!top.evlist->nr_entries &&  	    perf_evlist__add_default(top.evlist) < 0) {  		ui__error("Not enough memory for event selector list\n"); -		goto out_delete_maps; +		goto out_delete_evlist;  	}  	symbol_conf.nr_events = top.evlist->nr_entries; @@ -1196,26 +1202,18 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)  	if (top.delay_secs < 1)  		top.delay_secs = 1; -	if (opts->user_interval != ULLONG_MAX) -		opts->default_interval = opts->user_interval; -	if (opts->user_freq != UINT_MAX) -		opts->freq = opts->user_freq; - -	/* -	 * User specified count overrides default frequency. -	 */ -	if (opts->default_interval) -		opts->freq = 0; -	else if (opts->freq) { -		opts->default_interval = opts->freq; -	} else { -		ui__error("frequency and count are zero, aborting\n"); +	if (record_opts__config(opts)) {  		status = -EINVAL; -		goto out_delete_maps; +		goto out_delete_evlist;  	}  	top.sym_evsel = perf_evlist__first(top.evlist); +	if (!symbol_conf.use_callchain) { +		symbol_conf.cumulate_callchain = false; +		perf_hpp__cancel_cumulate(); +	} +  	symbol_conf.priv_size = sizeof(struct annotation);  	symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); @@ -1236,8 +1234,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)  	status = __cmd_top(&top); -out_delete_maps: -	perf_evlist__delete_maps(top.evlist);  out_delete_evlist:  	perf_evlist__delete(top.evlist); diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 71aa3e35406..f954c26de23 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -10,9 +10,13 @@  #include "util/strlist.h"  #include "util/intlist.h"  #include "util/thread_map.h" +#include "util/stat.h" +#include "trace-event.h" +#include "util/parse-events.h"  #include <libaudit.h>  #include <stdlib.h> +#include <sys/eventfd.h>  #include <sys/mman.h>  #include <linux/futex.h> @@ -33,49 +37,301 @@  # define MADV_UNMERGEABLE	13  #endif -static size_t syscall_arg__scnprintf_hex(char *bf, size_t size, -					 unsigned long arg, -					 u8 arg_idx __maybe_unused, -					 u8 *arg_mask __maybe_unused) +#ifndef EFD_SEMAPHORE +# define EFD_SEMAPHORE		1 +#endif + +struct tp_field { +	int offset; +	union { +		u64 (*integer)(struct tp_field *field, struct perf_sample *sample); +		void *(*pointer)(struct tp_field *field, struct perf_sample *sample); +	}; +}; + +#define TP_UINT_FIELD(bits) \ +static u64 tp_field__u##bits(struct tp_field *field, struct perf_sample *sample) \ +{ \ +	return *(u##bits *)(sample->raw_data + field->offset); \ +} + +TP_UINT_FIELD(8); +TP_UINT_FIELD(16); +TP_UINT_FIELD(32); +TP_UINT_FIELD(64); + +#define TP_UINT_FIELD__SWAPPED(bits) \ +static u64 tp_field__swapped_u##bits(struct tp_field *field, struct perf_sample *sample) \ +{ \ +	u##bits value = *(u##bits *)(sample->raw_data + field->offset); \ +	return bswap_##bits(value);\ +} + +TP_UINT_FIELD__SWAPPED(16); +TP_UINT_FIELD__SWAPPED(32); +TP_UINT_FIELD__SWAPPED(64); + +static int tp_field__init_uint(struct tp_field *field, +			       struct format_field *format_field, +			       bool needs_swap)  { -	return scnprintf(bf, size, "%#lx", arg); +	field->offset = format_field->offset; + +	switch (format_field->size) { +	case 1: +		field->integer = tp_field__u8; +		break; +	case 2: +		field->integer = needs_swap ? tp_field__swapped_u16 : tp_field__u16; +		break; +	case 4: +		field->integer = needs_swap ? tp_field__swapped_u32 : tp_field__u32; +		break; +	case 8: +		field->integer = needs_swap ? tp_field__swapped_u64 : tp_field__u64; +		break; +	default: +		return -1; +	} + +	return 0;  } -#define SCA_HEX syscall_arg__scnprintf_hex +static void *tp_field__ptr(struct tp_field *field, struct perf_sample *sample) +{ +	return sample->raw_data + field->offset; +} -static size_t syscall_arg__scnprintf_whence(char *bf, size_t size, -					    unsigned long arg, -					    u8 arg_idx __maybe_unused, -					    u8 *arg_mask __maybe_unused) +static int tp_field__init_ptr(struct tp_field *field, struct format_field *format_field)  { -	int whence = arg; +	field->offset = format_field->offset; +	field->pointer = tp_field__ptr; +	return 0; +} -	switch (whence) { -#define P_WHENCE(n) case SEEK_##n: return scnprintf(bf, size, #n) -	P_WHENCE(SET); -	P_WHENCE(CUR); -	P_WHENCE(END); -#ifdef SEEK_DATA -	P_WHENCE(DATA); -#endif -#ifdef SEEK_HOLE -	P_WHENCE(HOLE); -#endif -#undef P_WHENCE -	default: break; +struct syscall_tp { +	struct tp_field id; +	union { +		struct tp_field args, ret; +	}; +}; + +static int perf_evsel__init_tp_uint_field(struct perf_evsel *evsel, +					  struct tp_field *field, +					  const char *name) +{ +	struct format_field *format_field = perf_evsel__field(evsel, name); + +	if (format_field == NULL) +		return -1; + +	return tp_field__init_uint(field, format_field, evsel->needs_swap); +} + +#define perf_evsel__init_sc_tp_uint_field(evsel, name) \ +	({ struct syscall_tp *sc = evsel->priv;\ +	   perf_evsel__init_tp_uint_field(evsel, &sc->name, #name); }) + +static int perf_evsel__init_tp_ptr_field(struct perf_evsel *evsel, +					 struct tp_field *field, +					 const char *name) +{ +	struct format_field *format_field = perf_evsel__field(evsel, name); + +	if (format_field == NULL) +		return -1; + +	return tp_field__init_ptr(field, format_field); +} + +#define perf_evsel__init_sc_tp_ptr_field(evsel, name) \ +	({ struct syscall_tp *sc = evsel->priv;\ +	   perf_evsel__init_tp_ptr_field(evsel, &sc->name, #name); }) + +static void perf_evsel__delete_priv(struct perf_evsel *evsel) +{ +	zfree(&evsel->priv); +	perf_evsel__delete(evsel); +} + +static int perf_evsel__init_syscall_tp(struct perf_evsel *evsel, void *handler) +{ +	evsel->priv = malloc(sizeof(struct syscall_tp)); +	if (evsel->priv != NULL) { +		if (perf_evsel__init_sc_tp_uint_field(evsel, id)) +			goto out_delete; + +		evsel->handler = handler; +		return 0;  	} -	return scnprintf(bf, size, "%#x", whence); +	return -ENOMEM; + +out_delete: +	zfree(&evsel->priv); +	return -ENOENT; +} + +static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void *handler) +{ +	struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction); + +	/* older kernel (e.g., RHEL6) use syscalls:{enter,exit} */ +	if (evsel == NULL) +		evsel = perf_evsel__newtp("syscalls", direction); + +	if (evsel) { +		if (perf_evsel__init_syscall_tp(evsel, handler)) +			goto out_delete; +	} + +	return evsel; + +out_delete: +	perf_evsel__delete_priv(evsel); +	return NULL; +} + +#define perf_evsel__sc_tp_uint(evsel, name, sample) \ +	({ struct syscall_tp *fields = evsel->priv; \ +	   fields->name.integer(&fields->name, sample); }) + +#define perf_evsel__sc_tp_ptr(evsel, name, sample) \ +	({ struct syscall_tp *fields = evsel->priv; \ +	   fields->name.pointer(&fields->name, sample); }) + +static int perf_evlist__add_syscall_newtp(struct perf_evlist *evlist, +					  void *sys_enter_handler, +					  void *sys_exit_handler) +{ +	int ret = -1; +	struct perf_evsel *sys_enter, *sys_exit; + +	sys_enter = perf_evsel__syscall_newtp("sys_enter", sys_enter_handler); +	if (sys_enter == NULL) +		goto out; + +	if (perf_evsel__init_sc_tp_ptr_field(sys_enter, args)) +		goto out_delete_sys_enter; + +	sys_exit = perf_evsel__syscall_newtp("sys_exit", sys_exit_handler); +	if (sys_exit == NULL) +		goto out_delete_sys_enter; + +	if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret)) +		goto out_delete_sys_exit; + +	perf_evlist__add(evlist, sys_enter); +	perf_evlist__add(evlist, sys_exit); + +	ret = 0; +out: +	return ret; + +out_delete_sys_exit: +	perf_evsel__delete_priv(sys_exit); +out_delete_sys_enter: +	perf_evsel__delete_priv(sys_enter); +	goto out; +} + + +struct syscall_arg { +	unsigned long val; +	struct thread *thread; +	struct trace  *trace; +	void	      *parm; +	u8	      idx; +	u8	      mask; +}; + +struct strarray { +	int	    offset; +	int	    nr_entries; +	const char **entries; +}; + +#define DEFINE_STRARRAY(array) struct strarray strarray__##array = { \ +	.nr_entries = ARRAY_SIZE(array), \ +	.entries = array, \ +} + +#define DEFINE_STRARRAY_OFFSET(array, off) struct strarray strarray__##array = { \ +	.offset	    = off, \ +	.nr_entries = ARRAY_SIZE(array), \ +	.entries = array, \ +} + +static size_t __syscall_arg__scnprintf_strarray(char *bf, size_t size, +						const char *intfmt, +					        struct syscall_arg *arg) +{ +	struct strarray *sa = arg->parm; +	int idx = arg->val - sa->offset; + +	if (idx < 0 || idx >= sa->nr_entries) +		return scnprintf(bf, size, intfmt, arg->val); + +	return scnprintf(bf, size, "%s", sa->entries[idx]); +} + +static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size, +					      struct syscall_arg *arg) +{ +	return __syscall_arg__scnprintf_strarray(bf, size, "%d", arg); +} + +#define SCA_STRARRAY syscall_arg__scnprintf_strarray + +#if defined(__i386__) || defined(__x86_64__) +/* + * FIXME: Make this available to all arches as soon as the ioctl beautifier + * 	  gets rewritten to support all arches. + */ +static size_t syscall_arg__scnprintf_strhexarray(char *bf, size_t size, +						 struct syscall_arg *arg) +{ +	return __syscall_arg__scnprintf_strarray(bf, size, "%#x", arg); +} + +#define SCA_STRHEXARRAY syscall_arg__scnprintf_strhexarray +#endif /* defined(__i386__) || defined(__x86_64__) */ + +static size_t syscall_arg__scnprintf_fd(char *bf, size_t size, +					struct syscall_arg *arg); + +#define SCA_FD syscall_arg__scnprintf_fd + +static size_t syscall_arg__scnprintf_fd_at(char *bf, size_t size, +					   struct syscall_arg *arg) +{ +	int fd = arg->val; + +	if (fd == AT_FDCWD) +		return scnprintf(bf, size, "CWD"); + +	return syscall_arg__scnprintf_fd(bf, size, arg); +} + +#define SCA_FDAT syscall_arg__scnprintf_fd_at + +static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size, +					      struct syscall_arg *arg); + +#define SCA_CLOSE_FD syscall_arg__scnprintf_close_fd + +static size_t syscall_arg__scnprintf_hex(char *bf, size_t size, +					 struct syscall_arg *arg) +{ +	return scnprintf(bf, size, "%#lx", arg->val);  } -#define SCA_WHENCE syscall_arg__scnprintf_whence +#define SCA_HEX syscall_arg__scnprintf_hex  static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, -					       unsigned long arg, -					       u8 arg_idx __maybe_unused, -					       u8 *arg_mask __maybe_unused) +					       struct syscall_arg *arg)  { -	int printed = 0, prot = arg; +	int printed = 0, prot = arg->val;  	if (prot == PROT_NONE)  		return scnprintf(bf, size, "NONE"); @@ -104,10 +360,9 @@ static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size,  #define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot  static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, -						unsigned long arg, u8 arg_idx __maybe_unused, -						u8 *arg_mask __maybe_unused) +						struct syscall_arg *arg)  { -	int printed = 0, flags = arg; +	int printed = 0, flags = arg->val;  #define	P_MMAP_FLAG(n) \  	if (flags & MAP_##n) { \ @@ -148,10 +403,9 @@ static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size,  #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags  static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, -						      unsigned long arg, u8 arg_idx __maybe_unused, -						      u8 *arg_mask __maybe_unused) +						      struct syscall_arg *arg)  { -	int behavior = arg; +	int behavior = arg->val;  	switch (behavior) {  #define	P_MADV_BHV(n) case MADV_##n: return scnprintf(bf, size, #n) @@ -190,8 +444,38 @@ static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size,  #define SCA_MADV_BHV syscall_arg__scnprintf_madvise_behavior -static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, unsigned long arg, -					      u8 arg_idx __maybe_unused, u8 *arg_mask) +static size_t syscall_arg__scnprintf_flock(char *bf, size_t size, +					   struct syscall_arg *arg) +{ +	int printed = 0, op = arg->val; + +	if (op == 0) +		return scnprintf(bf, size, "NONE"); +#define	P_CMD(cmd) \ +	if ((op & LOCK_##cmd) == LOCK_##cmd) { \ +		printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #cmd); \ +		op &= ~LOCK_##cmd; \ +	} + +	P_CMD(SH); +	P_CMD(EX); +	P_CMD(NB); +	P_CMD(UN); +	P_CMD(MAND); +	P_CMD(RW); +	P_CMD(READ); +	P_CMD(WRITE); +#undef P_OP + +	if (op) +		printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", op); + +	return printed; +} + +#define SCA_FLOCK syscall_arg__scnprintf_flock + +static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct syscall_arg *arg)  {  	enum syscall_futex_args {  		SCF_UADDR   = (1 << 0), @@ -201,24 +485,24 @@ static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, unsigned lo  		SCF_UADDR2  = (1 << 4),  		SCF_VAL3    = (1 << 5),  	}; -	int op = arg; +	int op = arg->val;  	int cmd = op & FUTEX_CMD_MASK;  	size_t printed = 0;  	switch (cmd) {  #define	P_FUTEX_OP(n) case FUTEX_##n: printed = scnprintf(bf, size, #n); -	P_FUTEX_OP(WAIT);	    *arg_mask |= SCF_VAL3|SCF_UADDR2;		  break; -	P_FUTEX_OP(WAKE);	    *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; -	P_FUTEX_OP(FD);		    *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; -	P_FUTEX_OP(REQUEUE);	    *arg_mask |= SCF_VAL3|SCF_TIMEOUT;	          break; -	P_FUTEX_OP(CMP_REQUEUE);    *arg_mask |= SCF_TIMEOUT;			  break; -	P_FUTEX_OP(CMP_REQUEUE_PI); *arg_mask |= SCF_TIMEOUT;			  break; +	P_FUTEX_OP(WAIT);	    arg->mask |= SCF_VAL3|SCF_UADDR2;		  break; +	P_FUTEX_OP(WAKE);	    arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; +	P_FUTEX_OP(FD);		    arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; +	P_FUTEX_OP(REQUEUE);	    arg->mask |= SCF_VAL3|SCF_TIMEOUT;	          break; +	P_FUTEX_OP(CMP_REQUEUE);    arg->mask |= SCF_TIMEOUT;			  break; +	P_FUTEX_OP(CMP_REQUEUE_PI); arg->mask |= SCF_TIMEOUT;			  break;  	P_FUTEX_OP(WAKE_OP);							  break; -	P_FUTEX_OP(LOCK_PI);	    *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; -	P_FUTEX_OP(UNLOCK_PI);	    *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; -	P_FUTEX_OP(TRYLOCK_PI);	    *arg_mask |= SCF_VAL3|SCF_UADDR2;		  break; -	P_FUTEX_OP(WAIT_BITSET);    *arg_mask |= SCF_UADDR2;			  break; -	P_FUTEX_OP(WAKE_BITSET);    *arg_mask |= SCF_UADDR2;			  break; +	P_FUTEX_OP(LOCK_PI);	    arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; +	P_FUTEX_OP(UNLOCK_PI);	    arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; +	P_FUTEX_OP(TRYLOCK_PI);	    arg->mask |= SCF_VAL3|SCF_UADDR2;		  break; +	P_FUTEX_OP(WAIT_BITSET);    arg->mask |= SCF_UADDR2;			  break; +	P_FUTEX_OP(WAKE_BITSET);    arg->mask |= SCF_UADDR2;			  break;  	P_FUTEX_OP(WAIT_REQUEUE_PI);						  break;  	default: printed = scnprintf(bf, size, "%#x", cmd);			  break;  	} @@ -234,14 +518,194 @@ static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, unsigned lo  #define SCA_FUTEX_OP  syscall_arg__scnprintf_futex_op +static const char *epoll_ctl_ops[] = { "ADD", "DEL", "MOD", }; +static DEFINE_STRARRAY_OFFSET(epoll_ctl_ops, 1); + +static const char *itimers[] = { "REAL", "VIRTUAL", "PROF", }; +static DEFINE_STRARRAY(itimers); + +static const char *whences[] = { "SET", "CUR", "END", +#ifdef SEEK_DATA +"DATA", +#endif +#ifdef SEEK_HOLE +"HOLE", +#endif +}; +static DEFINE_STRARRAY(whences); + +static const char *fcntl_cmds[] = { +	"DUPFD", "GETFD", "SETFD", "GETFL", "SETFL", "GETLK", "SETLK", +	"SETLKW", "SETOWN", "GETOWN", "SETSIG", "GETSIG", "F_GETLK64", +	"F_SETLK64", "F_SETLKW64", "F_SETOWN_EX", "F_GETOWN_EX", +	"F_GETOWNER_UIDS", +}; +static DEFINE_STRARRAY(fcntl_cmds); + +static const char *rlimit_resources[] = { +	"CPU", "FSIZE", "DATA", "STACK", "CORE", "RSS", "NPROC", "NOFILE", +	"MEMLOCK", "AS", "LOCKS", "SIGPENDING", "MSGQUEUE", "NICE", "RTPRIO", +	"RTTIME", +}; +static DEFINE_STRARRAY(rlimit_resources); + +static const char *sighow[] = { "BLOCK", "UNBLOCK", "SETMASK", }; +static DEFINE_STRARRAY(sighow); + +static const char *clockid[] = { +	"REALTIME", "MONOTONIC", "PROCESS_CPUTIME_ID", "THREAD_CPUTIME_ID", +	"MONOTONIC_RAW", "REALTIME_COARSE", "MONOTONIC_COARSE", +}; +static DEFINE_STRARRAY(clockid); + +static const char *socket_families[] = { +	"UNSPEC", "LOCAL", "INET", "AX25", "IPX", "APPLETALK", "NETROM", +	"BRIDGE", "ATMPVC", "X25", "INET6", "ROSE", "DECnet", "NETBEUI", +	"SECURITY", "KEY", "NETLINK", "PACKET", "ASH", "ECONET", "ATMSVC", +	"RDS", "SNA", "IRDA", "PPPOX", "WANPIPE", "LLC", "IB", "CAN", "TIPC", +	"BLUETOOTH", "IUCV", "RXRPC", "ISDN", "PHONET", "IEEE802154", "CAIF", +	"ALG", "NFC", "VSOCK", +}; +static DEFINE_STRARRAY(socket_families); + +#ifndef SOCK_TYPE_MASK +#define SOCK_TYPE_MASK 0xf +#endif + +static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, +						      struct syscall_arg *arg) +{ +	size_t printed; +	int type = arg->val, +	    flags = type & ~SOCK_TYPE_MASK; + +	type &= SOCK_TYPE_MASK; +	/* + 	 * Can't use a strarray, MIPS may override for ABI reasons. + 	 */ +	switch (type) { +#define	P_SK_TYPE(n) case SOCK_##n: printed = scnprintf(bf, size, #n); break; +	P_SK_TYPE(STREAM); +	P_SK_TYPE(DGRAM); +	P_SK_TYPE(RAW); +	P_SK_TYPE(RDM); +	P_SK_TYPE(SEQPACKET); +	P_SK_TYPE(DCCP); +	P_SK_TYPE(PACKET); +#undef P_SK_TYPE +	default: +		printed = scnprintf(bf, size, "%#x", type); +	} + +#define	P_SK_FLAG(n) \ +	if (flags & SOCK_##n) { \ +		printed += scnprintf(bf + printed, size - printed, "|%s", #n); \ +		flags &= ~SOCK_##n; \ +	} + +	P_SK_FLAG(CLOEXEC); +	P_SK_FLAG(NONBLOCK); +#undef P_SK_FLAG + +	if (flags) +		printed += scnprintf(bf + printed, size - printed, "|%#x", flags); + +	return printed; +} + +#define SCA_SK_TYPE syscall_arg__scnprintf_socket_type + +#ifndef MSG_PROBE +#define MSG_PROBE	     0x10 +#endif +#ifndef MSG_WAITFORONE +#define MSG_WAITFORONE	0x10000 +#endif +#ifndef MSG_SENDPAGE_NOTLAST +#define MSG_SENDPAGE_NOTLAST 0x20000 +#endif +#ifndef MSG_FASTOPEN +#define MSG_FASTOPEN	     0x20000000 +#endif + +static size_t syscall_arg__scnprintf_msg_flags(char *bf, size_t size, +					       struct syscall_arg *arg) +{ +	int printed = 0, flags = arg->val; + +	if (flags == 0) +		return scnprintf(bf, size, "NONE"); +#define	P_MSG_FLAG(n) \ +	if (flags & MSG_##n) { \ +		printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ +		flags &= ~MSG_##n; \ +	} + +	P_MSG_FLAG(OOB); +	P_MSG_FLAG(PEEK); +	P_MSG_FLAG(DONTROUTE); +	P_MSG_FLAG(TRYHARD); +	P_MSG_FLAG(CTRUNC); +	P_MSG_FLAG(PROBE); +	P_MSG_FLAG(TRUNC); +	P_MSG_FLAG(DONTWAIT); +	P_MSG_FLAG(EOR); +	P_MSG_FLAG(WAITALL); +	P_MSG_FLAG(FIN); +	P_MSG_FLAG(SYN); +	P_MSG_FLAG(CONFIRM); +	P_MSG_FLAG(RST); +	P_MSG_FLAG(ERRQUEUE); +	P_MSG_FLAG(NOSIGNAL); +	P_MSG_FLAG(MORE); +	P_MSG_FLAG(WAITFORONE); +	P_MSG_FLAG(SENDPAGE_NOTLAST); +	P_MSG_FLAG(FASTOPEN); +	P_MSG_FLAG(CMSG_CLOEXEC); +#undef P_MSG_FLAG + +	if (flags) +		printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); + +	return printed; +} + +#define SCA_MSG_FLAGS syscall_arg__scnprintf_msg_flags + +static size_t syscall_arg__scnprintf_access_mode(char *bf, size_t size, +						 struct syscall_arg *arg) +{ +	size_t printed = 0; +	int mode = arg->val; + +	if (mode == F_OK) /* 0 */ +		return scnprintf(bf, size, "F"); +#define	P_MODE(n) \ +	if (mode & n##_OK) { \ +		printed += scnprintf(bf + printed, size - printed, "%s", #n); \ +		mode &= ~n##_OK; \ +	} + +	P_MODE(R); +	P_MODE(W); +	P_MODE(X); +#undef P_MODE + +	if (mode) +		printed += scnprintf(bf + printed, size - printed, "|%#x", mode); + +	return printed; +} + +#define SCA_ACCMODE syscall_arg__scnprintf_access_mode +  static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, -					       unsigned long arg, -					       u8 arg_idx, u8 *arg_mask) +					       struct syscall_arg *arg)  { -	int printed = 0, flags = arg; +	int printed = 0, flags = arg->val;  	if (!(flags & O_CREAT)) -		*arg_mask |= 1 << (arg_idx + 1); /* Mask the mode parm */ +		arg->mask |= 1 << (arg->idx + 1); /* Mask the mode parm */  	if (flags == 0)  		return scnprintf(bf, size, "RDONLY"); @@ -291,60 +755,341 @@ static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size,  #define SCA_OPEN_FLAGS syscall_arg__scnprintf_open_flags +static size_t syscall_arg__scnprintf_eventfd_flags(char *bf, size_t size, +						   struct syscall_arg *arg) +{ +	int printed = 0, flags = arg->val; + +	if (flags == 0) +		return scnprintf(bf, size, "NONE"); +#define	P_FLAG(n) \ +	if (flags & EFD_##n) { \ +		printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ +		flags &= ~EFD_##n; \ +	} + +	P_FLAG(SEMAPHORE); +	P_FLAG(CLOEXEC); +	P_FLAG(NONBLOCK); +#undef P_FLAG + +	if (flags) +		printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); + +	return printed; +} + +#define SCA_EFD_FLAGS syscall_arg__scnprintf_eventfd_flags + +static size_t syscall_arg__scnprintf_pipe_flags(char *bf, size_t size, +						struct syscall_arg *arg) +{ +	int printed = 0, flags = arg->val; + +#define	P_FLAG(n) \ +	if (flags & O_##n) { \ +		printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ +		flags &= ~O_##n; \ +	} + +	P_FLAG(CLOEXEC); +	P_FLAG(NONBLOCK); +#undef P_FLAG + +	if (flags) +		printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); + +	return printed; +} + +#define SCA_PIPE_FLAGS syscall_arg__scnprintf_pipe_flags + +static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscall_arg *arg) +{ +	int sig = arg->val; + +	switch (sig) { +#define	P_SIGNUM(n) case SIG##n: return scnprintf(bf, size, #n) +	P_SIGNUM(HUP); +	P_SIGNUM(INT); +	P_SIGNUM(QUIT); +	P_SIGNUM(ILL); +	P_SIGNUM(TRAP); +	P_SIGNUM(ABRT); +	P_SIGNUM(BUS); +	P_SIGNUM(FPE); +	P_SIGNUM(KILL); +	P_SIGNUM(USR1); +	P_SIGNUM(SEGV); +	P_SIGNUM(USR2); +	P_SIGNUM(PIPE); +	P_SIGNUM(ALRM); +	P_SIGNUM(TERM); +	P_SIGNUM(CHLD); +	P_SIGNUM(CONT); +	P_SIGNUM(STOP); +	P_SIGNUM(TSTP); +	P_SIGNUM(TTIN); +	P_SIGNUM(TTOU); +	P_SIGNUM(URG); +	P_SIGNUM(XCPU); +	P_SIGNUM(XFSZ); +	P_SIGNUM(VTALRM); +	P_SIGNUM(PROF); +	P_SIGNUM(WINCH); +	P_SIGNUM(IO); +	P_SIGNUM(PWR); +	P_SIGNUM(SYS); +#ifdef SIGEMT +	P_SIGNUM(EMT); +#endif +#ifdef SIGSTKFLT +	P_SIGNUM(STKFLT); +#endif +#ifdef SIGSWI +	P_SIGNUM(SWI); +#endif +	default: break; +	} + +	return scnprintf(bf, size, "%#x", sig); +} + +#define SCA_SIGNUM syscall_arg__scnprintf_signum + +#if defined(__i386__) || defined(__x86_64__) +/* + * FIXME: Make this available to all arches. + */ +#define TCGETS		0x5401 + +static const char *tioctls[] = { +	"TCGETS", "TCSETS", "TCSETSW", "TCSETSF", "TCGETA", "TCSETA", "TCSETAW", +	"TCSETAF", "TCSBRK", "TCXONC", "TCFLSH", "TIOCEXCL", "TIOCNXCL", +	"TIOCSCTTY", "TIOCGPGRP", "TIOCSPGRP", "TIOCOUTQ", "TIOCSTI", +	"TIOCGWINSZ", "TIOCSWINSZ", "TIOCMGET", "TIOCMBIS", "TIOCMBIC", +	"TIOCMSET", "TIOCGSOFTCAR", "TIOCSSOFTCAR", "FIONREAD", "TIOCLINUX", +	"TIOCCONS", "TIOCGSERIAL", "TIOCSSERIAL", "TIOCPKT", "FIONBIO", +	"TIOCNOTTY", "TIOCSETD", "TIOCGETD", "TCSBRKP", [0x27] = "TIOCSBRK", +	"TIOCCBRK", "TIOCGSID", "TCGETS2", "TCSETS2", "TCSETSW2", "TCSETSF2", +	"TIOCGRS485", "TIOCSRS485", "TIOCGPTN", "TIOCSPTLCK", +	"TIOCGDEV||TCGETX", "TCSETX", "TCSETXF", "TCSETXW", "TIOCSIG", +	"TIOCVHANGUP", "TIOCGPKT", "TIOCGPTLCK", "TIOCGEXCL", +	[0x50] = "FIONCLEX", "FIOCLEX", "FIOASYNC", "TIOCSERCONFIG", +	"TIOCSERGWILD", "TIOCSERSWILD", "TIOCGLCKTRMIOS", "TIOCSLCKTRMIOS", +	"TIOCSERGSTRUCT", "TIOCSERGETLSR", "TIOCSERGETMULTI", "TIOCSERSETMULTI", +	"TIOCMIWAIT", "TIOCGICOUNT", [0x60] = "FIOQSIZE", +}; + +static DEFINE_STRARRAY_OFFSET(tioctls, 0x5401); +#endif /* defined(__i386__) || defined(__x86_64__) */ + +#define STRARRAY(arg, name, array) \ +	  .arg_scnprintf = { [arg] = SCA_STRARRAY, }, \ +	  .arg_parm	 = { [arg] = &strarray__##array, } +  static struct syscall_fmt {  	const char *name;  	const char *alias; -	size_t	   (*arg_scnprintf[6])(char *bf, size_t size, unsigned long arg, u8 arg_idx, u8 *arg_mask); +	size_t	   (*arg_scnprintf[6])(char *bf, size_t size, struct syscall_arg *arg); +	void	   *arg_parm[6];  	bool	   errmsg;  	bool	   timeout;  	bool	   hexret;  } syscall_fmts[] = { -	{ .name	    = "access",	    .errmsg = true, }, +	{ .name	    = "access",	    .errmsg = true, +	  .arg_scnprintf = { [1] = SCA_ACCMODE, /* mode */ }, },  	{ .name	    = "arch_prctl", .errmsg = true, .alias = "prctl", },  	{ .name	    = "brk",	    .hexret = true,  	  .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, -	{ .name	    = "mmap",	    .hexret = true, }, +	{ .name     = "clock_gettime",  .errmsg = true, STRARRAY(0, clk_id, clockid), }, +	{ .name	    = "close",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_CLOSE_FD, /* fd */ }, },   	{ .name	    = "connect",    .errmsg = true, }, -	{ .name	    = "fstat",	    .errmsg = true, .alias = "newfstat", }, -	{ .name	    = "fstatat",    .errmsg = true, .alias = "newfstatat", }, +	{ .name	    = "dup",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "dup2",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "dup3",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "epoll_ctl",  .errmsg = true, STRARRAY(1, op, epoll_ctl_ops), }, +	{ .name	    = "eventfd2",   .errmsg = true, +	  .arg_scnprintf = { [1] = SCA_EFD_FLAGS, /* flags */ }, }, +	{ .name	    = "faccessat",  .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, +	{ .name	    = "fadvise64",  .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "fallocate",  .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "fchdir",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "fchmod",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "fchmodat",   .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },  +	{ .name	    = "fchown",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "fchownat",   .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },  +	{ .name	    = "fcntl",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ +			     [1] = SCA_STRARRAY, /* cmd */ }, +	  .arg_parm	 = { [1] = &strarray__fcntl_cmds, /* cmd */ }, }, +	{ .name	    = "fdatasync",  .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "flock",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ +			     [1] = SCA_FLOCK, /* cmd */ }, }, +	{ .name	    = "fsetxattr",  .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "fstat",	    .errmsg = true, .alias = "newfstat", +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "fstatat",    .errmsg = true, .alias = "newfstatat", +	  .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },  +	{ .name	    = "fstatfs",    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "fsync",    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "ftruncate", .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },   	{ .name	    = "futex",	    .errmsg = true,  	  .arg_scnprintf = { [1] = SCA_FUTEX_OP, /* op */ }, }, +	{ .name	    = "futimesat", .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },  +	{ .name	    = "getdents",   .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "getdents64", .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "getitimer",  .errmsg = true, STRARRAY(0, which, itimers), }, +	{ .name	    = "getrlimit",  .errmsg = true, STRARRAY(0, resource, rlimit_resources), },  	{ .name	    = "ioctl",	    .errmsg = true, -	  .arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, }, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */  +#if defined(__i386__) || defined(__x86_64__) +/* + * FIXME: Make this available to all arches. + */ +			     [1] = SCA_STRHEXARRAY, /* cmd */ +			     [2] = SCA_HEX, /* arg */ }, +	  .arg_parm	 = { [1] = &strarray__tioctls, /* cmd */ }, }, +#else +			     [2] = SCA_HEX, /* arg */ }, }, +#endif +	{ .name	    = "kill",	    .errmsg = true, +	  .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, +	{ .name	    = "linkat",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },   	{ .name	    = "lseek",	    .errmsg = true, -	  .arg_scnprintf = { [2] = SCA_WHENCE, /* whence */ }, }, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ +			     [2] = SCA_STRARRAY, /* whence */ }, +	  .arg_parm	 = { [2] = &strarray__whences, /* whence */ }, },  	{ .name	    = "lstat",	    .errmsg = true, .alias = "newlstat", },  	{ .name     = "madvise",    .errmsg = true,  	  .arg_scnprintf = { [0] = SCA_HEX,	 /* start */  			     [2] = SCA_MADV_BHV, /* behavior */ }, }, +	{ .name	    = "mkdirat",    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },  +	{ .name	    = "mknodat",    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },  +	{ .name	    = "mlock",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, +	{ .name	    = "mlockall",   .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, },  	{ .name	    = "mmap",	    .hexret = true,  	  .arg_scnprintf = { [0] = SCA_HEX,	  /* addr */  			     [2] = SCA_MMAP_PROT, /* prot */ -			     [3] = SCA_MMAP_FLAGS, /* flags */ }, }, +			     [3] = SCA_MMAP_FLAGS, /* flags */ +			     [4] = SCA_FD, 	  /* fd */ }, },  	{ .name	    = "mprotect",   .errmsg = true,  	  .arg_scnprintf = { [0] = SCA_HEX, /* start */  			     [2] = SCA_MMAP_PROT, /* prot */ }, },  	{ .name	    = "mremap",	    .hexret = true,  	  .arg_scnprintf = { [0] = SCA_HEX, /* addr */  			     [4] = SCA_HEX, /* new_addr */ }, }, +	{ .name	    = "munlock",    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, },  	{ .name	    = "munmap",	    .errmsg = true,  	  .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, +	{ .name	    = "name_to_handle_at", .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },  +	{ .name	    = "newfstatat", .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },   	{ .name	    = "open",	    .errmsg = true,  	  .arg_scnprintf = { [1] = SCA_OPEN_FLAGS, /* flags */ }, },  	{ .name	    = "open_by_handle_at", .errmsg = true, -	  .arg_scnprintf = { [2] = SCA_OPEN_FLAGS, /* flags */ }, }, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ +			     [2] = SCA_OPEN_FLAGS, /* flags */ }, },  	{ .name	    = "openat",	    .errmsg = true, -	  .arg_scnprintf = { [2] = SCA_OPEN_FLAGS, /* flags */ }, }, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ +			     [2] = SCA_OPEN_FLAGS, /* flags */ }, }, +	{ .name	    = "pipe2",	    .errmsg = true, +	  .arg_scnprintf = { [1] = SCA_PIPE_FLAGS, /* flags */ }, },  	{ .name	    = "poll",	    .errmsg = true, .timeout = true, },  	{ .name	    = "ppoll",	    .errmsg = true, .timeout = true, }, -	{ .name	    = "pread",	    .errmsg = true, .alias = "pread64", }, -	{ .name	    = "pwrite",	    .errmsg = true, .alias = "pwrite64", }, -	{ .name	    = "read",	    .errmsg = true, }, -	{ .name	    = "recvfrom",   .errmsg = true, }, +	{ .name	    = "pread",	    .errmsg = true, .alias = "pread64", +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "preadv",	    .errmsg = true, .alias = "pread", +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "prlimit64",  .errmsg = true, STRARRAY(1, resource, rlimit_resources), }, +	{ .name	    = "pwrite",	    .errmsg = true, .alias = "pwrite64", +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "pwritev",    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "read",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "readlinkat", .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },  +	{ .name	    = "readv",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "recvfrom",   .errmsg = true, +	  .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, +	{ .name	    = "recvmmsg",   .errmsg = true, +	  .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, +	{ .name	    = "recvmsg",    .errmsg = true, +	  .arg_scnprintf = { [2] = SCA_MSG_FLAGS, /* flags */ }, }, +	{ .name	    = "renameat",   .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },  +	{ .name	    = "rt_sigaction", .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_SIGNUM, /* sig */ }, }, +	{ .name	    = "rt_sigprocmask",  .errmsg = true, STRARRAY(0, how, sighow), }, +	{ .name	    = "rt_sigqueueinfo", .errmsg = true, +	  .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, +	{ .name	    = "rt_tgsigqueueinfo", .errmsg = true, +	  .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, },  	{ .name	    = "select",	    .errmsg = true, .timeout = true, }, -	{ .name	    = "socket",	    .errmsg = true, }, +	{ .name	    = "sendmmsg",    .errmsg = true, +	  .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, +	{ .name	    = "sendmsg",    .errmsg = true, +	  .arg_scnprintf = { [2] = SCA_MSG_FLAGS, /* flags */ }, }, +	{ .name	    = "sendto",	    .errmsg = true, +	  .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, +	{ .name	    = "setitimer",  .errmsg = true, STRARRAY(0, which, itimers), }, +	{ .name	    = "setrlimit",  .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, +	{ .name	    = "shutdown",   .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "socket",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ +			     [1] = SCA_SK_TYPE, /* type */ }, +	  .arg_parm	 = { [0] = &strarray__socket_families, /* family */ }, }, +	{ .name	    = "socketpair", .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ +			     [1] = SCA_SK_TYPE, /* type */ }, +	  .arg_parm	 = { [0] = &strarray__socket_families, /* family */ }, },  	{ .name	    = "stat",	    .errmsg = true, .alias = "newstat", }, +	{ .name	    = "symlinkat",  .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },  +	{ .name	    = "tgkill",	    .errmsg = true, +	  .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, +	{ .name	    = "tkill",	    .errmsg = true, +	  .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, },  	{ .name	    = "uname",	    .errmsg = true, .alias = "newuname", }, +	{ .name	    = "unlinkat",   .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, +	{ .name	    = "utimensat",  .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FDAT, /* dirfd */ }, }, +	{ .name	    = "write",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },  +	{ .name	    = "writev",	    .errmsg = true, +	  .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },   };  static int syscall_fmt__cmp(const void *name, const void *fmtp) @@ -364,8 +1109,8 @@ struct syscall {  	const char	    *name;  	bool		    filtered;  	struct syscall_fmt  *fmt; -	size_t		    (**arg_scnprintf)(char *bf, size_t size, -					      unsigned long arg, u8 arg_idx, u8 *args_mask); +	size_t		    (**arg_scnprintf)(char *bf, size_t size, struct syscall_arg *arg); +	void		    **arg_parm;  };  static size_t fprintf_duration(unsigned long t, FILE *fp) @@ -389,11 +1134,24 @@ struct thread_trace {  	unsigned long	  nr_events;  	char		  *entry_str;  	double		  runtime_ms; +	struct { +		int	  max; +		char	  **table; +	} paths; + +	struct intlist *syscall_stats;  };  static struct thread_trace *thread_trace__new(void)  { -	return zalloc(sizeof(struct thread_trace)); +	struct thread_trace *ttrace =  zalloc(sizeof(struct thread_trace)); + +	if (ttrace) +		ttrace->paths.max = -1; + +	ttrace->syscall_stats = intlist__new(NULL); + +	return ttrace;  }  static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) @@ -421,26 +1179,140 @@ fail:  struct trace {  	struct perf_tool	tool; -	int			audit_machine; +	struct { +		int		machine; +		int		open_id; +	}			audit;  	struct {  		int		max;  		struct syscall  *table;  	} syscalls; -	struct perf_record_opts opts; -	struct machine		host; +	struct record_opts	opts; +	struct machine		*host;  	u64			base_time;  	FILE			*output;  	unsigned long		nr_events;  	struct strlist		*ev_qualifier; -	bool			not_ev_qualifier; +	const char 		*last_vfs_getname;  	struct intlist		*tid_list;  	struct intlist		*pid_list; -	bool			sched; -	bool			multiple_threads;  	double			duration_filter;  	double			runtime_ms; +	struct { +		u64		vfs_getname, +				proc_getname; +	} stats; +	bool			not_ev_qualifier; +	bool			live; +	bool			full_time; +	bool			sched; +	bool			multiple_threads; +	bool			summary; +	bool			summary_only; +	bool			show_comm; +	bool			show_tool_stats;  }; +static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) +{ +	struct thread_trace *ttrace = thread->priv; + +	if (fd > ttrace->paths.max) { +		char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); + +		if (npath == NULL) +			return -1; + +		if (ttrace->paths.max != -1) { +			memset(npath + ttrace->paths.max + 1, 0, +			       (fd - ttrace->paths.max) * sizeof(char *)); +		} else { +			memset(npath, 0, (fd + 1) * sizeof(char *)); +		} + +		ttrace->paths.table = npath; +		ttrace->paths.max   = fd; +	} + +	ttrace->paths.table[fd] = strdup(pathname); + +	return ttrace->paths.table[fd] != NULL ? 0 : -1; +} + +static int thread__read_fd_path(struct thread *thread, int fd) +{ +	char linkname[PATH_MAX], pathname[PATH_MAX]; +	struct stat st; +	int ret; + +	if (thread->pid_ == thread->tid) { +		scnprintf(linkname, sizeof(linkname), +			  "/proc/%d/fd/%d", thread->pid_, fd); +	} else { +		scnprintf(linkname, sizeof(linkname), +			  "/proc/%d/task/%d/fd/%d", thread->pid_, thread->tid, fd); +	} + +	if (lstat(linkname, &st) < 0 || st.st_size + 1 > (off_t)sizeof(pathname)) +		return -1; + +	ret = readlink(linkname, pathname, sizeof(pathname)); + +	if (ret < 0 || ret > st.st_size) +		return -1; + +	pathname[ret] = '\0'; +	return trace__set_fd_pathname(thread, fd, pathname); +} + +static const char *thread__fd_path(struct thread *thread, int fd, +				   struct trace *trace) +{ +	struct thread_trace *ttrace = thread->priv; + +	if (ttrace == NULL) +		return NULL; + +	if (fd < 0) +		return NULL; + +	if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL)) +		if (!trace->live) +			return NULL; +		++trace->stats.proc_getname; +		if (thread__read_fd_path(thread, fd)) { +			return NULL; +	} + +	return ttrace->paths.table[fd]; +} + +static size_t syscall_arg__scnprintf_fd(char *bf, size_t size, +					struct syscall_arg *arg) +{ +	int fd = arg->val; +	size_t printed = scnprintf(bf, size, "%d", fd); +	const char *path = thread__fd_path(arg->thread, fd, arg->trace); + +	if (path) +		printed += scnprintf(bf + printed, size - printed, "<%s>", path); + +	return printed; +} + +static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size, +					      struct syscall_arg *arg) +{ +	int fd = arg->val; +	size_t printed = syscall_arg__scnprintf_fd(bf, size, arg); +	struct thread_trace *ttrace = arg->thread->priv; + +	if (ttrace && fd >= 0 && fd <= ttrace->paths.max) +		zfree(&ttrace->paths.table[fd]); + +	return printed; +} +  static bool trace__filter_duration(struct trace *trace, double t)  {  	return t < (trace->duration_filter * NSEC_PER_MSEC); @@ -454,10 +1326,12 @@ static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)  }  static bool done = false; +static bool interrupted = false; -static void sig_handler(int sig __maybe_unused) +static void sig_handler(int sig)  {  	done = true; +	interrupted = sig == SIGINT;  }  static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, @@ -466,14 +1340,17 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre  	size_t printed = trace__fprintf_tstamp(trace, tstamp, fp);  	printed += fprintf_duration(duration, fp); -	if (trace->multiple_threads) +	if (trace->multiple_threads) { +		if (trace->show_comm) +			printed += fprintf(fp, "%.14s/", thread__comm_str(thread));  		printed += fprintf(fp, "%d ", thread->tid); +	}  	return printed;  }  static int trace__process_event(struct trace *trace, struct machine *machine, -				union perf_event *event) +				union perf_event *event, struct perf_sample *sample)  {  	int ret = 0; @@ -481,9 +1358,9 @@ static int trace__process_event(struct trace *trace, struct machine *machine,  	case PERF_RECORD_LOST:  		color_fprintf(trace->output, PERF_COLOR_RED,  			      "LOST %" PRIu64 " events!\n", event->lost.lost); -		ret = machine__process_lost_event(machine, event); +		ret = machine__process_lost_event(machine, event, sample);  	default: -		ret = machine__process_event(machine, event); +		ret = machine__process_event(machine, event, sample);  		break;  	} @@ -492,11 +1369,11 @@ static int trace__process_event(struct trace *trace, struct machine *machine,  static int trace__tool_process(struct perf_tool *tool,  			       union perf_event *event, -			       struct perf_sample *sample __maybe_unused, +			       struct perf_sample *sample,  			       struct machine *machine)  {  	struct trace *trace = container_of(tool, struct trace, tool); -	return trace__process_event(trace, machine, event); +	return trace__process_event(trace, machine, event, sample);  }  static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) @@ -506,18 +1383,12 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)  	if (err)  		return err; -	machine__init(&trace->host, "", HOST_KERNEL_ID); -	machine__create_kernel_maps(&trace->host); - -	if (perf_target__has_task(&trace->opts.target)) { -		err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads, -							trace__tool_process, -							&trace->host); -	} else { -		err = perf_event__synthesize_threads(&trace->tool, trace__tool_process, -						     &trace->host); -	} +	trace->host = machine__new_host(); +	if (trace->host == NULL) +		return -ENOMEM; +	err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target, +					    evlist->threads, trace__tool_process, false);  	if (err)  		symbol__exit(); @@ -533,6 +1404,9 @@ static int syscall__set_arg_fmts(struct syscall *sc)  	if (sc->arg_scnprintf == NULL)  		return -1; +	if (sc->fmt) +		sc->arg_parm = sc->fmt->arg_parm; +  	for (field = sc->tp_format->format.fields->next; field; field = field->next) {  		if (sc->fmt && sc->fmt->arg_scnprintf[idx])  			sc->arg_scnprintf[idx] = sc->fmt->arg_scnprintf[idx]; @@ -548,7 +1422,7 @@ static int trace__read_syscall_info(struct trace *trace, int id)  {  	char tp_name[128];  	struct syscall *sc; -	const char *name = audit_syscall_to_name(id, trace->audit_machine); +	const char *name = audit_syscall_to_name(id, trace->audit.machine);  	if (name == NULL)  		return -1; @@ -589,11 +1463,11 @@ static int trace__read_syscall_info(struct trace *trace, int id)  	sc->fmt  = syscall_fmt__find(sc->name);  	snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name); -	sc->tp_format = event_format__new("syscalls", tp_name); +	sc->tp_format = trace_event__tp_format("syscalls", tp_name);  	if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) {  		snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias); -		sc->tp_format = event_format__new("syscalls", tp_name); +		sc->tp_format = trace_event__tp_format("syscalls", tp_name);  	}  	if (sc->tp_format == NULL) @@ -603,32 +1477,52 @@ static int trace__read_syscall_info(struct trace *trace, int id)  }  static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, -				      unsigned long *args) +				      unsigned long *args, struct trace *trace, +				      struct thread *thread)  { -	int i = 0;  	size_t printed = 0;  	if (sc->tp_format != NULL) {  		struct format_field *field; -		u8 mask = 0, bit = 1; +		u8 bit = 1; +		struct syscall_arg arg = { +			.idx	= 0, +			.mask	= 0, +			.trace  = trace, +			.thread = thread, +		};  		for (field = sc->tp_format->format.fields->next; field; -		     field = field->next, ++i, bit <<= 1) { -			if (mask & bit) +		     field = field->next, ++arg.idx, bit <<= 1) { +			if (arg.mask & bit) +				continue; +			/* + 			 * Suppress this argument if its value is zero and + 			 * and we don't have a string associated in an + 			 * strarray for it. + 			 */ +			if (args[arg.idx] == 0 && +			    !(sc->arg_scnprintf && +			      sc->arg_scnprintf[arg.idx] == SCA_STRARRAY && +			      sc->arg_parm[arg.idx]))  				continue;  			printed += scnprintf(bf + printed, size - printed,  					     "%s%s: ", printed ? ", " : "", field->name); - -			if (sc->arg_scnprintf && sc->arg_scnprintf[i]) { -				printed += sc->arg_scnprintf[i](bf + printed, size - printed, -								args[i], i, &mask); +			if (sc->arg_scnprintf && sc->arg_scnprintf[arg.idx]) { +				arg.val = args[arg.idx]; +				if (sc->arg_parm) +					arg.parm = sc->arg_parm[arg.idx]; +				printed += sc->arg_scnprintf[arg.idx](bf + printed, +								      size - printed, &arg);  			} else {  				printed += scnprintf(bf + printed, size - printed, -						     "%ld", args[i]); +						     "%ld", args[arg.idx]);  			}  		}  	} else { +		int i = 0; +  		while (i < 6) {  			printed += scnprintf(bf + printed, size - printed,  					     "%sarg%d: %ld", @@ -644,10 +1538,8 @@ typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel,  				  struct perf_sample *sample);  static struct syscall *trace__syscall_info(struct trace *trace, -					   struct perf_evsel *evsel, -					   struct perf_sample *sample) +					   struct perf_evsel *evsel, int id)  { -	int id = perf_evsel__intval(evsel, sample, "id");  	if (id < 0) { @@ -688,6 +1580,32 @@ out_cant_read:  	return NULL;  } +static void thread__update_stats(struct thread_trace *ttrace, +				 int id, struct perf_sample *sample) +{ +	struct int_node *inode; +	struct stats *stats; +	u64 duration = 0; + +	inode = intlist__findnew(ttrace->syscall_stats, id); +	if (inode == NULL) +		return; + +	stats = inode->priv; +	if (stats == NULL) { +		stats = malloc(sizeof(struct stats)); +		if (stats == NULL) +			return; +		init_stats(stats); +		inode->priv = stats; +	} + +	if (ttrace->entry_time && sample->time > ttrace->entry_time) +		duration = sample->time - ttrace->entry_time; + +	update_stats(stats, duration); +} +  static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,  			    struct perf_sample *sample)  { @@ -695,7 +1613,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,  	void *args;  	size_t printed = 0;  	struct thread *thread; -	struct syscall *sc = trace__syscall_info(trace, evsel, sample); +	int id = perf_evsel__sc_tp_uint(evsel, id, sample); +	struct syscall *sc = trace__syscall_info(trace, evsel, id);  	struct thread_trace *ttrace;  	if (sc == NULL) @@ -704,18 +1623,12 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,  	if (sc->filtered)  		return 0; -	thread = machine__findnew_thread(&trace->host, sample->pid, -					 sample->tid); +	thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);  	ttrace = thread__trace(thread, trace->output);  	if (ttrace == NULL)  		return -1; -	args = perf_evsel__rawptr(evsel, sample, "args"); -	if (args == NULL) { -		fprintf(trace->output, "Problems reading syscall arguments\n"); -		return -1; -	} - +	args = perf_evsel__sc_tp_ptr(evsel, args, sample);  	ttrace = thread->priv;  	if (ttrace->entry_str == NULL) { @@ -728,10 +1641,11 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,  	msg = ttrace->entry_str;  	printed += scnprintf(msg + printed, 1024 - printed, "%s(", sc->name); -	printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed,  args); +	printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, +					   args, trace, thread);  	if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { -		if (!trace->duration_filter) { +		if (!trace->duration_filter && !trace->summary_only) {  			trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output);  			fprintf(trace->output, "%-70s\n", ttrace->entry_str);  		} @@ -747,7 +1661,8 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,  	int ret;  	u64 duration = 0;  	struct thread *thread; -	struct syscall *sc = trace__syscall_info(trace, evsel, sample); +	int id = perf_evsel__sc_tp_uint(evsel, id, sample); +	struct syscall *sc = trace__syscall_info(trace, evsel, id);  	struct thread_trace *ttrace;  	if (sc == NULL) @@ -756,13 +1671,21 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,  	if (sc->filtered)  		return 0; -	thread = machine__findnew_thread(&trace->host, sample->pid, -					 sample->tid); +	thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);  	ttrace = thread__trace(thread, trace->output);  	if (ttrace == NULL)  		return -1; -	ret = perf_evsel__intval(evsel, sample, "ret"); +	if (trace->summary) +		thread__update_stats(ttrace, id, sample); + +	ret = perf_evsel__sc_tp_uint(evsel, ret, sample); + +	if (id == trace->audit.open_id && ret >= 0 && trace->last_vfs_getname) { +		trace__set_fd_pathname(thread, ret, trace->last_vfs_getname); +		trace->last_vfs_getname = NULL; +		++trace->stats.vfs_getname; +	}  	ttrace = thread->priv; @@ -775,6 +1698,9 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,  	} else if (trace->duration_filter)  		goto out; +	if (trace->summary_only) +		goto out; +  	trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output);  	if (ttrace->entry_pending) { @@ -808,12 +1734,19 @@ out:  	return 0;  } +static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel, +			      struct perf_sample *sample) +{ +	trace->last_vfs_getname = perf_evsel__rawptr(evsel, sample, "pathname"); +	return 0; +} +  static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel,  				     struct perf_sample *sample)  {          u64 runtime = perf_evsel__intval(evsel, sample, "runtime");  	double runtime_ms = (double)runtime / NSEC_PER_MSEC; -	struct thread *thread = machine__findnew_thread(&trace->host, +	struct thread *thread = machine__findnew_thread(trace->host,  							sample->pid,  							sample->tid);  	struct thread_trace *ttrace = thread__trace(thread, trace->output); @@ -856,30 +1789,22 @@ static int trace__process_sample(struct perf_tool *tool,  	struct trace *trace = container_of(tool, struct trace, tool);  	int err = 0; -	tracepoint_handler handler = evsel->handler.func; +	tracepoint_handler handler = evsel->handler;  	if (skip_sample(trace, sample))  		return 0; -	if (trace->base_time == 0) +	if (!trace->full_time && trace->base_time == 0)  		trace->base_time = sample->time; -	if (handler) +	if (handler) { +		++trace->nr_events;  		handler(trace, evsel, sample); +	}  	return err;  } -static bool -perf_session__has_tp(struct perf_session *session, const char *name) -{ -	struct perf_evsel *evsel; - -	evsel = perf_evlist__find_tracepoint_by_name(session->evlist, name); - -	return evsel != NULL; -} -  static int parse_target_str(struct trace *trace)  {  	if (trace->opts.target.pid) { @@ -901,6 +1826,62 @@ static int parse_target_str(struct trace *trace)  	return 0;  } +static int trace__record(int argc, const char **argv) +{ +	unsigned int rec_argc, i, j; +	const char **rec_argv; +	const char * const record_args[] = { +		"record", +		"-R", +		"-m", "1024", +		"-c", "1", +		"-e", +	}; + +	/* +1 is for the event string below */ +	rec_argc = ARRAY_SIZE(record_args) + 1 + argc; +	rec_argv = calloc(rec_argc + 1, sizeof(char *)); + +	if (rec_argv == NULL) +		return -ENOMEM; + +	for (i = 0; i < ARRAY_SIZE(record_args); i++) +		rec_argv[i] = record_args[i]; + +	/* event string may be different for older kernels - e.g., RHEL6 */ +	if (is_valid_tracepoint("raw_syscalls:sys_enter")) +		rec_argv[i] = "raw_syscalls:sys_enter,raw_syscalls:sys_exit"; +	else if (is_valid_tracepoint("syscalls:sys_enter")) +		rec_argv[i] = "syscalls:sys_enter,syscalls:sys_exit"; +	else { +		pr_err("Neither raw_syscalls nor syscalls events exist.\n"); +		return -1; +	} +	i++; + +	for (j = 0; j < (unsigned int)argc; j++, i++) +		rec_argv[i] = argv[j]; + +	return cmd_record(i, rec_argv, NULL); +} + +static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); + +static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist) +{ +	struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname"); +	if (evsel == NULL) +		return; + +	if (perf_evsel__field(evsel, "pathname") == NULL) { +		perf_evsel__delete(evsel); +		return; +	} + +	evsel->handler = trace__vfs_getname; +	perf_evlist__add(evlist, evsel); +} +  static int trace__run(struct trace *trace, int argc, const char **argv)  {  	struct perf_evlist *evlist = perf_evlist__new(); @@ -909,23 +1890,22 @@ static int trace__run(struct trace *trace, int argc, const char **argv)  	unsigned long before;  	const bool forks = argc > 0; +	trace->live = true; +  	if (evlist == NULL) {  		fprintf(trace->output, "Not enough memory to run!\n");  		goto out;  	} -	if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || -	    perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { -		fprintf(trace->output, "Couldn't read the raw_syscalls tracepoints information!\n"); -		goto out_delete_evlist; -	} +	if (perf_evlist__add_syscall_newtp(evlist, trace__sys_enter, trace__sys_exit)) +		goto out_error_tp; + +	perf_evlist__add_vfs_getname(evlist);  	if (trace->sched && -	    perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", -				   trace__sched_stat_runtime)) { -		fprintf(trace->output, "Couldn't read the sched_stat_runtime tracepoint information!\n"); -		goto out_delete_evlist; -	} +		perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", +				trace__sched_stat_runtime)) +		goto out_error_tp;  	err = perf_evlist__create_maps(evlist, &trace->opts.target);  	if (err < 0) { @@ -936,7 +1916,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)  	err = trace__symbols_init(trace, evlist);  	if (err < 0) {  		fprintf(trace->output, "Problems initializing symbol libraries!\n"); -		goto out_delete_maps; +		goto out_delete_evlist;  	}  	perf_evlist__config(evlist, &trace->opts); @@ -946,23 +1926,21 @@ static int trace__run(struct trace *trace, int argc, const char **argv)  	if (forks) {  		err = perf_evlist__prepare_workload(evlist, &trace->opts.target, -						    argv, false, false); +						    argv, false, NULL);  		if (err < 0) {  			fprintf(trace->output, "Couldn't run the workload!\n"); -			goto out_delete_maps; +			goto out_delete_evlist;  		}  	}  	err = perf_evlist__open(evlist); -	if (err < 0) { -		fprintf(trace->output, "Couldn't create the events: %s\n", strerror(errno)); -		goto out_delete_maps; -	} +	if (err < 0) +		goto out_error_open; -	err = perf_evlist__mmap(evlist, UINT_MAX, false); +	err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false);  	if (err < 0) {  		fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno)); -		goto out_close_evlist; +		goto out_delete_evlist;  	}  	perf_evlist__enable(evlist); @@ -987,70 +1965,97 @@ again:  			err = perf_evlist__parse_sample(evlist, event, &sample);  			if (err) {  				fprintf(trace->output, "Can't parse sample, err = %d, skipping...\n", err); -				continue; +				goto next_event;  			} -			if (trace->base_time == 0) +			if (!trace->full_time && trace->base_time == 0)  				trace->base_time = sample.time;  			if (type != PERF_RECORD_SAMPLE) { -				trace__process_event(trace, &trace->host, event); +				trace__process_event(trace, trace->host, event, &sample);  				continue;  			}  			evsel = perf_evlist__id2evsel(evlist, sample.id);  			if (evsel == NULL) {  				fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); -				continue; +				goto next_event;  			}  			if (sample.raw_data == NULL) {  				fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",  				       perf_evsel__name(evsel), sample.tid,  				       sample.cpu, sample.raw_size); -				continue; +				goto next_event;  			} -			handler = evsel->handler.func; +			handler = evsel->handler;  			handler(trace, evsel, &sample); +next_event: +			perf_evlist__mmap_consume(evlist, i); -			if (done) -				goto out_unmap_evlist; +			if (interrupted) +				goto out_disable;  		}  	}  	if (trace->nr_events == before) { -		if (done) -			goto out_unmap_evlist; +		int timeout = done ? 100 : -1; -		poll(evlist->pollfd, evlist->nr_fds, -1); +		if (poll(evlist->pollfd, evlist->nr_fds, timeout) > 0) +			goto again; +	} else { +		goto again;  	} -	if (done) -		perf_evlist__disable(evlist); +out_disable: +	perf_evlist__disable(evlist); -	goto again; +	if (!err) { +		if (trace->summary) +			trace__fprintf_thread_summary(trace, trace->output); + +		if (trace->show_tool_stats) { +			fprintf(trace->output, "Stats:\n " +					       " vfs_getname : %" PRIu64 "\n" +					       " proc_getname: %" PRIu64 "\n", +				trace->stats.vfs_getname, +				trace->stats.proc_getname); +		} +	} -out_unmap_evlist: -	perf_evlist__munmap(evlist); -out_close_evlist: -	perf_evlist__close(evlist); -out_delete_maps: -	perf_evlist__delete_maps(evlist);  out_delete_evlist:  	perf_evlist__delete(evlist);  out: +	trace->live = false;  	return err; +{ +	char errbuf[BUFSIZ]; + +out_error_tp: +	perf_evlist__strerror_tp(evlist, errno, errbuf, sizeof(errbuf)); +	goto out_error; + +out_error_open: +	perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf)); + +out_error: +	fprintf(trace->output, "%s\n", errbuf); +	goto out_delete_evlist; +}  }  static int trace__replay(struct trace *trace)  {  	const struct perf_evsel_str_handler handlers[] = { -		{ "raw_syscalls:sys_enter",  trace__sys_enter, }, -		{ "raw_syscalls:sys_exit",   trace__sys_exit, }, +		{ "probe:vfs_getname",	     trace__vfs_getname, }, +	}; +	struct perf_data_file file = { +		.path  = input_name, +		.mode  = PERF_DATA_MODE_READ,  	}; -  	struct perf_session *session; +	struct perf_evsel *evsel;  	int err = -1;  	trace->tool.sample	  = trace__process_sample; @@ -1072,22 +2077,46 @@ static int trace__replay(struct trace *trace)  	if (symbol__init() < 0)  		return -1; -	session = perf_session__new(input_name, O_RDONLY, 0, false, -				    &trace->tool); +	session = perf_session__new(&file, false, &trace->tool);  	if (session == NULL)  		return -ENOMEM; +	trace->host = &session->machines.host; +  	err = perf_session__set_tracepoints_handlers(session, handlers);  	if (err)  		goto out; -	if (!perf_session__has_tp(session, "raw_syscalls:sys_enter")) { -		pr_err("Data file does not have raw_syscalls:sys_enter events\n"); +	evsel = perf_evlist__find_tracepoint_by_name(session->evlist, +						     "raw_syscalls:sys_enter"); +	/* older kernels have syscalls tp versus raw_syscalls */ +	if (evsel == NULL) +		evsel = perf_evlist__find_tracepoint_by_name(session->evlist, +							     "syscalls:sys_enter"); +	if (evsel == NULL) { +		pr_err("Data file does not have raw_syscalls:sys_enter event\n");  		goto out;  	} -	if (!perf_session__has_tp(session, "raw_syscalls:sys_exit")) { -		pr_err("Data file does not have raw_syscalls:sys_exit events\n"); +	if (perf_evsel__init_syscall_tp(evsel, trace__sys_enter) < 0 || +	    perf_evsel__init_sc_tp_ptr_field(evsel, args)) { +		pr_err("Error during initialize raw_syscalls:sys_enter event\n"); +		goto out; +	} + +	evsel = perf_evlist__find_tracepoint_by_name(session->evlist, +						     "raw_syscalls:sys_exit"); +	if (evsel == NULL) +		evsel = perf_evlist__find_tracepoint_by_name(session->evlist, +							     "syscalls:sys_exit"); +	if (evsel == NULL) { +		pr_err("Data file does not have raw_syscalls:sys_exit event\n"); +		goto out; +	} + +	if (perf_evsel__init_syscall_tp(evsel, trace__sys_exit) < 0 || +	    perf_evsel__init_sc_tp_uint_field(evsel, ret)) { +		pr_err("Error during initialize raw_syscalls:sys_exit event\n");  		goto out;  	} @@ -1101,6 +2130,9 @@ static int trace__replay(struct trace *trace)  	if (err)  		pr_err("Failed to process events, error %d", err); +	else if (trace->summary) +		trace__fprintf_thread_summary(trace, trace->output); +  out:  	perf_session__delete(session); @@ -1111,47 +2143,101 @@ static size_t trace__fprintf_threads_header(FILE *fp)  {  	size_t printed; -	printed  = fprintf(fp, "\n _____________________________________________________________________\n"); -	printed += fprintf(fp," __)    Summary of events    (__\n\n"); -	printed += fprintf(fp,"              [ task - pid ]     [ events ] [ ratio ]  [ runtime ]\n"); -	printed += fprintf(fp," _____________________________________________________________________\n\n"); +	printed  = fprintf(fp, "\n Summary of events:\n\n");  	return printed;  } -static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) +static size_t thread__dump_stats(struct thread_trace *ttrace, +				 struct trace *trace, FILE *fp)  { -	size_t printed = trace__fprintf_threads_header(fp); -	struct rb_node *nd; - -	for (nd = rb_first(&trace->host.threads); nd; nd = rb_next(nd)) { -		struct thread *thread = rb_entry(nd, struct thread, rb_node); -		struct thread_trace *ttrace = thread->priv; -		const char *color; -		double ratio; - -		if (ttrace == NULL) -			continue; +	struct stats *stats; +	size_t printed = 0; +	struct syscall *sc; +	struct int_node *inode = intlist__first(ttrace->syscall_stats); -		ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; +	if (inode == NULL) +		return 0; -		color = PERF_COLOR_NORMAL; -		if (ratio > 50.0) -			color = PERF_COLOR_RED; -		else if (ratio > 25.0) -			color = PERF_COLOR_GREEN; -		else if (ratio > 5.0) -			color = PERF_COLOR_YELLOW; +	printed += fprintf(fp, "\n"); + +	printed += fprintf(fp, "   syscall            calls      min       avg       max      stddev\n"); +	printed += fprintf(fp, "                               (msec)    (msec)    (msec)        (%%)\n"); +	printed += fprintf(fp, "   --------------- -------- --------- --------- ---------     ------\n"); + +	/* each int_node is a syscall */ +	while (inode) { +		stats = inode->priv; +		if (stats) { +			double min = (double)(stats->min) / NSEC_PER_MSEC; +			double max = (double)(stats->max) / NSEC_PER_MSEC; +			double avg = avg_stats(stats); +			double pct; +			u64 n = (u64) stats->n; + +			pct = avg ? 100.0 * stddev_stats(stats)/avg : 0.0; +			avg /= NSEC_PER_MSEC; + +			sc = &trace->syscalls.table[inode->i]; +			printed += fprintf(fp, "   %-15s", sc->name); +			printed += fprintf(fp, " %8" PRIu64 " %9.3f %9.3f", +					   n, min, avg); +			printed += fprintf(fp, " %9.3f %9.2f%%\n", max, pct); +		} -		printed += color_fprintf(fp, color, "%20s", thread->comm); -		printed += fprintf(fp, " - %-5d :%11lu   [", thread->tid, ttrace->nr_events); -		printed += color_fprintf(fp, color, "%5.1f%%", ratio); -		printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); +		inode = intlist__next(inode);  	} +	printed += fprintf(fp, "\n\n"); +  	return printed;  } +/* struct used to pass data to per-thread function */ +struct summary_data { +	FILE *fp; +	struct trace *trace; +	size_t printed; +}; + +static int trace__fprintf_one_thread(struct thread *thread, void *priv) +{ +	struct summary_data *data = priv; +	FILE *fp = data->fp; +	size_t printed = data->printed; +	struct trace *trace = data->trace; +	struct thread_trace *ttrace = thread->priv; +	double ratio; + +	if (ttrace == NULL) +		return 0; + +	ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; + +	printed += fprintf(fp, " %s (%d), ", thread__comm_str(thread), thread->tid); +	printed += fprintf(fp, "%lu events, ", ttrace->nr_events); +	printed += fprintf(fp, "%.1f%%", ratio); +	printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms); +	printed += thread__dump_stats(ttrace, trace, fp); + +	data->printed += printed; + +	return 0; +} + +static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) +{ +	struct summary_data data = { +		.fp = fp, +		.trace = trace +	}; +	data.printed = trace__fprintf_threads_header(fp); + +	machine__for_each_thread(trace->host, trace__fprintf_one_thread, &data); + +	return data.printed; +} +  static int trace__set_duration(const struct option *opt, const char *str,  			       int unset __maybe_unused)  { @@ -1183,10 +2269,15 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)  	const char * const trace_usage[] = {  		"perf trace [<options>] [<command>]",  		"perf trace [<options>] -- <command> [<options>]", +		"perf trace record [<options>] [<command>]", +		"perf trace record [<options>] -- <command> [<options>]",  		NULL  	};  	struct trace trace = { -		.audit_machine = audit_detect_machine(), +		.audit = { +			.machine = audit_detect_machine(), +			.open_id = audit_name_to_syscall("open", trace.audit.machine), +		},  		.syscalls = {  			. max = -1,  		}, @@ -1197,14 +2288,18 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)  			},  			.user_freq     = UINT_MAX,  			.user_interval = ULLONG_MAX, -			.no_delay      = true, +			.no_buffering  = true,  			.mmap_pages    = 1024,  		},  		.output = stdout, +		.show_comm = true,  	};  	const char *output_name = NULL;  	const char *ev_qualifier_str = NULL;  	const struct option trace_options[] = { +	OPT_BOOLEAN(0, "comm", &trace.show_comm, +		    "show the thread COMM next to its id"), +	OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"),  	OPT_STRING('e', "expr", &ev_qualifier_str, "expr",  		    "list of events to trace"),  	OPT_STRING('o', "output", &output_name, "file", "output file name"), @@ -1219,8 +2314,9 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)  		    "list of cpus to monitor"),  	OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit,  		    "child tasks do not inherit counters"), -	OPT_UINTEGER('m', "mmap-pages", &trace.opts.mmap_pages, -		     "number of mmap data pages"), +	OPT_CALLBACK('m', "mmap-pages", &trace.opts.mmap_pages, "pages", +		     "number of mmap data pages", +		     perf_evlist__parse_mmap_pages),  	OPT_STRING('u', "uid", &trace.opts.target.uid_str, "user",  		   "user to profile"),  	OPT_CALLBACK(0, "duration", &trace, "float", @@ -1228,13 +2324,26 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)  		     trace__set_duration),  	OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"),  	OPT_INCR('v', "verbose", &verbose, "be more verbose"), +	OPT_BOOLEAN('T', "time", &trace.full_time, +		    "Show full timestamp, not time relative to first start"), +	OPT_BOOLEAN('s', "summary", &trace.summary_only, +		    "Show only syscall summary with statistics"), +	OPT_BOOLEAN('S', "with-summary", &trace.summary, +		    "Show all syscalls and summary with statistics"),  	OPT_END()  	};  	int err;  	char bf[BUFSIZ]; +	if ((argc > 1) && (strcmp(argv[1], "record") == 0)) +		return trace__record(argc-2, &argv[2]); +  	argc = parse_options(argc, argv, trace_options, trace_usage, 0); +	/* summary_only implies summary option, but don't overwrite summary if set */ +	if (trace.summary_only) +		trace.summary = trace.summary_only; +  	if (output_name != NULL) {  		err = trace__open_output(&trace, output_name);  		if (err < 0) { @@ -1258,21 +2367,21 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)  		}  	} -	err = perf_target__validate(&trace.opts.target); +	err = target__validate(&trace.opts.target);  	if (err) { -		perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); +		target__strerror(&trace.opts.target, err, bf, sizeof(bf));  		fprintf(trace.output, "%s", bf);  		goto out_close;  	} -	err = perf_target__parse_uid(&trace.opts.target); +	err = target__parse_uid(&trace.opts.target);  	if (err) { -		perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); +		target__strerror(&trace.opts.target, err, bf, sizeof(bf));  		fprintf(trace.output, "%s", bf);  		goto out_close;  	} -	if (!argc && perf_target__none(&trace.opts.target)) +	if (!argc && target__none(&trace.opts.target))  		trace.opts.target.system_wide = true;  	if (input_name) @@ -1280,9 +2389,6 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)  	else  		err = trace__run(&trace, argc, argv); -	if (trace.sched && !err) -		trace__fprintf_thread_summary(&trace, trace.output); -  out_close:  	if (output_name != NULL)  		fclose(trace.output); diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 5f6f9b3271b..f30ac5e5d27 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -1,57 +1,92 @@ -uname_M := $(shell uname -m 2>/dev/null || echo not) -ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ -                                  -e s/arm.*/arm/ -e s/sa110/arm/ \ -                                  -e s/s390x/s390/ -e s/parisc64/parisc/ \ -                                  -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ -                                  -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ ) -NO_PERF_REGS := 1 -CFLAGS := $(EXTRA_CFLAGS) $(EXTRA_WARNINGS) +ifeq ($(src-perf),) +src-perf := $(srctree)/tools/perf +endif -# Additional ARCH settings for x86 -ifeq ($(ARCH),i386) -  override ARCH := x86 -  NO_PERF_REGS := 0 -  LIBUNWIND_LIBS = -lunwind -lunwind-x86 +ifeq ($(obj-perf),) +obj-perf := $(OUTPUT)  endif -ifeq ($(ARCH),x86_64) -  override ARCH := x86 -  IS_X86_64 := 0 -  ifeq (, $(findstring m32,$(CFLAGS))) -    IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -x c - | tail -n 1) -  endif +ifneq ($(obj-perf),) +obj-perf := $(abspath $(obj-perf))/ +endif + +LIB_INCLUDE := $(srctree)/tools/lib/ +CFLAGS := $(EXTRA_CFLAGS) $(EXTRA_WARNINGS) + +include $(src-perf)/config/Makefile.arch + +NO_PERF_REGS := 1 + +# Additional ARCH settings for x86 +ifeq ($(ARCH),x86)    ifeq (${IS_X86_64}, 1) -    RAW_ARCH := x86_64 -    CFLAGS += -DARCH_X86_64 +    CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT      ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S +    LIBUNWIND_LIBS = -lunwind -lunwind-x86_64 +  else +    LIBUNWIND_LIBS = -lunwind -lunwind-x86    endif    NO_PERF_REGS := 0 -  LIBUNWIND_LIBS = -lunwind -lunwind-x86_64  endif -ifeq ($(NO_PERF_REGS),0) -  CFLAGS += -DHAVE_PERF_REGS +ifeq ($(ARCH),arm) +  NO_PERF_REGS := 0 +  LIBUNWIND_LIBS = -lunwind -lunwind-arm  endif -ifeq ($(src-perf),) -src-perf := $(srctree)/tools/perf +ifeq ($(ARCH),arm64) +  NO_PERF_REGS := 0 +  LIBUNWIND_LIBS = -lunwind -lunwind-aarch64  endif -ifeq ($(obj-perf),) -obj-perf := $(OUTPUT) +# So far there's only x86 and arm libdw unwind support merged in perf. +# Disable it on all other architectures in case libdw unwind +# support is detected in system. Add supported architectures +# to the check. +ifneq ($(ARCH),$(filter $(ARCH),x86 arm)) +  NO_LIBDW_DWARF_UNWIND := 1  endif -ifneq ($(obj-perf),) -obj-perf := $(abspath $(obj-perf))/ +ifeq ($(LIBUNWIND_LIBS),) +  NO_LIBUNWIND := 1 +else +  # +  # For linking with debug library, run like: +  # +  #   make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ +  # +  ifdef LIBUNWIND_DIR +    LIBUNWIND_CFLAGS  = -I$(LIBUNWIND_DIR)/include +    LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib +  endif +  LIBUNWIND_LDFLAGS += $(LIBUNWIND_LIBS) + +  # Set per-feature check compilation flags +  FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS) +  FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) +  FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS) +  FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS)  endif -LIB_INCLUDE := $(srctree)/tools/lib/ +ifeq ($(NO_PERF_REGS),0) +  CFLAGS += -DHAVE_PERF_REGS_SUPPORT +endif + +ifndef NO_LIBELF +  # for linking with debug library, run like: +  # make DEBUG=1 LIBDW_DIR=/opt/libdw/ +  ifdef LIBDW_DIR +    LIBDW_CFLAGS  := -I$(LIBDW_DIR)/include +    LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib +  endif +  FEATURE_CHECK_CFLAGS-libdw-dwarf-unwind := $(LIBDW_CFLAGS) +  FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind := $(LIBDW_LDFLAGS) -ldw +endif  # include ARCH specific config  -include $(src-perf)/arch/$(ARCH)/Makefile -include $(src-perf)/config/feature-tests.mak  include $(src-perf)/config/utilities.mak  ifeq ($(call get-executable,$(FLEX)),) @@ -67,10 +102,11 @@ ifneq ($(WERROR),0)    CFLAGS += -Werror  endif -ifeq ("$(origin DEBUG)", "command line") -  PERF_DEBUG = $(DEBUG) +ifndef DEBUG +  DEBUG := 0  endif -ifndef PERF_DEBUG + +ifeq ($(DEBUG),0)    CFLAGS += -O6  endif @@ -87,28 +123,133 @@ CFLAGS += -Wall  CFLAGS += -Wextra  CFLAGS += -std=gnu99 +# Enforce a non-executable stack, as we may regress (again) in the future by +# adding assembler files missing the .GNU-stack linker note. +LDFLAGS += -Wl,-z,noexecstack +  EXTLIBS = -lelf -lpthread -lrt -lm -ldl -ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y) -  CFLAGS += -fstack-protector-all +ifneq ($(OUTPUT),) +  OUTPUT_FEATURES = $(OUTPUT)config/feature-checks/ +  $(shell mkdir -p $(OUTPUT_FEATURES))  endif -ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wstack-protector,-Wstack-protector),y) -  CFLAGS += -Wstack-protector +feature_check = $(eval $(feature_check_code)) +define feature_check_code +  feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C config/feature-checks test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0) +endef + +feature_set = $(eval $(feature_set_code)) +define feature_set_code +  feature-$(1) := 1 +endef + +# +# Build the feature check binaries in parallel, ignore errors, ignore return value and suppress output: +# + +# +# Note that this is not a complete list of all feature tests, just +# those that are typically built on a fully configured system. +# +# [ Feature tests not mentioned here have to be built explicitly in +#   the rule that uses them - an example for that is the 'bionic' +#   feature check. ] +# +CORE_FEATURE_TESTS =			\ +	backtrace			\ +	dwarf				\ +	fortify-source			\ +	glibc				\ +	gtk2				\ +	gtk2-infobar			\ +	libaudit			\ +	libbfd				\ +	libelf				\ +	libelf-getphdrnum		\ +	libelf-mmap			\ +	libnuma				\ +	libperl				\ +	libpython			\ +	libpython-version		\ +	libslang			\ +	libunwind			\ +	stackprotector-all		\ +	timerfd				\ +	libdw-dwarf-unwind + +LIB_FEATURE_TESTS =			\ +	dwarf				\ +	glibc				\ +	gtk2				\ +	libaudit			\ +	libbfd				\ +	libelf				\ +	libnuma				\ +	libperl				\ +	libpython			\ +	libslang			\ +	libunwind			\ +	libdw-dwarf-unwind + +VF_FEATURE_TESTS =			\ +	backtrace			\ +	fortify-source			\ +	gtk2-infobar			\ +	libelf-getphdrnum		\ +	libelf-mmap			\ +	libpython-version		\ +	stackprotector-all		\ +	timerfd				\ +	libunwind-debug-frame		\ +	bionic				\ +	liberty				\ +	liberty-z			\ +	cplus-demangle + +# Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features. +# If in the future we need per-feature checks/flags for features not +# mentioned in this list we need to refactor this ;-). +set_test_all_flags = $(eval $(set_test_all_flags_code)) +define set_test_all_flags_code +  FEATURE_CHECK_CFLAGS-all  += $(FEATURE_CHECK_CFLAGS-$(1)) +  FEATURE_CHECK_LDFLAGS-all += $(FEATURE_CHECK_LDFLAGS-$(1)) +endef + +$(foreach feat,$(CORE_FEATURE_TESTS),$(call set_test_all_flags,$(feat))) + +# +# Special fast-path for the 'all features are available' case: +# +$(call feature_check,all,$(MSG)) + +# +# Just in case the build freshly failed, make sure we print the +# feature matrix: +# +ifeq ($(feature-all), 1) +  # +  # test-all.c passed - just set all the core feature flags to 1: +  # +  $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_set,$(feat))) +else +  $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -i -j -C config/feature-checks $(addsuffix .bin,$(CORE_FEATURE_TESTS)) >/dev/null 2>&1) +  $(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_check,$(feat)))  endif -ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wvolatile-register-var,-Wvolatile-register-var),y) -  CFLAGS += -Wvolatile-register-var +ifeq ($(feature-stackprotector-all), 1) +  CFLAGS += -fstack-protector-all  endif -ifndef PERF_DEBUG -  ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -D_FORTIFY_SOURCE=2,-D_FORTIFY_SOURCE=2),y) +ifeq ($(DEBUG),0) +  ifeq ($(feature-fortify-source), 1)      CFLAGS += -D_FORTIFY_SOURCE=2    endif  endif  CFLAGS += -I$(src-perf)/util/include  CFLAGS += -I$(src-perf)/arch/$(ARCH)/include +CFLAGS += -I$(srctree)/tools/include/  CFLAGS += -I$(srctree)/arch/$(ARCH)/include/uapi  CFLAGS += -I$(srctree)/arch/$(ARCH)/include  CFLAGS += -I$(srctree)/include/uapi @@ -128,120 +269,137 @@ CFLAGS += -I$(LIB_INCLUDE)  CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE  ifndef NO_BIONIC -ifeq ($(call try-cc,$(SOURCE_BIONIC),$(CFLAGS),bionic),y) -  BIONIC := 1 -  EXTLIBS := $(filter-out -lrt,$(EXTLIBS)) -  EXTLIBS := $(filter-out -lpthread,$(EXTLIBS)) +  $(call feature_check,bionic) +  ifeq ($(feature-bionic), 1) +    BIONIC := 1 +    EXTLIBS := $(filter-out -lrt,$(EXTLIBS)) +    EXTLIBS := $(filter-out -lpthread,$(EXTLIBS)) +  endif  endif -endif # NO_BIONIC  ifdef NO_LIBELF    NO_DWARF := 1    NO_DEMANGLE := 1    NO_LIBUNWIND := 1 +  NO_LIBDW_DWARF_UNWIND := 1  else -FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y) -  FLAGS_GLIBC=$(CFLAGS) $(LDFLAGS) -  ifeq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC),glibc),y) -    LIBC_SUPPORT := 1 -  endif -  ifeq ($(BIONIC),1) -    LIBC_SUPPORT := 1 -  endif -  ifeq ($(LIBC_SUPPORT),1) -    msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev); - -    NO_LIBELF := 1 -    NO_DWARF := 1 -    NO_DEMANGLE := 1 +  ifeq ($(feature-libelf), 0) +    ifeq ($(feature-glibc), 1) +      LIBC_SUPPORT := 1 +    endif +    ifeq ($(BIONIC),1) +      LIBC_SUPPORT := 1 +    endif +    ifeq ($(LIBC_SUPPORT),1) +      msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev); + +      NO_LIBELF := 1 +      NO_DWARF := 1 +      NO_DEMANGLE := 1 +      NO_LIBUNWIND := 1 +      NO_LIBDW_DWARF_UNWIND := 1 +    else +      ifneq ($(filter s% -static%,$(LDFLAGS),),) +        msg := $(error No static glibc found, please install glibc-static); +      else +        msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]); +      endif +    endif    else -    msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); -  endif -else -  # for linking with debug library, run like: -  # make DEBUG=1 LIBDW_DIR=/opt/libdw/ -  ifdef LIBDW_DIR -    LIBDW_CFLAGS  := -I$(LIBDW_DIR)/include -    LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib -  endif - -  FLAGS_DWARF=$(CFLAGS) $(LIBDW_CFLAGS) -ldw -lz -lelf $(LIBDW_LDFLAGS) $(LDFLAGS) $(EXTLIBS) -  ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF),libdw),y) -    msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); -    NO_DWARF := 1 -  endif # Dwarf support -endif # SOURCE_LIBELF +    ifndef NO_LIBDW_DWARF_UNWIND +      ifneq ($(feature-libdw-dwarf-unwind),1) +        NO_LIBDW_DWARF_UNWIND := 1 +        msg := $(warning No libdw DWARF unwind found, Please install elfutils-devel/libdw-dev >= 0.158 and/or set LIBDW_DIR); +      endif +    endif +    ifneq ($(feature-dwarf), 1) +      msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); +      NO_DWARF := 1 +    endif # Dwarf support +  endif # libelf support  endif # NO_LIBELF  ifndef NO_LIBELF -CFLAGS += -DLIBELF_SUPPORT -FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y) -  CFLAGS += -DLIBELF_MMAP -endif -ifeq ($(call try-cc,$(SOURCE_ELF_GETPHDRNUM),$(FLAGS_LIBELF),-DHAVE_ELF_GETPHDRNUM),y) -  CFLAGS += -DHAVE_ELF_GETPHDRNUM -endif +  CFLAGS += -DHAVE_LIBELF_SUPPORT -# include ARCH specific config --include $(src-perf)/arch/$(ARCH)/Makefile +  ifeq ($(feature-libelf-mmap), 1) +    CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT +  endif -ifndef NO_DWARF -ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) -  msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); -  NO_DWARF := 1 -else -  CFLAGS += -DDWARF_SUPPORT $(LIBDW_CFLAGS) -  LDFLAGS += $(LIBDW_LDFLAGS) -  EXTLIBS += -lelf -ldw -endif # PERF_HAVE_DWARF_REGS -endif # NO_DWARF +  ifeq ($(feature-libelf-getphdrnum), 1) +    CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT +  endif -endif # NO_LIBELF +  # include ARCH specific config +  -include $(src-perf)/arch/$(ARCH)/Makefile -ifndef NO_LIBELF -CFLAGS += -DLIBELF_SUPPORT -FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y) -  CFLAGS += -DLIBELF_MMAP -endif # try-cc +  ifndef NO_DWARF +    ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) +      msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); +      NO_DWARF := 1 +    else +      CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS) +      LDFLAGS += $(LIBDW_LDFLAGS) +      EXTLIBS += -lelf -ldw +    endif # PERF_HAVE_DWARF_REGS +  endif # NO_DWARF  endif # NO_LIBELF -# There's only x86 (both 32 and 64) support for CFI unwind so far -ifneq ($(ARCH),x86) -  NO_LIBUNWIND := 1 +ifndef NO_LIBUNWIND +  ifneq ($(feature-libunwind), 1) +    msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR); +    NO_LIBUNWIND := 1 +  endif  endif -ifndef NO_LIBUNWIND -# for linking with debug library, run like: -# make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ -ifdef LIBUNWIND_DIR -  LIBUNWIND_CFLAGS  := -I$(LIBUNWIND_DIR)/include -  LIBUNWIND_LDFLAGS := -L$(LIBUNWIND_DIR)/lib +dwarf-post-unwind := 1 +dwarf-post-unwind-text := BUG + +# setup DWARF post unwinder +ifdef NO_LIBUNWIND +  ifdef NO_LIBDW_DWARF_UNWIND +    msg := $(warning Disabling post unwind, no support found.); +    dwarf-post-unwind := 0 +  else +    dwarf-post-unwind-text := libdw +  endif +else +  dwarf-post-unwind-text := libunwind +  # Enable libunwind support by default. +  ifndef NO_LIBDW_DWARF_UNWIND +    NO_LIBDW_DWARF_UNWIND := 1 +  endif  endif -FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(CFLAGS) $(LIBUNWIND_LDFLAGS) $(LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) -ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y) -  msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); -  NO_LIBUNWIND := 1 -endif # Libunwind support -endif # NO_LIBUNWIND +ifeq ($(dwarf-post-unwind),1) +  CFLAGS += -DHAVE_DWARF_UNWIND_SUPPORT +else +  NO_DWARF_UNWIND := 1 +endif  ifndef NO_LIBUNWIND -  CFLAGS += -DLIBUNWIND_SUPPORT +  ifeq ($(ARCH),$(filter $(ARCH),arm arm64)) +    $(call feature_check,libunwind-debug-frame) +    ifneq ($(feature-libunwind-debug-frame), 1) +      msg := $(warning No debug_frame support found in libunwind); +      CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME +    endif +  else +    # non-ARM has no dwarf_find_debug_frame() function: +    CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME +  endif +  CFLAGS  += -DHAVE_LIBUNWIND_SUPPORT    EXTLIBS += $(LIBUNWIND_LIBS) -  CFLAGS += $(LIBUNWIND_CFLAGS) +  CFLAGS  += $(LIBUNWIND_CFLAGS)    LDFLAGS += $(LIBUNWIND_LDFLAGS) -endif # NO_LIBUNWIND +endif  ifndef NO_LIBAUDIT -  FLAGS_LIBAUDIT = $(CFLAGS) $(LDFLAGS) -laudit -  ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT),libaudit),y) +  ifneq ($(feature-libaudit), 1)      msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev);      NO_LIBAUDIT := 1    else -    CFLAGS += -DLIBAUDIT_SUPPORT +    CFLAGS += -DHAVE_LIBAUDIT_SUPPORT      EXTLIBS += -laudit    endif  endif @@ -251,30 +409,30 @@ ifdef NO_NEWT  endif  ifndef NO_SLANG -  FLAGS_SLANG=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -I/usr/include/slang -lslang -  ifneq ($(call try-cc,$(SOURCE_SLANG),$(FLAGS_SLANG),libslang),y) +  ifneq ($(feature-libslang), 1)      msg := $(warning slang not found, disables TUI support. Please install slang-devel or libslang-dev);      NO_SLANG := 1    else      # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h      CFLAGS += -I/usr/include/slang -    CFLAGS += -DSLANG_SUPPORT +    CFLAGS += -DHAVE_SLANG_SUPPORT      EXTLIBS += -lslang    endif  endif  ifndef NO_GTK2 -  FLAGS_GTK2=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) -  ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2),gtk2),y) +  FLAGS_GTK2=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) +  ifneq ($(feature-gtk2), 1)      msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);      NO_GTK2 := 1    else -    ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2),-DHAVE_GTK_INFO_BAR),y) -      CFLAGS += -DHAVE_GTK_INFO_BAR +    ifeq ($(feature-gtk2-infobar), 1) +      GTK_CFLAGS := -DHAVE_GTK_INFO_BAR_SUPPORT      endif -    CFLAGS += -DGTK2_SUPPORT -    CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) -    EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null) +    CFLAGS += -DHAVE_GTK2_SUPPORT +    GTK_CFLAGS += $(shell $(PKG_CONFIG) --cflags gtk+-2.0 2>/dev/null) +    GTK_LIBS := $(shell $(PKG_CONFIG) --libs gtk+-2.0 2>/dev/null) +    EXTLIBS += -ldl    endif  endif @@ -290,15 +448,22 @@ else    PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null`    FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) -  ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED),perl),y) +  ifneq ($(feature-libperl), 1)      CFLAGS += -DNO_LIBPERL      NO_LIBPERL := 1 +    msg := $(warning Missing perl devel files. Disabling perl scripting support, consider installing perl-ExtUtils-Embed);    else      LDFLAGS += $(PERL_EMBED_LDFLAGS)      EXTLIBS += $(PERL_EMBED_LIBADD)    endif  endif +ifeq ($(feature-timerfd), 1) +  CFLAGS += -DHAVE_TIMERFD_SUPPORT +else +  msg := $(warning No timerfd support. Disables 'perf kvm stat live'); +endif +  disable-python = $(eval $(disable-python_code))  define disable-python_code    CFLAGS += -DNO_LIBPYTHON @@ -335,11 +500,11 @@ else        PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null)        FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) -      ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED),python),y) +      ifneq ($(feature-libpython), 1)          $(call disable-python,Python.h (for Python 2.x))        else -        ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED),python version),y) +        ifneq ($(feature-libpython-version), 1)            $(warning Python 3 is not yet supported; please set)            $(warning PYTHON and/or PYTHON_CONFIG appropriately.)            $(warning If you also have Python 2 installed, then) @@ -362,33 +527,39 @@ else    endif  endif +ifeq ($(feature-libbfd), 1) +  EXTLIBS += -lbfd + +  # call all detections now so we get correct +  # status in VF output +  $(call feature_check,liberty) +  $(call feature_check,liberty-z) +  $(call feature_check,cplus-demangle) + +  ifeq ($(feature-liberty), 1) +    EXTLIBS += -liberty +  else +    ifeq ($(feature-liberty-z), 1) +      EXTLIBS += -liberty -lz +    endif +  endif +endif +  ifdef NO_DEMANGLE    CFLAGS += -DNO_DEMANGLE  else -  ifdef HAVE_CPLUS_DEMANGLE +  ifdef HAVE_CPLUS_DEMANGLE_SUPPORT      EXTLIBS += -liberty -    CFLAGS += -DHAVE_CPLUS_DEMANGLE +    CFLAGS += -DHAVE_CPLUS_DEMANGLE_SUPPORT    else -    FLAGS_BFD=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd -    has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD),libbfd) -    ifeq ($(has_bfd),y) -      EXTLIBS += -lbfd -    else -      FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty -      has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY),liberty) -      ifeq ($(has_bfd_iberty),y) -        EXTLIBS += -lbfd -liberty -      else -        FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz -        has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z),libz) -        ifeq ($(has_bfd_iberty_z),y) -          EXTLIBS += -lbfd -liberty -lz -        else -          FLAGS_CPLUS_DEMANGLE=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -liberty -          has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE),demangle) -          ifeq ($(has_cplus_demangle),y) +    ifneq ($(feature-libbfd), 1) +      ifneq ($(feature-liberty), 1) +        ifneq ($(feature-liberty-z), 1) +          # we dont have neither HAVE_CPLUS_DEMANGLE_SUPPORT +          # or any of 'bfd iberty z' trinity +          ifeq ($(feature-cplus-demangle), 1)              EXTLIBS += -liberty -            CFLAGS += -DHAVE_CPLUS_DEMANGLE +            CFLAGS += -DHAVE_CPLUS_DEMANGLE_SUPPORT            else              msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling)              CFLAGS += -DNO_DEMANGLE @@ -399,31 +570,22 @@ else    endif  endif -ifndef NO_STRLCPY -  ifeq ($(call try-cc,$(SOURCE_STRLCPY),,-DHAVE_STRLCPY),y) -    CFLAGS += -DHAVE_STRLCPY -  endif -endif - -ifndef NO_ON_EXIT -  ifeq ($(call try-cc,$(SOURCE_ON_EXIT),,-DHAVE_ON_EXIT),y) -    CFLAGS += -DHAVE_ON_EXIT -  endif +ifneq ($(filter -lbfd,$(EXTLIBS)),) +  CFLAGS += -DHAVE_LIBBFD_SUPPORT  endif  ifndef NO_BACKTRACE -  ifeq ($(call try-cc,$(SOURCE_BACKTRACE),,-DBACKTRACE_SUPPORT),y) -    CFLAGS += -DBACKTRACE_SUPPORT +  ifeq ($(feature-backtrace), 1) +    CFLAGS += -DHAVE_BACKTRACE_SUPPORT    endif  endif  ifndef NO_LIBNUMA -  FLAGS_LIBNUMA = $(CFLAGS) $(LDFLAGS) -lnuma -  ifneq ($(call try-cc,$(SOURCE_LIBNUMA),$(FLAGS_LIBNUMA),libnuma),y) -    msg := $(warning No numa.h found, disables 'perf bench numa mem' benchmark, please install numa-libs-devel or libnuma-dev); +  ifeq ($(feature-libnuma), 0) +    msg := $(warning No numa.h found, disables 'perf bench numa mem' benchmark, please install numactl-devel/libnuma-devel/libnuma-dev);      NO_LIBNUMA := 1    else -    CFLAGS += -DLIBNUMA_SUPPORT +    CFLAGS += -DHAVE_LIBNUMA_SUPPORT      EXTLIBS += -lnuma    endif  endif @@ -442,7 +604,7 @@ endif  # Make the path relative to DESTDIR, not to prefix  ifndef DESTDIR -prefix = $(HOME) +prefix ?= $(HOME)  endif  bindir_relative = bin  bindir = $(prefix)/$(bindir_relative) @@ -459,7 +621,12 @@ else  sysconfdir = $(prefix)/etc  ETC_PERFCONFIG = etc/perfconfig  endif +ifeq ($(IS_X86_64),1) +lib = lib64 +else  lib = lib +endif +libdir = $(prefix)/$(lib)  # Shell quote (do not use $(call) to accommodate ancient setups);  ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG)) @@ -472,6 +639,7 @@ template_dir_SQ = $(subst ','\'',$(template_dir))  htmldir_SQ = $(subst ','\'',$(htmldir))  prefix_SQ = $(subst ','\'',$(prefix))  sysconfdir_SQ = $(subst ','\'',$(sysconfdir)) +libdir_SQ = $(subst ','\'',$(libdir))  ifneq ($(filter /%,$(firstword $(perfexecdir))),)  perfexec_instdir = $(perfexecdir) @@ -479,3 +647,92 @@ else  perfexec_instdir = $(prefix)/$(perfexecdir)  endif  perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) + +# If we install to $(HOME) we keep the traceevent default: +# $(HOME)/.traceevent/plugins +# Otherwise we install plugins into the global $(libdir). +ifdef DESTDIR +plugindir=$(libdir)/traceevent/plugins +plugindir_SQ= $(subst ','\'',$(plugindir)) +endif + +# +# Print the result of the feature test: +# +feature_print_status = $(eval $(feature_print_status_code)) $(info $(MSG)) + +define feature_print_status_code +  ifeq ($(feature-$(1)), 1) +    MSG = $(shell printf '...%30s: [ \033[32mon\033[m  ]' $(1)) +  else +    MSG = $(shell printf '...%30s: [ \033[31mOFF\033[m ]' $(1)) +  endif +endef + +feature_print_var = $(eval $(feature_print_var_code)) $(info $(MSG)) +define feature_print_var_code +    MSG = $(shell printf '...%30s: %s' $(1) $($(1))) +endef + +feature_print_text = $(eval $(feature_print_text_code)) $(info $(MSG)) +define feature_print_text_code +    MSG = $(shell printf '...%30s: %s' $(1) $(2)) +endef + +PERF_FEATURES := $(foreach feat,$(LIB_FEATURE_TESTS),feature-$(feat)($(feature-$(feat)))) +PERF_FEATURES_FILE := $(shell touch $(OUTPUT)PERF-FEATURES; cat $(OUTPUT)PERF-FEATURES) + +ifeq ($(dwarf-post-unwind),1) +  PERF_FEATURES += dwarf-post-unwind($(dwarf-post-unwind-text)) +endif + +# The $(display_lib) controls the default detection message +# output. It's set if: +# - detected features differes from stored features from +#   last build (in PERF-FEATURES file) +# - one of the $(LIB_FEATURE_TESTS) is not detected +# - VF is enabled + +ifneq ("$(PERF_FEATURES)","$(PERF_FEATURES_FILE)") +  $(shell echo "$(PERF_FEATURES)" > $(OUTPUT)PERF-FEATURES) +  display_lib := 1 +endif + +feature_check = $(eval $(feature_check_code)) +define feature_check_code +  ifneq ($(feature-$(1)), 1) +    display_lib := 1 +  endif +endef + +$(foreach feat,$(LIB_FEATURE_TESTS),$(call feature_check,$(feat))) + +ifeq ($(VF),1) +  display_lib := 1 +  display_vf := 1 +endif + +ifeq ($(display_lib),1) +  $(info ) +  $(info Auto-detecting system features:) +  $(foreach feat,$(LIB_FEATURE_TESTS),$(call feature_print_status,$(feat),)) + +  ifeq ($(dwarf-post-unwind),1) +    $(call feature_print_text,"DWARF post unwind library", $(dwarf-post-unwind-text)) +  endif +endif + +ifeq ($(display_vf),1) +  $(foreach feat,$(VF_FEATURE_TESTS),$(call feature_print_status,$(feat),)) +  $(info ) +  $(call feature_print_var,prefix) +  $(call feature_print_var,bindir) +  $(call feature_print_var,libdir) +  $(call feature_print_var,sysconfdir) +  $(call feature_print_var,LIBUNWIND_DIR) +  $(call feature_print_var,LIBDW_DIR) +endif + +ifeq ($(display_lib),1) +  $(info ) +endif diff --git a/tools/perf/config/Makefile.arch b/tools/perf/config/Makefile.arch new file mode 100644 index 00000000000..4b06719ee98 --- /dev/null +++ b/tools/perf/config/Makefile.arch @@ -0,0 +1,23 @@ + +uname_M := $(shell uname -m 2>/dev/null || echo not) + +ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ +                                  -e s/arm.*/arm/ -e s/sa110/arm/ \ +                                  -e s/s390x/s390/ -e s/parisc64/parisc/ \ +                                  -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ +                                  -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ \ +                                  -e s/tile.*/tile/ ) + +# Additional ARCH settings for x86 +ifeq ($(ARCH),i386) +  override ARCH := x86 +endif + +ifeq ($(ARCH),x86_64) +  override ARCH := x86 +  IS_X86_64 := 0 +  ifeq (, $(findstring m32,$(CFLAGS))) +    IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -x c - | tail -n 1) +    RAW_ARCH := x86_64 +  endif +endif diff --git a/tools/perf/config/feature-checks/.gitignore b/tools/perf/config/feature-checks/.gitignore new file mode 100644 index 00000000000..80f3da0c351 --- /dev/null +++ b/tools/perf/config/feature-checks/.gitignore @@ -0,0 +1,2 @@ +*.d +*.bin diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile new file mode 100644 index 00000000000..64c84e5f051 --- /dev/null +++ b/tools/perf/config/feature-checks/Makefile @@ -0,0 +1,149 @@ + +FILES=					\ +	test-all.bin			\ +	test-backtrace.bin		\ +	test-bionic.bin			\ +	test-dwarf.bin			\ +	test-fortify-source.bin		\ +	test-glibc.bin			\ +	test-gtk2.bin			\ +	test-gtk2-infobar.bin		\ +	test-hello.bin			\ +	test-libaudit.bin		\ +	test-libbfd.bin			\ +	test-liberty.bin		\ +	test-liberty-z.bin		\ +	test-cplus-demangle.bin		\ +	test-libelf.bin			\ +	test-libelf-getphdrnum.bin	\ +	test-libelf-mmap.bin		\ +	test-libnuma.bin		\ +	test-libperl.bin		\ +	test-libpython.bin		\ +	test-libpython-version.bin	\ +	test-libslang.bin		\ +	test-libunwind.bin		\ +	test-libunwind-debug-frame.bin	\ +	test-stackprotector-all.bin	\ +	test-timerfd.bin		\ +	test-libdw-dwarf-unwind.bin + +CC := $(CROSS_COMPILE)gcc -MD +PKG_CONFIG := $(CROSS_COMPILE)pkg-config + +all: $(FILES) + +BUILD = $(CC) $(CFLAGS) -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS) + +############################### + +test-all.bin: +	$(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl + +test-hello.bin: +	$(BUILD) + +test-stackprotector-all.bin: +	$(BUILD) -Werror -fstack-protector-all + +test-fortify-source.bin: +	$(BUILD) -O2 -Werror -D_FORTIFY_SOURCE=2 + +test-bionic.bin: +	$(BUILD) + +test-libelf.bin: +	$(BUILD) -lelf + +test-glibc.bin: +	$(BUILD) + +test-dwarf.bin: +	$(BUILD) -ldw + +test-libelf-mmap.bin: +	$(BUILD) -lelf + +test-libelf-getphdrnum.bin: +	$(BUILD) -lelf + +test-libnuma.bin: +	$(BUILD) -lnuma + +test-libunwind.bin: +	$(BUILD) -lelf + +test-libunwind-debug-frame.bin: +	$(BUILD) -lelf + +test-libaudit.bin: +	$(BUILD) -laudit + +test-libslang.bin: +	$(BUILD) -I/usr/include/slang -lslang + +test-gtk2.bin: +	$(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) + +test-gtk2-infobar.bin: +	$(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) + +grep-libs  = $(filter -l%,$(1)) +strip-libs = $(filter-out -l%,$(1)) + +PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null) +PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS)) +PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS)) +PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` +FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) + +test-libperl.bin: +	$(BUILD) $(FLAGS_PERL_EMBED) + +override PYTHON := python +override PYTHON_CONFIG := python-config + +escape-for-shell-sq =  $(subst ','\'',$(1)) +shell-sq = '$(escape-for-shell-sq)' + +PYTHON_CONFIG_SQ = $(call shell-sq,$(PYTHON_CONFIG)) + +PYTHON_EMBED_LDOPTS = $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) +PYTHON_EMBED_LDFLAGS = $(call strip-libs,$(PYTHON_EMBED_LDOPTS)) +PYTHON_EMBED_LIBADD = $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) +PYTHON_EMBED_CCOPTS = $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) +FLAGS_PYTHON_EMBED = $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) + +test-libpython.bin: +	$(BUILD) $(FLAGS_PYTHON_EMBED) + +test-libpython-version.bin: +	$(BUILD) $(FLAGS_PYTHON_EMBED) + +test-libbfd.bin: +	$(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl + +test-liberty.bin: +	$(CC) -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' -lbfd -ldl -liberty + +test-liberty-z.bin: +	$(CC) -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' -lbfd -ldl -liberty -lz + +test-cplus-demangle.bin: +	$(BUILD) -liberty + +test-backtrace.bin: +	$(BUILD) + +test-timerfd.bin: +	$(BUILD) + +test-libdw-dwarf-unwind.bin: +	$(BUILD) + +-include *.d + +############################### + +clean: +	rm -f $(FILES) *.d diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c new file mode 100644 index 00000000000..fe5c1e5c952 --- /dev/null +++ b/tools/perf/config/feature-checks/test-all.c @@ -0,0 +1,116 @@ +/* + * test-all.c: Try to build all the main testcases at once. + * + * A well-configured system will have all the prereqs installed, so we can speed + * up auto-detection on such systems. + */ + +/* + * Quirk: Python and Perl headers cannot be in arbitrary places, so keep + * these 3 testcases at the top: + */ +#define main main_test_libpython +# include "test-libpython.c" +#undef main + +#define main main_test_libpython_version +# include "test-libpython-version.c" +#undef main + +#define main main_test_libperl +# include "test-libperl.c" +#undef main + +#define main main_test_hello +# include "test-hello.c" +#undef main + +#define main main_test_libelf +# include "test-libelf.c" +#undef main + +#define main main_test_libelf_mmap +# include "test-libelf-mmap.c" +#undef main + +#define main main_test_glibc +# include "test-glibc.c" +#undef main + +#define main main_test_dwarf +# include "test-dwarf.c" +#undef main + +#define main main_test_libelf_getphdrnum +# include "test-libelf-getphdrnum.c" +#undef main + +#define main main_test_libunwind +# include "test-libunwind.c" +#undef main + +#define main main_test_libaudit +# include "test-libaudit.c" +#undef main + +#define main main_test_libslang +# include "test-libslang.c" +#undef main + +#define main main_test_gtk2 +# include "test-gtk2.c" +#undef main + +#define main main_test_gtk2_infobar +# include "test-gtk2-infobar.c" +#undef main + +#define main main_test_libbfd +# include "test-libbfd.c" +#undef main + +#define main main_test_backtrace +# include "test-backtrace.c" +#undef main + +#define main main_test_libnuma +# include "test-libnuma.c" +#undef main + +#define main main_test_timerfd +# include "test-timerfd.c" +#undef main + +#define main main_test_stackprotector_all +# include "test-stackprotector-all.c" +#undef main + +#define main main_test_libdw_dwarf_unwind +# include "test-libdw-dwarf-unwind.c" +#undef main + +int main(int argc, char *argv[]) +{ +	main_test_libpython(); +	main_test_libpython_version(); +	main_test_libperl(); +	main_test_hello(); +	main_test_libelf(); +	main_test_libelf_mmap(); +	main_test_glibc(); +	main_test_dwarf(); +	main_test_libelf_getphdrnum(); +	main_test_libunwind(); +	main_test_libaudit(); +	main_test_libslang(); +	main_test_gtk2(argc, argv); +	main_test_gtk2_infobar(argc, argv); +	main_test_libbfd(); +	main_test_backtrace(); +	main_test_libnuma(); +	main_test_timerfd(); +	main_test_stackprotector_all(); +	main_test_libdw_dwarf_unwind(); + +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-backtrace.c b/tools/perf/config/feature-checks/test-backtrace.c new file mode 100644 index 00000000000..7124aa1dc8f --- /dev/null +++ b/tools/perf/config/feature-checks/test-backtrace.c @@ -0,0 +1,13 @@ +#include <execinfo.h> +#include <stdio.h> + +int main(void) +{ +	void *backtrace_fns[10]; +	size_t entries; + +	entries = backtrace(backtrace_fns, 10); +	backtrace_symbols_fd(backtrace_fns, entries, 1); + +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-bionic.c b/tools/perf/config/feature-checks/test-bionic.c new file mode 100644 index 00000000000..eac24e9513e --- /dev/null +++ b/tools/perf/config/feature-checks/test-bionic.c @@ -0,0 +1,6 @@ +#include <android/api-level.h> + +int main(void) +{ +	return __ANDROID_API__; +} diff --git a/tools/perf/config/feature-checks/test-cplus-demangle.c b/tools/perf/config/feature-checks/test-cplus-demangle.c new file mode 100644 index 00000000000..610c686e000 --- /dev/null +++ b/tools/perf/config/feature-checks/test-cplus-demangle.c @@ -0,0 +1,14 @@ +extern int printf(const char *format, ...); +extern char *cplus_demangle(const char *, int); + +int main(void) +{ +	char symbol[4096] = "FieldName__9ClassNameFd"; +	char *tmp; + +	tmp = cplus_demangle(symbol, 0); + +	printf("demangled symbol: {%s}\n", tmp); + +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-dwarf.c b/tools/perf/config/feature-checks/test-dwarf.c new file mode 100644 index 00000000000..3fc1801ce4a --- /dev/null +++ b/tools/perf/config/feature-checks/test-dwarf.c @@ -0,0 +1,10 @@ +#include <dwarf.h> +#include <elfutils/libdw.h> +#include <elfutils/version.h> + +int main(void) +{ +	Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); + +	return (long)dbg; +} diff --git a/tools/perf/config/feature-checks/test-fortify-source.c b/tools/perf/config/feature-checks/test-fortify-source.c new file mode 100644 index 00000000000..c9f398d8786 --- /dev/null +++ b/tools/perf/config/feature-checks/test-fortify-source.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int main(void) +{ +	return puts("hi"); +} diff --git a/tools/perf/config/feature-checks/test-glibc.c b/tools/perf/config/feature-checks/test-glibc.c new file mode 100644 index 00000000000..b0820345cd9 --- /dev/null +++ b/tools/perf/config/feature-checks/test-glibc.c @@ -0,0 +1,8 @@ +#include <gnu/libc-version.h> + +int main(void) +{ +	const char *version = gnu_get_libc_version(); + +	return (long)version; +} diff --git a/tools/perf/config/feature-checks/test-gtk2-infobar.c b/tools/perf/config/feature-checks/test-gtk2-infobar.c new file mode 100644 index 00000000000..397b4646d06 --- /dev/null +++ b/tools/perf/config/feature-checks/test-gtk2-infobar.c @@ -0,0 +1,11 @@ +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#include <gtk/gtk.h> +#pragma GCC diagnostic error "-Wstrict-prototypes" + +int main(int argc, char *argv[]) +{ +	gtk_init(&argc, &argv); +	gtk_info_bar_new(); + +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-gtk2.c b/tools/perf/config/feature-checks/test-gtk2.c new file mode 100644 index 00000000000..6bd80e50943 --- /dev/null +++ b/tools/perf/config/feature-checks/test-gtk2.c @@ -0,0 +1,10 @@ +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#include <gtk/gtk.h> +#pragma GCC diagnostic error "-Wstrict-prototypes" + +int main(int argc, char *argv[]) +{ +	gtk_init(&argc, &argv); + +        return 0; +} diff --git a/tools/perf/config/feature-checks/test-hello.c b/tools/perf/config/feature-checks/test-hello.c new file mode 100644 index 00000000000..c9f398d8786 --- /dev/null +++ b/tools/perf/config/feature-checks/test-hello.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int main(void) +{ +	return puts("hi"); +} diff --git a/tools/perf/config/feature-checks/test-libaudit.c b/tools/perf/config/feature-checks/test-libaudit.c new file mode 100644 index 00000000000..afc019f0864 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libaudit.c @@ -0,0 +1,10 @@ +#include <libaudit.h> + +extern int printf(const char *format, ...); + +int main(void) +{ +	printf("error message: %s\n", audit_errno_to_name(0)); + +	return audit_open(); +} diff --git a/tools/perf/config/feature-checks/test-libbfd.c b/tools/perf/config/feature-checks/test-libbfd.c new file mode 100644 index 00000000000..24059907e99 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libbfd.c @@ -0,0 +1,15 @@ +#include <bfd.h> + +extern int printf(const char *format, ...); + +int main(void) +{ +	char symbol[4096] = "FieldName__9ClassNameFd"; +	char *tmp; + +	tmp = bfd_demangle(0, symbol, 0); + +	printf("demangled symbol: {%s}\n", tmp); + +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-libdw-dwarf-unwind.c b/tools/perf/config/feature-checks/test-libdw-dwarf-unwind.c new file mode 100644 index 00000000000..f676a3ff442 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libdw-dwarf-unwind.c @@ -0,0 +1,13 @@ + +#include <elfutils/libdwfl.h> + +int main(void) +{ +	/* +	 * This function is guarded via: __nonnull_attribute__ (1, 2). +	 * Passing '1' as arguments value. This code is never executed, +	 * only compiled. +	 */ +	dwfl_thread_getframes((void *) 1, (void *) 1, NULL); +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-libelf-getphdrnum.c b/tools/perf/config/feature-checks/test-libelf-getphdrnum.c new file mode 100644 index 00000000000..d710459306c --- /dev/null +++ b/tools/perf/config/feature-checks/test-libelf-getphdrnum.c @@ -0,0 +1,8 @@ +#include <libelf.h> + +int main(void) +{ +	size_t dst; + +	return elf_getphdrnum(0, &dst); +} diff --git a/tools/perf/config/feature-checks/test-libelf-mmap.c b/tools/perf/config/feature-checks/test-libelf-mmap.c new file mode 100644 index 00000000000..564427d7ef1 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libelf-mmap.c @@ -0,0 +1,8 @@ +#include <libelf.h> + +int main(void) +{ +	Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0); + +	return (long)elf; +} diff --git a/tools/perf/config/feature-checks/test-libelf.c b/tools/perf/config/feature-checks/test-libelf.c new file mode 100644 index 00000000000..08db322d895 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libelf.c @@ -0,0 +1,8 @@ +#include <libelf.h> + +int main(void) +{ +	Elf *elf = elf_begin(0, ELF_C_READ, 0); + +	return (long)elf; +} diff --git a/tools/perf/config/feature-checks/test-libnuma.c b/tools/perf/config/feature-checks/test-libnuma.c new file mode 100644 index 00000000000..4763d9cd587 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libnuma.c @@ -0,0 +1,9 @@ +#include <numa.h> +#include <numaif.h> + +int main(void) +{ +	numa_available(); + +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-libperl.c b/tools/perf/config/feature-checks/test-libperl.c new file mode 100644 index 00000000000..8871f6a0fdb --- /dev/null +++ b/tools/perf/config/feature-checks/test-libperl.c @@ -0,0 +1,9 @@ +#include <EXTERN.h> +#include <perl.h> + +int main(void) +{ +	perl_alloc(); + +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-libpython-version.c b/tools/perf/config/feature-checks/test-libpython-version.c new file mode 100644 index 00000000000..facea122d81 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libpython-version.c @@ -0,0 +1,10 @@ +#include <Python.h> + +#if PY_VERSION_HEX >= 0x03000000 +	#error +#endif + +int main(void) +{ +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-libpython.c b/tools/perf/config/feature-checks/test-libpython.c new file mode 100644 index 00000000000..b24b28ad632 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libpython.c @@ -0,0 +1,8 @@ +#include <Python.h> + +int main(void) +{ +	Py_Initialize(); + +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-libslang.c b/tools/perf/config/feature-checks/test-libslang.c new file mode 100644 index 00000000000..22ff22ed94d --- /dev/null +++ b/tools/perf/config/feature-checks/test-libslang.c @@ -0,0 +1,6 @@ +#include <slang.h> + +int main(void) +{ +	return SLsmg_init_smg(); +} diff --git a/tools/perf/config/feature-checks/test-libunwind-debug-frame.c b/tools/perf/config/feature-checks/test-libunwind-debug-frame.c new file mode 100644 index 00000000000..0ef8087a104 --- /dev/null +++ b/tools/perf/config/feature-checks/test-libunwind-debug-frame.c @@ -0,0 +1,16 @@ +#include <libunwind.h> +#include <stdlib.h> + +extern int +UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, +				 unw_word_t ip, unw_word_t segbase, +				 const char *obj_name, unw_word_t start, +				 unw_word_t end); + +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) + +int main(void) +{ +	dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0); +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-libunwind.c b/tools/perf/config/feature-checks/test-libunwind.c new file mode 100644 index 00000000000..43b9369bcab --- /dev/null +++ b/tools/perf/config/feature-checks/test-libunwind.c @@ -0,0 +1,27 @@ +#include <libunwind.h> +#include <stdlib.h> + +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, +                                      unw_word_t ip, +                                      unw_dyn_info_t *di, +                                      unw_proc_info_t *pi, +                                      int need_unwind_info, void *arg); + + +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) + +static unw_accessors_t accessors; + +int main(void) +{ +	unw_addr_space_t addr_space; + +	addr_space = unw_create_addr_space(&accessors, 0); +	if (addr_space) +		return 0; + +	unw_init_remote(NULL, addr_space, NULL); +	dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); + +	return 0; +} diff --git a/tools/perf/config/feature-checks/test-stackprotector-all.c b/tools/perf/config/feature-checks/test-stackprotector-all.c new file mode 100644 index 00000000000..c9f398d8786 --- /dev/null +++ b/tools/perf/config/feature-checks/test-stackprotector-all.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int main(void) +{ +	return puts("hi"); +} diff --git a/tools/perf/config/feature-checks/test-timerfd.c b/tools/perf/config/feature-checks/test-timerfd.c new file mode 100644 index 00000000000..8c5c083b4d3 --- /dev/null +++ b/tools/perf/config/feature-checks/test-timerfd.c @@ -0,0 +1,18 @@ +/* + * test for timerfd functions used by perf-kvm-stat-live + */ +#include <sys/timerfd.h> + +int main(void) +{ +	struct itimerspec new_value; + +	int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); +	if (fd < 0) +		return 1; + +	if (timerfd_settime(fd, 0, &new_value, NULL) != 0) +		return 1; + +	return 0; +} diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak deleted file mode 100644 index d5a8dd44945..00000000000 --- a/tools/perf/config/feature-tests.mak +++ /dev/null @@ -1,246 +0,0 @@ -define SOURCE_HELLO -#include <stdio.h> -int main(void) -{ -	return puts(\"hi\"); -} -endef - -ifndef NO_DWARF -define SOURCE_DWARF -#include <dwarf.h> -#include <elfutils/libdw.h> -#include <elfutils/version.h> -#ifndef _ELFUTILS_PREREQ -#error -#endif - -int main(void) -{ -	Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); -	return (long)dbg; -} -endef -endif - -define SOURCE_LIBELF -#include <libelf.h> - -int main(void) -{ -	Elf *elf = elf_begin(0, ELF_C_READ, 0); -	return (long)elf; -} -endef - -define SOURCE_GLIBC -#include <gnu/libc-version.h> - -int main(void) -{ -	const char *version = gnu_get_libc_version(); -	return (long)version; -} -endef - -define SOURCE_BIONIC -#include <android/api-level.h> - -int main(void) -{ -	return __ANDROID_API__; -} -endef - -define SOURCE_ELF_MMAP -#include <libelf.h> -int main(void) -{ -	Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0); -	return (long)elf; -} -endef - -define SOURCE_ELF_GETPHDRNUM -#include <libelf.h> -int main(void) -{ -	size_t dst; -	return elf_getphdrnum(0, &dst); -} -endef - -ifndef NO_SLANG -define SOURCE_SLANG -#include <slang.h> - -int main(void) -{ -	return SLsmg_init_smg(); -} -endef -endif - -ifndef NO_GTK2 -define SOURCE_GTK2 -#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" -#include <gtk/gtk.h> -#pragma GCC diagnostic error \"-Wstrict-prototypes\" - -int main(int argc, char *argv[]) -{ -        gtk_init(&argc, &argv); - -        return 0; -} -endef - -define SOURCE_GTK2_INFOBAR -#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" -#include <gtk/gtk.h> -#pragma GCC diagnostic error \"-Wstrict-prototypes\" - -int main(void) -{ -	gtk_info_bar_new(); - -	return 0; -} -endef -endif - -ifndef NO_LIBPERL -define SOURCE_PERL_EMBED -#include <EXTERN.h> -#include <perl.h> - -int main(void) -{ -perl_alloc(); -return 0; -} -endef -endif - -ifndef NO_LIBPYTHON -define SOURCE_PYTHON_VERSION -#include <Python.h> -#if PY_VERSION_HEX >= 0x03000000 -	#error -#endif -int main(void) -{ -	return 0; -} -endef -define SOURCE_PYTHON_EMBED -#include <Python.h> -int main(void) -{ -	Py_Initialize(); -	return 0; -} -endef -endif - -define SOURCE_BFD -#include <bfd.h> - -int main(void) -{ -	bfd_demangle(0, 0, 0); -	return 0; -} -endef - -define SOURCE_CPLUS_DEMANGLE -extern char *cplus_demangle(const char *, int); - -int main(void) -{ -	cplus_demangle(0, 0); -	return 0; -} -endef - -define SOURCE_STRLCPY -#include <stdlib.h> -extern size_t strlcpy(char *dest, const char *src, size_t size); - -int main(void) -{ -	strlcpy(NULL, NULL, 0); -	return 0; -} -endef - -ifndef NO_LIBUNWIND -define SOURCE_LIBUNWIND -#include <libunwind.h> -#include <stdlib.h> - -extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, -                                      unw_word_t ip, -                                      unw_dyn_info_t *di, -                                      unw_proc_info_t *pi, -                                      int need_unwind_info, void *arg); - - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -int main(void) -{ -	unw_addr_space_t addr_space; -	addr_space = unw_create_addr_space(NULL, 0); -	unw_init_remote(NULL, addr_space, NULL); -	dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); -	return 0; -} -endef -endif - -ifndef NO_BACKTRACE -define SOURCE_BACKTRACE -#include <execinfo.h> -#include <stdio.h> - -int main(void) -{ -	backtrace(NULL, 0); -	backtrace_symbols(NULL, 0); -	return 0; -} -endef -endif - -ifndef NO_LIBAUDIT -define SOURCE_LIBAUDIT -#include <libaudit.h> - -int main(void) -{ -	printf(\"error message: %s\n\", audit_errno_to_name(0)); -	return audit_open(); -} -endef -endif - -define SOURCE_ON_EXIT -#include <stdio.h> - -int main(void) -{ -	return on_exit(NULL, NULL); -} -endef - -define SOURCE_LIBNUMA -#include <numa.h> -#include <numaif.h> - -int main(void) -{ -	numa_available(); -	return 0; -} -endef diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak index 94d2d4f9c35..4d985e0f03f 100644 --- a/tools/perf/config/utilities.mak +++ b/tools/perf/config/utilities.mak @@ -178,17 +178,3 @@ endef  _ge_attempt = $(if $(get-executable),$(get-executable),$(_gea_warn)$(call _gea_err,$(2)))  _gea_warn = $(warning The path '$(1)' is not executable.)  _gea_err  = $(if $(1),$(error Please set '$(1)' appropriately)) - -# try-cc -# Usage: option = $(call try-cc, source-to-build, cc-options, msg) -ifneq ($(V),1) -TRY_CC_OUTPUT= > /dev/null 2>&1 -endif -TRY_CC_MSG=echo "    CHK $(3)" 1>&2; - -try-cc = $(shell sh -c						  \ -	'TMP="$(OUTPUT)$(TMPOUT).$$$$";				  \ -	 $(TRY_CC_MSG)						  \ -	 echo "$(1)" |						  \ -	 $(CC) -x c - $(2) -o "$$TMP" $(TRY_CC_OUTPUT) && echo y; \ -	 rm -f "$$TMP"') diff --git a/tools/perf/design.txt b/tools/perf/design.txt index 67e5d0cace8..a28dca2582a 100644 --- a/tools/perf/design.txt +++ b/tools/perf/design.txt @@ -18,7 +18,7 @@ underlying hardware counters.  Performance counters are accessed via special file descriptors.  There's one file descriptor per virtual counter used. -The special file descriptor is opened via the perf_event_open() +The special file descriptor is opened via the sys_perf_event_open()  system call:     int sys_perf_event_open(struct perf_event_attr *hw_event_uptr, @@ -82,7 +82,7 @@ machine-specific.  If 'raw_type' is 0, then the 'type' field says what kind of counter  this is, with the following encoding: -enum perf_event_types { +enum perf_type_id {  	PERF_TYPE_HARDWARE		= 0,  	PERF_TYPE_SOFTWARE		= 1,  	PERF_TYPE_TRACEPOINT		= 2, @@ -95,7 +95,7 @@ specified by 'event_id':   * Generalized performance counter event types, used by the hw_event.event_id   * parameter of the sys_perf_event_open() syscall:   */ -enum hw_event_ids { +enum perf_hw_id {  	/*  	 * Common hardware events, generalized by the kernel:  	 */ @@ -129,7 +129,7 @@ software events, selected by 'event_id':   * physical and sw events of the kernel (and allow the profiling of them as   * well):   */ -enum sw_event_ids { +enum perf_sw_ids {  	PERF_COUNT_SW_CPU_CLOCK		= 0,  	PERF_COUNT_SW_TASK_CLOCK	= 1,  	PERF_COUNT_SW_PAGE_FAULTS	= 2, @@ -230,7 +230,7 @@ these events are recorded in the ring-buffer (see below).  The 'comm' bit allows tracking of process comm data on process creation.  This too is recorded in the ring-buffer (see below). -The 'pid' parameter to the perf_event_open() system call allows the +The 'pid' parameter to the sys_perf_event_open() system call allows the  counter to be specific to a task:   pid == 0: if the pid parameter is zero, the counter is attached to the @@ -260,7 +260,7 @@ The 'flags' parameter is currently unused and must be zero.  The 'group_fd' parameter allows counter "groups" to be set up.  A  counter group has one counter which is the group "leader".  The leader -is created first, with group_fd = -1 in the perf_event_open call +is created first, with group_fd = -1 in the sys_perf_event_open call  that creates it.  The rest of the group members are created  subsequently, with group_fd giving the fd of the group leader.  (A single counter on its own is created with group_fd = -1 and is @@ -454,7 +454,6 @@ So to start with, in order to add HAVE_PERF_EVENTS to your Kconfig, you  will need at least this:  	- asm/perf_event.h - a basic stub will suffice at first  	- support for atomic64 types (and associated helper functions) -	- set_perf_event_pending() implemented  If your architecture does have hardware capabilities, you can override the  weak stub hw_perf_event_init() to register hardware counters. diff --git a/tools/perf/perf-completion.sh b/tools/perf/perf-completion.sh new file mode 100644 index 00000000000..33569847fdc --- /dev/null +++ b/tools/perf/perf-completion.sh @@ -0,0 +1,206 @@ +# perf bash and zsh completion + +# Taken from git.git's completion script. +__my_reassemble_comp_words_by_ref() +{ +	local exclude i j first +	# Which word separators to exclude? +	exclude="${1//[^$COMP_WORDBREAKS]}" +	cword_=$COMP_CWORD +	if [ -z "$exclude" ]; then +		words_=("${COMP_WORDS[@]}") +		return +	fi +	# List of word completion separators has shrunk; +	# re-assemble words to complete. +	for ((i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do +		# Append each nonempty word consisting of just +		# word separator characters to the current word. +		first=t +		while +			[ $i -gt 0 ] && +			[ -n "${COMP_WORDS[$i]}" ] && +			# word consists of excluded word separators +			[ "${COMP_WORDS[$i]//[^$exclude]}" = "${COMP_WORDS[$i]}" ] +		do +			# Attach to the previous token, +			# unless the previous token is the command name. +			if [ $j -ge 2 ] && [ -n "$first" ]; then +				((j--)) +			fi +			first= +			words_[$j]=${words_[j]}${COMP_WORDS[i]} +			if [ $i = $COMP_CWORD ]; then +				cword_=$j +			fi +			if (($i < ${#COMP_WORDS[@]} - 1)); then +				((i++)) +			else +				# Done. +				return +			fi +		done +		words_[$j]=${words_[j]}${COMP_WORDS[i]} +		if [ $i = $COMP_CWORD ]; then +			cword_=$j +		fi +	done +} + +type _get_comp_words_by_ref &>/dev/null || +_get_comp_words_by_ref() +{ +	local exclude cur_ words_ cword_ +	if [ "$1" = "-n" ]; then +		exclude=$2 +		shift 2 +	fi +	__my_reassemble_comp_words_by_ref "$exclude" +	cur_=${words_[cword_]} +	while [ $# -gt 0 ]; do +		case "$1" in +		cur) +			cur=$cur_ +			;; +		prev) +			prev=${words_[$cword_-1]} +			;; +		words) +			words=("${words_[@]}") +			;; +		cword) +			cword=$cword_ +			;; +		esac +		shift +	done +} + +type __ltrim_colon_completions &>/dev/null || +__ltrim_colon_completions() +{ +	if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then +		# Remove colon-word prefix from COMPREPLY items +		local colon_word=${1%"${1##*:}"} +		local i=${#COMPREPLY[*]} +		while [[ $((--i)) -ge 0 ]]; do +			COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} +		done +	fi +} + +__perfcomp () +{ +	COMPREPLY=( $( compgen -W "$1" -- "$2" ) ) +} + +__perfcomp_colon () +{ +	__perfcomp "$1" "$2" +	__ltrim_colon_completions $cur +} + +__perf_main () +{ +	local cmd + +	cmd=${words[0]} +	COMPREPLY=() + +	# List perf subcommands or long options +	if [ $cword -eq 1 ]; then +		if [[ $cur == --* ]]; then +			__perfcomp '--help --version \ +			--exec-path --html-path --paginate --no-pager \ +			--perf-dir --work-tree --debugfs-dir' -- "$cur" +		else +			cmds=$($cmd --list-cmds) +			__perfcomp "$cmds" "$cur" +		fi +	# List possible events for -e option +	elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then +		evts=$($cmd list --raw-dump) +		__perfcomp_colon "$evts" "$cur" +	# List subcommands for perf commands +	elif [[ $prev == @(kvm|kmem|mem|lock|sched) ]]; then +		subcmds=$($cmd $prev --list-cmds) +		__perfcomp_colon "$subcmds" "$cur" +	# List long option names +	elif [[ $cur == --* ]];  then +		subcmd=${words[1]} +		opts=$($cmd $subcmd --list-opts) +		__perfcomp "$opts" "$cur" +	fi +} + +if [[ -n ${ZSH_VERSION-} ]]; then +	autoload -U +X compinit && compinit + +	__perfcomp () +	{ +		emulate -L zsh + +		local c IFS=$' \t\n' +		local -a array + +		for c in ${=1}; do +			case $c in +			--*=*|*.) ;; +			*) c="$c " ;; +			esac +			array[${#array[@]}+1]="$c" +		done + +		compset -P '*[=:]' +		compadd -Q -S '' -a -- array && _ret=0 +	} + +	__perfcomp_colon () +	{ +		emulate -L zsh + +		local cur_="${2-$cur}" +		local c IFS=$' \t\n' +		local -a array + +		if [[ "$cur_" == *:* ]]; then +			local colon_word=${cur_%"${cur_##*:}"} +		fi + +		for c in ${=1}; do +			case $c in +			--*=*|*.) ;; +			*) c="$c " ;; +			esac +			array[$#array+1]=${c#"$colon_word"} +		done + +		compset -P '*[=:]' +		compadd -Q -S '' -a -- array && _ret=0 +	} + +	_perf () +	{ +		local _ret=1 cur cword prev +		cur=${words[CURRENT]} +		prev=${words[CURRENT-1]} +		let cword=CURRENT-1 +		emulate ksh -c __perf_main +		let _ret && _default && _ret=0 +		return _ret +	} + +	compdef _perf perf +	return +fi + +type perf &>/dev/null && +_perf() +{ +	local cur words cword prev +	_get_comp_words_by_ref -n =: cur words cword prev +	__perf_main +} && + +complete -o bashdefault -o default -o nospace -F _perf perf 2>/dev/null \ +	|| complete -o default -o nospace -F _perf perf diff --git a/tools/perf/perf-sys.h b/tools/perf/perf-sys.h new file mode 100644 index 00000000000..5268a1481d2 --- /dev/null +++ b/tools/perf/perf-sys.h @@ -0,0 +1,190 @@ +#ifndef _PERF_SYS_H +#define _PERF_SYS_H + +#include <unistd.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <linux/types.h> +#include <linux/perf_event.h> +#include <asm/unistd.h> + +#if defined(__i386__) +#define mb()		asm volatile("lock; addl $0,0(%%esp)" ::: "memory") +#define wmb()		asm volatile("lock; addl $0,0(%%esp)" ::: "memory") +#define rmb()		asm volatile("lock; addl $0,0(%%esp)" ::: "memory") +#define cpu_relax()	asm volatile("rep; nop" ::: "memory"); +#define CPUINFO_PROC	"model name" +#ifndef __NR_perf_event_open +# define __NR_perf_event_open 336 +#endif +#ifndef __NR_futex +# define __NR_futex 240 +#endif +#ifndef __NR_gettid +# define __NR_gettid 224 +#endif +#endif + +#if defined(__x86_64__) +#define mb()		asm volatile("mfence" ::: "memory") +#define wmb()		asm volatile("sfence" ::: "memory") +#define rmb()		asm volatile("lfence" ::: "memory") +#define cpu_relax()	asm volatile("rep; nop" ::: "memory"); +#define CPUINFO_PROC	"model name" +#ifndef __NR_perf_event_open +# define __NR_perf_event_open 298 +#endif +#ifndef __NR_futex +# define __NR_futex 202 +#endif +#ifndef __NR_gettid +# define __NR_gettid 186 +#endif +#endif + +#ifdef __powerpc__ +#include "../../arch/powerpc/include/uapi/asm/unistd.h" +#define mb()		asm volatile ("sync" ::: "memory") +#define wmb()		asm volatile ("sync" ::: "memory") +#define rmb()		asm volatile ("sync" ::: "memory") +#define CPUINFO_PROC	"cpu" +#endif + +#ifdef __s390__ +#define mb()		asm volatile("bcr 15,0" ::: "memory") +#define wmb()		asm volatile("bcr 15,0" ::: "memory") +#define rmb()		asm volatile("bcr 15,0" ::: "memory") +#endif + +#ifdef __sh__ +#if defined(__SH4A__) || defined(__SH5__) +# define mb()		asm volatile("synco" ::: "memory") +# define wmb()		asm volatile("synco" ::: "memory") +# define rmb()		asm volatile("synco" ::: "memory") +#else +# define mb()		asm volatile("" ::: "memory") +# define wmb()		asm volatile("" ::: "memory") +# define rmb()		asm volatile("" ::: "memory") +#endif +#define CPUINFO_PROC	"cpu type" +#endif + +#ifdef __hppa__ +#define mb()		asm volatile("" ::: "memory") +#define wmb()		asm volatile("" ::: "memory") +#define rmb()		asm volatile("" ::: "memory") +#define CPUINFO_PROC	"cpu" +#endif + +#ifdef __sparc__ +#ifdef __LP64__ +#define mb()		asm volatile("ba,pt %%xcc, 1f\n"	\ +				     "membar #StoreLoad\n"	\ +				     "1:\n":::"memory") +#else +#define mb()		asm volatile("":::"memory") +#endif +#define wmb()		asm volatile("":::"memory") +#define rmb()		asm volatile("":::"memory") +#define CPUINFO_PROC	"cpu" +#endif + +#ifdef __alpha__ +#define mb()		asm volatile("mb" ::: "memory") +#define wmb()		asm volatile("wmb" ::: "memory") +#define rmb()		asm volatile("mb" ::: "memory") +#define CPUINFO_PROC	"cpu model" +#endif + +#ifdef __ia64__ +#define mb()		asm volatile ("mf" ::: "memory") +#define wmb()		asm volatile ("mf" ::: "memory") +#define rmb()		asm volatile ("mf" ::: "memory") +#define cpu_relax()	asm volatile ("hint @pause" ::: "memory") +#define CPUINFO_PROC	"model name" +#endif + +#ifdef __arm__ +/* + * Use the __kuser_memory_barrier helper in the CPU helper page. See + * arch/arm/kernel/entry-armv.S in the kernel source for details. + */ +#define mb()		((void(*)(void))0xffff0fa0)() +#define wmb()		((void(*)(void))0xffff0fa0)() +#define rmb()		((void(*)(void))0xffff0fa0)() +#define CPUINFO_PROC	"Processor" +#endif + +#ifdef __aarch64__ +#define mb()		asm volatile("dmb ish" ::: "memory") +#define wmb()		asm volatile("dmb ishst" ::: "memory") +#define rmb()		asm volatile("dmb ishld" ::: "memory") +#define cpu_relax()	asm volatile("yield" ::: "memory") +#endif + +#ifdef __mips__ +#define mb()		asm volatile(					\ +				".set	mips2\n\t"			\ +				"sync\n\t"				\ +				".set	mips0"				\ +				: /* no output */			\ +				: /* no input */			\ +				: "memory") +#define wmb()	mb() +#define rmb()	mb() +#define CPUINFO_PROC	"cpu model" +#endif + +#ifdef __arc__ +#define mb()		asm volatile("" ::: "memory") +#define wmb()		asm volatile("" ::: "memory") +#define rmb()		asm volatile("" ::: "memory") +#define CPUINFO_PROC	"Processor" +#endif + +#ifdef __metag__ +#define mb()		asm volatile("" ::: "memory") +#define wmb()		asm volatile("" ::: "memory") +#define rmb()		asm volatile("" ::: "memory") +#define CPUINFO_PROC	"CPU" +#endif + +#ifdef __xtensa__ +#define mb()		asm volatile("memw" ::: "memory") +#define wmb()		asm volatile("memw" ::: "memory") +#define rmb()		asm volatile("" ::: "memory") +#define CPUINFO_PROC	"core ID" +#endif + +#ifdef __tile__ +#define mb()		asm volatile ("mf" ::: "memory") +#define wmb()		asm volatile ("mf" ::: "memory") +#define rmb()		asm volatile ("mf" ::: "memory") +#define cpu_relax()	asm volatile ("mfspr zero, PASS" ::: "memory") +#define CPUINFO_PROC    "model name" +#endif + +#define barrier() asm volatile ("" ::: "memory") + +#ifndef cpu_relax +#define cpu_relax() barrier() +#endif + +static inline int +sys_perf_event_open(struct perf_event_attr *attr, +		      pid_t pid, int cpu, int group_fd, +		      unsigned long flags) +{ +	int fd; + +	fd = syscall(__NR_perf_event_open, attr, pid, cpu, +		     group_fd, flags); + +#ifdef HAVE_ATTR_TEST +	if (unlikely(test_attr__enabled)) +		test_attr__open(attr, pid, cpu, fd, group_fd, flags); +#endif +	return fd; +} + +#endif /* _PERF_SYS_H */ diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 85e1aed9520..95c58fc1528 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -13,7 +13,7 @@  #include "util/quote.h"  #include "util/run-command.h"  #include "util/parse-events.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include <pthread.h>  const char perf_usage_string[] = @@ -49,14 +49,14 @@ static struct cmd_struct commands[] = {  	{ "version",	cmd_version,	0 },  	{ "script",	cmd_script,	0 },  	{ "sched",	cmd_sched,	0 }, -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT  	{ "probe",	cmd_probe,	0 },  #endif  	{ "kmem",	cmd_kmem,	0 },  	{ "lock",	cmd_lock,	0 },  	{ "kvm",	cmd_kvm,	0 },  	{ "test",	cmd_test,	0 }, -#ifdef LIBAUDIT_SUPPORT +#ifdef HAVE_LIBAUDIT_SUPPORT  	{ "trace",	cmd_trace,	0 },  #endif  	{ "inject",	cmd_inject,	0 }, @@ -456,7 +456,9 @@ int main(int argc, const char **argv)  {  	const char *cmd; +	/* The page_size is placed in util object. */  	page_size = sysconf(_SC_PAGE_SIZE); +	cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);  	cmd = perf_extract_argv0_path(argv[0]);  	if (!cmd) @@ -480,7 +482,18 @@ int main(int argc, const char **argv)  		fprintf(stderr, "cannot handle %s internally", cmd);  		goto out;  	} - +	if (!prefixcmp(cmd, "trace")) { +#ifdef HAVE_LIBAUDIT_SUPPORT +		set_buildid_dir(); +		setup_path(); +		argv[0] = "trace"; +		return cmd_trace(argc, argv, NULL); +#else +		fprintf(stderr, +			"trace command not available: missing audit-libs devel package at build time.\n"); +		goto out; +#endif +	}  	/* Look for flags.. */  	argv++;  	argc--; diff --git a/tools/perf/perf.h b/tools/perf/perf.h index cf20187eee0..510c65f7285 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -1,126 +1,18 @@  #ifndef _PERF_PERF_H  #define _PERF_PERF_H -#include <asm/unistd.h> - -#if defined(__i386__) -#define rmb()		asm volatile("lock; addl $0,0(%%esp)" ::: "memory") -#define cpu_relax()	asm volatile("rep; nop" ::: "memory"); -#define CPUINFO_PROC	"model name" -#ifndef __NR_perf_event_open -# define __NR_perf_event_open 336 -#endif -#endif - -#if defined(__x86_64__) -#define rmb()		asm volatile("lfence" ::: "memory") -#define cpu_relax()	asm volatile("rep; nop" ::: "memory"); -#define CPUINFO_PROC	"model name" -#ifndef __NR_perf_event_open -# define __NR_perf_event_open 298 -#endif -#endif - -#ifdef __powerpc__ -#include "../../arch/powerpc/include/uapi/asm/unistd.h" -#define rmb()		asm volatile ("sync" ::: "memory") -#define cpu_relax()	asm volatile ("" ::: "memory"); -#define CPUINFO_PROC	"cpu" -#endif - -#ifdef __s390__ -#define rmb()		asm volatile("bcr 15,0" ::: "memory") -#define cpu_relax()	asm volatile("" ::: "memory"); -#endif - -#ifdef __sh__ -#if defined(__SH4A__) || defined(__SH5__) -# define rmb()		asm volatile("synco" ::: "memory") -#else -# define rmb()		asm volatile("" ::: "memory") -#endif -#define cpu_relax()	asm volatile("" ::: "memory") -#define CPUINFO_PROC	"cpu type" -#endif - -#ifdef __hppa__ -#define rmb()		asm volatile("" ::: "memory") -#define cpu_relax()	asm volatile("" ::: "memory"); -#define CPUINFO_PROC	"cpu" -#endif - -#ifdef __sparc__ -#define rmb()		asm volatile("":::"memory") -#define cpu_relax()	asm volatile("":::"memory") -#define CPUINFO_PROC	"cpu" -#endif - -#ifdef __alpha__ -#define rmb()		asm volatile("mb" ::: "memory") -#define cpu_relax()	asm volatile("" ::: "memory") -#define CPUINFO_PROC	"cpu model" -#endif - -#ifdef __ia64__ -#define rmb()		asm volatile ("mf" ::: "memory") -#define cpu_relax()	asm volatile ("hint @pause" ::: "memory") -#define CPUINFO_PROC	"model name" -#endif - -#ifdef __arm__ -/* - * Use the __kuser_memory_barrier helper in the CPU helper page. See - * arch/arm/kernel/entry-armv.S in the kernel source for details. - */ -#define rmb()		((void(*)(void))0xffff0fa0)() -#define cpu_relax()	asm volatile("":::"memory") -#define CPUINFO_PROC	"Processor" -#endif - -#ifdef __aarch64__ -#define rmb()		asm volatile("dmb ld" ::: "memory") -#define cpu_relax()	asm volatile("yield" ::: "memory") -#endif - -#ifdef __mips__ -#define rmb()		asm volatile(					\ -				".set	mips2\n\t"			\ -				"sync\n\t"				\ -				".set	mips0"				\ -				: /* no output */			\ -				: /* no input */			\ -				: "memory") -#define cpu_relax()	asm volatile("" ::: "memory") -#define CPUINFO_PROC	"cpu model" -#endif - -#ifdef __arc__ -#define rmb()		asm volatile("" ::: "memory") -#define cpu_relax()	rmb() -#define CPUINFO_PROC	"Processor" -#endif - -#ifdef __metag__ -#define rmb()		asm volatile("" ::: "memory") -#define cpu_relax()	asm volatile("" ::: "memory") -#define CPUINFO_PROC	"CPU" -#endif -  #include <time.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/syscall.h> - -#include <linux/perf_event.h> -#include "util/types.h"  #include <stdbool.h> +#include <linux/types.h> +#include <linux/perf_event.h> + +extern bool test_attr__enabled; +void test_attr__init(void); +void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, +		     int fd, int group_fd, unsigned long flags); -/* - * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all - * counters in the current task. - */ -#define PR_TASK_PERF_EVENTS_DISABLE   31 -#define PR_TASK_PERF_EVENTS_ENABLE    32 +#define HAVE_ATTR_TEST +#include "perf-sys.h"  #ifndef NSEC_PER_SEC  # define NSEC_PER_SEC			1000000000ULL @@ -137,65 +29,8 @@ static inline unsigned long long rdclock(void)  	return ts.tv_sec * 1000000000ULL + ts.tv_nsec;  } -/* - * Pick up some kernel type conventions: - */ -#define __user -#define asmlinkage - -#define unlikely(x)	__builtin_expect(!!(x), 0) -#define min(x, y) ({				\ -	typeof(x) _min1 = (x);			\ -	typeof(y) _min2 = (y);			\ -	(void) (&_min1 == &_min2);		\ -	_min1 < _min2 ? _min1 : _min2; }) - -extern bool test_attr__enabled; -void test_attr__init(void); -void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, -		     int fd, int group_fd, unsigned long flags); - -static inline int -sys_perf_event_open(struct perf_event_attr *attr, -		      pid_t pid, int cpu, int group_fd, -		      unsigned long flags) -{ -	int fd; - -	fd = syscall(__NR_perf_event_open, attr, pid, cpu, -		     group_fd, flags); - -	if (unlikely(test_attr__enabled)) -		test_attr__open(attr, pid, cpu, fd, group_fd, flags); - -	return fd; -} - -#define MAX_COUNTERS			256  #define MAX_NR_CPUS			256 -struct ip_callchain { -	u64 nr; -	u64 ips[0]; -}; - -struct branch_flags { -	u64 mispred:1; -	u64 predicted:1; -	u64 reserved:62; -}; - -struct branch_entry { -	u64				from; -	u64				to; -	struct branch_flags flags; -}; - -struct branch_stack { -	u64				nr; -	struct branch_entry	entries[0]; -}; -  extern const char *input_name;  extern bool perf_host, perf_guest;  extern const char perf_version_string[]; @@ -204,21 +39,16 @@ void pthread__unblock_sigwinch(void);  #include "util/target.h" -enum perf_call_graph_mode { -	CALLCHAIN_NONE, -	CALLCHAIN_FP, -	CALLCHAIN_DWARF -}; - -struct perf_record_opts { -	struct perf_target target; +struct record_opts { +	struct target target;  	int	     call_graph; +	bool         call_graph_enabled;  	bool	     group;  	bool	     inherit_stat; -	bool	     no_delay; +	bool	     no_buffering;  	bool	     no_inherit; +	bool	     no_inherit_set;  	bool	     no_samples; -	bool	     pipe_output;  	bool	     raw_samples;  	bool	     sample_address;  	bool	     sample_weight; @@ -231,6 +61,8 @@ struct perf_record_opts {  	u64	     default_interval;  	u64	     user_interval;  	u16	     stack_dump_size; +	bool	     sample_transaction; +	unsigned     initial_delay;  };  #endif diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/perf/scripts/python/Perf-Trace-Util/Context.c index 315067b8f55..fcd1dd66790 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/Context.c +++ b/tools/perf/scripts/python/Perf-Trace-Util/Context.c @@ -25,7 +25,7 @@  PyMODINIT_FUNC initperf_trace_context(void); -static PyObject *perf_trace_context_common_pc(PyObject *self, PyObject *args) +static PyObject *perf_trace_context_common_pc(PyObject *obj, PyObject *args)  {  	static struct scripting_context *scripting_context;  	PyObject *context; @@ -40,7 +40,7 @@ static PyObject *perf_trace_context_common_pc(PyObject *self, PyObject *args)  	return Py_BuildValue("i", retval);  } -static PyObject *perf_trace_context_common_flags(PyObject *self, +static PyObject *perf_trace_context_common_flags(PyObject *obj,  						 PyObject *args)  {  	static struct scripting_context *scripting_context; @@ -56,7 +56,7 @@ static PyObject *perf_trace_context_common_flags(PyObject *self,  	return Py_BuildValue("i", retval);  } -static PyObject *perf_trace_context_common_lock_depth(PyObject *self, +static PyObject *perf_trace_context_common_lock_depth(PyObject *obj,  						      PyObject *args)  {  	static struct scripting_context *scripting_context; diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 00218f503b2..2dfc9ad0e6f 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -1,4 +1,3 @@ -  /*   * The struct perf_event_attr test support.   * @@ -19,14 +18,8 @@   * permissions. All the event text files are stored there.   */ -/* - * Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select - * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu. - */ -#define __SANE_USERSPACE_TYPES__  #include <stdlib.h>  #include <stdio.h> -#include <inttypes.h>  #include <linux/types.h>  #include <linux/kernel.h>  #include "../perf.h" diff --git a/tools/perf/tests/attr/README b/tools/perf/tests/attr/README index d102957cd59..430024f618f 100644 --- a/tools/perf/tests/attr/README +++ b/tools/perf/tests/attr/README @@ -44,9 +44,9 @@ Following tests are defined (with perf commands):    perf record -c 123 kill                       (test-record-count)    perf record -d kill                           (test-record-data)    perf record -F 100 kill                       (test-record-freq) -  perf record -g -- kill                        (test-record-graph-default) -  perf record -g dwarf -- kill                  (test-record-graph-dwarf) -  perf record -g fp kill                        (test-record-graph-fp) +  perf record -g kill                           (test-record-graph-default) +  perf record --call-graph dwarf kill		(test-record-graph-dwarf) +  perf record --call-graph fp kill              (test-record-graph-fp)    perf record --group -e cycles,instructions kill (test-record-group)    perf record -e '{cycles,instructions}' kill   (test-record-group1)    perf record -D kill                           (test-record-no-delay) diff --git a/tools/perf/tests/attr/test-record-graph-default b/tools/perf/tests/attr/test-record-graph-default index 833d1849d76..853597a9a8f 100644 --- a/tools/perf/tests/attr/test-record-graph-default +++ b/tools/perf/tests/attr/test-record-graph-default @@ -1,6 +1,6 @@  [config]  command = record -args    = -g -- kill >/dev/null 2>&1 +args    = -g kill >/dev/null 2>&1  [event:base-record]  sample_type=295 diff --git a/tools/perf/tests/attr/test-record-graph-dwarf b/tools/perf/tests/attr/test-record-graph-dwarf index e93e082f520..d6f324ea578 100644 --- a/tools/perf/tests/attr/test-record-graph-dwarf +++ b/tools/perf/tests/attr/test-record-graph-dwarf @@ -1,6 +1,6 @@  [config]  command = record -args    = -g dwarf -- kill >/dev/null 2>&1 +args    = --call-graph dwarf -- kill >/dev/null 2>&1  [event:base-record]  sample_type=12583 diff --git a/tools/perf/tests/attr/test-record-graph-fp b/tools/perf/tests/attr/test-record-graph-fp index 7cef3743f03..055e3bee799 100644 --- a/tools/perf/tests/attr/test-record-graph-fp +++ b/tools/perf/tests/attr/test-record-graph-fp @@ -1,6 +1,6 @@  [config]  command = record -args    = -g fp kill >/dev/null 2>&1 +args    = --call-graph fp kill >/dev/null 2>&1  [event:base-record]  sample_type=295 diff --git a/tools/perf/tests/attr/test-record-no-inherit b/tools/perf/tests/attr/test-record-no-inherit index 9079a25cd64..44edcb2edcd 100644 --- a/tools/perf/tests/attr/test-record-no-inherit +++ b/tools/perf/tests/attr/test-record-no-inherit @@ -3,5 +3,5 @@ command = record  args    = -i kill >/dev/null 2>&1  [event:base-record] -sample_type=259 +sample_type=263  inherit=0 diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 1e67437fb4c..6f8b01bc603 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -3,6 +3,8 @@   *   * Builtin regression testing command: ever growing number of sanity tests   */ +#include <unistd.h> +#include <string.h>  #include "builtin.h"  #include "intlist.h"  #include "tests.h" @@ -50,10 +52,18 @@ static struct test {  		.func = test__pmu,  	},  	{ -		.desc = "Test dso data interface", +		.desc = "Test dso data read",  		.func = test__dso_data,  	},  	{ +		.desc = "Test dso data cache", +		.func = test__dso_data_cache, +	}, +	{ +		.desc = "Test dso data reopen", +		.func = test__dso_data_reopen, +	}, +	{  		.desc = "roundtrip evsel->name check",  		.func = test__perf_evsel__roundtrip_name_test,  	}, @@ -115,6 +125,34 @@ static struct test {  		.desc = "Test parsing with no sample_id_all bit set",  		.func = test__parse_no_sample_id_all,  	}, +#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) +#ifdef HAVE_DWARF_UNWIND_SUPPORT +	{ +		.desc = "Test dwarf unwind", +		.func = test__dwarf_unwind, +	}, +#endif +#endif +	{ +		.desc = "Test filtering hist entries", +		.func = test__hists_filter, +	}, +	{ +		.desc = "Test mmap thread lookup", +		.func = test__mmap_thread_lookup, +	}, +	{ +		.desc = "Test thread mg sharing", +		.func = test__thread_mg_share, +	}, +	{ +		.desc = "Test output sorting of hist entries", +		.func = test__hists_output, +	}, +	{ +		.desc = "Test cumulation of child hist entries", +		.func = test__hists_cumulate, +	},  	{  		.func = NULL,  	}, @@ -144,6 +182,34 @@ static bool perf_test__matches(int curr, int argc, const char *argv[])  	return false;  } +static int run_test(struct test *test) +{ +	int status, err = -1, child = fork(); + +	if (child < 0) { +		pr_err("failed to fork test: %s\n", strerror(errno)); +		return -1; +	} + +	if (!child) { +		pr_debug("test child forked, pid %d\n", getpid()); +		err = test->func(); +		exit(err); +	} + +	wait(&status); + +	if (WIFEXITED(status)) { +		err = WEXITSTATUS(status); +		pr_debug("test child finished with %d\n", err); +	} else if (WIFSIGNALED(status)) { +		err = -1; +		pr_debug("test child interrupted\n"); +	} + +	return err; +} +  static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)  {  	int i = 0; @@ -172,7 +238,7 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)  		}  		pr_debug("\n--- start ---\n"); -		err = tests[curr].func(); +		err = run_test(&tests[curr]);  		pr_debug("---- end ----\n%s:", tests[curr].desc);  		switch (err) { diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 6fb781d5586..67f2d632355 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -1,8 +1,7 @@ -#include <sys/types.h> +#include <linux/types.h>  #include <stdlib.h>  #include <unistd.h>  #include <stdio.h> -#include <inttypes.h>  #include <ctype.h>  #include <string.h> @@ -257,7 +256,7 @@ static int process_sample_event(struct machine *machine,  		return -1;  	} -	thread = machine__findnew_thread(machine, sample.pid, sample.pid); +	thread = machine__findnew_thread(machine, sample.pid, sample.tid);  	if (!thread) {  		pr_debug("machine__findnew_thread failed\n");  		return -1; @@ -275,8 +274,19 @@ static int process_event(struct machine *machine, struct perf_evlist *evlist,  	if (event->header.type == PERF_RECORD_SAMPLE)  		return process_sample_event(machine, evlist, event, state); -	if (event->header.type < PERF_RECORD_MAX) -		return machine__process_event(machine, event); +	if (event->header.type == PERF_RECORD_THROTTLE || +	    event->header.type == PERF_RECORD_UNTHROTTLE) +		return 0; + +	if (event->header.type < PERF_RECORD_MAX) { +		int ret; + +		ret = machine__process_event(machine, event, NULL); +		if (ret < 0) +			pr_debug("machine__process_event failed, event type %u\n", +				 event->header.type); +		return ret; +	}  	return 0;  } @@ -290,6 +300,7 @@ static int process_events(struct machine *machine, struct perf_evlist *evlist,  	for (i = 0; i < evlist->nr_mmaps; i++) {  		while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {  			ret = process_event(machine, evlist, event, state); +			perf_evlist__mmap_consume(evlist, i);  			if (ret < 0)  				return ret;  		} @@ -379,7 +390,7 @@ static int do_test_code_reading(bool try_kcore)  	struct machines machines;  	struct machine *machine;  	struct thread *thread; -	struct perf_record_opts opts = { +	struct record_opts opts = {  		.mmap_pages	     = UINT_MAX,  		.user_freq	     = UINT_MAX,  		.user_interval	     = ULLONG_MAX, @@ -440,7 +451,7 @@ static int do_test_code_reading(bool try_kcore)  	}  	ret = perf_event__synthesize_thread_map(NULL, threads, -						perf_event__process, machine); +						perf_event__process, machine, false);  	if (ret < 0) {  		pr_debug("perf_event__synthesize_thread_map failed\n");  		goto out_err; @@ -492,6 +503,7 @@ static int do_test_code_reading(bool try_kcore)  		if (ret < 0) {  			if (!excl_kernel) {  				excl_kernel = true; +				perf_evlist__set_maps(evlist, NULL, NULL);  				perf_evlist__delete(evlist);  				evlist = NULL;  				continue; @@ -528,14 +540,11 @@ static int do_test_code_reading(bool try_kcore)  		err = TEST_CODE_READING_OK;  out_err:  	if (evlist) { -		perf_evlist__munmap(evlist); -		perf_evlist__close(evlist);  		perf_evlist__delete(evlist); -	} -	if (cpus) +	} else {  		cpu_map__delete(cpus); -	if (threads)  		thread_map__delete(threads); +	}  	machines__destroy_kernel_maps(&machines);  	machine__delete_threads(machine);  	machines__exit(&machines); diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index dffe0551aca..630808cd7cc 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c @@ -1,22 +1,27 @@ -#include "util.h" -  #include <stdlib.h> -#include <sys/types.h> +#include <linux/types.h>  #include <sys/stat.h>  #include <fcntl.h>  #include <string.h> - +#include <sys/time.h> +#include <sys/resource.h> +#include <api/fs/fs.h> +#include "util.h"  #include "machine.h"  #include "symbol.h"  #include "tests.h"  static char *test_file(int size)  { -	static char buf_templ[] = "/tmp/test-XXXXXX"; +#define TEMPL "/tmp/perf-test-XXXXXX" +	static char buf_templ[sizeof(TEMPL)];  	char *templ = buf_templ;  	int fd, i;  	unsigned char *buf; +	strcpy(buf_templ, TEMPL); +#undef TEMPL +  	fd = mkstemp(templ);  	if (fd < 0) {  		perror("mkstemp failed"); @@ -35,6 +40,7 @@ static char *test_file(int size)  	if (size != write(fd, buf, size))  		templ = NULL; +	free(buf);  	close(fd);  	return templ;  } @@ -149,3 +155,204 @@ int test__dso_data(void)  	unlink(file);  	return 0;  } + +static long open_files_cnt(void) +{ +	char path[PATH_MAX]; +	struct dirent *dent; +	DIR *dir; +	long nr = 0; + +	scnprintf(path, PATH_MAX, "%s/self/fd", procfs__mountpoint()); +	pr_debug("fd path: %s\n", path); + +	dir = opendir(path); +	TEST_ASSERT_VAL("failed to open fd directory", dir); + +	while ((dent = readdir(dir)) != NULL) { +		if (!strcmp(dent->d_name, ".") || +		    !strcmp(dent->d_name, "..")) +			continue; + +		nr++; +	} + +	closedir(dir); +	return nr - 1; +} + +static struct dso **dsos; + +static int dsos__create(int cnt, int size) +{ +	int i; + +	dsos = malloc(sizeof(dsos) * cnt); +	TEST_ASSERT_VAL("failed to alloc dsos array", dsos); + +	for (i = 0; i < cnt; i++) { +		char *file; + +		file = test_file(size); +		TEST_ASSERT_VAL("failed to get dso file", file); + +		dsos[i] = dso__new(file); +		TEST_ASSERT_VAL("failed to get dso", dsos[i]); +	} + +	return 0; +} + +static void dsos__delete(int cnt) +{ +	int i; + +	for (i = 0; i < cnt; i++) { +		struct dso *dso = dsos[i]; + +		unlink(dso->name); +		dso__delete(dso); +	} + +	free(dsos); +} + +static int set_fd_limit(int n) +{ +	struct rlimit rlim; + +	if (getrlimit(RLIMIT_NOFILE, &rlim)) +		return -1; + +	pr_debug("file limit %ld, new %d\n", (long) rlim.rlim_cur, n); + +	rlim.rlim_cur = n; +	return setrlimit(RLIMIT_NOFILE, &rlim); +} + +int test__dso_data_cache(void) +{ +	struct machine machine; +	long nr_end, nr = open_files_cnt(); +	int dso_cnt, limit, i, fd; + +	memset(&machine, 0, sizeof(machine)); + +	/* set as system limit */ +	limit = nr * 4; +	TEST_ASSERT_VAL("failed to set file limit", !set_fd_limit(limit)); + +	/* and this is now our dso open FDs limit + 1 extra */ +	dso_cnt = limit / 2 + 1; +	TEST_ASSERT_VAL("failed to create dsos\n", +		!dsos__create(dso_cnt, TEST_FILE_SIZE)); + +	for (i = 0; i < (dso_cnt - 1); i++) { +		struct dso *dso = dsos[i]; + +		/* +		 * Open dsos via dso__data_fd or dso__data_read_offset. +		 * Both opens the data file and keep it open. +		 */ +		if (i % 2) { +			fd = dso__data_fd(dso, &machine); +			TEST_ASSERT_VAL("failed to get fd", fd > 0); +		} else { +			#define BUFSIZE 10 +			u8 buf[BUFSIZE]; +			ssize_t n; + +			n = dso__data_read_offset(dso, &machine, 0, buf, BUFSIZE); +			TEST_ASSERT_VAL("failed to read dso", n == BUFSIZE); +		} +	} + +	/* open +1 dso over the allowed limit */ +	fd = dso__data_fd(dsos[i], &machine); +	TEST_ASSERT_VAL("failed to get fd", fd > 0); + +	/* should force the first one to be closed */ +	TEST_ASSERT_VAL("failed to close dsos[0]", dsos[0]->data.fd == -1); + +	/* cleanup everything */ +	dsos__delete(dso_cnt); + +	/* Make sure we did not leak any file descriptor. */ +	nr_end = open_files_cnt(); +	pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end); +	TEST_ASSERT_VAL("failed leadking files", nr == nr_end); +	return 0; +} + +int test__dso_data_reopen(void) +{ +	struct machine machine; +	long nr_end, nr = open_files_cnt(); +	int fd, fd_extra; + +#define dso_0 (dsos[0]) +#define dso_1 (dsos[1]) +#define dso_2 (dsos[2]) + +	memset(&machine, 0, sizeof(machine)); + +	/* +	 * Test scenario: +	 * - create 3 dso objects +	 * - set process file descriptor limit to current +	 *   files count + 3 +	 * - test that the first dso gets closed when we +	 *   reach the files count limit +	 */ + +	/* Make sure we are able to open 3 fds anyway */ +	TEST_ASSERT_VAL("failed to set file limit", +			!set_fd_limit((nr + 3))); + +	TEST_ASSERT_VAL("failed to create dsos\n", !dsos__create(3, TEST_FILE_SIZE)); + +	/* open dso_0 */ +	fd = dso__data_fd(dso_0, &machine); +	TEST_ASSERT_VAL("failed to get fd", fd > 0); + +	/* open dso_1 */ +	fd = dso__data_fd(dso_1, &machine); +	TEST_ASSERT_VAL("failed to get fd", fd > 0); + +	/* +	 * open extra file descriptor and we just +	 * reached the files count limit +	 */ +	fd_extra = open("/dev/null", O_RDONLY); +	TEST_ASSERT_VAL("failed to open extra fd", fd_extra > 0); + +	/* open dso_2 */ +	fd = dso__data_fd(dso_2, &machine); +	TEST_ASSERT_VAL("failed to get fd", fd > 0); + +	/* +	 * dso_0 should get closed, because we reached +	 * the file descriptor limit +	 */ +	TEST_ASSERT_VAL("failed to close dso_0", dso_0->data.fd == -1); + +	/* open dso_0 */ +	fd = dso__data_fd(dso_0, &machine); +	TEST_ASSERT_VAL("failed to get fd", fd > 0); + +	/* +	 * dso_1 should get closed, because we reached +	 * the file descriptor limit +	 */ +	TEST_ASSERT_VAL("failed to close dso_1", dso_1->data.fd == -1); + +	/* cleanup everything */ +	close(fd_extra); +	dsos__delete(3); + +	/* Make sure we did not leak any file descriptor. */ +	nr_end = open_files_cnt(); +	pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end); +	TEST_ASSERT_VAL("failed leadking files", nr == nr_end); +	return 0; +} diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c new file mode 100644 index 00000000000..96adb730b74 --- /dev/null +++ b/tools/perf/tests/dwarf-unwind.c @@ -0,0 +1,144 @@ +#include <linux/compiler.h> +#include <linux/types.h> +#include <unistd.h> +#include "tests.h" +#include "debug.h" +#include "machine.h" +#include "event.h" +#include "unwind.h" +#include "perf_regs.h" +#include "map.h" +#include "thread.h" + +static int mmap_handler(struct perf_tool *tool __maybe_unused, +			union perf_event *event, +			struct perf_sample *sample __maybe_unused, +			struct machine *machine) +{ +	return machine__process_mmap2_event(machine, event, NULL); +} + +static int init_live_machine(struct machine *machine) +{ +	union perf_event event; +	pid_t pid = getpid(); + +	return perf_event__synthesize_mmap_events(NULL, &event, pid, pid, +						  mmap_handler, machine, true); +} + +#define MAX_STACK 6 + +static int unwind_entry(struct unwind_entry *entry, void *arg) +{ +	unsigned long *cnt = (unsigned long *) arg; +	char *symbol = entry->sym ? entry->sym->name : NULL; +	static const char *funcs[MAX_STACK] = { +		"test__arch_unwind_sample", +		"unwind_thread", +		"krava_3", +		"krava_2", +		"krava_1", +		"test__dwarf_unwind" +	}; + +	if (*cnt >= MAX_STACK) { +		pr_debug("failed: crossed the max stack value %d\n", MAX_STACK); +		return -1; +	} + +	if (!symbol) { +		pr_debug("failed: got unresolved address 0x%" PRIx64 "\n", +			 entry->ip); +		return -1; +	} + +	pr_debug("got: %s 0x%" PRIx64 "\n", symbol, entry->ip); +	return strcmp((const char *) symbol, funcs[(*cnt)++]); +} + +__attribute__ ((noinline)) +static int unwind_thread(struct thread *thread, struct machine *machine) +{ +	struct perf_sample sample; +	unsigned long cnt = 0; +	int err = -1; + +	memset(&sample, 0, sizeof(sample)); + +	if (test__arch_unwind_sample(&sample, thread)) { +		pr_debug("failed to get unwind sample\n"); +		goto out; +	} + +	err = unwind__get_entries(unwind_entry, &cnt, machine, thread, +				  &sample, MAX_STACK); +	if (err) +		pr_debug("unwind failed\n"); +	else if (cnt != MAX_STACK) { +		pr_debug("got wrong number of stack entries %lu != %d\n", +			 cnt, MAX_STACK); +		err = -1; +	} + + out: +	free(sample.user_stack.data); +	free(sample.user_regs.regs); +	return err; +} + +__attribute__ ((noinline)) +static int krava_3(struct thread *thread, struct machine *machine) +{ +	return unwind_thread(thread, machine); +} + +__attribute__ ((noinline)) +static int krava_2(struct thread *thread, struct machine *machine) +{ +	return krava_3(thread, machine); +} + +__attribute__ ((noinline)) +static int krava_1(struct thread *thread, struct machine *machine) +{ +	return krava_2(thread, machine); +} + +int test__dwarf_unwind(void) +{ +	struct machines machines; +	struct machine *machine; +	struct thread *thread; +	int err = -1; + +	machines__init(&machines); + +	machine = machines__find(&machines, HOST_KERNEL_ID); +	if (!machine) { +		pr_err("Could not get machine\n"); +		return -1; +	} + +	if (init_live_machine(machine)) { +		pr_err("Could not init machine\n"); +		goto out; +	} + +	if (verbose > 1) +		machine__fprintf(machine, stderr); + +	thread = machine__find_thread(machine, getpid(), getpid()); +	if (!thread) { +		pr_err("Could not get thread\n"); +		goto out; +	} + +	err = krava_1(thread, machine); + + out: +	machine__delete_threads(machine); +	machine__exit(machine); +	machines__exit(&machines); +	return err; +} diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index 0197bda9c46..465cdbc345c 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c @@ -79,7 +79,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names)  	}  	err = 0; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if (strcmp(perf_evsel__name(evsel), names[evsel->idx])) {  			--err;  			pr_debug("%s != %s\n", perf_evsel__name(evsel), names[evsel->idx]); diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c index 9b98c155483..35d7fdb2328 100644 --- a/tools/perf/tests/evsel-tp-sched.c +++ b/tools/perf/tests/evsel-tp-sched.c @@ -32,7 +32,7 @@ static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name,  int test__perf_evsel__tp_sched_test(void)  { -	struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch", 0); +	struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch");  	int ret = 0;  	if (evsel == NULL) { @@ -63,7 +63,7 @@ int test__perf_evsel__tp_sched_test(void)  	perf_evsel__delete(evsel); -	evsel = perf_evsel__newtp("sched", "sched_wakeup", 0); +	evsel = perf_evsel__newtp("sched", "sched_wakeup");  	if (perf_evsel__test_field(evsel, "comm", 16, true))  		ret = -1; @@ -74,9 +74,6 @@ int test__perf_evsel__tp_sched_test(void)  	if (perf_evsel__test_field(evsel, "prio", 4, true))  		ret = -1; -	if (perf_evsel__test_field(evsel, "success", 4, true)) -		ret = -1; -  	if (perf_evsel__test_field(evsel, "target_cpu", 4, true))  		ret = -1; diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c new file mode 100644 index 00000000000..a62c0913451 --- /dev/null +++ b/tools/perf/tests/hists_common.c @@ -0,0 +1,209 @@ +#include "perf.h" +#include "util/debug.h" +#include "util/symbol.h" +#include "util/sort.h" +#include "util/evsel.h" +#include "util/evlist.h" +#include "util/machine.h" +#include "util/thread.h" +#include "tests/hists_common.h" + +static struct { +	u32 pid; +	const char *comm; +} fake_threads[] = { +	{ FAKE_PID_PERF1, "perf" }, +	{ FAKE_PID_PERF2, "perf" }, +	{ FAKE_PID_BASH,  "bash" }, +}; + +static struct { +	u32 pid; +	u64 start; +	const char *filename; +} fake_mmap_info[] = { +	{ FAKE_PID_PERF1, FAKE_MAP_PERF,   "perf" }, +	{ FAKE_PID_PERF1, FAKE_MAP_LIBC,   "libc" }, +	{ FAKE_PID_PERF1, FAKE_MAP_KERNEL, "[kernel]" }, +	{ FAKE_PID_PERF2, FAKE_MAP_PERF,   "perf" }, +	{ FAKE_PID_PERF2, FAKE_MAP_LIBC,   "libc" }, +	{ FAKE_PID_PERF2, FAKE_MAP_KERNEL, "[kernel]" }, +	{ FAKE_PID_BASH,  FAKE_MAP_BASH,   "bash" }, +	{ FAKE_PID_BASH,  FAKE_MAP_LIBC,   "libc" }, +	{ FAKE_PID_BASH,  FAKE_MAP_KERNEL, "[kernel]" }, +}; + +struct fake_sym { +	u64 start; +	u64 length; +	const char *name; +}; + +static struct fake_sym perf_syms[] = { +	{ FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" }, +	{ FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "run_command" }, +	{ FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "cmd_record" }, +}; + +static struct fake_sym bash_syms[] = { +	{ FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" }, +	{ FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "xmalloc" }, +	{ FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "xfree" }, +}; + +static struct fake_sym libc_syms[] = { +	{ 700, 100, "malloc" }, +	{ 800, 100, "free" }, +	{ 900, 100, "realloc" }, +	{ FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "malloc" }, +	{ FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "free" }, +	{ FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "realloc" }, +}; + +static struct fake_sym kernel_syms[] = { +	{ FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "schedule" }, +	{ FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "page_fault" }, +	{ FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "sys_perf_event_open" }, +}; + +static struct { +	const char *dso_name; +	struct fake_sym *syms; +	size_t nr_syms; +} fake_symbols[] = { +	{ "perf", perf_syms, ARRAY_SIZE(perf_syms) }, +	{ "bash", bash_syms, ARRAY_SIZE(bash_syms) }, +	{ "libc", libc_syms, ARRAY_SIZE(libc_syms) }, +	{ "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) }, +}; + +struct machine *setup_fake_machine(struct machines *machines) +{ +	struct machine *machine = machines__find(machines, HOST_KERNEL_ID); +	size_t i; + +	if (machine == NULL) { +		pr_debug("Not enough memory for machine setup\n"); +		return NULL; +	} + +	for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { +		struct thread *thread; + +		thread = machine__findnew_thread(machine, fake_threads[i].pid, +						 fake_threads[i].pid); +		if (thread == NULL) +			goto out; + +		thread__set_comm(thread, fake_threads[i].comm, 0); +	} + +	for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { +		union perf_event fake_mmap_event = { +			.mmap = { +				.header = { .misc = PERF_RECORD_MISC_USER, }, +				.pid = fake_mmap_info[i].pid, +				.tid = fake_mmap_info[i].pid, +				.start = fake_mmap_info[i].start, +				.len = FAKE_MAP_LENGTH, +				.pgoff = 0ULL, +			}, +		}; + +		strcpy(fake_mmap_event.mmap.filename, +		       fake_mmap_info[i].filename); + +		machine__process_mmap_event(machine, &fake_mmap_event, NULL); +	} + +	for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { +		size_t k; +		struct dso *dso; + +		dso = __dsos__findnew(&machine->user_dsos, +				      fake_symbols[i].dso_name); +		if (dso == NULL) +			goto out; + +		/* emulate dso__load() */ +		dso__set_loaded(dso, MAP__FUNCTION); + +		for (k = 0; k < fake_symbols[i].nr_syms; k++) { +			struct symbol *sym; +			struct fake_sym *fsym = &fake_symbols[i].syms[k]; + +			sym = symbol__new(fsym->start, fsym->length, +					  STB_GLOBAL, fsym->name); +			if (sym == NULL) +				goto out; + +			symbols__insert(&dso->symbols[MAP__FUNCTION], sym); +		} +	} + +	return machine; + +out: +	pr_debug("Not enough memory for machine setup\n"); +	machine__delete_threads(machine); +	machine__delete(machine); +	return NULL; +} + +void print_hists_in(struct hists *hists) +{ +	int i = 0; +	struct rb_root *root; +	struct rb_node *node; + +	if (sort__need_collapse) +		root = &hists->entries_collapsed; +	else +		root = hists->entries_in; + +	pr_info("----- %s --------\n", __func__); +	node = rb_first(root); +	while (node) { +		struct hist_entry *he; + +		he = rb_entry(node, struct hist_entry, rb_node_in); + +		if (!he->filtered) { +			pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n", +				i, thread__comm_str(he->thread), +				he->ms.map->dso->short_name, +				he->ms.sym->name, he->stat.period); +		} + +		i++; +		node = rb_next(node); +	} +} + +void print_hists_out(struct hists *hists) +{ +	int i = 0; +	struct rb_root *root; +	struct rb_node *node; + +	root = &hists->entries; + +	pr_info("----- %s --------\n", __func__); +	node = rb_first(root); +	while (node) { +		struct hist_entry *he; + +		he = rb_entry(node, struct hist_entry, rb_node); + +		if (!he->filtered) { +			pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"/%"PRIu64"\n", +				i, thread__comm_str(he->thread), he->thread->tid, +				he->ms.map->dso->short_name, +				he->ms.sym->name, he->stat.period, +				he->stat_acc ? he->stat_acc->period : 0); +		} + +		i++; +		node = rb_next(node); +	} +} diff --git a/tools/perf/tests/hists_common.h b/tools/perf/tests/hists_common.h new file mode 100644 index 00000000000..888254e8665 --- /dev/null +++ b/tools/perf/tests/hists_common.h @@ -0,0 +1,75 @@ +#ifndef __PERF_TESTS__HISTS_COMMON_H__ +#define __PERF_TESTS__HISTS_COMMON_H__ + +struct machine; +struct machines; + +#define FAKE_PID_PERF1  100 +#define FAKE_PID_PERF2  200 +#define FAKE_PID_BASH   300 + +#define FAKE_MAP_PERF    0x400000 +#define FAKE_MAP_BASH    0x400000 +#define FAKE_MAP_LIBC    0x500000 +#define FAKE_MAP_KERNEL  0xf00000 +#define FAKE_MAP_LENGTH  0x100000 + +#define FAKE_SYM_OFFSET1  700 +#define FAKE_SYM_OFFSET2  800 +#define FAKE_SYM_OFFSET3  900 +#define FAKE_SYM_LENGTH   100 + +#define FAKE_IP_PERF_MAIN  FAKE_MAP_PERF + FAKE_SYM_OFFSET1 +#define FAKE_IP_PERF_RUN_COMMAND  FAKE_MAP_PERF + FAKE_SYM_OFFSET2 +#define FAKE_IP_PERF_CMD_RECORD  FAKE_MAP_PERF + FAKE_SYM_OFFSET3 +#define FAKE_IP_BASH_MAIN  FAKE_MAP_BASH + FAKE_SYM_OFFSET1 +#define FAKE_IP_BASH_XMALLOC  FAKE_MAP_BASH + FAKE_SYM_OFFSET2 +#define FAKE_IP_BASH_XFREE  FAKE_MAP_BASH + FAKE_SYM_OFFSET3 +#define FAKE_IP_LIBC_MALLOC  FAKE_MAP_LIBC + FAKE_SYM_OFFSET1 +#define FAKE_IP_LIBC_FREE  FAKE_MAP_LIBC + FAKE_SYM_OFFSET2 +#define FAKE_IP_LIBC_REALLOC  FAKE_MAP_LIBC + FAKE_SYM_OFFSET3 +#define FAKE_IP_KERNEL_SCHEDULE  FAKE_MAP_KERNEL + FAKE_SYM_OFFSET1 +#define FAKE_IP_KERNEL_PAGE_FAULT  FAKE_MAP_KERNEL + FAKE_SYM_OFFSET2 +#define FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN  FAKE_MAP_KERNEL + FAKE_SYM_OFFSET3 + +/* + * The setup_fake_machine() provides a test environment which consists + * of 3 processes that have 3 mappings and in turn, have 3 symbols + * respectively.  See below table: + * + * Command:  Pid  Shared Object               Symbol + * .............  .............  ................... + *    perf:  100           perf  main + *    perf:  100           perf  run_command + *    perf:  100           perf  cmd_record + *    perf:  100           libc  malloc + *    perf:  100           libc  free + *    perf:  100           libc  realloc + *    perf:  100       [kernel]  schedule + *    perf:  100       [kernel]  page_fault + *    perf:  100       [kernel]  sys_perf_event_open + *    perf:  200           perf  main + *    perf:  200           perf  run_command + *    perf:  200           perf  cmd_record + *    perf:  200           libc  malloc + *    perf:  200           libc  free + *    perf:  200           libc  realloc + *    perf:  200       [kernel]  schedule + *    perf:  200       [kernel]  page_fault + *    perf:  200       [kernel]  sys_perf_event_open + *    bash:  300           bash  main + *    bash:  300           bash  xmalloc + *    bash:  300           bash  xfree + *    bash:  300           libc  malloc + *    bash:  300           libc  free + *    bash:  300           libc  realloc + *    bash:  300       [kernel]  schedule + *    bash:  300       [kernel]  page_fault + *    bash:  300       [kernel]  sys_perf_event_open + */ +struct machine *setup_fake_machine(struct machines *machines); + +void print_hists_in(struct hists *hists); +void print_hists_out(struct hists *hists); + +#endif /* __PERF_TESTS__HISTS_COMMON_H__ */ diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c new file mode 100644 index 00000000000..0ac240db2e2 --- /dev/null +++ b/tools/perf/tests/hists_cumulate.c @@ -0,0 +1,726 @@ +#include "perf.h" +#include "util/debug.h" +#include "util/symbol.h" +#include "util/sort.h" +#include "util/evsel.h" +#include "util/evlist.h" +#include "util/machine.h" +#include "util/thread.h" +#include "util/parse-events.h" +#include "tests/tests.h" +#include "tests/hists_common.h" + +struct sample { +	u32 pid; +	u64 ip; +	struct thread *thread; +	struct map *map; +	struct symbol *sym; +}; + +/* For the numbers, see hists_common.c */ +static struct sample fake_samples[] = { +	/* perf [kernel] schedule() */ +	{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, }, +	/* perf [perf]   main() */ +	{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, }, +	/* perf [perf]   cmd_record() */ +	{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, }, +	/* perf [libc]   malloc() */ +	{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, }, +	/* perf [libc]   free() */ +	{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, }, +	/* perf [perf]   main() */ +	{ .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, }, +	/* perf [kernel] page_fault() */ +	{ .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, }, +	/* bash [bash]   main() */ +	{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_MAIN, }, +	/* bash [bash]   xmalloc() */ +	{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, }, +	/* bash [kernel] page_fault() */ +	{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, }, +}; + +/* + * Will be casted to struct ip_callchain which has all 64 bit entries + * of nr and ips[]. + */ +static u64 fake_callchains[][10] = { +	/*   schedule => run_command => main */ +	{ 3, FAKE_IP_KERNEL_SCHEDULE, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, }, +	/*   main  */ +	{ 1, FAKE_IP_PERF_MAIN, }, +	/*   cmd_record => run_command => main */ +	{ 3, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, }, +	/*   malloc => cmd_record => run_command => main */ +	{ 4, FAKE_IP_LIBC_MALLOC, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND, +	     FAKE_IP_PERF_MAIN, }, +	/*   free => cmd_record => run_command => main */ +	{ 4, FAKE_IP_LIBC_FREE, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND, +	     FAKE_IP_PERF_MAIN, }, +	/*   main */ +	{ 1, FAKE_IP_PERF_MAIN, }, +	/*   page_fault => sys_perf_event_open => run_command => main */ +	{ 4, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN, +	     FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, }, +	/*   main */ +	{ 1, FAKE_IP_BASH_MAIN, }, +	/*   xmalloc => malloc => xmalloc => malloc => xmalloc => main */ +	{ 6, FAKE_IP_BASH_XMALLOC, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC, +	     FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC, FAKE_IP_BASH_MAIN, }, +	/*   page_fault => malloc => main */ +	{ 3, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_MAIN, }, +}; + +static int add_hist_entries(struct hists *hists, struct machine *machine) +{ +	struct addr_location al; +	struct perf_evsel *evsel = hists_to_evsel(hists); +	struct perf_sample sample = { .period = 1000, }; +	size_t i; + +	for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { +		const union perf_event event = { +			.header = { +				.misc = PERF_RECORD_MISC_USER, +			}, +		}; +		struct hist_entry_iter iter = { +			.hide_unresolved = false, +		}; + +		if (symbol_conf.cumulate_callchain) +			iter.ops = &hist_iter_cumulative; +		else +			iter.ops = &hist_iter_normal; + +		sample.pid = fake_samples[i].pid; +		sample.tid = fake_samples[i].pid; +		sample.ip = fake_samples[i].ip; +		sample.callchain = (struct ip_callchain *)fake_callchains[i]; + +		if (perf_event__preprocess_sample(&event, machine, &al, +						  &sample) < 0) +			goto out; + +		if (hist_entry_iter__add(&iter, &al, evsel, &sample, +					 PERF_MAX_STACK_DEPTH, NULL) < 0) +			goto out; + +		fake_samples[i].thread = al.thread; +		fake_samples[i].map = al.map; +		fake_samples[i].sym = al.sym; +	} + +	return TEST_OK; + +out: +	pr_debug("Not enough memory for adding a hist entry\n"); +	return TEST_FAIL; +} + +static void del_hist_entries(struct hists *hists) +{ +	struct hist_entry *he; +	struct rb_root *root_in; +	struct rb_root *root_out; +	struct rb_node *node; + +	if (sort__need_collapse) +		root_in = &hists->entries_collapsed; +	else +		root_in = hists->entries_in; + +	root_out = &hists->entries; + +	while (!RB_EMPTY_ROOT(root_out)) { +		node = rb_first(root_out); + +		he = rb_entry(node, struct hist_entry, rb_node); +		rb_erase(node, root_out); +		rb_erase(&he->rb_node_in, root_in); +		hist_entry__free(he); +	} +} + +typedef int (*test_fn_t)(struct perf_evsel *, struct machine *); + +#define COMM(he)  (thread__comm_str(he->thread)) +#define DSO(he)   (he->ms.map->dso->short_name) +#define SYM(he)   (he->ms.sym->name) +#define CPU(he)   (he->cpu) +#define PID(he)   (he->thread->tid) +#define DEPTH(he) (he->callchain->max_depth) +#define CDSO(cl)  (cl->ms.map->dso->short_name) +#define CSYM(cl)  (cl->ms.sym->name) + +struct result { +	u64 children; +	u64 self; +	const char *comm; +	const char *dso; +	const char *sym; +}; + +struct callchain_result { +	u64 nr; +	struct { +		const char *dso; +		const char *sym; +	} node[10]; +}; + +static int do_test(struct hists *hists, struct result *expected, size_t nr_expected, +		   struct callchain_result *expected_callchain, size_t nr_callchain) +{ +	char buf[32]; +	size_t i, c; +	struct hist_entry *he; +	struct rb_root *root; +	struct rb_node *node; +	struct callchain_node *cnode; +	struct callchain_list *clist; + +	/* +	 * adding and deleting hist entries must be done outside of this +	 * function since TEST_ASSERT_VAL() returns in case of failure. +	 */ +	hists__collapse_resort(hists, NULL); +	hists__output_resort(hists); + +	if (verbose > 2) { +		pr_info("use callchain: %d, cumulate callchain: %d\n", +			symbol_conf.use_callchain, +			symbol_conf.cumulate_callchain); +		print_hists_out(hists); +	} + +	root = &hists->entries; +	for (node = rb_first(root), i = 0; +	     node && (he = rb_entry(node, struct hist_entry, rb_node)); +	     node = rb_next(node), i++) { +		scnprintf(buf, sizeof(buf), "Invalid hist entry #%zd", i); + +		TEST_ASSERT_VAL("Incorrect number of hist entry", +				i < nr_expected); +		TEST_ASSERT_VAL(buf, he->stat.period == expected[i].self && +				!strcmp(COMM(he), expected[i].comm) && +				!strcmp(DSO(he), expected[i].dso) && +				!strcmp(SYM(he), expected[i].sym)); + +		if (symbol_conf.cumulate_callchain) +			TEST_ASSERT_VAL(buf, he->stat_acc->period == expected[i].children); + +		if (!symbol_conf.use_callchain) +			continue; + +		/* check callchain entries */ +		root = &he->callchain->node.rb_root; +		cnode = rb_entry(rb_first(root), struct callchain_node, rb_node); + +		c = 0; +		list_for_each_entry(clist, &cnode->val, list) { +			scnprintf(buf, sizeof(buf), "Invalid callchain entry #%zd/%zd", i, c); + +			TEST_ASSERT_VAL("Incorrect number of callchain entry", +					c < expected_callchain[i].nr); +			TEST_ASSERT_VAL(buf, +				!strcmp(CDSO(clist), expected_callchain[i].node[c].dso) && +				!strcmp(CSYM(clist), expected_callchain[i].node[c].sym)); +			c++; +		} +		/* TODO: handle multiple child nodes properly */ +		TEST_ASSERT_VAL("Incorrect number of callchain entry", +				c <= expected_callchain[i].nr); +	} +	TEST_ASSERT_VAL("Incorrect number of hist entry", +			i == nr_expected); +	TEST_ASSERT_VAL("Incorrect number of callchain entry", +			!symbol_conf.use_callchain || nr_expected == nr_callchain); +	return 0; +} + +/* NO callchain + NO children */ +static int test1(struct perf_evsel *evsel, struct machine *machine) +{ +	int err; +	struct hists *hists = &evsel->hists; +	/* +	 * expected output: +	 * +	 * Overhead  Command  Shared Object          Symbol +	 * ========  =======  =============  ============== +	 *   20.00%     perf  perf           [.] main +	 *   10.00%     bash  [kernel]       [k] page_fault +	 *   10.00%     bash  bash           [.] main +	 *   10.00%     bash  bash           [.] xmalloc +	 *   10.00%     perf  [kernel]       [k] page_fault +	 *   10.00%     perf  [kernel]       [k] schedule +	 *   10.00%     perf  libc           [.] free +	 *   10.00%     perf  libc           [.] malloc +	 *   10.00%     perf  perf           [.] cmd_record +	 */ +	struct result expected[] = { +		{ 0, 2000, "perf", "perf",     "main" }, +		{ 0, 1000, "bash", "[kernel]", "page_fault" }, +		{ 0, 1000, "bash", "bash",     "main" }, +		{ 0, 1000, "bash", "bash",     "xmalloc" }, +		{ 0, 1000, "perf", "[kernel]", "page_fault" }, +		{ 0, 1000, "perf", "[kernel]", "schedule" }, +		{ 0, 1000, "perf", "libc",     "free" }, +		{ 0, 1000, "perf", "libc",     "malloc" }, +		{ 0, 1000, "perf", "perf",     "cmd_record" }, +	}; + +	symbol_conf.use_callchain = false; +	symbol_conf.cumulate_callchain = false; + +	setup_sorting(); +	callchain_register_param(&callchain_param); + +	err = add_hist_entries(hists, machine); +	if (err < 0) +		goto out; + +	err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0); + +out: +	del_hist_entries(hists); +	reset_output_field(); +	return err; +} + +/* callcain + NO children */ +static int test2(struct perf_evsel *evsel, struct machine *machine) +{ +	int err; +	struct hists *hists = &evsel->hists; +	/* +	 * expected output: +	 * +	 * Overhead  Command  Shared Object          Symbol +	 * ========  =======  =============  ============== +	 *   20.00%     perf  perf           [.] main +	 *              | +	 *              --- main +	 * +	 *   10.00%     bash  [kernel]       [k] page_fault +	 *              | +	 *              --- page_fault +	 *                  malloc +	 *                  main +	 * +	 *   10.00%     bash  bash           [.] main +	 *              | +	 *              --- main +	 * +	 *   10.00%     bash  bash           [.] xmalloc +	 *              | +	 *              --- xmalloc +	 *                  malloc +	 *                  xmalloc     <--- NOTE: there's a cycle +	 *                  malloc +	 *                  xmalloc +	 *                  main +	 * +	 *   10.00%     perf  [kernel]       [k] page_fault +	 *              | +	 *              --- page_fault +	 *                  sys_perf_event_open +	 *                  run_command +	 *                  main +	 * +	 *   10.00%     perf  [kernel]       [k] schedule +	 *              | +	 *              --- schedule +	 *                  run_command +	 *                  main +	 * +	 *   10.00%     perf  libc           [.] free +	 *              | +	 *              --- free +	 *                  cmd_record +	 *                  run_command +	 *                  main +	 * +	 *   10.00%     perf  libc           [.] malloc +	 *              | +	 *              --- malloc +	 *                  cmd_record +	 *                  run_command +	 *                  main +	 * +	 *   10.00%     perf  perf           [.] cmd_record +	 *              | +	 *              --- cmd_record +	 *                  run_command +	 *                  main +	 * +	 */ +	struct result expected[] = { +		{ 0, 2000, "perf", "perf",     "main" }, +		{ 0, 1000, "bash", "[kernel]", "page_fault" }, +		{ 0, 1000, "bash", "bash",     "main" }, +		{ 0, 1000, "bash", "bash",     "xmalloc" }, +		{ 0, 1000, "perf", "[kernel]", "page_fault" }, +		{ 0, 1000, "perf", "[kernel]", "schedule" }, +		{ 0, 1000, "perf", "libc",     "free" }, +		{ 0, 1000, "perf", "libc",     "malloc" }, +		{ 0, 1000, "perf", "perf",     "cmd_record" }, +	}; +	struct callchain_result expected_callchain[] = { +		{ +			1, {	{ "perf",     "main" }, }, +		}, +		{ +			3, {	{ "[kernel]", "page_fault" }, +				{ "libc",     "malloc" }, +				{ "bash",     "main" }, }, +		}, +		{ +			1, {	{ "bash",     "main" }, }, +		}, +		{ +			6, {	{ "bash",     "xmalloc" }, +				{ "libc",     "malloc" }, +				{ "bash",     "xmalloc" }, +				{ "libc",     "malloc" }, +				{ "bash",     "xmalloc" }, +				{ "bash",     "main" }, }, +		}, +		{ +			4, {	{ "[kernel]", "page_fault" }, +				{ "[kernel]", "sys_perf_event_open" }, +				{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +		{ +			3, {	{ "[kernel]", "schedule" }, +				{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +		{ +			4, {	{ "libc",     "free" }, +				{ "perf",     "cmd_record" }, +				{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +		{ +			4, {	{ "libc",     "malloc" }, +				{ "perf",     "cmd_record" }, +				{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +		{ +			3, {	{ "perf",     "cmd_record" }, +				{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +	}; + +	symbol_conf.use_callchain = true; +	symbol_conf.cumulate_callchain = false; + +	setup_sorting(); +	callchain_register_param(&callchain_param); + +	err = add_hist_entries(hists, machine); +	if (err < 0) +		goto out; + +	err = do_test(hists, expected, ARRAY_SIZE(expected), +		      expected_callchain, ARRAY_SIZE(expected_callchain)); + +out: +	del_hist_entries(hists); +	reset_output_field(); +	return err; +} + +/* NO callchain + children */ +static int test3(struct perf_evsel *evsel, struct machine *machine) +{ +	int err; +	struct hists *hists = &evsel->hists; +	/* +	 * expected output: +	 * +	 * Children      Self  Command  Shared Object                   Symbol +	 * ========  ========  =======  =============  ======================= +	 *   70.00%    20.00%     perf  perf           [.] main +	 *   50.00%     0.00%     perf  perf           [.] run_command +	 *   30.00%    10.00%     bash  bash           [.] main +	 *   30.00%    10.00%     perf  perf           [.] cmd_record +	 *   20.00%     0.00%     bash  libc           [.] malloc +	 *   10.00%    10.00%     bash  [kernel]       [k] page_fault +	 *   10.00%    10.00%     perf  [kernel]       [k] schedule +	 *   10.00%     0.00%     perf  [kernel]       [k] sys_perf_event_open +	 *   10.00%    10.00%     perf  [kernel]       [k] page_fault +	 *   10.00%    10.00%     perf  libc           [.] free +	 *   10.00%    10.00%     perf  libc           [.] malloc +	 *   10.00%    10.00%     bash  bash           [.] xmalloc +	 */ +	struct result expected[] = { +		{ 7000, 2000, "perf", "perf",     "main" }, +		{ 5000,    0, "perf", "perf",     "run_command" }, +		{ 3000, 1000, "bash", "bash",     "main" }, +		{ 3000, 1000, "perf", "perf",     "cmd_record" }, +		{ 2000,    0, "bash", "libc",     "malloc" }, +		{ 1000, 1000, "bash", "[kernel]", "page_fault" }, +		{ 1000, 1000, "perf", "[kernel]", "schedule" }, +		{ 1000,    0, "perf", "[kernel]", "sys_perf_event_open" }, +		{ 1000, 1000, "perf", "[kernel]", "page_fault" }, +		{ 1000, 1000, "perf", "libc",     "free" }, +		{ 1000, 1000, "perf", "libc",     "malloc" }, +		{ 1000, 1000, "bash", "bash",     "xmalloc" }, +	}; + +	symbol_conf.use_callchain = false; +	symbol_conf.cumulate_callchain = true; + +	setup_sorting(); +	callchain_register_param(&callchain_param); + +	err = add_hist_entries(hists, machine); +	if (err < 0) +		goto out; + +	err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0); + +out: +	del_hist_entries(hists); +	reset_output_field(); +	return err; +} + +/* callchain + children */ +static int test4(struct perf_evsel *evsel, struct machine *machine) +{ +	int err; +	struct hists *hists = &evsel->hists; +	/* +	 * expected output: +	 * +	 * Children      Self  Command  Shared Object                   Symbol +	 * ========  ========  =======  =============  ======================= +	 *   70.00%    20.00%     perf  perf           [.] main +	 *              | +	 *              --- main +	 * +	 *   50.00%     0.00%     perf  perf           [.] run_command +	 *              | +	 *              --- run_command +	 *                  main +	 * +	 *   30.00%    10.00%     bash  bash           [.] main +	 *              | +	 *              --- main +	 * +	 *   30.00%    10.00%     perf  perf           [.] cmd_record +	 *              | +	 *              --- cmd_record +	 *                  run_command +	 *                  main +	 * +	 *   20.00%     0.00%     bash  libc           [.] malloc +	 *              | +	 *              --- malloc +	 *                 | +	 *                 |--50.00%-- xmalloc +	 *                 |           main +	 *                  --50.00%-- main +	 * +	 *   10.00%    10.00%     bash  [kernel]       [k] page_fault +	 *              | +	 *              --- page_fault +	 *                  malloc +	 *                  main +	 * +	 *   10.00%    10.00%     perf  [kernel]       [k] schedule +	 *              | +	 *              --- schedule +	 *                  run_command +	 *                  main +	 * +	 *   10.00%     0.00%     perf  [kernel]       [k] sys_perf_event_open +	 *              | +	 *              --- sys_perf_event_open +	 *                  run_command +	 *                  main +	 * +	 *   10.00%    10.00%     perf  [kernel]       [k] page_fault +	 *              | +	 *              --- page_fault +	 *                  sys_perf_event_open +	 *                  run_command +	 *                  main +	 * +	 *   10.00%    10.00%     perf  libc           [.] free +	 *              | +	 *              --- free +	 *                  cmd_record +	 *                  run_command +	 *                  main +	 * +	 *   10.00%    10.00%     perf  libc           [.] malloc +	 *              | +	 *              --- malloc +	 *                  cmd_record +	 *                  run_command +	 *                  main +	 * +	 *   10.00%    10.00%     bash  bash           [.] xmalloc +	 *              | +	 *              --- xmalloc +	 *                  malloc +	 *                  xmalloc     <--- NOTE: there's a cycle +	 *                  malloc +	 *                  xmalloc +	 *                  main +	 * +	 */ +	struct result expected[] = { +		{ 7000, 2000, "perf", "perf",     "main" }, +		{ 5000,    0, "perf", "perf",     "run_command" }, +		{ 3000, 1000, "bash", "bash",     "main" }, +		{ 3000, 1000, "perf", "perf",     "cmd_record" }, +		{ 2000,    0, "bash", "libc",     "malloc" }, +		{ 1000, 1000, "bash", "[kernel]", "page_fault" }, +		{ 1000, 1000, "perf", "[kernel]", "schedule" }, +		{ 1000,    0, "perf", "[kernel]", "sys_perf_event_open" }, +		{ 1000, 1000, "perf", "[kernel]", "page_fault" }, +		{ 1000, 1000, "perf", "libc",     "free" }, +		{ 1000, 1000, "perf", "libc",     "malloc" }, +		{ 1000, 1000, "bash", "bash",     "xmalloc" }, +	}; +	struct callchain_result expected_callchain[] = { +		{ +			1, {	{ "perf",     "main" }, }, +		}, +		{ +			2, {	{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +		{ +			1, {	{ "bash",     "main" }, }, +		}, +		{ +			3, {	{ "perf",     "cmd_record" }, +				{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +		{ +			4, {	{ "libc",     "malloc" }, +				{ "bash",     "xmalloc" }, +				{ "bash",     "main" }, +				{ "bash",     "main" }, }, +		}, +		{ +			3, {	{ "[kernel]", "page_fault" }, +				{ "libc",     "malloc" }, +				{ "bash",     "main" }, }, +		}, +		{ +			3, {	{ "[kernel]", "schedule" }, +				{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +		{ +			3, {	{ "[kernel]", "sys_perf_event_open" }, +				{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +		{ +			4, {	{ "[kernel]", "page_fault" }, +				{ "[kernel]", "sys_perf_event_open" }, +				{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +		{ +			4, {	{ "libc",     "free" }, +				{ "perf",     "cmd_record" }, +				{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +		{ +			4, {	{ "libc",     "malloc" }, +				{ "perf",     "cmd_record" }, +				{ "perf",     "run_command" }, +				{ "perf",     "main" }, }, +		}, +		{ +			6, {	{ "bash",     "xmalloc" }, +				{ "libc",     "malloc" }, +				{ "bash",     "xmalloc" }, +				{ "libc",     "malloc" }, +				{ "bash",     "xmalloc" }, +				{ "bash",     "main" }, }, +		}, +	}; + +	symbol_conf.use_callchain = true; +	symbol_conf.cumulate_callchain = true; + +	setup_sorting(); +	callchain_register_param(&callchain_param); + +	err = add_hist_entries(hists, machine); +	if (err < 0) +		goto out; + +	err = do_test(hists, expected, ARRAY_SIZE(expected), +		      expected_callchain, ARRAY_SIZE(expected_callchain)); + +out: +	del_hist_entries(hists); +	reset_output_field(); +	return err; +} + +int test__hists_cumulate(void) +{ +	int err = TEST_FAIL; +	struct machines machines; +	struct machine *machine; +	struct perf_evsel *evsel; +	struct perf_evlist *evlist = perf_evlist__new(); +	size_t i; +	test_fn_t testcases[] = { +		test1, +		test2, +		test3, +		test4, +	}; + +	TEST_ASSERT_VAL("No memory", evlist); + +	err = parse_events(evlist, "cpu-clock"); +	if (err) +		goto out; + +	machines__init(&machines); + +	/* setup threads/dso/map/symbols also */ +	machine = setup_fake_machine(&machines); +	if (!machine) +		goto out; + +	if (verbose > 1) +		machine__fprintf(machine, stderr); + +	evsel = perf_evlist__first(evlist); + +	for (i = 0; i < ARRAY_SIZE(testcases); i++) { +		err = testcases[i](evsel, machine); +		if (err < 0) +			break; +	} + +out: +	/* tear down everything */ +	perf_evlist__delete(evlist); +	machines__exit(&machines); + +	return err; +} diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c new file mode 100644 index 00000000000..821f581fd93 --- /dev/null +++ b/tools/perf/tests/hists_filter.c @@ -0,0 +1,289 @@ +#include "perf.h" +#include "util/debug.h" +#include "util/symbol.h" +#include "util/sort.h" +#include "util/evsel.h" +#include "util/evlist.h" +#include "util/machine.h" +#include "util/thread.h" +#include "util/parse-events.h" +#include "tests/tests.h" +#include "tests/hists_common.h" + +struct sample { +	u32 pid; +	u64 ip; +	struct thread *thread; +	struct map *map; +	struct symbol *sym; +}; + +/* For the numbers, see hists_common.c */ +static struct sample fake_samples[] = { +	/* perf [kernel] schedule() */ +	{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, }, +	/* perf [perf]   main() */ +	{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, }, +	/* perf [libc]   malloc() */ +	{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, }, +	/* perf [perf]   main() */ +	{ .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, }, /* will be merged */ +	/* perf [perf]   cmd_record() */ +	{ .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, }, +	/* perf [kernel] page_fault() */ +	{ .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, }, +	/* bash [bash]   main() */ +	{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_MAIN, }, +	/* bash [bash]   xmalloc() */ +	{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, }, +	/* bash [libc]   malloc() */ +	{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_MALLOC, }, +	/* bash [kernel] page_fault() */ +	{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, }, +}; + +static int add_hist_entries(struct perf_evlist *evlist, +			    struct machine *machine __maybe_unused) +{ +	struct perf_evsel *evsel; +	struct addr_location al; +	struct perf_sample sample = { .period = 100, }; +	size_t i; + +	/* +	 * each evsel will have 10 samples but the 4th sample +	 * (perf [perf] main) will be collapsed to an existing entry +	 * so total 9 entries will be in the tree. +	 */ +	evlist__for_each(evlist, evsel) { +		for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { +			const union perf_event event = { +				.header = { +					.misc = PERF_RECORD_MISC_USER, +				}, +			}; +			struct hist_entry_iter iter = { +				.ops = &hist_iter_normal, +				.hide_unresolved = false, +			}; + +			/* make sure it has no filter at first */ +			evsel->hists.thread_filter = NULL; +			evsel->hists.dso_filter = NULL; +			evsel->hists.symbol_filter_str = NULL; + +			sample.pid = fake_samples[i].pid; +			sample.tid = fake_samples[i].pid; +			sample.ip = fake_samples[i].ip; + +			if (perf_event__preprocess_sample(&event, machine, &al, +							  &sample) < 0) +				goto out; + +			if (hist_entry_iter__add(&iter, &al, evsel, &sample, +						 PERF_MAX_STACK_DEPTH, NULL) < 0) +				goto out; + +			fake_samples[i].thread = al.thread; +			fake_samples[i].map = al.map; +			fake_samples[i].sym = al.sym; +		} +	} + +	return 0; + +out: +	pr_debug("Not enough memory for adding a hist entry\n"); +	return TEST_FAIL; +} + +int test__hists_filter(void) +{ +	int err = TEST_FAIL; +	struct machines machines; +	struct machine *machine; +	struct perf_evsel *evsel; +	struct perf_evlist *evlist = perf_evlist__new(); + +	TEST_ASSERT_VAL("No memory", evlist); + +	err = parse_events(evlist, "cpu-clock"); +	if (err) +		goto out; +	err = parse_events(evlist, "task-clock"); +	if (err) +		goto out; + +	/* default sort order (comm,dso,sym) will be used */ +	if (setup_sorting() < 0) +		goto out; + +	machines__init(&machines); + +	/* setup threads/dso/map/symbols also */ +	machine = setup_fake_machine(&machines); +	if (!machine) +		goto out; + +	if (verbose > 1) +		machine__fprintf(machine, stderr); + +	/* process sample events */ +	err = add_hist_entries(evlist, machine); +	if (err < 0) +		goto out; + +	evlist__for_each(evlist, evsel) { +		struct hists *hists = &evsel->hists; + +		hists__collapse_resort(hists, NULL); +		hists__output_resort(hists); + +		if (verbose > 2) { +			pr_info("Normal histogram\n"); +			print_hists_out(hists); +		} + +		TEST_ASSERT_VAL("Invalid nr samples", +				hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); +		TEST_ASSERT_VAL("Invalid nr hist entries", +				hists->nr_entries == 9); +		TEST_ASSERT_VAL("Invalid total period", +				hists->stats.total_period == 1000); +		TEST_ASSERT_VAL("Unmatched nr samples", +				hists->stats.nr_events[PERF_RECORD_SAMPLE] == +				hists->stats.nr_non_filtered_samples); +		TEST_ASSERT_VAL("Unmatched nr hist entries", +				hists->nr_entries == hists->nr_non_filtered_entries); +		TEST_ASSERT_VAL("Unmatched total period", +				hists->stats.total_period == +				hists->stats.total_non_filtered_period); + +		/* now applying thread filter for 'bash' */ +		evsel->hists.thread_filter = fake_samples[9].thread; +		hists__filter_by_thread(hists); + +		if (verbose > 2) { +			pr_info("Histogram for thread filter\n"); +			print_hists_out(hists); +		} + +		/* normal stats should be invariant */ +		TEST_ASSERT_VAL("Invalid nr samples", +				hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); +		TEST_ASSERT_VAL("Invalid nr hist entries", +				hists->nr_entries == 9); +		TEST_ASSERT_VAL("Invalid total period", +				hists->stats.total_period == 1000); + +		/* but filter stats are changed */ +		TEST_ASSERT_VAL("Unmatched nr samples for thread filter", +				hists->stats.nr_non_filtered_samples == 4); +		TEST_ASSERT_VAL("Unmatched nr hist entries for thread filter", +				hists->nr_non_filtered_entries == 4); +		TEST_ASSERT_VAL("Unmatched total period for thread filter", +				hists->stats.total_non_filtered_period == 400); + +		/* remove thread filter first */ +		evsel->hists.thread_filter = NULL; +		hists__filter_by_thread(hists); + +		/* now applying dso filter for 'kernel' */ +		evsel->hists.dso_filter = fake_samples[0].map->dso; +		hists__filter_by_dso(hists); + +		if (verbose > 2) { +			pr_info("Histogram for dso filter\n"); +			print_hists_out(hists); +		} + +		/* normal stats should be invariant */ +		TEST_ASSERT_VAL("Invalid nr samples", +				hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); +		TEST_ASSERT_VAL("Invalid nr hist entries", +				hists->nr_entries == 9); +		TEST_ASSERT_VAL("Invalid total period", +				hists->stats.total_period == 1000); + +		/* but filter stats are changed */ +		TEST_ASSERT_VAL("Unmatched nr samples for dso filter", +				hists->stats.nr_non_filtered_samples == 3); +		TEST_ASSERT_VAL("Unmatched nr hist entries for dso filter", +				hists->nr_non_filtered_entries == 3); +		TEST_ASSERT_VAL("Unmatched total period for dso filter", +				hists->stats.total_non_filtered_period == 300); + +		/* remove dso filter first */ +		evsel->hists.dso_filter = NULL; +		hists__filter_by_dso(hists); + +		/* +		 * now applying symbol filter for 'main'.  Also note that +		 * there's 3 samples that have 'main' symbol but the 4th +		 * entry of fake_samples was collapsed already so it won't +		 * be counted as a separate entry but the sample count and +		 * total period will be remained. +		 */ +		evsel->hists.symbol_filter_str = "main"; +		hists__filter_by_symbol(hists); + +		if (verbose > 2) { +			pr_info("Histogram for symbol filter\n"); +			print_hists_out(hists); +		} + +		/* normal stats should be invariant */ +		TEST_ASSERT_VAL("Invalid nr samples", +				hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); +		TEST_ASSERT_VAL("Invalid nr hist entries", +				hists->nr_entries == 9); +		TEST_ASSERT_VAL("Invalid total period", +				hists->stats.total_period == 1000); + +		/* but filter stats are changed */ +		TEST_ASSERT_VAL("Unmatched nr samples for symbol filter", +				hists->stats.nr_non_filtered_samples == 3); +		TEST_ASSERT_VAL("Unmatched nr hist entries for symbol filter", +				hists->nr_non_filtered_entries == 2); +		TEST_ASSERT_VAL("Unmatched total period for symbol filter", +				hists->stats.total_non_filtered_period == 300); + +		/* now applying all filters at once. */ +		evsel->hists.thread_filter = fake_samples[1].thread; +		evsel->hists.dso_filter = fake_samples[1].map->dso; +		hists__filter_by_thread(hists); +		hists__filter_by_dso(hists); + +		if (verbose > 2) { +			pr_info("Histogram for all filters\n"); +			print_hists_out(hists); +		} + +		/* normal stats should be invariant */ +		TEST_ASSERT_VAL("Invalid nr samples", +				hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); +		TEST_ASSERT_VAL("Invalid nr hist entries", +				hists->nr_entries == 9); +		TEST_ASSERT_VAL("Invalid total period", +				hists->stats.total_period == 1000); + +		/* but filter stats are changed */ +		TEST_ASSERT_VAL("Unmatched nr samples for all filter", +				hists->stats.nr_non_filtered_samples == 2); +		TEST_ASSERT_VAL("Unmatched nr hist entries for all filter", +				hists->nr_non_filtered_entries == 1); +		TEST_ASSERT_VAL("Unmatched total period for all filter", +				hists->stats.total_non_filtered_period == 200); +	} + + +	err = TEST_OK; + +out: +	/* tear down everything */ +	perf_evlist__delete(evlist); +	reset_output_field(); +	machines__exit(&machines); + +	return err; +} diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 4228ffc0d96..d4b34b0f50a 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -8,144 +8,7 @@  #include "machine.h"  #include "thread.h"  #include "parse-events.h" - -static struct { -	u32 pid; -	const char *comm; -} fake_threads[] = { -	{ 100, "perf" }, -	{ 200, "perf" }, -	{ 300, "bash" }, -}; - -static struct { -	u32 pid; -	u64 start; -	const char *filename; -} fake_mmap_info[] = { -	{ 100, 0x40000, "perf" }, -	{ 100, 0x50000, "libc" }, -	{ 100, 0xf0000, "[kernel]" }, -	{ 200, 0x40000, "perf" }, -	{ 200, 0x50000, "libc" }, -	{ 200, 0xf0000, "[kernel]" }, -	{ 300, 0x40000, "bash" }, -	{ 300, 0x50000, "libc" }, -	{ 300, 0xf0000, "[kernel]" }, -}; - -struct fake_sym { -	u64 start; -	u64 length; -	const char *name; -}; - -static struct fake_sym perf_syms[] = { -	{ 700, 100, "main" }, -	{ 800, 100, "run_command" }, -	{ 900, 100, "cmd_record" }, -}; - -static struct fake_sym bash_syms[] = { -	{ 700, 100, "main" }, -	{ 800, 100, "xmalloc" }, -	{ 900, 100, "xfree" }, -}; - -static struct fake_sym libc_syms[] = { -	{ 700, 100, "malloc" }, -	{ 800, 100, "free" }, -	{ 900, 100, "realloc" }, -}; - -static struct fake_sym kernel_syms[] = { -	{ 700, 100, "schedule" }, -	{ 800, 100, "page_fault" }, -	{ 900, 100, "sys_perf_event_open" }, -}; - -static struct { -	const char *dso_name; -	struct fake_sym *syms; -	size_t nr_syms; -} fake_symbols[] = { -	{ "perf", perf_syms, ARRAY_SIZE(perf_syms) }, -	{ "bash", bash_syms, ARRAY_SIZE(bash_syms) }, -	{ "libc", libc_syms, ARRAY_SIZE(libc_syms) }, -	{ "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) }, -}; - -static struct machine *setup_fake_machine(struct machines *machines) -{ -	struct machine *machine = machines__find(machines, HOST_KERNEL_ID); -	size_t i; - -	if (machine == NULL) { -		pr_debug("Not enough memory for machine setup\n"); -		return NULL; -	} - -	for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { -		struct thread *thread; - -		thread = machine__findnew_thread(machine, fake_threads[i].pid, -						 fake_threads[i].pid); -		if (thread == NULL) -			goto out; - -		thread__set_comm(thread, fake_threads[i].comm); -	} - -	for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { -		union perf_event fake_mmap_event = { -			.mmap = { -				.header = { .misc = PERF_RECORD_MISC_USER, }, -				.pid = fake_mmap_info[i].pid, -				.start = fake_mmap_info[i].start, -				.len = 0x1000ULL, -				.pgoff = 0ULL, -			}, -		}; - -		strcpy(fake_mmap_event.mmap.filename, -		       fake_mmap_info[i].filename); - -		machine__process_mmap_event(machine, &fake_mmap_event); -	} - -	for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { -		size_t k; -		struct dso *dso; - -		dso = __dsos__findnew(&machine->user_dsos, -				      fake_symbols[i].dso_name); -		if (dso == NULL) -			goto out; - -		/* emulate dso__load() */ -		dso__set_loaded(dso, MAP__FUNCTION); - -		for (k = 0; k < fake_symbols[i].nr_syms; k++) { -			struct symbol *sym; -			struct fake_sym *fsym = &fake_symbols[i].syms[k]; - -			sym = symbol__new(fsym->start, fsym->length, -					  STB_GLOBAL, fsym->name); -			if (sym == NULL) -				goto out; - -			symbols__insert(&dso->symbols[MAP__FUNCTION], sym); -		} -	} - -	return machine; - -out: -	pr_debug("Not enough memory for machine setup\n"); -	machine__delete_threads(machine); -	machine__delete(machine); -	return NULL; -} +#include "hists_common.h"  struct sample {  	u32 pid; @@ -155,43 +18,44 @@ struct sample {  	struct symbol *sym;  }; +/* For the numbers, see hists_common.c */  static struct sample fake_common_samples[] = {  	/* perf [kernel] schedule() */ -	{ .pid = 100, .ip = 0xf0000 + 700, }, +	{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },  	/* perf [perf]   main() */ -	{ .pid = 200, .ip = 0x40000 + 700, }, +	{ .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },  	/* perf [perf]   cmd_record() */ -	{ .pid = 200, .ip = 0x40000 + 900, }, +	{ .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, },  	/* bash [bash]   xmalloc() */ -	{ .pid = 300, .ip = 0x40000 + 800, }, +	{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, },  	/* bash [libc]   malloc() */ -	{ .pid = 300, .ip = 0x50000 + 700, }, +	{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_MALLOC, },  };  static struct sample fake_samples[][5] = {  	{  		/* perf [perf]   run_command() */ -		{ .pid = 100, .ip = 0x40000 + 800, }, +		{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_RUN_COMMAND, },  		/* perf [libc]   malloc() */ -		{ .pid = 100, .ip = 0x50000 + 700, }, +		{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },  		/* perf [kernel] page_fault() */ -		{ .pid = 100, .ip = 0xf0000 + 800, }, +		{ .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },  		/* perf [kernel] sys_perf_event_open() */ -		{ .pid = 200, .ip = 0xf0000 + 900, }, +		{ .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN, },  		/* bash [libc]   free() */ -		{ .pid = 300, .ip = 0x50000 + 800, }, +		{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_FREE, },  	},  	{  		/* perf [libc]   free() */ -		{ .pid = 200, .ip = 0x50000 + 800, }, +		{ .pid = FAKE_PID_PERF2, .ip = FAKE_IP_LIBC_FREE, },  		/* bash [libc]   malloc() */ -		{ .pid = 300, .ip = 0x50000 + 700, }, /* will be merged */ +		{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_MALLOC, }, /* will be merged */  		/* bash [bash]   xfee() */ -		{ .pid = 300, .ip = 0x40000 + 900, }, +		{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XFREE, },  		/* bash [libc]   realloc() */ -		{ .pid = 300, .ip = 0x50000 + 900, }, +		{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_REALLOC, },  		/* bash [kernel] page_fault() */ -		{ .pid = 300, .ip = 0xf0000 + 800, }, +		{ .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, },  	},  }; @@ -200,7 +64,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)  	struct perf_evsel *evsel;  	struct addr_location al;  	struct hist_entry *he; -	struct perf_sample sample = { .cpu = 0, }; +	struct perf_sample sample = { .period = 1, };  	size_t i = 0, k;  	/* @@ -208,7 +72,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)  	 * However the second evsel also has a collapsed entry for  	 * "bash [libc] malloc" so total 9 entries will be in the tree.  	 */ -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) {  			const union perf_event event = {  				.header = { @@ -217,12 +81,14 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)  			};  			sample.pid = fake_common_samples[k].pid; +			sample.tid = fake_common_samples[k].pid;  			sample.ip = fake_common_samples[k].ip;  			if (perf_event__preprocess_sample(&event, machine, &al,  							  &sample) < 0)  				goto out; -			he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); +			he = __hists__add_entry(&evsel->hists, &al, NULL, +						NULL, NULL, 1, 1, 0, true);  			if (he == NULL)  				goto out; @@ -239,12 +105,14 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)  			};  			sample.pid = fake_samples[i][k].pid; +			sample.tid = fake_samples[i][k].pid;  			sample.ip = fake_samples[i][k].ip;  			if (perf_event__preprocess_sample(&event, machine, &al,  							  &sample) < 0)  				goto out; -			he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); +			he = __hists__add_entry(&evsel->hists, &al, NULL, +						NULL, NULL, 1, 1, 0, true);  			if (he == NULL)  				goto out; @@ -400,33 +268,6 @@ static int validate_link(struct hists *leader, struct hists *other)  	return __validate_link(leader, 0) || __validate_link(other, 1);  } -static void print_hists(struct hists *hists) -{ -	int i = 0; -	struct rb_root *root; -	struct rb_node *node; - -	if (sort__need_collapse) -		root = &hists->entries_collapsed; -	else -		root = hists->entries_in; - -	pr_info("----- %s --------\n", __func__); -	node = rb_first(root); -	while (node) { -		struct hist_entry *he; - -		he = rb_entry(node, struct hist_entry, rb_node_in); - -		pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n", -			i, he->thread->comm, he->ms.map->dso->short_name, -			he->ms.sym->name, he->stat.period); - -		i++; -		node = rb_next(node); -	} -} -  int test__hists_link(void)  {  	int err = -1; @@ -464,11 +305,11 @@ int test__hists_link(void)  	if (err < 0)  		goto out; -	list_for_each_entry(evsel, &evlist->entries, node) { -		hists__collapse_resort(&evsel->hists); +	evlist__for_each(evlist, evsel) { +		hists__collapse_resort(&evsel->hists, NULL);  		if (verbose > 2) -			print_hists(&evsel->hists); +			print_hists_in(&evsel->hists);  	}  	first = perf_evlist__first(evlist); @@ -491,6 +332,7 @@ int test__hists_link(void)  out:  	/* tear down everything */  	perf_evlist__delete(evlist); +	reset_output_field();  	machines__exit(&machines);  	return err; diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c new file mode 100644 index 00000000000..e3bbd6c54c1 --- /dev/null +++ b/tools/perf/tests/hists_output.c @@ -0,0 +1,621 @@ +#include "perf.h" +#include "util/debug.h" +#include "util/symbol.h" +#include "util/sort.h" +#include "util/evsel.h" +#include "util/evlist.h" +#include "util/machine.h" +#include "util/thread.h" +#include "util/parse-events.h" +#include "tests/tests.h" +#include "tests/hists_common.h" + +struct sample { +	u32 cpu; +	u32 pid; +	u64 ip; +	struct thread *thread; +	struct map *map; +	struct symbol *sym; +}; + +/* For the numbers, see hists_common.c */ +static struct sample fake_samples[] = { +	/* perf [kernel] schedule() */ +	{ .cpu = 0, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, }, +	/* perf [perf]   main() */ +	{ .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, }, +	/* perf [perf]   cmd_record() */ +	{ .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, }, +	/* perf [libc]   malloc() */ +	{ .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, }, +	/* perf [libc]   free() */ +	{ .cpu = 2, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, }, +	/* perf [perf]   main() */ +	{ .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, }, +	/* perf [kernel] page_fault() */ +	{ .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, }, +	/* bash [bash]   main() */ +	{ .cpu = 3, .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_MAIN, }, +	/* bash [bash]   xmalloc() */ +	{ .cpu = 0, .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, }, +	/* bash [kernel] page_fault() */ +	{ .cpu = 1, .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, }, +}; + +static int add_hist_entries(struct hists *hists, struct machine *machine) +{ +	struct addr_location al; +	struct perf_evsel *evsel = hists_to_evsel(hists); +	struct perf_sample sample = { .period = 100, }; +	size_t i; + +	for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { +		const union perf_event event = { +			.header = { +				.misc = PERF_RECORD_MISC_USER, +			}, +		}; +		struct hist_entry_iter iter = { +			.ops = &hist_iter_normal, +			.hide_unresolved = false, +		}; + +		sample.cpu = fake_samples[i].cpu; +		sample.pid = fake_samples[i].pid; +		sample.tid = fake_samples[i].pid; +		sample.ip = fake_samples[i].ip; + +		if (perf_event__preprocess_sample(&event, machine, &al, +						  &sample) < 0) +			goto out; + +		if (hist_entry_iter__add(&iter, &al, evsel, &sample, +					 PERF_MAX_STACK_DEPTH, NULL) < 0) +			goto out; + +		fake_samples[i].thread = al.thread; +		fake_samples[i].map = al.map; +		fake_samples[i].sym = al.sym; +	} + +	return TEST_OK; + +out: +	pr_debug("Not enough memory for adding a hist entry\n"); +	return TEST_FAIL; +} + +static void del_hist_entries(struct hists *hists) +{ +	struct hist_entry *he; +	struct rb_root *root_in; +	struct rb_root *root_out; +	struct rb_node *node; + +	if (sort__need_collapse) +		root_in = &hists->entries_collapsed; +	else +		root_in = hists->entries_in; + +	root_out = &hists->entries; + +	while (!RB_EMPTY_ROOT(root_out)) { +		node = rb_first(root_out); + +		he = rb_entry(node, struct hist_entry, rb_node); +		rb_erase(node, root_out); +		rb_erase(&he->rb_node_in, root_in); +		hist_entry__free(he); +	} +} + +typedef int (*test_fn_t)(struct perf_evsel *, struct machine *); + +#define COMM(he)  (thread__comm_str(he->thread)) +#define DSO(he)   (he->ms.map->dso->short_name) +#define SYM(he)   (he->ms.sym->name) +#define CPU(he)   (he->cpu) +#define PID(he)   (he->thread->tid) + +/* default sort keys (no field) */ +static int test1(struct perf_evsel *evsel, struct machine *machine) +{ +	int err; +	struct hists *hists = &evsel->hists; +	struct hist_entry *he; +	struct rb_root *root; +	struct rb_node *node; + +	field_order = NULL; +	sort_order = NULL; /* equivalent to sort_order = "comm,dso,sym" */ + +	setup_sorting(); + +	/* +	 * expected output: +	 * +	 * Overhead  Command  Shared Object          Symbol +	 * ========  =======  =============  ============== +	 *   20.00%     perf  perf           [.] main +	 *   10.00%     bash  [kernel]       [k] page_fault +	 *   10.00%     bash  bash           [.] main +	 *   10.00%     bash  bash           [.] xmalloc +	 *   10.00%     perf  [kernel]       [k] page_fault +	 *   10.00%     perf  [kernel]       [k] schedule +	 *   10.00%     perf  libc           [.] free +	 *   10.00%     perf  libc           [.] malloc +	 *   10.00%     perf  perf           [.] cmd_record +	 */ +	err = add_hist_entries(hists, machine); +	if (err < 0) +		goto out; + +	hists__collapse_resort(hists, NULL); +	hists__output_resort(hists); + +	if (verbose > 2) { +		pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); +		print_hists_out(hists); +	} + +	root = &evsel->hists.entries; +	node = rb_first(root); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && +			!strcmp(SYM(he), "main") && he->stat.period == 200); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") && +			!strcmp(SYM(he), "page_fault") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && +			!strcmp(SYM(he), "main") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && +			!strcmp(SYM(he), "xmalloc") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && +			!strcmp(SYM(he), "page_fault") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && +			!strcmp(SYM(he), "schedule") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && +			!strcmp(SYM(he), "free") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && +			!strcmp(SYM(he), "malloc") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && +			!strcmp(SYM(he), "cmd_record") && he->stat.period == 100); + +out: +	del_hist_entries(hists); +	reset_output_field(); +	return err; +} + +/* mixed fields and sort keys */ +static int test2(struct perf_evsel *evsel, struct machine *machine) +{ +	int err; +	struct hists *hists = &evsel->hists; +	struct hist_entry *he; +	struct rb_root *root; +	struct rb_node *node; + +	field_order = "overhead,cpu"; +	sort_order = "pid"; + +	setup_sorting(); + +	/* +	 * expected output: +	 * +	 * Overhead  CPU  Command:  Pid +	 * ========  ===  ============= +	 *   30.00%    1  perf   :  100 +	 *   10.00%    0  perf   :  100 +	 *   10.00%    2  perf   :  100 +	 *   20.00%    2  perf   :  200 +	 *   10.00%    0  bash   :  300 +	 *   10.00%    1  bash   :  300 +	 *   10.00%    3  bash   :  300 +	 */ +	err = add_hist_entries(hists, machine); +	if (err < 0) +		goto out; + +	hists__collapse_resort(hists, NULL); +	hists__output_resort(hists); + +	if (verbose > 2) { +		pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); +		print_hists_out(hists); +	} + +	root = &evsel->hists.entries; +	node = rb_first(root); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 1 && PID(he) == 100 && he->stat.period == 300); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 0 && PID(he) == 100 && he->stat.period == 100); + +out: +	del_hist_entries(hists); +	reset_output_field(); +	return err; +} + +/* fields only (no sort key) */ +static int test3(struct perf_evsel *evsel, struct machine *machine) +{ +	int err; +	struct hists *hists = &evsel->hists; +	struct hist_entry *he; +	struct rb_root *root; +	struct rb_node *node; + +	field_order = "comm,overhead,dso"; +	sort_order = NULL; + +	setup_sorting(); + +	/* +	 * expected output: +	 * +	 * Command  Overhead  Shared Object +	 * =======  ========  ============= +	 *    bash    20.00%  bash +	 *    bash    10.00%  [kernel] +	 *    perf    30.00%  perf +	 *    perf    20.00%  [kernel] +	 *    perf    20.00%  libc +	 */ +	err = add_hist_entries(hists, machine); +	if (err < 0) +		goto out; + +	hists__collapse_resort(hists, NULL); +	hists__output_resort(hists); + +	if (verbose > 2) { +		pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); +		print_hists_out(hists); +	} + +	root = &evsel->hists.entries; +	node = rb_first(root); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && +			he->stat.period == 200); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") && +			he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && +			he->stat.period == 300); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && +			he->stat.period == 200); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && +			he->stat.period == 200); + +out: +	del_hist_entries(hists); +	reset_output_field(); +	return err; +} + +/* handle duplicate 'dso' field */ +static int test4(struct perf_evsel *evsel, struct machine *machine) +{ +	int err; +	struct hists *hists = &evsel->hists; +	struct hist_entry *he; +	struct rb_root *root; +	struct rb_node *node; + +	field_order = "dso,sym,comm,overhead,dso"; +	sort_order = "sym"; + +	setup_sorting(); + +	/* +	 * expected output: +	 * +	 * Shared Object          Symbol  Command  Overhead +	 * =============  ==============  =======  ======== +	 *          perf  [.] cmd_record     perf    10.00% +	 *          libc  [.] free           perf    10.00% +	 *          bash  [.] main           bash    10.00% +	 *          perf  [.] main           perf    20.00% +	 *          libc  [.] malloc         perf    10.00% +	 *      [kernel]  [k] page_fault     bash    10.00% +	 *      [kernel]  [k] page_fault     perf    10.00% +	 *      [kernel]  [k] schedule       perf    10.00% +	 *          bash  [.] xmalloc        bash    10.00% +	 */ +	err = add_hist_entries(hists, machine); +	if (err < 0) +		goto out; + +	hists__collapse_resort(hists, NULL); +	hists__output_resort(hists); + +	if (verbose > 2) { +		pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); +		print_hists_out(hists); +	} + +	root = &evsel->hists.entries; +	node = rb_first(root); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(DSO(he), "perf") && !strcmp(SYM(he), "cmd_record") && +			!strcmp(COMM(he), "perf") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(DSO(he), "libc") && !strcmp(SYM(he), "free") && +			!strcmp(COMM(he), "perf") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(DSO(he), "bash") && !strcmp(SYM(he), "main") && +			!strcmp(COMM(he), "bash") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(DSO(he), "perf") && !strcmp(SYM(he), "main") && +			!strcmp(COMM(he), "perf") && he->stat.period == 200); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(DSO(he), "libc") && !strcmp(SYM(he), "malloc") && +			!strcmp(COMM(he), "perf") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") && +			!strcmp(COMM(he), "bash") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") && +			!strcmp(COMM(he), "perf") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "schedule") && +			!strcmp(COMM(he), "perf") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			!strcmp(DSO(he), "bash") && !strcmp(SYM(he), "xmalloc") && +			!strcmp(COMM(he), "bash") && he->stat.period == 100); + +out: +	del_hist_entries(hists); +	reset_output_field(); +	return err; +} + +/* full sort keys w/o overhead field */ +static int test5(struct perf_evsel *evsel, struct machine *machine) +{ +	int err; +	struct hists *hists = &evsel->hists; +	struct hist_entry *he; +	struct rb_root *root; +	struct rb_node *node; + +	field_order = "cpu,pid,comm,dso,sym"; +	sort_order = "dso,pid"; + +	setup_sorting(); + +	/* +	 * expected output: +	 * +	 * CPU  Command:  Pid  Command  Shared Object          Symbol +	 * ===  =============  =======  =============  ============== +	 *   0     perf:  100     perf       [kernel]  [k] schedule +	 *   2     perf:  200     perf       [kernel]  [k] page_fault +	 *   1     bash:  300     bash       [kernel]  [k] page_fault +	 *   0     bash:  300     bash           bash  [.] xmalloc +	 *   3     bash:  300     bash           bash  [.] main +	 *   1     perf:  100     perf           libc  [.] malloc +	 *   2     perf:  100     perf           libc  [.] free +	 *   1     perf:  100     perf           perf  [.] cmd_record +	 *   1     perf:  100     perf           perf  [.] main +	 *   2     perf:  200     perf           perf  [.] main +	 */ +	err = add_hist_entries(hists, machine); +	if (err < 0) +		goto out; + +	hists__collapse_resort(hists, NULL); +	hists__output_resort(hists); + +	if (verbose > 2) { +		pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); +		print_hists_out(hists); +	} + +	root = &evsel->hists.entries; +	node = rb_first(root); +	he = rb_entry(node, struct hist_entry, rb_node); + +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 0 && PID(he) == 100 && +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && +			!strcmp(SYM(he), "schedule") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 2 && PID(he) == 200 && +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && +			!strcmp(SYM(he), "page_fault") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 1 && PID(he) == 300 && +			!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") && +			!strcmp(SYM(he), "page_fault") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 0 && PID(he) == 300 && +			!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && +			!strcmp(SYM(he), "xmalloc") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 3 && PID(he) == 300 && +			!strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && +			!strcmp(SYM(he), "main") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 1 && PID(he) == 100 && +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && +			!strcmp(SYM(he), "malloc") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 2 && PID(he) == 100 && +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && +			!strcmp(SYM(he), "free") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 1 && PID(he) == 100 && +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && +			!strcmp(SYM(he), "cmd_record") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 1 && PID(he) == 100 && +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && +			!strcmp(SYM(he), "main") && he->stat.period == 100); + +	node = rb_next(node); +	he = rb_entry(node, struct hist_entry, rb_node); +	TEST_ASSERT_VAL("Invalid hist entry", +			CPU(he) == 2 && PID(he) == 200 && +			!strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && +			!strcmp(SYM(he), "main") && he->stat.period == 100); + +out: +	del_hist_entries(hists); +	reset_output_field(); +	return err; +} + +int test__hists_output(void) +{ +	int err = TEST_FAIL; +	struct machines machines; +	struct machine *machine; +	struct perf_evsel *evsel; +	struct perf_evlist *evlist = perf_evlist__new(); +	size_t i; +	test_fn_t testcases[] = { +		test1, +		test2, +		test3, +		test4, +		test5, +	}; + +	TEST_ASSERT_VAL("No memory", evlist); + +	err = parse_events(evlist, "cpu-clock"); +	if (err) +		goto out; + +	machines__init(&machines); + +	/* setup threads/dso/map/symbols also */ +	machine = setup_fake_machine(&machines); +	if (!machine) +		goto out; + +	if (verbose > 1) +		machine__fprintf(machine, stderr); + +	evsel = perf_evlist__first(evlist); + +	for (i = 0; i < ARRAY_SIZE(testcases); i++) { +		err = testcases[i](evsel, machine); +		if (err < 0) +			break; +	} + +out: +	/* tear down everything */ +	perf_evlist__delete(evlist); +	machines__exit(&machines); + +	return err; +} diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c index d444ea2c47d..7a5ab7b0b8f 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c @@ -1,4 +1,4 @@ -#include <sys/types.h> +#include <linux/types.h>  #include <unistd.h>  #include <sys/prctl.h> @@ -36,6 +36,7 @@ static int find_comm(struct perf_evlist *evlist, const char *comm)  			    (pid_t)event->comm.tid == getpid() &&  			    strcmp(event->comm.comm, comm) == 0)  				found += 1; +			perf_evlist__mmap_consume(evlist, i);  		}  	}  	return found; @@ -50,7 +51,7 @@ static int find_comm(struct perf_evlist *evlist, const char *comm)   */  int test__keep_tracking(void)  { -	struct perf_record_opts opts = { +	struct record_opts opts = {  		.mmap_pages	     = UINT_MAX,  		.user_freq	     = UINT_MAX,  		.user_interval	     = ULLONG_MAX, @@ -141,14 +142,11 @@ int test__keep_tracking(void)  out_err:  	if (evlist) {  		perf_evlist__disable(evlist); -		perf_evlist__munmap(evlist); -		perf_evlist__close(evlist);  		perf_evlist__delete(evlist); -	} -	if (cpus) +	} else {  		cpu_map__delete(cpus); -	if (threads)  		thread_map__delete(threads); +	}  	return err;  } diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 2ca0abf1b2b..69a71ff84e0 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -1,6 +1,16 @@  PERF := .  MK   := Makefile +include config/Makefile.arch + +# FIXME looks like x86 is the only arch running tests ;-) +# we need some IS_(32/64) flag to make this generic +ifeq ($(IS_X86_64),1) +lib = lib64 +else +lib = lib +endif +  has = $(shell which $1 2>/dev/null)  # standard single make variable specified @@ -17,6 +27,7 @@ make_no_ui          := NO_NEWT=1 NO_SLANG=1 NO_GTK2=1  make_no_demangle    := NO_DEMANGLE=1  make_no_libelf      := NO_LIBELF=1  make_no_libunwind   := NO_LIBUNWIND=1 +make_no_libdw_dwarf_unwind := NO_LIBDW_DWARF_UNWIND=1  make_no_backtrace   := NO_BACKTRACE=1  make_no_libnuma     := NO_LIBNUMA=1  make_no_libaudit    := NO_LIBAUDIT=1 @@ -25,8 +36,9 @@ make_tags           := tags  make_cscope         := cscope  make_help           := help  make_doc            := doc -make_perf_o         := perf.o -make_util_map_o     := util/map.o +make_perf_o           := perf.o +make_util_map_o       := util/map.o +make_util_pmu_bison_o := util/pmu-bison.o  make_install        := install  make_install_bin    := install-bin  make_install_doc    := install-doc @@ -34,11 +46,13 @@ make_install_man    := install-man  make_install_html   := install-html  make_install_info   := install-info  make_install_pdf    := install-pdf +make_static         := LDFLAGS=-static  # all the NO_* variable combined  make_minimal        := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1  make_minimal        += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1  make_minimal        += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 +make_minimal        += NO_LIBDW_DWARF_UNWIND=1  # $(run) contains all available tests  run := make_pure @@ -55,6 +69,7 @@ run += make_no_ui  run += make_no_demangle  run += make_no_libelf  run += make_no_libunwind +run += make_no_libdw_dwarf_unwind  run += make_no_backtrace  run += make_no_libnuma  run += make_no_libaudit @@ -63,6 +78,7 @@ run += make_help  run += make_doc  run += make_perf_o  run += make_util_map_o +run += make_util_pmu_bison_o  run += make_install  run += make_install_bin  # FIXME 'install-*' commented out till they're fixed @@ -72,6 +88,7 @@ run += make_install_bin  # run += make_install_info  # run += make_install_pdf  run += make_minimal +run += make_static  ifneq ($(call has,ctags),)  run += make_tags @@ -103,13 +120,40 @@ test_make_doc_O  := $(test_ok)  test_make_python_perf_so := test -f $(PERF)/python/perf.so -test_make_perf_o     := test -f $(PERF)/perf.o -test_make_util_map_o := test -f $(PERF)/util/map.o - -test_make_install       := test -x $$TMP_DEST/bin/perf -test_make_install_O     := $(test_make_install) -test_make_install_bin   := $(test_make_install) -test_make_install_bin_O := $(test_make_install) +test_make_perf_o           := test -f $(PERF)/perf.o +test_make_util_map_o       := test -f $(PERF)/util/map.o +test_make_util_pmu_bison_o := test -f $(PERF)/util/pmu-bison.o + +define test_dest_files +  for file in $(1); do				\ +    if [ ! -x $$TMP_DEST/$$file ]; then		\ +      echo "  failed to find: $$file";		\ +    fi						\ +  done +endef + +installed_files_bin := bin/perf +installed_files_bin += etc/bash_completion.d/perf +installed_files_bin += libexec/perf-core/perf-archive + +installed_files_plugins := $(lib)/traceevent/plugins/plugin_cfg80211.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_scsi.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_xen.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_function.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_sched_switch.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_mac80211.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_kvm.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_kmem.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_hrtimer.so +installed_files_plugins += $(lib)/traceevent/plugins/plugin_jbd2.so + +installed_files_all := $(installed_files_bin) +installed_files_all += $(installed_files_plugins) + +test_make_install       := $(call test_dest_files,$(installed_files_all)) +test_make_install_O     := $(call test_dest_files,$(installed_files_all)) +test_make_install_bin   := $(call test_dest_files,$(installed_files_bin)) +test_make_install_bin_O := $(call test_dest_files,$(installed_files_bin))  # FIXME nothing gets installed  test_make_install_man    := test -f $$TMP_DEST/share/man/man1/perf.1 @@ -131,13 +175,10 @@ test_make_install_info_O := $(test_ok)  test_make_install_pdf    := $(test_ok)  test_make_install_pdf_O  := $(test_ok) -# Kbuild tests only -#test_make_python_perf_so_O := test -f $$TMP/tools/perf/python/perf.so -#test_make_perf_o_O         := test -f $$TMP/tools/perf/perf.o -#test_make_util_map_o_O     := test -f $$TMP/tools/perf/util/map.o - -test_make_perf_o_O     := true -test_make_util_map_o_O := true +test_make_python_perf_so_O    := test -f $$TMP_O/python/perf.so +test_make_perf_o_O            := test -f $$TMP_O/perf.o +test_make_util_map_o_O        := test -f $$TMP_O/util/map.o +test_make_util_pmu_bison_o_O := test -f $$TMP_O/util/pmu-bison.o  test_default = test -x $(PERF)/perf  test = $(if $(test_$1),$(test_$1),$(test_default)) @@ -162,10 +203,9 @@ $(run):  	cmd="cd $(PERF) && make -f $(MK) DESTDIR=$$TMP_DEST $($@)"; \  	echo "- $@: $$cmd" && echo $$cmd > $@ && \  	( eval $$cmd ) >> $@ 2>&1; \ -	echo "  test: $(call test,$@)"; \ +	echo "  test: $(call test,$@)" >> $@ 2>&1; \  	$(call test,$@) && \ -	rm -f $@ \ -	rm -rf $$TMP_DEST +	rm -rf $@ $$TMP_DEST || (cat $@ ; false)  $(run_O):  	$(call clean) @@ -174,16 +214,20 @@ $(run_O):  	cmd="cd $(PERF) && make -f $(MK) O=$$TMP_O DESTDIR=$$TMP_DEST $($(patsubst %_O,%,$@))"; \  	echo "- $@: $$cmd" && echo $$cmd > $@ && \  	( eval $$cmd ) >> $@ 2>&1 && \ -	echo "  test: $(call test_O,$@)"; \ +	echo "  test: $(call test_O,$@)" >> $@ 2>&1; \  	$(call test_O,$@) && \ -	rm -f $@ && \ -	rm -rf $$TMP_O \ -	rm -rf $$TMP_DEST +	rm -rf $@ $$TMP_O $$TMP_DEST || (cat $@ ; false) + +tarpkg: +	@cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \ +	echo "- $@: $$cmd" && echo $$cmd > $@ && \ +	( eval $$cmd ) >> $@ 2>&1 +	 -all: $(run) $(run_O) +all: $(run) $(run_O) tarpkg  	@echo OK  out: $(run_O)  	@echo OK -.PHONY: all $(run) $(run_O) clean +.PHONY: all $(run) $(run_O) tarpkg clean diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index c4185b9aeb8..142263492f6 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -65,10 +65,10 @@ int test__basic_mmap(void)  		char name[64];  		snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); -		evsels[i] = perf_evsel__newtp("syscalls", name, i); +		evsels[i] = perf_evsel__newtp("syscalls", name);  		if (evsels[i] == NULL) {  			pr_debug("perf_evsel__new\n"); -			goto out_free_evlist; +			goto out_delete_evlist;  		}  		evsels[i]->attr.wakeup_events = 1; @@ -80,7 +80,7 @@ int test__basic_mmap(void)  			pr_debug("failed to open counter: %s, "  				 "tweak /proc/sys/kernel/perf_event_paranoid?\n",  				 strerror(errno)); -			goto out_close_fd; +			goto out_delete_evlist;  		}  		nr_events[i] = 0; @@ -90,7 +90,7 @@ int test__basic_mmap(void)  	if (perf_evlist__mmap(evlist, 128, true) < 0) {  		pr_debug("failed to mmap events: %d (%s)\n", errno,  			 strerror(errno)); -		goto out_close_fd; +		goto out_delete_evlist;  	}  	for (i = 0; i < nsyscalls; ++i) @@ -105,13 +105,13 @@ int test__basic_mmap(void)  		if (event->header.type != PERF_RECORD_SAMPLE) {  			pr_debug("unexpected %s event\n",  				 perf_event__name(event->header.type)); -			goto out_munmap; +			goto out_delete_evlist;  		}  		err = perf_evlist__parse_sample(evlist, event, &sample);  		if (err) {  			pr_err("Can't parse sample, err = %d\n", err); -			goto out_munmap; +			goto out_delete_evlist;  		}  		err = -1; @@ -119,29 +119,27 @@ int test__basic_mmap(void)  		if (evsel == NULL) {  			pr_debug("event with id %" PRIu64  				 " doesn't map to an evsel\n", sample.id); -			goto out_munmap; +			goto out_delete_evlist;  		}  		nr_events[evsel->idx]++; +		perf_evlist__mmap_consume(evlist, 0);  	}  	err = 0; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {  			pr_debug("expected %d %s events, got %d\n",  				 expected_nr_events[evsel->idx],  				 perf_evsel__name(evsel), nr_events[evsel->idx]);  			err = -1; -			goto out_munmap; +			goto out_delete_evlist;  		}  	} -out_munmap: -	perf_evlist__munmap(evlist); -out_close_fd: -	for (i = 0; i < nsyscalls; ++i) -		perf_evsel__close_fd(evsels[i], 1, threads->nr); -out_free_evlist: +out_delete_evlist:  	perf_evlist__delete(evlist); +	cpus	= NULL; +	threads = NULL;  out_free_cpus:  	cpu_map__delete(cpus);  out_free_threads: diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c new file mode 100644 index 00000000000..4a456fef66c --- /dev/null +++ b/tools/perf/tests/mmap-thread-lookup.c @@ -0,0 +1,233 @@ +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include "debug.h" +#include "tests.h" +#include "machine.h" +#include "thread_map.h" +#include "symbol.h" +#include "thread.h" + +#define THREADS 4 + +static int go_away; + +struct thread_data { +	pthread_t	pt; +	pid_t		tid; +	void		*map; +	int		ready[2]; +}; + +static struct thread_data threads[THREADS]; + +static int thread_init(struct thread_data *td) +{ +	void *map; + +	map = mmap(NULL, page_size, +		   PROT_READ|PROT_WRITE|PROT_EXEC, +		   MAP_SHARED|MAP_ANONYMOUS, -1, 0); + +	if (map == MAP_FAILED) { +		perror("mmap failed"); +		return -1; +	} + +	td->map = map; +	td->tid = syscall(SYS_gettid); + +	pr_debug("tid = %d, map = %p\n", td->tid, map); +	return 0; +} + +static void *thread_fn(void *arg) +{ +	struct thread_data *td = arg; +	ssize_t ret; +	int go; + +	if (thread_init(td)) +		return NULL; + +	/* Signal thread_create thread is initialized. */ +	ret = write(td->ready[1], &go, sizeof(int)); +	if (ret != sizeof(int)) { +		pr_err("failed to notify\n"); +		return NULL; +	} + +	while (!go_away) { +		/* Waiting for main thread to kill us. */ +		usleep(100); +	} + +	munmap(td->map, page_size); +	return NULL; +} + +static int thread_create(int i) +{ +	struct thread_data *td = &threads[i]; +	int err, go; + +	if (pipe(td->ready)) +		return -1; + +	err = pthread_create(&td->pt, NULL, thread_fn, td); +	if (!err) { +		/* Wait for thread initialization. */ +		ssize_t ret = read(td->ready[0], &go, sizeof(int)); +		err = ret != sizeof(int); +	} + +	close(td->ready[0]); +	close(td->ready[1]); +	return err; +} + +static int threads_create(void) +{ +	struct thread_data *td0 = &threads[0]; +	int i, err = 0; + +	go_away = 0; + +	/* 0 is main thread */ +	if (thread_init(td0)) +		return -1; + +	for (i = 1; !err && i < THREADS; i++) +		err = thread_create(i); + +	return err; +} + +static int threads_destroy(void) +{ +	struct thread_data *td0 = &threads[0]; +	int i, err = 0; + +	/* cleanup the main thread */ +	munmap(td0->map, page_size); + +	go_away = 1; + +	for (i = 1; !err && i < THREADS; i++) +		err = pthread_join(threads[i].pt, NULL); + +	return err; +} + +typedef int (*synth_cb)(struct machine *machine); + +static int synth_all(struct machine *machine) +{ +	return perf_event__synthesize_threads(NULL, +					      perf_event__process, +					      machine, 0); +} + +static int synth_process(struct machine *machine) +{ +	struct thread_map *map; +	int err; + +	map = thread_map__new_by_pid(getpid()); + +	err = perf_event__synthesize_thread_map(NULL, map, +						perf_event__process, +						machine, 0); + +	thread_map__delete(map); +	return err; +} + +static int mmap_events(synth_cb synth) +{ +	struct machines machines; +	struct machine *machine; +	int err, i; + +	/* +	 * The threads_create will not return before all threads +	 * are spawned and all created memory map. +	 * +	 * They will loop until threads_destroy is called, so we +	 * can safely run synthesizing function. +	 */ +	TEST_ASSERT_VAL("failed to create threads", !threads_create()); + +	machines__init(&machines); +	machine = &machines.host; + +	dump_trace = verbose > 1 ? 1 : 0; + +	err = synth(machine); + +	dump_trace = 0; + +	TEST_ASSERT_VAL("failed to destroy threads", !threads_destroy()); +	TEST_ASSERT_VAL("failed to synthesize maps", !err); + +	/* +	 * All data is synthesized, try to find map for each +	 * thread object. +	 */ +	for (i = 0; i < THREADS; i++) { +		struct thread_data *td = &threads[i]; +		struct addr_location al; +		struct thread *thread; + +		thread = machine__findnew_thread(machine, getpid(), td->tid); + +		pr_debug("looking for map %p\n", td->map); + +		thread__find_addr_map(thread, machine, +				      PERF_RECORD_MISC_USER, MAP__FUNCTION, +				      (unsigned long) (td->map + 1), &al); + +		if (!al.map) { +			pr_debug("failed, couldn't find map\n"); +			err = -1; +			break; +		} + +		pr_debug("map %p, addr %" PRIx64 "\n", al.map, al.map->start); +	} + +	machine__delete_threads(machine); +	machines__exit(&machines); +	return err; +} + +/* + * This test creates 'THREADS' number of threads (including + * main thread) and each thread creates memory map. + * + * When threads are created, we synthesize them with both + * (separate tests): + *   perf_event__synthesize_thread_map (process based) + *   perf_event__synthesize_threads    (global) + * + * We test we can find all memory maps via: + *   thread__find_addr_map + * + * by using all thread objects. + */ +int test__mmap_thread_lookup(void) +{ +	/* perf_event__synthesize_threads synthesize */ +	TEST_ASSERT_VAL("failed with sythesizing all", +			!mmap_events(synth_all)); + +	/* perf_event__synthesize_thread_map synthesize */ +	TEST_ASSERT_VAL("failed with sythesizing process", +			!mmap_events(synth_process)); + +	return 0; +} diff --git a/tools/perf/tests/open-syscall-all-cpus.c b/tools/perf/tests/open-syscall-all-cpus.c index b0657a9ccda..5fecdbd2f5f 100644 --- a/tools/perf/tests/open-syscall-all-cpus.c +++ b/tools/perf/tests/open-syscall-all-cpus.c @@ -26,7 +26,7 @@ int test__open_syscall_event_on_all_cpus(void)  	CPU_ZERO(&cpu_set); -	evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); +	evsel = perf_evsel__newtp("syscalls", "sys_enter_open");  	if (evsel == NULL) {  		pr_debug("is debugfs mounted on /sys/kernel/debug?\n");  		goto out_thread_map_delete; diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index fc5b9fca8b4..c505ef2af24 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c @@ -6,15 +6,15 @@  int test__syscall_open_tp_fields(void)  { -	struct perf_record_opts opts = { +	struct record_opts opts = {  		.target = {  			.uid = UINT_MAX,  			.uses_mmap = true,  		}, -		.no_delay   = true, -		.freq	    = 1, -		.mmap_pages = 256, -		.raw_samples = true, +		.no_buffering = true, +		.freq	      = 1, +		.mmap_pages   = 256, +		.raw_samples  = true,  	};  	const char *filename = "/etc/passwd";  	int flags = O_RDONLY | O_DIRECTORY; @@ -27,7 +27,7 @@ int test__syscall_open_tp_fields(void)  		goto out;  	} -	evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); +	evsel = perf_evsel__newtp("syscalls", "sys_enter_open");  	if (evsel == NULL) {  		pr_debug("%s: perf_evsel__newtp\n", __func__);  		goto out_delete_evlist; @@ -48,13 +48,13 @@ int test__syscall_open_tp_fields(void)  	err = perf_evlist__open(evlist);  	if (err < 0) {  		pr_debug("perf_evlist__open: %s\n", strerror(errno)); -		goto out_delete_maps; +		goto out_delete_evlist;  	}  	err = perf_evlist__mmap(evlist, UINT_MAX, false);  	if (err < 0) {  		pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); -		goto out_close_evlist; +		goto out_delete_evlist;  	}  	perf_evlist__enable(evlist); @@ -77,13 +77,15 @@ int test__syscall_open_tp_fields(void)  				++nr_events; -				if (type != PERF_RECORD_SAMPLE) +				if (type != PERF_RECORD_SAMPLE) { +					perf_evlist__mmap_consume(evlist, i);  					continue; +				}  				err = perf_evsel__parse_sample(evsel, event, &sample);  				if (err) {  					pr_err("Can't parse sample, err = %d\n", err); -					goto out_munmap; +					goto out_delete_evlist;  				}  				tp_flags = perf_evsel__intval(evsel, &sample, "flags"); @@ -91,7 +93,7 @@ int test__syscall_open_tp_fields(void)  				if (flags != tp_flags) {  					pr_debug("%s: Expected flags=%#x, got %#x\n",  						 __func__, flags, tp_flags); -					goto out_munmap; +					goto out_delete_evlist;  				}  				goto out_ok; @@ -103,17 +105,11 @@ int test__syscall_open_tp_fields(void)  		if (++nr_polls > 5) {  			pr_debug("%s: no events!\n", __func__); -			goto out_munmap; +			goto out_delete_evlist;  		}  	}  out_ok:  	err = 0; -out_munmap: -	perf_evlist__munmap(evlist); -out_close_evlist: -	perf_evlist__close(evlist); -out_delete_maps: -	perf_evlist__delete_maps(evlist);  out_delete_evlist:  	perf_evlist__delete(evlist);  out: diff --git a/tools/perf/tests/open-syscall.c b/tools/perf/tests/open-syscall.c index befc0671f95..c1dc7d25f38 100644 --- a/tools/perf/tests/open-syscall.c +++ b/tools/perf/tests/open-syscall.c @@ -15,7 +15,7 @@ int test__open_syscall_event(void)  		return -1;  	} -	evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); +	evsel = perf_evsel__newtp("syscalls", "sys_enter_open");  	if (evsel == NULL) {  		pr_debug("is debugfs mounted on /sys/kernel/debug?\n");  		goto out_thread_map_delete; diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 48114d164e9..deba66955f8 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -2,8 +2,8 @@  #include "parse-events.h"  #include "evsel.h"  #include "evlist.h" -#include "sysfs.h" -#include <lk/debugfs.h> +#include <api/fs/fs.h> +#include <api/fs/debugfs.h>  #include "tests.h"  #include <linux/hw_breakpoint.h> @@ -30,7 +30,7 @@ static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist)  	TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1);  	TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		TEST_ASSERT_VAL("wrong type",  			PERF_TYPE_TRACEPOINT == evsel->attr.type);  		TEST_ASSERT_VAL("wrong sample_type", @@ -201,7 +201,7 @@ test__checkevent_tracepoint_multi_modifier(struct perf_evlist *evlist)  	TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		TEST_ASSERT_VAL("wrong exclude_user",  				!evsel->attr.exclude_user);  		TEST_ASSERT_VAL("wrong exclude_kernel", @@ -441,9 +441,8 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist)  static int test__checkevent_pmu_events(struct perf_evlist *evlist)  { -	struct perf_evsel *evsel; +	struct perf_evsel *evsel = perf_evlist__first(evlist); -	evsel = list_entry(evlist->entries.next, struct perf_evsel, node);  	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);  	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);  	TEST_ASSERT_VAL("wrong exclude_user", @@ -1175,188 +1174,240 @@ static int test__all_tracepoints(struct perf_evlist *evlist)  struct evlist_test {  	const char *name;  	__u32 type; +	const int id;  	int (*check)(struct perf_evlist *evlist);  };  static struct evlist_test test__events[] = { -	[0] = { +	{  		.name  = "syscalls:sys_enter_open",  		.check = test__checkevent_tracepoint, +		.id    = 0,  	}, -	[1] = { +	{  		.name  = "syscalls:*",  		.check = test__checkevent_tracepoint_multi, +		.id    = 1,  	}, -	[2] = { +	{  		.name  = "r1a",  		.check = test__checkevent_raw, +		.id    = 2,  	}, -	[3] = { +	{  		.name  = "1:1",  		.check = test__checkevent_numeric, +		.id    = 3,  	}, -	[4] = { +	{  		.name  = "instructions",  		.check = test__checkevent_symbolic_name, +		.id    = 4,  	}, -	[5] = { +	{  		.name  = "cycles/period=100000,config2/",  		.check = test__checkevent_symbolic_name_config, +		.id    = 5,  	}, -	[6] = { +	{  		.name  = "faults",  		.check = test__checkevent_symbolic_alias, +		.id    = 6,  	}, -	[7] = { +	{  		.name  = "L1-dcache-load-miss",  		.check = test__checkevent_genhw, +		.id    = 7,  	}, -	[8] = { +	{  		.name  = "mem:0",  		.check = test__checkevent_breakpoint, +		.id    = 8,  	}, -	[9] = { +	{  		.name  = "mem:0:x",  		.check = test__checkevent_breakpoint_x, +		.id    = 9,  	}, -	[10] = { +	{  		.name  = "mem:0:r",  		.check = test__checkevent_breakpoint_r, +		.id    = 10,  	}, -	[11] = { +	{  		.name  = "mem:0:w",  		.check = test__checkevent_breakpoint_w, +		.id    = 11,  	}, -	[12] = { +	{  		.name  = "syscalls:sys_enter_open:k",  		.check = test__checkevent_tracepoint_modifier, +		.id    = 12,  	}, -	[13] = { +	{  		.name  = "syscalls:*:u",  		.check = test__checkevent_tracepoint_multi_modifier, +		.id    = 13,  	}, -	[14] = { +	{  		.name  = "r1a:kp",  		.check = test__checkevent_raw_modifier, +		.id    = 14,  	}, -	[15] = { +	{  		.name  = "1:1:hp",  		.check = test__checkevent_numeric_modifier, +		.id    = 15,  	}, -	[16] = { +	{  		.name  = "instructions:h",  		.check = test__checkevent_symbolic_name_modifier, +		.id    = 16,  	}, -	[17] = { +	{  		.name  = "faults:u",  		.check = test__checkevent_symbolic_alias_modifier, +		.id    = 17,  	}, -	[18] = { +	{  		.name  = "L1-dcache-load-miss:kp",  		.check = test__checkevent_genhw_modifier, +		.id    = 18,  	}, -	[19] = { +	{  		.name  = "mem:0:u",  		.check = test__checkevent_breakpoint_modifier, +		.id    = 19,  	}, -	[20] = { +	{  		.name  = "mem:0:x:k",  		.check = test__checkevent_breakpoint_x_modifier, +		.id    = 20,  	}, -	[21] = { +	{  		.name  = "mem:0:r:hp",  		.check = test__checkevent_breakpoint_r_modifier, +		.id    = 21,  	}, -	[22] = { +	{  		.name  = "mem:0:w:up",  		.check = test__checkevent_breakpoint_w_modifier, +		.id    = 22,  	}, -	[23] = { +	{  		.name  = "r1,syscalls:sys_enter_open:k,1:1:hp",  		.check = test__checkevent_list, +		.id    = 23,  	}, -	[24] = { +	{  		.name  = "instructions:G",  		.check = test__checkevent_exclude_host_modifier, +		.id    = 24,  	}, -	[25] = { +	{  		.name  = "instructions:H",  		.check = test__checkevent_exclude_guest_modifier, +		.id    = 25,  	}, -	[26] = { +	{  		.name  = "mem:0:rw",  		.check = test__checkevent_breakpoint_rw, +		.id    = 26,  	}, -	[27] = { +	{  		.name  = "mem:0:rw:kp",  		.check = test__checkevent_breakpoint_rw_modifier, +		.id    = 27,  	}, -	[28] = { +	{  		.name  = "{instructions:k,cycles:upp}",  		.check = test__group1, +		.id    = 28,  	}, -	[29] = { +	{  		.name  = "{faults:k,cache-references}:u,cycles:k",  		.check = test__group2, +		.id    = 29,  	}, -	[30] = { +	{  		.name  = "group1{syscalls:sys_enter_open:H,cycles:kppp},group2{cycles,1:3}:G,instructions:u",  		.check = test__group3, +		.id    = 30,  	}, -	[31] = { +	{  		.name  = "{cycles:u,instructions:kp}:p",  		.check = test__group4, +		.id    = 31,  	}, -	[32] = { +	{  		.name  = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles",  		.check = test__group5, +		.id    = 32,  	}, -	[33] = { +	{  		.name  = "*:*",  		.check = test__all_tracepoints, +		.id    = 33,  	}, -	[34] = { +	{  		.name  = "{cycles,cache-misses:G}:H",  		.check = test__group_gh1, +		.id    = 34,  	}, -	[35] = { +	{  		.name  = "{cycles,cache-misses:H}:G",  		.check = test__group_gh2, +		.id    = 35,  	}, -	[36] = { +	{  		.name  = "{cycles:G,cache-misses:H}:u",  		.check = test__group_gh3, +		.id    = 36,  	}, -	[37] = { +	{  		.name  = "{cycles:G,cache-misses:H}:uG",  		.check = test__group_gh4, +		.id    = 37,  	}, -	[38] = { +	{  		.name  = "{cycles,cache-misses,branch-misses}:S",  		.check = test__leader_sample1, +		.id    = 38,  	}, -	[39] = { +	{  		.name  = "{instructions,branch-misses}:Su",  		.check = test__leader_sample2, +		.id    = 39,  	}, -	[40] = { +	{  		.name  = "instructions:uDp",  		.check = test__checkevent_pinned_modifier, +		.id    = 40,  	}, -	[41] = { +	{  		.name  = "{cycles,cache-misses,branch-misses}:D",  		.check = test__pinned_group, +		.id    = 41, +	}, +#if defined(__s390x__) +	{ +		.name  = "kvm-s390:kvm_s390_create_vm", +		.check = test__checkevent_tracepoint, +		.id    = 100,  	}, +#endif  };  static struct evlist_test test__events_pmu[] = { -	[0] = { +	{  		.name  = "cpu/config=10,config1,config2=3,period=1000/u",  		.check = test__checkevent_pmu, +		.id    = 0,  	}, -	[1] = { +	{  		.name  = "cpu/config=1,name=krava/u,cpu/config=2/u",  		.check = test__checkevent_pmu_name, +		.id    = 1,  	},  }; @@ -1386,10 +1437,10 @@ static int test_event(struct evlist_test *e)  	if (ret) {  		pr_debug("failed to parse event '%s', err %d\n",  			 e->name, ret); -		return ret; +	} else { +		ret = e->check(evlist);  	} - -	ret = e->check(evlist); +	  	perf_evlist__delete(evlist);  	return ret; @@ -1403,7 +1454,7 @@ static int test_events(struct evlist_test *events, unsigned cnt)  	for (i = 0; i < cnt; i++) {  		struct evlist_test *e = &events[i]; -		pr_debug("running test %d '%s'\n", i, e->name); +		pr_debug("running test %d '%s'\n", e->id, e->name);  		ret1 = test_event(e);  		if (ret1)  			ret2 = ret1; @@ -1456,7 +1507,7 @@ static int test_pmu(void)  	int ret;  	snprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu/format/", -		 sysfs_find_mountpoint()); +		 sysfs__mountpoint());  	ret = stat(path, &st);  	if (ret) @@ -1473,7 +1524,7 @@ static int test_pmu_events(void)  	int ret;  	snprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu/events/", -		 sysfs_find_mountpoint()); +		 sysfs__mountpoint());  	ret = stat(path, &st);  	if (ret) { diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c index e117b6c6a24..905019f9b74 100644 --- a/tools/perf/tests/parse-no-sample-id-all.c +++ b/tools/perf/tests/parse-no-sample-id-all.c @@ -1,4 +1,4 @@ -#include <sys/types.h> +#include <linux/types.h>  #include <stddef.h>  #include "tests.h" diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index b8a7056519a..aca1a83dd13 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -34,18 +34,18 @@ realloc:  int test__PERF_RECORD(void)  { -	struct perf_record_opts opts = { +	struct record_opts opts = {  		.target = {  			.uid = UINT_MAX,  			.uses_mmap = true,  		}, -		.no_delay   = true, -		.freq	    = 10, -		.mmap_pages = 256, +		.no_buffering = true, +		.freq	      = 10, +		.mmap_pages   = 256,  	};  	cpu_set_t cpu_mask;  	size_t cpu_mask_size = sizeof(cpu_mask); -	struct perf_evlist *evlist = perf_evlist__new(); +	struct perf_evlist *evlist = perf_evlist__new_default();  	struct perf_evsel *evsel;  	struct perf_sample sample;  	const char *cmd = "sleep"; @@ -66,16 +66,6 @@ int test__PERF_RECORD(void)  	}  	/* -	 * We need at least one evsel in the evlist, use the default -	 * one: "cycles". -	 */ -	err = perf_evlist__add_default(evlist); -	if (err < 0) { -		pr_debug("Not enough memory to create evsel\n"); -		goto out_delete_evlist; -	} - -	/*  	 * Create maps of threads and cpus to monitor. In this case  	 * we start with all threads and cpus (-1, -1) but then in  	 * perf_evlist__prepare_workload we'll fill in the only thread @@ -93,11 +83,10 @@ int test__PERF_RECORD(void)  	 * so that we have time to open the evlist (calling sys_perf_event_open  	 * on all the fds) and then mmap them.  	 */ -	err = perf_evlist__prepare_workload(evlist, &opts.target, argv, -					    false, false); +	err = perf_evlist__prepare_workload(evlist, &opts.target, argv, false, NULL);  	if (err < 0) {  		pr_debug("Couldn't run the workload!\n"); -		goto out_delete_maps; +		goto out_delete_evlist;  	}  	/* @@ -112,7 +101,7 @@ int test__PERF_RECORD(void)  	err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);  	if (err < 0) {  		pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno)); -		goto out_delete_maps; +		goto out_delete_evlist;  	}  	cpu = err; @@ -122,7 +111,7 @@ int test__PERF_RECORD(void)  	 */  	if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) {  		pr_debug("sched_setaffinity: %s\n", strerror(errno)); -		goto out_delete_maps; +		goto out_delete_evlist;  	}  	/* @@ -132,7 +121,7 @@ int test__PERF_RECORD(void)  	err = perf_evlist__open(evlist);  	if (err < 0) {  		pr_debug("perf_evlist__open: %s\n", strerror(errno)); -		goto out_delete_maps; +		goto out_delete_evlist;  	}  	/* @@ -143,7 +132,7 @@ int test__PERF_RECORD(void)  	err = perf_evlist__mmap(evlist, opts.mmap_pages, false);  	if (err < 0) {  		pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); -		goto out_close_evlist; +		goto out_delete_evlist;  	}  	/* @@ -176,7 +165,7 @@ int test__PERF_RECORD(void)  					if (verbose)  						perf_event__fprintf(event, stderr);  					pr_debug("Couldn't parse sample\n"); -					goto out_err; +					goto out_delete_evlist;  				}  				if (verbose) { @@ -263,6 +252,8 @@ int test__PERF_RECORD(void)  						 type);  					++errs;  				} + +				perf_evlist__mmap_consume(evlist, i);  			}  		} @@ -311,12 +302,6 @@ found_exit:  		pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]");  		++errs;  	} -out_err: -	perf_evlist__munmap(evlist); -out_close_evlist: -	perf_evlist__close(evlist); -out_delete_maps: -	perf_evlist__delete_maps(evlist);  out_delete_evlist:  	perf_evlist__delete(evlist);  out: diff --git a/tools/perf/tests/perf-targz-src-pkg b/tools/perf/tests/perf-targz-src-pkg new file mode 100755 index 00000000000..238aa3927c7 --- /dev/null +++ b/tools/perf/tests/perf-targz-src-pkg @@ -0,0 +1,21 @@ +#!/bin/sh +# Test one of the main kernel Makefile targets to generate a perf sources tarball +# suitable for build outside the full kernel sources. +# +# This is to test that the tools/perf/MANIFEST file lists all the files needed to +# be in such tarball, which sometimes gets broken when we move files around, +# like when we made some files that were in tools/perf/ available to other tools/ +# codebases by moving it to tools/include/, etc. + +PERF=$1 +cd ${PERF}/../.. +make perf-targz-src-pkg > /dev/null +TARBALL=$(ls -rt perf-*.tar.gz) +TMP_DEST=$(mktemp -d) +tar xf ${TARBALL} -C $TMP_DEST +rm -f ${TARBALL} +cd - > /dev/null +make -C $TMP_DEST/perf*/tools/perf > /dev/null 2>&1 +RC=$? +rm -rf ${TMP_DEST} +exit $RC diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-time-to-tsc.c index 0ab61b1f408..3b7cd4d32dc 100644 --- a/tools/perf/tests/perf-time-to-tsc.c +++ b/tools/perf/tests/perf-time-to-tsc.c @@ -1,7 +1,6 @@  #include <stdio.h> -#include <sys/types.h>  #include <unistd.h> -#include <inttypes.h> +#include <linux/types.h>  #include <sys/prctl.h>  #include "parse-events.h" @@ -46,7 +45,7 @@ static u64 rdtsc(void)   */  int test__perf_time_to_tsc(void)  { -	struct perf_record_opts opts = { +	struct record_opts opts = {  		.mmap_pages	     = UINT_MAX,  		.user_freq	     = UINT_MAX,  		.user_interval	     = ULLONG_MAX, @@ -122,7 +121,7 @@ int test__perf_time_to_tsc(void)  			if (event->header.type != PERF_RECORD_COMM ||  			    (pid_t)event->comm.pid != getpid() ||  			    (pid_t)event->comm.tid != getpid()) -				continue; +				goto next_event;  			if (strcmp(event->comm.comm, comm1) == 0) {  				CHECK__(perf_evsel__parse_sample(evsel, event, @@ -134,6 +133,8 @@ int test__perf_time_to_tsc(void)  								 &sample));  				comm2_time = sample.time;  			} +next_event: +			perf_evlist__mmap_consume(evlist, i);  		}  	} @@ -164,14 +165,8 @@ int test__perf_time_to_tsc(void)  out_err:  	if (evlist) {  		perf_evlist__disable(evlist); -		perf_evlist__munmap(evlist); -		perf_evlist__close(evlist);  		perf_evlist__delete(evlist);  	} -	if (cpus) -		cpu_map__delete(cpus); -	if (threads) -		thread_map__delete(threads);  	return err;  } diff --git a/tools/perf/tests/rdpmc.c b/tools/perf/tests/rdpmc.c index ff94886aad9..e59143fd9e7 100644 --- a/tools/perf/tests/rdpmc.c +++ b/tools/perf/tests/rdpmc.c @@ -2,15 +2,13 @@  #include <stdlib.h>  #include <signal.h>  #include <sys/mman.h> -#include "types.h" +#include <linux/types.h>  #include "perf.h"  #include "debug.h"  #include "tests.h"  #if defined(__x86_64__) || defined(__i386__) -#define barrier() asm volatile("" ::: "memory") -  static u64 rdpmc(unsigned int counter)  {  	unsigned int low, high; diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index 77f598dbd97..7ae8d17db3d 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -1,5 +1,5 @@  #include <stdbool.h> -#include <inttypes.h> +#include <linux/types.h>  #include "util.h"  #include "event.h" @@ -22,8 +22,8 @@  } while (0)  static bool samples_same(const struct perf_sample *s1, -			 const struct perf_sample *s2, u64 type, u64 regs_user, -			 u64 read_format) +			 const struct perf_sample *s2, +			 u64 type, u64 read_format)  {  	size_t i; @@ -95,8 +95,9 @@ static bool samples_same(const struct perf_sample *s1,  	}  	if (type & PERF_SAMPLE_REGS_USER) { -		size_t sz = hweight_long(regs_user) * sizeof(u64); +		size_t sz = hweight_long(s1->user_regs.mask) * sizeof(u64); +		COMP(user_regs.mask);  		COMP(user_regs.abi);  		if (s1->user_regs.abi &&  		    (!s1->user_regs.regs || !s2->user_regs.regs || @@ -121,6 +122,9 @@ static bool samples_same(const struct perf_sample *s1,  	if (type & PERF_SAMPLE_DATA_SRC)  		COMP(data_src); +	if (type & PERF_SAMPLE_TRANSACTION) +		COMP(transaction); +  	return true;  } @@ -165,11 +169,13 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)  		.cpu		= 110,  		.raw_size	= sizeof(raw_data),  		.data_src	= 111, +		.transaction	= 112,  		.raw_data	= (void *)raw_data,  		.callchain	= &callchain.callchain,  		.branch_stack	= &branch_stack.branch_stack,  		.user_regs	= {  			.abi	= PERF_SAMPLE_REGS_ABI_64, +			.mask	= sample_regs_user,  			.regs	= user_regs,  		},  		.user_stack	= { @@ -197,8 +203,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)  		sample.read.one.id    = 99;  	} -	sz = perf_event__sample_event_size(&sample, sample_type, -					   sample_regs_user, read_format); +	sz = perf_event__sample_event_size(&sample, sample_type, read_format);  	bufsz = sz + 4096; /* Add a bit for overrun checking */  	event = malloc(bufsz);  	if (!event) { @@ -211,8 +216,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)  	event->header.misc = 0;  	event->header.size = sz; -	err = perf_event__synthesize_sample(event, sample_type, -					    sample_regs_user, read_format, +	err = perf_event__synthesize_sample(event, sample_type, read_format,  					    &sample, false);  	if (err) {  		pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", @@ -240,8 +244,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)  		goto out_free;  	} -	if (!samples_same(&sample, &sample_out, sample_type, -			  sample_regs_user, read_format)) { +	if (!samples_same(&sample, &sample_out, sample_type, read_format)) {  		pr_debug("parsing failed for sample_type %#"PRIx64"\n",  			 sample_type);  		goto out_free; @@ -273,10 +276,11 @@ int test__sample_parsing(void)  	/*  	 * Fail the test if it has not been updated when new sample format bits -	 * were added. +	 * were added.  Please actually update the test rather than just change +	 * the condition below.  	 */ -	if (PERF_SAMPLE_MAX > PERF_SAMPLE_IDENTIFIER << 1) { -		pr_debug("sample format has changed - test needs updating\n"); +	if (PERF_SAMPLE_MAX > PERF_SAMPLE_TRANSACTION << 1) { +		pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n");  		return -1;  	} diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c index 2e41e2d32cc..983d6b8562a 100644 --- a/tools/perf/tests/sw-clock.c +++ b/tools/perf/tests/sw-clock.c @@ -9,7 +9,7 @@  #include "util/cpumap.h"  #include "util/thread_map.h" -#define NR_LOOPS  1000000 +#define NR_LOOPS  10000000  /*   * This test will open software clock events (cpu-clock, task-clock) @@ -34,7 +34,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)  		.freq = 1,  	}; -	attr.sample_freq = 10000; +	attr.sample_freq = 500;  	evlist = perf_evlist__new();  	if (evlist == NULL) { @@ -42,10 +42,10 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)  		return -1;  	} -	evsel = perf_evsel__new(&attr, 0); +	evsel = perf_evsel__new(&attr);  	if (evsel == NULL) {  		pr_debug("perf_evsel__new\n"); -		goto out_free_evlist; +		goto out_delete_evlist;  	}  	perf_evlist__add(evlist, evsel); @@ -54,16 +54,23 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)  	if (!evlist->cpus || !evlist->threads) {  		err = -ENOMEM;  		pr_debug("Not enough memory to create thread/cpu maps\n"); -		goto out_delete_maps; +		goto out_delete_evlist;  	} -	perf_evlist__open(evlist); +	if (perf_evlist__open(evlist)) { +		const char *knob = "/proc/sys/kernel/perf_event_max_sample_rate"; + +		err = -errno; +		pr_debug("Couldn't open evlist: %s\nHint: check %s, using %" PRIu64 " in this test.\n", +			 strerror(errno), knob, (u64)attr.sample_freq); +		goto out_delete_evlist; +	}  	err = perf_evlist__mmap(evlist, 128, true);  	if (err < 0) {  		pr_debug("failed to mmap event: %d (%s)\n", errno,  			 strerror(errno)); -		goto out_close_evlist; +		goto out_delete_evlist;  	}  	perf_evlist__enable(evlist); @@ -78,16 +85,18 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)  		struct perf_sample sample;  		if (event->header.type != PERF_RECORD_SAMPLE) -			continue; +			goto next_event;  		err = perf_evlist__parse_sample(evlist, event, &sample);  		if (err < 0) {  			pr_debug("Error during parse sample\n"); -			goto out_unmap_evlist; +			goto out_delete_evlist;  		}  		total_periods += sample.period;  		nr_samples++; +next_event: +		perf_evlist__mmap_consume(evlist, 0);  	}  	if ((u64) nr_samples == total_periods) { @@ -96,13 +105,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)  		err = -1;  	} -out_unmap_evlist: -	perf_evlist__munmap(evlist); -out_close_evlist: -	perf_evlist__close(evlist); -out_delete_maps: -	perf_evlist__delete_maps(evlist); -out_free_evlist: +out_delete_evlist:  	perf_evlist__delete(evlist);  	return err;  } diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index 28fe5894b06..5ff3db318f1 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -9,12 +9,21 @@  static int exited;  static int nr_exit; -static void sig_handler(int sig) +static void sig_handler(int sig __maybe_unused)  {  	exited = 1; +} -	if (sig == SIGUSR1) -		nr_exit = -1; +/* + * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since + * we asked by setting its exec_error to this handler. + */ +static void workload_exec_failed_signal(int signo __maybe_unused, +					siginfo_t *info __maybe_unused, +					void *ucontext __maybe_unused) +{ +	exited	= 1; +	nr_exit = -1;  }  /* @@ -28,29 +37,19 @@ int test__task_exit(void)  	union perf_event *event;  	struct perf_evsel *evsel;  	struct perf_evlist *evlist; -	struct perf_target target = { +	struct target target = {  		.uid		= UINT_MAX,  		.uses_mmap	= true,  	};  	const char *argv[] = { "true", NULL };  	signal(SIGCHLD, sig_handler); -	signal(SIGUSR1, sig_handler); -	evlist = perf_evlist__new(); +	evlist = perf_evlist__new_default();  	if (evlist == NULL) { -		pr_debug("perf_evlist__new\n"); +		pr_debug("perf_evlist__new_default\n");  		return -1;  	} -	/* -	 * We need at least one evsel in the evlist, use the default -	 * one: "cycles". -	 */ -	err = perf_evlist__add_default(evlist); -	if (err < 0) { -		pr_debug("Not enough memory to create evsel\n"); -		goto out_free_evlist; -	}  	/*  	 * Create maps of threads and cpus to monitor. In this case @@ -63,13 +62,14 @@ int test__task_exit(void)  	if (!evlist->cpus || !evlist->threads) {  		err = -ENOMEM;  		pr_debug("Not enough memory to create thread/cpu maps\n"); -		goto out_delete_maps; +		goto out_delete_evlist;  	} -	err = perf_evlist__prepare_workload(evlist, &target, argv, false, true); +	err = perf_evlist__prepare_workload(evlist, &target, argv, false, +					    workload_exec_failed_signal);  	if (err < 0) {  		pr_debug("Couldn't run the workload!\n"); -		goto out_delete_maps; +		goto out_delete_evlist;  	}  	evsel = perf_evlist__first(evlist); @@ -83,23 +83,23 @@ int test__task_exit(void)  	err = perf_evlist__open(evlist);  	if (err < 0) {  		pr_debug("Couldn't open the evlist: %s\n", strerror(-err)); -		goto out_delete_maps; +		goto out_delete_evlist;  	}  	if (perf_evlist__mmap(evlist, 128, true) < 0) {  		pr_debug("failed to mmap events: %d (%s)\n", errno,  			 strerror(errno)); -		goto out_close_evlist; +		goto out_delete_evlist;  	}  	perf_evlist__start_workload(evlist);  retry:  	while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { -		if (event->header.type != PERF_RECORD_EXIT) -			continue; +		if (event->header.type == PERF_RECORD_EXIT) +			nr_exit++; -		nr_exit++; +		perf_evlist__mmap_consume(evlist, 0);  	}  	if (!exited || !nr_exit) { @@ -112,12 +112,7 @@ retry:  		err = -1;  	} -	perf_evlist__munmap(evlist); -out_close_evlist: -	perf_evlist__close(evlist); -out_delete_maps: -	perf_evlist__delete_maps(evlist); -out_free_evlist: +out_delete_evlist:  	perf_evlist__delete(evlist);  	return err;  } diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index e0ac713857b..ed64790a395 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -28,6 +28,8 @@ int test__syscall_open_tp_fields(void);  int test__pmu(void);  int test__attr(void);  int test__dso_data(void); +int test__dso_data_cache(void); +int test__dso_data_reopen(void);  int test__parse_events(void);  int test__hists_link(void);  int test__python_use(void); @@ -40,5 +42,19 @@ int test__code_reading(void);  int test__sample_parsing(void);  int test__keep_tracking(void);  int test__parse_no_sample_id_all(void); +int test__dwarf_unwind(void); +int test__hists_filter(void); +int test__mmap_thread_lookup(void); +int test__thread_mg_share(void); +int test__hists_output(void); +int test__hists_cumulate(void); +#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) +#ifdef HAVE_DWARF_UNWIND_SUPPORT +struct thread; +struct perf_sample; +int test__arch_unwind_sample(struct perf_sample *sample, +			     struct thread *thread); +#endif +#endif  #endif /* TESTS_H */ diff --git a/tools/perf/tests/thread-mg-share.c b/tools/perf/tests/thread-mg-share.c new file mode 100644 index 00000000000..2b2e0dbe114 --- /dev/null +++ b/tools/perf/tests/thread-mg-share.c @@ -0,0 +1,90 @@ +#include "tests.h" +#include "machine.h" +#include "thread.h" +#include "map.h" + +int test__thread_mg_share(void) +{ +	struct machines machines; +	struct machine *machine; + +	/* thread group */ +	struct thread *leader; +	struct thread *t1, *t2, *t3; +	struct map_groups *mg; + +	/* other process */ +	struct thread *other, *other_leader; +	struct map_groups *other_mg; + +	/* +	 * This test create 2 processes abstractions (struct thread) +	 * with several threads and checks they properly share and +	 * maintain map groups info (struct map_groups). +	 * +	 * thread group (pid: 0, tids: 0, 1, 2, 3) +	 * other  group (pid: 4, tids: 4, 5) +	*/ + +	machines__init(&machines); +	machine = &machines.host; + +	/* create process with 4 threads */ +	leader = machine__findnew_thread(machine, 0, 0); +	t1     = machine__findnew_thread(machine, 0, 1); +	t2     = machine__findnew_thread(machine, 0, 2); +	t3     = machine__findnew_thread(machine, 0, 3); + +	/* and create 1 separated process, without thread leader */ +	other  = machine__findnew_thread(machine, 4, 5); + +	TEST_ASSERT_VAL("failed to create threads", +			leader && t1 && t2 && t3 && other); + +	mg = leader->mg; +	TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 4); + +	/* test the map groups pointer is shared */ +	TEST_ASSERT_VAL("map groups don't match", mg == t1->mg); +	TEST_ASSERT_VAL("map groups don't match", mg == t2->mg); +	TEST_ASSERT_VAL("map groups don't match", mg == t3->mg); + +	/* +	 * Verify the other leader was created by previous call. +	 * It should have shared map groups with no change in +	 * refcnt. +	 */ +	other_leader = machine__find_thread(machine, 4, 4); +	TEST_ASSERT_VAL("failed to find other leader", other_leader); + +	other_mg = other->mg; +	TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 2); + +	TEST_ASSERT_VAL("map groups don't match", other_mg == other_leader->mg); + +	/* release thread group */ +	thread__delete(leader); +	TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 3); + +	thread__delete(t1); +	TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 2); + +	thread__delete(t2); +	TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 1); + +	thread__delete(t3); + +	/* release other group  */ +	thread__delete(other_leader); +	TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 1); + +	thread__delete(other); + +	/* +	 * Cannot call machine__delete_threads(machine) now, +	 * because we've already released all the threads. +	 */ + +	machines__exit(&machines); +	return 0; +} diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index 2bd13edcbc1..3d9088003a5 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -26,7 +26,6 @@ int test__vmlinux_matches_kallsyms(void)  	struct map *kallsyms_map, *vmlinux_map;  	struct machine kallsyms, vmlinux;  	enum map_type type = MAP__FUNCTION; -	struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", };  	u64 mem_start, mem_end;  	/* @@ -70,14 +69,6 @@ int test__vmlinux_matches_kallsyms(void)  	 */  	kallsyms_map = machine__kernel_map(&kallsyms, type); -	sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL); -	if (sym == NULL) { -		pr_debug("dso__find_symbol_by_name "); -		goto out; -	} - -	ref_reloc_sym.addr = UM(sym->start); -  	/*  	 * Step 5:  	 * @@ -89,7 +80,6 @@ int test__vmlinux_matches_kallsyms(void)  	}  	vmlinux_map = machine__kernel_map(&vmlinux, type); -	map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym;  	/*  	 * Step 6: diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index bbc782e364b..3ccf6e14f89 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -194,7 +194,7 @@ int ui_browser__warning(struct ui_browser *browser, int timeout,  		ui_helpline__vpush(format, args);  		va_end(args);  	} else { -		while ((key == ui__question_window("Warning!", text, +		while ((key = ui__question_window("Warning!", text,  						   "Press any key...",  						   timeout)) == K_RESIZE)  			ui_browser__handle_resize(browser); @@ -256,8 +256,7 @@ int ui_browser__show(struct ui_browser *browser, const char *title,  	__ui_browser__show_title(browser, title);  	browser->title = title; -	free(browser->helpline); -	browser->helpline = NULL; +	zfree(&browser->helpline);  	va_start(ap, helpline);  	err = vasprintf(&browser->helpline, helpline, ap); @@ -268,12 +267,11 @@ int ui_browser__show(struct ui_browser *browser, const char *title,  	return err ? 0 : -1;  } -void ui_browser__hide(struct ui_browser *browser __maybe_unused) +void ui_browser__hide(struct ui_browser *browser)  {  	pthread_mutex_lock(&ui__lock);  	ui_helpline__pop(); -	free(browser->helpline); -	browser->helpline = NULL; +	zfree(&browser->helpline);  	pthread_mutex_unlock(&ui__lock);  } @@ -569,7 +567,7 @@ void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)  		browser->top = browser->top + browser->top_idx + offset;  		break;  	case SEEK_END: -		browser->top = browser->top + browser->nr_entries + offset; +		browser->top = browser->top + browser->nr_entries - 1 + offset;  		break;  	default:  		return; @@ -680,7 +678,7 @@ static void __ui_browser__line_arrow_down(struct ui_browser *browser,  	if (end >= browser->top_idx + browser->height)  		end_row = browser->height - 1;  	else -		end_row = end - browser->top_idx;; +		end_row = end - browser->top_idx;  	ui_browser__gotorc(browser, row, column);  	SLsmg_draw_vline(end_row - row + 1); diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index 404ff66a3e3..03d4d6295f1 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -1,9 +1,7 @@  #ifndef _PERF_UI_BROWSER_H_  #define _PERF_UI_BROWSER_H_ 1 -#include <stdbool.h> -#include <sys/types.h> -#include "../types.h" +#include <linux/types.h>  #define HE_COLORSET_TOP		50  #define HE_COLORSET_MEDIUM	51 @@ -21,32 +19,32 @@ struct ui_browser {  	void	      *priv;  	const char    *title;  	char	      *helpline; -	unsigned int  (*refresh)(struct ui_browser *self); -	void	      (*write)(struct ui_browser *self, void *entry, int row); -	void	      (*seek)(struct ui_browser *self, off_t offset, int whence); -	bool	      (*filter)(struct ui_browser *self, void *entry); +	unsigned int  (*refresh)(struct ui_browser *browser); +	void	      (*write)(struct ui_browser *browser, void *entry, int row); +	void	      (*seek)(struct ui_browser *browser, off_t offset, int whence); +	bool	      (*filter)(struct ui_browser *browser, void *entry);  	u32	      nr_entries;  	bool	      navkeypressed;  	bool	      use_navkeypressed;  };  int  ui_browser__set_color(struct ui_browser *browser, int color); -void ui_browser__set_percent_color(struct ui_browser *self, +void ui_browser__set_percent_color(struct ui_browser *browser,  				   double percent, bool current); -bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row); -void ui_browser__refresh_dimensions(struct ui_browser *self); -void ui_browser__reset_index(struct ui_browser *self); +bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row); +void ui_browser__refresh_dimensions(struct ui_browser *browser); +void ui_browser__reset_index(struct ui_browser *browser); -void ui_browser__gotorc(struct ui_browser *self, int y, int x); +void ui_browser__gotorc(struct ui_browser *browser, int y, int x);  void ui_browser__write_graph(struct ui_browser *browser, int graph);  void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,  			      u64 start, u64 end);  void __ui_browser__show_title(struct ui_browser *browser, const char *title);  void ui_browser__show_title(struct ui_browser *browser, const char *title); -int ui_browser__show(struct ui_browser *self, const char *title, +int ui_browser__show(struct ui_browser *browser, const char *title,  		     const char *helpline, ...); -void ui_browser__hide(struct ui_browser *self); -int ui_browser__refresh(struct ui_browser *self); +void ui_browser__hide(struct ui_browser *browser); +int ui_browser__refresh(struct ui_browser *browser);  int ui_browser__run(struct ui_browser *browser, int delay_secs);  void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries);  void ui_browser__handle_resize(struct ui_browser *browser); @@ -59,15 +57,17 @@ int ui_browser__help_window(struct ui_browser *browser, const char *text);  bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text);  int ui_browser__input_window(const char *title, const char *text, char *input,  			     const char *exit_msg, int delay_sec); +struct perf_session_env; +int tui__header_window(struct perf_session_env *env);  void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence);  unsigned int ui_browser__argv_refresh(struct ui_browser *browser); -void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); -unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); +void ui_browser__rb_tree_seek(struct ui_browser *browser, off_t offset, int whence); +unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser); -void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence); -unsigned int ui_browser__list_head_refresh(struct ui_browser *self); +void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence); +unsigned int ui_browser__list_head_refresh(struct ui_browser *browser);  void ui_browser__init(void);  void annotate_browser__init(void); diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 08545ae4699..f0697a3aede 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -442,35 +442,37 @@ static bool annotate_browser__callq(struct annotate_browser *browser,  {  	struct map_symbol *ms = browser->b.priv;  	struct disasm_line *dl = browser->selection; -	struct symbol *sym = ms->sym;  	struct annotation *notes; -	struct symbol *target; -	u64 ip; +	struct addr_map_symbol target = { +		.map = ms->map, +		.addr = map__objdump_2mem(ms->map, dl->ops.target.addr), +	};  	char title[SYM_TITLE_MAX_SIZE];  	if (!ins__is_call(dl->ins))  		return false; -	ip = ms->map->map_ip(ms->map, dl->ops.target.addr); -	target = map__find_symbol(ms->map, ip, NULL); -	if (target == NULL) { +	if (map_groups__find_ams(&target, NULL) || +	    map__rip_2objdump(target.map, target.map->map_ip(target.map, +							     target.addr)) != +	    dl->ops.target.addr) {  		ui_helpline__puts("The called function was not found.");  		return true;  	} -	notes = symbol__annotation(target); +	notes = symbol__annotation(target.sym);  	pthread_mutex_lock(¬es->lock); -	if (notes->src == NULL && symbol__alloc_hist(target) < 0) { +	if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {  		pthread_mutex_unlock(¬es->lock);  		ui__warning("Not enough memory for annotating '%s' symbol!\n", -			    target->name); +			    target.sym->name);  		return true;  	}  	pthread_mutex_unlock(¬es->lock); -	symbol__tui_annotate(target, ms->map, evsel, hbt); -	sym_title(sym, ms->map, title, sizeof(title)); +	symbol__tui_annotate(target.sym, target.map, evsel, hbt); +	sym_title(ms->sym, ms->map, title, sizeof(title));  	ui_browser__show_title(&browser->b, title);  	return true;  } diff --git a/tools/perf/ui/browsers/header.c b/tools/perf/ui/browsers/header.c new file mode 100644 index 00000000000..89c16b98861 --- /dev/null +++ b/tools/perf/ui/browsers/header.c @@ -0,0 +1,127 @@ +#include "util/cache.h" +#include "util/debug.h" +#include "ui/browser.h" +#include "ui/ui.h" +#include "ui/util.h" +#include "ui/libslang.h" +#include "util/header.h" +#include "util/session.h" + +static void ui_browser__argv_write(struct ui_browser *browser, +				   void *entry, int row) +{ +	char **arg = entry; +	char *str = *arg; +	char empty[] = " "; +	bool current_entry = ui_browser__is_current_entry(browser, row); +	unsigned long offset = (unsigned long)browser->priv; + +	if (offset >= strlen(str)) +		str = empty; +	else +		str = str + offset; + +	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : +						       HE_COLORSET_NORMAL); + +	slsmg_write_nstring(str, browser->width); +} + +static int list_menu__run(struct ui_browser *menu) +{ +	int key; +	unsigned long offset; +	const char help[] = +	"h/?/F1        Show this window\n" +	"UP/DOWN/PGUP\n" +	"PGDN/SPACE\n" +	"LEFT/RIGHT    Navigate\n" +	"q/ESC/CTRL+C  Exit browser"; + +	if (ui_browser__show(menu, "Header information", "Press 'q' to exit") < 0) +		return -1; + +	while (1) { +		key = ui_browser__run(menu, 0); + +		switch (key) { +		case K_RIGHT: +			offset = (unsigned long)menu->priv; +			offset += 10; +			menu->priv = (void *)offset; +			continue; +		case K_LEFT: +			offset = (unsigned long)menu->priv; +			if (offset >= 10) +				offset -= 10; +			menu->priv = (void *)offset; +			continue; +		case K_F1: +		case 'h': +		case '?': +			ui_browser__help_window(menu, help); +			continue; +		case K_ESC: +		case 'q': +		case CTRL('c'): +			key = -1; +			break; +		default: +			continue; +		} + +		break; +	} + +	ui_browser__hide(menu); +	return key; +} + +static int ui__list_menu(int argc, char * const argv[]) +{ +	struct ui_browser menu = { +		.entries    = (void *)argv, +		.refresh    = ui_browser__argv_refresh, +		.seek	    = ui_browser__argv_seek, +		.write	    = ui_browser__argv_write, +		.nr_entries = argc, +	}; + +	return list_menu__run(&menu); +} + +int tui__header_window(struct perf_session_env *env) +{ +	int i, argc = 0; +	char **argv; +	struct perf_session *session; +	char *ptr, *pos; +	size_t size; +	FILE *fp = open_memstream(&ptr, &size); + +	session = container_of(env, struct perf_session, header.env); +	perf_header__fprintf_info(session, fp, true); +	fclose(fp); + +	for (pos = ptr, argc = 0; (pos = strchr(pos, '\n')) != NULL; pos++) +		argc++; + +	argv = calloc(argc + 1, sizeof(*argv)); +	if (argv == NULL) +		goto out; + +	argv[0] = pos = ptr; +	for (i = 1; (pos = strchr(pos, '\n')) != NULL; i++) { +		*pos++ = '\0'; +		argv[i] = pos; +	} + +	BUG_ON(i != argc + 1); + +	ui__list_menu(argc, argv); + +out: +	free(argv); +	free(ptr); +	return 0; +} diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 7ef36c36047..04a229aa5c0 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -17,6 +17,7 @@  #include "../util.h"  #include "../ui.h"  #include "map.h" +#include "annotate.h"  struct hist_browser {  	struct ui_browser   b; @@ -26,13 +27,35 @@ struct hist_browser {  	int		     print_seq;  	bool		     show_dso;  	float		     min_pcnt; -	u64		     nr_pcnt_entries; +	u64		     nr_non_filtered_entries; +	u64		     nr_callchain_rows;  };  extern void hist_browser__init_hpp(void);  static int hists__browser_title(struct hists *hists, char *bf, size_t size,  				const char *ev_name); +static void hist_browser__update_nr_entries(struct hist_browser *hb); + +static struct rb_node *hists__filter_entries(struct rb_node *nd, +					     float min_pcnt); + +static bool hist_browser__has_filter(struct hist_browser *hb) +{ +	return hists__has_filter(hb->hists) || hb->min_pcnt; +} + +static u32 hist_browser__nr_entries(struct hist_browser *hb) +{ +	u32 nr_entries; + +	if (hist_browser__has_filter(hb)) +		nr_entries = hb->nr_non_filtered_entries; +	else +		nr_entries = hb->hists->nr_entries; + +	return nr_entries + hb->nr_callchain_rows; +}  static void hist_browser__refresh_dimensions(struct hist_browser *browser)  { @@ -43,7 +66,14 @@ static void hist_browser__refresh_dimensions(struct hist_browser *browser)  static void hist_browser__reset(struct hist_browser *browser)  { -	browser->b.nr_entries = browser->hists->nr_entries; +	/* +	 * The hists__remove_entry_filter() already folds non-filtered +	 * entries so we can assume it has 0 callchain rows. +	 */ +	browser->nr_callchain_rows = 0; + +	hist_browser__update_nr_entries(browser); +	browser->b.nr_entries = hist_browser__nr_entries(browser);  	hist_browser__refresh_dimensions(browser);  	ui_browser__reset_index(&browser->b);  } @@ -198,14 +228,16 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser)  		struct hist_entry *he = browser->he_selection;  		hist_entry__init_have_children(he); -		browser->hists->nr_entries -= he->nr_rows; +		browser->b.nr_entries -= he->nr_rows; +		browser->nr_callchain_rows -= he->nr_rows;  		if (he->ms.unfolded)  			he->nr_rows = callchain__count_rows(&he->sorted_chain);  		else  			he->nr_rows = 0; -		browser->hists->nr_entries += he->nr_rows; -		browser->b.nr_entries = browser->hists->nr_entries; + +		browser->b.nr_entries += he->nr_rows; +		browser->nr_callchain_rows += he->nr_rows;  		return true;  	} @@ -280,23 +312,27 @@ static void hist_entry__set_folding(struct hist_entry *he, bool unfold)  		he->nr_rows = 0;  } -static void hists__set_folding(struct hists *hists, bool unfold) +static void +__hist_browser__set_folding(struct hist_browser *browser, bool unfold)  {  	struct rb_node *nd; +	struct hists *hists = browser->hists; -	hists->nr_entries = 0; - -	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { +	for (nd = rb_first(&hists->entries); +	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; +	     nd = rb_next(nd)) {  		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);  		hist_entry__set_folding(he, unfold); -		hists->nr_entries += 1 + he->nr_rows; +		browser->nr_callchain_rows += he->nr_rows;  	}  }  static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)  { -	hists__set_folding(browser->hists, unfold); -	browser->b.nr_entries = browser->hists->nr_entries; +	browser->nr_callchain_rows = 0; +	__hist_browser__set_folding(browser, unfold); + +	browser->b.nr_entries = hist_browser__nr_entries(browser);  	/* Go to the start, we may be way after valid entries after a collapse */  	ui_browser__reset_index(&browser->b);  } @@ -310,8 +346,6 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser)  		"Or reduce the sampling frequency.");  } -static void hist_browser__update_pcnt_entries(struct hist_browser *hb); -  static int hist_browser__run(struct hist_browser *browser, const char *ev_name,  			     struct hist_browser_timer *hbt)  { @@ -320,9 +354,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name,  	int delay_secs = hbt ? hbt->refresh : 0;  	browser->b.entries = &browser->hists->entries; -	browser->b.nr_entries = browser->hists->nr_entries; -	if (browser->min_pcnt) -		browser->b.nr_entries = browser->nr_pcnt_entries; +	browser->b.nr_entries = hist_browser__nr_entries(browser);  	hist_browser__refresh_dimensions(browser);  	hists__browser_title(browser->hists, title, sizeof(title), ev_name); @@ -339,13 +371,10 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name,  			u64 nr_entries;  			hbt->timer(hbt->arg); -			if (browser->min_pcnt) { -				hist_browser__update_pcnt_entries(browser); -				nr_entries = browser->nr_pcnt_entries; -			} else { -				nr_entries = browser->hists->nr_entries; -			} +			if (hist_browser__has_filter(browser)) +				hist_browser__update_nr_entries(browser); +			nr_entries = hist_browser__nr_entries(browser);  			ui_browser__update_nr_entries(&browser->b, nr_entries);  			if (browser->hists->stats.nr_lost_warned != @@ -587,99 +616,27 @@ struct hpp_arg {  	bool current_entry;  }; -static int __hpp__color_callchain(struct hpp_arg *arg) +static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)  { -	if (!symbol_conf.use_callchain) -		return 0; - -	slsmg_printf("%c ", arg->folded_sign); -	return 2; -} - -static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he, -			    u64 (*get_field)(struct hist_entry *), -			    int (*callchain_cb)(struct hpp_arg *)) -{ -	int ret = 0; -	double percent = 0.0; -	struct hists *hists = he->hists;  	struct hpp_arg *arg = hpp->ptr; +	int ret; +	va_list args; +	double percent; -	if (hists->stats.total_period) -		percent = 100.0 * get_field(he) / hists->stats.total_period; +	va_start(args, fmt); +	percent = va_arg(args, double); +	va_end(args);  	ui_browser__set_percent_color(arg->b, percent, arg->current_entry); -	if (callchain_cb) -		ret += callchain_cb(arg); - -	ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); +	ret = scnprintf(hpp->buf, hpp->size, fmt, percent);  	slsmg_printf("%s", hpp->buf); -	if (symbol_conf.event_group) { -		int prev_idx, idx_delta; -		struct perf_evsel *evsel = hists_to_evsel(hists); -		struct hist_entry *pair; -		int nr_members = evsel->nr_members; - -		if (nr_members <= 1) -			goto out; - -		prev_idx = perf_evsel__group_idx(evsel); - -		list_for_each_entry(pair, &he->pairs.head, pairs.node) { -			u64 period = get_field(pair); -			u64 total = pair->hists->stats.total_period; - -			if (!total) -				continue; - -			evsel = hists_to_evsel(pair->hists); -			idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; - -			while (idx_delta--) { -				/* -				 * zero-fill group members in the middle which -				 * have no sample -				 */ -				ui_browser__set_percent_color(arg->b, 0.0, -							arg->current_entry); -				ret += scnprintf(hpp->buf, hpp->size, -						 " %6.2f%%", 0.0); -				slsmg_printf("%s", hpp->buf); -			} - -			percent = 100.0 * period / total; -			ui_browser__set_percent_color(arg->b, percent, -						      arg->current_entry); -			ret += scnprintf(hpp->buf, hpp->size, -					 " %6.2f%%", percent); -			slsmg_printf("%s", hpp->buf); - -			prev_idx = perf_evsel__group_idx(evsel); -		} - -		idx_delta = nr_members - prev_idx - 1; - -		while (idx_delta--) { -			/* -			 * zero-fill group members at last which have no sample -			 */ -			ui_browser__set_percent_color(arg->b, 0.0, -						      arg->current_entry); -			ret += scnprintf(hpp->buf, hpp->size, -					 " %6.2f%%", 0.0); -			slsmg_printf("%s", hpp->buf); -		} -	} -out: -	if (!arg->current_entry || !arg->b->navkeypressed) -		ui_browser__set_color(arg->b, HE_COLORSET_NORMAL); - +	advance_hpp(hpp, ret);  	return ret;  } -#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb)			\ +#define __HPP_COLOR_PERCENT_FN(_type, _field)				\  static u64 __hpp_get_##_field(struct hist_entry *he)			\  {									\  	return he->stat._field;						\ @@ -690,21 +647,43 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\  				struct perf_hpp *hpp,			\  				struct hist_entry *he)			\  {									\ -	return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb);	\ +	return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%",	\ +			  __hpp__slsmg_color_printf, true);		\ +} + +#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\ +static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\ +{									\ +	return he->stat_acc->_field;					\ +}									\ +									\ +static int								\ +hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\ +				struct perf_hpp *hpp,			\ +				struct hist_entry *he)			\ +{									\ +	if (!symbol_conf.cumulate_callchain) {				\ +		int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A");	\ +		slsmg_printf("%s", hpp->buf);				\ +									\ +		return ret;						\ +	}								\ +	return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%",	\ +			  __hpp__slsmg_color_printf, true);		\  } -__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain) -__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL) -__HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL) -__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL) -__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL) +__HPP_COLOR_PERCENT_FN(overhead, period) +__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) +__HPP_COLOR_PERCENT_FN(overhead_us, period_us) +__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) +__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) +__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)  #undef __HPP_COLOR_PERCENT_FN +#undef __HPP_COLOR_ACC_PERCENT_FN  void hist_browser__init_hpp(void)  { -	perf_hpp__init(); -  	perf_hpp__format[PERF_HPP__OVERHEAD].color =  				hist_browser__hpp_color_overhead;  	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = @@ -715,6 +694,8 @@ void hist_browser__init_hpp(void)  				hist_browser__hpp_color_overhead_guest_sys;  	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =  				hist_browser__hpp_color_overhead_guest_us; +	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = +				hist_browser__hpp_color_overhead_acc;  }  static int hist_browser__show_entry(struct hist_browser *browser, @@ -742,7 +723,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,  	if (row_offset == 0) {  		struct hpp_arg arg = { -			.b 		= &browser->b, +			.b		= &browser->b,  			.folded_sign	= folded_sign,  			.current_entry	= current_entry,  		}; @@ -755,11 +736,27 @@ static int hist_browser__show_entry(struct hist_browser *browser,  		ui_browser__gotorc(&browser->b, row, 0);  		perf_hpp__for_each_format(fmt) { -			if (!first) { +			if (perf_hpp__should_skip(fmt)) +				continue; + +			if (current_entry && browser->b.navkeypressed) { +				ui_browser__set_color(&browser->b, +						      HE_COLORSET_SELECTED); +			} else { +				ui_browser__set_color(&browser->b, +						      HE_COLORSET_NORMAL); +			} + +			if (first) { +				if (symbol_conf.use_callchain) { +					slsmg_printf("%c ", folded_sign); +					width -= 2; +				} +				first = false; +			} else {  				slsmg_printf("  ");  				width -= 2;  			} -			first = false;  			if (fmt->color) {  				width -= fmt->color(fmt, &hpp, entry); @@ -773,8 +770,8 @@ static int hist_browser__show_entry(struct hist_browser *browser,  		if (!browser->b.navkeypressed)  			width += 1; -		hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists); -		slsmg_write_nstring(s, width); +		slsmg_write_nstring("", width); +  		++row;  		++printed;  	} else @@ -811,12 +808,12 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)  	for (nd = browser->top; nd; nd = rb_next(nd)) {  		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); -		float percent = h->stat.period * 100.0 / -					hb->hists->stats.total_period; +		float percent;  		if (h->filtered)  			continue; +		percent = hist_entry__get_percent_limit(h);  		if (percent < hb->min_pcnt)  			continue; @@ -829,18 +826,13 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)  }  static struct rb_node *hists__filter_entries(struct rb_node *nd, -					     struct hists *hists,  					     float min_pcnt)  {  	while (nd != NULL) {  		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); -		float percent = h->stat.period * 100.0 / -					hists->stats.total_period; - -		if (percent < min_pcnt) -			return NULL; +		float percent = hist_entry__get_percent_limit(h); -		if (!h->filtered) +		if (!h->filtered && percent >= min_pcnt)  			return nd;  		nd = rb_next(nd); @@ -850,13 +842,11 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd,  }  static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, -						  struct hists *hists,  						  float min_pcnt)  {  	while (nd != NULL) {  		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); -		float percent = h->stat.period * 100.0 / -					hists->stats.total_period; +		float percent = hist_entry__get_percent_limit(h);  		if (!h->filtered && percent >= min_pcnt)  			return nd; @@ -885,14 +875,14 @@ static void ui_browser__hists_seek(struct ui_browser *browser,  	switch (whence) {  	case SEEK_SET:  		nd = hists__filter_entries(rb_first(browser->entries), -					   hb->hists, hb->min_pcnt); +					   hb->min_pcnt);  		break;  	case SEEK_CUR:  		nd = browser->top;  		goto do_offset;  	case SEEK_END:  		nd = hists__filter_prev_entries(rb_last(browser->entries), -						hb->hists, hb->min_pcnt); +						hb->min_pcnt);  		first = false;  		break;  	default: @@ -935,8 +925,7 @@ do_offset:  					break;  				}  			} -			nd = hists__filter_entries(rb_next(nd), hb->hists, -						   hb->min_pcnt); +			nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);  			if (nd == NULL)  				break;  			--offset; @@ -969,7 +958,7 @@ do_offset:  				}  			} -			nd = hists__filter_prev_entries(rb_prev(nd), hb->hists, +			nd = hists__filter_prev_entries(rb_prev(nd),  							hb->min_pcnt);  			if (nd == NULL)  				break; @@ -1108,27 +1097,35 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,  				       struct hist_entry *he, FILE *fp)  {  	char s[8192]; -	double percent;  	int printed = 0;  	char folded_sign = ' '; +	struct perf_hpp hpp = { +		.buf = s, +		.size = sizeof(s), +	}; +	struct perf_hpp_fmt *fmt; +	bool first = true; +	int ret;  	if (symbol_conf.use_callchain)  		folded_sign = hist_entry__folded(he); -	hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists); -	percent = (he->stat.period * 100.0) / browser->hists->stats.total_period; -  	if (symbol_conf.use_callchain)  		printed += fprintf(fp, "%c ", folded_sign); -	printed += fprintf(fp, " %5.2f%%", percent); - -	if (symbol_conf.show_nr_samples) -		printed += fprintf(fp, " %11u", he->stat.nr_events); +	perf_hpp__for_each_format(fmt) { +		if (perf_hpp__should_skip(fmt)) +			continue; -	if (symbol_conf.show_total_period) -		printed += fprintf(fp, " %12" PRIu64, he->stat.period); +		if (!first) { +			ret = scnprintf(hpp.buf, hpp.size, "  "); +			advance_hpp(&hpp, ret); +		} else +			first = false; +		ret = fmt->entry(fmt, &hpp, he); +		advance_hpp(&hpp, ret); +	}  	printed += fprintf(fp, "%s\n", rtrim(s));  	if (folded_sign == '-') @@ -1140,7 +1137,6 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,  static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)  {  	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), -						   browser->hists,  						   browser->min_pcnt);  	int printed = 0; @@ -1148,8 +1144,7 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)  		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);  		printed += hist_browser__fprintf_entry(browser, h, fp); -		nd = hists__filter_entries(rb_next(nd), browser->hists, -					   browser->min_pcnt); +		nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);  	}  	return printed; @@ -1231,6 +1226,11 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,  	char buf[512];  	size_t buflen = sizeof(buf); +	if (symbol_conf.filter_relative) { +		nr_samples = hists->stats.nr_non_filtered_samples; +		nr_events = hists->stats.total_non_filtered_period; +	} +  	if (perf_evsel__is_group_event(evsel)) {  		struct perf_evsel *pos; @@ -1238,8 +1238,13 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,  		ev_name = buf;  		for_each_group_member(pos, evsel) { -			nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; -			nr_events += pos->hists.stats.total_period; +			if (symbol_conf.filter_relative) { +				nr_samples += pos->hists.stats.nr_non_filtered_samples; +				nr_events += pos->hists.stats.total_non_filtered_period; +			} else { +				nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; +				nr_events += pos->hists.stats.total_period; +			}  		}  	} @@ -1255,7 +1260,7 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,  	if (thread)  		printed += scnprintf(bf + printed, size - printed,  				    ", Thread: %s(%d)", -				    (thread->comm_set ? thread->comm : ""), +				     (thread->comm_set ? thread__comm_str(thread) : ""),  				    thread->tid);  	if (dso)  		printed += scnprintf(bf + printed, size - printed, @@ -1267,10 +1272,8 @@ static inline void free_popup_options(char **options, int n)  {  	int i; -	for (i = 0; i < n; ++i) { -		free(options[i]); -		options[i] = NULL; -	} +	for (i = 0; i < n; ++i) +		zfree(&options[i]);  }  /* Check whether the browser is for 'top' or 'report' */ @@ -1329,7 +1332,7 @@ static int switch_data_file(void)  			abs_path[nr_options] = strdup(path);  			if (!abs_path[nr_options]) { -				free(options[nr_options]); +				zfree(&options[nr_options]);  				ui__warning("Can't search all data files due to memory shortage.\n");  				fclose(file);  				break; @@ -1368,18 +1371,22 @@ close_file_and_continue:  	return ret;  } -static void hist_browser__update_pcnt_entries(struct hist_browser *hb) +static void hist_browser__update_nr_entries(struct hist_browser *hb)  {  	u64 nr_entries = 0;  	struct rb_node *nd = rb_first(&hb->hists->entries); -	while (nd) { +	if (hb->min_pcnt == 0) { +		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; +		return; +	} + +	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {  		nr_entries++; -		nd = hists__filter_entries(rb_next(nd), hb->hists, -					   hb->min_pcnt); +		nd = rb_next(nd);  	} -	hb->nr_pcnt_entries = nr_entries; +	hb->nr_non_filtered_entries = nr_entries;  }  static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, @@ -1400,12 +1407,43 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  	char script_opt[64];  	int delay_secs = hbt ? hbt->refresh : 0; +#define HIST_BROWSER_HELP_COMMON					\ +	"h/?/F1        Show this window\n"				\ +	"UP/DOWN/PGUP\n"						\ +	"PGDN/SPACE    Navigate\n"					\ +	"q/ESC/CTRL+C  Exit browser\n\n"				\ +	"For multiple event sessions:\n\n"				\ +	"TAB/UNTAB     Switch events\n\n"				\ +	"For symbolic views (--sort has sym):\n\n"			\ +	"->            Zoom into DSO/Threads & Annotate current symbol\n" \ +	"<-            Zoom out\n"					\ +	"a             Annotate current symbol\n"			\ +	"C             Collapse all callchains\n"			\ +	"d             Zoom into current DSO\n"				\ +	"E             Expand all callchains\n"				\ +	"F             Toggle percentage of filtered entries\n"		\ + +	/* help messages are sorted by lexical order of the hotkey */ +	const char report_help[] = HIST_BROWSER_HELP_COMMON +	"i             Show header information\n" +	"P             Print histograms to perf.hist.N\n" +	"r             Run available scripts\n" +	"s             Switch to another data file in PWD\n" +	"t             Zoom into current Thread\n" +	"V             Verbose (DSO names in callchains, etc)\n" +	"/             Filter symbol by name"; +	const char top_help[] = HIST_BROWSER_HELP_COMMON +	"P             Print histograms to perf.hist.N\n" +	"t             Zoom into current Thread\n" +	"V             Verbose (DSO names in callchains, etc)\n" +	"/             Filter symbol by name"; +  	if (browser == NULL)  		return -1;  	if (min_pcnt) {  		browser->min_pcnt = min_pcnt; -		hist_browser__update_pcnt_entries(browser); +		hist_browser__update_nr_entries(browser);  	}  	fstack = pstack__new(2); @@ -1484,29 +1522,19 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  			if (is_report_browser(hbt))  				goto do_data_switch;  			continue; +		case 'i': +			/* env->arch is NULL for live-mode (i.e. perf top) */ +			if (env->arch) +				tui__header_window(env); +			continue; +		case 'F': +			symbol_conf.filter_relative ^= 1; +			continue;  		case K_F1:  		case 'h':  		case '?':  			ui_browser__help_window(&browser->b, -					"h/?/F1        Show this window\n" -					"UP/DOWN/PGUP\n" -					"PGDN/SPACE    Navigate\n" -					"q/ESC/CTRL+C  Exit browser\n\n" -					"For multiple event sessions:\n\n" -					"TAB/UNTAB Switch events\n\n" -					"For symbolic views (--sort has sym):\n\n" -					"->            Zoom into DSO/Threads & Annotate current symbol\n" -					"<-            Zoom out\n" -					"a             Annotate current symbol\n" -					"C             Collapse all callchains\n" -					"E             Expand all callchains\n" -					"d             Zoom into current DSO\n" -					"t             Zoom into current Thread\n" -					"r             Run available scripts('perf report' only)\n" -					"s             Switch to another data file in PWD ('perf report' only)\n" -					"P             Print histograms to perf.hist.N\n" -					"V             Verbose (DSO names in callchains, etc)\n" -					"/             Filter symbol by name"); +				is_report_browser(hbt) ? report_help : top_help);  			continue;  		case K_ENTER:  		case K_RIGHT: @@ -1566,19 +1594,24 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  					 bi->to.sym->name) > 0)  				annotate_t = nr_options++;  		} else { -  			if (browser->selection != NULL &&  			    browser->selection->sym != NULL && -			    !browser->selection->map->dso->annotate_warned && -				asprintf(&options[nr_options], "Annotate %s", -					 browser->selection->sym->name) > 0) -				annotate = nr_options++; +			    !browser->selection->map->dso->annotate_warned) { +				struct annotation *notes; + +				notes = symbol__annotation(browser->selection->sym); + +				if (notes->src && +				    asprintf(&options[nr_options], "Annotate %s", +						 browser->selection->sym->name) > 0) +					annotate = nr_options++; +			}  		}  		if (thread != NULL &&  		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",  			     (browser->hists->thread_filter ? "out of" : "into"), -			     (thread->comm_set ? thread->comm : ""), +			     (thread->comm_set ? thread__comm_str(thread) : ""),  			     thread->tid) > 0)  			zoom_thread = nr_options++; @@ -1598,7 +1631,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  			struct symbol *sym;  			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]", -				browser->he_selection->thread->comm) > 0) +				     thread__comm_str(browser->he_selection->thread)) > 0)  				scripts_comm = nr_options++;  			sym = browser->he_selection->ms.sym; @@ -1629,6 +1662,7 @@ retry_popup_menu:  		if (choice == annotate || choice == annotate_t || choice == annotate_f) {  			struct hist_entry *he; +			struct annotation *notes;  			int err;  do_annotate:  			if (!objdump_path && perf_session_env__lookup_objdump(env)) @@ -1652,6 +1686,10 @@ do_annotate:  				he->ms.map = he->branch_info->to.map;  			} +			notes = symbol__annotation(he->ms.sym); +			if (!notes->src) +				continue; +  			/*  			 * Don't let this be freed, say, by hists__decay_entry.  			 */ @@ -1679,14 +1717,14 @@ zoom_dso:  zoom_out_dso:  				ui_helpline__pop();  				browser->hists->dso_filter = NULL; -				sort_dso.elide = false; +				perf_hpp__set_elide(HISTC_DSO, false);  			} else {  				if (dso == NULL)  					continue;  				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",  						   dso->kernel ? "the Kernel" : dso->short_name);  				browser->hists->dso_filter = dso; -				sort_dso.elide = true; +				perf_hpp__set_elide(HISTC_DSO, true);  				pstack__push(fstack, &browser->hists->dso_filter);  			}  			hists__filter_by_dso(hists); @@ -1698,13 +1736,13 @@ zoom_thread:  zoom_out_thread:  				ui_helpline__pop();  				browser->hists->thread_filter = NULL; -				sort_thread.elide = false; +				perf_hpp__set_elide(HISTC_THREAD, false);  			} else {  				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", -						   thread->comm_set ? thread->comm : "", +						   thread->comm_set ? thread__comm_str(thread) : "",  						   thread->tid);  				browser->hists->thread_filter = thread; -				sort_thread.elide = true; +				perf_hpp__set_elide(HISTC_THREAD, false);  				pstack__push(fstack, &browser->hists->thread_filter);  			}  			hists__filter_by_thread(hists); @@ -1717,7 +1755,7 @@ do_scripts:  			memset(script_opt, 0, 64);  			if (choice == scripts_comm) -				sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm); +				sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));  			if (choice == scripts_symbol)  				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name); @@ -1847,15 +1885,15 @@ browse_hists:  			switch (key) {  			case K_TAB:  				if (pos->node.next == &evlist->entries) -					pos = list_entry(evlist->entries.next, struct perf_evsel, node); +					pos = perf_evlist__first(evlist);  				else -					pos = list_entry(pos->node.next, struct perf_evsel, node); +					pos = perf_evsel__next(pos);  				goto browse_hists;  			case K_UNTAB:  				if (pos->node.prev == &evlist->entries) -					pos = list_entry(evlist->entries.prev, struct perf_evsel, node); +					pos = perf_evlist__last(evlist);  				else -					pos = list_entry(pos->node.prev, struct perf_evsel, node); +					pos = perf_evsel__prev(pos);  				goto browse_hists;  			case K_ESC:  				if (!ui_browser__dialog_yesno(&menu->b, @@ -1889,7 +1927,7 @@ out:  	return key;  } -static bool filter_group_entries(struct ui_browser *self __maybe_unused, +static bool filter_group_entries(struct ui_browser *browser __maybe_unused,  				 void *entry)  {  	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); @@ -1923,7 +1961,7 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,  	ui_helpline__push("Press ESC to exit"); -	list_for_each_entry(pos, &evlist->entries, node) { +	evlist__for_each(evlist, pos) {  		const char *ev_name = perf_evsel__name(pos);  		size_t line_len = strlen(ev_name) + 7; @@ -1943,8 +1981,7 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,  single_entry:  	if (nr_entries == 1) { -		struct perf_evsel *first = list_entry(evlist->entries.next, -						      struct perf_evsel, node); +		struct perf_evsel *first = perf_evlist__first(evlist);  		const char *ev_name = perf_evsel__name(first);  		return perf_evsel__hists_browse(first, nr_entries, help, @@ -1956,9 +1993,10 @@ single_entry:  		struct perf_evsel *pos;  		nr_entries = 0; -		list_for_each_entry(pos, &evlist->entries, node) +		evlist__for_each(evlist, pos) {  			if (perf_evsel__is_group_leader(pos))  				nr_entries++; +		}  		if (nr_entries == 1)  			goto single_entry; diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c index 95c7cfb8f2c..b11639f3368 100644 --- a/tools/perf/ui/browsers/map.c +++ b/tools/perf/ui/browsers/map.c @@ -18,30 +18,30 @@ struct map_browser {  	u8		  addrlen;  }; -static void map_browser__write(struct ui_browser *self, void *nd, int row) +static void map_browser__write(struct ui_browser *browser, void *nd, int row)  {  	struct symbol *sym = rb_entry(nd, struct symbol, rb_node); -	struct map_browser *mb = container_of(self, struct map_browser, b); -	bool current_entry = ui_browser__is_current_entry(self, row); +	struct map_browser *mb = container_of(browser, struct map_browser, b); +	bool current_entry = ui_browser__is_current_entry(browser, row);  	int width; -	ui_browser__set_percent_color(self, 0, current_entry); +	ui_browser__set_percent_color(browser, 0, current_entry);  	slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ",  		     mb->addrlen, sym->start, mb->addrlen, sym->end,  		     sym->binding == STB_GLOBAL ? 'g' :  		     sym->binding == STB_LOCAL  ? 'l' : 'w'); -	width = self->width - ((mb->addrlen * 2) + 4); +	width = browser->width - ((mb->addrlen * 2) + 4);  	if (width > 0)  		slsmg_write_nstring(sym->name, width);  }  /* FIXME uber-kludgy, see comment on cmd_report... */ -static u32 *symbol__browser_index(struct symbol *self) +static u32 *symbol__browser_index(struct symbol *browser)  { -	return ((void *)self) - sizeof(struct rb_node) - sizeof(u32); +	return ((void *)browser) - sizeof(struct rb_node) - sizeof(u32);  } -static int map_browser__search(struct map_browser *self) +static int map_browser__search(struct map_browser *browser)  {  	char target[512];  	struct symbol *sym; @@ -53,37 +53,37 @@ static int map_browser__search(struct map_browser *self)  	if (target[0] == '0' && tolower(target[1]) == 'x') {  		u64 addr = strtoull(target, NULL, 16); -		sym = map__find_symbol(self->map, addr, NULL); +		sym = map__find_symbol(browser->map, addr, NULL);  	} else -		sym = map__find_symbol_by_name(self->map, target, NULL); +		sym = map__find_symbol_by_name(browser->map, target, NULL);  	if (sym != NULL) {  		u32 *idx = symbol__browser_index(sym); -		self->b.top = &sym->rb_node; -		self->b.index = self->b.top_idx = *idx; +		browser->b.top = &sym->rb_node; +		browser->b.index = browser->b.top_idx = *idx;  	} else  		ui_helpline__fpush("%s not found!", target);  	return 0;  } -static int map_browser__run(struct map_browser *self) +static int map_browser__run(struct map_browser *browser)  {  	int key; -	if (ui_browser__show(&self->b, self->map->dso->long_name, +	if (ui_browser__show(&browser->b, browser->map->dso->long_name,  			     "Press <- or ESC to exit, %s / to search",  			     verbose ? "" : "restart with -v to use") < 0)  		return -1;  	while (1) { -		key = ui_browser__run(&self->b, 0); +		key = ui_browser__run(&browser->b, 0);  		switch (key) {  		case '/':  			if (verbose) -				map_browser__search(self); +				map_browser__search(browser);  		default:  			break;                  case K_LEFT: @@ -94,20 +94,20 @@ static int map_browser__run(struct map_browser *self)  		}  	}  out: -	ui_browser__hide(&self->b); +	ui_browser__hide(&browser->b);  	return key;  } -int map__browse(struct map *self) +int map__browse(struct map *map)  {  	struct map_browser mb = {  		.b = { -			.entries = &self->dso->symbols[self->type], +			.entries = &map->dso->symbols[map->type],  			.refresh = ui_browser__rb_tree_refresh,  			.seek	 = ui_browser__rb_tree_seek,  			.write	 = map_browser__write,  		}, -		.map = self, +		.map = map,  	};  	struct rb_node *nd;  	char tmp[BITS_PER_LONG / 4]; diff --git a/tools/perf/ui/browsers/map.h b/tools/perf/ui/browsers/map.h index df8581a43e1..2d58e4b3eb6 100644 --- a/tools/perf/ui/browsers/map.h +++ b/tools/perf/ui/browsers/map.h @@ -2,5 +2,5 @@  #define _PERF_UI_MAP_BROWSER_H_ 1  struct map; -int map__browse(struct map *self); +int map__browse(struct map *map);  #endif /* _PERF_UI_MAP_BROWSER_H_ */ diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c index 12f009e61e9..402d2bd30b0 100644 --- a/tools/perf/ui/browsers/scripts.c +++ b/tools/perf/ui/browsers/scripts.c @@ -84,22 +84,22 @@ static void script_browser__write(struct ui_browser *browser,  	slsmg_write_nstring(sline->line, browser->width);  } -static int script_browser__run(struct perf_script_browser *self) +static int script_browser__run(struct perf_script_browser *browser)  {  	int key; -	if (ui_browser__show(&self->b, self->script_name, +	if (ui_browser__show(&browser->b, browser->script_name,  			     "Press <- or ESC to exit") < 0)  		return -1;  	while (1) { -		key = ui_browser__run(&self->b, 0); +		key = ui_browser__run(&browser->b, 0);  		/* We can add some special key handling here if needed */  		break;  	} -	ui_browser__hide(&self->b); +	ui_browser__hide(&browser->b);  	return key;  } @@ -173,8 +173,7 @@ int script_browse(const char *script_opt)  	if (script.b.width > AVERAGE_LINE_LEN)  		script.b.width = AVERAGE_LINE_LEN; -	if (line) -		free(line); +	free(line);  	pclose(fp);  	script.nr_lines = nr_entries; diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c index f538794615d..9c7ff8d31b2 100644 --- a/tools/perf/ui/gtk/annotate.c +++ b/tools/perf/ui/gtk/annotate.c @@ -154,9 +154,9 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,  	return 0;  } -int symbol__gtk_annotate(struct symbol *sym, struct map *map, -			 struct perf_evsel *evsel, -			 struct hist_browser_timer *hbt) +static int symbol__gtk_annotate(struct symbol *sym, struct map *map, +				struct perf_evsel *evsel, +				struct hist_browser_timer *hbt)  {  	GtkWidget *window;  	GtkWidget *notebook; @@ -226,6 +226,13 @@ int symbol__gtk_annotate(struct symbol *sym, struct map *map,  	return 0;  } +int hist_entry__gtk_annotate(struct hist_entry *he, +			     struct perf_evsel *evsel, +			     struct hist_browser_timer *hbt) +{ +	return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt); +} +  void perf_gtk__show_annotations(void)  {  	GtkWidget *window; diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c index c95012cdb43..c24d9122129 100644 --- a/tools/perf/ui/gtk/browser.c +++ b/tools/perf/ui/gtk/browser.c @@ -43,7 +43,7 @@ const char *perf_gtk__get_percent_color(double percent)  	return NULL;  } -#ifdef HAVE_GTK_INFO_BAR +#ifdef HAVE_GTK_INFO_BAR_SUPPORT  GtkWidget *perf_gtk__setup_info_bar(void)  {  	GtkWidget *info_bar; diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h index 3d96785ef15..0a9173ff9a6 100644 --- a/tools/perf/ui/gtk/gtk.h +++ b/tools/perf/ui/gtk/gtk.h @@ -12,7 +12,7 @@ struct perf_gtk_context {  	GtkWidget *main_window;  	GtkWidget *notebook; -#ifdef HAVE_GTK_INFO_BAR +#ifdef HAVE_GTK_INFO_BAR_SUPPORT  	GtkWidget *info_bar;  	GtkWidget *message_label;  #endif @@ -20,6 +20,9 @@ struct perf_gtk_context {  	guint statbar_ctx_id;  }; +int perf_gtk__init(void); +void perf_gtk__exit(bool wait_for_ok); +  extern struct perf_gtk_context *pgctx;  static inline bool perf_gtk__is_active_context(struct perf_gtk_context *ctx) @@ -31,7 +34,7 @@ struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window);  int perf_gtk__deactivate_context(struct perf_gtk_context **ctx);  void perf_gtk__init_helpline(void); -void perf_gtk__init_progress(void); +void gtk_ui_progress__init(void);  void perf_gtk__init_hpp(void);  void perf_gtk__signal(int sig); @@ -39,7 +42,7 @@ void perf_gtk__resize_window(GtkWidget *window);  const char *perf_gtk__get_percent_color(double percent);  GtkWidget *perf_gtk__setup_statusbar(void); -#ifdef HAVE_GTK_INFO_BAR +#ifdef HAVE_GTK_INFO_BAR_SUPPORT  GtkWidget *perf_gtk__setup_info_bar(void);  #else  static inline GtkWidget *perf_gtk__setup_info_bar(void) @@ -48,4 +51,17 @@ static inline GtkWidget *perf_gtk__setup_info_bar(void)  }  #endif +struct perf_evsel; +struct perf_evlist; +struct hist_entry; +struct hist_browser_timer; + +int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, +				  struct hist_browser_timer *hbt, +				  float min_pcnt); +int hist_entry__gtk_annotate(struct hist_entry *he, +			     struct perf_evsel *evsel, +			     struct hist_browser_timer *hbt); +void perf_gtk__show_annotations(void); +  #endif /* _PERF_GTK_H_ */ diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 2ca66cc1160..6ca60e482cd 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -8,16 +8,24 @@  #define MAX_COLUMNS			32 -static int __percent_color_snprintf(char *buf, size_t size, double percent) +static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...)  {  	int ret = 0; +	va_list args; +	double percent;  	const char *markup; +	char *buf = hpp->buf; +	size_t size = hpp->size; + +	va_start(args, fmt); +	percent = va_arg(args, double); +	va_end(args);  	markup = perf_gtk__get_percent_color(percent);  	if (markup)  		ret += scnprintf(buf, size, markup); -	ret += scnprintf(buf + ret, size - ret, " %6.2f%%", percent); +	ret += scnprintf(buf + ret, size - ret, fmt, percent);  	if (markup)  		ret += scnprintf(buf + ret, size - ret, "</span>"); @@ -25,66 +33,6 @@ static int __percent_color_snprintf(char *buf, size_t size, double percent)  	return ret;  } - -static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he, -			    u64 (*get_field)(struct hist_entry *)) -{ -	int ret; -	double percent = 0.0; -	struct hists *hists = he->hists; -	struct perf_evsel *evsel = hists_to_evsel(hists); - -	if (hists->stats.total_period) -		percent = 100.0 * get_field(he) / hists->stats.total_period; - -	ret = __percent_color_snprintf(hpp->buf, hpp->size, percent); - -	if (perf_evsel__is_group_event(evsel)) { -		int prev_idx, idx_delta; -		struct hist_entry *pair; -		int nr_members = evsel->nr_members; - -		prev_idx = perf_evsel__group_idx(evsel); - -		list_for_each_entry(pair, &he->pairs.head, pairs.node) { -			u64 period = get_field(pair); -			u64 total = pair->hists->stats.total_period; - -			evsel = hists_to_evsel(pair->hists); -			idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; - -			while (idx_delta--) { -				/* -				 * zero-fill group members in the middle which -				 * have no sample -				 */ -				ret += __percent_color_snprintf(hpp->buf + ret, -								hpp->size - ret, -								0.0); -			} - -			percent = 100.0 * period / total; -			ret += __percent_color_snprintf(hpp->buf + ret, -							hpp->size - ret, -							percent); - -			prev_idx = perf_evsel__group_idx(evsel); -		} - -		idx_delta = nr_members - prev_idx - 1; - -		while (idx_delta--) { -			/* -			 * zero-fill group members at last which have no sample -			 */ -			ret += __percent_color_snprintf(hpp->buf + ret, -							hpp->size - ret, -							0.0); -		} -	} -	return ret; -} -  #define __HPP_COLOR_PERCENT_FN(_type, _field)					\  static u64 he_get_##_field(struct hist_entry *he)				\  {										\ @@ -95,7 +43,22 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,  				       struct perf_hpp *hpp,			\  				       struct hist_entry *he)			\  {										\ -	return __hpp__color_fmt(hpp, he, he_get_##_field);			\ +	return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%",			\ +			  __percent_color_snprintf, true);			\ +} + +#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)				\ +static u64 he_get_acc_##_field(struct hist_entry *he)				\ +{										\ +	return he->stat_acc->_field;						\ +}										\ +										\ +static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,	\ +				       struct perf_hpp *hpp,			\ +				       struct hist_entry *he)			\ +{										\ +	return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%",		\ +			      __percent_color_snprintf, true);			\  }  __HPP_COLOR_PERCENT_FN(overhead, period) @@ -103,14 +66,13 @@ __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)  __HPP_COLOR_PERCENT_FN(overhead_us, period_us)  __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)  __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) +__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)  #undef __HPP_COLOR_PERCENT_FN  void perf_gtk__init_hpp(void)  { -	perf_hpp__init(); -  	perf_hpp__format[PERF_HPP__OVERHEAD].color =  				perf_gtk__hpp_color_overhead;  	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = @@ -121,6 +83,8 @@ void perf_gtk__init_hpp(void)  				perf_gtk__hpp_color_overhead_guest_sys;  	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =  				perf_gtk__hpp_color_overhead_guest_us; +	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = +				perf_gtk__hpp_color_overhead_acc;  }  static void callchain_list__sym_name(struct callchain_list *cl, @@ -204,7 +168,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,  	struct perf_hpp_fmt *fmt;  	GType col_types[MAX_COLUMNS];  	GtkCellRenderer *renderer; -	struct sort_entry *se;  	GtkTreeStore *store;  	struct rb_node *nd;  	GtkWidget *view; @@ -216,7 +179,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,  	struct perf_hpp hpp = {  		.buf		= s,  		.size		= sizeof(s), -		.ptr		= hists_to_evsel(hists),  	};  	nr_cols = 0; @@ -224,16 +186,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,  	perf_hpp__for_each_format(fmt)  		col_types[nr_cols++] = G_TYPE_STRING; -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		if (se->elide) -			continue; - -		if (se == &sort_sym) -			sym_col = nr_cols; - -		col_types[nr_cols++] = G_TYPE_STRING; -	} -  	store = gtk_tree_store_newv(nr_cols, col_types);  	view = gtk_tree_view_new(); @@ -243,21 +195,21 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,  	col_idx = 0;  	perf_hpp__for_each_format(fmt) { -		fmt->header(fmt, &hpp); +		if (perf_hpp__should_skip(fmt)) +			continue; -		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -							    -1, ltrim(s), -							    renderer, "markup", -							    col_idx++, NULL); -	} +		/* +		 * XXX no way to determine where symcol column is.. +		 *     Just use last column for now. +		 */ +		if (perf_hpp__is_sort_entry(fmt)) +			sym_col = col_idx; -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		if (se->elide) -			continue; +		fmt->header(fmt, &hpp, hists_to_evsel(hists));  		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -							    -1, se->se_header, -							    renderer, "text", +							    -1, ltrim(s), +							    renderer, "markup",  							    col_idx++, NULL);  	} @@ -280,12 +232,13 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,  	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {  		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);  		GtkTreeIter iter; -		float percent = h->stat.period * 100.0 / -					hists->stats.total_period; +		u64 total = hists__total_period(h->hists); +		float percent;  		if (h->filtered)  			continue; +		percent = hist_entry__get_percent_limit(h);  		if (percent < min_pcnt)  			continue; @@ -294,6 +247,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,  		col_idx = 0;  		perf_hpp__for_each_format(fmt) { +			if (perf_hpp__should_skip(fmt)) +				continue; +  			if (fmt->color)  				fmt->color(fmt, &hpp, h);  			else @@ -302,23 +258,10 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,  			gtk_tree_store_set(store, &iter, col_idx++, s, -1);  		} -		list_for_each_entry(se, &hist_entry__sort_list, list) { -			if (se->elide) -				continue; - -			se->se_snprintf(h, s, ARRAY_SIZE(s), -					hists__col_len(hists, se->se_width_idx)); - -			gtk_tree_store_set(store, &iter, col_idx++, s, -1); -		} -  		if (symbol_conf.use_callchain && sort__has_sym) { -			u64 total; -  			if (callchain_param.mode == CHAIN_GRAPH_REL) -				total = h->stat.period; -			else -				total = hists->stats.total_period; +				total = symbol_conf.cumulate_callchain ? +					h->stat_acc->period : h->stat.period;  			perf_gtk__add_callchain(&h->sorted_chain, store, &iter,  						sym_col, total); @@ -375,7 +318,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,  	gtk_container_add(GTK_CONTAINER(window), vbox); -	list_for_each_entry(pos, &evlist->entries, node) { +	evlist__for_each(evlist, pos) {  		struct hists *hists = &pos->hists;  		const char *evname = perf_evsel__name(pos);  		GtkWidget *scrolled_window; diff --git a/tools/perf/ui/gtk/progress.c b/tools/perf/ui/gtk/progress.c index 482bcf3df9b..b656655fbc3 100644 --- a/tools/perf/ui/gtk/progress.c +++ b/tools/perf/ui/gtk/progress.c @@ -7,14 +7,14 @@  static GtkWidget *dialog;  static GtkWidget *progress; -static void gtk_progress_update(u64 curr, u64 total, const char *title) +static void gtk_ui_progress__update(struct ui_progress *p)  { -	double fraction = total ? 1.0 * curr / total : 0.0; +	double fraction = p->total ? 1.0 * p->curr / p->total : 0.0;  	char buf[1024];  	if (dialog == NULL) {  		GtkWidget *vbox = gtk_vbox_new(TRUE, 5); -		GtkWidget *label = gtk_label_new(title); +		GtkWidget *label = gtk_label_new(p->title);  		dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);  		progress = gtk_progress_bar_new(); @@ -32,7 +32,7 @@ static void gtk_progress_update(u64 curr, u64 total, const char *title)  	}  	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction); -	snprintf(buf, sizeof(buf), "%"PRIu64" / %"PRIu64, curr, total); +	snprintf(buf, sizeof(buf), "%"PRIu64" / %"PRIu64, p->curr, p->total);  	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf);  	/* we didn't call gtk_main yet, so do it manually */ @@ -40,7 +40,7 @@ static void gtk_progress_update(u64 curr, u64 total, const char *title)  		gtk_main_iteration();  } -static void gtk_progress_finish(void) +static void gtk_ui_progress__finish(void)  {  	/* this will also destroy all of its children */  	gtk_widget_destroy(dialog); @@ -48,12 +48,12 @@ static void gtk_progress_finish(void)  	dialog = NULL;  } -static struct ui_progress gtk_progress_fns = { -	.update		= gtk_progress_update, -	.finish		= gtk_progress_finish, +static struct ui_progress_ops gtk_ui_progress__ops = { +	.update		= gtk_ui_progress__update, +	.finish		= gtk_ui_progress__finish,  }; -void perf_gtk__init_progress(void) +void gtk_ui_progress__init(void)  { -	progress_fns = >k_progress_fns; +	ui_progress__ops = >k_ui_progress__ops;  } diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c index 6c2dd2e423f..1d57676f821 100644 --- a/tools/perf/ui/gtk/setup.c +++ b/tools/perf/ui/gtk/setup.c @@ -8,7 +8,7 @@ int perf_gtk__init(void)  {  	perf_error__register(&perf_gtk_eops);  	perf_gtk__init_helpline(); -	perf_gtk__init_progress(); +	gtk_ui_progress__init();  	perf_gtk__init_hpp();  	return gtk_init_check(NULL, NULL) ? 0 : -1; diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c index c06942a41c7..52e7fc48af9 100644 --- a/tools/perf/ui/gtk/util.c +++ b/tools/perf/ui/gtk/util.c @@ -23,8 +23,7 @@ int perf_gtk__deactivate_context(struct perf_gtk_context **ctx)  	if (!perf_gtk__is_active_context(*ctx))  		return -1; -	free(*ctx); -	*ctx = NULL; +	zfree(ctx);  	return 0;  } @@ -53,7 +52,7 @@ static int perf_gtk__error(const char *format, va_list args)  	return 0;  } -#ifdef HAVE_GTK_INFO_BAR +#ifdef HAVE_GTK_INFO_BAR_SUPPORT  static int perf_gtk__warning_info_bar(const char *format, va_list args)  {  	char *msg; @@ -105,7 +104,7 @@ static int perf_gtk__warning_statusbar(const char *format, va_list args)  struct perf_error_ops perf_gtk_eops = {  	.error		= perf_gtk__error, -#ifdef HAVE_GTK_INFO_BAR +#ifdef HAVE_GTK_INFO_BAR_SUPPORT  	.warning	= perf_gtk__warning_info_bar,  #else  	.warning	= perf_gtk__warning_statusbar, diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 0a193281eba..498adb23c02 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -8,27 +8,33 @@  /* hist period print (hpp) functions */ -typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...); - -static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, -		      u64 (*get_field)(struct hist_entry *), -		      const char *fmt, hpp_snprint_fn print_fn, -		      bool fmt_percent) +#define hpp__call_print_fn(hpp, fn, fmt, ...)			\ +({								\ +	int __ret = fn(hpp, fmt, ##__VA_ARGS__);		\ +	advance_hpp(hpp, __ret);				\ +	__ret;							\ +}) + +int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, +	       hpp_field_fn get_field, const char *fmt, +	       hpp_snprint_fn print_fn, bool fmt_percent)  {  	int ret;  	struct hists *hists = he->hists;  	struct perf_evsel *evsel = hists_to_evsel(hists); +	char *buf = hpp->buf; +	size_t size = hpp->size;  	if (fmt_percent) {  		double percent = 0.0; +		u64 total = hists__total_period(hists); -		if (hists->stats.total_period) -			percent = 100.0 * get_field(he) / -				  hists->stats.total_period; +		if (total) +			percent = 100.0 * get_field(he) / total; -		ret = print_fn(hpp->buf, hpp->size, fmt, percent); +		ret = hpp__call_print_fn(hpp, print_fn, fmt, percent);  	} else -		ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he)); +		ret = hpp__call_print_fn(hpp, print_fn, fmt, get_field(he));  	if (perf_evsel__is_group_event(evsel)) {  		int prev_idx, idx_delta; @@ -39,7 +45,7 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,  		list_for_each_entry(pair, &he->pairs.head, pairs.node) {  			u64 period = get_field(pair); -			u64 total = pair->hists->stats.total_period; +			u64 total = hists__total_period(pair->hists);  			if (!total)  				continue; @@ -52,16 +58,22 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,  				 * zero-fill group members in the middle which  				 * have no sample  				 */ -				ret += print_fn(hpp->buf + ret, hpp->size - ret, -						fmt, 0); +				if (fmt_percent) { +					ret += hpp__call_print_fn(hpp, print_fn, +								  fmt, 0.0); +				} else { +					ret += hpp__call_print_fn(hpp, print_fn, +								  fmt, 0ULL); +				}  			} -			if (fmt_percent) -				ret += print_fn(hpp->buf + ret, hpp->size - ret, -						fmt, 100.0 * period / total); -			else -				ret += print_fn(hpp->buf + ret, hpp->size - ret, -						fmt, period); +			if (fmt_percent) { +				ret += hpp__call_print_fn(hpp, print_fn, fmt, +							  100.0 * period / total); +			} else { +				ret += hpp__call_print_fn(hpp, print_fn, fmt, +							  period); +			}  			prev_idx = perf_evsel__group_idx(evsel);  		} @@ -72,41 +84,166 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,  			/*  			 * zero-fill group members at last which have no sample  			 */ -			ret += print_fn(hpp->buf + ret, hpp->size - ret, -					fmt, 0); +			if (fmt_percent) { +				ret += hpp__call_print_fn(hpp, print_fn, +							  fmt, 0.0); +			} else { +				ret += hpp__call_print_fn(hpp, print_fn, +							  fmt, 0ULL); +			}  		}  	} + +	/* +	 * Restore original buf and size as it's where caller expects +	 * the result will be saved. +	 */ +	hpp->buf = buf; +	hpp->size = size; + +	return ret; +} + +int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he, +		   hpp_field_fn get_field, const char *fmt, +		   hpp_snprint_fn print_fn, bool fmt_percent) +{ +	if (!symbol_conf.cumulate_callchain) { +		return snprintf(hpp->buf, hpp->size, "%*s", +				fmt_percent ? 8 : 12, "N/A"); +	} + +	return __hpp__fmt(hpp, he, get_field, fmt, print_fn, fmt_percent); +} + +static int field_cmp(u64 field_a, u64 field_b) +{ +	if (field_a > field_b) +		return 1; +	if (field_a < field_b) +		return -1; +	return 0; +} + +static int __hpp__sort(struct hist_entry *a, struct hist_entry *b, +		       hpp_field_fn get_field) +{ +	s64 ret; +	int i, nr_members; +	struct perf_evsel *evsel; +	struct hist_entry *pair; +	u64 *fields_a, *fields_b; + +	ret = field_cmp(get_field(a), get_field(b)); +	if (ret || !symbol_conf.event_group) +		return ret; + +	evsel = hists_to_evsel(a->hists); +	if (!perf_evsel__is_group_event(evsel)) +		return ret; + +	nr_members = evsel->nr_members; +	fields_a = calloc(sizeof(*fields_a), nr_members); +	fields_b = calloc(sizeof(*fields_b), nr_members); + +	if (!fields_a || !fields_b) +		goto out; + +	list_for_each_entry(pair, &a->pairs.head, pairs.node) { +		evsel = hists_to_evsel(pair->hists); +		fields_a[perf_evsel__group_idx(evsel)] = get_field(pair); +	} + +	list_for_each_entry(pair, &b->pairs.head, pairs.node) { +		evsel = hists_to_evsel(pair->hists); +		fields_b[perf_evsel__group_idx(evsel)] = get_field(pair); +	} + +	for (i = 1; i < nr_members; i++) { +		ret = field_cmp(fields_a[i], fields_b[i]); +		if (ret) +			break; +	} + +out: +	free(fields_a); +	free(fields_b); + +	return ret; +} + +static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, +			   hpp_field_fn get_field) +{ +	s64 ret = 0; + +	if (symbol_conf.cumulate_callchain) { +		/* +		 * Put caller above callee when they have equal period. +		 */ +		ret = field_cmp(get_field(a), get_field(b)); +		if (ret) +			return ret; + +		ret = b->callchain->max_depth - a->callchain->max_depth; +	}  	return ret;  }  #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) 		\  static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused,	\ -			       struct perf_hpp *hpp)			\ +			       struct perf_hpp *hpp,			\ +			       struct perf_evsel *evsel)		\  {									\  	int len = _min_width;						\  									\ -	if (symbol_conf.event_group) {					\ -		struct perf_evsel *evsel = hpp->ptr;			\ -									\ +	if (symbol_conf.event_group)					\  		len = max(len, evsel->nr_members * _unit_width);	\ -	}								\ +									\  	return scnprintf(hpp->buf, hpp->size, "%*s", len, _str);	\  }  #define __HPP_WIDTH_FN(_type, _min_width, _unit_width) 			\  static int hpp__width_##_type(struct perf_hpp_fmt *fmt __maybe_unused,	\ -			      struct perf_hpp *hpp __maybe_unused)	\ +			      struct perf_hpp *hpp __maybe_unused,	\ +			      struct perf_evsel *evsel)			\  {									\  	int len = _min_width;						\  									\ -	if (symbol_conf.event_group) {					\ -		struct perf_evsel *evsel = hpp->ptr;			\ -									\ +	if (symbol_conf.event_group)					\  		len = max(len, evsel->nr_members * _unit_width);	\ -	}								\ +									\  	return len;							\  } +static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) +{ +	va_list args; +	ssize_t ssize = hpp->size; +	double percent; +	int ret; + +	va_start(args, fmt); +	percent = va_arg(args, double); +	ret = value_color_snprintf(hpp->buf, hpp->size, fmt, percent); +	va_end(args); + +	return (ret >= ssize) ? (ssize - 1) : ret; +} + +static int hpp_entry_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) +{ +	va_list args; +	ssize_t ssize = hpp->size; +	int ret; + +	va_start(args, fmt); +	ret = vsnprintf(hpp->buf, hpp->size, fmt, args); +	va_end(args); + +	return (ret >= ssize) ? (ssize - 1) : ret; +} +  #define __HPP_COLOR_PERCENT_FN(_type, _field)					\  static u64 he_get_##_field(struct hist_entry *he)				\  {										\ @@ -117,7 +254,7 @@ static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,		\  			      struct perf_hpp *hpp, struct hist_entry *he) 	\  {										\  	return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%",			\ -			  (hpp_snprint_fn)percent_color_snprintf, true);	\ +			  hpp_color_scnprintf, true);				\  }  #define __HPP_ENTRY_PERCENT_FN(_type, _field)					\ @@ -126,7 +263,41 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused,		\  {										\  	const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%";		\  	return __hpp__fmt(hpp, he, he_get_##_field, fmt,			\ -			  scnprintf, true);					\ +			  hpp_entry_scnprintf, true);				\ +} + +#define __HPP_SORT_FN(_type, _field)						\ +static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b)	\ +{										\ +	return __hpp__sort(a, b, he_get_##_field);				\ +} + +#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)				\ +static u64 he_get_acc_##_field(struct hist_entry *he)				\ +{										\ +	return he->stat_acc->_field;						\ +}										\ +										\ +static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,		\ +			      struct perf_hpp *hpp, struct hist_entry *he) 	\ +{										\ +	return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%",		\ +			      hpp_color_scnprintf, true);			\ +} + +#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field)				\ +static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused,		\ +			      struct perf_hpp *hpp, struct hist_entry *he) 	\ +{										\ +	const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%";		\ +	return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, fmt,		\ +			      hpp_entry_scnprintf, true);			\ +} + +#define __HPP_SORT_ACC_FN(_type, _field)					\ +static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b)	\ +{										\ +	return __hpp__sort_acc(a, b, he_get_acc_##_field);			\  }  #define __HPP_ENTRY_RAW_FN(_type, _field)					\ @@ -139,43 +310,85 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused,		\  			      struct perf_hpp *hpp, struct hist_entry *he) 	\  {										\  	const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64;	\ -	return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false);	\ +	return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt,			\ +			  hpp_entry_scnprintf, false);				\  } +#define __HPP_SORT_RAW_FN(_type, _field)					\ +static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b)	\ +{										\ +	return __hpp__sort(a, b, he_get_raw_##_field);				\ +} + +  #define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width)	\  __HPP_HEADER_FN(_type, _str, _min_width, _unit_width)			\  __HPP_WIDTH_FN(_type, _min_width, _unit_width)				\  __HPP_COLOR_PERCENT_FN(_type, _field)					\ -__HPP_ENTRY_PERCENT_FN(_type, _field) +__HPP_ENTRY_PERCENT_FN(_type, _field)					\ +__HPP_SORT_FN(_type, _field) + +#define HPP_PERCENT_ACC_FNS(_type, _str, _field, _min_width, _unit_width)\ +__HPP_HEADER_FN(_type, _str, _min_width, _unit_width)			\ +__HPP_WIDTH_FN(_type, _min_width, _unit_width)				\ +__HPP_COLOR_ACC_PERCENT_FN(_type, _field)				\ +__HPP_ENTRY_ACC_PERCENT_FN(_type, _field)				\ +__HPP_SORT_ACC_FN(_type, _field)  #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width)	\  __HPP_HEADER_FN(_type, _str, _min_width, _unit_width)			\  __HPP_WIDTH_FN(_type, _min_width, _unit_width)				\ -__HPP_ENTRY_RAW_FN(_type, _field) +__HPP_ENTRY_RAW_FN(_type, _field)					\ +__HPP_SORT_RAW_FN(_type, _field) +__HPP_HEADER_FN(overhead_self, "Self", 8, 8)  HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)  HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8)  HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8)  HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8)  HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) +HPP_PERCENT_ACC_FNS(overhead_acc, "Children", period, 8, 8)  HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)  HPP_RAW_FNS(period, "Period", period, 12, 12) +static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, +			    struct hist_entry *b __maybe_unused) +{ +	return 0; +} +  #define HPP__COLOR_PRINT_FNS(_name)			\  	{						\  		.header	= hpp__header_ ## _name,	\  		.width	= hpp__width_ ## _name,		\  		.color	= hpp__color_ ## _name,		\ -		.entry	= hpp__entry_ ## _name		\ +		.entry	= hpp__entry_ ## _name,		\ +		.cmp	= hpp__nop_cmp,			\ +		.collapse = hpp__nop_cmp,		\ +		.sort	= hpp__sort_ ## _name,		\ +	} + +#define HPP__COLOR_ACC_PRINT_FNS(_name)			\ +	{						\ +		.header	= hpp__header_ ## _name,	\ +		.width	= hpp__width_ ## _name,		\ +		.color	= hpp__color_ ## _name,		\ +		.entry	= hpp__entry_ ## _name,		\ +		.cmp	= hpp__nop_cmp,			\ +		.collapse = hpp__nop_cmp,		\ +		.sort	= hpp__sort_ ## _name,		\  	}  #define HPP__PRINT_FNS(_name)				\  	{						\  		.header	= hpp__header_ ## _name,	\  		.width	= hpp__width_ ## _name,		\ -		.entry	= hpp__entry_ ## _name		\ +		.entry	= hpp__entry_ ## _name,		\ +		.cmp	= hpp__nop_cmp,			\ +		.collapse = hpp__nop_cmp,		\ +		.sort	= hpp__sort_ ## _name,		\  	}  struct perf_hpp_fmt perf_hpp__format[] = { @@ -184,28 +397,63 @@ struct perf_hpp_fmt perf_hpp__format[] = {  	HPP__COLOR_PRINT_FNS(overhead_us),  	HPP__COLOR_PRINT_FNS(overhead_guest_sys),  	HPP__COLOR_PRINT_FNS(overhead_guest_us), +	HPP__COLOR_ACC_PRINT_FNS(overhead_acc),  	HPP__PRINT_FNS(samples),  	HPP__PRINT_FNS(period)  };  LIST_HEAD(perf_hpp__list); +LIST_HEAD(perf_hpp__sort_list);  #undef HPP__COLOR_PRINT_FNS +#undef HPP__COLOR_ACC_PRINT_FNS  #undef HPP__PRINT_FNS  #undef HPP_PERCENT_FNS +#undef HPP_PERCENT_ACC_FNS  #undef HPP_RAW_FNS  #undef __HPP_HEADER_FN  #undef __HPP_WIDTH_FN  #undef __HPP_COLOR_PERCENT_FN  #undef __HPP_ENTRY_PERCENT_FN +#undef __HPP_COLOR_ACC_PERCENT_FN +#undef __HPP_ENTRY_ACC_PERCENT_FN  #undef __HPP_ENTRY_RAW_FN +#undef __HPP_SORT_FN +#undef __HPP_SORT_ACC_FN +#undef __HPP_SORT_RAW_FN  void perf_hpp__init(void)  { +	struct list_head *list; +	int i; + +	for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { +		struct perf_hpp_fmt *fmt = &perf_hpp__format[i]; + +		INIT_LIST_HEAD(&fmt->list); + +		/* sort_list may be linked by setup_sorting() */ +		if (fmt->sort_list.next == NULL) +			INIT_LIST_HEAD(&fmt->sort_list); +	} + +	/* +	 * If user specified field order, no need to setup default fields. +	 */ +	if (field_order) +		return; + +	if (symbol_conf.cumulate_callchain) { +		perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC); + +		perf_hpp__format[PERF_HPP__OVERHEAD].header = +						hpp__header_overhead_self; +	} +  	perf_hpp__column_enable(PERF_HPP__OVERHEAD);  	if (symbol_conf.show_cpu_utilization) { @@ -223,6 +471,17 @@ void perf_hpp__init(void)  	if (symbol_conf.show_total_period)  		perf_hpp__column_enable(PERF_HPP__PERIOD); + +	/* prepend overhead field for backward compatiblity.  */ +	list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list; +	if (list_empty(list)) +		list_add(list, &perf_hpp__sort_list); + +	if (symbol_conf.cumulate_callchain) { +		list = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC].sort_list; +		if (list_empty(list)) +			list_add(list, &perf_hpp__sort_list); +	}  }  void perf_hpp__column_register(struct perf_hpp_fmt *format) @@ -230,29 +489,110 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format)  	list_add_tail(&format->list, &perf_hpp__list);  } +void perf_hpp__column_unregister(struct perf_hpp_fmt *format) +{ +	list_del(&format->list); +} + +void perf_hpp__register_sort_field(struct perf_hpp_fmt *format) +{ +	list_add_tail(&format->sort_list, &perf_hpp__sort_list); +} +  void perf_hpp__column_enable(unsigned col)  {  	BUG_ON(col >= PERF_HPP__MAX_INDEX);  	perf_hpp__column_register(&perf_hpp__format[col]);  } -int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, -			      struct hists *hists) +void perf_hpp__column_disable(unsigned col)  { -	const char *sep = symbol_conf.field_sep; -	struct sort_entry *se; -	int ret = 0; +	BUG_ON(col >= PERF_HPP__MAX_INDEX); +	perf_hpp__column_unregister(&perf_hpp__format[col]); +} + +void perf_hpp__cancel_cumulate(void) +{ +	if (field_order) +		return; + +	perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC); +	perf_hpp__format[PERF_HPP__OVERHEAD].header = hpp__header_overhead; +} -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		if (se->elide) +void perf_hpp__setup_output_field(void) +{ +	struct perf_hpp_fmt *fmt; + +	/* append sort keys to output field */ +	perf_hpp__for_each_sort_list(fmt) { +		if (!list_empty(&fmt->list))  			continue; -		ret += scnprintf(s + ret, size - ret, "%s", sep ?: "  "); -		ret += se->se_snprintf(he, s + ret, size - ret, -				       hists__col_len(hists, se->se_width_idx)); +		/* +		 * sort entry fields are dynamically created, +		 * so they can share a same sort key even though +		 * the list is empty. +		 */ +		if (perf_hpp__is_sort_entry(fmt)) { +			struct perf_hpp_fmt *pos; + +			perf_hpp__for_each_format(pos) { +				if (perf_hpp__same_sort_entry(pos, fmt)) +					goto next; +			} +		} + +		perf_hpp__column_register(fmt); +next: +		continue;  	} +} -	return ret; +void perf_hpp__append_sort_keys(void) +{ +	struct perf_hpp_fmt *fmt; + +	/* append output fields to sort keys */ +	perf_hpp__for_each_format(fmt) { +		if (!list_empty(&fmt->sort_list)) +			continue; + +		/* +		 * sort entry fields are dynamically created, +		 * so they can share a same sort key even though +		 * the list is empty. +		 */ +		if (perf_hpp__is_sort_entry(fmt)) { +			struct perf_hpp_fmt *pos; + +			perf_hpp__for_each_sort_list(pos) { +				if (perf_hpp__same_sort_entry(pos, fmt)) +					goto next; +			} +		} + +		perf_hpp__register_sort_field(fmt); +next: +		continue; +	} +} + +void perf_hpp__reset_output_field(void) +{ +	struct perf_hpp_fmt *fmt, *tmp; + +	/* reset output fields */ +	perf_hpp__for_each_format_safe(fmt, tmp) { +		list_del_init(&fmt->list); +		list_del_init(&fmt->sort_list); +	} + +	/* reset sort keys */ +	perf_hpp__for_each_sort_list_safe(fmt, tmp) { +		list_del_init(&fmt->list); +		list_del_init(&fmt->sort_list); +	}  }  /* @@ -261,24 +601,23 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size,  unsigned int hists__sort_list_width(struct hists *hists)  {  	struct perf_hpp_fmt *fmt; -	struct sort_entry *se; -	int i = 0, ret = 0; -	struct perf_hpp dummy_hpp = { -		.ptr	= hists_to_evsel(hists), -	}; +	int ret = 0; +	bool first = true; +	struct perf_hpp dummy_hpp;  	perf_hpp__for_each_format(fmt) { -		if (i) +		if (perf_hpp__should_skip(fmt)) +			continue; + +		if (first) +			first = false; +		else  			ret += 2; -		ret += fmt->width(fmt, &dummy_hpp); +		ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));  	} -	list_for_each_entry(se, &hist_entry__sort_list, list) -		if (!se->elide) -			ret += 2 + hists__col_len(hists, se->se_width_idx); - -	if (verbose) /* Addr + origin */ +	if (verbose && sort__has_sym) /* Addr + origin */  		ret += 3 + BITS_PER_LONG / 4;  	return ret; diff --git a/tools/perf/ui/progress.c b/tools/perf/ui/progress.c index 3ec695607a4..a0f24c7115c 100644 --- a/tools/perf/ui/progress.c +++ b/tools/perf/ui/progress.c @@ -1,26 +1,38 @@  #include "../cache.h"  #include "progress.h" -static void nop_progress_update(u64 curr __maybe_unused, -				u64 total __maybe_unused, -				const char *title __maybe_unused) +static void null_progress__update(struct ui_progress *p __maybe_unused)  {  } -static struct ui_progress default_progress_fns = +static struct ui_progress_ops null_progress__ops =  { -	.update		= nop_progress_update, +	.update = null_progress__update,  }; -struct ui_progress *progress_fns = &default_progress_fns; +struct ui_progress_ops *ui_progress__ops = &null_progress__ops; -void ui_progress__update(u64 curr, u64 total, const char *title) +void ui_progress__update(struct ui_progress *p, u64 adv)  { -	return progress_fns->update(curr, total, title); +	p->curr += adv; + +	if (p->curr >= p->next) { +		p->next += p->step; +		ui_progress__ops->update(p); +	} +} + +void ui_progress__init(struct ui_progress *p, u64 total, const char *title) +{ +	p->curr = 0; +	p->next = p->step = total / 16; +	p->total = total; +	p->title = title; +  }  void ui_progress__finish(void)  { -	if (progress_fns->finish) -		progress_fns->finish(); +	if (ui_progress__ops->finish) +		ui_progress__ops->finish();  } diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h index 257cc224f9c..f34f89eb607 100644 --- a/tools/perf/ui/progress.h +++ b/tools/perf/ui/progress.h @@ -1,18 +1,23 @@  #ifndef _PERF_UI_PROGRESS_H_  #define _PERF_UI_PROGRESS_H_ 1 -#include <../types.h> +#include <linux/types.h> +void ui_progress__finish(void); +   struct ui_progress { -	void (*update)(u64, u64, const char *); -	void (*finish)(void); +	const char *title; +	u64 curr, next, step, total;  }; +  +void ui_progress__init(struct ui_progress *p, u64 total, const char *title); +void ui_progress__update(struct ui_progress *p, u64 adv); -extern struct ui_progress *progress_fns; - -void ui_progress__init(void); +struct ui_progress_ops { +	void (*update)(struct ui_progress *p); +	void (*finish)(void); +}; -void ui_progress__update(u64 curr, u64 total, const char *title); -void ui_progress__finish(void); +extern struct ui_progress_ops *ui_progress__ops;  #endif diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index 47d9a571f26..ba51fa8a117 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c @@ -1,10 +1,64 @@  #include <pthread.h> +#include <dlfcn.h>  #include "../util/cache.h"  #include "../util/debug.h"  #include "../util/hist.h"  pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; +void *perf_gtk_handle; + +#ifdef HAVE_GTK2_SUPPORT +static int setup_gtk_browser(void) +{ +	int (*perf_ui_init)(void); + +	if (perf_gtk_handle) +		return 0; + +	perf_gtk_handle = dlopen(PERF_GTK_DSO, RTLD_LAZY); +	if (perf_gtk_handle == NULL) { +		char buf[PATH_MAX]; +		scnprintf(buf, sizeof(buf), "%s/%s", LIBDIR, PERF_GTK_DSO); +		perf_gtk_handle = dlopen(buf, RTLD_LAZY); +	} +	if (perf_gtk_handle == NULL) +		return -1; + +	perf_ui_init = dlsym(perf_gtk_handle, "perf_gtk__init"); +	if (perf_ui_init == NULL) +		goto out_close; + +	if (perf_ui_init() == 0) +		return 0; + +out_close: +	dlclose(perf_gtk_handle); +	return -1; +} + +static void exit_gtk_browser(bool wait_for_ok) +{ +	void (*perf_ui_exit)(bool); + +	if (perf_gtk_handle == NULL) +		return; + +	perf_ui_exit = dlsym(perf_gtk_handle, "perf_gtk__exit"); +	if (perf_ui_exit == NULL) +		goto out_close; + +	perf_ui_exit(wait_for_ok); + +out_close: +	dlclose(perf_gtk_handle); + +	perf_gtk_handle = NULL; +} +#else +static inline int setup_gtk_browser(void) { return -1; } +static inline void exit_gtk_browser(bool wait_for_ok __maybe_unused) {} +#endif  void setup_browser(bool fallback_to_pager)  { @@ -17,8 +71,11 @@ void setup_browser(bool fallback_to_pager)  	switch (use_browser) {  	case 2: -		if (perf_gtk__init() == 0) +		if (setup_gtk_browser() == 0)  			break; +		printf("GTK browser requested but could not find %s\n", +		       PERF_GTK_DSO); +		sleep(1);  		/* fall through */  	case 1:  		use_browser = 1; @@ -29,8 +86,6 @@ void setup_browser(bool fallback_to_pager)  		use_browser = 0;  		if (fallback_to_pager)  			setup_pager(); - -		perf_hpp__init();  		break;  	}  } @@ -39,7 +94,7 @@ void exit_browser(bool wait_for_ok)  {  	switch (use_browser) {  	case 2: -		perf_gtk__exit(wait_for_ok); +		exit_gtk_browser(wait_for_ok);  		break;  	case 1: diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 194e2f42ff5..90122abd372 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -183,7 +183,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,  			 * the symbol. No need to print it otherwise it appears as  			 * displayed twice.  			 */ -			if (!i++ && sort__first_dimension == SORT_SYM) +			if (!i++ && field_order == NULL && +			    sort_order && !prefixcmp(sort_order, "sym"))  				continue;  			if (!printed) {  				ret += callchain__fprintf_left_margin(fp, left_margin); @@ -213,20 +214,19 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,  	return ret;  } -static size_t __callchain__fprintf_flat(FILE *fp, -					struct callchain_node *self, +static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,  					u64 total_samples)  {  	struct callchain_list *chain;  	size_t ret = 0; -	if (!self) +	if (!node)  		return 0; -	ret += __callchain__fprintf_flat(fp, self->parent, total_samples); +	ret += __callchain__fprintf_flat(fp, node->parent, total_samples); -	list_for_each_entry(chain, &self->val, list) { +	list_for_each_entry(chain, &node->val, list) {  		if (chain->ip >= PERF_CONTEXT_MAX)  			continue;  		if (chain->ms.sym) @@ -239,15 +239,14 @@ static size_t __callchain__fprintf_flat(FILE *fp,  	return ret;  } -static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self, +static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree,  				      u64 total_samples)  {  	size_t ret = 0;  	u32 entries_printed = 0; -	struct rb_node *rb_node;  	struct callchain_node *chain; +	struct rb_node *rb_node = rb_first(tree); -	rb_node = rb_first(self);  	while (rb_node) {  		double percent; @@ -272,7 +271,9 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he,  {  	switch (callchain_param.mode) {  	case CHAIN_GRAPH_REL: -		return callchain__fprintf_graph(fp, &he->sorted_chain, he->stat.period, +		return callchain__fprintf_graph(fp, &he->sorted_chain, +						symbol_conf.cumulate_callchain ? +						he->stat_acc->period : he->stat.period,  						left_margin);  		break;  	case CHAIN_GRAPH_ABS: @@ -298,25 +299,24 @@ static size_t hist_entry__callchain_fprintf(struct hist_entry *he,  	int left_margin = 0;  	u64 total_period = hists->stats.total_period; -	if (sort__first_dimension == SORT_COMM) { -		struct sort_entry *se = list_first_entry(&hist_entry__sort_list, -							 typeof(*se), list); -		left_margin = hists__col_len(hists, se->se_width_idx); -		left_margin -= thread__comm_len(he->thread); -	} +	if (field_order == NULL && (sort_order == NULL || +				    !prefixcmp(sort_order, "comm"))) { +		struct perf_hpp_fmt *fmt; -	return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); -} +		perf_hpp__for_each_format(fmt) { +			if (!perf_hpp__is_sort_entry(fmt)) +				continue; -static inline void advance_hpp(struct perf_hpp *hpp, int inc) -{ -	hpp->buf  += inc; -	hpp->size -= inc; +			/* must be 'comm' sort entry */ +			left_margin = fmt->width(fmt, NULL, hists_to_evsel(hists)); +			left_margin -= thread__comm_len(he->thread); +			break; +		} +	} +	return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);  } -static int hist_entry__period_snprintf(struct perf_hpp *hpp, -				       struct hist_entry *he, -				       bool color) +static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)  {  	const char *sep = symbol_conf.field_sep;  	struct perf_hpp_fmt *fmt; @@ -328,6 +328,9 @@ static int hist_entry__period_snprintf(struct perf_hpp *hpp,  		return 0;  	perf_hpp__for_each_format(fmt) { +		if (perf_hpp__should_skip(fmt)) +			continue; +  		/*  		 * If there's no field_sep, we still need  		 * to display initial '  '. @@ -338,7 +341,7 @@ static int hist_entry__period_snprintf(struct perf_hpp *hpp,  		} else  			first = false; -		if (color && fmt->color) +		if (perf_hpp__use_color() && fmt->color)  			ret = fmt->color(fmt, hpp, he);  		else  			ret = fmt->entry(fmt, hpp, he); @@ -358,13 +361,11 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,  		.buf		= bf,  		.size		= size,  	}; -	bool color = !symbol_conf.field_sep;  	if (size == 0 || size > bfsz)  		size = hpp.size = bfsz; -	ret = hist_entry__period_snprintf(&hpp, he, color); -	hist_entry__sort_snprintf(he, bf + ret, size - ret, hists); +	hist_entry__snprintf(he, &hpp);  	ret = fprintf(fp, "%s\n", bf); @@ -378,18 +379,15 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,  		      int max_cols, float min_pcnt, FILE *fp)  {  	struct perf_hpp_fmt *fmt; -	struct sort_entry *se;  	struct rb_node *nd;  	size_t ret = 0;  	unsigned int width;  	const char *sep = symbol_conf.field_sep; -	const char *col_width = symbol_conf.col_width_list_str;  	int nr_rows = 0;  	char bf[96];  	struct perf_hpp dummy_hpp = {  		.buf	= bf,  		.size	= sizeof(bf), -		.ptr	= hists_to_evsel(hists),  	};  	bool first = true;  	size_t linesz; @@ -397,43 +395,28 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,  	init_rem_hits(); + +	perf_hpp__for_each_format(fmt) +		perf_hpp__reset_width(fmt, hists); +  	if (!show_header)  		goto print_entries;  	fprintf(fp, "# ");  	perf_hpp__for_each_format(fmt) { +		if (perf_hpp__should_skip(fmt)) +			continue; +  		if (!first)  			fprintf(fp, "%s", sep ?: "  ");  		else  			first = false; -		fmt->header(fmt, &dummy_hpp); +		fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));  		fprintf(fp, "%s", bf);  	} -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		if (se->elide) -			continue; -		if (sep) { -			fprintf(fp, "%c%s", *sep, se->se_header); -			continue; -		} -		width = strlen(se->se_header); -		if (symbol_conf.col_width_list_str) { -			if (col_width) { -				hists__set_col_len(hists, se->se_width_idx, -						   atoi(col_width)); -				col_width = strchr(col_width, ','); -				if (col_width) -					++col_width; -			} -		} -		if (!hists__new_col_len(hists, se->se_width_idx, width)) -			width = hists__col_len(hists, se->se_width_idx); -		fprintf(fp, "  %*s", width, se->se_header); -	} -  	fprintf(fp, "\n");  	if (max_rows && ++nr_rows >= max_rows)  		goto out; @@ -448,26 +431,15 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,  	perf_hpp__for_each_format(fmt) {  		unsigned int i; +		if (perf_hpp__should_skip(fmt)) +			continue; +  		if (!first)  			fprintf(fp, "%s", sep ?: "  ");  		else  			first = false; -		width = fmt->width(fmt, &dummy_hpp); -		for (i = 0; i < width; i++) -			fprintf(fp, "."); -	} - -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		unsigned int i; - -		if (se->elide) -			continue; - -		fprintf(fp, "  "); -		width = hists__col_len(hists, se->se_width_idx); -		if (width == 0) -			width = strlen(se->se_header); +		width = fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));  		for (i = 0; i < width; i++)  			fprintf(fp, ".");  	} @@ -482,6 +454,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,  print_entries:  	linesz = hists__sort_list_width(hists) + 3 + 1; +	linesz += perf_hpp__color_overhead();  	line = malloc(linesz);  	if (line == NULL) {  		ret = -1; @@ -490,12 +463,12 @@ print_entries:  	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {  		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); -		float percent = h->stat.period * 100.0 / -					hists->stats.total_period; +		float percent;  		if (h->filtered)  			continue; +		percent = hist_entry__get_percent_limit(h);  		if (percent < min_pcnt)  			continue; @@ -505,7 +478,7 @@ print_entries:  			break;  		if (h->ms.map == NULL && verbose > 1) { -			__map_groups__fprintf_maps(&h->thread->mg, +			__map_groups__fprintf_maps(h->thread->mg,  						   MAP__FUNCTION, verbose, fp);  			fprintf(fp, "%.10s end\n", graph_dotted_line);  		} @@ -513,7 +486,7 @@ print_entries:  	free(line);  out: -	free(rem_sq_bracket); +	zfree(&rem_sq_bracket);  	return ret;  } diff --git a/tools/perf/ui/tui/progress.c b/tools/perf/ui/tui/progress.c index 6c2184d53cb..c61d14b101e 100644 --- a/tools/perf/ui/tui/progress.c +++ b/tools/perf/ui/tui/progress.c @@ -2,9 +2,10 @@  #include "../progress.h"  #include "../libslang.h"  #include "../ui.h" +#include "tui.h"  #include "../browser.h" -static void tui_progress__update(u64 curr, u64 total, const char *title) +static void tui_progress__update(struct ui_progress *p)  {  	int bar, y;  	/* @@ -14,29 +15,30 @@ static void tui_progress__update(u64 curr, u64 total, const char *title)  	if (use_browser <= 0)  		return; -	if (total == 0) +	if (p->total == 0)  		return; -	ui__refresh_dimensions(true); +	ui__refresh_dimensions(false);  	pthread_mutex_lock(&ui__lock);  	y = SLtt_Screen_Rows / 2 - 2;  	SLsmg_set_color(0);  	SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols);  	SLsmg_gotorc(y++, 1); -	SLsmg_write_string((char *)title); +	SLsmg_write_string((char *)p->title); +	SLsmg_fill_region(y, 1, 1, SLtt_Screen_Cols - 2, ' ');  	SLsmg_set_color(HE_COLORSET_SELECTED); -	bar = ((SLtt_Screen_Cols - 2) * curr) / total; +	bar = ((SLtt_Screen_Cols - 2) * p->curr) / p->total;  	SLsmg_fill_region(y, 1, 1, bar, ' ');  	SLsmg_refresh();  	pthread_mutex_unlock(&ui__lock);  } -static struct ui_progress tui_progress_fns = +static struct ui_progress_ops tui_progress__ops =  {  	.update		= tui_progress__update,  }; -void ui_progress__init(void) +void tui_progress__init(void)  { -	progress_fns = &tui_progress_fns; +	ui_progress__ops = &tui_progress__ops;  } diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index b9401482d11..2f612562978 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c @@ -9,6 +9,7 @@  #include "../util.h"  #include "../libslang.h"  #include "../keysyms.h" +#include "tui.h"  static volatile int ui__need_resize; @@ -119,7 +120,7 @@ int ui__init(void)  	ui_helpline__init();  	ui_browser__init(); -	ui_progress__init(); +	tui_progress__init();  	signal(SIGSEGV, ui__signal);  	signal(SIGFPE, ui__signal); diff --git a/tools/perf/ui/tui/tui.h b/tools/perf/ui/tui/tui.h new file mode 100644 index 00000000000..18961c7b6ec --- /dev/null +++ b/tools/perf/ui/tui/tui.h @@ -0,0 +1,6 @@ +#ifndef _PERF_TUI_H_ +#define _PERF_TUI_H_ 1 + +void tui_progress__init(void); + +#endif /* _PERF_TUI_H_ */ diff --git a/tools/perf/ui/tui/util.c b/tools/perf/ui/tui/util.c index 092902e30ce..bf890f72fe8 100644 --- a/tools/perf/ui/tui/util.c +++ b/tools/perf/ui/tui/util.c @@ -92,6 +92,8 @@ int ui_browser__input_window(const char *title, const char *text, char *input,  		t = sep + 1;  	} +	pthread_mutex_lock(&ui__lock); +  	max_len += 2;  	nr_lines += 8;  	y = SLtt_Screen_Rows / 2 - nr_lines / 2; @@ -120,13 +122,19 @@ int ui_browser__input_window(const char *title, const char *text, char *input,  	SLsmg_write_nstring((char *)exit_msg, max_len);  	SLsmg_refresh(); +	pthread_mutex_unlock(&ui__lock); +  	x += 2;  	len = 0;  	key = ui__getch(delay_secs);  	while (key != K_TIMER && key != K_ENTER && key != K_ESC) { +		pthread_mutex_lock(&ui__lock); +  		if (key == K_BKSPC) { -			if (len == 0) +			if (len == 0) { +				pthread_mutex_unlock(&ui__lock);  				goto next_key; +			}  			SLsmg_gotorc(y, x + --len);  			SLsmg_write_char(' ');  		} else { @@ -136,6 +144,8 @@ int ui_browser__input_window(const char *title, const char *text, char *input,  		}  		SLsmg_refresh(); +		pthread_mutex_unlock(&ui__lock); +  		/* XXX more graceful overflow handling needed */  		if (len == sizeof(buf) - 1) {  			ui_helpline__push("maximum size of symbol name reached!"); @@ -174,6 +184,8 @@ int ui__question_window(const char *title, const char *text,  		t = sep + 1;  	} +	pthread_mutex_lock(&ui__lock); +  	max_len += 2;  	nr_lines += 4;  	y = SLtt_Screen_Rows / 2 - nr_lines / 2, @@ -195,6 +207,9 @@ int ui__question_window(const char *title, const char *text,  	SLsmg_gotorc(y + nr_lines - 1, x);  	SLsmg_write_nstring((char *)exit_msg, max_len);  	SLsmg_refresh(); + +	pthread_mutex_unlock(&ui__lock); +  	return ui__getch(delay_secs);  } @@ -215,9 +230,7 @@ static int __ui__warning(const char *title, const char *format, va_list args)  	if (vasprintf(&s, format, args) > 0) {  		int key; -		pthread_mutex_lock(&ui__lock);  		key = ui__question_window(title, s, "Press any key...", 0); -		pthread_mutex_unlock(&ui__lock);  		free(s);  		return key;  	} diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h index 70cb0d4eb8a..ab88383f8be 100644 --- a/tools/perf/ui/ui.h +++ b/tools/perf/ui/ui.h @@ -6,13 +6,14 @@  #include <linux/compiler.h>  extern pthread_mutex_t ui__lock; +extern void *perf_gtk_handle;  extern int use_browser;  void setup_browser(bool fallback_to_pager);  void exit_browser(bool wait_for_ok); -#ifdef SLANG_SUPPORT +#ifdef HAVE_SLANG_SUPPORT  int ui__init(void);  void ui__exit(bool wait_for_ok);  #else @@ -23,17 +24,6 @@ static inline int ui__init(void)  static inline void ui__exit(bool wait_for_ok __maybe_unused) {}  #endif -#ifdef GTK2_SUPPORT -int perf_gtk__init(void); -void perf_gtk__exit(bool wait_for_ok); -#else -static inline int perf_gtk__init(void) -{ -	return -1; -} -static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {} -#endif -  void ui__refresh_dimensions(bool force);  #endif /* _PERF_UI_H_ */ diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 15a77b7c0e3..39f17507578 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -19,6 +19,9 @@ if test -d ../../.git -o -f ../../.git  then  	TAG=$(git describe --abbrev=0 --match "v[0-9].[0-9]*" 2>/dev/null )  	CID=$(git log -1 --abbrev=4 --pretty=format:"%h" 2>/dev/null) && CID="-g$CID" +elif test -f ../../PERF-VERSION-FILE +then +	TAG=$(cut -d' ' -f3 ../../PERF-VERSION-FILE | sed -e 's/\"//g')  fi  if test -z "$TAG"  then @@ -40,7 +43,7 @@ else  	VC=unset  fi  test "$VN" = "$VC" || { -	echo >&2 "PERF_VERSION = $VN" +	echo >&2 "  PERF_VERSION = $VN"  	echo "#define PERF_VERSION \"$VN\"" >$GVF  } diff --git a/tools/perf/util/alias.c b/tools/perf/util/alias.c index e6d134773d0..c0b43ee40d9 100644 --- a/tools/perf/util/alias.c +++ b/tools/perf/util/alias.c @@ -55,8 +55,7 @@ int split_cmdline(char *cmdline, const char ***argv)  				src++;  				c = cmdline[src];  				if (!c) { -					free(*argv); -					*argv = NULL; +					zfree(argv);  					return error("cmdline ends with \\");  				}  			} @@ -68,8 +67,7 @@ int split_cmdline(char *cmdline, const char ***argv)  	cmdline[dst] = 0;  	if (quoted) { -		free(*argv); -		*argv = NULL; +		zfree(argv);  		return error("unclosed quote");  	} diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 7eae5488ece..809b4c50bea 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -8,6 +8,8 @@   */  #include "util.h" +#include "ui/ui.h" +#include "sort.h"  #include "build-id.h"  #include "color.h"  #include "cache.h" @@ -26,10 +28,10 @@ static int disasm_line__parse(char *line, char **namep, char **rawp);  static void ins__delete(struct ins_operands *ops)  { -	free(ops->source.raw); -	free(ops->source.name); -	free(ops->target.raw); -	free(ops->target.name); +	zfree(&ops->source.raw); +	zfree(&ops->source.name); +	zfree(&ops->target.raw); +	zfree(&ops->target.name);  }  static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, @@ -185,8 +187,7 @@ static int lock__parse(struct ins_operands *ops)  	return 0;  out_free_ops: -	free(ops->locked.ops); -	ops->locked.ops = NULL; +	zfree(&ops->locked.ops);  	return 0;  } @@ -205,9 +206,9 @@ static int lock__scnprintf(struct ins *ins, char *bf, size_t size,  static void lock__delete(struct ins_operands *ops)  { -	free(ops->locked.ops); -	free(ops->target.raw); -	free(ops->target.name); +	zfree(&ops->locked.ops); +	zfree(&ops->target.raw); +	zfree(&ops->target.name);  }  static struct ins_ops lock_ops = { @@ -256,8 +257,7 @@ static int mov__parse(struct ins_operands *ops)  	return 0;  out_free_source: -	free(ops->source.raw); -	ops->source.raw = NULL; +	zfree(&ops->source.raw);  	return -1;  } @@ -464,17 +464,12 @@ void symbol__annotate_zero_histograms(struct symbol *sym)  	pthread_mutex_unlock(¬es->lock);  } -int symbol__inc_addr_samples(struct symbol *sym, struct map *map, -			     int evidx, u64 addr) +static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, +				      struct annotation *notes, int evidx, u64 addr)  {  	unsigned offset; -	struct annotation *notes;  	struct sym_hist *h; -	notes = symbol__annotation(sym); -	if (notes->src == NULL) -		return -ENOMEM; -  	pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));  	if (addr < sym->start || addr > sym->end) @@ -491,6 +486,33 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map,  	return 0;  } +static int symbol__inc_addr_samples(struct symbol *sym, struct map *map, +				    int evidx, u64 addr) +{ +	struct annotation *notes; + +	if (sym == NULL) +		return 0; + +	notes = symbol__annotation(sym); +	if (notes->src == NULL) { +		if (symbol__alloc_hist(sym) < 0) +			return -ENOMEM; +	} + +	return __symbol__inc_addr_samples(sym, map, notes, evidx, addr); +} + +int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx) +{ +	return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr); +} + +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) +{ +	return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); +} +  static void disasm_line__init_ins(struct disasm_line *dl)  {  	dl->ins = ins__find(dl->name); @@ -538,8 +560,7 @@ static int disasm_line__parse(char *line, char **namep, char **rawp)  	return 0;  out_free_name: -	free(*namep); -	*namep = NULL; +	zfree(namep);  	return -1;  } @@ -564,7 +585,7 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privs  	return dl;  out_free_line: -	free(dl->line); +	zfree(&dl->line);  out_delete:  	free(dl);  	return NULL; @@ -572,8 +593,8 @@ out_delete:  void disasm_line__free(struct disasm_line *dl)  { -	free(dl->line); -	free(dl->name); +	zfree(&dl->line); +	zfree(&dl->name);  	if (dl->ins && dl->ins->ops->free)  		dl->ins->ops->free(&dl->ops);  	else @@ -825,20 +846,16 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,  		dl->ops.target.offset = dl->ops.target.addr -  					map__rip_2objdump(map, sym->start); -	/* -	 * kcore has no symbols, so add the call target name if it is on the -	 * same map. -	 */ +	/* kcore has no symbols, so add the call target name */  	if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { -		struct symbol *s; -		u64 ip = dl->ops.target.addr; - -		if (ip >= map->start && ip <= map->end) { -			ip = map->map_ip(map, ip); -			s = map__find_symbol(map, ip, NULL); -			if (s && s->start == ip) -				dl->ops.target.name = strdup(s->name); -		} +		struct addr_map_symbol target = { +			.map = map, +			.addr = dl->ops.target.addr, +		}; + +		if (!map_groups__find_ams(&target, NULL) && +		    target.sym->start == target.al_addr) +			dl->ops.target.name = strdup(target.sym->name);  	}  	disasm__add(¬es->src->source, dl); @@ -879,6 +896,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)  	FILE *file;  	int err = 0;  	char symfs_filename[PATH_MAX]; +	struct kcore_extract kce; +	bool delete_extract = false;  	if (filename) {  		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", @@ -902,7 +921,7 @@ fallback:  		 * cache, or is just a kallsyms file, well, lets hope that this  		 * DSO is the same as when 'perf record' ran.  		 */ -		filename = dso->long_name; +		filename = (char *)dso->long_name;  		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",  			 symbol_conf.symfs, filename);  		free_filename = false; @@ -940,6 +959,23 @@ fallback:  	pr_debug("annotating [%p] %30s : [%p] %30s\n",  		 dso, dso->long_name, sym, sym->name); +	if (dso__is_kcore(dso)) { +		kce.kcore_filename = symfs_filename; +		kce.addr = map__rip_2objdump(map, sym->start); +		kce.offs = sym->start; +		kce.len = sym->end + 1 - sym->start; +		if (!kcore_extract__create(&kce)) { +			delete_extract = true; +			strlcpy(symfs_filename, kce.extract_filename, +				sizeof(symfs_filename)); +			if (free_filename) { +				free(filename); +				free_filename = false; +			} +			filename = symfs_filename; +		} +	} +  	snprintf(command, sizeof(command),  		 "%s %s%s --start-address=0x%016" PRIx64  		 " --stop-address=0x%016" PRIx64 @@ -972,6 +1008,8 @@ fallback:  	pclose(file);  out_free_filename: +	if (delete_extract) +		kcore_extract__delete(&kce);  	if (free_filename)  		free(filename);  	return err; @@ -1070,24 +1108,21 @@ static void symbol__free_source_line(struct symbol *sym, int len)  			  (sizeof(src_line->p) * (src_line->nr_pcnt - 1));  	for (i = 0; i < len; i++) { -		free(src_line->path); +		free_srcline(src_line->path);  		src_line = (void *)src_line + sizeof_src_line;  	} -	free(notes->src->lines); -	notes->src->lines = NULL; +	zfree(¬es->src->lines);  }  /* Get the filename:line for the colored entries */  static int symbol__get_source_line(struct symbol *sym, struct map *map,  				   struct perf_evsel *evsel, -				   struct rb_root *root, int len, -				   const char *filename) +				   struct rb_root *root, int len)  {  	u64 start;  	int i, k;  	int evidx = evsel->idx; -	char cmd[PATH_MAX * 2];  	struct source_line *src_line;  	struct annotation *notes = symbol__annotation(sym);  	struct sym_hist *h = annotation__histogram(notes, evidx); @@ -1115,10 +1150,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,  	start = map__rip_2objdump(map, sym->start);  	for (i = 0; i < len; i++) { -		char *path = NULL; -		size_t line_len;  		u64 offset; -		FILE *fp;  		double percent_max = 0.0;  		src_line->nr_pcnt = nr_pcnt; @@ -1135,23 +1167,9 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,  			goto next;  		offset = start + i; -		sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); -		fp = popen(cmd, "r"); -		if (!fp) -			goto next; - -		if (getline(&path, &line_len, fp) < 0 || !line_len) -			goto next_close; - -		src_line->path = malloc(sizeof(char) * line_len + 1); -		if (!src_line->path) -			goto next_close; - -		strcpy(src_line->path, path); +		src_line->path = get_srcline(map->dso, offset);  		insert_source_line(&tmp_root, src_line); -	next_close: -		pclose(fp);  	next:  		src_line = (void *)src_line + sizeof_src_line;  	} @@ -1192,7 +1210,7 @@ static void print_summary(struct rb_root *root, const char *filename)  		path = src_line->path;  		color = get_percent_color(percent_max); -		color_fprintf(stdout, color, " %s", path); +		color_fprintf(stdout, color, " %s\n", path);  		node = rb_next(node);  	} @@ -1218,6 +1236,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,  	struct dso *dso = map->dso;  	char *filename;  	const char *d_filename; +	const char *evsel_name = perf_evsel__name(evsel);  	struct annotation *notes = symbol__annotation(sym);  	struct disasm_line *pos, *queue = NULL;  	u64 start = map__rip_2objdump(map, sym->start); @@ -1225,7 +1244,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,  	int more = 0;  	u64 len;  	int width = 8; -	int namelen; +	int namelen, evsel_name_len, graph_dotted_len;  	filename = strdup(dso->long_name);  	if (!filename) @@ -1238,14 +1257,17 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,  	len = symbol__size(sym);  	namelen = strlen(d_filename); +	evsel_name_len = strlen(evsel_name);  	if (perf_evsel__is_group_event(evsel))  		width *= evsel->nr_members; -	printf(" %-*.*s|	Source code & Disassembly of %s\n", -	       width, width, "Percent", d_filename); -	printf("-%-*.*s-------------------------------------\n", -	       width+namelen, width+namelen, graph_dotted_line); +	printf(" %-*.*s|	Source code & Disassembly of %s for %s\n", +	       width, width, "Percent", d_filename, evsel_name); + +	graph_dotted_len = width + namelen + evsel_name_len; +	printf("-%-*.*s-----------------------------------------\n", +	       graph_dotted_len, graph_dotted_len, graph_dotted_line);  	if (verbose)  		symbol__annotate_hits(sym, evsel); @@ -1356,7 +1378,6 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map,  			 bool full_paths, int min_pcnt, int max_lines)  {  	struct dso *dso = map->dso; -	const char *filename = dso->long_name;  	struct rb_root source_line = RB_ROOT;  	u64 len; @@ -1366,9 +1387,8 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map,  	len = symbol__size(sym);  	if (print_lines) { -		symbol__get_source_line(sym, map, evsel, &source_line, -					len, filename); -		print_summary(&source_line, filename); +		symbol__get_source_line(sym, map, evsel, &source_line, len); +		print_summary(&source_line, dso->long_name);  	}  	symbol__annotate_printf(sym, map, evsel, full_paths, @@ -1380,3 +1400,13 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map,  	return 0;  } + +int hist_entry__annotate(struct hist_entry *he, size_t privsize) +{ +	return symbol__annotate(he->ms.sym, he->ms.map, privsize); +} + +bool ui__has_annotation(void) +{ +	return use_browser == 1 && sort__has_sym; +} diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index af755156d27..112d6e26815 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -3,7 +3,7 @@  #include <stdbool.h>  #include <stdint.h> -#include "types.h" +#include <linux/types.h>  #include "symbol.h"  #include "hist.h"  #include "sort.h" @@ -132,12 +132,17 @@ static inline struct annotation *symbol__annotation(struct symbol *sym)  	return &a->annotation;  } -int symbol__inc_addr_samples(struct symbol *sym, struct map *map, -			     int evidx, u64 addr); +int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx); + +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); +  int symbol__alloc_hist(struct symbol *sym);  void symbol__annotate_zero_histograms(struct symbol *sym);  int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); + +int hist_entry__annotate(struct hist_entry *he, size_t privsize); +  int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym);  int symbol__annotate_printf(struct symbol *sym, struct map *map,  			    struct perf_evsel *evsel, bool full_paths, @@ -146,11 +151,13 @@ void symbol__annotate_zero_histogram(struct symbol *sym, int evidx);  void symbol__annotate_decay_histogram(struct symbol *sym, int evidx);  void disasm__purge(struct list_head *head); +bool ui__has_annotation(void); +  int symbol__tty_annotate(struct symbol *sym, struct map *map,  			 struct perf_evsel *evsel, bool print_lines,  			 bool full_paths, int min_pcnt, int max_lines); -#ifdef SLANG_SUPPORT +#ifdef HAVE_SLANG_SUPPORT  int symbol__tui_annotate(struct symbol *sym, struct map *map,  			 struct perf_evsel *evsel,  			 struct hist_browser_timer *hbt); @@ -165,30 +172,6 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,  }  #endif -#ifdef GTK2_SUPPORT -int symbol__gtk_annotate(struct symbol *sym, struct map *map, -			 struct perf_evsel *evsel, -			 struct hist_browser_timer *hbt); - -static inline int hist_entry__gtk_annotate(struct hist_entry *he, -					   struct perf_evsel *evsel, -					   struct hist_browser_timer *hbt) -{ -	return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt); -} - -void perf_gtk__show_annotations(void); -#else -static inline int hist_entry__gtk_annotate(struct hist_entry *he __maybe_unused, -				struct perf_evsel *evsel __maybe_unused, -				struct hist_browser_timer *hbt __maybe_unused) -{ -	return 0; -} - -static inline void perf_gtk__show_annotations(void) {} -#endif -  extern const char	*disassembler_style;  #endif	/* __PERF_ANNOTATE_H */ diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 7ded71d19d7..a904a4cfe7d 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -25,7 +25,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,  	struct addr_location al;  	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;  	struct thread *thread = machine__findnew_thread(machine, sample->pid, -							sample->pid); +							sample->tid);  	if (thread == NULL) {  		pr_err("problem processing %d event, skipping it.\n", @@ -89,14 +89,14 @@ int build_id__sprintf(const u8 *build_id, int len, char *bf)  	return raw - build_id;  } -char *dso__build_id_filename(struct dso *self, char *bf, size_t size) +char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)  {  	char build_id_hex[BUILD_ID_SIZE * 2 + 1]; -	if (!self->has_build_id) +	if (!dso->has_build_id)  		return NULL; -	build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); +	build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex);  	if (bf == NULL) {  		if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir,  			     build_id_hex, build_id_hex + 2) < 0) diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index a811f5c62e1..ae392561470 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -4,16 +4,15 @@  #define BUILD_ID_SIZE 20  #include "tool.h" -#include "types.h" +#include <linux/types.h>  extern struct perf_tool build_id__mark_dso_hit_ops;  struct dso;  int build_id__sprintf(const u8 *build_id, int len, char *bf); -char *dso__build_id_filename(struct dso *self, char *bf, size_t size); +char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size);  int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event,  			   struct perf_sample *sample, struct perf_evsel *evsel,  			   struct machine *machine); -  #endif diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 26e36723987..7b176dd02e1 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -70,8 +70,7 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2  extern char *perf_pathdup(const char *fmt, ...)  	__attribute__((format (printf, 1, 2))); -#ifndef HAVE_STRLCPY +/* Matches the libc/libbsd function attribute so we declare this unconditionally: */  extern size_t strlcpy(char *dest, const char *src, size_t size); -#endif  #endif /* __PERF_CACHE_H */ diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 482f68081cd..48b6d3f5001 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -15,17 +15,93 @@  #include <errno.h>  #include <math.h> +#include "asm/bug.h" +  #include "hist.h"  #include "util.h" +#include "sort.h" +#include "machine.h"  #include "callchain.h"  __thread struct callchain_cursor callchain_cursor; -#define chain_for_each_child(child, parent)	\ -	list_for_each_entry(child, &parent->children, siblings) +int +parse_callchain_report_opt(const char *arg) +{ +	char *tok, *tok2; +	char *endptr; + +	symbol_conf.use_callchain = true; + +	if (!arg) +		return 0; + +	tok = strtok((char *)arg, ","); +	if (!tok) +		return -1; -#define chain_for_each_child_safe(child, next, parent)	\ -	list_for_each_entry_safe(child, next, &parent->children, siblings) +	/* get the output mode */ +	if (!strncmp(tok, "graph", strlen(arg))) { +		callchain_param.mode = CHAIN_GRAPH_ABS; + +	} else if (!strncmp(tok, "flat", strlen(arg))) { +		callchain_param.mode = CHAIN_FLAT; +	} else if (!strncmp(tok, "fractal", strlen(arg))) { +		callchain_param.mode = CHAIN_GRAPH_REL; +	} else if (!strncmp(tok, "none", strlen(arg))) { +		callchain_param.mode = CHAIN_NONE; +		symbol_conf.use_callchain = false; +		return 0; +	} else { +		return -1; +	} + +	/* get the min percentage */ +	tok = strtok(NULL, ","); +	if (!tok) +		goto setup; + +	callchain_param.min_percent = strtod(tok, &endptr); +	if (tok == endptr) +		return -1; + +	/* get the print limit */ +	tok2 = strtok(NULL, ","); +	if (!tok2) +		goto setup; + +	if (tok2[0] != 'c') { +		callchain_param.print_limit = strtoul(tok2, &endptr, 0); +		tok2 = strtok(NULL, ","); +		if (!tok2) +			goto setup; +	} + +	/* get the call chain order */ +	if (!strncmp(tok2, "caller", strlen("caller"))) +		callchain_param.order = ORDER_CALLER; +	else if (!strncmp(tok2, "callee", strlen("callee"))) +		callchain_param.order = ORDER_CALLEE; +	else +		return -1; + +	/* Get the sort key */ +	tok2 = strtok(NULL, ","); +	if (!tok2) +		goto setup; +	if (!strncmp(tok2, "function", strlen("function"))) +		callchain_param.key = CCKEY_FUNCTION; +	else if (!strncmp(tok2, "address", strlen("address"))) +		callchain_param.key = CCKEY_ADDRESS; +	else +		return -1; +setup: +	if (callchain_register_param(&callchain_param) < 0) { +		pr_err("Can't register callchain params\n"); +		return -1; +	} +	return 0; +}  static void  rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, @@ -71,10 +147,16 @@ static void  __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,  		  u64 min_hit)  { +	struct rb_node *n;  	struct callchain_node *child; -	chain_for_each_child(child, node) +	n = rb_first(&node->rb_root_in); +	while (n) { +		child = rb_entry(n, struct callchain_node, rb_node_in); +		n = rb_next(n); +  		__sort_chain_flat(rb_root, child, min_hit); +	}  	if (node->hit && node->hit >= min_hit)  		rb_insert_callchain(rb_root, node, CHAIN_FLAT); @@ -94,11 +176,16 @@ sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,  static void __sort_chain_graph_abs(struct callchain_node *node,  				   u64 min_hit)  { +	struct rb_node *n;  	struct callchain_node *child;  	node->rb_root = RB_ROOT; +	n = rb_first(&node->rb_root_in); + +	while (n) { +		child = rb_entry(n, struct callchain_node, rb_node_in); +		n = rb_next(n); -	chain_for_each_child(child, node) {  		__sort_chain_graph_abs(child, min_hit);  		if (callchain_cumul_hits(child) >= min_hit)  			rb_insert_callchain(&node->rb_root, child, @@ -117,13 +204,18 @@ sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,  static void __sort_chain_graph_rel(struct callchain_node *node,  				   double min_percent)  { +	struct rb_node *n;  	struct callchain_node *child;  	u64 min_hit;  	node->rb_root = RB_ROOT;  	min_hit = ceil(node->children_hit * min_percent); -	chain_for_each_child(child, node) { +	n = rb_first(&node->rb_root_in); +	while (n) { +		child = rb_entry(n, struct callchain_node, rb_node_in); +		n = rb_next(n); +  		__sort_chain_graph_rel(child, min_percent);  		if (callchain_cumul_hits(child) >= min_hit)  			rb_insert_callchain(&node->rb_root, child, @@ -173,19 +265,26 @@ create_child(struct callchain_node *parent, bool inherit_children)  		return NULL;  	}  	new->parent = parent; -	INIT_LIST_HEAD(&new->children);  	INIT_LIST_HEAD(&new->val);  	if (inherit_children) { -		struct callchain_node *next; +		struct rb_node *n; +		struct callchain_node *child; + +		new->rb_root_in = parent->rb_root_in; +		parent->rb_root_in = RB_ROOT; -		list_splice(&parent->children, &new->children); -		INIT_LIST_HEAD(&parent->children); +		n = rb_first(&new->rb_root_in); +		while (n) { +			child = rb_entry(n, struct callchain_node, rb_node_in); +			child->parent = new; +			n = rb_next(n); +		} -		chain_for_each_child(next, new) -			next->parent = new; +		/* make it the first child */ +		rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node); +		rb_insert_color(&new->rb_node_in, &parent->rb_root_in);  	} -	list_add_tail(&new->siblings, &parent->children);  	return new;  } @@ -223,7 +322,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)  	}  } -static void +static struct callchain_node *  add_child(struct callchain_node *parent,  	  struct callchain_cursor *cursor,  	  u64 period) @@ -235,6 +334,19 @@ add_child(struct callchain_node *parent,  	new->children_hit = 0;  	new->hit = period; +	return new; +} + +static s64 match_chain(struct callchain_cursor_node *node, +		      struct callchain_list *cnode) +{ +	struct symbol *sym = node->sym; + +	if (cnode->ms.sym && sym && +	    callchain_param.key == CCKEY_FUNCTION) +		return cnode->ms.sym->start - sym->start; +	else +		return cnode->ip - node->ip;  }  /* @@ -272,9 +384,33 @@ split_add_child(struct callchain_node *parent,  	/* create a new child for the new branch if any */  	if (idx_total < cursor->nr) { +		struct callchain_node *first; +		struct callchain_list *cnode; +		struct callchain_cursor_node *node; +		struct rb_node *p, **pp; +  		parent->hit = 0; -		add_child(parent, cursor, period);  		parent->children_hit += period; + +		node = callchain_cursor_current(cursor); +		new = add_child(parent, cursor, period); + +		/* +		 * This is second child since we moved parent's children +		 * to new (first) child above. +		 */ +		p = parent->rb_root_in.rb_node; +		first = rb_entry(p, struct callchain_node, rb_node_in); +		cnode = list_first_entry(&first->val, struct callchain_list, +					 list); + +		if (match_chain(node, cnode) < 0) +			pp = &p->rb_left; +		else +			pp = &p->rb_right; + +		rb_link_node(&new->rb_node_in, p, pp); +		rb_insert_color(&new->rb_node_in, &parent->rb_root_in);  	} else {  		parent->hit = period;  	} @@ -291,16 +427,35 @@ append_chain_children(struct callchain_node *root,  		      u64 period)  {  	struct callchain_node *rnode; +	struct callchain_cursor_node *node; +	struct rb_node **p = &root->rb_root_in.rb_node; +	struct rb_node *parent = NULL; + +	node = callchain_cursor_current(cursor); +	if (!node) +		return;  	/* lookup in childrens */ -	chain_for_each_child(rnode, root) { -		unsigned int ret = append_chain(rnode, cursor, period); +	while (*p) { +		s64 ret; + +		parent = *p; +		rnode = rb_entry(parent, struct callchain_node, rb_node_in); -		if (!ret) +		/* If at least first entry matches, rely to children */ +		ret = append_chain(rnode, cursor, period); +		if (ret == 0)  			goto inc_children_hit; + +		if (ret < 0) +			p = &parent->rb_left; +		else +			p = &parent->rb_right;  	}  	/* nothing in children, add to the current node */ -	add_child(root, cursor, period); +	rnode = add_child(root, cursor, period); +	rb_link_node(&rnode->rb_node_in, parent, p); +	rb_insert_color(&rnode->rb_node_in, &root->rb_root_in);  inc_children_hit:  	root->children_hit += period; @@ -311,11 +466,11 @@ append_chain(struct callchain_node *root,  	     struct callchain_cursor *cursor,  	     u64 period)  { -	struct callchain_cursor_node *curr_snap = cursor->curr;  	struct callchain_list *cnode;  	u64 start = cursor->pos;  	bool found = false;  	u64 matches; +	int cmp = 0;  	/*  	 * Lookup in the current node @@ -325,32 +480,24 @@ append_chain(struct callchain_node *root,  	 */  	list_for_each_entry(cnode, &root->val, list) {  		struct callchain_cursor_node *node; -		struct symbol *sym;  		node = callchain_cursor_current(cursor);  		if (!node)  			break; -		sym = node->sym; - -		if (cnode->ms.sym && sym && -		    callchain_param.key == CCKEY_FUNCTION) { -			if (cnode->ms.sym->start != sym->start) -				break; -		} else if (cnode->ip != node->ip) +		cmp = match_chain(node, cnode); +		if (cmp)  			break; -		if (!found) -			found = true; +		found = true;  		callchain_cursor_advance(cursor);  	} -	/* matches not, relay on the parent */ +	/* matches not, relay no the parent */  	if (!found) { -		cursor->curr = curr_snap; -		cursor->pos = start; -		return -1; +		WARN_ONCE(!cmp, "Chain comparison error\n"); +		return cmp;  	}  	matches = cursor->pos - start; @@ -395,8 +542,9 @@ merge_chain_branch(struct callchain_cursor *cursor,  		   struct callchain_node *dst, struct callchain_node *src)  {  	struct callchain_cursor_node **old_last = cursor->last; -	struct callchain_node *child, *next_child; +	struct callchain_node *child;  	struct callchain_list *list, *next_list; +	struct rb_node *n;  	int old_pos = cursor->nr;  	int err = 0; @@ -412,12 +560,16 @@ merge_chain_branch(struct callchain_cursor *cursor,  		append_chain_children(dst, cursor, src->hit);  	} -	chain_for_each_child_safe(child, next_child, src) { +	n = rb_first(&src->rb_root_in); +	while (n) { +		child = container_of(n, struct callchain_node, rb_node_in); +		n = rb_next(n); +		rb_erase(&child->rb_node_in, &src->rb_root_in); +  		err = merge_chain_branch(cursor, dst, child);  		if (err)  			break; -		list_del(&child->siblings);  		free(child);  	} @@ -456,3 +608,67 @@ int callchain_cursor_append(struct callchain_cursor *cursor,  	return 0;  } + +int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, +			      struct perf_evsel *evsel, struct addr_location *al, +			      int max_stack) +{ +	if (sample->callchain == NULL) +		return 0; + +	if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || +	    sort__has_parent) { +		return machine__resolve_callchain(al->machine, evsel, al->thread, +						  sample, parent, al, max_stack); +	} +	return 0; +} + +int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample) +{ +	if (!symbol_conf.use_callchain) +		return 0; +	return callchain_append(he->callchain, &callchain_cursor, sample->period); +} + +int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node, +			bool hide_unresolved) +{ +	al->map = node->map; +	al->sym = node->sym; +	if (node->map) +		al->addr = node->map->map_ip(node->map, node->ip); +	else +		al->addr = node->ip; + +	if (al->sym == NULL) { +		if (hide_unresolved) +			return 0; +		if (al->map == NULL) +			goto out; +	} + +	if (al->map->groups == &al->machine->kmaps) { +		if (machine__is_host(al->machine)) { +			al->cpumode = PERF_RECORD_MISC_KERNEL; +			al->level = 'k'; +		} else { +			al->cpumode = PERF_RECORD_MISC_GUEST_KERNEL; +			al->level = 'g'; +		} +	} else { +		if (machine__is_host(al->machine)) { +			al->cpumode = PERF_RECORD_MISC_USER; +			al->level = '.'; +		} else if (perf_guest) { +			al->cpumode = PERF_RECORD_MISC_GUEST_USER; +			al->level = 'u'; +		} else { +			al->cpumode = PERF_RECORD_MISC_HYPERVISOR; +			al->level = 'H'; +		} +	} + +out: +	return 1; +} diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 2b585bc308c..8f84423a75d 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -7,6 +7,13 @@  #include "event.h"  #include "symbol.h" +enum perf_call_graph_mode { +	CALLCHAIN_NONE, +	CALLCHAIN_FP, +	CALLCHAIN_DWARF, +	CALLCHAIN_MAX +}; +  enum chain_mode {  	CHAIN_NONE,  	CHAIN_FLAT, @@ -21,11 +28,11 @@ enum chain_order {  struct callchain_node {  	struct callchain_node	*parent; -	struct list_head	siblings; -	struct list_head	children;  	struct list_head	val; -	struct rb_node		rb_node; /* to sort nodes in an rbtree */ -	struct rb_root		rb_root; /* sorted tree of children */ +	struct rb_node		rb_node_in; /* to insert nodes in an rbtree */ +	struct rb_node		rb_node;    /* to sort nodes in an output tree */ +	struct rb_root		rb_root_in; /* input tree of children */ +	struct rb_root		rb_root;    /* sorted output tree of children */  	unsigned int		val_nr;  	u64			hit;  	u64			children_hit; @@ -86,13 +93,12 @@ extern __thread struct callchain_cursor callchain_cursor;  static inline void callchain_init(struct callchain_root *root)  { -	INIT_LIST_HEAD(&root->node.siblings); -	INIT_LIST_HEAD(&root->node.children);  	INIT_LIST_HEAD(&root->node.val);  	root->node.parent = NULL;  	root->node.hit = 0;  	root->node.children_hit = 0; +	root->node.rb_root_in = RB_ROOT;  	root->max_depth = 0;  } @@ -146,7 +152,28 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor)  }  struct option; +struct hist_entry; +int record_parse_callchain(const char *arg, struct record_opts *opts);  int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); +int record_callchain_opt(const struct option *opt, const char *arg, int unset); + +int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, +			      struct perf_evsel *evsel, struct addr_location *al, +			      int max_stack); +int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); +int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node, +			bool hide_unresolved); +  extern const char record_callchain_help[]; +int parse_callchain_report_opt(const char *arg); + +static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, +					     struct callchain_cursor *src) +{ +	*dest = *src; + +	dest->first = src->curr; +	dest->nr -= src->pos; +}  #endif	/* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 96bbda1ddb8..88f7be39943 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -81,7 +81,7 @@ static int add_cgroup(struct perf_evlist *evlist, char *str)  	/*  	 * check if cgrp is already defined, if so we reuse it  	 */ -	list_for_each_entry(counter, &evlist->entries, node) { +	evlist__for_each(evlist, counter) {  		cgrp = counter->cgrp;  		if (!cgrp)  			continue; @@ -110,7 +110,7 @@ static int add_cgroup(struct perf_evlist *evlist, char *str)  	 * if add cgroup N, then need to find event N  	 */  	n = 0; -	list_for_each_entry(counter, &evlist->entries, node) { +	evlist__for_each(evlist, counter) {  		if (n == nr_cgroups)  			goto found;  		n++; @@ -133,7 +133,7 @@ void close_cgroup(struct cgroup_sel *cgrp)  	/* XXX: not reentrant */  	if (--cgrp->refcnt == 0) {  		close(cgrp->fd); -		free(cgrp->name); +		zfree(&cgrp->name);  		free(cgrp);  	}  } diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 11e46da17bb..87b8672eb41 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -1,6 +1,7 @@  #include <linux/kernel.h>  #include "cache.h"  #include "color.h" +#include <math.h>  int perf_use_color_default = -1; @@ -298,10 +299,10 @@ const char *get_percent_color(double percent)  	 * entries in green - and keep the low overhead places  	 * normal:  	 */ -	if (percent >= MIN_RED) +	if (fabs(percent) >= MIN_RED)  		color = PERF_COLOR_RED;  	else { -		if (percent > MIN_GREEN) +		if (fabs(percent) > MIN_GREEN)  			color = PERF_COLOR_GREEN;  	}  	return color; @@ -318,8 +319,19 @@ int percent_color_fprintf(FILE *fp, const char *fmt, double percent)  	return r;  } -int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent) +int value_color_snprintf(char *bf, size_t size, const char *fmt, double value)  { -	const char *color = get_percent_color(percent); -	return color_snprintf(bf, size, color, fmt, percent); +	const char *color = get_percent_color(value); +	return color_snprintf(bf, size, color, fmt, value); +} + +int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) +{ +	va_list args; +	double percent; + +	va_start(args, fmt); +	percent = va_arg(args, double); +	va_end(args); +	return value_color_snprintf(bf, size, fmt, percent);  } diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index dea082b7960..7ff30a62a13 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -39,7 +39,8 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);  int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...);  int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);  int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); -int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent); +int value_color_snprintf(char *bf, size_t size, const char *fmt, double value); +int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...);  int percent_color_fprintf(FILE *fp, const char *fmt, double percent);  const char *get_percent_color(double percent); diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c new file mode 100644 index 00000000000..f9e777629e2 --- /dev/null +++ b/tools/perf/util/comm.c @@ -0,0 +1,122 @@ +#include "comm.h" +#include "util.h" +#include <stdlib.h> +#include <stdio.h> + +struct comm_str { +	char *str; +	struct rb_node rb_node; +	int ref; +}; + +/* Should perhaps be moved to struct machine */ +static struct rb_root comm_str_root; + +static void comm_str__get(struct comm_str *cs) +{ +	cs->ref++; +} + +static void comm_str__put(struct comm_str *cs) +{ +	if (!--cs->ref) { +		rb_erase(&cs->rb_node, &comm_str_root); +		zfree(&cs->str); +		free(cs); +	} +} + +static struct comm_str *comm_str__alloc(const char *str) +{ +	struct comm_str *cs; + +	cs = zalloc(sizeof(*cs)); +	if (!cs) +		return NULL; + +	cs->str = strdup(str); +	if (!cs->str) { +		free(cs); +		return NULL; +	} + +	return cs; +} + +static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root) +{ +	struct rb_node **p = &root->rb_node; +	struct rb_node *parent = NULL; +	struct comm_str *iter, *new; +	int cmp; + +	while (*p != NULL) { +		parent = *p; +		iter = rb_entry(parent, struct comm_str, rb_node); + +		cmp = strcmp(str, iter->str); +		if (!cmp) +			return iter; + +		if (cmp < 0) +			p = &(*p)->rb_left; +		else +			p = &(*p)->rb_right; +	} + +	new = comm_str__alloc(str); +	if (!new) +		return NULL; + +	rb_link_node(&new->rb_node, parent, p); +	rb_insert_color(&new->rb_node, root); + +	return new; +} + +struct comm *comm__new(const char *str, u64 timestamp) +{ +	struct comm *comm = zalloc(sizeof(*comm)); + +	if (!comm) +		return NULL; + +	comm->start = timestamp; + +	comm->comm_str = comm_str__findnew(str, &comm_str_root); +	if (!comm->comm_str) { +		free(comm); +		return NULL; +	} + +	comm_str__get(comm->comm_str); + +	return comm; +} + +int comm__override(struct comm *comm, const char *str, u64 timestamp) +{ +	struct comm_str *new, *old = comm->comm_str; + +	new = comm_str__findnew(str, &comm_str_root); +	if (!new) +		return -ENOMEM; + +	comm_str__get(new); +	comm_str__put(old); +	comm->comm_str = new; +	comm->start = timestamp; + +	return 0; +} + +void comm__free(struct comm *comm) +{ +	comm_str__put(comm->comm_str); +	free(comm); +} + +const char *comm__str(const struct comm *comm) +{ +	return comm->comm_str->str; +} diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h new file mode 100644 index 00000000000..fac5bd51bef --- /dev/null +++ b/tools/perf/util/comm.h @@ -0,0 +1,21 @@ +#ifndef __PERF_COMM_H +#define __PERF_COMM_H + +#include "../perf.h" +#include <linux/rbtree.h> +#include <linux/list.h> + +struct comm_str; + +struct comm { +	struct comm_str *comm_str; +	u64 start; +	struct list_head list; +}; + +void comm__free(struct comm *comm); +struct comm *comm__new(const char *str, u64 timestamp); +const char *comm__str(const struct comm *comm); +int comm__override(struct comm *comm, const char *str, u64 timestamp); + +#endif  /* __PERF_COMM_H */ diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 3e0fdd369cc..24519e14ac5 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -11,6 +11,7 @@  #include "util.h"  #include "cache.h"  #include "exec_cmd.h" +#include "util/hist.h"  /* perf_hist_config */  #define MAXNAME (256) @@ -355,6 +356,9 @@ int perf_default_config(const char *var, const char *value,  	if (!prefixcmp(var, "core."))  		return perf_default_core_config(var, value); +	if (!prefixcmp(var, "hist.")) +		return perf_hist_config(var, value); +  	/* Add other config variables here. */  	return 0;  } diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index beb8cf9f997..c4e55b71010 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -1,5 +1,5 @@  #include "util.h" -#include "sysfs.h" +#include <api/fs/fs.h>  #include "../perf.h"  #include "cpumap.h"  #include <assert.h> @@ -216,7 +216,7 @@ int cpu_map__get_socket(struct cpu_map *map, int idx)  	cpu = map->map[idx]; -	mnt = sysfs_find_mountpoint(); +	mnt = sysfs__mountpoint();  	if (!mnt)  		return -1; @@ -279,7 +279,7 @@ int cpu_map__get_core(struct cpu_map *map, int idx)  	cpu = map->map[idx]; -	mnt = sysfs_find_mountpoint(); +	mnt = sysfs__mountpoint();  	if (!mnt)  		return -1; @@ -317,3 +317,163 @@ int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep)  {  	return cpu_map__build_map(cpus, corep, cpu_map__get_core);  } + +/* setup simple routines to easily access node numbers given a cpu number */ +static int get_max_num(char *path, int *max) +{ +	size_t num; +	char *buf; +	int err = 0; + +	if (filename__read_str(path, &buf, &num)) +		return -1; + +	buf[num] = '\0'; + +	/* start on the right, to find highest node num */ +	while (--num) { +		if ((buf[num] == ',') || (buf[num] == '-')) { +			num++; +			break; +		} +	} +	if (sscanf(&buf[num], "%d", max) < 1) { +		err = -1; +		goto out; +	} + +	/* convert from 0-based to 1-based */ +	(*max)++; + +out: +	free(buf); +	return err; +} + +/* Determine highest possible cpu in the system for sparse allocation */ +static void set_max_cpu_num(void) +{ +	const char *mnt; +	char path[PATH_MAX]; +	int ret = -1; + +	/* set up default */ +	max_cpu_num = 4096; + +	mnt = sysfs__mountpoint(); +	if (!mnt) +		goto out; + +	/* get the highest possible cpu number for a sparse allocation */ +	ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/possible", mnt); +	if (ret == PATH_MAX) { +		pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); +		goto out; +	} + +	ret = get_max_num(path, &max_cpu_num); + +out: +	if (ret) +		pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num); +} + +/* Determine highest possible node in the system for sparse allocation */ +static void set_max_node_num(void) +{ +	const char *mnt; +	char path[PATH_MAX]; +	int ret = -1; + +	/* set up default */ +	max_node_num = 8; + +	mnt = sysfs__mountpoint(); +	if (!mnt) +		goto out; + +	/* get the highest possible cpu number for a sparse allocation */ +	ret = snprintf(path, PATH_MAX, "%s/devices/system/node/possible", mnt); +	if (ret == PATH_MAX) { +		pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); +		goto out; +	} + +	ret = get_max_num(path, &max_node_num); + +out: +	if (ret) +		pr_err("Failed to read max nodes, using default of %d\n", max_node_num); +} + +static int init_cpunode_map(void) +{ +	int i; + +	set_max_cpu_num(); +	set_max_node_num(); + +	cpunode_map = calloc(max_cpu_num, sizeof(int)); +	if (!cpunode_map) { +		pr_err("%s: calloc failed\n", __func__); +		return -1; +	} + +	for (i = 0; i < max_cpu_num; i++) +		cpunode_map[i] = -1; + +	return 0; +} + +int cpu__setup_cpunode_map(void) +{ +	struct dirent *dent1, *dent2; +	DIR *dir1, *dir2; +	unsigned int cpu, mem; +	char buf[PATH_MAX]; +	char path[PATH_MAX]; +	const char *mnt; +	int n; + +	/* initialize globals */ +	if (init_cpunode_map()) +		return -1; + +	mnt = sysfs__mountpoint(); +	if (!mnt) +		return 0; + +	n = snprintf(path, PATH_MAX, "%s/devices/system/node", mnt); +	if (n == PATH_MAX) { +		pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); +		return -1; +	} + +	dir1 = opendir(path); +	if (!dir1) +		return 0; + +	/* walk tree and setup map */ +	while ((dent1 = readdir(dir1)) != NULL) { +		if (dent1->d_type != DT_DIR || sscanf(dent1->d_name, "node%u", &mem) < 1) +			continue; + +		n = snprintf(buf, PATH_MAX, "%s/%s", path, dent1->d_name); +		if (n == PATH_MAX) { +			pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); +			continue; +		} + +		dir2 = opendir(buf); +		if (!dir2) +			continue; +		while ((dent2 = readdir(dir2)) != NULL) { +			if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1) +				continue; +			cpunode_map[cpu] = mem; +		} +		closedir(dir2); +	} +	closedir(dir1); +	return 0; +} diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index b123bb9d6f5..61a65484900 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -4,6 +4,9 @@  #include <stdio.h>  #include <stdbool.h> +#include "perf.h" +#include "util/debug.h" +  struct cpu_map {  	int nr;  	int map[]; @@ -46,4 +49,36 @@ static inline bool cpu_map__empty(const struct cpu_map *map)  	return map ? map->map[0] == -1 : true;  } +int max_cpu_num; +int max_node_num; +int *cpunode_map; + +int cpu__setup_cpunode_map(void); + +static inline int cpu__max_node(void) +{ +	if (unlikely(!max_node_num)) +		pr_debug("cpu_map not initialized\n"); + +	return max_node_num; +} + +static inline int cpu__max_cpu(void) +{ +	if (unlikely(!max_cpu_num)) +		pr_debug("cpu_map not initialized\n"); + +	return max_cpu_num; +} + +static inline int cpu__get_node(int cpu) +{ +	if (unlikely(cpunode_map == NULL)) { +		pr_debug("cpu_map not initialized\n"); +		return -1; +	} + +	return cpunode_map[cpu]; +} +  #endif /* __PERF_CPUMAP_H */ diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c new file mode 100644 index 00000000000..55de44ecebe --- /dev/null +++ b/tools/perf/util/data.c @@ -0,0 +1,133 @@ +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> + +#include "data.h" +#include "util.h" + +static bool check_pipe(struct perf_data_file *file) +{ +	struct stat st; +	bool is_pipe = false; +	int fd = perf_data_file__is_read(file) ? +		 STDIN_FILENO : STDOUT_FILENO; + +	if (!file->path) { +		if (!fstat(fd, &st) && S_ISFIFO(st.st_mode)) +			is_pipe = true; +	} else { +		if (!strcmp(file->path, "-")) +			is_pipe = true; +	} + +	if (is_pipe) +		file->fd = fd; + +	return file->is_pipe = is_pipe; +} + +static int check_backup(struct perf_data_file *file) +{ +	struct stat st; + +	if (!stat(file->path, &st) && st.st_size) { +		/* TODO check errors properly */ +		char oldname[PATH_MAX]; +		snprintf(oldname, sizeof(oldname), "%s.old", +			 file->path); +		unlink(oldname); +		rename(file->path, oldname); +	} + +	return 0; +} + +static int open_file_read(struct perf_data_file *file) +{ +	struct stat st; +	int fd; + +	fd = open(file->path, O_RDONLY); +	if (fd < 0) { +		int err = errno; + +		pr_err("failed to open %s: %s", file->path, strerror(err)); +		if (err == ENOENT && !strcmp(file->path, "perf.data")) +			pr_err("  (try 'perf record' first)"); +		pr_err("\n"); +		return -err; +	} + +	if (fstat(fd, &st) < 0) +		goto out_close; + +	if (!file->force && st.st_uid && (st.st_uid != geteuid())) { +		pr_err("file %s not owned by current user or root\n", +		       file->path); +		goto out_close; +	} + +	if (!st.st_size) { +		pr_info("zero-sized file (%s), nothing to do!\n", +			file->path); +		goto out_close; +	} + +	file->size = st.st_size; +	return fd; + + out_close: +	close(fd); +	return -1; +} + +static int open_file_write(struct perf_data_file *file) +{ +	int fd; + +	if (check_backup(file)) +		return -1; + +	fd = open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); + +	if (fd < 0) +		pr_err("failed to open %s : %s\n", file->path, strerror(errno)); + +	return fd; +} + +static int open_file(struct perf_data_file *file) +{ +	int fd; + +	fd = perf_data_file__is_read(file) ? +	     open_file_read(file) : open_file_write(file); + +	file->fd = fd; +	return fd < 0 ? -1 : 0; +} + +int perf_data_file__open(struct perf_data_file *file) +{ +	if (check_pipe(file)) +		return 0; + +	if (!file->path) +		file->path = "perf.data"; + +	return open_file(file); +} + +void perf_data_file__close(struct perf_data_file *file) +{ +	close(file->fd); +} + +ssize_t perf_data_file__write(struct perf_data_file *file, +			      void *buf, size_t size) +{ +	return writen(file->fd, buf, size); +} diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h new file mode 100644 index 00000000000..2b15d0c95c7 --- /dev/null +++ b/tools/perf/util/data.h @@ -0,0 +1,50 @@ +#ifndef __PERF_DATA_H +#define __PERF_DATA_H + +#include <stdbool.h> + +enum perf_data_mode { +	PERF_DATA_MODE_WRITE, +	PERF_DATA_MODE_READ, +}; + +struct perf_data_file { +	const char		*path; +	int			 fd; +	bool			 is_pipe; +	bool			 force; +	unsigned long		 size; +	enum perf_data_mode	 mode; +}; + +static inline bool perf_data_file__is_read(struct perf_data_file *file) +{ +	return file->mode == PERF_DATA_MODE_READ; +} + +static inline bool perf_data_file__is_write(struct perf_data_file *file) +{ +	return file->mode == PERF_DATA_MODE_WRITE; +} + +static inline int perf_data_file__is_pipe(struct perf_data_file *file) +{ +	return file->is_pipe; +} + +static inline int perf_data_file__fd(struct perf_data_file *file) +{ +	return file->fd; +} + +static inline unsigned long perf_data_file__size(struct perf_data_file *file) +{ +	return file->size; +} + +int perf_data_file__open(struct perf_data_file *file); +void perf_data_file__close(struct perf_data_file *file); +ssize_t perf_data_file__write(struct perf_data_file *file, +			      void *buf, size_t size); + +#endif /* __PERF_DATA_H */ diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 399e74c34c1..299b5558650 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -16,23 +16,46 @@  int verbose;  bool dump_trace = false, quiet = false; -int eprintf(int level, const char *fmt, ...) +static int _eprintf(int level, const char *fmt, va_list args)  { -	va_list args;  	int ret = 0;  	if (verbose >= level) { -		va_start(args, fmt);  		if (use_browser >= 1)  			ui_helpline__vshow(fmt, args);  		else  			ret = vfprintf(stderr, fmt, args); -		va_end(args);  	}  	return ret;  } +int eprintf(int level, const char *fmt, ...) +{ +	va_list args; +	int ret; + +	va_start(args, fmt); +	ret = _eprintf(level, fmt, args); +	va_end(args); + +	return ret; +} + +/* + * Overloading libtraceevent standard info print + * function, display with -v in perf. + */ +void pr_stat(const char *fmt, ...) +{ +	va_list args; + +	va_start(args, fmt); +	_eprintf(1, fmt, args); +	va_end(args); +	eprintf(1, "\n"); +} +  int dump_printf(const char *fmt, ...)  {  	va_list args; diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index efbd98805ad..443694c36b0 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -17,4 +17,6 @@ void trace_event(union perf_event *event);  int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));  int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); +void pr_stat(const char *fmt, ...); +  #endif	/* __PERF_DEBUG_H */ diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index e3c1ff8512c..819f10414f0 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1,3 +1,6 @@ +#include <asm/bug.h> +#include <sys/time.h> +#include <sys/resource.h>  #include "symbol.h"  #include "dso.h"  #include "machine.h" @@ -7,19 +10,20 @@  char dso__symtab_origin(const struct dso *dso)  {  	static const char origin[] = { -		[DSO_BINARY_TYPE__KALLSYMS]		= 'k', -		[DSO_BINARY_TYPE__VMLINUX]		= 'v', -		[DSO_BINARY_TYPE__JAVA_JIT]		= 'j', -		[DSO_BINARY_TYPE__DEBUGLINK]		= 'l', -		[DSO_BINARY_TYPE__BUILD_ID_CACHE]	= 'B', -		[DSO_BINARY_TYPE__FEDORA_DEBUGINFO]	= 'f', -		[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO]	= 'u', -		[DSO_BINARY_TYPE__BUILDID_DEBUGINFO]	= 'b', -		[DSO_BINARY_TYPE__SYSTEM_PATH_DSO]	= 'd', -		[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE]	= 'K', -		[DSO_BINARY_TYPE__GUEST_KALLSYMS]	= 'g', -		[DSO_BINARY_TYPE__GUEST_KMODULE]	= 'G', -		[DSO_BINARY_TYPE__GUEST_VMLINUX]	= 'V', +		[DSO_BINARY_TYPE__KALLSYMS]			= 'k', +		[DSO_BINARY_TYPE__VMLINUX]			= 'v', +		[DSO_BINARY_TYPE__JAVA_JIT]			= 'j', +		[DSO_BINARY_TYPE__DEBUGLINK]			= 'l', +		[DSO_BINARY_TYPE__BUILD_ID_CACHE]		= 'B', +		[DSO_BINARY_TYPE__FEDORA_DEBUGINFO]		= 'f', +		[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO]		= 'u', +		[DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO]	= 'o', +		[DSO_BINARY_TYPE__BUILDID_DEBUGINFO]		= 'b', +		[DSO_BINARY_TYPE__SYSTEM_PATH_DSO]		= 'd', +		[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE]		= 'K', +		[DSO_BINARY_TYPE__GUEST_KALLSYMS]		= 'g', +		[DSO_BINARY_TYPE__GUEST_KMODULE]		= 'G', +		[DSO_BINARY_TYPE__GUEST_VMLINUX]		= 'V',  	};  	if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) @@ -27,8 +31,9 @@ char dso__symtab_origin(const struct dso *dso)  	return origin[dso->symtab_type];  } -int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, -			  char *root_dir, char *file, size_t size) +int dso__read_binary_type_filename(const struct dso *dso, +				   enum dso_binary_type type, +				   char *root_dir, char *filename, size_t size)  {  	char build_id_hex[BUILD_ID_SIZE * 2 + 1];  	int ret = 0; @@ -37,33 +42,55 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,  	case DSO_BINARY_TYPE__DEBUGLINK: {  		char *debuglink; -		strncpy(file, dso->long_name, size); -		debuglink = file + dso->long_name_len; -		while (debuglink != file && *debuglink != '/') +		strncpy(filename, dso->long_name, size); +		debuglink = filename + dso->long_name_len; +		while (debuglink != filename && *debuglink != '/')  			debuglink--;  		if (*debuglink == '/')  			debuglink++; -		filename__read_debuglink(dso->long_name, debuglink, -					 size - (debuglink - file)); +		ret = filename__read_debuglink(dso->long_name, debuglink, +					       size - (debuglink - filename));  		}  		break;  	case DSO_BINARY_TYPE__BUILD_ID_CACHE:  		/* skip the locally configured cache if a symfs is given */  		if (symbol_conf.symfs[0] || -		    (dso__build_id_filename(dso, file, size) == NULL)) +		    (dso__build_id_filename(dso, filename, size) == NULL))  			ret = -1;  		break;  	case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: -		snprintf(file, size, "%s/usr/lib/debug%s.debug", +		snprintf(filename, size, "%s/usr/lib/debug%s.debug",  			 symbol_conf.symfs, dso->long_name);  		break;  	case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: -		snprintf(file, size, "%s/usr/lib/debug%s", +		snprintf(filename, size, "%s/usr/lib/debug%s",  			 symbol_conf.symfs, dso->long_name);  		break; +	case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: +	{ +		const char *last_slash; +		size_t len; +		size_t dir_size; + +		last_slash = dso->long_name + dso->long_name_len; +		while (last_slash != dso->long_name && *last_slash != '/') +			last_slash--; + +		len = scnprintf(filename, size, "%s", symbol_conf.symfs); +		dir_size = last_slash - dso->long_name + 2; +		if (dir_size > (size - len)) { +			ret = -1; +			break; +		} +		len += scnprintf(filename + len, dir_size, "%s",  dso->long_name); +		len += scnprintf(filename + len , size - len, ".debug%s", +								last_slash); +		break; +	} +  	case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:  		if (!dso->has_build_id) {  			ret = -1; @@ -73,7 +100,7 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,  		build_id__sprintf(dso->build_id,  				  sizeof(dso->build_id),  				  build_id_hex); -		snprintf(file, size, +		snprintf(filename, size,  			 "%s/usr/lib/debug/.build-id/%.2s/%s.debug",  			 symbol_conf.symfs, build_id_hex, build_id_hex + 2);  		break; @@ -81,23 +108,23 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,  	case DSO_BINARY_TYPE__VMLINUX:  	case DSO_BINARY_TYPE__GUEST_VMLINUX:  	case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: -		snprintf(file, size, "%s%s", +		snprintf(filename, size, "%s%s",  			 symbol_conf.symfs, dso->long_name);  		break;  	case DSO_BINARY_TYPE__GUEST_KMODULE: -		snprintf(file, size, "%s%s%s", symbol_conf.symfs, +		snprintf(filename, size, "%s%s%s", symbol_conf.symfs,  			 root_dir, dso->long_name);  		break;  	case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: -		snprintf(file, size, "%s%s", symbol_conf.symfs, +		snprintf(filename, size, "%s%s", symbol_conf.symfs,  			 dso->long_name);  		break;  	case DSO_BINARY_TYPE__KCORE:  	case DSO_BINARY_TYPE__GUEST_KCORE: -		snprintf(file, size, "%s", dso->long_name); +		snprintf(filename, size, "%s", dso->long_name);  		break;  	default: @@ -112,52 +139,216 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,  	return ret;  } -static int open_dso(struct dso *dso, struct machine *machine) +/* + * Global list of open DSOs and the counter. + */ +static LIST_HEAD(dso__data_open); +static long dso__data_open_cnt; + +static void dso__list_add(struct dso *dso) +{ +	list_add_tail(&dso->data.open_entry, &dso__data_open); +	dso__data_open_cnt++; +} + +static void dso__list_del(struct dso *dso) +{ +	list_del(&dso->data.open_entry); +	WARN_ONCE(dso__data_open_cnt <= 0, +		  "DSO data fd counter out of bounds."); +	dso__data_open_cnt--; +} + +static void close_first_dso(void); + +static int do_open(char *name)  { -	char *root_dir = (char *) ""; -	char *name;  	int fd; -	name = malloc(PATH_MAX); +	do { +		fd = open(name, O_RDONLY); +		if (fd >= 0) +			return fd; + +		pr_debug("dso open failed, mmap: %s\n", strerror(errno)); +		if (!dso__data_open_cnt || errno != EMFILE) +			break; + +		close_first_dso(); +	} while (1); + +	return -1; +} + +static int __open_dso(struct dso *dso, struct machine *machine) +{ +	int fd; +	char *root_dir = (char *)""; +	char *name = malloc(PATH_MAX); +  	if (!name)  		return -ENOMEM;  	if (machine)  		root_dir = machine->root_dir; -	if (dso__binary_type_file(dso, dso->data_type, -				  root_dir, name, PATH_MAX)) { +	if (dso__read_binary_type_filename(dso, dso->binary_type, +					    root_dir, name, PATH_MAX)) {  		free(name);  		return -EINVAL;  	} -	fd = open(name, O_RDONLY); +	fd = do_open(name);  	free(name);  	return fd;  } +static void check_data_close(void); + +/** + * dso_close - Open DSO data file + * @dso: dso object + * + * Open @dso's data file descriptor and updates + * list/count of open DSO objects. + */ +static int open_dso(struct dso *dso, struct machine *machine) +{ +	int fd = __open_dso(dso, machine); + +	if (fd > 0) { +		dso__list_add(dso); +		/* +		 * Check if we crossed the allowed number +		 * of opened DSOs and close one if needed. +		 */ +		check_data_close(); +	} + +	return fd; +} + +static void close_data_fd(struct dso *dso) +{ +	if (dso->data.fd >= 0) { +		close(dso->data.fd); +		dso->data.fd = -1; +		dso->data.file_size = 0; +		dso__list_del(dso); +	} +} + +/** + * dso_close - Close DSO data file + * @dso: dso object + * + * Close @dso's data file descriptor and updates + * list/count of open DSO objects. + */ +static void close_dso(struct dso *dso) +{ +	close_data_fd(dso); +} + +static void close_first_dso(void) +{ +	struct dso *dso; + +	dso = list_first_entry(&dso__data_open, struct dso, data.open_entry); +	close_dso(dso); +} + +static rlim_t get_fd_limit(void) +{ +	struct rlimit l; +	rlim_t limit = 0; + +	/* Allow half of the current open fd limit. */ +	if (getrlimit(RLIMIT_NOFILE, &l) == 0) { +		if (l.rlim_cur == RLIM_INFINITY) +			limit = l.rlim_cur; +		else +			limit = l.rlim_cur / 2; +	} else { +		pr_err("failed to get fd limit\n"); +		limit = 1; +	} + +	return limit; +} + +static bool may_cache_fd(void) +{ +	static rlim_t limit; + +	if (!limit) +		limit = get_fd_limit(); + +	if (limit == RLIM_INFINITY) +		return true; + +	return limit > (rlim_t) dso__data_open_cnt; +} + +/* + * Check and close LRU dso if we crossed allowed limit + * for opened dso file descriptors. The limit is half + * of the RLIMIT_NOFILE files opened. +*/ +static void check_data_close(void) +{ +	bool cache_fd = may_cache_fd(); + +	if (!cache_fd) +		close_first_dso(); +} + +/** + * dso__data_close - Close DSO data file + * @dso: dso object + * + * External interface to close @dso's data file descriptor. + */ +void dso__data_close(struct dso *dso) +{ +	close_dso(dso); +} + +/** + * dso__data_fd - Get dso's data file descriptor + * @dso: dso object + * @machine: machine object + * + * External interface to find dso's file, open it and + * returns file descriptor. + */  int dso__data_fd(struct dso *dso, struct machine *machine)  { -	static enum dso_binary_type binary_type_data[] = { +	enum dso_binary_type binary_type_data[] = {  		DSO_BINARY_TYPE__BUILD_ID_CACHE,  		DSO_BINARY_TYPE__SYSTEM_PATH_DSO,  		DSO_BINARY_TYPE__NOT_FOUND,  	};  	int i = 0; -	if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND) -		return open_dso(dso, machine); +	if (dso->data.fd >= 0) +		return dso->data.fd; + +	if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) { +		dso->data.fd = open_dso(dso, machine); +		return dso->data.fd; +	}  	do {  		int fd; -		dso->data_type = binary_type_data[i++]; +		dso->binary_type = binary_type_data[i++];  		fd = open_dso(dso, machine);  		if (fd >= 0) -			return fd; +			return dso->data.fd = fd; -	} while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND); +	} while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND);  	return -EINVAL;  } @@ -177,11 +368,10 @@ dso_cache__free(struct rb_root *root)  	}  } -static struct dso_cache* -dso_cache__find(struct rb_root *root, u64 offset) +static struct dso_cache *dso_cache__find(const struct rb_root *root, u64 offset)  { -	struct rb_node **p = &root->rb_node; -	struct rb_node *parent = NULL; +	struct rb_node * const *p = &root->rb_node; +	const struct rb_node *parent = NULL;  	struct dso_cache *cache;  	while (*p != NULL) { @@ -238,16 +428,10 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset,  }  static ssize_t -dso_cache__read(struct dso *dso, struct machine *machine, -		 u64 offset, u8 *data, ssize_t size) +dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)  {  	struct dso_cache *cache;  	ssize_t ret; -	int fd; - -	fd = dso__data_fd(dso, machine); -	if (fd < 0) -		return -1;  	do {  		u64 cache_offset; @@ -261,16 +445,16 @@ dso_cache__read(struct dso *dso, struct machine *machine,  		cache_offset = offset & DSO__DATA_CACHE_MASK;  		ret = -EINVAL; -		if (-1 == lseek(fd, cache_offset, SEEK_SET)) +		if (-1 == lseek(dso->data.fd, cache_offset, SEEK_SET))  			break; -		ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); +		ret = read(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE);  		if (ret <= 0)  			break;  		cache->offset = cache_offset;  		cache->size   = ret; -		dso_cache__insert(&dso->cache, cache); +		dso_cache__insert(&dso->data.cache, cache);  		ret = dso_cache__memcpy(cache, offset, data, size); @@ -279,24 +463,27 @@ dso_cache__read(struct dso *dso, struct machine *machine,  	if (ret <= 0)  		free(cache); -	close(fd);  	return ret;  } -static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, -			      u64 offset, u8 *data, ssize_t size) +static ssize_t dso_cache_read(struct dso *dso, u64 offset, +			      u8 *data, ssize_t size)  {  	struct dso_cache *cache; -	cache = dso_cache__find(&dso->cache, offset); +	cache = dso_cache__find(&dso->data.cache, offset);  	if (cache)  		return dso_cache__memcpy(cache, offset, data, size);  	else -		return dso_cache__read(dso, machine, offset, data, size); +		return dso_cache__read(dso, offset, data, size);  } -ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, -			      u64 offset, u8 *data, ssize_t size) +/* + * Reads and caches dso data DSO__DATA_CACHE_SIZE size chunks + * in the rb_tree. Any read to already cached data is served + * by cached data. + */ +static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)  {  	ssize_t r = 0;  	u8 *p = data; @@ -304,7 +491,7 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,  	do {  		ssize_t ret; -		ret = dso_cache_read(dso, machine, offset, p, size); +		ret = dso_cache_read(dso, offset, p, size);  		if (ret < 0)  			return ret; @@ -324,6 +511,67 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,  	return r;  } +static int data_file_size(struct dso *dso) +{ +	struct stat st; + +	if (!dso->data.file_size) { +		if (fstat(dso->data.fd, &st)) { +			pr_err("dso mmap failed, fstat: %s\n", strerror(errno)); +			return -1; +		} +		dso->data.file_size = st.st_size; +	} + +	return 0; +} + +static ssize_t data_read_offset(struct dso *dso, u64 offset, +				u8 *data, ssize_t size) +{ +	if (data_file_size(dso)) +		return -1; + +	/* Check the offset sanity. */ +	if (offset > dso->data.file_size) +		return -1; + +	if (offset + size < offset) +		return -1; + +	return cached_read(dso, offset, data, size); +} + +/** + * dso__data_read_offset - Read data from dso file offset + * @dso: dso object + * @machine: machine object + * @offset: file offset + * @data: buffer to store data + * @size: size of the @data buffer + * + * External interface to read data from dso file offset. Open + * dso data file and use cached_read to get the data. + */ +ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, +			      u64 offset, u8 *data, ssize_t size) +{ +	if (dso__data_fd(dso, machine) < 0) +		return -1; + +	return data_read_offset(dso, offset, data, size); +} + +/** + * dso__data_read_addr - Read data from dso address + * @dso: dso object + * @machine: machine object + * @add: virtual memory address + * @data: buffer to store data + * @size: size of the @data buffer + * + * External interface to read data from dso address. + */  ssize_t dso__data_read_addr(struct dso *dso, struct map *map,  			    struct machine *machine, u64 addr,  			    u8 *data, ssize_t size) @@ -356,32 +604,63 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name,  	 * processing we had no idea this was the kernel dso.  	 */  	if (dso != NULL) { -		dso__set_short_name(dso, short_name); +		dso__set_short_name(dso, short_name, false);  		dso->kernel = dso_type;  	}  	return dso;  } -void dso__set_long_name(struct dso *dso, char *name) +void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)  {  	if (name == NULL)  		return; -	dso->long_name = name; -	dso->long_name_len = strlen(name); + +	if (dso->long_name_allocated) +		free((char *)dso->long_name); + +	dso->long_name		 = name; +	dso->long_name_len	 = strlen(name); +	dso->long_name_allocated = name_allocated;  } -void dso__set_short_name(struct dso *dso, const char *name) +void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated)  {  	if (name == NULL)  		return; -	dso->short_name = name; -	dso->short_name_len = strlen(name); + +	if (dso->short_name_allocated) +		free((char *)dso->short_name); + +	dso->short_name		  = name; +	dso->short_name_len	  = strlen(name); +	dso->short_name_allocated = name_allocated;  }  static void dso__set_basename(struct dso *dso)  { -	dso__set_short_name(dso, basename(dso->long_name)); +       /* +        * basename() may modify path buffer, so we must pass +        * a copy. +        */ +       char *base, *lname = strdup(dso->long_name); + +       if (!lname) +               return; + +       /* +        * basename() may return a pointer to internal +        * storage which is reused in subsequent calls +        * so copy the result. +        */ +       base = strdup(basename(lname)); + +       free(lname); + +       if (!base) +               return; + +       dso__set_short_name(dso, base, true);  }  int dso__name_len(const struct dso *dso) @@ -416,20 +695,24 @@ struct dso *dso__new(const char *name)  	if (dso != NULL) {  		int i;  		strcpy(dso->name, name); -		dso__set_long_name(dso, dso->name); -		dso__set_short_name(dso, dso->name); +		dso__set_long_name(dso, dso->name, false); +		dso__set_short_name(dso, dso->name, false);  		for (i = 0; i < MAP__NR_TYPES; ++i)  			dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; -		dso->cache = RB_ROOT; +		dso->data.cache = RB_ROOT; +		dso->data.fd = -1;  		dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; -		dso->data_type   = DSO_BINARY_TYPE__NOT_FOUND; +		dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND;  		dso->loaded = 0;  		dso->rel = 0;  		dso->sorted_by_name = 0;  		dso->has_build_id = 0; +		dso->has_srcline = 1; +		dso->a2l_fails = 1;  		dso->kernel = DSO_TYPE_USER;  		dso->needs_swap = DSO_SWAP__UNSET;  		INIT_LIST_HEAD(&dso->node); +		INIT_LIST_HEAD(&dso->data.open_entry);  	}  	return dso; @@ -440,11 +723,21 @@ void dso__delete(struct dso *dso)  	int i;  	for (i = 0; i < MAP__NR_TYPES; ++i)  		symbols__delete(&dso->symbols[i]); -	if (dso->sname_alloc) -		free((char *)dso->short_name); -	if (dso->lname_alloc) -		free(dso->long_name); -	dso_cache__free(&dso->cache); + +	if (dso->short_name_allocated) { +		zfree((char **)&dso->short_name); +		dso->short_name_allocated = false; +	} + +	if (dso->long_name_allocated) { +		zfree((char **)&dso->long_name); +		dso->long_name_allocated = false; +	} + +	dso__data_close(dso); +	dso_cache__free(&dso->data.cache); +	dso__free_a2l(dso); +	zfree(&dso->symsrc_filename);  	free(dso);  } @@ -519,7 +812,7 @@ void dsos__add(struct list_head *head, struct dso *dso)  	list_add_tail(&dso->node, head);  } -struct dso *dsos__find(struct list_head *head, const char *name, bool cmp_short) +struct dso *dsos__find(const struct list_head *head, const char *name, bool cmp_short)  {  	struct dso *pos; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index b793053335d..ad553ba257b 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -4,8 +4,9 @@  #include <linux/types.h>  #include <linux/rbtree.h>  #include <stdbool.h> -#include "types.h" +#include <linux/types.h>  #include "map.h" +#include "build-id.h"  enum dso_binary_type {  	DSO_BINARY_TYPE__KALLSYMS = 0, @@ -23,6 +24,7 @@ enum dso_binary_type {  	DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,  	DSO_BINARY_TYPE__KCORE,  	DSO_BINARY_TYPE__GUEST_KCORE, +	DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,  	DSO_BINARY_TYPE__NOT_FOUND,  }; @@ -74,28 +76,50 @@ struct dso {  	struct list_head node;  	struct rb_root	 symbols[MAP__NR_TYPES];  	struct rb_root	 symbol_names[MAP__NR_TYPES]; -	struct rb_root	 cache; +	void		 *a2l; +	char		 *symsrc_filename; +	unsigned int	 a2l_fails;  	enum dso_kernel_type	kernel;  	enum dso_swap_type	needs_swap;  	enum dso_binary_type	symtab_type; -	enum dso_binary_type	data_type; +	enum dso_binary_type	binary_type;  	u8		 adjust_symbols:1;  	u8		 has_build_id:1; +	u8		 has_srcline:1;  	u8		 hit:1;  	u8		 annotate_warned:1; -	u8		 sname_alloc:1; -	u8		 lname_alloc:1; +	u8		 short_name_allocated:1; +	u8		 long_name_allocated:1;  	u8		 sorted_by_name;  	u8		 loaded;  	u8		 rel;  	u8		 build_id[BUILD_ID_SIZE];  	const char	 *short_name; -	char		 *long_name; +	const char	 *long_name;  	u16		 long_name_len;  	u16		 short_name_len; + +	/* dso data file */ +	struct { +		struct rb_root	 cache; +		int		 fd; +		size_t		 file_size; +		struct list_head open_entry; +	} data; +  	char		 name[0];  }; +/* dso__for_each_symbol - iterate over the symbols of given type + * + * @dso: the 'struct dso *' in which symbols itereated + * @pos: the 'struct symbol *' to use as a loop cursor + * @n: the 'struct rb_node *' to use as a temporary storage + * @type: the 'enum map_type' type of symbols + */ +#define dso__for_each_symbol(dso, pos, n, type)	\ +	symbols__for_each_entry(&(dso)->symbols[(type)], pos, n) +  static inline void dso__set_loaded(struct dso *dso, enum map_type type)  {  	dso->loaded |= (1 << type); @@ -104,8 +128,8 @@ static inline void dso__set_loaded(struct dso *dso, enum map_type type)  struct dso *dso__new(const char *name);  void dso__delete(struct dso *dso); -void dso__set_short_name(struct dso *dso, const char *name); -void dso__set_long_name(struct dso *dso, char *name); +void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated); +void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated);  int dso__name_len(const struct dso *dso); @@ -122,10 +146,50 @@ void dso__read_running_kernel_build_id(struct dso *dso,  int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir);  char dso__symtab_origin(const struct dso *dso); -int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, -			  char *root_dir, char *file, size_t size); - +int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, +				   char *root_dir, char *filename, size_t size); + +/* + * The dso__data_* external interface provides following functions: + *   dso__data_fd + *   dso__data_close + *   dso__data_read_offset + *   dso__data_read_addr + * + * Please refer to the dso.c object code for each function and + * arguments documentation. Following text tries to explain the + * dso file descriptor caching. + * + * The dso__data* interface allows caching of opened file descriptors + * to speed up the dso data accesses. The idea is to leave the file + * descriptor opened ideally for the whole life of the dso object. + * + * The current usage of the dso__data_* interface is as follows: + * + * Get DSO's fd: + *   int fd = dso__data_fd(dso, machine); + *   USE 'fd' SOMEHOW + * + * Read DSO's data: + *   n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE); + *   n = dso__data_read_addr(dso_0, &machine, 0, buf, BUFSIZE); + * + * Eventually close DSO's fd: + *   dso__data_close(dso); + * + * It is not necessary to close the DSO object data file. Each time new + * DSO data file is opened, the limit (RLIMIT_NOFILE/2) is checked. Once + * it is crossed, the oldest opened DSO object is closed. + * + * The dso__delete function calls close_dso function to ensure the + * data file descriptor gets closed/unmapped before the dso object + * is freed. + * + * TODO +*/  int dso__data_fd(struct dso *dso, struct machine *machine); +void dso__data_close(struct dso *dso); +  ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,  			      u64 offset, u8 *data, ssize_t size);  ssize_t dso__data_read_addr(struct dso *dso, struct map *map, @@ -137,7 +201,7 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name,  				const char *short_name, int dso_type);  void dsos__add(struct list_head *head, struct dso *dso); -struct dso *dsos__find(struct list_head *head, const char *name, +struct dso *dsos__find(const struct list_head *head, const char *name,  		       bool cmp_short);  struct dso *__dsos__findnew(struct list_head *head, const char *name);  bool __dsos__read_build_ids(struct list_head *head, bool with_hits); @@ -153,14 +217,16 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp);  static inline bool dso__is_vmlinux(struct dso *dso)  { -	return dso->data_type == DSO_BINARY_TYPE__VMLINUX || -	       dso->data_type == DSO_BINARY_TYPE__GUEST_VMLINUX; +	return dso->binary_type == DSO_BINARY_TYPE__VMLINUX || +	       dso->binary_type == DSO_BINARY_TYPE__GUEST_VMLINUX;  }  static inline bool dso__is_kcore(struct dso *dso)  { -	return dso->data_type == DSO_BINARY_TYPE__KCORE || -	       dso->data_type == DSO_BINARY_TYPE__GUEST_KCORE; +	return dso->binary_type == DSO_BINARY_TYPE__KCORE || +	       dso->binary_type == DSO_BINARY_TYPE__GUEST_KCORE;  } +void dso__free_a2l(struct dso *dso); +  #endif /* __PERF_DSO */ diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index e23bde19d59..cc66c4049e0 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -426,7 +426,7 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)   * @die_mem: a buffer for result DIE   *   * Search a non-inlined function DIE which includes @addr. Stores the - * DIE to @die_mem and returns it if found. Returns NULl if failed. + * DIE to @die_mem and returns it if found. Returns NULL if failed.   */  Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,  				    Dwarf_Die *die_mem) @@ -454,15 +454,32 @@ static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data)  }  /** + * die_find_top_inlinefunc - Search the top inlined function at given address + * @sp_die: a subprogram DIE which including @addr + * @addr: target address + * @die_mem: a buffer for result DIE + * + * Search an inlined function DIE which includes @addr. Stores the + * DIE to @die_mem and returns it if found. Returns NULL if failed. + * Even if several inlined functions are expanded recursively, this + * doesn't trace it down, and returns the topmost one. + */ +Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, +				   Dwarf_Die *die_mem) +{ +	return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); +} + +/**   * die_find_inlinefunc - Search an inlined function at given address - * @cu_die: a CU DIE which including @addr + * @sp_die: a subprogram DIE which including @addr   * @addr: target address   * @die_mem: a buffer for result DIE   *   * Search an inlined function DIE which includes @addr. Stores the - * DIE to @die_mem and returns it if found. Returns NULl if failed. + * DIE to @die_mem and returns it if found. Returns NULL if failed.   * If several inlined functions are expanded recursively, this trace - * it and returns deepest one. + * it down and returns deepest one.   */  Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,  			       Dwarf_Die *die_mem) @@ -730,14 +747,17 @@ struct __find_variable_param {  static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)  {  	struct __find_variable_param *fvp = data; +	Dwarf_Attribute attr;  	int tag;  	tag = dwarf_tag(die_mem);  	if ((tag == DW_TAG_formal_parameter ||  	     tag == DW_TAG_variable) && -	    die_compare_name(die_mem, fvp->name)) +	    die_compare_name(die_mem, fvp->name) && +	/* Does the DIE have location information or external instance? */ +	    (dwarf_attr(die_mem, DW_AT_external, &attr) || +	     dwarf_attr(die_mem, DW_AT_location, &attr)))  		return DIE_FIND_CB_END; -  	if (dwarf_haspc(die_mem, fvp->addr))  		return DIE_FIND_CB_CONTINUE;  	else diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index 8658d41697d..b4fe90c6cb2 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -79,7 +79,11 @@ extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die,  extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,  				    Dwarf_Die *die_mem); -/* Search an inlined function including given address */ +/* Search the top inlined function including given address */ +extern Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, +					  Dwarf_Die *die_mem); + +/* Search the deepest inlined function including given address */  extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,  				      Dwarf_Die *die_mem); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 9b393e7dca6..d0281bdfa58 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -1,12 +1,15 @@  #include <linux/types.h> +#include <sys/mman.h>  #include "event.h"  #include "debug.h" +#include "hist.h"  #include "machine.h"  #include "sort.h"  #include "string.h"  #include "strlist.h"  #include "thread.h"  #include "thread_map.h" +#include "symbol/kallsyms.h"  static const char *perf_event__names[] = {  	[0]					= "TOTAL", @@ -93,20 +96,20 @@ static pid_t perf_event__get_comm_tgid(pid_t pid, char *comm, size_t len)  static pid_t perf_event__synthesize_comm(struct perf_tool *tool,  					 union perf_event *event, pid_t pid, -					 int full,  					 perf_event__handler_t process,  					 struct machine *machine)  { -	char filename[PATH_MAX];  	size_t size; -	DIR *tasks; -	struct dirent dirent, *next;  	pid_t tgid;  	memset(&event->comm, 0, sizeof(event->comm)); -	tgid = perf_event__get_comm_tgid(pid, event->comm.comm, -					 sizeof(event->comm.comm)); +	if (machine__is_host(machine)) +		tgid = perf_event__get_comm_tgid(pid, event->comm.comm, +						 sizeof(event->comm.comm)); +	else +		tgid = machine->pid; +  	if (tgid < 0)  		goto out; @@ -119,64 +122,53 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool,  	event->comm.header.size = (sizeof(event->comm) -  				(sizeof(event->comm.comm) - size) +  				machine->id_hdr_size); -	if (!full) { -		event->comm.tid = pid; - -		if (process(tool, event, &synth_sample, machine) != 0) -			return -1; - -		goto out; -	} +	event->comm.tid = pid; -	snprintf(filename, sizeof(filename), "/proc/%d/task", pid); - -	tasks = opendir(filename); -	if (tasks == NULL) { -		pr_debug("couldn't open %s\n", filename); -		return 0; -	} +	if (process(tool, event, &synth_sample, machine) != 0) +		return -1; -	while (!readdir_r(tasks, &dirent, &next) && next) { -		char *end; -		pid = strtol(dirent.d_name, &end, 10); -		if (*end) -			continue; +out: +	return tgid; +} -		/* already have tgid; jut want to update the comm */ -		(void) perf_event__get_comm_tgid(pid, event->comm.comm, -					 sizeof(event->comm.comm)); +static int perf_event__synthesize_fork(struct perf_tool *tool, +				       union perf_event *event, pid_t pid, +				       pid_t tgid, perf_event__handler_t process, +				       struct machine *machine) +{ +	memset(&event->fork, 0, sizeof(event->fork) + machine->id_hdr_size); -		size = strlen(event->comm.comm) + 1; -		size = PERF_ALIGN(size, sizeof(u64)); -		memset(event->comm.comm + size, 0, machine->id_hdr_size); -		event->comm.header.size = (sizeof(event->comm) - -					  (sizeof(event->comm.comm) - size) + -					  machine->id_hdr_size); +	/* this is really a clone event but we use fork to synthesize it */ +	event->fork.ppid = tgid; +	event->fork.ptid = tgid; +	event->fork.pid  = tgid; +	event->fork.tid  = pid; +	event->fork.header.type = PERF_RECORD_FORK; -		event->comm.tid = pid; +	event->fork.header.size = (sizeof(event->fork) + machine->id_hdr_size); -		if (process(tool, event, &synth_sample, machine) != 0) { -			tgid = -1; -			break; -		} -	} +	if (process(tool, event, &synth_sample, machine) != 0) +		return -1; -	closedir(tasks); -out: -	return tgid; +	return 0;  } -static int perf_event__synthesize_mmap_events(struct perf_tool *tool, -					      union perf_event *event, -					      pid_t pid, pid_t tgid, -					      perf_event__handler_t process, -					      struct machine *machine) +int perf_event__synthesize_mmap_events(struct perf_tool *tool, +				       union perf_event *event, +				       pid_t pid, pid_t tgid, +				       perf_event__handler_t process, +				       struct machine *machine, +				       bool mmap_data)  {  	char filename[PATH_MAX];  	FILE *fp;  	int rc = 0; -	snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); +	if (machine__is_default_guest(machine)) +		return 0; + +	snprintf(filename, sizeof(filename), "%s/proc/%d/maps", +		 machine->root_dir, pid);  	fp = fopen(filename, "r");  	if (fp == NULL) { @@ -188,10 +180,6 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,  	}  	event->header.type = PERF_RECORD_MMAP2; -	/* -	 * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c -	 */ -	event->header.misc = PERF_RECORD_MISC_USER;  	while (1) {  		char bf[BUFSIZ]; @@ -215,13 +203,43 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,  		       &event->mmap2.min,  		       &ino, execname); +		/* + 		 * Anon maps don't have the execname. + 		 */ +		if (n < 7) +			continue; +  		event->mmap2.ino = (u64)ino; -		if (n != 8) -			continue; +		/* +		 * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c +		 */ +		if (machine__is_host(machine)) +			event->header.misc = PERF_RECORD_MISC_USER; +		else +			event->header.misc = PERF_RECORD_MISC_GUEST_USER; + +		/* map protection and flags bits */ +		event->mmap2.prot = 0; +		event->mmap2.flags = 0; +		if (prot[0] == 'r') +			event->mmap2.prot |= PROT_READ; +		if (prot[1] == 'w') +			event->mmap2.prot |= PROT_WRITE; +		if (prot[2] == 'x') +			event->mmap2.prot |= PROT_EXEC; + +		if (prot[3] == 's') +			event->mmap2.flags |= MAP_SHARED; +		else +			event->mmap2.flags |= MAP_PRIVATE; -		if (prot[2] != 'x') -			continue; +		if (prot[2] != 'x') { +			if (!mmap_data || prot[0] != 'r') +				continue; + +			event->header.misc |= PERF_RECORD_MISC_MMAP_DATA; +		}  		if (!strcmp(execname, ""))  			strcpy(execname, anonstr); @@ -305,25 +323,80 @@ int perf_event__synthesize_modules(struct perf_tool *tool,  static int __event__synthesize_thread(union perf_event *comm_event,  				      union perf_event *mmap_event, +				      union perf_event *fork_event,  				      pid_t pid, int full,  					  perf_event__handler_t process,  				      struct perf_tool *tool, -				      struct machine *machine) +				      struct machine *machine, bool mmap_data)  { -	pid_t tgid = perf_event__synthesize_comm(tool, comm_event, pid, full, +	char filename[PATH_MAX]; +	DIR *tasks; +	struct dirent dirent, *next; +	pid_t tgid; + +	/* special case: only send one comm event using passed in pid */ +	if (!full) { +		tgid = perf_event__synthesize_comm(tool, comm_event, pid, +						   process, machine); + +		if (tgid == -1) +			return -1; + +		return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, +							  process, machine, mmap_data); +	} + +	if (machine__is_default_guest(machine)) +		return 0; + +	snprintf(filename, sizeof(filename), "%s/proc/%d/task", +		 machine->root_dir, pid); + +	tasks = opendir(filename); +	if (tasks == NULL) { +		pr_debug("couldn't open %s\n", filename); +		return 0; +	} + +	while (!readdir_r(tasks, &dirent, &next) && next) { +		char *end; +		int rc = 0; +		pid_t _pid; + +		_pid = strtol(dirent.d_name, &end, 10); +		if (*end) +			continue; + +		tgid = perf_event__synthesize_comm(tool, comm_event, _pid, +						   process, machine); +		if (tgid == -1) +			return -1; + +		if (_pid == pid) { +			/* process the parent's maps too */ +			rc = perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, +						process, machine, mmap_data); +		} else { +			/* only fork the tid's map, to save time */ +			rc = perf_event__synthesize_fork(tool, fork_event, _pid, tgid,  						 process, machine); -	if (tgid == -1) -		return -1; -	return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, -						  process, machine); +		} + +		if (rc) +			return rc; +	} + +	closedir(tasks); +	return 0;  }  int perf_event__synthesize_thread_map(struct perf_tool *tool,  				      struct thread_map *threads,  				      perf_event__handler_t process, -				      struct machine *machine) +				      struct machine *machine, +				      bool mmap_data)  { -	union perf_event *comm_event, *mmap_event; +	union perf_event *comm_event, *mmap_event, *fork_event;  	int err = -1, thread, j;  	comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); @@ -334,11 +407,17 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,  	if (mmap_event == NULL)  		goto out_free_comm; +	fork_event = malloc(sizeof(fork_event->fork) + machine->id_hdr_size); +	if (fork_event == NULL) +		goto out_free_mmap; +  	err = 0;  	for (thread = 0; thread < threads->nr; ++thread) {  		if (__event__synthesize_thread(comm_event, mmap_event, +					       fork_event,  					       threads->map[thread], 0, -					       process, tool, machine)) { +					       process, tool, machine, +					       mmap_data)) {  			err = -1;  			break;  		} @@ -360,15 +439,18 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,  			/* if not, generate events for it */  			if (need_leader && -			    __event__synthesize_thread(comm_event, -						      mmap_event, -						      comm_event->comm.pid, 0, -						      process, tool, machine)) { +			    __event__synthesize_thread(comm_event, mmap_event, +						       fork_event, +						       comm_event->comm.pid, 0, +						       process, tool, machine, +						       mmap_data)) {  				err = -1;  				break;  			}  		}  	} +	free(fork_event); +out_free_mmap:  	free(mmap_event);  out_free_comm:  	free(comm_event); @@ -378,13 +460,17 @@ out:  int perf_event__synthesize_threads(struct perf_tool *tool,  				   perf_event__handler_t process, -				   struct machine *machine) +				   struct machine *machine, bool mmap_data)  {  	DIR *proc; +	char proc_path[PATH_MAX];  	struct dirent dirent, *next; -	union perf_event *comm_event, *mmap_event; +	union perf_event *comm_event, *mmap_event, *fork_event;  	int err = -1; +	if (machine__is_default_guest(machine)) +		return 0; +  	comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size);  	if (comm_event == NULL)  		goto out; @@ -393,10 +479,16 @@ int perf_event__synthesize_threads(struct perf_tool *tool,  	if (mmap_event == NULL)  		goto out_free_comm; -	proc = opendir("/proc"); -	if (proc == NULL) +	fork_event = malloc(sizeof(fork_event->fork) + machine->id_hdr_size); +	if (fork_event == NULL)  		goto out_free_mmap; +	snprintf(proc_path, sizeof(proc_path), "%s/proc", machine->root_dir); +	proc = opendir(proc_path); + +	if (proc == NULL) +		goto out_free_fork; +  	while (!readdir_r(proc, &dirent, &next) && next) {  		char *end;  		pid_t pid = strtol(dirent.d_name, &end, 10); @@ -407,12 +499,14 @@ int perf_event__synthesize_threads(struct perf_tool *tool,   		 * We may race with exiting thread, so don't stop just because   		 * one thread couldn't be synthesized.   		 */ -		__event__synthesize_thread(comm_event, mmap_event, pid, 1, -					   process, tool, machine); +		__event__synthesize_thread(comm_event, mmap_event, fork_event, pid, +					   1, process, tool, machine, mmap_data);  	}  	err = 0;  	closedir(proc); +out_free_fork: +	free(fork_event);  out_free_mmap:  	free(mmap_event);  out_free_comm: @@ -443,23 +537,32 @@ static int find_symbol_cb(void *arg, const char *name, char type,  	return 1;  } +u64 kallsyms__get_function_start(const char *kallsyms_filename, +				 const char *symbol_name) +{ +	struct process_symbol_args args = { .name = symbol_name, }; + +	if (kallsyms__parse(kallsyms_filename, &args, find_symbol_cb) <= 0) +		return 0; + +	return args.start; +} +  int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,  				       perf_event__handler_t process, -				       struct machine *machine, -				       const char *symbol_name) +				       struct machine *machine)  {  	size_t size; -	const char *filename, *mmap_name; -	char path[PATH_MAX]; +	const char *mmap_name;  	char name_buff[PATH_MAX];  	struct map *map; +	struct kmap *kmap;  	int err;  	/*  	 * We should get this from /sys/kernel/sections/.text, but till that is  	 * available use this, and after it is use this as a fallback for older  	 * kernels.  	 */ -	struct process_symbol_args args = { .name = symbol_name, };  	union perf_event *event = zalloc((sizeof(event->mmap) +  					  machine->id_hdr_size));  	if (event == NULL) { @@ -475,30 +578,19 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,  		 * see kernel/perf_event.c __perf_event_mmap  		 */  		event->header.misc = PERF_RECORD_MISC_KERNEL; -		filename = "/proc/kallsyms";  	} else {  		event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; -		if (machine__is_default_guest(machine)) -			filename = (char *) symbol_conf.default_guest_kallsyms; -		else { -			sprintf(path, "%s/proc/kallsyms", machine->root_dir); -			filename = path; -		} -	} - -	if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) { -		free(event); -		return -ENOENT;  	}  	map = machine->vmlinux_maps[MAP__FUNCTION]; +	kmap = map__kmap(map);  	size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), -			"%s%s", mmap_name, symbol_name) + 1; +			"%s%s", mmap_name, kmap->ref_reloc_sym->name) + 1;  	size = PERF_ALIGN(size, sizeof(u64));  	event->mmap.header.type = PERF_RECORD_MMAP;  	event->mmap.header.size = (sizeof(event->mmap) -  			(sizeof(event->mmap.filename) - size) + machine->id_hdr_size); -	event->mmap.pgoff = args.start; +	event->mmap.pgoff = kmap->ref_reloc_sym->addr;  	event->mmap.start = map->start;  	event->mmap.len   = map->end - event->mmap.start;  	event->mmap.pid   = machine->pid; @@ -516,52 +608,58 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp)  int perf_event__process_comm(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_comm_event(machine, event); +	return machine__process_comm_event(machine, event, sample);  }  int perf_event__process_lost(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_lost_event(machine, event); +	return machine__process_lost_event(machine, event, sample);  }  size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)  { -	return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n", +	return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n",  		       event->mmap.pid, event->mmap.tid, event->mmap.start, -		       event->mmap.len, event->mmap.pgoff, event->mmap.filename); +		       event->mmap.len, event->mmap.pgoff, +		       (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', +		       event->mmap.filename);  }  size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)  {  	return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 -			   " %02x:%02x %"PRIu64" %"PRIu64"]: %s\n", +			   " %02x:%02x %"PRIu64" %"PRIu64"]: %c%c%c%c %s\n",  		       event->mmap2.pid, event->mmap2.tid, event->mmap2.start,  		       event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj,  		       event->mmap2.min, event->mmap2.ino,  		       event->mmap2.ino_generation, +		       (event->mmap2.prot & PROT_READ) ? 'r' : '-', +		       (event->mmap2.prot & PROT_WRITE) ? 'w' : '-', +		       (event->mmap2.prot & PROT_EXEC) ? 'x' : '-', +		       (event->mmap2.flags & MAP_SHARED) ? 's' : 'p',  		       event->mmap2.filename);  }  int perf_event__process_mmap(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_mmap_event(machine, event); +	return machine__process_mmap_event(machine, event, sample);  }  int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_mmap2_event(machine, event); +	return machine__process_mmap2_event(machine, event, sample);  }  size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) @@ -573,18 +671,18 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp)  int perf_event__process_fork(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_fork_event(machine, event); +	return machine__process_fork_event(machine, event, sample);  }  int perf_event__process_exit(struct perf_tool *tool __maybe_unused,  			     union perf_event *event, -			     struct perf_sample *sample __maybe_unused, +			     struct perf_sample *sample,  			     struct machine *machine)  { -	return machine__process_exit_event(machine, event); +	return machine__process_exit_event(machine, event, sample);  }  size_t perf_event__fprintf(union perf_event *event, FILE *fp) @@ -615,24 +713,25 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)  int perf_event__process(struct perf_tool *tool __maybe_unused,  			union perf_event *event, -			struct perf_sample *sample __maybe_unused, +			struct perf_sample *sample,  			struct machine *machine)  { -	return machine__process_event(machine, event); +	return machine__process_event(machine, event, sample);  } -void thread__find_addr_map(struct thread *self, +void thread__find_addr_map(struct thread *thread,  			   struct machine *machine, u8 cpumode,  			   enum map_type type, u64 addr,  			   struct addr_location *al)  { -	struct map_groups *mg = &self->mg; +	struct map_groups *mg = thread->mg;  	bool load_map = false; -	al->thread = self; +	al->machine = machine; +	al->thread = thread;  	al->addr = addr;  	al->cpumode = cpumode; -	al->filtered = false; +	al->filtered = 0;  	if (machine == NULL) {  		al->map = NULL; @@ -649,25 +748,20 @@ void thread__find_addr_map(struct thread *self,  		al->level = 'g';  		mg = &machine->kmaps;  		load_map = true; +	} else if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) { +		al->level = 'u';  	} else { -		/* -		 * 'u' means guest os user space. -		 * TODO: We don't support guest user space. Might support late. -		 */ -		if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) -			al->level = 'u'; -		else -			al->level = 'H'; +		al->level = 'H';  		al->map = NULL;  		if ((cpumode == PERF_RECORD_MISC_GUEST_USER ||  			cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&  			!perf_guest) -			al->filtered = true; +			al->filtered |= (1 << HIST_FILTER__GUEST);  		if ((cpumode == PERF_RECORD_MISC_USER ||  			cpumode == PERF_RECORD_MISC_KERNEL) &&  			!perf_host) -			al->filtered = true; +			al->filtered |= (1 << HIST_FILTER__HOST);  		return;  	} @@ -719,16 +813,12 @@ int perf_event__preprocess_sample(const union perf_event *event,  {  	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;  	struct thread *thread = machine__findnew_thread(machine, sample->pid, -							sample->pid); +							sample->tid);  	if (thread == NULL)  		return -1; -	if (symbol_conf.comm_list && -	    !strlist__has_entry(symbol_conf.comm_list, thread->comm)) -		goto out_filtered; - -	dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid); +	dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);  	/*  	 * Have we already created the kernel maps for this machine?  	 * @@ -745,6 +835,10 @@ int perf_event__preprocess_sample(const union perf_event *event,  	dump_printf(" ...... dso: %s\n",  		    al->map ? al->map->dso->long_name :  			al->level == 'H' ? "[hypervisor]" : "<not found>"); + +	if (thread__is_filtered(thread)) +		al->filtered |= (1 << HIST_FILTER__THREAD); +  	al->sym = NULL;  	al->cpu = sample->cpu; @@ -756,8 +850,9 @@ int perf_event__preprocess_sample(const union perf_event *event,  						  dso->short_name) ||  			       (dso->short_name != dso->long_name &&  				strlist__has_entry(symbol_conf.dso_list, -						   dso->long_name))))) -			goto out_filtered; +						   dso->long_name))))) { +			al->filtered |= (1 << HIST_FILTER__DSO); +		}  		al->sym = map__find_symbol(al->map, al->addr,  					   machine->symbol_filter); @@ -765,12 +860,9 @@ int perf_event__preprocess_sample(const union perf_event *event,  	if (symbol_conf.sym_list &&  		(!al->sym || !strlist__has_entry(symbol_conf.sym_list, -						al->sym->name))) -		goto out_filtered; - -	return 0; +						al->sym->name))) { +		al->filtered |= (1 << HIST_FILTER__SYMBOL); +	} -out_filtered: -	al->filtered = true;  	return 0;  } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index c67ecc457d2..e5dd40addb3 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -7,6 +7,7 @@  #include "../perf.h"  #include "map.h"  #include "build-id.h" +#include "perf_regs.h"  struct mmap_event {  	struct perf_event_header header; @@ -27,6 +28,8 @@ struct mmap2_event {  	u32 min;  	u64 ino;  	u64 ino_generation; +	u32 prot; +	u32 flags;  	char filename[PATH_MAX];  }; @@ -61,6 +64,12 @@ struct read_event {  	u64 id;  }; +struct throttle_event { +	struct perf_event_header header; +	u64 time; +	u64 id; +	u64 stream_id; +};  #define PERF_SAMPLE_MASK				\  	(PERF_SAMPLE_IP | PERF_SAMPLE_TID |		\ @@ -69,6 +78,9 @@ struct read_event {  	 PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD |		\  	 PERF_SAMPLE_IDENTIFIER) +/* perf sample has 16 bits size limit */ +#define PERF_SAMPLE_MAX_SIZE (1 << 16) +  struct sample_event {  	struct perf_event_header        header;  	u64 array[]; @@ -76,7 +88,12 @@ struct sample_event {  struct regs_dump {  	u64 abi; +	u64 mask;  	u64 *regs; + +	/* Cached values/mask filled by first register access. */ +	u64 cache_regs[PERF_REGS_MAX]; +	u64 cache_mask;  };  struct stack_dump { @@ -102,6 +119,30 @@ struct sample_read {  	};  }; +struct ip_callchain { +	u64 nr; +	u64 ips[0]; +}; + +struct branch_flags { +	u64 mispred:1; +	u64 predicted:1; +	u64 in_tx:1; +	u64 abort:1; +	u64 reserved:60; +}; + +struct branch_entry { +	u64			from; +	u64			to; +	struct branch_flags	flags; +}; + +struct branch_stack { +	u64			nr; +	struct branch_entry	entries[0]; +}; +  struct perf_sample {  	u64 ip;  	u32 pid, tid; @@ -111,6 +152,7 @@ struct perf_sample {  	u64 stream_id;  	u64 period;  	u64 weight; +	u64 transaction;  	u32 cpu;  	u32 raw_size;  	u64 data_src; @@ -177,6 +219,7 @@ union perf_event {  	struct fork_event		fork;  	struct lost_event		lost;  	struct read_event		read; +	struct throttle_event		throttle;  	struct sample_event		sample;  	struct attr_event		attr;  	struct event_type_event		event_type; @@ -197,14 +240,13 @@ typedef int (*perf_event__handler_t)(struct perf_tool *tool,  int perf_event__synthesize_thread_map(struct perf_tool *tool,  				      struct thread_map *threads,  				      perf_event__handler_t process, -				      struct machine *machine); +				      struct machine *machine, bool mmap_data);  int perf_event__synthesize_threads(struct perf_tool *tool,  				   perf_event__handler_t process, -				   struct machine *machine); +				   struct machine *machine, bool mmap_data);  int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,  				       perf_event__handler_t process, -				       struct machine *machine, -				       const char *symbol_name); +				       struct machine *machine);  int perf_event__synthesize_modules(struct perf_tool *tool,  				   perf_event__handler_t process, @@ -240,7 +282,8 @@ int perf_event__process(struct perf_tool *tool,  			struct machine *machine);  struct addr_location; -int perf_event__preprocess_sample(const union perf_event *self, + +int perf_event__preprocess_sample(const union perf_event *event,  				  struct machine *machine,  				  struct addr_location *al,  				  struct perf_sample *sample); @@ -248,16 +291,26 @@ int perf_event__preprocess_sample(const union perf_event *self,  const char *perf_event__name(unsigned int id);  size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, -				     u64 sample_regs_user, u64 read_format); +				     u64 read_format);  int perf_event__synthesize_sample(union perf_event *event, u64 type, -				  u64 sample_regs_user, u64 read_format, +				  u64 read_format,  				  const struct perf_sample *sample,  				  bool swapped); +int perf_event__synthesize_mmap_events(struct perf_tool *tool, +				       union perf_event *event, +				       pid_t pid, pid_t tgid, +				       perf_event__handler_t process, +				       struct machine *machine, +				       bool mmap_data); +  size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp);  size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp);  size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp);  size_t perf_event__fprintf_task(union perf_event *event, FILE *fp);  size_t perf_event__fprintf(union perf_event *event, FILE *fp); +u64 kallsyms__get_function_start(const char *kallsyms_filename, +				 const char *symbol_name); +  #endif /* __PERF_RECORD_H */ diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index f9f77bee0b1..59ef2802fcf 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -7,7 +7,7 @@   * Released under the GPL v2. (and only v2, not any later version)   */  #include "util.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include <poll.h>  #include "cpumap.h"  #include "thread_map.h" @@ -18,6 +18,7 @@  #include <unistd.h>  #include "parse-events.h" +#include "parse-options.h"  #include <sys/mman.h> @@ -49,6 +50,18 @@ struct perf_evlist *perf_evlist__new(void)  	return evlist;  } +struct perf_evlist *perf_evlist__new_default(void) +{ +	struct perf_evlist *evlist = perf_evlist__new(); + +	if (evlist && perf_evlist__add_default(evlist)) { +		perf_evlist__delete(evlist); +		evlist = NULL; +	} + +	return evlist; +} +  /**   * perf_evlist__set_id_pos - set the positions of event ids.   * @evlist: selected event list @@ -68,7 +81,7 @@ static void perf_evlist__update_id_pos(struct perf_evlist *evlist)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) +	evlist__for_each(evlist, evsel)  		perf_evsel__calc_id_pos(evsel);  	perf_evlist__set_id_pos(evlist); @@ -78,7 +91,7 @@ static void perf_evlist__purge(struct perf_evlist *evlist)  {  	struct perf_evsel *pos, *n; -	list_for_each_entry_safe(pos, n, &evlist->entries, node) { +	evlist__for_each_safe(evlist, n, pos) {  		list_del_init(&pos->node);  		perf_evsel__delete(pos);  	} @@ -88,14 +101,18 @@ static void perf_evlist__purge(struct perf_evlist *evlist)  void perf_evlist__exit(struct perf_evlist *evlist)  { -	free(evlist->mmap); -	free(evlist->pollfd); -	evlist->mmap = NULL; -	evlist->pollfd = NULL; +	zfree(&evlist->mmap); +	zfree(&evlist->pollfd);  }  void perf_evlist__delete(struct perf_evlist *evlist)  { +	perf_evlist__munmap(evlist); +	perf_evlist__close(evlist); +	cpu_map__delete(evlist->cpus); +	thread_map__delete(evlist->threads); +	evlist->cpus = NULL; +	evlist->threads = NULL;  	perf_evlist__purge(evlist);  	perf_evlist__exit(evlist);  	free(evlist); @@ -104,6 +121,8 @@ void perf_evlist__delete(struct perf_evlist *evlist)  void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)  {  	list_add_tail(&entry->node, &evlist->entries); +	entry->idx = evlist->nr_entries; +  	if (!evlist->nr_entries++)  		perf_evlist__set_id_pos(evlist);  } @@ -129,7 +148,7 @@ void __perf_evlist__set_leader(struct list_head *list)  	leader->nr_members = evsel->idx - leader->idx + 1; -	list_for_each_entry(evsel, list, node) { +	__evlist__for_each(list, evsel) {  		evsel->leader = leader;  	}  } @@ -152,7 +171,7 @@ int perf_evlist__add_default(struct perf_evlist *evlist)  	event_attr_init(&attr); -	evsel = perf_evsel__new(&attr, 0); +	evsel = perf_evsel__new(&attr);  	if (evsel == NULL)  		goto error; @@ -177,7 +196,7 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist,  	size_t i;  	for (i = 0; i < nr_attrs; i++) { -		evsel = perf_evsel__new(attrs + i, evlist->nr_entries + i); +		evsel = perf_evsel__new_idx(attrs + i, evlist->nr_entries + i);  		if (evsel == NULL)  			goto out_delete_partial_list;  		list_add_tail(&evsel->node, &head); @@ -188,7 +207,7 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist,  	return 0;  out_delete_partial_list: -	list_for_each_entry_safe(evsel, n, &head, node) +	__evlist__for_each_safe(&head, n, evsel)  		perf_evsel__delete(evsel);  	return -1;  } @@ -209,7 +228,7 @@ perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if (evsel->attr.type   == PERF_TYPE_TRACEPOINT &&  		    (int)evsel->attr.config == id)  			return evsel; @@ -224,7 +243,7 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if ((evsel->attr.type == PERF_TYPE_TRACEPOINT) &&  		    (strcmp(evsel->name, name) == 0))  			return evsel; @@ -236,13 +255,12 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,  int perf_evlist__add_newtp(struct perf_evlist *evlist,  			   const char *sys, const char *name, void *handler)  { -	struct perf_evsel *evsel; +	struct perf_evsel *evsel = perf_evsel__newtp(sys, name); -	evsel = perf_evsel__newtp(sys, name, evlist->nr_entries);  	if (evsel == NULL)  		return -1; -	evsel->handler.func = handler; +	evsel->handler = handler;  	perf_evlist__add(evlist, evsel);  	return 0;  } @@ -255,7 +273,7 @@ void perf_evlist__disable(struct perf_evlist *evlist)  	int nr_threads = thread_map__nr(evlist->threads);  	for (cpu = 0; cpu < nr_cpus; cpu++) { -		list_for_each_entry(pos, &evlist->entries, node) { +		evlist__for_each(evlist, pos) {  			if (!perf_evsel__is_group_leader(pos) || !pos->fd)  				continue;  			for (thread = 0; thread < nr_threads; thread++) @@ -273,7 +291,7 @@ void perf_evlist__enable(struct perf_evlist *evlist)  	int nr_threads = thread_map__nr(evlist->threads);  	for (cpu = 0; cpu < nr_cpus; cpu++) { -		list_for_each_entry(pos, &evlist->entries, node) { +		evlist__for_each(evlist, pos) {  			if (!perf_evsel__is_group_leader(pos) || !pos->fd)  				continue;  			for (thread = 0; thread < nr_threads; thread++) @@ -527,7 +545,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)  		if ((old & md->mask) + size != ((old + size) & md->mask)) {  			unsigned int offset = old;  			unsigned int len = min(sizeof(*event), size), cpy; -			void *dst = &md->event_copy; +			void *dst = md->event_copy;  			do {  				cpy = min(md->mask + 1 - (offset & md->mask), len); @@ -537,7 +555,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)  				len -= cpy;  			} while (len); -			event = &md->event_copy; +			event = (union perf_event *) md->event_copy;  		}  		old += size; @@ -545,12 +563,19 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)  	md->prev = old; -	if (!evlist->overwrite) -		perf_mmap__write_tail(md, old); -  	return event;  } +void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) +{ +	if (!evlist->overwrite) { +		struct perf_mmap *md = &evlist->mmap[idx]; +		unsigned int old = md->prev; + +		perf_mmap__write_tail(md, old); +	} +} +  static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)  {  	if (evlist->mmap[idx].base != NULL) { @@ -563,11 +588,13 @@ void perf_evlist__munmap(struct perf_evlist *evlist)  {  	int i; +	if (evlist->mmap == NULL) +		return; +  	for (i = 0; i < evlist->nr_mmaps; i++)  		__perf_evlist__munmap(evlist, i); -	free(evlist->mmap); -	evlist->mmap = NULL; +	zfree(&evlist->mmap);  }  static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) @@ -587,6 +614,8 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist,  	evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot,  				      MAP_SHARED, fd, 0);  	if (evlist->mmap[idx].base == MAP_FAILED) { +		pr_debug2("failed to mmap perf event ring buffer, error %d\n", +			  errno);  		evlist->mmap[idx].base = NULL;  		return -1;  	} @@ -595,9 +624,36 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist,  	return 0;  } -static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int mask) +static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, +				       int prot, int mask, int cpu, int thread, +				       int *output)  {  	struct perf_evsel *evsel; + +	evlist__for_each(evlist, evsel) { +		int fd = FD(evsel, cpu, thread); + +		if (*output == -1) { +			*output = fd; +			if (__perf_evlist__mmap(evlist, idx, prot, mask, +						*output) < 0) +				return -1; +		} else { +			if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) +				return -1; +		} + +		if ((evsel->attr.read_format & PERF_FORMAT_ID) && +		    perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) +			return -1; +	} + +	return 0; +} + +static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, +				     int mask) +{  	int cpu, thread;  	int nr_cpus = cpu_map__nr(evlist->cpus);  	int nr_threads = thread_map__nr(evlist->threads); @@ -607,23 +663,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m  		int output = -1;  		for (thread = 0; thread < nr_threads; thread++) { -			list_for_each_entry(evsel, &evlist->entries, node) { -				int fd = FD(evsel, cpu, thread); - -				if (output == -1) { -					output = fd; -					if (__perf_evlist__mmap(evlist, cpu, -								prot, mask, output) < 0) -						goto out_unmap; -				} else { -					if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) -						goto out_unmap; -				} - -				if ((evsel->attr.read_format & PERF_FORMAT_ID) && -				    perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) -					goto out_unmap; -			} +			if (perf_evlist__mmap_per_evsel(evlist, cpu, prot, mask, +							cpu, thread, &output)) +				goto out_unmap;  		}  	} @@ -635,9 +677,9 @@ out_unmap:  	return -1;  } -static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, int mask) +static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, +					int mask)  { -	struct perf_evsel *evsel;  	int thread;  	int nr_threads = thread_map__nr(evlist->threads); @@ -645,23 +687,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in  	for (thread = 0; thread < nr_threads; thread++) {  		int output = -1; -		list_for_each_entry(evsel, &evlist->entries, node) { -			int fd = FD(evsel, 0, thread); - -			if (output == -1) { -				output = fd; -				if (__perf_evlist__mmap(evlist, thread, -							prot, mask, output) < 0) -					goto out_unmap; -			} else { -				if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) -					goto out_unmap; -			} - -			if ((evsel->attr.read_format & PERF_FORMAT_ID) && -			    perf_evlist__id_add_fd(evlist, evsel, 0, thread, fd) < 0) -				goto out_unmap; -		} +		if (perf_evlist__mmap_per_evsel(evlist, thread, prot, mask, 0, +						thread, &output)) +			goto out_unmap;  	}  	return 0; @@ -672,20 +700,92 @@ out_unmap:  	return -1;  } -/** perf_evlist__mmap - Create per cpu maps to receive events - * - * @evlist - list of events - * @pages - map length in pages - * @overwrite - overwrite older events? - * - * If overwrite is false the user needs to signal event consuption using: - * - *	struct perf_mmap *m = &evlist->mmap[cpu]; - *	unsigned int head = perf_mmap__read_head(m); +static size_t perf_evlist__mmap_size(unsigned long pages) +{ +	/* 512 kiB: default amount of unprivileged mlocked memory */ +	if (pages == UINT_MAX) +		pages = (512 * 1024) / page_size; +	else if (!is_power_of_2(pages)) +		return 0; + +	return (pages + 1) * page_size; +} + +static long parse_pages_arg(const char *str, unsigned long min, +			    unsigned long max) +{ +	unsigned long pages, val; +	static struct parse_tag tags[] = { +		{ .tag  = 'B', .mult = 1       }, +		{ .tag  = 'K', .mult = 1 << 10 }, +		{ .tag  = 'M', .mult = 1 << 20 }, +		{ .tag  = 'G', .mult = 1 << 30 }, +		{ .tag  = 0 }, +	}; + +	if (str == NULL) +		return -EINVAL; + +	val = parse_tag_value(str, tags); +	if (val != (unsigned long) -1) { +		/* we got file size value */ +		pages = PERF_ALIGN(val, page_size) / page_size; +	} else { +		/* we got pages count value */ +		char *eptr; +		pages = strtoul(str, &eptr, 10); +		if (*eptr != '\0') +			return -EINVAL; +	} + +	if (pages == 0 && min == 0) { +		/* leave number of pages at 0 */ +	} else if (!is_power_of_2(pages)) { +		/* round pages up to next power of 2 */ +		pages = next_pow2_l(pages); +		if (!pages) +			return -EINVAL; +		pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", +			pages * page_size, pages); +	} + +	if (pages > max) +		return -EINVAL; + +	return pages; +} + +int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, +				  int unset __maybe_unused) +{ +	unsigned int *mmap_pages = opt->value; +	unsigned long max = UINT_MAX; +	long pages; + +	if (max > SIZE_MAX / page_size) +		max = SIZE_MAX / page_size; + +	pages = parse_pages_arg(str, 1, max); +	if (pages < 0) { +		pr_err("Invalid argument for --mmap_pages/-m\n"); +		return -1; +	} + +	*mmap_pages = pages; +	return 0; +} + +/** + * perf_evlist__mmap - Create mmaps to receive events. + * @evlist: list of events + * @pages: map length in pages + * @overwrite: overwrite older events?   * - *	perf_mmap__write_tail(m, head) + * If @overwrite is %false the user needs to signal event consumption using + * perf_mmap__write_tail().  Using perf_evlist__mmap_read() does this + * automatically.   * - * Using perf_evlist__read_on_cpu does this automatically. + * Return: %0 on success, negative error code otherwise.   */  int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,  		      bool overwrite) @@ -695,14 +795,6 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,  	const struct thread_map *threads = evlist->threads;  	int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), mask; -        /* 512 kiB: default amount of unprivileged mlocked memory */ -        if (pages == UINT_MAX) -                pages = (512 * 1024) / page_size; -	else if (!is_power_of_2(pages)) -		return -EINVAL; - -	mask = pages * page_size - 1; -  	if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0)  		return -ENOMEM; @@ -710,9 +802,11 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,  		return -ENOMEM;  	evlist->overwrite = overwrite; -	evlist->mmap_len = (pages + 1) * page_size; +	evlist->mmap_len = perf_evlist__mmap_size(pages); +	pr_debug("mmap size %zuB\n", evlist->mmap_len); +	mask = evlist->mmap_len - page_size - 1; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if ((evsel->attr.read_format & PERF_FORMAT_ID) &&  		    evsel->sample_id == NULL &&  		    perf_evsel__alloc_id(evsel, cpu_map__nr(cpus), threads->nr) < 0) @@ -725,8 +819,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,  	return perf_evlist__mmap_per_cpu(evlist, prot, mask);  } -int perf_evlist__create_maps(struct perf_evlist *evlist, -			     struct perf_target *target) +int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)  {  	evlist->threads = thread_map__new_str(target->pid, target->tid,  					      target->uid); @@ -734,9 +827,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist,  	if (evlist->threads == NULL)  		return -1; -	if (perf_target__has_task(target)) -		evlist->cpus = cpu_map__dummy_new(); -	else if (!perf_target__has_cpu(target) && !target->uses_mmap) +	if (target__uses_dummy_map(target))  		evlist->cpus = cpu_map__dummy_new();  	else  		evlist->cpus = cpu_map__new(target->cpu_list); @@ -751,14 +842,6 @@ out_delete_threads:  	return -1;  } -void perf_evlist__delete_maps(struct perf_evlist *evlist) -{ -	cpu_map__delete(evlist->cpus); -	thread_map__delete(evlist->threads); -	evlist->cpus	= NULL; -	evlist->threads = NULL; -} -  int perf_evlist__apply_filters(struct perf_evlist *evlist)  {  	struct perf_evsel *evsel; @@ -766,7 +849,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist)  	const int ncpus = cpu_map__nr(evlist->cpus),  		  nthreads = thread_map__nr(evlist->threads); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if (evsel->filter == NULL)  			continue; @@ -785,7 +868,7 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)  	const int ncpus = cpu_map__nr(evlist->cpus),  		  nthreads = thread_map__nr(evlist->threads); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter);  		if (err)  			break; @@ -804,7 +887,7 @@ bool perf_evlist__valid_sample_type(struct perf_evlist *evlist)  	if (evlist->id_pos < 0 || evlist->is_pos < 0)  		return false; -	list_for_each_entry(pos, &evlist->entries, node) { +	evlist__for_each(evlist, pos) {  		if (pos->id_pos != evlist->id_pos ||  		    pos->is_pos != evlist->is_pos)  			return false; @@ -820,7 +903,7 @@ u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist)  	if (evlist->combined_sample_type)  		return evlist->combined_sample_type; -	list_for_each_entry(evsel, &evlist->entries, node) +	evlist__for_each(evlist, evsel)  		evlist->combined_sample_type |= evsel->attr.sample_type;  	return evlist->combined_sample_type; @@ -838,7 +921,7 @@ bool perf_evlist__valid_read_format(struct perf_evlist *evlist)  	u64 read_format = first->attr.read_format;  	u64 sample_type = first->attr.sample_type; -	list_for_each_entry_continue(pos, &evlist->entries, node) { +	evlist__for_each(evlist, pos) {  		if (read_format != pos->attr.read_format)  			return false;  	} @@ -895,7 +978,7 @@ bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist)  {  	struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; -	list_for_each_entry_continue(pos, &evlist->entries, node) { +	evlist__for_each_continue(evlist, pos) {  		if (first->attr.sample_id_all != pos->attr.sample_id_all)  			return false;  	} @@ -920,9 +1003,12 @@ void perf_evlist__close(struct perf_evlist *evlist)  	struct perf_evsel *evsel;  	int ncpus = cpu_map__nr(evlist->cpus);  	int nthreads = thread_map__nr(evlist->threads); +	int n; -	list_for_each_entry_reverse(evsel, &evlist->entries, node) -		perf_evsel__close(evsel, ncpus, nthreads); +	evlist__for_each_reverse(evlist, evsel) { +		n = evsel->cpus ? evsel->cpus->nr : ncpus; +		perf_evsel__close(evsel, n, nthreads); +	}  }  int perf_evlist__open(struct perf_evlist *evlist) @@ -932,7 +1018,7 @@ int perf_evlist__open(struct perf_evlist *evlist)  	perf_evlist__update_id_pos(evlist); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		err = perf_evsel__open(evsel, evlist->cpus, evlist->threads);  		if (err < 0)  			goto out_err; @@ -945,10 +1031,9 @@ out_err:  	return err;  } -int perf_evlist__prepare_workload(struct perf_evlist *evlist, -				  struct perf_target *target, +int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target,  				  const char *argv[], bool pipe_output, -				  bool want_signal) +				  void (*exec_error)(int signo, siginfo_t *info, void *ucontext))  {  	int child_ready_pipe[2], go_pipe[2];  	char bf; @@ -992,13 +1077,26 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist,  		execvp(argv[0], (char **)argv); -		perror(argv[0]); -		if (want_signal) -			kill(getppid(), SIGUSR1); +		if (exec_error) { +			union sigval val; + +			val.sival_int = errno; +			if (sigqueue(getppid(), SIGUSR1, val)) +				perror(argv[0]); +		} else +			perror(argv[0]);  		exit(-1);  	} -	if (perf_target__none(target)) +	if (exec_error) { +		struct sigaction act = { +			.sa_flags     = SA_SIGINFO, +			.sa_sigaction = exec_error, +		}; +		sigaction(SIGUSR1, &act, NULL); +	} + +	if (target__none(target))  		evlist->threads->map[0] = evlist->workload.pid;  	close(child_ready_pipe[1]); @@ -1059,10 +1157,89 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp)  	struct perf_evsel *evsel;  	size_t printed = 0; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		printed += fprintf(fp, "%s%s", evsel->idx ? ", " : "",  				   perf_evsel__name(evsel));  	} -	return printed + fprintf(fp, "\n");; +	return printed + fprintf(fp, "\n"); +} + +int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, +			     int err, char *buf, size_t size) +{ +	char sbuf[128]; + +	switch (err) { +	case ENOENT: +		scnprintf(buf, size, "%s", +			  "Error:\tUnable to find debugfs\n" +			  "Hint:\tWas your kernel was compiled with debugfs support?\n" +			  "Hint:\tIs the debugfs filesystem mounted?\n" +			  "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); +		break; +	case EACCES: +		scnprintf(buf, size, +			  "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" +			  "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", +			  debugfs_mountpoint, debugfs_mountpoint); +		break; +	default: +		scnprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); +		break; +	} + +	return 0; +} + +int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, +			       int err, char *buf, size_t size) +{ +	int printed, value; +	char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); + +	switch (err) { +	case EACCES: +	case EPERM: +		printed = scnprintf(buf, size, +				    "Error:\t%s.\n" +				    "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); + +		value = perf_event_paranoid(); + +		printed += scnprintf(buf + printed, size - printed, "\nHint:\t"); + +		if (value >= 2) { +			printed += scnprintf(buf + printed, size - printed, +					     "For your workloads it needs to be <= 1\nHint:\t"); +		} +		printed += scnprintf(buf + printed, size - printed, +				     "For system wide tracing it needs to be set to -1"); + +		printed += scnprintf(buf + printed, size - printed, +				    ".\nHint:\tThe current value is %d.", value); +		break; +	default: +		scnprintf(buf, size, "%s", emsg); +		break; +	} + +	return 0; +} + +void perf_evlist__to_front(struct perf_evlist *evlist, +			   struct perf_evsel *move_evsel) +{ +	struct perf_evsel *evsel, *n; +	LIST_HEAD(move); + +	if (move_evsel == perf_evlist__first(evlist)) +		return; + +	evlist__for_each_safe(evlist, n, evsel) { +		if (evsel->leader == move_evsel->leader) +			list_move_tail(&evsel->node, &move); +	} + +	list_splice(&move, &evlist->entries);  } diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 880d7139d2f..f5173cd6369 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -12,7 +12,7 @@  struct pollfd;  struct thread_map;  struct cpu_map; -struct perf_record_opts; +struct record_opts;  #define PERF_EVLIST__HLIST_BITS 8  #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) @@ -21,7 +21,7 @@ struct perf_mmap {  	void		 *base;  	int		 mask;  	unsigned int	 prev; -	union perf_event event_copy; +	char		 event_copy[PERF_SAMPLE_MAX_SIZE];  };  struct perf_evlist { @@ -31,7 +31,7 @@ struct perf_evlist {  	int		 nr_groups;  	int		 nr_fds;  	int		 nr_mmaps; -	int		 mmap_len; +	size_t		 mmap_len;  	int		 id_pos;  	int		 is_pos;  	u64		 combined_sample_type; @@ -53,6 +53,7 @@ struct perf_evsel_str_handler {  };  struct perf_evlist *perf_evlist__new(void); +struct perf_evlist *perf_evlist__new_default(void);  void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,  		       struct thread_map *threads);  void perf_evlist__exit(struct perf_evlist *evlist); @@ -87,22 +88,29 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);  struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id); -union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); +union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx); + +void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx);  int perf_evlist__open(struct perf_evlist *evlist);  void perf_evlist__close(struct perf_evlist *evlist);  void perf_evlist__set_id_pos(struct perf_evlist *evlist);  bool perf_can_sample_identifier(void); -void perf_evlist__config(struct perf_evlist *evlist, -			 struct perf_record_opts *opts); +void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts); +int record_opts__config(struct record_opts *opts);  int perf_evlist__prepare_workload(struct perf_evlist *evlist, -				  struct perf_target *target, +				  struct target *target,  				  const char *argv[], bool pipe_output, -				  bool want_signal); +				  void (*exec_error)(int signo, siginfo_t *info, +						     void *ucontext));  int perf_evlist__start_workload(struct perf_evlist *evlist); +int perf_evlist__parse_mmap_pages(const struct option *opt, +				  const char *str, +				  int unset); +  int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,  		      bool overwrite);  void perf_evlist__munmap(struct perf_evlist *evlist); @@ -126,9 +134,7 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist,  	evlist->threads	= threads;  } -int perf_evlist__create_maps(struct perf_evlist *evlist, -			     struct perf_target *target); -void perf_evlist__delete_maps(struct perf_evlist *evlist); +int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target);  int perf_evlist__apply_filters(struct perf_evlist *evlist);  void __perf_evlist__set_leader(struct list_head *list); @@ -163,10 +169,13 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist)  size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); +int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size); +int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); +  static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm)  {  	struct perf_event_mmap_page *pc = mm->base; -	int head = pc->data_head; +	int head = ACCESS_ONCE(pc->data_head);  	rmb();  	return head;  } @@ -179,8 +188,78 @@ static inline void perf_mmap__write_tail(struct perf_mmap *md,  	/*  	 * ensure all reads are done before we write the tail out.  	 */ -	/* mb(); */ +	mb();  	pc->data_tail = tail;  } +bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str); +void perf_evlist__to_front(struct perf_evlist *evlist, +			   struct perf_evsel *move_evsel); + +/** + * __evlist__for_each - iterate thru all the evsels + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each(list, evsel) \ +        list_for_each_entry(evsel, list, node) + +/** + * evlist__for_each - iterate thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each(evlist, evsel) \ +	__evlist__for_each(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_continue - continue iteration thru all the evsels + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_continue(list, evsel) \ +        list_for_each_entry_continue(evsel, list, node) + +/** + * evlist__for_each_continue - continue iteration thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each_continue(evlist, evsel) \ +	__evlist__for_each_continue(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_reverse - iterate thru all the evsels in reverse order + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_reverse(list, evsel) \ +        list_for_each_entry_reverse(evsel, list, node) + +/** + * evlist__for_each_reverse - iterate thru all the evsels in reverse order + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each_reverse(evlist, evsel) \ +	__evlist__for_each_reverse(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_safe - safely iterate thru all the evsels + * @list: list_head instance to iterate + * @tmp: struct evsel temp iterator + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_safe(list, tmp, evsel) \ +        list_for_each_entry_safe(evsel, tmp, list, node) + +/** + * evlist__for_each_safe - safely iterate thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + * @tmp: struct evsel temp iterator + */ +#define evlist__for_each_safe(evlist, tmp, evsel) \ +	__evlist__for_each_safe(&(evlist)->entries, tmp, evsel) +  #endif /* __PERF_EVLIST_H */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0ce9febf1ba..8606175fe1e 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -9,7 +9,7 @@  #include <byteswap.h>  #include <linux/bitops.h> -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include <traceevent/event-parse.h>  #include <linux/hw_breakpoint.h>  #include <linux/perf_event.h> @@ -23,6 +23,7 @@  #include "target.h"  #include "perf_regs.h"  #include "debug.h" +#include "trace-event.h"  static struct {  	bool sample_id_all; @@ -162,13 +163,15 @@ void perf_evsel__init(struct perf_evsel *evsel,  	evsel->idx	   = idx;  	evsel->attr	   = *attr;  	evsel->leader	   = evsel; +	evsel->unit	   = ""; +	evsel->scale	   = 1.0;  	INIT_LIST_HEAD(&evsel->node);  	hists__init(&evsel->hists);  	evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);  	perf_evsel__calc_id_pos(evsel);  } -struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) +struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)  {  	struct perf_evsel *evsel = zalloc(sizeof(*evsel)); @@ -178,48 +181,7 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)  	return evsel;  } -struct event_format *event_format__new(const char *sys, const char *name) -{ -	int fd, n; -	char *filename; -	void *bf = NULL, *nbf; -	size_t size = 0, alloc_size = 0; -	struct event_format *format = NULL; - -	if (asprintf(&filename, "%s/%s/%s/format", tracing_events_path, sys, name) < 0) -		goto out; - -	fd = open(filename, O_RDONLY); -	if (fd < 0) -		goto out_free_filename; - -	do { -		if (size == alloc_size) { -			alloc_size += BUFSIZ; -			nbf = realloc(bf, alloc_size); -			if (nbf == NULL) -				goto out_free_bf; -			bf = nbf; -		} - -		n = read(fd, bf + size, alloc_size - size); -		if (n < 0) -			goto out_free_bf; -		size += n; -	} while (n > 0); - -	pevent_parse_format(&format, bf, size, sys); - -out_free_bf: -	free(bf); -	close(fd); -out_free_filename: -	free(filename); -out: -	return format; -} - -struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx) +struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx)  {  	struct perf_evsel *evsel = zalloc(sizeof(*evsel)); @@ -233,7 +195,7 @@ struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx)  		if (asprintf(&evsel->name, "%s:%s", sys, name) < 0)  			goto out_free; -		evsel->tp_format = event_format__new(sys, name); +		evsel->tp_format = trace_event__tp_format(sys, name);  		if (evsel->tp_format == NULL)  			goto out_free; @@ -246,7 +208,7 @@ struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx)  	return evsel;  out_free: -	free(evsel->name); +	zfree(&evsel->name);  	free(evsel);  	return NULL;  } @@ -538,6 +500,34 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)  	return ret;  } +static void +perf_evsel__config_callgraph(struct perf_evsel *evsel, +			     struct record_opts *opts) +{ +	bool function = perf_evsel__is_function_event(evsel); +	struct perf_event_attr *attr = &evsel->attr; + +	perf_evsel__set_sample_bit(evsel, CALLCHAIN); + +	if (opts->call_graph == CALLCHAIN_DWARF) { +		if (!function) { +			perf_evsel__set_sample_bit(evsel, REGS_USER); +			perf_evsel__set_sample_bit(evsel, STACK_USER); +			attr->sample_regs_user = PERF_REGS_MASK; +			attr->sample_stack_user = opts->stack_dump_size; +			attr->exclude_callchain_user = 1; +		} else { +			pr_info("Cannot use DWARF unwind for function trace event," +				" falling back to framepointers.\n"); +		} +	} + +	if (function) { +		pr_info("Disabling user space callchains for function trace event.\n"); +		attr->exclude_callchain_user = 1; +	} +} +  /*   * The enable_on_exec/disabled value strategy:   * @@ -566,12 +556,12 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)   *     enable/disable events specifically, as there's no   *     initial traced exec call.   */ -void perf_evsel__config(struct perf_evsel *evsel, -			struct perf_record_opts *opts) +void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)  {  	struct perf_evsel *leader = evsel->leader;  	struct perf_event_attr *attr = &evsel->attr;  	int track = !evsel->idx; /* only the first counter needs these */ +	bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread;  	attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1;  	attr->inherit	    = !opts->no_inherit; @@ -599,10 +589,10 @@ void perf_evsel__config(struct perf_evsel *evsel,  	}  	/* -	 * We default some events to a 1 default interval. But keep +	 * We default some events to have a default interval. But keep  	 * it a weak assumption overridable by the user.  	 */ -	if (!attr->sample_period || (opts->user_freq != UINT_MAX && +	if (!attr->sample_period || (opts->user_freq != UINT_MAX ||  				     opts->user_interval != ULLONG_MAX)) {  		if (opts->freq) {  			perf_evsel__set_sample_bit(evsel, PERIOD); @@ -633,19 +623,10 @@ void perf_evsel__config(struct perf_evsel *evsel,  		attr->mmap_data = track;  	} -	if (opts->call_graph) { -		perf_evsel__set_sample_bit(evsel, CALLCHAIN); +	if (opts->call_graph_enabled) +		perf_evsel__config_callgraph(evsel, opts); -		if (opts->call_graph == CALLCHAIN_DWARF) { -			perf_evsel__set_sample_bit(evsel, REGS_USER); -			perf_evsel__set_sample_bit(evsel, STACK_USER); -			attr->sample_regs_user = PERF_REGS_MASK; -			attr->sample_stack_user = opts->stack_dump_size; -			attr->exclude_callchain_user = 1; -		} -	} - -	if (perf_target__has_cpu(&opts->target)) +	if (target__has_cpu(&opts->target))  		perf_evsel__set_sample_bit(evsel, CPU);  	if (opts->period) @@ -653,7 +634,7 @@ void perf_evsel__config(struct perf_evsel *evsel,  	if (!perf_missing_features.sample_id_all &&  	    (opts->sample_time || !opts->no_inherit || -	     perf_target__has_cpu(&opts->target))) +	     target__has_cpu(&opts->target) || per_cpu))  		perf_evsel__set_sample_bit(evsel, TIME);  	if (opts->raw_samples) { @@ -663,9 +644,9 @@ void perf_evsel__config(struct perf_evsel *evsel,  	}  	if (opts->sample_address) -		attr->sample_type	|= PERF_SAMPLE_DATA_SRC; +		perf_evsel__set_sample_bit(evsel, DATA_SRC); -	if (opts->no_delay) { +	if (opts->no_buffering) {  		attr->watermark = 0;  		attr->wakeup_events = 1;  	} @@ -675,12 +656,15 @@ void perf_evsel__config(struct perf_evsel *evsel,  	}  	if (opts->sample_weight) -		attr->sample_type	|= PERF_SAMPLE_WEIGHT; +		perf_evsel__set_sample_bit(evsel, WEIGHT);  	attr->mmap  = track;  	attr->mmap2 = track && !perf_missing_features.mmap2;  	attr->comm  = track; +	if (opts->sample_transaction) +		perf_evsel__set_sample_bit(evsel, TRANSACTION); +  	/*  	 * XXX see the function comment above  	 * @@ -694,7 +678,8 @@ void perf_evsel__config(struct perf_evsel *evsel,  	 * Setting enable_on_exec for independent events and  	 * group leaders for traced executed by perf.  	 */ -	if (perf_target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) +	if (target__none(&opts->target) && perf_evsel__is_group_leader(evsel) && +		!opts->initial_delay)  		attr->enable_on_exec = 1;  } @@ -786,8 +771,7 @@ void perf_evsel__free_id(struct perf_evsel *evsel)  {  	xyarray__delete(evsel->sample_id);  	evsel->sample_id = NULL; -	free(evsel->id); -	evsel->id = NULL; +	zfree(&evsel->id);  }  void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) @@ -803,7 +787,7 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)  void perf_evsel__free_counts(struct perf_evsel *evsel)  { -	free(evsel->counts); +	zfree(&evsel->counts);  }  void perf_evsel__exit(struct perf_evsel *evsel) @@ -817,10 +801,10 @@ void perf_evsel__delete(struct perf_evsel *evsel)  {  	perf_evsel__exit(evsel);  	close_cgroup(evsel->cgrp); -	free(evsel->group_name); +	zfree(&evsel->group_name);  	if (evsel->tp_format)  		pevent_free_format(evsel->tp_format); -	free(evsel->name); +	zfree(&evsel->name);  	free(evsel);  } @@ -983,6 +967,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp)  	ret += PRINT_ATTR2(exclude_host, exclude_guest);  	ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel,  			    "excl.callchain_user", exclude_callchain_user); +	ret += PRINT_ATTR_U32(mmap2);  	ret += PRINT_ATTR_U32(wakeup_events);  	ret += PRINT_ATTR_U32(wakeup_watermark); @@ -1039,7 +1024,7 @@ retry_sample_id:  			group_fd = get_group_fd(evsel, cpu, thread);  retry_open: -			pr_debug2("perf_event_open: pid %d  cpu %d  group_fd %d  flags %#lx\n", +			pr_debug2("sys_perf_event_open: pid %d  cpu %d  group_fd %d  flags %#lx\n",  				  pid, cpus->map[cpu], group_fd, flags);  			FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, @@ -1048,6 +1033,8 @@ retry_open:  								     group_fd, flags);  			if (FD(evsel, cpu, thread) < 0) {  				err = -errno; +				pr_debug2("sys_perf_event_open failed, error %d\n", +					  err);  				goto try_fallback;  			}  			set_rlimit = NO_CHANGE; @@ -1114,7 +1101,6 @@ void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads)  	perf_evsel__close_fd(evsel, ncpus, nthreads);  	perf_evsel__free_fd(evsel); -	evsel->fd = NULL;  }  static struct { @@ -1214,6 +1200,7 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel,  		sample->pid = u.val32[0];  		sample->tid = u.val32[1]; +		array--;  	}  	return 0; @@ -1253,7 +1240,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,  	memset(data, 0, sizeof(*data));  	data->cpu = data->pid = data->tid = -1;  	data->stream_id = data->id = data->time = -1ULL; -	data->period = 1; +	data->period = evsel->attr.sample_period;  	data->weight = 0;  	if (event->header.type != PERF_RECORD_SAMPLE) { @@ -1429,10 +1416,11 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,  		array++;  		if (data->user_regs.abi) { -			u64 regs_user = evsel->attr.sample_regs_user; +			u64 mask = evsel->attr.sample_regs_user; -			sz = hweight_long(regs_user) * sizeof(u64); +			sz = hweight_long(mask) * sizeof(u64);  			OVERFLOW_CHECK(array, sz, max_size); +			data->user_regs.mask = mask;  			data->user_regs.regs = (u64 *)array;  			array = (void *)array + sz;  		} @@ -1453,6 +1441,9 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,  			array = (void *)array + sz;  			OVERFLOW_CHECK_u64(array);  			data->user_stack.size = *array++; +			if (WARN_ONCE(data->user_stack.size > sz, +				      "user stack dump failure\n")) +				return -EFAULT;  		}  	} @@ -1470,11 +1461,18 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,  		array++;  	} +	data->transaction = 0; +	if (type & PERF_SAMPLE_TRANSACTION) { +		OVERFLOW_CHECK_u64(array); +		data->transaction = *array; +		array++; +	} +  	return 0;  }  size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, -				     u64 sample_regs_user, u64 read_format) +				     u64 read_format)  {  	size_t sz, result = sizeof(struct sample_event); @@ -1540,7 +1538,7 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,  	if (type & PERF_SAMPLE_REGS_USER) {  		if (sample->user_regs.abi) {  			result += sizeof(u64); -			sz = hweight_long(sample_regs_user) * sizeof(u64); +			sz = hweight_long(sample->user_regs.mask) * sizeof(u64);  			result += sz;  		} else {  			result += sizeof(u64); @@ -1562,11 +1560,14 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,  	if (type & PERF_SAMPLE_DATA_SRC)  		result += sizeof(u64); +	if (type & PERF_SAMPLE_TRANSACTION) +		result += sizeof(u64); +  	return result;  }  int perf_event__synthesize_sample(union perf_event *event, u64 type, -				  u64 sample_regs_user, u64 read_format, +				  u64 read_format,  				  const struct perf_sample *sample,  				  bool swapped)  { @@ -1707,7 +1708,7 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,  	if (type & PERF_SAMPLE_REGS_USER) {  		if (sample->user_regs.abi) {  			*array++ = sample->user_regs.abi; -			sz = hweight_long(sample_regs_user) * sizeof(u64); +			sz = hweight_long(sample->user_regs.mask) * sizeof(u64);  			memcpy(array, sample->user_regs.regs, sz);  			array = (void *)array + sz;  		} else { @@ -1735,6 +1736,11 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,  		array++;  	} +	if (type & PERF_SAMPLE_TRANSACTION) { +		*array = sample->transaction; +		array++; +	} +  	return 0;  } @@ -1974,16 +1980,14 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err,  		evsel->attr.type   = PERF_TYPE_SOFTWARE;  		evsel->attr.config = PERF_COUNT_SW_CPU_CLOCK; -		free(evsel->name); -		evsel->name = NULL; +		zfree(&evsel->name);  		return true;  	}  	return false;  } -int perf_evsel__open_strerror(struct perf_evsel *evsel, -			      struct perf_target *target, +int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,  			      int err, char *msg, size_t size)  {  	switch (err) { diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 4a7bdc713ba..a52e9a5bb2d 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -5,12 +5,12 @@  #include <stdbool.h>  #include <stddef.h>  #include <linux/perf_event.h> -#include "types.h" +#include <linux/types.h>  #include "xyarray.h"  #include "cgroup.h"  #include "hist.h"  #include "symbol.h" -  +  struct perf_counts_values {  	union {  		struct { @@ -68,16 +68,15 @@ struct perf_evsel {  	u32			ids;  	struct hists		hists;  	char			*name; +	double			scale; +	const char		*unit;  	struct event_format	*tp_format;  	union {  		void		*priv;  		off_t		id_offset;  	};  	struct cgroup_sel	*cgrp; -	struct { -		void		*func; -		void		*data; -	} handler; +	void			*handler;  	struct cpu_map		*cpus;  	unsigned int		sample_size;  	int			id_pos; @@ -92,15 +91,31 @@ struct perf_evsel {  	char			*group_name;  }; +union u64_swap { +	u64 val64; +	u32 val32[2]; +}; +  #define hists_to_evsel(h) container_of(h, struct perf_evsel, hists)  struct cpu_map;  struct thread_map;  struct perf_evlist; -struct perf_record_opts; +struct record_opts; + +struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx); + +static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) +{ +	return perf_evsel__new_idx(attr, 0); +} -struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); -struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx); +struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx); + +static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name) +{ +	return perf_evsel__newtp_idx(sys, name, 0); +}  struct event_format *event_format__new(const char *sys, const char *name); @@ -110,7 +125,7 @@ void perf_evsel__exit(struct perf_evsel *evsel);  void perf_evsel__delete(struct perf_evsel *evsel);  void perf_evsel__config(struct perf_evsel *evsel, -			struct perf_record_opts *opts); +			struct record_opts *opts);  int __perf_evsel__sample_size(u64 sample_type);  void perf_evsel__calc_id_pos(struct perf_evsel *evsel); @@ -130,6 +145,7 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];  int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,  					    char *bf, size_t size);  const char *perf_evsel__name(struct perf_evsel *evsel); +  const char *perf_evsel__group_name(struct perf_evsel *evsel);  int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); @@ -197,6 +213,12 @@ static inline bool perf_evsel__match2(struct perf_evsel *e1,  	       (e1->attr.config == e2->attr.config);  } +#define perf_evsel__cmp(a, b)			\ +	((a) &&					\ +	 (b) &&					\ +	 (a)->attr.type == (b)->attr.type &&	\ +	 (a)->attr.config == (b)->attr.config) +  int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,  			      int cpu, int thread, bool scale); @@ -265,6 +287,11 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel)  	return list_entry(evsel->node.next, struct perf_evsel, node);  } +static inline struct perf_evsel *perf_evsel__prev(struct perf_evsel *evsel) +{ +	return list_entry(evsel->node.prev, struct perf_evsel, node); +} +  /**   * perf_evsel__is_group_leader - Return whether given evsel is a leader event   * @@ -293,6 +320,24 @@ static inline bool perf_evsel__is_group_event(struct perf_evsel *evsel)  	return perf_evsel__is_group_leader(evsel) && evsel->nr_members > 1;  } +/** + * perf_evsel__is_function_event - Return whether given evsel is a function + * trace event + * + * @evsel - evsel selector to be tested + * + * Return %true if event is function trace event + */ +static inline bool perf_evsel__is_function_event(struct perf_evsel *evsel) +{ +#define FUNCTION_EVENT "ftrace:function" + +	return evsel->name && +	       !strncmp(FUNCTION_EVENT, evsel->name, sizeof(FUNCTION_EVENT)); + +#undef FUNCTION_EVENT +} +  struct perf_attr_details {  	bool freq;  	bool verbose; @@ -304,8 +349,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,  bool perf_evsel__fallback(struct perf_evsel *evsel, int err,  			  char *msg, size_t msgsize); -int perf_evsel__open_strerror(struct perf_evsel *evsel, -			      struct perf_target *target, +int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,  			      int err, char *msg, size_t size);  static inline int perf_evsel__group_idx(struct perf_evsel *evsel) diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh index 3ac38031d53..36a885d2cd2 100755 --- a/tools/perf/util/generate-cmdlist.sh +++ b/tools/perf/util/generate-cmdlist.sh @@ -22,7 +22,7 @@ do       }' "Documentation/perf-$cmd.txt"  done -echo "#ifdef LIBELF_SUPPORT" +echo "#ifdef HAVE_LIBELF_SUPPORT"  sed -n -e 's/^perf-\([^ 	]*\)[ 	].* full.*/\1/p' command-list.txt |  sort |  while read cmd @@ -35,5 +35,5 @@ do  	    p       }' "Documentation/perf-$cmd.txt"  done -echo "#endif /* LIBELF_SUPPORT */" +echo "#endif /* HAVE_LIBELF_SUPPORT */"  echo "};" diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index ce69901176d..893f8e2df92 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -22,6 +22,7 @@  #include "vdso.h"  #include "strbuf.h"  #include "build-id.h" +#include "data.h"  static bool no_buildid_cache = false; @@ -176,7 +177,7 @@ perf_header__set_cmdline(int argc, const char **argv)  			continue;		\  		else -static int write_buildid(char *name, size_t name_len, u8 *build_id, +static int write_buildid(const char *name, size_t name_len, u8 *build_id,  			 pid_t pid, u16 misc, int fd)  {  	int err; @@ -208,7 +209,7 @@ static int __dsos__write_buildid_table(struct list_head *head,  	dsos__for_each_with_build_id(pos, head) {  		int err; -		char  *name; +		const char *name;  		size_t name_len;  		if (!pos->hit) @@ -386,7 +387,7 @@ static int dso__cache_build_id(struct dso *dso, struct machine *machine,  {  	bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';  	bool is_vdso = is_vdso_map(dso->short_name); -	char *name = dso->long_name; +	const char *name = dso->long_name;  	char nm[PATH_MAX];  	if (dso__is_kcore(dso)) { @@ -642,8 +643,7 @@ static int write_event_desc(int fd, struct perf_header *h __maybe_unused,  	if (ret < 0)  		return ret; -	list_for_each_entry(evsel, &evlist->entries, node) { - +	evlist__for_each(evlist, evsel) {  		ret = do_write(fd, &evsel->attr, sz);  		if (ret < 0)  			return ret; @@ -799,10 +799,10 @@ static void free_cpu_topo(struct cpu_topo *tp)  		return;  	for (i = 0 ; i < tp->core_sib; i++) -		free(tp->core_siblings[i]); +		zfree(&tp->core_siblings[i]);  	for (i = 0 ; i < tp->thread_sib; i++) -		free(tp->thread_siblings[i]); +		zfree(&tp->thread_siblings[i]);  	free(tp);  } @@ -930,7 +930,7 @@ static int write_topo_node(int fd, int node)  		/* skip over invalid lines */  		if (!strchr(buf, ':'))  			continue; -		if (sscanf(buf, "%*s %*d %s %"PRIu64, field, &mem) != 2) +		if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)  			goto done;  		if (!strcmp(field, "MemTotal:"))  			mem_total = mem; @@ -1091,7 +1091,7 @@ static int write_group_desc(int fd, struct perf_header *h __maybe_unused,  	if (ret < 0)  		return ret; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if (perf_evsel__is_group_leader(evsel) &&  		    evsel->nr_members > 1) {  			const char *name = evsel->group_name ?: "{anon_group}"; @@ -1231,10 +1231,8 @@ static void free_event_desc(struct perf_evsel *events)  		return;  	for (evsel = events; evsel->attr.size; evsel++) { -		if (evsel->name) -			free(evsel->name); -		if (evsel->id) -			free(evsel->id); +		zfree(&evsel->name); +		zfree(&evsel->id);  	}  	free(events); @@ -1325,8 +1323,7 @@ read_event_desc(struct perf_header *ph, int fd)  		}  	}  out: -	if (buf) -		free(buf); +	free(buf);  	return events;  error:  	if (events) @@ -1489,7 +1486,7 @@ static void print_group_desc(struct perf_header *ph, int fd __maybe_unused,  	session = container_of(ph, struct perf_session, header); -	list_for_each_entry(evsel, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, evsel) {  		if (perf_evsel__is_group_leader(evsel) &&  		    evsel->nr_members > 1) {  			fprintf(fp, "# group: %s{%s", evsel->group_name ?: "", @@ -1708,7 +1705,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused,  			  struct perf_header *ph, int fd,  			  void *data __maybe_unused)  { -	size_t ret; +	ssize_t ret;  	u32 nr;  	ret = readn(fd, &nr, sizeof(nr)); @@ -1752,7 +1749,7 @@ static int process_total_mem(struct perf_file_section *section __maybe_unused,  			     void *data __maybe_unused)  {  	uint64_t mem; -	size_t ret; +	ssize_t ret;  	ret = readn(fd, &mem, sizeof(mem));  	if (ret != sizeof(mem)) @@ -1770,7 +1767,7 @@ perf_evlist__find_by_index(struct perf_evlist *evlist, int idx)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		if (evsel->idx == idx)  			return evsel;  	} @@ -1821,7 +1818,7 @@ static int process_cmdline(struct perf_file_section *section __maybe_unused,  			   struct perf_header *ph, int fd,  			   void *data __maybe_unused)  { -	size_t ret; +	ssize_t ret;  	char *str;  	u32 nr, i;  	struct strbuf sb; @@ -1857,7 +1854,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused  				struct perf_header *ph, int fd,  				void *data __maybe_unused)  { -	size_t ret; +	ssize_t ret;  	u32 nr, i;  	char *str;  	struct strbuf sb; @@ -1913,7 +1910,7 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse  				 struct perf_header *ph, int fd,  				 void *data __maybe_unused)  { -	size_t ret; +	ssize_t ret;  	u32 nr, node, i;  	char *str;  	uint64_t mem_total, mem_free; @@ -1973,7 +1970,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused  				struct perf_header *ph, int fd,  				void *data __maybe_unused)  { -	size_t ret; +	ssize_t ret;  	char *name;  	u32 pmu_num;  	u32 type; @@ -2073,12 +2070,14 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused,  	session->evlist->nr_groups = nr_groups;  	i = nr = 0; -	list_for_each_entry(evsel, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, evsel) {  		if (evsel->idx == (int) desc[i].leader_idx) {  			evsel->leader = evsel;  			/* {anon_group} is a dummy name */ -			if (strcmp(desc[i].name, "{anon_group}")) +			if (strcmp(desc[i].name, "{anon_group}")) {  				evsel->group_name = desc[i].name; +				desc[i].name = NULL; +			}  			evsel->nr_members = desc[i].nr_members;  			if (i >= nr_groups || nr > 0) { @@ -2104,8 +2103,8 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused,  	ret = 0;  out_free: -	while ((int) --i >= 0) -		free(desc[i].name); +	for (i = 0; i < nr_groups; i++) +		zfree(&desc[i].name);  	free(desc);  	return ret; @@ -2189,7 +2188,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)  {  	struct header_print_data hd;  	struct perf_header *header = &session->header; -	int fd = session->fd; +	int fd = perf_data_file__fd(session->file);  	hd.fp = fp;  	hd.full = full; @@ -2298,7 +2297,7 @@ int perf_session__write_header(struct perf_session *session,  	lseek(fd, sizeof(f_header), SEEK_SET); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(session->evlist, evsel) {  		evsel->id_offset = lseek(fd, 0, SEEK_CUR);  		err = do_write(fd, evsel->id, evsel->ids * sizeof(u64));  		if (err < 0) { @@ -2309,7 +2308,7 @@ int perf_session__write_header(struct perf_session *session,  	attr_offset = lseek(fd, 0, SEEK_CUR); -	list_for_each_entry(evsel, &evlist->entries, node) { +	evlist__for_each(evlist, evsel) {  		f_attr = (struct perf_file_attr){  			.attr = evsel->attr,  			.ids  = { @@ -2324,7 +2323,8 @@ int perf_session__write_header(struct perf_session *session,  		}  	} -	header->data_offset = lseek(fd, 0, SEEK_CUR); +	if (!header->data_offset) +		header->data_offset = lseek(fd, 0, SEEK_CUR);  	header->feat_offset = header->data_offset + header->data_size;  	if (at_exit) { @@ -2531,7 +2531,7 @@ static int check_magic_endian(u64 magic, uint64_t hdr_sz,  int perf_file_header__read(struct perf_file_header *header,  			   struct perf_header *ph, int fd)  { -	int ret; +	ssize_t ret;  	lseek(fd, 0, SEEK_SET); @@ -2625,7 +2625,7 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,  				       struct perf_header *ph, int fd,  				       bool repipe)  { -	int ret; +	ssize_t ret;  	ret = readn(fd, header, sizeof(*header));  	if (ret <= 0) @@ -2650,7 +2650,8 @@ static int perf_header__read_pipe(struct perf_session *session)  	struct perf_header *header = &session->header;  	struct perf_pipe_file_header f_header; -	if (perf_file_header__read_pipe(&f_header, header, session->fd, +	if (perf_file_header__read_pipe(&f_header, header, +					perf_data_file__fd(session->file),  					session->repipe) < 0) {  		pr_debug("incompatible file format\n");  		return -EINVAL; @@ -2665,7 +2666,7 @@ static int read_attr(int fd, struct perf_header *ph,  	struct perf_event_attr *attr = &f_attr->attr;  	size_t sz, left;  	size_t our_sz = sizeof(f_attr->attr); -	int ret; +	ssize_t ret;  	memset(f_attr, 0, sizeof(*f_attr)); @@ -2740,7 +2741,7 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist,  {  	struct perf_evsel *pos; -	list_for_each_entry(pos, &evlist->entries, node) { +	evlist__for_each(evlist, pos) {  		if (pos->attr.type == PERF_TYPE_TRACEPOINT &&  		    perf_evsel__prepare_tracepoint_event(pos, pevent))  			return -1; @@ -2751,23 +2752,36 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist,  int perf_session__read_header(struct perf_session *session)  { +	struct perf_data_file *file = session->file;  	struct perf_header *header = &session->header;  	struct perf_file_header	f_header;  	struct perf_file_attr	f_attr;  	u64			f_id;  	int nr_attrs, nr_ids, i, j; -	int fd = session->fd; +	int fd = perf_data_file__fd(file);  	session->evlist = perf_evlist__new();  	if (session->evlist == NULL)  		return -ENOMEM; -	if (session->fd_pipe) +	if (perf_data_file__is_pipe(file))  		return perf_header__read_pipe(session);  	if (perf_file_header__read(&f_header, header, fd) < 0)  		return -EINVAL; +	/* +	 * Sanity check that perf.data was written cleanly; data size is +	 * initialized to 0 and updated only if the on_exit function is run. +	 * If data size is still 0 then the file contains only partial +	 * information.  Just warn user and process it as much as it can. +	 */ +	if (f_header.data.size == 0) { +		pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" +			   "Was the 'perf record' command properly terminated?\n", +			   file->path); +	} +  	nr_attrs = f_header.attrs.size / f_header.attr_size;  	lseek(fd, f_header.attrs.offset, SEEK_SET); @@ -2782,7 +2796,7 @@ int perf_session__read_header(struct perf_session *session)  			perf_event__attr_swap(&f_attr.attr);  		tmp = lseek(fd, 0, SEEK_CUR); -		evsel = perf_evsel__new(&f_attr.attr, i); +		evsel = perf_evsel__new(&f_attr.attr);  		if (evsel == NULL)  			goto out_delete_evlist; @@ -2817,11 +2831,11 @@ int perf_session__read_header(struct perf_session *session)  	symbol_conf.nr_events = nr_attrs; -	perf_header__process_sections(header, fd, &session->pevent, +	perf_header__process_sections(header, fd, &session->tevent,  				      perf_file_section__process);  	if (perf_evlist__prepare_tracepoint_events(session->evlist, -						   session->pevent)) +						   session->tevent.pevent))  		goto out_delete_evlist;  	return 0; @@ -2875,7 +2889,7 @@ int perf_event__synthesize_attrs(struct perf_tool *tool,  	struct perf_evsel *evsel;  	int err = 0; -	list_for_each_entry(evsel, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, evsel) {  		err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids,  						  evsel->id, process);  		if (err) { @@ -2901,7 +2915,7 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused,  			return -ENOMEM;  	} -	evsel = perf_evsel__new(&event->attr.attr, evlist->nr_entries); +	evsel = perf_evsel__new(&event->attr.attr);  	if (evsel == NULL)  		return -ENOMEM; @@ -2978,18 +2992,19 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused,  				     struct perf_session *session)  {  	ssize_t size_read, padding, size = event->tracing_data.size; -	off_t offset = lseek(session->fd, 0, SEEK_CUR); +	int fd = perf_data_file__fd(session->file); +	off_t offset = lseek(fd, 0, SEEK_CUR);  	char buf[BUFSIZ];  	/* setup for reading amidst mmap */ -	lseek(session->fd, offset + sizeof(struct tracing_data_event), +	lseek(fd, offset + sizeof(struct tracing_data_event),  	      SEEK_SET); -	size_read = trace_report(session->fd, &session->pevent, +	size_read = trace_report(fd, &session->tevent,  				 session->repipe);  	padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; -	if (readn(session->fd, buf, padding) < 0) { +	if (readn(fd, buf, padding) < 0) {  		pr_err("%s: reading input file", __func__);  		return -1;  	} @@ -3007,7 +3022,7 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused,  	}  	perf_evlist__prepare_tracepoint_events(session->evlist, -					       session->pevent); +					       session->tevent.pevent);  	return size_read + padding;  } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 307c9aed972..d08cfe49940 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -4,10 +4,10 @@  #include <linux/perf_event.h>  #include <sys/types.h>  #include <stdbool.h> -#include "types.h" +#include <linux/bitmap.h> +#include <linux/types.h>  #include "event.h" -#include <linux/bitmap.h>  enum {  	HEADER_RESERVED		= 0,	/* always cleared */ @@ -77,16 +77,16 @@ struct perf_session_env {  	unsigned long long	total_mem;  	int			nr_cmdline; -	char			*cmdline;  	int			nr_sibling_cores; -	char			*sibling_cores;  	int			nr_sibling_threads; -	char			*sibling_threads;  	int			nr_numa_nodes; -	char			*numa_nodes;  	int			nr_pmu_mappings; -	char			*pmu_mappings;  	int			nr_groups; +	char			*cmdline; +	char			*sibling_cores; +	char			*sibling_threads; +	char			*numa_nodes; +	char			*pmu_mappings;  };  struct perf_header { diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 8b1f6e891b8..86c37c47226 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c @@ -22,8 +22,8 @@ static void clean_cmdnames(struct cmdnames *cmds)  	unsigned int i;  	for (i = 0; i < cmds->cnt; ++i) -		free(cmds->names[i]); -	free(cmds->names); +		zfree(&cmds->names[i]); +	zfree(&cmds->names);  	cmds->cnt = 0;  	cmds->alloc = 0;  } @@ -263,9 +263,8 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)  	for (i = 0; i < old->cnt; i++)  		cmds->names[cmds->cnt++] = old->names[i]; -	free(old->names); +	zfree(&old->names);  	old->cnt = 0; -	old->names = NULL;  }  const char *help_unknown_cmd(const char *cmd) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9ff6cf3e9a9..30df6187ee0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1,10 +1,10 @@ -#include "annotate.h"  #include "util.h"  #include "build-id.h"  #include "hist.h"  #include "session.h"  #include "sort.h"  #include "evsel.h" +#include "annotate.h"  #include <math.h>  static bool hists__filter_entry_by_dso(struct hists *hists, @@ -14,13 +14,6 @@ static bool hists__filter_entry_by_thread(struct hists *hists,  static bool hists__filter_entry_by_symbol(struct hists *hists,  					  struct hist_entry *he); -enum hist_filter { -	HIST_FILTER__DSO, -	HIST_FILTER__THREAD, -	HIST_FILTER__PARENT, -	HIST_FILTER__SYMBOL, -}; -  struct callchain_param	callchain_param = {  	.mode	= CHAIN_GRAPH_REL,  	.min_percent = 0.5, @@ -135,6 +128,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)  			       + unresolved_col_width + 2;  			hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,  					   symlen); +			hists__new_col_len(hists, HISTC_MEM_DCACHELINE, +					   symlen + 1);  		} else {  			symlen = unresolved_col_width + 4 + 2;  			hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, @@ -160,6 +155,10 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)  	hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3);  	hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);  	hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); + +	if (h->transaction) +		hists__new_col_len(hists, HISTC_TRANSACTION, +				   hist_entry__transaction_len());  }  void hists__output_recalc_col_len(struct hists *hists, int max_rows) @@ -178,21 +177,21 @@ void hists__output_recalc_col_len(struct hists *hists, int max_rows)  	}  } -static void hist_entry__add_cpumode_period(struct hist_entry *he, -					   unsigned int cpumode, u64 period) +static void he_stat__add_cpumode_period(struct he_stat *he_stat, +					unsigned int cpumode, u64 period)  {  	switch (cpumode) {  	case PERF_RECORD_MISC_KERNEL: -		he->stat.period_sys += period; +		he_stat->period_sys += period;  		break;  	case PERF_RECORD_MISC_USER: -		he->stat.period_us += period; +		he_stat->period_us += period;  		break;  	case PERF_RECORD_MISC_GUEST_KERNEL: -		he->stat.period_guest_sys += period; +		he_stat->period_guest_sys += period;  		break;  	case PERF_RECORD_MISC_GUEST_USER: -		he->stat.period_guest_us += period; +		he_stat->period_guest_us += period;  		break;  	default:  		break; @@ -219,24 +218,30 @@ static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src)  	dest->weight		+= src->weight;  } -static void hist_entry__decay(struct hist_entry *he) +static void he_stat__decay(struct he_stat *he_stat)  { -	he->stat.period = (he->stat.period * 7) / 8; -	he->stat.nr_events = (he->stat.nr_events * 7) / 8; +	he_stat->period = (he_stat->period * 7) / 8; +	he_stat->nr_events = (he_stat->nr_events * 7) / 8;  	/* XXX need decay for weight too? */  }  static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)  {  	u64 prev_period = he->stat.period; +	u64 diff;  	if (prev_period == 0)  		return true; -	hist_entry__decay(he); +	he_stat__decay(&he->stat); +	if (symbol_conf.cumulate_callchain) +		he_stat__decay(he->stat_acc); +	diff = prev_period - he->stat.period; + +	hists->stats.total_period -= diff;  	if (!he->filtered) -		hists->stats.total_period -= prev_period - he->stat.period; +		hists->stats.total_non_filtered_period -= diff;  	return he->stat.period == 0;  } @@ -263,8 +268,11 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)  			if (sort__need_collapse)  				rb_erase(&n->rb_node_in, &hists->entries_collapsed); -			hist_entry__free(n);  			--hists->nr_entries; +			if (!n->filtered) +				--hists->nr_non_filtered_entries; + +			hist_entry__free(n);  		}  	}  } @@ -273,25 +281,43 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)   * histogram, sorted on item, collects periods   */ -static struct hist_entry *hist_entry__new(struct hist_entry *template) +static struct hist_entry *hist_entry__new(struct hist_entry *template, +					  bool sample_self)  { -	size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; -	struct hist_entry *he = zalloc(sizeof(*he) + callchain_size); +	size_t callchain_size = 0; +	struct hist_entry *he; + +	if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) +		callchain_size = sizeof(struct callchain_root); + +	he = zalloc(sizeof(*he) + callchain_size);  	if (he != NULL) {  		*he = *template; +		if (symbol_conf.cumulate_callchain) { +			he->stat_acc = malloc(sizeof(he->stat)); +			if (he->stat_acc == NULL) { +				free(he); +				return NULL; +			} +			memcpy(he->stat_acc, &he->stat, sizeof(he->stat)); +			if (!sample_self) +				memset(&he->stat, 0, sizeof(he->stat)); +		} +  		if (he->ms.map)  			he->ms.map->referenced = true;  		if (he->branch_info) {  			/*  			 * This branch info is (a part of) allocated from -			 * machine__resolve_bstack() and will be freed after +			 * sample__resolve_bstack() and will be freed after  			 * adding new entries.  So we need to save a copy.  			 */  			he->branch_info = malloc(sizeof(*he->branch_info));  			if (he->branch_info == NULL) { +				free(he->stat_acc);  				free(he);  				return NULL;  			} @@ -321,15 +347,6 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)  	return he;  } -void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) -{ -	if (!h->filtered) { -		hists__calc_col_len(hists, h); -		++hists->nr_entries; -		hists->stats.total_period += h->stat.period; -	} -} -  static u8 symbol__parent_filter(const struct symbol *parent)  {  	if (symbol_conf.exclude_other && parent == NULL) @@ -338,15 +355,16 @@ static u8 symbol__parent_filter(const struct symbol *parent)  }  static struct hist_entry *add_hist_entry(struct hists *hists, -				      struct hist_entry *entry, -				      struct addr_location *al, -				      u64 period, -				      u64 weight) +					 struct hist_entry *entry, +					 struct addr_location *al, +					 bool sample_self)  {  	struct rb_node **p;  	struct rb_node *parent = NULL;  	struct hist_entry *he; -	int cmp; +	int64_t cmp; +	u64 period = entry->stat.period; +	u64 weight = entry->stat.weight;  	p = &hists->entries_in->rb_node; @@ -363,13 +381,16 @@ static struct hist_entry *add_hist_entry(struct hists *hists,  		cmp = hist_entry__cmp(he, entry);  		if (!cmp) { -			he_stat__add_period(&he->stat, period, weight); +			if (sample_self) +				he_stat__add_period(&he->stat, period, weight); +			if (symbol_conf.cumulate_callchain) +				he_stat__add_period(he->stat_acc, period, weight);  			/* -			 * This mem info was allocated from machine__resolve_mem +			 * This mem info was allocated from sample__resolve_mem  			 * and will not be used anymore.  			 */ -			free(entry->mem_info); +			zfree(&entry->mem_info);  			/* If the map of an existing hist_entry has  			 * become out-of-date due to an exec() or @@ -391,115 +412,488 @@ static struct hist_entry *add_hist_entry(struct hists *hists,  			p = &(*p)->rb_right;  	} -	he = hist_entry__new(entry); +	he = hist_entry__new(entry, sample_self);  	if (!he)  		return NULL;  	rb_link_node(&he->rb_node_in, parent, p);  	rb_insert_color(&he->rb_node_in, hists->entries_in);  out: -	hist_entry__add_cpumode_period(he, al->cpumode, period); +	if (sample_self) +		he_stat__add_cpumode_period(&he->stat, al->cpumode, period); +	if (symbol_conf.cumulate_callchain) +		he_stat__add_cpumode_period(he->stat_acc, al->cpumode, period);  	return he;  } -struct hist_entry *__hists__add_mem_entry(struct hists *self, -					  struct addr_location *al, -					  struct symbol *sym_parent, -					  struct mem_info *mi, -					  u64 period, -					  u64 weight) +struct hist_entry *__hists__add_entry(struct hists *hists, +				      struct addr_location *al, +				      struct symbol *sym_parent, +				      struct branch_info *bi, +				      struct mem_info *mi, +				      u64 period, u64 weight, u64 transaction, +				      bool sample_self)  {  	struct hist_entry entry = {  		.thread	= al->thread, +		.comm = thread__comm(al->thread),  		.ms = {  			.map	= al->map,  			.sym	= al->sym,  		}, +		.cpu	 = al->cpu, +		.cpumode = al->cpumode, +		.ip	 = al->addr, +		.level	 = al->level,  		.stat = { +			.nr_events = 1,  			.period	= period,  			.weight = weight, -			.nr_events = 1,  		}, -		.cpu	= al->cpu, -		.ip	= al->addr, -		.level	= al->level,  		.parent = sym_parent, -		.filtered = symbol__parent_filter(sym_parent), -		.hists = self, +		.filtered = symbol__parent_filter(sym_parent) | al->filtered, +		.hists	= hists, +		.branch_info = bi,  		.mem_info = mi, -		.branch_info = NULL, +		.transaction = transaction,  	}; -	return add_hist_entry(self, &entry, al, period, weight); + +	return add_hist_entry(hists, &entry, al, sample_self);  } -struct hist_entry *__hists__add_branch_entry(struct hists *self, -					     struct addr_location *al, -					     struct symbol *sym_parent, -					     struct branch_info *bi, -					     u64 period, -					     u64 weight) +static int +iter_next_nop_entry(struct hist_entry_iter *iter __maybe_unused, +		    struct addr_location *al __maybe_unused)  { -	struct hist_entry entry = { -		.thread	= al->thread, -		.ms = { -			.map	= bi->to.map, -			.sym	= bi->to.sym, -		}, -		.cpu	= al->cpu, -		.ip	= bi->to.addr, -		.level	= al->level, -		.stat = { -			.period	= period, -			.nr_events = 1, -			.weight = weight, -		}, -		.parent = sym_parent, -		.filtered = symbol__parent_filter(sym_parent), -		.branch_info = bi, -		.hists	= self, -		.mem_info = NULL, -	}; +	return 0; +} + +static int +iter_add_next_nop_entry(struct hist_entry_iter *iter __maybe_unused, +			struct addr_location *al __maybe_unused) +{ +	return 0; +} + +static int +iter_prepare_mem_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ +	struct perf_sample *sample = iter->sample; +	struct mem_info *mi; + +	mi = sample__resolve_mem(sample, al); +	if (mi == NULL) +		return -ENOMEM; -	return add_hist_entry(self, &entry, al, period, weight); +	iter->priv = mi; +	return 0;  } -struct hist_entry *__hists__add_entry(struct hists *self, -				      struct addr_location *al, -				      struct symbol *sym_parent, u64 period, -				      u64 weight) +static int +iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)  { -	struct hist_entry entry = { -		.thread	= al->thread, +	u64 cost; +	struct mem_info *mi = iter->priv; +	struct hist_entry *he; + +	if (mi == NULL) +		return -EINVAL; + +	cost = iter->sample->weight; +	if (!cost) +		cost = 1; + +	/* +	 * must pass period=weight in order to get the correct +	 * sorting from hists__collapse_resort() which is solely +	 * based on periods. We want sorting be done on nr_events * weight +	 * and this is indirectly achieved by passing period=weight here +	 * and the he_stat__add_period() function. +	 */ +	he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi, +				cost, cost, 0, true); +	if (!he) +		return -ENOMEM; + +	iter->he = he; +	return 0; +} + +static int +iter_finish_mem_entry(struct hist_entry_iter *iter, +		      struct addr_location *al __maybe_unused) +{ +	struct perf_evsel *evsel = iter->evsel; +	struct hist_entry *he = iter->he; +	int err = -EINVAL; + +	if (he == NULL) +		goto out; + +	hists__inc_nr_samples(&evsel->hists, he->filtered); + +	err = hist_entry__append_callchain(he, iter->sample); + +out: +	/* +	 * We don't need to free iter->priv (mem_info) here since +	 * the mem info was either already freed in add_hist_entry() or +	 * passed to a new hist entry by hist_entry__new(). +	 */ +	iter->priv = NULL; + +	iter->he = NULL; +	return err; +} + +static int +iter_prepare_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ +	struct branch_info *bi; +	struct perf_sample *sample = iter->sample; + +	bi = sample__resolve_bstack(sample, al); +	if (!bi) +		return -ENOMEM; + +	iter->curr = 0; +	iter->total = sample->branch_stack->nr; + +	iter->priv = bi; +	return 0; +} + +static int +iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused, +			     struct addr_location *al __maybe_unused) +{ +	/* to avoid calling callback function */ +	iter->he = NULL; + +	return 0; +} + +static int +iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ +	struct branch_info *bi = iter->priv; +	int i = iter->curr; + +	if (bi == NULL) +		return 0; + +	if (iter->curr >= iter->total) +		return 0; + +	al->map = bi[i].to.map; +	al->sym = bi[i].to.sym; +	al->addr = bi[i].to.addr; +	return 1; +} + +static int +iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ +	struct branch_info *bi; +	struct perf_evsel *evsel = iter->evsel; +	struct hist_entry *he = NULL; +	int i = iter->curr; +	int err = 0; + +	bi = iter->priv; + +	if (iter->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym)) +		goto out; + +	/* +	 * The report shows the percentage of total branches captured +	 * and not events sampled. Thus we use a pseudo period of 1. +	 */ +	he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL, +				1, 1, 0, true); +	if (he == NULL) +		return -ENOMEM; + +	hists__inc_nr_samples(&evsel->hists, he->filtered); + +out: +	iter->he = he; +	iter->curr++; +	return err; +} + +static int +iter_finish_branch_entry(struct hist_entry_iter *iter, +			 struct addr_location *al __maybe_unused) +{ +	zfree(&iter->priv); +	iter->he = NULL; + +	return iter->curr >= iter->total ? 0 : -1; +} + +static int +iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused, +			  struct addr_location *al __maybe_unused) +{ +	return 0; +} + +static int +iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ +	struct perf_evsel *evsel = iter->evsel; +	struct perf_sample *sample = iter->sample; +	struct hist_entry *he; + +	he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, +				sample->period, sample->weight, +				sample->transaction, true); +	if (he == NULL) +		return -ENOMEM; + +	iter->he = he; +	return 0; +} + +static int +iter_finish_normal_entry(struct hist_entry_iter *iter, +			 struct addr_location *al __maybe_unused) +{ +	struct hist_entry *he = iter->he; +	struct perf_evsel *evsel = iter->evsel; +	struct perf_sample *sample = iter->sample; + +	if (he == NULL) +		return 0; + +	iter->he = NULL; + +	hists__inc_nr_samples(&evsel->hists, he->filtered); + +	return hist_entry__append_callchain(he, sample); +} + +static int +iter_prepare_cumulative_entry(struct hist_entry_iter *iter __maybe_unused, +			      struct addr_location *al __maybe_unused) +{ +	struct hist_entry **he_cache; + +	callchain_cursor_commit(&callchain_cursor); + +	/* +	 * This is for detecting cycles or recursions so that they're +	 * cumulated only one time to prevent entries more than 100% +	 * overhead. +	 */ +	he_cache = malloc(sizeof(*he_cache) * (PERF_MAX_STACK_DEPTH + 1)); +	if (he_cache == NULL) +		return -ENOMEM; + +	iter->priv = he_cache; +	iter->curr = 0; + +	return 0; +} + +static int +iter_add_single_cumulative_entry(struct hist_entry_iter *iter, +				 struct addr_location *al) +{ +	struct perf_evsel *evsel = iter->evsel; +	struct perf_sample *sample = iter->sample; +	struct hist_entry **he_cache = iter->priv; +	struct hist_entry *he; +	int err = 0; + +	he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, +				sample->period, sample->weight, +				sample->transaction, true); +	if (he == NULL) +		return -ENOMEM; + +	iter->he = he; +	he_cache[iter->curr++] = he; + +	callchain_append(he->callchain, &callchain_cursor, sample->period); + +	/* +	 * We need to re-initialize the cursor since callchain_append() +	 * advanced the cursor to the end. +	 */ +	callchain_cursor_commit(&callchain_cursor); + +	hists__inc_nr_samples(&evsel->hists, he->filtered); + +	return err; +} + +static int +iter_next_cumulative_entry(struct hist_entry_iter *iter, +			   struct addr_location *al) +{ +	struct callchain_cursor_node *node; + +	node = callchain_cursor_current(&callchain_cursor); +	if (node == NULL) +		return 0; + +	return fill_callchain_info(al, node, iter->hide_unresolved); +} + +static int +iter_add_next_cumulative_entry(struct hist_entry_iter *iter, +			       struct addr_location *al) +{ +	struct perf_evsel *evsel = iter->evsel; +	struct perf_sample *sample = iter->sample; +	struct hist_entry **he_cache = iter->priv; +	struct hist_entry *he; +	struct hist_entry he_tmp = { +		.cpu = al->cpu, +		.thread = al->thread, +		.comm = thread__comm(al->thread), +		.ip = al->addr,  		.ms = { -			.map	= al->map, -			.sym	= al->sym, +			.map = al->map, +			.sym = al->sym,  		}, -		.cpu	= al->cpu, -		.ip	= al->addr, -		.level	= al->level, -		.stat = { -			.period	= period, -			.nr_events = 1, -			.weight = weight, -		}, -		.parent = sym_parent, -		.filtered = symbol__parent_filter(sym_parent), -		.hists	= self, -		.branch_info = NULL, -		.mem_info = NULL, +		.parent = iter->parent,  	}; +	int i; +	struct callchain_cursor cursor; + +	callchain_cursor_snapshot(&cursor, &callchain_cursor); + +	callchain_cursor_advance(&callchain_cursor); + +	/* +	 * Check if there's duplicate entries in the callchain. +	 * It's possible that it has cycles or recursive calls. +	 */ +	for (i = 0; i < iter->curr; i++) { +		if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) { +			/* to avoid calling callback function */ +			iter->he = NULL; +			return 0; +		} +	} + +	he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, +				sample->period, sample->weight, +				sample->transaction, false); +	if (he == NULL) +		return -ENOMEM; + +	iter->he = he; +	he_cache[iter->curr++] = he; -	return add_hist_entry(self, &entry, al, period, weight); +	callchain_append(he->callchain, &cursor, sample->period); +	return 0; +} + +static int +iter_finish_cumulative_entry(struct hist_entry_iter *iter, +			     struct addr_location *al __maybe_unused) +{ +	zfree(&iter->priv); +	iter->he = NULL; + +	return 0; +} + +const struct hist_iter_ops hist_iter_mem = { +	.prepare_entry 		= iter_prepare_mem_entry, +	.add_single_entry 	= iter_add_single_mem_entry, +	.next_entry 		= iter_next_nop_entry, +	.add_next_entry 	= iter_add_next_nop_entry, +	.finish_entry 		= iter_finish_mem_entry, +}; + +const struct hist_iter_ops hist_iter_branch = { +	.prepare_entry 		= iter_prepare_branch_entry, +	.add_single_entry 	= iter_add_single_branch_entry, +	.next_entry 		= iter_next_branch_entry, +	.add_next_entry 	= iter_add_next_branch_entry, +	.finish_entry 		= iter_finish_branch_entry, +}; + +const struct hist_iter_ops hist_iter_normal = { +	.prepare_entry 		= iter_prepare_normal_entry, +	.add_single_entry 	= iter_add_single_normal_entry, +	.next_entry 		= iter_next_nop_entry, +	.add_next_entry 	= iter_add_next_nop_entry, +	.finish_entry 		= iter_finish_normal_entry, +}; + +const struct hist_iter_ops hist_iter_cumulative = { +	.prepare_entry 		= iter_prepare_cumulative_entry, +	.add_single_entry 	= iter_add_single_cumulative_entry, +	.next_entry 		= iter_next_cumulative_entry, +	.add_next_entry 	= iter_add_next_cumulative_entry, +	.finish_entry 		= iter_finish_cumulative_entry, +}; + +int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, +			 struct perf_evsel *evsel, struct perf_sample *sample, +			 int max_stack_depth, void *arg) +{ +	int err, err2; + +	err = sample__resolve_callchain(sample, &iter->parent, evsel, al, +					max_stack_depth); +	if (err) +		return err; + +	iter->evsel = evsel; +	iter->sample = sample; + +	err = iter->ops->prepare_entry(iter, al); +	if (err) +		goto out; + +	err = iter->ops->add_single_entry(iter, al); +	if (err) +		goto out; + +	if (iter->he && iter->add_entry_cb) { +		err = iter->add_entry_cb(iter, al, true, arg); +		if (err) +			goto out; +	} + +	while (iter->ops->next_entry(iter, al)) { +		err = iter->ops->add_next_entry(iter, al); +		if (err) +			break; + +		if (iter->he && iter->add_entry_cb) { +			err = iter->add_entry_cb(iter, al, false, arg); +			if (err) +				goto out; +		} +	} + +out: +	err2 = iter->ops->finish_entry(iter, al); +	if (!err) +		err = err2; + +	return err;  }  int64_t  hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)  { -	struct sort_entry *se; +	struct perf_hpp_fmt *fmt;  	int64_t cmp = 0; -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		cmp = se->se_cmp(left, right); +	perf_hpp__for_each_sort_list(fmt) { +		if (perf_hpp__should_skip(fmt)) +			continue; + +		cmp = fmt->cmp(left, right);  		if (cmp)  			break;  	} @@ -510,15 +904,14 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)  int64_t  hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)  { -	struct sort_entry *se; +	struct perf_hpp_fmt *fmt;  	int64_t cmp = 0; -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		int64_t (*f)(struct hist_entry *, struct hist_entry *); - -		f = se->se_collapse ?: se->se_cmp; +	perf_hpp__for_each_sort_list(fmt) { +		if (perf_hpp__should_skip(fmt)) +			continue; -		cmp = f(left, right); +		cmp = fmt->collapse(left, right);  		if (cmp)  			break;  	} @@ -528,8 +921,10 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)  void hist_entry__free(struct hist_entry *he)  { -	free(he->branch_info); -	free(he->mem_info); +	zfree(&he->branch_info); +	zfree(&he->mem_info); +	zfree(&he->stat_acc); +	free_srcline(he->srcline);  	free(he);  } @@ -554,6 +949,8 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,  		if (!cmp) {  			he_stat__add_stat(&iter->stat, &he->stat); +			if (symbol_conf.cumulate_callchain) +				he_stat__add_stat(iter->stat_acc, he->stat_acc);  			if (symbol_conf.use_callchain) {  				callchain_cursor_reset(&callchain_cursor); @@ -598,7 +995,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he)  	hists__filter_entry_by_symbol(hists, he);  } -void hists__collapse_resort(struct hists *hists) +void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)  {  	struct rb_root *root;  	struct rb_node *next; @@ -625,67 +1022,55 @@ void hists__collapse_resort(struct hists *hists)  			 */  			hists__apply_filters(hists, n);  		} +		if (prog) +			ui_progress__update(prog, 1);  	}  } -/* - * reverse the map, sort on period. - */ - -static int period_cmp(u64 period_a, u64 period_b) -{ -	if (period_a > period_b) -		return 1; -	if (period_a < period_b) -		return -1; -	return 0; -} - -static int hist_entry__sort_on_period(struct hist_entry *a, -				      struct hist_entry *b) +static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)  { -	int ret; -	int i, nr_members; -	struct perf_evsel *evsel; -	struct hist_entry *pair; -	u64 *periods_a, *periods_b; +	struct perf_hpp_fmt *fmt; +	int64_t cmp = 0; -	ret = period_cmp(a->stat.period, b->stat.period); -	if (ret || !symbol_conf.event_group) -		return ret; +	perf_hpp__for_each_sort_list(fmt) { +		if (perf_hpp__should_skip(fmt)) +			continue; -	evsel = hists_to_evsel(a->hists); -	nr_members = evsel->nr_members; -	if (nr_members <= 1) -		return ret; +		cmp = fmt->sort(a, b); +		if (cmp) +			break; +	} -	periods_a = zalloc(sizeof(periods_a) * nr_members); -	periods_b = zalloc(sizeof(periods_b) * nr_members); +	return cmp; +} -	if (!periods_a || !periods_b) -		goto out; +static void hists__reset_filter_stats(struct hists *hists) +{ +	hists->nr_non_filtered_entries = 0; +	hists->stats.total_non_filtered_period = 0; +} -	list_for_each_entry(pair, &a->pairs.head, pairs.node) { -		evsel = hists_to_evsel(pair->hists); -		periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period; -	} +void hists__reset_stats(struct hists *hists) +{ +	hists->nr_entries = 0; +	hists->stats.total_period = 0; -	list_for_each_entry(pair, &b->pairs.head, pairs.node) { -		evsel = hists_to_evsel(pair->hists); -		periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period; -	} +	hists__reset_filter_stats(hists); +} -	for (i = 1; i < nr_members; i++) { -		ret = period_cmp(periods_a[i], periods_b[i]); -		if (ret) -			break; -	} +static void hists__inc_filter_stats(struct hists *hists, struct hist_entry *h) +{ +	hists->nr_non_filtered_entries++; +	hists->stats.total_non_filtered_period += h->stat.period; +} -out: -	free(periods_a); -	free(periods_b); +void hists__inc_stats(struct hists *hists, struct hist_entry *h) +{ +	if (!h->filtered) +		hists__inc_filter_stats(hists, h); -	return ret; +	hists->nr_entries++; +	hists->stats.total_period += h->stat.period;  }  static void __hists__insert_output_entry(struct rb_root *entries, @@ -704,7 +1089,7 @@ static void __hists__insert_output_entry(struct rb_root *entries,  		parent = *p;  		iter = rb_entry(parent, struct hist_entry, rb_node); -		if (hist_entry__sort_on_period(he, iter) > 0) +		if (hist_entry__sort(he, iter) > 0)  			p = &(*p)->rb_left;  		else  			p = &(*p)->rb_right; @@ -731,8 +1116,7 @@ void hists__output_resort(struct hists *hists)  	next = rb_first(root);  	hists->entries = RB_ROOT; -	hists->nr_entries = 0; -	hists->stats.total_period = 0; +	hists__reset_stats(hists);  	hists__reset_col_len(hists);  	while (next) { @@ -740,7 +1124,10 @@ void hists__output_resort(struct hists *hists)  		next = rb_next(&n->rb_node_in);  		__hists__insert_output_entry(&hists->entries, n, min_callchain_hits); -		hists__inc_nr_entries(hists, n); +		hists__inc_stats(hists, n); + +		if (!n->filtered) +			hists__calc_col_len(hists, n);  	}  } @@ -751,13 +1138,13 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h  	if (h->filtered)  		return; -	++hists->nr_entries; -	if (h->ms.unfolded) -		hists->nr_entries += h->nr_rows; +	/* force fold unfiltered entry for simplicity */ +	h->ms.unfolded = false;  	h->row_offset = 0; -	hists->stats.total_period += h->stat.period; -	hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events; +	hists->stats.nr_non_filtered_samples += h->stat.nr_events; + +	hists__inc_filter_stats(hists, h);  	hists__calc_col_len(hists, h);  } @@ -778,8 +1165,9 @@ void hists__filter_by_dso(struct hists *hists)  {  	struct rb_node *nd; -	hists->nr_entries = hists->stats.total_period = 0; -	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; +	hists->stats.nr_non_filtered_samples = 0; + +	hists__reset_filter_stats(hists);  	hists__reset_col_len(hists);  	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { @@ -811,8 +1199,9 @@ void hists__filter_by_thread(struct hists *hists)  {  	struct rb_node *nd; -	hists->nr_entries = hists->stats.total_period = 0; -	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; +	hists->stats.nr_non_filtered_samples = 0; + +	hists__reset_filter_stats(hists);  	hists__reset_col_len(hists);  	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { @@ -842,8 +1231,9 @@ void hists__filter_by_symbol(struct hists *hists)  {  	struct rb_node *nd; -	hists->nr_entries = hists->stats.total_period = 0; -	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; +	hists->stats.nr_non_filtered_samples = 0; + +	hists__reset_filter_stats(hists);  	hists__reset_col_len(hists);  	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { @@ -856,16 +1246,6 @@ void hists__filter_by_symbol(struct hists *hists)  	}  } -int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) -{ -	return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); -} - -int hist_entry__annotate(struct hist_entry *he, size_t privsize) -{ -	return symbol__annotate(he->ms.sym, he->ms.map, privsize); -} -  void events_stats__inc(struct events_stats *stats, u32 type)  {  	++stats->nr_events[0]; @@ -877,6 +1257,13 @@ void hists__inc_nr_events(struct hists *hists, u32 type)  	events_stats__inc(&hists->stats, type);  } +void hists__inc_nr_samples(struct hists *hists, bool filtered) +{ +	events_stats__inc(&hists->stats, PERF_RECORD_SAMPLE); +	if (!filtered) +		hists->stats.nr_non_filtered_samples++; +} +  static struct hist_entry *hists__add_dummy_entry(struct hists *hists,  						 struct hist_entry *pair)  { @@ -884,7 +1271,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,  	struct rb_node **p;  	struct rb_node *parent = NULL;  	struct hist_entry *he; -	int cmp; +	int64_t cmp;  	if (sort__need_collapse)  		root = &hists->entries_collapsed; @@ -908,13 +1295,13 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,  			p = &(*p)->rb_right;  	} -	he = hist_entry__new(pair); +	he = hist_entry__new(pair, true);  	if (he) {  		memset(&he->stat, 0, sizeof(he->stat));  		he->hists = hists;  		rb_link_node(&he->rb_node_in, parent, p);  		rb_insert_color(&he->rb_node_in, root); -		hists__inc_nr_entries(hists, he); +		hists__inc_stats(hists, he);  		he->dummy = true;  	}  out: @@ -998,3 +1385,30 @@ int hists__link(struct hists *leader, struct hists *other)  	return 0;  } + +u64 hists__total_period(struct hists *hists) +{ +	return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period : +		hists->stats.total_period; +} + +int parse_filter_percentage(const struct option *opt __maybe_unused, +			    const char *arg, int unset __maybe_unused) +{ +	if (!strcmp(arg, "relative")) +		symbol_conf.filter_relative = true; +	else if (!strcmp(arg, "absolute")) +		symbol_conf.filter_relative = false; +	else +		return -1; + +	return 0; +} + +int perf_hist_config(const char *var, const char *value) +{ +	if (!strcmp(var, "hist.percentage")) +		return parse_filter_percentage(NULL, value, 0); + +	return 0; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1329b6b6ffe..742f49a8572 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -5,6 +5,8 @@  #include <pthread.h>  #include "callchain.h"  #include "header.h" +#include "color.h" +#include "ui/progress.h"  extern struct callchain_param callchain_param; @@ -12,6 +14,15 @@ struct hist_entry;  struct addr_location;  struct symbol; +enum hist_filter { +	HIST_FILTER__DSO, +	HIST_FILTER__THREAD, +	HIST_FILTER__PARENT, +	HIST_FILTER__SYMBOL, +	HIST_FILTER__GUEST, +	HIST_FILTER__HOST, +}; +  /*   * The kernel collects the number of events it couldn't send in a stretch and   * when possible sends this number in a PERF_RECORD_LOST event. The number of @@ -26,9 +37,11 @@ struct symbol;   */  struct events_stats {  	u64 total_period; +	u64 total_non_filtered_period;  	u64 total_lost;  	u64 total_invalid_chains;  	u32 nr_events[PERF_RECORD_HEADER_MAX]; +	u32 nr_non_filtered_samples;  	u32 nr_lost_warned;  	u32 nr_unknown_events;  	u32 nr_invalid_chains; @@ -45,6 +58,8 @@ enum hist_column {  	HISTC_CPU,  	HISTC_SRCLINE,  	HISTC_MISPREDICT, +	HISTC_IN_TX, +	HISTC_ABORT,  	HISTC_SYMBOL_FROM,  	HISTC_SYMBOL_TO,  	HISTC_DSO_FROM, @@ -57,6 +72,8 @@ enum hist_column {  	HISTC_MEM_TLB,  	HISTC_MEM_LVL,  	HISTC_MEM_SNOOP, +	HISTC_MEM_DCACHELINE, +	HISTC_TRANSACTION,  	HISTC_NR_COLS, /* Last entry */  }; @@ -69,6 +86,7 @@ struct hists {  	struct rb_root		entries;  	struct rb_root		entries_collapsed;  	u64			nr_entries; +	u64			nr_non_filtered_entries;  	const struct thread	*thread_filter;  	const struct dso	*dso_filter;  	const char		*uid_filter_str; @@ -79,54 +97,87 @@ struct hists {  	u16			col_len[HISTC_NR_COLS];  }; -struct hist_entry *__hists__add_entry(struct hists *self, +struct hist_entry_iter; + +struct hist_iter_ops { +	int (*prepare_entry)(struct hist_entry_iter *, struct addr_location *); +	int (*add_single_entry)(struct hist_entry_iter *, struct addr_location *); +	int (*next_entry)(struct hist_entry_iter *, struct addr_location *); +	int (*add_next_entry)(struct hist_entry_iter *, struct addr_location *); +	int (*finish_entry)(struct hist_entry_iter *, struct addr_location *); +}; + +struct hist_entry_iter { +	int total; +	int curr; + +	bool hide_unresolved; + +	struct perf_evsel *evsel; +	struct perf_sample *sample; +	struct hist_entry *he; +	struct symbol *parent; +	void *priv; + +	const struct hist_iter_ops *ops; +	/* user-defined callback function (optional) */ +	int (*add_entry_cb)(struct hist_entry_iter *iter, +			    struct addr_location *al, bool single, void *arg); +}; + +extern const struct hist_iter_ops hist_iter_normal; +extern const struct hist_iter_ops hist_iter_branch; +extern const struct hist_iter_ops hist_iter_mem; +extern const struct hist_iter_ops hist_iter_cumulative; + +struct hist_entry *__hists__add_entry(struct hists *hists,  				      struct addr_location *al, -				      struct symbol *parent, u64 period, -				      u64 weight); +				      struct symbol *parent, +				      struct branch_info *bi, +				      struct mem_info *mi, u64 period, +				      u64 weight, u64 transaction, +				      bool sample_self); +int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, +			 struct perf_evsel *evsel, struct perf_sample *sample, +			 int max_stack_depth, void *arg); +  int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);  int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); -int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, +int hist_entry__transaction_len(void); +int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size,  			      struct hists *hists);  void hist_entry__free(struct hist_entry *); -struct hist_entry *__hists__add_branch_entry(struct hists *self, -					     struct addr_location *al, -					     struct symbol *sym_parent, -					     struct branch_info *bi, -					     u64 period, -					     u64 weight); - -struct hist_entry *__hists__add_mem_entry(struct hists *self, -					  struct addr_location *al, -					  struct symbol *sym_parent, -					  struct mem_info *mi, -					  u64 period, -					  u64 weight); - -void hists__output_resort(struct hists *self); -void hists__collapse_resort(struct hists *self); +void hists__output_resort(struct hists *hists); +void hists__collapse_resort(struct hists *hists, struct ui_progress *prog);  void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);  void hists__output_recalc_col_len(struct hists *hists, int max_rows); -void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); -void hists__inc_nr_events(struct hists *self, u32 type); +u64 hists__total_period(struct hists *hists); +void hists__reset_stats(struct hists *hists); +void hists__inc_stats(struct hists *hists, struct hist_entry *h); +void hists__inc_nr_events(struct hists *hists, u32 type); +void hists__inc_nr_samples(struct hists *hists, bool filtered);  void events_stats__inc(struct events_stats *stats, u32 type);  size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); -size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, +size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,  		      int max_cols, float min_pcnt, FILE *fp); -int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); -int hist_entry__annotate(struct hist_entry *self, size_t privsize); -  void hists__filter_by_dso(struct hists *hists);  void hists__filter_by_thread(struct hists *hists);  void hists__filter_by_symbol(struct hists *hists); -u16 hists__col_len(struct hists *self, enum hist_column col); -void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); -bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); +static inline bool hists__has_filter(struct hists *hists) +{ +	return hists->thread_filter || hists->dso_filter || +		hists->symbol_filter_str; +} + +u16 hists__col_len(struct hists *hists, enum hist_column col); +void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len); +bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len);  void hists__reset_col_len(struct hists *hists);  void hists__calc_col_len(struct hists *hists, struct hist_entry *he); @@ -141,21 +192,38 @@ struct perf_hpp {  };  struct perf_hpp_fmt { -	int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp); -	int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp); +	int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, +		      struct perf_evsel *evsel); +	int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, +		     struct perf_evsel *evsel);  	int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,  		     struct hist_entry *he);  	int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,  		     struct hist_entry *he); +	int64_t (*cmp)(struct hist_entry *a, struct hist_entry *b); +	int64_t (*collapse)(struct hist_entry *a, struct hist_entry *b); +	int64_t (*sort)(struct hist_entry *a, struct hist_entry *b);  	struct list_head list; +	struct list_head sort_list; +	bool elide;  };  extern struct list_head perf_hpp__list; +extern struct list_head perf_hpp__sort_list;  #define perf_hpp__for_each_format(format) \  	list_for_each_entry(format, &perf_hpp__list, list) +#define perf_hpp__for_each_format_safe(format, tmp)	\ +	list_for_each_entry_safe(format, tmp, &perf_hpp__list, list) + +#define perf_hpp__for_each_sort_list(format) \ +	list_for_each_entry(format, &perf_hpp__sort_list, sort_list) + +#define perf_hpp__for_each_sort_list_safe(format, tmp)	\ +	list_for_each_entry_safe(format, tmp, &perf_hpp__sort_list, sort_list) +  extern struct perf_hpp_fmt perf_hpp__format[];  enum { @@ -165,6 +233,7 @@ enum {  	PERF_HPP__OVERHEAD_US,  	PERF_HPP__OVERHEAD_GUEST_SYS,  	PERF_HPP__OVERHEAD_GUEST_US, +	PERF_HPP__OVERHEAD_ACC,  	PERF_HPP__SAMPLES,  	PERF_HPP__PERIOD, @@ -173,7 +242,54 @@ enum {  void perf_hpp__init(void);  void perf_hpp__column_register(struct perf_hpp_fmt *format); +void perf_hpp__column_unregister(struct perf_hpp_fmt *format);  void perf_hpp__column_enable(unsigned col); +void perf_hpp__column_disable(unsigned col); +void perf_hpp__cancel_cumulate(void); + +void perf_hpp__register_sort_field(struct perf_hpp_fmt *format); +void perf_hpp__setup_output_field(void); +void perf_hpp__reset_output_field(void); +void perf_hpp__append_sort_keys(void); + +bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); +bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); + +static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format) +{ +	return format->elide; +} + +void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); + +typedef u64 (*hpp_field_fn)(struct hist_entry *he); +typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); +typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); + +int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, +	       hpp_field_fn get_field, const char *fmt, +	       hpp_snprint_fn print_fn, bool fmt_percent); +int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he, +		   hpp_field_fn get_field, const char *fmt, +		   hpp_snprint_fn print_fn, bool fmt_percent); + +static inline void advance_hpp(struct perf_hpp *hpp, int inc) +{ +	hpp->buf  += inc; +	hpp->size -= inc; +} + +static inline size_t perf_hpp__use_color(void) +{ +	return !symbol_conf.field_sep; +} + +static inline size_t perf_hpp__color_overhead(void) +{ +	return perf_hpp__use_color() ? +	       (COLOR_MAXLEN + sizeof(PERF_COLOR_RESET)) * PERF_HPP__MAX_INDEX +	       : 0; +}  struct perf_evlist; @@ -183,7 +299,7 @@ struct hist_browser_timer {  	int refresh;  }; -#ifdef SLANG_SUPPORT +#ifdef HAVE_SLANG_SUPPORT  #include "../ui/keysyms.h"  int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,  			     struct hist_browser_timer *hbt); @@ -204,12 +320,9 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,  	return 0;  } -static inline int hist_entry__tui_annotate(struct hist_entry *self -					   __maybe_unused, -					   struct perf_evsel *evsel -					   __maybe_unused, -					   struct hist_browser_timer *hbt -					   __maybe_unused) +static inline int hist_entry__tui_annotate(struct hist_entry *he __maybe_unused, +					   struct perf_evsel *evsel __maybe_unused, +					   struct hist_browser_timer *hbt __maybe_unused)  {  	return 0;  } @@ -224,20 +337,11 @@ static inline int script_browse(const char *script_opt __maybe_unused)  #define K_SWITCH_INPUT_DATA -3000  #endif -#ifdef GTK2_SUPPORT -int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, -				  struct hist_browser_timer *hbt __maybe_unused, -				  float min_pcnt); -#else -static inline -int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, -				  const char *help __maybe_unused, -				  struct hist_browser_timer *hbt __maybe_unused, -				  float min_pcnt __maybe_unused) -{ -	return 0; -} -#endif +unsigned int hists__sort_list_width(struct hists *hists); + +struct option; +int parse_filter_percentage(const struct option *opt __maybe_unused, +			    const char *arg, int unset __maybe_unused); +int perf_hist_config(const char *var, const char *value); -unsigned int hists__sort_list_width(struct hists *self);  #endif	/* __PERF_HIST_H */ diff --git a/tools/perf/util/include/asm/bug.h b/tools/perf/util/include/asm/bug.h deleted file mode 100644 index 7fcc6810adc..00000000000 --- a/tools/perf/util/include/asm/bug.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _PERF_ASM_GENERIC_BUG_H -#define _PERF_ASM_GENERIC_BUG_H - -#define __WARN_printf(arg...)	do { fprintf(stderr, arg); } while (0) - -#define WARN(condition, format...) ({		\ -	int __ret_warn_on = !!(condition);	\ -	if (unlikely(__ret_warn_on))		\ -		__WARN_printf(format);		\ -	unlikely(__ret_warn_on);		\ -}) - -#define WARN_ONCE(condition, format...)	({	\ -	static int __warned;			\ -	int __ret_warn_once = !!(condition);	\ -						\ -	if (unlikely(__ret_warn_once))		\ -		if (WARN(!__warned, format)) 	\ -			__warned = 1;		\ -	unlikely(__ret_warn_once);		\ -}) -#endif diff --git a/tools/perf/util/include/asm/hash.h b/tools/perf/util/include/asm/hash.h new file mode 100644 index 00000000000..d82b170bb21 --- /dev/null +++ b/tools/perf/util/include/asm/hash.h @@ -0,0 +1,6 @@ +#ifndef __ASM_GENERIC_HASH_H +#define __ASM_GENERIC_HASH_H + +/* Stub */ + +#endif /* __ASM_GENERIC_HASH_H */ diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h index cf6727e99c4..8f149655f49 100644 --- a/tools/perf/util/include/dwarf-regs.h +++ b/tools/perf/util/include/dwarf-regs.h @@ -1,7 +1,7 @@  #ifndef _PERF_DWARF_REGS_H_  #define _PERF_DWARF_REGS_H_ -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT  const char *get_arch_regstr(unsigned int n);  #endif diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index bb162e40c76..01ffd12dc79 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h @@ -4,6 +4,9 @@  #include <string.h>  #include <linux/bitops.h> +#define DECLARE_BITMAP(name,bits) \ +	unsigned long name[BITS_TO_LONGS(bits)] +  int __bitmap_weight(const unsigned long *bitmap, int bits);  void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,  		 const unsigned long *bitmap2, int bits); diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index 45cf10a562b..dadfa7e5428 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h @@ -87,13 +87,15 @@ static __always_inline unsigned long __ffs(unsigned long word)  	return num;  } +typedef const unsigned long __attribute__((__may_alias__)) long_alias_t; +  /*   * Find the first set bit in a memory region.   */  static inline unsigned long  find_first_bit(const unsigned long *addr, unsigned long size)  { -	const unsigned long *p = addr; +	long_alias_t *p = (long_alias_t *) addr;  	unsigned long result = 0;  	unsigned long tmp; diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h deleted file mode 100644 index 96b919dae11..00000000000 --- a/tools/perf/util/include/linux/compiler.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _PERF_LINUX_COMPILER_H_ -#define _PERF_LINUX_COMPILER_H_ - -#ifndef __always_inline -#define __always_inline	inline -#endif -#define __user -#ifndef __attribute_const__ -#define __attribute_const__ -#endif - -#ifndef __maybe_unused -#define __maybe_unused		__attribute__((unused)) -#endif -#define __packed	__attribute__((__packed__)) - -#ifndef __force -#define __force -#endif - -#endif diff --git a/tools/perf/util/include/linux/export.h b/tools/perf/util/include/linux/export.h deleted file mode 100644 index b43e2dc21e0..00000000000 --- a/tools/perf/util/include/linux/export.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef PERF_LINUX_MODULE_H -#define PERF_LINUX_MODULE_H - -#define EXPORT_SYMBOL(name) - -#endif diff --git a/tools/perf/util/include/linux/hash.h b/tools/perf/util/include/linux/hash.h deleted file mode 100644 index 201f5739799..00000000000 --- a/tools/perf/util/include/linux/hash.h +++ /dev/null @@ -1,5 +0,0 @@ -#include "../../../../include/linux/hash.h" - -#ifndef PERF_HASH_H -#define PERF_HASH_H -#endif diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index d8c927c868e..9844c31b7c2 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h @@ -94,12 +94,6 @@ static inline int scnprintf(char * buf, size_t size, const char * fmt, ...)  	return (i >= ssize) ? (ssize - 1) : i;  } -static inline unsigned long -simple_strtoul(const char *nptr, char **endptr, int base) -{ -	return strtoul(nptr, endptr, base); -} -  int eprintf(int level,  	    const char *fmt, ...) __attribute__((format(printf, 2, 3))); diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index 1d928a0ce99..76ddbc72634 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h @@ -1,5 +1,5 @@  #include <linux/kernel.h> -#include <linux/prefetch.h> +#include <linux/types.h>  #include "../../../../include/linux/list.h" diff --git a/tools/perf/util/include/linux/magic.h b/tools/perf/util/include/linux/magic.h deleted file mode 100644 index 58b64ed4da1..00000000000 --- a/tools/perf/util/include/linux/magic.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _PERF_LINUX_MAGIC_H_ -#define _PERF_LINUX_MAGIC_H_ - -#ifndef DEBUGFS_MAGIC -#define DEBUGFS_MAGIC          0x64626720 -#endif - -#ifndef SYSFS_MAGIC -#define SYSFS_MAGIC            0x62656572 -#endif - -#endif diff --git a/tools/perf/util/include/linux/prefetch.h b/tools/perf/util/include/linux/prefetch.h deleted file mode 100644 index 7841e485d8c..00000000000 --- a/tools/perf/util/include/linux/prefetch.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef PERF_LINUX_PREFETCH_H -#define PERF_LINUX_PREFETCH_H - -static inline void prefetch(void *a __attribute__((unused))) { } - -#endif diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h deleted file mode 100644 index eb464786c08..00000000000 --- a/tools/perf/util/include/linux/types.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _PERF_LINUX_TYPES_H_ -#define _PERF_LINUX_TYPES_H_ - -#include <asm/types.h> - -#ifndef __bitwise -#define __bitwise -#endif - -#ifndef __le32 -typedef __u32 __bitwise __le32; -#endif - -#define DECLARE_BITMAP(name,bits) \ -	unsigned long name[BITS_TO_LONGS(bits)] - -struct list_head { -	struct list_head *next, *prev; -}; - -struct hlist_head { -	struct hlist_node *first; -}; - -struct hlist_node { -	struct hlist_node *next, **pprev; -}; - -#endif diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c index 11a8d86f7fe..89715b64a31 100644 --- a/tools/perf/util/intlist.c +++ b/tools/perf/util/intlist.c @@ -20,6 +20,7 @@ static struct rb_node *intlist__node_new(struct rblist *rblist __maybe_unused,  	if (node != NULL) {  		node->i = i; +		node->priv = NULL;  		rc = &node->rb_node;  	} @@ -57,22 +58,36 @@ void intlist__remove(struct intlist *ilist, struct int_node *node)  	rblist__remove_node(&ilist->rblist, &node->rb_node);  } -struct int_node *intlist__find(struct intlist *ilist, int i) +static struct int_node *__intlist__findnew(struct intlist *ilist, +					   int i, bool create)  { -	struct int_node *node; +	struct int_node *node = NULL;  	struct rb_node *rb_node;  	if (ilist == NULL)  		return NULL; -	node = NULL; -	rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); +	if (create) +		rb_node = rblist__findnew(&ilist->rblist, (void *)((long)i)); +	else +		rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); +  	if (rb_node)  		node = container_of(rb_node, struct int_node, rb_node);  	return node;  } +struct int_node *intlist__find(struct intlist *ilist, int i) +{ +	return __intlist__findnew(ilist, i, false); +} + +struct int_node *intlist__findnew(struct intlist *ilist, int i) +{ +	return __intlist__findnew(ilist, i, true); +} +  static int intlist__parse_list(struct intlist *ilist, const char *s)  {  	char *sep; diff --git a/tools/perf/util/intlist.h b/tools/perf/util/intlist.h index 62351dad848..aa6877d3685 100644 --- a/tools/perf/util/intlist.h +++ b/tools/perf/util/intlist.h @@ -9,6 +9,7 @@  struct int_node {  	struct rb_node rb_node;  	int i; +	void *priv;  };  struct intlist { @@ -23,6 +24,7 @@ int intlist__add(struct intlist *ilist, int i);  struct int_node *intlist__entry(const struct intlist *ilist, unsigned int idx);  struct int_node *intlist__find(struct intlist *ilist, int i); +struct int_node *intlist__findnew(struct intlist *ilist, int i);  static inline bool intlist__has_entry(struct intlist *ilist, int i)  { diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 6188d2876a7..c73e1fc12e5 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -9,6 +9,7 @@  #include "strlist.h"  #include "thread.h"  #include <stdbool.h> +#include <symbol/kallsyms.h>  #include "unwind.h"  int machine__init(struct machine *machine, const char *root_dir, pid_t pid) @@ -26,6 +27,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)  	machine->pid = pid;  	machine->symbol_filter = NULL; +	machine->id_hdr_size = 0;  	machine->root_dir = strdup(root_dir);  	if (machine->root_dir == NULL) @@ -40,12 +42,29 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)  			return -ENOMEM;  		snprintf(comm, sizeof(comm), "[guest/%d]", pid); -		thread__set_comm(thread, comm); +		thread__set_comm(thread, comm, 0);  	}  	return 0;  } +struct machine *machine__new_host(void) +{ +	struct machine *machine = malloc(sizeof(*machine)); + +	if (machine != NULL) { +		machine__init(machine, "", HOST_KERNEL_ID); + +		if (machine__create_kernel_maps(machine) < 0) +			goto out_delete; +	} + +	return machine; +out_delete: +	free(machine); +	return NULL; +} +  static void dsos__delete(struct list_head *dsos)  {  	struct dso *pos, *n; @@ -84,8 +103,7 @@ void machine__exit(struct machine *machine)  	map_groups__exit(&machine->kmaps);  	dsos__delete(&machine->user_dsos);  	dsos__delete(&machine->kernel_dsos); -	free(machine->root_dir); -	machine->root_dir = NULL; +	zfree(&machine->root_dir);  }  void machine__delete(struct machine *machine) @@ -298,6 +316,17 @@ static struct thread *__machine__findnew_thread(struct machine *machine,  		rb_link_node(&th->rb_node, parent, p);  		rb_insert_color(&th->rb_node, &machine->threads);  		machine->last_match = th; + +		/* +		 * We have to initialize map_groups separately +		 * after rb tree is updated. +		 * +		 * The reason is that we call machine__findnew_thread +		 * within thread__init_map_groups to find the thread +		 * leader and that would screwed the rb tree. +		 */ +		if (thread__init_map_groups(th, machine)) +			return NULL;  	}  	return th; @@ -309,12 +338,14 @@ struct thread *machine__findnew_thread(struct machine *machine, pid_t pid,  	return __machine__findnew_thread(machine, pid, tid, true);  } -struct thread *machine__find_thread(struct machine *machine, pid_t tid) +struct thread *machine__find_thread(struct machine *machine, pid_t pid, +				    pid_t tid)  { -	return __machine__findnew_thread(machine, 0, tid, false); +	return __machine__findnew_thread(machine, pid, tid, false);  } -int machine__process_comm_event(struct machine *machine, union perf_event *event) +int machine__process_comm_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample)  {  	struct thread *thread = machine__findnew_thread(machine,  							event->comm.pid, @@ -323,7 +354,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event  	if (dump_trace)  		perf_event__fprintf_comm(event, stdout); -	if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { +	if (thread == NULL || thread__set_comm(thread, event->comm.comm, sample->time)) {  		dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");  		return -1;  	} @@ -332,7 +363,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event  }  int machine__process_lost_event(struct machine *machine __maybe_unused, -				union perf_event *event) +				union perf_event *event, struct perf_sample *sample __maybe_unused)  {  	dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",  		    event->lost.id, event->lost.lost); @@ -465,49 +496,50 @@ struct process_args {  	u64 start;  }; -static int symbol__in_kernel(void *arg, const char *name, -			     char type __maybe_unused, u64 start) +static void machine__get_kallsyms_filename(struct machine *machine, char *buf, +					   size_t bufsz)  { -	struct process_args *args = arg; - -	if (strchr(name, '[')) -		return 0; - -	args->start = start; -	return 1; +	if (machine__is_default_guest(machine)) +		scnprintf(buf, bufsz, "%s", symbol_conf.default_guest_kallsyms); +	else +		scnprintf(buf, bufsz, "%s/proc/kallsyms", machine->root_dir);  } -/* Figure out the start address of kernel map from /proc/kallsyms */ -static u64 machine__get_kernel_start_addr(struct machine *machine) +const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL}; + +/* Figure out the start address of kernel map from /proc/kallsyms. + * Returns the name of the start symbol in *symbol_name. Pass in NULL as + * symbol_name if it's not that important. + */ +static u64 machine__get_kernel_start_addr(struct machine *machine, +					  const char **symbol_name)  { -	const char *filename; -	char path[PATH_MAX]; -	struct process_args args; +	char filename[PATH_MAX]; +	int i; +	const char *name; +	u64 addr = 0; -	if (machine__is_host(machine)) { -		filename = "/proc/kallsyms"; -	} else { -		if (machine__is_default_guest(machine)) -			filename = (char *)symbol_conf.default_guest_kallsyms; -		else { -			sprintf(path, "%s/proc/kallsyms", machine->root_dir); -			filename = path; -		} -	} +	machine__get_kallsyms_filename(machine, filename, PATH_MAX);  	if (symbol__restricted_filename(filename, "/proc/kallsyms"))  		return 0; -	if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) -		return 0; +	for (i = 0; (name = ref_reloc_sym_names[i]) != NULL; i++) { +		addr = kallsyms__get_function_start(filename, name); +		if (addr) +			break; +	} + +	if (symbol_name) +		*symbol_name = name; -	return args.start; +	return addr;  }  int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)  {  	enum map_type type; -	u64 start = machine__get_kernel_start_addr(machine); +	u64 start = machine__get_kernel_start_addr(machine, NULL);  	for (type = 0; type < MAP__NR_TYPES; ++type) {  		struct kmap *kmap; @@ -547,11 +579,10 @@ void machine__destroy_kernel_maps(struct machine *machine)  			 * on one of them.  			 */  			if (type == MAP__FUNCTION) { -				free((char *)kmap->ref_reloc_sym->name); -				kmap->ref_reloc_sym->name = NULL; -				free(kmap->ref_reloc_sym); -			} -			kmap->ref_reloc_sym = NULL; +				zfree((char **)&kmap->ref_reloc_sym->name); +				zfree(&kmap->ref_reloc_sym); +			} else +				kmap->ref_reloc_sym = NULL;  		}  		map__delete(machine->vmlinux_maps[type]); @@ -699,7 +730,7 @@ static char *get_kernel_version(const char *root_dir)  }  static int map_groups__set_modules_path_dir(struct map_groups *mg, -				const char *dir_name) +				const char *dir_name, int depth)  {  	struct dirent *dent;  	DIR *dir = opendir(dir_name); @@ -724,7 +755,15 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,  			    !strcmp(dent->d_name, ".."))  				continue; -			ret = map_groups__set_modules_path_dir(mg, path); +			/* Do not follow top-level source and build symlinks */ +			if (depth == 0) { +				if (!strcmp(dent->d_name, "source") || +				    !strcmp(dent->d_name, "build")) +					continue; +			} + +			ret = map_groups__set_modules_path_dir(mg, path, +							       depth + 1);  			if (ret < 0)  				goto out;  		} else { @@ -749,8 +788,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,  				ret = -1;  				goto out;  			} -			dso__set_long_name(map->dso, long_name); -			map->dso->lname_alloc = 1; +			dso__set_long_name(map->dso, long_name, true);  			dso__kernel_module_get_build_id(map->dso, "");  		}  	} @@ -769,87 +807,60 @@ static int machine__set_modules_path(struct machine *machine)  	if (!version)  		return -1; -	snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", +	snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s",  		 machine->root_dir, version);  	free(version); -	return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); +	return map_groups__set_modules_path_dir(&machine->kmaps, modules_path, 0);  } -static int machine__create_modules(struct machine *machine) +static int machine__create_module(void *arg, const char *name, u64 start)  { -	char *line = NULL; -	size_t n; -	FILE *file; +	struct machine *machine = arg;  	struct map *map; + +	map = machine__new_module(machine, start, name); +	if (map == NULL) +		return -1; + +	dso__kernel_module_get_build_id(map->dso, machine->root_dir); + +	return 0; +} + +static int machine__create_modules(struct machine *machine) +{  	const char *modules;  	char path[PATH_MAX]; -	if (machine__is_default_guest(machine)) +	if (machine__is_default_guest(machine)) {  		modules = symbol_conf.default_guest_modules; -	else { -		sprintf(path, "%s/proc/modules", machine->root_dir); +	} else { +		snprintf(path, PATH_MAX, "%s/proc/modules", machine->root_dir);  		modules = path;  	}  	if (symbol__restricted_filename(modules, "/proc/modules"))  		return -1; -	file = fopen(modules, "r"); -	if (file == NULL) +	if (modules__parse(modules, machine, machine__create_module))  		return -1; -	while (!feof(file)) { -		char name[PATH_MAX]; -		u64 start; -		char *sep; -		int line_len; - -		line_len = getline(&line, &n, file); -		if (line_len < 0) -			break; - -		if (!line) -			goto out_failure; - -		line[--line_len] = '\0'; /* \n */ - -		sep = strrchr(line, 'x'); -		if (sep == NULL) -			continue; - -		hex2u64(sep + 1, &start); - -		sep = strchr(line, ' '); -		if (sep == NULL) -			continue; - -		*sep = '\0'; - -		snprintf(name, sizeof(name), "[%s]", line); -		map = machine__new_module(machine, start, name); -		if (map == NULL) -			goto out_delete_line; -		dso__kernel_module_get_build_id(map->dso, machine->root_dir); -	} +	if (!machine__set_modules_path(machine)) +		return 0; -	free(line); -	fclose(file); +	pr_debug("Problems setting modules path maps, continuing anyway...\n"); -	if (machine__set_modules_path(machine) < 0) { -		pr_debug("Problems setting modules path maps, continuing anyway...\n"); -	}  	return 0; - -out_delete_line: -	free(line); -out_failure: -	return -1;  }  int machine__create_kernel_maps(struct machine *machine)  {  	struct dso *kernel = machine__get_kernel(machine); +	const char *name; +	u64 addr = machine__get_kernel_start_addr(machine, &name); +	if (!addr) +		return -1;  	if (kernel == NULL ||  	    __machine__create_kernel_maps(machine, kernel) < 0) @@ -868,6 +879,13 @@ int machine__create_kernel_maps(struct machine *machine)  	 * Now that we have all the maps created, just set the ->end of them:  	 */  	map_groups__fixup_end(&machine->kmaps); + +	if (maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, name, +					     addr)) { +		machine__destroy_kernel_maps(machine); +		return -1; +	} +  	return 0;  } @@ -952,8 +970,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine,  		if (name == NULL)  			goto out_problem; -		map->dso->short_name = name; -		map->dso->sname_alloc = 1; +		dso__set_short_name(map->dso, name, true);  		map->end = map->start + event->mmap.len;  	} else if (is_kernel_mmap) {  		const char *symbol_name = (event->mmap.filename + @@ -998,7 +1015,8 @@ out_problem:  }  int machine__process_mmap2_event(struct machine *machine, -				 union perf_event *event) +				 union perf_event *event, +				 struct perf_sample *sample __maybe_unused)  {  	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;  	struct thread *thread; @@ -1018,7 +1036,7 @@ int machine__process_mmap2_event(struct machine *machine,  	}  	thread = machine__findnew_thread(machine, event->mmap2.pid, -					event->mmap2.pid); +					event->mmap2.tid);  	if (thread == NULL)  		goto out_problem; @@ -1032,6 +1050,8 @@ int machine__process_mmap2_event(struct machine *machine,  			event->mmap2.pid, event->mmap2.maj,  			event->mmap2.min, event->mmap2.ino,  			event->mmap2.ino_generation, +			event->mmap2.prot, +			event->mmap2.flags,  			event->mmap2.filename, type);  	if (map == NULL) @@ -1045,7 +1065,8 @@ out_problem:  	return 0;  } -int machine__process_mmap_event(struct machine *machine, union perf_event *event) +int machine__process_mmap_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample __maybe_unused)  {  	u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;  	struct thread *thread; @@ -1065,7 +1086,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event  	}  	thread = machine__findnew_thread(machine, event->mmap.pid, -					 event->mmap.pid); +					 event->mmap.tid);  	if (thread == NULL)  		goto out_problem; @@ -1076,7 +1097,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event  	map = map__new(&machine->user_dsos, event->mmap.start,  			event->mmap.len, event->mmap.pgoff, -			event->mmap.pid, 0, 0, 0, 0, +			event->mmap.pid, 0, 0, 0, 0, 0, 0,  			event->mmap.filename,  			type); @@ -1102,9 +1123,12 @@ static void machine__remove_thread(struct machine *machine, struct thread *th)  	list_add_tail(&th->node, &machine->dead_threads);  } -int machine__process_fork_event(struct machine *machine, union perf_event *event) +int machine__process_fork_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample)  { -	struct thread *thread = machine__find_thread(machine, event->fork.tid); +	struct thread *thread = machine__find_thread(machine, +						     event->fork.pid, +						     event->fork.tid);  	struct thread *parent = machine__findnew_thread(machine,  							event->fork.ppid,  							event->fork.ptid); @@ -1119,7 +1143,7 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event  		perf_event__fprintf_task(event, stdout);  	if (thread == NULL || parent == NULL || -	    thread__fork(thread, parent) < 0) { +	    thread__fork(thread, parent, sample->time) < 0) {  		dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");  		return -1;  	} @@ -1127,10 +1151,12 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event  	return 0;  } -int machine__process_exit_event(struct machine *machine __maybe_unused, -				union perf_event *event) +int machine__process_exit_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample __maybe_unused)  { -	struct thread *thread = machine__find_thread(machine, event->fork.tid); +	struct thread *thread = machine__find_thread(machine, +						     event->fork.pid, +						     event->fork.tid);  	if (dump_trace)  		perf_event__fprintf_task(event, stdout); @@ -1141,23 +1167,24 @@ int machine__process_exit_event(struct machine *machine __maybe_unused,  	return 0;  } -int machine__process_event(struct machine *machine, union perf_event *event) +int machine__process_event(struct machine *machine, union perf_event *event, +			   struct perf_sample *sample)  {  	int ret;  	switch (event->header.type) {  	case PERF_RECORD_COMM: -		ret = machine__process_comm_event(machine, event); break; +		ret = machine__process_comm_event(machine, event, sample); break;  	case PERF_RECORD_MMAP: -		ret = machine__process_mmap_event(machine, event); break; +		ret = machine__process_mmap_event(machine, event, sample); break;  	case PERF_RECORD_MMAP2: -		ret = machine__process_mmap2_event(machine, event); break; +		ret = machine__process_mmap2_event(machine, event, sample); break;  	case PERF_RECORD_FORK: -		ret = machine__process_fork_event(machine, event); break; +		ret = machine__process_fork_event(machine, event, sample); break;  	case PERF_RECORD_EXIT: -		ret = machine__process_exit_event(machine, event); break; +		ret = machine__process_exit_event(machine, event, sample); break;  	case PERF_RECORD_LOST: -		ret = machine__process_lost_event(machine, event); break; +		ret = machine__process_lost_event(machine, event, sample); break;  	default:  		ret = -1;  		break; @@ -1173,39 +1200,22 @@ static bool symbol__match_regex(struct symbol *sym, regex_t *regex)  	return 0;  } -static const u8 cpumodes[] = { -	PERF_RECORD_MISC_USER, -	PERF_RECORD_MISC_KERNEL, -	PERF_RECORD_MISC_GUEST_USER, -	PERF_RECORD_MISC_GUEST_KERNEL -}; -#define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) -  static void ip__resolve_ams(struct machine *machine, struct thread *thread,  			    struct addr_map_symbol *ams,  			    u64 ip)  {  	struct addr_location al; -	size_t i; -	u8 m;  	memset(&al, 0, sizeof(al)); +	/* +	 * We cannot use the header.misc hint to determine whether a +	 * branch stack address is user, kernel, guest, hypervisor. +	 * Branches may straddle the kernel/user/hypervisor boundaries. +	 * Thus, we have to try consecutively until we find a match +	 * or else, the symbol is unknown +	 */ +	thread__find_cpumode_addr_location(thread, machine, MAP__FUNCTION, ip, &al); -	for (i = 0; i < NCPUMODES; i++) { -		m = cpumodes[i]; -		/* -		 * We cannot use the header.misc hint to determine whether a -		 * branch stack address is user, kernel, guest, hypervisor. -		 * Branches may straddle the kernel/user/hypervisor boundaries. -		 * Thus, we have to try consecutively until we find a match -		 * or else, the symbol is unknown -		 */ -		thread__find_addr_location(thread, machine, m, MAP__FUNCTION, -				ip, &al); -		if (al.sym) -			goto found; -	} -found:  	ams->addr = ip;  	ams->al_addr = al.addr;  	ams->sym = al.sym; @@ -1227,37 +1237,35 @@ static void ip__resolve_data(struct machine *machine, struct thread *thread,  	ams->map = al.map;  } -struct mem_info *machine__resolve_mem(struct machine *machine, -				      struct thread *thr, -				      struct perf_sample *sample, -				      u8 cpumode) +struct mem_info *sample__resolve_mem(struct perf_sample *sample, +				     struct addr_location *al)  {  	struct mem_info *mi = zalloc(sizeof(*mi));  	if (!mi)  		return NULL; -	ip__resolve_ams(machine, thr, &mi->iaddr, sample->ip); -	ip__resolve_data(machine, thr, cpumode, &mi->daddr, sample->addr); +	ip__resolve_ams(al->machine, al->thread, &mi->iaddr, sample->ip); +	ip__resolve_data(al->machine, al->thread, al->cpumode, +			 &mi->daddr, sample->addr);  	mi->data_src.val = sample->data_src;  	return mi;  } -struct branch_info *machine__resolve_bstack(struct machine *machine, -					    struct thread *thr, -					    struct branch_stack *bs) +struct branch_info *sample__resolve_bstack(struct perf_sample *sample, +					   struct addr_location *al)  { -	struct branch_info *bi;  	unsigned int i; +	const struct branch_stack *bs = sample->branch_stack; +	struct branch_info *bi = calloc(bs->nr, sizeof(struct branch_info)); -	bi = calloc(bs->nr, sizeof(struct branch_info));  	if (!bi)  		return NULL;  	for (i = 0; i < bs->nr; i++) { -		ip__resolve_ams(machine, thr, &bi[i].to, bs->entries[i].to); -		ip__resolve_ams(machine, thr, &bi[i].from, bs->entries[i].from); +		ip__resolve_ams(al->machine, al->thread, &bi[i].to, bs->entries[i].to); +		ip__resolve_ams(al->machine, al->thread, &bi[i].from, bs->entries[i].from);  		bi[i].flags = bs->entries[i].flags;  	}  	return bi; @@ -1267,10 +1275,12 @@ static int machine__resolve_callchain_sample(struct machine *machine,  					     struct thread *thread,  					     struct ip_callchain *chain,  					     struct symbol **parent, -					     struct addr_location *root_al) +					     struct addr_location *root_al, +					     int max_stack)  {  	u8 cpumode = PERF_RECORD_MISC_USER; -	unsigned int i; +	int chain_nr = min(max_stack, (int)chain->nr); +	int i;  	int err;  	callchain_cursor_reset(&callchain_cursor); @@ -1280,7 +1290,7 @@ static int machine__resolve_callchain_sample(struct machine *machine,  		return 0;  	} -	for (i = 0; i < chain->nr; i++) { +	for (i = 0; i < chain_nr; i++) {  		u64 ip;  		struct addr_location al; @@ -1313,7 +1323,7 @@ static int machine__resolve_callchain_sample(struct machine *machine,  			continue;  		} -		al.filtered = false; +		al.filtered = 0;  		thread__find_addr_location(thread, machine, cpumode,  					   MAP__FUNCTION, ip, &al);  		if (al.sym != NULL) { @@ -1327,8 +1337,6 @@ static int machine__resolve_callchain_sample(struct machine *machine,  				*root_al = al;  				callchain_cursor_reset(&callchain_cursor);  			} -			if (!symbol_conf.use_callchain) -				break;  		}  		err = callchain_cursor_append(&callchain_cursor, @@ -1352,12 +1360,14 @@ int machine__resolve_callchain(struct machine *machine,  			       struct thread *thread,  			       struct perf_sample *sample,  			       struct symbol **parent, -			       struct addr_location *root_al) +			       struct addr_location *root_al, +			       int max_stack)  {  	int ret;  	ret = machine__resolve_callchain_sample(machine, thread, -						sample->callchain, parent, root_al); +						sample->callchain, parent, +						root_al, max_stack);  	if (ret)  		return ret; @@ -1372,7 +1382,41 @@ int machine__resolve_callchain(struct machine *machine,  		return 0;  	return unwind__get_entries(unwind_entry, &callchain_cursor, machine, -				   thread, evsel->attr.sample_regs_user, -				   sample); +				   thread, sample, max_stack);  } + +int machine__for_each_thread(struct machine *machine, +			     int (*fn)(struct thread *thread, void *p), +			     void *priv) +{ +	struct rb_node *nd; +	struct thread *thread; +	int rc = 0; + +	for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { +		thread = rb_entry(nd, struct thread, rb_node); +		rc = fn(thread, priv); +		if (rc != 0) +			return rc; +	} + +	list_for_each_entry(thread, &machine->dead_threads, node) { +		rc = fn(thread, priv); +		if (rc != 0) +			return rc; +	} +	return rc; +} + +int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, +				  struct target *target, struct thread_map *threads, +				  perf_event__handler_t process, bool data_mmap) +{ +	if (target__has_task(target)) +		return perf_event__synthesize_thread_map(tool, threads, process, machine, data_mmap); +	else if (target__has_cpu(target)) +		return perf_event__synthesize_threads(tool, process, machine, data_mmap); +	/* command specified */ +	return 0; +} diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 58a6be1fc73..c8c74a11939 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -4,6 +4,7 @@  #include <sys/types.h>  #include <linux/rbtree.h>  #include "map.h" +#include "event.h"  struct addr_location;  struct branch_stack; @@ -17,6 +18,8 @@ union perf_event;  #define	HOST_KERNEL_ID			(-1)  #define	DEFAULT_GUEST_KERNEL_ID		(0) +extern const char *ref_reloc_sym_names[]; +  struct machine {  	struct rb_node	  rb_node;  	pid_t		  pid; @@ -38,15 +41,23 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type)  	return machine->vmlinux_maps[type];  } -struct thread *machine__find_thread(struct machine *machine, pid_t tid); - -int machine__process_comm_event(struct machine *machine, union perf_event *event); -int machine__process_exit_event(struct machine *machine, union perf_event *event); -int machine__process_fork_event(struct machine *machine, union perf_event *event); -int machine__process_lost_event(struct machine *machine, union perf_event *event); -int machine__process_mmap_event(struct machine *machine, union perf_event *event); -int machine__process_mmap2_event(struct machine *machine, union perf_event *event); -int machine__process_event(struct machine *machine, union perf_event *event); +struct thread *machine__find_thread(struct machine *machine, pid_t pid, +				    pid_t tid); + +int machine__process_comm_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample); +int machine__process_exit_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample); +int machine__process_fork_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample); +int machine__process_lost_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample); +int machine__process_mmap_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample); +int machine__process_mmap2_event(struct machine *machine, union perf_event *event, +				 struct perf_sample *sample); +int machine__process_event(struct machine *machine, union perf_event *event, +				struct perf_sample *sample);  typedef void (*machine__process_t)(struct machine *machine, void *data); @@ -74,24 +85,24 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size);  void machines__set_symbol_filter(struct machines *machines,  				 symbol_filter_t symbol_filter); +struct machine *machine__new_host(void);  int machine__init(struct machine *machine, const char *root_dir, pid_t pid);  void machine__exit(struct machine *machine);  void machine__delete_dead_threads(struct machine *machine);  void machine__delete_threads(struct machine *machine);  void machine__delete(struct machine *machine); -struct branch_info *machine__resolve_bstack(struct machine *machine, -					    struct thread *thread, -					    struct branch_stack *bs); -struct mem_info *machine__resolve_mem(struct machine *machine, -				      struct thread *thread, -				      struct perf_sample *sample, u8 cpumode); +struct branch_info *sample__resolve_bstack(struct perf_sample *sample, +					   struct addr_location *al); +struct mem_info *sample__resolve_mem(struct perf_sample *sample, +				     struct addr_location *al);  int machine__resolve_callchain(struct machine *machine,  			       struct perf_evsel *evsel,  			       struct thread *thread,  			       struct perf_sample *sample,  			       struct symbol **parent, -			       struct addr_location *root_al); +			       struct addr_location *root_al, +			       int max_stack);  /*   * Default guest kernel is defined by parameter --guestkallsyms @@ -165,4 +176,19 @@ void machines__destroy_kernel_maps(struct machines *machines);  size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); +int machine__for_each_thread(struct machine *machine, +			     int (*fn)(struct thread *thread, void *p), +			     void *priv); + +int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, +				  struct target *target, struct thread_map *threads, +				  perf_event__handler_t process, bool data_mmap); +static inline +int machine__synthesize_threads(struct machine *machine, struct target *target, +				struct thread_map *threads, bool data_mmap) +{ +	return __machine__synthesize_threads(machine, NULL, target, threads, +					     perf_event__process, data_mmap); +} +  #endif /* __PERF_MACHINE_H */ diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 4f6680d2043..25c571f4cba 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -11,6 +11,7 @@  #include "strlist.h"  #include "vdso.h"  #include "build-id.h" +#include "util.h"  #include <linux/string.h>  const char *map_type__name[MAP__NR_TYPES] = { @@ -31,6 +32,93 @@ static inline int is_no_dso_memory(const char *filename)  	       !strcmp(filename, "[heap]");  } +static inline int is_android_lib(const char *filename) +{ +	return !strncmp(filename, "/data/app-lib", 13) || +	       !strncmp(filename, "/system/lib", 11); +} + +static inline bool replace_android_lib(const char *filename, char *newfilename) +{ +	const char *libname; +	char *app_abi; +	size_t app_abi_length, new_length; +	size_t lib_length = 0; + +	libname  = strrchr(filename, '/'); +	if (libname) +		lib_length = strlen(libname); + +	app_abi = getenv("APP_ABI"); +	if (!app_abi) +		return false; + +	app_abi_length = strlen(app_abi); + +	if (!strncmp(filename, "/data/app-lib", 13)) { +		char *apk_path; + +		if (!app_abi_length) +			return false; + +		new_length = 7 + app_abi_length + lib_length; + +		apk_path = getenv("APK_PATH"); +		if (apk_path) { +			new_length += strlen(apk_path) + 1; +			if (new_length > PATH_MAX) +				return false; +			snprintf(newfilename, new_length, +				 "%s/libs/%s/%s", apk_path, app_abi, libname); +		} else { +			if (new_length > PATH_MAX) +				return false; +			snprintf(newfilename, new_length, +				 "libs/%s/%s", app_abi, libname); +		} +		return true; +	} + +	if (!strncmp(filename, "/system/lib/", 11)) { +		char *ndk, *app; +		const char *arch; +		size_t ndk_length; +		size_t app_length; + +		ndk = getenv("NDK_ROOT"); +		app = getenv("APP_PLATFORM"); + +		if (!(ndk && app)) +			return false; + +		ndk_length = strlen(ndk); +		app_length = strlen(app); + +		if (!(ndk_length && app_length && app_abi_length)) +			return false; + +		arch = !strncmp(app_abi, "arm", 3) ? "arm" : +		       !strncmp(app_abi, "mips", 4) ? "mips" : +		       !strncmp(app_abi, "x86", 3) ? "x86" : NULL; + +		if (!arch) +			return false; + +		new_length = 27 + ndk_length + +			     app_length + lib_length +			   + strlen(arch); + +		if (new_length > PATH_MAX) +			return false; +		snprintf(newfilename, new_length, +			"%s/platforms/%s/arch-%s/usr/lib/%s", +			ndk, app, arch, libname); + +		return true; +	} +	return false; +} +  void map__init(struct map *map, enum map_type type,  	       u64 start, u64 end, u64 pgoff, struct dso *dso)  { @@ -38,6 +126,7 @@ void map__init(struct map *map, enum map_type type,  	map->start    = start;  	map->end      = end;  	map->pgoff    = pgoff; +	map->reloc    = 0;  	map->dso      = dso;  	map->map_ip   = map__map_ip;  	map->unmap_ip = map__unmap_ip; @@ -49,7 +138,7 @@ void map__init(struct map *map, enum map_type type,  struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,  		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, -		     u64 ino_gen, char *filename, +		     u64 ino_gen, u32 prot, u32 flags, char *filename,  		     enum map_type type)  {  	struct map *map = malloc(sizeof(*map)); @@ -57,8 +146,9 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,  	if (map != NULL) {  		char newfilename[PATH_MAX];  		struct dso *dso; -		int anon, no_dso, vdso; +		int anon, no_dso, vdso, android; +		android = is_android_lib(filename);  		anon = is_anon_memory(filename);  		vdso = is_vdso_map(filename);  		no_dso = is_no_dso_memory(filename); @@ -67,12 +157,19 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,  		map->min = d_min;  		map->ino = ino;  		map->ino_generation = ino_gen; +		map->prot = prot; +		map->flags = flags; -		if (anon) { +		if ((anon || no_dso) && type == MAP__FUNCTION) {  			snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);  			filename = newfilename;  		} +		if (android) { +			if (replace_android_lib(filename, newfilename)) +				filename = newfilename; +		} +  		if (vdso) {  			pgoff = 0;  			dso = vdso__dso_findnew(dsos__list); @@ -92,7 +189,7 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,  			 * functions still return NULL, and we avoid the  			 * unnecessary map__load warning.  			 */ -			if (no_dso) +			if (type != MAP__FUNCTION)  				dso__set_loaded(dso, map->type);  		}  	} @@ -172,7 +269,7 @@ int map__load(struct map *map, symbol_filter_t filter)  		pr_warning(", continuing without symbols\n");  		return -1;  	} else if (nr == 0) { -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT  		const size_t len = strlen(name);  		const size_t real_len = len - sizeof(DSO__DELETED); @@ -252,10 +349,32 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp)  	return fprintf(fp, "%s", dsoname);  } -/* +int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, +			 FILE *fp) +{ +	char *srcline; +	int ret = 0; + +	if (map && map->dso) { +		srcline = get_srcline(map->dso, +				      map__rip_2objdump(map, addr)); +		if (srcline != SRCLINE_UNKNOWN) +			ret = fprintf(fp, "%s%s", prefix, srcline); +		free_srcline(srcline); +	} +	return ret; +} + +/** + * map__rip_2objdump - convert symbol start address to objdump address. + * @map: memory map + * @rip: symbol start address + *   * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN.   * map->dso->adjust_symbols==1 for ET_EXEC-like cases except ET_REL which is   * relative to section start. + * + * Return: Address suitable for passing to "objdump --start-address="   */  u64 map__rip_2objdump(struct map *map, u64 rip)  { @@ -265,7 +384,30 @@ u64 map__rip_2objdump(struct map *map, u64 rip)  	if (map->dso->rel)  		return rip - map->pgoff; -	return map->unmap_ip(map, rip); +	return map->unmap_ip(map, rip) - map->reloc; +} + +/** + * map__objdump_2mem - convert objdump address to a memory address. + * @map: memory map + * @ip: objdump address + * + * Closely related to map__rip_2objdump(), this function takes an address from + * objdump and converts it to a memory address.  Note this assumes that @map + * contains the address.  To be sure the result is valid, check it forwards + * e.g. map__rip_2objdump(map->map_ip(map, map__objdump_2mem(map, ip))) == ip + * + * Return: Memory address. + */ +u64 map__objdump_2mem(struct map *map, u64 ip) +{ +	if (!map->dso->adjust_symbols) +		return map->unmap_ip(map, ip); + +	if (map->dso->rel) +		return map->unmap_ip(map, ip + map->pgoff); + +	return ip + map->reloc;  }  void map_groups__init(struct map_groups *mg) @@ -276,6 +418,7 @@ void map_groups__init(struct map_groups *mg)  		INIT_LIST_HEAD(&mg->removed_maps[i]);  	}  	mg->machine = NULL; +	mg->refcnt = 1;  }  static void maps__delete(struct rb_root *maps) @@ -311,6 +454,28 @@ void map_groups__exit(struct map_groups *mg)  	}  } +struct map_groups *map_groups__new(void) +{ +	struct map_groups *mg = malloc(sizeof(*mg)); + +	if (mg != NULL) +		map_groups__init(mg); + +	return mg; +} + +void map_groups__delete(struct map_groups *mg) +{ +	map_groups__exit(mg); +	free(mg); +} + +void map_groups__put(struct map_groups *mg) +{ +	if (--mg->refcnt == 0) +		map_groups__delete(mg); +} +  void map_groups__flush(struct map_groups *mg)  {  	int type; @@ -340,7 +505,8 @@ struct symbol *map_groups__find_symbol(struct map_groups *mg,  {  	struct map *map = map_groups__find(mg, type, addr); -	if (map != NULL) { +	/* Ensure map is loaded before using map->map_ip */ +	if (map != NULL && map__load(map, filter) >= 0) {  		if (mapp != NULL)  			*mapp = map;  		return map__find_symbol(map, map->map_ip(map, addr), filter); @@ -371,6 +537,23 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,  	return NULL;  } +int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter) +{ +	if (ams->addr < ams->map->start || ams->addr > ams->map->end) { +		if (ams->map->groups == NULL) +			return -1; +		ams->map = map_groups__find(ams->map->groups, ams->map->type, +					    ams->addr); +		if (ams->map == NULL) +			return -1; +	} + +	ams->al_addr = ams->map->map_ip(ams->map, ams->addr); +	ams->sym = map__find_symbol(ams->map, ams->al_addr, filter); + +	return ams->sym ? 0 : -1; +} +  size_t __map_groups__fprintf_maps(struct map_groups *mg,  				  enum map_type type, int verbose, FILE *fp)  { diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 4886ca28053..7758c72522e 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -6,7 +6,7 @@  #include <linux/rbtree.h>  #include <stdio.h>  #include <stdbool.h> -#include "types.h" +#include <linux/types.h>  enum map_type {  	MAP__FUNCTION = 0, @@ -35,7 +35,10 @@ struct map {  	bool			referenced;  	bool			erange_warned;  	u32			priv; +	u32			prot; +	u32			flags;  	u64			pgoff; +	u64			reloc;  	u32			maj, min; /* only valid for MMAP2 record */  	u64			ino;      /* only valid for MMAP2 record */  	u64			ino_generation;/* only valid for MMAP2 record */ @@ -58,8 +61,20 @@ struct map_groups {  	struct rb_root	 maps[MAP__NR_TYPES];  	struct list_head removed_maps[MAP__NR_TYPES];  	struct machine	 *machine; +	int		 refcnt;  }; +struct map_groups *map_groups__new(void); +void map_groups__delete(struct map_groups *mg); + +static inline struct map_groups *map_groups__get(struct map_groups *mg) +{ +	++mg->refcnt; +	return mg; +} + +void map_groups__put(struct map_groups *mg); +  static inline struct kmap *map__kmap(struct map *map)  {  	return (struct kmap *)(map + 1); @@ -84,15 +99,28 @@ static inline u64 identity__map_ip(struct map *map __maybe_unused, u64 ip)  /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */  u64 map__rip_2objdump(struct map *map, u64 rip); +/* objdump address -> memory address */ +u64 map__objdump_2mem(struct map *map, u64 ip); +  struct symbol; +/* map__for_each_symbol - iterate over the symbols in the given map + * + * @map: the 'struct map *' in which symbols itereated + * @pos: the 'struct symbol *' to use as a loop cursor + * @n: the 'struct rb_node *' to use as a temporary storage + * Note: caller must ensure map->dso is not NULL (map is loaded). + */ +#define map__for_each_symbol(map, pos, n)	\ +	dso__for_each_symbol(map->dso, pos, n, map->type) +  typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);  void map__init(struct map *map, enum map_type type,  	       u64 start, u64 end, u64 pgoff, struct dso *dso);  struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,  		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, -		     u64 ino_gen, +		     u64 ino_gen, u32 prot, u32 flags,  		     char *filename, enum map_type type);  struct map *map__new2(u64 start, struct dso *dso, enum map_type type);  void map__delete(struct map *map); @@ -100,6 +128,8 @@ struct map *map__clone(struct map *map);  int map__overlap(struct map *l, struct map *r);  size_t map__fprintf(struct map *map, FILE *fp);  size_t map__fprintf_dsoname(struct map *map, FILE *fp); +int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, +			 FILE *fp);  int map__load(struct map *map, symbol_filter_t filter);  struct symbol *map__find_symbol(struct map *map, @@ -167,6 +197,10 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,  					       struct map **mapp,  					       symbol_filter_t filter); +struct addr_map_symbol; + +int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter); +  static inline  struct symbol *map_groups__find_function_by_name(struct map_groups *mg,  						 const char *name, struct map **mapp, diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c index 3322b8446e8..31ee02d4e98 100644 --- a/tools/perf/util/pager.c +++ b/tools/perf/util/pager.c @@ -57,13 +57,13 @@ void setup_pager(void)  	}  	if (!pager)  		pager = getenv("PAGER"); -	if (!pager) { -		if (!access("/usr/bin/pager", X_OK)) -			pager = "/usr/bin/pager"; -	} +	if (!(pager || access("/usr/bin/pager", X_OK))) +		pager = "/usr/bin/pager"; +	if (!(pager || access("/usr/bin/less", X_OK))) +		pager = "/usr/bin/less";  	if (!pager) -		pager = "less"; -	else if (!*pager || !strcmp(pager, "cat")) +		pager = "cat"; +	if (!*pager || !strcmp(pager, "cat"))  		return;  	spawned_pager = 1; /* means we are emitting to terminal */ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 98125319b15..1e15df10a88 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -10,7 +10,7 @@  #include "symbol.h"  #include "cache.h"  #include "header.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include "parse-events-bison.h"  #define YY_EXTRA_TYPE int  #include "parse-events-flex.h" @@ -204,7 +204,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)  				}  				path->name = malloc(MAX_EVENT_LENGTH);  				if (!path->name) { -					free(path->system); +					zfree(&path->system);  					free(path);  					return NULL;  				} @@ -236,8 +236,8 @@ struct tracepoint_path *tracepoint_name_to_path(const char *name)  	path->name = strdup(str+1);  	if (path->system == NULL || path->name == NULL) { -		free(path->system); -		free(path->name); +		zfree(&path->system); +		zfree(&path->name);  		free(path);  		path = NULL;  	} @@ -269,29 +269,30 @@ const char *event_type(int type) -static int __add_event(struct list_head *list, int *idx, -		       struct perf_event_attr *attr, -		       char *name, struct cpu_map *cpus) +static struct perf_evsel * +__add_event(struct list_head *list, int *idx, +	    struct perf_event_attr *attr, +	    char *name, struct cpu_map *cpus)  {  	struct perf_evsel *evsel;  	event_attr_init(attr); -	evsel = perf_evsel__new(attr, (*idx)++); +	evsel = perf_evsel__new_idx(attr, (*idx)++);  	if (!evsel) -		return -ENOMEM; +		return NULL;  	evsel->cpus = cpus;  	if (name)  		evsel->name = strdup(name);  	list_add_tail(&evsel->node, list); -	return 0; +	return evsel;  }  static int add_event(struct list_head *list, int *idx,  		     struct perf_event_attr *attr, char *name)  { -	return __add_event(list, idx, attr, name, NULL); +	return __add_event(list, idx, attr, name, NULL) ? 0 : -ENOMEM;  }  static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) @@ -378,7 +379,7 @@ static int add_tracepoint(struct list_head *list, int *idx,  {  	struct perf_evsel *evsel; -	evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++); +	evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++);  	if (!evsel)  		return -ENOMEM; @@ -633,6 +634,9 @@ int parse_events_add_pmu(struct list_head *list, int *idx,  {  	struct perf_event_attr attr;  	struct perf_pmu *pmu; +	struct perf_evsel *evsel; +	const char *unit; +	double scale;  	pmu = perf_pmu__find(name);  	if (!pmu) @@ -640,7 +644,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx,  	memset(&attr, 0, sizeof(attr)); -	if (perf_pmu__check_alias(pmu, head_config)) +	if (perf_pmu__check_alias(pmu, head_config, &unit, &scale))  		return -EINVAL;  	/* @@ -652,8 +656,14 @@ int parse_events_add_pmu(struct list_head *list, int *idx,  	if (perf_pmu__config(pmu, &attr, head_config))  		return -EINVAL; -	return __add_event(list, idx, &attr, pmu_event_name(head_config), -			   pmu->cpus); +	evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), +			    pmu->cpus); +	if (evsel) { +		evsel->unit = unit; +		evsel->scale = scale; +	} + +	return evsel ? 0 : -ENOMEM;  }  int parse_events__modifier_group(struct list_head *list, @@ -810,8 +820,7 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add)  	if (!add && get_event_modifier(&mod, str, NULL))  		return -EINVAL; -	list_for_each_entry(evsel, list, node) { - +	__evlist__for_each(list, evsel) {  		if (add && get_event_modifier(&mod, str, evsel))  			return -EINVAL; @@ -835,7 +844,7 @@ int parse_events_name(struct list_head *list, char *name)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, list, node) { +	__evlist__for_each(list, evsel) {  		if (!evsel->name)  			evsel->name = strdup(name);  	} @@ -907,7 +916,7 @@ int parse_events_terms(struct list_head *terms, const char *str)  	ret = parse_events__scanner(str, &data, PE_START_TERMS);  	if (!ret) {  		list_splice(data.terms, terms); -		free(data.terms); +		zfree(&data.terms);  		return 0;  	} @@ -998,8 +1007,10 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,  	char evt_path[MAXPATHLEN];  	char dir_path[MAXPATHLEN]; -	if (debugfs_valid_mountpoint(tracing_events_path)) +	if (debugfs_valid_mountpoint(tracing_events_path)) { +		printf("  [ Tracepoints not available: %s ]\n", strerror(errno));  		return; +	}  	sys_dir = opendir(tracing_events_path);  	if (!sys_dir) @@ -1080,12 +1091,12 @@ int is_valid_tracepoint(const char *event_string)  static bool is_event_supported(u8 type, unsigned config)  {  	bool ret = true; +	int open_return;  	struct perf_evsel *evsel;  	struct perf_event_attr attr = {  		.type = type,  		.config = config,  		.disabled = 1, -		.exclude_kernel = 1,  	};  	struct {  		struct thread_map map; @@ -1095,9 +1106,22 @@ static bool is_event_supported(u8 type, unsigned config)  		.threads = { 0 },  	}; -	evsel = perf_evsel__new(&attr, 0); +	evsel = perf_evsel__new(&attr);  	if (evsel) { -		ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; +		open_return = perf_evsel__open(evsel, NULL, &tmap.map); +		ret = open_return >= 0; + +		if (open_return == -EACCES) { +			/* +			 * This happens if the paranoid value +			 * /proc/sys/kernel/perf_event_paranoid is set to 2 +			 * Re-run with exclude_kernel set; we don't do that +			 * by default as some ARM machines do not support it. +			 * +			 */ +			evsel->attr.exclude_kernel = 1; +			ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; +		}  		perf_evsel__delete(evsel);  	} diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index f1cb4c4b3c7..df094b4ed5e 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -6,9 +6,8 @@  #include <linux/list.h>  #include <stdbool.h> -#include "types.h" +#include <linux/types.h>  #include <linux/perf_event.h> -#include "types.h"  struct list_head;  struct perf_evsel; diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 91346b75396..343299575b3 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -126,6 +126,37 @@ modifier_bp	[rwx]{1,3}  } +<config>{ +config			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } +config1			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } +config2			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } +name			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } +period			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } +branch_type		{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } +,			{ return ','; } +"/"			{ BEGIN(INITIAL); return '/'; } +{name_minus}		{ return str(yyscanner, PE_NAME); } +} + +<mem>{ +{modifier_bp}		{ return str(yyscanner, PE_MODIFIER_BP); } +:			{ return ':'; } +{num_dec}		{ return value(yyscanner, 10); } +{num_hex}		{ return value(yyscanner, 16); } +	/* +	 * We need to separate 'mem:' scanner part, in order to get specific +	 * modifier bits parsed out. Otherwise we would need to handle PE_NAME +	 * and we'd need to parse it manually. During the escape from <mem> +	 * state we need to put the escaping char back, so we dont miss it. +	 */ +.			{ unput(*yytext); BEGIN(INITIAL); } +	/* +	 * We destroy the scanner after reaching EOF, +	 * but anyway just to be sure get back to INIT state. +	 */ +<<EOF>>			{ BEGIN(INITIAL); } +} +  cpu-cycles|cycles				{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }  stalled-cycles-frontend|idle-cycles-frontend	{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }  stalled-cycles-backend|idle-cycles-backend	{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } @@ -162,18 +193,6 @@ speculative-read|speculative-load	|  refs|Reference|ops|access		|  misses|miss				{ return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } -<config>{ -config			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } -config1			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } -config2			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } -name			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } -period			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } -branch_type		{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } -,			{ return ','; } -"/"			{ BEGIN(INITIAL); return '/'; } -{name_minus}		{ return str(yyscanner, PE_NAME); } -} -  mem:			{ BEGIN(mem); return PE_PREFIX_MEM; }  r{num_raw_hex}		{ return raw(yyscanner); }  {num_dec}		{ return value(yyscanner, 10); } @@ -189,25 +208,7 @@ r{num_raw_hex}		{ return raw(yyscanner); }  "}"			{ return '}'; }  =			{ return '='; }  \n			{ } - -<mem>{ -{modifier_bp}		{ return str(yyscanner, PE_MODIFIER_BP); } -:			{ return ':'; } -{num_dec}		{ return value(yyscanner, 10); } -{num_hex}		{ return value(yyscanner, 16); } -	/* -	 * We need to separate 'mem:' scanner part, in order to get specific -	 * modifier bits parsed out. Otherwise we would need to handle PE_NAME -	 * and we'd need to parse it manually. During the escape from <mem> -	 * state we need to put the escaping char back, so we dont miss it. -	 */ -.			{ unput(*yytext); BEGIN(INITIAL); } -	/* -	 * We destroy the scanner after reaching EOF, -	 * but anyway just to be sure get back to INIT state. -	 */ -<<EOF>>			{ BEGIN(INITIAL); } -} +.			{ }  %% diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 4eb67ec333f..0bc87ba46bf 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -9,7 +9,7 @@  #include <linux/compiler.h>  #include <linux/list.h> -#include "types.h" +#include <linux/types.h>  #include "util.h"  #include "parse-events.h"  #include "parse-events-bison.h" @@ -299,6 +299,18 @@ PE_PREFIX_MEM PE_VALUE sep_dc  }  event_legacy_tracepoint: +PE_NAME '-' PE_NAME ':' PE_NAME +{ +	struct parse_events_evlist *data = _data; +	struct list_head *list; +	char sys_name[128]; +	snprintf(&sys_name, 128, "%s-%s", $1, $3); + +	ALLOC_LIST(list); +	ABORT_ON(parse_events_add_tracepoint(list, &data->idx, &sys_name, $5)); +	$$ = list; +} +|  PE_NAME ':' PE_NAME  {  	struct parse_events_evlist *data = _data; diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 2bc9e70df7e..bf48092983c 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -78,6 +78,8 @@ static int get_value(struct parse_opt_ctx_t *p,  	case OPTION_BOOLEAN:  		*(bool *)opt->value = unset ? false : true; +		if (opt->set) +			*(bool *)opt->set = true;  		return 0;  	case OPTION_INCR: @@ -224,6 +226,24 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,  			return 0;  		}  		if (!rest) { +			if (!prefixcmp(options->long_name, "no-")) { +				/* +				 * The long name itself starts with "no-", so +				 * accept the option without "no-" so that users +				 * do not have to enter "no-no-" to get the +				 * negation. +				 */ +				rest = skip_prefix(arg, options->long_name + 3); +				if (rest) { +					flags |= OPT_UNSET; +					goto match; +				} +				/* Abbreviated case */ +				if (!prefixcmp(options->long_name + 3, arg)) { +					flags |= OPT_UNSET; +					goto is_abbreviated; +				} +			}  			/* abbreviated? */  			if (!strncmp(options->long_name, arg, arg_end - arg)) {  is_abbreviated: @@ -259,6 +279,7 @@ is_abbreviated:  			if (!rest)  				continue;  		} +match:  		if (*rest) {  			if (*rest != '=')  				continue; @@ -339,10 +360,10 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,  		if (arg[1] != '-') {  			ctx->opt = arg + 1;  			if (internal_help && *ctx->opt == 'h') -				return parse_options_usage(usagestr, options); +				return usage_with_options_internal(usagestr, options, 0);  			switch (parse_short_opt(ctx, options)) {  			case -1: -				return parse_options_usage(usagestr, options); +				return parse_options_usage(usagestr, options, arg + 1, 1);  			case -2:  				goto unknown;  			default: @@ -352,10 +373,11 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,  				check_typos(arg + 1, options);  			while (ctx->opt) {  				if (internal_help && *ctx->opt == 'h') -					return parse_options_usage(usagestr, options); +					return usage_with_options_internal(usagestr, options, 0); +				arg = ctx->opt;  				switch (parse_short_opt(ctx, options)) {  				case -1: -					return parse_options_usage(usagestr, options); +					return parse_options_usage(usagestr, options, arg, 1);  				case -2:  					/* fake a short option thing to hide the fact that we may have  					 * started to parse aggregated stuff @@ -383,12 +405,14 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,  		if (internal_help && !strcmp(arg + 2, "help-all"))  			return usage_with_options_internal(usagestr, options, 1);  		if (internal_help && !strcmp(arg + 2, "help")) -			return parse_options_usage(usagestr, options); +			return usage_with_options_internal(usagestr, options, 0);  		if (!strcmp(arg + 2, "list-opts")) -			return PARSE_OPT_LIST; +			return PARSE_OPT_LIST_OPTS; +		if (!strcmp(arg + 2, "list-cmds")) +			return PARSE_OPT_LIST_SUBCMDS;  		switch (parse_long_opt(ctx, arg + 2, options)) {  		case -1: -			return parse_options_usage(usagestr, options); +			return parse_options_usage(usagestr, options, arg + 2, 0);  		case -2:  			goto unknown;  		default: @@ -411,25 +435,45 @@ int parse_options_end(struct parse_opt_ctx_t *ctx)  	return ctx->cpidx + ctx->argc;  } -int parse_options(int argc, const char **argv, const struct option *options, -		  const char * const usagestr[], int flags) +int parse_options_subcommand(int argc, const char **argv, const struct option *options, +			const char *const subcommands[], const char *usagestr[], int flags)  {  	struct parse_opt_ctx_t ctx;  	perf_header__set_cmdline(argc, argv); +	/* build usage string if it's not provided */ +	if (subcommands && !usagestr[0]) { +		struct strbuf buf = STRBUF_INIT; + +		strbuf_addf(&buf, "perf %s [<options>] {", argv[0]); +		for (int i = 0; subcommands[i]; i++) { +			if (i) +				strbuf_addstr(&buf, "|"); +			strbuf_addstr(&buf, subcommands[i]); +		} +		strbuf_addstr(&buf, "}"); + +		usagestr[0] = strdup(buf.buf); +		strbuf_release(&buf); +	} +  	parse_options_start(&ctx, argc, argv, flags);  	switch (parse_options_step(&ctx, options, usagestr)) {  	case PARSE_OPT_HELP:  		exit(129);  	case PARSE_OPT_DONE:  		break; -	case PARSE_OPT_LIST: +	case PARSE_OPT_LIST_OPTS:  		while (options->type != OPTION_END) {  			printf("--%s ", options->long_name);  			options++;  		}  		exit(130); +	case PARSE_OPT_LIST_SUBCMDS: +		for (int i = 0; subcommands[i]; i++) +			printf("%s ", subcommands[i]); +		exit(130);  	default: /* PARSE_OPT_UNKNOWN */  		if (ctx.argv[0][1] == '-') {  			error("unknown option `%s'", ctx.argv[0] + 2); @@ -442,9 +486,99 @@ int parse_options(int argc, const char **argv, const struct option *options,  	return parse_options_end(&ctx);  } +int parse_options(int argc, const char **argv, const struct option *options, +		  const char * const usagestr[], int flags) +{ +	return parse_options_subcommand(argc, argv, options, NULL, +					(const char **) usagestr, flags); +} +  #define USAGE_OPTS_WIDTH 24  #define USAGE_GAP         2 +static void print_option_help(const struct option *opts, int full) +{ +	size_t pos; +	int pad; + +	if (opts->type == OPTION_GROUP) { +		fputc('\n', stderr); +		if (*opts->help) +			fprintf(stderr, "%s\n", opts->help); +		return; +	} +	if (!full && (opts->flags & PARSE_OPT_HIDDEN)) +		return; + +	pos = fprintf(stderr, "    "); +	if (opts->short_name) +		pos += fprintf(stderr, "-%c", opts->short_name); +	else +		pos += fprintf(stderr, "    "); + +	if (opts->long_name && opts->short_name) +		pos += fprintf(stderr, ", "); +	if (opts->long_name) +		pos += fprintf(stderr, "--%s", opts->long_name); + +	switch (opts->type) { +	case OPTION_ARGUMENT: +		break; +	case OPTION_LONG: +	case OPTION_U64: +	case OPTION_INTEGER: +	case OPTION_UINTEGER: +		if (opts->flags & PARSE_OPT_OPTARG) +			if (opts->long_name) +				pos += fprintf(stderr, "[=<n>]"); +			else +				pos += fprintf(stderr, "[<n>]"); +		else +			pos += fprintf(stderr, " <n>"); +		break; +	case OPTION_CALLBACK: +		if (opts->flags & PARSE_OPT_NOARG) +			break; +		/* FALLTHROUGH */ +	case OPTION_STRING: +		if (opts->argh) { +			if (opts->flags & PARSE_OPT_OPTARG) +				if (opts->long_name) +					pos += fprintf(stderr, "[=<%s>]", opts->argh); +				else +					pos += fprintf(stderr, "[<%s>]", opts->argh); +			else +				pos += fprintf(stderr, " <%s>", opts->argh); +		} else { +			if (opts->flags & PARSE_OPT_OPTARG) +				if (opts->long_name) +					pos += fprintf(stderr, "[=...]"); +				else +					pos += fprintf(stderr, "[...]"); +			else +				pos += fprintf(stderr, " ..."); +		} +		break; +	default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ +	case OPTION_END: +	case OPTION_GROUP: +	case OPTION_BIT: +	case OPTION_BOOLEAN: +	case OPTION_INCR: +	case OPTION_SET_UINT: +	case OPTION_SET_PTR: +		break; +	} + +	if (pos <= USAGE_OPTS_WIDTH) +		pad = USAGE_OPTS_WIDTH - pos; +	else { +		fputc('\n', stderr); +		pad = USAGE_OPTS_WIDTH; +	} +	fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); +} +  int usage_with_options_internal(const char * const *usagestr,  				const struct option *opts, int full)  { @@ -464,87 +598,9 @@ int usage_with_options_internal(const char * const *usagestr,  	if (opts->type != OPTION_GROUP)  		fputc('\n', stderr); -	for (; opts->type != OPTION_END; opts++) { -		size_t pos; -		int pad; - -		if (opts->type == OPTION_GROUP) { -			fputc('\n', stderr); -			if (*opts->help) -				fprintf(stderr, "%s\n", opts->help); -			continue; -		} -		if (!full && (opts->flags & PARSE_OPT_HIDDEN)) -			continue; - -		pos = fprintf(stderr, "    "); -		if (opts->short_name) -			pos += fprintf(stderr, "-%c", opts->short_name); -		else -			pos += fprintf(stderr, "    "); - -		if (opts->long_name && opts->short_name) -			pos += fprintf(stderr, ", "); -		if (opts->long_name) -			pos += fprintf(stderr, "--%s", opts->long_name); +	for (  ; opts->type != OPTION_END; opts++) +		print_option_help(opts, full); -		switch (opts->type) { -		case OPTION_ARGUMENT: -			break; -		case OPTION_LONG: -		case OPTION_U64: -		case OPTION_INTEGER: -		case OPTION_UINTEGER: -			if (opts->flags & PARSE_OPT_OPTARG) -				if (opts->long_name) -					pos += fprintf(stderr, "[=<n>]"); -				else -					pos += fprintf(stderr, "[<n>]"); -			else -				pos += fprintf(stderr, " <n>"); -			break; -		case OPTION_CALLBACK: -			if (opts->flags & PARSE_OPT_NOARG) -				break; -			/* FALLTHROUGH */ -		case OPTION_STRING: -			if (opts->argh) { -				if (opts->flags & PARSE_OPT_OPTARG) -					if (opts->long_name) -						pos += fprintf(stderr, "[=<%s>]", opts->argh); -					else -						pos += fprintf(stderr, "[<%s>]", opts->argh); -				else -					pos += fprintf(stderr, " <%s>", opts->argh); -			} else { -				if (opts->flags & PARSE_OPT_OPTARG) -					if (opts->long_name) -						pos += fprintf(stderr, "[=...]"); -					else -						pos += fprintf(stderr, "[...]"); -				else -					pos += fprintf(stderr, " ..."); -			} -			break; -		default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ -		case OPTION_END: -		case OPTION_GROUP: -		case OPTION_BIT: -		case OPTION_BOOLEAN: -		case OPTION_INCR: -		case OPTION_SET_UINT: -		case OPTION_SET_PTR: -			break; -		} - -		if (pos <= USAGE_OPTS_WIDTH) -			pad = USAGE_OPTS_WIDTH - pos; -		else { -			fputc('\n', stderr); -			pad = USAGE_OPTS_WIDTH; -		} -		fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); -	}  	fputc('\n', stderr);  	return PARSE_OPT_HELP; @@ -559,9 +615,45 @@ void usage_with_options(const char * const *usagestr,  }  int parse_options_usage(const char * const *usagestr, -			const struct option *opts) +			const struct option *opts, +			const char *optstr, bool short_opt)  { -	return usage_with_options_internal(usagestr, opts, 0); +	if (!usagestr) +		goto opt; + +	fprintf(stderr, "\n usage: %s\n", *usagestr++); +	while (*usagestr && **usagestr) +		fprintf(stderr, "    or: %s\n", *usagestr++); +	while (*usagestr) { +		fprintf(stderr, "%s%s\n", +				**usagestr ? "    " : "", +				*usagestr); +		usagestr++; +	} +	fputc('\n', stderr); + +opt: +	for (  ; opts->type != OPTION_END; opts++) { +		if (short_opt) { +			if (opts->short_name == *optstr) +				break; +			continue; +		} + +		if (opts->long_name == NULL) +			continue; + +		if (!prefixcmp(optstr, opts->long_name)) +			break; +		if (!prefixcmp(optstr, "no-") && +		    !prefixcmp(optstr + 3, opts->long_name)) +			break; +	} + +	if (opts->type != OPTION_END) +		print_option_help(opts, 0); + +	return PARSE_OPT_HELP;  } diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 7bb5999940c..d8dac8ac5f3 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -82,6 +82,9 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);   *   OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in   *   the value when met.   *   CALLBACKS can use it like they want. + * + * `set`:: + *   whether an option was set by the user   */  struct option {  	enum parse_opt_type type; @@ -94,6 +97,7 @@ struct option {  	int flags;  	parse_opt_cb *callback;  	intptr_t defval; +	bool *set;  };  #define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) @@ -103,6 +107,10 @@ struct option {  #define OPT_GROUP(h)                { .type = OPTION_GROUP, .help = (h) }  #define OPT_BIT(s, l, v, h, b)      { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }  #define OPT_BOOLEAN(s, l, v, h)     { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } +#define OPT_BOOLEAN_SET(s, l, v, os, h) \ +	{ .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \ +	.value = check_vtype(v, bool *), .help = (h), \ +	.set = check_vtype(os, bool *)}  #define OPT_INCR(s, l, v, h)        { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }  #define OPT_SET_UINT(s, l, v, h, i)  { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) }  #define OPT_SET_PTR(s, l, v, h, p)  { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } @@ -132,6 +140,11 @@ extern int parse_options(int argc, const char **argv,                           const struct option *options,                           const char * const usagestr[], int flags); +extern int parse_options_subcommand(int argc, const char **argv, +				const struct option *options, +				const char *const subcommands[], +				const char *usagestr[], int flags); +  extern NORETURN void usage_with_options(const char * const *usagestr,                                          const struct option *options); @@ -140,7 +153,8 @@ extern NORETURN void usage_with_options(const char * const *usagestr,  enum {  	PARSE_OPT_HELP = -1,  	PARSE_OPT_DONE, -	PARSE_OPT_LIST, +	PARSE_OPT_LIST_OPTS, +	PARSE_OPT_LIST_SUBCMDS,  	PARSE_OPT_UNKNOWN,  }; @@ -158,7 +172,9 @@ struct parse_opt_ctx_t {  };  extern int parse_options_usage(const char * const *usagestr, -			       const struct option *opts); +			       const struct option *opts, +			       const char *optstr, +			       bool short_opt);  extern void parse_options_start(struct parse_opt_ctx_t *ctx,  				int argc, const char **argv, int flags); diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index a8c49548ca4..5d13cb45b31 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c @@ -22,19 +22,23 @@ static const char *get_perf_dir(void)  	return ".";  } -#ifndef HAVE_STRLCPY -size_t strlcpy(char *dest, const char *src, size_t size) +/* + * If libc has strlcpy() then that version will override this + * implementation: + */ +size_t __weak strlcpy(char *dest, const char *src, size_t size)  {  	size_t ret = strlen(src);  	if (size) {  		size_t len = (ret >= size) ? size - 1 : ret; +  		memcpy(dest, src, len);  		dest[len] = '\0';  	} +  	return ret;  } -#endif  static char *get_pathname(void)  { diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c new file mode 100644 index 00000000000..43168fb0d9a --- /dev/null +++ b/tools/perf/util/perf_regs.c @@ -0,0 +1,27 @@ +#include <errno.h> +#include "perf_regs.h" +#include "event.h" + +int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) +{ +	int i, idx = 0; +	u64 mask = regs->mask; + +	if (regs->cache_mask & (1 << id)) +		goto out; + +	if (!(mask & (1 << id))) +		return -EINVAL; + +	for (i = 0; i < id; i++) { +		if (mask & (1 << i)) +			idx++; +	} + +	regs->cache_mask |= (1 << id); +	regs->cache_regs[id] = regs->regs[idx]; + +out: +	*valp = regs->cache_regs[id]; +	return 0; +} diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 5a4f2b6f373..980dbf76bc9 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -1,14 +1,29 @@  #ifndef __PERF_REGS_H  #define __PERF_REGS_H -#ifdef HAVE_PERF_REGS +#include <linux/types.h> + +struct regs_dump; + +#ifdef HAVE_PERF_REGS_SUPPORT  #include <perf_regs.h> + +int perf_reg_value(u64 *valp, struct regs_dump *regs, int id); +  #else  #define PERF_REGS_MASK	0 +#define PERF_REGS_MAX	0  static inline const char *perf_reg_name(int id __maybe_unused)  {  	return NULL;  } -#endif /* HAVE_PERF_REGS */ + +static inline int perf_reg_value(u64 *valp __maybe_unused, +				 struct regs_dump *regs __maybe_unused, +				 int id __maybe_unused) +{ +	return 0; +} +#endif /* HAVE_PERF_REGS_SUPPORT */  #endif /* __PERF_REGS_H */ diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index bc9d8069d37..7a811eb61f7 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1,19 +1,23 @@  #include <linux/list.h>  #include <sys/types.h> -#include <sys/stat.h>  #include <unistd.h>  #include <stdio.h>  #include <dirent.h> -#include "sysfs.h" +#include <api/fs/fs.h> +#include <locale.h>  #include "util.h"  #include "pmu.h"  #include "parse-events.h"  #include "cpumap.h" +#define UNIT_MAX_LEN	31 /* max length for event unit name */ +  struct perf_pmu_alias {  	char *name;  	struct list_head terms;  	struct list_head list; +	char unit[UNIT_MAX_LEN+1]; +	double scale;  };  struct perf_pmu_format { @@ -77,9 +81,8 @@ static int pmu_format(const char *name, struct list_head *format)  {  	struct stat st;  	char path[PATH_MAX]; -	const char *sysfs; +	const char *sysfs = sysfs__mountpoint(); -	sysfs = sysfs_find_mountpoint();  	if (!sysfs)  		return -1; @@ -95,7 +98,80 @@ static int pmu_format(const char *name, struct list_head *format)  	return 0;  } -static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) +static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) +{ +	struct stat st; +	ssize_t sret; +	char scale[128]; +	int fd, ret = -1; +	char path[PATH_MAX]; +	const char *lc; + +	snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); + +	fd = open(path, O_RDONLY); +	if (fd == -1) +		return -1; + +	if (fstat(fd, &st) < 0) +		goto error; + +	sret = read(fd, scale, sizeof(scale)-1); +	if (sret < 0) +		goto error; + +	scale[sret] = '\0'; +	/* +	 * save current locale +	 */ +	lc = setlocale(LC_NUMERIC, NULL); + +	/* +	 * force to C locale to ensure kernel +	 * scale string is converted correctly. +	 * kernel uses default C locale. +	 */ +	setlocale(LC_NUMERIC, "C"); + +	alias->scale = strtod(scale, NULL); + +	/* restore locale */ +	setlocale(LC_NUMERIC, lc); + +	ret = 0; +error: +	close(fd); +	return ret; +} + +static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name) +{ +	char path[PATH_MAX]; +	ssize_t sret; +	int fd; + +	snprintf(path, PATH_MAX, "%s/%s.unit", dir, name); + +	fd = open(path, O_RDONLY); +	if (fd == -1) +		return -1; + +		sret = read(fd, alias->unit, UNIT_MAX_LEN); +	if (sret < 0) +		goto error; + +	close(fd); + +	alias->unit[sret] = '\0'; + +	return 0; +error: +	close(fd); +	alias->unit[0] = '\0'; +	return -1; +} + +static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)  {  	struct perf_pmu_alias *alias;  	char buf[256]; @@ -111,6 +187,9 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)  		return -ENOMEM;  	INIT_LIST_HEAD(&alias->terms); +	alias->scale = 1.0; +	alias->unit[0] = '\0'; +  	ret = parse_events_terms(&alias->terms, buf);  	if (ret) {  		free(alias); @@ -118,7 +197,14 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)  	}  	alias->name = strdup(name); +	/* +	 * load unit name and scale if available +	 */ +	perf_pmu__parse_unit(alias, dir, name); +	perf_pmu__parse_scale(alias, dir, name); +  	list_add_tail(&alias->list, list); +  	return 0;  } @@ -130,6 +216,7 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)  {  	struct dirent *evt_ent;  	DIR *event_dir; +	size_t len;  	int ret = 0;  	event_dir = opendir(dir); @@ -144,13 +231,24 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)  		if (!strcmp(name, ".") || !strcmp(name, ".."))  			continue; +		/* +		 * skip .unit and .scale info files +		 * parsed in perf_pmu__new_alias() +		 */ +		len = strlen(name); +		if (len > 5 && !strcmp(name + len - 5, ".unit")) +			continue; +		if (len > 6 && !strcmp(name + len - 6, ".scale")) +			continue; +  		snprintf(path, PATH_MAX, "%s/%s", dir, name);  		ret = -EINVAL;  		file = fopen(path, "r");  		if (!file)  			break; -		ret = perf_pmu__new_alias(head, name, file); + +		ret = perf_pmu__new_alias(head, dir, name, file);  		fclose(file);  	} @@ -166,9 +264,8 @@ static int pmu_aliases(const char *name, struct list_head *head)  {  	struct stat st;  	char path[PATH_MAX]; -	const char *sysfs; +	const char *sysfs = sysfs__mountpoint(); -	sysfs = sysfs_find_mountpoint();  	if (!sysfs)  		return -1; @@ -187,17 +284,17 @@ static int pmu_aliases(const char *name, struct list_head *head)  static int pmu_alias_terms(struct perf_pmu_alias *alias,  			   struct list_head *terms)  { -	struct parse_events_term *term, *clone; +	struct parse_events_term *term, *cloned;  	LIST_HEAD(list);  	int ret;  	list_for_each_entry(term, &alias->terms, list) { -		ret = parse_events_term__clone(&clone, term); +		ret = parse_events_term__clone(&cloned, term);  		if (ret) {  			parse_events__free_terms(&list);  			return ret;  		} -		list_add_tail(&clone->list, &list); +		list_add_tail(&cloned->list, &list);  	}  	list_splice(&list, terms);  	return 0; @@ -212,11 +309,10 @@ static int pmu_type(const char *name, __u32 *type)  {  	struct stat st;  	char path[PATH_MAX]; -	const char *sysfs;  	FILE *file;  	int ret = 0; +	const char *sysfs = sysfs__mountpoint(); -	sysfs = sysfs_find_mountpoint();  	if (!sysfs)  		return -1; @@ -241,11 +337,10 @@ static int pmu_type(const char *name, __u32 *type)  static void pmu_read_sysfs(void)  {  	char path[PATH_MAX]; -	const char *sysfs;  	DIR *dir;  	struct dirent *dent; +	const char *sysfs = sysfs__mountpoint(); -	sysfs = sysfs_find_mountpoint();  	if (!sysfs)  		return; @@ -270,11 +365,10 @@ static struct cpu_map *pmu_cpumask(const char *name)  {  	struct stat st;  	char path[PATH_MAX]; -	const char *sysfs;  	FILE *file;  	struct cpu_map *cpus; +	const char *sysfs = sysfs__mountpoint(); -	sysfs = sysfs_find_mountpoint();  	if (!sysfs)  		return NULL; @@ -411,7 +505,7 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value)  /*   * Setup one of config[12] attr members based on the - * user input data - temr parameter. + * user input data - term parameter.   */  static int pmu_config_term(struct list_head *formats,  			   struct perf_event_attr *attr, @@ -513,16 +607,46 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,  	return NULL;  } + +static int check_unit_scale(struct perf_pmu_alias *alias, +			    const char **unit, double *scale) +{ +	/* +	 * Only one term in event definition can +	 * define unit and scale, fail if there's +	 * more than one. +	 */ +	if ((*unit && alias->unit) || +	    (*scale && alias->scale)) +		return -EINVAL; + +	if (alias->unit) +		*unit = alias->unit; + +	if (alias->scale) +		*scale = alias->scale; + +	return 0; +} +  /*   * Find alias in the terms list and replace it with the terms   * defined for the alias   */ -int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, +			  const char **unit, double *scale)  {  	struct parse_events_term *term, *h;  	struct perf_pmu_alias *alias;  	int ret; +	/* +	 * Mark unit and scale as not set +	 * (different from default values, see below) +	 */ +	*unit   = NULL; +	*scale  = 0.0; +  	list_for_each_entry_safe(term, h, head_terms, list) {  		alias = pmu_find_alias(pmu, term);  		if (!alias) @@ -530,9 +654,26 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)  		ret = pmu_alias_terms(alias, &term->list);  		if (ret)  			return ret; + +		ret = check_unit_scale(alias, unit, scale); +		if (ret) +			return ret; +  		list_del(&term->list);  		free(term);  	} + +	/* +	 * if no unit or scale foundin aliases, then +	 * set defaults as for evsel +	 * unit cannot left to NULL +	 */ +	if (*unit == NULL) +		*unit   = ""; + +	if (*scale == 0.0) +		*scale  = 1.0; +  	return 0;  } @@ -630,10 +771,26 @@ void print_pmu_events(const char *event_glob, bool name_only)  			continue;  		}  		printf("  %-50s [Kernel PMU event]\n", aliases[j]); -		free(aliases[j]); +		zfree(&aliases[j]);  		printed++;  	}  	if (printed)  		printf("\n");  	free(aliases);  } + +bool pmu_have_event(const char *pname, const char *name) +{ +	struct perf_pmu *pmu; +	struct perf_pmu_alias *alias; + +	pmu = NULL; +	while ((pmu = perf_pmu__scan(pmu)) != NULL) { +		if (strcmp(pname, pmu->name)) +			continue; +		list_for_each_entry(alias, &pmu->aliases, list) +			if (!strcmp(alias->name, name)) +				return true; +	} +	return false; +} diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 6b2cbe2d4cc..c14a543ce1f 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -1,7 +1,7 @@  #ifndef __PMU_H  #define __PMU_H -#include <linux/bitops.h> +#include <linux/bitmap.h>  #include <linux/perf_event.h>  #include <stdbool.h> @@ -28,7 +28,8 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,  int perf_pmu__config_terms(struct list_head *formats,  			   struct perf_event_attr *attr,  			   struct list_head *head_terms); -int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, +			  const char **unit, double *scale);  struct list_head *perf_pmu__alias(struct perf_pmu *pmu,  				  struct list_head *head_terms);  int perf_pmu_wrap(void); @@ -42,6 +43,7 @@ int perf_pmu__format_parse(char *dir, struct list_head *head);  struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);  void print_pmu_events(const char *event_glob, bool name_only); +bool pmu_have_event(const char *pname, const char *name);  int perf_pmu__test(void);  #endif /* __PMU_H */ diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index aa04bf9c9ad..9a0a1839a37 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -40,14 +40,13 @@  #include "color.h"  #include "symbol.h"  #include "thread.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include "trace-event.h"	/* For __maybe_unused */  #include "probe-event.h"  #include "probe-finder.h"  #include "session.h"  #define MAX_CMDLEN 256 -#define MAX_PROBE_ARGS 128  #define PERFPROBE_GROUP "probe"  bool probe_event_dry_run;	/* Dry run flag */ @@ -71,33 +70,32 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)  }  static char *synthesize_perf_probe_point(struct perf_probe_point *pp); -static int convert_name_to_addr(struct perf_probe_event *pev, -				const char *exec); -static struct machine machine; +static void clear_probe_trace_event(struct probe_trace_event *tev); +static struct machine *host_machine;  /* Initialize symbol maps and path of vmlinux/modules */ -static int init_vmlinux(void) +static int init_symbol_maps(bool user_only)  {  	int ret;  	symbol_conf.sort_by_name = true; -	if (symbol_conf.vmlinux_name == NULL) -		symbol_conf.try_vmlinux_path = true; -	else -		pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);  	ret = symbol__init();  	if (ret < 0) {  		pr_debug("Failed to init symbol map.\n");  		goto out;  	} -	ret = machine__init(&machine, "", HOST_KERNEL_ID); -	if (ret < 0) -		goto out; +	if (host_machine || user_only)	/* already initialized */ +		return 0; -	if (machine__create_kernel_maps(&machine) < 0) { -		pr_debug("machine__create_kernel_maps() failed.\n"); -		goto out; +	if (symbol_conf.vmlinux_name) +		pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); + +	host_machine = machine__new_host(); +	if (!host_machine) { +		pr_debug("machine__new_host() failed.\n"); +		symbol__exit(); +		ret = -1;  	}  out:  	if (ret < 0) @@ -105,21 +103,66 @@ out:  	return ret;  } +static void exit_symbol_maps(void) +{ +	if (host_machine) { +		machine__delete(host_machine); +		host_machine = NULL; +	} +	symbol__exit(); +} +  static struct symbol *__find_kernel_function_by_name(const char *name,  						     struct map **mapp)  { -	return machine__find_kernel_function_by_name(&machine, name, mapp, +	return machine__find_kernel_function_by_name(host_machine, name, mapp,  						     NULL);  } +static struct symbol *__find_kernel_function(u64 addr, struct map **mapp) +{ +	return machine__find_kernel_function(host_machine, addr, mapp, NULL); +} + +static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void) +{ +	/* kmap->ref_reloc_sym should be set if host_machine is initialized */ +	struct kmap *kmap; + +	if (map__load(host_machine->vmlinux_maps[MAP__FUNCTION], NULL) < 0) +		return NULL; + +	kmap = map__kmap(host_machine->vmlinux_maps[MAP__FUNCTION]); +	return kmap->ref_reloc_sym; +} + +static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) +{ +	struct ref_reloc_sym *reloc_sym; +	struct symbol *sym; +	struct map *map; + +	/* ref_reloc_sym is just a label. Need a special fix*/ +	reloc_sym = kernel_get_ref_reloc_sym(); +	if (reloc_sym && strcmp(name, reloc_sym->name) == 0) +		return (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr; +	else { +		sym = __find_kernel_function_by_name(name, &map); +		if (sym) +			return map->unmap_ip(map, sym->start) - +				(reloc) ? 0 : map->reloc; +	} +	return 0; +} +  static struct map *kernel_get_module_map(const char *module)  {  	struct rb_node *nd; -	struct map_groups *grp = &machine.kmaps; +	struct map_groups *grp = &host_machine->kmaps;  	/* A file path -- this is an offline module */  	if (module && strchr(module, '/')) -		return machine__new_module(&machine, 0, module); +		return machine__new_module(host_machine, 0, module);  	if (!module)  		module = "kernel"; @@ -141,7 +184,7 @@ static struct dso *kernel_get_module_dso(const char *module)  	const char *vmlinux_name;  	if (module) { -		list_for_each_entry(dso, &machine.kernel_dsos, node) { +		list_for_each_entry(dso, &host_machine->kernel_dsos, node) {  			if (strncmp(dso->short_name + 1, module,  				    dso->short_name_len - 2) == 0)  				goto found; @@ -150,12 +193,12 @@ static struct dso *kernel_get_module_dso(const char *module)  		return NULL;  	} -	map = machine.vmlinux_maps[MAP__FUNCTION]; +	map = host_machine->vmlinux_maps[MAP__FUNCTION];  	dso = map->dso;  	vmlinux_name = symbol_conf.vmlinux_name;  	if (vmlinux_name) { -		if (dso__load_vmlinux(dso, map, vmlinux_name, NULL) <= 0) +		if (dso__load_vmlinux(dso, map, vmlinux_name, false, NULL) <= 0)  			return NULL;  	} else {  		if (dso__load_vmlinux_path(dso, map, NULL) <= 0) { @@ -173,46 +216,54 @@ const char *kernel_get_module_path(const char *module)  	return (dso) ? dso->long_name : NULL;  } -static int init_user_exec(void) +static int convert_exec_to_group(const char *exec, char **result)  { -	int ret = 0; +	char *ptr1, *ptr2, *exec_copy; +	char buf[64]; +	int ret; -	symbol_conf.try_vmlinux_path = false; -	symbol_conf.sort_by_name = true; -	ret = symbol__init(); +	exec_copy = strdup(exec); +	if (!exec_copy) +		return -ENOMEM; +	ptr1 = basename(exec_copy); +	if (!ptr1) { +		ret = -EINVAL; +		goto out; +	} + +	ptr2 = strpbrk(ptr1, "-._"); +	if (ptr2) +		*ptr2 = '\0'; +	ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1);  	if (ret < 0) -		pr_debug("Failed to init symbol map.\n"); +		goto out; +	*result = strdup(buf); +	ret = *result ? 0 : -ENOMEM; + +out: +	free(exec_copy);  	return ret;  } -static int convert_to_perf_probe_point(struct probe_trace_point *tp, -					struct perf_probe_point *pp) +static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)  { -	pp->function = strdup(tp->symbol); - -	if (pp->function == NULL) -		return -ENOMEM; - -	pp->offset = tp->offset; -	pp->retprobe = tp->retprobe; +	int i; -	return 0; +	for (i = 0; i < ntevs; i++) +		clear_probe_trace_event(tevs + i);  } -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT +  /* Open new debuginfo of given module */  static struct debuginfo *open_debuginfo(const char *module)  { -	const char *path; +	const char *path = module; -	/* A file path -- this is an offline module */ -	if (module && strchr(module, '/')) -		path = module; -	else { +	if (!module || !strchr(module, '/')) {  		path = kernel_get_module_path(module); -  		if (!path) {  			pr_err("Failed to find path of %s module.\n",  			       module ?: "kernel"); @@ -222,44 +273,110 @@ static struct debuginfo *open_debuginfo(const char *module)  	return debuginfo__new(path);  } +static int get_text_start_address(const char *exec, unsigned long *address) +{ +	Elf *elf; +	GElf_Ehdr ehdr; +	GElf_Shdr shdr; +	int fd, ret = -ENOENT; + +	fd = open(exec, O_RDONLY); +	if (fd < 0) +		return -errno; + +	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); +	if (elf == NULL) +		return -EINVAL; + +	if (gelf_getehdr(elf, &ehdr) == NULL) +		goto out; + +	if (!elf_section_by_name(elf, &ehdr, &shdr, ".text", NULL)) +		goto out; + +	*address = shdr.sh_addr - shdr.sh_offset; +	ret = 0; +out: +	elf_end(elf); +	return ret; +} +  /*   * Convert trace point to probe point with debuginfo - * Currently only handles kprobes.   */ -static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, -					struct perf_probe_point *pp) +static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, +					    struct perf_probe_point *pp, +					    bool is_kprobe)  { -	struct symbol *sym; -	struct map *map; -	u64 addr; +	struct debuginfo *dinfo = NULL; +	unsigned long stext = 0; +	u64 addr = tp->address;  	int ret = -ENOENT; -	struct debuginfo *dinfo; -	sym = __find_kernel_function_by_name(tp->symbol, &map); -	if (sym) { -		addr = map->unmap_ip(map, sym->start + tp->offset); -		pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, -			 tp->offset, addr); +	/* convert the address to dwarf address */ +	if (!is_kprobe) { +		if (!addr) { +			ret = -EINVAL; +			goto error; +		} +		ret = get_text_start_address(tp->module, &stext); +		if (ret < 0) +			goto error; +		addr += stext; +	} else { +		addr = kernel_get_symbol_address_by_name(tp->symbol, false); +		if (addr == 0) +			goto error; +		addr += tp->offset; +	} + +	pr_debug("try to find information at %" PRIx64 " in %s\n", addr, +		 tp->module ? : "kernel"); -		dinfo = debuginfo__new_online_kernel(addr); -		if (dinfo) { -			ret = debuginfo__find_probe_point(dinfo, +	dinfo = open_debuginfo(tp->module); +	if (dinfo) { +		ret = debuginfo__find_probe_point(dinfo,  						 (unsigned long)addr, pp); -			debuginfo__delete(dinfo); -		} else { -			pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", -				 addr); -			ret = -ENOENT; -		} +		debuginfo__delete(dinfo); +	} else { +		pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", addr); +		ret = -ENOENT;  	} -	if (ret <= 0) { -		pr_debug("Failed to find corresponding probes from " -			 "debuginfo. Use kprobe event information.\n"); -		return convert_to_perf_probe_point(tp, pp); + +	if (ret > 0) { +		pp->retprobe = tp->retprobe; +		return 0;  	} -	pp->retprobe = tp->retprobe; +error: +	pr_debug("Failed to find corresponding probes from debuginfo.\n"); +	return ret ? : -ENOENT; +} -	return 0; +static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs, +					  int ntevs, const char *exec) +{ +	int i, ret = 0; +	unsigned long stext = 0; + +	if (!exec) +		return 0; + +	ret = get_text_start_address(exec, &stext); +	if (ret < 0) +		return ret; + +	for (i = 0; i < ntevs && ret >= 0; i++) { +		/* point.address is the addres of point.symbol + point.offset */ +		tevs[i].point.address -= stext; +		tevs[i].point.module = strdup(exec); +		if (!tevs[i].point.module) { +			ret = -ENOMEM; +			break; +		} +		tevs[i].uprobes = true; +	} + +	return ret;  }  static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, @@ -291,12 +408,46 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,  		}  	} -	if (tmp) -		free(tmp); - +	free(tmp);  	return ret;  } +/* Post processing the probe events */ +static int post_process_probe_trace_events(struct probe_trace_event *tevs, +					   int ntevs, const char *module, +					   bool uprobe) +{ +	struct ref_reloc_sym *reloc_sym; +	char *tmp; +	int i; + +	if (uprobe) +		return add_exec_to_probe_trace_events(tevs, ntevs, module); + +	/* Note that currently ref_reloc_sym based probe is not for drivers */ +	if (module) +		return add_module_to_probe_trace_events(tevs, ntevs, module); + +	reloc_sym = kernel_get_ref_reloc_sym(); +	if (!reloc_sym) { +		pr_warning("Relocated base symbol is not found!\n"); +		return -EINVAL; +	} + +	for (i = 0; i < ntevs; i++) { +		if (tevs[i].point.address) { +			tmp = strdup(reloc_sym->name); +			if (!tmp) +				return -ENOMEM; +			free(tevs[i].point.symbol); +			tevs[i].point.symbol = tmp; +			tevs[i].point.offset = tevs[i].point.address - +					       reloc_sym->unrelocated_addr; +		} +	} +	return 0; +} +  /* Try to find perf_probe_event with debuginfo */  static int try_to_find_probe_trace_events(struct perf_probe_event *pev,  					  struct probe_trace_event **tevs, @@ -306,15 +457,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,  	struct debuginfo *dinfo;  	int ntevs, ret = 0; -	if (pev->uprobes) { -		if (need_dwarf) { -			pr_warning("Debuginfo-analysis is not yet supported" -					" with -x/--exec option.\n"); -			return -ENOSYS; -		} -		return convert_name_to_addr(pev, target); -	} -  	dinfo = open_debuginfo(target);  	if (!dinfo) { @@ -326,16 +468,20 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,  		return 0;  	} +	pr_debug("Try to find probe point from debuginfo.\n");  	/* Searching trace events corresponding to a probe event */  	ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs);  	debuginfo__delete(dinfo);  	if (ntevs > 0) {	/* Succeeded to find trace events */ -		pr_debug("find %d probe_trace_events.\n", ntevs); -		if (target) -			ret = add_module_to_probe_trace_events(*tevs, ntevs, -							       target); +		pr_debug("Found %d probe_trace_events.\n", ntevs); +		ret = post_process_probe_trace_events(*tevs, ntevs, +							target, pev->uprobes); +		if (ret < 0) { +			clear_probe_trace_events(*tevs, ntevs); +			zfree(tevs); +		}  		return ret < 0 ? ret : ntevs;  	} @@ -402,15 +548,13 @@ static int get_real_path(const char *raw_path, const char *comp_dir,  		case EFAULT:  			raw_path = strchr(++raw_path, '/');  			if (!raw_path) { -				free(*new_path); -				*new_path = NULL; +				zfree(new_path);  				return -ENOENT;  			}  			continue;  		default: -			free(*new_path); -			*new_path = NULL; +			zfree(new_path);  			return -errno;  		}  	} @@ -466,20 +610,16 @@ static int _show_one_line(FILE *fp, int l, bool skip, bool show_num)   * Show line-range always requires debuginfo to find source file and   * line number.   */ -int show_line_range(struct line_range *lr, const char *module) +static int __show_line_range(struct line_range *lr, const char *module)  {  	int l = 1; -	struct line_node *ln; +	struct int_node *ln;  	struct debuginfo *dinfo;  	FILE *fp;  	int ret;  	char *tmp;  	/* Search a line range */ -	ret = init_vmlinux(); -	if (ret < 0) -		return ret; -  	dinfo = open_debuginfo(module);  	if (!dinfo) {  		pr_warning("Failed to open debuginfo file.\n"); @@ -488,11 +628,11 @@ int show_line_range(struct line_range *lr, const char *module)  	ret = debuginfo__find_line_range(dinfo, lr);  	debuginfo__delete(dinfo); -	if (ret == 0) { +	if (ret == 0 || ret == -ENOENT) {  		pr_warning("Specified source line is not found.\n");  		return -ENOENT;  	} else if (ret < 0) { -		pr_warning("Debuginfo analysis failed. (%d)\n", ret); +		pr_warning("Debuginfo analysis failed.\n");  		return ret;  	} @@ -501,7 +641,7 @@ int show_line_range(struct line_range *lr, const char *module)  	ret = get_real_path(tmp, lr->comp_dir, &lr->path);  	free(tmp);	/* Free old path */  	if (ret < 0) { -		pr_warning("Failed to find source file. (%d)\n", ret); +		pr_warning("Failed to find source file path.\n");  		return ret;  	} @@ -526,8 +666,8 @@ int show_line_range(struct line_range *lr, const char *module)  			goto end;  	} -	list_for_each_entry(ln, &lr->line_list, list) { -		for (; ln->line > l; l++) { +	intlist__for_each(ln, lr->line_list) { +		for (; ln->i > l; l++) {  			ret = show_one_line(fp, l - lr->offset);  			if (ret < 0)  				goto end; @@ -549,6 +689,19 @@ end:  	return ret;  } +int show_line_range(struct line_range *lr, const char *module) +{ +	int ret; + +	ret = init_symbol_maps(false); +	if (ret < 0) +		return ret; +	ret = __show_line_range(lr, module); +	exit_symbol_maps(); + +	return ret; +} +  static int show_available_vars_at(struct debuginfo *dinfo,  				  struct perf_probe_event *pev,  				  int max_vls, struct strfilter *_filter, @@ -568,9 +721,14 @@ static int show_available_vars_at(struct debuginfo *dinfo,  	ret = debuginfo__find_available_vars_at(dinfo, pev, &vls,  						max_vls, externs);  	if (ret <= 0) { -		pr_err("Failed to find variables at %s (%d)\n", buf, ret); +		if (ret == 0 || ret == -ENOENT) { +			pr_err("Failed to find the address of %s\n", buf); +			ret = -ENOENT; +		} else +			pr_warning("Debuginfo analysis failed.\n");  		goto end;  	} +  	/* Some variables are found */  	fprintf(stdout, "Available variables at %s\n", buf);  	for (i = 0; i < ret; i++) { @@ -581,7 +739,7 @@ static int show_available_vars_at(struct debuginfo *dinfo,  		 */  		fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,  			vl->point.offset); -		free(vl->point.symbol); +		zfree(&vl->point.symbol);  		nvars = 0;  		if (vl->vars) {  			strlist__for_each(node, vl->vars) { @@ -610,14 +768,15 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,  	int i, ret = 0;  	struct debuginfo *dinfo; -	ret = init_vmlinux(); +	ret = init_symbol_maps(false);  	if (ret < 0)  		return ret;  	dinfo = open_debuginfo(module);  	if (!dinfo) {  		pr_warning("Failed to open debuginfo file.\n"); -		return -ENOENT; +		ret = -ENOENT; +		goto out;  	}  	setup_pager(); @@ -627,37 +786,31 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,  					     externs);  	debuginfo__delete(dinfo); +out: +	exit_symbol_maps();  	return ret;  } -#else	/* !DWARF_SUPPORT */ +#else	/* !HAVE_DWARF_SUPPORT */ -static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, -					struct perf_probe_point *pp) +static int +find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused, +				 struct perf_probe_point *pp __maybe_unused, +				 bool is_kprobe __maybe_unused)  { -	struct symbol *sym; - -	sym = __find_kernel_function_by_name(tp->symbol, NULL); -	if (!sym) { -		pr_err("Failed to find symbol %s in kernel.\n", tp->symbol); -		return -ENOENT; -	} - -	return convert_to_perf_probe_point(tp, pp); +	return -ENOSYS;  }  static int try_to_find_probe_trace_events(struct perf_probe_event *pev,  				struct probe_trace_event **tevs __maybe_unused, -				int max_tevs __maybe_unused, const char *target) +				int max_tevs __maybe_unused, +				const char *target __maybe_unused)  {  	if (perf_probe_event_need_dwarf(pev)) {  		pr_warning("Debuginfo-analysis is not supported.\n");  		return -ENOSYS;  	} -	if (pev->uprobes) -		return convert_name_to_addr(pev, target); -  	return 0;  } @@ -679,6 +832,26 @@ int show_available_vars(struct perf_probe_event *pevs __maybe_unused,  }  #endif +void line_range__clear(struct line_range *lr) +{ +	free(lr->function); +	free(lr->file); +	free(lr->path); +	free(lr->comp_dir); +	intlist__delete(lr->line_list); +	memset(lr, 0, sizeof(*lr)); +} + +int line_range__init(struct line_range *lr) +{ +	memset(lr, 0, sizeof(*lr)); +	lr->line_list = intlist__new(NULL); +	if (!lr->line_list) +		return -ENOMEM; +	else +		return 0; +} +  static int parse_line_num(char **ptr, int *val, const char *what)  {  	const char *start = *ptr; @@ -1150,16 +1323,21 @@ static int parse_probe_trace_command(const char *cmd,  	} else  		p = argv[1];  	fmt1_str = strtok_r(p, "+", &fmt); -	tp->symbol = strdup(fmt1_str); -	if (tp->symbol == NULL) { -		ret = -ENOMEM; -		goto out; +	if (fmt1_str[0] == '0')	/* only the address started with 0x */ +		tp->address = strtoul(fmt1_str, NULL, 0); +	else { +		/* Only the symbol-based probe has offset */ +		tp->symbol = strdup(fmt1_str); +		if (tp->symbol == NULL) { +			ret = -ENOMEM; +			goto out; +		} +		fmt2_str = strtok_r(NULL, "", &fmt); +		if (fmt2_str == NULL) +			tp->offset = 0; +		else +			tp->offset = strtoul(fmt2_str, NULL, 10);  	} -	fmt2_str = strtok_r(NULL, "", &fmt); -	if (fmt2_str == NULL) -		tp->offset = 0; -	else -		tp->offset = strtoul(fmt2_str, NULL, 10);  	tev->nargs = argc - 2;  	tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); @@ -1279,8 +1457,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)  error:  	pr_debug("Failed to synthesize perf probe point: %s\n",  		 strerror(-ret)); -	if (buf) -		free(buf); +	free(buf);  	return NULL;  } @@ -1402,20 +1579,27 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)  	if (buf == NULL)  		return NULL; +	len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s ", tp->retprobe ? 'r' : 'p', +			 tev->group, tev->event); +	if (len <= 0) +		goto error; + +	/* Uprobes must have tp->address and tp->module */ +	if (tev->uprobes && (!tp->address || !tp->module)) +		goto error; + +	/* Use the tp->address for uprobes */  	if (tev->uprobes) -		len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s:%s", -				 tp->retprobe ? 'r' : 'p', -				 tev->group, tev->event, -				 tp->module, tp->symbol); +		ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx", +				 tp->module, tp->address);  	else -		len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", -				 tp->retprobe ? 'r' : 'p', -				 tev->group, tev->event, +		ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu",  				 tp->module ?: "", tp->module ? ":" : "",  				 tp->symbol, tp->offset); -	if (len <= 0) +	if (ret <= 0)  		goto error; +	len += ret;  	for (i = 0; i < tev->nargs; i++) {  		ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, @@ -1431,6 +1615,79 @@ error:  	return NULL;  } +static int find_perf_probe_point_from_map(struct probe_trace_point *tp, +					  struct perf_probe_point *pp, +					  bool is_kprobe) +{ +	struct symbol *sym = NULL; +	struct map *map; +	u64 addr; +	int ret = -ENOENT; + +	if (!is_kprobe) { +		map = dso__new_map(tp->module); +		if (!map) +			goto out; +		addr = tp->address; +		sym = map__find_symbol(map, addr, NULL); +	} else { +		addr = kernel_get_symbol_address_by_name(tp->symbol, true); +		if (addr) { +			addr += tp->offset; +			sym = __find_kernel_function(addr, &map); +		} +	} +	if (!sym) +		goto out; + +	pp->retprobe = tp->retprobe; +	pp->offset = addr - map->unmap_ip(map, sym->start); +	pp->function = strdup(sym->name); +	ret = pp->function ? 0 : -ENOMEM; + +out: +	if (map && !is_kprobe) { +		dso__delete(map->dso); +		map__delete(map); +	} + +	return ret; +} + +static int convert_to_perf_probe_point(struct probe_trace_point *tp, +					struct perf_probe_point *pp, +					bool is_kprobe) +{ +	char buf[128]; +	int ret; + +	ret = find_perf_probe_point_from_dwarf(tp, pp, is_kprobe); +	if (!ret) +		return 0; +	ret = find_perf_probe_point_from_map(tp, pp, is_kprobe); +	if (!ret) +		return 0; + +	pr_debug("Failed to find probe point from both of dwarf and map.\n"); + +	if (tp->symbol) { +		pp->function = strdup(tp->symbol); +		pp->offset = tp->offset; +	} else if (!tp->module && !is_kprobe) { +		ret = e_snprintf(buf, 128, "0x%" PRIx64, (u64)tp->address); +		if (ret < 0) +			return ret; +		pp->function = strdup(buf); +		pp->offset = 0; +	} +	if (pp->function == NULL) +		return -ENOMEM; + +	pp->retprobe = tp->retprobe; + +	return 0; +} +  static int convert_to_perf_probe_event(struct probe_trace_event *tev,  			       struct perf_probe_event *pev, bool is_kprobe)  { @@ -1444,11 +1701,7 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev,  		return -ENOMEM;  	/* Convert trace_point to probe_point */ -	if (is_kprobe) -		ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); -	else -		ret = convert_to_perf_probe_point(&tev->point, &pev->point); - +	ret = convert_to_perf_probe_point(&tev->point, &pev->point, is_kprobe);  	if (ret < 0)  		return ret; @@ -1481,34 +1734,25 @@ void clear_perf_probe_event(struct perf_probe_event *pev)  	struct perf_probe_arg_field *field, *next;  	int i; -	if (pev->event) -		free(pev->event); -	if (pev->group) -		free(pev->group); -	if (pp->file) -		free(pp->file); -	if (pp->function) -		free(pp->function); -	if (pp->lazy_line) -		free(pp->lazy_line); +	free(pev->event); +	free(pev->group); +	free(pp->file); +	free(pp->function); +	free(pp->lazy_line); +  	for (i = 0; i < pev->nargs; i++) { -		if (pev->args[i].name) -			free(pev->args[i].name); -		if (pev->args[i].var) -			free(pev->args[i].var); -		if (pev->args[i].type) -			free(pev->args[i].type); +		free(pev->args[i].name); +		free(pev->args[i].var); +		free(pev->args[i].type);  		field = pev->args[i].field;  		while (field) {  			next = field->next; -			if (field->name) -				free(field->name); +			zfree(&field->name);  			free(field);  			field = next;  		}  	} -	if (pev->args) -		free(pev->args); +	free(pev->args);  	memset(pev, 0, sizeof(*pev));  } @@ -1517,21 +1761,14 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)  	struct probe_trace_arg_ref *ref, *next;  	int i; -	if (tev->event) -		free(tev->event); -	if (tev->group) -		free(tev->group); -	if (tev->point.symbol) -		free(tev->point.symbol); -	if (tev->point.module) -		free(tev->point.module); +	free(tev->event); +	free(tev->group); +	free(tev->point.symbol); +	free(tev->point.module);  	for (i = 0; i < tev->nargs; i++) { -		if (tev->args[i].name) -			free(tev->args[i].name); -		if (tev->args[i].value) -			free(tev->args[i].value); -		if (tev->args[i].type) -			free(tev->args[i].type); +		free(tev->args[i].name); +		free(tev->args[i].value); +		free(tev->args[i].type);  		ref = tev->args[i].ref;  		while (ref) {  			next = ref->next; @@ -1539,8 +1776,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)  			ref = next;  		}  	} -	if (tev->args) -		free(tev->args); +	free(tev->args);  	memset(tev, 0, sizeof(*tev));  } @@ -1632,7 +1868,8 @@ static struct strlist *get_probe_trace_command_rawlist(int fd)  }  /* Show an event */ -static int show_perf_probe_event(struct perf_probe_event *pev) +static int show_perf_probe_event(struct perf_probe_event *pev, +				 const char *module)  {  	int i, ret;  	char buf[128]; @@ -1648,6 +1885,8 @@ static int show_perf_probe_event(struct perf_probe_event *pev)  		return ret;  	printf("  %-20s (on %s", buf, place); +	if (module) +		printf(" in %s", module);  	if (pev->nargs > 0) {  		printf(" with"); @@ -1685,7 +1924,8 @@ static int __show_perf_probe_events(int fd, bool is_kprobe)  			ret = convert_to_perf_probe_event(&tev, &pev,  								is_kprobe);  			if (ret >= 0) -				ret = show_perf_probe_event(&pev); +				ret = show_perf_probe_event(&pev, +							    tev.point.module);  		}  		clear_perf_probe_event(&pev);  		clear_probe_trace_event(&tev); @@ -1708,7 +1948,7 @@ int show_perf_probe_events(void)  	if (fd < 0)  		return fd; -	ret = init_vmlinux(); +	ret = init_symbol_maps(false);  	if (ret < 0)  		return ret; @@ -1721,6 +1961,7 @@ int show_perf_probe_events(void)  		close(fd);  	} +	exit_symbol_maps();  	return ret;  } @@ -1883,7 +2124,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,  		group = pev->group;  		pev->event = tev->event;  		pev->group = tev->group; -		show_perf_probe_event(pev); +		show_perf_probe_event(pev, tev->point.module);  		/* Trick here - restore current event/group */  		pev->event = (char *)event;  		pev->group = (char *)group; @@ -1909,98 +2150,175 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,  	return ret;  } -static int convert_to_probe_trace_events(struct perf_probe_event *pev, -					  struct probe_trace_event **tevs, -					  int max_tevs, const char *target) +static char *looking_function_name; +static int num_matched_functions; + +static int probe_function_filter(struct map *map __maybe_unused, +				      struct symbol *sym)  { +	if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) && +	    strcmp(looking_function_name, sym->name) == 0) { +		num_matched_functions++; +		return 0; +	} +	return 1; +} + +#define strdup_or_goto(str, label)	\ +	({ char *__p = strdup(str); if (!__p) goto label; __p; }) + +/* + * Find probe function addresses from map. + * Return an error or the number of found probe_trace_event + */ +static int find_probe_trace_events_from_map(struct perf_probe_event *pev, +					    struct probe_trace_event **tevs, +					    int max_tevs, const char *target) +{ +	struct map *map = NULL; +	struct kmap *kmap = NULL; +	struct ref_reloc_sym *reloc_sym = NULL;  	struct symbol *sym; -	int ret = 0, i; +	struct rb_node *nd;  	struct probe_trace_event *tev; +	struct perf_probe_point *pp = &pev->point; +	struct probe_trace_point *tp; +	int ret, i; -	/* Convert perf_probe_event with debuginfo */ -	ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); -	if (ret != 0) -		return ret;	/* Found in debuginfo or got an error */ - -	/* Allocate trace event buffer */ -	tev = *tevs = zalloc(sizeof(struct probe_trace_event)); -	if (tev == NULL) -		return -ENOMEM; +	/* Init maps of given executable or kernel */ +	if (pev->uprobes) +		map = dso__new_map(target); +	else +		map = kernel_get_module_map(target); +	if (!map) { +		ret = -EINVAL; +		goto out; +	} -	/* Copy parameters */ -	tev->point.symbol = strdup(pev->point.function); -	if (tev->point.symbol == NULL) { -		ret = -ENOMEM; -		goto error; +	/* +	 * Load matched symbols: Since the different local symbols may have +	 * same name but different addresses, this lists all the symbols. +	 */ +	num_matched_functions = 0; +	looking_function_name = pp->function; +	ret = map__load(map, probe_function_filter); +	if (ret || num_matched_functions == 0) { +		pr_err("Failed to find symbol %s in %s\n", pp->function, +			target ? : "kernel"); +		ret = -ENOENT; +		goto out; +	} else if (num_matched_functions > max_tevs) { +		pr_err("Too many functions matched in %s\n", +			target ? : "kernel"); +		ret = -E2BIG; +		goto out;  	} -	if (target) { -		tev->point.module = strdup(target); -		if (tev->point.module == NULL) { -			ret = -ENOMEM; -			goto error; +	if (!pev->uprobes) { +		kmap = map__kmap(map); +		reloc_sym = kmap->ref_reloc_sym; +		if (!reloc_sym) { +			pr_warning("Relocated base symbol is not found!\n"); +			ret = -EINVAL; +			goto out;  		}  	} -	tev->point.offset = pev->point.offset; -	tev->point.retprobe = pev->point.retprobe; -	tev->nargs = pev->nargs; -	tev->uprobes = pev->uprobes; +	/* Setup result trace-probe-events */ +	*tevs = zalloc(sizeof(*tev) * num_matched_functions); +	if (!*tevs) { +		ret = -ENOMEM; +		goto out; +	} -	if (tev->nargs) { -		tev->args = zalloc(sizeof(struct probe_trace_arg) -				   * tev->nargs); -		if (tev->args == NULL) { -			ret = -ENOMEM; -			goto error; +	ret = 0; +	map__for_each_symbol(map, sym, nd) { +		tev = (*tevs) + ret; +		tp = &tev->point; +		if (ret == num_matched_functions) { +			pr_warning("Too many symbols are listed. Skip it.\n"); +			break; +		} +		ret++; + +		if (pp->offset > sym->end - sym->start) { +			pr_warning("Offset %ld is bigger than the size of %s\n", +				   pp->offset, sym->name); +			ret = -ENOENT; +			goto err_out; +		} +		/* Add one probe point */ +		tp->address = map->unmap_ip(map, sym->start) + pp->offset; +		if (reloc_sym) { +			tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out); +			tp->offset = tp->address - reloc_sym->addr; +		} else { +			tp->symbol = strdup_or_goto(sym->name, nomem_out); +			tp->offset = pp->offset; +		} +		tp->retprobe = pp->retprobe; +		if (target) +			tev->point.module = strdup_or_goto(target, nomem_out); +		tev->uprobes = pev->uprobes; +		tev->nargs = pev->nargs; +		if (tev->nargs) { +			tev->args = zalloc(sizeof(struct probe_trace_arg) * +					   tev->nargs); +			if (tev->args == NULL) +				goto nomem_out;  		}  		for (i = 0; i < tev->nargs; i++) { -			if (pev->args[i].name) { -				tev->args[i].name = strdup(pev->args[i].name); -				if (tev->args[i].name == NULL) { -					ret = -ENOMEM; -					goto error; -				} -			} -			tev->args[i].value = strdup(pev->args[i].var); -			if (tev->args[i].value == NULL) { -				ret = -ENOMEM; -				goto error; -			} -			if (pev->args[i].type) { -				tev->args[i].type = strdup(pev->args[i].type); -				if (tev->args[i].type == NULL) { -					ret = -ENOMEM; -					goto error; -				} -			} +			if (pev->args[i].name) +				tev->args[i].name = +					strdup_or_goto(pev->args[i].name, +							nomem_out); + +			tev->args[i].value = strdup_or_goto(pev->args[i].var, +							    nomem_out); +			if (pev->args[i].type) +				tev->args[i].type = +					strdup_or_goto(pev->args[i].type, +							nomem_out);  		}  	} -	if (pev->uprobes) -		return 1; +out: +	if (map && pev->uprobes) { +		/* Only when using uprobe(exec) map needs to be released */ +		dso__delete(map->dso); +		map__delete(map); +	} +	return ret; -	/* Currently just checking function name from symbol map */ -	sym = __find_kernel_function_by_name(tev->point.symbol, NULL); -	if (!sym) { -		pr_warning("Kernel symbol \'%s\' not found.\n", -			   tev->point.symbol); -		ret = -ENOENT; -		goto error; -	} else if (tev->point.offset > sym->end - sym->start) { -		pr_warning("Offset specified is greater than size of %s\n", -			   tev->point.symbol); -		ret = -ENOENT; -		goto error; +nomem_out: +	ret = -ENOMEM; +err_out: +	clear_probe_trace_events(*tevs, num_matched_functions); +	zfree(tevs); +	goto out; +} +static int convert_to_probe_trace_events(struct perf_probe_event *pev, +					  struct probe_trace_event **tevs, +					  int max_tevs, const char *target) +{ +	int ret; + +	if (pev->uprobes && !pev->group) { +		/* Replace group name if not given */ +		ret = convert_exec_to_group(target, &pev->group); +		if (ret != 0) { +			pr_warning("Failed to make a group name.\n"); +			return ret; +		}  	} -	return 1; -error: -	clear_probe_trace_event(tev); -	free(tev); -	*tevs = NULL; -	return ret; +	/* Convert perf_probe_event with debuginfo */ +	ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); +	if (ret != 0) +		return ret;	/* Found in debuginfo or got an error */ + +	return find_probe_trace_events_from_map(pev, tevs, max_tevs, target);  }  struct __event_package { @@ -2021,12 +2339,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,  	if (pkgs == NULL)  		return -ENOMEM; -	if (!pevs->uprobes) -		/* Init vmlinux path */ -		ret = init_vmlinux(); -	else -		ret = init_user_exec(); - +	ret = init_symbol_maps(pevs->uprobes);  	if (ret < 0) {  		free(pkgs);  		return ret; @@ -2057,9 +2370,10 @@ end:  	for (i = 0; i < npevs; i++) {  		for (j = 0; j < pkgs[i].ntevs; j++)  			clear_probe_trace_event(&pkgs[i].tevs[j]); -		free(pkgs[i].tevs); +		zfree(&pkgs[i].tevs);  	}  	free(pkgs); +	exit_symbol_maps();  	return ret;  } @@ -2209,166 +2523,51 @@ static struct strfilter *available_func_filter;  static int filter_available_functions(struct map *map __maybe_unused,  				      struct symbol *sym)  { -	if (sym->binding == STB_GLOBAL && +	if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) &&  	    strfilter__compare(available_func_filter, sym->name))  		return 0;  	return 1;  } -static int __show_available_funcs(struct map *map) -{ -	if (map__load(map, filter_available_functions)) { -		pr_err("Failed to load map.\n"); -		return -EINVAL; -	} -	if (!dso__sorted_by_name(map->dso, map->type)) -		dso__sort_by_name(map->dso, map->type); - -	dso__fprintf_symbols_by_name(map->dso, map->type, stdout); -	return 0; -} - -static int available_kernel_funcs(const char *module) +int show_available_funcs(const char *target, struct strfilter *_filter, +					bool user)  {  	struct map *map;  	int ret; -	ret = init_vmlinux(); +	ret = init_symbol_maps(user);  	if (ret < 0)  		return ret; -	map = kernel_get_module_map(module); +	/* Get a symbol map */ +	if (user) +		map = dso__new_map(target); +	else +		map = kernel_get_module_map(target);  	if (!map) { -		pr_err("Failed to find %s map.\n", (module) ? : "kernel"); +		pr_err("Failed to get a map for %s\n", (target) ? : "kernel");  		return -EINVAL;  	} -	return __show_available_funcs(map); -} -static int available_user_funcs(const char *target) -{ -	struct map *map; -	int ret; - -	ret = init_user_exec(); -	if (ret < 0) -		return ret; - -	map = dso__new_map(target); -	ret = __show_available_funcs(map); -	dso__delete(map->dso); -	map__delete(map); -	return ret; -} - -int show_available_funcs(const char *target, struct strfilter *_filter, -					bool user) -{ -	setup_pager(); +	/* Load symbols with given filter */  	available_func_filter = _filter; - -	if (!user) -		return available_kernel_funcs(target); - -	return available_user_funcs(target); -} - -/* - * uprobe_events only accepts address: - * Convert function and any offset to address - */ -static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec) -{ -	struct perf_probe_point *pp = &pev->point; -	struct symbol *sym; -	struct map *map = NULL; -	char *function = NULL, *name = NULL; -	int ret = -EINVAL; -	unsigned long long vaddr = 0; - -	if (!pp->function) { -		pr_warning("No function specified for uprobes"); -		goto out; -	} - -	function = strdup(pp->function); -	if (!function) { -		pr_warning("Failed to allocate memory by strdup.\n"); -		ret = -ENOMEM; -		goto out; -	} - -	name = realpath(exec, NULL); -	if (!name) { -		pr_warning("Cannot find realpath for %s.\n", exec); -		goto out; -	} -	map = dso__new_map(name); -	if (!map) { -		pr_warning("Cannot find appropriate DSO for %s.\n", exec); -		goto out; -	} -	available_func_filter = strfilter__new(function, NULL);  	if (map__load(map, filter_available_functions)) { -		pr_err("Failed to load map.\n"); -		goto out; -	} - -	sym = map__find_symbol_by_name(map, function, NULL); -	if (!sym) { -		pr_warning("Cannot find %s in DSO %s\n", function, exec); -		goto out; -	} - -	if (map->start > sym->start) -		vaddr = map->start; -	vaddr += sym->start + pp->offset + map->pgoff; -	pp->offset = 0; - -	if (!pev->event) { -		pev->event = function; -		function = NULL; -	} -	if (!pev->group) { -		char *ptr1, *ptr2, *exec_copy; - -		pev->group = zalloc(sizeof(char *) * 64); -		exec_copy = strdup(exec); -		if (!exec_copy) { -			ret = -ENOMEM; -			pr_warning("Failed to copy exec string.\n"); -			goto out; -		} - -		ptr1 = strdup(basename(exec_copy)); -		if (ptr1) { -			ptr2 = strpbrk(ptr1, "-._"); -			if (ptr2) -				*ptr2 = '\0'; -			e_snprintf(pev->group, 64, "%s_%s", PERFPROBE_GROUP, -					ptr1); -			free(ptr1); -		} -		free(exec_copy); -	} -	free(pp->function); -	pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS); -	if (!pp->function) { -		ret = -ENOMEM; -		pr_warning("Failed to allocate memory by zalloc.\n"); -		goto out; +		pr_err("Failed to load symbols in %s\n", (target) ? : "kernel"); +		goto end;  	} -	e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr); -	ret = 0; +	if (!dso__sorted_by_name(map->dso, map->type)) +		dso__sort_by_name(map->dso, map->type); -out: -	if (map) { +	/* Show all (filtered) symbols */ +	setup_pager(); +	dso__fprintf_symbols_by_name(map->dso, map->type, stdout); +end: +	if (user) {  		dso__delete(map->dso);  		map__delete(map);  	} -	if (function) -		free(function); -	if (name) -		free(name); +	exit_symbol_maps(); +  	return ret;  } + diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index f9f3de8b422..776c9347a3b 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -2,6 +2,7 @@  #define _PROBE_EVENT_H  #include <stdbool.h> +#include "intlist.h"  #include "strlist.h"  #include "strfilter.h" @@ -12,6 +13,7 @@ struct probe_trace_point {  	char		*symbol;	/* Base symbol */  	char		*module;	/* Module name */  	unsigned long	offset;		/* Offset from symbol */ +	unsigned long	address;	/* Actual address of the trace point */  	bool		retprobe;	/* Return probe flag */  }; @@ -75,13 +77,6 @@ struct perf_probe_event {  	struct perf_probe_arg	*args;	/* Arguments */  }; - -/* Line number container */ -struct line_node { -	struct list_head	list; -	int			line; -}; -  /* Line range */  struct line_range {  	char			*file;		/* File name */ @@ -91,7 +86,7 @@ struct line_range {  	int			offset;		/* Start line offset */  	char			*path;		/* Real path name */  	char			*comp_dir;	/* Compile directory */ -	struct list_head	line_list;	/* Visible lines */ +	struct intlist		*line_list;	/* Visible lines */  };  /* List of variables */ @@ -119,6 +114,12 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev);  /* Command string to line-range */  extern int parse_line_range_desc(const char *cmd, struct line_range *lr); +/* Release line range members */ +extern void line_range__clear(struct line_range *lr); + +/* Initialize line range */ +extern int line_range__init(struct line_range *lr); +  /* Internal use: Return kernel/module path */  extern const char *kernel_get_module_path(const char *module); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 371476cb8dd..98e30476641 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -34,7 +34,9 @@  #include <linux/bitops.h>  #include "event.h" +#include "dso.h"  #include "debug.h" +#include "intlist.h"  #include "util.h"  #include "symbol.h"  #include "probe-finder.h" @@ -42,65 +44,6 @@  /* Kprobe tracer basic type is up to u64 */  #define MAX_BASIC_TYPE_BITS	64 -/* Line number list operations */ - -/* Add a line to line number list */ -static int line_list__add_line(struct list_head *head, int line) -{ -	struct line_node *ln; -	struct list_head *p; - -	/* Reverse search, because new line will be the last one */ -	list_for_each_entry_reverse(ln, head, list) { -		if (ln->line < line) { -			p = &ln->list; -			goto found; -		} else if (ln->line == line)	/* Already exist */ -			return 1; -	} -	/* List is empty, or the smallest entry */ -	p = head; -found: -	pr_debug("line list: add a line %u\n", line); -	ln = zalloc(sizeof(struct line_node)); -	if (ln == NULL) -		return -ENOMEM; -	ln->line = line; -	INIT_LIST_HEAD(&ln->list); -	list_add(&ln->list, p); -	return 0; -} - -/* Check if the line in line number list */ -static int line_list__has_line(struct list_head *head, int line) -{ -	struct line_node *ln; - -	/* Reverse search, because new line will be the last one */ -	list_for_each_entry(ln, head, list) -		if (ln->line == line) -			return 1; - -	return 0; -} - -/* Init line number list */ -static void line_list__init(struct list_head *head) -{ -	INIT_LIST_HEAD(head); -} - -/* Free line number list */ -static void line_list__free(struct list_head *head) -{ -	struct line_node *ln; -	while (!list_empty(head)) { -		ln = list_first_entry(head, struct line_node, list); -		list_del(&ln->list); -		free(ln); -	} -} -  /* Dwarf FL wrappers */  static char *debuginfo_path;	/* Currently dummy */ @@ -115,7 +58,7 @@ static const Dwfl_Callbacks offline_callbacks = {  };  /* Get a Dwarf from offline image */ -static int debuginfo__init_offline_dwarf(struct debuginfo *self, +static int debuginfo__init_offline_dwarf(struct debuginfo *dbg,  					 const char *path)  {  	int fd; @@ -124,136 +67,83 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *self,  	if (fd < 0)  		return fd; -	self->dwfl = dwfl_begin(&offline_callbacks); -	if (!self->dwfl) +	dbg->dwfl = dwfl_begin(&offline_callbacks); +	if (!dbg->dwfl)  		goto error; -	self->mod = dwfl_report_offline(self->dwfl, "", "", fd); -	if (!self->mod) +	dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); +	if (!dbg->mod)  		goto error; -	self->dbg = dwfl_module_getdwarf(self->mod, &self->bias); -	if (!self->dbg) +	dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); +	if (!dbg->dbg)  		goto error;  	return 0;  error: -	if (self->dwfl) -		dwfl_end(self->dwfl); +	if (dbg->dwfl) +		dwfl_end(dbg->dwfl);  	else  		close(fd); -	memset(self, 0, sizeof(*self)); +	memset(dbg, 0, sizeof(*dbg));  	return -ENOENT;  } -#if _ELFUTILS_PREREQ(0, 148) -/* This method is buggy if elfutils is older than 0.148 */ -static int __linux_kernel_find_elf(Dwfl_Module *mod, -				   void **userdata, -				   const char *module_name, -				   Dwarf_Addr base, -				   char **file_name, Elf **elfp) -{ -	int fd; -	const char *path = kernel_get_module_path(module_name); - -	pr_debug2("Use file %s for %s\n", path, module_name); -	if (path) { -		fd = open(path, O_RDONLY); -		if (fd >= 0) { -			*file_name = strdup(path); -			return fd; -		} -	} -	/* If failed, try to call standard method */ -	return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base, -					  file_name, elfp); -} - -static const Dwfl_Callbacks kernel_callbacks = { -	.find_debuginfo = dwfl_standard_find_debuginfo, -	.debuginfo_path = &debuginfo_path, - -	.find_elf = __linux_kernel_find_elf, -	.section_address = dwfl_linux_kernel_module_section_address, -}; - -/* Get a Dwarf from live kernel image */ -static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, -					       Dwarf_Addr addr) +static struct debuginfo *__debuginfo__new(const char *path)  { -	self->dwfl = dwfl_begin(&kernel_callbacks); -	if (!self->dwfl) -		return -EINVAL; - -	/* Load the kernel dwarves: Don't care the result here */ -	dwfl_linux_kernel_report_kernel(self->dwfl); -	dwfl_linux_kernel_report_modules(self->dwfl); - -	self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias); -	/* Here, check whether we could get a real dwarf */ -	if (!self->dbg) { -		pr_debug("Failed to find kernel dwarf at %lx\n", -			 (unsigned long)addr); -		dwfl_end(self->dwfl); -		memset(self, 0, sizeof(*self)); -		return -ENOENT; -	} +	struct debuginfo *dbg = zalloc(sizeof(*dbg)); +	if (!dbg) +		return NULL; -	return 0; +	if (debuginfo__init_offline_dwarf(dbg, path) < 0) +		zfree(&dbg); +	if (dbg) +		pr_debug("Open Debuginfo file: %s\n", path); +	return dbg;  } -#else -/* With older elfutils, this just support kernel module... */ -static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, -					       Dwarf_Addr addr __maybe_unused) -{ -	const char *path = kernel_get_module_path("kernel"); - -	if (!path) { -		pr_err("Failed to find vmlinux path\n"); -		return -ENOENT; -	} -	pr_debug2("Use file %s for debuginfo\n", path); -	return debuginfo__init_offline_dwarf(self, path); -} -#endif +enum dso_binary_type distro_dwarf_types[] = { +	DSO_BINARY_TYPE__FEDORA_DEBUGINFO, +	DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, +	DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, +	DSO_BINARY_TYPE__BUILDID_DEBUGINFO, +	DSO_BINARY_TYPE__NOT_FOUND, +};  struct debuginfo *debuginfo__new(const char *path)  { -	struct debuginfo *self = zalloc(sizeof(struct debuginfo)); -	if (!self) -		return NULL; - -	if (debuginfo__init_offline_dwarf(self, path) < 0) { -		free(self); -		self = NULL; -	} - -	return self; -} - -struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) -{ -	struct debuginfo *self = zalloc(sizeof(struct debuginfo)); -	if (!self) -		return NULL; +	enum dso_binary_type *type; +	char buf[PATH_MAX], nil = '\0'; +	struct dso *dso; +	struct debuginfo *dinfo = NULL; + +	/* Try to open distro debuginfo files */ +	dso = dso__new(path); +	if (!dso) +		goto out; -	if (debuginfo__init_online_kernel_dwarf(self, (Dwarf_Addr)addr) < 0) { -		free(self); -		self = NULL; +	for (type = distro_dwarf_types; +	     !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND; +	     type++) { +		if (dso__read_binary_type_filename(dso, *type, &nil, +						   buf, PATH_MAX) < 0) +			continue; +		dinfo = __debuginfo__new(buf);  	} +	dso__delete(dso); -	return self; +out: +	/* if failed to open all distro debuginfo, open given binary */ +	return dinfo ? : __debuginfo__new(path);  } -void debuginfo__delete(struct debuginfo *self) +void debuginfo__delete(struct debuginfo *dbg)  { -	if (self) { -		if (self->dwfl) -			dwfl_end(self->dwfl); -		free(self); +	if (dbg) { +		if (dbg->dwfl) +			dwfl_end(dbg->dwfl); +		free(dbg);  	}  } @@ -273,12 +163,15 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)  /*   * Convert a location into trace_arg.   * If tvar == NULL, this just checks variable can be converted. + * If fentry == true and vr_die is a parameter, do huristic search + * for the location fuzzed by function entry mcount.   */  static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, -				     Dwarf_Op *fb_ops, +				     Dwarf_Op *fb_ops, Dwarf_Die *sp_die,  				     struct probe_trace_arg *tvar)  {  	Dwarf_Attribute attr; +	Dwarf_Addr tmp = 0;  	Dwarf_Op *op;  	size_t nops;  	unsigned int regn; @@ -291,12 +184,29 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,  		goto static_var;  	/* TODO: handle more than 1 exprs */ -	if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || -	    dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 || -	    nops == 0) { -		/* TODO: Support const_value */ +	if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) +		return -EINVAL;	/* Broken DIE ? */ +	if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { +		ret = dwarf_entrypc(sp_die, &tmp); +		if (ret || addr != tmp || +		    dwarf_tag(vr_die) != DW_TAG_formal_parameter || +		    dwarf_highpc(sp_die, &tmp)) +			return -ENOENT; +		/* +		 * This is fuzzed by fentry mcount. We try to find the +		 * parameter location at the earliest address. +		 */ +		for (addr += 1; addr <= tmp; addr++) { +			if (dwarf_getlocation_addr(&attr, addr, &op, +						   &nops, 1) > 0) +				goto found; +		}  		return -ENOENT;  	} +found: +	if (nops == 0) +		/* TODO: Support const_value */ +		return -ENOENT;  	if (op->atom == DW_OP_addr) {  static_var: @@ -563,7 +473,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,  	}  	if (die_find_member(&type, field->name, die_mem) == NULL) { -		pr_warning("%s(tyep:%s) has no member %s.\n", varname, +		pr_warning("%s(type:%s) has no member %s.\n", varname,  			   dwarf_diename(&type), field->name);  		return -EINVAL;  	} @@ -600,13 +510,13 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)  		 dwarf_diename(vr_die));  	ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, -					pf->tvar); -	if (ret == -ENOENT) +					&pf->sp_die, pf->tvar); +	if (ret == -ENOENT || ret == -EINVAL)  		pr_err("Failed to find the location of %s at this address.\n"  		       " Perhaps, it has been optimized out.\n", pf->pvar->var);  	else if (ret == -ENOTSUP)  		pr_err("Sorry, we don't support this variable location yet.\n"); -	else if (pf->pvar->field) { +	else if (ret == 0 && pf->pvar->field) {  		ret = convert_variable_fields(vr_die, pf->pvar->var,  					      pf->pvar->field, &pf->tvar->ref,  					      &die_mem); @@ -663,14 +573,13 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)  	if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) {  		/* Search again in global variables */  		if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) +			pr_warning("Failed to find '%s' in this function.\n", +				   pf->pvar->var);  			ret = -ENOENT;  	}  	if (ret >= 0)  		ret = convert_variable(&vr_die, pf); -	if (ret < 0) -		pr_warning("Failed to find '%s' in this function.\n", -			   pf->pvar->var);  	return ret;  } @@ -708,6 +617,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod,  		return -ENOENT;  	}  	tp->offset = (unsigned long)(paddr - sym.st_value); +	tp->address = (unsigned long)paddr;  	tp->symbol = strdup(symbol);  	if (!tp->symbol)  		return -ENOMEM; @@ -862,7 +772,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)  }  /* Find lines which match lazy pattern */ -static int find_lazy_match_lines(struct list_head *head, +static int find_lazy_match_lines(struct intlist *list,  				 const char *fname, const char *pat)  {  	FILE *fp; @@ -883,7 +793,7 @@ static int find_lazy_match_lines(struct list_head *head,  			line[len - 1] = '\0';  		if (strlazymatch(line, pat)) { -			line_list__add_line(head, linenum); +			intlist__add(list, linenum);  			count++;  		}  		linenum++; @@ -906,7 +816,7 @@ static int probe_point_lazy_walker(const char *fname, int lineno,  	Dwarf_Die *sc_die, die_mem;  	int ret; -	if (!line_list__has_line(&pf->lcache, lineno) || +	if (!intlist__has_entry(pf->lcache, lineno) ||  	    strtailcmp(fname, pf->fname) != 0)  		return 0; @@ -934,9 +844,9 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)  {  	int ret = 0; -	if (list_empty(&pf->lcache)) { +	if (intlist__empty(pf->lcache)) {  		/* Matching lazy line pattern */ -		ret = find_lazy_match_lines(&pf->lcache, pf->fname, +		ret = find_lazy_match_lines(pf->lcache, pf->fname,  					    pf->pev->point.lazy_line);  		if (ret <= 0)  			return ret; @@ -1063,7 +973,7 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)  }  /* Find probe points from debuginfo */ -static int debuginfo__find_probes(struct debuginfo *self, +static int debuginfo__find_probes(struct debuginfo *dbg,  				  struct probe_finder *pf)  {  	struct perf_probe_point *pp = &pf->pev->point; @@ -1074,11 +984,13 @@ static int debuginfo__find_probes(struct debuginfo *self,  #if _ELFUTILS_PREREQ(0, 142)  	/* Get the call frame information from this dwarf */ -	pf->cfi = dwarf_getcfi(self->dbg); +	pf->cfi = dwarf_getcfi_elf(dwarf_getelf(dbg->dbg));  #endif  	off = 0; -	line_list__init(&pf->lcache); +	pf->lcache = intlist__new(NULL); +	if (!pf->lcache) +		return -ENOMEM;  	/* Fastpath: lookup by function name from .debug_pubnames section */  	if (pp->function) { @@ -1093,7 +1005,7 @@ static int debuginfo__find_probes(struct debuginfo *self,  			.data = pf,  		}; -		dwarf_getpubnames(self->dbg, pubname_search_cb, +		dwarf_getpubnames(dbg->dbg, pubname_search_cb,  				  &pubname_param, 0);  		if (pubname_param.found) {  			ret = probe_point_search_cb(&pf->sp_die, &probe_param); @@ -1103,9 +1015,9 @@ static int debuginfo__find_probes(struct debuginfo *self,  	}  	/* Loop on CUs (Compilation Unit) */ -	while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { +	while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) {  		/* Get the DIE(Debugging Information Entry) of this CU */ -		diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die); +		diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die);  		if (!diep)  			continue; @@ -1131,17 +1043,86 @@ static int debuginfo__find_probes(struct debuginfo *self,  	}  found: -	line_list__free(&pf->lcache); +	intlist__delete(pf->lcache); +	pf->lcache = NULL;  	return ret;  } +struct local_vars_finder { +	struct probe_finder *pf; +	struct perf_probe_arg *args; +	int max_args; +	int nargs; +	int ret; +}; + +/* Collect available variables in this scope */ +static int copy_variables_cb(Dwarf_Die *die_mem, void *data) +{ +	struct local_vars_finder *vf = data; +	struct probe_finder *pf = vf->pf; +	int tag; + +	tag = dwarf_tag(die_mem); +	if (tag == DW_TAG_formal_parameter || +	    tag == DW_TAG_variable) { +		if (convert_variable_location(die_mem, vf->pf->addr, +					      vf->pf->fb_ops, &pf->sp_die, +					      NULL) == 0) { +			vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); +			if (vf->args[vf->nargs].var == NULL) { +				vf->ret = -ENOMEM; +				return DIE_FIND_CB_END; +			} +			pr_debug(" %s", vf->args[vf->nargs].var); +			vf->nargs++; +		} +	} + +	if (dwarf_haspc(die_mem, vf->pf->addr)) +		return DIE_FIND_CB_CONTINUE; +	else +		return DIE_FIND_CB_SIBLING; +} + +static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf, +			     struct perf_probe_arg *args) +{ +	Dwarf_Die die_mem; +	int i; +	int n = 0; +	struct local_vars_finder vf = {.pf = pf, .args = args, +				.max_args = MAX_PROBE_ARGS, .ret = 0}; + +	for (i = 0; i < pf->pev->nargs; i++) { +		/* var never be NULL */ +		if (strcmp(pf->pev->args[i].var, "$vars") == 0) { +			pr_debug("Expanding $vars into:"); +			vf.nargs = n; +			/* Special local variables */ +			die_find_child(sc_die, copy_variables_cb, (void *)&vf, +				       &die_mem); +			pr_debug(" (%d)\n", vf.nargs - n); +			if (vf.ret < 0) +				return vf.ret; +			n = vf.nargs; +		} else { +			/* Copy normal argument */ +			args[n] = pf->pev->args[i]; +			n++; +		} +	} +	return n; +} +  /* Add a found probe point into trace event list */  static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)  {  	struct trace_event_finder *tf =  			container_of(pf, struct trace_event_finder, pf);  	struct probe_trace_event *tev; +	struct perf_probe_arg *args;  	int ret, i;  	/* Check number of tevs */ @@ -1161,31 +1142,45 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)  	pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,  		 tev->point.offset); -	/* Find each argument */ -	tev->nargs = pf->pev->nargs; -	tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); -	if (tev->args == NULL) +	/* Expand special probe argument if exist */ +	args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS); +	if (args == NULL)  		return -ENOMEM; -	for (i = 0; i < pf->pev->nargs; i++) { -		pf->pvar = &pf->pev->args[i]; + +	ret = expand_probe_args(sc_die, pf, args); +	if (ret < 0) +		goto end; + +	tev->nargs = ret; +	tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); +	if (tev->args == NULL) { +		ret = -ENOMEM; +		goto end; +	} + +	/* Find each argument */ +	for (i = 0; i < tev->nargs; i++) { +		pf->pvar = &args[i];  		pf->tvar = &tev->args[i];  		/* Variable should be found from scope DIE */  		ret = find_variable(sc_die, pf);  		if (ret != 0) -			return ret; +			break;  	} -	return 0; +end: +	free(args); +	return ret;  }  /* Find probe_trace_events specified by perf_probe_event from debuginfo */ -int debuginfo__find_trace_events(struct debuginfo *self, +int debuginfo__find_trace_events(struct debuginfo *dbg,  				 struct perf_probe_event *pev,  				 struct probe_trace_event **tevs, int max_tevs)  {  	struct trace_event_finder tf = {  			.pf = {.pev = pev, .callback = add_probe_trace_event}, -			.mod = self->mod, .max_tevs = max_tevs}; +			.mod = dbg->mod, .max_tevs = max_tevs};  	int ret;  	/* Allocate result tevs array */ @@ -1196,10 +1191,9 @@ int debuginfo__find_trace_events(struct debuginfo *self,  	tf.tevs = *tevs;  	tf.ntevs = 0; -	ret = debuginfo__find_probes(self, &tf.pf); +	ret = debuginfo__find_probes(dbg, &tf.pf);  	if (ret < 0) { -		free(*tevs); -		*tevs = NULL; +		zfree(tevs);  		return ret;  	} @@ -1222,7 +1216,8 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)  	if (tag == DW_TAG_formal_parameter ||  	    tag == DW_TAG_variable) {  		ret = convert_variable_location(die_mem, af->pf.addr, -						af->pf.fb_ops, NULL); +						af->pf.fb_ops, &af->pf.sp_die, +						NULL);  		if (ret == 0) {  			ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);  			pr_debug2("Add new var: %s\n", buf); @@ -1285,15 +1280,19 @@ out:  	return ret;  } -/* Find available variables at given probe point */ -int debuginfo__find_available_vars_at(struct debuginfo *self, +/* + * Find available variables at given probe point + * Return the number of found probe points. Return 0 if there is no + * matched probe point. Return <0 if an error occurs. + */ +int debuginfo__find_available_vars_at(struct debuginfo *dbg,  				      struct perf_probe_event *pev,  				      struct variable_list **vls,  				      int max_vls, bool externs)  {  	struct available_var_finder af = {  			.pf = {.pev = pev, .callback = add_available_vars}, -			.mod = self->mod, +			.mod = dbg->mod,  			.max_vls = max_vls, .externs = externs};  	int ret; @@ -1305,17 +1304,14 @@ int debuginfo__find_available_vars_at(struct debuginfo *self,  	af.vls = *vls;  	af.nvls = 0; -	ret = debuginfo__find_probes(self, &af.pf); +	ret = debuginfo__find_probes(dbg, &af.pf);  	if (ret < 0) {  		/* Free vlist for error */  		while (af.nvls--) { -			if (af.vls[af.nvls].point.symbol) -				free(af.vls[af.nvls].point.symbol); -			if (af.vls[af.nvls].vars) -				strlist__delete(af.vls[af.nvls].vars); +			zfree(&af.vls[af.nvls].point.symbol); +			strlist__delete(af.vls[af.nvls].vars);  		} -		free(af.vls); -		*vls = NULL; +		zfree(vls);  		return ret;  	} @@ -1323,19 +1319,19 @@ int debuginfo__find_available_vars_at(struct debuginfo *self,  }  /* Reverse search */ -int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, +int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,  				struct perf_probe_point *ppt)  {  	Dwarf_Die cudie, spdie, indie; -	Dwarf_Addr _addr, baseaddr; -	const char *fname = NULL, *func = NULL, *tmp; +	Dwarf_Addr _addr = 0, baseaddr = 0; +	const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp;  	int baseline = 0, lineno = 0, ret = 0;  	/* Adjust address with bias */ -	addr += self->bias; +	addr += dbg->bias;  	/* Find cu die */ -	if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) { +	if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr - dbg->bias, &cudie)) {  		pr_warning("Failed to find debug information for address %lx\n",  			   addr);  		ret = -EINVAL; @@ -1349,27 +1345,36 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,  	/* Find a corresponding function (name, baseline and baseaddr) */  	if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) {  		/* Get function entry information */ -		tmp = dwarf_diename(&spdie); -		if (!tmp || +		func = basefunc = dwarf_diename(&spdie); +		if (!func ||  		    dwarf_entrypc(&spdie, &baseaddr) != 0 || -		    dwarf_decl_line(&spdie, &baseline) != 0) +		    dwarf_decl_line(&spdie, &baseline) != 0) { +			lineno = 0;  			goto post; -		func = tmp; +		} -		if (addr == (unsigned long)baseaddr) +		fname = dwarf_decl_file(&spdie); +		if (addr == (unsigned long)baseaddr) {  			/* Function entry - Relative line number is 0 */  			lineno = baseline; -		else if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, -					     &indie)) { +			goto post; +		} + +		/* Track down the inline functions step by step */ +		while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr, +						&indie)) { +			/* There is an inline function */  			if (dwarf_entrypc(&indie, &_addr) == 0 && -			    _addr == addr) +			    _addr == addr) {  				/*  				 * addr is at an inline function entry.  				 * In this case, lineno should be the call-site -				 * line number. +				 * line number. (overwrite lineinfo)  				 */  				lineno = die_get_call_lineno(&indie); -			else { +				fname = die_get_call_file(&indie); +				break; +			} else {  				/*  				 * addr is in an inline function body.  				 * Since lineno points one of the lines @@ -1377,19 +1382,27 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,  				 * be the entry line of the inline function.  				 */  				tmp = dwarf_diename(&indie); -				if (tmp && -				    dwarf_decl_line(&spdie, &baseline) == 0) -					func = tmp; +				if (!tmp || +				    dwarf_decl_line(&indie, &baseline) != 0) +					break; +				func = tmp; +				spdie = indie;  			}  		} +		/* Verify the lineno and baseline are in a same file */ +		tmp = dwarf_decl_file(&spdie); +		if (!tmp || strcmp(tmp, fname) != 0) +			lineno = 0;  	}  post:  	/* Make a relative line number or an offset */  	if (lineno)  		ppt->line = lineno - baseline; -	else if (func) +	else if (basefunc) {  		ppt->offset = addr - (unsigned long)baseaddr; +		func = basefunc; +	}  	/* Duplicate strings */  	if (func) { @@ -1402,10 +1415,7 @@ post:  	if (fname) {  		ppt->file = strdup(fname);  		if (ppt->file == NULL) { -			if (ppt->function) { -				free(ppt->function); -				ppt->function = NULL; -			} +			zfree(&ppt->function);  			ret = -ENOMEM;  			goto end;  		} @@ -1426,7 +1436,7 @@ static int line_range_add_line(const char *src, unsigned int lineno,  		if (lr->path == NULL)  			return -ENOMEM;  	} -	return line_list__add_line(&lr->line_list, lineno); +	return intlist__add(lr->line_list, lineno);  }  static int line_range_walk_cb(const char *fname, int lineno, @@ -1434,13 +1444,15 @@ static int line_range_walk_cb(const char *fname, int lineno,  			      void *data)  {  	struct line_finder *lf = data; +	int err;  	if ((strtailcmp(fname, lf->fname) != 0) ||  	    (lf->lno_s > lineno || lf->lno_e < lineno))  		return 0; -	if (line_range_add_line(fname, lineno, lf->lr) < 0) -		return -EINVAL; +	err = line_range_add_line(fname, lineno, lf->lr); +	if (err < 0 && err != -EEXIST) +		return err;  	return 0;  } @@ -1454,27 +1466,27 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)  	/* Update status */  	if (ret >= 0) -		if (!list_empty(&lf->lr->line_list)) +		if (!intlist__empty(lf->lr->line_list))  			ret = lf->found = 1;  		else  			ret = 0;	/* Lines are not found */  	else { -		free(lf->lr->path); -		lf->lr->path = NULL; +		zfree(&lf->lr->path);  	}  	return ret;  }  static int line_range_inline_cb(Dwarf_Die *in_die, void *data)  { -	find_line_range_by_line(in_die, data); +	int ret = find_line_range_by_line(in_die, data);  	/*  	 * We have to check all instances of inlined function, because  	 * some execution paths can be optimized out depends on the -	 * function argument of instances +	 * function argument of instances. However, if an error occurs, +	 * it should be handled by the caller.  	 */ -	return 0; +	return ret < 0 ? ret : 0;  }  /* Search function definition from function name */ @@ -1519,7 +1531,7 @@ static int find_line_range_by_func(struct line_finder *lf)  	return param.retval;  } -int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) +int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr)  {  	struct line_finder lf = {.lr = lr, .found = 0};  	int ret = 0; @@ -1536,7 +1548,7 @@ int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr)  		struct dwarf_callback_param line_range_param = {  			.data = (void *)&lf, .retval = 0}; -		dwarf_getpubnames(self->dbg, pubname_search_cb, +		dwarf_getpubnames(dbg->dbg, pubname_search_cb,  				  &pubname_param, 0);  		if (pubname_param.found) {  			line_range_search_cb(&lf.sp_die, &line_range_param); @@ -1547,12 +1559,12 @@ int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr)  	/* Loop on CUs (Compilation Unit) */  	while (!lf.found && ret >= 0) { -		if (dwarf_nextcu(self->dbg, off, &noff, &cuhl, +		if (dwarf_nextcu(dbg->dbg, off, &noff, &cuhl,  				 NULL, NULL, NULL) != 0)  			break;  		/* Get the DIE(Debugging Information Entry) of this CU */ -		diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die); +		diep = dwarf_offdie(dbg->dbg, off + cuhl, &lf.cu_die);  		if (!diep)  			continue; diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 3b7d6301896..92590b2c7e1 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -3,10 +3,12 @@  #include <stdbool.h>  #include "util.h" +#include "intlist.h"  #include "probe-event.h"  #define MAX_PROBE_BUFFER	1024  #define MAX_PROBES		 128 +#define MAX_PROBE_ARGS		 128  static inline int is_c_varname(const char *name)  { @@ -14,7 +16,7 @@ static inline int is_c_varname(const char *name)  	return isalpha(name[0]) || name[0] == '_';  } -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT  #include "dwarf-aux.h" @@ -28,27 +30,27 @@ struct debuginfo {  	Dwarf_Addr	bias;  }; +/* This also tries to open distro debuginfo */  extern struct debuginfo *debuginfo__new(const char *path); -extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr); -extern void debuginfo__delete(struct debuginfo *self); +extern void debuginfo__delete(struct debuginfo *dbg);  /* Find probe_trace_events specified by perf_probe_event from debuginfo */ -extern int debuginfo__find_trace_events(struct debuginfo *self, +extern int debuginfo__find_trace_events(struct debuginfo *dbg,  					struct perf_probe_event *pev,  					struct probe_trace_event **tevs,  					int max_tevs);  /* Find a perf_probe_point from debuginfo */ -extern int debuginfo__find_probe_point(struct debuginfo *self, +extern int debuginfo__find_probe_point(struct debuginfo *dbg,  				       unsigned long addr,  				       struct perf_probe_point *ppt);  /* Find a line range */ -extern int debuginfo__find_line_range(struct debuginfo *self, +extern int debuginfo__find_line_range(struct debuginfo *dbg,  				      struct line_range *lr);  /* Find available variables */ -extern int debuginfo__find_available_vars_at(struct debuginfo *self, +extern int debuginfo__find_available_vars_at(struct debuginfo *dbg,  					     struct perf_probe_event *pev,  					     struct variable_list **vls,  					     int max_points, bool externs); @@ -65,7 +67,7 @@ struct probe_finder {  	const char		*fname;		/* Real file name */  	Dwarf_Die		cu_die;		/* Current CU */  	Dwarf_Die		sp_die; -	struct list_head	lcache;		/* Line cache for lazy match */ +	struct intlist		*lcache;	/* Line cache for lazy match */  	/* For variable searching */  #if _ELFUTILS_PREREQ(0, 142) @@ -105,6 +107,6 @@ struct line_finder {  	int			found;  }; -#endif /* DWARF_SUPPORT */ +#endif /* HAVE_DWARF_SUPPORT */  #endif /*_PROBE_FINDER_H */ diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h index 4cedea59f51..c3cb6584d52 100644 --- a/tools/perf/util/pstack.h +++ b/tools/perf/util/pstack.h @@ -5,10 +5,10 @@  struct pstack;  struct pstack *pstack__new(unsigned short max_nr_entries); -void pstack__delete(struct pstack *self); -bool pstack__empty(const struct pstack *self); -void pstack__remove(struct pstack *self, void *key); -void pstack__push(struct pstack *self, void *key); -void *pstack__pop(struct pstack *self); +void pstack__delete(struct pstack *pstack); +bool pstack__empty(const struct pstack *pstack); +void pstack__remove(struct pstack *pstack, void *key); +void pstack__push(struct pstack *pstack, void *key); +void *pstack__pop(struct pstack *pstack);  #endif /* _PERF_PSTACK_ */ diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index f75ae1b9900..16a475a7d49 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -17,5 +17,6 @@ util/xyarray.c  util/cgroup.c  util/rblist.c  util/strlist.c -util/sysfs.c +../lib/api/fs/fs.c +util/trace-event.c  ../../lib/rbtree.c diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 71b5412bbbb..122669c18ff 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -33,13 +33,6 @@ int eprintf(int level, const char *fmt, ...)  # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,  #endif -struct throttle_event { -	struct perf_event_header header; -	u64			 time; -	u64			 id; -	u64			 stream_id; -}; -  PyMODINIT_FUNC initperf(void);  #define member_def(type, member, ptype, help) \ @@ -822,6 +815,8 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,  		PyObject *pyevent = pyrf_event__new(event);  		struct pyrf_event *pevent = (struct pyrf_event *)pyevent; +		perf_evlist__mmap_consume(evlist, cpu); +  		if (pyevent == NULL)  			return PyErr_NoMemory(); @@ -913,9 +908,10 @@ static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i)  	if (i >= pevlist->evlist.nr_entries)  		return NULL; -	list_for_each_entry(pos, &pevlist->evlist.entries, node) +	evlist__for_each(&pevlist->evlist, pos) {  		if (i-- == 0)  			break; +	}  	return Py_BuildValue("O", container_of(pos, struct pyrf_evsel, evsel));  } @@ -1036,6 +1032,7 @@ PyMODINIT_FUNC initperf(void)  	    pyrf_cpu_map__setup_types() < 0)  		return; +	/* The page_size is placed in util object. */  	page_size = sysconf(_SC_PAGE_SIZE);  	Py_INCREF(&pyrf_evlist__type); diff --git a/tools/perf/util/rblist.c b/tools/perf/util/rblist.c index a16cdd2625a..0dfe27d9945 100644 --- a/tools/perf/util/rblist.c +++ b/tools/perf/util/rblist.c @@ -48,10 +48,12 @@ void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node)  	rblist->node_delete(rblist, rb_node);  } -struct rb_node *rblist__find(struct rblist *rblist, const void *entry) +static struct rb_node *__rblist__findnew(struct rblist *rblist, +					 const void *entry, +					 bool create)  {  	struct rb_node **p = &rblist->entries.rb_node; -	struct rb_node *parent = NULL; +	struct rb_node *parent = NULL, *new_node = NULL;  	while (*p != NULL) {  		int rc; @@ -67,7 +69,26 @@ struct rb_node *rblist__find(struct rblist *rblist, const void *entry)  			return parent;  	} -	return NULL; +	if (create) { +		new_node = rblist->node_new(rblist, entry); +		if (new_node) { +			rb_link_node(new_node, parent, p); +			rb_insert_color(new_node, &rblist->entries); +			++rblist->nr_entries; +		} +	} + +	return new_node; +} + +struct rb_node *rblist__find(struct rblist *rblist, const void *entry) +{ +	return __rblist__findnew(rblist, entry, false); +} + +struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry) +{ +	return __rblist__findnew(rblist, entry, true);  }  void rblist__init(struct rblist *rblist) diff --git a/tools/perf/util/rblist.h b/tools/perf/util/rblist.h index 6d0cae5ae83..ff9913b994c 100644 --- a/tools/perf/util/rblist.h +++ b/tools/perf/util/rblist.h @@ -32,6 +32,7 @@ void rblist__delete(struct rblist *rblist);  int rblist__add_node(struct rblist *rblist, const void *new_entry);  void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node);  struct rb_node *rblist__find(struct rblist *rblist, const void *entry); +struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry);  struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx);  static inline bool rblist__empty(const struct rblist *rblist) diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 18d73aa2f0f..049e0a09ccd 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -2,6 +2,8 @@  #include "evsel.h"  #include "cpumap.h"  #include "parse-events.h" +#include <api/fs/fs.h> +#include "util.h"  typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel); @@ -72,8 +74,7 @@ bool perf_can_sample_identifier(void)  	return perf_probe_api(perf_probe_sample_identifier);  } -void perf_evlist__config(struct perf_evlist *evlist, -			struct perf_record_opts *opts) +void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts)  {  	struct perf_evsel *evsel;  	bool use_sample_identifier = false; @@ -88,21 +89,127 @@ void perf_evlist__config(struct perf_evlist *evlist,  	if (evlist->cpus->map[0] < 0)  		opts->no_inherit = true; -	list_for_each_entry(evsel, &evlist->entries, node) +	evlist__for_each(evlist, evsel)  		perf_evsel__config(evsel, opts);  	if (evlist->nr_entries > 1) {  		struct perf_evsel *first = perf_evlist__first(evlist); -		list_for_each_entry(evsel, &evlist->entries, node) { +		evlist__for_each(evlist, evsel) {  			if (evsel->attr.sample_type == first->attr.sample_type)  				continue;  			use_sample_identifier = perf_can_sample_identifier();  			break;  		} -		list_for_each_entry(evsel, &evlist->entries, node) +		evlist__for_each(evlist, evsel)  			perf_evsel__set_sample_id(evsel, use_sample_identifier);  	}  	perf_evlist__set_id_pos(evlist);  } + +static int get_max_rate(unsigned int *rate) +{ +	char path[PATH_MAX]; +	const char *procfs = procfs__mountpoint(); + +	if (!procfs) +		return -1; + +	snprintf(path, PATH_MAX, +		 "%s/sys/kernel/perf_event_max_sample_rate", procfs); + +	return filename__read_int(path, (int *) rate); +} + +static int record_opts__config_freq(struct record_opts *opts) +{ +	bool user_freq = opts->user_freq != UINT_MAX; +	unsigned int max_rate; + +	if (opts->user_interval != ULLONG_MAX) +		opts->default_interval = opts->user_interval; +	if (user_freq) +		opts->freq = opts->user_freq; + +	/* +	 * User specified count overrides default frequency. +	 */ +	if (opts->default_interval) +		opts->freq = 0; +	else if (opts->freq) { +		opts->default_interval = opts->freq; +	} else { +		pr_err("frequency and count are zero, aborting\n"); +		return -1; +	} + +	if (get_max_rate(&max_rate)) +		return 0; + +	/* +	 * User specified frequency is over current maximum. +	 */ +	if (user_freq && (max_rate < opts->freq)) { +		pr_err("Maximum frequency rate (%u) reached.\n" +		   "Please use -F freq option with lower value or consider\n" +		   "tweaking /proc/sys/kernel/perf_event_max_sample_rate.\n", +		   max_rate); +		return -1; +	} + +	/* +	 * Default frequency is over current maximum. +	 */ +	if (max_rate < opts->freq) { +		pr_warning("Lowering default frequency rate to %u.\n" +			   "Please consider tweaking " +			   "/proc/sys/kernel/perf_event_max_sample_rate.\n", +			   max_rate); +		opts->freq = max_rate; +	} + +	return 0; +} + +int record_opts__config(struct record_opts *opts) +{ +	return record_opts__config_freq(opts); +} + +bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) +{ +	struct perf_evlist *temp_evlist; +	struct perf_evsel *evsel; +	int err, fd, cpu; +	bool ret = false; + +	temp_evlist = perf_evlist__new(); +	if (!temp_evlist) +		return false; + +	err = parse_events(temp_evlist, str); +	if (err) +		goto out_delete; + +	evsel = perf_evlist__last(temp_evlist); + +	if (!evlist || cpu_map__empty(evlist->cpus)) { +		struct cpu_map *cpus = cpu_map__new(NULL); + +		cpu =  cpus ? cpus->map[0] : 0; +		cpu_map__delete(cpus); +	} else { +		cpu = evlist->cpus->map[0]; +	} + +	fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0); +	if (fd >= 0) { +		close(fd); +		ret = true; +	} + +out_delete: +	perf_evlist__delete(temp_evlist); +	return ret; +} diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index a85e4ae5f3a..af7da565a75 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -194,8 +194,7 @@ static void define_event_symbols(struct event_format *event,  		zero_flag_atom = 0;  		break;  	case PRINT_FIELD: -		if (cur_field_name) -			free(cur_field_name); +		free(cur_field_name);  		cur_field_name = strdup(args->field.name);  		break;  	case PRINT_FLAGS: @@ -216,6 +215,7 @@ static void define_event_symbols(struct event_format *event,  	case PRINT_BSTRING:  	case PRINT_DYNAMIC_ARRAY:  	case PRINT_STRING: +	case PRINT_BITMASK:  		break;  	case PRINT_TYPE:  		define_event_symbols(event, ev_name, args->typecast.item); @@ -257,12 +257,9 @@ static inline struct event_format *find_cache_event(struct perf_evsel *evsel)  	return event;  } -static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, -				    struct perf_sample *sample, +static void perl_process_tracepoint(struct perf_sample *sample,  				    struct perf_evsel *evsel, -				    struct machine *machine __maybe_unused, -				    struct thread *thread, -					struct addr_location *al) +				    struct thread *thread)  {  	struct format_field *field;  	static char handler[256]; @@ -273,7 +270,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,  	int cpu = sample->cpu;  	void *data = sample->raw_data;  	unsigned long long nsecs = sample->time; -	char *comm = thread->comm; +	const char *comm = thread__comm_str(thread);  	dSP; @@ -282,7 +279,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,  	event = find_cache_event(evsel);  	if (!event) -		die("ug! no event found for type %" PRIu64, evsel->attr.config); +		die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);  	pid = raw_field_value(event, "common_pid", data); @@ -349,10 +346,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,  static void perl_process_event_generic(union perf_event *event,  				       struct perf_sample *sample, -				       struct perf_evsel *evsel, -				       struct machine *machine __maybe_unused, -				       struct thread *thread __maybe_unused, -					   struct addr_location *al __maybe_unused) +				       struct perf_evsel *evsel)  {  	dSP; @@ -377,12 +371,11 @@ static void perl_process_event_generic(union perf_event *event,  static void perl_process_event(union perf_event *event,  			       struct perf_sample *sample,  			       struct perf_evsel *evsel, -			       struct machine *machine,  			       struct thread *thread, -				   struct addr_location *al) +			       struct addr_location *al __maybe_unused)  { -	perl_process_tracepoint(event, sample, evsel, machine, thread, al); -	perl_process_event_generic(event, sample, evsel, machine, thread, al); +	perl_process_tracepoint(sample, evsel, thread); +	perl_process_event_generic(event, sample, evsel);  }  static void run_start_sub(void) diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cc75a3cef38..1c419321f70 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -56,6 +56,17 @@ static void handler_call_die(const char *handler_name)  	Py_FatalError("problem in Python trace event handler");  } +/* + * Insert val into into the dictionary and decrement the reference counter. + * This is necessary for dictionaries since PyDict_SetItemString() does not  + * steal a reference, as opposed to PyTuple_SetItem(). + */ +static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObject *val) +{ +	PyDict_SetItemString(dict, key, val); +	Py_DECREF(val); +} +  static void define_value(enum print_arg_type field_type,  			 const char *ev_name,  			 const char *field_name, @@ -150,8 +161,7 @@ static void define_event_symbols(struct event_format *event,  		zero_flag_atom = 0;  		break;  	case PRINT_FIELD: -		if (cur_field_name) -			free(cur_field_name); +		free(cur_field_name);  		cur_field_name = strdup(args->field.name);  		break;  	case PRINT_FLAGS: @@ -187,6 +197,7 @@ static void define_event_symbols(struct event_format *event,  	case PRINT_BSTRING:  	case PRINT_DYNAMIC_ARRAY:  	case PRINT_FUNC: +	case PRINT_BITMASK:  		/* we should warn... */  		return;  	} @@ -220,13 +231,10 @@ static inline struct event_format *find_cache_event(struct perf_evsel *evsel)  	return event;  } -static void python_process_tracepoint(union perf_event *perf_event -				      __maybe_unused, -				 struct perf_sample *sample, -				 struct perf_evsel *evsel, -				 struct machine *machine __maybe_unused, -				 struct thread *thread, -				 struct addr_location *al) +static void python_process_tracepoint(struct perf_sample *sample, +				      struct perf_evsel *evsel, +				      struct thread *thread, +				      struct addr_location *al)  {  	PyObject *handler, *retval, *context, *t, *obj, *dict = NULL;  	static char handler_name[256]; @@ -239,7 +247,7 @@ static void python_process_tracepoint(union perf_event *perf_event  	int cpu = sample->cpu;  	void *data = sample->raw_data;  	unsigned long long nsecs = sample->time; -	char *comm = thread->comm; +	const char *comm = thread__comm_str(thread);  	t = PyTuple_New(MAX_FIELDS);  	if (!t) @@ -279,11 +287,11 @@ static void python_process_tracepoint(union perf_event *perf_event  		PyTuple_SetItem(t, n++, PyInt_FromLong(pid));  		PyTuple_SetItem(t, n++, PyString_FromString(comm));  	} else { -		PyDict_SetItemString(dict, "common_cpu", PyInt_FromLong(cpu)); -		PyDict_SetItemString(dict, "common_s", PyInt_FromLong(s)); -		PyDict_SetItemString(dict, "common_ns", PyInt_FromLong(ns)); -		PyDict_SetItemString(dict, "common_pid", PyInt_FromLong(pid)); -		PyDict_SetItemString(dict, "common_comm", PyString_FromString(comm)); +		pydict_set_item_string_decref(dict, "common_cpu", PyInt_FromLong(cpu)); +		pydict_set_item_string_decref(dict, "common_s", PyInt_FromLong(s)); +		pydict_set_item_string_decref(dict, "common_ns", PyInt_FromLong(ns)); +		pydict_set_item_string_decref(dict, "common_pid", PyInt_FromLong(pid)); +		pydict_set_item_string_decref(dict, "common_comm", PyString_FromString(comm));  	}  	for (field = event->format.fields; field; field = field->next) {  		if (field->flags & FIELD_IS_STRING) { @@ -313,7 +321,7 @@ static void python_process_tracepoint(union perf_event *perf_event  		if (handler)  			PyTuple_SetItem(t, n++, obj);  		else -			PyDict_SetItemString(dict, field->name, obj); +			pydict_set_item_string_decref(dict, field->name, obj);  	}  	if (!handler) @@ -340,11 +348,8 @@ static void python_process_tracepoint(union perf_event *perf_event  	Py_DECREF(t);  } -static void python_process_general_event(union perf_event *perf_event -					 __maybe_unused, -					 struct perf_sample *sample, +static void python_process_general_event(struct perf_sample *sample,  					 struct perf_evsel *evsel, -					 struct machine *machine __maybe_unused,  					 struct thread *thread,  					 struct addr_location *al)  { @@ -370,21 +375,21 @@ static void python_process_general_event(union perf_event *perf_event  	if (!handler || !PyCallable_Check(handler))  		goto exit; -	PyDict_SetItemString(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); -	PyDict_SetItemString(dict, "attr", PyString_FromStringAndSize( +	pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); +	pydict_set_item_string_decref(dict, "attr", PyString_FromStringAndSize(  			(const char *)&evsel->attr, sizeof(evsel->attr))); -	PyDict_SetItemString(dict, "sample", PyString_FromStringAndSize( +	pydict_set_item_string_decref(dict, "sample", PyString_FromStringAndSize(  			(const char *)sample, sizeof(*sample))); -	PyDict_SetItemString(dict, "raw_buf", PyString_FromStringAndSize( +	pydict_set_item_string_decref(dict, "raw_buf", PyString_FromStringAndSize(  			(const char *)sample->raw_data, sample->raw_size)); -	PyDict_SetItemString(dict, "comm", -			PyString_FromString(thread->comm)); +	pydict_set_item_string_decref(dict, "comm", +			PyString_FromString(thread__comm_str(thread)));  	if (al->map) { -		PyDict_SetItemString(dict, "dso", +		pydict_set_item_string_decref(dict, "dso",  			PyString_FromString(al->map->dso->name));  	}  	if (al->sym) { -		PyDict_SetItemString(dict, "symbol", +		pydict_set_item_string_decref(dict, "symbol",  			PyString_FromString(al->sym->name));  	} @@ -400,22 +405,19 @@ exit:  	Py_DECREF(t);  } -static void python_process_event(union perf_event *perf_event, +static void python_process_event(union perf_event *event __maybe_unused,  				 struct perf_sample *sample,  				 struct perf_evsel *evsel, -				 struct machine *machine,  				 struct thread *thread,  				 struct addr_location *al)  {  	switch (evsel->attr.type) {  	case PERF_TYPE_TRACEPOINT: -		python_process_tracepoint(perf_event, sample, evsel, -					  machine, thread, al); +		python_process_tracepoint(sample, evsel, thread, al);  		break;  	/* Reserve for future process_hw/sw/raw APIs */  	default: -		python_process_general_event(perf_event, sample, evsel, -					     machine, thread, al); +		python_process_general_event(sample, evsel, thread, al);  	}  } @@ -621,6 +623,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile)  			fprintf(ofp, "%s=", f->name);  			if (f->flags & FIELD_IS_STRING ||  			    f->flags & FIELD_IS_FLAG || +			    f->flags & FIELD_IS_ARRAY ||  			    f->flags & FIELD_IS_SYMBOLIC)  				fprintf(ofp, "%%s");  			else if (f->flags & FIELD_IS_SIGNED) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 70ffa41518f..64a186edc7b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -16,73 +16,34 @@  #include "perf_regs.h"  #include "vdso.h" -static int perf_session__open(struct perf_session *self, bool force) +static int perf_session__open(struct perf_session *session)  { -	struct stat input_stat; +	struct perf_data_file *file = session->file; -	if (!strcmp(self->filename, "-")) { -		self->fd_pipe = true; -		self->fd = STDIN_FILENO; - -		if (perf_session__read_header(self) < 0) -			pr_err("incompatible file format (rerun with -v to learn more)"); - -		return 0; -	} - -	self->fd = open(self->filename, O_RDONLY); -	if (self->fd < 0) { -		int err = errno; - -		pr_err("failed to open %s: %s", self->filename, strerror(err)); -		if (err == ENOENT && !strcmp(self->filename, "perf.data")) -			pr_err("  (try 'perf record' first)"); -		pr_err("\n"); -		return -errno; -	} - -	if (fstat(self->fd, &input_stat) < 0) -		goto out_close; - -	if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { -		pr_err("file %s not owned by current user or root\n", -		       self->filename); -		goto out_close; -	} - -	if (!input_stat.st_size) { -		pr_info("zero-sized file (%s), nothing to do!\n", -			self->filename); -		goto out_close; -	} - -	if (perf_session__read_header(self) < 0) { +	if (perf_session__read_header(session) < 0) {  		pr_err("incompatible file format (rerun with -v to learn more)"); -		goto out_close; +		return -1;  	} -	if (!perf_evlist__valid_sample_type(self->evlist)) { +	if (perf_data_file__is_pipe(file)) +		return 0; + +	if (!perf_evlist__valid_sample_type(session->evlist)) {  		pr_err("non matching sample_type"); -		goto out_close; +		return -1;  	} -	if (!perf_evlist__valid_sample_id_all(self->evlist)) { +	if (!perf_evlist__valid_sample_id_all(session->evlist)) {  		pr_err("non matching sample_id_all"); -		goto out_close; +		return -1;  	} -	if (!perf_evlist__valid_read_format(self->evlist)) { +	if (!perf_evlist__valid_read_format(session->evlist)) {  		pr_err("non matching read_format"); -		goto out_close; +		return -1;  	} -	self->size = input_stat.st_size;  	return 0; - -out_close: -	close(self->fd); -	self->fd = -1; -	return -1;  }  void perf_session__set_id_hdr_size(struct perf_session *session) @@ -92,71 +53,70 @@ void perf_session__set_id_hdr_size(struct perf_session *session)  	machines__set_id_hdr_size(&session->machines, id_hdr_size);  } -int perf_session__create_kernel_maps(struct perf_session *self) +int perf_session__create_kernel_maps(struct perf_session *session)  { -	int ret = machine__create_kernel_maps(&self->machines.host); +	int ret = machine__create_kernel_maps(&session->machines.host);  	if (ret >= 0) -		ret = machines__create_guest_kernel_maps(&self->machines); +		ret = machines__create_guest_kernel_maps(&session->machines);  	return ret;  } -static void perf_session__destroy_kernel_maps(struct perf_session *self) +static void perf_session__destroy_kernel_maps(struct perf_session *session)  { -	machines__destroy_kernel_maps(&self->machines); +	machines__destroy_kernel_maps(&session->machines);  } -struct perf_session *perf_session__new(const char *filename, int mode, -				       bool force, bool repipe, -				       struct perf_tool *tool) +struct perf_session *perf_session__new(struct perf_data_file *file, +				       bool repipe, struct perf_tool *tool)  { -	struct perf_session *self; -	struct stat st; -	size_t len; - -	if (!filename || !strlen(filename)) { -		if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) -			filename = "-"; -		else -			filename = "perf.data"; -	} - -	len = strlen(filename); -	self = zalloc(sizeof(*self) + len); +	struct perf_session *session = zalloc(sizeof(*session)); -	if (self == NULL) +	if (!session)  		goto out; -	memcpy(self->filename, filename, len); -	self->repipe = repipe; -	INIT_LIST_HEAD(&self->ordered_samples.samples); -	INIT_LIST_HEAD(&self->ordered_samples.sample_cache); -	INIT_LIST_HEAD(&self->ordered_samples.to_free); -	machines__init(&self->machines); +	session->repipe = repipe; +	INIT_LIST_HEAD(&session->ordered_samples.samples); +	INIT_LIST_HEAD(&session->ordered_samples.sample_cache); +	INIT_LIST_HEAD(&session->ordered_samples.to_free); +	machines__init(&session->machines); -	if (mode == O_RDONLY) { -		if (perf_session__open(self, force) < 0) +	if (file) { +		if (perf_data_file__open(file))  			goto out_delete; -		perf_session__set_id_hdr_size(self); -	} else if (mode == O_WRONLY) { + +		session->file = file; + +		if (perf_data_file__is_read(file)) { +			if (perf_session__open(session) < 0) +				goto out_close; + +			perf_session__set_id_hdr_size(session); +		} +	} + +	if (!file || perf_data_file__is_write(file)) {  		/*  		 * In O_RDONLY mode this will be performed when reading the  		 * kernel MMAP event, in perf_event__process_mmap().  		 */ -		if (perf_session__create_kernel_maps(self) < 0) +		if (perf_session__create_kernel_maps(session) < 0)  			goto out_delete;  	}  	if (tool && tool->ordering_requires_timestamps && -	    tool->ordered_samples && !perf_evlist__sample_id_all(self->evlist)) { +	    tool->ordered_samples && !perf_evlist__sample_id_all(session->evlist)) {  		dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");  		tool->ordered_samples = false;  	} -out: -	return self; -out_delete: -	perf_session__delete(self); +	return session; + + out_close: +	perf_data_file__close(file); + out_delete: +	perf_session__delete(session); + out:  	return NULL;  } @@ -172,29 +132,30 @@ static void perf_session__delete_threads(struct perf_session *session)  static void perf_session_env__delete(struct perf_session_env *env)  { -	free(env->hostname); -	free(env->os_release); -	free(env->version); -	free(env->arch); -	free(env->cpu_desc); -	free(env->cpuid); - -	free(env->cmdline); -	free(env->sibling_cores); -	free(env->sibling_threads); -	free(env->numa_nodes); -	free(env->pmu_mappings); -} - -void perf_session__delete(struct perf_session *self) -{ -	perf_session__destroy_kernel_maps(self); -	perf_session__delete_dead_threads(self); -	perf_session__delete_threads(self); -	perf_session_env__delete(&self->header.env); -	machines__exit(&self->machines); -	close(self->fd); -	free(self); +	zfree(&env->hostname); +	zfree(&env->os_release); +	zfree(&env->version); +	zfree(&env->arch); +	zfree(&env->cpu_desc); +	zfree(&env->cpuid); + +	zfree(&env->cmdline); +	zfree(&env->sibling_cores); +	zfree(&env->sibling_threads); +	zfree(&env->numa_nodes); +	zfree(&env->pmu_mappings); +} + +void perf_session__delete(struct perf_session *session) +{ +	perf_session__destroy_kernel_maps(session); +	perf_session__delete_dead_threads(session); +	perf_session__delete_threads(session); +	perf_session_env__delete(&session->header.env); +	machines__exit(&session->machines); +	if (session->file) +		perf_data_file__close(session->file); +	free(session);  	vdso__exit();  } @@ -256,6 +217,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)  		tool->sample = process_event_sample_stub;  	if (tool->mmap == NULL)  		tool->mmap = process_event_stub; +	if (tool->mmap2 == NULL) +		tool->mmap2 = process_event_stub;  	if (tool->comm == NULL)  		tool->comm = process_event_stub;  	if (tool->fork == NULL) @@ -284,27 +247,6 @@ void perf_tool__fill_defaults(struct perf_tool *tool)  	}  } -void mem_bswap_32(void *src, int byte_size) -{ -	u32 *m = src; -	while (byte_size > 0) { -		*m = bswap_32(*m); -		byte_size -= sizeof(u32); -		++m; -	} -} - -void mem_bswap_64(void *src, int byte_size) -{ -	u64 *m = src; - -	while (byte_size > 0) { -		*m = bswap_64(*m); -		byte_size -= sizeof(u64); -		++m; -	} -} -  static void swap_sample_id_all(union perf_event *event, void *data)  {  	void *end = (void *) event + event->header.size; @@ -395,6 +337,17 @@ static void perf_event__read_swap(union perf_event *event, bool sample_id_all)  		swap_sample_id_all(event, &event->read + 1);  } +static void perf_event__throttle_swap(union perf_event *event, +				      bool sample_id_all) +{ +	event->throttle.time	  = bswap_64(event->throttle.time); +	event->throttle.id	  = bswap_64(event->throttle.id); +	event->throttle.stream_id = bswap_64(event->throttle.stream_id); + +	if (sample_id_all) +		swap_sample_id_all(event, &event->throttle + 1); +} +  static u8 revbyte(u8 b)  {  	int rev = (b >> 4) | ((b & 0xf) << 4); @@ -440,6 +393,9 @@ void perf_event__attr_swap(struct perf_event_attr *attr)  	attr->bp_type		= bswap_32(attr->bp_type);  	attr->bp_addr		= bswap_64(attr->bp_addr);  	attr->bp_len		= bswap_64(attr->bp_len); +	attr->branch_sample_type = bswap_64(attr->branch_sample_type); +	attr->sample_regs_user	 = bswap_64(attr->sample_regs_user); +	attr->sample_stack_user  = bswap_32(attr->sample_stack_user);  	swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64));  } @@ -480,6 +436,8 @@ static perf_event__swap_op perf_event__swap_ops[] = {  	[PERF_RECORD_EXIT]		  = perf_event__task_swap,  	[PERF_RECORD_LOST]		  = perf_event__all64_swap,  	[PERF_RECORD_READ]		  = perf_event__read_swap, +	[PERF_RECORD_THROTTLE]		  = perf_event__throttle_swap, +	[PERF_RECORD_UNTHROTTLE]	  = perf_event__throttle_swap,  	[PERF_RECORD_SAMPLE]		  = perf_event__all64_swap,  	[PERF_RECORD_HEADER_ATTR]	  = perf_event__hdr_attr_swap,  	[PERF_RECORD_HEADER_EVENT_TYPE]	  = perf_event__event_type_swap, @@ -523,13 +481,16 @@ static int flush_sample_queue(struct perf_session *s,  	struct perf_sample sample;  	u64 limit = os->next_flush;  	u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; -	unsigned idx = 0, progress_next = os->nr_samples / 16;  	bool show_progress = limit == ULLONG_MAX; +	struct ui_progress prog;  	int ret;  	if (!tool->ordered_samples || !limit)  		return 0; +	if (show_progress) +		ui_progress__init(&prog, os->nr_samples, "Processing time ordered events..."); +  	list_for_each_entry_safe(iter, tmp, head, list) {  		if (session_done())  			return 0; @@ -550,11 +511,9 @@ static int flush_sample_queue(struct perf_session *s,  		os->last_flush = iter->timestamp;  		list_del(&iter->list);  		list_add(&iter->list, &os->sample_cache); -		if (show_progress && (++idx >= progress_next)) { -			progress_next += os->nr_samples / 16; -			ui_progress__update(idx, os->nr_samples, -					    "Processing time ordered events..."); -		} + +		if (show_progress) +			ui_progress__update(&prog, 1);  	}  	if (list_empty(head)) { @@ -743,11 +702,12 @@ static void regs_dump__printf(u64 mask, u64 *regs)  	}  } -static void regs_user__printf(struct perf_sample *sample, u64 mask) +static void regs_user__printf(struct perf_sample *sample)  {  	struct regs_dump *user_regs = &sample->user_regs;  	if (user_regs->regs) { +		u64 mask = user_regs->mask;  		printf("... user regs: mask 0x%" PRIx64 "\n", mask);  		regs_dump__printf(mask, user_regs->regs);  	} @@ -834,7 +794,7 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,  	if (!dump_trace)  		return; -	printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 " addr: %#" PRIx64 "\n", +	printf("(IP, 0x%x): %d/%d: %#" PRIx64 " period: %" PRIu64 " addr: %#" PRIx64 "\n",  	       event->header.misc, sample->pid, sample->tid, sample->ip,  	       sample->period, sample->addr); @@ -847,7 +807,7 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,  		branch_stack__printf(sample);  	if (sample_type & PERF_SAMPLE_REGS_USER) -		regs_user__printf(sample, evsel->attr.sample_regs_user); +		regs_user__printf(sample);  	if (sample_type & PERF_SAMPLE_STACK_USER)  		stack_user__printf(&sample->user_stack); @@ -858,6 +818,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,  	if (sample_type & PERF_SAMPLE_DATA_SRC)  		printf(" . data_src: 0x%"PRIx64"\n", sample->data_src); +	if (sample_type & PERF_SAMPLE_TRANSACTION) +		printf("... transaction: %" PRIx64 "\n", sample->transaction); +  	if (sample_type & PERF_SAMPLE_READ)  		sample_read__printf(sample, evsel->attr.read_format);  } @@ -868,6 +831,7 @@ static struct machine *  					       struct perf_sample *sample)  {  	const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; +	struct machine *machine;  	if (perf_guest &&  	    ((cpumode == PERF_RECORD_MISC_GUEST_KERNEL) || @@ -880,7 +844,11 @@ static struct machine *  		else  			pid = sample->pid; -		return perf_session__findnew_machine(session, pid); +		machine = perf_session__find_machine(session, pid); +		if (!machine) +			machine = perf_session__findnew_machine(session, +						DEFAULT_GUEST_KERNEL_ID); +		return machine;  	}  	return &session->machines.host; @@ -1029,6 +997,7 @@ static int perf_session_deliver_event(struct perf_session *session,  static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,  					    struct perf_tool *tool, u64 file_offset)  { +	int fd = perf_data_file__fd(session->file);  	int err;  	dump_event(session, event, file_offset, NULL); @@ -1040,9 +1009,15 @@ static int perf_session__process_user_event(struct perf_session *session, union  		if (err == 0)  			perf_session__set_id_hdr_size(session);  		return err; +	case PERF_RECORD_HEADER_EVENT_TYPE: +		/* +		 * Depreceated, but we need to handle it for sake +		 * of old data files create in pipe mode. +		 */ +		return 0;  	case PERF_RECORD_HEADER_TRACING_DATA:  		/* setup for reading amidst mmap */ -		lseek(session->fd, file_offset, SEEK_SET); +		lseek(fd, file_offset, SEEK_SET);  		return tool->tracing_data(tool, event, session);  	case PERF_RECORD_HEADER_BUILD_ID:  		return tool->build_id(tool, event, session); @@ -1099,11 +1074,11 @@ static int perf_session__process_event(struct perf_session *session,  					  file_offset);  } -void perf_event_header__bswap(struct perf_event_header *self) +void perf_event_header__bswap(struct perf_event_header *hdr)  { -	self->type = bswap_32(self->type); -	self->misc = bswap_16(self->misc); -	self->size = bswap_16(self->size); +	hdr->type = bswap_32(hdr->type); +	hdr->misc = bswap_16(hdr->misc); +	hdr->size = bswap_16(hdr->size);  }  struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) @@ -1111,11 +1086,11 @@ struct thread *perf_session__findnew(struct perf_session *session, pid_t pid)  	return machine__findnew_thread(&session->machines.host, 0, pid);  } -static struct thread *perf_session__register_idle_thread(struct perf_session *self) +static struct thread *perf_session__register_idle_thread(struct perf_session *session)  { -	struct thread *thread = perf_session__findnew(self, 0); +	struct thread *thread = perf_session__findnew(session, 0); -	if (thread == NULL || thread__set_comm(thread, "swapper")) { +	if (thread == NULL || thread__set_comm(thread, "swapper", 0)) {  		pr_err("problem inserting idle task.\n");  		thread = NULL;  	} @@ -1165,15 +1140,16 @@ static void perf_session__warn_about_errors(const struct perf_session *session,  volatile int session_done; -static int __perf_session__process_pipe_events(struct perf_session *self, +static int __perf_session__process_pipe_events(struct perf_session *session,  					       struct perf_tool *tool)  { +	int fd = perf_data_file__fd(session->file);  	union perf_event *event;  	uint32_t size, cur_size = 0;  	void *buf = NULL;  	int skip = 0;  	u64 head; -	int err; +	ssize_t err;  	void *p;  	perf_tool__fill_defaults(tool); @@ -1186,7 +1162,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self,  		return -errno;  more:  	event = buf; -	err = readn(self->fd, event, sizeof(struct perf_event_header)); +	err = readn(fd, event, sizeof(struct perf_event_header));  	if (err <= 0) {  		if (err == 0)  			goto done; @@ -1195,7 +1171,7 @@ more:  		goto out_err;  	} -	if (self->header.needs_swap) +	if (session->header.needs_swap)  		perf_event_header__bswap(&event->header);  	size = event->header.size; @@ -1218,7 +1194,7 @@ more:  	p += sizeof(struct perf_event_header);  	if (size - sizeof(struct perf_event_header)) { -		err = readn(self->fd, p, size - sizeof(struct perf_event_header)); +		err = readn(fd, p, size - sizeof(struct perf_event_header));  		if (err <= 0) {  			if (err == 0) {  				pr_err("unexpected end of event stream\n"); @@ -1230,7 +1206,7 @@ more:  		}  	} -	if ((skip = perf_session__process_event(self, event, tool, head)) < 0) { +	if ((skip = perf_session__process_event(session, event, tool, head)) < 0) {  		pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n",  		       head, event->header.size, event->header.type);  		err = -EINVAL; @@ -1245,11 +1221,13 @@ more:  	if (!session_done())  		goto more;  done: -	err = 0; +	/* do the final flush for ordered samples */ +	session->ordered_samples.next_flush = ULLONG_MAX; +	err = flush_sample_queue(session, tool);  out_err:  	free(buf); -	perf_session__warn_about_errors(self, tool); -	perf_session_free_sample_buffers(self); +	perf_session__warn_about_errors(session, tool); +	perf_session_free_sample_buffers(session);  	return err;  } @@ -1297,12 +1275,14 @@ int __perf_session__process_events(struct perf_session *session,  				   u64 data_offset, u64 data_size,  				   u64 file_size, struct perf_tool *tool)  { -	u64 head, page_offset, file_offset, file_pos, progress_next; +	int fd = perf_data_file__fd(session->file); +	u64 head, page_offset, file_offset, file_pos;  	int err, mmap_prot, mmap_flags, map_idx = 0;  	size_t	mmap_size;  	char *buf, *mmaps[NUM_MMAPS];  	union perf_event *event;  	uint32_t size; +	struct ui_progress prog;  	perf_tool__fill_defaults(tool); @@ -1310,10 +1290,10 @@ int __perf_session__process_events(struct perf_session *session,  	file_offset = page_offset;  	head = data_offset - page_offset; -	if (data_offset + data_size < file_size) +	if (data_size && (data_offset + data_size < file_size))  		file_size = data_offset + data_size; -	progress_next = file_size / 16; +	ui_progress__init(&prog, file_size, "Processing events...");  	mmap_size = MMAP_SIZE;  	if (mmap_size > file_size) @@ -1329,7 +1309,7 @@ int __perf_session__process_events(struct perf_session *session,  		mmap_flags = MAP_PRIVATE;  	}  remap: -	buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd, +	buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, fd,  		   file_offset);  	if (buf == MAP_FAILED) {  		pr_err("failed to mmap file\n"); @@ -1368,19 +1348,15 @@ more:  	head += size;  	file_pos += size; -	if (file_pos >= progress_next) { -		progress_next += file_size / 16; -		ui_progress__update(file_pos, file_size, -				    "Processing events..."); -	} +	ui_progress__update(&prog, size); -	err = 0;  	if (session_done()) -		goto out_err; +		goto out;  	if (file_pos < file_size)  		goto more; +out:  	/* do the final flush for ordered samples */  	session->ordered_samples.next_flush = ULLONG_MAX;  	err = flush_sample_queue(session, tool); @@ -1391,21 +1367,22 @@ out_err:  	return err;  } -int perf_session__process_events(struct perf_session *self, +int perf_session__process_events(struct perf_session *session,  				 struct perf_tool *tool)  { +	u64 size = perf_data_file__size(session->file);  	int err; -	if (perf_session__register_idle_thread(self) == NULL) +	if (perf_session__register_idle_thread(session) == NULL)  		return -ENOMEM; -	if (!self->fd_pipe) -		err = __perf_session__process_events(self, -						     self->header.data_offset, -						     self->header.data_size, -						     self->size, tool); +	if (!perf_data_file__is_pipe(session->file)) +		err = __perf_session__process_events(session, +						     session->header.data_offset, +						     session->header.data_size, +						     size, tool);  	else -		err = __perf_session__process_pipe_events(self, tool); +		err = __perf_session__process_pipe_events(session, tool);  	return err;  } @@ -1414,7 +1391,7 @@ bool perf_session__has_traces(struct perf_session *session, const char *msg)  {  	struct perf_evsel *evsel; -	list_for_each_entry(evsel, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, evsel) {  		if (evsel->attr.type == PERF_TYPE_TRACEPOINT)  			return true;  	} @@ -1454,15 +1431,15 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps,  	return 0;  } -size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) +size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp)  { -	return machines__fprintf_dsos(&self->machines, fp); +	return machines__fprintf_dsos(&session->machines, fp);  } -size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, +size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp,  					  bool (skip)(struct dso *dso, int parm), int parm)  { -	return machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm); +	return machines__fprintf_dsos_buildid(&session->machines, fp, skip, parm);  }  size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) @@ -1472,7 +1449,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)  	ret += events_stats__fprintf(&session->stats, fp); -	list_for_each_entry(pos, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, pos) {  		ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));  		ret += events_stats__fprintf(&pos->hists.stats, fp);  	} @@ -1494,56 +1471,63 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,  {  	struct perf_evsel *pos; -	list_for_each_entry(pos, &session->evlist->entries, node) { +	evlist__for_each(session->evlist, pos) {  		if (pos->attr.type == type)  			return pos;  	}  	return NULL;  } -void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, -			  struct perf_sample *sample, struct machine *machine, +void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, +			  struct addr_location *al,  			  unsigned int print_opts, unsigned int stack_depth)  { -	struct addr_location al;  	struct callchain_cursor_node *node;  	int print_ip = print_opts & PRINT_IP_OPT_IP;  	int print_sym = print_opts & PRINT_IP_OPT_SYM;  	int print_dso = print_opts & PRINT_IP_OPT_DSO;  	int print_symoffset = print_opts & PRINT_IP_OPT_SYMOFFSET;  	int print_oneline = print_opts & PRINT_IP_OPT_ONELINE; +	int print_srcline = print_opts & PRINT_IP_OPT_SRCLINE;  	char s = print_oneline ? ' ' : '\t'; -	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { -		error("problem processing %d event, skipping it.\n", -			event->header.type); -		return; -	} -  	if (symbol_conf.use_callchain && sample->callchain) { +		struct addr_location node_al; -		if (machine__resolve_callchain(machine, evsel, al.thread, -					       sample, NULL, NULL) != 0) { +		if (machine__resolve_callchain(al->machine, evsel, al->thread, +					       sample, NULL, NULL, +					       PERF_MAX_STACK_DEPTH) != 0) {  			if (verbose)  				error("Failed to resolve callchain. Skipping\n");  			return;  		}  		callchain_cursor_commit(&callchain_cursor); +		if (print_symoffset) +			node_al = *al; +  		while (stack_depth) { +			u64 addr = 0; +  			node = callchain_cursor_current(&callchain_cursor);  			if (!node)  				break; +			if (node->sym && node->sym->ignore) +				goto next; +  			if (print_ip)  				printf("%c%16" PRIx64, s, node->ip); +			if (node->map) +				addr = node->map->map_ip(node->map, node->ip); +  			if (print_sym) {  				printf(" ");  				if (print_symoffset) { -					al.addr = node->ip; -					al.map  = node->map; -					symbol__fprintf_symname_offs(node->sym, &al, stdout); +					node_al.addr = addr; +					node_al.map  = node->map; +					symbol__fprintf_symname_offs(node->sym, &node_al, stdout);  				} else  					symbol__fprintf_symname(node->sym, stdout);  			} @@ -1554,39 +1538,49 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,  				printf(")");  			} +			if (print_srcline) +				map__fprintf_srcline(node->map, addr, "\n  ", +						     stdout); +  			if (!print_oneline)  				printf("\n"); -			callchain_cursor_advance(&callchain_cursor); -  			stack_depth--; +next: +			callchain_cursor_advance(&callchain_cursor);  		}  	} else { +		if (al->sym && al->sym->ignore) +			return; +  		if (print_ip)  			printf("%16" PRIx64, sample->ip);  		if (print_sym) {  			printf(" ");  			if (print_symoffset) -				symbol__fprintf_symname_offs(al.sym, &al, +				symbol__fprintf_symname_offs(al->sym, al,  							     stdout);  			else -				symbol__fprintf_symname(al.sym, stdout); +				symbol__fprintf_symname(al->sym, stdout);  		}  		if (print_dso) {  			printf(" ("); -			map__fprintf_dsoname(al.map, stdout); +			map__fprintf_dsoname(al->map, stdout);  			printf(")");  		} + +		if (print_srcline) +			map__fprintf_srcline(al->map, al->addr, "\n  ", stdout);  	}  }  int perf_session__cpu_bitmap(struct perf_session *session,  			     const char *cpu_list, unsigned long *cpu_bitmap)  { -	int i; +	int i, err = -1;  	struct cpu_map *map;  	for (i = 0; i < PERF_TYPE_MAX; ++i) { @@ -1615,25 +1609,31 @@ int perf_session__cpu_bitmap(struct perf_session *session,  		if (cpu >= MAX_NR_CPUS) {  			pr_err("Requested CPU %d too large. "  			       "Consider raising MAX_NR_CPUS\n", cpu); -			return -1; +			goto out_delete_map;  		}  		set_bit(cpu, cpu_bitmap);  	} -	return 0; +	err = 0; + +out_delete_map: +	cpu_map__delete(map); +	return err;  }  void perf_session__fprintf_info(struct perf_session *session, FILE *fp,  				bool full)  {  	struct stat st; -	int ret; +	int fd, ret;  	if (session == NULL || fp == NULL)  		return; -	ret = fstat(session->fd, &st); +	fd = perf_data_file__fd(session->file); + +	ret = fstat(fd, &st);  	if (ret == -1)  		return; @@ -1662,9 +1662,9 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,  			continue;  		err = -EEXIST; -		if (evsel->handler.func != NULL) +		if (evsel->handler != NULL)  			goto out; -		evsel->handler.func = assocs[i].handler; +		evsel->handler = assocs[i].handler;  	}  	err = 0; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 04bf7373a7e..3140f8ae614 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -1,12 +1,14 @@  #ifndef __PERF_SESSION_H  #define __PERF_SESSION_H +#include "trace-event.h"  #include "hist.h"  #include "event.h"  #include "header.h"  #include "machine.h"  #include "symbol.h"  #include "thread.h" +#include "data.h"  #include <linux/rbtree.h>  #include <linux/perf_event.h> @@ -29,16 +31,13 @@ struct ordered_samples {  struct perf_session {  	struct perf_header	header; -	unsigned long		size;  	struct machines		machines;  	struct perf_evlist	*evlist; -	struct pevent		*pevent; +	struct trace_event	tevent;  	struct events_stats	stats; -	int			fd; -	bool			fd_pipe;  	bool			repipe;  	struct ordered_samples	ordered_samples; -	char			filename[1]; +	struct perf_data_file	*file;  };  #define PRINT_IP_OPT_IP		(1<<0) @@ -46,20 +45,20 @@ struct perf_session {  #define PRINT_IP_OPT_DSO		(1<<2)  #define PRINT_IP_OPT_SYMOFFSET	(1<<3)  #define PRINT_IP_OPT_ONELINE	(1<<4) +#define PRINT_IP_OPT_SRCLINE	(1<<5)  struct perf_tool; -struct perf_session *perf_session__new(const char *filename, int mode, -				       bool force, bool repipe, -				       struct perf_tool *tool); +struct perf_session *perf_session__new(struct perf_data_file *file, +				       bool repipe, struct perf_tool *tool);  void perf_session__delete(struct perf_session *session); -void perf_event_header__bswap(struct perf_event_header *self); +void perf_event_header__bswap(struct perf_event_header *hdr); -int __perf_session__process_events(struct perf_session *self, +int __perf_session__process_events(struct perf_session *session,  				   u64 data_offset, u64 data_size, u64 size,  				   struct perf_tool *tool); -int perf_session__process_events(struct perf_session *self, +int perf_session__process_events(struct perf_session *session,  				 struct perf_tool *tool);  int perf_session_queue_event(struct perf_session *s, union perf_event *event, @@ -67,37 +66,36 @@ int perf_session_queue_event(struct perf_session *s, union perf_event *event,  void perf_tool__fill_defaults(struct perf_tool *tool); -int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel, +int perf_session__resolve_callchain(struct perf_session *session, +				    struct perf_evsel *evsel,  				    struct thread *thread,  				    struct ip_callchain *chain,  				    struct symbol **parent); -bool perf_session__has_traces(struct perf_session *self, const char *msg); +bool perf_session__has_traces(struct perf_session *session, const char *msg); -void mem_bswap_64(void *src, int byte_size); -void mem_bswap_32(void *src, int byte_size);  void perf_event__attr_swap(struct perf_event_attr *attr); -int perf_session__create_kernel_maps(struct perf_session *self); +int perf_session__create_kernel_maps(struct perf_session *session);  void perf_session__set_id_hdr_size(struct perf_session *session);  static inline -struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) +struct machine *perf_session__find_machine(struct perf_session *session, pid_t pid)  { -	return machines__find(&self->machines, pid); +	return machines__find(&session->machines, pid);  }  static inline -struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) +struct machine *perf_session__findnew_machine(struct perf_session *session, pid_t pid)  { -	return machines__findnew(&self->machines, pid); +	return machines__findnew(&session->machines, pid);  } -struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); -size_t perf_session__fprintf(struct perf_session *self, FILE *fp); +struct thread *perf_session__findnew(struct perf_session *session, pid_t pid); +size_t perf_session__fprintf(struct perf_session *session, FILE *fp); -size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); +size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp);  size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp,  					  bool (fn)(struct dso *dso, int parm), int parm); @@ -107,8 +105,8 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp);  struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,  					    unsigned int type); -void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, -			  struct perf_sample *sample, struct machine *machine, +void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, +			  struct addr_location *al,  			  unsigned int print_opts, unsigned int stack_depth);  int perf_session__cpu_bitmap(struct perf_session *session, diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 58ea5ca6c25..d0aee4b9dfd 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -25,7 +25,7 @@ cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter'  build_lib = getenv('PYTHON_EXTBUILD_LIB')  build_tmp = getenv('PYTHON_EXTBUILD_TMP')  libtraceevent = getenv('LIBTRACEEVENT') -liblk = getenv('LIBLK') +libapikfs = getenv('LIBAPIKFS')  ext_sources = [f.strip() for f in file('util/python-ext-sources')  				if len(f.strip()) > 0 and f[0] != '#'] @@ -34,7 +34,7 @@ perf = Extension('perf',  		  sources = ext_sources,  		  include_dirs = ['util/include'],  		  extra_compile_args = cflags, -		  extra_objects = [libtraceevent, liblk], +		  extra_objects = [libtraceevent, libapikfs],                   )  setup(name='perf', diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 5f118a08951..1ec57dd8228 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,22 +1,28 @@ +#include <sys/mman.h>  #include "sort.h"  #include "hist.h" +#include "comm.h"  #include "symbol.h" +#include "evsel.h"  regex_t		parent_regex;  const char	default_parent_pattern[] = "^sys_|^do_page_fault";  const char	*parent_pattern = default_parent_pattern;  const char	default_sort_order[] = "comm,dso,symbol"; -const char	*sort_order = default_sort_order; +const char	default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to"; +const char	default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; +const char	default_top_sort_order[] = "dso,symbol"; +const char	default_diff_sort_order[] = "dso,symbol"; +const char	*sort_order; +const char	*field_order;  regex_t		ignore_callees_regex;  int		have_ignore_callees = 0;  int		sort__need_collapse = 0;  int		sort__has_parent = 0;  int		sort__has_sym = 0; +int		sort__has_dso = 0;  enum sort_mode	sort__mode = SORT_MODE__NORMAL; -enum sort_type	sort__first_dimension; - -LIST_HEAD(hist_entry__sort_list);  static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)  { @@ -42,7 +48,7 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)  	return n;  } -static int64_t cmp_null(void *l, void *r) +static int64_t cmp_null(const void *l, const void *r)  {  	if (!l && !r)  		return 0; @@ -60,11 +66,12 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)  	return right->thread->tid - left->thread->tid;  } -static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  { +	const char *comm = thread__comm_str(he->thread);  	return repsep_snprintf(bf, size, "%*s:%5d", width - 6, -			      self->thread->comm ?: "", self->thread->tid); +			       comm ?: "", he->thread->tid);  }  struct sort_entry sort_thread = { @@ -79,31 +86,34 @@ struct sort_entry sort_thread = {  static int64_t  sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)  { -	return right->thread->tid - left->thread->tid; +	/* Compare the addr that should be unique among comm */ +	return comm__str(right->comm) - comm__str(left->comm);  }  static int64_t  sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)  { -	char *comm_l = left->thread->comm; -	char *comm_r = right->thread->comm; - -	if (!comm_l || !comm_r) -		return cmp_null(comm_l, comm_r); +	/* Compare the addr that should be unique among comm */ +	return comm__str(right->comm) - comm__str(left->comm); +} -	return strcmp(comm_l, comm_r); +static int64_t +sort__comm_sort(struct hist_entry *left, struct hist_entry *right) +{ +	return strcmp(comm__str(right->comm), comm__str(left->comm));  } -static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,  				     size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); +	return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm));  }  struct sort_entry sort_comm = {  	.se_header	= "Command",  	.se_cmp		= sort__comm_cmp,  	.se_collapse	= sort__comm_collapse, +	.se_sort	= sort__comm_sort,  	.se_snprintf	= hist_entry__comm_snprintf,  	.se_width_idx	= HISTC_COMM,  }; @@ -117,7 +127,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)  	const char *dso_name_l, *dso_name_r;  	if (!dso_l || !dso_r) -		return cmp_null(dso_l, dso_r); +		return cmp_null(dso_r, dso_l);  	if (verbose) {  		dso_name_l = dso_l->long_name; @@ -133,7 +143,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)  static int64_t  sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)  { -	return _sort__dso_cmp(left->ms.map, right->ms.map); +	return _sort__dso_cmp(right->ms.map, left->ms.map);  }  static int _hist_entry__dso_snprintf(struct map *map, char *bf, @@ -148,10 +158,10 @@ static int _hist_entry__dso_snprintf(struct map *map, char *bf,  	return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");  } -static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); +	return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);  }  struct sort_entry sort_dso = { @@ -163,6 +173,11 @@ struct sort_entry sort_dso = {  /* --sort symbol */ +static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip) +{ +	return (int64_t)(right_ip - left_ip); +} +  static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)  {  	u64 ip_l, ip_r; @@ -182,12 +197,33 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)  static int64_t  sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)  { +	int64_t ret; +  	if (!left->ms.sym && !right->ms.sym) -		return right->level - left->level; +		return _sort__addr_cmp(left->ip, right->ip); + +	/* +	 * comparing symbol address alone is not enough since it's a +	 * relative address within a dso. +	 */ +	if (!sort__has_dso) { +		ret = sort__dso_cmp(left, right); +		if (ret != 0) +			return ret; +	}  	return _sort__sym_cmp(left->ms.sym, right->ms.sym);  } +static int64_t +sort__sym_sort(struct hist_entry *left, struct hist_entry *right) +{ +	if (!left->ms.sym || !right->ms.sym) +		return cmp_null(left->ms.sym, right->ms.sym); + +	return strcmp(right->ms.sym->name, left->ms.sym->name); +} +  static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,  				     u64 ip, char level, char *bf, size_t size,  				     unsigned int width) @@ -224,16 +260,17 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,  	return ret;  } -static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, -					 self->level, bf, size, width); +	return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip, +					 he->level, bf, size, width);  }  struct sort_entry sort_sym = {  	.se_header	= "Symbol",  	.se_cmp		= sort__sym_cmp, +	.se_sort	= sort__sym_sort,  	.se_snprintf	= hist_entry__sym_snprintf,  	.se_width_idx	= HISTC_SYMBOL,  }; @@ -243,50 +280,32 @@ struct sort_entry sort_sym = {  static int64_t  sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)  { -	return (int64_t)(right->ip - left->ip); +	if (!left->srcline) { +		if (!left->ms.map) +			left->srcline = SRCLINE_UNKNOWN; +		else { +			struct map *map = left->ms.map; +			left->srcline = get_srcline(map->dso, +					    map__rip_2objdump(map, left->ip)); +		} +	} +	if (!right->srcline) { +		if (!right->ms.map) +			right->srcline = SRCLINE_UNKNOWN; +		else { +			struct map *map = right->ms.map; +			right->srcline = get_srcline(map->dso, +					    map__rip_2objdump(map, right->ip)); +		} +	} +	return strcmp(right->srcline, left->srcline);  } -static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,  					size_t size,  					unsigned int width __maybe_unused)  { -	FILE *fp = NULL; -	char cmd[PATH_MAX + 2], *path = self->srcline, *nl; -	size_t line_len; - -	if (path != NULL) -		goto out_path; - -	if (!self->ms.map) -		goto out_ip; - -	if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) -		goto out_ip; - -	snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, -		 self->ms.map->dso->long_name, self->ip); -	fp = popen(cmd, "r"); -	if (!fp) -		goto out_ip; - -	if (getline(&path, &line_len, fp) < 0 || !line_len) -		goto out_ip; -	self->srcline = strdup(path); -	if (self->srcline == NULL) -		goto out_ip; - -	nl = strchr(self->srcline, '\n'); -	if (nl != NULL) -		*nl = '\0'; -	path = self->srcline; -out_path: -	if (fp) -		pclose(fp); -	return repsep_snprintf(bf, size, "%s", path); -out_ip: -	if (fp) -		pclose(fp); -	return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); +	return repsep_snprintf(bf, size, "%s", he->srcline);  }  struct sort_entry sort_srcline = { @@ -307,14 +326,14 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)  	if (!sym_l || !sym_r)  		return cmp_null(sym_l, sym_r); -	return strcmp(sym_l->name, sym_r->name); +	return strcmp(sym_r->name, sym_l->name);  } -static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  {  	return repsep_snprintf(bf, size, "%-*s", width, -			      self->parent ? self->parent->name : "[other]"); +			      he->parent ? he->parent->name : "[other]");  }  struct sort_entry sort_parent = { @@ -332,10 +351,10 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)  	return right->cpu - left->cpu;  } -static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, -				       size_t size, unsigned int width) +static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, +				    size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%*d", width, self->cpu); +	return repsep_snprintf(bf, size, "%*d", width, he->cpu);  }  struct sort_entry sort_cpu = { @@ -354,10 +373,10 @@ sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)  			      right->branch_info->from.map);  } -static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return _hist_entry__dso_snprintf(self->branch_info->from.map, +	return _hist_entry__dso_snprintf(he->branch_info->from.map,  					 bf, size, width);  } @@ -368,10 +387,10 @@ sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)  			      right->branch_info->to.map);  } -static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  { -	return _hist_entry__dso_snprintf(self->branch_info->to.map, +	return _hist_entry__dso_snprintf(he->branch_info->to.map,  					 bf, size, width);  } @@ -382,7 +401,7 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)  	struct addr_map_symbol *from_r = &right->branch_info->from;  	if (!from_l->sym && !from_r->sym) -		return right->level - left->level; +		return _sort__addr_cmp(from_l->addr, from_r->addr);  	return _sort__sym_cmp(from_l->sym, from_r->sym);  } @@ -394,26 +413,26 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)  	struct addr_map_symbol *to_r = &right->branch_info->to;  	if (!to_l->sym && !to_r->sym) -		return right->level - left->level; +		return _sort__addr_cmp(to_l->addr, to_r->addr);  	return _sort__sym_cmp(to_l->sym, to_r->sym);  } -static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,  					 size_t size, unsigned int width)  { -	struct addr_map_symbol *from = &self->branch_info->from; +	struct addr_map_symbol *from = &he->branch_info->from;  	return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, -					 self->level, bf, size, width); +					 he->level, bf, size, width);  } -static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  { -	struct addr_map_symbol *to = &self->branch_info->to; +	struct addr_map_symbol *to = &he->branch_info->to;  	return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, -					 self->level, bf, size, width); +					 he->level, bf, size, width);  } @@ -456,13 +475,13 @@ sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)  	return mp || p;  } -static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width){  	static const char *out = "N/A"; -	if (self->branch_info->flags.predicted) +	if (he->branch_info->flags.predicted)  		out = "N"; -	else if (self->branch_info->flags.mispred) +	else if (he->branch_info->flags.mispred)  		out = "Y";  	return repsep_snprintf(bf, size, "%-*s", width, out); @@ -482,19 +501,19 @@ sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)  	return (int64_t)(r - l);  } -static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	uint64_t addr = 0;  	struct map *map = NULL;  	struct symbol *sym = NULL; -	if (self->mem_info) { -		addr = self->mem_info->daddr.addr; -		map = self->mem_info->daddr.map; -		sym = self->mem_info->daddr.sym; +	if (he->mem_info) { +		addr = he->mem_info->daddr.addr; +		map = he->mem_info->daddr.map; +		sym = he->mem_info->daddr.sym;  	} -	return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size, +	return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,  					 width);  } @@ -512,13 +531,13 @@ sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)  	return _sort__dso_cmp(map_l, map_r);  } -static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	struct map *map = NULL; -	if (self->mem_info) -		map = self->mem_info->daddr.map; +	if (he->mem_info) +		map = he->mem_info->daddr.map;  	return _hist_entry__dso_snprintf(map, bf, size, width);  } @@ -542,14 +561,14 @@ sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)  	return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);  } -static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	const char *out;  	u64 mask = PERF_MEM_LOCK_NA; -	if (self->mem_info) -		mask = self->mem_info->data_src.mem_lock; +	if (he->mem_info) +		mask = he->mem_info->data_src.mem_lock;  	if (mask & PERF_MEM_LOCK_NA)  		out = "N/A"; @@ -591,7 +610,7 @@ static const char * const tlb_access[] = {  };  #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) -static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	char out[64]; @@ -602,8 +621,8 @@ static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf,  	out[0] = '\0'; -	if (self->mem_info) -		m = self->mem_info->data_src.mem_dtlb; +	if (he->mem_info) +		m = he->mem_info->data_src.mem_dtlb;  	hit = m & PERF_MEM_TLB_HIT;  	miss = m & PERF_MEM_TLB_MISS; @@ -668,7 +687,7 @@ static const char * const mem_lvl[] = {  };  #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) -static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	char out[64]; @@ -677,8 +696,8 @@ static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,  	u64 m =  PERF_MEM_LVL_NA;  	u64 hit, miss; -	if (self->mem_info) -		m  = self->mem_info->data_src.mem_lvl; +	if (he->mem_info) +		m  = he->mem_info->data_src.mem_lvl;  	out[0] = '\0'; @@ -736,7 +755,7 @@ static const char * const snoop_access[] = {  };  #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) -static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	char out[64]; @@ -746,8 +765,8 @@ static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,  	out[0] = '\0'; -	if (self->mem_info) -		m = self->mem_info->data_src.mem_snoop; +	if (he->mem_info) +		m = he->mem_info->data_src.mem_snoop;  	for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {  		if (!(m & 0x1)) @@ -766,6 +785,104 @@ static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,  	return repsep_snprintf(bf, size, "%-*s", width, out);  } +static inline  u64 cl_address(u64 address) +{ +	/* return the cacheline of the address */ +	return (address & ~(cacheline_size - 1)); +} + +static int64_t +sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	u64 l, r; +	struct map *l_map, *r_map; + +	if (!left->mem_info)  return -1; +	if (!right->mem_info) return 1; + +	/* group event types together */ +	if (left->cpumode > right->cpumode) return -1; +	if (left->cpumode < right->cpumode) return 1; + +	l_map = left->mem_info->daddr.map; +	r_map = right->mem_info->daddr.map; + +	/* if both are NULL, jump to sort on al_addr instead */ +	if (!l_map && !r_map) +		goto addr; + +	if (!l_map) return -1; +	if (!r_map) return 1; + +	if (l_map->maj > r_map->maj) return -1; +	if (l_map->maj < r_map->maj) return 1; + +	if (l_map->min > r_map->min) return -1; +	if (l_map->min < r_map->min) return 1; + +	if (l_map->ino > r_map->ino) return -1; +	if (l_map->ino < r_map->ino) return 1; + +	if (l_map->ino_generation > r_map->ino_generation) return -1; +	if (l_map->ino_generation < r_map->ino_generation) return 1; + +	/* +	 * Addresses with no major/minor numbers are assumed to be +	 * anonymous in userspace.  Sort those on pid then address. +	 * +	 * The kernel and non-zero major/minor mapped areas are +	 * assumed to be unity mapped.  Sort those on address. +	 */ + +	if ((left->cpumode != PERF_RECORD_MISC_KERNEL) && +	    (!(l_map->flags & MAP_SHARED)) && +	    !l_map->maj && !l_map->min && !l_map->ino && +	    !l_map->ino_generation) { +		/* userspace anonymous */ + +		if (left->thread->pid_ > right->thread->pid_) return -1; +		if (left->thread->pid_ < right->thread->pid_) return 1; +	} + +addr: +	/* al_addr does all the right addr - start + offset calculations */ +	l = cl_address(left->mem_info->daddr.al_addr); +	r = cl_address(right->mem_info->daddr.al_addr); + +	if (l > r) return -1; +	if (l < r) return 1; + +	return 0; +} + +static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf, +					  size_t size, unsigned int width) +{ + +	uint64_t addr = 0; +	struct map *map = NULL; +	struct symbol *sym = NULL; +	char level = he->level; + +	if (he->mem_info) { +		addr = cl_address(he->mem_info->daddr.al_addr); +		map = he->mem_info->daddr.map; +		sym = he->mem_info->daddr.sym; + +		/* print [s] for shared data mmaps */ +		if ((he->cpumode != PERF_RECORD_MISC_KERNEL) && +		     map && (map->type == MAP__VARIABLE) && +		    (map->flags & MAP_SHARED) && +		    (map->maj || map->min || map->ino || +		     map->ino_generation)) +			level = 's'; +		else if (!map) +			level = 'X'; +	} +	return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size, +					 width); +} +  struct sort_entry sort_mispredict = {  	.se_header	= "Branch Mispredicted",  	.se_cmp		= sort__mispredict_cmp, @@ -784,10 +901,10 @@ sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)  	return he_weight(left) - he_weight(right);  } -static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self)); +	return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));  }  struct sort_entry sort_local_weight = { @@ -803,10 +920,10 @@ sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)  	return left->stat.weight - right->stat.weight;  } -static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,  					      size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight); +	return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);  }  struct sort_entry sort_global_weight = { @@ -858,6 +975,134 @@ struct sort_entry sort_mem_snoop = {  	.se_width_idx	= HISTC_MEM_SNOOP,  }; +struct sort_entry sort_mem_dcacheline = { +	.se_header	= "Data Cacheline", +	.se_cmp		= sort__dcacheline_cmp, +	.se_snprintf	= hist_entry__dcacheline_snprintf, +	.se_width_idx	= HISTC_MEM_DCACHELINE, +}; + +static int64_t +sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	return left->branch_info->flags.abort != +		right->branch_info->flags.abort; +} + +static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, +				    size_t size, unsigned int width) +{ +	static const char *out = "."; + +	if (he->branch_info->flags.abort) +		out = "A"; +	return repsep_snprintf(bf, size, "%-*s", width, out); +} + +struct sort_entry sort_abort = { +	.se_header	= "Transaction abort", +	.se_cmp		= sort__abort_cmp, +	.se_snprintf	= hist_entry__abort_snprintf, +	.se_width_idx	= HISTC_ABORT, +}; + +static int64_t +sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	return left->branch_info->flags.in_tx != +		right->branch_info->flags.in_tx; +} + +static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, +				    size_t size, unsigned int width) +{ +	static const char *out = "."; + +	if (he->branch_info->flags.in_tx) +		out = "T"; + +	return repsep_snprintf(bf, size, "%-*s", width, out); +} + +struct sort_entry sort_in_tx = { +	.se_header	= "Branch in transaction", +	.se_cmp		= sort__in_tx_cmp, +	.se_snprintf	= hist_entry__in_tx_snprintf, +	.se_width_idx	= HISTC_IN_TX, +}; + +static int64_t +sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	return left->transaction - right->transaction; +} + +static inline char *add_str(char *p, const char *str) +{ +	strcpy(p, str); +	return p + strlen(str); +} + +static struct txbit { +	unsigned flag; +	const char *name; +	int skip_for_len; +} txbits[] = { +	{ PERF_TXN_ELISION,        "EL ",        0 }, +	{ PERF_TXN_TRANSACTION,    "TX ",        1 }, +	{ PERF_TXN_SYNC,           "SYNC ",      1 }, +	{ PERF_TXN_ASYNC,          "ASYNC ",     0 }, +	{ PERF_TXN_RETRY,          "RETRY ",     0 }, +	{ PERF_TXN_CONFLICT,       "CON ",       0 }, +	{ PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 }, +	{ PERF_TXN_CAPACITY_READ,  "CAP-READ ",  0 }, +	{ 0, NULL, 0 } +}; + +int hist_entry__transaction_len(void) +{ +	int i; +	int len = 0; + +	for (i = 0; txbits[i].name; i++) { +		if (!txbits[i].skip_for_len) +			len += strlen(txbits[i].name); +	} +	len += 4; /* :XX<space> */ +	return len; +} + +static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf, +					    size_t size, unsigned int width) +{ +	u64 t = he->transaction; +	char buf[128]; +	char *p = buf; +	int i; + +	buf[0] = 0; +	for (i = 0; txbits[i].name; i++) +		if (txbits[i].flag & t) +			p = add_str(p, txbits[i].name); +	if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC))) +		p = add_str(p, "NEITHER "); +	if (t & PERF_TXN_ABORT_MASK) { +		sprintf(p, ":%" PRIx64, +			(t & PERF_TXN_ABORT_MASK) >> +			PERF_TXN_ABORT_SHIFT); +		p += strlen(p); +	} + +	return repsep_snprintf(bf, size, "%-*s", width, buf); +} + +struct sort_entry sort_transaction = { +	.se_header	= "Transaction                ", +	.se_cmp		= sort__transaction_cmp, +	.se_snprintf	= hist_entry__transaction_snprintf, +	.se_width_idx	= HISTC_TRANSACTION, +}; +  struct sort_dimension {  	const char		*name;  	struct sort_entry	*entry; @@ -876,6 +1121,7 @@ static struct sort_dimension common_sort_dimensions[] = {  	DIM(SORT_SRCLINE, "srcline", sort_srcline),  	DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),  	DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), +	DIM(SORT_TRANSACTION, "transaction", sort_transaction),  };  #undef DIM @@ -888,6 +1134,8 @@ static struct sort_dimension bstack_sort_dimensions[] = {  	DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),  	DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),  	DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), +	DIM(SORT_IN_TX, "in_tx", sort_in_tx), +	DIM(SORT_ABORT, "abort", sort_abort),  };  #undef DIM @@ -901,23 +1149,199 @@ static struct sort_dimension memory_sort_dimensions[] = {  	DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),  	DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),  	DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), +	DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),  };  #undef DIM -static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) +struct hpp_dimension { +	const char		*name; +	struct perf_hpp_fmt	*fmt; +	int			taken; +}; + +#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], } + +static struct hpp_dimension hpp_sort_dimensions[] = { +	DIM(PERF_HPP__OVERHEAD, "overhead"), +	DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"), +	DIM(PERF_HPP__OVERHEAD_US, "overhead_us"), +	DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"), +	DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"), +	DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"), +	DIM(PERF_HPP__SAMPLES, "sample"), +	DIM(PERF_HPP__PERIOD, "period"), +}; + +#undef DIM + +struct hpp_sort_entry { +	struct perf_hpp_fmt hpp; +	struct sort_entry *se; +}; + +bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)  { -	if (sd->taken) +	struct hpp_sort_entry *hse_a; +	struct hpp_sort_entry *hse_b; + +	if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b)) +		return false; + +	hse_a = container_of(a, struct hpp_sort_entry, hpp); +	hse_b = container_of(b, struct hpp_sort_entry, hpp); + +	return hse_a->se == hse_b->se; +} + +void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) +{ +	struct hpp_sort_entry *hse; + +	if (!perf_hpp__is_sort_entry(fmt))  		return; +	hse = container_of(fmt, struct hpp_sort_entry, hpp); +	hists__new_col_len(hists, hse->se->se_width_idx, +			   strlen(hse->se->se_header)); +} + +static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, +			      struct perf_evsel *evsel) +{ +	struct hpp_sort_entry *hse; +	size_t len; + +	hse = container_of(fmt, struct hpp_sort_entry, hpp); +	len = hists__col_len(&evsel->hists, hse->se->se_width_idx); + +	return scnprintf(hpp->buf, hpp->size, "%*s", len, hse->se->se_header); +} + +static int __sort__hpp_width(struct perf_hpp_fmt *fmt, +			     struct perf_hpp *hpp __maybe_unused, +			     struct perf_evsel *evsel) +{ +	struct hpp_sort_entry *hse; + +	hse = container_of(fmt, struct hpp_sort_entry, hpp); + +	return hists__col_len(&evsel->hists, hse->se->se_width_idx); +} + +static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, +			     struct hist_entry *he) +{ +	struct hpp_sort_entry *hse; +	size_t len; + +	hse = container_of(fmt, struct hpp_sort_entry, hpp); +	len = hists__col_len(he->hists, hse->se->se_width_idx); + +	return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); +} + +static struct hpp_sort_entry * +__sort_dimension__alloc_hpp(struct sort_dimension *sd) +{ +	struct hpp_sort_entry *hse; + +	hse = malloc(sizeof(*hse)); +	if (hse == NULL) { +		pr_err("Memory allocation failed\n"); +		return NULL; +	} + +	hse->se = sd->entry; +	hse->hpp.header = __sort__hpp_header; +	hse->hpp.width = __sort__hpp_width; +	hse->hpp.entry = __sort__hpp_entry; +	hse->hpp.color = NULL; + +	hse->hpp.cmp = sd->entry->se_cmp; +	hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp; +	hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse; + +	INIT_LIST_HEAD(&hse->hpp.list); +	INIT_LIST_HEAD(&hse->hpp.sort_list); +	hse->hpp.elide = false; + +	return hse; +} + +bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format) +{ +	return format->header == __sort__hpp_header; +} + +static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd) +{ +	struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); + +	if (hse == NULL) +		return -1; + +	perf_hpp__register_sort_field(&hse->hpp); +	return 0; +} + +static int __sort_dimension__add_hpp_output(struct sort_dimension *sd) +{ +	struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); + +	if (hse == NULL) +		return -1; + +	perf_hpp__column_register(&hse->hpp); +	return 0; +} + +static int __sort_dimension__add(struct sort_dimension *sd) +{ +	if (sd->taken) +		return 0; + +	if (__sort_dimension__add_hpp_sort(sd) < 0) +		return -1; +  	if (sd->entry->se_collapse)  		sort__need_collapse = 1; -	if (list_empty(&hist_entry__sort_list)) -		sort__first_dimension = idx; +	sd->taken = 1; + +	return 0; +} + +static int __hpp_dimension__add(struct hpp_dimension *hd) +{ +	if (!hd->taken) { +		hd->taken = 1; + +		perf_hpp__register_sort_field(hd->fmt); +	} +	return 0; +} + +static int __sort_dimension__add_output(struct sort_dimension *sd) +{ +	if (sd->taken) +		return 0; + +	if (__sort_dimension__add_hpp_output(sd) < 0) +		return -1; -	list_add_tail(&sd->entry->list, &hist_entry__sort_list);  	sd->taken = 1; +	return 0; +} + +static int __hpp_dimension__add_output(struct hpp_dimension *hd) +{ +	if (!hd->taken) { +		hd->taken = 1; + +		perf_hpp__column_register(hd->fmt); +	} +	return 0;  }  int sort_dimension__add(const char *tok) @@ -942,10 +1366,20 @@ int sort_dimension__add(const char *tok)  			sort__has_parent = 1;  		} else if (sd->entry == &sort_sym) {  			sort__has_sym = 1; +		} else if (sd->entry == &sort_dso) { +			sort__has_dso = 1;  		} -		__sort_dimension__add(sd, i); -		return 0; +		return __sort_dimension__add(sd); +	} + +	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { +		struct hpp_dimension *hd = &hpp_sort_dimensions[i]; + +		if (strncasecmp(tok, hd->name, strlen(tok))) +			continue; + +		return __hpp_dimension__add(hd);  	}  	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { @@ -960,7 +1394,7 @@ int sort_dimension__add(const char *tok)  		if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)  			sort__has_sym = 1; -		__sort_dimension__add(sd, i + __SORT_BRANCH_STACK); +		__sort_dimension__add(sd);  		return 0;  	} @@ -976,18 +1410,47 @@ int sort_dimension__add(const char *tok)  		if (sd->entry == &sort_mem_daddr_sym)  			sort__has_sym = 1; -		__sort_dimension__add(sd, i + __SORT_MEMORY_MODE); +		__sort_dimension__add(sd);  		return 0;  	}  	return -ESRCH;  } -int setup_sorting(void) +static const char *get_default_sort_order(void) +{ +	const char *default_sort_orders[] = { +		default_sort_order, +		default_branch_sort_order, +		default_mem_sort_order, +		default_top_sort_order, +		default_diff_sort_order, +	}; + +	BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders)); + +	return default_sort_orders[sort__mode]; +} + +static int __setup_sorting(void)  { -	char *tmp, *tok, *str = strdup(sort_order); +	char *tmp, *tok, *str; +	const char *sort_keys = sort_order;  	int ret = 0; +	if (sort_keys == NULL) { +		if (field_order) { +			/* +			 * If user specified field order but no sort order, +			 * we'll honor it and not add default sort orders. +			 */ +			return 0; +		} + +		sort_keys = get_default_sort_order(); +	} + +	str = strdup(sort_keys);  	if (str == NULL) {  		error("Not enough memory to setup sort keys");  		return -ENOMEM; @@ -1009,53 +1472,235 @@ int setup_sorting(void)  	return ret;  } -static void sort_entry__setup_elide(struct sort_entry *self, -				    struct strlist *list, -				    const char *list_name, FILE *fp) +void perf_hpp__set_elide(int idx, bool elide) +{ +	struct perf_hpp_fmt *fmt; +	struct hpp_sort_entry *hse; + +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		hse = container_of(fmt, struct hpp_sort_entry, hpp); +		if (hse->se->se_width_idx == idx) { +			fmt->elide = elide; +			break; +		} +	} +} + +static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)  {  	if (list && strlist__nr_entries(list) == 1) {  		if (fp != NULL)  			fprintf(fp, "# %s: %s\n", list_name,  				strlist__entry(list, 0)->s); -		self->elide = true; +		return true;  	} +	return false; +} + +static bool get_elide(int idx, FILE *output) +{ +	switch (idx) { +	case HISTC_SYMBOL: +		return __get_elide(symbol_conf.sym_list, "symbol", output); +	case HISTC_DSO: +		return __get_elide(symbol_conf.dso_list, "dso", output); +	case HISTC_COMM: +		return __get_elide(symbol_conf.comm_list, "comm", output); +	default: +		break; +	} + +	if (sort__mode != SORT_MODE__BRANCH) +		return false; + +	switch (idx) { +	case HISTC_SYMBOL_FROM: +		return __get_elide(symbol_conf.sym_from_list, "sym_from", output); +	case HISTC_SYMBOL_TO: +		return __get_elide(symbol_conf.sym_to_list, "sym_to", output); +	case HISTC_DSO_FROM: +		return __get_elide(symbol_conf.dso_from_list, "dso_from", output); +	case HISTC_DSO_TO: +		return __get_elide(symbol_conf.dso_to_list, "dso_to", output); +	default: +		break; +	} + +	return false;  }  void sort__setup_elide(FILE *output)  { -	sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -				"dso", output); -	sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, -				"comm", output); -	sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, -				"symbol", output); - -	if (sort__mode == SORT_MODE__BRANCH) { -		sort_entry__setup_elide(&sort_dso_from, -					symbol_conf.dso_from_list, -					"dso_from", output); -		sort_entry__setup_elide(&sort_dso_to, -					symbol_conf.dso_to_list, -					"dso_to", output); -		sort_entry__setup_elide(&sort_sym_from, -					symbol_conf.sym_from_list, -					"sym_from", output); -		sort_entry__setup_elide(&sort_sym_to, -					symbol_conf.sym_to_list, -					"sym_to", output); -	} else if (sort__mode == SORT_MODE__MEMORY) { -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"symbol_daddr", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"dso_daddr", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"mem", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"local_weight", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"tlb", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"snoop", output); +	struct perf_hpp_fmt *fmt; +	struct hpp_sort_entry *hse; + +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		hse = container_of(fmt, struct hpp_sort_entry, hpp); +		fmt->elide = get_elide(hse->se->se_width_idx, output); +	} + +	/* +	 * It makes no sense to elide all of sort entries. +	 * Just revert them to show up again. +	 */ +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		if (!fmt->elide) +			return;  	} +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		fmt->elide = false; +	} +} + +static int output_field_add(char *tok) +{ +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { +		struct sort_dimension *sd = &common_sort_dimensions[i]; + +		if (strncasecmp(tok, sd->name, strlen(tok))) +			continue; + +		return __sort_dimension__add_output(sd); +	} + +	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { +		struct hpp_dimension *hd = &hpp_sort_dimensions[i]; + +		if (strncasecmp(tok, hd->name, strlen(tok))) +			continue; + +		return __hpp_dimension__add_output(hd); +	} + +	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { +		struct sort_dimension *sd = &bstack_sort_dimensions[i]; + +		if (strncasecmp(tok, sd->name, strlen(tok))) +			continue; + +		return __sort_dimension__add_output(sd); +	} + +	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { +		struct sort_dimension *sd = &memory_sort_dimensions[i]; + +		if (strncasecmp(tok, sd->name, strlen(tok))) +			continue; + +		return __sort_dimension__add_output(sd); +	} + +	return -ESRCH; +} + +static void reset_dimensions(void) +{ +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) +		common_sort_dimensions[i].taken = 0; + +	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) +		hpp_sort_dimensions[i].taken = 0; + +	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) +		bstack_sort_dimensions[i].taken = 0; + +	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) +		memory_sort_dimensions[i].taken = 0; +} + +static int __setup_output_field(void) +{ +	char *tmp, *tok, *str; +	int ret = 0; + +	if (field_order == NULL) +		return 0; + +	reset_dimensions(); + +	str = strdup(field_order); +	if (str == NULL) { +		error("Not enough memory to setup output fields"); +		return -ENOMEM; +	} + +	for (tok = strtok_r(str, ", ", &tmp); +			tok; tok = strtok_r(NULL, ", ", &tmp)) { +		ret = output_field_add(tok); +		if (ret == -EINVAL) { +			error("Invalid --fields key: `%s'", tok); +			break; +		} else if (ret == -ESRCH) { +			error("Unknown --fields key: `%s'", tok); +			break; +		} +	} + +	free(str); +	return ret; +} + +int setup_sorting(void) +{ +	int err; + +	err = __setup_sorting(); +	if (err < 0) +		return err; + +	if (parent_pattern != default_parent_pattern) { +		err = sort_dimension__add("parent"); +		if (err < 0) +			return err; +	} + +	reset_dimensions(); + +	/* +	 * perf diff doesn't use default hpp output fields. +	 */ +	if (sort__mode != SORT_MODE__DIFF) +		perf_hpp__init(); + +	err = __setup_output_field(); +	if (err < 0) +		return err; + +	/* copy sort keys to output fields */ +	perf_hpp__setup_output_field(); +	/* and then copy output fields to sort keys */ +	perf_hpp__append_sort_keys(); + +	return 0; +} + +void reset_output_field(void) +{ +	sort__need_collapse = 0; +	sort__has_parent = 0; +	sort__has_sym = 0; +	sort__has_dso = 0; + +	field_order = NULL; +	sort_order = NULL; + +	reset_dimensions(); +	perf_hpp__reset_output_field();  } diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 4e80dbd271e..041f0c9cea2 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -20,12 +20,12 @@  #include "parse-options.h"  #include "parse-events.h" - +#include "hist.h"  #include "thread.h" -#include "sort.h"  extern regex_t parent_regex;  extern const char *sort_order; +extern const char *field_order;  extern const char default_parent_pattern[];  extern const char *parent_pattern;  extern const char default_sort_order[]; @@ -82,10 +82,14 @@ struct hist_entry {  		struct list_head head;  	} pairs;  	struct he_stat		stat; +	struct he_stat		*stat_acc;  	struct map_symbol	ms;  	struct thread		*thread; +	struct comm		*comm;  	u64			ip; +	u64			transaction;  	s32			cpu; +	u8			cpumode;  	struct hist_entry_diff	diff; @@ -128,10 +132,27 @@ static inline void hist_entry__add_pair(struct hist_entry *pair,  	list_add_tail(&pair->pairs.node, &he->pairs.head);  } +static inline float hist_entry__get_percent_limit(struct hist_entry *he) +{ +	u64 period = he->stat.period; +	u64 total_period = hists__total_period(he->hists); + +	if (unlikely(total_period == 0)) +		return 0; + +	if (symbol_conf.cumulate_callchain) +		period = he->stat_acc->period; + +	return period * 100.0 / total_period; +} + +  enum sort_mode {  	SORT_MODE__NORMAL,  	SORT_MODE__BRANCH,  	SORT_MODE__MEMORY, +	SORT_MODE__TOP, +	SORT_MODE__DIFF,  };  enum sort_type { @@ -145,6 +166,7 @@ enum sort_type {  	SORT_SRCLINE,  	SORT_LOCAL_WEIGHT,  	SORT_GLOBAL_WEIGHT, +	SORT_TRANSACTION,  	/* branch stack specific sort keys */  	__SORT_BRANCH_STACK, @@ -153,6 +175,8 @@ enum sort_type {  	SORT_SYM_FROM,  	SORT_SYM_TO,  	SORT_MISPREDICT, +	SORT_ABORT, +	SORT_IN_TX,  	/* memory mode specific sort keys */  	__SORT_MEMORY_MODE, @@ -162,6 +186,7 @@ enum sort_type {  	SORT_MEM_TLB,  	SORT_MEM_LVL,  	SORT_MEM_SNOOP, +	SORT_MEM_DCACHELINE,  };  /* @@ -175,18 +200,21 @@ struct sort_entry {  	int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);  	int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); -	int	(*se_snprintf)(struct hist_entry *self, char *bf, size_t size, +	int64_t	(*se_sort)(struct hist_entry *, struct hist_entry *); +	int	(*se_snprintf)(struct hist_entry *he, char *bf, size_t size,  			       unsigned int width);  	u8	se_width_idx; -	bool	elide;  };  extern struct sort_entry sort_thread;  extern struct list_head hist_entry__sort_list;  int setup_sorting(void); +int setup_output_field(void); +void reset_output_field(void);  extern int sort_dimension__add(const char *);  void sort__setup_elide(FILE *fp); +void perf_hpp__set_elide(int idx, bool elide);  int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c new file mode 100644 index 00000000000..f3e4bc5fe5d --- /dev/null +++ b/tools/perf/util/srcline.c @@ -0,0 +1,299 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <linux/kernel.h> + +#include "util/dso.h" +#include "util/util.h" +#include "util/debug.h" + +#ifdef HAVE_LIBBFD_SUPPORT + +/* + * Implement addr2line using libbfd. + */ +#define PACKAGE "perf" +#include <bfd.h> + +struct a2l_data { +	const char 	*input; +	unsigned long 	addr; + +	bool 		found; +	const char 	*filename; +	const char 	*funcname; +	unsigned 	line; + +	bfd 		*abfd; +	asymbol 	**syms; +}; + +static int bfd_error(const char *string) +{ +	const char *errmsg; + +	errmsg = bfd_errmsg(bfd_get_error()); +	fflush(stdout); + +	if (string) +		pr_debug("%s: %s\n", string, errmsg); +	else +		pr_debug("%s\n", errmsg); + +	return -1; +} + +static int slurp_symtab(bfd *abfd, struct a2l_data *a2l) +{ +	long storage; +	long symcount; +	asymbol **syms; +	bfd_boolean dynamic = FALSE; + +	if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) +		return bfd_error(bfd_get_filename(abfd)); + +	storage = bfd_get_symtab_upper_bound(abfd); +	if (storage == 0L) { +		storage = bfd_get_dynamic_symtab_upper_bound(abfd); +		dynamic = TRUE; +	} +	if (storage < 0L) +		return bfd_error(bfd_get_filename(abfd)); + +	syms = malloc(storage); +	if (dynamic) +		symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); +	else +		symcount = bfd_canonicalize_symtab(abfd, syms); + +	if (symcount < 0) { +		free(syms); +		return bfd_error(bfd_get_filename(abfd)); +	} + +	a2l->syms = syms; +	return 0; +} + +static void find_address_in_section(bfd *abfd, asection *section, void *data) +{ +	bfd_vma pc, vma; +	bfd_size_type size; +	struct a2l_data *a2l = data; + +	if (a2l->found) +		return; + +	if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) +		return; + +	pc = a2l->addr; +	vma = bfd_get_section_vma(abfd, section); +	size = bfd_get_section_size(section); + +	if (pc < vma || pc >= vma + size) +		return; + +	a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma, +					   &a2l->filename, &a2l->funcname, +					   &a2l->line); +} + +static struct a2l_data *addr2line_init(const char *path) +{ +	bfd *abfd; +	struct a2l_data *a2l = NULL; + +	abfd = bfd_openr(path, NULL); +	if (abfd == NULL) +		return NULL; + +	if (!bfd_check_format(abfd, bfd_object)) +		goto out; + +	a2l = zalloc(sizeof(*a2l)); +	if (a2l == NULL) +		goto out; + +	a2l->abfd = abfd; +	a2l->input = strdup(path); +	if (a2l->input == NULL) +		goto out; + +	if (slurp_symtab(abfd, a2l)) +		goto out; + +	return a2l; + +out: +	if (a2l) { +		zfree((char **)&a2l->input); +		free(a2l); +	} +	bfd_close(abfd); +	return NULL; +} + +static void addr2line_cleanup(struct a2l_data *a2l) +{ +	if (a2l->abfd) +		bfd_close(a2l->abfd); +	zfree((char **)&a2l->input); +	zfree(&a2l->syms); +	free(a2l); +} + +static int addr2line(const char *dso_name, unsigned long addr, +		     char **file, unsigned int *line, struct dso *dso) +{ +	int ret = 0; +	struct a2l_data *a2l = dso->a2l; + +	if (!a2l) { +		dso->a2l = addr2line_init(dso_name); +		a2l = dso->a2l; +	} + +	if (a2l == NULL) { +		pr_warning("addr2line_init failed for %s\n", dso_name); +		return 0; +	} + +	a2l->addr = addr; +	a2l->found = false; + +	bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); + +	if (a2l->found && a2l->filename) { +		*file = strdup(a2l->filename); +		*line = a2l->line; + +		if (*file) +			ret = 1; +	} + +	return ret; +} + +void dso__free_a2l(struct dso *dso) +{ +	struct a2l_data *a2l = dso->a2l; + +	if (!a2l) +		return; + +	addr2line_cleanup(a2l); + +	dso->a2l = NULL; +} + +#else /* HAVE_LIBBFD_SUPPORT */ + +static int addr2line(const char *dso_name, unsigned long addr, +		     char **file, unsigned int *line_nr, +		     struct dso *dso __maybe_unused) +{ +	FILE *fp; +	char cmd[PATH_MAX]; +	char *filename = NULL; +	size_t len; +	char *sep; +	int ret = 0; + +	scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, +		  dso_name, addr); + +	fp = popen(cmd, "r"); +	if (fp == NULL) { +		pr_warning("popen failed for %s\n", dso_name); +		return 0; +	} + +	if (getline(&filename, &len, fp) < 0 || !len) { +		pr_warning("addr2line has no output for %s\n", dso_name); +		goto out; +	} + +	sep = strchr(filename, '\n'); +	if (sep) +		*sep = '\0'; + +	if (!strcmp(filename, "??:0")) { +		pr_debug("no debugging info in %s\n", dso_name); +		free(filename); +		goto out; +	} + +	sep = strchr(filename, ':'); +	if (sep) { +		*sep++ = '\0'; +		*file = filename; +		*line_nr = strtoul(sep, NULL, 0); +		ret = 1; +	} +out: +	pclose(fp); +	return ret; +} + +void dso__free_a2l(struct dso *dso __maybe_unused) +{ +} + +#endif /* HAVE_LIBBFD_SUPPORT */ + +/* + * Number of addr2line failures (without success) before disabling it for that + * dso. + */ +#define A2L_FAIL_LIMIT 123 + +char *get_srcline(struct dso *dso, unsigned long addr) +{ +	char *file = NULL; +	unsigned line = 0; +	char *srcline; +	const char *dso_name; + +	if (!dso->has_srcline) +		return SRCLINE_UNKNOWN; + +	if (dso->symsrc_filename) +		dso_name = dso->symsrc_filename; +	else +		dso_name = dso->long_name; + +	if (dso_name[0] == '[') +		goto out; + +	if (!strncmp(dso_name, "/tmp/perf-", 10)) +		goto out; + +	if (!addr2line(dso_name, addr, &file, &line, dso)) +		goto out; + +	if (asprintf(&srcline, "%s:%u", file, line) < 0) { +		free(file); +		goto out; +	} + +	dso->a2l_fails = 0; + +	free(file); +	return srcline; + +out: +	if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) { +		dso->has_srcline = 0; +		dso__free_a2l(dso); +	} +	return SRCLINE_UNKNOWN; +} + +void free_srcline(char *srcline) +{ +	if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0) +		free(srcline); +} diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index ae8ccd7227c..5667fc3e39c 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -1,7 +1,7 @@  #ifndef __PERF_STATS_H  #define __PERF_STATS_H -#include "types.h" +#include <linux/types.h>  struct stats  { diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c index cfa906882e2..4abe23550c7 100644 --- a/tools/perf/util/strbuf.c +++ b/tools/perf/util/strbuf.c @@ -28,7 +28,7 @@ void strbuf_init(struct strbuf *sb, ssize_t hint)  void strbuf_release(struct strbuf *sb)  {  	if (sb->alloc) { -		free(sb->buf); +		zfree(&sb->buf);  		strbuf_init(sb, 0);  	}  } diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index 834c8ebfe38..79a757a2a15 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c @@ -10,22 +10,22 @@ static const char *OP_not	= "!";	/* Logical NOT */  #define is_operator(c)	((c) == '|' || (c) == '&' || (c) == '!')  #define is_separator(c)	(is_operator(c) || (c) == '(' || (c) == ')') -static void strfilter_node__delete(struct strfilter_node *self) +static void strfilter_node__delete(struct strfilter_node *node)  { -	if (self) { -		if (self->p && !is_operator(*self->p)) -			free((char *)self->p); -		strfilter_node__delete(self->l); -		strfilter_node__delete(self->r); -		free(self); +	if (node) { +		if (node->p && !is_operator(*node->p)) +			zfree((char **)&node->p); +		strfilter_node__delete(node->l); +		strfilter_node__delete(node->r); +		free(node);  	}  } -void strfilter__delete(struct strfilter *self) +void strfilter__delete(struct strfilter *filter)  { -	if (self) { -		strfilter_node__delete(self->root); -		free(self); +	if (filter) { +		strfilter_node__delete(filter->root); +		free(filter);  	}  } @@ -62,15 +62,15 @@ static struct strfilter_node *strfilter_node__alloc(const char *op,  						    struct strfilter_node *l,  						    struct strfilter_node *r)  { -	struct strfilter_node *ret = zalloc(sizeof(struct strfilter_node)); +	struct strfilter_node *node = zalloc(sizeof(*node)); -	if (ret) { -		ret->p = op; -		ret->l = l; -		ret->r = r; +	if (node) { +		node->p = op; +		node->l = l; +		node->r = r;  	} -	return ret; +	return node;  }  static struct strfilter_node *strfilter_node__new(const char *s, @@ -154,46 +154,46 @@ error:   */  struct strfilter *strfilter__new(const char *rules, const char **err)  { -	struct strfilter *ret = zalloc(sizeof(struct strfilter)); +	struct strfilter *filter = zalloc(sizeof(*filter));  	const char *ep = NULL; -	if (ret) -		ret->root = strfilter_node__new(rules, &ep); +	if (filter) +		filter->root = strfilter_node__new(rules, &ep); -	if (!ret || !ret->root || *ep != '\0') { +	if (!filter || !filter->root || *ep != '\0') {  		if (err)  			*err = ep; -		strfilter__delete(ret); -		ret = NULL; +		strfilter__delete(filter); +		filter = NULL;  	} -	return ret; +	return filter;  } -static bool strfilter_node__compare(struct strfilter_node *self, +static bool strfilter_node__compare(struct strfilter_node *node,  				    const char *str)  { -	if (!self || !self->p) +	if (!node || !node->p)  		return false; -	switch (*self->p) { +	switch (*node->p) {  	case '|':	/* OR */ -		return strfilter_node__compare(self->l, str) || -			strfilter_node__compare(self->r, str); +		return strfilter_node__compare(node->l, str) || +			strfilter_node__compare(node->r, str);  	case '&':	/* AND */ -		return strfilter_node__compare(self->l, str) && -			strfilter_node__compare(self->r, str); +		return strfilter_node__compare(node->l, str) && +			strfilter_node__compare(node->r, str);  	case '!':	/* NOT */ -		return !strfilter_node__compare(self->r, str); +		return !strfilter_node__compare(node->r, str);  	default: -		return strglobmatch(str, self->p); +		return strglobmatch(str, node->p);  	}  }  /* Return true if STR matches the filter rules */ -bool strfilter__compare(struct strfilter *self, const char *str) +bool strfilter__compare(struct strfilter *filter, const char *str)  { -	if (!self) +	if (!filter)  		return false; -	return strfilter_node__compare(self->root, str); +	return strfilter_node__compare(filter->root, str);  } diff --git a/tools/perf/util/strfilter.h b/tools/perf/util/strfilter.h index 00f58a7506d..fe611f3c9e3 100644 --- a/tools/perf/util/strfilter.h +++ b/tools/perf/util/strfilter.h @@ -30,19 +30,19 @@ struct strfilter *strfilter__new(const char *rules, const char **err);  /**   * strfilter__compare - compare given string and a string filter - * @self: String filter + * @filter: String filter   * @str: target string   * - * Compare @str and @self. Return true if the str match the rule + * Compare @str and @filter. Return true if the str match the rule   */ -bool strfilter__compare(struct strfilter *self, const char *str); +bool strfilter__compare(struct strfilter *filter, const char *str);  /**   * strfilter__delete - delete a string filter - * @self: String filter to delete + * @filter: String filter to delete   * - * Delete @self. + * Delete @filter.   */ -void strfilter__delete(struct strfilter *self); +void strfilter__delete(struct strfilter *filter);  #endif diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index f0b0c008c50..2553e5b55b8 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -128,7 +128,7 @@ void argv_free(char **argv)  {  	char **p;  	for (p = argv; *p; p++) -		free(*p); +		zfree(p);  	free(argv);  } diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index eabdce0a2da..71f9d102b96 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c @@ -5,6 +5,7 @@   */  #include "strlist.h" +#include "util.h"  #include <errno.h>  #include <stdio.h>  #include <stdlib.h> @@ -38,7 +39,7 @@ out_delete:  static void str_node__delete(struct str_node *snode, bool dupstr)  {  	if (dupstr) -		free((void *)snode->s); +		zfree((char **)&snode->s);  	free(snode);  } diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index 96c866045d6..6a0a13d07a2 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -17,8 +17,12 @@  #include <stdlib.h>  #include <unistd.h>  #include <string.h> +#include <linux/bitmap.h> +#include "perf.h"  #include "svghelper.h" +#include "util.h" +#include "cpumap.h"  static u64 first_time, last_time;  static u64 turbo_frequency, max_freq; @@ -28,6 +32,8 @@ static u64 turbo_frequency, max_freq;  #define SLOT_HEIGHT 25.0  int svg_page_width = 1000; +u64 svg_highlight; +const char *svg_highlight_name;  #define MIN_TEXT_SIZE 0.01 @@ -39,9 +45,14 @@ static double cpu2slot(int cpu)  	return 2 * cpu + 1;  } +static int *topology_map; +  static double cpu2y(int cpu)  { -	return cpu2slot(cpu) * SLOT_MULT; +	if (topology_map) +		return cpu2slot(topology_map[cpu]) * SLOT_MULT; +	else +		return cpu2slot(cpu) * SLOT_MULT;  }  static double time2pixels(u64 __time) @@ -95,6 +106,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end)  	total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT;  	fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n"); +	fprintf(svgfile, "<!DOCTYPE svg SYSTEM \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");  	fprintf(svgfile, "<svg width=\"%i\" height=\"%" PRIu64 "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", svg_page_width, total_height);  	fprintf(svgfile, "<defs>\n  <style type=\"text/css\">\n    <![CDATA[\n"); @@ -103,6 +115,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end)  	fprintf(svgfile, "      rect.process  { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:1;   stroke:rgb(  0,  0,  0); } \n");  	fprintf(svgfile, "      rect.process2 { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n");  	fprintf(svgfile, "      rect.sample   { fill:rgb(  0,  0,255); fill-opacity:0.8; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n"); +	fprintf(svgfile, "      rect.sample_hi{ fill:rgb(255,128,  0); fill-opacity:0.8; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n");  	fprintf(svgfile, "      rect.blocked  { fill:rgb(255,  0,  0); fill-opacity:0.5; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n");  	fprintf(svgfile, "      rect.waiting  { fill:rgb(224,214,  0); fill-opacity:0.8; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n");  	fprintf(svgfile, "      rect.WAITING  { fill:rgb(255,214, 48); fill-opacity:0.6; stroke-width:0;   stroke:rgb(  0,  0,  0); } \n"); @@ -128,14 +141,42 @@ void svg_box(int Yslot, u64 start, u64 end, const char *type)  		time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type);  } -void svg_sample(int Yslot, int cpu, u64 start, u64 end) +static char *time_to_string(u64 duration); +void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) +{ +	if (!svgfile) +		return; + +	fprintf(svgfile, "<g>\n"); +	fprintf(svgfile, "<title>#%d blocked %s</title>\n", cpu, +		time_to_string(end - start)); +	if (backtrace) +		fprintf(svgfile, "<desc>Blocked on:\n%s</desc>\n", backtrace); +	svg_box(Yslot, start, end, "blocked"); +	fprintf(svgfile, "</g>\n"); +} + +void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)  {  	double text_size; +	const char *type; +  	if (!svgfile)  		return; -	fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"sample\"/>\n", -		time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT); +	if (svg_highlight && end - start > svg_highlight) +		type = "sample_hi"; +	else +		type = "sample"; +	fprintf(svgfile, "<g>\n"); + +	fprintf(svgfile, "<title>#%d running %s</title>\n", +		cpu, time_to_string(end - start)); +	if (backtrace) +		fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); +	fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"%s\"/>\n", +		time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, +		type);  	text_size = (time2pixels(end)-time2pixels(start));  	if (cpu > 9) @@ -148,6 +189,7 @@ void svg_sample(int Yslot, int cpu, u64 start, u64 end)  		fprintf(svgfile, "<text x=\"%1.8f\" y=\"%1.8f\" font-size=\"%1.8fpt\">%i</text>\n",  			time2pixels(start), Yslot *  SLOT_MULT + SLOT_HEIGHT - 1, text_size,  cpu + 1); +	fprintf(svgfile, "</g>\n");  }  static char *time_to_string(u64 duration) @@ -168,7 +210,7 @@ static char *time_to_string(u64 duration)  	return text;  } -void svg_waiting(int Yslot, u64 start, u64 end) +void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)  {  	char *text;  	const char *style; @@ -192,6 +234,9 @@ void svg_waiting(int Yslot, u64 start, u64 end)  	font_size = round_text_size(font_size);  	fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT); +	fprintf(svgfile, "<title>#%d waiting %s</title>\n", cpu, time_to_string(end - start)); +	if (backtrace) +		fprintf(svgfile, "<desc>Waiting on:\n%s</desc>\n", backtrace);  	fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",  		time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style);  	if (font_size > MIN_TEXT_SIZE) @@ -242,28 +287,42 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq)  	max_freq = __max_freq;  	turbo_frequency = __turbo_freq; +	fprintf(svgfile, "<g>\n"); +  	fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"cpu\"/>\n",  		time2pixels(first_time),  		time2pixels(last_time)-time2pixels(first_time),  		cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); -	sprintf(cpu_string, "CPU %i", (int)cpu+1); +	sprintf(cpu_string, "CPU %i", (int)cpu);  	fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n",  		10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string);  	fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\" font-size=\"1.25pt\">%s</text>\n",  		10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model()); + +	fprintf(svgfile, "</g>\n");  } -void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name) +void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace)  {  	double width; +	const char *type;  	if (!svgfile)  		return; +	if (svg_highlight && end - start >= svg_highlight) +		type = "sample_hi"; +	else if (svg_highlight_name && strstr(name, svg_highlight_name)) +		type = "sample_hi"; +	else +		type = "sample";  	fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), cpu2y(cpu)); +	fprintf(svgfile, "<title>%d %s running %s</title>\n", pid, name, time_to_string(end - start)); +	if (backtrace) +		fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace);  	fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",  		time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type);  	width = time2pixels(end)-time2pixels(start); @@ -288,6 +347,8 @@ void svg_cstate(int cpu, u64 start, u64 end, int type)  		return; +	fprintf(svgfile, "<g>\n"); +  	if (type > 6)  		type = 6;  	sprintf(style, "c%i", type); @@ -306,6 +367,8 @@ void svg_cstate(int cpu, u64 start, u64 end, int type)  	if (width > MIN_TEXT_SIZE)  		fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"%3.8fpt\">C%i</text>\n",  			time2pixels(start), cpu2y(cpu)+width, width, type); + +	fprintf(svgfile, "</g>\n");  }  static char *HzToHuman(unsigned long hz) @@ -339,6 +402,8 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq)  	if (!svgfile)  		return; +	fprintf(svgfile, "<g>\n"); +  	if (max_freq)  		height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT);  	height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height; @@ -347,10 +412,11 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq)  	fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"0.25pt\">%s</text>\n",  		time2pixels(start), height+0.9, HzToHuman(freq)); +	fprintf(svgfile, "</g>\n");  } -void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2) +void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace)  {  	double height; @@ -358,6 +424,15 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc  		return; +	fprintf(svgfile, "<g>\n"); + +	fprintf(svgfile, "<title>%s wakes up %s</title>\n", +		desc1 ? desc1 : "?", +		desc2 ? desc2 : "?"); + +	if (backtrace) +		fprintf(svgfile, "<desc>%s</desc>\n", backtrace); +  	if (row1 < row2) {  		if (row1) {  			fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", @@ -395,9 +470,11 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc  	if (row1)  		fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(32,255,32)\"/>\n",  			time2pixels(start), height); + +	fprintf(svgfile, "</g>\n");  } -void svg_wakeline(u64 start, int row1, int row2) +void svg_wakeline(u64 start, int row1, int row2, const char *backtrace)  {  	double height; @@ -405,6 +482,11 @@ void svg_wakeline(u64 start, int row1, int row2)  		return; +	fprintf(svgfile, "<g>\n"); + +	if (backtrace) +		fprintf(svgfile, "<desc>%s</desc>\n", backtrace); +  	if (row1 < row2)  		fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",  			time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT,  time2pixels(start), row2 * SLOT_MULT); @@ -417,17 +499,28 @@ void svg_wakeline(u64 start, int row1, int row2)  		height += SLOT_HEIGHT;  	fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(32,255,32)\"/>\n",  			time2pixels(start), height); + +	fprintf(svgfile, "</g>\n");  } -void svg_interrupt(u64 start, int row) +void svg_interrupt(u64 start, int row, const char *backtrace)  {  	if (!svgfile)  		return; +	fprintf(svgfile, "<g>\n"); + +	fprintf(svgfile, "<title>Wakeup from interrupt</title>\n"); + +	if (backtrace) +		fprintf(svgfile, "<desc>%s</desc>\n", backtrace); +  	fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(255,128,128)\"/>\n",  			time2pixels(start), row * SLOT_MULT);  	fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(255,128,128)\"/>\n",  			time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT); + +	fprintf(svgfile, "</g>\n");  }  void svg_text(int Yslot, u64 start, const char *text) @@ -455,6 +548,7 @@ void svg_legenda(void)  	if (!svgfile)  		return; +	fprintf(svgfile, "<g>\n");  	svg_legenda_box(0,	"Running", "sample");  	svg_legenda_box(100,	"Idle","c1");  	svg_legenda_box(200,	"Deeper Idle", "c3"); @@ -462,6 +556,7 @@ void svg_legenda(void)  	svg_legenda_box(550,	"Sleeping", "process2");  	svg_legenda_box(650,	"Waiting for cpu", "waiting");  	svg_legenda_box(800,	"Blocked on IO", "blocked"); +	fprintf(svgfile, "</g>\n");  }  void svg_time_grid(void) @@ -499,3 +594,123 @@ void svg_close(void)  		svgfile = NULL;  	}  } + +#define cpumask_bits(maskp) ((maskp)->bits) +typedef struct { DECLARE_BITMAP(bits, MAX_NR_CPUS); } cpumask_t; + +struct topology { +	cpumask_t *sib_core; +	int sib_core_nr; +	cpumask_t *sib_thr; +	int sib_thr_nr; +}; + +static void scan_thread_topology(int *map, struct topology *t, int cpu, int *pos) +{ +	int i; +	int thr; + +	for (i = 0; i < t->sib_thr_nr; i++) { +		if (!test_bit(cpu, cpumask_bits(&t->sib_thr[i]))) +			continue; + +		for_each_set_bit(thr, +				 cpumask_bits(&t->sib_thr[i]), +				 MAX_NR_CPUS) +			if (map[thr] == -1) +				map[thr] = (*pos)++; +	} +} + +static void scan_core_topology(int *map, struct topology *t) +{ +	int pos = 0; +	int i; +	int cpu; + +	for (i = 0; i < t->sib_core_nr; i++) +		for_each_set_bit(cpu, +				 cpumask_bits(&t->sib_core[i]), +				 MAX_NR_CPUS) +			scan_thread_topology(map, t, cpu, &pos); +} + +static int str_to_bitmap(char *s, cpumask_t *b) +{ +	int i; +	int ret = 0; +	struct cpu_map *m; +	int c; + +	m = cpu_map__new(s); +	if (!m) +		return -1; + +	for (i = 0; i < m->nr; i++) { +		c = m->map[i]; +		if (c >= MAX_NR_CPUS) { +			ret = -1; +			break; +		} + +		set_bit(c, cpumask_bits(b)); +	} + +	cpu_map__delete(m); + +	return ret; +} + +int svg_build_topology_map(char *sib_core, int sib_core_nr, +			   char *sib_thr, int sib_thr_nr) +{ +	int i; +	struct topology t; + +	t.sib_core_nr = sib_core_nr; +	t.sib_thr_nr = sib_thr_nr; +	t.sib_core = calloc(sib_core_nr, sizeof(cpumask_t)); +	t.sib_thr = calloc(sib_thr_nr, sizeof(cpumask_t)); + +	if (!t.sib_core || !t.sib_thr) { +		fprintf(stderr, "topology: no memory\n"); +		goto exit; +	} + +	for (i = 0; i < sib_core_nr; i++) { +		if (str_to_bitmap(sib_core, &t.sib_core[i])) { +			fprintf(stderr, "topology: can't parse siblings map\n"); +			goto exit; +		} + +		sib_core += strlen(sib_core) + 1; +	} + +	for (i = 0; i < sib_thr_nr; i++) { +		if (str_to_bitmap(sib_thr, &t.sib_thr[i])) { +			fprintf(stderr, "topology: can't parse siblings map\n"); +			goto exit; +		} + +		sib_thr += strlen(sib_thr) + 1; +	} + +	topology_map = malloc(sizeof(int) * MAX_NR_CPUS); +	if (!topology_map) { +		fprintf(stderr, "topology: no memory\n"); +		goto exit; +	} + +	for (i = 0; i < MAX_NR_CPUS; i++) +		topology_map[i] = -1; + +	scan_core_topology(topology_map, &t); + +	return 0; + +exit: +	zfree(&t.sib_core); +	zfree(&t.sib_thr); + +	return -1; +} diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index e0781989cc3..e3aff5332e3 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h @@ -1,28 +1,33 @@  #ifndef __PERF_SVGHELPER_H  #define __PERF_SVGHELPER_H -#include "types.h" +#include <linux/types.h>  extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);  extern void svg_box(int Yslot, u64 start, u64 end, const char *type); -extern void svg_sample(int Yslot, int cpu, u64 start, u64 end); -extern void svg_waiting(int Yslot, u64 start, u64 end); +extern void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); +extern void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); +extern void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace);  extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency); -extern void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name); +extern void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace);  extern void svg_cstate(int cpu, u64 start, u64 end, int type);  extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq);  extern void svg_time_grid(void);  extern void svg_legenda(void); -extern void svg_wakeline(u64 start, int row1, int row2); -extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2); -extern void svg_interrupt(u64 start, int row); +extern void svg_wakeline(u64 start, int row1, int row2, const char *backtrace); +extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace); +extern void svg_interrupt(u64 start, int row, const char *backtrace);  extern void svg_text(int Yslot, u64 start, const char *text);  extern void svg_close(void); +extern int svg_build_topology_map(char *sib_core, int sib_core_nr, +				  char *sib_thr, int sib_thr_nr);  extern int svg_page_width; +extern u64 svg_highlight; +extern const char *svg_highlight_name;  #endif /* __PERF_SVGHELPER_H */ diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index a9c829be521..6864661a79d 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -6,9 +6,11 @@  #include <inttypes.h>  #include "symbol.h" +#include "vdso.h" +#include <symbol/kallsyms.h>  #include "debug.h" -#ifndef HAVE_ELF_GETPHDRNUM +#ifndef HAVE_ELF_GETPHDRNUM_SUPPORT  static int elf_getphdrnum(Elf *elf, size_t *dst)  {  	GElf_Ehdr gehdr; @@ -135,9 +137,8 @@ static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)  	return -1;  } -static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, -				    GElf_Shdr *shp, const char *name, -				    size_t *idx) +Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, +			     GElf_Shdr *shp, const char *name, size_t *idx)  {  	Elf_Scn *sec = NULL;  	size_t cnt = 1; @@ -151,15 +152,15 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,  		gelf_getshdr(sec, shp);  		str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); -		if (!strcmp(name, str)) { +		if (str && !strcmp(name, str)) {  			if (idx)  				*idx = cnt; -			break; +			return sec;  		}  		++cnt;  	} -	return sec; +	return NULL;  }  #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ @@ -487,27 +488,29 @@ int filename__read_debuglink(const char *filename, char *debuglink,  	ek = elf_kind(elf);  	if (ek != ELF_K_ELF) -		goto out_close; +		goto out_elf_end;  	if (gelf_getehdr(elf, &ehdr) == NULL) {  		pr_err("%s: cannot get elf header.\n", __func__); -		goto out_close; +		goto out_elf_end;  	}  	sec = elf_section_by_name(elf, &ehdr, &shdr,  				  ".gnu_debuglink", NULL);  	if (sec == NULL) -		goto out_close; +		goto out_elf_end;  	data = elf_getdata(sec, NULL);  	if (data == NULL) -		goto out_close; +		goto out_elf_end;  	/* the start of this section is a zero-terminated string */  	strncpy(debuglink, data->d_buf, size); -	elf_end(elf); +	err = 0; +out_elf_end: +	elf_end(elf);  out_close:  	close(fd);  out: @@ -553,7 +556,7 @@ bool symsrc__has_symtab(struct symsrc *ss)  void symsrc__destroy(struct symsrc *ss)  { -	free(ss->name); +	zfree(&ss->name);  	elf_end(ss->elf);  	close(ss->fd);  } @@ -616,6 +619,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,  		GElf_Shdr shdr;  		ss->adjust_symbols = (ehdr.e_type == ET_EXEC ||  				ehdr.e_type == ET_REL || +				is_vdso_map(dso->short_name) ||  				elf_section_by_name(elf, &ehdr, &shdr,  						     ".gnu.prelink_undo",  						     NULL) != NULL); @@ -751,6 +755,8 @@ int dso__load_sym(struct dso *dso, struct map *map,  			if (strcmp(elf_name, kmap->ref_reloc_sym->name))  				continue;  			kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; +			map->reloc = kmap->ref_reloc_sym->addr - +				     kmap->ref_reloc_sym->unrelocated_addr;  			break;  		}  	} @@ -922,6 +928,7 @@ int dso__load_sym(struct dso *dso, struct map *map,  				  (u64)shdr.sh_offset);  			sym.st_value -= shdr.sh_addr - shdr.sh_offset;  		} +new_symbol:  		/*  		 * We need to figure out if the object was created from C++ sources  		 * DWARF DW_compile_unit has this, but we don't always have access @@ -933,7 +940,6 @@ int dso__load_sym(struct dso *dso, struct map *map,  			if (demangled != NULL)  				elf_name = demangled;  		} -new_symbol:  		f = symbol__new(sym.st_value, sym.st_size,  				GELF_ST_BIND(sym.st_info), elf_name);  		free(demangled); @@ -1018,6 +1024,601 @@ int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,  	return err;  } +static int copy_bytes(int from, off_t from_offs, int to, off_t to_offs, u64 len) +{ +	ssize_t r; +	size_t n; +	int err = -1; +	char *buf = malloc(page_size); + +	if (buf == NULL) +		return -1; + +	if (lseek(to, to_offs, SEEK_SET) != to_offs) +		goto out; + +	if (lseek(from, from_offs, SEEK_SET) != from_offs) +		goto out; + +	while (len) { +		n = page_size; +		if (len < n) +			n = len; +		/* Use read because mmap won't work on proc files */ +		r = read(from, buf, n); +		if (r < 0) +			goto out; +		if (!r) +			break; +		n = r; +		r = write(to, buf, n); +		if (r < 0) +			goto out; +		if ((size_t)r != n) +			goto out; +		len -= n; +	} + +	err = 0; +out: +	free(buf); +	return err; +} + +struct kcore { +	int fd; +	int elfclass; +	Elf *elf; +	GElf_Ehdr ehdr; +}; + +static int kcore__open(struct kcore *kcore, const char *filename) +{ +	GElf_Ehdr *ehdr; + +	kcore->fd = open(filename, O_RDONLY); +	if (kcore->fd == -1) +		return -1; + +	kcore->elf = elf_begin(kcore->fd, ELF_C_READ, NULL); +	if (!kcore->elf) +		goto out_close; + +	kcore->elfclass = gelf_getclass(kcore->elf); +	if (kcore->elfclass == ELFCLASSNONE) +		goto out_end; + +	ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); +	if (!ehdr) +		goto out_end; + +	return 0; + +out_end: +	elf_end(kcore->elf); +out_close: +	close(kcore->fd); +	return -1; +} + +static int kcore__init(struct kcore *kcore, char *filename, int elfclass, +		       bool temp) +{ +	GElf_Ehdr *ehdr; + +	kcore->elfclass = elfclass; + +	if (temp) +		kcore->fd = mkstemp(filename); +	else +		kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400); +	if (kcore->fd == -1) +		return -1; + +	kcore->elf = elf_begin(kcore->fd, ELF_C_WRITE, NULL); +	if (!kcore->elf) +		goto out_close; + +	if (!gelf_newehdr(kcore->elf, elfclass)) +		goto out_end; + +	ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); +	if (!ehdr) +		goto out_end; + +	return 0; + +out_end: +	elf_end(kcore->elf); +out_close: +	close(kcore->fd); +	unlink(filename); +	return -1; +} + +static void kcore__close(struct kcore *kcore) +{ +	elf_end(kcore->elf); +	close(kcore->fd); +} + +static int kcore__copy_hdr(struct kcore *from, struct kcore *to, size_t count) +{ +	GElf_Ehdr *ehdr = &to->ehdr; +	GElf_Ehdr *kehdr = &from->ehdr; + +	memcpy(ehdr->e_ident, kehdr->e_ident, EI_NIDENT); +	ehdr->e_type      = kehdr->e_type; +	ehdr->e_machine   = kehdr->e_machine; +	ehdr->e_version   = kehdr->e_version; +	ehdr->e_entry     = 0; +	ehdr->e_shoff     = 0; +	ehdr->e_flags     = kehdr->e_flags; +	ehdr->e_phnum     = count; +	ehdr->e_shentsize = 0; +	ehdr->e_shnum     = 0; +	ehdr->e_shstrndx  = 0; + +	if (from->elfclass == ELFCLASS32) { +		ehdr->e_phoff     = sizeof(Elf32_Ehdr); +		ehdr->e_ehsize    = sizeof(Elf32_Ehdr); +		ehdr->e_phentsize = sizeof(Elf32_Phdr); +	} else { +		ehdr->e_phoff     = sizeof(Elf64_Ehdr); +		ehdr->e_ehsize    = sizeof(Elf64_Ehdr); +		ehdr->e_phentsize = sizeof(Elf64_Phdr); +	} + +	if (!gelf_update_ehdr(to->elf, ehdr)) +		return -1; + +	if (!gelf_newphdr(to->elf, count)) +		return -1; + +	return 0; +} + +static int kcore__add_phdr(struct kcore *kcore, int idx, off_t offset, +			   u64 addr, u64 len) +{ +	GElf_Phdr gphdr; +	GElf_Phdr *phdr; + +	phdr = gelf_getphdr(kcore->elf, idx, &gphdr); +	if (!phdr) +		return -1; + +	phdr->p_type	= PT_LOAD; +	phdr->p_flags	= PF_R | PF_W | PF_X; +	phdr->p_offset	= offset; +	phdr->p_vaddr	= addr; +	phdr->p_paddr	= 0; +	phdr->p_filesz	= len; +	phdr->p_memsz	= len; +	phdr->p_align	= page_size; + +	if (!gelf_update_phdr(kcore->elf, idx, phdr)) +		return -1; + +	return 0; +} + +static off_t kcore__write(struct kcore *kcore) +{ +	return elf_update(kcore->elf, ELF_C_WRITE); +} + +struct phdr_data { +	off_t offset; +	u64 addr; +	u64 len; +}; + +struct kcore_copy_info { +	u64 stext; +	u64 etext; +	u64 first_symbol; +	u64 last_symbol; +	u64 first_module; +	u64 last_module_symbol; +	struct phdr_data kernel_map; +	struct phdr_data modules_map; +}; + +static int kcore_copy__process_kallsyms(void *arg, const char *name, char type, +					u64 start) +{ +	struct kcore_copy_info *kci = arg; + +	if (!symbol_type__is_a(type, MAP__FUNCTION)) +		return 0; + +	if (strchr(name, '[')) { +		if (start > kci->last_module_symbol) +			kci->last_module_symbol = start; +		return 0; +	} + +	if (!kci->first_symbol || start < kci->first_symbol) +		kci->first_symbol = start; + +	if (!kci->last_symbol || start > kci->last_symbol) +		kci->last_symbol = start; + +	if (!strcmp(name, "_stext")) { +		kci->stext = start; +		return 0; +	} + +	if (!strcmp(name, "_etext")) { +		kci->etext = start; +		return 0; +	} + +	return 0; +} + +static int kcore_copy__parse_kallsyms(struct kcore_copy_info *kci, +				      const char *dir) +{ +	char kallsyms_filename[PATH_MAX]; + +	scnprintf(kallsyms_filename, PATH_MAX, "%s/kallsyms", dir); + +	if (symbol__restricted_filename(kallsyms_filename, "/proc/kallsyms")) +		return -1; + +	if (kallsyms__parse(kallsyms_filename, kci, +			    kcore_copy__process_kallsyms) < 0) +		return -1; + +	return 0; +} + +static int kcore_copy__process_modules(void *arg, +				       const char *name __maybe_unused, +				       u64 start) +{ +	struct kcore_copy_info *kci = arg; + +	if (!kci->first_module || start < kci->first_module) +		kci->first_module = start; + +	return 0; +} + +static int kcore_copy__parse_modules(struct kcore_copy_info *kci, +				     const char *dir) +{ +	char modules_filename[PATH_MAX]; + +	scnprintf(modules_filename, PATH_MAX, "%s/modules", dir); + +	if (symbol__restricted_filename(modules_filename, "/proc/modules")) +		return -1; + +	if (modules__parse(modules_filename, kci, +			   kcore_copy__process_modules) < 0) +		return -1; + +	return 0; +} + +static void kcore_copy__map(struct phdr_data *p, u64 start, u64 end, u64 pgoff, +			    u64 s, u64 e) +{ +	if (p->addr || s < start || s >= end) +		return; + +	p->addr = s; +	p->offset = (s - start) + pgoff; +	p->len = e < end ? e - s : end - s; +} + +static int kcore_copy__read_map(u64 start, u64 len, u64 pgoff, void *data) +{ +	struct kcore_copy_info *kci = data; +	u64 end = start + len; + +	kcore_copy__map(&kci->kernel_map, start, end, pgoff, kci->stext, +			kci->etext); + +	kcore_copy__map(&kci->modules_map, start, end, pgoff, kci->first_module, +			kci->last_module_symbol); + +	return 0; +} + +static int kcore_copy__read_maps(struct kcore_copy_info *kci, Elf *elf) +{ +	if (elf_read_maps(elf, true, kcore_copy__read_map, kci) < 0) +		return -1; + +	return 0; +} + +static int kcore_copy__calc_maps(struct kcore_copy_info *kci, const char *dir, +				 Elf *elf) +{ +	if (kcore_copy__parse_kallsyms(kci, dir)) +		return -1; + +	if (kcore_copy__parse_modules(kci, dir)) +		return -1; + +	if (kci->stext) +		kci->stext = round_down(kci->stext, page_size); +	else +		kci->stext = round_down(kci->first_symbol, page_size); + +	if (kci->etext) { +		kci->etext = round_up(kci->etext, page_size); +	} else if (kci->last_symbol) { +		kci->etext = round_up(kci->last_symbol, page_size); +		kci->etext += page_size; +	} + +	kci->first_module = round_down(kci->first_module, page_size); + +	if (kci->last_module_symbol) { +		kci->last_module_symbol = round_up(kci->last_module_symbol, +						   page_size); +		kci->last_module_symbol += page_size; +	} + +	if (!kci->stext || !kci->etext) +		return -1; + +	if (kci->first_module && !kci->last_module_symbol) +		return -1; + +	return kcore_copy__read_maps(kci, elf); +} + +static int kcore_copy__copy_file(const char *from_dir, const char *to_dir, +				 const char *name) +{ +	char from_filename[PATH_MAX]; +	char to_filename[PATH_MAX]; + +	scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name); +	scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name); + +	return copyfile_mode(from_filename, to_filename, 0400); +} + +static int kcore_copy__unlink(const char *dir, const char *name) +{ +	char filename[PATH_MAX]; + +	scnprintf(filename, PATH_MAX, "%s/%s", dir, name); + +	return unlink(filename); +} + +static int kcore_copy__compare_fds(int from, int to) +{ +	char *buf_from; +	char *buf_to; +	ssize_t ret; +	size_t len; +	int err = -1; + +	buf_from = malloc(page_size); +	buf_to = malloc(page_size); +	if (!buf_from || !buf_to) +		goto out; + +	while (1) { +		/* Use read because mmap won't work on proc files */ +		ret = read(from, buf_from, page_size); +		if (ret < 0) +			goto out; + +		if (!ret) +			break; + +		len = ret; + +		if (readn(to, buf_to, len) != (int)len) +			goto out; + +		if (memcmp(buf_from, buf_to, len)) +			goto out; +	} + +	err = 0; +out: +	free(buf_to); +	free(buf_from); +	return err; +} + +static int kcore_copy__compare_files(const char *from_filename, +				     const char *to_filename) +{ +	int from, to, err = -1; + +	from = open(from_filename, O_RDONLY); +	if (from < 0) +		return -1; + +	to = open(to_filename, O_RDONLY); +	if (to < 0) +		goto out_close_from; + +	err = kcore_copy__compare_fds(from, to); + +	close(to); +out_close_from: +	close(from); +	return err; +} + +static int kcore_copy__compare_file(const char *from_dir, const char *to_dir, +				    const char *name) +{ +	char from_filename[PATH_MAX]; +	char to_filename[PATH_MAX]; + +	scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name); +	scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name); + +	return kcore_copy__compare_files(from_filename, to_filename); +} + +/** + * kcore_copy - copy kallsyms, modules and kcore from one directory to another. + * @from_dir: from directory + * @to_dir: to directory + * + * This function copies kallsyms, modules and kcore files from one directory to + * another.  kallsyms and modules are copied entirely.  Only code segments are + * copied from kcore.  It is assumed that two segments suffice: one for the + * kernel proper and one for all the modules.  The code segments are determined + * from kallsyms and modules files.  The kernel map starts at _stext or the + * lowest function symbol, and ends at _etext or the highest function symbol. + * The module map starts at the lowest module address and ends at the highest + * module symbol.  Start addresses are rounded down to the nearest page.  End + * addresses are rounded up to the nearest page.  An extra page is added to the + * highest kernel symbol and highest module symbol to, hopefully, encompass that + * symbol too.  Because it contains only code sections, the resulting kcore is + * unusual.  One significant peculiarity is that the mapping (start -> pgoff) + * is not the same for the kernel map and the modules map.  That happens because + * the data is copied adjacently whereas the original kcore has gaps.  Finally, + * kallsyms and modules files are compared with their copies to check that + * modules have not been loaded or unloaded while the copies were taking place. + * + * Return: %0 on success, %-1 on failure. + */ +int kcore_copy(const char *from_dir, const char *to_dir) +{ +	struct kcore kcore; +	struct kcore extract; +	size_t count = 2; +	int idx = 0, err = -1; +	off_t offset = page_size, sz, modules_offset = 0; +	struct kcore_copy_info kci = { .stext = 0, }; +	char kcore_filename[PATH_MAX]; +	char extract_filename[PATH_MAX]; + +	if (kcore_copy__copy_file(from_dir, to_dir, "kallsyms")) +		return -1; + +	if (kcore_copy__copy_file(from_dir, to_dir, "modules")) +		goto out_unlink_kallsyms; + +	scnprintf(kcore_filename, PATH_MAX, "%s/kcore", from_dir); +	scnprintf(extract_filename, PATH_MAX, "%s/kcore", to_dir); + +	if (kcore__open(&kcore, kcore_filename)) +		goto out_unlink_modules; + +	if (kcore_copy__calc_maps(&kci, from_dir, kcore.elf)) +		goto out_kcore_close; + +	if (kcore__init(&extract, extract_filename, kcore.elfclass, false)) +		goto out_kcore_close; + +	if (!kci.modules_map.addr) +		count -= 1; + +	if (kcore__copy_hdr(&kcore, &extract, count)) +		goto out_extract_close; + +	if (kcore__add_phdr(&extract, idx++, offset, kci.kernel_map.addr, +			    kci.kernel_map.len)) +		goto out_extract_close; + +	if (kci.modules_map.addr) { +		modules_offset = offset + kci.kernel_map.len; +		if (kcore__add_phdr(&extract, idx, modules_offset, +				    kci.modules_map.addr, kci.modules_map.len)) +			goto out_extract_close; +	} + +	sz = kcore__write(&extract); +	if (sz < 0 || sz > offset) +		goto out_extract_close; + +	if (copy_bytes(kcore.fd, kci.kernel_map.offset, extract.fd, offset, +		       kci.kernel_map.len)) +		goto out_extract_close; + +	if (modules_offset && copy_bytes(kcore.fd, kci.modules_map.offset, +					 extract.fd, modules_offset, +					 kci.modules_map.len)) +		goto out_extract_close; + +	if (kcore_copy__compare_file(from_dir, to_dir, "modules")) +		goto out_extract_close; + +	if (kcore_copy__compare_file(from_dir, to_dir, "kallsyms")) +		goto out_extract_close; + +	err = 0; + +out_extract_close: +	kcore__close(&extract); +	if (err) +		unlink(extract_filename); +out_kcore_close: +	kcore__close(&kcore); +out_unlink_modules: +	if (err) +		kcore_copy__unlink(to_dir, "modules"); +out_unlink_kallsyms: +	if (err) +		kcore_copy__unlink(to_dir, "kallsyms"); + +	return err; +} + +int kcore_extract__create(struct kcore_extract *kce) +{ +	struct kcore kcore; +	struct kcore extract; +	size_t count = 1; +	int idx = 0, err = -1; +	off_t offset = page_size, sz; + +	if (kcore__open(&kcore, kce->kcore_filename)) +		return -1; + +	strcpy(kce->extract_filename, PERF_KCORE_EXTRACT); +	if (kcore__init(&extract, kce->extract_filename, kcore.elfclass, true)) +		goto out_kcore_close; + +	if (kcore__copy_hdr(&kcore, &extract, count)) +		goto out_extract_close; + +	if (kcore__add_phdr(&extract, idx, offset, kce->addr, kce->len)) +		goto out_extract_close; + +	sz = kcore__write(&extract); +	if (sz < 0 || sz > offset) +		goto out_extract_close; + +	if (copy_bytes(kcore.fd, kce->offs, extract.fd, offset, kce->len)) +		goto out_extract_close; + +	err = 0; + +out_extract_close: +	kcore__close(&extract); +	if (err) +		unlink(kce->extract_filename); +out_kcore_close: +	kcore__close(&kcore); + +	return err; +} + +void kcore_extract__delete(struct kcore_extract *kce) +{ +	unlink(kce->extract_filename); +} +  void symbol__elf_init(void)  {  	elf_version(EV_CURRENT); diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 3a802c300fc..bd15f490d04 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -1,4 +1,5 @@  #include "symbol.h" +#include "util.h"  #include <stdio.h>  #include <fcntl.h> @@ -253,6 +254,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused,  	if (!ss->name)  		goto out_close; +	ss->fd = fd;  	ss->type = type;  	return 0; @@ -274,7 +276,7 @@ bool symsrc__has_symtab(struct symsrc *ss __maybe_unused)  void symsrc__destroy(struct symsrc *ss)  { -	free(ss->name); +	zfree(&ss->name);  	close(ss->fd);  } @@ -308,6 +310,21 @@ int file__read_maps(int fd __maybe_unused, bool exe __maybe_unused,  	return -1;  } +int kcore_extract__create(struct kcore_extract *kce __maybe_unused) +{ +	return -1; +} + +void kcore_extract__delete(struct kcore_extract *kce __maybe_unused) +{ +} + +int kcore_copy(const char *from_dir __maybe_unused, +	       const char *to_dir __maybe_unused) +{ +	return -1; +} +  void symbol__elf_init(void)  {  } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 7eb0362f4ff..7b9096f29cd 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -18,12 +18,9 @@  #include <elf.h>  #include <limits.h> +#include <symbol/kallsyms.h>  #include <sys/utsname.h> -#ifndef KSYM_NAME_LEN -#define KSYM_NAME_LEN 256 -#endif -  static int dso__load_kernel_sym(struct dso *dso, struct map *map,  				symbol_filter_t filter);  static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, @@ -32,11 +29,12 @@ int vmlinux_path__nr_entries;  char **vmlinux_path;  struct symbol_conf symbol_conf = { -	.use_modules	  = true, -	.try_vmlinux_path = true, -	.annotate_src	  = true, -	.demangle	  = true, -	.symfs            = "", +	.use_modules		= true, +	.try_vmlinux_path	= true, +	.annotate_src		= true, +	.demangle		= true, +	.cumulate_callchain	= true, +	.symfs			= "",  };  static enum dso_binary_type binary_type_symtab[] = { @@ -51,6 +49,7 @@ static enum dso_binary_type binary_type_symtab[] = {  	DSO_BINARY_TYPE__SYSTEM_PATH_DSO,  	DSO_BINARY_TYPE__GUEST_KMODULE,  	DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, +	DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,  	DSO_BINARY_TYPE__NOT_FOUND,  }; @@ -159,10 +158,12 @@ again:  		if (choose_best_symbol(curr, next) == SYMBOL_A) {  			rb_erase(&next->rb_node, symbols); +			symbol__delete(next);  			goto again;  		} else {  			nd = rb_next(&curr->rb_node);  			rb_erase(&curr->rb_node, symbols); +			symbol__delete(curr);  		}  	}  } @@ -410,7 +411,7 @@ struct symbol *dso__find_symbol(struct dso *dso,  	return symbols__find(&dso->symbols[type], addr);  } -struct symbol *dso__first_symbol(struct dso *dso, enum map_type type) +static struct symbol *dso__first_symbol(struct dso *dso, enum map_type type)  {  	return symbols__first(&dso->symbols[type]);  } @@ -443,60 +444,62 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso,  	return ret;  } -int kallsyms__parse(const char *filename, void *arg, -		    int (*process_symbol)(void *arg, const char *name, -					  char type, u64 start)) +int modules__parse(const char *filename, void *arg, +		   int (*process_module)(void *arg, const char *name, +					 u64 start))  {  	char *line = NULL;  	size_t n; -	int err = -1; -	FILE *file = fopen(filename, "r"); +	FILE *file; +	int err = 0; +	file = fopen(filename, "r");  	if (file == NULL) -		goto out_failure; - -	err = 0; +		return -1; -	while (!feof(file)) { +	while (1) { +		char name[PATH_MAX];  		u64 start; -		int line_len, len; -		char symbol_type; -		char *symbol_name; +		char *sep; +		ssize_t line_len;  		line_len = getline(&line, &n, file); -		if (line_len < 0 || !line) -			break; +		if (line_len < 0) { +			if (feof(file)) +				break; +			err = -1; +			goto out; +		} + +		if (!line) { +			err = -1; +			goto out; +		}  		line[--line_len] = '\0'; /* \n */ -		len = hex2u64(line, &start); +		sep = strrchr(line, 'x'); +		if (sep == NULL) +			continue; -		len++; -		if (len + 2 >= line_len) +		hex2u64(sep + 1, &start); + +		sep = strchr(line, ' '); +		if (sep == NULL)  			continue; -		symbol_type = line[len]; -		len += 2; -		symbol_name = line + len; -		len = line_len - len; +		*sep = '\0'; -		if (len >= KSYM_NAME_LEN) { -			err = -1; -			break; -		} +		scnprintf(name, sizeof(name), "[%s]", line); -		err = process_symbol(arg, symbol_name, -				     symbol_type, start); +		err = process_module(arg, name, start);  		if (err)  			break;  	} - +out:  	free(line);  	fclose(file);  	return err; - -out_failure: -	return -1;  }  struct process_kallsyms_args { @@ -504,12 +507,34 @@ struct process_kallsyms_args {  	struct dso *dso;  }; -static u8 kallsyms2elf_type(char type) +bool symbol__is_idle(struct symbol *sym)  { -	if (type == 'W') -		return STB_WEAK; +	const char * const idle_symbols[] = { +		"cpu_idle", +		"intel_idle", +		"default_idle", +		"native_safe_halt", +		"enter_idle", +		"exit_idle", +		"mwait_idle", +		"mwait_idle_with_hints", +		"poll_idle", +		"ppc64_runlatch_off", +		"pseries_dedicated_idle_sleep", +		NULL +	}; + +	int i; + +	if (!sym) +		return false; -	return isupper(type) ? STB_GLOBAL : STB_LOCAL; +	for (i = 0; idle_symbols[i]; i++) { +		if (!strcmp(idle_symbols[i], sym->name)) +			return true; +	} + +	return false;  }  static int map__process_kallsym_symbol(void *arg, const char *name, @@ -603,7 +628,7 @@ static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map,   * kernel range is broken in several maps, named [kernel].N, as we don't have   * the original ELF section names vmlinux have.   */ -static int dso__split_kallsyms(struct dso *dso, struct map *map, +static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta,  			       symbol_filter_t filter)  {  	struct map_groups *kmaps = map__kmap(map)->kmaps; @@ -668,6 +693,12 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map,  			char dso_name[PATH_MAX];  			struct dso *ndso; +			if (delta) { +				/* Kernel was relocated at boot time */ +				pos->start -= delta; +				pos->end -= delta; +			} +  			if (count == 0) {  				curr_map = map;  				goto filter_symbol; @@ -697,6 +728,10 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map,  			curr_map->map_ip = curr_map->unmap_ip = identity__map_ip;  			map_groups__insert(kmaps, curr_map);  			++kernel_range; +		} else if (delta) { +			/* Kernel was relocated at boot time */ +			pos->start -= delta; +			pos->end -= delta;  		}  filter_symbol:  		if (filter && filter(curr_map, pos)) { @@ -739,51 +774,259 @@ bool symbol__restricted_filename(const char *filename,  	return restricted;  } -struct kcore_mapfn_data { -	struct dso *dso; -	enum map_type type; -	struct list_head maps; +struct module_info { +	struct rb_node rb_node; +	char *name; +	u64 start;  }; -static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) +static void add_module(struct module_info *mi, struct rb_root *modules)  { -	struct kcore_mapfn_data *md = data; -	struct map *map; +	struct rb_node **p = &modules->rb_node; +	struct rb_node *parent = NULL; +	struct module_info *m; -	map = map__new2(start, md->dso, md->type); -	if (map == NULL) +	while (*p != NULL) { +		parent = *p; +		m = rb_entry(parent, struct module_info, rb_node); +		if (strcmp(mi->name, m->name) < 0) +			p = &(*p)->rb_left; +		else +			p = &(*p)->rb_right; +	} +	rb_link_node(&mi->rb_node, parent, p); +	rb_insert_color(&mi->rb_node, modules); +} + +static void delete_modules(struct rb_root *modules) +{ +	struct module_info *mi; +	struct rb_node *next = rb_first(modules); + +	while (next) { +		mi = rb_entry(next, struct module_info, rb_node); +		next = rb_next(&mi->rb_node); +		rb_erase(&mi->rb_node, modules); +		zfree(&mi->name); +		free(mi); +	} +} + +static struct module_info *find_module(const char *name, +				       struct rb_root *modules) +{ +	struct rb_node *n = modules->rb_node; + +	while (n) { +		struct module_info *m; +		int cmp; + +		m = rb_entry(n, struct module_info, rb_node); +		cmp = strcmp(name, m->name); +		if (cmp < 0) +			n = n->rb_left; +		else if (cmp > 0) +			n = n->rb_right; +		else +			return m; +	} + +	return NULL; +} + +static int __read_proc_modules(void *arg, const char *name, u64 start) +{ +	struct rb_root *modules = arg; +	struct module_info *mi; + +	mi = zalloc(sizeof(struct module_info)); +	if (!mi)  		return -ENOMEM; -	map->end = map->start + len; -	map->pgoff = pgoff; +	mi->name = strdup(name); +	mi->start = start; -	list_add(&map->node, &md->maps); +	if (!mi->name) { +		free(mi); +		return -ENOMEM; +	} + +	add_module(mi, modules); + +	return 0; +} + +static int read_proc_modules(const char *filename, struct rb_root *modules) +{ +	if (symbol__restricted_filename(filename, "/proc/modules")) +		return -1; + +	if (modules__parse(filename, modules, __read_proc_modules)) { +		delete_modules(modules); +		return -1; +	}  	return 0;  } +int compare_proc_modules(const char *from, const char *to) +{ +	struct rb_root from_modules = RB_ROOT; +	struct rb_root to_modules = RB_ROOT; +	struct rb_node *from_node, *to_node; +	struct module_info *from_m, *to_m; +	int ret = -1; + +	if (read_proc_modules(from, &from_modules)) +		return -1; + +	if (read_proc_modules(to, &to_modules)) +		goto out_delete_from; + +	from_node = rb_first(&from_modules); +	to_node = rb_first(&to_modules); +	while (from_node) { +		if (!to_node) +			break; + +		from_m = rb_entry(from_node, struct module_info, rb_node); +		to_m = rb_entry(to_node, struct module_info, rb_node); + +		if (from_m->start != to_m->start || +		    strcmp(from_m->name, to_m->name)) +			break; + +		from_node = rb_next(from_node); +		to_node = rb_next(to_node); +	} + +	if (!from_node && !to_node) +		ret = 0; + +	delete_modules(&to_modules); +out_delete_from: +	delete_modules(&from_modules); + +	return ret; +} + +static int do_validate_kcore_modules(const char *filename, struct map *map, +				  struct map_groups *kmaps) +{ +	struct rb_root modules = RB_ROOT; +	struct map *old_map; +	int err; + +	err = read_proc_modules(filename, &modules); +	if (err) +		return err; + +	old_map = map_groups__first(kmaps, map->type); +	while (old_map) { +		struct map *next = map_groups__next(old_map); +		struct module_info *mi; + +		if (old_map == map || old_map->start == map->start) { +			/* The kernel map */ +			old_map = next; +			continue; +		} + +		/* Module must be in memory at the same address */ +		mi = find_module(old_map->dso->short_name, &modules); +		if (!mi || mi->start != old_map->start) { +			err = -EINVAL; +			goto out; +		} + +		old_map = next; +	} +out: +	delete_modules(&modules); +	return err; +} +  /* - * If kallsyms is referenced by name then we look for kcore in the same + * If kallsyms is referenced by name then we look for filename in the same   * directory.   */ -static bool kcore_filename_from_kallsyms_filename(char *kcore_filename, -						  const char *kallsyms_filename) +static bool filename_from_kallsyms_filename(char *filename, +					    const char *base_name, +					    const char *kallsyms_filename)  {  	char *name; -	strcpy(kcore_filename, kallsyms_filename); -	name = strrchr(kcore_filename, '/'); +	strcpy(filename, kallsyms_filename); +	name = strrchr(filename, '/');  	if (!name)  		return false; -	if (!strcmp(name, "/kallsyms")) { -		strcpy(name, "/kcore"); +	name += 1; + +	if (!strcmp(name, "kallsyms")) { +		strcpy(name, base_name);  		return true;  	}  	return false;  } +static int validate_kcore_modules(const char *kallsyms_filename, +				  struct map *map) +{ +	struct map_groups *kmaps = map__kmap(map)->kmaps; +	char modules_filename[PATH_MAX]; + +	if (!filename_from_kallsyms_filename(modules_filename, "modules", +					     kallsyms_filename)) +		return -EINVAL; + +	if (do_validate_kcore_modules(modules_filename, map, kmaps)) +		return -EINVAL; + +	return 0; +} + +static int validate_kcore_addresses(const char *kallsyms_filename, +				    struct map *map) +{ +	struct kmap *kmap = map__kmap(map); + +	if (kmap->ref_reloc_sym && kmap->ref_reloc_sym->name) { +		u64 start; + +		start = kallsyms__get_function_start(kallsyms_filename, +						     kmap->ref_reloc_sym->name); +		if (start != kmap->ref_reloc_sym->addr) +			return -EINVAL; +	} + +	return validate_kcore_modules(kallsyms_filename, map); +} + +struct kcore_mapfn_data { +	struct dso *dso; +	enum map_type type; +	struct list_head maps; +}; + +static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) +{ +	struct kcore_mapfn_data *md = data; +	struct map *map; + +	map = map__new2(start, md->dso, md->type); +	if (map == NULL) +		return -ENOMEM; + +	map->end = map->start + len; +	map->pgoff = pgoff; + +	list_add(&map->node, &md->maps); + +	return 0; +} +  static int dso__load_kcore(struct dso *dso, struct map *map,  			   const char *kallsyms_filename)  { @@ -800,8 +1043,12 @@ static int dso__load_kcore(struct dso *dso, struct map *map,  	if (map != machine->vmlinux_maps[map->type])  		return -EINVAL; -	if (!kcore_filename_from_kallsyms_filename(kcore_filename, -						   kallsyms_filename)) +	if (!filename_from_kallsyms_filename(kcore_filename, "kcore", +					     kallsyms_filename)) +		return -EINVAL; + +	/* Modules and kernel must be present at their original addresses */ +	if (validate_kcore_addresses(kallsyms_filename, map))  		return -EINVAL;  	md.dso = dso; @@ -870,10 +1117,10 @@ static int dso__load_kcore(struct dso *dso, struct map *map,  	 * dso__data_read_addr().  	 */  	if (dso->kernel == DSO_TYPE_GUEST_KERNEL) -		dso->data_type = DSO_BINARY_TYPE__GUEST_KCORE; +		dso->binary_type = DSO_BINARY_TYPE__GUEST_KCORE;  	else -		dso->data_type = DSO_BINARY_TYPE__KCORE; -	dso__set_long_name(dso, strdup(kcore_filename)); +		dso->binary_type = DSO_BINARY_TYPE__KCORE; +	dso__set_long_name(dso, strdup(kcore_filename), true);  	close(fd); @@ -894,15 +1141,41 @@ out_err:  	return -EINVAL;  } +/* + * If the kernel is relocated at boot time, kallsyms won't match.  Compute the + * delta based on the relocation reference symbol. + */ +static int kallsyms__delta(struct map *map, const char *filename, u64 *delta) +{ +	struct kmap *kmap = map__kmap(map); +	u64 addr; + +	if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->name) +		return 0; + +	addr = kallsyms__get_function_start(filename, +					    kmap->ref_reloc_sym->name); +	if (!addr) +		return -1; + +	*delta = addr - kmap->ref_reloc_sym->addr; +	return 0; +} +  int dso__load_kallsyms(struct dso *dso, const char *filename,  		       struct map *map, symbol_filter_t filter)  { +	u64 delta = 0; +  	if (symbol__restricted_filename(filename, "/proc/kallsyms"))  		return -1;  	if (dso__load_all_kallsyms(dso, filename, map) < 0)  		return -1; +	if (kallsyms__delta(map, filename, &delta)) +		return -1; +  	symbols__fixup_duplicate(&dso->symbols[map->type]);  	symbols__fixup_end(&dso->symbols[map->type]); @@ -914,7 +1187,7 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,  	if (!dso__load_kcore(dso, map, filename))  		return dso__split_kallsyms_for_kcore(dso, map, filter);  	else -		return dso__split_kallsyms(dso, map, filter); +		return dso__split_kallsyms(dso, map, delta, filter);  }  static int dso__load_perf_map(struct dso *dso, struct map *map, @@ -979,6 +1252,46 @@ out_failure:  	return -1;  } +static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod, +					   enum dso_binary_type type) +{ +	switch (type) { +	case DSO_BINARY_TYPE__JAVA_JIT: +	case DSO_BINARY_TYPE__DEBUGLINK: +	case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: +	case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: +	case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: +	case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: +	case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: +		return !kmod && dso->kernel == DSO_TYPE_USER; + +	case DSO_BINARY_TYPE__KALLSYMS: +	case DSO_BINARY_TYPE__VMLINUX: +	case DSO_BINARY_TYPE__KCORE: +		return dso->kernel == DSO_TYPE_KERNEL; + +	case DSO_BINARY_TYPE__GUEST_KALLSYMS: +	case DSO_BINARY_TYPE__GUEST_VMLINUX: +	case DSO_BINARY_TYPE__GUEST_KCORE: +		return dso->kernel == DSO_TYPE_GUEST_KERNEL; + +	case DSO_BINARY_TYPE__GUEST_KMODULE: +	case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: +		/* +		 * kernel modules know their symtab type - it's set when +		 * creating a module dso in machine__new_module(). +		 */ +		return kmod && dso->symtab_type == type; + +	case DSO_BINARY_TYPE__BUILD_ID_CACHE: +		return true; + +	case DSO_BINARY_TYPE__NOT_FOUND: +	default: +		return false; +	} +} +  int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  {  	char *name; @@ -989,6 +1302,7 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  	int ss_pos = 0;  	struct symsrc ss_[2];  	struct symsrc *syms_ss = NULL, *runtime_ss = NULL; +	bool kmod;  	dso__set_loaded(dso, map->type); @@ -1029,7 +1343,11 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  	if (!name)  		return -1; -	/* Iterate over candidate debug images. +	kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || +		dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE; + +	/* +	 * Iterate over candidate debug images.  	 * Keep track of "interesting" ones (those which have a symtab, dynsym,  	 * and/or opd section) for processing.  	 */ @@ -1039,8 +1357,11 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  		enum dso_binary_type symtab_type = binary_type_symtab[i]; -		if (dso__binary_type_file(dso, symtab_type, -					  root_dir, name, PATH_MAX)) +		if (!dso__is_compatible_symtab_type(dso, kmod, symtab_type)) +			continue; + +		if (dso__read_binary_type_filename(dso, symtab_type, +						   root_dir, name, PATH_MAX))  			continue;  		/* Name is now the name of the next image to try */ @@ -1050,6 +1371,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  		if (!syms_ss && symsrc__has_symtab(ss)) {  			syms_ss = ss;  			next_slot = true; +			if (!dso->symsrc_filename) +				dso->symsrc_filename = strdup(name);  		}  		if (!runtime_ss && symsrc__possibly_runtime(ss)) { @@ -1062,6 +1385,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  			if (syms_ss && runtime_ss)  				break; +		} else { +			symsrc__destroy(ss);  		}  	} @@ -1077,15 +1402,10 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  	if (!runtime_ss && syms_ss)  		runtime_ss = syms_ss; -	if (syms_ss) { -		int km; - -		km = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || -		     dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE; -		ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, km); -	} else { +	if (syms_ss) +		ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, kmod); +	else  		ret = -1; -	}  	if (ret > 0) {  		int nr_plt; @@ -1120,7 +1440,8 @@ struct map *map_groups__find_by_name(struct map_groups *mg,  }  int dso__load_vmlinux(struct dso *dso, struct map *map, -		      const char *vmlinux, symbol_filter_t filter) +		      const char *vmlinux, bool vmlinux_allocated, +		      symbol_filter_t filter)  {  	int err = -1;  	struct symsrc ss; @@ -1146,10 +1467,10 @@ int dso__load_vmlinux(struct dso *dso, struct map *map,  	if (err > 0) {  		if (dso->kernel == DSO_TYPE_GUEST_KERNEL) -			dso->data_type = DSO_BINARY_TYPE__GUEST_VMLINUX; +			dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX;  		else -			dso->data_type = DSO_BINARY_TYPE__VMLINUX; -		dso__set_long_name(dso, (char *)vmlinux); +			dso->binary_type = DSO_BINARY_TYPE__VMLINUX; +		dso__set_long_name(dso, vmlinux, vmlinux_allocated);  		dso__set_loaded(dso, map->type);  		pr_debug("Using %s for symbols\n", symfs_vmlinux);  	} @@ -1168,26 +1489,125 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,  	filename = dso__build_id_filename(dso, NULL, 0);  	if (filename != NULL) { -		err = dso__load_vmlinux(dso, map, filename, filter); -		if (err > 0) { -			dso->lname_alloc = 1; +		err = dso__load_vmlinux(dso, map, filename, true, filter); +		if (err > 0)  			goto out; -		}  		free(filename);  	}  	for (i = 0; i < vmlinux_path__nr_entries; ++i) { -		err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter); -		if (err > 0) { -			dso__set_long_name(dso, strdup(vmlinux_path[i])); -			dso->lname_alloc = 1; +		err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); +		if (err > 0)  			break; -		}  	}  out:  	return err;  } +static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz) +{ +	char kallsyms_filename[PATH_MAX]; +	struct dirent *dent; +	int ret = -1; +	DIR *d; + +	d = opendir(dir); +	if (!d) +		return -1; + +	while (1) { +		dent = readdir(d); +		if (!dent) +			break; +		if (dent->d_type != DT_DIR) +			continue; +		scnprintf(kallsyms_filename, sizeof(kallsyms_filename), +			  "%s/%s/kallsyms", dir, dent->d_name); +		if (!validate_kcore_addresses(kallsyms_filename, map)) { +			strlcpy(dir, kallsyms_filename, dir_sz); +			ret = 0; +			break; +		} +	} + +	closedir(d); + +	return ret; +} + +static char *dso__find_kallsyms(struct dso *dso, struct map *map) +{ +	u8 host_build_id[BUILD_ID_SIZE]; +	char sbuild_id[BUILD_ID_SIZE * 2 + 1]; +	bool is_host = false; +	char path[PATH_MAX]; + +	if (!dso->has_build_id) { +		/* +		 * Last resort, if we don't have a build-id and couldn't find +		 * any vmlinux file, try the running kernel kallsyms table. +		 */ +		goto proc_kallsyms; +	} + +	if (sysfs__read_build_id("/sys/kernel/notes", host_build_id, +				 sizeof(host_build_id)) == 0) +		is_host = dso__build_id_equal(dso, host_build_id); + +	build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); + +	scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", buildid_dir, +		  sbuild_id); + +	/* Use /proc/kallsyms if possible */ +	if (is_host) { +		DIR *d; +		int fd; + +		/* If no cached kcore go with /proc/kallsyms */ +		d = opendir(path); +		if (!d) +			goto proc_kallsyms; +		closedir(d); + +		/* +		 * Do not check the build-id cache, until we know we cannot use +		 * /proc/kcore. +		 */ +		fd = open("/proc/kcore", O_RDONLY); +		if (fd != -1) { +			close(fd); +			/* If module maps match go with /proc/kallsyms */ +			if (!validate_kcore_addresses("/proc/kallsyms", map)) +				goto proc_kallsyms; +		} + +		/* Find kallsyms in build-id cache with kcore */ +		if (!find_matching_kcore(map, path, sizeof(path))) +			return strdup(path); + +		goto proc_kallsyms; +	} + +	/* Find kallsyms in build-id cache with kcore */ +	if (!find_matching_kcore(map, path, sizeof(path))) +		return strdup(path); + +	scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s", +		  buildid_dir, sbuild_id); + +	if (access(path, F_OK)) { +		pr_err("No kallsyms or vmlinux with build-id %s was found\n", +		       sbuild_id); +		return NULL; +	} + +	return strdup(path); + +proc_kallsyms: +	return strdup("/proc/kallsyms"); +} +  static int dso__load_kernel_sym(struct dso *dso, struct map *map,  				symbol_filter_t filter)  { @@ -1214,19 +1634,12 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,  		goto do_kallsyms;  	} -	if (symbol_conf.vmlinux_name != NULL) { -		err = dso__load_vmlinux(dso, map, -					symbol_conf.vmlinux_name, filter); -		if (err > 0) { -			dso__set_long_name(dso, -					   strdup(symbol_conf.vmlinux_name)); -			dso->lname_alloc = 1; -			return err; -		} -		return err; +	if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) { +		return dso__load_vmlinux(dso, map, symbol_conf.vmlinux_name, +					 false, filter);  	} -	if (vmlinux_path != NULL) { +	if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) {  		err = dso__load_vmlinux_path(dso, map, filter);  		if (err > 0)  			return err; @@ -1236,51 +1649,11 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,  	if (symbol_conf.symfs[0] != 0)  		return -1; -	/* -	 * Say the kernel DSO was created when processing the build-id header table, -	 * we have a build-id, so check if it is the same as the running kernel, -	 * using it if it is. -	 */ -	if (dso->has_build_id) { -		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) { -			if (dso__build_id_equal(dso, kallsyms_build_id)) { -				kallsyms_filename = "/proc/kallsyms"; -				goto do_kallsyms; -			} -		} -		/* -		 * Now look if we have it on the build-id cache in -		 * $HOME/.debug/[kernel.kallsyms]. -		 */ -		build_id__sprintf(dso->build_id, sizeof(dso->build_id), -				  sbuild_id); - -		if (asprintf(&kallsyms_allocated_filename, -			     "%s/.debug/[kernel.kallsyms]/%s", -			     getenv("HOME"), sbuild_id) == -1) { -			pr_err("Not enough memory for kallsyms file lookup\n"); -			return -1; -		} +	kallsyms_allocated_filename = dso__find_kallsyms(dso, map); +	if (!kallsyms_allocated_filename) +		return -1; -		kallsyms_filename = kallsyms_allocated_filename; - -		if (access(kallsyms_filename, F_OK)) { -			pr_err("No kallsyms or vmlinux with build-id %s " -			       "was found\n", sbuild_id); -			free(kallsyms_allocated_filename); -			return -1; -		} -	} else { -		/* -		 * Last resort, if we don't have a build-id and couldn't find -		 * any vmlinux file, try the running kernel kallsyms table. -		 */ -		kallsyms_filename = "/proc/kallsyms"; -	} +	kallsyms_filename = kallsyms_allocated_filename;  do_kallsyms:  	err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); @@ -1289,7 +1662,7 @@ do_kallsyms:  	free(kallsyms_allocated_filename);  	if (err > 0 && !dso__is_kcore(dso)) { -		dso__set_long_name(dso, strdup("[kernel.kallsyms]")); +		dso__set_long_name(dso, "[kernel.kallsyms]", false);  		map__fixup_start(map);  		map__fixup_end(map);  	} @@ -1319,7 +1692,8 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,  		 */  		if (symbol_conf.default_guest_vmlinux_name != NULL) {  			err = dso__load_vmlinux(dso, map, -				symbol_conf.default_guest_vmlinux_name, filter); +						symbol_conf.default_guest_vmlinux_name, +						false, filter);  			return err;  		} @@ -1336,7 +1710,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,  		pr_debug("Using %s for symbols\n", kallsyms_filename);  	if (err > 0 && !dso__is_kcore(dso)) {  		machine__mmap_name(machine, path, sizeof(path)); -		dso__set_long_name(dso, strdup(path)); +		dso__set_long_name(dso, strdup(path), true);  		map__fixup_start(map);  		map__fixup_end(map);  	} @@ -1346,13 +1720,10 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,  static void vmlinux_path__exit(void)  { -	while (--vmlinux_path__nr_entries >= 0) { -		free(vmlinux_path[vmlinux_path__nr_entries]); -		vmlinux_path[vmlinux_path__nr_entries] = NULL; -	} +	while (--vmlinux_path__nr_entries >= 0) +		zfree(&vmlinux_path[vmlinux_path__nr_entries]); -	free(vmlinux_path); -	vmlinux_path = NULL; +	zfree(&vmlinux_path);  }  static int vmlinux_path__init(void) @@ -1404,7 +1775,7 @@ out_fail:  	return -1;  } -static int setup_list(struct strlist **list, const char *list_str, +int setup_list(struct strlist **list, const char *list_str,  		      const char *list_name)  {  	if (list_str == NULL) diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index fd5b70ea298..615c752dd76 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -12,8 +12,9 @@  #include <byteswap.h>  #include <libgen.h>  #include "build-id.h" +#include "event.h" -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT  #include <libelf.h>  #include <gelf.h>  #endif @@ -21,7 +22,7 @@  #include "dso.h" -#ifdef HAVE_CPLUS_DEMANGLE +#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT  extern char *cplus_demangle(const char *, int);  static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i) @@ -46,12 +47,17 @@ static inline char *bfd_demangle(void __maybe_unused *v,   * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP;   * for newer versions we can use mmap to reduce memory usage:   */ -#ifdef LIBELF_MMAP +#ifdef HAVE_LIBELF_MMAP_SUPPORT  # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP  #else  # define PERF_ELF_C_READ_MMAP ELF_C_READ  #endif +#ifdef HAVE_LIBELF_SUPPORT +extern Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, +				GElf_Shdr *shp, const char *name, size_t *idx); +#endif +  #ifndef DMGL_PARAMS  #define DMGL_PARAMS      (1 << 0)       /* Include function args */  #define DMGL_ANSI        (1 << 1)       /* Include const, volatile, etc */ @@ -74,6 +80,17 @@ struct symbol {  void symbol__delete(struct symbol *sym);  void symbols__delete(struct rb_root *symbols); +/* symbols__for_each_entry - iterate over symbols (rb_root) + * + * @symbols: the rb_root of symbols + * @pos: the 'struct symbol *' to use as a loop cursor + * @nd: the 'struct rb_node *' to use as a temporary storage + */ +#define symbols__for_each_entry(symbols, pos, nd)			\ +	for (nd = rb_first(symbols);					\ +	     nd && (pos = rb_entry(nd, struct symbol, rb_node));	\ +	     nd = rb_next(nd)) +  static inline size_t symbol__size(const struct symbol *sym)  {  	return sym->end - sym->start + 1; @@ -85,12 +102,14 @@ struct symbol_conf {  	unsigned short	priv_size;  	unsigned short	nr_events;  	bool		try_vmlinux_path, +			ignore_vmlinux,  			show_kernel_path,  			use_modules,  			sort_by_name,  			show_nr_samples,  			show_total_period,  			use_callchain, +			cumulate_callchain,  			exclude_other,  			show_cpu_utilization,  			initialized, @@ -98,7 +117,8 @@ struct symbol_conf {  			annotate_asm_raw,  			annotate_src,  			event_group, -			demangle; +			demangle, +			filter_relative;  	const char	*vmlinux_name,  			*kallsyms_name,  			*source_prefix, @@ -163,12 +183,13 @@ struct mem_info {  };  struct addr_location { +	struct machine *machine;  	struct thread *thread;  	struct map    *map;  	struct symbol *sym;  	u64	      addr;  	char	      level; -	bool	      filtered; +	u8	      filtered;  	u8	      cpumode;  	s32	      cpu;  }; @@ -178,7 +199,7 @@ struct symsrc {  	int fd;  	enum dso_binary_type type; -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT  	Elf *elf;  	GElf_Ehdr ehdr; @@ -205,7 +226,8 @@ bool symsrc__possibly_runtime(struct symsrc *ss);  int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter);  int dso__load_vmlinux(struct dso *dso, struct map *map, -		      const char *vmlinux, symbol_filter_t filter); +		      const char *vmlinux, bool vmlinux_allocated, +		      symbol_filter_t filter);  int dso__load_vmlinux_path(struct dso *dso, struct map *map,  			   symbol_filter_t filter);  int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, @@ -215,13 +237,12 @@ struct symbol *dso__find_symbol(struct dso *dso, enum map_type type,  				u64 addr);  struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,  					const char *name); -struct symbol *dso__first_symbol(struct dso *dso, enum map_type type);  int filename__read_build_id(const char *filename, void *bf, size_t size);  int sysfs__read_build_id(const char *filename, void *bf, size_t size); -int kallsyms__parse(const char *filename, void *arg, -		    int (*process_symbol)(void *arg, const char *name, -					  char type, u64 start)); +int modules__parse(const char *filename, void *arg, +		   int (*process_module)(void *arg, const char *name, +					 u64 start));  int filename__read_debuglink(const char *filename, char *debuglink,  			     size_t size); @@ -236,6 +257,7 @@ size_t symbol__fprintf(struct symbol *sym, FILE *fp);  bool symbol_type__is_a(char symbol_type, enum map_type map_type);  bool symbol__restricted_filename(const char *filename,  				 const char *restricted_filename); +bool symbol__is_idle(struct symbol *sym);  int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,  		  struct symsrc *runtime_ss, symbol_filter_t filter, @@ -252,4 +274,24 @@ typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data);  int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,  		    bool *is_64_bit); +#define PERF_KCORE_EXTRACT "/tmp/perf-kcore-XXXXXX" + +struct kcore_extract { +	char *kcore_filename; +	u64 addr; +	u64 offs; +	u64 len; +	char extract_filename[sizeof(PERF_KCORE_EXTRACT)]; +	int fd; +}; + +int kcore_extract__create(struct kcore_extract *kce); +void kcore_extract__delete(struct kcore_extract *kce); + +int kcore_copy(const char *from_dir, const char *to_dir); +int compare_proc_modules(const char *from, const char *to); + +int setup_list(struct strlist **list, const char *list_str, +	       const char *list_name); +  #endif /* __PERF_SYMBOL */ diff --git a/tools/perf/util/sysfs.c b/tools/perf/util/sysfs.c deleted file mode 100644 index f71e9eafe15..00000000000 --- a/tools/perf/util/sysfs.c +++ /dev/null @@ -1,60 +0,0 @@ - -#include "util.h" -#include "sysfs.h" - -static const char * const sysfs_known_mountpoints[] = { -	"/sys", -	0, -}; - -static int sysfs_found; -char sysfs_mountpoint[PATH_MAX + 1]; - -static int sysfs_valid_mountpoint(const char *sysfs) -{ -	struct statfs st_fs; - -	if (statfs(sysfs, &st_fs) < 0) -		return -ENOENT; -	else if (st_fs.f_type != (long) SYSFS_MAGIC) -		return -ENOENT; - -	return 0; -} - -const char *sysfs_find_mountpoint(void) -{ -	const char * const *ptr; -	char type[100]; -	FILE *fp; - -	if (sysfs_found) -		return (const char *) sysfs_mountpoint; - -	ptr = sysfs_known_mountpoints; -	while (*ptr) { -		if (sysfs_valid_mountpoint(*ptr) == 0) { -			sysfs_found = 1; -			strcpy(sysfs_mountpoint, *ptr); -			return sysfs_mountpoint; -		} -		ptr++; -	} - -	/* give up and parse /proc/mounts */ -	fp = fopen("/proc/mounts", "r"); -	if (fp == NULL) -		return NULL; - -	while (!sysfs_found && -	       fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", -		      sysfs_mountpoint, type) == 2) { - -		if (strcmp(type, "sysfs") == 0) -			sysfs_found = 1; -	} - -	fclose(fp); - -	return sysfs_found ? sysfs_mountpoint : NULL; -} diff --git a/tools/perf/util/sysfs.h b/tools/perf/util/sysfs.h deleted file mode 100644 index a813b720393..00000000000 --- a/tools/perf/util/sysfs.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __SYSFS_H__ -#define __SYSFS_H__ - -const char *sysfs_find_mountpoint(void); - -#endif /* __DEBUGFS_H__ */ diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c index 065528b7563..e74c5963dc7 100644 --- a/tools/perf/util/target.c +++ b/tools/perf/util/target.c @@ -13,9 +13,9 @@  #include <string.h> -enum perf_target_errno perf_target__validate(struct perf_target *target) +enum target_errno target__validate(struct target *target)  { -	enum perf_target_errno ret = PERF_ERRNO_TARGET__SUCCESS; +	enum target_errno ret = TARGET_ERRNO__SUCCESS;  	if (target->pid)  		target->tid = target->pid; @@ -23,42 +23,49 @@ enum perf_target_errno perf_target__validate(struct perf_target *target)  	/* CPU and PID are mutually exclusive */  	if (target->tid && target->cpu_list) {  		target->cpu_list = NULL; -		if (ret == PERF_ERRNO_TARGET__SUCCESS) -			ret = PERF_ERRNO_TARGET__PID_OVERRIDE_CPU; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__PID_OVERRIDE_CPU;  	}  	/* UID and PID are mutually exclusive */  	if (target->tid && target->uid_str) {  		target->uid_str = NULL; -		if (ret == PERF_ERRNO_TARGET__SUCCESS) -			ret = PERF_ERRNO_TARGET__PID_OVERRIDE_UID; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__PID_OVERRIDE_UID;  	}  	/* UID and CPU are mutually exclusive */  	if (target->uid_str && target->cpu_list) {  		target->cpu_list = NULL; -		if (ret == PERF_ERRNO_TARGET__SUCCESS) -			ret = PERF_ERRNO_TARGET__UID_OVERRIDE_CPU; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__UID_OVERRIDE_CPU;  	}  	/* PID and SYSTEM are mutually exclusive */  	if (target->tid && target->system_wide) {  		target->system_wide = false; -		if (ret == PERF_ERRNO_TARGET__SUCCESS) -			ret = PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__PID_OVERRIDE_SYSTEM;  	}  	/* UID and SYSTEM are mutually exclusive */  	if (target->uid_str && target->system_wide) {  		target->system_wide = false; -		if (ret == PERF_ERRNO_TARGET__SUCCESS) -			ret = PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__UID_OVERRIDE_SYSTEM; +	} + +	/* THREAD and SYSTEM/CPU are mutually exclusive */ +	if (target->per_thread && (target->system_wide || target->cpu_list)) { +		target->per_thread = false; +		if (ret == TARGET_ERRNO__SUCCESS) +			ret = TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD;  	}  	return ret;  } -enum perf_target_errno perf_target__parse_uid(struct perf_target *target) +enum target_errno target__parse_uid(struct target *target)  {  	struct passwd pwd, *result;  	char buf[1024]; @@ -66,7 +73,7 @@ enum perf_target_errno perf_target__parse_uid(struct perf_target *target)  	target->uid = UINT_MAX;  	if (str == NULL) -		return PERF_ERRNO_TARGET__SUCCESS; +		return TARGET_ERRNO__SUCCESS;  	/* Try user name first */  	getpwnam_r(str, &pwd, buf, sizeof(buf), &result); @@ -79,32 +86,33 @@ enum perf_target_errno perf_target__parse_uid(struct perf_target *target)  		int uid = strtol(str, &endptr, 10);  		if (*endptr != '\0') -			return PERF_ERRNO_TARGET__INVALID_UID; +			return TARGET_ERRNO__INVALID_UID;  		getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);  		if (result == NULL) -			return PERF_ERRNO_TARGET__USER_NOT_FOUND; +			return TARGET_ERRNO__USER_NOT_FOUND;  	}  	target->uid = result->pw_uid; -	return PERF_ERRNO_TARGET__SUCCESS; +	return TARGET_ERRNO__SUCCESS;  }  /* - * This must have a same ordering as the enum perf_target_errno. + * This must have a same ordering as the enum target_errno.   */ -static const char *perf_target__error_str[] = { +static const char *target__error_str[] = {  	"PID/TID switch overriding CPU",  	"PID/TID switch overriding UID",  	"UID switch overriding CPU",  	"PID/TID switch overriding SYSTEM",  	"UID switch overriding SYSTEM", +	"SYSTEM/CPU switch overriding PER-THREAD",  	"Invalid User: %s",  	"Problems obtaining information for user %s",  }; -int perf_target__strerror(struct perf_target *target, int errnum, +int target__strerror(struct target *target, int errnum,  			  char *buf, size_t buflen)  {  	int idx; @@ -124,21 +132,20 @@ int perf_target__strerror(struct perf_target *target, int errnum,  		return 0;  	} -	if (errnum <  __PERF_ERRNO_TARGET__START || -	    errnum >= __PERF_ERRNO_TARGET__END) +	if (errnum <  __TARGET_ERRNO__START || errnum >= __TARGET_ERRNO__END)  		return -1; -	idx = errnum - __PERF_ERRNO_TARGET__START; -	msg = perf_target__error_str[idx]; +	idx = errnum - __TARGET_ERRNO__START; +	msg = target__error_str[idx];  	switch (errnum) { -	case PERF_ERRNO_TARGET__PID_OVERRIDE_CPU -	 ... PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM: +	case TARGET_ERRNO__PID_OVERRIDE_CPU ... +	     TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD:  		snprintf(buf, buflen, "%s", msg);  		break; -	case PERF_ERRNO_TARGET__INVALID_UID: -	case PERF_ERRNO_TARGET__USER_NOT_FOUND: +	case TARGET_ERRNO__INVALID_UID: +	case TARGET_ERRNO__USER_NOT_FOUND:  		snprintf(buf, buflen, msg, target->uid_str);  		break; diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h index a4be8575fda..7381b1ca404 100644 --- a/tools/perf/util/target.h +++ b/tools/perf/util/target.h @@ -4,7 +4,7 @@  #include <stdbool.h>  #include <sys/types.h> -struct perf_target { +struct target {  	const char   *pid;  	const char   *tid;  	const char   *cpu_list; @@ -12,10 +12,12 @@ struct perf_target {  	uid_t	     uid;  	bool	     system_wide;  	bool	     uses_mmap; +	bool	     default_per_cpu; +	bool	     per_thread;  }; -enum perf_target_errno { -	PERF_ERRNO_TARGET__SUCCESS		= 0, +enum target_errno { +	TARGET_ERRNO__SUCCESS		= 0,  	/*  	 * Choose an arbitrary negative big number not to clash with standard @@ -24,42 +26,54 @@ enum perf_target_errno {  	 *  	 * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html  	 */ -	__PERF_ERRNO_TARGET__START		= -10000, +	__TARGET_ERRNO__START		= -10000, +	/* for target__validate() */ +	TARGET_ERRNO__PID_OVERRIDE_CPU	= __TARGET_ERRNO__START, +	TARGET_ERRNO__PID_OVERRIDE_UID, +	TARGET_ERRNO__UID_OVERRIDE_CPU, +	TARGET_ERRNO__PID_OVERRIDE_SYSTEM, +	TARGET_ERRNO__UID_OVERRIDE_SYSTEM, +	TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD, -	/* for perf_target__validate() */ -	PERF_ERRNO_TARGET__PID_OVERRIDE_CPU	= __PERF_ERRNO_TARGET__START, -	PERF_ERRNO_TARGET__PID_OVERRIDE_UID, -	PERF_ERRNO_TARGET__UID_OVERRIDE_CPU, -	PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM, -	PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM, +	/* for target__parse_uid() */ +	TARGET_ERRNO__INVALID_UID, +	TARGET_ERRNO__USER_NOT_FOUND, -	/* for perf_target__parse_uid() */ -	PERF_ERRNO_TARGET__INVALID_UID, -	PERF_ERRNO_TARGET__USER_NOT_FOUND, - -	__PERF_ERRNO_TARGET__END, +	__TARGET_ERRNO__END,  }; -enum perf_target_errno perf_target__validate(struct perf_target *target); -enum perf_target_errno perf_target__parse_uid(struct perf_target *target); +enum target_errno target__validate(struct target *target); +enum target_errno target__parse_uid(struct target *target); -int perf_target__strerror(struct perf_target *target, int errnum, char *buf, -			  size_t buflen); +int target__strerror(struct target *target, int errnum, char *buf, size_t buflen); -static inline bool perf_target__has_task(struct perf_target *target) +static inline bool target__has_task(struct target *target)  {  	return target->tid || target->pid || target->uid_str;  } -static inline bool perf_target__has_cpu(struct perf_target *target) +static inline bool target__has_cpu(struct target *target)  {  	return target->system_wide || target->cpu_list;  } -static inline bool perf_target__none(struct perf_target *target) +static inline bool target__none(struct target *target) +{ +	return !target__has_task(target) && !target__has_cpu(target); +} + +static inline bool target__uses_dummy_map(struct target *target)  { -	return !perf_target__has_task(target) && !perf_target__has_cpu(target); +	bool use_dummy = false; + +	if (target->default_per_cpu) +		use_dummy = target->per_thread ? true : false; +	else if (target__has_task(target) || +	         (!target__has_cpu(target) && !target->uses_mmap)) +		use_dummy = true; + +	return use_dummy;  }  #endif /* _PERF_TARGET_H */ diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index e3d4a550a70..2fde0d5e40b 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -6,86 +6,188 @@  #include "thread.h"  #include "util.h"  #include "debug.h" +#include "comm.h" + +int thread__init_map_groups(struct thread *thread, struct machine *machine) +{ +	struct thread *leader; +	pid_t pid = thread->pid_; + +	if (pid == thread->tid) { +		thread->mg = map_groups__new(); +	} else { +		leader = machine__findnew_thread(machine, pid, pid); +		if (leader) +			thread->mg = map_groups__get(leader->mg); +	} + +	return thread->mg ? 0 : -1; +}  struct thread *thread__new(pid_t pid, pid_t tid)  { -	struct thread *self = zalloc(sizeof(*self)); - -	if (self != NULL) { -		map_groups__init(&self->mg); -		self->pid_ = pid; -		self->tid = tid; -		self->ppid = -1; -		self->comm = malloc(32); -		if (self->comm) -			snprintf(self->comm, 32, ":%d", self->tid); +	char *comm_str; +	struct comm *comm; +	struct thread *thread = zalloc(sizeof(*thread)); + +	if (thread != NULL) { +		thread->pid_ = pid; +		thread->tid = tid; +		thread->ppid = -1; +		INIT_LIST_HEAD(&thread->comm_list); + +		comm_str = malloc(32); +		if (!comm_str) +			goto err_thread; + +		snprintf(comm_str, 32, ":%d", tid); +		comm = comm__new(comm_str, 0); +		free(comm_str); +		if (!comm) +			goto err_thread; + +		list_add(&comm->list, &thread->comm_list); +	} + +	return thread; + +err_thread: +	free(thread); +	return NULL; +} + +void thread__delete(struct thread *thread) +{ +	struct comm *comm, *tmp; + +	map_groups__put(thread->mg); +	thread->mg = NULL; +	list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { +		list_del(&comm->list); +		comm__free(comm);  	} -	return self; +	free(thread);  } -void thread__delete(struct thread *self) +struct comm *thread__comm(const struct thread *thread)  { -	map_groups__exit(&self->mg); -	free(self->comm); -	free(self); +	if (list_empty(&thread->comm_list)) +		return NULL; + +	return list_first_entry(&thread->comm_list, struct comm, list);  } -int thread__set_comm(struct thread *self, const char *comm) +/* CHECKME: time should always be 0 if event aren't ordered */ +int thread__set_comm(struct thread *thread, const char *str, u64 timestamp)  { +	struct comm *new, *curr = thread__comm(thread);  	int err; -	if (self->comm) -		free(self->comm); -	self->comm = strdup(comm); -	err = self->comm == NULL ? -ENOMEM : 0; -	if (!err) { -		self->comm_set = true; +	/* Override latest entry if it had no specific time coverage */ +	if (!curr->start) { +		err = comm__override(curr, str, timestamp); +		if (err) +			return err; +	} else { +		new = comm__new(str, timestamp); +		if (!new) +			return -ENOMEM; +		list_add(&new->list, &thread->comm_list);  	} -	return err; + +	thread->comm_set = true; + +	return 0;  } -int thread__comm_len(struct thread *self) +const char *thread__comm_str(const struct thread *thread)  { -	if (!self->comm_len) { -		if (!self->comm) +	const struct comm *comm = thread__comm(thread); + +	if (!comm) +		return NULL; + +	return comm__str(comm); +} + +/* CHECKME: it should probably better return the max comm len from its comm list */ +int thread__comm_len(struct thread *thread) +{ +	if (!thread->comm_len) { +		const char *comm = thread__comm_str(thread); +		if (!comm)  			return 0; -		self->comm_len = strlen(self->comm); +		thread->comm_len = strlen(comm);  	} -	return self->comm_len; +	return thread->comm_len;  }  size_t thread__fprintf(struct thread *thread, FILE *fp)  { -	return fprintf(fp, "Thread %d %s\n", thread->tid, thread->comm) + -	       map_groups__fprintf(&thread->mg, verbose, fp); +	return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + +	       map_groups__fprintf(thread->mg, verbose, fp);  } -void thread__insert_map(struct thread *self, struct map *map) +void thread__insert_map(struct thread *thread, struct map *map)  { -	map_groups__fixup_overlappings(&self->mg, map, verbose, stderr); -	map_groups__insert(&self->mg, map); +	map_groups__fixup_overlappings(thread->mg, map, verbose, stderr); +	map_groups__insert(thread->mg, map);  } -int thread__fork(struct thread *self, struct thread *parent) +static int thread__clone_map_groups(struct thread *thread, +				    struct thread *parent)  {  	int i; +	/* This is new thread, we share map groups for process. */ +	if (thread->pid_ == parent->pid_) +		return 0; + +	/* But this one is new process, copy maps. */ +	for (i = 0; i < MAP__NR_TYPES; ++i) +		if (map_groups__clone(thread->mg, parent->mg, i) < 0) +			return -ENOMEM; + +	return 0; +} + +int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) +{ +	int err; +  	if (parent->comm_set) { -		if (self->comm) -			free(self->comm); -		self->comm = strdup(parent->comm); -		if (!self->comm) +		const char *comm = thread__comm_str(parent); +		if (!comm)  			return -ENOMEM; -		self->comm_set = true; +		err = thread__set_comm(thread, comm, timestamp); +		if (err) +			return err; +		thread->comm_set = true;  	} -	for (i = 0; i < MAP__NR_TYPES; ++i) -		if (map_groups__clone(&self->mg, &parent->mg, i) < 0) -			return -ENOMEM; +	thread->ppid = parent->tid; +	return thread__clone_map_groups(thread, parent); +} -	self->ppid = parent->tid; +void thread__find_cpumode_addr_location(struct thread *thread, +					struct machine *machine, +					enum map_type type, u64 addr, +					struct addr_location *al) +{ +	size_t i; +	const u8 const cpumodes[] = { +		PERF_RECORD_MISC_USER, +		PERF_RECORD_MISC_KERNEL, +		PERF_RECORD_MISC_GUEST_USER, +		PERF_RECORD_MISC_GUEST_KERNEL +	}; -	return 0; +	for (i = 0; i < ARRAY_SIZE(cpumodes); i++) { +		thread__find_addr_location(thread, machine, cpumodes[i], type, +					   addr, al); +		if (al->map) +			break; +	}  } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 4ebbb40d46d..3c0c2724f82 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -2,49 +2,49 @@  #define __PERF_THREAD_H  #include <linux/rbtree.h> +#include <linux/list.h>  #include <unistd.h>  #include <sys/types.h>  #include "symbol.h" +#include <strlist.h>  struct thread {  	union {  		struct rb_node	 rb_node;  		struct list_head node;  	}; -	struct map_groups	mg; +	struct map_groups	*mg;  	pid_t			pid_; /* Not all tools update this */  	pid_t			tid;  	pid_t			ppid;  	char			shortname[3];  	bool			comm_set;  	bool			dead; /* if set thread has exited */ -	char			*comm; +	struct list_head	comm_list;  	int			comm_len;  	void			*priv;  };  struct machine; +struct comm;  struct thread *thread__new(pid_t pid, pid_t tid); -void thread__delete(struct thread *self); +int thread__init_map_groups(struct thread *thread, struct machine *machine); +void thread__delete(struct thread *thread);  static inline void thread__exited(struct thread *thread)  {  	thread->dead = true;  } -int thread__set_comm(struct thread *self, const char *comm); -int thread__comm_len(struct thread *self); -void thread__insert_map(struct thread *self, struct map *map); -int thread__fork(struct thread *self, struct thread *parent); +int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); +int thread__comm_len(struct thread *thread); +struct comm *thread__comm(const struct thread *thread); +const char *thread__comm_str(const struct thread *thread); +void thread__insert_map(struct thread *thread, struct map *map); +int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp);  size_t thread__fprintf(struct thread *thread, FILE *fp); -static inline struct map *thread__find_map(struct thread *self, -					   enum map_type type, u64 addr) -{ -	return self ? map_groups__find(&self->mg, type, addr) : NULL; -} -  void thread__find_addr_map(struct thread *thread, struct machine *machine,  			   u8 cpumode, enum map_type type, u64 addr,  			   struct addr_location *al); @@ -53,6 +53,11 @@ void thread__find_addr_location(struct thread *thread, struct machine *machine,  				u8 cpumode, enum map_type type, u64 addr,  				struct addr_location *al); +void thread__find_cpumode_addr_location(struct thread *thread, +					struct machine *machine, +					enum map_type type, u64 addr, +					struct addr_location *al); +  static inline void *thread__priv(struct thread *thread)  {  	return thread->priv; @@ -62,4 +67,15 @@ static inline void thread__set_priv(struct thread *thread, void *p)  {  	thread->priv = p;  } + +static inline bool thread__is_filtered(struct thread *thread) +{ +	if (symbol_conf.comm_list && +	    !strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread))) { +		return true; +	} + +	return false; +} +  #endif	/* __PERF_THREAD_H */ diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 9b5f856cc28..5d321591210 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -9,6 +9,7 @@  #include "strlist.h"  #include <string.h>  #include "thread_map.h" +#include "util.h"  /* Skip "." and ".." directories */  static int filter(const struct dirent *dir) @@ -40,7 +41,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid)  	}  	for (i=0; i<items; i++) -		free(namelist[i]); +		zfree(&namelist[i]);  	free(namelist);  	return threads; @@ -117,7 +118,7 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)  			threads->map[threads->nr + i] = atoi(namelist[i]->d_name);  		for (i = 0; i < items; i++) -			free(namelist[i]); +			zfree(&namelist[i]);  		free(namelist);  		threads->nr += items; @@ -134,12 +135,11 @@ out_free_threads:  out_free_namelist:  	for (i = 0; i < items; i++) -		free(namelist[i]); +		zfree(&namelist[i]);  	free(namelist);  out_free_closedir: -	free(threads); -	threads = NULL; +	zfree(&threads);  	goto out_closedir;  } @@ -194,7 +194,7 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)  		for (i = 0; i < items; i++) {  			threads->map[j++] = atoi(namelist[i]->d_name); -			free(namelist[i]); +			zfree(&namelist[i]);  		}  		threads->nr = total_tasks;  		free(namelist); @@ -206,12 +206,11 @@ out:  out_free_namelist:  	for (i = 0; i < items; i++) -		free(namelist[i]); +		zfree(&namelist[i]);  	free(namelist);  out_free_threads: -	free(threads); -	threads = NULL; +	zfree(&threads);  	goto out;  } @@ -262,8 +261,7 @@ out:  	return threads;  out_free_threads: -	free(threads); -	threads = NULL; +	zfree(&threads);  	goto out;  } diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index f857b51b6bd..8e517def925 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -26,8 +26,8 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)  	float samples_per_sec;  	float ksamples_per_sec;  	float esamples_percent; -	struct perf_record_opts *opts = &top->record_opts; -	struct perf_target *target = &opts->target; +	struct record_opts *opts = &top->record_opts; +	struct target *target = &opts->target;  	size_t ret = 0;  	if (top->samples) { diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index b554ffc462b..f92c37abb0a 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -2,7 +2,7 @@  #define __PERF_TOP_H 1  #include "tool.h" -#include "types.h" +#include <linux/types.h>  #include <stddef.h>  #include <stdbool.h>  #include <termios.h> @@ -14,7 +14,7 @@ struct perf_session;  struct perf_top {  	struct perf_tool   tool;  	struct perf_evlist *evlist; -	struct perf_record_opts record_opts; +	struct record_opts record_opts;  	/*  	 * Symbols will be added here in perf_event__process_sample and will  	 * get out after decayed. @@ -24,6 +24,7 @@ struct perf_top {  	u64		   exact_samples;  	u64		   guest_us_samples, guest_kernel_samples;  	int		   print_entries, count_filter, delay_secs; +	int		   max_stack;  	bool		   hide_kernel_symbols, hide_user_symbols, zero;  	bool		   use_tui, use_stdio;  	bool		   kptr_restrict_warned; diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index f3c9e551bd3..7e6fcfe8b43 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -38,7 +38,7 @@  #include "../perf.h"  #include "trace-event.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include "evsel.h"  #define VERSION "0.5" @@ -397,8 +397,8 @@ put_tracepoints_path(struct tracepoint_path *tps)  		struct tracepoint_path *t = tps;  		tps = tps->next; -		free(t->name); -		free(t->system); +		zfree(&t->name); +		zfree(&t->system);  		free(t);  	}  } @@ -562,10 +562,8 @@ out:  		output_fd = fd;  	} -	if (err) { -		free(tdata); -		tdata = NULL; -	} +	if (err) +		zfree(&tdata);  	put_tracepoints_path(tps);  	return tdata; diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index e9e1c03f927..c36636fd825 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -28,19 +28,6 @@  #include "util.h"  #include "trace-event.h" -struct pevent *read_trace_init(int file_bigendian, int host_bigendian) -{ -	struct pevent *pevent = pevent_alloc(); - -	if (pevent != NULL) { -		pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); -		pevent_set_file_bigendian(pevent, file_bigendian); -		pevent_set_host_bigendian(pevent, host_bigendian); -	} - -	return pevent; -} -  static int get_common_field(struct scripting_context *context,  			    int *offset, int *size, const char *type)  { @@ -120,42 +107,6 @@ raw_field_value(struct event_format *event, const char *name, void *data)  	return val;  } -void *raw_field_ptr(struct event_format *event, const char *name, void *data) -{ -	struct format_field *field; - -	field = pevent_find_any_field(event, name); -	if (!field) -		return NULL; - -	if (field->flags & FIELD_IS_DYNAMIC) { -		int offset; - -		offset = *(int *)(data + field->offset); -		offset &= 0xffff; - -		return data + offset; -	} - -	return data + field->offset; -} - -int trace_parse_common_type(struct pevent *pevent, void *data) -{ -	struct pevent_record record; - -	record.data = data; -	return pevent_data_type(pevent, &record); -} - -int trace_parse_common_pid(struct pevent *pevent, void *data) -{ -	struct pevent_record record; - -	record.data = data; -	return pevent_data_pid(pevent, &record); -} -  unsigned long long read_size(struct event_format *event, void *ptr, int size)  {  	return pevent_read_number(event->pevent, ptr, size); @@ -175,6 +126,7 @@ void event_format__print(struct event_format *event,  	trace_seq_init(&s);  	pevent_event_info(&s, event, &record);  	trace_seq_do_printf(&s); +	trace_seq_destroy(&s);  }  void parse_proc_kallsyms(struct pevent *pevent, diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index f2112270c66..e113e180c48 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -343,7 +343,7 @@ static int read_event_files(struct pevent *pevent)  	return 0;  } -ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) +ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)  {  	char buf[BUFSIZ];  	char test[] = { 23, 8, 68 }; @@ -356,11 +356,9 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe)  	int host_bigendian;  	int file_long_size;  	int file_page_size; -	struct pevent *pevent; +	struct pevent *pevent = NULL;  	int err; -	*ppevent = NULL; -  	repipe = __repipe;  	input_fd = fd; @@ -390,12 +388,17 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe)  	file_bigendian = buf[0];  	host_bigendian = bigendian(); -	pevent = read_trace_init(file_bigendian, host_bigendian); -	if (pevent == NULL) { -		pr_debug("read_trace_init failed"); +	if (trace_event__init(tevent)) { +		pr_debug("trace_event__init failed");  		goto out;  	} +	pevent = tevent->pevent; + +	pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); +	pevent_set_file_bigendian(pevent, file_bigendian); +	pevent_set_host_bigendian(pevent, host_bigendian); +  	if (do_read(buf, 1) < 0)  		goto out;  	file_long_size = buf[0]; @@ -432,11 +435,10 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe)  		pevent_print_printk(pevent);  	} -	*ppevent = pevent;  	pevent = NULL;  out:  	if (pevent) -		pevent_free(pevent); +		trace_event__cleanup(tevent);  	return size;  } diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 95199e4eea9..57aaccc1692 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -38,9 +38,8 @@ static int stop_script_unsupported(void)  static void process_event_unsupported(union perf_event *event __maybe_unused,  				      struct perf_sample *sample __maybe_unused,  				      struct perf_evsel *evsel __maybe_unused, -				      struct machine *machine __maybe_unused,  				      struct thread *thread __maybe_unused, -					  struct addr_location *al __maybe_unused) +				      struct addr_location *al __maybe_unused)  {  } diff --git a/tools/perf/util/trace-event.c b/tools/perf/util/trace-event.c new file mode 100644 index 00000000000..6322d37164c --- /dev/null +++ b/tools/perf/util/trace-event.c @@ -0,0 +1,82 @@ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <linux/kernel.h> +#include <traceevent/event-parse.h> +#include "trace-event.h" +#include "util.h" + +/* + * global trace_event object used by trace_event__tp_format + * + * TODO There's no cleanup call for this. Add some sort of + * __exit function support and call trace_event__cleanup + * there. + */ +static struct trace_event tevent; + +int trace_event__init(struct trace_event *t) +{ +	struct pevent *pevent = pevent_alloc(); + +	if (pevent) { +		t->plugin_list = traceevent_load_plugins(pevent); +		t->pevent  = pevent; +	} + +	return pevent ? 0 : -1; +} + +void trace_event__cleanup(struct trace_event *t) +{ +	traceevent_unload_plugins(t->plugin_list, t->pevent); +	pevent_free(t->pevent); +} + +static struct event_format* +tp_format(const char *sys, const char *name) +{ +	struct pevent *pevent = tevent.pevent; +	struct event_format *event = NULL; +	char path[PATH_MAX]; +	size_t size; +	char *data; + +	scnprintf(path, PATH_MAX, "%s/%s/%s/format", +		  tracing_events_path, sys, name); + +	if (filename__read_str(path, &data, &size)) +		return NULL; + +	pevent_parse_format(pevent, &event, data, size, sys); + +	free(data); +	return event; +} + +struct event_format* +trace_event__tp_format(const char *sys, const char *name) +{ +	static bool initialized; + +	if (!initialized) { +		int be = traceevent_host_bigendian(); +		struct pevent *pevent; + +		if (trace_event__init(&tevent)) +			return NULL; + +		pevent = tevent.pevent; +		pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); +		pevent_set_file_bigendian(pevent, be); +		pevent_set_host_bigendian(pevent, be); +		initialized = true; +	} + +	return tp_format(sys, name); +} diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index fafe1a40444..7b6d6868832 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -3,19 +3,26 @@  #include <traceevent/event-parse.h>  #include "parse-events.h" -#include "session.h"  struct machine;  struct perf_sample;  union perf_event;  struct perf_tool;  struct thread; +struct plugin_list; -extern struct pevent *perf_pevent; +struct trace_event { +	struct pevent		*pevent; +	struct plugin_list	*plugin_list; +}; + +int trace_event__init(struct trace_event *t); +void trace_event__cleanup(struct trace_event *t); +struct event_format* +trace_event__tp_format(const char *sys, const char *name);  int bigendian(void); -struct pevent *read_trace_init(int file_bigendian, int host_bigendian);  void event_format__print(struct event_format *event,  			 int cpu, void *data, int size); @@ -23,26 +30,19 @@ int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size);  int parse_event_file(struct pevent *pevent,  		     char *buf, unsigned long size, char *sys); -struct pevent_record *trace_peek_data(struct pevent *pevent, int cpu); -  unsigned long long  raw_field_value(struct event_format *event, const char *name, void *data); -void *raw_field_ptr(struct event_format *event, const char *name, void *data);  void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size);  void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size); -ssize_t trace_report(int fd, struct pevent **pevent, bool repipe); - -int trace_parse_common_type(struct pevent *pevent, void *data); -int trace_parse_common_pid(struct pevent *pevent, void *data); +ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe);  struct event_format *trace_find_next_event(struct pevent *pevent,  					   struct event_format *event);  unsigned long long read_size(struct event_format *event, void *ptr, int size);  unsigned long long eval_flag(const char *flag); -struct pevent_record *trace_read_data(struct pevent *pevent, int cpu);  int read_tracing_data(int fd, struct list_head *pattrs);  struct tracing_data { @@ -68,7 +68,6 @@ struct scripting_ops {  	void (*process_event) (union perf_event *event,  			       struct perf_sample *sample,  			       struct perf_evsel *evsel, -			       struct machine *machine,  			       struct thread *thread,  				   struct addr_location *al);  	int (*generate_script) (struct pevent *pevent, const char *outfile); diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h deleted file mode 100644 index c51fa6b70a2..00000000000 --- a/tools/perf/util/types.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef __PERF_TYPES_H -#define __PERF_TYPES_H - -#include <stdint.h> - -/* - * We define u64 as uint64_t for every architecture - * so that we can print it with "%"PRIx64 without getting warnings. - */ -typedef uint64_t	   u64; -typedef int64_t		   s64; -typedef unsigned int	   u32; -typedef signed int	   s32; -typedef unsigned short	   u16; -typedef signed short	   s16; -typedef unsigned char	   u8; -typedef signed char	   s8; - -union u64_swap { -	u64 val64; -	u32 val32[2]; -}; - -#endif /* __PERF_TYPES_H */ diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c new file mode 100644 index 00000000000..5ec80a575b5 --- /dev/null +++ b/tools/perf/util/unwind-libdw.c @@ -0,0 +1,210 @@ +#include <linux/compiler.h> +#include <elfutils/libdw.h> +#include <elfutils/libdwfl.h> +#include <inttypes.h> +#include <errno.h> +#include "unwind.h" +#include "unwind-libdw.h" +#include "machine.h" +#include "thread.h" +#include <linux/types.h> +#include "event.h" +#include "perf_regs.h" + +static char *debuginfo_path; + +static const Dwfl_Callbacks offline_callbacks = { +	.find_debuginfo		= dwfl_standard_find_debuginfo, +	.debuginfo_path		= &debuginfo_path, +	.section_address	= dwfl_offline_section_address, +}; + +static int __report_module(struct addr_location *al, u64 ip, +			    struct unwind_info *ui) +{ +	Dwfl_Module *mod; +	struct dso *dso = NULL; + +	thread__find_addr_location(ui->thread, ui->machine, +				   PERF_RECORD_MISC_USER, +				   MAP__FUNCTION, ip, al); + +	if (al->map) +		dso = al->map->dso; + +	if (!dso) +		return 0; + +	mod = dwfl_addrmodule(ui->dwfl, ip); +	if (!mod) +		mod = dwfl_report_elf(ui->dwfl, dso->short_name, +				      dso->long_name, -1, al->map->start, +				      false); + +	return mod && dwfl_addrmodule(ui->dwfl, ip) == mod ? 0 : -1; +} + +static int report_module(u64 ip, struct unwind_info *ui) +{ +	struct addr_location al; + +	return __report_module(&al, ip, ui); +} + +static int entry(u64 ip, struct unwind_info *ui) + +{ +	struct unwind_entry e; +	struct addr_location al; + +	if (__report_module(&al, ip, ui)) +		return -1; + +	e.ip  = ip; +	e.map = al.map; +	e.sym = al.sym; + +	pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", +		 al.sym ? al.sym->name : "''", +		 ip, +		 al.map ? al.map->map_ip(al.map, ip) : (u64) 0); + +	return ui->cb(&e, ui->arg); +} + +static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp) +{ +	/* We want only single thread to be processed. */ +	if (*thread_argp != NULL) +		return 0; + +	*thread_argp = arg; +	return dwfl_pid(dwfl); +} + +static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr, +			  Dwarf_Word *data) +{ +	struct addr_location al; +	ssize_t size; + +	thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, +			      MAP__FUNCTION, addr, &al); +	if (!al.map) { +		pr_debug("unwind: no map for %lx\n", (unsigned long)addr); +		return -1; +	} + +	if (!al.map->dso) +		return -1; + +	size = dso__data_read_addr(al.map->dso, al.map, ui->machine, +				   addr, (u8 *) data, sizeof(*data)); + +	return !(size == sizeof(*data)); +} + +static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *result, +			void *arg) +{ +	struct unwind_info *ui = arg; +	struct stack_dump *stack = &ui->sample->user_stack; +	u64 start, end; +	int offset; +	int ret; + +	ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP); +	if (ret) +		return false; + +	end = start + stack->size; + +	/* Check overflow. */ +	if (addr + sizeof(Dwarf_Word) < addr) +		return false; + +	if (addr < start || addr + sizeof(Dwarf_Word) > end) { +		ret = access_dso_mem(ui, addr, result); +		if (ret) { +			pr_debug("unwind: access_mem 0x%" PRIx64 " not inside range" +				 " 0x%" PRIx64 "-0x%" PRIx64 "\n", +				addr, start, end); +			return false; +		} +		return true; +	} + +	offset  = addr - start; +	*result = *(Dwarf_Word *)&stack->data[offset]; +	pr_debug("unwind: access_mem addr 0x%" PRIx64 ", val %lx, offset %d\n", +		 addr, (unsigned long)*result, offset); +	return true; +} + +static const Dwfl_Thread_Callbacks callbacks = { +	.next_thread		= next_thread, +	.memory_read		= memory_read, +	.set_initial_registers	= libdw__arch_set_initial_registers, +}; + +static int +frame_callback(Dwfl_Frame *state, void *arg) +{ +	struct unwind_info *ui = arg; +	Dwarf_Addr pc; + +	if (!dwfl_frame_pc(state, &pc, NULL)) { +		pr_err("%s", dwfl_errmsg(-1)); +		return DWARF_CB_ABORT; +	} + +	return entry(pc, ui) || !(--ui->max_stack) ? +	       DWARF_CB_ABORT : DWARF_CB_OK; +} + +int unwind__get_entries(unwind_entry_cb_t cb, void *arg, +			struct machine *machine, struct thread *thread, +			struct perf_sample *data, +			int max_stack) +{ +	struct unwind_info ui = { +		.sample		= data, +		.thread		= thread, +		.machine	= machine, +		.cb		= cb, +		.arg		= arg, +		.max_stack	= max_stack, +	}; +	Dwarf_Word ip; +	int err = -EINVAL; + +	if (!data->user_regs.regs) +		return -EINVAL; + +	ui.dwfl = dwfl_begin(&offline_callbacks); +	if (!ui.dwfl) +		goto out; + +	err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); +	if (err) +		goto out; + +	err = report_module(ip, &ui); +	if (err) +		goto out; + +	if (!dwfl_attach_state(ui.dwfl, EM_NONE, thread->tid, &callbacks, &ui)) +		goto out; + +	err = dwfl_getthread_frames(ui.dwfl, thread->tid, frame_callback, &ui); + +	if (err && !ui.max_stack) +		err = 0; + + out: +	if (err) +		pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1)); + +	dwfl_end(ui.dwfl); +	return 0; +} diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h new file mode 100644 index 00000000000..417a1426f3a --- /dev/null +++ b/tools/perf/util/unwind-libdw.h @@ -0,0 +1,21 @@ +#ifndef __PERF_UNWIND_LIBDW_H +#define __PERF_UNWIND_LIBDW_H + +#include <elfutils/libdwfl.h> +#include "event.h" +#include "thread.h" +#include "unwind.h" + +bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg); + +struct unwind_info { +	Dwfl			*dwfl; +	struct perf_sample      *sample; +	struct machine          *machine; +	struct thread           *thread; +	unwind_entry_cb_t	cb; +	void			*arg; +	int			max_stack; +}; + +#endif /* __PERF_UNWIND_LIBDW_H */ diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind-libunwind.c index 2f891f7e70b..25578b98f5c 100644 --- a/tools/perf/util/unwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -28,6 +28,7 @@  #include "session.h"  #include "perf_regs.h"  #include "unwind.h" +#include "symbol.h"  #include "util.h"  extern int @@ -39,6 +40,15 @@ UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,  #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +extern int +UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, +				 unw_word_t ip, +				 unw_word_t segbase, +				 const char *obj_name, unw_word_t start, +				 unw_word_t end); + +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +  #define DW_EH_PE_FORMAT_MASK	0x0f	/* format of the encoded value */  #define DW_EH_PE_APPL_MASK	0x70	/* how the value is to be applied */ @@ -76,7 +86,6 @@ struct unwind_info {  	struct perf_sample	*sample;  	struct machine		*machine;  	struct thread		*thread; -	u64			sample_uregs;  };  #define dw_read(ptr, type, end) ({	\ @@ -149,23 +158,6 @@ static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,  	__v;                                                    \  	}) -static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, -				    GElf_Shdr *shp, const char *name) -{ -	Elf_Scn *sec = NULL; - -	while ((sec = elf_nextscn(elf, sec)) != NULL) { -		char *str; - -		gelf_getshdr(sec, shp); -		str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); -		if (!strcmp(name, str)) -			break; -	} - -	return sec; -} -  static u64 elf_section_offset(int fd, const char *name)  {  	Elf *elf; @@ -181,7 +173,7 @@ static u64 elf_section_offset(int fd, const char *name)  		if (gelf_getehdr(elf, &ehdr) == NULL)  			break; -		if (!elf_section_by_name(elf, &ehdr, &shdr, name)) +		if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))  			break;  		offset = shdr.sh_offset; @@ -245,8 +237,9 @@ static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,  	return 0;  } -static int read_unwind_spec(struct dso *dso, struct machine *machine, -			    u64 *table_data, u64 *segbase, u64 *fde_count) +static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, +				     u64 *table_data, u64 *segbase, +				     u64 *fde_count)  {  	int ret = -EINVAL, fd;  	u64 offset; @@ -255,18 +248,36 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine,  	if (fd < 0)  		return -EINVAL; +	/* Check the .eh_frame section for unwinding info */  	offset = elf_section_offset(fd, ".eh_frame_hdr"); -	close(fd);  	if (offset)  		ret = unwind_spec_ehframe(dso, machine, offset,  					  table_data, segbase,  					  fde_count); -	/* TODO .debug_frame check if eh_frame_hdr fails */  	return ret;  } +#ifndef NO_LIBUNWIND_DEBUG_FRAME +static int read_unwind_spec_debug_frame(struct dso *dso, +					struct machine *machine, u64 *offset) +{ +	int fd = dso__data_fd(dso, machine); + +	if (fd < 0) +		return -EINVAL; + +	/* Check the .debug_frame section for unwinding info */ +	*offset = elf_section_offset(fd, ".debug_frame"); + +	if (*offset) +		return 0; + +	return -EINVAL; +} +#endif +  static struct map *find_map(unw_word_t ip, struct unwind_info *ui)  {  	struct addr_location al; @@ -291,20 +302,33 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,  	pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); -	if (read_unwind_spec(map->dso, ui->machine, -			     &table_data, &segbase, &fde_count)) -		return -EINVAL; +	/* Check the .eh_frame section for unwinding info */ +	if (!read_unwind_spec_eh_frame(map->dso, ui->machine, +				       &table_data, &segbase, &fde_count)) { +		memset(&di, 0, sizeof(di)); +		di.format   = UNW_INFO_FORMAT_REMOTE_TABLE; +		di.start_ip = map->start; +		di.end_ip   = map->end; +		di.u.rti.segbase    = map->start + segbase; +		di.u.rti.table_data = map->start + table_data; +		di.u.rti.table_len  = fde_count * sizeof(struct table_entry) +				      / sizeof(unw_word_t); +		return dwarf_search_unwind_table(as, ip, &di, pi, +						 need_unwind_info, arg); +	} + +#ifndef NO_LIBUNWIND_DEBUG_FRAME +	/* Check the .debug_frame section for unwinding info */ +	if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) { +		memset(&di, 0, sizeof(di)); +		if (dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name, +					   map->start, map->end)) +			return dwarf_search_unwind_table(as, ip, &di, pi, +							 need_unwind_info, arg); +	} +#endif -	memset(&di, 0, sizeof(di)); -	di.format   = UNW_INFO_FORMAT_REMOTE_TABLE; -	di.start_ip = map->start; -	di.end_ip   = map->end; -	di.u.rti.segbase    = map->start + segbase; -	di.u.rti.table_data = map->start + table_data; -	di.u.rti.table_len  = fde_count * sizeof(struct table_entry) -			      / sizeof(unw_word_t); -	return dwarf_search_unwind_table(as, ip, &di, pi, -					 need_unwind_info, arg); +	return -EINVAL;  }  static int access_fpreg(unw_addr_space_t __maybe_unused as, @@ -364,30 +388,13 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,  	return !(size == sizeof(*data));  } -static int reg_value(unw_word_t *valp, struct regs_dump *regs, int id, -		     u64 sample_regs) -{ -	int i, idx = 0; - -	if (!(sample_regs & (1 << id))) -		return -EINVAL; - -	for (i = 0; i < id; i++) { -		if (sample_regs & (1 << i)) -			idx++; -	} - -	*valp = regs->regs[idx]; -	return 0; -} -  static int access_mem(unw_addr_space_t __maybe_unused as,  		      unw_word_t addr, unw_word_t *valp,  		      int __write, void *arg)  {  	struct unwind_info *ui = arg;  	struct stack_dump *stack = &ui->sample->user_stack; -	unw_word_t start, end; +	u64 start, end;  	int offset;  	int ret; @@ -397,8 +404,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as,  		return 0;  	} -	ret = reg_value(&start, &ui->sample->user_regs, PERF_REG_SP, -			ui->sample_uregs); +	ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP);  	if (ret)  		return ret; @@ -411,8 +417,9 @@ static int access_mem(unw_addr_space_t __maybe_unused as,  	if (addr < start || addr + sizeof(unw_word_t) >= end) {  		ret = access_dso_mem(ui, addr, valp);  		if (ret) { -			pr_debug("unwind: access_mem %p not inside range %p-%p\n", -				(void *)addr, (void *)start, (void *)end); +			pr_debug("unwind: access_mem %p not inside range" +				 " 0x%" PRIx64 "-0x%" PRIx64 "\n", +				 (void *) addr, start, end);  			*valp = 0;  			return ret;  		} @@ -421,8 +428,8 @@ static int access_mem(unw_addr_space_t __maybe_unused as,  	offset = addr - start;  	*valp  = *(unw_word_t *)&stack->data[offset]; -	pr_debug("unwind: access_mem addr %p, val %lx, offset %d\n", -		 (void *)addr, (unsigned long)*valp, offset); +	pr_debug("unwind: access_mem addr %p val %lx, offset %d\n", +		 (void *) addr, (unsigned long)*valp, offset);  	return 0;  } @@ -432,6 +439,7 @@ static int access_reg(unw_addr_space_t __maybe_unused as,  {  	struct unwind_info *ui = arg;  	int id, ret; +	u64 val;  	/* Don't support write, I suspect we don't need it. */  	if (__write) { @@ -444,16 +452,17 @@ static int access_reg(unw_addr_space_t __maybe_unused as,  		return 0;  	} -	id = unwind__arch_reg_id(regnum); +	id = libunwind__arch_reg_id(regnum);  	if (id < 0)  		return -EINVAL; -	ret = reg_value(valp, &ui->sample->user_regs, id, ui->sample_uregs); +	ret = perf_reg_value(&val, &ui->sample->user_regs, id);  	if (ret) {  		pr_err("unwind: can't read reg %d\n", regnum);  		return ret;  	} +	*valp = (unw_word_t) val;  	pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);  	return 0;  } @@ -516,7 +525,7 @@ static unw_accessors_t accessors = {  };  static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, -		       void *arg) +		       void *arg, int max_stack)  {  	unw_addr_space_t addr_space;  	unw_cursor_t c; @@ -532,11 +541,11 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,  	if (ret)  		display_error(ret); -	while (!ret && (unw_step(&c) > 0)) { +	while (!ret && (unw_step(&c) > 0) && max_stack--) {  		unw_word_t ip;  		unw_get_reg(&c, UNW_REG_IP, &ip); -		ret = entry(ip, ui->thread, ui->machine, cb, arg); +		ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0;  	}  	unw_destroy_addr_space(addr_space); @@ -545,12 +554,11 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,  int unwind__get_entries(unwind_entry_cb_t cb, void *arg,  			struct machine *machine, struct thread *thread, -			u64 sample_uregs, struct perf_sample *data) +			struct perf_sample *data, int max_stack)  { -	unw_word_t ip; +	u64 ip;  	struct unwind_info ui = {  		.sample       = data, -		.sample_uregs = sample_uregs,  		.thread       = thread,  		.machine      = machine,  	}; @@ -559,7 +567,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,  	if (!data->user_regs.regs)  		return -EINVAL; -	ret = reg_value(&ip, &data->user_regs, PERF_REG_IP, sample_uregs); +	ret = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP);  	if (ret)  		return ret; @@ -567,5 +575,5 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,  	if (ret)  		return -ENOMEM; -	return get_entries(&ui, cb, arg); +	return --max_stack > 0 ? get_entries(&ui, cb, arg, max_stack) : 0;  } diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index cb6bc503a79..f03061260b4 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -1,7 +1,7 @@  #ifndef __UNWIND_H  #define __UNWIND_H -#include "types.h" +#include <linux/types.h>  #include "event.h"  #include "symbol.h" @@ -13,23 +13,25 @@ struct unwind_entry {  typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); -#ifdef LIBUNWIND_SUPPORT +#ifdef HAVE_DWARF_UNWIND_SUPPORT  int unwind__get_entries(unwind_entry_cb_t cb, void *arg,  			struct machine *machine,  			struct thread *thread, -			u64 sample_uregs, -			struct perf_sample *data); -int unwind__arch_reg_id(int regnum); +			struct perf_sample *data, int max_stack); +/* libunwind specific */ +#ifdef HAVE_LIBUNWIND_SUPPORT +int libunwind__arch_reg_id(int regnum); +#endif  #else  static inline int  unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,  		    void *arg __maybe_unused,  		    struct machine *machine __maybe_unused,  		    struct thread *thread __maybe_unused, -		    u64 sample_uregs __maybe_unused, -		    struct perf_sample *data __maybe_unused) +		    struct perf_sample *data __maybe_unused, +		    int max_stack __maybe_unused)  {  	return 0;  } -#endif /* LIBUNWIND_SUPPORT */ +#endif /* HAVE_DWARF_UNWIND_SUPPORT */  #endif /* __UNWIND_H */ diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 6d17b18e915..95aefa78bb0 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -1,16 +1,23 @@  #include "../perf.h"  #include "util.h" +#include <api/fs/fs.h>  #include <sys/mman.h> -#ifdef BACKTRACE_SUPPORT +#ifdef HAVE_BACKTRACE_SUPPORT  #include <execinfo.h>  #endif  #include <stdio.h>  #include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <byteswap.h> +#include <linux/kernel.h>  /*   * XXX We need to find a better place for these things...   */  unsigned int page_size; +int cacheline_size;  bool test_attr__enabled; @@ -55,17 +62,20 @@ int mkdir_p(char *path, mode_t mode)  	return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;  } -static int slow_copyfile(const char *from, const char *to) +static int slow_copyfile(const char *from, const char *to, mode_t mode)  { -	int err = 0; +	int err = -1;  	char *line = NULL;  	size_t n;  	FILE *from_fp = fopen(from, "r"), *to_fp; +	mode_t old_umask;  	if (from_fp == NULL)  		goto out; +	old_umask = umask(mode ^ 0777);  	to_fp = fopen(to, "w"); +	umask(old_umask);  	if (to_fp == NULL)  		goto out_fclose_from; @@ -82,7 +92,7 @@ out:  	return err;  } -int copyfile(const char *from, const char *to) +int copyfile_mode(const char *from, const char *to, mode_t mode)  {  	int fromfd, tofd;  	struct stat st; @@ -93,13 +103,13 @@ int copyfile(const char *from, const char *to)  		goto out;  	if (st.st_size == 0) /* /proc? do it slowly... */ -		return slow_copyfile(from, to); +		return slow_copyfile(from, to, mode);  	fromfd = open(from, O_RDONLY);  	if (fromfd < 0)  		goto out; -	tofd = creat(to, 0755); +	tofd = creat(to, mode);  	if (tofd < 0)  		goto out_close_from; @@ -121,6 +131,11 @@ out:  	return err;  } +int copyfile(const char *from, const char *to) +{ +	return copyfile_mode(from, to, 0755); +} +  unsigned long convert_unit(unsigned long value, char *unit)  {  	*unit = ' '; @@ -143,21 +158,42 @@ unsigned long convert_unit(unsigned long value, char *unit)  	return value;  } -int readn(int fd, void *buf, size_t n) +static ssize_t ion(bool is_read, int fd, void *buf, size_t n)  {  	void *buf_start = buf; +	size_t left = n; -	while (n) { -		int ret = read(fd, buf, n); +	while (left) { +		ssize_t ret = is_read ? read(fd, buf, left) : +					write(fd, buf, left); +		if (ret < 0 && errno == EINTR) +			continue;  		if (ret <= 0)  			return ret; -		n -= ret; -		buf += ret; +		left -= ret; +		buf  += ret;  	} -	return buf - buf_start; +	BUG_ON((size_t)(buf - buf_start) != n); +	return n; +} + +/* + * Read exactly 'n' bytes or return an error. + */ +ssize_t readn(int fd, void *buf, size_t n) +{ +	return ion(true, fd, buf, n); +} + +/* + * Write exactly 'n' bytes or return an error. + */ +ssize_t writen(int fd, void *buf, size_t n) +{ +	return ion(false, fd, buf, n);  }  size_t hex_width(u64 v) @@ -204,7 +240,7 @@ int hex2u64(const char *ptr, u64 *long_val)  }  /* Obtain a backtrace and print it to stdout. */ -#ifdef BACKTRACE_SUPPORT +#ifdef HAVE_BACKTRACE_SUPPORT  void dump_stack(void)  {  	void *array[16]; @@ -361,3 +397,146 @@ int parse_nsec_time(const char *str, u64 *ptime)  	*ptime = time_sec * NSEC_PER_SEC + time_nsec;  	return 0;  } + +unsigned long parse_tag_value(const char *str, struct parse_tag *tags) +{ +	struct parse_tag *i = tags; + +	while (i->tag) { +		char *s; + +		s = strchr(str, i->tag); +		if (s) { +			unsigned long int value; +			char *endptr; + +			value = strtoul(str, &endptr, 10); +			if (s != endptr) +				break; + +			if (value > ULONG_MAX / i->mult) +				break; +			value *= i->mult; +			return value; +		} +		i++; +	} + +	return (unsigned long) -1; +} + +int filename__read_int(const char *filename, int *value) +{ +	char line[64]; +	int fd = open(filename, O_RDONLY), err = -1; + +	if (fd < 0) +		return -1; + +	if (read(fd, line, sizeof(line)) > 0) { +		*value = atoi(line); +		err = 0; +	} + +	close(fd); +	return err; +} + +int filename__read_str(const char *filename, char **buf, size_t *sizep) +{ +	size_t size = 0, alloc_size = 0; +	void *bf = NULL, *nbf; +	int fd, n, err = 0; + +	fd = open(filename, O_RDONLY); +	if (fd < 0) +		return -errno; + +	do { +		if (size == alloc_size) { +			alloc_size += BUFSIZ; +			nbf = realloc(bf, alloc_size); +			if (!nbf) { +				err = -ENOMEM; +				break; +			} + +			bf = nbf; +		} + +		n = read(fd, bf + size, alloc_size - size); +		if (n < 0) { +			if (size) { +				pr_warning("read failed %d: %s\n", +					   errno, strerror(errno)); +				err = 0; +			} else +				err = -errno; + +			break; +		} + +		size += n; +	} while (n > 0); + +	if (!err) { +		*sizep = size; +		*buf   = bf; +	} else +		free(bf); + +	close(fd); +	return err; +} + +const char *get_filename_for_perf_kvm(void) +{ +	const char *filename; + +	if (perf_host && !perf_guest) +		filename = strdup("perf.data.host"); +	else if (!perf_host && perf_guest) +		filename = strdup("perf.data.guest"); +	else +		filename = strdup("perf.data.kvm"); + +	return filename; +} + +int perf_event_paranoid(void) +{ +	char path[PATH_MAX]; +	const char *procfs = procfs__mountpoint(); +	int value; + +	if (!procfs) +		return INT_MAX; + +	scnprintf(path, PATH_MAX, "%s/sys/kernel/perf_event_paranoid", procfs); + +	if (filename__read_int(path, &value)) +		return INT_MAX; + +	return value; +} + +void mem_bswap_32(void *src, int byte_size) +{ +	u32 *m = src; +	while (byte_size > 0) { +		*m = bswap_32(*m); +		byte_size -= sizeof(u32); +		++m; +	} +} + +void mem_bswap_64(void *src, int byte_size) +{ +	u64 *m = src; + +	while (byte_size > 0) { +		*m = bswap_64(*m); +		byte_size -= sizeof(u64); +		++m; +	} +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index a5353594904..66864364ccb 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -69,10 +69,11 @@  #include <sys/ioctl.h>  #include <inttypes.h>  #include <linux/magic.h> -#include "types.h" +#include <linux/types.h>  #include <sys/ttydefaults.h> -#include <lk/debugfs.h> +#include <api/fs/debugfs.h>  #include <termios.h> +#include <linux/bitops.h>  extern const char *graph_line;  extern const char *graph_dotted_line; @@ -128,6 +129,8 @@ void put_tracing_file(char *file);  #endif  #endif +#define PERF_GTK_DSO  "libperf-gtk.so" +  /* General helper functions */  extern void usage(const char *err) NORETURN;  extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); @@ -183,6 +186,8 @@ static inline void *zalloc(size_t size)  	return calloc(1, size);  } +#define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) +  static inline int has_extension(const char *filename, const char *ext)  {  	size_t len = strlen(filename); @@ -241,6 +246,7 @@ static inline int sane_case(int x, int high)  int mkdir_p(char *path, mode_t mode);  int copyfile(const char *from, const char *to); +int copyfile_mode(const char *from, const char *to, mode_t mode);  s64 perf_atoll(const char *str);  char **argv_split(const char *str, int *argcp); @@ -250,7 +256,8 @@ bool strlazymatch(const char *str, const char *pat);  int strtailcmp(const char *s1, const char *s2);  char *strxfrchar(char *s, char from, char to);  unsigned long convert_unit(unsigned long value, char *unit); -int readn(int fd, void *buf, size_t size); +ssize_t readn(int fd, void *buf, size_t n); +ssize_t writen(int fd, void *buf, size_t n);  struct perf_event_attr; @@ -270,6 +277,24 @@ bool is_power_of_2(unsigned long n)  	return (n != 0 && ((n & (n - 1)) == 0));  } +static inline unsigned next_pow2(unsigned x) +{ +	if (!x) +		return 1; +	return 1ULL << (32 - __builtin_clz(x - 1)); +} + +static inline unsigned long next_pow2_l(unsigned long x) +{ +#if BITS_PER_LONG == 64 +	if (x <= (1UL << 31)) +		return next_pow2(x); +	return (unsigned long)next_pow2(x >> 32) << 32; +#else +	return next_pow2(x); +#endif +} +  size_t hex_width(u64 v);  int hex2u64(const char *ptr, u64 *val); @@ -279,6 +304,30 @@ char *rtrim(char *s);  void dump_stack(void);  extern unsigned int page_size; +extern int cacheline_size;  void get_term_dimensions(struct winsize *ws); + +struct parse_tag { +	char tag; +	int mult; +}; + +unsigned long parse_tag_value(const char *str, struct parse_tag *tags); + +#define SRCLINE_UNKNOWN  ((char *) "??:0") + +struct dso; + +char *get_srcline(struct dso *dso, unsigned long addr); +void free_srcline(char *srcline); + +int filename__read_int(const char *filename, int *value); +int filename__read_str(const char *filename, char **buf, size_t *sizep); +int perf_event_paranoid(void); + +void mem_bswap_64(void *src, int byte_size); +void mem_bswap_32(void *src, int byte_size); + +const char *get_filename_for_perf_kvm(void);  #endif /* GIT_COMPAT_UTIL_H */ diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index 697c8b4e59c..0fb3c1fcd3e 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c @@ -31,14 +31,14 @@ void perf_read_values_destroy(struct perf_read_values *values)  		return;  	for (i = 0; i < values->threads; i++) -		free(values->value[i]); -	free(values->value); -	free(values->pid); -	free(values->tid); -	free(values->counterrawid); +		zfree(&values->value[i]); +	zfree(&values->value); +	zfree(&values->pid); +	zfree(&values->tid); +	zfree(&values->counterrawid);  	for (i = 0; i < values->counters; i++) -		free(values->countername[i]); -	free(values->countername); +		zfree(&values->countername[i]); +	zfree(&values->countername);  }  static void perf_read_values__enlarge_threads(struct perf_read_values *values) diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h index 2fa967e1a88..b21a80c6cf8 100644 --- a/tools/perf/util/values.h +++ b/tools/perf/util/values.h @@ -1,7 +1,7 @@  #ifndef __PERF_VALUES_H  #define __PERF_VALUES_H -#include "types.h" +#include <linux/types.h>  struct perf_read_values {  	int threads; diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c index 39159822d58..0ddb3b8a89e 100644 --- a/tools/perf/util/vdso.c +++ b/tools/perf/util/vdso.c @@ -103,7 +103,7 @@ struct dso *vdso__dso_findnew(struct list_head *head)  		dso = dso__new(VDSO__MAP_NAME);  		if (dso != NULL) {  			dsos__add(head, dso); -			dso__set_long_name(dso, file); +			dso__set_long_name(dso, file, false);  		}  	}  | 
