Merge commit 'v2.6.30-rc6' into perfcounters/core
Ingo Molnar [Mon, 18 May 2009 05:37:44 +0000 (07:37 +0200)]
Merge reason: this branch was on an -rc4 base, merge it up to -rc6
              to get the latest upstream fixes.

Signed-off-by: Ingo Molnar <mingo@elte.hu>

112 files changed:
Documentation/perf_counter/.gitignore [new file with mode: 0644]
Documentation/perf_counter/Documentation/perf-help.txt [new file with mode: 0644]
Documentation/perf_counter/Documentation/perf-record.txt [new file with mode: 0644]
Documentation/perf_counter/Documentation/perf-stat.txt [new file with mode: 0644]
Documentation/perf_counter/Documentation/perf-top.txt [new file with mode: 0644]
Documentation/perf_counter/Makefile [new file with mode: 0644]
Documentation/perf_counter/builtin-help.c [new file with mode: 0644]
Documentation/perf_counter/builtin-record.c [new file with mode: 0644]
Documentation/perf_counter/builtin-stat.c [new file with mode: 0644]
Documentation/perf_counter/builtin-top.c [new file with mode: 0644]
Documentation/perf_counter/builtin.h [new file with mode: 0644]
Documentation/perf_counter/command-list.txt [new file with mode: 0644]
Documentation/perf_counter/design.txt [new file with mode: 0644]
Documentation/perf_counter/perf-report.cc [new file with mode: 0644]
Documentation/perf_counter/perf.c [new file with mode: 0644]
Documentation/perf_counter/perf.h [new file with mode: 0644]
Documentation/perf_counter/util/PERF-VERSION-GEN [new file with mode: 0755]
Documentation/perf_counter/util/abspath.c [new file with mode: 0644]
Documentation/perf_counter/util/alias.c [new file with mode: 0644]
Documentation/perf_counter/util/cache.h [new file with mode: 0644]
Documentation/perf_counter/util/config.c [new file with mode: 0644]
Documentation/perf_counter/util/ctype.c [new file with mode: 0644]
Documentation/perf_counter/util/exec_cmd.c [new file with mode: 0644]
Documentation/perf_counter/util/exec_cmd.h [new file with mode: 0644]
Documentation/perf_counter/util/generate-cmdlist.sh [new file with mode: 0755]
Documentation/perf_counter/util/help.c [new file with mode: 0644]
Documentation/perf_counter/util/help.h [new file with mode: 0644]
Documentation/perf_counter/util/levenshtein.c [new file with mode: 0644]
Documentation/perf_counter/util/levenshtein.h [new file with mode: 0644]
Documentation/perf_counter/util/parse-options.c [new file with mode: 0644]
Documentation/perf_counter/util/parse-options.h [new file with mode: 0644]
Documentation/perf_counter/util/path.c [new file with mode: 0644]
Documentation/perf_counter/util/quote.c [new file with mode: 0644]
Documentation/perf_counter/util/quote.h [new file with mode: 0644]
Documentation/perf_counter/util/run-command.c [new file with mode: 0644]
Documentation/perf_counter/util/run-command.h [new file with mode: 0644]
Documentation/perf_counter/util/strbuf.c [new file with mode: 0644]
Documentation/perf_counter/util/strbuf.h [new file with mode: 0644]
Documentation/perf_counter/util/usage.c [new file with mode: 0644]
Documentation/perf_counter/util/util.h [new file with mode: 0644]
Documentation/perf_counter/util/wrapper.c [new file with mode: 0644]
MAINTAINERS
arch/powerpc/include/asm/hw_irq.h
arch/powerpc/include/asm/paca.h
arch/powerpc/include/asm/perf_counter.h [new file with mode: 0644]
arch/powerpc/include/asm/reg.h
arch/powerpc/include/asm/systbl.h
arch/powerpc/include/asm/unistd.h
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/entry_64.S
arch/powerpc/kernel/irq.c
arch/powerpc/kernel/perf_counter.c [new file with mode: 0644]
arch/powerpc/kernel/power4-pmu.c [new file with mode: 0644]
arch/powerpc/kernel/power5+-pmu.c [new file with mode: 0644]
arch/powerpc/kernel/power5-pmu.c [new file with mode: 0644]
arch/powerpc/kernel/power6-pmu.c [new file with mode: 0644]
arch/powerpc/kernel/ppc970-pmu.c [new file with mode: 0644]
arch/powerpc/mm/fault.c
arch/powerpc/platforms/Kconfig.cputype
arch/x86/Kconfig
arch/x86/ia32/ia32entry.S
arch/x86/include/asm/atomic_32.h
arch/x86/include/asm/entry_arch.h
arch/x86/include/asm/hardirq.h
arch/x86/include/asm/hw_irq.h
arch/x86/include/asm/intel_arch_perfmon.h [deleted file]
arch/x86/include/asm/irq_vectors.h
arch/x86/include/asm/perf_counter.h [new file with mode: 0644]
arch/x86/include/asm/unistd_32.h
arch/x86/include/asm/unistd_64.h
arch/x86/kernel/apic/apic.c
arch/x86/kernel/cpu/Makefile
arch/x86/kernel/cpu/common.c
arch/x86/kernel/cpu/perf_counter.c [new file with mode: 0644]
arch/x86/kernel/cpu/perfctr-watchdog.c
arch/x86/kernel/entry_64.S
arch/x86/kernel/irq.c
arch/x86/kernel/irqinit_32.c
arch/x86/kernel/irqinit_64.c
arch/x86/kernel/signal.c
arch/x86/kernel/syscall_table_32.S
arch/x86/kernel/traps.c
arch/x86/mm/fault.c
arch/x86/oprofile/nmi_int.c
arch/x86/oprofile/op_model_ppro.c
drivers/char/sysrq.c
fs/exec.c
include/linux/compat.h
include/linux/init_task.h
include/linux/kernel_stat.h
include/linux/mutex.h
include/linux/perf_counter.h [new file with mode: 0644]
include/linux/prctl.h
include/linux/sched.h
include/linux/signal.h
include/linux/syscalls.h
init/Kconfig
kernel/Makefile
kernel/compat.c
kernel/exit.c
kernel/fork.c
kernel/mutex.c
kernel/perf_counter.c [new file with mode: 0644]
kernel/rtmutex.c
kernel/sched.c
kernel/signal.c
kernel/sys.c
kernel/sys_ni.c
kernel/sysctl.c
kernel/timer.c
mm/mmap.c

diff --git a/Documentation/perf_counter/.gitignore b/Documentation/perf_counter/.gitignore
new file mode 100644 (file)
index 0000000..41c0b20
--- /dev/null
@@ -0,0 +1,179 @@
+GIT-BUILD-OPTIONS
+GIT-CFLAGS
+GIT-GUI-VARS
+GIT-VERSION-FILE
+git
+git-add
+git-add--interactive
+git-am
+git-annotate
+git-apply
+git-archimport
+git-archive
+git-bisect
+git-bisect--helper
+git-blame
+git-branch
+git-bundle
+git-cat-file
+git-check-attr
+git-check-ref-format
+git-checkout
+git-checkout-index
+git-cherry
+git-cherry-pick
+git-clean
+git-clone
+git-commit
+git-commit-tree
+git-config
+git-count-objects
+git-cvsexportcommit
+git-cvsimport
+git-cvsserver
+git-daemon
+git-diff
+git-diff-files
+git-diff-index
+git-diff-tree
+git-difftool
+git-difftool--helper
+git-describe
+git-fast-export
+git-fast-import
+git-fetch
+git-fetch--tool
+git-fetch-pack
+git-filter-branch
+git-fmt-merge-msg
+git-for-each-ref
+git-format-patch
+git-fsck
+git-fsck-objects
+git-gc
+git-get-tar-commit-id
+git-grep
+git-hash-object
+git-help
+git-http-fetch
+git-http-push
+git-imap-send
+git-index-pack
+git-init
+git-init-db
+git-instaweb
+git-log
+git-lost-found
+git-ls-files
+git-ls-remote
+git-ls-tree
+git-mailinfo
+git-mailsplit
+git-merge
+git-merge-base
+git-merge-index
+git-merge-file
+git-merge-tree
+git-merge-octopus
+git-merge-one-file
+git-merge-ours
+git-merge-recursive
+git-merge-resolve
+git-merge-subtree
+git-mergetool
+git-mergetool--lib
+git-mktag
+git-mktree
+git-name-rev
+git-mv
+git-pack-redundant
+git-pack-objects
+git-pack-refs
+git-parse-remote
+git-patch-id
+git-peek-remote
+git-prune
+git-prune-packed
+git-pull
+git-push
+git-quiltimport
+git-read-tree
+git-rebase
+git-rebase--interactive
+git-receive-pack
+git-reflog
+git-relink
+git-remote
+git-repack
+git-repo-config
+git-request-pull
+git-rerere
+git-reset
+git-rev-list
+git-rev-parse
+git-revert
+git-rm
+git-send-email
+git-send-pack
+git-sh-setup
+git-shell
+git-shortlog
+git-show
+git-show-branch
+git-show-index
+git-show-ref
+git-stage
+git-stash
+git-status
+git-stripspace
+git-submodule
+git-svn
+git-symbolic-ref
+git-tag
+git-tar-tree
+git-unpack-file
+git-unpack-objects
+git-update-index
+git-update-ref
+git-update-server-info
+git-upload-archive
+git-upload-pack
+git-var
+git-verify-pack
+git-verify-tag
+git-web--browse
+git-whatchanged
+git-write-tree
+git-core-*/?*
+gitk-wish
+gitweb/gitweb.cgi
+test-chmtime
+test-ctype
+test-date
+test-delta
+test-dump-cache-tree
+test-genrandom
+test-match-trees
+test-parse-options
+test-path-utils
+test-sha1
+test-sigchain
+common-cmds.h
+*.tar.gz
+*.dsc
+*.deb
+git.spec
+*.exe
+*.[aos]
+*.py[co]
+config.mak
+autom4te.cache
+config.cache
+config.log
+config.status
+config.mak.autogen
+config.mak.append
+configure
+tags
+TAGS
+cscope*
diff --git a/Documentation/perf_counter/Documentation/perf-help.txt b/Documentation/perf_counter/Documentation/perf-help.txt
new file mode 100644 (file)
index 0000000..f85fed5
--- /dev/null
@@ -0,0 +1,38 @@
+perf-help(1)
+===========
+
+NAME
+----
+perf-help - display help information about perf
+
+SYNOPSIS
+--------
+'perf help' [-a|--all] [COMMAND]
+
+DESCRIPTION
+-----------
+
+With no options and no COMMAND given, the synopsis of the 'perf'
+command and a list of the most commonly used perf commands are printed
+on the standard output.
+
+If the option '--all' or '-a' is given, then all available commands are
+printed on the standard output.
+
+If a perf command is named, a manual page for that command is brought
+up. The 'man' program is used by default for this purpose, but this
+can be overridden by other options or configuration variables.
+
+Note that `perf --help ...` is identical to `perf help ...` because the
+former is internally converted into the latter.
+
+OPTIONS
+-------
+-a::
+--all::
+       Prints all the available commands on the standard output. This
+       option supersedes any other option.
+
+PERF
+----
+Part of the linkperf:perf[1] suite
diff --git a/Documentation/perf_counter/Documentation/perf-record.txt b/Documentation/perf_counter/Documentation/perf-record.txt
new file mode 100644 (file)
index 0000000..d07700e
--- /dev/null
@@ -0,0 +1,63 @@
+perf-record(1)
+==========
+
+NAME
+----
+perf-record - Run a command and record its profile into output.perf
+
+SYNOPSIS
+--------
+[verse]
+'perf record' [-e <EVENT> | --event=EVENT] [-l] [-a] <command>
+
+DESCRIPTION
+-----------
+This command runs a command and gathers a performance counter profile
+from it, into output.perf - without displaying anything.
+
+This file can then be inspected later on, using 'perf report'.
+
+
+OPTIONS
+-------
+<command>...::
+       Any command you can specify in a shell.
+
+-e::
+--event=::
+                             0:0: cpu-cycles          
+                             0:0: cycles              
+                             0:1: instructions        
+                             0:2: cache-references    
+                             0:3: cache-misses        
+                             0:4: branch-instructions 
+                             0:4: branches            
+                             0:5: branch-misses       
+                             0:6: bus-cycles          
+                             1:0: cpu-clock           
+                             1:1: task-clock          
+                             1:2: page-faults         
+                             1:2: faults              
+                             1:5: minor-faults        
+                             1:6: major-faults        
+                             1:3: context-switches    
+                             1:3: cs                  
+                             1:4: cpu-migrations      
+                             1:4: migrations          
+                           rNNN: raw PMU events (eventsel+umask)
+
+-a::
+        system-wide collection
+
+-l::
+        scale counter values
+
+Configuration
+-------------
+
+EXAMPLES
+--------
+
+SEE ALSO
+--------
+linkperf:git-stat[1]
diff --git a/Documentation/perf_counter/Documentation/perf-stat.txt b/Documentation/perf_counter/Documentation/perf-stat.txt
new file mode 100644 (file)
index 0000000..7fcab27
--- /dev/null
@@ -0,0 +1,76 @@
+perf-stat(1)
+==========
+
+NAME
+----
+perf-stat - Run a command and gather performance counter statistics
+
+SYNOPSIS
+--------
+[verse]
+'perf stat' [-e <EVENT> | --event=EVENT] [-l] [-a] <command>
+
+DESCRIPTION
+-----------
+This command runs a command and gathers performance counter statistics
+from it.
+
+
+OPTIONS
+-------
+<command>...::
+       Any command you can specify in a shell.
+
+-e::
+--event=::
+                             0:0: cpu-cycles          
+                             0:0: cycles              
+                             0:1: instructions        
+                             0:2: cache-references    
+                             0:3: cache-misses        
+                             0:4: branch-instructions 
+                             0:4: branches            
+                             0:5: branch-misses       
+                             0:6: bus-cycles          
+                             1:0: cpu-clock           
+                             1:1: task-clock          
+                             1:2: page-faults         
+                             1:2: faults              
+                             1:5: minor-faults        
+                             1:6: major-faults        
+                             1:3: context-switches    
+                             1:3: cs                  
+                             1:4: cpu-migrations      
+                             1:4: migrations          
+                           rNNN: raw PMU events (eventsel+umask)
+
+-a::
+        system-wide collection
+
+-l::
+        scale counter values
+
+Configuration
+-------------
+
+EXAMPLES
+--------
+
+$ perf stat sleep 1
+
+ Performance counter stats for 'sleep':
+
+       0.678356  task clock ticks     (msecs)
+              7  context switches     (events)
+              4  CPU migrations       (events)
+            232  pagefaults           (events)
+        1810403  CPU cycles           (events)
+         946759  instructions         (events)
+          18952  cache references     (events)
+           4885  cache misses         (events)
+
+ Wall-clock time elapsed:  1001.252894 msecs
+
+SEE ALSO
+--------
+linkperf:git-tops[1]
diff --git a/Documentation/perf_counter/Documentation/perf-top.txt b/Documentation/perf_counter/Documentation/perf-top.txt
new file mode 100644 (file)
index 0000000..057333b
--- /dev/null
@@ -0,0 +1,61 @@
+perf-top(1)
+==========
+
+NAME
+----
+perf-top - Run a command and profile it
+
+SYNOPSIS
+--------
+[verse]
+'perf top' [-e <EVENT> | --event=EVENT] [-l] [-a] <command>
+
+DESCRIPTION
+-----------
+This command runs a command and gathers a performance counter profile
+from it.
+
+
+OPTIONS
+-------
+<command>...::
+       Any command you can specify in a shell.
+
+-e::
+--event=::
+                             0:0: cpu-cycles          
+                             0:0: cycles              
+                             0:1: instructions        
+                             0:2: cache-references    
+                             0:3: cache-misses        
+                             0:4: branch-instructions 
+                             0:4: branches            
+                             0:5: branch-misses       
+                             0:6: bus-cycles          
+                             1:0: cpu-clock           
+                             1:1: task-clock          
+                             1:2: page-faults         
+                             1:2: faults              
+                             1:5: minor-faults        
+                             1:6: major-faults        
+                             1:3: context-switches    
+                             1:3: cs                  
+                             1:4: cpu-migrations      
+                             1:4: migrations          
+                           rNNN: raw PMU events (eventsel+umask)
+
+-a::
+        system-wide collection
+
+-l::
+        scale counter values
+
+Configuration
+-------------
+
+EXAMPLES
+--------
+
+SEE ALSO
+--------
+linkperf:git-stat[1]
diff --git a/Documentation/perf_counter/Makefile b/Documentation/perf_counter/Makefile
new file mode 100644 (file)
index 0000000..481e4c2
--- /dev/null
@@ -0,0 +1,849 @@
+# The default target of this Makefile is...
+all::
+
+# Define V=1 to have a more verbose compile.
+#
+# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
+# or vsnprintf() return -1 instead of number of characters which would
+# have been written to the final string if enough space had been available.
+#
+# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
+# when attempting to read from an fopen'ed directory.
+#
+# Define NO_OPENSSL environment variable if you do not have OpenSSL.
+# This also implies MOZILLA_SHA1.
+#
+# Define CURLDIR=/foo/bar if your curl header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
+# Define EXPATDIR=/foo/bar if your expat header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
+# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
+#
+# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
+# d_type in struct dirent (latest Cygwin -- will be fixed soonish).
+#
+# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
+# do not support the 'size specifiers' introduced by C99, namely ll, hh,
+# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).
+# some C compilers supported these specifiers prior to C99 as an extension.
+#
+# Define NO_STRCASESTR if you don't have strcasestr.
+#
+# Define NO_MEMMEM if you don't have memmem.
+#
+# Define NO_STRTOUMAX if you don't have strtoumax in the C library.
+# If your compiler also does not support long long or does not have
+# strtoull, define NO_STRTOULL.
+#
+# Define NO_SETENV if you don't have setenv in the C library.
+#
+# Define NO_UNSETENV if you don't have unsetenv in the C library.
+#
+# Define NO_MKDTEMP if you don't have mkdtemp in the C library.
+#
+# Define NO_SYS_SELECT_H if you don't have sys/select.h.
+#
+# Define NO_SYMLINK_HEAD if you never want .perf/HEAD to be a symbolic link.
+# Enable it on Windows.  By default, symrefs are still used.
+#
+# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability
+# tests.  These tests take up a significant amount of the total test time
+# but are not needed unless you plan to talk to SVN repos.
+#
+# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink
+# installed in /sw, but don't want PERF to link against any libraries
+# installed there.  If defined you may specify your own (or Fink's)
+# include directories and library directories by defining CFLAGS
+# and LDFLAGS appropriately.
+#
+# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,
+# have DarwinPorts installed in /opt/local, but don't want PERF to
+# link against any libraries installed there.  If defined you may
+# specify your own (or DarwinPort's) include directories and
+# library directories by defining CFLAGS and LDFLAGS appropriately.
+#
+# Define PPC_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for PowerPC.
+#
+# Define ARM_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for ARM.
+#
+# Define MOZILLA_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
+# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
+# choice) has very fast version optimized for i586.
+#
+# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
+#
+# Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
+#
+# Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
+# Patrick Mauritz).
+#
+# Define NO_MMAP if you want to avoid mmap.
+#
+# Define NO_PTHREADS if you do not have or do not want to use Pthreads.
+#
+# Define NO_PREAD if you have a problem with pread() system call (e.g.
+# cygwin.dll before v1.5.22).
+#
+# Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is
+# generally faster on your platform than accessing the working directory.
+#
+# Define NO_TRUSTABLE_FILEMODE if your filesystem may claim to support
+# the executable mode bit, but doesn't really do so.
+#
+# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
+#
+# Define NO_SOCKADDR_STORAGE if your platform does not have struct
+# sockaddr_storage.
+#
+# Define NO_ICONV if your libc does not properly support iconv.
+#
+# Define OLD_ICONV if your library has an old iconv(), where the second
+# (input buffer pointer) parameter is declared with type (const char **).
+#
+# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
+#
+# Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib"
+# that tells runtime paths to dynamic libraries;
+# "-Wl,-rpath=/path/lib" is used instead.
+#
+# Define USE_NSEC below if you want perf to care about sub-second file mtimes
+# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
+# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
+# randomly break unless your underlying filesystem supports those sub-second
+# times (my ext3 doesn't).
+#
+# Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
+# "st_ctim"
+#
+# Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec"
+# available.  This automatically turns USE_NSEC off.
+#
+# Define USE_STDEV below if you want perf to care about the underlying device
+# change being considered an inode change from the update-index perspective.
+#
+# Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
+# field that counts the on-disk footprint in 512-byte blocks.
+#
+# 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 NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
+# MakeMaker (e.g. using ActiveState under Cygwin).
+#
+# Define NO_PERL if you do not want Perl scripts or libraries at all.
+#
+# Define INTERNAL_QSORT to use Git's implementation of qsort(), which
+# is a simplified version of the merge sort used in glibc. This is
+# recommended if Git triggers O(n^2) behavior in your platform's qsort().
+#
+# Define NO_EXTERNAL_GREP if you don't want "perf grep" to ever call
+# your external grep (e.g., if your system lacks grep, if its grep is
+# broken, or spawning external process is slower than built-in grep perf has).
+
+PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
+       @$(SHELL_PATH) util/PERF-VERSION-GEN
+-include PERF-VERSION-FILE
+
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
+uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
+uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
+uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
+uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
+uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
+
+# CFLAGS and LDFLAGS are for the users to override from the command line.
+
+CFLAGS = -g -O2 -Wall
+LDFLAGS = -lpthread -lrt
+ALL_CFLAGS = $(CFLAGS)
+ALL_LDFLAGS = $(LDFLAGS)
+STRIP ?= strip
+
+# Among the variables below, these:
+#   perfexecdir
+#   template_dir
+#   mandir
+#   infodir
+#   htmldir
+#   ETC_PERFCONFIG (but not sysconfdir)
+# can be specified as a relative path some/where/else;
+# this is interpreted as relative to $(prefix) and "perf" at
+# runtime figures out where they are based on the path to the executable.
+# This can help installing the suite in a relocatable way.
+
+prefix = $(HOME)
+bindir_relative = bin
+bindir = $(prefix)/$(bindir_relative)
+mandir = share/man
+infodir = share/info
+perfexecdir = libexec/perf-core
+sharedir = $(prefix)/share
+template_dir = share/perf-core/templates
+htmldir = share/doc/perf-doc
+ifeq ($(prefix),/usr)
+sysconfdir = /etc
+ETC_PERFCONFIG = $(sysconfdir)/perfconfig
+else
+sysconfdir = $(prefix)/etc
+ETC_PERFCONFIG = etc/perfconfig
+endif
+lib = lib
+# DESTDIR=
+
+export prefix bindir sharedir sysconfdir
+
+CC = gcc
+AR = ar
+RM = rm -f
+TAR = tar
+FIND = find
+INSTALL = install
+RPMBUILD = rpmbuild
+PTHREAD_LIBS = -lpthread
+
+# 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__
+
+
+
+### --- END CONFIGURATION SECTION ---
+
+# Those must not be GNU-specific; they are shared with perl/ which may
+# be built by a different compiler. (Note that this is an artifact now
+# but it still might be nice to keep that distinction.)
+BASIC_CFLAGS =
+BASIC_LDFLAGS =
+
+# Guard against environment variables
+BUILTIN_OBJS =
+BUILT_INS =
+COMPAT_CFLAGS =
+COMPAT_OBJS =
+LIB_H =
+LIB_OBJS =
+PROGRAMS = perf-report
+SCRIPT_PERL =
+SCRIPT_SH =
+TEST_PROGRAMS =
+
+#
+# No scripts right now:
+#
+
+# SCRIPT_SH += perf-am.sh
+
+#
+# No Perl scripts right now:
+#
+
+# SCRIPT_PERL += perf-add--interactive.perl
+
+SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
+         $(patsubst %.perl,%,$(SCRIPT_PERL))
+
+# Empty...
+EXTRA_PROGRAMS =
+
+# ... and all the rest that could be moved out of bindir to perfexecdir
+PROGRAMS += $(EXTRA_PROGRAMS)
+
+#
+# Single 'perf' binary right now:
+#
+PROGRAMS += perf
+
+# List built-in command $C whose implementation cmd_$C() is not in
+# builtin-$C.o but is linked in as part of some other command.
+BUILT_INS += $(patsubst builtin-%.o,perf-%$X,$(BUILTIN_OBJS))
+
+#
+# None right now:
+#
+# BUILT_INS += perf-init $X
+
+# 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 = perf$X
+
+# 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
+
+LIB_FILE=libperf.a
+
+LIB_H += ../../include/linux/perf_counter.h
+LIB_H += perf.h
+LIB_H += util/levenshtein.h
+LIB_H += util/parse-options.h
+LIB_H += util/quote.h
+LIB_H += util/util.h
+LIB_H += util/help.h
+LIB_H += util/strbuf.h
+LIB_H += util/run-command.h
+
+LIB_OBJS += util/abspath.o
+LIB_OBJS += util/alias.o
+LIB_OBJS += util/config.o
+LIB_OBJS += util/ctype.o
+LIB_OBJS += util/exec_cmd.o
+LIB_OBJS += util/help.o
+LIB_OBJS += util/levenshtein.o
+LIB_OBJS += util/parse-options.o
+LIB_OBJS += util/path.o
+LIB_OBJS += util/run-command.o
+LIB_OBJS += util/quote.o
+LIB_OBJS += util/strbuf.o
+LIB_OBJS += util/usage.o
+LIB_OBJS += util/wrapper.o
+
+BUILTIN_OBJS += builtin-help.o
+BUILTIN_OBJS += builtin-record.o
+BUILTIN_OBJS += builtin-stat.o
+BUILTIN_OBJS += builtin-top.o
+
+PERFLIBS = $(LIB_FILE)
+EXTLIBS =
+
+#
+# Platform specific tweaks
+#
+
+# 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 config.mak.autogen
+-include config.mak
+
+ifeq ($(uname_S),Darwin)
+       ifndef NO_FINK
+               ifeq ($(shell test -d /sw/lib && echo y),y)
+                       BASIC_CFLAGS += -I/sw/include
+                       BASIC_LDFLAGS += -L/sw/lib
+               endif
+       endif
+       ifndef NO_DARWIN_PORTS
+               ifeq ($(shell test -d /opt/local/lib && echo y),y)
+                       BASIC_CFLAGS += -I/opt/local/include
+                       BASIC_LDFLAGS += -L/opt/local/lib
+               endif
+       endif
+       PTHREAD_LIBS =
+endif
+
+ifndef CC_LD_DYNPATH
+       ifdef NO_R_TO_GCC_LINKER
+               # Some gcc does not accept and pass -R to the linker to specify
+               # the runtime dynamic library path.
+               CC_LD_DYNPATH = -Wl,-rpath,
+       else
+               CC_LD_DYNPATH = -R
+       endif
+endif
+
+ifdef ZLIB_PATH
+       BASIC_CFLAGS += -I$(ZLIB_PATH)/include
+       EXTLIBS += -L$(ZLIB_PATH)/$(lib) $(CC_LD_DYNPATH)$(ZLIB_PATH)/$(lib)
+endif
+EXTLIBS += -lz
+
+ifdef NEEDS_SOCKET
+       EXTLIBS += -lsocket
+endif
+ifdef NEEDS_NSL
+       EXTLIBS += -lnsl
+endif
+ifdef NO_D_TYPE_IN_DIRENT
+       BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT
+endif
+ifdef NO_D_INO_IN_DIRENT
+       BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
+endif
+ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
+       BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
+endif
+ifdef USE_NSEC
+       BASIC_CFLAGS += -DUSE_NSEC
+endif
+ifdef USE_ST_TIMESPEC
+       BASIC_CFLAGS += -DUSE_ST_TIMESPEC
+endif
+ifdef NO_NSEC
+       BASIC_CFLAGS += -DNO_NSEC
+endif
+ifdef NO_C99_FORMAT
+       BASIC_CFLAGS += -DNO_C99_FORMAT
+endif
+ifdef SNPRINTF_RETURNS_BOGUS
+       COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS
+       COMPAT_OBJS += compat/snprintf.o
+endif
+ifdef FREAD_READS_DIRECTORIES
+       COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
+       COMPAT_OBJS += compat/fopen.o
+endif
+ifdef NO_SYMLINK_HEAD
+       BASIC_CFLAGS += -DNO_SYMLINK_HEAD
+endif
+ifdef NO_STRCASESTR
+       COMPAT_CFLAGS += -DNO_STRCASESTR
+       COMPAT_OBJS += compat/strcasestr.o
+endif
+ifdef NO_STRTOUMAX
+       COMPAT_CFLAGS += -DNO_STRTOUMAX
+       COMPAT_OBJS += compat/strtoumax.o
+endif
+ifdef NO_STRTOULL
+       COMPAT_CFLAGS += -DNO_STRTOULL
+endif
+ifdef NO_SETENV
+       COMPAT_CFLAGS += -DNO_SETENV
+       COMPAT_OBJS += compat/setenv.o
+endif
+ifdef NO_MKDTEMP
+       COMPAT_CFLAGS += -DNO_MKDTEMP
+       COMPAT_OBJS += compat/mkdtemp.o
+endif
+ifdef NO_UNSETENV
+       COMPAT_CFLAGS += -DNO_UNSETENV
+       COMPAT_OBJS += compat/unsetenv.o
+endif
+ifdef NO_SYS_SELECT_H
+       BASIC_CFLAGS += -DNO_SYS_SELECT_H
+endif
+ifdef NO_MMAP
+       COMPAT_CFLAGS += -DNO_MMAP
+       COMPAT_OBJS += compat/mmap.o
+else
+       ifdef USE_WIN32_MMAP
+               COMPAT_CFLAGS += -DUSE_WIN32_MMAP
+               COMPAT_OBJS += compat/win32mmap.o
+       endif
+endif
+ifdef NO_PREAD
+       COMPAT_CFLAGS += -DNO_PREAD
+       COMPAT_OBJS += compat/pread.o
+endif
+ifdef NO_FAST_WORKING_DIRECTORY
+       BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
+endif
+ifdef NO_TRUSTABLE_FILEMODE
+       BASIC_CFLAGS += -DNO_TRUSTABLE_FILEMODE
+endif
+ifdef NO_IPV6
+       BASIC_CFLAGS += -DNO_IPV6
+endif
+ifdef NO_UINTMAX_T
+       BASIC_CFLAGS += -Duintmax_t=uint32_t
+endif
+ifdef NO_SOCKADDR_STORAGE
+ifdef NO_IPV6
+       BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in
+else
+       BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in6
+endif
+endif
+ifdef NO_INET_NTOP
+       LIB_OBJS += compat/inet_ntop.o
+endif
+ifdef NO_INET_PTON
+       LIB_OBJS += compat/inet_pton.o
+endif
+
+ifdef NO_ICONV
+       BASIC_CFLAGS += -DNO_ICONV
+endif
+
+ifdef OLD_ICONV
+       BASIC_CFLAGS += -DOLD_ICONV
+endif
+
+ifdef NO_DEFLATE_BOUND
+       BASIC_CFLAGS += -DNO_DEFLATE_BOUND
+endif
+
+ifdef PPC_SHA1
+       SHA1_HEADER = "ppc/sha1.h"
+       LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
+else
+ifdef ARM_SHA1
+       SHA1_HEADER = "arm/sha1.h"
+       LIB_OBJS += arm/sha1.o arm/sha1_arm.o
+else
+ifdef MOZILLA_SHA1
+       SHA1_HEADER = "mozilla-sha1/sha1.h"
+       LIB_OBJS += mozilla-sha1/sha1.o
+else
+       SHA1_HEADER = <openssl/sha.h>
+       EXTLIBS += $(LIB_4_CRYPTO)
+endif
+endif
+endif
+ifdef NO_PERL_MAKEMAKER
+       export NO_PERL_MAKEMAKER
+endif
+ifdef NO_HSTRERROR
+       COMPAT_CFLAGS += -DNO_HSTRERROR
+       COMPAT_OBJS += compat/hstrerror.o
+endif
+ifdef NO_MEMMEM
+       COMPAT_CFLAGS += -DNO_MEMMEM
+       COMPAT_OBJS += compat/memmem.o
+endif
+ifdef INTERNAL_QSORT
+       COMPAT_CFLAGS += -DINTERNAL_QSORT
+       COMPAT_OBJS += compat/qsort.o
+endif
+ifdef RUNTIME_PREFIX
+       COMPAT_CFLAGS += -DRUNTIME_PREFIX
+endif
+
+ifdef DIR_HAS_BSD_GROUP_SEMANTICS
+       COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
+endif
+ifdef NO_EXTERNAL_GREP
+       BASIC_CFLAGS += -DNO_EXTERNAL_GREP
+endif
+
+ifeq ($(PERL_PATH),)
+NO_PERL=NoThanks
+endif
+
+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1  =
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+       QUIET_CC       = @echo '   ' CC $@;
+       QUIET_AR       = @echo '   ' AR $@;
+       QUIET_LINK     = @echo '   ' LINK $@;
+       QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
+       QUIET_GEN      = @echo '   ' GEN $@;
+       QUIET_SUBDIR0  = +@subdir=
+       QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
+                        $(MAKE) $(PRINT_DIR) -C $$subdir
+       export V
+       export QUIET_GEN
+       export QUIET_BUILT_IN
+endif
+endif
+
+ifdef ASCIIDOC8
+       export ASCIIDOC8
+endif
+
+# Shell quote (do not use $(call) to accommodate ancient setups);
+
+SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
+ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG))
+
+DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
+bindir_SQ = $(subst ','\'',$(bindir))
+bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
+mandir_SQ = $(subst ','\'',$(mandir))
+infodir_SQ = $(subst ','\'',$(infodir))
+perfexecdir_SQ = $(subst ','\'',$(perfexecdir))
+template_dir_SQ = $(subst ','\'',$(template_dir))
+htmldir_SQ = $(subst ','\'',$(htmldir))
+prefix_SQ = $(subst ','\'',$(prefix))
+
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+
+LIBS = $(PERFLIBS) $(EXTLIBS)
+
+BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
+       $(COMPAT_CFLAGS)
+LIB_OBJS += $(COMPAT_OBJS)
+
+ALL_CFLAGS += $(BASIC_CFLAGS)
+ALL_LDFLAGS += $(BASIC_LDFLAGS)
+
+export TAR INSTALL DESTDIR SHELL_PATH
+
+
+### Build rules
+
+SHELL = $(SHELL_PATH)
+
+all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) PERF-BUILD-OPTIONS
+ifneq (,$X)
+       $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';)
+endif
+
+all::
+
+please_set_SHELL_PATH_to_a_more_modern_shell:
+       @$$(:)
+
+shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
+
+strip: $(PROGRAMS) perf$X
+       $(STRIP) $(STRIP_OPTS) $(PROGRAMS) perf$X
+
+perf.o: perf.c common-cmds.h PERF-CFLAGS
+       $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \
+               '-DPERF_HTML_PATH="$(htmldir_SQ)"' \
+               $(ALL_CFLAGS) -c $(filter %.c,$^)
+
+perf$X: perf.o $(BUILTIN_OBJS) $(PERFLIBS)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ perf.o \
+               $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
+
+builtin-help.o: builtin-help.c common-cmds.h PERF-CFLAGS
+       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
+               '-DPERF_HTML_PATH="$(htmldir_SQ)"' \
+               '-DPERF_MAN_PATH="$(mandir_SQ)"' \
+               '-DPERF_INFO_PATH="$(infodir_SQ)"' $<
+
+$(BUILT_INS): perf$X
+       $(QUIET_BUILT_IN)$(RM) $@ && \
+       ln perf$X $@ 2>/dev/null || \
+       ln -s perf$X $@ 2>/dev/null || \
+       cp perf$X $@
+
+common-cmds.h: util/generate-cmdlist.sh command-list.txt
+
+common-cmds.h: $(wildcard Documentation/perf-*.txt)
+       $(QUIET_GEN)util/generate-cmdlist.sh > $@+ && mv $@+ $@
+
+$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
+       $(QUIET_GEN)$(RM) $@ $@+ && \
+       sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+           -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
+           -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
+           -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \
+           -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
+           $@.sh >$@+ && \
+       chmod +x $@+ && \
+       mv $@+ $@
+
+configure: configure.ac
+       $(QUIET_GEN)$(RM) $@ $<+ && \
+       sed -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \
+           $< > $<+ && \
+       autoconf -o $@ $<+ && \
+       $(RM) $<+
+
+# These can record PERF_VERSION
+perf.o perf.spec \
+       $(patsubst %.sh,%,$(SCRIPT_SH)) \
+       $(patsubst %.perl,%,$(SCRIPT_PERL)) \
+       : PERF-VERSION-FILE
+
+%.o: %.c PERF-CFLAGS
+       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
+%.s: %.c PERF-CFLAGS
+       $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
+%.o: %.S
+       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
+
+util/exec_cmd.o: util/exec_cmd.c PERF-CFLAGS
+       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
+               '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \
+               '-DBINDIR="$(bindir_relative_SQ)"' \
+               '-DPREFIX="$(prefix_SQ)"' \
+               $<
+
+builtin-init-db.o: builtin-init-db.c PERF-CFLAGS
+       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
+
+util/config.o: util/config.c PERF-CFLAGS
+       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
+
+perf-%$X: %.o $(PERFLIBS)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+
+$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
+$(patsubst perf-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
+builtin-revert.o wt-status.o: wt-status.h
+
+$(LIB_FILE): $(LIB_OBJS)
+       $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
+
+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 ','\'',$(ALL_CFLAGS)):\
+             $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
+
+PERF-CFLAGS: .FORCE-PERF-CFLAGS
+       @FLAGS='$(TRACK_CFLAGS)'; \
+           if test x"$$FLAGS" != x"`cat PERF-CFLAGS 2>/dev/null`" ; then \
+               echo 1>&2 "    * new build flags or prefix"; \
+               echo "$$FLAGS" >PERF-CFLAGS; \
+            fi
+
+# We need to apply sq twice, once to protect from the shell
+# that runs PERF-BUILD-OPTIONS, and then again to protect it
+# and the first level quoting from the shell that runs "echo".
+PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS
+       @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
+       @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
+       @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
+       @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
+
+### Testing rules
+
+#
+# None right now:
+#
+# TEST_PROGRAMS += test-something$X
+
+all:: $(TEST_PROGRAMS)
+
+# GNU make supports exporting all variables by "export" without parameters.
+# However, the environment gets quite big, and some programs have problems
+# with that.
+
+export NO_SVN_TESTS
+
+check: common-cmds.h
+       if sparse; \
+       then \
+               for i in *.c */*.c; \
+               do \
+                       sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; \
+               done; \
+       else \
+               echo 2>&1 "Did you mean 'make test'?"; \
+               exit 1; \
+       fi
+
+remove-dashes:
+       ./fixup-builtins $(BUILT_INS) $(PROGRAMS) $(SCRIPTS)
+
+### Installation rules
+
+ifneq ($(filter /%,$(firstword $(template_dir))),)
+template_instdir = $(template_dir)
+else
+template_instdir = $(prefix)/$(template_dir)
+endif
+export template_instdir
+
+ifneq ($(filter /%,$(firstword $(perfexecdir))),)
+perfexec_instdir = $(perfexecdir)
+else
+perfexec_instdir = $(prefix)/$(perfexecdir)
+endif
+perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir))
+export perfexec_instdir
+
+install: all
+       $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
+       $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
+       $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
+ifneq (,$X)
+       $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';)
+endif
+
+### Maintainer's dist rules
+
+perf.spec: perf.spec.in
+       sed -e 's/@@VERSION@@/$(PERF_VERSION)/g' < $< > $@+
+       mv $@+ $@
+
+PERF_TARNAME=perf-$(PERF_VERSION)
+dist: perf.spec perf-archive$(X) configure
+       ./perf-archive --format=tar \
+               --prefix=$(PERF_TARNAME)/ HEAD^{tree} > $(PERF_TARNAME).tar
+       @mkdir -p $(PERF_TARNAME)
+       @cp perf.spec configure $(PERF_TARNAME)
+       @echo $(PERF_VERSION) > $(PERF_TARNAME)/version
+       $(TAR) rf $(PERF_TARNAME).tar \
+               $(PERF_TARNAME)/perf.spec \
+               $(PERF_TARNAME)/configure \
+               $(PERF_TARNAME)/version
+       @$(RM) -r $(PERF_TARNAME)
+       gzip -f -9 $(PERF_TARNAME).tar
+
+rpm: dist
+       $(RPMBUILD) -ta $(PERF_TARNAME).tar.gz
+
+### Cleaning rules
+
+distclean: clean
+       $(RM) configure
+
+clean:
+       $(RM) *.o */*.o $(LIB_FILE)
+       $(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X
+       $(RM) $(TEST_PROGRAMS)
+       $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
+       $(RM) -r autom4te.cache
+       $(RM) config.log config.mak.autogen config.mak.append config.status config.cache
+       $(RM) -r $(PERF_TARNAME) .doc-tmp-dir
+       $(RM) $(PERF_TARNAME).tar.gz perf-core_$(PERF_VERSION)-*.tar.gz
+       $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
+       $(RM) PERF-VERSION-FILE PERF-CFLAGS PERF-BUILD-OPTIONS
+
+# temporary hack:
+perf-report: perf-report.cc ../../include/linux/perf_counter.h Makefile
+       g++ -g -O2 -Wall -lrt -o $@ $<
+
+.PHONY: all install clean strip
+.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: .FORCE-PERF-BUILD-OPTIONS
+
+### Make sure built-ins do not have dups and listed in perf.c
+#
+check-builtins::
+       ./check-builtins.sh
+
+### Test suite coverage testing
+#
+.PHONY: coverage coverage-clean coverage-build coverage-report
+
+coverage:
+       $(MAKE) coverage-build
+       $(MAKE) coverage-report
+
+coverage-clean:
+       rm -f *.gcda *.gcno
+
+COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs
+COVERAGE_LDFLAGS = $(CFLAGS)  -O0 -lgcov
+
+coverage-build: coverage-clean
+       $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all
+       $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \
+               -j1 test
+
+coverage-report:
+       gcov -b *.c */*.c
+       grep '^function.*called 0 ' *.c.gcov */*.c.gcov \
+               | sed -e 's/\([^:]*\)\.gcov: *function \([^ ]*\) called.*/\1: \2/' \
+               | tee coverage-untested-functions
diff --git a/Documentation/perf_counter/builtin-help.c b/Documentation/perf_counter/builtin-help.c
new file mode 100644 (file)
index 0000000..6616de0
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * builtin-help.c
+ *
+ * Builtin help command
+ */
+#include "util/cache.h"
+#include "builtin.h"
+#include "util/exec_cmd.h"
+#include "common-cmds.h"
+#include "util/parse-options.h"
+#include "util/run-command.h"
+#include "util/help.h"
+
+static struct man_viewer_list {
+       struct man_viewer_list *next;
+       char name[FLEX_ARRAY];
+} *man_viewer_list;
+
+static struct man_viewer_info_list {
+       struct man_viewer_info_list *next;
+       const char *info;
+       char name[FLEX_ARRAY];
+} *man_viewer_info_list;
+
+enum help_format {
+       HELP_FORMAT_MAN,
+       HELP_FORMAT_INFO,
+       HELP_FORMAT_WEB,
+};
+
+static int show_all = 0;
+static enum help_format help_format = HELP_FORMAT_MAN;
+static struct option builtin_help_options[] = {
+       OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
+       OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
+       OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
+                       HELP_FORMAT_WEB),
+       OPT_SET_INT('i', "info", &help_format, "show info page",
+                       HELP_FORMAT_INFO),
+       OPT_END(),
+};
+
+static const char * const builtin_help_usage[] = {
+       "perf help [--all] [--man|--web|--info] [command]",
+       NULL
+};
+
+static enum help_format parse_help_format(const char *format)
+{
+       if (!strcmp(format, "man"))
+               return HELP_FORMAT_MAN;
+       if (!strcmp(format, "info"))
+               return HELP_FORMAT_INFO;
+       if (!strcmp(format, "web") || !strcmp(format, "html"))
+               return HELP_FORMAT_WEB;
+       die("unrecognized help format '%s'", format);
+}
+
+static const char *get_man_viewer_info(const char *name)
+{
+       struct man_viewer_info_list *viewer;
+
+       for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
+       {
+               if (!strcasecmp(name, viewer->name))
+                       return viewer->info;
+       }
+       return NULL;
+}
+
+static int check_emacsclient_version(void)
+{
+       struct strbuf buffer = STRBUF_INIT;
+       struct child_process ec_process;
+       const char *argv_ec[] = { "emacsclient", "--version", NULL };
+       int version;
+
+       /* emacsclient prints its version number on stderr */
+       memset(&ec_process, 0, sizeof(ec_process));
+       ec_process.argv = argv_ec;
+       ec_process.err = -1;
+       ec_process.stdout_to_stderr = 1;
+       if (start_command(&ec_process)) {
+               fprintf(stderr, "Failed to start emacsclient.\n");
+               return -1;
+       }
+       strbuf_read(&buffer, ec_process.err, 20);
+       close(ec_process.err);
+
+       /*
+        * Don't bother checking return value, because "emacsclient --version"
+        * seems to always exits with code 1.
+        */
+       finish_command(&ec_process);
+
+       if (prefixcmp(buffer.buf, "emacsclient")) {
+               fprintf(stderr, "Failed to parse emacsclient version.\n");
+               strbuf_release(&buffer);
+               return -1;
+       }
+
+       strbuf_remove(&buffer, 0, strlen("emacsclient"));
+       version = atoi(buffer.buf);
+
+       if (version < 22) {
+               fprintf(stderr,
+                       "emacsclient version '%d' too old (< 22).\n",
+                       version);
+               strbuf_release(&buffer);
+               return -1;
+       }
+
+       strbuf_release(&buffer);
+       return 0;
+}
+
+static void exec_woman_emacs(const char* path, const char *page)
+{
+       if (!check_emacsclient_version()) {
+               /* This works only with emacsclient version >= 22. */
+               struct strbuf man_page = STRBUF_INIT;
+
+               if (!path)
+                       path = "emacsclient";
+               strbuf_addf(&man_page, "(woman \"%s\")", page);
+               execlp(path, "emacsclient", "-e", man_page.buf, NULL);
+               warning("failed to exec '%s': %s", path, strerror(errno));
+       }
+}
+
+static void exec_man_konqueror(const char* path, const char *page)
+{
+       const char *display = getenv("DISPLAY");
+       if (display && *display) {
+               struct strbuf man_page = STRBUF_INIT;
+               const char *filename = "kfmclient";
+
+               /* It's simpler to launch konqueror using kfmclient. */
+               if (path) {
+                       const char *file = strrchr(path, '/');
+                       if (file && !strcmp(file + 1, "konqueror")) {
+                               char *new = strdup(path);
+                               char *dest = strrchr(new, '/');
+
+                               /* strlen("konqueror") == strlen("kfmclient") */
+                               strcpy(dest + 1, "kfmclient");
+                               path = new;
+                       }
+                       if (file)
+                               filename = file;
+               } else
+                       path = "kfmclient";
+               strbuf_addf(&man_page, "man:%s(1)", page);
+               execlp(path, filename, "newTab", man_page.buf, NULL);
+               warning("failed to exec '%s': %s", path, strerror(errno));
+       }
+}
+
+static void exec_man_man(const char* path, const char *page)
+{
+       if (!path)
+               path = "man";
+       execlp(path, "man", page, NULL);
+       warning("failed to exec '%s': %s", path, strerror(errno));
+}
+
+static void exec_man_cmd(const char *cmd, const char *page)
+{
+       struct strbuf shell_cmd = STRBUF_INIT;
+       strbuf_addf(&shell_cmd, "%s %s", cmd, page);
+       execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
+       warning("failed to exec '%s': %s", cmd, strerror(errno));
+}
+
+static void add_man_viewer(const char *name)
+{
+       struct man_viewer_list **p = &man_viewer_list;
+       size_t len = strlen(name);
+
+       while (*p)
+               p = &((*p)->next);
+       *p = calloc(1, (sizeof(**p) + len + 1));
+       strncpy((*p)->name, name, len);
+}
+
+static int supported_man_viewer(const char *name, size_t len)
+{
+       return (!strncasecmp("man", name, len) ||
+               !strncasecmp("woman", name, len) ||
+               !strncasecmp("konqueror", name, len));
+}
+
+static void do_add_man_viewer_info(const char *name,
+                                  size_t len,
+                                  const char *value)
+{
+       struct man_viewer_info_list *new = calloc(1, sizeof(*new) + len + 1);
+
+       strncpy(new->name, name, len);
+       new->info = strdup(value);
+       new->next = man_viewer_info_list;
+       man_viewer_info_list = new;
+}
+
+static int add_man_viewer_path(const char *name,
+                              size_t len,
+                              const char *value)
+{
+       if (supported_man_viewer(name, len))
+               do_add_man_viewer_info(name, len, value);
+       else
+               warning("'%s': path for unsupported man viewer.\n"
+                       "Please consider using 'man.<tool>.cmd' instead.",
+                       name);
+
+       return 0;
+}
+
+static int add_man_viewer_cmd(const char *name,
+                             size_t len,
+                             const char *value)
+{
+       if (supported_man_viewer(name, len))
+               warning("'%s': cmd for supported man viewer.\n"
+                       "Please consider using 'man.<tool>.path' instead.",
+                       name);
+       else
+               do_add_man_viewer_info(name, len, value);
+
+       return 0;
+}
+
+static int add_man_viewer_info(const char *var, const char *value)
+{
+       const char *name = var + 4;
+       const char *subkey = strrchr(name, '.');
+
+       if (!subkey)
+               return error("Config with no key for man viewer: %s", name);
+
+       if (!strcmp(subkey, ".path")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return add_man_viewer_path(name, subkey - name, value);
+       }
+       if (!strcmp(subkey, ".cmd")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return add_man_viewer_cmd(name, subkey - name, value);
+       }
+
+       warning("'%s': unsupported man viewer sub key.", subkey);
+       return 0;
+}
+
+static int perf_help_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "help.format")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               help_format = parse_help_format(value);
+               return 0;
+       }
+       if (!strcmp(var, "man.viewer")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               add_man_viewer(value);
+               return 0;
+       }
+       if (!prefixcmp(var, "man."))
+               return add_man_viewer_info(var, value);
+
+       return perf_default_config(var, value, cb);
+}
+
+static struct cmdnames main_cmds, other_cmds;
+
+void list_common_cmds_help(void)
+{
+       int i, longest = 0;
+
+       for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+               if (longest < strlen(common_cmds[i].name))
+                       longest = strlen(common_cmds[i].name);
+       }
+
+       puts("The most commonly used perf commands are:");
+       for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+               printf("   %s   ", common_cmds[i].name);
+               mput_char(' ', longest - strlen(common_cmds[i].name));
+               puts(common_cmds[i].help);
+       }
+}
+
+static int is_perf_command(const char *s)
+{
+       return is_in_cmdlist(&main_cmds, s) ||
+               is_in_cmdlist(&other_cmds, s);
+}
+
+static const char *prepend(const char *prefix, const char *cmd)
+{
+       size_t pre_len = strlen(prefix);
+       size_t cmd_len = strlen(cmd);
+       char *p = malloc(pre_len + cmd_len + 1);
+       memcpy(p, prefix, pre_len);
+       strcpy(p + pre_len, cmd);
+       return p;
+}
+
+static const char *cmd_to_page(const char *perf_cmd)
+{
+       if (!perf_cmd)
+               return "perf";
+       else if (!prefixcmp(perf_cmd, "perf"))
+               return perf_cmd;
+       else if (is_perf_command(perf_cmd))
+               return prepend("perf-", perf_cmd);
+       else
+               return prepend("perf", perf_cmd);
+}
+
+static void setup_man_path(void)
+{
+       struct strbuf new_path = STRBUF_INIT;
+       const char *old_path = getenv("MANPATH");
+
+       /* We should always put ':' after our path. If there is no
+        * old_path, the ':' at the end will let 'man' to try
+        * system-wide paths after ours to find the manual page. If
+        * there is old_path, we need ':' as delimiter. */
+       strbuf_addstr(&new_path, system_path(PERF_MAN_PATH));
+       strbuf_addch(&new_path, ':');
+       if (old_path)
+               strbuf_addstr(&new_path, old_path);
+
+       setenv("MANPATH", new_path.buf, 1);
+
+       strbuf_release(&new_path);
+}
+
+static void exec_viewer(const char *name, const char *page)
+{
+       const char *info = get_man_viewer_info(name);
+
+       if (!strcasecmp(name, "man"))
+               exec_man_man(info, page);
+       else if (!strcasecmp(name, "woman"))
+               exec_woman_emacs(info, page);
+       else if (!strcasecmp(name, "konqueror"))
+               exec_man_konqueror(info, page);
+       else if (info)
+               exec_man_cmd(info, page);
+       else
+               warning("'%s': unknown man viewer.", name);
+}
+
+static void show_man_page(const char *perf_cmd)
+{
+       struct man_viewer_list *viewer;
+       const char *page = cmd_to_page(perf_cmd);
+       const char *fallback = getenv("PERF_MAN_VIEWER");
+
+       setup_man_path();
+       for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+       {
+               exec_viewer(viewer->name, page); /* will return when unable */
+       }
+       if (fallback)
+               exec_viewer(fallback, page);
+       exec_viewer("man", page);
+       die("no man viewer handled the request");
+}
+
+static void show_info_page(const char *perf_cmd)
+{
+       const char *page = cmd_to_page(perf_cmd);
+       setenv("INFOPATH", system_path(PERF_INFO_PATH), 1);
+       execlp("info", "info", "perfman", page, NULL);
+}
+
+static void get_html_page_path(struct strbuf *page_path, const char *page)
+{
+       struct stat st;
+       const char *html_path = system_path(PERF_HTML_PATH);
+
+       /* Check that we have a perf documentation directory. */
+       if (stat(mkpath("%s/perf.html", html_path), &st)
+           || !S_ISREG(st.st_mode))
+               die("'%s': not a documentation directory.", html_path);
+
+       strbuf_init(page_path, 0);
+       strbuf_addf(page_path, "%s/%s.html", html_path, page);
+}
+
+/*
+ * If open_html is not defined in a platform-specific way (see for
+ * example compat/mingw.h), we use the script web--browse to display
+ * HTML.
+ */
+#ifndef open_html
+void open_html(const char *path)
+{
+       execl_perf_cmd("web--browse", "-c", "help.browser", path, NULL);
+}
+#endif
+
+static void show_html_page(const char *perf_cmd)
+{
+       const char *page = cmd_to_page(perf_cmd);
+       struct strbuf page_path; /* it leaks but we exec bellow */
+
+       get_html_page_path(&page_path, page);
+
+       open_html(page_path.buf);
+}
+
+int cmd_help(int argc, const char **argv, const char *prefix)
+{
+       const char *alias;
+       load_command_list("perf-", &main_cmds, &other_cmds);
+
+       perf_config(perf_help_config, NULL);
+
+       argc = parse_options(argc, argv, builtin_help_options,
+                       builtin_help_usage, 0);
+
+       if (show_all) {
+               printf("usage: %s\n\n", perf_usage_string);
+               list_commands("perf commands", &main_cmds, &other_cmds);
+               printf("%s\n", perf_more_info_string);
+               return 0;
+       }
+
+       if (!argv[0]) {
+               printf("usage: %s\n\n", perf_usage_string);
+               list_common_cmds_help();
+               printf("\n%s\n", perf_more_info_string);
+               return 0;
+       }
+
+       alias = alias_lookup(argv[0]);
+       if (alias && !is_perf_command(argv[0])) {
+               printf("`perf %s' is aliased to `%s'\n", argv[0], alias);
+               return 0;
+       }
+
+       switch (help_format) {
+       case HELP_FORMAT_MAN:
+               show_man_page(argv[0]);
+               break;
+       case HELP_FORMAT_INFO:
+               show_info_page(argv[0]);
+               break;
+       case HELP_FORMAT_WEB:
+               show_html_page(argv[0]);
+               break;
+       }
+
+       return 0;
+}
diff --git a/Documentation/perf_counter/builtin-record.c b/Documentation/perf_counter/builtin-record.c
new file mode 100644 (file)
index 0000000..efb8759
--- /dev/null
@@ -0,0 +1,613 @@
+
+
+#include "util/util.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <getopt.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <sched.h>
+#include <pthread.h>
+
+#include <sys/syscall.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+
+#include <linux/unistd.h>
+#include <linux/types.h>
+
+#include "../../include/linux/perf_counter.h"
+
+#include "perf.h"
+
+#define ALIGN(x,a)             __ALIGN_MASK(x,(typeof(x))(a)-1)
+#define __ALIGN_MASK(x,mask)   (((x)+(mask))&~(mask))
+
+static int                     nr_counters                     =  0;
+static __u64                   event_id[MAX_COUNTERS]          = { };
+static int                     default_interval = 100000;
+static int                     event_count[MAX_COUNTERS];
+static int                     fd[MAX_NR_CPUS][MAX_COUNTERS];
+static int                     nr_cpus                         =  0;
+static unsigned int            page_size;
+static unsigned int            mmap_pages                      = 16;
+static int                     output;
+static char                    *output_name                    = "output.perf";
+static int                     group                           = 0;
+static unsigned int            realtime_prio                   = 0;
+static int                     system_wide                     = 0;
+static pid_t                   target_pid                      = -1;
+static int                     inherit                         = 1;
+static int                     nmi                             = 1;
+
+const unsigned int default_count[] = {
+       1000000,
+       1000000,
+         10000,
+         10000,
+       1000000,
+         10000,
+};
+
+struct event_symbol {
+       __u64 event;
+       char *symbol;
+};
+
+static struct event_symbol event_symbols[] = {
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES),                "cpu-cycles",           },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES),                "cycles",               },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_INSTRUCTIONS),              "instructions",         },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_REFERENCES),          "cache-references",     },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_MISSES),              "cache-misses",         },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS),       "branch-instructions",  },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS),       "branches",             },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_MISSES),             "branch-misses",        },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BUS_CYCLES),                "bus-cycles",           },
+
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_CLOCK),                 "cpu-clock",            },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK),                "task-clock",           },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS),               "page-faults",          },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS),               "faults",               },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MIN),           "minor-faults",         },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MAJ),           "major-faults",         },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES),          "context-switches",     },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES),          "cs",                   },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS),            "cpu-migrations",       },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS),            "migrations",           },
+};
+
+/*
+ * Each event can have multiple symbolic names.
+ * Symbolic names are (almost) exactly matched.
+ */
+static __u64 match_event_symbols(char *str)
+{
+       __u64 config, id;
+       int type;
+       unsigned int i;
+
+       if (sscanf(str, "r%llx", &config) == 1)
+               return config | PERF_COUNTER_RAW_MASK;
+
+       if (sscanf(str, "%d:%llu", &type, &id) == 2)
+               return EID(type, id);
+
+       for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
+               if (!strncmp(str, event_symbols[i].symbol,
+                            strlen(event_symbols[i].symbol)))
+                       return event_symbols[i].event;
+       }
+
+       return ~0ULL;
+}
+
+static int parse_events(char *str)
+{
+       __u64 config;
+
+again:
+       if (nr_counters == MAX_COUNTERS)
+               return -1;
+
+       config = match_event_symbols(str);
+       if (config == ~0ULL)
+               return -1;
+
+       event_id[nr_counters] = config;
+       nr_counters++;
+
+       str = strstr(str, ",");
+       if (str) {
+               str++;
+               goto again;
+       }
+
+       return 0;
+}
+
+#define __PERF_COUNTER_FIELD(config, name) \
+       ((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT)
+
+#define PERF_COUNTER_RAW(config)       __PERF_COUNTER_FIELD(config, RAW)
+#define PERF_COUNTER_CONFIG(config)    __PERF_COUNTER_FIELD(config, CONFIG)
+#define PERF_COUNTER_TYPE(config)      __PERF_COUNTER_FIELD(config, TYPE)
+#define PERF_COUNTER_ID(config)                __PERF_COUNTER_FIELD(config, EVENT)
+
+static void display_events_help(void)
+{
+       unsigned int i;
+       __u64 e;
+
+       printf(
+       " -e EVENT     --event=EVENT   #  symbolic-name        abbreviations");
+
+       for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
+               int type, id;
+
+               e = event_symbols[i].event;
+               type = PERF_COUNTER_TYPE(e);
+               id = PERF_COUNTER_ID(e);
+
+               printf("\n                             %d:%d: %-20s",
+                               type, id, event_symbols[i].symbol);
+       }
+
+       printf("\n"
+       "                           rNNN: raw PMU events (eventsel+umask)\n\n");
+}
+
+static void display_help(void)
+{
+       printf(
+       "Usage: perf-record [<options>] <cmd>\n"
+       "perf-record Options (up to %d event types can be specified at once):\n\n",
+                MAX_COUNTERS);
+
+       display_events_help();
+
+       printf(
+       " -c CNT    --count=CNT          # event period to sample\n"
+       " -m pages  --mmap_pages=<pages> # number of mmap data pages\n"
+       " -o file   --output=<file>      # output file\n"
+       " -p pid    --pid=<pid>          # record events on existing pid\n"
+       " -r prio   --realtime=<prio>    # use RT prio\n"
+       " -s        --system             # system wide profiling\n"
+       );
+
+       exit(0);
+}
+
+static void process_options(int argc, const char *argv[])
+{
+       int error = 0, counter;
+
+       for (;;) {
+               int option_index = 0;
+               /** Options for getopt */
+               static struct option long_options[] = {
+                       {"count",       required_argument,      NULL, 'c'},
+                       {"event",       required_argument,      NULL, 'e'},
+                       {"mmap_pages",  required_argument,      NULL, 'm'},
+                       {"output",      required_argument,      NULL, 'o'},
+                       {"pid",         required_argument,      NULL, 'p'},
+                       {"realtime",    required_argument,      NULL, 'r'},
+                       {"system",      no_argument,            NULL, 's'},
+                       {"inherit",     no_argument,            NULL, 'i'},
+                       {"nmi",         no_argument,            NULL, 'n'},
+                       {NULL,          0,                      NULL,  0 }
+               };
+               int c = getopt_long(argc, argv, "+:c:e:m:o:p:r:sin",
+                                   long_options, &option_index);
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'c': default_interval              =   atoi(optarg); break;
+               case 'e': error                         = parse_events(optarg); break;
+               case 'm': mmap_pages                    =   atoi(optarg); break;
+               case 'o': output_name                   = strdup(optarg); break;
+               case 'p': target_pid                    =   atoi(optarg); break;
+               case 'r': realtime_prio                 =   atoi(optarg); break;
+               case 's': system_wide                   ^=             1; break;
+               case 'i': inherit                       ^=             1; break;
+               case 'n': nmi                           ^=             1; break;
+               default: error = 1; break;
+               }
+       }
+
+       if (argc - optind == 0 && target_pid == -1)
+               error = 1;
+
+       if (error)
+               display_help();
+
+       if (!nr_counters) {
+               nr_counters = 1;
+               event_id[0] = 0;
+       }
+
+       for (counter = 0; counter < nr_counters; counter++) {
+               if (event_count[counter])
+                       continue;
+
+               event_count[counter] = default_interval;
+       }
+}
+
+struct mmap_data {
+       int counter;
+       void *base;
+       unsigned int mask;
+       unsigned int prev;
+};
+
+static unsigned int mmap_read_head(struct mmap_data *md)
+{
+       struct perf_counter_mmap_page *pc = md->base;
+       int head;
+
+       head = pc->data_head;
+       rmb();
+
+       return head;
+}
+
+static long events;
+static struct timeval last_read, this_read;
+
+static void mmap_read(struct mmap_data *md)
+{
+       unsigned int head = mmap_read_head(md);
+       unsigned int old = md->prev;
+       unsigned char *data = md->base + page_size;
+       unsigned long size;
+       void *buf;
+       int diff;
+
+       gettimeofday(&this_read, NULL);
+
+       /*
+        * If we're further behind than half the buffer, there's a chance
+        * the writer will bite our tail and screw up the events under us.
+        *
+        * If we somehow ended up ahead of the head, we got messed up.
+        *
+        * In either case, truncate and restart at head.
+        */
+       diff = head - old;
+       if (diff > md->mask / 2 || diff < 0) {
+               struct timeval iv;
+               unsigned long msecs;
+
+               timersub(&this_read, &last_read, &iv);
+               msecs = iv.tv_sec*1000 + iv.tv_usec/1000;
+
+               fprintf(stderr, "WARNING: failed to keep up with mmap data."
+                               "  Last read %lu msecs ago.\n", msecs);
+
+               /*
+                * head points to a known good entry, start there.
+                */
+               old = head;
+       }
+
+       last_read = this_read;
+
+       if (old != head)
+               events++;
+
+       size = head - old;
+
+       if ((old & md->mask) + size != (head & md->mask)) {
+               buf = &data[old & md->mask];
+               size = md->mask + 1 - (old & md->mask);
+               old += size;
+               while (size) {
+                       int ret = write(output, buf, size);
+                       if (ret < 0) {
+                               perror("failed to write");
+                               exit(-1);
+                       }
+                       size -= ret;
+                       buf += ret;
+               }
+       }
+
+       buf = &data[old & md->mask];
+       size = head - old;
+       old += size;
+       while (size) {
+               int ret = write(output, buf, size);
+               if (ret < 0) {
+                       perror("failed to write");
+                       exit(-1);
+               }
+               size -= ret;
+               buf += ret;
+       }
+
+       md->prev = old;
+}
+
+static volatile int done = 0;
+
+static void sig_handler(int sig)
+{
+       done = 1;
+}
+
+static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS];
+static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
+
+static int nr_poll;
+static int nr_cpu;
+
+struct mmap_event {
+       struct perf_event_header header;
+       __u32 pid, tid;
+       __u64 start;
+       __u64 len;
+       __u64 pgoff;
+       char filename[PATH_MAX];
+};
+struct comm_event {
+       struct perf_event_header header;
+       __u32 pid,tid;
+       char comm[16];
+};
+
+static pid_t pid_synthesize_comm_event(pid_t pid)
+{
+       char filename[PATH_MAX];
+       char bf[BUFSIZ];
+       struct comm_event comm_ev;
+       size_t size;
+       int fd;
+
+       snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "couldn't open %s\n", filename);
+               exit(EXIT_FAILURE);
+       }
+       if (read(fd, bf, sizeof(bf)) < 0) {
+               fprintf(stderr, "couldn't read %s\n", filename);
+               exit(EXIT_FAILURE);
+       }
+       close(fd);
+
+       pid_t spid, ppid;
+       char state;
+       char comm[18];
+
+       memset(&comm_ev, 0, sizeof(comm_ev));
+        int nr = sscanf(bf, "%d %s %c %d %d ",
+                       &spid, comm, &state, &ppid, &comm_ev.pid);
+       if (nr != 5) {
+               fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n",
+                       filename);
+               exit(EXIT_FAILURE);
+       }
+       comm_ev.header.type = PERF_EVENT_COMM;
+       comm_ev.tid = pid;
+       size = strlen(comm);
+       comm[--size] = '\0'; /* Remove the ')' at the end */
+       --size; /* Remove the '(' at the begin */
+       memcpy(comm_ev.comm, comm + 1, size);
+       size = ALIGN(size, sizeof(uint64_t));
+       comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size);
+       int ret = write(output, &comm_ev, comm_ev.header.size);
+       if (ret < 0) {
+               perror("failed to write");
+               exit(-1);
+       }
+       return comm_ev.pid;
+}
+
+static void pid_synthesize_mmap_events(pid_t pid, pid_t pgid)
+{
+       char filename[PATH_MAX];
+       FILE *fp;
+
+       snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
+
+       fp = fopen(filename, "r");
+       if (fp == NULL) {
+               fprintf(stderr, "couldn't open %s\n", filename);
+               exit(EXIT_FAILURE);
+       }
+       while (1) {
+               char bf[BUFSIZ];
+               unsigned char vm_read, vm_write, vm_exec, vm_mayshare;
+               struct mmap_event mmap_ev = {
+                       .header.type = PERF_EVENT_MMAP,
+               };
+               unsigned long ino;
+               int major, minor;
+               size_t size;
+               if (fgets(bf, sizeof(bf), fp) == NULL)
+                       break;
+
+               /* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */
+               sscanf(bf, "%llx-%llx %c%c%c%c %llx %x:%x %lu",
+                       &mmap_ev.start, &mmap_ev.len,
+                        &vm_read, &vm_write, &vm_exec, &vm_mayshare,
+                        &mmap_ev.pgoff, &major, &minor, &ino);
+               if (vm_exec == 'x') {
+                       char *execname = strrchr(bf, ' ');
+
+                       if (execname == NULL || execname[1] != '/')
+                               continue;
+
+                       execname += 1;
+                       size = strlen(execname);
+                       execname[size - 1] = '\0'; /* Remove \n */
+                       memcpy(mmap_ev.filename, execname, size);
+                       size = ALIGN(size, sizeof(uint64_t));
+                       mmap_ev.len -= mmap_ev.start;
+                       mmap_ev.header.size = (sizeof(mmap_ev) -
+                                              (sizeof(mmap_ev.filename) - size));
+                       mmap_ev.pid = pgid;
+                       mmap_ev.tid = pid;
+
+                       if (write(output, &mmap_ev, mmap_ev.header.size) < 0) {
+                               perror("failed to write");
+                               exit(-1);
+                       }
+               }
+       }
+
+       fclose(fp);
+}
+
+static void open_counters(int cpu, pid_t pid)
+{
+       struct perf_counter_hw_event hw_event;
+       int counter, group_fd;
+       int track = 1;
+
+       if (pid > 0) {
+               pid_t pgid = pid_synthesize_comm_event(pid);
+               pid_synthesize_mmap_events(pid, pgid);
+       }
+
+       group_fd = -1;
+       for (counter = 0; counter < nr_counters; counter++) {
+
+               memset(&hw_event, 0, sizeof(hw_event));
+               hw_event.config         = event_id[counter];
+               hw_event.irq_period     = event_count[counter];
+               hw_event.record_type    = PERF_RECORD_IP | PERF_RECORD_TID;
+               hw_event.nmi            = nmi;
+               hw_event.mmap           = track;
+               hw_event.comm           = track;
+               hw_event.inherit        = (cpu < 0) && inherit;
+
+               track = 0; // only the first counter needs these
+
+               fd[nr_cpu][counter] =
+                       sys_perf_counter_open(&hw_event, pid, cpu, group_fd, 0);
+
+               if (fd[nr_cpu][counter] < 0) {
+                       int err = errno;
+                       printf("kerneltop error: syscall returned with %d (%s)\n",
+                                       fd[nr_cpu][counter], strerror(err));
+                       if (err == EPERM)
+                               printf("Are you root?\n");
+                       exit(-1);
+               }
+               assert(fd[nr_cpu][counter] >= 0);
+               fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK);
+
+               /*
+                * First counter acts as the group leader:
+                */
+               if (group && group_fd == -1)
+                       group_fd = fd[nr_cpu][counter];
+
+               event_array[nr_poll].fd = fd[nr_cpu][counter];
+               event_array[nr_poll].events = POLLIN;
+               nr_poll++;
+
+               mmap_array[nr_cpu][counter].counter = counter;
+               mmap_array[nr_cpu][counter].prev = 0;
+               mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1;
+               mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size,
+                               PROT_READ, MAP_SHARED, fd[nr_cpu][counter], 0);
+               if (mmap_array[nr_cpu][counter].base == MAP_FAILED) {
+                       printf("kerneltop error: failed to mmap with %d (%s)\n",
+                                       errno, strerror(errno));
+                       exit(-1);
+               }
+       }
+       nr_cpu++;
+}
+
+int cmd_record(int argc, const char **argv)
+{
+       int i, counter;
+       pid_t pid;
+       int ret;
+
+       page_size = sysconf(_SC_PAGE_SIZE);
+
+       process_options(argc, argv);
+
+       nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+       assert(nr_cpus <= MAX_NR_CPUS);
+       assert(nr_cpus >= 0);
+
+       output = open(output_name, O_CREAT|O_RDWR, S_IRWXU);
+       if (output < 0) {
+               perror("failed to create output file");
+               exit(-1);
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (!system_wide) {
+               open_counters(-1, target_pid != -1 ? target_pid : 0);
+       } else for (i = 0; i < nr_cpus; i++)
+               open_counters(i, target_pid);
+
+       signal(SIGCHLD, sig_handler);
+       signal(SIGINT, sig_handler);
+
+       if (target_pid == -1) {
+               pid = fork();
+               if (pid < 0)
+                       perror("failed to fork");
+
+               if (!pid) {
+                       if (execvp(argv[0], argv)) {
+                               perror(argv[0]);
+                               exit(-1);
+                       }
+               }
+       }
+
+       if (realtime_prio) {
+               struct sched_param param;
+
+               param.sched_priority = realtime_prio;
+               if (sched_setscheduler(0, SCHED_FIFO, &param)) {
+                       printf("Could not set realtime priority.\n");
+                       exit(-1);
+               }
+       }
+
+       /*
+        * TODO: store the current /proc/$/maps information somewhere
+        */
+
+       while (!done) {
+               int hits = events;
+
+               for (i = 0; i < nr_cpu; i++) {
+                       for (counter = 0; counter < nr_counters; counter++)
+                               mmap_read(&mmap_array[i][counter]);
+               }
+
+               if (hits == events)
+                       ret = poll(event_array, nr_poll, 100);
+       }
+
+       return 0;
+}
diff --git a/Documentation/perf_counter/builtin-stat.c b/Documentation/perf_counter/builtin-stat.c
new file mode 100644 (file)
index 0000000..03518d7
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+ * kerneltop.c: show top kernel functions - performance counters showcase
+
+   Build with:
+
+     cc -O6 -Wall -c -o kerneltop.o kerneltop.c -lrt
+
+   Sample output:
+
+------------------------------------------------------------------------------
+ KernelTop:    2669 irqs/sec  [NMI, cache-misses/cache-refs],  (all, cpu: 2)
+------------------------------------------------------------------------------
+
+             weight         RIP          kernel function
+             ______   ________________   _______________
+
+              35.20 - ffffffff804ce74b : skb_copy_and_csum_dev
+              33.00 - ffffffff804cb740 : sock_alloc_send_skb
+              31.26 - ffffffff804ce808 : skb_push
+              22.43 - ffffffff80510004 : tcp_established_options
+              19.00 - ffffffff8027d250 : find_get_page
+              15.76 - ffffffff804e4fc9 : eth_type_trans
+              15.20 - ffffffff804d8baa : dst_release
+              14.86 - ffffffff804cf5d8 : skb_release_head_state
+              14.00 - ffffffff802217d5 : read_hpet
+              12.00 - ffffffff804ffb7f : __ip_local_out
+              11.97 - ffffffff804fc0c8 : ip_local_deliver_finish
+               8.54 - ffffffff805001a3 : ip_queue_xmit
+ */
+
+/*
+ * perfstat:  /usr/bin/time -alike performance counter statistics utility
+
+          It summarizes the counter events of all tasks (and child tasks),
+          covering all CPUs that the command (or workload) executes on.
+          It only counts the per-task events of the workload started,
+          independent of how many other tasks run on those CPUs.
+
+   Sample output:
+
+   $ ./perfstat -e 1 -e 3 -e 5 ls -lR /usr/include/ >/dev/null
+
+   Performance counter stats for 'ls':
+
+           163516953 instructions
+                2295 cache-misses
+             2855182 branch-misses
+ */
+
+ /*
+  * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com>
+  *
+  * Improvements and fixes by:
+  *
+  *   Arjan van de Ven <arjan@linux.intel.com>
+  *   Yanmin Zhang <yanmin.zhang@intel.com>
+  *   Wu Fengguang <fengguang.wu@intel.com>
+  *   Mike Galbraith <efault@gmx.de>
+  *   Paul Mackerras <paulus@samba.org>
+  *
+  * Released under the GPL v2. (and only v2, not any later version)
+  */
+
+#include "util/util.h"
+
+#include <getopt.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <sched.h>
+#include <pthread.h>
+
+#include <sys/syscall.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+
+#include <linux/unistd.h>
+#include <linux/types.h>
+
+#include "../../include/linux/perf_counter.h"
+
+#include "perf.h"
+
+#define EVENT_MASK_KERNEL              1
+#define EVENT_MASK_USER                        2
+
+static int                     system_wide                     =  0;
+
+static int                     nr_counters                     =  0;
+static __u64                   event_id[MAX_COUNTERS]          = {
+       EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK),
+       EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES),
+       EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS),
+       EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS),
+
+       EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES),
+       EID(PERF_TYPE_HARDWARE, PERF_COUNT_INSTRUCTIONS),
+       EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_REFERENCES),
+       EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_MISSES),
+};
+static int                     default_interval = 100000;
+static int                     event_count[MAX_COUNTERS];
+static int                     fd[MAX_NR_CPUS][MAX_COUNTERS];
+static int                     event_mask[MAX_COUNTERS];
+
+static int                     tid                             = -1;
+static int                     profile_cpu                     = -1;
+static int                     nr_cpus                         =  0;
+static int                     nmi                             =  1;
+static int                     group                           =  0;
+static unsigned int            page_size;
+
+static int                     zero;
+
+static int                     scale                           =  1;
+
+static const unsigned int default_count[] = {
+       1000000,
+       1000000,
+         10000,
+         10000,
+       1000000,
+         10000,
+};
+
+static char *hw_event_names[] = {
+       "CPU cycles",
+       "instructions",
+       "cache references",
+       "cache misses",
+       "branches",
+       "branch misses",
+       "bus cycles",
+};
+
+static char *sw_event_names[] = {
+       "cpu clock ticks",
+       "task clock ticks",
+       "pagefaults",
+       "context switches",
+       "CPU migrations",
+       "minor faults",
+       "major faults",
+};
+
+struct event_symbol {
+       __u64 event;
+       char *symbol;
+};
+
+static struct event_symbol event_symbols[] = {
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES),                "cpu-cycles",           },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES),                "cycles",               },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_INSTRUCTIONS),              "instructions",         },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_REFERENCES),          "cache-references",     },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_MISSES),              "cache-misses",         },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS),       "branch-instructions",  },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS),       "branches",             },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_MISSES),             "branch-misses",        },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BUS_CYCLES),                "bus-cycles",           },
+
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_CLOCK),                 "cpu-clock",            },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK),                "task-clock",           },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS),               "page-faults",          },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS),               "faults",               },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MIN),           "minor-faults",         },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MAJ),           "major-faults",         },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES),          "context-switches",     },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES),          "cs",                   },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS),            "cpu-migrations",       },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS),            "migrations",           },
+};
+
+#define __PERF_COUNTER_FIELD(config, name) \
+       ((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT)
+
+#define PERF_COUNTER_RAW(config)       __PERF_COUNTER_FIELD(config, RAW)
+#define PERF_COUNTER_CONFIG(config)    __PERF_COUNTER_FIELD(config, CONFIG)
+#define PERF_COUNTER_TYPE(config)      __PERF_COUNTER_FIELD(config, TYPE)
+#define PERF_COUNTER_ID(config)                __PERF_COUNTER_FIELD(config, EVENT)
+
+static void display_events_help(void)
+{
+       unsigned int i;
+       __u64 e;
+
+       printf(
+       " -e EVENT     --event=EVENT   #  symbolic-name        abbreviations");
+
+       for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
+               int type, id;
+
+               e = event_symbols[i].event;
+               type = PERF_COUNTER_TYPE(e);
+               id = PERF_COUNTER_ID(e);
+
+               printf("\n                             %d:%d: %-20s",
+                               type, id, event_symbols[i].symbol);
+       }
+
+       printf("\n"
+       "                           rNNN: raw PMU events (eventsel+umask)\n\n");
+}
+
+static void display_help(void)
+{
+       printf(
+       "Usage: perfstat [<events...>] <cmd...>\n\n"
+       "PerfStat Options (up to %d event types can be specified):\n\n",
+                MAX_COUNTERS);
+
+       display_events_help();
+
+       printf(
+       " -l                           # scale counter values\n"
+       " -a                           # system-wide collection\n");
+       exit(0);
+}
+
+static char *event_name(int ctr)
+{
+       __u64 config = event_id[ctr];
+       int type = PERF_COUNTER_TYPE(config);
+       int id = PERF_COUNTER_ID(config);
+       static char buf[32];
+
+       if (PERF_COUNTER_RAW(config)) {
+               sprintf(buf, "raw 0x%llx", PERF_COUNTER_CONFIG(config));
+               return buf;
+       }
+
+       switch (type) {
+       case PERF_TYPE_HARDWARE:
+               if (id < PERF_HW_EVENTS_MAX)
+                       return hw_event_names[id];
+               return "unknown-hardware";
+
+       case PERF_TYPE_SOFTWARE:
+               if (id < PERF_SW_EVENTS_MAX)
+                       return sw_event_names[id];
+               return "unknown-software";
+
+       default:
+               break;
+       }
+
+       return "unknown";
+}
+
+/*
+ * Each event can have multiple symbolic names.
+ * Symbolic names are (almost) exactly matched.
+ */
+static __u64 match_event_symbols(char *str)
+{
+       __u64 config, id;
+       int type;
+       unsigned int i;
+       char mask_str[4];
+
+       if (sscanf(str, "r%llx", &config) == 1)
+               return config | PERF_COUNTER_RAW_MASK;
+
+       switch (sscanf(str, "%d:%llu:%2s", &type, &id, mask_str)) {
+               case 3:
+                       if (strchr(mask_str, 'u'))
+                               event_mask[nr_counters] |= EVENT_MASK_USER;
+                       if (strchr(mask_str, 'k'))
+                               event_mask[nr_counters] |= EVENT_MASK_KERNEL;
+               case 2:
+                       return EID(type, id);
+
+               default:
+                       break;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
+               if (!strncmp(str, event_symbols[i].symbol,
+                            strlen(event_symbols[i].symbol)))
+                       return event_symbols[i].event;
+       }
+
+       return ~0ULL;
+}
+
+static int parse_events(char *str)
+{
+       __u64 config;
+
+again:
+       if (nr_counters == MAX_COUNTERS)
+               return -1;
+
+       config = match_event_symbols(str);
+       if (config == ~0ULL)
+               return -1;
+
+       event_id[nr_counters] = config;
+       nr_counters++;
+
+       str = strstr(str, ",");
+       if (str) {
+               str++;
+               goto again;
+       }
+
+       return 0;
+}
+
+
+/*
+ * perfstat
+ */
+
+char fault_here[1000000];
+
+static void create_perfstat_counter(int counter)
+{
+       struct perf_counter_hw_event hw_event;
+
+       memset(&hw_event, 0, sizeof(hw_event));
+       hw_event.config         = event_id[counter];
+       hw_event.record_type    = 0;
+       hw_event.nmi            = 0;
+       hw_event.exclude_kernel = event_mask[counter] & EVENT_MASK_KERNEL;
+       hw_event.exclude_user   = event_mask[counter] & EVENT_MASK_USER;
+
+       if (scale)
+               hw_event.read_format    = PERF_FORMAT_TOTAL_TIME_ENABLED |
+                                         PERF_FORMAT_TOTAL_TIME_RUNNING;
+
+       if (system_wide) {
+               int cpu;
+               for (cpu = 0; cpu < nr_cpus; cpu ++) {
+                       fd[cpu][counter] = sys_perf_counter_open(&hw_event, -1, cpu, -1, 0);
+                       if (fd[cpu][counter] < 0) {
+                               printf("perfstat error: syscall returned with %d (%s)\n",
+                                               fd[cpu][counter], strerror(errno));
+                               exit(-1);
+                       }
+               }
+       } else {
+               hw_event.inherit        = 1;
+               hw_event.disabled       = 1;
+
+               fd[0][counter] = sys_perf_counter_open(&hw_event, 0, -1, -1, 0);
+               if (fd[0][counter] < 0) {
+                       printf("perfstat error: syscall returned with %d (%s)\n",
+                                       fd[0][counter], strerror(errno));
+                       exit(-1);
+               }
+       }
+}
+
+int do_perfstat(int argc, char *argv[])
+{
+       unsigned long long t0, t1;
+       int counter;
+       ssize_t res;
+       int status;
+       int pid;
+
+       if (!system_wide)
+               nr_cpus = 1;
+
+       for (counter = 0; counter < nr_counters; counter++)
+               create_perfstat_counter(counter);
+
+       argc -= optind;
+       argv += optind;
+
+       if (!argc)
+               display_help();
+
+       /*
+        * Enable counters and exec the command:
+        */
+       t0 = rdclock();
+       prctl(PR_TASK_PERF_COUNTERS_ENABLE);
+
+       if ((pid = fork()) < 0)
+               perror("failed to fork");
+       if (!pid) {
+               if (execvp(argv[0], argv)) {
+                       perror(argv[0]);
+                       exit(-1);
+               }
+       }
+       while (wait(&status) >= 0)
+               ;
+       prctl(PR_TASK_PERF_COUNTERS_DISABLE);
+       t1 = rdclock();
+
+       fflush(stdout);
+
+       fprintf(stderr, "\n");
+       fprintf(stderr, " Performance counter stats for \'%s\':\n",
+               argv[0]);
+       fprintf(stderr, "\n");
+
+       for (counter = 0; counter < nr_counters; counter++) {
+               int cpu, nv;
+               __u64 count[3], single_count[3];
+               int scaled;
+
+               count[0] = count[1] = count[2] = 0;
+               nv = scale ? 3 : 1;
+               for (cpu = 0; cpu < nr_cpus; cpu ++) {
+                       res = read(fd[cpu][counter],
+                                  single_count, nv * sizeof(__u64));
+                       assert(res == nv * sizeof(__u64));
+
+                       count[0] += single_count[0];
+                       if (scale) {
+                               count[1] += single_count[1];
+                               count[2] += single_count[2];
+                       }
+               }
+
+               scaled = 0;
+               if (scale) {
+                       if (count[2] == 0) {
+                               fprintf(stderr, " %14s  %-20s\n",
+                                       "<not counted>", event_name(counter));
+                               continue;
+                       }
+                       if (count[2] < count[1]) {
+                               scaled = 1;
+                               count[0] = (unsigned long long)
+                                       ((double)count[0] * count[1] / count[2] + 0.5);
+                       }
+               }
+
+               if (event_id[counter] == EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_CLOCK) ||
+                   event_id[counter] == EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK)) {
+
+                       double msecs = (double)count[0] / 1000000;
+
+                       fprintf(stderr, " %14.6f  %-20s (msecs)",
+                               msecs, event_name(counter));
+               } else {
+                       fprintf(stderr, " %14Ld  %-20s (events)",
+                               count[0], event_name(counter));
+               }
+               if (scaled)
+                       fprintf(stderr, "  (scaled from %.2f%%)",
+                               (double) count[2] / count[1] * 100);
+               fprintf(stderr, "\n");
+       }
+       fprintf(stderr, "\n");
+       fprintf(stderr, " Wall-clock time elapsed: %12.6f msecs\n",
+                       (double)(t1-t0)/1e6);
+       fprintf(stderr, "\n");
+
+       return 0;
+}
+
+static void process_options(int argc, char **argv)
+{
+       int error = 0, counter;
+
+       for (;;) {
+               int option_index = 0;
+               /** Options for getopt */
+               static struct option long_options[] = {
+                       {"count",       required_argument,      NULL, 'c'},
+                       {"cpu",         required_argument,      NULL, 'C'},
+                       {"delay",       required_argument,      NULL, 'd'},
+                       {"dump_symtab", no_argument,            NULL, 'D'},
+                       {"event",       required_argument,      NULL, 'e'},
+                       {"filter",      required_argument,      NULL, 'f'},
+                       {"group",       required_argument,      NULL, 'g'},
+                       {"help",        no_argument,            NULL, 'h'},
+                       {"nmi",         required_argument,      NULL, 'n'},
+                       {"munmap_info", no_argument,            NULL, 'U'},
+                       {"pid",         required_argument,      NULL, 'p'},
+                       {"realtime",    required_argument,      NULL, 'r'},
+                       {"scale",       no_argument,            NULL, 'l'},
+                       {"symbol",      required_argument,      NULL, 's'},
+                       {"stat",        no_argument,            NULL, 'S'},
+                       {"vmlinux",     required_argument,      NULL, 'x'},
+                       {"zero",        no_argument,            NULL, 'z'},
+                       {NULL,          0,                      NULL,  0 }
+               };
+               int c = getopt_long(argc, argv, "+:ac:C:d:De:f:g:hln:m:p:r:s:Sx:zMU",
+                                   long_options, &option_index);
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'a': system_wide                   =              1; break;
+               case 'c': default_interval              =   atoi(optarg); break;
+               case 'C':
+                       /* CPU and PID are mutually exclusive */
+                       if (tid != -1) {
+                               printf("WARNING: CPU switch overriding PID\n");
+                               sleep(1);
+                               tid = -1;
+                       }
+                       profile_cpu                     =   atoi(optarg); break;
+
+               case 'e': error                         = parse_events(optarg); break;
+
+               case 'g': group                         =   atoi(optarg); break;
+               case 'h':                                 display_help(); break;
+               case 'l': scale                         =              1; break;
+               case 'n': nmi                           =   atoi(optarg); break;
+               case 'p':
+                       /* CPU and PID are mutually exclusive */
+                       if (profile_cpu != -1) {
+                               printf("WARNING: PID switch overriding CPU\n");
+                               sleep(1);
+                               profile_cpu = -1;
+                       }
+                       tid                             =   atoi(optarg); break;
+               case 'z': zero                          =              1; break;
+               default: error = 1; break;
+               }
+       }
+       if (error)
+               display_help();
+
+       if (!nr_counters) {
+               nr_counters = 8;
+       }
+
+       for (counter = 0; counter < nr_counters; counter++) {
+               if (event_count[counter])
+                       continue;
+
+               event_count[counter] = default_interval;
+       }
+}
+
+static void skip_signal(int signo)
+{
+}
+
+int cmd_stat(int argc, char **argv, const char *prefix)
+{
+       sigset_t blocked;
+
+       page_size = sysconf(_SC_PAGE_SIZE);
+
+       process_options(argc, argv);
+
+       nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+       assert(nr_cpus <= MAX_NR_CPUS);
+       assert(nr_cpus >= 0);
+
+       /*
+        * We dont want to block the signals - that would cause
+        * child tasks to inherit that and Ctrl-C would not work.
+        * What we want is for Ctrl-C to work in the exec()-ed
+        * task, but being ignored by perf stat itself:
+        */
+       signal(SIGINT,  skip_signal);
+       signal(SIGALRM, skip_signal);
+       signal(SIGABRT, skip_signal);
+
+       return do_perfstat(argc, argv);
+}
diff --git a/Documentation/perf_counter/builtin-top.c b/Documentation/perf_counter/builtin-top.c
new file mode 100644 (file)
index 0000000..814b2e4
--- /dev/null
@@ -0,0 +1,1146 @@
+/*
+ * kerneltop.c: show top kernel functions - performance counters showcase
+
+   Build with:
+
+     make -C Documentation/perf_counter/
+
+   Sample output:
+
+------------------------------------------------------------------------------
+ KernelTop:    2669 irqs/sec  [NMI, cache-misses/cache-refs],  (all, cpu: 2)
+------------------------------------------------------------------------------
+
+             weight         RIP          kernel function
+             ______   ________________   _______________
+
+              35.20 - ffffffff804ce74b : skb_copy_and_csum_dev
+              33.00 - ffffffff804cb740 : sock_alloc_send_skb
+              31.26 - ffffffff804ce808 : skb_push
+              22.43 - ffffffff80510004 : tcp_established_options
+              19.00 - ffffffff8027d250 : find_get_page
+              15.76 - ffffffff804e4fc9 : eth_type_trans
+              15.20 - ffffffff804d8baa : dst_release
+              14.86 - ffffffff804cf5d8 : skb_release_head_state
+              14.00 - ffffffff802217d5 : read_hpet
+              12.00 - ffffffff804ffb7f : __ip_local_out
+              11.97 - ffffffff804fc0c8 : ip_local_deliver_finish
+               8.54 - ffffffff805001a3 : ip_queue_xmit
+ */
+
+ /*
+  * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com>
+  *
+  * Improvements and fixes by:
+  *
+  *   Arjan van de Ven <arjan@linux.intel.com>
+  *   Yanmin Zhang <yanmin.zhang@intel.com>
+  *   Wu Fengguang <fengguang.wu@intel.com>
+  *   Mike Galbraith <efault@gmx.de>
+  *   Paul Mackerras <paulus@samba.org>
+  *
+  * Released under the GPL v2. (and only v2, not any later version)
+  */
+
+#include "util/util.h"
+
+#include <getopt.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <sched.h>
+#include <pthread.h>
+
+#include <sys/syscall.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+
+#include <linux/unistd.h>
+#include <linux/types.h>
+
+#include "../../include/linux/perf_counter.h"
+
+#include "perf.h"
+
+static int                     system_wide                     =  0;
+
+static int                     nr_counters                     =  0;
+static __u64                   event_id[MAX_COUNTERS]          = {
+       EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK),
+       EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES),
+       EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS),
+       EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS),
+
+       EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES),
+       EID(PERF_TYPE_HARDWARE, PERF_COUNT_INSTRUCTIONS),
+       EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_REFERENCES),
+       EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_MISSES),
+};
+static int                     default_interval = 100000;
+static int                     event_count[MAX_COUNTERS];
+static int                     fd[MAX_NR_CPUS][MAX_COUNTERS];
+
+static __u64                   count_filter                   = 100;
+
+static int                     tid                             = -1;
+static int                     profile_cpu                     = -1;
+static int                     nr_cpus                         =  0;
+static int                     nmi                             =  1;
+static unsigned int            realtime_prio                   =  0;
+static int                     group                           =  0;
+static unsigned int            page_size;
+static unsigned int            mmap_pages                      =  16;
+static int                     use_mmap                        = 0;
+static int                     use_munmap                      = 0;
+static int                     freq                            = 0;
+
+static char                    *vmlinux;
+
+static char                    *sym_filter;
+static unsigned long           filter_start;
+static unsigned long           filter_end;
+
+static int                     delay_secs                      =  2;
+static int                     zero;
+static int                     dump_symtab;
+
+static int                     scale;
+
+struct source_line {
+       uint64_t                EIP;
+       unsigned long           count;
+       char                    *line;
+       struct source_line      *next;
+};
+
+static struct source_line      *lines;
+static struct source_line      **lines_tail;
+
+static const unsigned int default_count[] = {
+       1000000,
+       1000000,
+         10000,
+         10000,
+       1000000,
+         10000,
+};
+
+static char *hw_event_names[] = {
+       "CPU cycles",
+       "instructions",
+       "cache references",
+       "cache misses",
+       "branches",
+       "branch misses",
+       "bus cycles",
+};
+
+static char *sw_event_names[] = {
+       "cpu clock ticks",
+       "task clock ticks",
+       "pagefaults",
+       "context switches",
+       "CPU migrations",
+       "minor faults",
+       "major faults",
+};
+
+struct event_symbol {
+       __u64 event;
+       char *symbol;
+};
+
+static struct event_symbol event_symbols[] = {
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES),                "cpu-cycles",           },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CPU_CYCLES),                "cycles",               },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_INSTRUCTIONS),              "instructions",         },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_REFERENCES),          "cache-references",     },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_CACHE_MISSES),              "cache-misses",         },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS),       "branch-instructions",  },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_INSTRUCTIONS),       "branches",             },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BRANCH_MISSES),             "branch-misses",        },
+       {EID(PERF_TYPE_HARDWARE, PERF_COUNT_BUS_CYCLES),                "bus-cycles",           },
+
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_CLOCK),                 "cpu-clock",            },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_TASK_CLOCK),                "task-clock",           },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS),               "page-faults",          },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS),               "faults",               },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MIN),           "minor-faults",         },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_PAGE_FAULTS_MAJ),           "major-faults",         },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES),          "context-switches",     },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CONTEXT_SWITCHES),          "cs",                   },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS),            "cpu-migrations",       },
+       {EID(PERF_TYPE_SOFTWARE, PERF_COUNT_CPU_MIGRATIONS),            "migrations",           },
+};
+
+#define __PERF_COUNTER_FIELD(config, name) \
+       ((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT)
+
+#define PERF_COUNTER_RAW(config)       __PERF_COUNTER_FIELD(config, RAW)
+#define PERF_COUNTER_CONFIG(config)    __PERF_COUNTER_FIELD(config, CONFIG)
+#define PERF_COUNTER_TYPE(config)      __PERF_COUNTER_FIELD(config, TYPE)
+#define PERF_COUNTER_ID(config)                __PERF_COUNTER_FIELD(config, EVENT)
+
+static void display_events_help(void)
+{
+       unsigned int i;
+       __u64 e;
+
+       printf(
+       " -e EVENT     --event=EVENT   #  symbolic-name        abbreviations");
+
+       for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
+               int type, id;
+
+               e = event_symbols[i].event;
+               type = PERF_COUNTER_TYPE(e);
+               id = PERF_COUNTER_ID(e);
+
+               printf("\n                             %d:%d: %-20s",
+                               type, id, event_symbols[i].symbol);
+       }
+
+       printf("\n"
+       "                           rNNN: raw PMU events (eventsel+umask)\n\n");
+}
+
+static void display_help(void)
+{
+       printf(
+       "Usage: kerneltop [<options>]\n"
+       "   Or: kerneltop -S [<options>] COMMAND [ARGS]\n\n"
+       "KernelTop Options (up to %d event types can be specified at once):\n\n",
+                MAX_COUNTERS);
+
+       display_events_help();
+
+       printf(
+       " -c CNT    --count=CNT        # event period to sample\n\n"
+       " -C CPU    --cpu=CPU          # CPU (-1 for all)                 [default: -1]\n"
+       " -p PID    --pid=PID          # PID of sampled task (-1 for all) [default: -1]\n\n"
+       " -l                           # show scale factor for RR events\n"
+       " -d delay  --delay=<seconds>  # sampling/display delay           [default:  2]\n"
+       " -f CNT    --filter=CNT       # min-event-count filter          [default: 100]\n\n"
+       " -r prio   --realtime=<prio>  # event acquisition runs with SCHED_FIFO policy\n"
+       " -s symbol --symbol=<symbol>  # function to be showed annotated one-shot\n"
+       " -x path   --vmlinux=<path>   # the vmlinux binary, required for -s use\n"
+       " -z        --zero             # zero counts after display\n"
+       " -D        --dump_symtab      # dump symbol table to stderr on startup\n"
+       " -m pages  --mmap_pages=<pages> # number of mmap data pages\n"
+       " -M        --mmap_info        # print mmap info stream\n"
+       " -U        --munmap_info      # print munmap info stream\n"
+       );
+
+       exit(0);
+}
+
+static char *event_name(int ctr)
+{
+       __u64 config = event_id[ctr];
+       int type = PERF_COUNTER_TYPE(config);
+       int id = PERF_COUNTER_ID(config);
+       static char buf[32];
+
+       if (PERF_COUNTER_RAW(config)) {
+               sprintf(buf, "raw 0x%llx", PERF_COUNTER_CONFIG(config));
+               return buf;
+       }
+
+       switch (type) {
+       case PERF_TYPE_HARDWARE:
+               if (id < PERF_HW_EVENTS_MAX)
+                       return hw_event_names[id];
+               return "unknown-hardware";
+
+       case PERF_TYPE_SOFTWARE:
+               if (id < PERF_SW_EVENTS_MAX)
+                       return sw_event_names[id];
+               return "unknown-software";
+
+       default:
+               break;
+       }
+
+       return "unknown";
+}
+
+/*
+ * Each event can have multiple symbolic names.
+ * Symbolic names are (almost) exactly matched.
+ */
+static __u64 match_event_symbols(char *str)
+{
+       __u64 config, id;
+       int type;
+       unsigned int i;
+
+       if (sscanf(str, "r%llx", &config) == 1)
+               return config | PERF_COUNTER_RAW_MASK;
+
+       if (sscanf(str, "%d:%llu", &type, &id) == 2)
+               return EID(type, id);
+
+       for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
+               if (!strncmp(str, event_symbols[i].symbol,
+                            strlen(event_symbols[i].symbol)))
+                       return event_symbols[i].event;
+       }
+
+       return ~0ULL;
+}
+
+static int parse_events(char *str)
+{
+       __u64 config;
+
+again:
+       if (nr_counters == MAX_COUNTERS)
+               return -1;
+
+       config = match_event_symbols(str);
+       if (config == ~0ULL)
+               return -1;
+
+       event_id[nr_counters] = config;
+       nr_counters++;
+
+       str = strstr(str, ",");
+       if (str) {
+               str++;
+               goto again;
+       }
+
+       return 0;
+}
+
+/*
+ * Symbols
+ */
+
+static uint64_t                        min_ip;
+static uint64_t                        max_ip = -1ll;
+
+struct sym_entry {
+       unsigned long long      addr;
+       char                    *sym;
+       unsigned long           count[MAX_COUNTERS];
+       int                     skip;
+       struct source_line      *source;
+};
+
+#define MAX_SYMS               100000
+
+static int sym_table_count;
+
+struct sym_entry               *sym_filter_entry;
+
+static struct sym_entry                sym_table[MAX_SYMS];
+
+static void show_details(struct sym_entry *sym);
+
+/*
+ * Ordering weight: count-1 * count-2 * ... / count-n
+ */
+static double sym_weight(const struct sym_entry *sym)
+{
+       double weight;
+       int counter;
+
+       weight = sym->count[0];
+
+       for (counter = 1; counter < nr_counters-1; counter++)
+               weight *= sym->count[counter];
+
+       weight /= (sym->count[counter] + 1);
+
+       return weight;
+}
+
+static int compare(const void *__sym1, const void *__sym2)
+{
+       const struct sym_entry *sym1 = __sym1, *sym2 = __sym2;
+
+       return sym_weight(sym1) < sym_weight(sym2);
+}
+
+static long                    events;
+static long                    userspace_events;
+static const char              CONSOLE_CLEAR[] = "\e[H\e[2J";
+
+static struct sym_entry                tmp[MAX_SYMS];
+
+static void print_sym_table(void)
+{
+       int i, printed;
+       int counter;
+       float events_per_sec = events/delay_secs;
+       float kevents_per_sec = (events-userspace_events)/delay_secs;
+       float sum_kevents = 0.0;
+
+       events = userspace_events = 0;
+       memcpy(tmp, sym_table, sizeof(sym_table[0])*sym_table_count);
+       qsort(tmp, sym_table_count, sizeof(tmp[0]), compare);
+
+       for (i = 0; i < sym_table_count && tmp[i].count[0]; i++)
+               sum_kevents += tmp[i].count[0];
+
+       write(1, CONSOLE_CLEAR, strlen(CONSOLE_CLEAR));
+
+       printf(
+"------------------------------------------------------------------------------\n");
+       printf( " KernelTop:%8.0f irqs/sec  kernel:%4.1f%% [%s, ",
+               events_per_sec,
+               100.0 - (100.0*((events_per_sec-kevents_per_sec)/events_per_sec)),
+               nmi ? "NMI" : "IRQ");
+
+       if (nr_counters == 1)
+               printf("%d ", event_count[0]);
+
+       for (counter = 0; counter < nr_counters; counter++) {
+               if (counter)
+                       printf("/");
+
+               printf("%s", event_name(counter));
+       }
+
+       printf( "], ");
+
+       if (tid != -1)
+               printf(" (tid: %d", tid);
+       else
+               printf(" (all");
+
+       if (profile_cpu != -1)
+               printf(", cpu: %d)\n", profile_cpu);
+       else {
+               if (tid != -1)
+                       printf(")\n");
+               else
+                       printf(", %d CPUs)\n", nr_cpus);
+       }
+
+       printf("------------------------------------------------------------------------------\n\n");
+
+       if (nr_counters == 1)
+               printf("             events    pcnt");
+       else
+               printf("  weight     events    pcnt");
+
+       printf("         RIP          kernel function\n"
+                      "  ______     ______   _____   ________________   _______________\n\n"
+       );
+
+       for (i = 0, printed = 0; i < sym_table_count; i++) {
+               float pcnt;
+               int count;
+
+               if (printed <= 18 && tmp[i].count[0] >= count_filter) {
+                       pcnt = 100.0 - (100.0*((sum_kevents-tmp[i].count[0])/sum_kevents));
+
+                       if (nr_counters == 1)
+                               printf("%19.2f - %4.1f%% - %016llx : %s\n",
+                                       sym_weight(tmp + i),
+                                       pcnt, tmp[i].addr, tmp[i].sym);
+                       else
+                               printf("%8.1f %10ld - %4.1f%% - %016llx : %s\n",
+                                       sym_weight(tmp + i),
+                                       tmp[i].count[0],
+                                       pcnt, tmp[i].addr, tmp[i].sym);
+                       printed++;
+               }
+               /*
+                * Add decay to the counts:
+                */
+               for (count = 0; count < nr_counters; count++)
+                       sym_table[i].count[count] = zero ? 0 : sym_table[i].count[count] * 7 / 8;
+       }
+
+       if (sym_filter_entry)
+               show_details(sym_filter_entry);
+
+       {
+               struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
+
+               if (poll(&stdin_poll, 1, 0) == 1) {
+                       printf("key pressed - exiting.\n");
+                       exit(0);
+               }
+       }
+}
+
+static void *display_thread(void *arg)
+{
+       printf("KernelTop refresh period: %d seconds\n", delay_secs);
+
+       while (!sleep(delay_secs))
+               print_sym_table();
+
+       return NULL;
+}
+
+static int read_symbol(FILE *in, struct sym_entry *s)
+{
+       static int filter_match = 0;
+       char *sym, stype;
+       char str[500];
+       int rc, pos;
+
+       rc = fscanf(in, "%llx %c %499s", &s->addr, &stype, str);
+       if (rc == EOF)
+               return -1;
+
+       assert(rc == 3);
+
+       /* skip until end of line: */
+       pos = strlen(str);
+       do {
+               rc = fgetc(in);
+               if (rc == '\n' || rc == EOF || pos >= 499)
+                       break;
+               str[pos] = rc;
+               pos++;
+       } while (1);
+       str[pos] = 0;
+
+       sym = str;
+
+       /* Filter out known duplicates and non-text symbols. */
+       if (!strcmp(sym, "_text"))
+               return 1;
+       if (!min_ip && !strcmp(sym, "_stext"))
+               return 1;
+       if (!strcmp(sym, "_etext") || !strcmp(sym, "_sinittext"))
+               return 1;
+       if (stype != 'T' && stype != 't')
+               return 1;
+       if (!strncmp("init_module", sym, 11) || !strncmp("cleanup_module", sym, 14))
+               return 1;
+       if (strstr(sym, "_text_start") || strstr(sym, "_text_end"))
+               return 1;
+
+       s->sym = malloc(strlen(str)+1);
+       assert(s->sym);
+
+       strcpy((char *)s->sym, str);
+       s->skip = 0;
+
+       /* Tag events to be skipped. */
+       if (!strcmp("default_idle", s->sym) || !strcmp("cpu_idle", s->sym))
+               s->skip = 1;
+       else if (!strcmp("enter_idle", s->sym) || !strcmp("exit_idle", s->sym))
+               s->skip = 1;
+       else if (!strcmp("mwait_idle", s->sym))
+               s->skip = 1;
+
+       if (filter_match == 1) {
+               filter_end = s->addr;
+               filter_match = -1;
+               if (filter_end - filter_start > 10000) {
+                       printf("hm, too large filter symbol <%s> - skipping.\n",
+                               sym_filter);
+                       printf("symbol filter start: %016lx\n", filter_start);
+                       printf("                end: %016lx\n", filter_end);
+                       filter_end = filter_start = 0;
+                       sym_filter = NULL;
+                       sleep(1);
+               }
+       }
+       if (filter_match == 0 && sym_filter && !strcmp(s->sym, sym_filter)) {
+               filter_match = 1;
+               filter_start = s->addr;
+       }
+
+       return 0;
+}
+
+static int compare_addr(const void *__sym1, const void *__sym2)
+{
+       const struct sym_entry *sym1 = __sym1, *sym2 = __sym2;
+
+       return sym1->addr > sym2->addr;
+}
+
+static void sort_symbol_table(void)
+{
+       int i, dups;
+
+       do {
+               qsort(sym_table, sym_table_count, sizeof(sym_table[0]), compare_addr);
+               for (i = 0, dups = 0; i < sym_table_count; i++) {
+                       if (sym_table[i].addr == sym_table[i+1].addr) {
+                               sym_table[i+1].addr = -1ll;
+                               dups++;
+                       }
+               }
+               sym_table_count -= dups;
+       } while(dups);
+}
+
+static void parse_symbols(void)
+{
+       struct sym_entry *last;
+
+       FILE *kallsyms = fopen("/proc/kallsyms", "r");
+
+       if (!kallsyms) {
+               printf("Could not open /proc/kallsyms - no CONFIG_KALLSYMS_ALL=y?\n");
+               exit(-1);
+       }
+
+       while (!feof(kallsyms)) {
+               if (read_symbol(kallsyms, &sym_table[sym_table_count]) == 0) {
+                       sym_table_count++;
+                       assert(sym_table_count <= MAX_SYMS);
+               }
+       }
+
+       sort_symbol_table();
+       min_ip = sym_table[0].addr;
+       max_ip = sym_table[sym_table_count-1].addr;
+       last = sym_table + sym_table_count++;
+
+       last->addr = -1ll;
+       last->sym = "<end>";
+
+       if (filter_end) {
+               int count;
+               for (count=0; count < sym_table_count; count ++) {
+                       if (!strcmp(sym_table[count].sym, sym_filter)) {
+                               sym_filter_entry = &sym_table[count];
+                               break;
+                       }
+               }
+       }
+       if (dump_symtab) {
+               int i;
+
+               for (i = 0; i < sym_table_count; i++)
+                       fprintf(stderr, "%llx %s\n",
+                               sym_table[i].addr, sym_table[i].sym);
+       }
+}
+
+/*
+ * Source lines
+ */
+
+static void parse_vmlinux(char *filename)
+{
+       FILE *file;
+       char command[PATH_MAX*2];
+       if (!filename)
+               return;
+
+       sprintf(command, "objdump --start-address=0x%016lx --stop-address=0x%016lx -dS %s", filter_start, filter_end, filename);
+
+       file = popen(command, "r");
+       if (!file)
+               return;
+
+       lines_tail = &lines;
+       while (!feof(file)) {
+               struct source_line *src;
+               size_t dummy = 0;
+               char *c;
+
+               src = malloc(sizeof(struct source_line));
+               assert(src != NULL);
+               memset(src, 0, sizeof(struct source_line));
+
+               if (getline(&src->line, &dummy, file) < 0)
+                       break;
+               if (!src->line)
+                       break;
+
+               c = strchr(src->line, '\n');
+               if (c)
+                       *c = 0;
+
+               src->next = NULL;
+               *lines_tail = src;
+               lines_tail = &src->next;
+
+               if (strlen(src->line)>8 && src->line[8] == ':')
+                       src->EIP = strtoull(src->line, NULL, 16);
+               if (strlen(src->line)>8 && src->line[16] == ':')
+                       src->EIP = strtoull(src->line, NULL, 16);
+       }
+       pclose(file);
+}
+
+static void record_precise_ip(uint64_t ip)
+{
+       struct source_line *line;
+
+       for (line = lines; line; line = line->next) {
+               if (line->EIP == ip)
+                       line->count++;
+               if (line->EIP > ip)
+                       break;
+       }
+}
+
+static void lookup_sym_in_vmlinux(struct sym_entry *sym)
+{
+       struct source_line *line;
+       char pattern[PATH_MAX];
+       sprintf(pattern, "<%s>:", sym->sym);
+
+       for (line = lines; line; line = line->next) {
+               if (strstr(line->line, pattern)) {
+                       sym->source = line;
+                       break;
+               }
+       }
+}
+
+static void show_lines(struct source_line *line_queue, int line_queue_count)
+{
+       int i;
+       struct source_line *line;
+
+       line = line_queue;
+       for (i = 0; i < line_queue_count; i++) {
+               printf("%8li\t%s\n", line->count, line->line);
+               line = line->next;
+       }
+}
+
+#define TRACE_COUNT     3
+
+static void show_details(struct sym_entry *sym)
+{
+       struct source_line *line;
+       struct source_line *line_queue = NULL;
+       int displayed = 0;
+       int line_queue_count = 0;
+
+       if (!sym->source)
+               lookup_sym_in_vmlinux(sym);
+       if (!sym->source)
+               return;
+
+       printf("Showing details for %s\n", sym->sym);
+
+       line = sym->source;
+       while (line) {
+               if (displayed && strstr(line->line, ">:"))
+                       break;
+
+               if (!line_queue_count)
+                       line_queue = line;
+               line_queue_count ++;
+
+               if (line->count >= count_filter) {
+                       show_lines(line_queue, line_queue_count);
+                       line_queue_count = 0;
+                       line_queue = NULL;
+               } else if (line_queue_count > TRACE_COUNT) {
+                       line_queue = line_queue->next;
+                       line_queue_count --;
+               }
+
+               line->count = 0;
+               displayed++;
+               if (displayed > 300)
+                       break;
+               line = line->next;
+       }
+}
+
+/*
+ * Binary search in the histogram table and record the hit:
+ */
+static void record_ip(uint64_t ip, int counter)
+{
+       int left_idx, middle_idx, right_idx, idx;
+       unsigned long left, middle, right;
+
+       record_precise_ip(ip);
+
+       left_idx = 0;
+       right_idx = sym_table_count-1;
+       assert(ip <= max_ip && ip >= min_ip);
+
+       while (left_idx + 1 < right_idx) {
+               middle_idx = (left_idx + right_idx) / 2;
+
+               left   = sym_table[  left_idx].addr;
+               middle = sym_table[middle_idx].addr;
+               right  = sym_table[ right_idx].addr;
+
+               if (!(left <= middle && middle <= right)) {
+                       printf("%016lx...\n%016lx...\n%016lx\n", left, middle, right);
+                       printf("%d %d %d\n", left_idx, middle_idx, right_idx);
+               }
+               assert(left <= middle && middle <= right);
+               if (!(left <= ip && ip <= right)) {
+                       printf(" left: %016lx\n", left);
+                       printf("   ip: %016lx\n", (unsigned long)ip);
+                       printf("right: %016lx\n", right);
+               }
+               assert(left <= ip && ip <= right);
+               /*
+                * [ left .... target .... middle .... right ]
+                *   => right := middle
+                */
+               if (ip < middle) {
+                       right_idx = middle_idx;
+                       continue;
+               }
+               /*
+                * [ left .... middle ... target ... right ]
+                *   => left := middle
+                */
+               left_idx = middle_idx;
+       }
+
+       idx = left_idx;
+
+       if (!sym_table[idx].skip)
+               sym_table[idx].count[counter]++;
+       else events--;
+}
+
+static void process_event(uint64_t ip, int counter)
+{
+       events++;
+
+       if (ip < min_ip || ip > max_ip) {
+               userspace_events++;
+               return;
+       }
+
+       record_ip(ip, counter);
+}
+
+static void process_options(int argc, char **argv)
+{
+       int error = 0, counter;
+
+       for (;;) {
+               int option_index = 0;
+               /** Options for getopt */
+               static struct option long_options[] = {
+                       {"count",       required_argument,      NULL, 'c'},
+                       {"cpu",         required_argument,      NULL, 'C'},
+                       {"delay",       required_argument,      NULL, 'd'},
+                       {"dump_symtab", no_argument,            NULL, 'D'},
+                       {"event",       required_argument,      NULL, 'e'},
+                       {"filter",      required_argument,      NULL, 'f'},
+                       {"group",       required_argument,      NULL, 'g'},
+                       {"help",        no_argument,            NULL, 'h'},
+                       {"nmi",         required_argument,      NULL, 'n'},
+                       {"mmap_info",   no_argument,            NULL, 'M'},
+                       {"mmap_pages",  required_argument,      NULL, 'm'},
+                       {"munmap_info", no_argument,            NULL, 'U'},
+                       {"pid",         required_argument,      NULL, 'p'},
+                       {"realtime",    required_argument,      NULL, 'r'},
+                       {"scale",       no_argument,            NULL, 'l'},
+                       {"symbol",      required_argument,      NULL, 's'},
+                       {"stat",        no_argument,            NULL, 'S'},
+                       {"vmlinux",     required_argument,      NULL, 'x'},
+                       {"zero",        no_argument,            NULL, 'z'},
+                       {"freq",        required_argument,      NULL, 'F'},
+                       {NULL,          0,                      NULL,  0 }
+               };
+               int c = getopt_long(argc, argv, "+:ac:C:d:De:f:g:hln:m:p:r:s:Sx:zMUF:",
+                                   long_options, &option_index);
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'a': system_wide                   =              1; break;
+               case 'c': default_interval              =   atoi(optarg); break;
+               case 'C':
+                       /* CPU and PID are mutually exclusive */
+                       if (tid != -1) {
+                               printf("WARNING: CPU switch overriding PID\n");
+                               sleep(1);
+                               tid = -1;
+                       }
+                       profile_cpu                     =   atoi(optarg); break;
+               case 'd': delay_secs                    =   atoi(optarg); break;
+               case 'D': dump_symtab                   =              1; break;
+
+               case 'e': error                         = parse_events(optarg); break;
+
+               case 'f': count_filter                  =   atoi(optarg); break;
+               case 'g': group                         =   atoi(optarg); break;
+               case 'h':                                 display_help(); break;
+               case 'l': scale                         =              1; break;
+               case 'n': nmi                           =   atoi(optarg); break;
+               case 'p':
+                       /* CPU and PID are mutually exclusive */
+                       if (profile_cpu != -1) {
+                               printf("WARNING: PID switch overriding CPU\n");
+                               sleep(1);
+                               profile_cpu = -1;
+                       }
+                       tid                             =   atoi(optarg); break;
+               case 'r': realtime_prio                 =   atoi(optarg); break;
+               case 's': sym_filter                    = strdup(optarg); break;
+               case 'x': vmlinux                       = strdup(optarg); break;
+               case 'z': zero                          =              1; break;
+               case 'm': mmap_pages                    =   atoi(optarg); break;
+               case 'M': use_mmap                      =              1; break;
+               case 'U': use_munmap                    =              1; break;
+               case 'F': freq = 1; default_interval    =   atoi(optarg); break;
+               default: error = 1; break;
+               }
+       }
+       if (error)
+               display_help();
+
+       if (!nr_counters) {
+               nr_counters = 1;
+               event_id[0] = 0;
+       }
+
+       for (counter = 0; counter < nr_counters; counter++) {
+               if (event_count[counter])
+                       continue;
+
+               event_count[counter] = default_interval;
+       }
+}
+
+struct mmap_data {
+       int counter;
+       void *base;
+       unsigned int mask;
+       unsigned int prev;
+};
+
+static unsigned int mmap_read_head(struct mmap_data *md)
+{
+       struct perf_counter_mmap_page *pc = md->base;
+       int head;
+
+       head = pc->data_head;
+       rmb();
+
+       return head;
+}
+
+struct timeval last_read, this_read;
+
+static void mmap_read(struct mmap_data *md)
+{
+       unsigned int head = mmap_read_head(md);
+       unsigned int old = md->prev;
+       unsigned char *data = md->base + page_size;
+       int diff;
+
+       gettimeofday(&this_read, NULL);
+
+       /*
+        * If we're further behind than half the buffer, there's a chance
+        * the writer will bite our tail and screw up the events under us.
+        *
+        * If we somehow ended up ahead of the head, we got messed up.
+        *
+        * In either case, truncate and restart at head.
+        */
+       diff = head - old;
+       if (diff > md->mask / 2 || diff < 0) {
+               struct timeval iv;
+               unsigned long msecs;
+
+               timersub(&this_read, &last_read, &iv);
+               msecs = iv.tv_sec*1000 + iv.tv_usec/1000;
+
+               fprintf(stderr, "WARNING: failed to keep up with mmap data."
+                               "  Last read %lu msecs ago.\n", msecs);
+
+               /*
+                * head points to a known good entry, start there.
+                */
+               old = head;
+       }
+
+       last_read = this_read;
+
+       for (; old != head;) {
+               struct ip_event {
+                       struct perf_event_header header;
+                       __u64 ip;
+                       __u32 pid, tid;
+               };
+               struct mmap_event {
+                       struct perf_event_header header;
+                       __u32 pid, tid;
+                       __u64 start;
+                       __u64 len;
+                       __u64 pgoff;
+                       char filename[PATH_MAX];
+               };
+
+               typedef union event_union {
+                       struct perf_event_header header;
+                       struct ip_event ip;
+                       struct mmap_event mmap;
+               } event_t;
+
+               event_t *event = (event_t *)&data[old & md->mask];
+
+               event_t event_copy;
+
+               size_t size = event->header.size;
+
+               /*
+                * Event straddles the mmap boundary -- header should always
+                * be inside due to u64 alignment of output.
+                */
+               if ((old & md->mask) + size != ((old + size) & md->mask)) {
+                       unsigned int offset = old;
+                       unsigned int len = min(sizeof(*event), size), cpy;
+                       void *dst = &event_copy;
+
+                       do {
+                               cpy = min(md->mask + 1 - (offset & md->mask), len);
+                               memcpy(dst, &data[offset & md->mask], cpy);
+                               offset += cpy;
+                               dst += cpy;
+                               len -= cpy;
+                       } while (len);
+
+                       event = &event_copy;
+               }
+
+               old += size;
+
+               if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
+                       if (event->header.type & PERF_RECORD_IP)
+                               process_event(event->ip.ip, md->counter);
+               } else {
+                       switch (event->header.type) {
+                               case PERF_EVENT_MMAP:
+                               case PERF_EVENT_MUNMAP:
+                                       printf("%s: %Lu %Lu %Lu %s\n",
+                                                       event->header.type == PERF_EVENT_MMAP
+                                                       ? "mmap" : "munmap",
+                                                       event->mmap.start,
+                                                       event->mmap.len,
+                                                       event->mmap.pgoff,
+                                                       event->mmap.filename);
+                                       break;
+                       }
+               }
+       }
+
+       md->prev = old;
+}
+
+int cmd_top(int argc, char **argv, const char *prefix)
+{
+       struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS];
+       struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
+       struct perf_counter_hw_event hw_event;
+       pthread_t thread;
+       int i, counter, group_fd, nr_poll = 0;
+       unsigned int cpu;
+       int ret;
+
+       page_size = sysconf(_SC_PAGE_SIZE);
+
+       process_options(argc, argv);
+
+       nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+       assert(nr_cpus <= MAX_NR_CPUS);
+       assert(nr_cpus >= 0);
+
+       if (tid != -1 || profile_cpu != -1)
+               nr_cpus = 1;
+
+       parse_symbols();
+       if (vmlinux && sym_filter_entry)
+               parse_vmlinux(vmlinux);
+
+       for (i = 0; i < nr_cpus; i++) {
+               group_fd = -1;
+               for (counter = 0; counter < nr_counters; counter++) {
+
+                       cpu     = profile_cpu;
+                       if (tid == -1 && profile_cpu == -1)
+                               cpu = i;
+
+                       memset(&hw_event, 0, sizeof(hw_event));
+                       hw_event.config         = event_id[counter];
+                       hw_event.irq_period     = event_count[counter];
+                       hw_event.record_type    = PERF_RECORD_IP | PERF_RECORD_TID;
+                       hw_event.nmi            = nmi;
+                       hw_event.mmap           = use_mmap;
+                       hw_event.munmap         = use_munmap;
+                       hw_event.freq           = freq;
+
+                       fd[i][counter] = sys_perf_counter_open(&hw_event, tid, cpu, group_fd, 0);
+                       if (fd[i][counter] < 0) {
+                               int err = errno;
+                               printf("kerneltop error: syscall returned with %d (%s)\n",
+                                       fd[i][counter], strerror(err));
+                               if (err == EPERM)
+                                       printf("Are you root?\n");
+                               exit(-1);
+                       }
+                       assert(fd[i][counter] >= 0);
+                       fcntl(fd[i][counter], F_SETFL, O_NONBLOCK);
+
+                       /*
+                        * First counter acts as the group leader:
+                        */
+                       if (group && group_fd == -1)
+                               group_fd = fd[i][counter];
+
+                       event_array[nr_poll].fd = fd[i][counter];
+                       event_array[nr_poll].events = POLLIN;
+                       nr_poll++;
+
+                       mmap_array[i][counter].counter = counter;
+                       mmap_array[i][counter].prev = 0;
+                       mmap_array[i][counter].mask = mmap_pages*page_size - 1;
+                       mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size,
+                                       PROT_READ, MAP_SHARED, fd[i][counter], 0);
+                       if (mmap_array[i][counter].base == MAP_FAILED) {
+                               printf("kerneltop error: failed to mmap with %d (%s)\n",
+                                               errno, strerror(errno));
+                               exit(-1);
+                       }
+               }
+       }
+
+       if (pthread_create(&thread, NULL, display_thread, NULL)) {
+               printf("Could not create display thread.\n");
+               exit(-1);
+       }
+
+       if (realtime_prio) {
+               struct sched_param param;
+
+               param.sched_priority = realtime_prio;
+               if (sched_setscheduler(0, SCHED_FIFO, &param)) {
+                       printf("Could not set realtime priority.\n");
+                       exit(-1);
+               }
+       }
+
+       while (1) {
+               int hits = events;
+
+               for (i = 0; i < nr_cpus; i++) {
+                       for (counter = 0; counter < nr_counters; counter++)
+                               mmap_read(&mmap_array[i][counter]);
+               }
+
+               if (hits == events)
+                       ret = poll(event_array, nr_poll, 100);
+       }
+
+       return 0;
+}
diff --git a/Documentation/perf_counter/builtin.h b/Documentation/perf_counter/builtin.h
new file mode 100644 (file)
index 0000000..d32318a
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef BUILTIN_H
+#define BUILTIN_H
+
+#include "util/util.h"
+#include "util/strbuf.h"
+
+extern const char perf_version_string[];
+extern const char perf_usage_string[];
+extern const char perf_more_info_string[];
+
+extern void list_common_cmds_help(void);
+extern const char *help_unknown_cmd(const char *cmd);
+extern void prune_packed_objects(int);
+extern int read_line_with_nul(char *buf, int size, FILE *file);
+extern int check_pager_config(const char *cmd);
+
+extern int cmd_help(int argc, const char **argv, const char *prefix);
+extern int cmd_record(int argc, const char **argv, const char *prefix);
+extern int cmd_stat(int argc, const char **argv, const char *prefix);
+extern int cmd_top(int argc, const char **argv, const char *prefix);
+extern int cmd_version(int argc, const char **argv, const char *prefix);
+#endif
diff --git a/Documentation/perf_counter/command-list.txt b/Documentation/perf_counter/command-list.txt
new file mode 100644 (file)
index 0000000..d15210a
--- /dev/null
@@ -0,0 +1,6 @@
+# List of known perf commands.
+# command name                         category [deprecated] [common]
+perf-record                             mainporcelain common
+perf-stat                               mainporcelain common
+perf-top                                mainporcelain common
+
diff --git a/Documentation/perf_counter/design.txt b/Documentation/perf_counter/design.txt
new file mode 100644 (file)
index 0000000..9930c4b
--- /dev/null
@@ -0,0 +1,449 @@
+
+Performance Counters for Linux
+------------------------------
+
+Performance counters are special hardware registers available on most modern
+CPUs. These registers count the number of certain types of hw events: such
+as instructions executed, cachemisses suffered, or branches mis-predicted -
+without slowing down the kernel or applications. These registers can also
+trigger interrupts when a threshold number of events have passed - and can
+thus be used to profile the code that runs on that CPU.
+
+The Linux Performance Counter subsystem provides an abstraction of these
+hardware capabilities. It provides per task and per CPU counters, counter
+groups, and it provides event capabilities on top of those.  It
+provides "virtual" 64-bit counters, regardless of the width of the
+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_counter_open()
+system call:
+
+   int sys_perf_counter_open(struct perf_counter_hw_event *hw_event_uptr,
+                            pid_t pid, int cpu, int group_fd,
+                            unsigned long flags);
+
+The syscall returns the new fd. The fd can be used via the normal
+VFS system calls: read() can be used to read the counter, fcntl()
+can be used to set the blocking mode, etc.
+
+Multiple counters can be kept open at a time, and the counters
+can be poll()ed.
+
+When creating a new counter fd, 'perf_counter_hw_event' is:
+
+struct perf_counter_hw_event {
+        /*
+         * The MSB of the config word signifies if the rest contains cpu
+         * specific (raw) counter configuration data, if unset, the next
+         * 7 bits are an event type and the rest of the bits are the event
+         * identifier.
+         */
+        __u64                   config;
+
+        __u64                   irq_period;
+        __u32                   record_type;
+        __u32                   read_format;
+
+        __u64                   disabled       :  1, /* off by default        */
+                                nmi            :  1, /* NMI sampling          */
+                                inherit        :  1, /* children inherit it   */
+                                pinned         :  1, /* must always be on PMU */
+                                exclusive      :  1, /* only group on PMU     */
+                                exclude_user   :  1, /* don't count user      */
+                                exclude_kernel :  1, /* ditto kernel          */
+                                exclude_hv     :  1, /* ditto hypervisor      */
+                                exclude_idle   :  1, /* don't count when idle */
+                                mmap           :  1, /* include mmap data     */
+                                munmap         :  1, /* include munmap data   */
+                                comm           :  1, /* include comm data     */
+
+                                __reserved_1   : 52;
+
+        __u32                   extra_config_len;
+        __u32                   wakeup_events;  /* wakeup every n events */
+
+        __u64                   __reserved_2;
+        __u64                   __reserved_3;
+};
+
+The 'config' field specifies what the counter should count.  It
+is divided into 3 bit-fields:
+
+raw_type: 1 bit   (most significant bit)       0x8000_0000_0000_0000
+type:    7 bits  (next most significant)       0x7f00_0000_0000_0000
+event_id: 56 bits (least significant)          0x00ff_ffff_ffff_ffff
+
+If 'raw_type' is 1, then the counter will count a hardware event
+specified by the remaining 63 bits of event_config.  The encoding is
+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 {
+       PERF_TYPE_HARDWARE              = 0,
+       PERF_TYPE_SOFTWARE              = 1,
+       PERF_TYPE_TRACEPOINT            = 2,
+};
+
+A counter of PERF_TYPE_HARDWARE will count the hardware event
+specified by 'event_id':
+
+/*
+ * Generalized performance counter event types, used by the hw_event.event_id
+ * parameter of the sys_perf_counter_open() syscall:
+ */
+enum hw_event_ids {
+       /*
+        * Common hardware events, generalized by the kernel:
+        */
+       PERF_COUNT_CPU_CYCLES           = 0,
+       PERF_COUNT_INSTRUCTIONS         = 1,
+       PERF_COUNT_CACHE_REFERENCES     = 2,
+       PERF_COUNT_CACHE_MISSES         = 3,
+       PERF_COUNT_BRANCH_INSTRUCTIONS  = 4,
+       PERF_COUNT_BRANCH_MISSES        = 5,
+       PERF_COUNT_BUS_CYCLES           = 6,
+};
+
+These are standardized types of events that work relatively uniformly
+on all CPUs that implement Performance Counters support under Linux,
+although there may be variations (e.g., different CPUs might count
+cache references and misses at different levels of the cache hierarchy).
+If a CPU is not able to count the selected event, then the system call
+will return -EINVAL.
+
+More hw_event_types are supported as well, but they are CPU-specific
+and accessed as raw events.  For example, to count "External bus
+cycles while bus lock signal asserted" events on Intel Core CPUs, pass
+in a 0x4064 event_id value and set hw_event.raw_type to 1.
+
+A counter of type PERF_TYPE_SOFTWARE will count one of the available
+software events, selected by 'event_id':
+
+/*
+ * Special "software" counters provided by the kernel, even if the hardware
+ * does not support performance counters. These counters measure various
+ * physical and sw events of the kernel (and allow the profiling of them as
+ * well):
+ */
+enum sw_event_ids {
+       PERF_COUNT_CPU_CLOCK            = 0,
+       PERF_COUNT_TASK_CLOCK           = 1,
+       PERF_COUNT_PAGE_FAULTS          = 2,
+       PERF_COUNT_CONTEXT_SWITCHES     = 3,
+       PERF_COUNT_CPU_MIGRATIONS       = 4,
+       PERF_COUNT_PAGE_FAULTS_MIN      = 5,
+       PERF_COUNT_PAGE_FAULTS_MAJ      = 6,
+};
+
+Counters of the type PERF_TYPE_TRACEPOINT are available when the ftrace event
+tracer is available, and event_id values can be obtained from
+/debug/tracing/events/*/*/id
+
+
+Counters come in two flavours: counting counters and sampling
+counters.  A "counting" counter is one that is used for counting the
+number of events that occur, and is characterised by having
+irq_period = 0.
+
+
+A read() on a counter returns the current value of the counter and possible
+additional values as specified by 'read_format', each value is a u64 (8 bytes)
+in size.
+
+/*
+ * Bits that can be set in hw_event.read_format to request that
+ * reads on the counter should return the indicated quantities,
+ * in increasing order of bit value, after the counter value.
+ */
+enum perf_counter_read_format {
+        PERF_FORMAT_TOTAL_TIME_ENABLED  =  1,
+        PERF_FORMAT_TOTAL_TIME_RUNNING  =  2,
+};
+
+Using these additional values one can establish the overcommit ratio for a
+particular counter allowing one to take the round-robin scheduling effect
+into account.
+
+
+A "sampling" counter is one that is set up to generate an interrupt
+every N events, where N is given by 'irq_period'.  A sampling counter
+has irq_period > 0. The record_type controls what data is recorded on each
+interrupt:
+
+/*
+ * Bits that can be set in hw_event.record_type to request information
+ * in the overflow packets.
+ */
+enum perf_counter_record_format {
+        PERF_RECORD_IP          = 1U << 0,
+        PERF_RECORD_TID         = 1U << 1,
+        PERF_RECORD_TIME        = 1U << 2,
+        PERF_RECORD_ADDR        = 1U << 3,
+        PERF_RECORD_GROUP       = 1U << 4,
+        PERF_RECORD_CALLCHAIN   = 1U << 5,
+};
+
+Such (and other) events will be recorded in a ring-buffer, which is
+available to user-space using mmap() (see below).
+
+The 'disabled' bit specifies whether the counter starts out disabled
+or enabled.  If it is initially disabled, it can be enabled by ioctl
+or prctl (see below).
+
+The 'nmi' bit specifies, for hardware events, whether the counter
+should be set up to request non-maskable interrupts (NMIs) or normal
+interrupts.  This bit is ignored if the user doesn't have
+CAP_SYS_ADMIN privilege (i.e. is not root) or if the CPU doesn't
+generate NMIs from hardware counters.
+
+The 'inherit' bit, if set, specifies that this counter should count
+events on descendant tasks as well as the task specified.  This only
+applies to new descendents, not to any existing descendents at the
+time the counter is created (nor to any new descendents of existing
+descendents).
+
+The 'pinned' bit, if set, specifies that the counter should always be
+on the CPU if at all possible.  It only applies to hardware counters
+and only to group leaders.  If a pinned counter cannot be put onto the
+CPU (e.g. because there are not enough hardware counters or because of
+a conflict with some other event), then the counter goes into an
+'error' state, where reads return end-of-file (i.e. read() returns 0)
+until the counter is subsequently enabled or disabled.
+
+The 'exclusive' bit, if set, specifies that when this counter's group
+is on the CPU, it should be the only group using the CPU's counters.
+In future, this will allow sophisticated monitoring programs to supply
+extra configuration information via 'extra_config_len' to exploit
+advanced features of the CPU's Performance Monitor Unit (PMU) that are
+not otherwise accessible and that might disrupt other hardware
+counters.
+
+The 'exclude_user', 'exclude_kernel' and 'exclude_hv' bits provide a
+way to request that counting of events be restricted to times when the
+CPU is in user, kernel and/or hypervisor mode.
+
+The 'mmap' and 'munmap' bits allow recording of PROT_EXEC mmap/munmap
+operations, these can be used to relate userspace IP addresses to actual
+code, even after the mapping (or even the whole process) is gone,
+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_counter_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
+ current task.
+
+ pid > 0: the counter is attached to a specific task (if the current task
+ has sufficient privilege to do so)
+
+ pid < 0: all tasks are counted (per cpu counters)
+
+The 'cpu' parameter allows a counter to be made specific to a CPU:
+
+ cpu >= 0: the counter is restricted to a specific CPU
+ cpu == -1: the counter counts on all CPUs
+
+(Note: the combination of 'pid == -1' and 'cpu == -1' is not valid.)
+
+A 'pid > 0' and 'cpu == -1' counter is a per task counter that counts
+events of that task and 'follows' that task to whatever CPU the task
+gets schedule to. Per task counters can be created by any user, for
+their own tasks.
+
+A 'pid == -1' and 'cpu == x' counter is a per CPU counter that counts
+all events on CPU-x. Per CPU counters need CAP_SYS_ADMIN privilege.
+
+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_counter_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
+considered to be a group with only 1 member.)
+
+A counter group is scheduled onto the CPU as a unit, that is, it will
+only be put onto the CPU if all of the counters in the group can be
+put onto the CPU.  This means that the values of the member counters
+can be meaningfully compared, added, divided (to get ratios), etc.,
+with each other, since they have counted events for the same set of
+executed instructions.
+
+
+Like stated, asynchronous events, like counter overflow or PROT_EXEC mmap
+tracking are logged into a ring-buffer. This ring-buffer is created and
+accessed through mmap().
+
+The mmap size should be 1+2^n pages, where the first page is a meta-data page
+(struct perf_counter_mmap_page) that contains various bits of information such
+as where the ring-buffer head is.
+
+/*
+ * Structure of the page that can be mapped via mmap
+ */
+struct perf_counter_mmap_page {
+        __u32   version;                /* version number of this structure */
+        __u32   compat_version;         /* lowest version this is compat with */
+
+        /*
+         * Bits needed to read the hw counters in user-space.
+         *
+         *   u32 seq;
+         *   s64 count;
+         *
+         *   do {
+         *     seq = pc->lock;
+         *
+         *     barrier()
+         *     if (pc->index) {
+         *       count = pmc_read(pc->index - 1);
+         *       count += pc->offset;
+         *     } else
+         *       goto regular_read;
+         *
+         *     barrier();
+         *   } while (pc->lock != seq);
+         *
+         * NOTE: for obvious reason this only works on self-monitoring
+         *       processes.
+         */
+        __u32   lock;                   /* seqlock for synchronization */
+        __u32   index;                  /* hardware counter identifier */
+        __s64   offset;                 /* add to hardware counter value */
+
+        /*
+         * Control data for the mmap() data buffer.
+         *
+         * User-space reading this value should issue an rmb(), on SMP capable
+         * platforms, after reading this value -- see perf_counter_wakeup().
+         */
+        __u32   data_head;              /* head in the data section */
+};
+
+NOTE: the hw-counter userspace bits are arch specific and are currently only
+      implemented on powerpc.
+
+The following 2^n pages are the ring-buffer which contains events of the form:
+
+#define PERF_EVENT_MISC_KERNEL          (1 << 0)
+#define PERF_EVENT_MISC_USER            (1 << 1)
+#define PERF_EVENT_MISC_OVERFLOW        (1 << 2)
+
+struct perf_event_header {
+        __u32   type;
+        __u16   misc;
+        __u16   size;
+};
+
+enum perf_event_type {
+
+        /*
+         * The MMAP events record the PROT_EXEC mappings so that we can
+         * correlate userspace IPs to code. They have the following structure:
+         *
+         * struct {
+         *      struct perf_event_header        header;
+         *
+         *      u32                             pid, tid;
+         *      u64                             addr;
+         *      u64                             len;
+         *      u64                             pgoff;
+         *      char                            filename[];
+         * };
+         */
+        PERF_EVENT_MMAP                 = 1,
+        PERF_EVENT_MUNMAP               = 2,
+
+        /*
+         * struct {
+         *      struct perf_event_header        header;
+         *
+         *      u32                             pid, tid;
+         *      char                            comm[];
+         * };
+         */
+        PERF_EVENT_COMM                 = 3,
+
+        /*
+         * When header.misc & PERF_EVENT_MISC_OVERFLOW the event_type field
+         * will be PERF_RECORD_*
+         *
+         * struct {
+         *      struct perf_event_header        header;
+         *
+         *      { u64                   ip;       } && PERF_RECORD_IP
+         *      { u32                   pid, tid; } && PERF_RECORD_TID
+         *      { u64                   time;     } && PERF_RECORD_TIME
+         *      { u64                   addr;     } && PERF_RECORD_ADDR
+         *
+         *      { u64                   nr;
+         *        { u64 event, val; }   cnt[nr];  } && PERF_RECORD_GROUP
+         *
+         *      { u16                   nr,
+         *                              hv,
+         *                              kernel,
+         *                              user;
+         *        u64                   ips[nr];  } && PERF_RECORD_CALLCHAIN
+         * };
+         */
+};
+
+NOTE: PERF_RECORD_CALLCHAIN is arch specific and currently only implemented
+      on x86.
+
+Notification of new events is possible through poll()/select()/epoll() and
+fcntl() managing signals.
+
+Normally a notification is generated for every page filled, however one can
+additionally set perf_counter_hw_event.wakeup_events to generate one every
+so many counter overflow events.
+
+Future work will include a splice() interface to the ring-buffer.
+
+
+Counters can be enabled and disabled in two ways: via ioctl and via
+prctl.  When a counter is disabled, it doesn't count or generate
+events but does continue to exist and maintain its count value.
+
+An individual counter or counter group can be enabled with
+
+       ioctl(fd, PERF_COUNTER_IOC_ENABLE);
+
+or disabled with
+
+       ioctl(fd, PERF_COUNTER_IOC_DISABLE);
+
+Enabling or disabling the leader of a group enables or disables the
+whole group; that is, while the group leader is disabled, none of the
+counters in the group will count.  Enabling or disabling a member of a
+group other than the leader only affects that counter - disabling an
+non-leader stops that counter from counting but doesn't affect any
+other counter.
+
+Additionally, non-inherited overflow counters can use
+
+       ioctl(fd, PERF_COUNTER_IOC_REFRESH, nr);
+
+to enable a counter for 'nr' events, after which it gets disabled again.
+
+A process can enable or disable all the counter groups that are
+attached to it, using prctl:
+
+       prctl(PR_TASK_PERF_COUNTERS_ENABLE);
+
+       prctl(PR_TASK_PERF_COUNTERS_DISABLE);
+
+This applies to all counters on the current process, whether created
+by this process or by another, and doesn't affect any counters that
+this process has created on other processes.  It only enables or
+disables the group leaders, not any other members in the groups.
+
diff --git a/Documentation/perf_counter/perf-report.cc b/Documentation/perf_counter/perf-report.cc
new file mode 100644 (file)
index 0000000..8855107
--- /dev/null
@@ -0,0 +1,515 @@
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <getopt.h>
+#include <assert.h>
+
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <linux/unistd.h>
+#include <linux/types.h>
+
+#include "../../include/linux/perf_counter.h"
+
+#include <set>
+#include <map>
+#include <string>
+
+
+#define SHOW_KERNEL    1
+#define SHOW_USER      2
+#define SHOW_HV                4
+
+static char            const *input_name = "output.perf";
+static int             input;
+static int             show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
+
+static unsigned long   page_size;
+static unsigned long   mmap_window = 32;
+
+struct ip_event {
+       struct perf_event_header header;
+       __u64 ip;
+       __u32 pid, tid;
+};
+struct mmap_event {
+       struct perf_event_header header;
+       __u32 pid, tid;
+       __u64 start;
+       __u64 len;
+       __u64 pgoff;
+       char filename[PATH_MAX];
+};
+struct comm_event {
+       struct perf_event_header header;
+       __u32 pid,tid;
+       char comm[16];
+};
+
+typedef union event_union {
+       struct perf_event_header header;
+       struct ip_event ip;
+       struct mmap_event mmap;
+       struct comm_event comm;
+} event_t;
+
+struct section {
+       uint64_t start;
+       uint64_t end;
+
+       uint64_t offset;
+
+       std::string name;
+
+       section() { };
+
+       section(uint64_t stab) : end(stab) { };
+
+       section(uint64_t start, uint64_t size, uint64_t offset, std::string name) :
+               start(start), end(start + size), offset(offset), name(name)
+       { };
+
+       bool operator < (const struct section &s) const {
+               return end < s.end;
+       };
+};
+
+typedef std::set<struct section> sections_t;
+
+struct symbol {
+       uint64_t start;
+       uint64_t end;
+
+       std::string name;
+
+       symbol() { };
+
+       symbol(uint64_t ip) : start(ip) { }
+
+       symbol(uint64_t start, uint64_t len, std::string name) :
+               start(start), end(start + len), name(name)
+       { };
+
+       bool operator < (const struct symbol &s) const {
+               return start < s.start;
+       };
+};
+
+typedef std::set<struct symbol> symbols_t;
+
+struct dso {
+       sections_t sections;
+       symbols_t syms;
+};
+
+static std::map<std::string, struct dso> dsos;
+
+static void load_dso_sections(std::string dso_name)
+{
+       struct dso &dso = dsos[dso_name];
+
+       std::string cmd = "readelf -DSW " + dso_name;
+
+       FILE *file = popen(cmd.c_str(), "r");
+       if (!file) {
+               perror("failed to open pipe");
+               exit(-1);
+       }
+
+       char *line = NULL;
+       size_t n = 0;
+
+       while (!feof(file)) {
+               uint64_t addr, off, size;
+               char name[32];
+
+               if (getline(&line, &n, file) < 0)
+                       break;
+               if (!line)
+                       break;
+
+               if (sscanf(line, "  [%*2d] %16s %*14s %Lx %Lx %Lx",
+                                       name, &addr, &off, &size) == 4) {
+
+                       dso.sections.insert(section(addr, size, addr - off, name));
+               }
+#if 0
+               /*
+                * for reading readelf symbols (-s), however these don't seem
+                * to include nearly everything, so use nm for that.
+                */
+               if (sscanf(line, " %*4d %*3d: %Lx %5Lu %*7s %*6s %*7s %3d %s",
+                          &start, &size, &section, sym) == 4) {
+
+                       start -= dso.section_offsets[section];
+
+                       dso.syms.insert(symbol(start, size, std::string(sym)));
+               }
+#endif
+       }
+       pclose(file);
+}
+
+static void load_dso_symbols(std::string dso_name, std::string args)
+{
+       struct dso &dso = dsos[dso_name];
+
+       std::string cmd = "nm -nSC " + args + " " + dso_name;
+
+       FILE *file = popen(cmd.c_str(), "r");
+       if (!file) {
+               perror("failed to open pipe");
+               exit(-1);
+       }
+
+       char *line = NULL;
+       size_t n = 0;
+
+       while (!feof(file)) {
+               uint64_t start, size;
+               char c;
+               char sym[1024];
+
+               if (getline(&line, &n, file) < 0)
+                       break;
+               if (!line)
+                       break;
+
+
+               if (sscanf(line, "%Lx %Lx %c %s", &start, &size, &c, sym) == 4) {
+                       sections_t::const_iterator si =
+                               dso.sections.upper_bound(section(start));
+                       if (si == dso.sections.end()) {
+                               printf("symbol in unknown section: %s\n", sym);
+                               continue;
+                       }
+
+                       start -= si->offset;
+
+                       dso.syms.insert(symbol(start, size, sym));
+               }
+       }
+       pclose(file);
+}
+
+static void load_dso(std::string dso_name)
+{
+       load_dso_sections(dso_name);
+       load_dso_symbols(dso_name, "-D"); /* dynamic symbols */
+       load_dso_symbols(dso_name, "");   /* regular ones */
+}
+
+void load_kallsyms(void)
+{
+       struct dso &dso = dsos["[kernel]"];
+
+       FILE *file = fopen("/proc/kallsyms", "r");
+       if (!file) {
+               perror("failed to open kallsyms");
+               exit(-1);
+       }
+
+       char *line;
+       size_t n;
+
+       while (!feof(file)) {
+               uint64_t start;
+               char c;
+               char sym[1024000];
+
+               if (getline(&line, &n, file) < 0)
+                       break;
+               if (!line)
+                       break;
+
+               if (sscanf(line, "%Lx %c %s", &start, &c, sym) == 3)
+                       dso.syms.insert(symbol(start, 0x1000000, std::string(sym)));
+       }
+       fclose(file);
+}
+
+struct map {
+       uint64_t start;
+       uint64_t end;
+       uint64_t pgoff;
+
+       std::string dso;
+
+       map() { };
+
+       map(uint64_t ip) : end(ip) { }
+
+       map(mmap_event *mmap) {
+               start = mmap->start;
+               end = mmap->start + mmap->len;
+               pgoff = mmap->pgoff;
+
+               dso = std::string(mmap->filename);
+
+               if (dsos.find(dso) == dsos.end())
+                       load_dso(dso);
+       };
+
+       bool operator < (const struct map &m) const {
+               return end < m.end;
+       };
+};
+
+typedef std::set<struct map> maps_t;
+
+static std::map<int, maps_t> maps;
+
+static std::map<int, std::string> comms;
+
+static std::map<std::string, int> hist;
+static std::multimap<int, std::string> rev_hist;
+
+static std::string resolve_comm(int pid)
+{
+       std::string comm;
+
+       std::map<int, std::string>::const_iterator ci = comms.find(pid);
+       if (ci != comms.end()) {
+               comm = ci->second;
+       } else {
+               char pid_str[30];
+
+               sprintf(pid_str, ":%d", pid);
+               comm = pid_str;
+       }
+
+       return comm;
+}
+
+static std::string resolve_user_symbol(int pid, uint64_t ip)
+{
+       std::string sym = "<unknown>";
+
+       maps_t &m = maps[pid];
+       maps_t::const_iterator mi = m.upper_bound(map(ip));
+       if (mi == m.end())
+               return sym;
+
+       ip -= mi->start + mi->pgoff;
+
+       symbols_t &s = dsos[mi->dso].syms;
+       symbols_t::const_iterator si = s.upper_bound(symbol(ip));
+
+       sym = mi->dso + ": <unknown>";
+
+       if (si == s.begin())
+               return sym;
+       si--;
+
+       if (si->start <= ip && ip < si->end)
+               sym = mi->dso + ": " + si->name;
+#if 0
+       else if (si->start <= ip)
+               sym = mi->dso + ": ?" + si->name;
+#endif
+
+       return sym;
+}
+
+static std::string resolve_kernel_symbol(uint64_t ip)
+{
+       std::string sym = "<unknown>";
+
+       symbols_t &s = dsos["[kernel]"].syms;
+       symbols_t::const_iterator si = s.upper_bound(symbol(ip));
+
+       if (si == s.begin())
+               return sym;
+       si--;
+
+       if (si->start <= ip && ip < si->end)
+               sym = si->name;
+
+       return sym;
+}
+
+static void display_help(void)
+{
+       printf(
+       "Usage: perf-report [<options>]\n"
+       " -i file   --input=<file>      # input file\n"
+       );
+
+       exit(0);
+}
+
+static void process_options(int argc, char *argv[])
+{
+       int error = 0;
+
+       for (;;) {
+               int option_index = 0;
+               /** Options for getopt */
+               static struct option long_options[] = {
+                       {"input",       required_argument,      NULL, 'i'},
+                       {"no-user",     no_argument,            NULL, 'u'},
+                       {"no-kernel",   no_argument,            NULL, 'k'},
+                       {"no-hv",       no_argument,            NULL, 'h'},
+                       {NULL,          0,                      NULL,  0 }
+               };
+               int c = getopt_long(argc, argv, "+:i:kuh",
+                                   long_options, &option_index);
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'i': input_name                    = strdup(optarg); break;
+               case 'k': show_mask &= ~SHOW_KERNEL; break;
+               case 'u': show_mask &= ~SHOW_USER; break;
+               case 'h': show_mask &= ~SHOW_HV; break;
+               default: error = 1; break;
+               }
+       }
+
+       if (error)
+               display_help();
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned long offset = 0;
+       unsigned long head = 0;
+       struct stat stat;
+       char *buf;
+       event_t *event;
+       int ret;
+       unsigned long total = 0;
+
+       page_size = getpagesize();
+
+       process_options(argc, argv);
+
+       input = open(input_name, O_RDONLY);
+       if (input < 0) {
+               perror("failed to open file");
+               exit(-1);
+       }
+
+       ret = fstat(input, &stat);
+       if (ret < 0) {
+               perror("failed to stat file");
+               exit(-1);
+       }
+
+       if (!stat.st_size) {
+               fprintf(stderr, "zero-sized file, nothing to do!\n");
+               exit(0);
+       }
+
+       load_kallsyms();
+
+remap:
+       buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
+                          MAP_SHARED, input, offset);
+       if (buf == MAP_FAILED) {
+               perror("failed to mmap file");
+               exit(-1);
+       }
+
+more:
+       event = (event_t *)(buf + head);
+
+       if (head + event->header.size >= page_size * mmap_window) {
+               unsigned long shift = page_size * (head / page_size);
+               int ret;
+
+               ret = munmap(buf, page_size * mmap_window);
+               assert(ret == 0);
+
+               offset += shift;
+               head -= shift;
+               goto remap;
+       }
+
+
+       if (!event->header.size) {
+               fprintf(stderr, "zero-sized event at file offset %ld\n", offset + head);
+               fprintf(stderr, "skipping %ld bytes of events.\n", stat.st_size - offset - head);
+               goto done;
+       }
+
+       head += event->header.size;
+
+       if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
+               std::string comm, sym, level;
+               int show = 0;
+               char output[1024];
+
+               if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
+                       show |= SHOW_KERNEL;
+                       level = " [k] ";
+                       sym = resolve_kernel_symbol(event->ip.ip);
+               } else if (event->header.misc & PERF_EVENT_MISC_USER) {
+                       show |= SHOW_USER;
+                       level = " [.] ";
+                       sym = resolve_user_symbol(event->ip.pid, event->ip.ip);
+               } else {
+                       show |= SHOW_HV;
+                       level = " [H] ";
+               }
+
+               if (show & show_mask) {
+                       comm = resolve_comm(event->ip.pid);
+                       snprintf(output, sizeof(output), "%16s %s %s",
+                                       comm.c_str(), level.c_str(), sym.c_str());
+                       hist[output]++;
+               }
+
+               total++;
+
+       } else switch (event->header.type) {
+       case PERF_EVENT_MMAP:
+               maps[event->mmap.pid].insert(map(&event->mmap));
+               break;
+
+       case PERF_EVENT_COMM:
+               comms[event->comm.pid] = std::string(event->comm.comm);
+               break;
+       }
+
+       if (offset + head < stat.st_size)
+               goto more;
+
+done:
+
+       close(input);
+
+       std::map<std::string, int>::iterator hi = hist.begin();
+
+       while (hi != hist.end()) {
+               rev_hist.insert(std::pair<int, std::string>(hi->second, hi->first));
+               hist.erase(hi++);
+       }
+
+       std::multimap<int, std::string>::const_iterator ri = rev_hist.begin();
+
+       while (ri != rev_hist.end()) {
+               printf(" %5.2f %s\n", (100.0 * ri->first)/total, ri->second.c_str());
+               ri++;
+       }
+
+       return 0;
+}
+
diff --git a/Documentation/perf_counter/perf.c b/Documentation/perf_counter/perf.c
new file mode 100644 (file)
index 0000000..594d270
--- /dev/null
@@ -0,0 +1,414 @@
+#include "builtin.h"
+#include "util/exec_cmd.h"
+#include "util/cache.h"
+#include "util/quote.h"
+#include "util/run-command.h"
+
+const char perf_usage_string[] =
+       "perf [--version] [--help] COMMAND [ARGS]";
+
+const char perf_more_info_string[] =
+       "See 'perf help COMMAND' for more information on a specific command.";
+
+static int use_pager = -1;
+struct pager_config {
+       const char *cmd;
+       int val;
+};
+
+static int pager_command_config(const char *var, const char *value, void *data)
+{
+       struct pager_config *c = data;
+       if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd))
+               c->val = perf_config_bool(var, value);
+       return 0;
+}
+
+/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
+int check_pager_config(const char *cmd)
+{
+       struct pager_config c;
+       c.cmd = cmd;
+       c.val = -1;
+       perf_config(pager_command_config, &c);
+       return c.val;
+}
+
+static void commit_pager_choice(void) {
+       switch (use_pager) {
+       case 0:
+               setenv("PERF_PAGER", "cat", 1);
+               break;
+       case 1:
+               /* setup_pager(); */
+               break;
+       default:
+               break;
+       }
+}
+
+static int handle_options(const char*** argv, int* argc, int* envchanged)
+{
+       int handled = 0;
+
+       while (*argc > 0) {
+               const char *cmd = (*argv)[0];
+               if (cmd[0] != '-')
+                       break;
+
+               /*
+                * For legacy reasons, the "version" and "help"
+                * commands can be written with "--" prepended
+                * to make them look like flags.
+                */
+               if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
+                       break;
+
+               /*
+                * Check remaining flags.
+                */
+               if (!prefixcmp(cmd, "--exec-path")) {
+                       cmd += 11;
+                       if (*cmd == '=')
+                               perf_set_argv_exec_path(cmd + 1);
+                       else {
+                               puts(perf_exec_path());
+                               exit(0);
+                       }
+               } else if (!strcmp(cmd, "--html-path")) {
+                       puts(system_path(PERF_HTML_PATH));
+                       exit(0);
+               } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
+                       use_pager = 1;
+               } else if (!strcmp(cmd, "--no-pager")) {
+                       use_pager = 0;
+                       if (envchanged)
+                               *envchanged = 1;
+               } else if (!strcmp(cmd, "--perf-dir")) {
+                       if (*argc < 2) {
+                               fprintf(stderr, "No directory given for --perf-dir.\n" );
+                               usage(perf_usage_string);
+                       }
+                       setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1);
+                       if (envchanged)
+                               *envchanged = 1;
+                       (*argv)++;
+                       (*argc)--;
+                       handled++;
+               } else if (!prefixcmp(cmd, "--perf-dir=")) {
+                       setenv(PERF_DIR_ENVIRONMENT, cmd + 10, 1);
+                       if (envchanged)
+                               *envchanged = 1;
+               } else if (!strcmp(cmd, "--work-tree")) {
+                       if (*argc < 2) {
+                               fprintf(stderr, "No directory given for --work-tree.\n" );
+                               usage(perf_usage_string);
+                       }
+                       setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
+                       if (envchanged)
+                               *envchanged = 1;
+                       (*argv)++;
+                       (*argc)--;
+               } else if (!prefixcmp(cmd, "--work-tree=")) {
+                       setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
+                       if (envchanged)
+                               *envchanged = 1;
+               } else {
+                       fprintf(stderr, "Unknown option: %s\n", cmd);
+                       usage(perf_usage_string);
+               }
+
+               (*argv)++;
+               (*argc)--;
+               handled++;
+       }
+       return handled;
+}
+
+static int handle_alias(int *argcp, const char ***argv)
+{
+       int envchanged = 0, ret = 0, saved_errno = errno;
+       int count, option_count;
+       const char** new_argv;
+       const char *alias_command;
+       char *alias_string;
+
+       alias_command = (*argv)[0];
+       alias_string = alias_lookup(alias_command);
+       if (alias_string) {
+               if (alias_string[0] == '!') {
+                       if (*argcp > 1) {
+                               struct strbuf buf;
+
+                               strbuf_init(&buf, PATH_MAX);
+                               strbuf_addstr(&buf, alias_string);
+                               sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
+                               free(alias_string);
+                               alias_string = buf.buf;
+                       }
+                       ret = system(alias_string + 1);
+                       if (ret >= 0 && WIFEXITED(ret) &&
+                           WEXITSTATUS(ret) != 127)
+                               exit(WEXITSTATUS(ret));
+                       die("Failed to run '%s' when expanding alias '%s'",
+                           alias_string + 1, alias_command);
+               }
+               count = split_cmdline(alias_string, &new_argv);
+               if (count < 0)
+                       die("Bad alias.%s string", alias_command);
+               option_count = handle_options(&new_argv, &count, &envchanged);
+               if (envchanged)
+                       die("alias '%s' changes environment variables\n"
+                                "You can use '!perf' in the alias to do this.",
+                                alias_command);
+               memmove(new_argv - option_count, new_argv,
+                               count * sizeof(char *));
+               new_argv -= option_count;
+
+               if (count < 1)
+                       die("empty alias for %s", alias_command);
+
+               if (!strcmp(alias_command, new_argv[0]))
+                       die("recursive alias: %s", alias_command);
+
+               new_argv = realloc(new_argv, sizeof(char*) *
+                                   (count + *argcp + 1));
+               /* insert after command name */
+               memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp);
+               new_argv[count+*argcp] = NULL;
+
+               *argv = new_argv;
+               *argcp += count - 1;
+
+               ret = 1;
+       }
+
+       errno = saved_errno;
+
+       return ret;
+}
+
+const char perf_version_string[] = PERF_VERSION;
+
+#define RUN_SETUP      (1<<0)
+#define USE_PAGER      (1<<1)
+/*
+ * require working tree to be present -- anything uses this needs
+ * RUN_SETUP for reading from the configuration file.
+ */
+#define NEED_WORK_TREE (1<<2)
+
+struct cmd_struct {
+       const char *cmd;
+       int (*fn)(int, const char **, const char *);
+       int option;
+};
+
+static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
+{
+       int status;
+       struct stat st;
+       const char *prefix;
+
+       prefix = NULL;
+       if (p->option & RUN_SETUP)
+               prefix = NULL; /* setup_perf_directory(); */
+
+       if (use_pager == -1 && p->option & RUN_SETUP)
+               use_pager = check_pager_config(p->cmd);
+       if (use_pager == -1 && p->option & USE_PAGER)
+               use_pager = 1;
+       commit_pager_choice();
+
+       if (p->option & NEED_WORK_TREE)
+               /* setup_work_tree() */;
+
+       status = p->fn(argc, argv, prefix);
+       if (status)
+               return status & 0xff;
+
+       /* Somebody closed stdout? */
+       if (fstat(fileno(stdout), &st))
+               return 0;
+       /* Ignore write errors for pipes and sockets.. */
+       if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
+               return 0;
+
+       /* Check for ENOSPC and EIO errors.. */
+       if (fflush(stdout))
+               die("write failure on standard output: %s", strerror(errno));
+       if (ferror(stdout))
+               die("unknown write failure on standard output");
+       if (fclose(stdout))
+               die("close failed on standard output: %s", strerror(errno));
+       return 0;
+}
+
+static void handle_internal_command(int argc, const char **argv)
+{
+       const char *cmd = argv[0];
+       static struct cmd_struct commands[] = {
+               { "help", cmd_help, 0 },
+               { "record", cmd_record, 0 },
+               { "stat", cmd_stat, 0 },
+               { "top", cmd_top, 0 },
+               { "version", cmd_version, 0 },
+       };
+       int i;
+       static const char ext[] = STRIP_EXTENSION;
+
+       if (sizeof(ext) > 1) {
+               i = strlen(argv[0]) - strlen(ext);
+               if (i > 0 && !strcmp(argv[0] + i, ext)) {
+                       char *argv0 = strdup(argv[0]);
+                       argv[0] = cmd = argv0;
+                       argv0[i] = '\0';
+               }
+       }
+
+       /* Turn "perf cmd --help" into "perf help cmd" */
+       if (argc > 1 && !strcmp(argv[1], "--help")) {
+               argv[1] = argv[0];
+               argv[0] = cmd = "help";
+       }
+
+       for (i = 0; i < ARRAY_SIZE(commands); i++) {
+               struct cmd_struct *p = commands+i;
+               if (strcmp(p->cmd, cmd))
+                       continue;
+               exit(run_builtin(p, argc, argv));
+       }
+}
+
+static void execv_dashed_external(const char **argv)
+{
+       struct strbuf cmd = STRBUF_INIT;
+       const char *tmp;
+       int status;
+
+       strbuf_addf(&cmd, "perf-%s", argv[0]);
+
+       /*
+        * argv[0] must be the perf command, but the argv array
+        * belongs to the caller, and may be reused in
+        * subsequent loop iterations. Save argv[0] and
+        * restore it on error.
+        */
+       tmp = argv[0];
+       argv[0] = cmd.buf;
+
+       /*
+        * if we fail because the command is not found, it is
+        * OK to return. Otherwise, we just pass along the status code.
+        */
+       status = run_command_v_opt(argv, 0);
+       if (status != -ERR_RUN_COMMAND_EXEC) {
+               if (IS_RUN_COMMAND_ERR(status))
+                       die("unable to run '%s'", argv[0]);
+               exit(-status);
+       }
+       errno = ENOENT; /* as if we called execvp */
+
+       argv[0] = tmp;
+
+       strbuf_release(&cmd);
+}
+
+static int run_argv(int *argcp, const char ***argv)
+{
+       int done_alias = 0;
+
+       while (1) {
+               /* See if it's an internal command */
+               handle_internal_command(*argcp, *argv);
+
+               /* .. then try the external ones */
+               execv_dashed_external(*argv);
+
+               /* It could be an alias -- this works around the insanity
+                * of overriding "perf log" with "perf show" by having
+                * alias.log = show
+                */
+               if (done_alias || !handle_alias(argcp, argv))
+                       break;
+               done_alias = 1;
+       }
+
+       return done_alias;
+}
+
+
+int main(int argc, const char **argv)
+{
+       const char *cmd;
+
+       cmd = perf_extract_argv0_path(argv[0]);
+       if (!cmd)
+               cmd = "perf-help";
+
+       /*
+        * "perf-xxxx" is the same as "perf xxxx", but we obviously:
+        *
+        *  - cannot take flags in between the "perf" and the "xxxx".
+        *  - cannot execute it externally (since it would just do
+        *    the same thing over again)
+        *
+        * So we just directly call the internal command handler, and
+        * die if that one cannot handle it.
+        */
+       if (!prefixcmp(cmd, "perf-")) {
+               cmd += 4;
+               argv[0] = cmd;
+               handle_internal_command(argc, argv);
+               die("cannot handle %s internally", cmd);
+       }
+
+       /* Look for flags.. */
+       argv++;
+       argc--;
+       handle_options(&argv, &argc, NULL);
+       commit_pager_choice();
+       if (argc > 0) {
+               if (!prefixcmp(argv[0], "--"))
+                       argv[0] += 2;
+       } else {
+               /* The user didn't specify a command; give them help */
+               printf("usage: %s\n\n", perf_usage_string);
+               list_common_cmds_help();
+               printf("\n%s\n", perf_more_info_string);
+               exit(1);
+       }
+       cmd = argv[0];
+
+       /*
+        * We use PATH to find perf commands, but we prepend some higher
+        * precidence paths: the "--exec-path" option, the PERF_EXEC_PATH
+        * environment, and the $(perfexecdir) from the Makefile at build
+        * time.
+        */
+       setup_path();
+
+       while (1) {
+               static int done_help = 0;
+               static int was_alias = 0;
+               was_alias = run_argv(&argc, &argv);
+               if (errno != ENOENT)
+                       break;
+               if (was_alias) {
+                       fprintf(stderr, "Expansion of alias '%s' failed; "
+                               "'%s' is not a perf-command\n",
+                               cmd, argv[0]);
+                       exit(1);
+               }
+               if (!done_help) {
+                       cmd = argv[0] = help_unknown_cmd(cmd);
+                       done_help = 1;
+               } else
+                       break;
+       }
+
+       fprintf(stderr, "Failed to run command '%s': %s\n",
+               cmd, strerror(errno));
+
+       return 1;
+}
diff --git a/Documentation/perf_counter/perf.h b/Documentation/perf_counter/perf.h
new file mode 100644 (file)
index 0000000..6fa3656
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef _PERF_PERF_H
+#define _PERF_PERF_H
+
+/*
+ * prctl(PR_TASK_PERF_COUNTERS_DISABLE) will (cheaply) disable all
+ * counters in the current task.
+ */
+#define PR_TASK_PERF_COUNTERS_DISABLE   31
+#define PR_TASK_PERF_COUNTERS_ENABLE    32
+
+#ifndef NSEC_PER_SEC
+# define NSEC_PER_SEC                  1000000000ULL
+#endif
+
+static inline unsigned long long rdclock(void)
+{
+       struct timespec ts;
+
+       clock_gettime(CLOCK_MONOTONIC, &ts);
+       return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+
+/*
+ * Pick up some kernel type conventions:
+ */
+#define __user
+#define asmlinkage
+
+#if defined(__x86_64__) || defined(__i386__)
+#include "../../arch/x86/include/asm/unistd.h"
+#define rmb()          asm volatile("lfence" ::: "memory")
+#define cpu_relax()    asm volatile("rep; nop" ::: "memory");
+#endif
+
+#ifdef __powerpc__
+#include "../../arch/powerpc/include/asm/unistd.h"
+#define rmb()          asm volatile ("sync" ::: "memory")
+#define cpu_relax()    asm volatile ("" ::: "memory");
+#endif
+
+#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; })
+
+static inline int
+sys_perf_counter_open(struct perf_counter_hw_event *hw_event_uptr,
+                     pid_t pid, int cpu, int group_fd,
+                     unsigned long flags)
+{
+       return syscall(__NR_perf_counter_open, hw_event_uptr, pid, cpu,
+                      group_fd, flags);
+}
+
+#define MAX_COUNTERS                   64
+#define MAX_NR_CPUS                    256
+
+#define EID(type, id) (((__u64)(type) << PERF_COUNTER_TYPE_SHIFT) | (id))
+
+#endif
diff --git a/Documentation/perf_counter/util/PERF-VERSION-GEN b/Documentation/perf_counter/util/PERF-VERSION-GEN
new file mode 100755 (executable)
index 0000000..c561d15
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+GVF=PERF-VERSION-FILE
+DEF_VER=v0.0.1.PERF
+
+LF='
+'
+
+# First see if there is a version file (included in release tarballs),
+# then try git-describe, then default.
+if test -f version
+then
+       VN=$(cat version) || VN="$DEF_VER"
+elif test -d .git -o -f .git &&
+       VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
+       case "$VN" in
+       *$LF*) (exit 1) ;;
+       v[0-9]*)
+               git update-index -q --refresh
+               test -z "$(git diff-index --name-only HEAD --)" ||
+               VN="$VN-dirty" ;;
+       esac
+then
+       VN=$(echo "$VN" | sed -e 's/-/./g');
+else
+       VN="$DEF_VER"
+fi
+
+VN=$(expr "$VN" : v*'\(.*\)')
+
+if test -r $GVF
+then
+       VC=$(sed -e 's/^PERF_VERSION = //' <$GVF)
+else
+       VC=unset
+fi
+test "$VN" = "$VC" || {
+       echo >&2 "PERF_VERSION = $VN"
+       echo "PERF_VERSION = $VN" >$GVF
+}
+
+
diff --git a/Documentation/perf_counter/util/abspath.c b/Documentation/perf_counter/util/abspath.c
new file mode 100644 (file)
index 0000000..649f34f
--- /dev/null
@@ -0,0 +1,117 @@
+#include "cache.h"
+
+/*
+ * Do not use this for inspecting *tracked* content.  When path is a
+ * symlink to a directory, we do not want to say it is a directory when
+ * dealing with tracked content in the working tree.
+ */
+int is_directory(const char *path)
+{
+       struct stat st;
+       return (!stat(path, &st) && S_ISDIR(st.st_mode));
+}
+
+/* We allow "recursive" symbolic links. Only within reason, though. */
+#define MAXDEPTH 5
+
+const char *make_absolute_path(const char *path)
+{
+       static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
+       char cwd[1024] = "";
+       int buf_index = 1, len;
+
+       int depth = MAXDEPTH;
+       char *last_elem = NULL;
+       struct stat st;
+
+       if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
+               die ("Too long path: %.*s", 60, path);
+
+       while (depth--) {
+               if (!is_directory(buf)) {
+                       char *last_slash = strrchr(buf, '/');
+                       if (last_slash) {
+                               *last_slash = '\0';
+                               last_elem = xstrdup(last_slash + 1);
+                       } else {
+                               last_elem = xstrdup(buf);
+                               *buf = '\0';
+                       }
+               }
+
+               if (*buf) {
+                       if (!*cwd && !getcwd(cwd, sizeof(cwd)))
+                               die ("Could not get current working directory");
+
+                       if (chdir(buf))
+                               die ("Could not switch to '%s'", buf);
+               }
+               if (!getcwd(buf, PATH_MAX))
+                       die ("Could not get current working directory");
+
+               if (last_elem) {
+                       int len = strlen(buf);
+                       if (len + strlen(last_elem) + 2 > PATH_MAX)
+                               die ("Too long path name: '%s/%s'",
+                                               buf, last_elem);
+                       buf[len] = '/';
+                       strcpy(buf + len + 1, last_elem);
+                       free(last_elem);
+                       last_elem = NULL;
+               }
+
+               if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
+                       len = readlink(buf, next_buf, PATH_MAX);
+                       if (len < 0)
+                               die ("Invalid symlink: %s", buf);
+                       if (PATH_MAX <= len)
+                               die("symbolic link too long: %s", buf);
+                       next_buf[len] = '\0';
+                       buf = next_buf;
+                       buf_index = 1 - buf_index;
+                       next_buf = bufs[buf_index];
+               } else
+                       break;
+       }
+
+       if (*cwd && chdir(cwd))
+               die ("Could not change back to '%s'", cwd);
+
+       return buf;
+}
+
+static const char *get_pwd_cwd(void)
+{
+       static char cwd[PATH_MAX + 1];
+       char *pwd;
+       struct stat cwd_stat, pwd_stat;
+       if (getcwd(cwd, PATH_MAX) == NULL)
+               return NULL;
+       pwd = getenv("PWD");
+       if (pwd && strcmp(pwd, cwd)) {
+               stat(cwd, &cwd_stat);
+               if (!stat(pwd, &pwd_stat) &&
+                   pwd_stat.st_dev == cwd_stat.st_dev &&
+                   pwd_stat.st_ino == cwd_stat.st_ino) {
+                       strlcpy(cwd, pwd, PATH_MAX);
+               }
+       }
+       return cwd;
+}
+
+const char *make_nonrelative_path(const char *path)
+{
+       static char buf[PATH_MAX + 1];
+
+       if (is_absolute_path(path)) {
+               if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
+                       die("Too long path: %.*s", 60, path);
+       } else {
+               const char *cwd = get_pwd_cwd();
+               if (!cwd)
+                       die("Cannot determine the current working directory");
+               if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
+                       die("Too long path: %.*s", 60, path);
+       }
+       return buf;
+}
diff --git a/Documentation/perf_counter/util/alias.c b/Documentation/perf_counter/util/alias.c
new file mode 100644 (file)
index 0000000..9b3dd2b
--- /dev/null
@@ -0,0 +1,77 @@
+#include "cache.h"
+
+static const char *alias_key;
+static char *alias_val;
+
+static int alias_lookup_cb(const char *k, const char *v, void *cb)
+{
+       if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
+               if (!v)
+                       return config_error_nonbool(k);
+               alias_val = strdup(v);
+               return 0;
+       }
+       return 0;
+}
+
+char *alias_lookup(const char *alias)
+{
+       alias_key = alias;
+       alias_val = NULL;
+       perf_config(alias_lookup_cb, NULL);
+       return alias_val;
+}
+
+int split_cmdline(char *cmdline, const char ***argv)
+{
+       int src, dst, count = 0, size = 16;
+       char quoted = 0;
+
+       *argv = malloc(sizeof(char*) * size);
+
+       /* split alias_string */
+       (*argv)[count++] = cmdline;
+       for (src = dst = 0; cmdline[src];) {
+               char c = cmdline[src];
+               if (!quoted && isspace(c)) {
+                       cmdline[dst++] = 0;
+                       while (cmdline[++src]
+                                       && isspace(cmdline[src]))
+                               ; /* skip */
+                       if (count >= size) {
+                               size += 16;
+                               *argv = realloc(*argv, sizeof(char*) * size);
+                       }
+                       (*argv)[count++] = cmdline + dst;
+               } else if (!quoted && (c == '\'' || c == '"')) {
+                       quoted = c;
+                       src++;
+               } else if (c == quoted) {
+                       quoted = 0;
+                       src++;
+               } else {
+                       if (c == '\\' && quoted != '\'') {
+                               src++;
+                               c = cmdline[src];
+                               if (!c) {
+                                       free(*argv);
+                                       *argv = NULL;
+                                       return error("cmdline ends with \\");
+                               }
+                       }
+                       cmdline[dst++] = c;
+                       src++;
+               }
+       }
+
+       cmdline[dst] = 0;
+
+       if (quoted) {
+               free(*argv);
+               *argv = NULL;
+               return error("unclosed quote");
+       }
+
+       return count;
+}
+
diff --git a/Documentation/perf_counter/util/cache.h b/Documentation/perf_counter/util/cache.h
new file mode 100644 (file)
index 0000000..7108051
--- /dev/null
@@ -0,0 +1,117 @@
+#ifndef CACHE_H
+#define CACHE_H
+
+#include "util.h"
+#include "strbuf.h"
+
+#define PERF_DIR_ENVIRONMENT "PERF_DIR"
+#define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE"
+#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
+#define DB_ENVIRONMENT "PERF_OBJECT_DIRECTORY"
+#define INDEX_ENVIRONMENT "PERF_INDEX_FILE"
+#define GRAFT_ENVIRONMENT "PERF_GRAFT_FILE"
+#define TEMPLATE_DIR_ENVIRONMENT "PERF_TEMPLATE_DIR"
+#define CONFIG_ENVIRONMENT "PERF_CONFIG"
+#define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH"
+#define CEILING_DIRECTORIES_ENVIRONMENT "PERF_CEILING_DIRECTORIES"
+#define PERFATTRIBUTES_FILE ".perfattributes"
+#define INFOATTRIBUTES_FILE "info/attributes"
+#define ATTRIBUTE_MACRO_PREFIX "[attr]"
+
+typedef int (*config_fn_t)(const char *, const char *, void *);
+extern int perf_default_config(const char *, const char *, void *);
+extern int perf_config_from_file(config_fn_t fn, const char *, void *);
+extern int perf_config(config_fn_t fn, void *);
+extern int perf_parse_ulong(const char *, unsigned long *);
+extern int perf_config_int(const char *, const char *);
+extern unsigned long perf_config_ulong(const char *, const char *);
+extern int perf_config_bool_or_int(const char *, const char *, int *);
+extern int perf_config_bool(const char *, const char *);
+extern int perf_config_string(const char **, const char *, const char *);
+extern int perf_config_set(const char *, const char *);
+extern int perf_config_set_multivar(const char *, const char *, const char *, int);
+extern int perf_config_rename_section(const char *, const char *);
+extern const char *perf_etc_perfconfig(void);
+extern int check_repository_format_version(const char *var, const char *value, void *cb);
+extern int perf_config_system(void);
+extern int perf_config_global(void);
+extern int config_error_nonbool(const char *);
+extern const char *config_exclusive_filename;
+
+#define MAX_PERFNAME (1000)
+extern char perf_default_email[MAX_PERFNAME];
+extern char perf_default_name[MAX_PERFNAME];
+extern int user_ident_explicitly_given;
+
+extern const char *perf_log_output_encoding;
+extern const char *perf_mailmap_file;
+
+/* IO helper functions */
+extern void maybe_flush_or_die(FILE *, const char *);
+extern int copy_fd(int ifd, int ofd);
+extern int copy_file(const char *dst, const char *src, int mode);
+extern ssize_t read_in_full(int fd, void *buf, size_t count);
+extern ssize_t write_in_full(int fd, const void *buf, size_t count);
+extern void write_or_die(int fd, const void *buf, size_t count);
+extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
+extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
+extern void fsync_or_die(int fd, const char *);
+
+/* pager.c */
+extern void setup_pager(void);
+extern const char *pager_program;
+extern int pager_in_use(void);
+extern int pager_use_color;
+
+extern const char *editor_program;
+extern const char *excludes_file;
+
+char *alias_lookup(const char *alias);
+int split_cmdline(char *cmdline, const char ***argv);
+
+#define alloc_nr(x) (((x)+16)*3/2)
+
+/*
+ * Realloc the buffer pointed at by variable 'x' so that it can hold
+ * at least 'nr' entries; the number of entries currently allocated
+ * is 'alloc', using the standard growing factor alloc_nr() macro.
+ *
+ * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
+ */
+#define ALLOC_GROW(x, nr, alloc) \
+       do { \
+               if ((nr) > alloc) { \
+                       if (alloc_nr(alloc) < (nr)) \
+                               alloc = (nr); \
+                       else \
+                               alloc = alloc_nr(alloc); \
+                       x = xrealloc((x), alloc * sizeof(*(x))); \
+               } \
+       } while(0)
+
+
+static inline int is_absolute_path(const char *path)
+{
+       return path[0] == '/';
+}
+
+const char *make_absolute_path(const char *path);
+const char *make_nonrelative_path(const char *path);
+const char *make_relative_path(const char *abs, const char *base);
+int normalize_path_copy(char *dst, const char *src);
+int longest_ancestor_length(const char *path, const char *prefix_list);
+char *strip_path_suffix(const char *path, const char *suffix);
+
+extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+
+extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
+       __attribute__((format (printf, 3, 4)));
+extern char *perf_snpath(char *buf, size_t n, const char *fmt, ...)
+       __attribute__((format (printf, 3, 4)));
+extern char *perf_pathdup(const char *fmt, ...)
+       __attribute__((format (printf, 1, 2)));
+
+extern size_t strlcpy(char *dest, const char *src, size_t size);
+
+#endif /* CACHE_H */
diff --git a/Documentation/perf_counter/util/config.c b/Documentation/perf_counter/util/config.c
new file mode 100644 (file)
index 0000000..3dd13fa
--- /dev/null
@@ -0,0 +1,873 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ * Copyright (C) Johannes Schindelin, 2005
+ *
+ */
+#include "util.h"
+#include "cache.h"
+#include "exec_cmd.h"
+
+#define MAXNAME (256)
+
+static FILE *config_file;
+static const char *config_file_name;
+static int config_linenr;
+static int config_file_eof;
+
+const char *config_exclusive_filename = NULL;
+
+static int get_next_char(void)
+{
+       int c;
+       FILE *f;
+
+       c = '\n';
+       if ((f = config_file) != NULL) {
+               c = fgetc(f);
+               if (c == '\r') {
+                       /* DOS like systems */
+                       c = fgetc(f);
+                       if (c != '\n') {
+                               ungetc(c, f);
+                               c = '\r';
+                       }
+               }
+               if (c == '\n')
+                       config_linenr++;
+               if (c == EOF) {
+                       config_file_eof = 1;
+                       c = '\n';
+               }
+       }
+       return c;
+}
+
+static char *parse_value(void)
+{
+       static char value[1024];
+       int quote = 0, comment = 0, len = 0, space = 0;
+
+       for (;;) {
+               int c = get_next_char();
+               if (len >= sizeof(value) - 1)
+                       return NULL;
+               if (c == '\n') {
+                       if (quote)
+                               return NULL;
+                       value[len] = 0;
+                       return value;
+               }
+               if (comment)
+                       continue;
+               if (isspace(c) && !quote) {
+                       space = 1;
+                       continue;
+               }
+               if (!quote) {
+                       if (c == ';' || c == '#') {
+                               comment = 1;
+                               continue;
+                       }
+               }
+               if (space) {
+                       if (len)
+                               value[len++] = ' ';
+                       space = 0;
+               }
+               if (c == '\\') {
+                       c = get_next_char();
+                       switch (c) {
+                       case '\n':
+                               continue;
+                       case 't':
+                               c = '\t';
+                               break;
+                       case 'b':
+                               c = '\b';
+                               break;
+                       case 'n':
+                               c = '\n';
+                               break;
+                       /* Some characters escape as themselves */
+                       case '\\': case '"':
+                               break;
+                       /* Reject unknown escape sequences */
+                       default:
+                               return NULL;
+                       }
+                       value[len++] = c;
+                       continue;
+               }
+               if (c == '"') {
+                       quote = 1-quote;
+                       continue;
+               }
+               value[len++] = c;
+       }
+}
+
+static inline int iskeychar(int c)
+{
+       return isalnum(c) || c == '-';
+}
+
+static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
+{
+       int c;
+       char *value;
+
+       /* Get the full name */
+       for (;;) {
+               c = get_next_char();
+               if (config_file_eof)
+                       break;
+               if (!iskeychar(c))
+                       break;
+               name[len++] = tolower(c);
+               if (len >= MAXNAME)
+                       return -1;
+       }
+       name[len] = 0;
+       while (c == ' ' || c == '\t')
+               c = get_next_char();
+
+       value = NULL;
+       if (c != '\n') {
+               if (c != '=')
+                       return -1;
+               value = parse_value();
+               if (!value)
+                       return -1;
+       }
+       return fn(name, value, data);
+}
+
+static int get_extended_base_var(char *name, int baselen, int c)
+{
+       do {
+               if (c == '\n')
+                       return -1;
+               c = get_next_char();
+       } while (isspace(c));
+
+       /* We require the format to be '[base "extension"]' */
+       if (c != '"')
+               return -1;
+       name[baselen++] = '.';
+
+       for (;;) {
+               int c = get_next_char();
+               if (c == '\n')
+                       return -1;
+               if (c == '"')
+                       break;
+               if (c == '\\') {
+                       c = get_next_char();
+                       if (c == '\n')
+                               return -1;
+               }
+               name[baselen++] = c;
+               if (baselen > MAXNAME / 2)
+                       return -1;
+       }
+
+       /* Final ']' */
+       if (get_next_char() != ']')
+               return -1;
+       return baselen;
+}
+
+static int get_base_var(char *name)
+{
+       int baselen = 0;
+
+       for (;;) {
+               int c = get_next_char();
+               if (config_file_eof)
+                       return -1;
+               if (c == ']')
+                       return baselen;
+               if (isspace(c))
+                       return get_extended_base_var(name, baselen, c);
+               if (!iskeychar(c) && c != '.')
+                       return -1;
+               if (baselen > MAXNAME / 2)
+                       return -1;
+               name[baselen++] = tolower(c);
+       }
+}
+
+static int perf_parse_file(config_fn_t fn, void *data)
+{
+       int comment = 0;
+       int baselen = 0;
+       static char var[MAXNAME];
+
+       /* U+FEFF Byte Order Mark in UTF8 */
+       static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
+       const unsigned char *bomptr = utf8_bom;
+
+       for (;;) {
+               int c = get_next_char();
+               if (bomptr && *bomptr) {
+                       /* We are at the file beginning; skip UTF8-encoded BOM
+                        * if present. Sane editors won't put this in on their
+                        * own, but e.g. Windows Notepad will do it happily. */
+                       if ((unsigned char) c == *bomptr) {
+                               bomptr++;
+                               continue;
+                       } else {
+                               /* Do not tolerate partial BOM. */
+                               if (bomptr != utf8_bom)
+                                       break;
+                               /* No BOM at file beginning. Cool. */
+                               bomptr = NULL;
+                       }
+               }
+               if (c == '\n') {
+                       if (config_file_eof)
+                               return 0;
+                       comment = 0;
+                       continue;
+               }
+               if (comment || isspace(c))
+                       continue;
+               if (c == '#' || c == ';') {
+                       comment = 1;
+                       continue;
+               }
+               if (c == '[') {
+                       baselen = get_base_var(var);
+                       if (baselen <= 0)
+                               break;
+                       var[baselen++] = '.';
+                       var[baselen] = 0;
+                       continue;
+               }
+               if (!isalpha(c))
+                       break;
+               var[baselen] = tolower(c);
+               if (get_value(fn, data, var, baselen+1) < 0)
+                       break;
+       }
+       die("bad config file line %d in %s", config_linenr, config_file_name);
+}
+
+static int parse_unit_factor(const char *end, unsigned long *val)
+{
+       if (!*end)
+               return 1;
+       else if (!strcasecmp(end, "k")) {
+               *val *= 1024;
+               return 1;
+       }
+       else if (!strcasecmp(end, "m")) {
+               *val *= 1024 * 1024;
+               return 1;
+       }
+       else if (!strcasecmp(end, "g")) {
+               *val *= 1024 * 1024 * 1024;
+               return 1;
+       }
+       return 0;
+}
+
+static int perf_parse_long(const char *value, long *ret)
+{
+       if (value && *value) {
+               char *end;
+               long val = strtol(value, &end, 0);
+               unsigned long factor = 1;
+               if (!parse_unit_factor(end, &factor))
+                       return 0;
+               *ret = val * factor;
+               return 1;
+       }
+       return 0;
+}
+
+int perf_parse_ulong(const char *value, unsigned long *ret)
+{
+       if (value && *value) {
+               char *end;
+               unsigned long val = strtoul(value, &end, 0);
+               if (!parse_unit_factor(end, &val))
+                       return 0;
+               *ret = val;
+               return 1;
+       }
+       return 0;
+}
+
+static void die_bad_config(const char *name)
+{
+       if (config_file_name)
+               die("bad config value for '%s' in %s", name, config_file_name);
+       die("bad config value for '%s'", name);
+}
+
+int perf_config_int(const char *name, const char *value)
+{
+       long ret = 0;
+       if (!perf_parse_long(value, &ret))
+               die_bad_config(name);
+       return ret;
+}
+
+unsigned long perf_config_ulong(const char *name, const char *value)
+{
+       unsigned long ret;
+       if (!perf_parse_ulong(value, &ret))
+               die_bad_config(name);
+       return ret;
+}
+
+int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
+{
+       *is_bool = 1;
+       if (!value)
+               return 1;
+       if (!*value)
+               return 0;
+       if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
+               return 1;
+       if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
+               return 0;
+       *is_bool = 0;
+       return perf_config_int(name, value);
+}
+
+int perf_config_bool(const char *name, const char *value)
+{
+       int discard;
+       return !!perf_config_bool_or_int(name, value, &discard);
+}
+
+int perf_config_string(const char **dest, const char *var, const char *value)
+{
+       if (!value)
+               return config_error_nonbool(var);
+       *dest = strdup(value);
+       return 0;
+}
+
+static int perf_default_core_config(const char *var, const char *value)
+{
+       /* Add other config variables here and to Documentation/config.txt. */
+       return 0;
+}
+
+int perf_default_config(const char *var, const char *value, void *dummy)
+{
+       if (!prefixcmp(var, "core."))
+               return perf_default_core_config(var, value);
+
+       /* Add other config variables here and to Documentation/config.txt. */
+       return 0;
+}
+
+int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
+{
+       int ret;
+       FILE *f = fopen(filename, "r");
+
+       ret = -1;
+       if (f) {
+               config_file = f;
+               config_file_name = filename;
+               config_linenr = 1;
+               config_file_eof = 0;
+               ret = perf_parse_file(fn, data);
+               fclose(f);
+               config_file_name = NULL;
+       }
+       return ret;
+}
+
+const char *perf_etc_perfconfig(void)
+{
+       static const char *system_wide;
+       if (!system_wide)
+               system_wide = system_path(ETC_PERFCONFIG);
+       return system_wide;
+}
+
+static int perf_env_bool(const char *k, int def)
+{
+       const char *v = getenv(k);
+       return v ? perf_config_bool(k, v) : def;
+}
+
+int perf_config_system(void)
+{
+       return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
+}
+
+int perf_config_global(void)
+{
+       return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
+}
+
+int perf_config(config_fn_t fn, void *data)
+{
+       int ret = 0, found = 0;
+       char *repo_config = NULL;
+       const char *home = NULL;
+
+       /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
+       if (config_exclusive_filename)
+               return perf_config_from_file(fn, config_exclusive_filename, data);
+       if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
+               ret += perf_config_from_file(fn, perf_etc_perfconfig(),
+                                           data);
+               found += 1;
+       }
+
+       home = getenv("HOME");
+       if (perf_config_global() && home) {
+               char *user_config = strdup(mkpath("%s/.perfconfig", home));
+               if (!access(user_config, R_OK)) {
+                       ret += perf_config_from_file(fn, user_config, data);
+                       found += 1;
+               }
+               free(user_config);
+       }
+
+       repo_config = perf_pathdup("config");
+       if (!access(repo_config, R_OK)) {
+               ret += perf_config_from_file(fn, repo_config, data);
+               found += 1;
+       }
+       free(repo_config);
+       if (found == 0)
+               return -1;
+       return ret;
+}
+
+/*
+ * Find all the stuff for perf_config_set() below.
+ */
+
+#define MAX_MATCHES 512
+
+static struct {
+       int baselen;
+       char* key;
+       int do_not_match;
+       regex_t* value_regex;
+       int multi_replace;
+       size_t offset[MAX_MATCHES];
+       enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
+       int seen;
+} store;
+
+static int matches(const char* key, const char* value)
+{
+       return !strcmp(key, store.key) &&
+               (store.value_regex == NULL ||
+                (store.do_not_match ^
+                 !regexec(store.value_regex, value, 0, NULL, 0)));
+}
+
+static int store_aux(const char* key, const char* value, void *cb)
+{
+       const char *ep;
+       size_t section_len;
+
+       switch (store.state) {
+       case KEY_SEEN:
+               if (matches(key, value)) {
+                       if (store.seen == 1 && store.multi_replace == 0) {
+                               warning("%s has multiple values", key);
+                       } else if (store.seen >= MAX_MATCHES) {
+                               error("too many matches for %s", key);
+                               return 1;
+                       }
+
+                       store.offset[store.seen] = ftell(config_file);
+                       store.seen++;
+               }
+               break;
+       case SECTION_SEEN:
+               /*
+                * What we are looking for is in store.key (both
+                * section and var), and its section part is baselen
+                * long.  We found key (again, both section and var).
+                * We would want to know if this key is in the same
+                * section as what we are looking for.  We already
+                * know we are in the same section as what should
+                * hold store.key.
+                */
+               ep = strrchr(key, '.');
+               section_len = ep - key;
+
+               if ((section_len != store.baselen) ||
+                   memcmp(key, store.key, section_len+1)) {
+                       store.state = SECTION_END_SEEN;
+                       break;
+               }
+
+               /*
+                * Do not increment matches: this is no match, but we
+                * just made sure we are in the desired section.
+                */
+               store.offset[store.seen] = ftell(config_file);
+               /* fallthru */
+       case SECTION_END_SEEN:
+       case START:
+               if (matches(key, value)) {
+                       store.offset[store.seen] = ftell(config_file);
+                       store.state = KEY_SEEN;
+                       store.seen++;
+               } else {
+                       if (strrchr(key, '.') - key == store.baselen &&
+                             !strncmp(key, store.key, store.baselen)) {
+                                       store.state = SECTION_SEEN;
+                                       store.offset[store.seen] = ftell(config_file);
+                       }
+               }
+       }
+       return 0;
+}
+
+static int store_write_section(int fd, const char* key)
+{
+       const char *dot;
+       int i, success;
+       struct strbuf sb = STRBUF_INIT;
+
+       dot = memchr(key, '.', store.baselen);
+       if (dot) {
+               strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
+               for (i = dot - key + 1; i < store.baselen; i++) {
+                       if (key[i] == '"' || key[i] == '\\')
+                               strbuf_addch(&sb, '\\');
+                       strbuf_addch(&sb, key[i]);
+               }
+               strbuf_addstr(&sb, "\"]\n");
+       } else {
+               strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
+       }
+
+       success = write_in_full(fd, sb.buf, sb.len) == sb.len;
+       strbuf_release(&sb);
+
+       return success;
+}
+
+static int store_write_pair(int fd, const char* key, const char* value)
+{
+       int i, success;
+       int length = strlen(key + store.baselen + 1);
+       const char *quote = "";
+       struct strbuf sb = STRBUF_INIT;
+
+       /*
+        * Check to see if the value needs to be surrounded with a dq pair.
+        * Note that problematic characters are always backslash-quoted; this
+        * check is about not losing leading or trailing SP and strings that
+        * follow beginning-of-comment characters (i.e. ';' and '#') by the
+        * configuration parser.
+        */
+       if (value[0] == ' ')
+               quote = "\"";
+       for (i = 0; value[i]; i++)
+               if (value[i] == ';' || value[i] == '#')
+                       quote = "\"";
+       if (i && value[i - 1] == ' ')
+               quote = "\"";
+
+       strbuf_addf(&sb, "\t%.*s = %s",
+                   length, key + store.baselen + 1, quote);
+
+       for (i = 0; value[i]; i++)
+               switch (value[i]) {
+               case '\n':
+                       strbuf_addstr(&sb, "\\n");
+                       break;
+               case '\t':
+                       strbuf_addstr(&sb, "\\t");
+                       break;
+               case '"':
+               case '\\':
+                       strbuf_addch(&sb, '\\');
+               default:
+                       strbuf_addch(&sb, value[i]);
+                       break;
+               }
+       strbuf_addf(&sb, "%s\n", quote);
+
+       success = write_in_full(fd, sb.buf, sb.len) == sb.len;
+       strbuf_release(&sb);
+
+       return success;
+}
+
+static ssize_t find_beginning_of_line(const char* contents, size_t size,
+       size_t offset_, int* found_bracket)
+{
+       size_t equal_offset = size, bracket_offset = size;
+       ssize_t offset;
+
+contline:
+       for (offset = offset_-2; offset > 0
+                       && contents[offset] != '\n'; offset--)
+               switch (contents[offset]) {
+                       case '=': equal_offset = offset; break;
+                       case ']': bracket_offset = offset; break;
+               }
+       if (offset > 0 && contents[offset-1] == '\\') {
+               offset_ = offset;
+               goto contline;
+       }
+       if (bracket_offset < equal_offset) {
+               *found_bracket = 1;
+               offset = bracket_offset+1;
+       } else
+               offset++;
+
+       return offset;
+}
+
+int perf_config_set(const char* key, const char* value)
+{
+       return perf_config_set_multivar(key, value, NULL, 0);
+}
+
+/*
+ * If value==NULL, unset in (remove from) config,
+ * if value_regex!=NULL, disregard key/value pairs where value does not match.
+ * if multi_replace==0, nothing, or only one matching key/value is replaced,
+ *     else all matching key/values (regardless how many) are removed,
+ *     before the new pair is written.
+ *
+ * Returns 0 on success.
+ *
+ * This function does this:
+ *
+ * - it locks the config file by creating ".perf/config.lock"
+ *
+ * - it then parses the config using store_aux() as validator to find
+ *   the position on the key/value pair to replace. If it is to be unset,
+ *   it must be found exactly once.
+ *
+ * - the config file is mmap()ed and the part before the match (if any) is
+ *   written to the lock file, then the changed part and the rest.
+ *
+ * - the config file is removed and the lock file rename()d to it.
+ *
+ */
+int perf_config_set_multivar(const char* key, const char* value,
+       const char* value_regex, int multi_replace)
+{
+       int i, dot;
+       int fd = -1, in_fd;
+       int ret = 0;
+       char* config_filename;
+       const char* last_dot = strrchr(key, '.');
+
+       if (config_exclusive_filename)
+               config_filename = strdup(config_exclusive_filename);
+       else
+               config_filename = perf_pathdup("config");
+
+       /*
+        * Since "key" actually contains the section name and the real
+        * key name separated by a dot, we have to know where the dot is.
+        */
+
+       if (last_dot == NULL) {
+               error("key does not contain a section: %s", key);
+               ret = 2;
+               goto out_free;
+       }
+       store.baselen = last_dot - key;
+
+       store.multi_replace = multi_replace;
+
+       /*
+        * Validate the key and while at it, lower case it for matching.
+        */
+       store.key = malloc(strlen(key) + 1);
+       dot = 0;
+       for (i = 0; key[i]; i++) {
+               unsigned char c = key[i];
+               if (c == '.')
+                       dot = 1;
+               /* Leave the extended basename untouched.. */
+               if (!dot || i > store.baselen) {
+                       if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
+                               error("invalid key: %s", key);
+                               free(store.key);
+                               ret = 1;
+                               goto out_free;
+                       }
+                       c = tolower(c);
+               } else if (c == '\n') {
+                       error("invalid key (newline): %s", key);
+                       free(store.key);
+                       ret = 1;
+                       goto out_free;
+               }
+               store.key[i] = c;
+       }
+       store.key[i] = 0;
+
+       /*
+        * If .perf/config does not exist yet, write a minimal version.
+        */
+       in_fd = open(config_filename, O_RDONLY);
+       if ( in_fd < 0 ) {
+               free(store.key);
+
+               if ( ENOENT != errno ) {
+                       error("opening %s: %s", config_filename,
+                             strerror(errno));
+                       ret = 3; /* same as "invalid config file" */
+                       goto out_free;
+               }
+               /* if nothing to unset, error out */
+               if (value == NULL) {
+                       ret = 5;
+                       goto out_free;
+               }
+
+               store.key = (char*)key;
+               if (!store_write_section(fd, key) ||
+                   !store_write_pair(fd, key, value))
+                       goto write_err_out;
+       } else {
+               struct stat st;
+               char* contents;
+               size_t contents_sz, copy_begin, copy_end;
+               int i, new_line = 0;
+
+               if (value_regex == NULL)
+                       store.value_regex = NULL;
+               else {
+                       if (value_regex[0] == '!') {
+                               store.do_not_match = 1;
+                               value_regex++;
+                       } else
+                               store.do_not_match = 0;
+
+                       store.value_regex = (regex_t*)malloc(sizeof(regex_t));
+                       if (regcomp(store.value_regex, value_regex,
+                                       REG_EXTENDED)) {
+                               error("invalid pattern: %s", value_regex);
+                               free(store.value_regex);
+                               ret = 6;
+                               goto out_free;
+                       }
+               }
+
+               store.offset[0] = 0;
+               store.state = START;
+               store.seen = 0;
+
+               /*
+                * After this, store.offset will contain the *end* offset
+                * of the last match, or remain at 0 if no match was found.
+                * As a side effect, we make sure to transform only a valid
+                * existing config file.
+                */
+               if (perf_config_from_file(store_aux, config_filename, NULL)) {
+                       error("invalid config file %s", config_filename);
+                       free(store.key);
+                       if (store.value_regex != NULL) {
+                               regfree(store.value_regex);
+                               free(store.value_regex);
+                       }
+                       ret = 3;
+                       goto out_free;
+               }
+
+               free(store.key);
+               if (store.value_regex != NULL) {
+                       regfree(store.value_regex);
+                       free(store.value_regex);
+               }
+
+               /* if nothing to unset, or too many matches, error out */
+               if ((store.seen == 0 && value == NULL) ||
+                               (store.seen > 1 && multi_replace == 0)) {
+                       ret = 5;
+                       goto out_free;
+               }
+
+               fstat(in_fd, &st);
+               contents_sz = xsize_t(st.st_size);
+               contents = mmap(NULL, contents_sz, PROT_READ,
+                       MAP_PRIVATE, in_fd, 0);
+               close(in_fd);
+
+               if (store.seen == 0)
+                       store.seen = 1;
+
+               for (i = 0, copy_begin = 0; i < store.seen; i++) {
+                       if (store.offset[i] == 0) {
+                               store.offset[i] = copy_end = contents_sz;
+                       } else if (store.state != KEY_SEEN) {
+                               copy_end = store.offset[i];
+                       } else
+                               copy_end = find_beginning_of_line(
+                                       contents, contents_sz,
+                                       store.offset[i]-2, &new_line);
+
+                       if (copy_end > 0 && contents[copy_end-1] != '\n')
+                               new_line = 1;
+
+                       /* write the first part of the config */
+                       if (copy_end > copy_begin) {
+                               if (write_in_full(fd, contents + copy_begin,
+                                                 copy_end - copy_begin) <
+                                   copy_end - copy_begin)
+                                       goto write_err_out;
+                               if (new_line &&
+                                   write_in_full(fd, "\n", 1) != 1)
+                                       goto write_err_out;
+                       }
+                       copy_begin = store.offset[i];
+               }
+
+               /* write the pair (value == NULL means unset) */
+               if (value != NULL) {
+                       if (store.state == START) {
+                               if (!store_write_section(fd, key))
+                                       goto write_err_out;
+                       }
+                       if (!store_write_pair(fd, key, value))
+                               goto write_err_out;
+               }
+
+               /* write the rest of the config */
+               if (copy_begin < contents_sz)
+                       if (write_in_full(fd, contents + copy_begin,
+                                         contents_sz - copy_begin) <
+                           contents_sz - copy_begin)
+                               goto write_err_out;
+
+               munmap(contents, contents_sz);
+       }
+
+       ret = 0;
+
+out_free:
+       free(config_filename);
+       return ret;
+
+write_err_out:
+       goto out_free;
+
+}
+
+/*
+ * Call this to report error for your variable that should not
+ * get a boolean value (i.e. "[my] var" means "true").
+ */
+int config_error_nonbool(const char *var)
+{
+       return error("Missing value for '%s'", var);
+}
diff --git a/Documentation/perf_counter/util/ctype.c b/Documentation/perf_counter/util/ctype.c
new file mode 100644 (file)
index 0000000..b90ec00
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Sane locale-independent, ASCII ctype.
+ *
+ * No surprises, and works with signed and unsigned chars.
+ */
+#include "cache.h"
+
+enum {
+       S = GIT_SPACE,
+       A = GIT_ALPHA,
+       D = GIT_DIGIT,
+       G = GIT_GLOB_SPECIAL,   /* *, ?, [, \\ */
+       R = GIT_REGEX_SPECIAL,  /* $, (, ), +, ., ^, {, | * */
+};
+
+unsigned char sane_ctype[256] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,         /*   0.. 15 */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         /*  16.. 31 */
+       S, 0, 0, 0, R, 0, 0, 0, R, R, G, R, 0, 0, R, 0,         /*  32.. 47 */
+       D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G,         /*  48.. 63 */
+       0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,         /*  64.. 79 */
+       A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, 0,         /*  80.. 95 */
+       0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,         /*  96..111 */
+       A, A, A, A, A, A, A, A, A, A, A, R, R, 0, 0, 0,         /* 112..127 */
+       /* Nothing in the 128.. range */
+};
diff --git a/Documentation/perf_counter/util/exec_cmd.c b/Documentation/perf_counter/util/exec_cmd.c
new file mode 100644 (file)
index 0000000..d392922
--- /dev/null
@@ -0,0 +1,165 @@
+#include "cache.h"
+#include "exec_cmd.h"
+#include "quote.h"
+#define MAX_ARGS       32
+
+extern char **environ;
+static const char *argv_exec_path;
+static const char *argv0_path;
+
+const char *system_path(const char *path)
+{
+#ifdef RUNTIME_PREFIX
+       static const char *prefix;
+#else
+       static const char *prefix = PREFIX;
+#endif
+       struct strbuf d = STRBUF_INIT;
+
+       if (is_absolute_path(path))
+               return path;
+
+#ifdef RUNTIME_PREFIX
+       assert(argv0_path);
+       assert(is_absolute_path(argv0_path));
+
+       if (!prefix &&
+           !(prefix = strip_path_suffix(argv0_path, PERF_EXEC_PATH)) &&
+           !(prefix = strip_path_suffix(argv0_path, BINDIR)) &&
+           !(prefix = strip_path_suffix(argv0_path, "perf"))) {
+               prefix = PREFIX;
+               fprintf(stderr, "RUNTIME_PREFIX requested, "
+                               "but prefix computation failed.  "
+                               "Using static fallback '%s'.\n", prefix);
+       }
+#endif
+
+       strbuf_addf(&d, "%s/%s", prefix, path);
+       path = strbuf_detach(&d, NULL);
+       return path;
+}
+
+const char *perf_extract_argv0_path(const char *argv0)
+{
+       const char *slash;
+
+       if (!argv0 || !*argv0)
+               return NULL;
+       slash = argv0 + strlen(argv0);
+
+       while (argv0 <= slash && !is_dir_sep(*slash))
+               slash--;
+
+       if (slash >= argv0) {
+               argv0_path = strndup(argv0, slash - argv0);
+               return slash + 1;
+       }
+
+       return argv0;
+}
+
+void perf_set_argv_exec_path(const char *exec_path)
+{
+       argv_exec_path = exec_path;
+       /*
+        * Propagate this setting to external programs.
+        */
+       setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1);
+}
+
+
+/* Returns the highest-priority, location to look for perf programs. */
+const char *perf_exec_path(void)
+{
+       const char *env;
+
+       if (argv_exec_path)
+               return argv_exec_path;
+
+       env = getenv(EXEC_PATH_ENVIRONMENT);
+       if (env && *env) {
+               return env;
+       }
+
+       return system_path(PERF_EXEC_PATH);
+}
+
+static void add_path(struct strbuf *out, const char *path)
+{
+       if (path && *path) {
+               if (is_absolute_path(path))
+                       strbuf_addstr(out, path);
+               else
+                       strbuf_addstr(out, make_nonrelative_path(path));
+
+               strbuf_addch(out, PATH_SEP);
+       }
+}
+
+void setup_path(void)
+{
+       const char *old_path = getenv("PATH");
+       struct strbuf new_path = STRBUF_INIT;
+
+       add_path(&new_path, perf_exec_path());
+       add_path(&new_path, argv0_path);
+
+       if (old_path)
+               strbuf_addstr(&new_path, old_path);
+       else
+               strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin");
+
+       setenv("PATH", new_path.buf, 1);
+
+       strbuf_release(&new_path);
+}
+
+const char **prepare_perf_cmd(const char **argv)
+{
+       int argc;
+       const char **nargv;
+
+       for (argc = 0; argv[argc]; argc++)
+               ; /* just counting */
+       nargv = malloc(sizeof(*nargv) * (argc + 2));
+
+       nargv[0] = "perf";
+       for (argc = 0; argv[argc]; argc++)
+               nargv[argc + 1] = argv[argc];
+       nargv[argc + 1] = NULL;
+       return nargv;
+}
+
+int execv_perf_cmd(const char **argv) {
+       const char **nargv = prepare_perf_cmd(argv);
+
+       /* execvp() can only ever return if it fails */
+       execvp("perf", (char **)nargv);
+
+       free(nargv);
+       return -1;
+}
+
+
+int execl_perf_cmd(const char *cmd,...)
+{
+       int argc;
+       const char *argv[MAX_ARGS + 1];
+       const char *arg;
+       va_list param;
+
+       va_start(param, cmd);
+       argv[0] = cmd;
+       argc = 1;
+       while (argc < MAX_ARGS) {
+               arg = argv[argc++] = va_arg(param, char *);
+               if (!arg)
+                       break;
+       }
+       va_end(param);
+       if (MAX_ARGS <= argc)
+               return error("too many args to run %s", cmd);
+
+       argv[argc] = NULL;
+       return execv_perf_cmd(argv);
+}
diff --git a/Documentation/perf_counter/util/exec_cmd.h b/Documentation/perf_counter/util/exec_cmd.h
new file mode 100644 (file)
index 0000000..effe25e
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef PERF_EXEC_CMD_H
+#define PERF_EXEC_CMD_H
+
+extern void perf_set_argv_exec_path(const char *exec_path);
+extern const char *perf_extract_argv0_path(const char *path);
+extern const char *perf_exec_path(void);
+extern void setup_path(void);
+extern const char **prepare_perf_cmd(const char **argv);
+extern int execv_perf_cmd(const char **argv); /* NULL terminated */
+extern int execl_perf_cmd(const char *cmd, ...);
+extern const char *system_path(const char *path);
+
+#endif /* PERF_EXEC_CMD_H */
diff --git a/Documentation/perf_counter/util/generate-cmdlist.sh b/Documentation/perf_counter/util/generate-cmdlist.sh
new file mode 100755 (executable)
index 0000000..f06f6fd
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+echo "/* Automatically generated by $0 */
+struct cmdname_help
+{
+    char name[16];
+    char help[80];
+};
+
+static struct cmdname_help common_cmds[] = {"
+
+sed -n -e 's/^perf-\([^        ]*\)[   ].* common.*/\1/p' command-list.txt |
+sort |
+while read cmd
+do
+     sed -n '
+     /^NAME/,/perf-'"$cmd"'/H
+     ${
+            x
+            s/.*perf-'"$cmd"' - \(.*\)/  {"'"$cmd"'", "\1"},/
+           p
+     }' "Documentation/perf-$cmd.txt"
+done
+echo "};"
diff --git a/Documentation/perf_counter/util/help.c b/Documentation/perf_counter/util/help.c
new file mode 100644 (file)
index 0000000..edde541
--- /dev/null
@@ -0,0 +1,366 @@
+#include "cache.h"
+#include "../builtin.h"
+#include "exec_cmd.h"
+#include "levenshtein.h"
+#include "help.h"
+
+/* most GUI terminals set COLUMNS (although some don't export it) */
+static int term_columns(void)
+{
+       char *col_string = getenv("COLUMNS");
+       int n_cols;
+
+       if (col_string && (n_cols = atoi(col_string)) > 0)
+               return n_cols;
+
+#ifdef TIOCGWINSZ
+       {
+               struct winsize ws;
+               if (!ioctl(1, TIOCGWINSZ, &ws)) {
+                       if (ws.ws_col)
+                               return ws.ws_col;
+               }
+       }
+#endif
+
+       return 80;
+}
+
+void add_cmdname(struct cmdnames *cmds, const char *name, int len)
+{
+       struct cmdname *ent = malloc(sizeof(*ent) + len + 1);
+
+       ent->len = len;
+       memcpy(ent->name, name, len);
+       ent->name[len] = 0;
+
+       ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc);
+       cmds->names[cmds->cnt++] = ent;
+}
+
+static void clean_cmdnames(struct cmdnames *cmds)
+{
+       int i;
+       for (i = 0; i < cmds->cnt; ++i)
+               free(cmds->names[i]);
+       free(cmds->names);
+       cmds->cnt = 0;
+       cmds->alloc = 0;
+}
+
+static int cmdname_compare(const void *a_, const void *b_)
+{
+       struct cmdname *a = *(struct cmdname **)a_;
+       struct cmdname *b = *(struct cmdname **)b_;
+       return strcmp(a->name, b->name);
+}
+
+static void uniq(struct cmdnames *cmds)
+{
+       int i, j;
+
+       if (!cmds->cnt)
+               return;
+
+       for (i = j = 1; i < cmds->cnt; i++)
+               if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name))
+                       cmds->names[j++] = cmds->names[i];
+
+       cmds->cnt = j;
+}
+
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
+{
+       int ci, cj, ei;
+       int cmp;
+
+       ci = cj = ei = 0;
+       while (ci < cmds->cnt && ei < excludes->cnt) {
+               cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name);
+               if (cmp < 0)
+                       cmds->names[cj++] = cmds->names[ci++];
+               else if (cmp == 0)
+                       ci++, ei++;
+               else if (cmp > 0)
+                       ei++;
+       }
+
+       while (ci < cmds->cnt)
+               cmds->names[cj++] = cmds->names[ci++];
+
+       cmds->cnt = cj;
+}
+
+static void pretty_print_string_list(struct cmdnames *cmds, int longest)
+{
+       int cols = 1, rows;
+       int space = longest + 1; /* min 1 SP between words */
+       int max_cols = term_columns() - 1; /* don't print *on* the edge */
+       int i, j;
+
+       if (space < max_cols)
+               cols = max_cols / space;
+       rows = (cmds->cnt + cols - 1) / cols;
+
+       for (i = 0; i < rows; i++) {
+               printf("  ");
+
+               for (j = 0; j < cols; j++) {
+                       int n = j * rows + i;
+                       int size = space;
+                       if (n >= cmds->cnt)
+                               break;
+                       if (j == cols-1 || n + rows >= cmds->cnt)
+                               size = 1;
+                       printf("%-*s", size, cmds->names[n]->name);
+               }
+               putchar('\n');
+       }
+}
+
+static int is_executable(const char *name)
+{
+       struct stat st;
+
+       if (stat(name, &st) || /* stat, not lstat */
+           !S_ISREG(st.st_mode))
+               return 0;
+
+#ifdef __MINGW32__
+       /* cannot trust the executable bit, peek into the file instead */
+       char buf[3] = { 0 };
+       int n;
+       int fd = open(name, O_RDONLY);
+       st.st_mode &= ~S_IXUSR;
+       if (fd >= 0) {
+               n = read(fd, buf, 2);
+               if (n == 2)
+                       /* DOS executables start with "MZ" */
+                       if (!strcmp(buf, "#!") || !strcmp(buf, "MZ"))
+                               st.st_mode |= S_IXUSR;
+               close(fd);
+       }
+#endif
+       return st.st_mode & S_IXUSR;
+}
+
+static void list_commands_in_dir(struct cmdnames *cmds,
+                                        const char *path,
+                                        const char *prefix)
+{
+       int prefix_len;
+       DIR *dir = opendir(path);
+       struct dirent *de;
+       struct strbuf buf = STRBUF_INIT;
+       int len;
+
+       if (!dir)
+               return;
+       if (!prefix)
+               prefix = "perf-";
+       prefix_len = strlen(prefix);
+
+       strbuf_addf(&buf, "%s/", path);
+       len = buf.len;
+
+       while ((de = readdir(dir)) != NULL) {
+               int entlen;
+
+               if (prefixcmp(de->d_name, prefix))
+                       continue;
+
+               strbuf_setlen(&buf, len);
+               strbuf_addstr(&buf, de->d_name);
+               if (!is_executable(buf.buf))
+                       continue;
+
+               entlen = strlen(de->d_name) - prefix_len;
+               if (has_extension(de->d_name, ".exe"))
+                       entlen -= 4;
+
+               add_cmdname(cmds, de->d_name + prefix_len, entlen);
+       }
+       closedir(dir);
+       strbuf_release(&buf);
+}
+
+void load_command_list(const char *prefix,
+               struct cmdnames *main_cmds,
+               struct cmdnames *other_cmds)
+{
+       const char *env_path = getenv("PATH");
+       const char *exec_path = perf_exec_path();
+
+       if (exec_path) {
+               list_commands_in_dir(main_cmds, exec_path, prefix);
+               qsort(main_cmds->names, main_cmds->cnt,
+                     sizeof(*main_cmds->names), cmdname_compare);
+               uniq(main_cmds);
+       }
+
+       if (env_path) {
+               char *paths, *path, *colon;
+               path = paths = strdup(env_path);
+               while (1) {
+                       if ((colon = strchr(path, PATH_SEP)))
+                               *colon = 0;
+                       if (!exec_path || strcmp(path, exec_path))
+                               list_commands_in_dir(other_cmds, path, prefix);
+
+                       if (!colon)
+                               break;
+                       path = colon + 1;
+               }
+               free(paths);
+
+               qsort(other_cmds->names, other_cmds->cnt,
+                     sizeof(*other_cmds->names), cmdname_compare);
+               uniq(other_cmds);
+       }
+       exclude_cmds(other_cmds, main_cmds);
+}
+
+void list_commands(const char *title, struct cmdnames *main_cmds,
+                  struct cmdnames *other_cmds)
+{
+       int i, longest = 0;
+
+       for (i = 0; i < main_cmds->cnt; i++)
+               if (longest < main_cmds->names[i]->len)
+                       longest = main_cmds->names[i]->len;
+       for (i = 0; i < other_cmds->cnt; i++)
+               if (longest < other_cmds->names[i]->len)
+                       longest = other_cmds->names[i]->len;
+
+       if (main_cmds->cnt) {
+               const char *exec_path = perf_exec_path();
+               printf("available %s in '%s'\n", title, exec_path);
+               printf("----------------");
+               mput_char('-', strlen(title) + strlen(exec_path));
+               putchar('\n');
+               pretty_print_string_list(main_cmds, longest);
+               putchar('\n');
+       }
+
+       if (other_cmds->cnt) {
+               printf("%s available from elsewhere on your $PATH\n", title);
+               printf("---------------------------------------");
+               mput_char('-', strlen(title));
+               putchar('\n');
+               pretty_print_string_list(other_cmds, longest);
+               putchar('\n');
+       }
+}
+
+int is_in_cmdlist(struct cmdnames *c, const char *s)
+{
+       int i;
+       for (i = 0; i < c->cnt; i++)
+               if (!strcmp(s, c->names[i]->name))
+                       return 1;
+       return 0;
+}
+
+static int autocorrect;
+static struct cmdnames aliases;
+
+static int perf_unknown_cmd_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "help.autocorrect"))
+               autocorrect = perf_config_int(var,value);
+       /* Also use aliases for command lookup */
+       if (!prefixcmp(var, "alias."))
+               add_cmdname(&aliases, var + 6, strlen(var + 6));
+
+       return perf_default_config(var, value, cb);
+}
+
+static int levenshtein_compare(const void *p1, const void *p2)
+{
+       const struct cmdname *const *c1 = p1, *const *c2 = p2;
+       const char *s1 = (*c1)->name, *s2 = (*c2)->name;
+       int l1 = (*c1)->len;
+       int l2 = (*c2)->len;
+       return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
+}
+
+static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
+{
+       int i;
+       ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);
+
+       for (i = 0; i < old->cnt; i++)
+               cmds->names[cmds->cnt++] = old->names[i];
+       free(old->names);
+       old->cnt = 0;
+       old->names = NULL;
+}
+
+const char *help_unknown_cmd(const char *cmd)
+{
+       int i, n, best_similarity = 0;
+       struct cmdnames main_cmds, other_cmds;
+
+       memset(&main_cmds, 0, sizeof(main_cmds));
+       memset(&other_cmds, 0, sizeof(main_cmds));
+       memset(&aliases, 0, sizeof(aliases));
+
+       perf_config(perf_unknown_cmd_config, NULL);
+
+       load_command_list("perf-", &main_cmds, &other_cmds);
+
+       add_cmd_list(&main_cmds, &aliases);
+       add_cmd_list(&main_cmds, &other_cmds);
+       qsort(main_cmds.names, main_cmds.cnt,
+             sizeof(main_cmds.names), cmdname_compare);
+       uniq(&main_cmds);
+
+       /* This reuses cmdname->len for similarity index */
+       for (i = 0; i < main_cmds.cnt; ++i)
+               main_cmds.names[i]->len =
+                       levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
+
+       qsort(main_cmds.names, main_cmds.cnt,
+             sizeof(*main_cmds.names), levenshtein_compare);
+
+       if (!main_cmds.cnt)
+               die ("Uh oh. Your system reports no Git commands at all.");
+
+       best_similarity = main_cmds.names[0]->len;
+       n = 1;
+       while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
+               ++n;
+       if (autocorrect && n == 1) {
+               const char *assumed = main_cmds.names[0]->name;
+               main_cmds.names[0] = NULL;
+               clean_cmdnames(&main_cmds);
+               fprintf(stderr, "WARNING: You called a Git program named '%s', "
+                       "which does not exist.\n"
+                       "Continuing under the assumption that you meant '%s'\n",
+                       cmd, assumed);
+               if (autocorrect > 0) {
+                       fprintf(stderr, "in %0.1f seconds automatically...\n",
+                               (float)autocorrect/10.0);
+                       poll(NULL, 0, autocorrect * 100);
+               }
+               return assumed;
+       }
+
+       fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd);
+
+       if (best_similarity < 6) {
+               fprintf(stderr, "\nDid you mean %s?\n",
+                       n < 2 ? "this": "one of these");
+
+               for (i = 0; i < n; i++)
+                       fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
+       }
+
+       exit(1);
+}
+
+int cmd_version(int argc, const char **argv, const char *prefix)
+{
+       printf("perf version %s\n", perf_version_string);
+       return 0;
+}
diff --git a/Documentation/perf_counter/util/help.h b/Documentation/perf_counter/util/help.h
new file mode 100644 (file)
index 0000000..56bc154
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef HELP_H
+#define HELP_H
+
+struct cmdnames {
+       int alloc;
+       int cnt;
+       struct cmdname {
+               size_t len; /* also used for similarity index in help.c */
+               char name[FLEX_ARRAY];
+       } **names;
+};
+
+static inline void mput_char(char c, unsigned int num)
+{
+       while(num--)
+               putchar(c);
+}
+
+void load_command_list(const char *prefix,
+               struct cmdnames *main_cmds,
+               struct cmdnames *other_cmds);
+void add_cmdname(struct cmdnames *cmds, const char *name, int len);
+/* Here we require that excludes is a sorted list. */
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
+int is_in_cmdlist(struct cmdnames *c, const char *s);
+void list_commands(const char *title, struct cmdnames *main_cmds,
+                  struct cmdnames *other_cmds);
+
+#endif /* HELP_H */
diff --git a/Documentation/perf_counter/util/levenshtein.c b/Documentation/perf_counter/util/levenshtein.c
new file mode 100644 (file)
index 0000000..e521d15
--- /dev/null
@@ -0,0 +1,84 @@
+#include "cache.h"
+#include "levenshtein.h"
+
+/*
+ * This function implements the Damerau-Levenshtein algorithm to
+ * calculate a distance between strings.
+ *
+ * Basically, it says how many letters need to be swapped, substituted,
+ * deleted from, or added to string1, at least, to get string2.
+ *
+ * The idea is to build a distance matrix for the substrings of both
+ * strings.  To avoid a large space complexity, only the last three rows
+ * are kept in memory (if swaps had the same or higher cost as one deletion
+ * plus one insertion, only two rows would be needed).
+ *
+ * At any stage, "i + 1" denotes the length of the current substring of
+ * string1 that the distance is calculated for.
+ *
+ * row2 holds the current row, row1 the previous row (i.e. for the substring
+ * of string1 of length "i"), and row0 the row before that.
+ *
+ * In other words, at the start of the big loop, row2[j + 1] contains the
+ * Damerau-Levenshtein distance between the substring of string1 of length
+ * "i" and the substring of string2 of length "j + 1".
+ *
+ * All the big loop does is determine the partial minimum-cost paths.
+ *
+ * It does so by calculating the costs of the path ending in characters
+ * i (in string1) and j (in string2), respectively, given that the last
+ * operation is a substition, a swap, a deletion, or an insertion.
+ *
+ * This implementation allows the costs to be weighted:
+ *
+ * - w (as in "sWap")
+ * - s (as in "Substitution")
+ * - a (for insertion, AKA "Add")
+ * - d (as in "Deletion")
+ *
+ * Note that this algorithm calculates a distance _iff_ d == a.
+ */
+int levenshtein(const char *string1, const char *string2,
+               int w, int s, int a, int d)
+{
+       int len1 = strlen(string1), len2 = strlen(string2);
+       int *row0 = malloc(sizeof(int) * (len2 + 1));
+       int *row1 = malloc(sizeof(int) * (len2 + 1));
+       int *row2 = malloc(sizeof(int) * (len2 + 1));
+       int i, j;
+
+       for (j = 0; j <= len2; j++)
+               row1[j] = j * a;
+       for (i = 0; i < len1; i++) {
+               int *dummy;
+
+               row2[0] = (i + 1) * d;
+               for (j = 0; j < len2; j++) {
+                       /* substitution */
+                       row2[j + 1] = row1[j] + s * (string1[i] != string2[j]);
+                       /* swap */
+                       if (i > 0 && j > 0 && string1[i - 1] == string2[j] &&
+                                       string1[i] == string2[j - 1] &&
+                                       row2[j + 1] > row0[j - 1] + w)
+                               row2[j + 1] = row0[j - 1] + w;
+                       /* deletion */
+                       if (row2[j + 1] > row1[j + 1] + d)
+                               row2[j + 1] = row1[j + 1] + d;
+                       /* insertion */
+                       if (row2[j + 1] > row2[j] + a)
+                               row2[j + 1] = row2[j] + a;
+               }
+
+               dummy = row0;
+               row0 = row1;
+               row1 = row2;
+               row2 = dummy;
+       }
+
+       i = row1[len2];
+       free(row0);
+       free(row1);
+       free(row2);
+
+       return i;
+}
diff --git a/Documentation/perf_counter/util/levenshtein.h b/Documentation/perf_counter/util/levenshtein.h
new file mode 100644 (file)
index 0000000..0173abe
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef LEVENSHTEIN_H
+#define LEVENSHTEIN_H
+
+int levenshtein(const char *string1, const char *string2,
+       int swap_penalty, int substition_penalty,
+       int insertion_penalty, int deletion_penalty);
+
+#endif
diff --git a/Documentation/perf_counter/util/parse-options.c b/Documentation/perf_counter/util/parse-options.c
new file mode 100644 (file)
index 0000000..28b34c1
--- /dev/null
@@ -0,0 +1,492 @@
+#include "util.h"
+#include "parse-options.h"
+#include "cache.h"
+
+#define OPT_SHORT 1
+#define OPT_UNSET 2
+
+static int opterror(const struct option *opt, const char *reason, int flags)
+{
+       if (flags & OPT_SHORT)
+               return error("switch `%c' %s", opt->short_name, reason);
+       if (flags & OPT_UNSET)
+               return error("option `no-%s' %s", opt->long_name, reason);
+       return error("option `%s' %s", opt->long_name, reason);
+}
+
+static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
+                  int flags, const char **arg)
+{
+       if (p->opt) {
+               *arg = p->opt;
+               p->opt = NULL;
+       } else if (p->argc == 1 && (opt->flags & PARSE_OPT_LASTARG_DEFAULT)) {
+               *arg = (const char *)opt->defval;
+       } else if (p->argc > 1) {
+               p->argc--;
+               *arg = *++p->argv;
+       } else
+               return opterror(opt, "requires a value", flags);
+       return 0;
+}
+
+static int get_value(struct parse_opt_ctx_t *p,
+                    const struct option *opt, int flags)
+{
+       const char *s, *arg;
+       const int unset = flags & OPT_UNSET;
+
+       if (unset && p->opt)
+               return opterror(opt, "takes no value", flags);
+       if (unset && (opt->flags & PARSE_OPT_NONEG))
+               return opterror(opt, "isn't available", flags);
+
+       if (!(flags & OPT_SHORT) && p->opt) {
+               switch (opt->type) {
+               case OPTION_CALLBACK:
+                       if (!(opt->flags & PARSE_OPT_NOARG))
+                               break;
+                       /* FALLTHROUGH */
+               case OPTION_BOOLEAN:
+               case OPTION_BIT:
+               case OPTION_SET_INT:
+               case OPTION_SET_PTR:
+                       return opterror(opt, "takes no value", flags);
+               default:
+                       break;
+               }
+       }
+
+       switch (opt->type) {
+       case OPTION_BIT:
+               if (unset)
+                       *(int *)opt->value &= ~opt->defval;
+               else
+                       *(int *)opt->value |= opt->defval;
+               return 0;
+
+       case OPTION_BOOLEAN:
+               *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
+               return 0;
+
+       case OPTION_SET_INT:
+               *(int *)opt->value = unset ? 0 : opt->defval;
+               return 0;
+
+       case OPTION_SET_PTR:
+               *(void **)opt->value = unset ? NULL : (void *)opt->defval;
+               return 0;
+
+       case OPTION_STRING:
+               if (unset)
+                       *(const char **)opt->value = NULL;
+               else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
+                       *(const char **)opt->value = (const char *)opt->defval;
+               else
+                       return get_arg(p, opt, flags, (const char **)opt->value);
+               return 0;
+
+       case OPTION_CALLBACK:
+               if (unset)
+                       return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
+               if (opt->flags & PARSE_OPT_NOARG)
+                       return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
+               if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
+                       return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
+               if (get_arg(p, opt, flags, &arg))
+                       return -1;
+               return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
+
+       case OPTION_INTEGER:
+               if (unset) {
+                       *(int *)opt->value = 0;
+                       return 0;
+               }
+               if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+                       *(int *)opt->value = opt->defval;
+                       return 0;
+               }
+               if (get_arg(p, opt, flags, &arg))
+                       return -1;
+               *(int *)opt->value = strtol(arg, (char **)&s, 10);
+               if (*s)
+                       return opterror(opt, "expects a numerical value", flags);
+               return 0;
+
+       default:
+               die("should not happen, someone must be hit on the forehead");
+       }
+}
+
+static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
+{
+       for (; options->type != OPTION_END; options++) {
+               if (options->short_name == *p->opt) {
+                       p->opt = p->opt[1] ? p->opt + 1 : NULL;
+                       return get_value(p, options, OPT_SHORT);
+               }
+       }
+       return -2;
+}
+
+static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
+                          const struct option *options)
+{
+       const char *arg_end = strchr(arg, '=');
+       const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
+       int abbrev_flags = 0, ambiguous_flags = 0;
+
+       if (!arg_end)
+               arg_end = arg + strlen(arg);
+
+       for (; options->type != OPTION_END; options++) {
+               const char *rest;
+               int flags = 0;
+
+               if (!options->long_name)
+                       continue;
+
+               rest = skip_prefix(arg, options->long_name);
+               if (options->type == OPTION_ARGUMENT) {
+                       if (!rest)
+                               continue;
+                       if (*rest == '=')
+                               return opterror(options, "takes no value", flags);
+                       if (*rest)
+                               continue;
+                       p->out[p->cpidx++] = arg - 2;
+                       return 0;
+               }
+               if (!rest) {
+                       /* abbreviated? */
+                       if (!strncmp(options->long_name, arg, arg_end - arg)) {
+is_abbreviated:
+                               if (abbrev_option) {
+                                       /*
+                                        * If this is abbreviated, it is
+                                        * ambiguous. So when there is no
+                                        * exact match later, we need to
+                                        * error out.
+                                        */
+                                       ambiguous_option = abbrev_option;
+                                       ambiguous_flags = abbrev_flags;
+                               }
+                               if (!(flags & OPT_UNSET) && *arg_end)
+                                       p->opt = arg_end + 1;
+                               abbrev_option = options;
+                               abbrev_flags = flags;
+                               continue;
+                       }
+                       /* negated and abbreviated very much? */
+                       if (!prefixcmp("no-", arg)) {
+                               flags |= OPT_UNSET;
+                               goto is_abbreviated;
+                       }
+                       /* negated? */
+                       if (strncmp(arg, "no-", 3))
+                               continue;
+                       flags |= OPT_UNSET;
+                       rest = skip_prefix(arg + 3, options->long_name);
+                       /* abbreviated and negated? */
+                       if (!rest && !prefixcmp(options->long_name, arg + 3))
+                               goto is_abbreviated;
+                       if (!rest)
+                               continue;
+               }
+               if (*rest) {
+                       if (*rest != '=')
+                               continue;
+                       p->opt = rest + 1;
+               }
+               return get_value(p, options, flags);
+       }
+
+       if (ambiguous_option)
+               return error("Ambiguous option: %s "
+                       "(could be --%s%s or --%s%s)",
+                       arg,
+                       (ambiguous_flags & OPT_UNSET) ?  "no-" : "",
+                       ambiguous_option->long_name,
+                       (abbrev_flags & OPT_UNSET) ?  "no-" : "",
+                       abbrev_option->long_name);
+       if (abbrev_option)
+               return get_value(p, abbrev_option, abbrev_flags);
+       return -2;
+}
+
+static void check_typos(const char *arg, const struct option *options)
+{
+       if (strlen(arg) < 3)
+               return;
+
+       if (!prefixcmp(arg, "no-")) {
+               error ("did you mean `--%s` (with two dashes ?)", arg);
+               exit(129);
+       }
+
+       for (; options->type != OPTION_END; options++) {
+               if (!options->long_name)
+                       continue;
+               if (!prefixcmp(options->long_name, arg)) {
+                       error ("did you mean `--%s` (with two dashes ?)", arg);
+                       exit(129);
+               }
+       }
+}
+
+void parse_options_start(struct parse_opt_ctx_t *ctx,
+                        int argc, const char **argv, int flags)
+{
+       memset(ctx, 0, sizeof(*ctx));
+       ctx->argc = argc - 1;
+       ctx->argv = argv + 1;
+       ctx->out  = argv;
+       ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
+       ctx->flags = flags;
+       if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
+           (flags & PARSE_OPT_STOP_AT_NON_OPTION))
+               die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
+}
+
+static int usage_with_options_internal(const char * const *,
+                                      const struct option *, int);
+
+int parse_options_step(struct parse_opt_ctx_t *ctx,
+                      const struct option *options,
+                      const char * const usagestr[])
+{
+       int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
+
+       /* we must reset ->opt, unknown short option leave it dangling */
+       ctx->opt = NULL;
+
+       for (; ctx->argc; ctx->argc--, ctx->argv++) {
+               const char *arg = ctx->argv[0];
+
+               if (*arg != '-' || !arg[1]) {
+                       if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
+                               break;
+                       ctx->out[ctx->cpidx++] = ctx->argv[0];
+                       continue;
+               }
+
+               if (arg[1] != '-') {
+                       ctx->opt = arg + 1;
+                       if (internal_help && *ctx->opt == 'h')
+                               return parse_options_usage(usagestr, options);
+                       switch (parse_short_opt(ctx, options)) {
+                       case -1:
+                               return parse_options_usage(usagestr, options);
+                       case -2:
+                               goto unknown;
+                       }
+                       if (ctx->opt)
+                               check_typos(arg + 1, options);
+                       while (ctx->opt) {
+                               if (internal_help && *ctx->opt == 'h')
+                                       return parse_options_usage(usagestr, options);
+                               switch (parse_short_opt(ctx, options)) {
+                               case -1:
+                                       return parse_options_usage(usagestr, options);
+                               case -2:
+                                       /* fake a short option thing to hide the fact that we may have
+                                        * started to parse aggregated stuff
+                                        *
+                                        * This is leaky, too bad.
+                                        */
+                                       ctx->argv[0] = strdup(ctx->opt - 1);
+                                       *(char *)ctx->argv[0] = '-';
+                                       goto unknown;
+                               }
+                       }
+                       continue;
+               }
+
+               if (!arg[2]) { /* "--" */
+                       if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
+                               ctx->argc--;
+                               ctx->argv++;
+                       }
+                       break;
+               }
+
+               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);
+               switch (parse_long_opt(ctx, arg + 2, options)) {
+               case -1:
+                       return parse_options_usage(usagestr, options);
+               case -2:
+                       goto unknown;
+               }
+               continue;
+unknown:
+               if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
+                       return PARSE_OPT_UNKNOWN;
+               ctx->out[ctx->cpidx++] = ctx->argv[0];
+               ctx->opt = NULL;
+       }
+       return PARSE_OPT_DONE;
+}
+
+int parse_options_end(struct parse_opt_ctx_t *ctx)
+{
+       memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
+       ctx->out[ctx->cpidx + ctx->argc] = NULL;
+       return ctx->cpidx + ctx->argc;
+}
+
+int parse_options(int argc, const char **argv, const struct option *options,
+                 const char * const usagestr[], int flags)
+{
+       struct parse_opt_ctx_t ctx;
+
+       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;
+       default: /* PARSE_OPT_UNKNOWN */
+               if (ctx.argv[0][1] == '-') {
+                       error("unknown option `%s'", ctx.argv[0] + 2);
+               } else {
+                       error("unknown switch `%c'", *ctx.opt);
+               }
+               usage_with_options(usagestr, options);
+       }
+
+       return parse_options_end(&ctx);
+}
+
+#define USAGE_OPTS_WIDTH 24
+#define USAGE_GAP         2
+
+int usage_with_options_internal(const char * const *usagestr,
+                               const struct option *opts, int full)
+{
+       if (!usagestr)
+               return PARSE_OPT_HELP;
+
+       fprintf(stderr, "usage: %s\n", *usagestr++);
+       while (*usagestr && **usagestr)
+               fprintf(stderr, "   or: %s\n", *usagestr++);
+       while (*usagestr) {
+               fprintf(stderr, "%s%s\n",
+                               **usagestr ? "    " : "",
+                               *usagestr);
+               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);
+               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_INTEGER:
+                       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
+                                       &nb