From 289d1054e972b445fe8f7bbcbebf40b1bec37384 Mon Sep 17 00:00:00 2001 From: Wanlong Gao Date: Thu, 7 Mar 2013 08:53:00 +1100 Subject: lguest: fix paths in comments After commit 07fe997, lguest tool has already moved from Documentation/virtual/lguest/ to tools/lguest/. Signed-off-by: Wanlong Gao Signed-off-by: Rusty Russell --- tools/lguest/lguest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/lguest/lguest.txt b/tools/lguest/lguest.txt index 7203ace65e83..06e1f4649511 100644 --- a/tools/lguest/lguest.txt +++ b/tools/lguest/lguest.txt @@ -70,7 +70,7 @@ Running Lguest: - Run an lguest as root: - Documentation/virtual/lguest/lguest 64 vmlinux --tunnet=192.168.19.1 \ + tools/lguest/lguest 64 vmlinux --tunnet=192.168.19.1 \ --block=rootfile root=/dev/vda Explanation: -- cgit v1.2.3 From 752d96657cf4844793ac4d62d02a0733396ef16c Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 8 Mar 2013 09:33:35 -0500 Subject: ktest: Allow tests to use different GRUB_MENUs To save connecting and searching for a given grub menu for each test, ktest.pl will cache the grub number it found. The problem is that different tests might use a different grub menu, but ktest.pl will ignore it. Instead, have ktest.pl check if the grub menu it used to cache the content is the same as when it grabbed the menu. If not, grab it again, otherwise just return the cached value. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 4e67d52eb3a2..7958cd4d6567 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -108,6 +108,7 @@ my $scp_to_target; my $scp_to_target_install; my $power_off; my $grub_menu; +my $last_grub_menu; my $grub_file; my $grub_number; my $grub_reboot; @@ -1538,7 +1539,8 @@ sub run_scp_mod { sub get_grub2_index { - return if (defined($grub_number)); + return if (defined($grub_number) && defined($last_grub_menu) && + $last_grub_menu eq $grub_menu); doprint "Find grub2 menu ... "; $grub_number = -1; @@ -1565,6 +1567,7 @@ sub get_grub2_index { die "Could not find '$grub_menu' in $grub_file on $machine" if (!$found); doprint "$grub_number\n"; + $last_grub_menu = $grub_menu; } sub get_grub_index { @@ -1577,7 +1580,8 @@ sub get_grub_index { if ($reboot_type ne "grub") { return; } - return if (defined($grub_number)); + return if (defined($grub_number) && defined($last_grub_menu) && + $last_grub_menu eq $grub_menu); doprint "Find grub menu ... "; $grub_number = -1; @@ -1604,6 +1608,7 @@ sub get_grub_index { die "Could not find '$grub_menu' in /boot/grub/menu on $machine" if (!$found); doprint "$grub_number\n"; + $last_grub_menu = $grub_menu; } sub wait_for_input -- cgit v1.2.3 From 149c2319c6316d979de90ce2c8665e9c02d5927b Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 15 Mar 2013 10:58:02 -0400 Subject: tools/power turbostat: additional Haswell CPU-id There is an additional HSW CPU-id, 0x46, which has C-states exactly like CPU-id 0x45. Signed-off-by: Len Brown --- tools/power/x86/turbostat/turbostat.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 6f3214ed4444..321e066a0753 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -1421,6 +1421,7 @@ int has_nehalem_turbo_ratio_limit(unsigned int family, unsigned int model) case 0x3C: /* HSW */ case 0x3F: /* HSW */ case 0x45: /* HSW */ + case 0x46: /* HSW */ return 1; case 0x2E: /* Nehalem-EX Xeon - Beckton */ case 0x2F: /* Westmere-EX Xeon - Eagleton */ @@ -1515,6 +1516,7 @@ void rapl_probe(unsigned int family, unsigned int model) case 0x3C: /* HSW */ case 0x3F: /* HSW */ case 0x45: /* HSW */ + case 0x46: /* HSW */ do_rapl = RAPL_PKG | RAPL_CORES | RAPL_GFX; break; case 0x2D: @@ -1754,6 +1756,7 @@ int is_snb(unsigned int family, unsigned int model) case 0x3C: /* HSW */ case 0x3F: /* HSW */ case 0x45: /* HSW */ + case 0x46: /* HSW */ return 1; } return 0; @@ -2276,7 +2279,7 @@ int main(int argc, char **argv) cmdline(argc, argv); if (verbose) - fprintf(stderr, "turbostat v3.2 February 11, 2013" + fprintf(stderr, "turbostat v3.3 March 15, 2013" " - Len Brown \n"); turbostat_init(); -- cgit v1.2.3 From 861e10be08c69808065d755d3e3cab5d520a2d32 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Thu, 14 Mar 2013 15:27:51 -0700 Subject: perf tools: Fix build on non-glibc systems due to libio.h absence Including libio.h causes build failures on uClibc systems (which lack libio.h). It appears that libio.h was only included to pull in a definition for NULL, so it has been replaced by stddef.h. On powerpc, libio.h was conditionally included, but could be removed completely as it is unneeded. Also, the included of stdlib.h was changed to stddef.h (as again, only NULL is needed). Signed-off-by: Cody P Schafer Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1363300074-26288-1-git-send-email-cody@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm/util/dwarf-regs.c | 5 +---- tools/perf/arch/powerpc/util/dwarf-regs.c | 5 +---- tools/perf/arch/s390/util/dwarf-regs.c | 2 +- tools/perf/arch/sh/util/dwarf-regs.c | 2 +- tools/perf/arch/sparc/util/dwarf-regs.c | 2 +- tools/perf/arch/x86/util/dwarf-regs.c | 2 +- 6 files changed, 6 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/perf/arch/arm/util/dwarf-regs.c b/tools/perf/arch/arm/util/dwarf-regs.c index e8d5c551c69c..33ec5b339da8 100644 --- a/tools/perf/arch/arm/util/dwarf-regs.c +++ b/tools/perf/arch/arm/util/dwarf-regs.c @@ -8,10 +8,7 @@ * published by the Free Software Foundation. */ -#include -#ifndef __UCLIBC__ -#include -#endif +#include #include struct pt_regs_dwarfnum { diff --git a/tools/perf/arch/powerpc/util/dwarf-regs.c b/tools/perf/arch/powerpc/util/dwarf-regs.c index 7cdd61d0e27c..733151cdf46e 100644 --- a/tools/perf/arch/powerpc/util/dwarf-regs.c +++ b/tools/perf/arch/powerpc/util/dwarf-regs.c @@ -9,10 +9,7 @@ * 2 of the License, or (at your option) any later version. */ -#include -#ifndef __UCLIBC__ -#include -#endif +#include #include diff --git a/tools/perf/arch/s390/util/dwarf-regs.c b/tools/perf/arch/s390/util/dwarf-regs.c index e19653e025fa..0469df02ee62 100644 --- a/tools/perf/arch/s390/util/dwarf-regs.c +++ b/tools/perf/arch/s390/util/dwarf-regs.c @@ -6,7 +6,7 @@ * */ -#include +#include #include #define NUM_GPRS 16 diff --git a/tools/perf/arch/sh/util/dwarf-regs.c b/tools/perf/arch/sh/util/dwarf-regs.c index a11edb007a6c..0d0897f57a10 100644 --- a/tools/perf/arch/sh/util/dwarf-regs.c +++ b/tools/perf/arch/sh/util/dwarf-regs.c @@ -19,7 +19,7 @@ * */ -#include +#include #include /* diff --git a/tools/perf/arch/sparc/util/dwarf-regs.c b/tools/perf/arch/sparc/util/dwarf-regs.c index 0ab88483720c..92eda412fed3 100644 --- a/tools/perf/arch/sparc/util/dwarf-regs.c +++ b/tools/perf/arch/sparc/util/dwarf-regs.c @@ -9,7 +9,7 @@ * 2 of the License, or (at your option) any later version. */ -#include +#include #include #define SPARC_MAX_REGS 96 diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c index a794d3081928..be22dd463232 100644 --- a/tools/perf/arch/x86/util/dwarf-regs.c +++ b/tools/perf/arch/x86/util/dwarf-regs.c @@ -20,7 +20,7 @@ * */ -#include +#include #include /* -- cgit v1.2.3 From fed1208841f5db92cc3bede4b1004e9e986d843e Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 20 Feb 2013 16:32:27 +0100 Subject: perf tools: Remove a write-only variable in the debugfs code debugfs_premounted is written-to only so drop it. This functionality is covered by debugfs_found now. Make it a bool while at it. Signed-off-by: Borislav Petkov Cc: Ingo Molnar Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1361374353-30385-2-git-send-email-bp@alien8.de Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/debugfs.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c index dd8b19319c03..e55495c7823a 100644 --- a/tools/perf/util/debugfs.c +++ b/tools/perf/util/debugfs.c @@ -5,7 +5,6 @@ #include #include -static int debugfs_premounted; char debugfs_mountpoint[PATH_MAX + 1] = "/sys/kernel/debug"; char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events"; @@ -15,7 +14,7 @@ static const char *debugfs_known_mountpoints[] = { 0, }; -static int debugfs_found; +static bool debugfs_found; /* find the path to the mounted debugfs */ const char *debugfs_find_mountpoint(void) @@ -30,7 +29,7 @@ const char *debugfs_find_mountpoint(void) ptr = debugfs_known_mountpoints; while (*ptr) { if (debugfs_valid_mountpoint(*ptr) == 0) { - debugfs_found = 1; + debugfs_found = true; strcpy(debugfs_mountpoint, *ptr); return debugfs_mountpoint; } @@ -52,7 +51,7 @@ const char *debugfs_find_mountpoint(void) if (strcmp(type, "debugfs") != 0) return NULL; - debugfs_found = 1; + debugfs_found = true; return debugfs_mountpoint; } @@ -82,10 +81,8 @@ static void debugfs_set_tracing_events_path(const char *mountpoint) char *debugfs_mount(const char *mountpoint) { /* see if it's already mounted */ - if (debugfs_find_mountpoint()) { - debugfs_premounted = 1; + if (debugfs_find_mountpoint()) goto out; - } /* if not mounted and no argument */ if (mountpoint == NULL) { @@ -100,7 +97,7 @@ char *debugfs_mount(const char *mountpoint) return NULL; /* save the mountpoint */ - debugfs_found = 1; + debugfs_found = true; strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); out: debugfs_set_tracing_events_path(debugfs_mountpoint); -- cgit v1.2.3 From a50e43332756a5ac8d00f3367a54d9effeb9c674 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 20 Feb 2013 16:32:28 +0100 Subject: perf tools: Honor parallel jobs We need to hand down parallel build options like the internal make --jobserver-fds one so that parallel builds can also happen when building perf from the toplevel directory. Make it so #1! Signed-off-by: Borislav Petkov Cc: Ingo Molnar Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1361374353-30385-3-git-send-email-bp@alien8.de Signed-off-by: Arnaldo Carvalho de Melo --- tools/scripts/Makefile.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include index 2964b96aa55f..00a05f45b39a 100644 --- a/tools/scripts/Makefile.include +++ b/tools/scripts/Makefile.include @@ -70,7 +70,7 @@ ifndef V QUIET_BISON = @echo ' ' BISON $@; descend = \ - @echo ' ' DESCEND $(1); \ + +@echo ' ' DESCEND $(1); \ mkdir -p $(OUTPUT)$(1) && \ $(MAKE) $(COMMAND_O) subdir=$(if $(subdir),$(subdir)/$(1),$(1)) $(PRINT_DIR) -C $(1) $(2) endif -- cgit v1.2.3 From 9e4a66482e9a96405eb9ee7f7bf28c9799ca8670 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 20 Feb 2013 16:32:29 +0100 Subject: perf tools: Correct Makefile.include It looks at O= and adjusts the $(OUTPUT) variable based on what the output directory will be. However, when O is defined but empty, it wrongly becomes the user's $HOME dir which is not what we want. So check it is not empty before working with it further. Signed-off-by: Borislav Petkov Cc: Ingo Molnar Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1361374353-30385-4-git-send-email-bp@alien8.de Signed-off-by: Arnaldo Carvalho de Melo --- tools/scripts/Makefile.include | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include index 00a05f45b39a..f03e681f8891 100644 --- a/tools/scripts/Makefile.include +++ b/tools/scripts/Makefile.include @@ -1,3 +1,4 @@ +ifneq ($(O),) ifeq ($(origin O), command line) dummy := $(if $(shell test -d $(O) || echo $(O)),$(error O=$(O) does not exist),) ABSOLUTE_O := $(shell cd $(O) ; pwd) @@ -7,9 +8,10 @@ ifeq ($(objtree),) objtree := $(O) endif endif +endif -ifneq ($(OUTPUT),) # check that the output directory actually exists +ifneq ($(OUTPUT),) OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd) $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) endif -- cgit v1.2.3 From 66857b5a8bc61b0c5e7a9c96f02558ef6d4109c6 Mon Sep 17 00:00:00 2001 From: liguang Date: Tue, 26 Feb 2013 12:12:52 +0800 Subject: perf tools: Sort command-list.txt alphabetically Signed-off-by: liguang Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1361851974-25307-1-git-send-email-lig.fnst@cn.fujitsu.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/command-list.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'tools') diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 3e86bbd8c2d5..a28e31be6cb4 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -10,17 +10,17 @@ perf-buildid-list mainporcelain common perf-diff mainporcelain common perf-evlist mainporcelain common perf-inject mainporcelain common +perf-kmem mainporcelain common +perf-kvm mainporcelain common perf-list mainporcelain common -perf-sched mainporcelain common +perf-lock mainporcelain common +perf-probe mainporcelain full perf-record mainporcelain common perf-report mainporcelain common +perf-sched mainporcelain common +perf-script mainporcelain common perf-stat mainporcelain common +perf-test mainporcelain common perf-timechart mainporcelain common perf-top mainporcelain common perf-trace mainporcelain common -perf-script mainporcelain common -perf-probe mainporcelain full -perf-kmem mainporcelain common -perf-lock mainporcelain common -perf-kvm mainporcelain common -perf-test mainporcelain common -- cgit v1.2.3 From 097c87582c5719adbd8b700f4250e1f1d9f15ebf Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 25 Feb 2013 10:52:49 +0100 Subject: perf tests: Make attr script verbose friendly Making the attr test script runner to pass proper verbose option. Also making single '-v' be more reader friendly and display just the test name. Making the current output to be display for '-vv'. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Oleg Nesterov Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1361785972-7431-3-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/attr.c | 9 +++++++-- tools/perf/tests/attr.py | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index bdcceb886f77..038de3ecb8cb 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -147,10 +147,15 @@ void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, static int run_dir(const char *d, const char *perf) { + char v[] = "-vvvvv"; + int vcnt = min(verbose, (int) sizeof(v) - 1); char cmd[3*PATH_MAX]; - snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %s", - d, d, perf, verbose ? "-v" : ""); + if (verbose) + vcnt++; + + snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %.*s", + d, d, perf, vcnt, v); return system(cmd); } diff --git a/tools/perf/tests/attr.py b/tools/perf/tests/attr.py index 2f629ca485bc..e0ea51335e9c 100644 --- a/tools/perf/tests/attr.py +++ b/tools/perf/tests/attr.py @@ -121,7 +121,7 @@ class Test(object): parser = ConfigParser.SafeConfigParser() parser.read(path) - log.debug("running '%s'" % path) + log.warning("running '%s'" % path) self.path = path self.test_dir = options.test_dir @@ -172,7 +172,7 @@ class Test(object): self.perf, self.command, tempdir, self.args) ret = os.WEXITSTATUS(os.system(cmd)) - log.warning(" running '%s' ret %d " % (cmd, ret)) + log.info(" '%s' ret %d " % (cmd, ret)) if ret != int(self.ret): raise Unsup(self) -- cgit v1.2.3 From c21d0030cfe17d87f8ad80a4205c8203bdb3949b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 25 Feb 2013 10:52:50 +0100 Subject: perf tests: Make attr script test event cpu Make attr script to check for 'cpu' when testing event properties. This will allow us to check the '-C X' option for both record and stat commands. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Oleg Nesterov Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1361785972-7431-4-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/attr.py | 1 + tools/perf/tests/attr/base-record | 1 + tools/perf/tests/attr/base-stat | 1 + 3 files changed, 3 insertions(+) (limited to 'tools') diff --git a/tools/perf/tests/attr.py b/tools/perf/tests/attr.py index e0ea51335e9c..c9b4b6269b51 100644 --- a/tools/perf/tests/attr.py +++ b/tools/perf/tests/attr.py @@ -24,6 +24,7 @@ class Unsup(Exception): class Event(dict): terms = [ + 'cpu', 'flags', 'type', 'size', diff --git a/tools/perf/tests/attr/base-record b/tools/perf/tests/attr/base-record index 5bc3880f7be5..b4fc835de607 100644 --- a/tools/perf/tests/attr/base-record +++ b/tools/perf/tests/attr/base-record @@ -2,6 +2,7 @@ fd=1 group_fd=-1 flags=0 +cpu=* type=0|1 size=96 config=0 diff --git a/tools/perf/tests/attr/base-stat b/tools/perf/tests/attr/base-stat index 4bd79a82784f..748ee949a204 100644 --- a/tools/perf/tests/attr/base-stat +++ b/tools/perf/tests/attr/base-stat @@ -2,6 +2,7 @@ fd=1 group_fd=-1 flags=0 +cpu=* type=0 size=96 config=0 -- cgit v1.2.3 From b03ec1b53070e0fae9de72b584d94b65a4a97635 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 25 Feb 2013 10:52:51 +0100 Subject: perf tests: Add attr record -C cpu test Adding test to validate perf_event_attr data for command: 'record -C 0' Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Oleg Nesterov Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1361785972-7431-5-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/attr/test-record-C0 | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tools/perf/tests/attr/test-record-C0 (limited to 'tools') diff --git a/tools/perf/tests/attr/test-record-C0 b/tools/perf/tests/attr/test-record-C0 new file mode 100644 index 000000000000..d6a7e43f61b3 --- /dev/null +++ b/tools/perf/tests/attr/test-record-C0 @@ -0,0 +1,13 @@ +[config] +command = record +args = -C 0 kill >/dev/null 2>&1 + +[event:base-record] +cpu=0 + +# no enable on exec for CPU attached +enable_on_exec=0 + +# PERF_SAMPLE_IP | PERF_SAMPLE_TID PERF_SAMPLE_TIME | # PERF_SAMPLE_PERIOD +# + PERF_SAMPLE_CPU added by -C 0 +sample_type=391 -- cgit v1.2.3 From 9687b89d21999301ed386855c04b60d00ed1ec02 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 25 Feb 2013 10:52:52 +0100 Subject: perf tests: Add attr stat -C cpu test Adding test to validate perf_event_attr data for command: 'stat -C 0' Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Oleg Nesterov Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1361785972-7431-6-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/attr/test-stat-C0 | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tools/perf/tests/attr/test-stat-C0 (limited to 'tools') diff --git a/tools/perf/tests/attr/test-stat-C0 b/tools/perf/tests/attr/test-stat-C0 new file mode 100644 index 000000000000..aa835950751f --- /dev/null +++ b/tools/perf/tests/attr/test-stat-C0 @@ -0,0 +1,9 @@ +[config] +command = stat +args = -e cycles -C 0 kill >/dev/null 2>&1 +ret = 1 + +[event:base-stat] +# events are enabled by default when attached to cpu +disabled=0 +enable_on_exec=0 -- cgit v1.2.3 From 85c66be101e1847f0eb46dcb48d5738572129694 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 20 Feb 2013 16:32:30 +0100 Subject: perf tools: Introduce tools/lib/lk library This introduces the tools/lib/lk library, that will gradually have the routines that now are used in tools/perf/ and other tools and that can be shared. Start by carving out debugfs routines for general use. Signed-off-by: Borislav Petkov Cc: Ingo Molnar Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1361374353-30385-5-git-send-email-bp@alien8.de [ committer note: Add tools/lib/lk/ to perf's MANIFEST so that its tarballs continue to build ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/Makefile | 16 ++++- tools/lib/lk/Makefile | 35 +++++++++++ tools/lib/lk/debugfs.c | 116 +++++++++++++++++++++++++++++++++++++ tools/lib/lk/debugfs.h | 31 ++++++++++ tools/perf/MANIFEST | 1 + tools/perf/Makefile | 34 ++++++++--- tools/perf/builtin-kvm.c | 2 +- tools/perf/builtin-probe.c | 2 +- tools/perf/perf.c | 2 +- tools/perf/tests/parse-events.c | 2 +- tools/perf/util/debugfs.c | 111 ----------------------------------- tools/perf/util/debugfs.h | 12 ---- tools/perf/util/evlist.c | 2 +- tools/perf/util/evsel.c | 2 +- tools/perf/util/parse-events.c | 2 +- tools/perf/util/probe-event.c | 2 +- tools/perf/util/python-ext-sources | 1 - tools/perf/util/setup.py | 3 +- tools/perf/util/trace-event-info.c | 2 +- 19 files changed, 235 insertions(+), 143 deletions(-) create mode 100644 tools/lib/lk/Makefile create mode 100644 tools/lib/lk/debugfs.c create mode 100644 tools/lib/lk/debugfs.h delete mode 100644 tools/perf/util/debugfs.c delete mode 100644 tools/perf/util/debugfs.h (limited to 'tools') diff --git a/tools/Makefile b/tools/Makefile index 798fa0ef048e..623b1cd86cb3 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -33,7 +33,13 @@ help: cpupower: FORCE $(call descend,power/$@) -firewire lguest perf usb virtio vm: FORCE +firewire guest usb virtio vm: FORCE + $(call descend,$@) + +liblk: FORCE + $(call descend,lib/lk) + +perf: liblk FORCE $(call descend,$@) selftests: FORCE @@ -61,7 +67,13 @@ install: cpupower_install firewire_install lguest_install perf_install \ cpupower_clean: $(call descend,power/cpupower,clean) -firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean: +firewire_clean lguest_clean usb_clean virtio_clean vm_clean: + $(call descend,$(@:_clean=),clean) + +liblk_clean: + $(call descend,lib/lk,clean) + +perf_clean: liblk_clean $(call descend,$(@:_clean=),clean) selftests_clean: diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile new file mode 100644 index 000000000000..8cf576f1a003 --- /dev/null +++ b/tools/lib/lk/Makefile @@ -0,0 +1,35 @@ +include ../../scripts/Makefile.include + +# guard against environment variables +LIB_H= +LIB_OBJS= + +LIB_H += debugfs.h + +LIB_OBJS += $(OUTPUT)debugfs.o + +LIBFILE = liblk.a + +CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -fPIC +EXTLIBS = -lpthread -lrt -lelf -lm +ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 +ALL_LDFLAGS = $(LDFLAGS) + +RM = rm -f + +$(LIBFILE): $(LIB_OBJS) + $(QUIET_AR)$(RM) $@ && $(AR) rcs $(OUTPUT)$@ $(LIB_OBJS) + +$(LIB_OBJS): $(LIB_H) + +$(OUTPUT)%.o: %.c + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< +$(OUTPUT)%.s: %.c + $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< +$(OUTPUT)%.o: %.S + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< + +clean: + $(RM) $(LIB_OBJS) $(LIBFILE) + +.PHONY: clean diff --git a/tools/lib/lk/debugfs.c b/tools/lib/lk/debugfs.c new file mode 100644 index 000000000000..9cda7a6f5917 --- /dev/null +++ b/tools/lib/lk/debugfs.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debugfs.h" + +char debugfs_mountpoint[PATH_MAX + 1] = "/sys/kernel/debug"; +char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events"; + +static const char * const debugfs_known_mountpoints[] = { + "/sys/kernel/debug/", + "/debug/", + 0, +}; + +static bool debugfs_found; + +/* find the path to the mounted debugfs */ +const char *debugfs_find_mountpoint(void) +{ + const char * const *ptr; + char type[100]; + FILE *fp; + + if (debugfs_found) + return (const char *)debugfs_mountpoint; + + ptr = debugfs_known_mountpoints; + while (*ptr) { + if (debugfs_valid_mountpoint(*ptr) == 0) { + debugfs_found = true; + strcpy(debugfs_mountpoint, *ptr); + return debugfs_mountpoint; + } + ptr++; + } + + /* give up and parse /proc/mounts */ + fp = fopen("/proc/mounts", "r"); + if (fp == NULL) + return NULL; + + while (fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", + debugfs_mountpoint, type) == 2) { + if (strcmp(type, "debugfs") == 0) + break; + } + fclose(fp); + + if (strcmp(type, "debugfs") != 0) + return NULL; + + debugfs_found = true; + + return debugfs_mountpoint; +} + +/* verify that a mountpoint is actually a debugfs instance */ + +int debugfs_valid_mountpoint(const char *debugfs) +{ + struct statfs st_fs; + + if (statfs(debugfs, &st_fs) < 0) + return -ENOENT; + else if (st_fs.f_type != (long) DEBUGFS_MAGIC) + return -ENOENT; + + return 0; +} + +static void debugfs_set_tracing_events_path(const char *mountpoint) +{ + snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", + mountpoint, "tracing/events"); +} + +/* mount the debugfs somewhere if it's not mounted */ + +char *debugfs_mount(const char *mountpoint) +{ + /* see if it's already mounted */ + if (debugfs_find_mountpoint()) + goto out; + + /* if not mounted and no argument */ + if (mountpoint == NULL) { + /* see if environment variable set */ + mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT); + /* if no environment variable, use default */ + if (mountpoint == NULL) + mountpoint = "/sys/kernel/debug"; + } + + if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0) + return NULL; + + /* save the mountpoint */ + debugfs_found = true; + strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); +out: + debugfs_set_tracing_events_path(debugfs_mountpoint); + return debugfs_mountpoint; +} + +void debugfs_set_path(const char *mountpoint) +{ + snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint); + debugfs_set_tracing_events_path(mountpoint); +} diff --git a/tools/lib/lk/debugfs.h b/tools/lib/lk/debugfs.h new file mode 100644 index 000000000000..bc5ad2df7c0a --- /dev/null +++ b/tools/lib/lk/debugfs.h @@ -0,0 +1,31 @@ +#ifndef __LK_DEBUGFS_H__ +#define __LK_DEBUGFS_H__ + +#define _STR(x) #x +#define STR(x) _STR(x) + +/* + * On most systems would have given us this, but not on some systems + * (e.g. GNU/Hurd). + */ +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#ifndef DEBUGFS_MAGIC +#define DEBUGFS_MAGIC 0x64626720 +#endif + +#ifndef PERF_DEBUGFS_ENVIRONMENT +#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR" +#endif + +const char *debugfs_find_mountpoint(void); +int debugfs_valid_mountpoint(const char *debugfs); +char *debugfs_mount(const char *mountpoint); +void debugfs_set_path(const char *mountpoint); + +extern char debugfs_mountpoint[]; +extern char tracing_events_path[]; + +#endif /* __LK_DEBUGFS_H__ */ diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 39d41068484f..025de796067c 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -1,6 +1,7 @@ tools/perf tools/scripts tools/lib/traceevent +tools/lib/lk include/linux/const.h include/linux/perf_event.h include/linux/rbtree.h diff --git a/tools/perf/Makefile b/tools/perf/Makefile index bb74c79cd16e..3dcd6273a90b 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -215,6 +215,7 @@ BASIC_CFLAGS = \ -Iutil \ -I. \ -I$(TRACE_EVENT_DIR) \ + -I../lib/ \ -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE BASIC_LDFLAGS = @@ -240,19 +241,28 @@ SCRIPT_SH += perf-archive.sh grep-libs = $(filter -l%,$(1)) strip-libs = $(filter-out -l%,$(1)) +LK_DIR = ../lib/lk/ TRACE_EVENT_DIR = ../lib/traceevent/ +LK_PATH=$(LK_DIR) + ifneq ($(OUTPUT),) TE_PATH=$(OUTPUT) +ifneq ($(subdir),) + LK_PATH=$(OUTPUT)$(LK_DIR) +else + LK_PATH=$(OUTPUT) +endif else TE_PATH=$(TRACE_EVENT_DIR) endif LIBTRACEEVENT = $(TE_PATH)libtraceevent.a -TE_LIB := -L$(TE_PATH) -ltraceevent - export LIBTRACEEVENT +LIBLK = $(LK_PATH)liblk.a +export LIBLK + # python extension build directories PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ @@ -355,7 +365,6 @@ LIB_H += util/cache.h LIB_H += util/callchain.h LIB_H += util/build-id.h LIB_H += util/debug.h -LIB_H += util/debugfs.h LIB_H += util/sysfs.h LIB_H += util/pmu.h LIB_H += util/event.h @@ -416,7 +425,6 @@ LIB_OBJS += $(OUTPUT)util/annotate.o LIB_OBJS += $(OUTPUT)util/build-id.o LIB_OBJS += $(OUTPUT)util/config.o LIB_OBJS += $(OUTPUT)util/ctype.o -LIB_OBJS += $(OUTPUT)util/debugfs.o LIB_OBJS += $(OUTPUT)util/sysfs.o LIB_OBJS += $(OUTPUT)util/pmu.o LIB_OBJS += $(OUTPUT)util/environment.o @@ -536,7 +544,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o BUILTIN_OBJS += $(OUTPUT)builtin-inject.o BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o -PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT) +PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT) # # Platform specific tweaks @@ -1051,6 +1059,18 @@ $(LIBTRACEEVENT): $(LIBTRACEEVENT)-clean: $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean +# if subdir is set, we've been called from above so target has been built +# already +$(LIBLK): +ifeq ($(subdir),) + $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) liblk.a +endif + +$(LIBLK)-clean: +ifeq ($(subdir),) + $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean +endif + help: @echo 'Perf make targets:' @echo ' doc - make *all* documentation (see below)' @@ -1171,7 +1191,7 @@ $(INSTALL_DOC_TARGETS): ### Cleaning rules -clean: $(LIBTRACEEVENT)-clean +clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) $(RM) $(ALL_PROGRAMS) perf $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* @@ -1181,6 +1201,6 @@ clean: $(LIBTRACEEVENT)-clean $(RM) $(OUTPUT)util/*-flex* $(python-clean) -.PHONY: all install clean strip $(LIBTRACEEVENT) +.PHONY: all install clean strip $(LIBTRACEEVENT) $(LIBLK) .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 37a769d7f9fe..533501e2b07c 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -12,7 +12,7 @@ #include "util/parse-options.h" #include "util/trace-event.h" #include "util/debug.h" -#include "util/debugfs.h" +#include #include "util/tool.h" #include "util/stat.h" diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index de38a034b129..e8a66f9a6715 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -37,7 +37,7 @@ #include "util/strfilter.h" #include "util/symbol.h" #include "util/debug.h" -#include "util/debugfs.h" +#include #include "util/parse-options.h" #include "util/probe-finder.h" #include "util/probe-event.h" diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 095b88207cd3..f53b735e2822 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -13,7 +13,7 @@ #include "util/quote.h" #include "util/run-command.h" #include "util/parse-events.h" -#include "util/debugfs.h" +#include #include const char perf_usage_string[] = diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index c5636f36fe31..0d3d0c59f924 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -3,7 +3,7 @@ #include "evsel.h" #include "evlist.h" #include "sysfs.h" -#include "debugfs.h" +#include #include "tests.h" #include diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c deleted file mode 100644 index e55495c7823a..000000000000 --- a/tools/perf/util/debugfs.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "util.h" -#include "debugfs.h" -#include "cache.h" - -#include -#include - -char debugfs_mountpoint[PATH_MAX + 1] = "/sys/kernel/debug"; -char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events"; - -static const char *debugfs_known_mountpoints[] = { - "/sys/kernel/debug/", - "/debug/", - 0, -}; - -static bool debugfs_found; - -/* find the path to the mounted debugfs */ -const char *debugfs_find_mountpoint(void) -{ - const char **ptr; - char type[100]; - FILE *fp; - - if (debugfs_found) - return (const char *) debugfs_mountpoint; - - ptr = debugfs_known_mountpoints; - while (*ptr) { - if (debugfs_valid_mountpoint(*ptr) == 0) { - debugfs_found = true; - strcpy(debugfs_mountpoint, *ptr); - return debugfs_mountpoint; - } - ptr++; - } - - /* give up and parse /proc/mounts */ - fp = fopen("/proc/mounts", "r"); - if (fp == NULL) - return NULL; - - while (fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", - debugfs_mountpoint, type) == 2) { - if (strcmp(type, "debugfs") == 0) - break; - } - fclose(fp); - - if (strcmp(type, "debugfs") != 0) - return NULL; - - debugfs_found = true; - - return debugfs_mountpoint; -} - -/* verify that a mountpoint is actually a debugfs instance */ - -int debugfs_valid_mountpoint(const char *debugfs) -{ - struct statfs st_fs; - - if (statfs(debugfs, &st_fs) < 0) - return -ENOENT; - else if (st_fs.f_type != (long) DEBUGFS_MAGIC) - return -ENOENT; - - return 0; -} - -static void debugfs_set_tracing_events_path(const char *mountpoint) -{ - snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", - mountpoint, "tracing/events"); -} - -/* mount the debugfs somewhere if it's not mounted */ - -char *debugfs_mount(const char *mountpoint) -{ - /* see if it's already mounted */ - if (debugfs_find_mountpoint()) - goto out; - - /* if not mounted and no argument */ - if (mountpoint == NULL) { - /* see if environment variable set */ - mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT); - /* if no environment variable, use default */ - if (mountpoint == NULL) - mountpoint = "/sys/kernel/debug"; - } - - if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0) - return NULL; - - /* save the mountpoint */ - debugfs_found = true; - strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); -out: - debugfs_set_tracing_events_path(debugfs_mountpoint); - return debugfs_mountpoint; -} - -void debugfs_set_path(const char *mountpoint) -{ - snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint); - debugfs_set_tracing_events_path(mountpoint); -} diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h deleted file mode 100644 index 68f3e87ec57f..000000000000 --- a/tools/perf/util/debugfs.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __DEBUGFS_H__ -#define __DEBUGFS_H__ - -const char *debugfs_find_mountpoint(void); -int debugfs_valid_mountpoint(const char *debugfs); -char *debugfs_mount(const char *mountpoint); -void debugfs_set_path(const char *mountpoint); - -extern char debugfs_mountpoint[]; -extern char tracing_events_path[]; - -#endif /* __DEBUGFS_H__ */ diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index bc4ad7977438..7626bb49508d 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -7,7 +7,7 @@ * Released under the GPL v2. (and only v2, not any later version) */ #include "util.h" -#include "debugfs.h" +#include #include #include "cpumap.h" #include "thread_map.h" diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 9c82f98f26de..dc16231f7a5d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -10,7 +10,7 @@ #include #include #include "asm/bug.h" -#include "debugfs.h" +#include #include "event-parse.h" #include "evsel.h" #include "evlist.h" diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c84f48cf9678..6c8bb0fb189b 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -10,7 +10,7 @@ #include "symbol.h" #include "cache.h" #include "header.h" -#include "debugfs.h" +#include #include "parse-events-bison.h" #define YY_EXTRA_TYPE int #include "parse-events-flex.h" diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 49a256e6e0a2..aa04bf9c9ad7 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -40,7 +40,7 @@ #include "color.h" #include "symbol.h" #include "thread.h" -#include "debugfs.h" +#include #include "trace-event.h" /* For __maybe_unused */ #include "probe-event.h" #include "probe-finder.h" diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 64536a993f4a..f75ae1b9900c 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -15,7 +15,6 @@ util/thread_map.c util/util.c util/xyarray.c util/cgroup.c -util/debugfs.c util/rblist.c util/strlist.c util/sysfs.c diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 73d510269784..6b0ed322907e 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -24,6 +24,7 @@ cflags += getenv('CFLAGS', '').split() build_lib = getenv('PYTHON_EXTBUILD_LIB') build_tmp = getenv('PYTHON_EXTBUILD_TMP') libtraceevent = getenv('LIBTRACEEVENT') +liblk = getenv('LIBLK') ext_sources = [f.strip() for f in file('util/python-ext-sources') if len(f.strip()) > 0 and f[0] != '#'] @@ -32,7 +33,7 @@ perf = Extension('perf', sources = ext_sources, include_dirs = ['util/include'], extra_compile_args = cflags, - extra_objects = [libtraceevent], + extra_objects = [libtraceevent, liblk], ) setup(name='perf', diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index a8d81c35ef66..36b9b49d0177 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -38,7 +38,7 @@ #include "../perf.h" #include "trace-event.h" -#include "debugfs.h" +#include #include "evsel.h" #define VERSION "0.5" -- cgit v1.2.3 From 1355915ac626da30a0c02ccd4569c1e5ce2cbb82 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 20 Feb 2013 16:32:31 +0100 Subject: perf tools: Extract perf-specific stuff from debugfs.c Move them to util.c and simplify code a bit. Signed-off-by: Borislav Petkov Cc: Ingo Molnar Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1361374353-30385-6-git-send-email-bp@alien8.de Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/lk/debugfs.c | 15 --------------- tools/lib/lk/debugfs.h | 2 -- tools/perf/perf.c | 6 +++--- tools/perf/util/trace-event-info.c | 2 +- tools/perf/util/util.c | 27 +++++++++++++++++++++++++++ tools/perf/util/util.h | 7 +++++-- 6 files changed, 36 insertions(+), 23 deletions(-) (limited to 'tools') diff --git a/tools/lib/lk/debugfs.c b/tools/lib/lk/debugfs.c index 9cda7a6f5917..099e7cd022e4 100644 --- a/tools/lib/lk/debugfs.c +++ b/tools/lib/lk/debugfs.c @@ -11,7 +11,6 @@ #include "debugfs.h" char debugfs_mountpoint[PATH_MAX + 1] = "/sys/kernel/debug"; -char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events"; static const char * const debugfs_known_mountpoints[] = { "/sys/kernel/debug/", @@ -75,14 +74,7 @@ int debugfs_valid_mountpoint(const char *debugfs) return 0; } -static void debugfs_set_tracing_events_path(const char *mountpoint) -{ - snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", - mountpoint, "tracing/events"); -} - /* mount the debugfs somewhere if it's not mounted */ - char *debugfs_mount(const char *mountpoint) { /* see if it's already mounted */ @@ -105,12 +97,5 @@ char *debugfs_mount(const char *mountpoint) debugfs_found = true; strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); out: - debugfs_set_tracing_events_path(debugfs_mountpoint); return debugfs_mountpoint; } - -void debugfs_set_path(const char *mountpoint) -{ - snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint); - debugfs_set_tracing_events_path(mountpoint); -} diff --git a/tools/lib/lk/debugfs.h b/tools/lib/lk/debugfs.h index bc5ad2df7c0a..935c59bdb442 100644 --- a/tools/lib/lk/debugfs.h +++ b/tools/lib/lk/debugfs.h @@ -23,9 +23,7 @@ const char *debugfs_find_mountpoint(void); int debugfs_valid_mountpoint(const char *debugfs); char *debugfs_mount(const char *mountpoint); -void debugfs_set_path(const char *mountpoint); extern char debugfs_mountpoint[]; -extern char tracing_events_path[]; #endif /* __LK_DEBUGFS_H__ */ diff --git a/tools/perf/perf.c b/tools/perf/perf.c index f53b735e2822..f6ba7b73f40e 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -193,13 +193,13 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) fprintf(stderr, "No directory given for --debugfs-dir.\n"); usage(perf_usage_string); } - debugfs_set_path((*argv)[1]); + perf_debugfs_set_path((*argv)[1]); if (envchanged) *envchanged = 1; (*argv)++; (*argc)--; } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) { - debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR)); + perf_debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR)); fprintf(stderr, "dir: %s\n", debugfs_mountpoint); if (envchanged) *envchanged = 1; @@ -461,7 +461,7 @@ int main(int argc, const char **argv) if (!cmd) cmd = "perf-help"; /* get debugfs mount point from /proc/mounts */ - debugfs_mount(NULL); + perf_debugfs_mount(NULL); /* * "perf-xxxx" is the same as "perf xxxx", but we obviously: * diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 36b9b49d0177..5c1509ab0c29 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -80,7 +80,7 @@ static void *malloc_or_die(unsigned int size) static const char *find_debugfs(void) { - const char *path = debugfs_mount(NULL); + const char *path = perf_debugfs_mount(NULL); if (!path) die("Your kernel not support debugfs filesystem"); diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 805d1f52c5b4..59d868add275 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -17,6 +17,8 @@ bool test_attr__enabled; bool perf_host = true; bool perf_guest = false; +char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events"; + void event_attr_init(struct perf_event_attr *attr) { if (!perf_host) @@ -242,3 +244,28 @@ void get_term_dimensions(struct winsize *ws) ws->ws_row = 25; ws->ws_col = 80; } + +static void set_tracing_events_path(const char *mountpoint) +{ + snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", + mountpoint, "tracing/events"); +} + +const char *perf_debugfs_mount(const char *mountpoint) +{ + const char *mnt; + + mnt = debugfs_mount(mountpoint); + if (!mnt) + return NULL; + + set_tracing_events_path(mnt); + + return mnt; +} + +void perf_debugfs_set_path(const char *mntpt) +{ + snprintf(debugfs_mountpoint, strlen(debugfs_mountpoint), "%s", mntpt); + set_tracing_events_path(mntpt); +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 09b4c26b71aa..6a0781c3a573 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -73,10 +73,14 @@ #include #include "types.h" #include +#include extern const char *graph_line; extern const char *graph_dotted_line; extern char buildid_dir[]; +extern char tracing_events_path[]; +extern void perf_debugfs_set_path(const char *mountpoint); +const char *perf_debugfs_mount(const char *mountpoint); /* On most systems would have given us this, but * not on some systems (e.g. GNU/Hurd). @@ -274,5 +278,4 @@ extern unsigned int page_size; struct winsize; void get_term_dimensions(struct winsize *ws); - -#endif +#endif /* GIT_COMPAT_UTIL_H */ -- cgit v1.2.3 From 5a439645eaf3c0c64ae303ca57f9a4467cbdc6f3 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 20 Feb 2013 16:32:33 +0100 Subject: tools/vm: Switch to liblk library page-flags.c had some older version of debugfs_mount copied from perf so convert it to using the version in the tools library. Signed-off-by: Borislav Petkov Cc: Ingo Molnar Cc: Steven Rostedt Cc: Wu Fengguang Link: http://lkml.kernel.org/r/1361374353-30385-8-git-send-email-bp@alien8.de Signed-off-by: Arnaldo Carvalho de Melo --- tools/vm/Makefile | 17 +++++++++-- tools/vm/page-types.c | 85 +++++---------------------------------------------- 2 files changed, 21 insertions(+), 81 deletions(-) (limited to 'tools') diff --git a/tools/vm/Makefile b/tools/vm/Makefile index 8e30e5c40f8a..24e9ddd93fa4 100644 --- a/tools/vm/Makefile +++ b/tools/vm/Makefile @@ -1,11 +1,22 @@ # Makefile for vm tools +# +TARGETS=page-types slabinfo + +LK_DIR = ../lib/lk +LIBLK = $(LK_DIR)/liblk.a CC = $(CROSS_COMPILE)gcc -CFLAGS = -Wall -Wextra +CFLAGS = -Wall -Wextra -I../lib/ +LDFLAGS = $(LIBLK) + +$(TARGETS): liblk + +liblk: + make -C $(LK_DIR) -all: page-types slabinfo %: %.c - $(CC) $(CFLAGS) -o $@ $^ + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) clean: $(RM) page-types slabinfo + make -C ../lib/lk clean diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c index b76edf2f8333..71c9c2511ee7 100644 --- a/tools/vm/page-types.c +++ b/tools/vm/page-types.c @@ -36,7 +36,7 @@ #include #include "../../include/uapi/linux/magic.h" #include "../../include/uapi/linux/kernel-page-flags.h" - +#include #ifndef MAX_PATH # define MAX_PATH 256 @@ -178,7 +178,7 @@ static int kpageflags_fd; static int opt_hwpoison; static int opt_unpoison; -static char hwpoison_debug_fs[MAX_PATH+1]; +static char *hwpoison_debug_fs; static int hwpoison_inject_fd; static int hwpoison_forget_fd; @@ -458,81 +458,6 @@ static uint64_t kpageflags_flags(uint64_t flags) return flags; } -/* verify that a mountpoint is actually a debugfs instance */ -static int debugfs_valid_mountpoint(const char *debugfs) -{ - struct statfs st_fs; - - if (statfs(debugfs, &st_fs) < 0) - return -ENOENT; - else if (st_fs.f_type != (long) DEBUGFS_MAGIC) - return -ENOENT; - - return 0; -} - -/* find the path to the mounted debugfs */ -static const char *debugfs_find_mountpoint(void) -{ - const char *const *ptr; - char type[100]; - FILE *fp; - - ptr = debugfs_known_mountpoints; - while (*ptr) { - if (debugfs_valid_mountpoint(*ptr) == 0) { - strcpy(hwpoison_debug_fs, *ptr); - return hwpoison_debug_fs; - } - ptr++; - } - - /* give up and parse /proc/mounts */ - fp = fopen("/proc/mounts", "r"); - if (fp == NULL) - perror("Can't open /proc/mounts for read"); - - while (fscanf(fp, "%*s %" - STR(MAX_PATH) - "s %99s %*s %*d %*d\n", - hwpoison_debug_fs, type) == 2) { - if (strcmp(type, "debugfs") == 0) - break; - } - fclose(fp); - - if (strcmp(type, "debugfs") != 0) - return NULL; - - return hwpoison_debug_fs; -} - -/* mount the debugfs somewhere if it's not mounted */ - -static void debugfs_mount(void) -{ - const char *const *ptr; - - /* see if it's already mounted */ - if (debugfs_find_mountpoint()) - return; - - ptr = debugfs_known_mountpoints; - while (*ptr) { - if (mount(NULL, *ptr, "debugfs", 0, NULL) == 0) { - /* save the mountpoint */ - strcpy(hwpoison_debug_fs, *ptr); - break; - } - ptr++; - } - - if (*ptr == NULL) { - perror("mount debugfs"); - exit(EXIT_FAILURE); - } -} - /* * page actions */ @@ -541,7 +466,11 @@ static void prepare_hwpoison_fd(void) { char buf[MAX_PATH + 1]; - debugfs_mount(); + hwpoison_debug_fs = debugfs_mount(NULL); + if (!hwpoison_debug_fs) { + perror("mount debugfs"); + exit(EXIT_FAILURE); + } if (opt_hwpoison && !hwpoison_inject_fd) { snprintf(buf, MAX_PATH, "%s/hwpoison/corrupt-pfn", -- cgit v1.2.3 From b28b130719af6e7f56e0bbdac38ba703a36ba5d5 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Sun, 3 Mar 2013 20:25:33 +0100 Subject: tools lib lk: Fix _FORTIFY_SOURCE builds Jiri Olsa triggers the following build error: SUBDIR ../lib/lk/ CC debugfs.o In file included from /usr/include/errno.h:29:0, from debugfs.c:1: /usr/include/features.h:314:4: error: #warning _FORTIFY_SOURCE requires compiling with optimization (-O) [-Werror=cpp] This is because enabling buffer overflow checks through _FORTIFY_SOURCE require compiler optimizations to be enabled too. However, those are not. Enable them by simply copying the perf optimization level. It can be expanded later if we want to support debug builds, etc. Signed-off-by: Borislav Petkov Reported-by: Jiri Olsa Link: http://lkml.kernel.org/r/1362338733-8718-1-git-send-email-bp@alien8.de Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/lk/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile index 8cf576f1a003..926cbf3efc7f 100644 --- a/tools/lib/lk/Makefile +++ b/tools/lib/lk/Makefile @@ -10,7 +10,7 @@ LIB_OBJS += $(OUTPUT)debugfs.o LIBFILE = liblk.a -CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -fPIC +CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -fPIC EXTLIBS = -lpthread -lrt -lelf -lm ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 ALL_LDFLAGS = $(LDFLAGS) -- cgit v1.2.3 From 334fe7a3c63624eb1bba42f81eb088d5665d9f3e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 11 Mar 2013 16:43:12 +0900 Subject: perf evlist: Remove cpus and threads arguments from perf_evlist__new() It's almost always used with NULL for both arguments. Get rid of the arguments from the signature and use perf_evlist__set_maps() if needed. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362987798-24969-1-git-send-email-namhyung@kernel.org [ committer note: replaced spaces with tabs in some of the affected lines ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 2 +- tools/perf/builtin-stat.c | 2 +- tools/perf/builtin-top.c | 2 +- tools/perf/builtin-trace.c | 2 +- tools/perf/tests/evsel-roundtrip-name.c | 4 ++-- tools/perf/tests/hists_link.c | 2 +- tools/perf/tests/mmap-basic.c | 4 +++- tools/perf/tests/open-syscall-tp-fields.c | 2 +- tools/perf/tests/parse-events.c | 2 +- tools/perf/tests/perf-record.c | 2 +- tools/perf/util/evlist.c | 5 ++--- tools/perf/util/evlist.h | 3 +-- tools/perf/util/header.c | 4 ++-- 13 files changed, 18 insertions(+), 18 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f1a939ebc19c..e3261eae0ad7 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -964,7 +964,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) struct perf_record *rec = &record; char errbuf[BUFSIZ]; - evsel_list = perf_evlist__new(NULL, NULL); + evsel_list = perf_evlist__new(); if (evsel_list == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 99848761f573..020329dca005 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1336,7 +1336,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) setlocale(LC_ALL, ""); - evsel_list = perf_evlist__new(NULL, NULL); + evsel_list = perf_evlist__new(); if (evsel_list == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 72f6eb7b4173..c5601aa7a870 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1116,7 +1116,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) NULL }; - top.evlist = perf_evlist__new(NULL, NULL); + top.evlist = perf_evlist__new(); if (top.evlist == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index d222d7fc7e96..6198eb11e1c6 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -419,7 +419,7 @@ out_dump: static int trace__run(struct trace *trace, int argc, const char **argv) { - struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); + struct perf_evlist *evlist = perf_evlist__new(); struct perf_evsel *evsel; int err = -1, i; unsigned long before; diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index 0fd99a9adb91..0197bda9c461 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c @@ -8,7 +8,7 @@ static int perf_evsel__roundtrip_cache_name_test(void) char name[128]; int type, op, err = 0, ret = 0, i, idx; struct perf_evsel *evsel; - struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); + struct perf_evlist *evlist = perf_evlist__new(); if (evlist == NULL) return -ENOMEM; @@ -64,7 +64,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names) { int i, err; struct perf_evsel *evsel; - struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); + struct perf_evlist *evlist = perf_evlist__new(); if (evlist == NULL) return -ENOMEM; diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 1be64a6c5daf..e0c0267858a1 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -436,7 +436,7 @@ int test__hists_link(void) struct machines machines; struct machine *machine = NULL; struct perf_evsel *evsel, *first; - struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); + struct perf_evlist *evlist = perf_evlist__new(); if (evlist == NULL) return -ENOMEM; diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index cdd50755af51..5b1b5aba722b 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -53,12 +53,14 @@ int test__basic_mmap(void) goto out_free_cpus; } - evlist = perf_evlist__new(cpus, threads); + evlist = perf_evlist__new(); if (evlist == NULL) { pr_debug("perf_evlist__new\n"); goto out_free_cpus; } + perf_evlist__set_maps(evlist, cpus, threads); + for (i = 0; i < nsyscalls; ++i) { char name[64]; diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index 1c52fdc1164e..02cb74174e23 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c @@ -18,7 +18,7 @@ int test__syscall_open_tp_fields(void) }; const char *filename = "/etc/passwd"; int flags = O_RDONLY | O_DIRECTORY; - struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); + struct perf_evlist *evlist = perf_evlist__new(); struct perf_evsel *evsel; int err = -1, i, nr_events = 0, nr_polls = 0; diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 0d3d0c59f924..88e2f44cb157 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1218,7 +1218,7 @@ static int test_event(struct evlist_test *e) struct perf_evlist *evlist; int ret; - evlist = perf_evlist__new(NULL, NULL); + evlist = perf_evlist__new(); if (evlist == NULL) return -ENOMEM; diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 1e8e5128d0da..f6ba75a983a8 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -45,7 +45,7 @@ int test__PERF_RECORD(void) }; cpu_set_t cpu_mask; size_t cpu_mask_size = sizeof(cpu_mask); - struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); + struct perf_evlist *evlist = perf_evlist__new(); struct perf_evsel *evsel; struct perf_sample sample; const char *cmd = "sleep"; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 7626bb49508d..a199f1887bef 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -38,13 +38,12 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, evlist->workload.pid = -1; } -struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, - struct thread_map *threads) +struct perf_evlist *perf_evlist__new(void) { struct perf_evlist *evlist = zalloc(sizeof(*evlist)); if (evlist != NULL) - perf_evlist__init(evlist, cpus, threads); + perf_evlist__init(evlist, NULL, NULL); return evlist; } diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 2dd07bd60b4f..9a7b76e3a873 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -49,8 +49,7 @@ struct perf_evsel_str_handler { void *handler; }; -struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, - struct thread_map *threads); +struct perf_evlist *perf_evlist__new(void); void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, struct thread_map *threads); void perf_evlist__exit(struct perf_evlist *evlist); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f4bfd79ef6a7..a9b7349f7c5f 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2789,7 +2789,7 @@ int perf_session__read_header(struct perf_session *session, int fd) u64 f_id; int nr_attrs, nr_ids, i, j; - session->evlist = perf_evlist__new(NULL, NULL); + session->evlist = perf_evlist__new(); if (session->evlist == NULL) return -ENOMEM; @@ -2940,7 +2940,7 @@ int perf_event__process_attr(union perf_event *event, struct perf_evlist *evlist = *pevlist; if (evlist == NULL) { - *pevlist = evlist = perf_evlist__new(NULL, NULL); + *pevlist = evlist = perf_evlist__new(); if (evlist == NULL) return -ENOMEM; } -- cgit v1.2.3 From 85397956de304106e2fdace2db8f69ab4e966bc5 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 11 Mar 2013 16:43:13 +0900 Subject: perf evlist: Use cpu_map__nr() helper Use the cpu_map__nr() helper to protect a possible NULL cpu map dereference. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362987798-24969-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index a199f1887bef..a482547495b6 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -228,7 +228,7 @@ void perf_evlist__disable(struct perf_evlist *evlist) int cpu, thread; struct perf_evsel *pos; - for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { + for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { list_for_each_entry(pos, &evlist->entries, node) { if (!perf_evsel__is_group_leader(pos)) continue; @@ -443,7 +443,7 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m struct perf_evsel *evsel; int cpu, thread; - for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { + for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { int output = -1; for (thread = 0; thread < evlist->threads->nr; thread++) { @@ -470,7 +470,7 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m return 0; out_unmap: - for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { + for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { if (evlist->mmap[cpu].base != NULL) { munmap(evlist->mmap[cpu].base, evlist->mmap_len); evlist->mmap[cpu].base = NULL; @@ -725,7 +725,7 @@ int perf_evlist__open(struct perf_evlist *evlist) return 0; out_err: - ncpus = evlist->cpus ? evlist->cpus->nr : 1; + ncpus = cpu_map__nr(evlist->cpus); nthreads = evlist->threads ? evlist->threads->nr : 1; list_for_each_entry_reverse(evsel, &evlist->entries, node) -- cgit v1.2.3 From b3a319d528fd57ef600731ee1b84d00b7204881d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 11 Mar 2013 16:43:14 +0900 Subject: perf evlist: Add thread_map__nr() helper Introduce and use the thread_map__nr() function to protect a possible NULL pointer dereference and cleanup the code a bit. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362987798-24969-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 4 ++-- tools/perf/util/evlist.c | 37 +++++++++++++++++++++++-------------- tools/perf/util/thread_map.h | 5 +++++ 3 files changed, 30 insertions(+), 16 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 020329dca005..20ffaf98782e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -249,7 +249,7 @@ static int read_counter_aggr(struct perf_evsel *counter) int i; if (__perf_evsel__read(counter, perf_evsel__nr_cpus(counter), - evsel_list->threads->nr, scale) < 0) + thread_map__nr(evsel_list->threads), scale) < 0) return -1; for (i = 0; i < 3; i++) @@ -488,7 +488,7 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv) list_for_each_entry(counter, &evsel_list->entries, node) { read_counter_aggr(counter); perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), - evsel_list->threads->nr); + thread_map__nr(evsel_list->threads)); } } diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index a482547495b6..7d71a691b864 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -227,12 +227,14 @@ void perf_evlist__disable(struct perf_evlist *evlist) { int cpu, thread; struct perf_evsel *pos; + int nr_cpus = cpu_map__nr(evlist->cpus); + int nr_threads = thread_map__nr(evlist->threads); - for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { + for (cpu = 0; cpu < nr_cpus; cpu++) { list_for_each_entry(pos, &evlist->entries, node) { if (!perf_evsel__is_group_leader(pos)) continue; - for (thread = 0; thread < evlist->threads->nr; thread++) + for (thread = 0; thread < nr_threads; thread++) ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_DISABLE, 0); } @@ -243,12 +245,14 @@ void perf_evlist__enable(struct perf_evlist *evlist) { int cpu, thread; struct perf_evsel *pos; + int nr_cpus = cpu_map__nr(evlist->cpus); + int nr_threads = thread_map__nr(evlist->threads); - for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { + for (cpu = 0; cpu < nr_cpus; cpu++) { list_for_each_entry(pos, &evlist->entries, node) { if (!perf_evsel__is_group_leader(pos)) continue; - for (thread = 0; thread < evlist->threads->nr; thread++) + for (thread = 0; thread < nr_threads; thread++) ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); } @@ -257,7 +261,9 @@ void perf_evlist__enable(struct perf_evlist *evlist) static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) { - int nfds = cpu_map__nr(evlist->cpus) * evlist->threads->nr * evlist->nr_entries; + int nr_cpus = cpu_map__nr(evlist->cpus); + int nr_threads = thread_map__nr(evlist->threads); + int nfds = nr_cpus * nr_threads * evlist->nr_entries; evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); return evlist->pollfd != NULL ? 0 : -ENOMEM; } @@ -417,7 +423,7 @@ static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) { evlist->nr_mmaps = cpu_map__nr(evlist->cpus); if (cpu_map__all(evlist->cpus)) - evlist->nr_mmaps = evlist->threads->nr; + evlist->nr_mmaps = thread_map__nr(evlist->threads); evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); return evlist->mmap != NULL ? 0 : -ENOMEM; } @@ -442,11 +448,13 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m { struct perf_evsel *evsel; int cpu, thread; + int nr_cpus = cpu_map__nr(evlist->cpus); + int nr_threads = thread_map__nr(evlist->threads); - for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { + for (cpu = 0; cpu < nr_cpus; cpu++) { int output = -1; - for (thread = 0; thread < evlist->threads->nr; thread++) { + for (thread = 0; thread < nr_threads; thread++) { list_for_each_entry(evsel, &evlist->entries, node) { int fd = FD(evsel, cpu, thread); @@ -470,7 +478,7 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m return 0; out_unmap: - for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { + for (cpu = 0; cpu < nr_cpus; cpu++) { if (evlist->mmap[cpu].base != NULL) { munmap(evlist->mmap[cpu].base, evlist->mmap_len); evlist->mmap[cpu].base = NULL; @@ -483,8 +491,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in { struct perf_evsel *evsel; int thread; + int nr_threads = thread_map__nr(evlist->threads); - for (thread = 0; thread < evlist->threads->nr; thread++) { + for (thread = 0; thread < nr_threads; thread++) { int output = -1; list_for_each_entry(evsel, &evlist->entries, node) { @@ -509,7 +518,7 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in return 0; out_unmap: - for (thread = 0; thread < evlist->threads->nr; thread++) { + for (thread = 0; thread < nr_threads; thread++) { if (evlist->mmap[thread].base != NULL) { munmap(evlist->mmap[thread].base, evlist->mmap_len); evlist->mmap[thread].base = NULL; @@ -610,7 +619,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist) struct perf_evsel *evsel; int err = 0; const int ncpus = cpu_map__nr(evlist->cpus), - nthreads = evlist->threads->nr; + nthreads = thread_map__nr(evlist->threads); list_for_each_entry(evsel, &evlist->entries, node) { if (evsel->filter == NULL) @@ -629,7 +638,7 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) struct perf_evsel *evsel; int err = 0; const int ncpus = cpu_map__nr(evlist->cpus), - nthreads = evlist->threads->nr; + nthreads = thread_map__nr(evlist->threads); list_for_each_entry(evsel, &evlist->entries, node) { err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); @@ -726,7 +735,7 @@ int perf_evlist__open(struct perf_evlist *evlist) return 0; out_err: ncpus = cpu_map__nr(evlist->cpus); - nthreads = evlist->threads ? evlist->threads->nr : 1; + nthreads = thread_map__nr(evlist->threads); list_for_each_entry_reverse(evsel, &evlist->entries, node) perf_evsel__close(evsel, ncpus, nthreads); diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index f718df8a3c59..0cd8b3108084 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -21,4 +21,9 @@ void thread_map__delete(struct thread_map *threads); size_t thread_map__fprintf(struct thread_map *threads, FILE *fp); +static inline int thread_map__nr(struct thread_map *threads) +{ + return threads ? threads->nr : 1; +} + #endif /* __PERF_THREAD_MAP_H */ -- cgit v1.2.3 From 6ef73ec449af998ca34673636d00decc8e808544 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 11 Mar 2013 16:43:15 +0900 Subject: perf evlist: Pass struct perf_target to perf_evlist__prepare_workload() It's a preparation step of removing @opts arg from the function so that it can be used more widely. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362987798-24969-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 3 ++- tools/perf/builtin-trace.c | 3 ++- tools/perf/tests/perf-record.c | 2 +- tools/perf/util/evlist.c | 3 ++- tools/perf/util/evlist.h | 1 + 5 files changed, 8 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index e3261eae0ad7..a80301797e89 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -474,7 +474,8 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) } if (forks) { - err = perf_evlist__prepare_workload(evsel_list, opts, argv); + err = perf_evlist__prepare_workload(evsel_list, &opts->target, + opts, argv); if (err < 0) { pr_err("Couldn't run the workload!\n"); goto out_delete_session; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 6198eb11e1c6..1de3971437c9 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -461,7 +461,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) signal(SIGINT, sig_handler); if (forks) { - err = perf_evlist__prepare_workload(evlist, &trace->opts, argv); + err = perf_evlist__prepare_workload(evlist, &trace->opts.target, + &trace->opts, argv); if (err < 0) { printf("Couldn't run the workload!\n"); goto out_delete_evlist; diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index f6ba75a983a8..adf6b4a21a60 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -93,7 +93,7 @@ int test__PERF_RECORD(void) * so that we have time to open the evlist (calling sys_perf_event_open * on all the fds) and then mmap them. */ - err = perf_evlist__prepare_workload(evlist, &opts, argv); + err = perf_evlist__prepare_workload(evlist, &opts.target, &opts, argv); if (err < 0) { pr_debug("Couldn't run the workload!\n"); goto out_delete_maps; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 7d71a691b864..291884c804e9 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -745,6 +745,7 @@ out_err: } int perf_evlist__prepare_workload(struct perf_evlist *evlist, + struct perf_target *target, struct perf_record_opts *opts, const char *argv[]) { @@ -800,7 +801,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, exit(-1); } - if (perf_target__none(&opts->target)) + if (perf_target__none(target)) evlist->threads->map[0] = evlist->workload.pid; close(child_ready_pipe[1]); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 9a7b76e3a873..e089906cb4de 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -86,6 +86,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct perf_record_opts *opts); int perf_evlist__prepare_workload(struct perf_evlist *evlist, + struct perf_target *target, struct perf_record_opts *opts, const char *argv[]); int perf_evlist__start_workload(struct perf_evlist *evlist); -- cgit v1.2.3 From 119fa3c922ff53a334507e198b2e3c66e99f54dc Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 11 Mar 2013 16:43:16 +0900 Subject: perf evlist: Do not pass struct record_opts to perf_evlist__prepare_workload() Since it's only used for checking ->pipe_output, we can pass the result directly. Now the perf_evlist__prepare_workload() don't have a dependency of struct perf_record_opts, it can be called from other places like perf stat. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362987798-24969-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 2 +- tools/perf/builtin-trace.c | 2 +- tools/perf/tests/perf-record.c | 2 +- tools/perf/util/evlist.c | 5 ++--- tools/perf/util/evlist.h | 3 +-- 5 files changed, 6 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a80301797e89..2a43c4423f6a 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -475,7 +475,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) if (forks) { err = perf_evlist__prepare_workload(evsel_list, &opts->target, - opts, argv); + argv, opts->pipe_output); if (err < 0) { pr_err("Couldn't run the workload!\n"); goto out_delete_session; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 1de3971437c9..3d9944c3d851 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -462,7 +462,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) if (forks) { err = perf_evlist__prepare_workload(evlist, &trace->opts.target, - &trace->opts, argv); + argv, false); if (err < 0) { printf("Couldn't run the workload!\n"); goto out_delete_evlist; diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index adf6b4a21a60..a1c41b7d3c07 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -93,7 +93,7 @@ int test__PERF_RECORD(void) * so that we have time to open the evlist (calling sys_perf_event_open * on all the fds) and then mmap them. */ - err = perf_evlist__prepare_workload(evlist, &opts.target, &opts, argv); + err = perf_evlist__prepare_workload(evlist, &opts.target, argv, false); if (err < 0) { pr_debug("Couldn't run the workload!\n"); goto out_delete_maps; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 291884c804e9..9a337f091b22 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -746,8 +746,7 @@ out_err: int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct perf_target *target, - struct perf_record_opts *opts, - const char *argv[]) + const char *argv[], bool pipe_output) { int child_ready_pipe[2], go_pipe[2]; char bf; @@ -769,7 +768,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, } if (!evlist->workload.pid) { - if (opts->pipe_output) + if (pipe_output) dup2(2, 1); close(child_ready_pipe[0]); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index e089906cb4de..276a5acc56e6 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -87,8 +87,7 @@ void perf_evlist__config(struct perf_evlist *evlist, int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct perf_target *target, - struct perf_record_opts *opts, - const char *argv[]); + const char *argv[], bool pipe_output); int perf_evlist__start_workload(struct perf_evlist *evlist); int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, -- cgit v1.2.3 From 55e162ea764cb5b38f27ea0b16ee7d31c1a5aedb Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 11 Mar 2013 16:43:17 +0900 Subject: perf evlist: Add want_signal parameter to perf_evlist__prepare_workload() In case a caller doesn't want to receive SIGUSR1 when the child failed to exec(). Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362987798-24969-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 3 ++- tools/perf/builtin-trace.c | 2 +- tools/perf/tests/perf-record.c | 3 ++- tools/perf/util/evlist.c | 6 ++++-- tools/perf/util/evlist.h | 3 ++- 5 files changed, 11 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 2a43c4423f6a..80cc3ea07788 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -475,7 +475,8 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) if (forks) { err = perf_evlist__prepare_workload(evsel_list, &opts->target, - argv, opts->pipe_output); + argv, opts->pipe_output, + true); if (err < 0) { pr_err("Couldn't run the workload!\n"); goto out_delete_session; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 3d9944c3d851..f0c20ef0cd1c 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -462,7 +462,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) if (forks) { err = perf_evlist__prepare_workload(evlist, &trace->opts.target, - argv, false); + argv, false, false); if (err < 0) { printf("Couldn't run the workload!\n"); goto out_delete_evlist; diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index a1c41b7d3c07..ffab5a41ff0a 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -93,7 +93,8 @@ int test__PERF_RECORD(void) * so that we have time to open the evlist (calling sys_perf_event_open * on all the fds) and then mmap them. */ - err = perf_evlist__prepare_workload(evlist, &opts.target, argv, false); + err = perf_evlist__prepare_workload(evlist, &opts.target, argv, + false, false); if (err < 0) { pr_debug("Couldn't run the workload!\n"); goto out_delete_maps; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 9a337f091b22..5b012b8d7a14 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -746,7 +746,8 @@ out_err: int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct perf_target *target, - const char *argv[], bool pipe_output) + const char *argv[], bool pipe_output, + bool want_signal) { int child_ready_pipe[2], go_pipe[2]; char bf; @@ -796,7 +797,8 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, execvp(argv[0], (char **)argv); perror(argv[0]); - kill(getppid(), SIGUSR1); + if (want_signal) + kill(getppid(), SIGUSR1); exit(-1); } diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 276a5acc56e6..c096da7d6d58 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -87,7 +87,8 @@ void perf_evlist__config(struct perf_evlist *evlist, int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct perf_target *target, - const char *argv[], bool pipe_output); + const char *argv[], bool pipe_output, + bool want_signal); int perf_evlist__start_workload(struct perf_evlist *evlist); int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, -- cgit v1.2.3 From acf2892270dcc4288c572b1159474c81f3819749 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 11 Mar 2013 16:43:18 +0900 Subject: perf stat: Use perf_evlist__prepare/start_workload() The perf stat had an open code to the duplicated work. Use the helper as it now can be called without struct perf_record_opts. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362987798-24969-7-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 58 ++++++----------------------------------------- 1 file changed, 7 insertions(+), 51 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 20ffaf98782e..69fe6ed89627 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -337,16 +337,14 @@ static void print_interval(void) } } -static int __run_perf_stat(int argc __maybe_unused, const char **argv) +static int __run_perf_stat(int argc, const char **argv) { char msg[512]; unsigned long long t0, t1; struct perf_evsel *counter; struct timespec ts; int status = 0; - int child_ready_pipe[2], go_pipe[2]; const bool forks = (argc > 0); - char buf; if (interval) { ts.tv_sec = interval / 1000; @@ -362,55 +360,12 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv) return -1; } - if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { - perror("failed to create pipes"); - return -1; - } - if (forks) { - if ((child_pid = fork()) < 0) - perror("failed to fork"); - - if (!child_pid) { - close(child_ready_pipe[0]); - close(go_pipe[1]); - fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); - - /* - * Do a dummy execvp to get the PLT entry resolved, - * so we avoid the resolver overhead on the real - * execvp call. - */ - execvp("", (char **)argv); - - /* - * Tell the parent we're ready to go - */ - close(child_ready_pipe[1]); - - /* - * Wait until the parent tells us to go. - */ - if (read(go_pipe[0], &buf, 1) == -1) - perror("unable to read pipe"); - - execvp(argv[0], (char **)argv); - - perror(argv[0]); - exit(-1); + if (perf_evlist__prepare_workload(evsel_list, &target, argv, + false, false) < 0) { + perror("failed to prepare workload"); + return -1; } - - if (perf_target__none(&target)) - evsel_list->threads->map[0] = child_pid; - - /* - * Wait for the child to be ready to exec. - */ - close(child_ready_pipe[1]); - close(go_pipe[0]); - if (read(child_ready_pipe[0], &buf, 1) == -1) - perror("unable to read pipe"); - close(child_ready_pipe[0]); } if (group) @@ -457,7 +412,8 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv) clock_gettime(CLOCK_MONOTONIC, &ref_time); if (forks) { - close(go_pipe[1]); + perf_evlist__start_workload(evsel_list); + if (interval) { while (!waitpid(child_pid, &status, WNOHANG)) { nanosleep(&ts, NULL); -- cgit v1.2.3 From db8fd07a541fc2d5e8076f0151286e19591465b3 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Mar 2013 14:53:21 +0900 Subject: perf annotate: Pass evsel instead of evidx on annotation functions Pass evsel instead of evidx. This is a preparation for supporting event group view in annotation and no functional change is intended. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362462812-30885-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 16 +++++++++------- tools/perf/builtin-top.c | 2 +- tools/perf/ui/browsers/annotate.c | 30 +++++++++++++++++------------- tools/perf/ui/browsers/hists.c | 2 +- tools/perf/ui/gtk/annotate.c | 10 ++++++---- tools/perf/util/annotate.c | 36 +++++++++++++++++++----------------- tools/perf/util/annotate.h | 36 +++++++++++++++++++----------------- tools/perf/util/hist.h | 5 +++-- 8 files changed, 75 insertions(+), 62 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 2e6961ea3184..2f015a99481b 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -109,14 +109,16 @@ static int process_sample_event(struct perf_tool *tool, return 0; } -static int hist_entry__tty_annotate(struct hist_entry *he, int evidx, +static int hist_entry__tty_annotate(struct hist_entry *he, + struct perf_evsel *evsel, struct perf_annotate *ann) { - return symbol__tty_annotate(he->ms.sym, he->ms.map, evidx, + return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel, ann->print_line, ann->full_paths, 0, 0); } -static void hists__find_annotations(struct hists *self, int evidx, +static void hists__find_annotations(struct hists *self, + struct perf_evsel *evsel, struct perf_annotate *ann) { struct rb_node *nd = rb_first(&self->entries), *next; @@ -142,14 +144,14 @@ find_next: if (use_browser == 2) { int ret; - ret = hist_entry__gtk_annotate(he, evidx, NULL); + ret = hist_entry__gtk_annotate(he, evsel, NULL); if (!ret || !ann->skip_missing) return; /* skip missing symbols */ nd = rb_next(nd); } else if (use_browser == 1) { - key = hist_entry__tui_annotate(he, evidx, NULL); + key = hist_entry__tui_annotate(he, evsel, NULL); switch (key) { case -1: if (!ann->skip_missing) @@ -168,7 +170,7 @@ find_next: if (next != NULL) nd = next; } else { - hist_entry__tty_annotate(he, evidx, ann); + hist_entry__tty_annotate(he, evsel, ann); nd = rb_next(nd); /* * Since we have a hist_entry per IP for the same @@ -230,7 +232,7 @@ static int __cmd_annotate(struct perf_annotate *ann) total_nr_samples += nr_samples; hists__collapse_resort(hists); hists__output_resort(hists); - hists__find_annotations(hists, pos->idx, ann); + hists__find_annotations(hists, pos, ann); } } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index c5601aa7a870..b5520ad0dbb8 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -231,7 +231,7 @@ static void perf_top__show_details(struct perf_top *top) printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name); printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter); - more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx, + more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel, 0, top->sym_pcnt_filter, top->print_entries, 4); if (top->zero) symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx); diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 7dca1555c610..67798472384b 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -8,6 +8,7 @@ #include "../../util/hist.h" #include "../../util/sort.h" #include "../../util/symbol.h" +#include "../../util/evsel.h" #include #include @@ -331,7 +332,7 @@ static void annotate_browser__set_rb_top(struct annotate_browser *browser, } static void annotate_browser__calc_percent(struct annotate_browser *browser, - int evidx) + struct perf_evsel *evsel) { struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; @@ -344,7 +345,7 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, list_for_each_entry(pos, ¬es->src->source, node) { struct browser_disasm_line *bpos = disasm_line__browser(pos); - bpos->percent = disasm_line__calc_percent(pos, sym, evidx); + bpos->percent = disasm_line__calc_percent(pos, sym, evsel->idx); if (bpos->percent < 0.01) { RB_CLEAR_NODE(&bpos->rb_node); continue; @@ -401,7 +402,8 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser) browser->b.nr_entries = browser->nr_asm_entries; } -static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, +static bool annotate_browser__callq(struct annotate_browser *browser, + struct perf_evsel *evsel, struct hist_browser_timer *hbt) { struct map_symbol *ms = browser->b.priv; @@ -432,7 +434,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, } pthread_mutex_unlock(¬es->lock); - symbol__tui_annotate(target, ms->map, evidx, hbt); + symbol__tui_annotate(target, ms->map, evsel, hbt); ui_browser__show_title(&browser->b, sym->name); return true; } @@ -615,7 +617,8 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser browser->addr_width += browser->jumps_width + 1; } -static int annotate_browser__run(struct annotate_browser *browser, int evidx, +static int annotate_browser__run(struct annotate_browser *browser, + struct perf_evsel *evsel, struct hist_browser_timer *hbt) { struct rb_node *nd = NULL; @@ -628,7 +631,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, if (ui_browser__show(&browser->b, sym->name, help) < 0) return -1; - annotate_browser__calc_percent(browser, evidx); + annotate_browser__calc_percent(browser, evsel); if (browser->curr_hot) { annotate_browser__set_rb_top(browser, browser->curr_hot); @@ -641,7 +644,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, key = ui_browser__run(&browser->b, delay_secs); if (delay_secs != 0) { - annotate_browser__calc_percent(browser, evidx); + annotate_browser__calc_percent(browser, evsel); /* * Current line focus got out of the list of most active * lines, NULL it so that if TAB|UNTAB is pressed, we @@ -657,7 +660,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, hbt->timer(hbt->arg); if (delay_secs != 0) - symbol__annotate_decay_histogram(sym, evidx); + symbol__annotate_decay_histogram(sym, evsel->idx); continue; case K_TAB: if (nd != NULL) { @@ -754,7 +757,7 @@ show_help: goto show_sup_ins; goto out; } else if (!(annotate_browser__jump(browser) || - annotate_browser__callq(browser, evidx, hbt))) { + annotate_browser__callq(browser, evsel, hbt))) { show_sup_ins: ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); } @@ -776,10 +779,10 @@ out: return key; } -int hist_entry__tui_annotate(struct hist_entry *he, int evidx, +int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, struct hist_browser_timer *hbt) { - return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt); + return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt); } static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, @@ -826,7 +829,8 @@ static inline int width_jumps(int n) return 1; } -int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, +int symbol__tui_annotate(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, struct hist_browser_timer *hbt) { struct disasm_line *pos, *n; @@ -909,7 +913,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, annotate_browser__update_addr_width(&browser); - ret = annotate_browser__run(&browser, evidx, hbt); + ret = annotate_browser__run(&browser, evsel, hbt); list_for_each_entry_safe(pos, n, ¬es->src->source, node) { list_del(&pos->node); disasm_line__free(pos); diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index aa22704047d6..0e125e1543dc 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1599,7 +1599,7 @@ do_annotate: * Don't let this be freed, say, by hists__decay_entry. */ he->used = true; - err = hist_entry__tui_annotate(he, evsel->idx, hbt); + err = hist_entry__tui_annotate(he, evsel, hbt); he->used = false; /* * offer option to annotate the other branch source or target diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c index 7d8dc581a545..6e2fc7e3f093 100644 --- a/tools/perf/ui/gtk/annotate.c +++ b/tools/perf/ui/gtk/annotate.c @@ -1,6 +1,7 @@ #include "gtk.h" #include "util/debug.h" #include "util/annotate.h" +#include "util/evsel.h" #include "ui/helpline.h" @@ -85,7 +86,7 @@ static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl) } static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, - struct map *map, int evidx, + struct map *map, struct perf_evsel *evsel, struct hist_browser_timer *hbt __maybe_unused) { struct disasm_line *pos, *n; @@ -121,7 +122,7 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, gtk_list_store_append(store, &iter); - if (perf_gtk__get_percent(s, sizeof(s), sym, pos, evidx)) + if (perf_gtk__get_percent(s, sizeof(s), sym, pos, evsel->idx)) gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1); if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos)) gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1); @@ -139,7 +140,8 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, return 0; } -int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx, +int symbol__gtk_annotate(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, struct hist_browser_timer *hbt) { GtkWidget *window; @@ -206,7 +208,7 @@ int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx, gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); - perf_gtk__annotate_symbol(scrolled_window, sym, map, evidx, hbt); + perf_gtk__annotate_symbol(scrolled_window, sym, map, evsel, hbt); return 0; } diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index d33fe937e6f1..7eac5f0895ee 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -14,6 +14,7 @@ #include "symbol.h" #include "debug.h" #include "annotate.h" +#include "evsel.h" #include #include @@ -603,7 +604,7 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa } static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, - int evidx, u64 len, int min_pcnt, int printed, + struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, int max_lines, struct disasm_line *queue) { static const char *prev_line; @@ -616,7 +617,7 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st const char *color; struct annotation *notes = symbol__annotation(sym); struct source_line *src_line = notes->src->lines; - struct sym_hist *h = annotation__histogram(notes, evidx); + struct sym_hist *h = annotation__histogram(notes, evsel->idx); s64 offset = dl->offset; const u64 addr = start + offset; struct disasm_line *next; @@ -648,7 +649,7 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st list_for_each_entry_from(queue, ¬es->src->source, node) { if (queue == dl) break; - disasm_line__print(queue, sym, start, evidx, len, + disasm_line__print(queue, sym, start, evsel, len, 0, 0, 1, NULL); } } @@ -935,7 +936,8 @@ static void symbol__free_source_line(struct symbol *sym, int len) /* Get the filename:line for the colored entries */ static int symbol__get_source_line(struct symbol *sym, struct map *map, - int evidx, struct rb_root *root, int len, + struct perf_evsel *evsel, + struct rb_root *root, int len, const char *filename) { u64 start; @@ -943,7 +945,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, char cmd[PATH_MAX * 2]; struct source_line *src_line; struct annotation *notes = symbol__annotation(sym); - struct sym_hist *h = annotation__histogram(notes, evidx); + struct sym_hist *h = annotation__histogram(notes, evsel->idx); struct rb_root tmp_root = RB_ROOT; if (!h->sum) @@ -1018,10 +1020,10 @@ static void print_summary(struct rb_root *root, const char *filename) } } -static void symbol__annotate_hits(struct symbol *sym, int evidx) +static void symbol__annotate_hits(struct symbol *sym, struct perf_evsel *evsel) { struct annotation *notes = symbol__annotation(sym); - struct sym_hist *h = annotation__histogram(notes, evidx); + struct sym_hist *h = annotation__histogram(notes, evsel->idx); u64 len = symbol__size(sym), offset; for (offset = 0; offset < len; ++offset) @@ -1031,9 +1033,9 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx) printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); } -int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, - bool full_paths, int min_pcnt, int max_lines, - int context) +int symbol__annotate_printf(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, bool full_paths, + int min_pcnt, int max_lines, int context) { struct dso *dso = map->dso; char *filename; @@ -1060,7 +1062,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, printf("------------------------------------------------\n"); if (verbose) - symbol__annotate_hits(sym, evidx); + symbol__annotate_hits(sym, evsel); list_for_each_entry(pos, ¬es->src->source, node) { if (context && queue == NULL) { @@ -1068,7 +1070,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, queue_len = 0; } - switch (disasm_line__print(pos, sym, start, evidx, len, + switch (disasm_line__print(pos, sym, start, evsel, len, min_pcnt, printed, max_lines, queue)) { case 0: @@ -1163,9 +1165,9 @@ size_t disasm__fprintf(struct list_head *head, FILE *fp) return printed; } -int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, - bool print_lines, bool full_paths, int min_pcnt, - int max_lines) +int symbol__tty_annotate(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, bool print_lines, + bool full_paths, int min_pcnt, int max_lines) { struct dso *dso = map->dso; const char *filename = dso->long_name; @@ -1178,12 +1180,12 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, len = symbol__size(sym); if (print_lines) { - symbol__get_source_line(sym, map, evidx, &source_line, + symbol__get_source_line(sym, map, evsel, &source_line, len, filename); print_summary(&source_line, filename); } - symbol__annotate_printf(sym, map, evidx, full_paths, + symbol__annotate_printf(sym, map, evsel, full_paths, min_pcnt, max_lines, 0); if (print_lines) symbol__free_source_line(sym, len); diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index c422440fe611..376395475663 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -130,47 +130,49 @@ void symbol__annotate_zero_histograms(struct symbol *sym); int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym); -int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, - bool full_paths, int min_pcnt, int max_lines, - int context); +int symbol__annotate_printf(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, bool full_paths, + int min_pcnt, int max_lines, int context); void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); void disasm__purge(struct list_head *head); -int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, - bool print_lines, bool full_paths, int min_pcnt, - int max_lines); +int symbol__tty_annotate(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, bool print_lines, + bool full_paths, int min_pcnt, int max_lines); #ifdef NEWT_SUPPORT -int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, +int symbol__tui_annotate(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, struct hist_browser_timer *hbt); #else static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, - struct map *map __maybe_unused, - int evidx __maybe_unused, - struct hist_browser_timer *hbt - __maybe_unused) + struct map *map __maybe_unused, + struct perf_evsel *evsel __maybe_unused, + struct hist_browser_timer *hbt + __maybe_unused) { return 0; } #endif #ifdef GTK2_SUPPORT -int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx, +int symbol__gtk_annotate(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, struct hist_browser_timer *hbt); -static inline int hist_entry__gtk_annotate(struct hist_entry *he, int evidx, +static inline int hist_entry__gtk_annotate(struct hist_entry *he, + struct perf_evsel *evsel, struct hist_browser_timer *hbt) { - return symbol__gtk_annotate(he->ms.sym, he->ms.map, evidx, hbt); + return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt); } void perf_gtk__show_annotations(void); #else static inline int hist_entry__gtk_annotate(struct hist_entry *he __maybe_unused, - int evidx __maybe_unused, - struct hist_browser_timer *hbt - __maybe_unused) + struct perf_evsel *evsel __maybe_unused, + struct hist_browser_timer *hbt __maybe_unused) { return 0; } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 226a4ae2f936..848331377bdb 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -177,7 +177,7 @@ struct hist_browser_timer { #ifdef NEWT_SUPPORT #include "../ui/keysyms.h" -int hist_entry__tui_annotate(struct hist_entry *he, int evidx, +int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, struct hist_browser_timer *hbt); int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, @@ -196,7 +196,8 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, static inline int hist_entry__tui_annotate(struct hist_entry *self __maybe_unused, - int evidx __maybe_unused, + struct perf_evsel *evsel + __maybe_unused, struct hist_browser_timer *hbt __maybe_unused) { -- cgit v1.2.3 From 3aec150af3de6c00570bdacf45bf5a999ab9cf1d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Mar 2013 14:53:22 +0900 Subject: perf annotate: Add a comment on the symbol__parse_objdump_line() The symbol__parse_objdump_line() parses result of the objdump run but it's hard to follow if one doesn't know the output format of the objdump. Add a head comment on the function to help her. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362462812-30885-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 7eac5f0895ee..fa347b169e27 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -689,6 +689,26 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st return 0; } +/* + * symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw) + * which looks like following + * + * 0000000000415500 <_init>: + * 415500: sub $0x8,%rsp + * 415504: mov 0x2f5ad5(%rip),%rax # 70afe0 <_DYNAMIC+0x2f8> + * 41550b: test %rax,%rax + * 41550e: je 415515 <_init+0x15> + * 415510: callq 416e70 <__gmon_start__@plt> + * 415515: add $0x8,%rsp + * 415519: retq + * + * it will be parsed and saved into struct disasm_line as + * + * + * The offset will be a relative offset from the start of the symbol and -1 + * means that it's not a disassembly line so should be treated differently. + * The ops.raw part will be parsed further according to type of the instruction. + */ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, FILE *file, size_t privsize) { -- cgit v1.2.3 From e5ccf9f45d8bff6bfeafa561d2238b0e4beb415e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Mar 2013 14:53:23 +0900 Subject: perf annotate: Factor out disasm__calc_percent() Factor out calculation of histogram of a symbol into disasm__calc_percent. It'll be used for later changes. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362462812-30885-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 49 ++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 19 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index fa347b169e27..a91d7b186081 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -603,6 +603,33 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa return NULL; } +static double disasm__calc_percent(struct disasm_line *next, + struct annotation *notes, int evidx, + s64 offset, u64 len, const char **path) +{ + struct source_line *src_line = notes->src->lines; + struct sym_hist *h = annotation__histogram(notes, evidx); + unsigned int hits = 0; + double percent = 0.0; + + while (offset < (s64)len && + (next == NULL || offset < next->offset)) { + if (src_line) { + if (*path == NULL) + *path = src_line[offset].path; + percent += src_line[offset].percent; + } else + hits += h->addr[offset]; + + ++offset; + } + + if (src_line == NULL && h->sum) + percent = 100.0 * hits / h->sum; + + return percent; +} + static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, int max_lines, struct disasm_line *queue) @@ -612,33 +639,17 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st if (dl->offset != -1) { const char *path = NULL; - unsigned int hits = 0; - double percent = 0.0; + double percent; const char *color; struct annotation *notes = symbol__annotation(sym); - struct source_line *src_line = notes->src->lines; - struct sym_hist *h = annotation__histogram(notes, evsel->idx); s64 offset = dl->offset; const u64 addr = start + offset; struct disasm_line *next; next = disasm__get_next_ip_line(¬es->src->source, dl); - while (offset < (s64)len && - (next == NULL || offset < next->offset)) { - if (src_line) { - if (path == NULL) - path = src_line[offset].path; - percent += src_line[offset].percent; - } else - hits += h->addr[offset]; - - ++offset; - } - - if (src_line == NULL && h->sum) - percent = 100.0 * hits / h->sum; - + percent = disasm__calc_percent(next, notes, evsel->idx, + offset, len, &path); if (percent < min_pcnt) return -1; -- cgit v1.2.3 From bd64fcb8805d8e4575f95f0df22f43b74418a4ec Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Mar 2013 14:53:24 +0900 Subject: perf annotate: Cleanup disasm__calc_percent() The loop end condition is calculated from next disasm_line or the symbol size if it's the last disasm_line. But it doesn't need to be calculated at every iteration. Moving it out of the function can simplify code a bit. Also the src_line doesn't need to be checked in every time. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362462812-30885-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index a91d7b186081..ae71325d3dc7 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -603,29 +603,28 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa return NULL; } -static double disasm__calc_percent(struct disasm_line *next, - struct annotation *notes, int evidx, - s64 offset, u64 len, const char **path) +static double disasm__calc_percent(struct annotation *notes, int evidx, + s64 offset, s64 end, const char **path) { struct source_line *src_line = notes->src->lines; struct sym_hist *h = annotation__histogram(notes, evidx); unsigned int hits = 0; double percent = 0.0; - while (offset < (s64)len && - (next == NULL || offset < next->offset)) { - if (src_line) { + if (src_line) { + while (offset < end) { if (*path == NULL) *path = src_line[offset].path; - percent += src_line[offset].percent; - } else - hits += h->addr[offset]; - ++offset; - } + percent += src_line[offset++].percent; + } + } else { + while (offset < end) + hits += h->addr[offset++]; - if (src_line == NULL && h->sum) - percent = 100.0 * hits / h->sum; + if (h->sum) + percent = 100.0 * hits / h->sum; + } return percent; } @@ -648,8 +647,9 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st next = disasm__get_next_ip_line(¬es->src->source, dl); - percent = disasm__calc_percent(next, notes, evsel->idx, - offset, len, &path); + percent = disasm__calc_percent(notes, evsel->idx, offset, + next ? next->offset : (s64) len, + &path); if (percent < min_pcnt) return -1; -- cgit v1.2.3 From b1dd443296b4f8c6869eba790eec950f80392aea Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Mar 2013 14:53:25 +0900 Subject: perf annotate: Add basic support to event group view Add --group option to enable event grouping. When enabled, all the group members information will be shown with the leader so skip non-leader events. It only supports --stdio output currently. Later patches will extend additional features. $ perf annotate --group --stdio ... Percent | Source code & Disassembly of libpthread-2.15.so -------------------------------------------------------------------------------- : : : : Disassembly of section .text: : : 000000387dc0aa50 <__pthread_mutex_unlock_usercnt>: 8.08 2.40 5.29 : 387dc0aa50: mov %rdi,%rdx 0.00 0.00 0.00 : 387dc0aa53: mov 0x10(%rdi),%edi 0.00 0.00 0.00 : 387dc0aa56: mov %edi,%eax 0.00 0.80 0.00 : 387dc0aa58: and $0x7f,%eax 3.03 2.40 3.53 : 387dc0aa5b: test $0x7c,%dil 0.00 0.00 0.00 : 387dc0aa5f: jne 387dc0aaa9 <__pthread_mutex_unlock_use 0.00 0.00 0.00 : 387dc0aa61: test %eax,%eax 0.00 0.00 0.00 : 387dc0aa63: jne 387dc0aa85 <__pthread_mutex_unlock_use 0.00 0.00 0.00 : 387dc0aa65: and $0x80,%edi 0.00 0.00 0.00 : 387dc0aa6b: test %esi,%esi 3.03 5.60 7.06 : 387dc0aa6d: movl $0x0,0x8(%rdx) 0.00 0.00 0.59 : 387dc0aa74: je 387dc0aa7a <__pthread_mutex_unlock_use 0.00 0.00 0.00 : 387dc0aa76: subl $0x1,0xc(%rdx) 2.02 5.60 1.18 : 387dc0aa7a: mov %edi,%esi 0.00 0.00 0.00 : 387dc0aa7c: lock decl (%rdx) 83.84 83.20 82.35 : 387dc0aa7f: jne 387dc0aada <_L_unlock_586> 0.00 0.00 0.00 : 387dc0aa81: nop 0.00 0.00 0.00 : 387dc0aa82: xor %eax,%eax 0.00 0.00 0.00 : 387dc0aa84: retq ... Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362462812-30885-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-annotate.txt | 3 ++ tools/perf/builtin-annotate.c | 7 ++++ tools/perf/util/annotate.c | 64 +++++++++++++++++++++++++----- 3 files changed, 63 insertions(+), 11 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index 5ad07ef417f0..e9cd39a92dc2 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt @@ -93,6 +93,9 @@ OPTIONS --skip-missing:: Skip symbols that cannot be annotated. +--group:: + Show event group information together + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-report[1] diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 2f015a99481b..ae36f3cb5410 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -232,6 +232,11 @@ static int __cmd_annotate(struct perf_annotate *ann) total_nr_samples += nr_samples; hists__collapse_resort(hists); hists__output_resort(hists); + + if (symbol_conf.event_group && + !perf_evsel__is_group_leader(pos)) + continue; + hists__find_annotations(hists, pos, ann); } } @@ -314,6 +319,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) "Specify disassembler style (e.g. -M intel for intel syntax)"), OPT_STRING(0, "objdump", &objdump_path, "path", "objdump binary to use for disassembly and annotations"), + OPT_BOOLEAN(0, "group", &symbol_conf.event_group, + "Show event group information together"), OPT_END() }; diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index ae71325d3dc7..0955cff5b0ef 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -638,7 +638,9 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st if (dl->offset != -1) { const char *path = NULL; - double percent; + double percent, max_percent = 0.0; + double *ppercents = &percent; + int i, nr_percent = 1; const char *color; struct annotation *notes = symbol__annotation(sym); s64 offset = dl->offset; @@ -647,10 +649,27 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st next = disasm__get_next_ip_line(¬es->src->source, dl); - percent = disasm__calc_percent(notes, evsel->idx, offset, - next ? next->offset : (s64) len, - &path); - if (percent < min_pcnt) + if (symbol_conf.event_group && + perf_evsel__is_group_leader(evsel) && + evsel->nr_members > 1) { + nr_percent = evsel->nr_members; + ppercents = calloc(nr_percent, sizeof(double)); + if (ppercents == NULL) + return -1; + } + + for (i = 0; i < nr_percent; i++) { + percent = disasm__calc_percent(notes, + evsel->idx + i, offset, + next ? next->offset : (s64) len, + &path); + + ppercents[i] = percent; + if (percent > max_percent) + max_percent = percent; + } + + if (max_percent < min_pcnt) return -1; if (max_lines && printed >= max_lines) @@ -665,7 +684,7 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st } } - color = get_percent_color(percent); + color = get_percent_color(max_percent); /* * Also color the filename and line if needed, with @@ -681,20 +700,35 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st } } - color_fprintf(stdout, color, " %7.2f", percent); + for (i = 0; i < nr_percent; i++) { + percent = ppercents[i]; + color = get_percent_color(percent); + color_fprintf(stdout, color, " %7.2f", percent); + } + printf(" : "); color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); + + if (ppercents != &percent) + free(ppercents); + } else if (max_lines && printed >= max_lines) return 1; else { + int width = 8; + if (queue) return -1; + if (symbol_conf.event_group && + perf_evsel__is_group_leader(evsel)) + width *= evsel->nr_members; + if (!*dl->line) - printf(" :\n"); + printf(" %*s:\n", width, " "); else - printf(" : %s\n", dl->line); + printf(" %*s: %s\n", width, " ", dl->line); } return 0; @@ -1077,6 +1111,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int printed = 2, queue_len = 0; int more = 0; u64 len; + int width = 8; + int namelen; filename = strdup(dso->long_name); if (!filename) @@ -1088,9 +1124,15 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, d_filename = basename(filename); len = symbol__size(sym); + namelen = strlen(d_filename); + + if (symbol_conf.event_group && perf_evsel__is_group_leader(evsel)) + width *= evsel->nr_members; - printf(" Percent | Source code & Disassembly of %s\n", d_filename); - printf("------------------------------------------------\n"); + printf(" %-*.*s| Source code & Disassembly of %s\n", + width, width, "Percent", d_filename); + printf("-%-*.*s-------------------------------------\n", + width+namelen, width+namelen, graph_dotted_line); if (verbose) symbol__annotate_hits(sym, evsel); -- cgit v1.2.3 From 759ff497e0e6749437b6723f8d26de0b1833c199 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Mar 2013 14:53:26 +0900 Subject: perf evsel: Introduce perf_evsel__is_group_event() helper The perf_evsel__is_group_event function is for checking whether given evsel needs event group view support or not. Please note that it's different to the existing perf_evsel__is_group_leader() which checks only the given evsel is a leader or a standalone (i.e. non-group) event regardless of event group feature. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362462812-30885-7-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 2 +- tools/perf/ui/browsers/hists.c | 4 ++-- tools/perf/ui/gtk/hists.c | 7 ++----- tools/perf/ui/hist.c | 7 ++----- tools/perf/util/annotate.c | 9 +++------ tools/perf/util/evsel.h | 24 ++++++++++++++++++++++++ 6 files changed, 34 insertions(+), 19 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 96b5a7fee4bb..3f4a79ba5ada 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -314,7 +314,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, char buf[512]; size_t size = sizeof(buf); - if (symbol_conf.event_group && evsel->nr_members > 1) { + if (perf_evsel__is_group_event(evsel)) { struct perf_evsel *pos; perf_evsel__group_desc(evsel, buf, size); diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 0e125e1543dc..a5843fd6ab51 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1193,7 +1193,7 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, char buf[512]; size_t buflen = sizeof(buf); - if (symbol_conf.event_group && evsel->nr_members > 1) { + if (perf_evsel__is_group_event(evsel)) { struct perf_evsel *pos; perf_evsel__group_desc(evsel, buf, buflen); @@ -1709,7 +1709,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser, ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : HE_COLORSET_NORMAL); - if (symbol_conf.event_group && evsel->nr_members > 1) { + if (perf_evsel__is_group_event(evsel)) { struct perf_evsel *pos; ev_name = perf_evsel__group_name(evsel); diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 1e764a8ad259..6f259b3d14e2 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -32,21 +32,18 @@ static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he, int ret; double percent = 0.0; struct hists *hists = he->hists; + struct perf_evsel *evsel = hists_to_evsel(hists); if (hists->stats.total_period) percent = 100.0 * get_field(he) / hists->stats.total_period; ret = __percent_color_snprintf(hpp->buf, hpp->size, percent); - if (symbol_conf.event_group) { + if (perf_evsel__is_group_event(evsel)) { int prev_idx, idx_delta; - struct perf_evsel *evsel = hists_to_evsel(hists); struct hist_entry *pair; int nr_members = evsel->nr_members; - if (nr_members <= 1) - return ret; - prev_idx = perf_evsel__group_idx(evsel); list_for_each_entry(pair, &he->pairs.head, pairs.node) { diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index d671e63aa351..4bf91b09d62d 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -16,6 +16,7 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, { int ret; struct hists *hists = he->hists; + struct perf_evsel *evsel = hists_to_evsel(hists); if (fmt_percent) { double percent = 0.0; @@ -28,15 +29,11 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, } else ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he)); - if (symbol_conf.event_group) { + if (perf_evsel__is_group_event(evsel)) { int prev_idx, idx_delta; - struct perf_evsel *evsel = hists_to_evsel(hists); struct hist_entry *pair; int nr_members = evsel->nr_members; - if (nr_members <= 1) - return ret; - prev_idx = perf_evsel__group_idx(evsel); list_for_each_entry(pair, &he->pairs.head, pairs.node) { diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 0955cff5b0ef..f080cc40f00b 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -649,9 +649,7 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st next = disasm__get_next_ip_line(¬es->src->source, dl); - if (symbol_conf.event_group && - perf_evsel__is_group_leader(evsel) && - evsel->nr_members > 1) { + if (perf_evsel__is_group_event(evsel)) { nr_percent = evsel->nr_members; ppercents = calloc(nr_percent, sizeof(double)); if (ppercents == NULL) @@ -721,8 +719,7 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st if (queue) return -1; - if (symbol_conf.event_group && - perf_evsel__is_group_leader(evsel)) + if (perf_evsel__is_group_event(evsel)) width *= evsel->nr_members; if (!*dl->line) @@ -1126,7 +1123,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, len = symbol__size(sym); namelen = strlen(d_filename); - if (symbol_conf.event_group && perf_evsel__is_group_leader(evsel)) + if (perf_evsel__is_group_event(evsel)) width *= evsel->nr_members; printf(" %-*.*s| Source code & Disassembly of %s\n", diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 52021c3087df..bf758e53c929 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -9,6 +9,7 @@ #include "xyarray.h" #include "cgroup.h" #include "hist.h" +#include "symbol.h" struct perf_counts_values { union { @@ -246,11 +247,34 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel) return list_entry(evsel->node.next, struct perf_evsel, node); } +/** + * perf_evsel__is_group_leader - Return whether given evsel is a leader event + * + * @evsel - evsel selector to be tested + * + * Return %true if @evsel is a group leader or a stand-alone event + */ static inline bool perf_evsel__is_group_leader(const struct perf_evsel *evsel) { return evsel->leader == evsel; } +/** + * perf_evsel__is_group_event - Return whether given evsel is a group event + * + * @evsel - evsel selector to be tested + * + * Return %true iff event group view is enabled and @evsel is a actual group + * leader which has other members in the group + */ +static inline bool perf_evsel__is_group_event(struct perf_evsel *evsel) +{ + if (!symbol_conf.event_group) + return false; + + return perf_evsel__is_group_leader(evsel) && evsel->nr_members > 1; +} + struct perf_attr_details { bool freq; bool verbose; -- cgit v1.2.3 From c5a8368ca667d22a6e45396f23a5392d90396f39 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Mar 2013 14:53:27 +0900 Subject: perf annotate: Factor out struct source_line_percent The source_line_percent struct contains percentage value of the symbol histogram. This is a preparation of event group view change. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362462812-30885-8-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate.c | 2 +- tools/perf/util/annotate.c | 14 +++++++------- tools/perf/util/annotate.h | 8 ++++++-- 3 files changed, 14 insertions(+), 10 deletions(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 67798472384b..cfae57f90146 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -257,7 +257,7 @@ static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *s while (offset < (s64)len && (next == NULL || offset < next->offset)) { if (src_line) { - percent += src_line[offset].percent; + percent += src_line[offset].p[0].percent; } else hits += h->addr[offset]; diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index f080cc40f00b..ebf2596d7e2e 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -616,7 +616,7 @@ static double disasm__calc_percent(struct annotation *notes, int evidx, if (*path == NULL) *path = src_line[offset].path; - percent += src_line[offset++].percent; + percent += src_line[offset++].p[0].percent; } } else { while (offset < end) @@ -929,7 +929,7 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin ret = strcmp(iter->path, src_line->path); if (ret == 0) { - iter->percent_sum += src_line->percent; + iter->p[0].percent_sum += src_line->p[0].percent; return; } @@ -939,7 +939,7 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin p = &(*p)->rb_right; } - src_line->percent_sum = src_line->percent; + src_line->p[0].percent_sum = src_line->p[0].percent; rb_link_node(&src_line->node, parent, p); rb_insert_color(&src_line->node, root); @@ -955,7 +955,7 @@ static void __resort_source_line(struct rb_root *root, struct source_line *src_l parent = *p; iter = rb_entry(parent, struct source_line, node); - if (src_line->percent_sum > iter->percent_sum) + if (src_line->p[0].percent_sum > iter->p[0].percent_sum) p = &(*p)->rb_left; else p = &(*p)->rb_right; @@ -1025,8 +1025,8 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, u64 offset; FILE *fp; - src_line[i].percent = 100.0 * h->addr[i] / h->sum; - if (src_line[i].percent <= 0.5) + src_line[i].p[0].percent = 100.0 * h->addr[i] / h->sum; + if (src_line[i].p[0].percent <= 0.5) continue; offset = start + i; @@ -1073,7 +1073,7 @@ static void print_summary(struct rb_root *root, const char *filename) char *path; src_line = rb_entry(node, struct source_line, node); - percent = src_line->percent_sum; + percent = src_line->p[0].percent_sum; color = get_percent_color(percent); path = src_line->path; diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 376395475663..bb2e3f998983 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -74,11 +74,15 @@ struct sym_hist { u64 addr[0]; }; -struct source_line { - struct rb_node node; +struct source_line_percent { double percent; double percent_sum; +}; + +struct source_line { + struct rb_node node; char *path; + struct source_line_percent p[1]; }; /** struct annotated_source - symbols with hits have this attached as in sannotation -- cgit v1.2.3 From 1491c22a5f8563951d3a798758f82b471ecbf501 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Mar 2013 14:53:28 +0900 Subject: perf annotate: Support event group view for --print-line Dynamically allocate source_line_percent according to a number of group members and save nr_pcnt to the struct source_line. This way we can handle multiple events in a general manner. However since the size of struct source_line is not fixed anymore, iterating whole source_line should care about its size. $ perf annotate --group --stdio --print-line Sorted summary for file /lib/ld-2.11.1.so ---------------------------------------------- 33.33 0.00 /build/buildd/eglibc-2.11.1/elf/rtld.c:381 33.33 0.00 /build/buildd/eglibc-2.11.1/elf/dynamic-link.h:128 33.33 0.00 /build/buildd/eglibc-2.11.1/elf/do-rel.h:105 0.00 75.00 /build/buildd/eglibc-2.11.1/elf/dynamic-link.h:137 0.00 25.00 /build/buildd/eglibc-2.11.1/elf/dynamic-link.h:187 ... Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362462812-30885-9-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 130 +++++++++++++++++++++++++++++++++------------ tools/perf/util/annotate.h | 1 + 2 files changed, 98 insertions(+), 33 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index ebf2596d7e2e..05e34df5d041 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -607,18 +607,26 @@ static double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, s64 end, const char **path) { struct source_line *src_line = notes->src->lines; - struct sym_hist *h = annotation__histogram(notes, evidx); - unsigned int hits = 0; double percent = 0.0; if (src_line) { + size_t sizeof_src_line = sizeof(*src_line) + + sizeof(src_line->p) * (src_line->nr_pcnt - 1); + while (offset < end) { + src_line = (void *)notes->src->lines + + (sizeof_src_line * offset); + if (*path == NULL) - *path = src_line[offset].path; + *path = src_line->path; - percent += src_line[offset++].p[0].percent; + percent += src_line->p[evidx].percent; + offset++; } } else { + struct sym_hist *h = annotation__histogram(notes, evidx); + unsigned int hits = 0; + while (offset < end) hits += h->addr[offset++]; @@ -658,9 +666,10 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st for (i = 0; i < nr_percent; i++) { percent = disasm__calc_percent(notes, - evsel->idx + i, offset, - next ? next->offset : (s64) len, - &path); + notes->src->lines ? i : evsel->idx + i, + offset, + next ? next->offset : (s64) len, + &path); ppercents[i] = percent; if (percent > max_percent) @@ -921,7 +930,7 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin struct source_line *iter; struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; - int ret; + int i, ret; while (*p != NULL) { parent = *p; @@ -929,7 +938,8 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin ret = strcmp(iter->path, src_line->path); if (ret == 0) { - iter->p[0].percent_sum += src_line->p[0].percent; + for (i = 0; i < src_line->nr_pcnt; i++) + iter->p[i].percent_sum += src_line->p[i].percent; return; } @@ -939,12 +949,26 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin p = &(*p)->rb_right; } - src_line->p[0].percent_sum = src_line->p[0].percent; + for (i = 0; i < src_line->nr_pcnt; i++) + src_line->p[i].percent_sum = src_line->p[i].percent; rb_link_node(&src_line->node, parent, p); rb_insert_color(&src_line->node, root); } +static int cmp_source_line(struct source_line *a, struct source_line *b) +{ + int i; + + for (i = 0; i < a->nr_pcnt; i++) { + if (a->p[i].percent_sum == b->p[i].percent_sum) + continue; + return a->p[i].percent_sum > b->p[i].percent_sum; + } + + return 0; +} + static void __resort_source_line(struct rb_root *root, struct source_line *src_line) { struct source_line *iter; @@ -955,7 +979,7 @@ static void __resort_source_line(struct rb_root *root, struct source_line *src_l parent = *p; iter = rb_entry(parent, struct source_line, node); - if (src_line->p[0].percent_sum > iter->p[0].percent_sum) + if (cmp_source_line(src_line, iter)) p = &(*p)->rb_left; else p = &(*p)->rb_right; @@ -987,12 +1011,18 @@ static void symbol__free_source_line(struct symbol *sym, int len) { struct annotation *notes = symbol__annotation(sym); struct source_line *src_line = notes->src->lines; + size_t sizeof_src_line; int i; - for (i = 0; i < len; i++) - free(src_line[i].path); + sizeof_src_line = sizeof(*src_line) + + (sizeof(src_line->p) * (src_line->nr_pcnt - 1)); - free(src_line); + for (i = 0; i < len; i++) { + free(src_line->path); + src_line = (void *)src_line + sizeof_src_line; + } + + free(notes->src->lines); notes->src->lines = NULL; } @@ -1003,17 +1033,30 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, const char *filename) { u64 start; - int i; + int i, k; + int evidx = evsel->idx; char cmd[PATH_MAX * 2]; struct source_line *src_line; struct annotation *notes = symbol__annotation(sym); - struct sym_hist *h = annotation__histogram(notes, evsel->idx); + struct sym_hist *h = annotation__histogram(notes, evidx); struct rb_root tmp_root = RB_ROOT; + int nr_pcnt = 1; + u64 h_sum = h->sum; + size_t sizeof_src_line = sizeof(struct source_line); + + if (perf_evsel__is_group_event(evsel)) { + for (i = 1; i < evsel->nr_members; i++) { + h = annotation__histogram(notes, evidx + i); + h_sum += h->sum; + } + nr_pcnt = evsel->nr_members; + sizeof_src_line += (nr_pcnt - 1) * sizeof(src_line->p); + } - if (!h->sum) + if (!h_sum) return 0; - src_line = notes->src->lines = calloc(len, sizeof(struct source_line)); + src_line = notes->src->lines = calloc(len, sizeof_src_line); if (!notes->src->lines) return -1; @@ -1024,29 +1067,41 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, size_t line_len; u64 offset; FILE *fp; + double percent_max = 0.0; - src_line[i].p[0].percent = 100.0 * h->addr[i] / h->sum; - if (src_line[i].p[0].percent <= 0.5) - continue; + src_line->nr_pcnt = nr_pcnt; + + for (k = 0; k < nr_pcnt; k++) { + h = annotation__histogram(notes, evidx + k); + src_line->p[k].percent = 100.0 * h->addr[i] / h->sum; + + if (src_line->p[k].percent > percent_max) + percent_max = src_line->p[k].percent; + } + + if (percent_max <= 0.5) + goto next; offset = start + i; sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); fp = popen(cmd, "r"); if (!fp) - continue; + goto next; if (getline(&path, &line_len, fp) < 0 || !line_len) - goto next; + goto next_close; - src_line[i].path = malloc(sizeof(char) * line_len + 1); - if (!src_line[i].path) - goto next; + src_line->path = malloc(sizeof(char) * line_len + 1); + if (!src_line->path) + goto next_close; - strcpy(src_line[i].path, path); - insert_source_line(&tmp_root, &src_line[i]); + strcpy(src_line->path, path); + insert_source_line(&tmp_root, src_line); - next: + next_close: pclose(fp); + next: + src_line = (void *)src_line + sizeof_src_line; } resort_source_line(root, &tmp_root); @@ -1068,16 +1123,25 @@ static void print_summary(struct rb_root *root, const char *filename) node = rb_first(root); while (node) { - double percent; + double percent, percent_max = 0.0; const char *color; char *path; + int i; src_line = rb_entry(node, struct source_line, node); - percent = src_line->p[0].percent_sum; - color = get_percent_color(percent); + for (i = 0; i < src_line->nr_pcnt; i++) { + percent = src_line->p[i].percent_sum; + color = get_percent_color(percent); + color_fprintf(stdout, color, " %7.2f", percent); + + if (percent > percent_max) + percent_max = percent; + } + path = src_line->path; + color = get_percent_color(percent_max); + color_fprintf(stdout, color, " %s", path); - color_fprintf(stdout, color, " %7.2f %s", percent, path); node = rb_next(node); } } diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index bb2e3f998983..68f851e6c685 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -82,6 +82,7 @@ struct source_line_percent { struct source_line { struct rb_node node; char *path; + int nr_pcnt; struct source_line_percent p[1]; }; -- cgit v1.2.3 From ab77df672cdbf7a0235a9de3289c173e2fce68e5 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Mar 2013 14:53:29 +0900 Subject: perf annotate browser: Make browser_disasm_line->percent an array Make percent field of struct browser_disasm_line an array and move it to the last. This is a preparation of event group view feature. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362462812-30885-10-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index cfae57f90146..62369f0b6608 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -14,10 +14,10 @@ struct browser_disasm_line { struct rb_node rb_node; - double percent; u32 idx; int idx_asm; int jump_sources; + double percent[1]; }; static struct annotate_browser_opt { @@ -97,9 +97,9 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int int width = browser->width, printed; char bf[256]; - if (dl->offset != -1 && bdl->percent != 0.0) { - ui_browser__set_percent_color(browser, bdl->percent, current_entry); - slsmg_printf("%6.2f ", bdl->percent); + if (dl->offset != -1 && bdl->percent[0] != 0.0) { + ui_browser__set_percent_color(browser, bdl->percent[0], current_entry); + slsmg_printf("%6.2f ", bdl->percent[0]); } else { ui_browser__set_percent_color(browser, 0, current_entry); slsmg_write_nstring(" ", 7); @@ -283,7 +283,7 @@ static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_l while (*p != NULL) { parent = *p; l = rb_entry(parent, struct browser_disasm_line, rb_node); - if (bdl->percent < l->percent) + if (bdl->percent[0] < l->percent[0]) p = &(*p)->rb_left; else p = &(*p)->rb_right; @@ -345,8 +345,8 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, list_for_each_entry(pos, ¬es->src->source, node) { struct browser_disasm_line *bpos = disasm_line__browser(pos); - bpos->percent = disasm_line__calc_percent(pos, sym, evsel->idx); - if (bpos->percent < 0.01) { + bpos->percent[0] = disasm_line__calc_percent(pos, sym, evsel->idx); + if (bpos->percent[0] < 0.01) { RB_CLEAR_NODE(&bpos->rb_node); continue; } -- cgit v1.2.3 From e64aa75bf5559be3ce72e53ae28b76a2f633ca06 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Mar 2013 14:53:30 +0900 Subject: perf annotate browser: Use disasm__calc_percent() The disasm_line__calc_percent() which was used by annotate browser code almost duplicates disasm__calc_percent. Let's get rid of the code duplication. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362462812-30885-11-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate.c | 50 +++++++++++---------------------------- tools/perf/util/annotate.c | 4 ++-- tools/perf/util/annotate.h | 4 ++++ 3 files changed, 20 insertions(+), 38 deletions(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 62369f0b6608..8b16926dd56e 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -240,40 +240,6 @@ static unsigned int annotate_browser__refresh(struct ui_browser *browser) return ret; } -static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx) -{ - double percent = 0.0; - - if (dl->offset != -1) { - int len = sym->end - sym->start; - unsigned int hits = 0; - struct annotation *notes = symbol__annotation(sym); - struct source_line *src_line = notes->src->lines; - struct sym_hist *h = annotation__histogram(notes, evidx); - s64 offset = dl->offset; - struct disasm_line *next; - - next = disasm__get_next_ip_line(¬es->src->source, dl); - while (offset < (s64)len && - (next == NULL || offset < next->offset)) { - if (src_line) { - percent += src_line[offset].p[0].percent; - } else - hits += h->addr[offset]; - - ++offset; - } - /* - * If the percentage wasn't already calculated in - * symbol__get_source_line, do it now: - */ - if (src_line == NULL && h->sum) - percent = 100.0 * hits / h->sum; - } - - return percent; -} - static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl) { struct rb_node **p = &root->rb_node; @@ -337,7 +303,8 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); - struct disasm_line *pos; + struct disasm_line *pos, *next; + s64 len = symbol__size(sym); browser->entries = RB_ROOT; @@ -345,7 +312,18 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, list_for_each_entry(pos, ¬es->src->source, node) { struct browser_disasm_line *bpos = disasm_line__browser(pos); - bpos->percent[0] = disasm_line__calc_percent(pos, sym, evsel->idx); + const char *path = NULL; + + if (pos->offset == -1) { + RB_CLEAR_NODE(&bpos->rb_node); + continue; + } + + next = disasm__get_next_ip_line(¬es->src->source, pos); + bpos->percent[0] = disasm__calc_percent(notes, evsel->idx, + pos->offset, next ? next->offset : len, + &path); + if (bpos->percent[0] < 0.01) { RB_CLEAR_NODE(&bpos->rb_node); continue; diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 05e34df5d041..d102716c43a1 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -603,8 +603,8 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa return NULL; } -static double disasm__calc_percent(struct annotation *notes, int evidx, - s64 offset, s64 end, const char **path) +double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, + s64 end, const char **path) { struct source_line *src_line = notes->src->lines; double percent = 0.0; diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 68f851e6c685..6f3c16f01ab4 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -50,6 +50,8 @@ bool ins__is_jump(const struct ins *ins); bool ins__is_call(const struct ins *ins); int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops); +struct annotation; + struct disasm_line { struct list_head node; s64 offset; @@ -68,6 +70,8 @@ void disasm_line__free(struct disasm_line *dl); struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos); int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw); size_t disasm__fprintf(struct list_head *head, FILE *fp); +double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, + s64 end, const char **path); struct sym_hist { u64 sum; -- cgit v1.2.3 From d8d7cd93e6b5f42bd2ae77680b5dc27415ba7492 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Mar 2013 14:53:32 +0900 Subject: perf annotate/gtk: Support event group view on GTK Add support for event group view to GTK annotation browser. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362462812-30885-13-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/gtk/annotate.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c index 6e2fc7e3f093..f538794615db 100644 --- a/tools/perf/ui/gtk/annotate.c +++ b/tools/perf/ui/gtk/annotate.c @@ -33,7 +33,7 @@ static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym, return 0; symhist = annotation__histogram(symbol__annotation(sym), evidx); - if (!symhist->addr[dl->offset]) + if (!symbol_conf.event_group && !symhist->addr[dl->offset]) return 0; percent = 100.0 * symhist->addr[dl->offset] / symhist->sum; @@ -119,10 +119,24 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, list_for_each_entry(pos, ¬es->src->source, node) { GtkTreeIter iter; + int ret = 0; gtk_list_store_append(store, &iter); - if (perf_gtk__get_percent(s, sizeof(s), sym, pos, evsel->idx)) + if (perf_evsel__is_group_event(evsel)) { + for (i = 0; i < evsel->nr_members; i++) { + ret += perf_gtk__get_percent(s + ret, + sizeof(s) - ret, + sym, pos, + evsel->idx + i); + ret += scnprintf(s + ret, sizeof(s) - ret, " "); + } + } else { + ret = perf_gtk__get_percent(s, sizeof(s), sym, pos, + evsel->idx); + } + + if (ret) gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1); if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos)) gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1); -- cgit v1.2.3 From c7e7b6101361025fbea03833c6aee18e3d7bed34 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 10 Nov 2012 01:21:02 +0900 Subject: perf annotate browser: Support event group view on TUI Dynamically allocate browser_disasm_line according to a number of group members. This way we can handle multiple events in a general manner. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/878v5tl2vc.fsf@sejong.aot.lge.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate.c | 93 +++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 18 deletions(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 8b16926dd56e..f56247a03a22 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -17,6 +17,10 @@ struct browser_disasm_line { u32 idx; int idx_asm; int jump_sources; + /* + * actual length of this array is saved on the nr_events field + * of the struct annotate_browser + */ double percent[1]; }; @@ -34,8 +38,9 @@ struct annotate_browser { struct ui_browser b; struct rb_root entries; struct rb_node *curr_hot; - struct disasm_line *selection; + struct disasm_line *selection; struct disasm_line **offsets; + int nr_events; u64 start; int nr_asm_entries; int nr_entries; @@ -95,14 +100,24 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int (!current_entry || (browser->use_navkeypressed && !browser->navkeypressed))); int width = browser->width, printed; + int i, pcnt_width = 7 * ab->nr_events; + double percent_max = 0.0; char bf[256]; - if (dl->offset != -1 && bdl->percent[0] != 0.0) { - ui_browser__set_percent_color(browser, bdl->percent[0], current_entry); - slsmg_printf("%6.2f ", bdl->percent[0]); + for (i = 0; i < ab->nr_events; i++) { + if (bdl->percent[i] > percent_max) + percent_max = bdl->percent[i]; + } + + if (dl->offset != -1 && percent_max != 0.0) { + for (i = 0; i < ab->nr_events; i++) { + ui_browser__set_percent_color(browser, bdl->percent[i], + current_entry); + slsmg_printf("%6.2f ", bdl->percent[i]); + } } else { ui_browser__set_percent_color(browser, 0, current_entry); - slsmg_write_nstring(" ", 7); + slsmg_write_nstring(" ", pcnt_width); } SLsmg_write_char(' '); @@ -112,12 +127,12 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int width += 1; if (!*dl->line) - slsmg_write_nstring(" ", width - 7); + slsmg_write_nstring(" ", width - pcnt_width); else if (dl->offset == -1) { printed = scnprintf(bf, sizeof(bf), "%*s ", ab->addr_width, " "); slsmg_write_nstring(bf, printed); - slsmg_write_nstring(dl->line, width - printed - 6); + slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1); } else { u64 addr = dl->offset; int color = -1; @@ -176,7 +191,7 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int } disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset); - slsmg_write_nstring(bf, width - 10 - printed); + slsmg_write_nstring(bf, width - pcnt_width - 3 - printed); } if (current_entry) @@ -201,6 +216,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) unsigned int from, to; struct map_symbol *ms = ab->b.priv; struct symbol *sym = ms->sym; + u8 pcnt_width = 7; /* PLT symbols contain external offsets */ if (strstr(sym->name, "@plt")) @@ -224,23 +240,44 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) to = (u64)btarget->idx; } + pcnt_width *= ab->nr_events; + ui_browser__set_color(browser, HE_COLORSET_CODE); - __ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to); + __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width, + from, to); } static unsigned int annotate_browser__refresh(struct ui_browser *browser) { + struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); int ret = ui_browser__list_head_refresh(browser); + int pcnt_width; + + pcnt_width = 7 * ab->nr_events; if (annotate_browser__opts.jump_arrows) annotate_browser__draw_current_jump(browser); ui_browser__set_color(browser, HE_COLORSET_NORMAL); - __ui_browser__vline(browser, 7, 0, browser->height - 1); + __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1); return ret; } -static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl) +static int disasm__cmp(struct browser_disasm_line *a, + struct browser_disasm_line *b, int nr_pcnt) +{ + int i; + + for (i = 0; i < nr_pcnt; i++) { + if (a->percent[i] == b->percent[i]) + continue; + return a->percent[i] < b->percent[i]; + } + return 0; +} + +static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl, + int nr_events) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; @@ -249,7 +286,8 @@ static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_l while (*p != NULL) { parent = *p; l = rb_entry(parent, struct browser_disasm_line, rb_node); - if (bdl->percent[0] < l->percent[0]) + + if (disasm__cmp(bdl, l, nr_events)) p = &(*p)->rb_left; else p = &(*p)->rb_right; @@ -313,6 +351,8 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, list_for_each_entry(pos, ¬es->src->source, node) { struct browser_disasm_line *bpos = disasm_line__browser(pos); const char *path = NULL; + double max_percent = 0.0; + int i; if (pos->offset == -1) { RB_CLEAR_NODE(&bpos->rb_node); @@ -320,15 +360,24 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, } next = disasm__get_next_ip_line(¬es->src->source, pos); - bpos->percent[0] = disasm__calc_percent(notes, evsel->idx, - pos->offset, next ? next->offset : len, - &path); - if (bpos->percent[0] < 0.01) { + for (i = 0; i < browser->nr_events; i++) { + bpos->percent[i] = disasm__calc_percent(notes, + evsel->idx + i, + pos->offset, + next ? next->offset : len, + &path); + + if (max_percent < bpos->percent[i]) + max_percent = bpos->percent[i]; + } + + if (max_percent < 0.01) { RB_CLEAR_NODE(&bpos->rb_node); continue; } - disasm_rb_tree__insert(&browser->entries, bpos); + disasm_rb_tree__insert(&browser->entries, bpos, + browser->nr_events); } pthread_mutex_unlock(¬es->lock); @@ -829,6 +878,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, }, }; int ret = -1; + int nr_pcnt = 1; + size_t sizeof_bdl = sizeof(struct browser_disasm_line); if (sym == NULL) return -1; @@ -844,7 +895,12 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, return -1; } - if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) { + if (perf_evsel__is_group_event(evsel)) { + nr_pcnt = evsel->nr_members; + sizeof_bdl += sizeof(double) * (nr_pcnt - 1); + } + + if (symbol__annotate(sym, map, sizeof_bdl) < 0) { ui__error("%s", ui_helpline__last_msg); goto out_free_offsets; } @@ -882,6 +938,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); browser.max_addr_width = hex_width(sym->end); browser.jumps_width = width_jumps(browser.max_jump_sources); + browser.nr_events = nr_pcnt; browser.b.nr_entries = browser.nr_entries; browser.b.entries = ¬es->src->source, browser.b.width += 18; /* Percentage */ -- cgit v1.2.3 From 0d7f5b57a4373993121df4b4216e9628233b075b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 12 Mar 2013 13:59:20 +0900 Subject: perf trace: Get rid of a duplicate code Checking of sample.raw_data is duplicated and seems an artifact of some git auto merging stuff. Kill it. Signed-off-by: Namhyung Kim Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1363064360-7641-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index f0c20ef0cd1c..49fedb51d569 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -527,13 +527,6 @@ again: continue; } - if (sample.raw_data == NULL) { - printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", - perf_evsel__name(evsel), sample.tid, - sample.cpu, sample.raw_size); - continue; - } - handler = evsel->handler.func; handler(trace, evsel, &sample); } -- cgit v1.2.3 From eba7181d56da7e8198f0c70e3d7074bab47a5910 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 12 Mar 2013 23:07:26 -0600 Subject: perf tools: Remove unused tracing functions Leftovers from before libtraceevent integration. Signed-off-by: David Ahern Link: http://lkml.kernel.org/r/1363151248-16674-3-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-parse.c | 37 ------------------------------------- tools/perf/util/trace-event.h | 4 ---- 2 files changed, 41 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 3aabcd687cd5..4454835a9ebc 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -183,43 +183,6 @@ void event_format__print(struct event_format *event, trace_seq_do_printf(&s); } -void print_trace_event(struct pevent *pevent, int cpu, void *data, int size) -{ - int type = trace_parse_common_type(pevent, data); - struct event_format *event = pevent_find_event(pevent, type); - - if (!event) { - warning("ug! no event found for type %d", type); - return; - } - - event_format__print(event, cpu, data, size); -} - -void print_event(struct pevent *pevent, int cpu, void *data, int size, - unsigned long long nsecs, char *comm) -{ - struct pevent_record record; - struct trace_seq s; - int pid; - - pevent->latency_format = latency_format; - - record.ts = nsecs; - record.cpu = cpu; - record.size = size; - record.data = data; - pid = pevent_data_pid(pevent, &record); - - if (!pevent_pid_is_registered(pevent, pid)) - pevent_register_comm(pevent, comm, pid); - - trace_seq_init(&s); - pevent_print_event(pevent, &s, &record); - trace_seq_do_printf(&s); - printf("\n"); -} - void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size __maybe_unused) { diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index a55fd37ffea1..28ccde8ba20f 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -30,13 +30,9 @@ enum { int bigendian(void); struct pevent *read_trace_init(int file_bigendian, int host_bigendian); -void print_trace_event(struct pevent *pevent, int cpu, void *data, int size); void event_format__print(struct event_format *event, int cpu, void *data, int size); -void print_event(struct pevent *pevent, int cpu, void *data, int size, - unsigned long long nsecs, char *comm); - int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size); int parse_event_file(struct pevent *pevent, char *buf, unsigned long size, char *sys); -- cgit v1.2.3 From c1ad050caad5fbff13fd2f54f49e184bd71de90d Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 12 Mar 2013 23:07:27 -0600 Subject: perf session: Remove unused perf_session__remove_thread method Should have been removed on this changeset, that removed the last user of it: 743eb868657bdb1b26c7b24077ca21c67c82c777 perf tools: Resolve machine earlier and pass it to perf_event_ops Signed-off-by: David Ahern Link: http://lkml.kernel.org/r/1363151248-16674-4-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 12 ------------ tools/perf/util/session.h | 1 - 2 files changed, 13 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index bd85280bb6e8..ab265c2cfab3 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1365,18 +1365,6 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp) return machine__fprintf(&session->machines.host, fp); } -void perf_session__remove_thread(struct perf_session *session, - struct thread *th) -{ - /* - * FIXME: This one makes no sense, we need to remove the thread from - * the machine it belongs to, perf_session can have many machines, so - * doing it always on ->machines.host is wrong. Fix when auditing all - * the 'perf kvm' code. - */ - machine__remove_thread(&session->machines.host, th); -} - struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, unsigned int type) { diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index b5c0847edfa9..6b51d47acdba 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -72,7 +72,6 @@ void perf_event__attr_swap(struct perf_event_attr *attr); int perf_session__create_kernel_maps(struct perf_session *self); void perf_session__set_id_hdr_size(struct perf_session *session); -void perf_session__remove_thread(struct perf_session *self, struct thread *th); static inline struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) -- cgit v1.2.3 From ed8996a6d59b9eb00a50d7d30887ba9f28eb4bb0 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 12 Mar 2013 23:07:28 -0600 Subject: perf machine: Move machine__remove_thread and make static As the now only user, machine__process_exit_event, that is what tools use to process PERF_RECORD_EXIT events, is on the same object file. Signed-off-by: David Ahern Link: http://lkml.kernel.org/r/1363151248-16674-5-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 22 +++++++++++----------- tools/perf/util/machine.h | 1 - 2 files changed, 11 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index efdb38e65a92..c5e3b123782b 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1003,6 +1003,17 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event return 0; } +static void machine__remove_thread(struct machine *machine, struct thread *th) +{ + machine->last_match = NULL; + rb_erase(&th->rb_node, &machine->threads); + /* + * We may have references to this thread, for instance in some hist_entry + * instances, so just move them to a separate list. + */ + list_add_tail(&th->node, &machine->dead_threads); +} + int machine__process_exit_event(struct machine *machine, union perf_event *event) { struct thread *thread = machine__find_thread(machine, event->fork.tid); @@ -1039,17 +1050,6 @@ int machine__process_event(struct machine *machine, union perf_event *event) return ret; } -void machine__remove_thread(struct machine *machine, struct thread *th) -{ - machine->last_match = NULL; - rb_erase(&th->rb_node, &machine->threads); - /* - * We may have references to this thread, for instance in some hist_entry - * instances, so just move them to a separate list. - */ - list_add_tail(&th->node, &machine->dead_threads); -} - static bool symbol__match_parent_regex(struct symbol *sym) { if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 5ac5892f2326..e0b2c00b2e75 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -97,7 +97,6 @@ static inline bool machine__is_host(struct machine *machine) } struct thread *machine__findnew_thread(struct machine *machine, pid_t pid); -void machine__remove_thread(struct machine *machine, struct thread *th); size_t machine__fprintf(struct machine *machine, FILE *fp); -- cgit v1.2.3 From db3c6bf811581c626471a6aecdf0024575b707d7 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 13 Mar 2013 12:24:42 +0800 Subject: perf report: Remove duplicated include Signed-off-by: Wei Yongjun Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/CAPgLHd9=EXaH1hv4jeVvTa4tZFsjnx+8+g3zqmmUKqQ5qRqTEA@mail.gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 1 - 1 file changed, 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3f4a79ba5ada..296bd219977a 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -13,7 +13,6 @@ #include "util/annotate.h" #include "util/color.h" #include -#include "util/cache.h" #include #include "util/symbol.h" #include "util/callchain.h" -- cgit v1.2.3 From f3ff40ec8d92b36e60ebbbdb604ffeb5cfe6545f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 13 Mar 2013 20:19:40 +0900 Subject: perf tools: Remove unused trace_read_data function And functions that called only from the trace_read_data(). Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363173585-9754-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-read.c | 201 ------------------------------------- 1 file changed, 201 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 3741572696af..7cb24635adf2 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -41,8 +41,6 @@ static int input_fd; -static int read_page; - int file_bigendian; int host_bigendian; static int long_size; @@ -287,205 +285,6 @@ static void read_event_files(struct pevent *pevent) } } -struct cpu_data { - unsigned long long offset; - unsigned long long size; - unsigned long long timestamp; - struct pevent_record *next; - char *page; - int cpu; - int index; - int page_size; -}; - -static struct cpu_data *cpu_data; - -static void update_cpu_data_index(int cpu) -{ - cpu_data[cpu].offset += page_size; - cpu_data[cpu].size -= page_size; - cpu_data[cpu].index = 0; -} - -static void get_next_page(int cpu) -{ - off_t save_seek; - off_t ret; - - if (!cpu_data[cpu].page) - return; - - if (read_page) { - if (cpu_data[cpu].size <= page_size) { - free(cpu_data[cpu].page); - cpu_data[cpu].page = NULL; - return; - } - - update_cpu_data_index(cpu); - - /* other parts of the code may expect the pointer to not move */ - save_seek = lseek(input_fd, 0, SEEK_CUR); - - ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET); - if (ret == (off_t)-1) - die("failed to lseek"); - ret = read(input_fd, cpu_data[cpu].page, page_size); - if (ret < 0) - die("failed to read page"); - - /* reset the file pointer back */ - lseek(input_fd, save_seek, SEEK_SET); - - return; - } - - munmap(cpu_data[cpu].page, page_size); - cpu_data[cpu].page = NULL; - - if (cpu_data[cpu].size <= page_size) - return; - - update_cpu_data_index(cpu); - - cpu_data[cpu].page = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE, - input_fd, cpu_data[cpu].offset); - if (cpu_data[cpu].page == MAP_FAILED) - die("failed to mmap cpu %d at offset 0x%llx", - cpu, cpu_data[cpu].offset); -} - -static unsigned int type_len4host(unsigned int type_len_ts) -{ - if (file_bigendian) - return (type_len_ts >> 27) & ((1 << 5) - 1); - else - return type_len_ts & ((1 << 5) - 1); -} - -static unsigned int ts4host(unsigned int type_len_ts) -{ - if (file_bigendian) - return type_len_ts & ((1 << 27) - 1); - else - return type_len_ts >> 5; -} - -static int calc_index(void *ptr, int cpu) -{ - return (unsigned long)ptr - (unsigned long)cpu_data[cpu].page; -} - -struct pevent_record *trace_peek_data(struct pevent *pevent, int cpu) -{ - struct pevent_record *data; - void *page = cpu_data[cpu].page; - int idx = cpu_data[cpu].index; - void *ptr = page + idx; - unsigned long long extend; - unsigned int type_len_ts; - unsigned int type_len; - unsigned int delta; - unsigned int length = 0; - - if (cpu_data[cpu].next) - return cpu_data[cpu].next; - - if (!page) - return NULL; - - if (!idx) { - /* FIXME: handle header page */ - if (header_page_ts_size != 8) - die("expected a long long type for timestamp"); - cpu_data[cpu].timestamp = data2host8(pevent, ptr); - ptr += 8; - switch (header_page_size_size) { - case 4: - cpu_data[cpu].page_size = data2host4(pevent, ptr); - ptr += 4; - break; - case 8: - cpu_data[cpu].page_size = data2host8(pevent, ptr); - ptr += 8; - break; - default: - die("bad long size"); - } - ptr = cpu_data[cpu].page + header_page_data_offset; - } - -read_again: - idx = calc_index(ptr, cpu); - - if (idx >= cpu_data[cpu].page_size) { - get_next_page(cpu); - return trace_peek_data(pevent, cpu); - } - - type_len_ts = data2host4(pevent, ptr); - ptr += 4; - - type_len = type_len4host(type_len_ts); - delta = ts4host(type_len_ts); - - switch (type_len) { - case RINGBUF_TYPE_PADDING: - if (!delta) - die("error, hit unexpected end of page"); - length = data2host4(pevent, ptr); - ptr += 4; - length *= 4; - ptr += length; - goto read_again; - - case RINGBUF_TYPE_TIME_EXTEND: - extend = data2host4(pevent, ptr); - ptr += 4; - extend <<= TS_SHIFT; - extend += delta; - cpu_data[cpu].timestamp += extend; - goto read_again; - - case RINGBUF_TYPE_TIME_STAMP: - ptr += 12; - break; - case 0: - length = data2host4(pevent, ptr); - ptr += 4; - die("here! length=%d", length); - break; - default: - length = type_len * 4; - break; - } - - cpu_data[cpu].timestamp += delta; - - data = malloc_or_die(sizeof(*data)); - memset(data, 0, sizeof(*data)); - - data->ts = cpu_data[cpu].timestamp; - data->size = length; - data->data = ptr; - ptr += length; - - cpu_data[cpu].index = calc_index(ptr, cpu); - cpu_data[cpu].next = data; - - return data; -} - -struct pevent_record *trace_read_data(struct pevent *pevent, int cpu) -{ - struct pevent_record *data; - - data = trace_peek_data(pevent, cpu); - cpu_data[cpu].next = NULL; - - return data; -} - ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) { char buf[BUFSIZ]; -- cgit v1.2.3 From d301de830d89454a47947e9a3851708e8f3a8822 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 13 Mar 2013 20:19:41 +0900 Subject: perf tools: Remove unused struct definitions struct event_list and struct events are never used. Just get rid of them. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363173585-9754-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-info.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 5c1509ab0c29..b0bbd76f4a56 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -55,18 +55,6 @@ unsigned int page_size; static const char *output_file = "trace.info"; static int output_fd; -struct event_list { - struct event_list *next; - const char *event; -}; - -struct events { - struct events *sibling; - struct events *children; - struct events *next; - char *name; -}; - static void *malloc_or_die(unsigned int size) { -- cgit v1.2.3 From 024b13082e9d4a50f2d39c5fe2d1179261e7aa22 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 13 Mar 2013 20:19:42 +0900 Subject: perf tools: Remove unnecessary calc_data_size variable It's not set from anywhere so no need to keep it. Looks like an unneeded copy of the same variable in trace-event-read.c Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363173585-9754-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-info.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index b0bbd76f4a56..783ab51e3ff7 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -119,17 +119,10 @@ static void put_tracing_file(char *file) free(file); } -static ssize_t calc_data_size; - static ssize_t write_or_die(const void *buf, size_t len) { int ret; - if (calc_data_size) { - calc_data_size += len; - return len; - } - ret = write(output_fd, buf, len); if (ret < 0) die("writing to '%s'", output_file); -- cgit v1.2.3 From e5f5e5ee78457198184abf3e43d95ea0fab21272 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 13 Mar 2013 20:19:43 +0900 Subject: perf tools: Remove unused macro definitions They're never used and looks like leftovers from the porting of trace-cmd code. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363173585-9754-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-info.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 783ab51e3ff7..3c452b587daa 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -43,13 +43,6 @@ #define VERSION "0.5" -#define TRACE_CTRL "tracing_on" -#define TRACE "trace" -#define AVAILABLE "available_tracers" -#define CURRENT "current_tracer" -#define ITER_CTRL "trace_options" -#define MAX_LATENCY "tracing_max_latency" - unsigned int page_size; static const char *output_file = "trace.info"; -- cgit v1.2.3 From 45fa534cffb246872de0cb8af207bea4a09aeb2f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 13 Mar 2013 20:19:44 +0900 Subject: perf tools: Remove duplicated page_size definition It's defined in util/util.c and gets set from the begining of perf run. No need to duplicate it. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363173585-9754-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-info.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 3c452b587daa..5729f434c5b1 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -43,8 +43,6 @@ #define VERSION "0.5" -unsigned int page_size; - static const char *output_file = "trace.info"; static int output_fd; @@ -431,7 +429,6 @@ static void tracing_data_header(void) write_or_die(buf, 1); /* save page_size */ - page_size = sysconf(_SC_PAGESIZE); write_or_die(&page_size, 4); } -- cgit v1.2.3 From 5a6bef47b418676546ab86d25631c3cfb9ffaf2a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 10 Mar 2013 19:41:10 +0100 Subject: perf tests: Test breakpoint overflow signal handler Adding automated test for breakpoint event signal handler checking if it's executed properly. The test is related to the proper handling of the RF EFLAGS bit on x86_64, but it's generic for all archs. First we check the signal handler is properly called and that the following debug exception return to user space wouldn't trigger recursive breakpoint. This is related to x86_64 RF EFLAGS bit being managed in a wrong way. Second we check that we can set breakpoint in signal handler, which is not possible on x86_64 if the signal handler is executed with RF EFLAG set. This test is inpired by overflow tests done by Vince Weaver. Signed-off-by: Jiri Olsa Cc: "H. Peter Anvin" Cc: Andi Kleen Cc: Corey Ashford Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Ingo Molnar Cc: Oleg Nesterov Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/1362940871-24486-6-git-send-email-jolsa@redhat.com [ committer note: s/pr_err/pr_debug/g i.e. print just OK or FAILED in non verbose mode ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 1 + tools/perf/tests/bp_signal.c | 186 ++++++++++++++++++++++++++++++++++++++++ tools/perf/tests/builtin-test.c | 4 + tools/perf/tests/tests.h | 1 + 4 files changed, 192 insertions(+) create mode 100644 tools/perf/tests/bp_signal.c (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 3dcd6273a90b..21e0b4b04465 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -511,6 +511,7 @@ LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o LIB_OBJS += $(OUTPUT)tests/pmu.o LIB_OBJS += $(OUTPUT)tests/hists_link.o LIB_OBJS += $(OUTPUT)tests/python-use.o +LIB_OBJS += $(OUTPUT)tests/bp_signal.o BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o BUILTIN_OBJS += $(OUTPUT)builtin-bench.o diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c new file mode 100644 index 000000000000..68daa289e94c --- /dev/null +++ b/tools/perf/tests/bp_signal.c @@ -0,0 +1,186 @@ +/* + * Inspired by breakpoint overflow test done by + * Vince Weaver for perf_event_tests + * (git://github.com/deater/perf_event_tests) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" +#include "debug.h" +#include "perf.h" + +static int fd1; +static int fd2; +static int overflows; + +__attribute__ ((noinline)) +static int test_function(void) +{ + return time(NULL); +} + +static void sig_handler(int signum __maybe_unused, + siginfo_t *oh __maybe_unused, + void *uc __maybe_unused) +{ + overflows++; + + if (overflows > 10) { + /* + * This should be executed only once during + * this test, if we are here for the 10th + * time, consider this the recursive issue. + * + * We can get out of here by disable events, + * so no new SIGIO is delivered. + */ + ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0); + ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0); + } +} + +static int bp_event(void *fn, int setup_signal) +{ + struct perf_event_attr pe; + int fd; + + memset(&pe, 0, sizeof(struct perf_event_attr)); + pe.type = PERF_TYPE_BREAKPOINT; + pe.size = sizeof(struct perf_event_attr); + + pe.config = 0; + pe.bp_type = HW_BREAKPOINT_X; + pe.bp_addr = (unsigned long) fn; + pe.bp_len = sizeof(long); + + pe.sample_period = 1; + pe.sample_type = PERF_SAMPLE_IP; + pe.wakeup_events = 1; + + pe.disabled = 1; + pe.exclude_kernel = 1; + pe.exclude_hv = 1; + + fd = sys_perf_event_open(&pe, 0, -1, -1, 0); + if (fd < 0) { + pr_debug("failed opening event %llx\n", pe.config); + return TEST_FAIL; + } + + if (setup_signal) { + fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC); + fcntl(fd, F_SETSIG, SIGIO); + fcntl(fd, F_SETOWN, getpid()); + } + + ioctl(fd, PERF_EVENT_IOC_RESET, 0); + + return fd; +} + +static long long bp_count(int fd) +{ + long long count; + int ret; + + ret = read(fd, &count, sizeof(long long)); + if (ret != sizeof(long long)) { + pr_debug("failed to read: %d\n", ret); + return TEST_FAIL; + } + + return count; +} + +int test__bp_signal(void) +{ + struct sigaction sa; + long long count1, count2; + + /* setup SIGIO signal handler */ + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_sigaction = (void *) sig_handler; + sa.sa_flags = SA_SIGINFO; + + if (sigaction(SIGIO, &sa, NULL) < 0) { + pr_debug("failed setting up signal handler\n"); + return TEST_FAIL; + } + + /* + * We create following events: + * + * fd1 - breakpoint event on test_function with SIGIO + * signal configured. We should get signal + * notification each time the breakpoint is hit + * + * fd2 - breakpoint event on sig_handler without SIGIO + * configured. + * + * Following processing should happen: + * - execute test_function + * - fd1 event breakpoint hit -> count1 == 1 + * - SIGIO is delivered -> overflows == 1 + * - fd2 event breakpoint hit -> count2 == 1 + * + * The test case check following error conditions: + * - we get stuck in signal handler because of debug + * exception being triggered receursively due to + * the wrong RF EFLAG management + * + * - we never trigger the sig_handler breakpoint due + * to the rong RF EFLAG management + * + */ + + fd1 = bp_event(test_function, 1); + fd2 = bp_event(sig_handler, 0); + + ioctl(fd1, PERF_EVENT_IOC_ENABLE, 0); + ioctl(fd2, PERF_EVENT_IOC_ENABLE, 0); + + /* + * Kick off the test by trigering 'fd1' + * breakpoint. + */ + test_function(); + + ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0); + ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0); + + count1 = bp_count(fd1); + count2 = bp_count(fd2); + + close(fd1); + close(fd2); + + pr_debug("count1 %lld, count2 %lld, overflow %d\n", + count1, count2, overflows); + + if (count1 != 1) { + if (count1 == 11) + pr_debug("failed: RF EFLAG recursion issue detected\n"); + else + pr_debug("failed: wrong count for bp1%lld\n", count1); + } + + if (overflows != 1) + pr_debug("failed: wrong overflow hit\n"); + + if (count2 != 1) + pr_debug("failed: wrong count for bp2\n"); + + return count1 == 1 && overflows == 1 && count2 == 1 ? + TEST_OK : TEST_FAIL; +} diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index acb98e0e39f2..37b108bf973c 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -77,6 +77,10 @@ static struct test { .desc = "Try 'use perf' in python, checking link problems", .func = test__python_use, }, + { + .desc = "Test breakpoint overflow signal handler", + .func = test__bp_signal, + }, { .func = NULL, }, diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 5de0be1ff4b6..05d0e58064a3 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -23,5 +23,6 @@ int test__dso_data(void); int test__parse_events(void); int test__hists_link(void); int test__python_use(void); +int test__bp_signal(void); #endif /* TESTS_H */ -- cgit v1.2.3 From 06933e3a732bb305b0721f1051a45264588e0519 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 10 Mar 2013 19:41:11 +0100 Subject: perf tests: Test breakpoint overflow signal handler counts Adding automated test to check the exact number of breakpoint event overflows and counts. This test was originally done by Vince Weaver for perf_event_tests. Signed-off-by: Jiri Olsa Cc: "H. Peter Anvin" Cc: Andi Kleen Cc: Corey Ashford Cc: Frederic Weisbecker Cc: H. Peter Anvin Cc: Ingo Molnar Cc: Oleg Nesterov Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/1362940871-24486-7-git-send-email-jolsa@redhat.com [ committer note: s/pr_err/pr_debug/g i.e. print just OK or FAILED in non verbose mode ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 1 + tools/perf/tests/bp_signal_overflow.c | 126 ++++++++++++++++++++++++++++++++++ tools/perf/tests/builtin-test.c | 4 ++ tools/perf/tests/tests.h | 1 + 4 files changed, 132 insertions(+) create mode 100644 tools/perf/tests/bp_signal_overflow.c (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 21e0b4b04465..990e9a113190 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -512,6 +512,7 @@ LIB_OBJS += $(OUTPUT)tests/pmu.o LIB_OBJS += $(OUTPUT)tests/hists_link.o LIB_OBJS += $(OUTPUT)tests/python-use.o LIB_OBJS += $(OUTPUT)tests/bp_signal.o +LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o BUILTIN_OBJS += $(OUTPUT)builtin-bench.o diff --git a/tools/perf/tests/bp_signal_overflow.c b/tools/perf/tests/bp_signal_overflow.c new file mode 100644 index 000000000000..fe7ed28815f8 --- /dev/null +++ b/tools/perf/tests/bp_signal_overflow.c @@ -0,0 +1,126 @@ +/* + * Originally done by Vince Weaver for + * perf_event_tests (git://github.com/deater/perf_event_tests) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests.h" +#include "debug.h" +#include "perf.h" + +static int overflows; + +__attribute__ ((noinline)) +static int test_function(void) +{ + return time(NULL); +} + +static void sig_handler(int signum __maybe_unused, + siginfo_t *oh __maybe_unused, + void *uc __maybe_unused) +{ + overflows++; +} + +static long long bp_count(int fd) +{ + long long count; + int ret; + + ret = read(fd, &count, sizeof(long long)); + if (ret != sizeof(long long)) { + pr_debug("failed to read: %d\n", ret); + return TEST_FAIL; + } + + return count; +} + +#define EXECUTIONS 10000 +#define THRESHOLD 100 + +int test__bp_signal_overflow(void) +{ + struct perf_event_attr pe; + struct sigaction sa; + long long count; + int fd, i, fails = 0; + + /* setup SIGIO signal handler */ + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_sigaction = (void *) sig_handler; + sa.sa_flags = SA_SIGINFO; + + if (sigaction(SIGIO, &sa, NULL) < 0) { + pr_debug("failed setting up signal handler\n"); + return TEST_FAIL; + } + + memset(&pe, 0, sizeof(struct perf_event_attr)); + pe.type = PERF_TYPE_BREAKPOINT; + pe.size = sizeof(struct perf_event_attr); + + pe.config = 0; + pe.bp_type = HW_BREAKPOINT_X; + pe.bp_addr = (unsigned long) test_function; + pe.bp_len = sizeof(long); + + pe.sample_period = THRESHOLD; + pe.sample_type = PERF_SAMPLE_IP; + pe.wakeup_events = 1; + + pe.disabled = 1; + pe.exclude_kernel = 1; + pe.exclude_hv = 1; + + fd = sys_perf_event_open(&pe, 0, -1, -1, 0); + if (fd < 0) { + pr_debug("failed opening event %llx\n", pe.config); + return TEST_FAIL; + } + + fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC); + fcntl(fd, F_SETSIG, SIGIO); + fcntl(fd, F_SETOWN, getpid()); + + ioctl(fd, PERF_EVENT_IOC_RESET, 0); + ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); + + for (i = 0; i < EXECUTIONS; i++) + test_function(); + + ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); + + count = bp_count(fd); + + close(fd); + + pr_debug("count %lld, overflow %d\n", + count, overflows); + + if (count != EXECUTIONS) { + pr_debug("\tWrong number of executions %lld != %d\n", + count, EXECUTIONS); + fails++; + } + + if (overflows != EXECUTIONS / THRESHOLD) { + pr_debug("\tWrong number of overflows %d != %d\n", + overflows, EXECUTIONS / THRESHOLD); + fails++; + } + + return fails ? TEST_FAIL : TEST_OK; +} diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 37b108bf973c..45d9ad442d56 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -81,6 +81,10 @@ static struct test { .desc = "Test breakpoint overflow signal handler", .func = test__bp_signal, }, + { + .desc = "Test breakpoint overflow sampling", + .func = test__bp_signal_overflow, + }, { .func = NULL, }, diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 05d0e58064a3..6cf1ec4866d3 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -24,5 +24,6 @@ int test__parse_events(void); int test__hists_link(void); int test__python_use(void); int test__bp_signal(void); +int test__bp_signal_overflow(void); #endif /* TESTS_H */ -- cgit v1.2.3 From 736b05a0462aff65140865bacd5e04d1813e73e1 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 15 Mar 2013 14:48:49 +0900 Subject: perf evsel: Cleanup perf_evsel__exit() Use perf_evsel__free_* because they do the same thing and ensures the pointer has NULL value at the end. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1363326533-3310-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index dc16231f7a5d..7fde9fb79966 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -673,9 +673,8 @@ void perf_evsel__free_counts(struct perf_evsel *evsel) void perf_evsel__exit(struct perf_evsel *evsel) { assert(list_empty(&evsel->node)); - xyarray__delete(evsel->fd); - xyarray__delete(evsel->sample_id); - free(evsel->id); + perf_evsel__free_fd(evsel); + perf_evsel__free_id(evsel); } void perf_evsel__delete(struct perf_evsel *evsel) -- cgit v1.2.3 From a74b4b66cc027110272a18cd50cc6ee93483e78d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 15 Mar 2013 14:48:48 +0900 Subject: perf evlist: Introduce perf_evlist__close() It's a pair of perf_evlist__open(). Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1363326533-3310-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 19 ++++++++++++------- tools/perf/util/evlist.h | 1 + 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 5b012b8d7a14..1344fbd2472e 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -721,10 +721,20 @@ void perf_evlist__set_selected(struct perf_evlist *evlist, evlist->selected = evsel; } +void perf_evlist__close(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + int ncpus = cpu_map__nr(evlist->cpus); + int nthreads = thread_map__nr(evlist->threads); + + list_for_each_entry_reverse(evsel, &evlist->entries, node) + perf_evsel__close(evsel, ncpus, nthreads); +} + int perf_evlist__open(struct perf_evlist *evlist) { struct perf_evsel *evsel; - int err, ncpus, nthreads; + int err; list_for_each_entry(evsel, &evlist->entries, node) { err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); @@ -734,12 +744,7 @@ int perf_evlist__open(struct perf_evlist *evlist) return 0; out_err: - ncpus = cpu_map__nr(evlist->cpus); - nthreads = thread_map__nr(evlist->threads); - - list_for_each_entry_reverse(evsel, &evlist->entries, node) - perf_evsel__close(evsel, ncpus, nthreads); - + perf_evlist__close(evlist); errno = -err; return err; } diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index c096da7d6d58..0583d36252be 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -81,6 +81,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); int perf_evlist__open(struct perf_evlist *evlist); +void perf_evlist__close(struct perf_evlist *evlist); void perf_evlist__config(struct perf_evlist *evlist, struct perf_record_opts *opts); -- cgit v1.2.3 From 3beb0861438f63bc2025f8afba213dc3d0458bc5 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 15 Mar 2013 14:48:50 +0900 Subject: perf trace: Free evlist resources properly on return path The trace_run() function calls several evlist functions but misses some pair-wise cleanup routines on return path. Fix it. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1363326533-3310-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 49fedb51d569..ab3ed4af1466 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -452,7 +452,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) err = trace__symbols_init(trace, evlist); if (err < 0) { printf("Problems initializing symbol libraries!\n"); - goto out_delete_evlist; + goto out_delete_maps; } perf_evlist__config(evlist, &trace->opts); @@ -465,20 +465,20 @@ static int trace__run(struct trace *trace, int argc, const char **argv) argv, false, false); if (err < 0) { printf("Couldn't run the workload!\n"); - goto out_delete_evlist; + goto out_delete_maps; } } err = perf_evlist__open(evlist); if (err < 0) { printf("Couldn't create the events: %s\n", strerror(errno)); - goto out_delete_evlist; + goto out_delete_maps; } err = perf_evlist__mmap(evlist, UINT_MAX, false); if (err < 0) { printf("Couldn't mmap the events: %s\n", strerror(errno)); - goto out_delete_evlist; + goto out_close_evlist; } perf_evlist__enable(evlist); @@ -534,7 +534,7 @@ again: if (trace->nr_events == before) { if (done) - goto out_delete_evlist; + goto out_unmap_evlist; poll(evlist->pollfd, evlist->nr_fds, -1); } @@ -544,6 +544,12 @@ again: goto again; +out_unmap_evlist: + perf_evlist__munmap(evlist); +out_close_evlist: + perf_evlist__close(evlist); +out_delete_maps: + perf_evlist__delete_maps(evlist); out_delete_evlist: perf_evlist__delete(evlist); out: -- cgit v1.2.3 From 8fa60e1fbaecd2e652abe41f68a934c1759663f3 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 15 Mar 2013 14:48:51 +0900 Subject: perf record: Fixup return path of cmd_record() The error path of calling perf_target__parse_uid wrongly went to out_free_fd. Also add missing evlist cleanup routines. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1363326533-3310-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 80cc3ea07788..9f2344a2c506 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1028,7 +1028,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) ui__error("%s", errbuf); err = -saved_errno; - goto out_free_fd; + goto out_symbol_exit; } err = -ENOMEM; @@ -1059,6 +1059,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) } err = __cmd_record(&record, argc, argv); + + perf_evlist__munmap(evsel_list); + perf_evlist__close(evsel_list); out_free_fd: perf_evlist__delete_maps(evsel_list); out_symbol_exit: -- cgit v1.2.3 From 9b5b7cdc5139fdcc30ee56d9cd162da60453f6d8 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 15 Mar 2013 14:48:52 +0900 Subject: perf tests: Fixup return path of open-syscall-tp-fields test case Add missing evlist cleanup functions. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1363326533-3310-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/open-syscall-tp-fields.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index 02cb74174e23..fc5b9fca8b47 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c @@ -48,13 +48,13 @@ int test__syscall_open_tp_fields(void) err = perf_evlist__open(evlist); if (err < 0) { pr_debug("perf_evlist__open: %s\n", strerror(errno)); - goto out_delete_evlist; + goto out_delete_maps; } err = perf_evlist__mmap(evlist, UINT_MAX, false); if (err < 0) { pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); - goto out_delete_evlist; + goto out_close_evlist; } perf_evlist__enable(evlist); @@ -110,6 +110,10 @@ out_ok: err = 0; out_munmap: perf_evlist__munmap(evlist); +out_close_evlist: + perf_evlist__close(evlist); +out_delete_maps: + perf_evlist__delete_maps(evlist); out_delete_evlist: perf_evlist__delete(evlist); out: -- cgit v1.2.3 From da522c17035a8415232d850b539ea60063fc7ecc Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 15 Mar 2013 14:48:53 +0900 Subject: perf tests: Fixup return path of perf record test case Add missing perf_evlist__close() function. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1363326533-3310-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/perf-record.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index ffab5a41ff0a..72d8881873b0 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -143,7 +143,7 @@ int test__PERF_RECORD(void) err = perf_evlist__mmap(evlist, opts.mmap_pages, false); if (err < 0) { pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); - goto out_delete_maps; + goto out_close_evlist; } /* @@ -306,6 +306,8 @@ found_exit: } out_err: perf_evlist__munmap(evlist); +out_close_evlist: + perf_evlist__close(evlist); out_delete_maps: perf_evlist__delete_maps(evlist); out_delete_evlist: -- cgit v1.2.3 From d723a55096b81a13c319485f01994e0a539efcf9 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 15 Mar 2013 14:58:11 +0900 Subject: perf test: Add test case for checking number of EXIT events The new test__task_exit() test runs a simple "/usr/bin/true" workload and then checks whether the number of EXIT event is 1 or not. Signed-off-by: Namhyung Kim Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/87obeljax4.fsf@sejong.aot.lge.com [ committer note: Fixup conflicts with f4c66b4 ( bp overflow tests ) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 1 + tools/perf/tests/builtin-test.c | 4 ++ tools/perf/tests/task-exit.c | 123 ++++++++++++++++++++++++++++++++++++++++ tools/perf/tests/tests.h | 1 + 4 files changed, 129 insertions(+) create mode 100644 tools/perf/tests/task-exit.c (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 990e9a113190..8e1bba35a1ee 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -513,6 +513,7 @@ LIB_OBJS += $(OUTPUT)tests/hists_link.o LIB_OBJS += $(OUTPUT)tests/python-use.o LIB_OBJS += $(OUTPUT)tests/bp_signal.o LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o +LIB_OBJS += $(OUTPUT)tests/task-exit.o BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o BUILTIN_OBJS += $(OUTPUT)builtin-bench.o diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 45d9ad442d56..9b5c70a180d2 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -85,6 +85,10 @@ static struct test { .desc = "Test breakpoint overflow sampling", .func = test__bp_signal_overflow, }, + { + .desc = "Test number of exit event of a simple workload", + .func = test__task_exit, + }, { .func = NULL, }, diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c new file mode 100644 index 000000000000..28fe5894b061 --- /dev/null +++ b/tools/perf/tests/task-exit.c @@ -0,0 +1,123 @@ +#include "evlist.h" +#include "evsel.h" +#include "thread_map.h" +#include "cpumap.h" +#include "tests.h" + +#include + +static int exited; +static int nr_exit; + +static void sig_handler(int sig) +{ + exited = 1; + + if (sig == SIGUSR1) + nr_exit = -1; +} + +/* + * This test will start a workload that does nothing then it checks + * if the number of exit event reported by the kernel is 1 or not + * in order to check the kernel returns correct number of event. + */ +int test__task_exit(void) +{ + int err = -1; + union perf_event *event; + struct perf_evsel *evsel; + struct perf_evlist *evlist; + struct perf_target target = { + .uid = UINT_MAX, + .uses_mmap = true, + }; + const char *argv[] = { "true", NULL }; + + signal(SIGCHLD, sig_handler); + signal(SIGUSR1, sig_handler); + + evlist = perf_evlist__new(); + if (evlist == NULL) { + pr_debug("perf_evlist__new\n"); + return -1; + } + /* + * We need at least one evsel in the evlist, use the default + * one: "cycles". + */ + err = perf_evlist__add_default(evlist); + if (err < 0) { + pr_debug("Not enough memory to create evsel\n"); + goto out_free_evlist; + } + + /* + * Create maps of threads and cpus to monitor. In this case + * we start with all threads and cpus (-1, -1) but then in + * perf_evlist__prepare_workload we'll fill in the only thread + * we're monitoring, the one forked there. + */ + evlist->cpus = cpu_map__dummy_new(); + evlist->threads = thread_map__new_by_tid(-1); + if (!evlist->cpus || !evlist->threads) { + err = -ENOMEM; + pr_debug("Not enough memory to create thread/cpu maps\n"); + goto out_delete_maps; + } + + err = perf_evlist__prepare_workload(evlist, &target, argv, false, true); + if (err < 0) { + pr_debug("Couldn't run the workload!\n"); + goto out_delete_maps; + } + + evsel = perf_evlist__first(evlist); + evsel->attr.task = 1; + evsel->attr.sample_freq = 0; + evsel->attr.inherit = 0; + evsel->attr.watermark = 0; + evsel->attr.wakeup_events = 1; + evsel->attr.exclude_kernel = 1; + + err = perf_evlist__open(evlist); + if (err < 0) { + pr_debug("Couldn't open the evlist: %s\n", strerror(-err)); + goto out_delete_maps; + } + + if (perf_evlist__mmap(evlist, 128, true) < 0) { + pr_debug("failed to mmap events: %d (%s)\n", errno, + strerror(errno)); + goto out_close_evlist; + } + + perf_evlist__start_workload(evlist); + +retry: + while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { + if (event->header.type != PERF_RECORD_EXIT) + continue; + + nr_exit++; + } + + if (!exited || !nr_exit) { + poll(evlist->pollfd, evlist->nr_fds, -1); + goto retry; + } + + if (nr_exit != 1) { + pr_debug("received %d EXIT records\n", nr_exit); + err = -1; + } + + perf_evlist__munmap(evlist); +out_close_evlist: + perf_evlist__close(evlist); +out_delete_maps: + perf_evlist__delete_maps(evlist); +out_free_evlist: + perf_evlist__delete(evlist); + return err; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 6cf1ec4866d3..b33b3286ad6e 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -25,5 +25,6 @@ int test__hists_link(void); int test__python_use(void); int test__bp_signal(void); int test__bp_signal_overflow(void); +int test__task_exit(void); #endif /* TESTS_H */ -- cgit v1.2.3 From a7e191c376fad084d9f3c7ac89a1f7c47462ebc8 Mon Sep 17 00:00:00 2001 From: Frederik Deweerdt Date: Fri, 1 Mar 2013 13:02:27 -0500 Subject: perf stat: Introduce --repeat forever The following patch causes 'perf stat --repeat 0' to be interpreted as 'forever', displaying the stats for every run. We act as if a single run was asked, and reset the stats in each iteration. In this mode SIGINT is passed to perf to be able to stop the loop with Ctrl+C. Signed-off-by: Frederik Deweerdt Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20130301180227.GA24385@ks398093.ip-192-95-24.net Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 2 +- tools/perf/builtin-stat.c | 45 ++++++++++++++++++++++++++++++---- tools/perf/util/evsel.c | 6 +++++ tools/perf/util/evsel.h | 1 + 4 files changed, 48 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index faf4f4feebcc..23e587ad549e 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -52,7 +52,7 @@ OPTIONS -r:: --repeat=:: - repeat command and print average + stddev (max: 100) + repeat command and print average + stddev (max: 100). 0 means forever. -B:: --big-num:: diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 69fe6ed89627..021783ae2bfa 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -94,6 +94,7 @@ static const char *pre_cmd = NULL; static const char *post_cmd = NULL; static bool sync_run = false; static unsigned int interval = 0; +static bool forever = false; static struct timespec ref_time; static struct cpu_map *sock_map; @@ -125,6 +126,11 @@ static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel) return perf_evsel__cpus(evsel)->nr; } +static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel) +{ + memset(evsel->priv, 0, sizeof(struct perf_stat)); +} + static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) { evsel->priv = zalloc(sizeof(struct perf_stat)); @@ -173,6 +179,22 @@ static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; static struct stats walltime_nsecs_stats; +static void reset_stats(void) +{ + memset(runtime_nsecs_stats, 0, sizeof(runtime_nsecs_stats)); + memset(runtime_cycles_stats, 0, sizeof(runtime_cycles_stats)); + memset(runtime_stalled_cycles_front_stats, 0, sizeof(runtime_stalled_cycles_front_stats)); + memset(runtime_stalled_cycles_back_stats, 0, sizeof(runtime_stalled_cycles_back_stats)); + memset(runtime_branches_stats, 0, sizeof(runtime_branches_stats)); + memset(runtime_cacherefs_stats, 0, sizeof(runtime_cacherefs_stats)); + memset(runtime_l1_dcache_stats, 0, sizeof(runtime_l1_dcache_stats)); + memset(runtime_l1_icache_stats, 0, sizeof(runtime_l1_icache_stats)); + memset(runtime_ll_cache_stats, 0, sizeof(runtime_ll_cache_stats)); + memset(runtime_itlb_cache_stats, 0, sizeof(runtime_itlb_cache_stats)); + memset(runtime_dtlb_cache_stats, 0, sizeof(runtime_dtlb_cache_stats)); + memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats)); +} + static int create_perf_stat_counter(struct perf_evsel *evsel) { struct perf_event_attr *attr = &evsel->attr; @@ -1252,7 +1274,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_INTEGER('r', "repeat", &run_count, - "repeat command and print average + stddev (max: 100)"), + "repeat command and print average + stddev (max: 100, forever: 0)"), OPT_BOOLEAN('n', "null", &null_run, "null run - dont start any counters"), OPT_INCR('d', "detailed", &detailed_run, @@ -1355,8 +1377,12 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (!argc && !perf_target__has_task(&target)) usage_with_options(stat_usage, options); - if (run_count <= 0) + if (run_count < 0) { usage_with_options(stat_usage, options); + } else if (run_count == 0) { + forever = true; + run_count = 1; + } /* no_aggr, cgroup are for system-wide only */ if ((no_aggr || nr_cgroups) && !perf_target__has_cpu(&target)) { @@ -1413,21 +1439,30 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) * task, but being ignored by perf stat itself: */ atexit(sig_atexit); - signal(SIGINT, skip_signal); + if (!forever) + signal(SIGINT, skip_signal); signal(SIGCHLD, skip_signal); signal(SIGALRM, skip_signal); signal(SIGABRT, skip_signal); status = 0; - for (run_idx = 0; run_idx < run_count; run_idx++) { + for (run_idx = 0; forever || run_idx < run_count; run_idx++) { if (run_count != 1 && verbose) fprintf(output, "[ perf stat: executing run #%d ... ]\n", run_idx + 1); status = run_perf_stat(argc, argv); + if (forever && status != -1) { + print_stat(argc, argv); + list_for_each_entry(pos, &evsel_list->entries, node) { + perf_evsel__reset_stat_priv(pos); + perf_evsel__reset_counts(pos, perf_evsel__nr_cpus(pos)); + } + reset_stats(); + } } - if (status != -1 && !interval) + if (!forever && status != -1 && !interval) print_stat(argc, argv); out_free_fd: list_for_each_entry(pos, &evsel_list->entries, node) { diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 7fde9fb79966..1adb824610f0 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -633,6 +633,12 @@ int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) return 0; } +void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus) +{ + memset(evsel->counts, 0, (sizeof(*evsel->counts) + + (ncpus * sizeof(struct perf_counts_values)))); +} + int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) { evsel->counts = zalloc((sizeof(*evsel->counts) + diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index bf758e53c929..3f156ccc1acb 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -121,6 +121,7 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); +void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus); void perf_evsel__free_fd(struct perf_evsel *evsel); void perf_evsel__free_id(struct perf_evsel *evsel); void perf_evsel__free_counts(struct perf_evsel *evsel); -- cgit v1.2.3 From 77d6a5289343665a41f55c0ea46c169b3f551a24 Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Wed, 13 Mar 2013 14:14:12 +0100 Subject: tools: hv: daemon should subscribe only to CN_KVP_IDX group Previously HyperV daemon set sockaddr_nl.nl_groups to CN_KVP_IDX. Netlink documentation says: "nl_groups is a bit mask with every bit representing a netlink group number". Since CN_KVP_IDX value is "9" HyperV daemon was receiving Netlink messages also from group number "1" which is used by CGroup Rules Engine Daemon. This caused the daemon to segfault (at least on 2.6.32 kernel). HyperV daemon should set nl_groups to zero and specify multicast group CN_KVP_IDX only by using socket options. Signed-off-by: Tomas Hozza Acked-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_kvp_daemon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index c800ea4c8bf9..908a612ac992 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -1443,7 +1443,7 @@ int main(void) addr.nl_family = AF_NETLINK; addr.nl_pad = 0; addr.nl_pid = 0; - addr.nl_groups = CN_KVP_IDX; + addr.nl_groups = 0; error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); @@ -1452,7 +1452,7 @@ int main(void) close(fd); exit(EXIT_FAILURE); } - sock_opt = addr.nl_groups; + sock_opt = CN_KVP_IDX; setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt)); /* * Register ourselves with the kernel. -- cgit v1.2.3 From f4685fa6d0427c3948a5120a9658fad7ae81facd Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Wed, 13 Mar 2013 14:14:13 +0100 Subject: tools: hv: daemon setsockopt should use options macros HyperV daemon should use macros for option values when calling setsockopt. Using specific numeric values instead of macros is confusing. Signed-off-by: Tomas Hozza Acked-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_kvp_daemon.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 908a612ac992..704a0f9a063d 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -102,6 +102,10 @@ static struct utsname uts_buf; #define MAX_FILE_NAME 100 #define ENTRIES_PER_BLOCK 50 +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + struct kvp_record { char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; @@ -1407,7 +1411,7 @@ netlink_send(int fd, struct cn_msg *msg) int main(void) { - int fd, len, sock_opt; + int fd, len, nl_group; int error; struct cn_msg *message; struct pollfd pfd; @@ -1452,8 +1456,8 @@ int main(void) close(fd); exit(EXIT_FAILURE); } - sock_opt = CN_KVP_IDX; - setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt)); + nl_group = CN_KVP_IDX; + setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)); /* * Register ourselves with the kernel. */ -- cgit v1.2.3 From 75db601496e97ffa2476dcd00053c0ca95e977a5 Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Wed, 13 Mar 2013 14:14:14 +0100 Subject: tools: hv: daemon should check type of received Netlink msg HyperV KVP daemon should check nlmsg_type in received netlink message header. If message type is NLMSG_DONE daemon can proceed with processing otherwise it should wait for next message. Signed-off-by: Tomas Hozza Acked-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_kvp_daemon.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools') diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 704a0f9a063d..5a1f6489d185 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -1503,6 +1503,10 @@ int main(void) } incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; + + if (incoming_msg->nlmsg_type != NLMSG_DONE) + continue; + incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; -- cgit v1.2.3 From 96dd86fa588169b745a71aedf2070e80f4943623 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 15 Mar 2013 12:30:06 -0700 Subject: Drivers: hv: Add a new driver to support host initiated backup This driver supports host initiated backup of the guest. On Windows guests, the host can generate application consistent backups using the Windows VSS framework. On Linux, we ensure that the backup will be file system consistent. This driver allows the host to initiate a "Freeze" operation on all the mounted file systems in the guest. Once the mounted file systems in the guest are frozen, the host snapshots the guest's file systems. Once this is done, the guest's file systems are "thawed". This driver has a user-level component (daemon) that invokes the appropriate operation on all the mounted file systems in response to the requests from the host. The duration for which the guest is frozen is very short - a few seconds. During this interval, the diff disk is comitted. In this version of the patch I have addressed the feedback from Olaf Herring. Also, some of the connector related issues have been fixed. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Cc: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_vss_daemon.c | 220 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 tools/hv/hv_vss_daemon.c (limited to 'tools') diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c new file mode 100644 index 000000000000..95269952aa92 --- /dev/null +++ b/tools/hv/hv_vss_daemon.c @@ -0,0 +1,220 @@ +/* + * An implementation of the host initiated guest snapshot for Hyper-V. + * + * + * Copyright (C) 2013, Microsoft, Inc. + * Author : K. Y. Srinivasan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char vss_recv_buffer[4096]; +static char vss_send_buffer[4096]; +static struct sockaddr_nl addr; + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + + +static int vss_operate(int operation) +{ + char *fs_op; + char cmd[512]; + char buf[512]; + FILE *file; + char *p; + char *x; + int error; + + switch (operation) { + case VSS_OP_FREEZE: + fs_op = "-f "; + break; + case VSS_OP_THAW: + fs_op = "-u "; + break; + } + + file = popen("mount | awk '/^\/dev\// { print $3}'", "r"); + if (file == NULL) + return; + + while ((p = fgets(buf, sizeof(buf), file)) != NULL) { + x = strchr(p, '\n'); + *x = '\0'; + if (!strncmp(p, "/", sizeof("/"))) + continue; + + sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, p); + syslog(LOG_INFO, "VSS cmd is %s\n", cmd); + error = system(cmd); + } + pclose(file); + + sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, "/"); + syslog(LOG_INFO, "VSS cmd is %s\n", cmd); + error = system(cmd); + + return error; +} + +static int netlink_send(int fd, struct cn_msg *msg) +{ + struct nlmsghdr *nlh; + unsigned int size; + struct msghdr message; + char buffer[64]; + struct iovec iov[2]; + + size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); + + nlh = (struct nlmsghdr *)buffer; + nlh->nlmsg_seq = 0; + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_type = NLMSG_DONE; + nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh)); + nlh->nlmsg_flags = 0; + + iov[0].iov_base = nlh; + iov[0].iov_len = sizeof(*nlh); + + iov[1].iov_base = msg; + iov[1].iov_len = size; + + memset(&message, 0, sizeof(message)); + message.msg_name = &addr; + message.msg_namelen = sizeof(addr); + message.msg_iov = iov; + message.msg_iovlen = 2; + + return sendmsg(fd, &message, 0); +} + +int main(void) +{ + int fd, len, nl_group; + int error; + struct cn_msg *message; + struct pollfd pfd; + struct nlmsghdr *incoming_msg; + struct cn_msg *incoming_cn_msg; + int op; + struct hv_vss_msg *vss_msg; + + daemon(1, 0); + openlog("Hyper-V VSS", 0, LOG_USER); + syslog(LOG_INFO, "VSS starting; pid is:%d", getpid()); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (fd < 0) { + syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); + exit(EXIT_FAILURE); + } + addr.nl_family = AF_NETLINK; + addr.nl_pad = 0; + addr.nl_pid = 0; + addr.nl_groups = 0; + + + error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (error < 0) { + syslog(LOG_ERR, "bind failed; error:%d", error); + close(fd); + exit(EXIT_FAILURE); + } + nl_group = CN_VSS_IDX; + setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)); + /* + * Register ourselves with the kernel. + */ + message = (struct cn_msg *)vss_send_buffer; + message->id.idx = CN_VSS_IDX; + message->id.val = CN_VSS_VAL; + message->ack = 0; + vss_msg = (struct hv_vss_msg *)message->data; + vss_msg->vss_hdr.operation = VSS_OP_REGISTER; + + message->len = sizeof(struct hv_vss_msg); + + len = netlink_send(fd, message); + if (len < 0) { + syslog(LOG_ERR, "netlink_send failed; error:%d", len); + close(fd); + exit(EXIT_FAILURE); + } + + pfd.fd = fd; + + while (1) { + struct sockaddr *addr_p = (struct sockaddr *) &addr; + socklen_t addr_l = sizeof(addr); + pfd.events = POLLIN; + pfd.revents = 0; + poll(&pfd, 1, -1); + + len = recvfrom(fd, vss_recv_buffer, sizeof(vss_recv_buffer), 0, + addr_p, &addr_l); + + if (len < 0 || addr.nl_pid) { + syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", + addr.nl_pid, errno, strerror(errno)); + close(fd); + return -1; + } + + incoming_msg = (struct nlmsghdr *)vss_recv_buffer; + + if (incoming_msg->nlmsg_type != NLMSG_DONE) + continue; + + incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); + vss_msg = (struct hv_vss_msg *)incoming_cn_msg->data; + op = vss_msg->vss_hdr.operation; + error = HV_S_OK; + + switch (op) { + case VSS_OP_FREEZE: + case VSS_OP_THAW: + error = vss_operate(op); + if (error) + error = HV_E_FAIL; + break; + default: + syslog(LOG_ERR, "Illegal op:%d\n", op); + } + vss_msg->error = error; + len = netlink_send(fd, incoming_cn_msg); + if (len < 0) { + syslog(LOG_ERR, "net_link send failed; error:%d", len); + exit(EXIT_FAILURE); + } + } + +} -- cgit v1.2.3 From d134ffb919ab142b2359ae45a0cf4a5bfa1ff283 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 18 Mar 2013 11:24:21 -0300 Subject: perf stat: Introduce evlist methods to allocate/free the stats Reducing the noise in the main logic. Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-o219lnci04hlilxi6711wtcr@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 67 ++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 24 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 021783ae2bfa..ba0bdd87c279 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -166,6 +166,35 @@ static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel) evsel->prev_raw_counts = NULL; } +static void perf_evlist__free_stats(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + + list_for_each_entry(evsel, &evlist->entries, node) { + perf_evsel__free_stat_priv(evsel); + perf_evsel__free_counts(evsel); + perf_evsel__free_prev_raw_counts(evsel); + } +} + +static int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw) +{ + struct perf_evsel *evsel; + + list_for_each_entry(evsel, &evlist->entries, node) { + if (perf_evsel__alloc_stat_priv(evsel) < 0 || + perf_evsel__alloc_counts(evsel, perf_evsel__nr_cpus(evsel)) < 0 || + (alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel) < 0)) + goto out_free; + } + + return 0; + +out_free: + perf_evlist__free_stats(evlist); + return -1; +} + static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; static struct stats runtime_cycles_stats[MAX_NR_CPUS]; static struct stats runtime_stalled_cycles_front_stats[MAX_NR_CPUS]; @@ -179,8 +208,15 @@ static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; static struct stats walltime_nsecs_stats; -static void reset_stats(void) +static void perf_stat__reset_stats(struct perf_evlist *evlist) { + struct perf_evsel *evsel; + + list_for_each_entry(evsel, &evlist->entries, node) { + perf_evsel__reset_stat_priv(evsel); + perf_evsel__reset_counts(evsel, perf_evsel__nr_cpus(evsel)); + } + memset(runtime_nsecs_stats, 0, sizeof(runtime_nsecs_stats)); memset(runtime_cycles_stats, 0, sizeof(runtime_cycles_stats)); memset(runtime_stalled_cycles_front_stats, 0, sizeof(runtime_stalled_cycles_front_stats)); @@ -1308,7 +1344,6 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) "perf stat [] []", NULL }; - struct perf_evsel *pos; int status = -ENOMEM, run_idx; const char *mode; @@ -1420,17 +1455,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) return -1; } - list_for_each_entry(pos, &evsel_list->entries, node) { - if (perf_evsel__alloc_stat_priv(pos) < 0 || - perf_evsel__alloc_counts(pos, perf_evsel__nr_cpus(pos)) < 0) - goto out_free_fd; - } - if (interval) { - list_for_each_entry(pos, &evsel_list->entries, node) { - if (perf_evsel__alloc_prev_raw_counts(pos) < 0) - goto out_free_fd; - } - } + if (perf_evlist__alloc_stats(evsel_list, interval)) + goto out_free_maps; /* * We dont want to block the signals - that would cause @@ -1454,22 +1480,15 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) status = run_perf_stat(argc, argv); if (forever && status != -1) { print_stat(argc, argv); - list_for_each_entry(pos, &evsel_list->entries, node) { - perf_evsel__reset_stat_priv(pos); - perf_evsel__reset_counts(pos, perf_evsel__nr_cpus(pos)); - } - reset_stats(); + perf_stat__reset_stats(evsel_list); } } if (!forever && status != -1 && !interval) print_stat(argc, argv); -out_free_fd: - list_for_each_entry(pos, &evsel_list->entries, node) { - perf_evsel__free_stat_priv(pos); - perf_evsel__free_counts(pos); - perf_evsel__free_prev_raw_counts(pos); - } + + perf_evlist__free_stats(evsel_list); +out_free_maps: perf_evlist__delete_maps(evsel_list); out: perf_evlist__delete(evsel_list); -- cgit v1.2.3 From bc96b361cbf90e61d2665b1305cd2c4ac1fd9cfc Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 18 Mar 2013 11:41:47 +0900 Subject: perf tests: Add a test case for checking sw clock event frequency This test case checks frequency conversion of hrtimer-based software clock events (cpu-clock, task-clock) have valid (non-1) periods. Signed-off-by: Namhyung Kim Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1363574507-18808-2-git-send-email-namhyung@kernel.org [ committer note: Moved .sample_freq to outside named init block to cope with some gcc versions ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 1 + tools/perf/tests/builtin-test.c | 4 ++ tools/perf/tests/sw-clock.c | 119 ++++++++++++++++++++++++++++++++++++++++ tools/perf/tests/tests.h | 1 + 4 files changed, 125 insertions(+) create mode 100644 tools/perf/tests/sw-clock.c (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 8e1bba35a1ee..0230b75ed7f9 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -514,6 +514,7 @@ LIB_OBJS += $(OUTPUT)tests/python-use.o LIB_OBJS += $(OUTPUT)tests/bp_signal.o LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o LIB_OBJS += $(OUTPUT)tests/task-exit.o +LIB_OBJS += $(OUTPUT)tests/sw-clock.o BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o BUILTIN_OBJS += $(OUTPUT)builtin-bench.o diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 9b5c70a180d2..0918ada4cc41 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -89,6 +89,10 @@ static struct test { .desc = "Test number of exit event of a simple workload", .func = test__task_exit, }, + { + .desc = "Test software clock events have valid period values", + .func = test__sw_clock_freq, + }, { .func = NULL, }, diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c new file mode 100644 index 000000000000..2e41e2d32ccc --- /dev/null +++ b/tools/perf/tests/sw-clock.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include + +#include "tests.h" +#include "util/evsel.h" +#include "util/evlist.h" +#include "util/cpumap.h" +#include "util/thread_map.h" + +#define NR_LOOPS 1000000 + +/* + * This test will open software clock events (cpu-clock, task-clock) + * then check their frequency -> period conversion has no artifact of + * setting period to 1 forcefully. + */ +static int __test__sw_clock_freq(enum perf_sw_ids clock_id) +{ + int i, err = -1; + volatile int tmp = 0; + u64 total_periods = 0; + int nr_samples = 0; + union perf_event *event; + struct perf_evsel *evsel; + struct perf_evlist *evlist; + struct perf_event_attr attr = { + .type = PERF_TYPE_SOFTWARE, + .config = clock_id, + .sample_type = PERF_SAMPLE_PERIOD, + .exclude_kernel = 1, + .disabled = 1, + .freq = 1, + }; + + attr.sample_freq = 10000; + + evlist = perf_evlist__new(); + if (evlist == NULL) { + pr_debug("perf_evlist__new\n"); + return -1; + } + + evsel = perf_evsel__new(&attr, 0); + if (evsel == NULL) { + pr_debug("perf_evsel__new\n"); + goto out_free_evlist; + } + perf_evlist__add(evlist, evsel); + + evlist->cpus = cpu_map__dummy_new(); + evlist->threads = thread_map__new_by_tid(getpid()); + if (!evlist->cpus || !evlist->threads) { + err = -ENOMEM; + pr_debug("Not enough memory to create thread/cpu maps\n"); + goto out_delete_maps; + } + + perf_evlist__open(evlist); + + err = perf_evlist__mmap(evlist, 128, true); + if (err < 0) { + pr_debug("failed to mmap event: %d (%s)\n", errno, + strerror(errno)); + goto out_close_evlist; + } + + perf_evlist__enable(evlist); + + /* collect samples */ + for (i = 0; i < NR_LOOPS; i++) + tmp++; + + perf_evlist__disable(evlist); + + while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { + struct perf_sample sample; + + if (event->header.type != PERF_RECORD_SAMPLE) + continue; + + err = perf_evlist__parse_sample(evlist, event, &sample); + if (err < 0) { + pr_debug("Error during parse sample\n"); + goto out_unmap_evlist; + } + + total_periods += sample.period; + nr_samples++; + } + + if ((u64) nr_samples == total_periods) { + pr_debug("All (%d) samples have period value of 1!\n", + nr_samples); + err = -1; + } + +out_unmap_evlist: + perf_evlist__munmap(evlist); +out_close_evlist: + perf_evlist__close(evlist); +out_delete_maps: + perf_evlist__delete_maps(evlist); +out_free_evlist: + perf_evlist__delete(evlist); + return err; +} + +int test__sw_clock_freq(void) +{ + int ret; + + ret = __test__sw_clock_freq(PERF_COUNT_SW_CPU_CLOCK); + if (!ret) + ret = __test__sw_clock_freq(PERF_COUNT_SW_TASK_CLOCK); + + return ret; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index b33b3286ad6e..dd7feae2d37b 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -26,5 +26,6 @@ int test__python_use(void); int test__bp_signal(void); int test__bp_signal_overflow(void); int test__task_exit(void); +int test__sw_clock_freq(void); #endif /* TESTS_H */ -- cgit v1.2.3 From b0aa73bf081da6810dacd750b9f8186640e172db Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 19 Mar 2013 14:49:44 -0400 Subject: net: Add socket() system call self test. Signed-off-by: David S. Miller --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/net-socket/Makefile | 16 ++++ .../testing/selftests/net-socket/run_netsocktests | 12 +++ tools/testing/selftests/net-socket/socket.c | 92 ++++++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 tools/testing/selftests/net-socket/Makefile create mode 100644 tools/testing/selftests/net-socket/run_netsocktests create mode 100644 tools/testing/selftests/net-socket/socket.c (limited to 'tools') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 3cc0ad7ae863..7c6280f1cf4d 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -5,6 +5,7 @@ TARGETS += vm TARGETS += cpu-hotplug TARGETS += memory-hotplug TARGETS += efivarfs +TARGETS += net-socket all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/net-socket/Makefile b/tools/testing/selftests/net-socket/Makefile new file mode 100644 index 000000000000..f27ee10da530 --- /dev/null +++ b/tools/testing/selftests/net-socket/Makefile @@ -0,0 +1,16 @@ +# Makefile for net-socket selftests + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +NET_SOCK_PROGS = socket + +all: $(NET_SOCK_PROGS) +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +run_tests: all + @/bin/sh ./run_netsocktests || echo "vmtests: [FAIL]" + +clean: + $(RM) $(NET_SOCK_PROGS) diff --git a/tools/testing/selftests/net-socket/run_netsocktests b/tools/testing/selftests/net-socket/run_netsocktests new file mode 100644 index 000000000000..c09a682df56a --- /dev/null +++ b/tools/testing/selftests/net-socket/run_netsocktests @@ -0,0 +1,12 @@ +#!/bin/bash + +echo "--------------------" +echo "running socket test" +echo "--------------------" +./socket +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi + diff --git a/tools/testing/selftests/net-socket/socket.c b/tools/testing/selftests/net-socket/socket.c new file mode 100644 index 000000000000..0f227f2f9be9 --- /dev/null +++ b/tools/testing/selftests/net-socket/socket.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include + +struct socket_testcase { + int domain; + int type; + int protocol; + + /* 0 = valid file descriptor + * -foo = error foo + */ + int expect; + + /* If non-zero, accept EAFNOSUPPORT to handle the case + * of the protocol not being configured into the kernel. + */ + int nosupport_ok; +}; + +static struct socket_testcase tests[] = { + { AF_MAX, 0, 0, -EAFNOSUPPORT, 0 }, + { AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_TCP, -EPROTONOSUPPORT, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, 1 }, + { AF_INET, SOCK_STREAM, IPPROTO_UDP, -EPROTONOSUPPORT, 1 }, +}; + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define ERR_STRING_SZ 64 + +static int run_tests(void) +{ + char err_string1[ERR_STRING_SZ]; + char err_string2[ERR_STRING_SZ]; + int i, err; + + err = 0; + for (i = 0; i < ARRAY_SIZE(tests); i++) { + struct socket_testcase *s = &tests[i]; + int fd; + + fd = socket(s->domain, s->type, s->protocol); + if (fd < 0) { + if (s->nosupport_ok && + errno == EAFNOSUPPORT) + continue; + + if (s->expect < 0 && + errno == -s->expect) + continue; + + strerror_r(-s->expect, err_string1, ERR_STRING_SZ); + strerror_r(errno, err_string2, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "err (%s) got (%s)\n", + s->domain, s->type, s->protocol, + err_string1, err_string2); + + err = -1; + break; + } else { + close(fd); + + if (s->expect < 0) { + strerror_r(errno, err_string1, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "success got err (%s)\n", + s->domain, s->type, s->protocol, + err_string1); + + err = -1; + break; + } + } + } + + return err; +} + +int main(void) +{ + int err = run_tests(); + + return err; +} -- cgit v1.2.3 From 77f65ebdca506870d99bfabe52bde222511022ec Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 19 Mar 2013 10:18:11 +0000 Subject: packet: packet fanout rollover during socket overload Changes: v3->v2: rebase (no other changes) passes selftest v2->v1: read f->num_members only once fix bug: test rollover mode + flag Minimize packet drop in a fanout group. If one socket is full, roll over packets to another from the group. Maintain flow affinity during normal load using an rxhash fanout policy, while dispersing unexpected traffic storms that hit a single cpu, such as spoofed-source DoS flows. Rollover breaks affinity for flows arriving at saturated sockets during those conditions. The patch adds a fanout policy ROLLOVER that rotates between sockets, filling each socket before moving to the next. It also adds a fanout flag ROLLOVER. If passed along with any other fanout policy, the primary policy is applied until the chosen socket is full. Then, rollover selects another socket, to delay packet drop until the entire system is saturated. Probing sockets is not free. Selecting the last used socket, as rollover does, is a greedy approach that maximizes chance of success, at the cost of extreme load imbalance. In practice, with sufficiently long queues to absorb bursts, sockets are drained in parallel and load balance looks uniform in `top`. To avoid contention, scales counters with number of sockets and accesses them lockfree. Values are bounds checked to ensure correctness. Tested using an application with 9 threads pinned to CPUs, one socket per thread and sufficient busywork per packet operation to limits each thread to handling 32 Kpps. When sent 500 Kpps single UDP stream packets, a FANOUT_CPU setup processes 32 Kpps in total without this patch, 270 Kpps with the patch. Tested with read() and with a packet ring (V1). Also, passes psock_fanout.c unit test added to selftests. Signed-off-by: Willem de Bruijn Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/net-afpacket/Makefile | 18 ++ .../testing/selftests/net-afpacket/psock_fanout.c | 326 +++++++++++++++++++++ .../selftests/net-afpacket/run_afpackettests | 16 + 4 files changed, 361 insertions(+) create mode 100644 tools/testing/selftests/net-afpacket/Makefile create mode 100644 tools/testing/selftests/net-afpacket/psock_fanout.c create mode 100644 tools/testing/selftests/net-afpacket/run_afpackettests (limited to 'tools') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 7c6280f1cf4d..7f50078d0e8c 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -6,6 +6,7 @@ TARGETS += cpu-hotplug TARGETS += memory-hotplug TARGETS += efivarfs TARGETS += net-socket +TARGETS += net-afpacket all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/net-afpacket/Makefile b/tools/testing/selftests/net-afpacket/Makefile new file mode 100644 index 000000000000..45f2ffb7fda7 --- /dev/null +++ b/tools/testing/selftests/net-afpacket/Makefile @@ -0,0 +1,18 @@ +# Makefile for net-socket selftests + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +CFLAGS += -I../../../../usr/include/ + +AF_PACKET_PROGS = psock_fanout + +all: $(AF_PACKET_PROGS) +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +run_tests: all + @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" + +clean: + $(RM) $(AF_PACKET_PROGS) diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c new file mode 100644 index 000000000000..09dbf93c53d4 --- /dev/null +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -0,0 +1,326 @@ +/* + * Copyright 2013 Google Inc. + * Author: Willem de Bruijn (willemb@google.com) + * + * A basic test of packet socket fanout behavior. + * + * Control: + * - create fanout fails as expected with illegal flag combinations + * - join fanout fails as expected with diverging types or flags + * + * Datapath: + * Open a pair of packet sockets and a pair of INET sockets, send a known + * number of packets across the two INET sockets and count the number of + * packets enqueued onto the two packet sockets. + * + * The test currently runs for + * - PACKET_FANOUT_HASH + * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER + * - PACKET_FANOUT_ROLLOVER + * + * Todo: + * - datapath: PACKET_FANOUT_LB + * - datapath: PACKET_FANOUT_CPU + * - functionality: PACKET_FANOUT_FLAG_DEFRAG + * + * License (GPLv2): + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Hack: build even if local includes are old */ +#ifndef PACKET_FANOUT +#define PACKET_FANOUT 18 +#define PACKET_FANOUT_HASH 0 +#define PACKET_FANOUT_LB 1 +#define PACKET_FANOUT_CPU 2 +#define PACKET_FANOUT_FLAG_DEFRAG 0x8000 + +#ifndef PACKET_FANOUT_ROLLOVER +#define PACKET_FANOUT_ROLLOVER 3 +#endif + +#ifndef PACKET_FANOUT_FLAG_ROLLOVER +#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 +#endif + +#endif + +#define DATA_LEN 100 +#define DATA_CHAR 'a' + +static void pair_udp_open(int fds[], uint16_t port) +{ + struct sockaddr_in saddr, daddr; + + fds[0] = socket(PF_INET, SOCK_DGRAM, 0); + fds[1] = socket(PF_INET, SOCK_DGRAM, 0); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: socket dgram\n"); + exit(1); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&daddr, 0, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_port = htons(port + 1); + daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* must bind both to get consistent hash result */ + if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } + if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { + perror("bind"); + exit(1); + } + if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } +} + +static void pair_udp_send(int fds[], int num) +{ + char buf[DATA_LEN], rbuf[DATA_LEN]; + + memset(buf, DATA_CHAR, sizeof(buf)); + while (num--) { + /* Should really handle EINTR and EAGAIN */ + if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { + fprintf(stderr, "ERROR: send failed left=%d\n", num); + exit(1); + } + if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { + fprintf(stderr, "ERROR: recv failed left=%d\n", num); + exit(1); + } + if (memcmp(buf, rbuf, sizeof(buf))) { + fprintf(stderr, "ERROR: data failed left=%d\n", num); + exit(1); + } + } +} + +static void sock_fanout_setfilter(int fd) +{ + struct sock_filter bpf_filter[] = { + { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ + { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ + { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ + { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x6, 0, 0, 0x00000060 }, /* RET match */ +/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ + }; + struct sock_fprog bpf_prog; + + bpf_prog.filter = bpf_filter; + bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, + sizeof(bpf_prog))) { + perror("setsockopt SO_ATTACH_FILTER"); + exit(1); + } +} + +/* Open a socket in a given fanout mode. + * @return -1 if mode is bad, a valid socket otherwise */ +static int sock_fanout_open(uint16_t typeflags, int num_packets) +{ + int fd, val; + + fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + if (fd < 0) { + perror("socket packet"); + exit(1); + } + + /* fanout group ID is always 0: tests whether old groups are deleted */ + val = ((int) typeflags) << 16; + if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { + if (close(fd)) { + perror("close packet"); + exit(1); + } + return -1; + } + + val = sizeof(struct iphdr) + sizeof(struct udphdr) + DATA_LEN; + val *= num_packets; + /* hack: apparently, the above calculation is too small (TODO: fix) */ + val *= 3; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val))) { + perror("setsockopt SO_RCVBUF"); + exit(1); + } + + sock_fanout_setfilter(fd); + return fd; +} + +static void sock_fanout_read(int fds[], const int expect[]) +{ + struct tpacket_stats stats; + socklen_t ssize; + int ret[2]; + + ssize = sizeof(stats); + if (getsockopt(fds[0], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { + perror("getsockopt statistics 0"); + exit(1); + } + ret[0] = stats.tp_packets - stats.tp_drops; + ssize = sizeof(stats); + if (getsockopt(fds[1], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { + perror("getsockopt statistics 1"); + exit(1); + } + ret[1] = stats.tp_packets - stats.tp_drops; + + fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", + ret[0], ret[1], expect[0], expect[1]); + + if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && + (!(ret[0] == expect[1] && ret[1] == expect[0]))) { + fprintf(stderr, "ERROR: incorrect queue lengths\n"); + exit(1); + } +} + +/* Test illegal mode + flag combination */ +static void test_control_single(void) +{ + fprintf(stderr, "test: control single socket\n"); + + if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | + PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { + fprintf(stderr, "ERROR: opened socket with dual rollover\n"); + exit(1); + } +} + +/* Test illegal group with different modes or flags */ +static void test_control_group(void) +{ + int fds[2]; + + fprintf(stderr, "test: control multiple sockets\n"); + + fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[0] == -1) { + fprintf(stderr, "ERROR: failed to open HASH socket\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong mode\n"); + exit(1); + } + fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[1] == -1) { + fprintf(stderr, "ERROR: failed to join group\n"); + exit(1); + } + if (close(fds[1]) || close(fds[0])) { + fprintf(stderr, "ERROR: closing sockets\n"); + exit(1); + } +} + +static void test_datapath(uint16_t typeflags, + const int expect1[], const int expect2[]) +{ + const int expect0[] = { 0, 0 }; + int fds[2], fds_udp[2][2]; + + fprintf(stderr, "test: datapath 0x%hx\n", typeflags); + + fds[0] = sock_fanout_open(typeflags, 20); + fds[1] = sock_fanout_open(typeflags, 20); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: failed open\n"); + exit(1); + } + pair_udp_open(fds_udp[0], 8000); + pair_udp_open(fds_udp[1], 8002); + sock_fanout_read(fds, expect0); + + /* Send data, but not enough to overflow a queue */ + pair_udp_send(fds_udp[0], 15); + pair_udp_send(fds_udp[1], 5); + sock_fanout_read(fds, expect1); + + /* Send more data, overflow the queue */ + pair_udp_send(fds_udp[0], 15); + /* TODO: ensure consistent order between expect1 and expect2 */ + sock_fanout_read(fds, expect2); + + if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || + close(fds_udp[0][1]) || close(fds_udp[0][0]) || + close(fds[1]) || close(fds[0])) { + fprintf(stderr, "close datapath\n"); + exit(1); + } +} + +int main(int argc, char **argv) +{ + const int expect_hash[2][2] = { { 15, 5 }, { 5, 0 } }; + const int expect_hash_rb[2][2] = { { 15, 5 }, { 5, 10 } }; + const int expect_rb[2][2] = { { 20, 0 }, { 0, 15 } }; + + test_control_single(); + test_control_group(); + + test_datapath(PACKET_FANOUT_HASH, expect_hash[0], expect_hash[1]); + test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, + expect_hash_rb[0], expect_hash_rb[1]); + test_datapath(PACKET_FANOUT_ROLLOVER, expect_rb[0], expect_rb[1]); + + printf("OK. All tests passed\n"); + return 0; +} diff --git a/tools/testing/selftests/net-afpacket/run_afpackettests b/tools/testing/selftests/net-afpacket/run_afpackettests new file mode 100644 index 000000000000..7907824c6355 --- /dev/null +++ b/tools/testing/selftests/net-afpacket/run_afpackettests @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ $(id -u) != 0 ]; then + echo $msg must be run as root >&2 + exit 0 +fi + +echo "--------------------" +echo "running psock_fanout test" +echo "--------------------" +./psock_fanout +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi -- cgit v1.2.3 From 947124460d0c47d465b5846946d3f2b5cee6026e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 19 Mar 2013 17:05:50 -0400 Subject: net: Fix failure string in net-socket selftests Makefile. Signed-off-by: David S. Miller --- tools/testing/selftests/net-socket/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/net-socket/Makefile b/tools/testing/selftests/net-socket/Makefile index f27ee10da530..2450fd8bb10a 100644 --- a/tools/testing/selftests/net-socket/Makefile +++ b/tools/testing/selftests/net-socket/Makefile @@ -10,7 +10,7 @@ all: $(NET_SOCK_PROGS) $(CC) $(CFLAGS) -o $@ $^ run_tests: all - @/bin/sh ./run_netsocktests || echo "vmtests: [FAIL]" + @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]" clean: $(RM) $(NET_SOCK_PROGS) -- cgit v1.2.3 From b44540ea024b9280c9be9d055f084e0956bcfa44 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 19 Mar 2013 18:08:45 -0400 Subject: net: Get rid of compat defines in psock_fanout.c selftest. Reported-by: Daniel Baluta Signed-off-by: David S. Miller --- tools/testing/selftests/net-afpacket/psock_fanout.c | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c index 09dbf93c53d4..af9b0196b0be 100644 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -56,24 +56,6 @@ #include #include -/* Hack: build even if local includes are old */ -#ifndef PACKET_FANOUT -#define PACKET_FANOUT 18 -#define PACKET_FANOUT_HASH 0 -#define PACKET_FANOUT_LB 1 -#define PACKET_FANOUT_CPU 2 -#define PACKET_FANOUT_FLAG_DEFRAG 0x8000 - -#ifndef PACKET_FANOUT_ROLLOVER -#define PACKET_FANOUT_ROLLOVER 3 -#endif - -#ifndef PACKET_FANOUT_FLAG_ROLLOVER -#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 -#endif - -#endif - #define DATA_LEN 100 #define DATA_CHAR 'a' -- cgit v1.2.3 From 73640c991e2f2804939af70567b23e4c54b7c266 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 18 Mar 2013 13:22:18 +1030 Subject: tools/virtio: fix build for 3.8 Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- tools/virtio/Makefile | 2 +- tools/virtio/linux/virtio.h | 7 ++++++- tools/virtio/virtio_test.c | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/virtio/Makefile b/tools/virtio/Makefile index d1d442ed106a..b48c4329e644 100644 --- a/tools/virtio/Makefile +++ b/tools/virtio/Makefile @@ -1,7 +1,7 @@ all: test mod test: virtio_test virtio_test: virtio_ring.o virtio_test.o -CFLAGS += -g -O2 -Wall -I. -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -MMD +CFLAGS += -g -O2 -Wall -I. -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD vpath %.c ../../drivers/virtio mod: ${MAKE} -C `pwd`/../.. M=`pwd`/vhost_test diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h index 81847dd08bd0..390c4cb3b018 100644 --- a/tools/virtio/linux/virtio.h +++ b/tools/virtio/linux/virtio.h @@ -85,6 +85,8 @@ typedef __u16 u16; typedef enum { GFP_KERNEL, GFP_ATOMIC, + __GFP_HIGHMEM, + __GFP_HIGH } gfp_t; typedef enum { IRQ_NONE, @@ -163,6 +165,8 @@ struct virtqueue { void (*callback)(struct virtqueue *vq); const char *name; struct virtio_device *vdev; + unsigned int index; + unsigned int num_free; void *priv; }; @@ -206,7 +210,8 @@ bool virtqueue_enable_cb(struct virtqueue *vq); bool virtqueue_enable_cb_delayed(struct virtqueue *vq); void *virtqueue_detach_unused_buf(struct virtqueue *vq); -struct virtqueue *vring_new_virtqueue(unsigned int num, +struct virtqueue *vring_new_virtqueue(unsigned int index, + unsigned int num, unsigned int vring_align, struct virtio_device *vdev, bool weak_barriers, diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c index fcc9aa25fd08..faf3f0c9d71f 100644 --- a/tools/virtio/virtio_test.c +++ b/tools/virtio/virtio_test.c @@ -92,7 +92,8 @@ static void vq_info_add(struct vdev_info *dev, int num) assert(r >= 0); memset(info->ring, 0, vring_size(num, 4096)); vring_init(&info->vring, num, info->ring, 4096); - info->vq = vring_new_virtqueue(info->vring.num, 4096, &dev->vdev, + info->vq = vring_new_virtqueue(info->idx, + info->vring.num, 4096, &dev->vdev, true, info->ring, vq_notify, vq_callback, "test"); assert(info->vq); -- cgit v1.2.3 From 61d0b5a4b2777dcf5daef245e212b3c1fa8091ca Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Mar 2013 13:22:19 +1030 Subject: tools/virtio: separate headers more. This makes them a bit more like the kernel headers, so we can include more real kernel headers in our tests. In addition this means that we don't break tools/virtio with the next patch. Signed-off-by: Rusty Russell --- tools/virtio/asm/barrier.h | 14 +++ tools/virtio/linux/bug.h | 10 ++ tools/virtio/linux/err.h | 26 +++++ tools/virtio/linux/export.h | 5 + tools/virtio/linux/irqreturn.h | 1 + tools/virtio/linux/kernel.h | 112 +++++++++++++++++++++ tools/virtio/linux/module.h | 1 + tools/virtio/linux/printk.h | 4 + tools/virtio/linux/ratelimit.h | 4 + tools/virtio/linux/scatterlist.h | 173 ++++++++++++++++++++++++++++++++ tools/virtio/linux/types.h | 28 ++++++ tools/virtio/linux/uaccess.h | 50 +++++++++ tools/virtio/linux/uio.h | 1 + tools/virtio/linux/virtio.h | 150 +-------------------------- tools/virtio/linux/virtio_config.h | 6 ++ tools/virtio/linux/virtio_ring.h | 1 + tools/virtio/linux/vringh.h | 1 + tools/virtio/uapi/linux/uio.h | 1 + tools/virtio/uapi/linux/virtio_config.h | 1 + tools/virtio/uapi/linux/virtio_ring.h | 4 + tools/virtio/virtio_test.c | 4 + 21 files changed, 450 insertions(+), 147 deletions(-) create mode 100644 tools/virtio/asm/barrier.h create mode 100644 tools/virtio/linux/bug.h create mode 100644 tools/virtio/linux/err.h create mode 100644 tools/virtio/linux/export.h create mode 100644 tools/virtio/linux/irqreturn.h create mode 100644 tools/virtio/linux/kernel.h create mode 100644 tools/virtio/linux/printk.h create mode 100644 tools/virtio/linux/ratelimit.h create mode 100644 tools/virtio/linux/scatterlist.h create mode 100644 tools/virtio/linux/types.h create mode 100644 tools/virtio/linux/uaccess.h create mode 100644 tools/virtio/linux/uio.h create mode 100644 tools/virtio/linux/virtio_config.h create mode 100644 tools/virtio/linux/virtio_ring.h create mode 100644 tools/virtio/linux/vringh.h create mode 100644 tools/virtio/uapi/linux/uio.h create mode 100644 tools/virtio/uapi/linux/virtio_config.h create mode 100644 tools/virtio/uapi/linux/virtio_ring.h (limited to 'tools') diff --git a/tools/virtio/asm/barrier.h b/tools/virtio/asm/barrier.h new file mode 100644 index 000000000000..aff61e13306c --- /dev/null +++ b/tools/virtio/asm/barrier.h @@ -0,0 +1,14 @@ +#if defined(__i386__) || defined(__x86_64__) +#define barrier() asm volatile("" ::: "memory") +#define mb() __sync_synchronize() + +#define smp_mb() mb() +# define smp_rmb() barrier() +# define smp_wmb() barrier() +/* Weak barriers should be used. If not - it's a bug */ +# define rmb() abort() +# define wmb() abort() +#else +#error Please fill in barrier macros +#endif + diff --git a/tools/virtio/linux/bug.h b/tools/virtio/linux/bug.h new file mode 100644 index 000000000000..fb94f0787c47 --- /dev/null +++ b/tools/virtio/linux/bug.h @@ -0,0 +1,10 @@ +#ifndef BUG_H +#define BUG_H + +#define BUG_ON(__BUG_ON_cond) assert(!(__BUG_ON_cond)) + +#define BUILD_BUG_ON(x) + +#define BUG() abort() + +#endif /* BUG_H */ diff --git a/tools/virtio/linux/err.h b/tools/virtio/linux/err.h new file mode 100644 index 000000000000..e32eff8b2a14 --- /dev/null +++ b/tools/virtio/linux/err.h @@ -0,0 +1,26 @@ +#ifndef ERR_H +#define ERR_H +#define MAX_ERRNO 4095 + +#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO) + +static inline void * __must_check ERR_PTR(long error) +{ + return (void *) error; +} + +static inline long __must_check PTR_ERR(const void *ptr) +{ + return (long) ptr; +} + +static inline long __must_check IS_ERR(const void *ptr) +{ + return IS_ERR_VALUE((unsigned long)ptr); +} + +static inline long __must_check IS_ERR_OR_NULL(const void *ptr) +{ + return !ptr || IS_ERR_VALUE((unsigned long)ptr); +} +#endif /* ERR_H */ diff --git a/tools/virtio/linux/export.h b/tools/virtio/linux/export.h new file mode 100644 index 000000000000..7311d326894a --- /dev/null +++ b/tools/virtio/linux/export.h @@ -0,0 +1,5 @@ +#define EXPORT_SYMBOL(sym) +#define EXPORT_SYMBOL_GPL(sym) +#define EXPORT_SYMBOL_GPL_FUTURE(sym) +#define EXPORT_UNUSED_SYMBOL(sym) +#define EXPORT_UNUSED_SYMBOL_GPL(sym) diff --git a/tools/virtio/linux/irqreturn.h b/tools/virtio/linux/irqreturn.h new file mode 100644 index 000000000000..a3c4e7be7089 --- /dev/null +++ b/tools/virtio/linux/irqreturn.h @@ -0,0 +1 @@ +#include "../../../include/linux/irqreturn.h" diff --git a/tools/virtio/linux/kernel.h b/tools/virtio/linux/kernel.h new file mode 100644 index 000000000000..fba705963968 --- /dev/null +++ b/tools/virtio/linux/kernel.h @@ -0,0 +1,112 @@ +#ifndef KERNEL_H +#define KERNEL_H +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define CONFIG_SMP + +#define PAGE_SIZE getpagesize() +#define PAGE_MASK (~(PAGE_SIZE-1)) + +typedef unsigned long long dma_addr_t; +typedef size_t __kernel_size_t; + +struct page { + unsigned long long dummy; +}; + +/* Physical == Virtual */ +#define virt_to_phys(p) ((unsigned long)p) +#define phys_to_virt(a) ((void *)(unsigned long)(a)) +/* Page address: Virtual / 4K */ +#define page_to_phys(p) ((dma_addr_t)(unsigned long)(p)) +#define virt_to_page(p) ((struct page *)((unsigned long)p & PAGE_MASK)) + +#define offset_in_page(p) (((unsigned long)p) % PAGE_SIZE) + +#define __printf(a,b) __attribute__((format(printf,a,b))) + +typedef enum { + GFP_KERNEL, + GFP_ATOMIC, + __GFP_HIGHMEM, + __GFP_HIGH +} gfp_t; + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +extern void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end; +static inline void *kmalloc(size_t s, gfp_t gfp) +{ + if (__kmalloc_fake) + return __kmalloc_fake; + return malloc(s); +} + +static inline void kfree(void *p) +{ + if (p >= __kfree_ignore_start && p < __kfree_ignore_end) + return; + free(p); +} + +static inline void *krealloc(void *p, size_t s, gfp_t gfp) +{ + return realloc(p, s); +} + + +static inline unsigned long __get_free_page(gfp_t gfp) +{ + void *p; + + posix_memalign(&p, PAGE_SIZE, PAGE_SIZE); + return (unsigned long)p; +} + +static inline void free_page(unsigned long addr) +{ + free((void *)addr); +} + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define uninitialized_var(x) x = x + +# ifndef likely +# define likely(x) (__builtin_expect(!!(x), 1)) +# endif +# ifndef unlikely +# define unlikely(x) (__builtin_expect(!!(x), 0)) +# endif + +#define pr_err(format, ...) fprintf (stderr, format, ## __VA_ARGS__) +#ifdef DEBUG +#define pr_debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__) +#else +#define pr_debug(format, ...) do {} while (0) +#endif +#define dev_err(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__) +#define dev_warn(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__) + +#define min(x, y) ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void) (&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; }) + +#endif /* KERNEL_H */ diff --git a/tools/virtio/linux/module.h b/tools/virtio/linux/module.h index e69de29bb2d1..3039a7e972b6 100644 --- a/tools/virtio/linux/module.h +++ b/tools/virtio/linux/module.h @@ -0,0 +1 @@ +#include diff --git a/tools/virtio/linux/printk.h b/tools/virtio/linux/printk.h new file mode 100644 index 000000000000..9f2423bd89c2 --- /dev/null +++ b/tools/virtio/linux/printk.h @@ -0,0 +1,4 @@ +#include "../../../include/linux/kern_levels.h" + +#define printk printf +#define vprintk vprintf diff --git a/tools/virtio/linux/ratelimit.h b/tools/virtio/linux/ratelimit.h new file mode 100644 index 000000000000..dcce1725f90d --- /dev/null +++ b/tools/virtio/linux/ratelimit.h @@ -0,0 +1,4 @@ +#define DEFINE_RATELIMIT_STATE(name, interval_init, burst_init) int name = 0 + +#define __ratelimit(x) (*(x)) + diff --git a/tools/virtio/linux/scatterlist.h b/tools/virtio/linux/scatterlist.h new file mode 100644 index 000000000000..b2cf7d0f6133 --- /dev/null +++ b/tools/virtio/linux/scatterlist.h @@ -0,0 +1,173 @@ +#ifndef SCATTERLIST_H +#define SCATTERLIST_H +#include + +struct scatterlist { + unsigned long page_link; + unsigned int offset; + unsigned int length; + dma_addr_t dma_address; +}; + +/* Scatterlist helpers, stolen from linux/scatterlist.h */ +#define sg_is_chain(sg) ((sg)->page_link & 0x01) +#define sg_is_last(sg) ((sg)->page_link & 0x02) +#define sg_chain_ptr(sg) \ + ((struct scatterlist *) ((sg)->page_link & ~0x03)) + +/** + * sg_assign_page - Assign a given page to an SG entry + * @sg: SG entry + * @page: The page + * + * Description: + * Assign page to sg entry. Also see sg_set_page(), the most commonly used + * variant. + * + **/ +static inline void sg_assign_page(struct scatterlist *sg, struct page *page) +{ + unsigned long page_link = sg->page_link & 0x3; + + /* + * In order for the low bit stealing approach to work, pages + * must be aligned at a 32-bit boundary as a minimum. + */ + BUG_ON((unsigned long) page & 0x03); +#ifdef CONFIG_DEBUG_SG + BUG_ON(sg->sg_magic != SG_MAGIC); + BUG_ON(sg_is_chain(sg)); +#endif + sg->page_link = page_link | (unsigned long) page; +} + +/** + * sg_set_page - Set sg entry to point at given page + * @sg: SG entry + * @page: The page + * @len: Length of data + * @offset: Offset into page + * + * Description: + * Use this function to set an sg entry pointing at a page, never assign + * the page directly. We encode sg table information in the lower bits + * of the page pointer. See sg_page() for looking up the page belonging + * to an sg entry. + * + **/ +static inline void sg_set_page(struct scatterlist *sg, struct page *page, + unsigned int len, unsigned int offset) +{ + sg_assign_page(sg, page); + sg->offset = offset; + sg->length = len; +} + +static inline struct page *sg_page(struct scatterlist *sg) +{ +#ifdef CONFIG_DEBUG_SG + BUG_ON(sg->sg_magic != SG_MAGIC); + BUG_ON(sg_is_chain(sg)); +#endif + return (struct page *)((sg)->page_link & ~0x3); +} + +/* + * Loop over each sg element, following the pointer to a new list if necessary + */ +#define for_each_sg(sglist, sg, nr, __i) \ + for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg)) + +/** + * sg_chain - Chain two sglists together + * @prv: First scatterlist + * @prv_nents: Number of entries in prv + * @sgl: Second scatterlist + * + * Description: + * Links @prv@ and @sgl@ together, to form a longer scatterlist. + * + **/ +static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents, + struct scatterlist *sgl) +{ + /* + * offset and length are unused for chain entry. Clear them. + */ + prv[prv_nents - 1].offset = 0; + prv[prv_nents - 1].length = 0; + + /* + * Set lowest bit to indicate a link pointer, and make sure to clear + * the termination bit if it happens to be set. + */ + prv[prv_nents - 1].page_link = ((unsigned long) sgl | 0x01) & ~0x02; +} + +/** + * sg_mark_end - Mark the end of the scatterlist + * @sg: SG entryScatterlist + * + * Description: + * Marks the passed in sg entry as the termination point for the sg + * table. A call to sg_next() on this entry will return NULL. + * + **/ +static inline void sg_mark_end(struct scatterlist *sg) +{ +#ifdef CONFIG_DEBUG_SG + BUG_ON(sg->sg_magic != SG_MAGIC); +#endif + /* + * Set termination bit, clear potential chain bit + */ + sg->page_link |= 0x02; + sg->page_link &= ~0x01; +} + +static inline struct scatterlist *sg_next(struct scatterlist *sg) +{ +#ifdef CONFIG_DEBUG_SG + BUG_ON(sg->sg_magic != SG_MAGIC); +#endif + if (sg_is_last(sg)) + return NULL; + + sg++; + if (unlikely(sg_is_chain(sg))) + sg = sg_chain_ptr(sg); + + return sg; +} + +static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) +{ + memset(sgl, 0, sizeof(*sgl) * nents); +#ifdef CONFIG_DEBUG_SG + { + unsigned int i; + for (i = 0; i < nents; i++) + sgl[i].sg_magic = SG_MAGIC; + } +#endif + sg_mark_end(&sgl[nents - 1]); +} + +static inline dma_addr_t sg_phys(struct scatterlist *sg) +{ + return page_to_phys(sg_page(sg)) + sg->offset; +} + +static inline void sg_set_buf(struct scatterlist *sg, const void *buf, + unsigned int buflen) +{ + sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf)); +} + +static inline void sg_init_one(struct scatterlist *sg, + const void *buf, unsigned int buflen) +{ + sg_init_table(sg, 1); + sg_set_buf(sg, buf, buflen); +} +#endif /* SCATTERLIST_H */ diff --git a/tools/virtio/linux/types.h b/tools/virtio/linux/types.h new file mode 100644 index 000000000000..f8ebb9a2b3d6 --- /dev/null +++ b/tools/virtio/linux/types.h @@ -0,0 +1,28 @@ +#ifndef TYPES_H +#define TYPES_H +#include + +#define __force +#define __user +#define __must_check +#define __cold + +typedef uint64_t u64; +typedef int64_t s64; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint8_t u8; +typedef int8_t s8; + +typedef uint64_t __u64; +typedef int64_t __s64; +typedef uint32_t __u32; +typedef int32_t __s32; +typedef uint16_t __u16; +typedef int16_t __s16; +typedef uint8_t __u8; +typedef int8_t __s8; + +#endif /* TYPES_H */ diff --git a/tools/virtio/linux/uaccess.h b/tools/virtio/linux/uaccess.h new file mode 100644 index 000000000000..0a578fe18653 --- /dev/null +++ b/tools/virtio/linux/uaccess.h @@ -0,0 +1,50 @@ +#ifndef UACCESS_H +#define UACCESS_H +extern void *__user_addr_min, *__user_addr_max; + +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) + +static inline void __chk_user_ptr(const volatile void *p, size_t size) +{ + assert(p >= __user_addr_min && p + size <= __user_addr_max); +} + +#define put_user(x, ptr) \ +({ \ + typeof(ptr) __pu_ptr = (ptr); \ + __chk_user_ptr(__pu_ptr, sizeof(*__pu_ptr)); \ + ACCESS_ONCE(*(__pu_ptr)) = x; \ + 0; \ +}) + +#define get_user(x, ptr) \ +({ \ + typeof(ptr) __pu_ptr = (ptr); \ + __chk_user_ptr(__pu_ptr, sizeof(*__pu_ptr)); \ + x = ACCESS_ONCE(*(__pu_ptr)); \ + 0; \ +}) + +static void volatile_memcpy(volatile char *to, const volatile char *from, + unsigned long n) +{ + while (n--) + *(to++) = *(from++); +} + +static inline int copy_from_user(void *to, const void __user volatile *from, + unsigned long n) +{ + __chk_user_ptr(from, n); + volatile_memcpy(to, from, n); + return 0; +} + +static inline int copy_to_user(void __user volatile *to, const void *from, + unsigned long n) +{ + __chk_user_ptr(to, n); + volatile_memcpy(to, from, n); + return 0; +} +#endif /* UACCESS_H */ diff --git a/tools/virtio/linux/uio.h b/tools/virtio/linux/uio.h new file mode 100644 index 000000000000..ecbdf922b0f4 --- /dev/null +++ b/tools/virtio/linux/uio.h @@ -0,0 +1 @@ +#include "../../../include/linux/uio.h" diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h index 390c4cb3b018..e4af6591f5ff 100644 --- a/tools/virtio/linux/virtio.h +++ b/tools/virtio/linux/virtio.h @@ -1,129 +1,7 @@ #ifndef LINUX_VIRTIO_H #define LINUX_VIRTIO_H - -#include -#include -#include -#include -#include -#include - -#include -#include - -typedef unsigned long long dma_addr_t; - -struct scatterlist { - unsigned long page_link; - unsigned int offset; - unsigned int length; - dma_addr_t dma_address; -}; - -struct page { - unsigned long long dummy; -}; - -#define BUG_ON(__BUG_ON_cond) assert(!(__BUG_ON_cond)) - -/* Physical == Virtual */ -#define virt_to_phys(p) ((unsigned long)p) -#define phys_to_virt(a) ((void *)(unsigned long)(a)) -/* Page address: Virtual / 4K */ -#define virt_to_page(p) ((struct page*)((virt_to_phys(p) / 4096) * \ - sizeof(struct page))) -#define offset_in_page(p) (((unsigned long)p) % 4096) -#define sg_phys(sg) ((sg->page_link & ~0x3) / sizeof(struct page) * 4096 + \ - sg->offset) -static inline void sg_mark_end(struct scatterlist *sg) -{ - /* - * Set termination bit, clear potential chain bit - */ - sg->page_link |= 0x02; - sg->page_link &= ~0x01; -} -static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) -{ - memset(sgl, 0, sizeof(*sgl) * nents); - sg_mark_end(&sgl[nents - 1]); -} -static inline void sg_assign_page(struct scatterlist *sg, struct page *page) -{ - unsigned long page_link = sg->page_link & 0x3; - - /* - * In order for the low bit stealing approach to work, pages - * must be aligned at a 32-bit boundary as a minimum. - */ - BUG_ON((unsigned long) page & 0x03); - sg->page_link = page_link | (unsigned long) page; -} - -static inline void sg_set_page(struct scatterlist *sg, struct page *page, - unsigned int len, unsigned int offset) -{ - sg_assign_page(sg, page); - sg->offset = offset; - sg->length = len; -} - -static inline void sg_set_buf(struct scatterlist *sg, const void *buf, - unsigned int buflen) -{ - sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf)); -} - -static inline void sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen) -{ - sg_init_table(sg, 1); - sg_set_buf(sg, buf, buflen); -} - -typedef __u16 u16; - -typedef enum { - GFP_KERNEL, - GFP_ATOMIC, - __GFP_HIGHMEM, - __GFP_HIGH -} gfp_t; -typedef enum { - IRQ_NONE, - IRQ_HANDLED -} irqreturn_t; - -static inline void *kmalloc(size_t s, gfp_t gfp) -{ - return malloc(s); -} - -static inline void kfree(void *p) -{ - free(p); -} - -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) - -#define uninitialized_var(x) x = x - -# ifndef likely -# define likely(x) (__builtin_expect(!!(x), 1)) -# endif -# ifndef unlikely -# define unlikely(x) (__builtin_expect(!!(x), 0)) -# endif - -#define pr_err(format, ...) fprintf (stderr, format, ## __VA_ARGS__) -#ifdef DEBUG -#define pr_debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__) -#else -#define pr_debug(format, ...) do {} while (0) -#endif -#define dev_err(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__) -#define dev_warn(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__) +#include +#include /* TODO: empty stubs for now. Broken but enough for virtio_ring.c */ #define list_add_tail(a, b) do {} while (0) @@ -133,6 +11,7 @@ static inline void kfree(void *p) #define BITS_PER_BYTE 8 #define BITS_PER_LONG (sizeof(long) * BITS_PER_BYTE) #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) + /* TODO: Not atomic as it should be: * we don't use this for anything important. */ static inline void clear_bit(int nr, volatile unsigned long *addr) @@ -147,10 +26,6 @@ static inline int test_bit(int nr, const volatile unsigned long *addr) { return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); } - -/* The only feature we care to support */ -#define virtio_has_feature(dev, feature) \ - test_bit((feature), (dev)->features) /* end of stubs */ struct virtio_device { @@ -170,28 +45,9 @@ struct virtqueue { void *priv; }; -#define EXPORT_SYMBOL_GPL(__EXPORT_SYMBOL_GPL_name) \ - void __EXPORT_SYMBOL_GPL##__EXPORT_SYMBOL_GPL_name() { \ -} #define MODULE_LICENSE(__MODULE_LICENSE_value) \ const char *__MODULE_LICENSE_name = __MODULE_LICENSE_value -#define CONFIG_SMP - -#if defined(__i386__) || defined(__x86_64__) -#define barrier() asm volatile("" ::: "memory") -#define mb() __sync_synchronize() - -#define smp_mb() mb() -# define smp_rmb() barrier() -# define smp_wmb() barrier() -/* Weak barriers should be used. If not - it's a bug */ -# define rmb() abort() -# define wmb() abort() -#else -#error Please fill in barrier macros -#endif - /* Interfaces exported by virtio_ring. */ int virtqueue_add_buf(struct virtqueue *vq, struct scatterlist sg[], diff --git a/tools/virtio/linux/virtio_config.h b/tools/virtio/linux/virtio_config.h new file mode 100644 index 000000000000..5049967f99f7 --- /dev/null +++ b/tools/virtio/linux/virtio_config.h @@ -0,0 +1,6 @@ +#define VIRTIO_TRANSPORT_F_START 28 +#define VIRTIO_TRANSPORT_F_END 32 + +#define virtio_has_feature(dev, feature) \ + test_bit((feature), (dev)->features) + diff --git a/tools/virtio/linux/virtio_ring.h b/tools/virtio/linux/virtio_ring.h new file mode 100644 index 000000000000..8949c4e2772c --- /dev/null +++ b/tools/virtio/linux/virtio_ring.h @@ -0,0 +1 @@ +#include "../../../include/linux/virtio_ring.h" diff --git a/tools/virtio/linux/vringh.h b/tools/virtio/linux/vringh.h new file mode 100644 index 000000000000..9348957be56e --- /dev/null +++ b/tools/virtio/linux/vringh.h @@ -0,0 +1 @@ +#include "../../../include/linux/vringh.h" diff --git a/tools/virtio/uapi/linux/uio.h b/tools/virtio/uapi/linux/uio.h new file mode 100644 index 000000000000..7230e9002207 --- /dev/null +++ b/tools/virtio/uapi/linux/uio.h @@ -0,0 +1 @@ +#include diff --git a/tools/virtio/uapi/linux/virtio_config.h b/tools/virtio/uapi/linux/virtio_config.h new file mode 100644 index 000000000000..4c86675f0159 --- /dev/null +++ b/tools/virtio/uapi/linux/virtio_config.h @@ -0,0 +1 @@ +#include "../../../../include/uapi/linux/virtio_config.h" diff --git a/tools/virtio/uapi/linux/virtio_ring.h b/tools/virtio/uapi/linux/virtio_ring.h new file mode 100644 index 000000000000..4d99c78234d3 --- /dev/null +++ b/tools/virtio/uapi/linux/virtio_ring.h @@ -0,0 +1,4 @@ +#ifndef VIRTIO_RING_H +#define VIRTIO_RING_H +#include "../../../../include/uapi/linux/virtio_ring.h" +#endif /* VIRTIO_RING_H */ diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c index faf3f0c9d71f..814ae803c878 100644 --- a/tools/virtio/virtio_test.c +++ b/tools/virtio/virtio_test.c @@ -10,11 +10,15 @@ #include #include #include +#include #include #include #include #include "../../drivers/vhost/test.h" +/* Unused */ +void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end; + struct vq_info { int kick; int call; -- cgit v1.2.3 From 1515c5ce26ae45a8ecea4e68a484562ca4356442 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Mar 2013 13:50:24 +1030 Subject: tools/virtio: add vring_test. This is mainly to test the drivers/vhost/vringh.c code, but it also uses the drivers/virtio/virtio_ring.c code for the guest side. Usage for testing the basic implementation: ./vringh_test # Test with indirect descriptors ./vringh_test --indirect # Test with indirect descriptors and event indexex ./vringh_test --indirect --eventidx You can run a parallel stress test by adding --parallel to any of the above options. eg ./vringh_test --parallel: Using CPUS 0 and 3 Guest: notified 10107974, pinged 107970 Host: notified 108158, pinged 3172148 ./vringh_test --indirect --eventidx --parallel: Using CPUS 0 and 3 Guest: notified 156357, pinged 156251 Host: notified 156251, pinged 78179 Average of 50 times doing ./vringh_test --indirect --eventidx --parallel: 2.840000-3.040000(2.927292)user Signed-off-by: Rusty Russell --- tools/virtio/Makefile | 10 +- tools/virtio/linux/uio.h | 2 + tools/virtio/vringh_test.c | 739 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 747 insertions(+), 4 deletions(-) create mode 100644 tools/virtio/vringh_test.c (limited to 'tools') diff --git a/tools/virtio/Makefile b/tools/virtio/Makefile index b48c4329e644..3187c62d9814 100644 --- a/tools/virtio/Makefile +++ b/tools/virtio/Makefile @@ -1,12 +1,14 @@ all: test mod -test: virtio_test +test: virtio_test vringh_test virtio_test: virtio_ring.o virtio_test.o -CFLAGS += -g -O2 -Wall -I. -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -vpath %.c ../../drivers/virtio +vringh_test: vringh_test.o vringh.o virtio_ring.o + +CFLAGS += -g -O2 -Wall -I. -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE +vpath %.c ../../drivers/virtio ../../drivers/vhost mod: ${MAKE} -C `pwd`/../.. M=`pwd`/vhost_test .PHONY: all test mod clean clean: - ${RM} *.o vhost_test/*.o vhost_test/.*.cmd \ + ${RM} *.o vringh_test virtio_test vhost_test/*.o vhost_test/.*.cmd \ vhost_test/Module.symvers vhost_test/modules.order *.d -include *.d diff --git a/tools/virtio/linux/uio.h b/tools/virtio/linux/uio.h index ecbdf922b0f4..cd20f0ba3081 100644 --- a/tools/virtio/linux/uio.h +++ b/tools/virtio/linux/uio.h @@ -1 +1,3 @@ +#include + #include "../../../include/linux/uio.h" diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c new file mode 100644 index 000000000000..6a48ca5c101f --- /dev/null +++ b/tools/virtio/vringh_test.c @@ -0,0 +1,739 @@ +/* Simple test of virtio code, entirely in userpsace. */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USER_MEM (1024*1024) +void *__user_addr_min, *__user_addr_max; +void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end; +static u64 user_addr_offset; + +#define RINGSIZE 256 +#define ALIGN 4096 + +static void never_notify_host(struct virtqueue *vq) +{ + abort(); +} + +static void never_callback_guest(struct virtqueue *vq) +{ + abort(); +} + +static bool getrange_iov(struct vringh *vrh, u64 addr, struct vringh_range *r) +{ + if (addr < (u64)(unsigned long)__user_addr_min - user_addr_offset) + return false; + if (addr >= (u64)(unsigned long)__user_addr_max - user_addr_offset) + return false; + + r->start = (u64)(unsigned long)__user_addr_min - user_addr_offset; + r->end_incl = (u64)(unsigned long)__user_addr_max - 1 - user_addr_offset; + r->offset = user_addr_offset; + return true; +} + +/* We return single byte ranges. */ +static bool getrange_slow(struct vringh *vrh, u64 addr, struct vringh_range *r) +{ + if (addr < (u64)(unsigned long)__user_addr_min - user_addr_offset) + return false; + if (addr >= (u64)(unsigned long)__user_addr_max - user_addr_offset) + return false; + + r->start = addr; + r->end_incl = r->start; + r->offset = user_addr_offset; + return true; +} + +struct guest_virtio_device { + struct virtio_device vdev; + int to_host_fd; + unsigned long notifies; +}; + +static void parallel_notify_host(struct virtqueue *vq) +{ + struct guest_virtio_device *gvdev; + + gvdev = container_of(vq->vdev, struct guest_virtio_device, vdev); + write(gvdev->to_host_fd, "", 1); + gvdev->notifies++; +} + +static void no_notify_host(struct virtqueue *vq) +{ +} + +#define NUM_XFERS (10000000) + +/* We aim for two "distant" cpus. */ +static void find_cpus(unsigned int *first, unsigned int *last) +{ + unsigned int i; + + *first = -1U; + *last = 0; + for (i = 0; i < 4096; i++) { + cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(i, &set); + if (sched_setaffinity(getpid(), sizeof(set), &set) == 0) { + if (i < *first) + *first = i; + if (i > *last) + *last = i; + } + } +} + +/* Opencoded version for fast mode */ +static inline int vringh_get_head(struct vringh *vrh, u16 *head) +{ + u16 avail_idx, i; + int err; + + err = get_user(avail_idx, &vrh->vring.avail->idx); + if (err) + return err; + + if (vrh->last_avail_idx == avail_idx) + return 0; + + /* Only get avail ring entries after they have been exposed by guest. */ + virtio_rmb(vrh->weak_barriers); + + i = vrh->last_avail_idx & (vrh->vring.num - 1); + + err = get_user(*head, &vrh->vring.avail->ring[i]); + if (err) + return err; + + vrh->last_avail_idx++; + return 1; +} + +static int parallel_test(unsigned long features, + bool (*getrange)(struct vringh *vrh, + u64 addr, struct vringh_range *r), + bool fast_vringh) +{ + void *host_map, *guest_map; + int fd, mapsize, to_guest[2], to_host[2]; + unsigned long xfers = 0, notifies = 0, receives = 0; + unsigned int first_cpu, last_cpu; + cpu_set_t cpu_set; + char buf[128]; + + /* Create real file to mmap. */ + fd = open("/tmp/vringh_test-file", O_RDWR|O_CREAT|O_TRUNC, 0600); + if (fd < 0) + err(1, "Opening /tmp/vringh_test-file"); + + /* Extra room at the end for some data, and indirects */ + mapsize = vring_size(RINGSIZE, ALIGN) + + RINGSIZE * 2 * sizeof(int) + + RINGSIZE * 6 * sizeof(struct vring_desc); + mapsize = (mapsize + getpagesize() - 1) & ~(getpagesize() - 1); + ftruncate(fd, mapsize); + + /* Parent and child use separate addresses, to check our mapping logic! */ + host_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + guest_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + pipe(to_guest); + pipe(to_host); + + CPU_ZERO(&cpu_set); + find_cpus(&first_cpu, &last_cpu); + printf("Using CPUS %u and %u\n", first_cpu, last_cpu); + fflush(stdout); + + if (fork() != 0) { + struct vringh vrh; + int status, err, rlen = 0; + char rbuf[5]; + + /* We are the host: never access guest addresses! */ + munmap(guest_map, mapsize); + + __user_addr_min = host_map; + __user_addr_max = __user_addr_min + mapsize; + user_addr_offset = host_map - guest_map; + assert(user_addr_offset); + + close(to_guest[0]); + close(to_host[1]); + + vring_init(&vrh.vring, RINGSIZE, host_map, ALIGN); + vringh_init_user(&vrh, features, RINGSIZE, true, + vrh.vring.desc, vrh.vring.avail, vrh.vring.used); + CPU_SET(first_cpu, &cpu_set); + if (sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set)) + errx(1, "Could not set affinity to cpu %u", first_cpu); + + while (xfers < NUM_XFERS) { + struct iovec host_riov[2], host_wiov[2]; + struct vringh_iov riov, wiov; + u16 head, written; + + if (fast_vringh) { + for (;;) { + err = vringh_get_head(&vrh, &head); + if (err != 0) + break; + err = vringh_need_notify_user(&vrh); + if (err < 0) + errx(1, "vringh_need_notify_user: %i", + err); + if (err) { + write(to_guest[1], "", 1); + notifies++; + } + } + if (err != 1) + errx(1, "vringh_get_head"); + written = 0; + goto complete; + } else { + vringh_iov_init(&riov, + host_riov, + ARRAY_SIZE(host_riov)); + vringh_iov_init(&wiov, + host_wiov, + ARRAY_SIZE(host_wiov)); + + err = vringh_getdesc_user(&vrh, &riov, &wiov, + getrange, &head); + } + if (err == 0) { + err = vringh_need_notify_user(&vrh); + if (err < 0) + errx(1, "vringh_need_notify_user: %i", + err); + if (err) { + write(to_guest[1], "", 1); + notifies++; + } + + if (!vringh_notify_enable_user(&vrh)) + continue; + + /* Swallow all notifies at once. */ + if (read(to_host[0], buf, sizeof(buf)) < 1) + break; + + vringh_notify_disable_user(&vrh); + receives++; + continue; + } + if (err != 1) + errx(1, "vringh_getdesc_user: %i", err); + + /* We simply copy bytes. */ + if (riov.used) { + rlen = vringh_iov_pull_user(&riov, rbuf, + sizeof(rbuf)); + if (rlen != 4) + errx(1, "vringh_iov_pull_user: %i", + rlen); + assert(riov.i == riov.used); + written = 0; + } else { + err = vringh_iov_push_user(&wiov, rbuf, rlen); + if (err != rlen) + errx(1, "vringh_iov_push_user: %i", + err); + assert(wiov.i == wiov.used); + written = err; + } + complete: + xfers++; + + err = vringh_complete_user(&vrh, head, written); + if (err != 0) + errx(1, "vringh_complete_user: %i", err); + } + + err = vringh_need_notify_user(&vrh); + if (err < 0) + errx(1, "vringh_need_notify_user: %i", err); + if (err) { + write(to_guest[1], "", 1); + notifies++; + } + wait(&status); + if (!WIFEXITED(status)) + errx(1, "Child died with signal %i?", WTERMSIG(status)); + if (WEXITSTATUS(status) != 0) + errx(1, "Child exited %i?", WEXITSTATUS(status)); + printf("Host: notified %lu, pinged %lu\n", notifies, receives); + return 0; + } else { + struct guest_virtio_device gvdev; + struct virtqueue *vq; + unsigned int *data; + struct vring_desc *indirects; + unsigned int finished = 0; + + /* We pass sg[]s pointing into here, but we need RINGSIZE+1 */ + data = guest_map + vring_size(RINGSIZE, ALIGN); + indirects = (void *)data + (RINGSIZE + 1) * 2 * sizeof(int); + + /* We are the guest. */ + munmap(host_map, mapsize); + + close(to_guest[1]); + close(to_host[0]); + + gvdev.vdev.features[0] = features; + gvdev.to_host_fd = to_host[1]; + gvdev.notifies = 0; + + CPU_SET(first_cpu, &cpu_set); + if (sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set)) + err(1, "Could not set affinity to cpu %u", first_cpu); + + vq = vring_new_virtqueue(0, RINGSIZE, ALIGN, &gvdev.vdev, true, + guest_map, fast_vringh ? no_notify_host + : parallel_notify_host, + never_callback_guest, "guest vq"); + + /* Don't kfree indirects. */ + __kfree_ignore_start = indirects; + __kfree_ignore_end = indirects + RINGSIZE * 6; + + while (xfers < NUM_XFERS) { + struct scatterlist sg[4]; + unsigned int num_sg, len; + int *dbuf, err; + bool output = !(xfers % 2); + + /* Consume bufs. */ + while ((dbuf = virtqueue_get_buf(vq, &len)) != NULL) { + if (len == 4) + assert(*dbuf == finished - 1); + else if (!fast_vringh) + assert(*dbuf == finished); + finished++; + } + + /* Produce a buffer. */ + dbuf = data + (xfers % (RINGSIZE + 1)); + + if (output) + *dbuf = xfers; + else + *dbuf = -1; + + switch ((xfers / sizeof(*dbuf)) % 4) { + case 0: + /* Nasty three-element sg list. */ + sg_init_table(sg, num_sg = 3); + sg_set_buf(&sg[0], (void *)dbuf, 1); + sg_set_buf(&sg[1], (void *)dbuf + 1, 2); + sg_set_buf(&sg[2], (void *)dbuf + 3, 1); + break; + case 1: + sg_init_table(sg, num_sg = 2); + sg_set_buf(&sg[0], (void *)dbuf, 1); + sg_set_buf(&sg[1], (void *)dbuf + 1, 3); + break; + case 2: + sg_init_table(sg, num_sg = 1); + sg_set_buf(&sg[0], (void *)dbuf, 4); + break; + case 3: + sg_init_table(sg, num_sg = 4); + sg_set_buf(&sg[0], (void *)dbuf, 1); + sg_set_buf(&sg[1], (void *)dbuf + 1, 1); + sg_set_buf(&sg[2], (void *)dbuf + 2, 1); + sg_set_buf(&sg[3], (void *)dbuf + 3, 1); + break; + } + + /* May allocate an indirect, so force it to allocate + * user addr */ + __kmalloc_fake = indirects + (xfers % RINGSIZE) * 4; + if (output) + err = virtqueue_add_buf(vq, sg, num_sg, 0, dbuf, + GFP_KERNEL); + else + err = virtqueue_add_buf(vq, sg, 0, num_sg, dbuf, + GFP_KERNEL); + + if (err == -ENOSPC) { + if (!virtqueue_enable_cb_delayed(vq)) + continue; + /* Swallow all notifies at once. */ + if (read(to_guest[0], buf, sizeof(buf)) < 1) + break; + + receives++; + virtqueue_disable_cb(vq); + continue; + } + + if (err) + errx(1, "virtqueue_add_buf: %i", err); + + xfers++; + virtqueue_kick(vq); + } + + /* Any extra? */ + while (finished != xfers) { + int *dbuf; + unsigned int len; + + /* Consume bufs. */ + dbuf = virtqueue_get_buf(vq, &len); + if (dbuf) { + if (len == 4) + assert(*dbuf == finished - 1); + else + assert(len == 0); + finished++; + continue; + } + + if (!virtqueue_enable_cb_delayed(vq)) + continue; + if (read(to_guest[0], buf, sizeof(buf)) < 1) + break; + + receives++; + virtqueue_disable_cb(vq); + } + + printf("Guest: notified %lu, pinged %lu\n", + gvdev.notifies, receives); + vring_del_virtqueue(vq); + return 0; + } +} + +int main(int argc, char *argv[]) +{ + struct virtio_device vdev; + struct virtqueue *vq; + struct vringh vrh; + struct scatterlist guest_sg[RINGSIZE]; + struct iovec host_riov[2], host_wiov[2]; + struct vringh_iov riov, wiov; + struct vring_used_elem used[RINGSIZE]; + char buf[28]; + u16 head; + int err; + unsigned i; + void *ret; + bool (*getrange)(struct vringh *vrh, u64 addr, struct vringh_range *r); + bool fast_vringh = false, parallel = false; + + getrange = getrange_iov; + vdev.features[0] = 0; + + while (argv[1]) { + if (strcmp(argv[1], "--indirect") == 0) + vdev.features[0] |= (1 << VIRTIO_RING_F_INDIRECT_DESC); + else if (strcmp(argv[1], "--eventidx") == 0) + vdev.features[0] |= (1 << VIRTIO_RING_F_EVENT_IDX); + else if (strcmp(argv[1], "--slow-range") == 0) + getrange = getrange_slow; + else if (strcmp(argv[1], "--fast-vringh") == 0) + fast_vringh = true; + else if (strcmp(argv[1], "--parallel") == 0) + parallel = true; + else + errx(1, "Unknown arg %s", argv[1]); + argv++; + } + + if (parallel) + return parallel_test(vdev.features[0], getrange, fast_vringh); + + if (posix_memalign(&__user_addr_min, PAGE_SIZE, USER_MEM) != 0) + abort(); + __user_addr_max = __user_addr_min + USER_MEM; + memset(__user_addr_min, 0, vring_size(RINGSIZE, ALIGN)); + + /* Set up guest side. */ + vq = vring_new_virtqueue(0, RINGSIZE, ALIGN, &vdev, true, + __user_addr_min, + never_notify_host, never_callback_guest, + "guest vq"); + + /* Set up host side. */ + vring_init(&vrh.vring, RINGSIZE, __user_addr_min, ALIGN); + vringh_init_user(&vrh, vdev.features[0], RINGSIZE, true, + vrh.vring.desc, vrh.vring.avail, vrh.vring.used); + + /* No descriptor to get yet... */ + err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange, &head); + if (err != 0) + errx(1, "vringh_getdesc_user: %i", err); + + /* Guest puts in a descriptor. */ + memcpy(__user_addr_max - 1, "a", 1); + sg_init_table(guest_sg, 1); + sg_set_buf(&guest_sg[0], __user_addr_max - 1, 1); + sg_init_table(guest_sg+1, 1); + sg_set_buf(&guest_sg[1], __user_addr_max - 3, 2); + + /* May allocate an indirect, so force it to allocate user addr */ + __kmalloc_fake = __user_addr_min + vring_size(RINGSIZE, ALIGN); + err = virtqueue_add_buf(vq, guest_sg, 1, 1, &err, GFP_KERNEL); + if (err) + errx(1, "virtqueue_add_buf: %i", err); + __kmalloc_fake = NULL; + + /* Host retreives it. */ + vringh_iov_init(&riov, host_riov, ARRAY_SIZE(host_riov)); + vringh_iov_init(&wiov, host_wiov, ARRAY_SIZE(host_wiov)); + + err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange, &head); + if (err != 1) + errx(1, "vringh_getdesc_user: %i", err); + + assert(riov.used == 1); + assert(riov.iov[0].iov_base == __user_addr_max - 1); + assert(riov.iov[0].iov_len == 1); + if (getrange != getrange_slow) { + assert(wiov.used == 1); + assert(wiov.iov[0].iov_base == __user_addr_max - 3); + assert(wiov.iov[0].iov_len == 2); + } else { + assert(wiov.used == 2); + assert(wiov.iov[0].iov_base == __user_addr_max - 3); + assert(wiov.iov[0].iov_len == 1); + assert(wiov.iov[1].iov_base == __user_addr_max - 2); + assert(wiov.iov[1].iov_len == 1); + } + + err = vringh_iov_pull_user(&riov, buf, 5); + if (err != 1) + errx(1, "vringh_iov_pull_user: %i", err); + assert(buf[0] == 'a'); + assert(riov.i == 1); + assert(vringh_iov_pull_user(&riov, buf, 5) == 0); + + memcpy(buf, "bcdef", 5); + err = vringh_iov_push_user(&wiov, buf, 5); + if (err != 2) + errx(1, "vringh_iov_push_user: %i", err); + assert(memcmp(__user_addr_max - 3, "bc", 2) == 0); + assert(wiov.i == wiov.used); + assert(vringh_iov_push_user(&wiov, buf, 5) == 0); + + /* Host is done. */ + err = vringh_complete_user(&vrh, head, err); + if (err != 0) + errx(1, "vringh_complete_user: %i", err); + + /* Guest should see used token now. */ + __kfree_ignore_start = __user_addr_min + vring_size(RINGSIZE, ALIGN); + __kfree_ignore_end = __kfree_ignore_start + 1; + ret = virtqueue_get_buf(vq, &i); + if (ret != &err) + errx(1, "virtqueue_get_buf: %p", ret); + assert(i == 2); + + /* Guest puts in a huge descriptor. */ + sg_init_table(guest_sg, RINGSIZE); + for (i = 0; i < RINGSIZE; i++) { + sg_set_buf(&guest_sg[i], + __user_addr_max - USER_MEM/4, USER_MEM/4); + } + + /* Fill contents with recognisable garbage. */ + for (i = 0; i < USER_MEM/4; i++) + ((char *)__user_addr_max - USER_MEM/4)[i] = i; + + /* This will allocate an indirect, so force it to allocate user addr */ + __kmalloc_fake = __user_addr_min + vring_size(RINGSIZE, ALIGN); + err = virtqueue_add_buf(vq, guest_sg, RINGSIZE, 0, &err, GFP_KERNEL); + if (err) + errx(1, "virtqueue_add_buf (large): %i", err); + __kmalloc_fake = NULL; + + /* Host picks it up (allocates new iov). */ + vringh_iov_init(&riov, host_riov, ARRAY_SIZE(host_riov)); + vringh_iov_init(&wiov, host_wiov, ARRAY_SIZE(host_wiov)); + + err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange, &head); + if (err != 1) + errx(1, "vringh_getdesc_user: %i", err); + + assert(riov.max_num & VRINGH_IOV_ALLOCATED); + assert(riov.iov != host_riov); + if (getrange != getrange_slow) + assert(riov.used == RINGSIZE); + else + assert(riov.used == RINGSIZE * USER_MEM/4); + + assert(!(wiov.max_num & VRINGH_IOV_ALLOCATED)); + assert(wiov.used == 0); + + /* Pull data back out (in odd chunks), should be as expected. */ + for (i = 0; i < RINGSIZE * USER_MEM/4; i += 3) { + err = vringh_iov_pull_user(&riov, buf, 3); + if (err != 3 && i + err != RINGSIZE * USER_MEM/4) + errx(1, "vringh_iov_pull_user large: %i", err); + assert(buf[0] == (char)i); + assert(err < 2 || buf[1] == (char)(i + 1)); + assert(err < 3 || buf[2] == (char)(i + 2)); + } + assert(riov.i == riov.used); + vringh_iov_cleanup(&riov); + vringh_iov_cleanup(&wiov); + + /* Complete using multi interface, just because we can. */ + used[0].id = head; + used[0].len = 0; + err = vringh_complete_multi_user(&vrh, used, 1); + if (err) + errx(1, "vringh_complete_multi_user(1): %i", err); + + /* Free up those descriptors. */ + ret = virtqueue_get_buf(vq, &i); + if (ret != &err) + errx(1, "virtqueue_get_buf: %p", ret); + + /* Add lots of descriptors. */ + sg_init_table(guest_sg, 1); + sg_set_buf(&guest_sg[0], __user_addr_max - 1, 1); + for (i = 0; i < RINGSIZE; i++) { + err = virtqueue_add_buf(vq, guest_sg, 1, 0, &err, GFP_KERNEL); + if (err) + errx(1, "virtqueue_add_buf (multiple): %i", err); + } + + /* Now get many, and consume them all at once. */ + vringh_iov_init(&riov, host_riov, ARRAY_SIZE(host_riov)); + vringh_iov_init(&wiov, host_wiov, ARRAY_SIZE(host_wiov)); + + for (i = 0; i < RINGSIZE; i++) { + err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange, &head); + if (err != 1) + errx(1, "vringh_getdesc_user: %i", err); + used[i].id = head; + used[i].len = 0; + } + /* Make sure it wraps around ring, to test! */ + assert(vrh.vring.used->idx % RINGSIZE != 0); + err = vringh_complete_multi_user(&vrh, used, RINGSIZE); + if (err) + errx(1, "vringh_complete_multi_user: %i", err); + + /* Free those buffers. */ + for (i = 0; i < RINGSIZE; i++) { + unsigned len; + assert(virtqueue_get_buf(vq, &len) != NULL); + } + + /* Test weird (but legal!) indirect. */ + if (vdev.features[0] & (1 << VIRTIO_RING_F_INDIRECT_DESC)) { + char *data = __user_addr_max - USER_MEM/4; + struct vring_desc *d = __user_addr_max - USER_MEM/2; + struct vring vring; + + /* Force creation of direct, which we modify. */ + vdev.features[0] &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC); + vq = vring_new_virtqueue(0, RINGSIZE, ALIGN, &vdev, true, + __user_addr_min, + never_notify_host, + never_callback_guest, + "guest vq"); + + sg_init_table(guest_sg, 4); + sg_set_buf(&guest_sg[0], d, sizeof(*d)*2); + sg_set_buf(&guest_sg[1], d + 2, sizeof(*d)*1); + sg_set_buf(&guest_sg[2], data + 6, 4); + sg_set_buf(&guest_sg[3], d + 3, sizeof(*d)*3); + + err = virtqueue_add_buf(vq, guest_sg, 4, 0, &err, GFP_KERNEL); + if (err) + errx(1, "virtqueue_add_buf (indirect): %i", err); + + vring_init(&vring, RINGSIZE, __user_addr_min, ALIGN); + + /* They're used in order, but double-check... */ + assert(vring.desc[0].addr == (unsigned long)d); + assert(vring.desc[1].addr == (unsigned long)(d+2)); + assert(vring.desc[2].addr == (unsigned long)data + 6); + assert(vring.desc[3].addr == (unsigned long)(d+3)); + vring.desc[0].flags |= VRING_DESC_F_INDIRECT; + vring.desc[1].flags |= VRING_DESC_F_INDIRECT; + vring.desc[3].flags |= VRING_DESC_F_INDIRECT; + + /* First indirect */ + d[0].addr = (unsigned long)data; + d[0].len = 1; + d[0].flags = VRING_DESC_F_NEXT; + d[0].next = 1; + d[1].addr = (unsigned long)data + 1; + d[1].len = 2; + d[1].flags = 0; + + /* Second indirect */ + d[2].addr = (unsigned long)data + 3; + d[2].len = 3; + d[2].flags = 0; + + /* Third indirect */ + d[3].addr = (unsigned long)data + 10; + d[3].len = 5; + d[3].flags = VRING_DESC_F_NEXT; + d[3].next = 1; + d[4].addr = (unsigned long)data + 15; + d[4].len = 6; + d[4].flags = VRING_DESC_F_NEXT; + d[4].next = 2; + d[5].addr = (unsigned long)data + 21; + d[5].len = 7; + d[5].flags = 0; + + /* Host picks it up (allocates new iov). */ + vringh_iov_init(&riov, host_riov, ARRAY_SIZE(host_riov)); + vringh_iov_init(&wiov, host_wiov, ARRAY_SIZE(host_wiov)); + + err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange, &head); + if (err != 1) + errx(1, "vringh_getdesc_user: %i", err); + + if (head != 0) + errx(1, "vringh_getdesc_user: head %i not 0", head); + + assert(riov.max_num & VRINGH_IOV_ALLOCATED); + if (getrange != getrange_slow) + assert(riov.used == 7); + else + assert(riov.used == 28); + err = vringh_iov_pull_user(&riov, buf, 29); + assert(err == 28); + + /* Data should be linear. */ + for (i = 0; i < err; i++) + assert(buf[i] == i); + vringh_iov_cleanup(&riov); + } + + /* Don't leak memory... */ + vring_del_virtqueue(vq); + free(__user_addr_min); + + return 0; +} -- cgit v1.2.3 From 13816c768d46586e925b22736992258d6105ad2c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Mar 2013 15:37:09 +1030 Subject: virtio_ring: virtqueue_add_sgs, to add multiple sgs. virtio_scsi can really use this, to avoid the current hack of copying the whole sg array. Some other things get slightly neater, too. This causes a slowdown in virtqueue_add_buf(), which is implemented as a wrapper. This is addressed in the next patches. for i in `seq 50`; do /usr/bin/time -f 'Wall time:%e' ./vringh_test --indirect --eventidx --parallel --fast-vringh; done 2>&1 | stats --trim-outliers: Before: Using CPUS 0 and 3 Guest: notified 0, pinged 39009-39063(39062) Host: notified 39009-39063(39062), pinged 0 Wall time:1.700000-1.950000(1.723542) After: Using CPUS 0 and 3 Guest: notified 0, pinged 39062-39063(39063) Host: notified 39062-39063(39063), pinged 0 Wall time:1.760000-2.220000(1.789167) Signed-off-by: Rusty Russell Reviewed-by: Wanlong Gao Reviewed-by: Asias He --- tools/virtio/linux/scatterlist.h | 16 ++++++++++++++++ tools/virtio/linux/virtio.h | 7 +++++++ 2 files changed, 23 insertions(+) (limited to 'tools') diff --git a/tools/virtio/linux/scatterlist.h b/tools/virtio/linux/scatterlist.h index b2cf7d0f6133..68c9e2adc996 100644 --- a/tools/virtio/linux/scatterlist.h +++ b/tools/virtio/linux/scatterlist.h @@ -125,6 +125,22 @@ static inline void sg_mark_end(struct scatterlist *sg) sg->page_link &= ~0x01; } +/** + * sg_unmark_end - Undo setting the end of the scatterlist + * @sg: SG entryScatterlist + * + * Description: + * Removes the termination marker from the given entry of the scatterlist. + * + **/ +static inline void sg_unmark_end(struct scatterlist *sg) +{ +#ifdef CONFIG_DEBUG_SG + BUG_ON(sg->sg_magic != SG_MAGIC); +#endif + sg->page_link &= ~0x02; +} + static inline struct scatterlist *sg_next(struct scatterlist *sg) { #ifdef CONFIG_DEBUG_SG diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h index e4af6591f5ff..5fa612ad932c 100644 --- a/tools/virtio/linux/virtio.h +++ b/tools/virtio/linux/virtio.h @@ -56,6 +56,13 @@ int virtqueue_add_buf(struct virtqueue *vq, void *data, gfp_t gfp); +int virtqueue_add_sgs(struct virtqueue *vq, + struct scatterlist *sgs[], + unsigned int out_sgs, + unsigned int in_sgs, + void *data, + gfp_t gfp); + void virtqueue_kick(struct virtqueue *vq); void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len); -- cgit v1.2.3 From e538ebaf78455ff87dec2c34d4f9c128844e3f3f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Mar 2013 15:44:26 +1030 Subject: tools/virtio: make vringh_test use inbuf/outbuf. As expected, the simplified accessors are faster. for i in `seq 50`; do /usr/bin/time -f 'Wall time:%e' ./vringh_test --indirect --eventidx --parallel --fast-vringh; done 2>&1 | stats --trim-outliers: Before: Using CPUS 0 and 3 Guest: notified 0, pinged 39062-39063(39063) Host: notified 39062-39063(39063), pinged 0 Wall time:1.760000-2.220000(1.789167) After: Using CPUS 0 and 3 Guest: notified 0, pinged 39037-39063(39062) Host: notified 39037-39063(39062), pinged 0 Wall time:1.640000-1.810000(1.676875) Signed-off-by: Rusty Russell --- tools/virtio/linux/virtio.h | 10 ++++++++++ tools/virtio/vringh_test.c | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h index 5fa612ad932c..6df181a6bcc6 100644 --- a/tools/virtio/linux/virtio.h +++ b/tools/virtio/linux/virtio.h @@ -63,6 +63,16 @@ int virtqueue_add_sgs(struct virtqueue *vq, void *data, gfp_t gfp); +int virtqueue_add_outbuf(struct virtqueue *vq, + struct scatterlist sg[], unsigned int num, + void *data, + gfp_t gfp); + +int virtqueue_add_inbuf(struct virtqueue *vq, + struct scatterlist sg[], unsigned int num, + void *data, + gfp_t gfp); + void virtqueue_kick(struct virtqueue *vq); void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len); diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c index 6a48ca5c101f..bb0bd9403e9e 100644 --- a/tools/virtio/vringh_test.c +++ b/tools/virtio/vringh_test.c @@ -369,11 +369,11 @@ static int parallel_test(unsigned long features, * user addr */ __kmalloc_fake = indirects + (xfers % RINGSIZE) * 4; if (output) - err = virtqueue_add_buf(vq, sg, num_sg, 0, dbuf, - GFP_KERNEL); + err = virtqueue_add_outbuf(vq, sg, num_sg, dbuf, + GFP_KERNEL); else - err = virtqueue_add_buf(vq, sg, 0, num_sg, dbuf, - GFP_KERNEL); + err = virtqueue_add_inbuf(vq, sg, num_sg, + dbuf, GFP_KERNEL); if (err == -ENOSPC) { if (!virtqueue_enable_cb_delayed(vq)) -- cgit v1.2.3 From cf994e0afae97382c0aa3cbc395805605d07a6e9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Mar 2013 15:44:30 +1030 Subject: tools/virtio: remove virtqueue_add_buf() from tests. Make the rest of the paths use virtqueue_add_sgs or add_outbuf. Signed-off-by: Rusty Russell --- tools/virtio/linux/virtio.h | 7 ------- tools/virtio/virtio_test.c | 6 +++--- tools/virtio/vringh_test.c | 22 ++++++++++++---------- 3 files changed, 15 insertions(+), 20 deletions(-) (limited to 'tools') diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h index 6df181a6bcc6..cd801838156f 100644 --- a/tools/virtio/linux/virtio.h +++ b/tools/virtio/linux/virtio.h @@ -49,13 +49,6 @@ struct virtqueue { const char *__MODULE_LICENSE_name = __MODULE_LICENSE_value /* Interfaces exported by virtio_ring. */ -int virtqueue_add_buf(struct virtqueue *vq, - struct scatterlist sg[], - unsigned int out_num, - unsigned int in_num, - void *data, - gfp_t gfp); - int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[], unsigned int out_sgs, diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c index 814ae803c878..da7a19558281 100644 --- a/tools/virtio/virtio_test.c +++ b/tools/virtio/virtio_test.c @@ -166,9 +166,9 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq, do { if (started < bufs) { sg_init_one(&sl, dev->buf, dev->buf_size); - r = virtqueue_add_buf(vq->vq, &sl, 1, 0, - dev->buf + started, - GFP_ATOMIC); + r = virtqueue_add_outbuf(vq->vq, &sl, 1, + dev->buf + started, + GFP_ATOMIC); if (likely(r == 0)) { ++started; virtqueue_kick(vq->vq); diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c index bb0bd9403e9e..d053ea40c001 100644 --- a/tools/virtio/vringh_test.c +++ b/tools/virtio/vringh_test.c @@ -388,7 +388,7 @@ static int parallel_test(unsigned long features, } if (err) - errx(1, "virtqueue_add_buf: %i", err); + errx(1, "virtqueue_add_in/outbuf: %i", err); xfers++; virtqueue_kick(vq); @@ -431,7 +431,7 @@ int main(int argc, char *argv[]) struct virtio_device vdev; struct virtqueue *vq; struct vringh vrh; - struct scatterlist guest_sg[RINGSIZE]; + struct scatterlist guest_sg[RINGSIZE], *sgs[2]; struct iovec host_riov[2], host_wiov[2]; struct vringh_iov riov, wiov; struct vring_used_elem used[RINGSIZE]; @@ -492,12 +492,14 @@ int main(int argc, char *argv[]) sg_set_buf(&guest_sg[0], __user_addr_max - 1, 1); sg_init_table(guest_sg+1, 1); sg_set_buf(&guest_sg[1], __user_addr_max - 3, 2); + sgs[0] = &guest_sg[0]; + sgs[1] = &guest_sg[1]; /* May allocate an indirect, so force it to allocate user addr */ __kmalloc_fake = __user_addr_min + vring_size(RINGSIZE, ALIGN); - err = virtqueue_add_buf(vq, guest_sg, 1, 1, &err, GFP_KERNEL); + err = virtqueue_add_sgs(vq, sgs, 1, 1, &err, GFP_KERNEL); if (err) - errx(1, "virtqueue_add_buf: %i", err); + errx(1, "virtqueue_add_sgs: %i", err); __kmalloc_fake = NULL; /* Host retreives it. */ @@ -564,9 +566,9 @@ int main(int argc, char *argv[]) /* This will allocate an indirect, so force it to allocate user addr */ __kmalloc_fake = __user_addr_min + vring_size(RINGSIZE, ALIGN); - err = virtqueue_add_buf(vq, guest_sg, RINGSIZE, 0, &err, GFP_KERNEL); + err = virtqueue_add_outbuf(vq, guest_sg, RINGSIZE, &err, GFP_KERNEL); if (err) - errx(1, "virtqueue_add_buf (large): %i", err); + errx(1, "virtqueue_add_outbuf (large): %i", err); __kmalloc_fake = NULL; /* Host picks it up (allocates new iov). */ @@ -616,9 +618,9 @@ int main(int argc, char *argv[]) sg_init_table(guest_sg, 1); sg_set_buf(&guest_sg[0], __user_addr_max - 1, 1); for (i = 0; i < RINGSIZE; i++) { - err = virtqueue_add_buf(vq, guest_sg, 1, 0, &err, GFP_KERNEL); + err = virtqueue_add_outbuf(vq, guest_sg, 1, &err, GFP_KERNEL); if (err) - errx(1, "virtqueue_add_buf (multiple): %i", err); + errx(1, "virtqueue_add_outbuf (multiple): %i", err); } /* Now get many, and consume them all at once. */ @@ -664,9 +666,9 @@ int main(int argc, char *argv[]) sg_set_buf(&guest_sg[2], data + 6, 4); sg_set_buf(&guest_sg[3], d + 3, sizeof(*d)*3); - err = virtqueue_add_buf(vq, guest_sg, 4, 0, &err, GFP_KERNEL); + err = virtqueue_add_outbuf(vq, guest_sg, 4, &err, GFP_KERNEL); if (err) - errx(1, "virtqueue_add_buf (indirect): %i", err); + errx(1, "virtqueue_add_outbuf (indirect): %i", err); vring_init(&vring, RINGSIZE, __user_addr_min, ALIGN); -- cgit v1.2.3 From 23a9072e3af0d9538e25837fb2b56bb94e4a8e67 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 19 Mar 2013 20:42:44 +0000 Subject: net: fix psock_fanout selftest hash collision Fix flaky results with PACKET_FANOUT_HASH depending on whether the two flows hash into the same packet socket or not. Also adds tests for PACKET_FANOUT_LB and PACKET_FANOUT_CPU and replaces the counting method with a packet ring. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- .../testing/selftests/net-afpacket/psock_fanout.c | 160 +++++++++++++++------ 1 file changed, 120 insertions(+), 40 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c index af9b0196b0be..f765f09931bd 100644 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -16,11 +16,11 @@ * The test currently runs for * - PACKET_FANOUT_HASH * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER + * - PACKET_FANOUT_LB + * - PACKET_FANOUT_CPU * - PACKET_FANOUT_ROLLOVER * * Todo: - * - datapath: PACKET_FANOUT_LB - * - datapath: PACKET_FANOUT_CPU * - functionality: PACKET_FANOUT_FLAG_DEFRAG * * License (GPLv2): @@ -39,18 +39,23 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ +#define _GNU_SOURCE /* for sched_setaffinity */ + #include #include +#include #include #include #include #include #include -#include +#include +#include #include #include #include #include +#include #include #include #include @@ -58,6 +63,8 @@ #define DATA_LEN 100 #define DATA_CHAR 'a' +#define RING_NUM_FRAMES 20 +#define PORT_BASE 8000 static void pair_udp_open(int fds[], uint16_t port) { @@ -162,37 +169,55 @@ static int sock_fanout_open(uint16_t typeflags, int num_packets) return -1; } - val = sizeof(struct iphdr) + sizeof(struct udphdr) + DATA_LEN; - val *= num_packets; - /* hack: apparently, the above calculation is too small (TODO: fix) */ - val *= 3; - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val))) { - perror("setsockopt SO_RCVBUF"); - exit(1); - } - sock_fanout_setfilter(fd); return fd; } -static void sock_fanout_read(int fds[], const int expect[]) +static char *sock_fanout_open_ring(int fd) { - struct tpacket_stats stats; - socklen_t ssize; - int ret[2]; + struct tpacket_req req = { + .tp_block_size = getpagesize(), + .tp_frame_size = getpagesize(), + .tp_block_nr = RING_NUM_FRAMES, + .tp_frame_nr = RING_NUM_FRAMES, + }; + char *ring; - ssize = sizeof(stats); - if (getsockopt(fds[0], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { - perror("getsockopt statistics 0"); + if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, + sizeof(req))) { + perror("packetsock ring setsockopt"); exit(1); } - ret[0] = stats.tp_packets - stats.tp_drops; - ssize = sizeof(stats); - if (getsockopt(fds[1], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { - perror("getsockopt statistics 1"); + + ring = mmap(0, req.tp_block_size * req.tp_block_nr, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (!ring) { + fprintf(stderr, "packetsock ring mmap\n"); exit(1); } - ret[1] = stats.tp_packets - stats.tp_drops; + + return ring; +} + +static int sock_fanout_read_ring(int fd, void *ring) +{ + struct tpacket_hdr *header = ring; + int count = 0; + + while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { + count++; + header = ring + (count * getpagesize()); + } + + return count; +} + +static int sock_fanout_read(int fds[], char *rings[], const int expect[]) +{ + int ret[2]; + + ret[0] = sock_fanout_read_ring(fds[0], rings[0]); + ret[1] = sock_fanout_read_ring(fds[1], rings[1]); fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", ret[0], ret[1], expect[0], expect[1]); @@ -200,8 +225,10 @@ static void sock_fanout_read(int fds[], const int expect[]) if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && (!(ret[0] == expect[1] && ret[1] == expect[0]))) { fprintf(stderr, "ERROR: incorrect queue lengths\n"); - exit(1); + return 1; } + + return 0; } /* Test illegal mode + flag combination */ @@ -253,11 +280,12 @@ static void test_control_group(void) } } -static void test_datapath(uint16_t typeflags, - const int expect1[], const int expect2[]) +static int test_datapath(uint16_t typeflags, int port_off, + const int expect1[], const int expect2[]) { const int expect0[] = { 0, 0 }; - int fds[2], fds_udp[2][2]; + char *rings[2]; + int fds[2], fds_udp[2][2], ret; fprintf(stderr, "test: datapath 0x%hx\n", typeflags); @@ -267,41 +295,93 @@ static void test_datapath(uint16_t typeflags, fprintf(stderr, "ERROR: failed open\n"); exit(1); } - pair_udp_open(fds_udp[0], 8000); - pair_udp_open(fds_udp[1], 8002); - sock_fanout_read(fds, expect0); + rings[0] = sock_fanout_open_ring(fds[0]); + rings[1] = sock_fanout_open_ring(fds[1]); + pair_udp_open(fds_udp[0], PORT_BASE); + pair_udp_open(fds_udp[1], PORT_BASE + port_off); + sock_fanout_read(fds, rings, expect0); /* Send data, but not enough to overflow a queue */ pair_udp_send(fds_udp[0], 15); pair_udp_send(fds_udp[1], 5); - sock_fanout_read(fds, expect1); + ret = sock_fanout_read(fds, rings, expect1); /* Send more data, overflow the queue */ pair_udp_send(fds_udp[0], 15); /* TODO: ensure consistent order between expect1 and expect2 */ - sock_fanout_read(fds, expect2); + ret |= sock_fanout_read(fds, rings, expect2); + if (munmap(rings[1], RING_NUM_FRAMES * getpagesize()) || + munmap(rings[0], RING_NUM_FRAMES * getpagesize())) { + fprintf(stderr, "close rings\n"); + exit(1); + } if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || close(fds_udp[0][1]) || close(fds_udp[0][0]) || close(fds[1]) || close(fds[0])) { fprintf(stderr, "close datapath\n"); exit(1); } + + return ret; +} + +static int set_cpuaffinity(int cpuid) +{ + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(cpuid, &mask); + if (sched_setaffinity(0, sizeof(mask), &mask)) { + if (errno != EINVAL) { + fprintf(stderr, "setaffinity %d\n", cpuid); + exit(1); + } + return 1; + } + + return 0; } int main(int argc, char **argv) { - const int expect_hash[2][2] = { { 15, 5 }, { 5, 0 } }; - const int expect_hash_rb[2][2] = { { 15, 5 }, { 5, 10 } }; - const int expect_rb[2][2] = { { 20, 0 }, { 0, 15 } }; + const int expect_hash[2][2] = { { 15, 5 }, { 20, 5 } }; + const int expect_hash_rb[2][2] = { { 15, 5 }, { 20, 15 } }; + const int expect_lb[2][2] = { { 10, 10 }, { 18, 17 } }; + const int expect_rb[2][2] = { { 20, 0 }, { 20, 15 } }; + const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } }; + const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; + int port_off = 2, tries = 5, ret; test_control_single(); test_control_group(); - test_datapath(PACKET_FANOUT_HASH, expect_hash[0], expect_hash[1]); - test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, - expect_hash_rb[0], expect_hash_rb[1]); - test_datapath(PACKET_FANOUT_ROLLOVER, expect_rb[0], expect_rb[1]); + /* find a set of ports that do not collide onto the same socket */ + ret = test_datapath(PACKET_FANOUT_HASH, port_off, + expect_hash[0], expect_hash[1]); + while (ret && tries--) { + fprintf(stderr, "info: trying alternate ports (%d)\n", tries); + ret = test_datapath(PACKET_FANOUT_HASH, ++port_off, + expect_hash[0], expect_hash[1]); + } + + ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, + port_off, expect_hash_rb[0], expect_hash_rb[1]); + ret |= test_datapath(PACKET_FANOUT_LB, + port_off, expect_lb[0], expect_lb[1]); + ret |= test_datapath(PACKET_FANOUT_ROLLOVER, + port_off, expect_rb[0], expect_rb[1]); + + set_cpuaffinity(0); + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu0[0], expect_cpu0[1]); + if (!set_cpuaffinity(1)) + /* TODO: test that choice alternates with previous */ + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu1[0], expect_cpu1[1]); + + if (ret) + return 1; printf("OK. All tests passed\n"); return 0; -- cgit v1.2.3 From 4c1d8d0617a39c8325a7c2fd80ac14bf40fd8cc6 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Wed, 20 Mar 2013 19:28:56 +0200 Subject: net: fix psock_fanout selftest bind error message Signed-off-by: Daniel Baluta Signed-off-by: David S. Miller --- tools/testing/selftests/net-afpacket/psock_fanout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c index f765f09931bd..226e5e33105a 100644 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -97,7 +97,7 @@ static void pair_udp_open(int fds[], uint16_t port) exit(1); } if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { - perror("bind"); + perror("connect"); exit(1); } } -- cgit v1.2.3 From a6f68034de8a5784dfeabb337506254c80b4c8c6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 20 Mar 2013 15:07:56 -0400 Subject: net: Move selftests to common net/ subdirectory. Suggested-by: Daniel Baluta Signed-off-by: David S. Miller --- tools/testing/selftests/Makefile | 3 +- tools/testing/selftests/net-afpacket/Makefile | 18 - .../testing/selftests/net-afpacket/psock_fanout.c | 388 --------------------- .../selftests/net-afpacket/run_afpackettests | 16 - tools/testing/selftests/net-socket/Makefile | 16 - .../testing/selftests/net-socket/run_netsocktests | 12 - tools/testing/selftests/net-socket/socket.c | 92 ----- tools/testing/selftests/net/Makefile | 19 + tools/testing/selftests/net/psock_fanout.c | 388 +++++++++++++++++++++ tools/testing/selftests/net/run_afpackettests | 16 + tools/testing/selftests/net/run_netsocktests | 12 + tools/testing/selftests/net/socket.c | 92 +++++ 12 files changed, 528 insertions(+), 544 deletions(-) delete mode 100644 tools/testing/selftests/net-afpacket/Makefile delete mode 100644 tools/testing/selftests/net-afpacket/psock_fanout.c delete mode 100644 tools/testing/selftests/net-afpacket/run_afpackettests delete mode 100644 tools/testing/selftests/net-socket/Makefile delete mode 100644 tools/testing/selftests/net-socket/run_netsocktests delete mode 100644 tools/testing/selftests/net-socket/socket.c create mode 100644 tools/testing/selftests/net/Makefile create mode 100644 tools/testing/selftests/net/psock_fanout.c create mode 100644 tools/testing/selftests/net/run_afpackettests create mode 100644 tools/testing/selftests/net/run_netsocktests create mode 100644 tools/testing/selftests/net/socket.c (limited to 'tools') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 7f50078d0e8c..a4805932972b 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -5,8 +5,7 @@ TARGETS += vm TARGETS += cpu-hotplug TARGETS += memory-hotplug TARGETS += efivarfs -TARGETS += net-socket -TARGETS += net-afpacket +TARGETS += net all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/net-afpacket/Makefile b/tools/testing/selftests/net-afpacket/Makefile deleted file mode 100644 index 45f2ffb7fda7..000000000000 --- a/tools/testing/selftests/net-afpacket/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# Makefile for net-socket selftests - -CC = $(CROSS_COMPILE)gcc -CFLAGS = -Wall - -CFLAGS += -I../../../../usr/include/ - -AF_PACKET_PROGS = psock_fanout - -all: $(AF_PACKET_PROGS) -%: %.c - $(CC) $(CFLAGS) -o $@ $^ - -run_tests: all - @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" - -clean: - $(RM) $(AF_PACKET_PROGS) diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c deleted file mode 100644 index 226e5e33105a..000000000000 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * Author: Willem de Bruijn (willemb@google.com) - * - * A basic test of packet socket fanout behavior. - * - * Control: - * - create fanout fails as expected with illegal flag combinations - * - join fanout fails as expected with diverging types or flags - * - * Datapath: - * Open a pair of packet sockets and a pair of INET sockets, send a known - * number of packets across the two INET sockets and count the number of - * packets enqueued onto the two packet sockets. - * - * The test currently runs for - * - PACKET_FANOUT_HASH - * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER - * - PACKET_FANOUT_LB - * - PACKET_FANOUT_CPU - * - PACKET_FANOUT_ROLLOVER - * - * Todo: - * - functionality: PACKET_FANOUT_FLAG_DEFRAG - * - * License (GPLv2): - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE /* for sched_setaffinity */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DATA_LEN 100 -#define DATA_CHAR 'a' -#define RING_NUM_FRAMES 20 -#define PORT_BASE 8000 - -static void pair_udp_open(int fds[], uint16_t port) -{ - struct sockaddr_in saddr, daddr; - - fds[0] = socket(PF_INET, SOCK_DGRAM, 0); - fds[1] = socket(PF_INET, SOCK_DGRAM, 0); - if (fds[0] == -1 || fds[1] == -1) { - fprintf(stderr, "ERROR: socket dgram\n"); - exit(1); - } - - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(port); - saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - memset(&daddr, 0, sizeof(daddr)); - daddr.sin_family = AF_INET; - daddr.sin_port = htons(port + 1); - daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - /* must bind both to get consistent hash result */ - if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { - perror("bind"); - exit(1); - } - if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { - perror("bind"); - exit(1); - } - if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { - perror("connect"); - exit(1); - } -} - -static void pair_udp_send(int fds[], int num) -{ - char buf[DATA_LEN], rbuf[DATA_LEN]; - - memset(buf, DATA_CHAR, sizeof(buf)); - while (num--) { - /* Should really handle EINTR and EAGAIN */ - if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { - fprintf(stderr, "ERROR: send failed left=%d\n", num); - exit(1); - } - if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { - fprintf(stderr, "ERROR: recv failed left=%d\n", num); - exit(1); - } - if (memcmp(buf, rbuf, sizeof(buf))) { - fprintf(stderr, "ERROR: data failed left=%d\n", num); - exit(1); - } - } -} - -static void sock_fanout_setfilter(int fd) -{ - struct sock_filter bpf_filter[] = { - { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ - { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ - { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ - { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ - { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ - { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ - { 0x6, 0, 0, 0x00000060 }, /* RET match */ -/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ - }; - struct sock_fprog bpf_prog; - - bpf_prog.filter = bpf_filter; - bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, - sizeof(bpf_prog))) { - perror("setsockopt SO_ATTACH_FILTER"); - exit(1); - } -} - -/* Open a socket in a given fanout mode. - * @return -1 if mode is bad, a valid socket otherwise */ -static int sock_fanout_open(uint16_t typeflags, int num_packets) -{ - int fd, val; - - fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); - if (fd < 0) { - perror("socket packet"); - exit(1); - } - - /* fanout group ID is always 0: tests whether old groups are deleted */ - val = ((int) typeflags) << 16; - if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { - if (close(fd)) { - perror("close packet"); - exit(1); - } - return -1; - } - - sock_fanout_setfilter(fd); - return fd; -} - -static char *sock_fanout_open_ring(int fd) -{ - struct tpacket_req req = { - .tp_block_size = getpagesize(), - .tp_frame_size = getpagesize(), - .tp_block_nr = RING_NUM_FRAMES, - .tp_frame_nr = RING_NUM_FRAMES, - }; - char *ring; - - if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, - sizeof(req))) { - perror("packetsock ring setsockopt"); - exit(1); - } - - ring = mmap(0, req.tp_block_size * req.tp_block_nr, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (!ring) { - fprintf(stderr, "packetsock ring mmap\n"); - exit(1); - } - - return ring; -} - -static int sock_fanout_read_ring(int fd, void *ring) -{ - struct tpacket_hdr *header = ring; - int count = 0; - - while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { - count++; - header = ring + (count * getpagesize()); - } - - return count; -} - -static int sock_fanout_read(int fds[], char *rings[], const int expect[]) -{ - int ret[2]; - - ret[0] = sock_fanout_read_ring(fds[0], rings[0]); - ret[1] = sock_fanout_read_ring(fds[1], rings[1]); - - fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", - ret[0], ret[1], expect[0], expect[1]); - - if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && - (!(ret[0] == expect[1] && ret[1] == expect[0]))) { - fprintf(stderr, "ERROR: incorrect queue lengths\n"); - return 1; - } - - return 0; -} - -/* Test illegal mode + flag combination */ -static void test_control_single(void) -{ - fprintf(stderr, "test: control single socket\n"); - - if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | - PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { - fprintf(stderr, "ERROR: opened socket with dual rollover\n"); - exit(1); - } -} - -/* Test illegal group with different modes or flags */ -static void test_control_group(void) -{ - int fds[2]; - - fprintf(stderr, "test: control multiple sockets\n"); - - fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20); - if (fds[0] == -1) { - fprintf(stderr, "ERROR: failed to open HASH socket\n"); - exit(1); - } - if (sock_fanout_open(PACKET_FANOUT_HASH | - PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) { - fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); - exit(1); - } - if (sock_fanout_open(PACKET_FANOUT_HASH | - PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) { - fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); - exit(1); - } - if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) { - fprintf(stderr, "ERROR: joined group with wrong mode\n"); - exit(1); - } - fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20); - if (fds[1] == -1) { - fprintf(stderr, "ERROR: failed to join group\n"); - exit(1); - } - if (close(fds[1]) || close(fds[0])) { - fprintf(stderr, "ERROR: closing sockets\n"); - exit(1); - } -} - -static int test_datapath(uint16_t typeflags, int port_off, - const int expect1[], const int expect2[]) -{ - const int expect0[] = { 0, 0 }; - char *rings[2]; - int fds[2], fds_udp[2][2], ret; - - fprintf(stderr, "test: datapath 0x%hx\n", typeflags); - - fds[0] = sock_fanout_open(typeflags, 20); - fds[1] = sock_fanout_open(typeflags, 20); - if (fds[0] == -1 || fds[1] == -1) { - fprintf(stderr, "ERROR: failed open\n"); - exit(1); - } - rings[0] = sock_fanout_open_ring(fds[0]); - rings[1] = sock_fanout_open_ring(fds[1]); - pair_udp_open(fds_udp[0], PORT_BASE); - pair_udp_open(fds_udp[1], PORT_BASE + port_off); - sock_fanout_read(fds, rings, expect0); - - /* Send data, but not enough to overflow a queue */ - pair_udp_send(fds_udp[0], 15); - pair_udp_send(fds_udp[1], 5); - ret = sock_fanout_read(fds, rings, expect1); - - /* Send more data, overflow the queue */ - pair_udp_send(fds_udp[0], 15); - /* TODO: ensure consistent order between expect1 and expect2 */ - ret |= sock_fanout_read(fds, rings, expect2); - - if (munmap(rings[1], RING_NUM_FRAMES * getpagesize()) || - munmap(rings[0], RING_NUM_FRAMES * getpagesize())) { - fprintf(stderr, "close rings\n"); - exit(1); - } - if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || - close(fds_udp[0][1]) || close(fds_udp[0][0]) || - close(fds[1]) || close(fds[0])) { - fprintf(stderr, "close datapath\n"); - exit(1); - } - - return ret; -} - -static int set_cpuaffinity(int cpuid) -{ - cpu_set_t mask; - - CPU_ZERO(&mask); - CPU_SET(cpuid, &mask); - if (sched_setaffinity(0, sizeof(mask), &mask)) { - if (errno != EINVAL) { - fprintf(stderr, "setaffinity %d\n", cpuid); - exit(1); - } - return 1; - } - - return 0; -} - -int main(int argc, char **argv) -{ - const int expect_hash[2][2] = { { 15, 5 }, { 20, 5 } }; - const int expect_hash_rb[2][2] = { { 15, 5 }, { 20, 15 } }; - const int expect_lb[2][2] = { { 10, 10 }, { 18, 17 } }; - const int expect_rb[2][2] = { { 20, 0 }, { 20, 15 } }; - const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } }; - const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; - int port_off = 2, tries = 5, ret; - - test_control_single(); - test_control_group(); - - /* find a set of ports that do not collide onto the same socket */ - ret = test_datapath(PACKET_FANOUT_HASH, port_off, - expect_hash[0], expect_hash[1]); - while (ret && tries--) { - fprintf(stderr, "info: trying alternate ports (%d)\n", tries); - ret = test_datapath(PACKET_FANOUT_HASH, ++port_off, - expect_hash[0], expect_hash[1]); - } - - ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, - port_off, expect_hash_rb[0], expect_hash_rb[1]); - ret |= test_datapath(PACKET_FANOUT_LB, - port_off, expect_lb[0], expect_lb[1]); - ret |= test_datapath(PACKET_FANOUT_ROLLOVER, - port_off, expect_rb[0], expect_rb[1]); - - set_cpuaffinity(0); - ret |= test_datapath(PACKET_FANOUT_CPU, port_off, - expect_cpu0[0], expect_cpu0[1]); - if (!set_cpuaffinity(1)) - /* TODO: test that choice alternates with previous */ - ret |= test_datapath(PACKET_FANOUT_CPU, port_off, - expect_cpu1[0], expect_cpu1[1]); - - if (ret) - return 1; - - printf("OK. All tests passed\n"); - return 0; -} diff --git a/tools/testing/selftests/net-afpacket/run_afpackettests b/tools/testing/selftests/net-afpacket/run_afpackettests deleted file mode 100644 index 7907824c6355..000000000000 --- a/tools/testing/selftests/net-afpacket/run_afpackettests +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -if [ $(id -u) != 0 ]; then - echo $msg must be run as root >&2 - exit 0 -fi - -echo "--------------------" -echo "running psock_fanout test" -echo "--------------------" -./psock_fanout -if [ $? -ne 0 ]; then - echo "[FAIL]" -else - echo "[PASS]" -fi diff --git a/tools/testing/selftests/net-socket/Makefile b/tools/testing/selftests/net-socket/Makefile deleted file mode 100644 index 2450fd8bb10a..000000000000 --- a/tools/testing/selftests/net-socket/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# Makefile for net-socket selftests - -CC = $(CROSS_COMPILE)gcc -CFLAGS = -Wall - -NET_SOCK_PROGS = socket - -all: $(NET_SOCK_PROGS) -%: %.c - $(CC) $(CFLAGS) -o $@ $^ - -run_tests: all - @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]" - -clean: - $(RM) $(NET_SOCK_PROGS) diff --git a/tools/testing/selftests/net-socket/run_netsocktests b/tools/testing/selftests/net-socket/run_netsocktests deleted file mode 100644 index c09a682df56a..000000000000 --- a/tools/testing/selftests/net-socket/run_netsocktests +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -echo "--------------------" -echo "running socket test" -echo "--------------------" -./socket -if [ $? -ne 0 ]; then - echo "[FAIL]" -else - echo "[PASS]" -fi - diff --git a/tools/testing/selftests/net-socket/socket.c b/tools/testing/selftests/net-socket/socket.c deleted file mode 100644 index 0f227f2f9be9..000000000000 --- a/tools/testing/selftests/net-socket/socket.c +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -struct socket_testcase { - int domain; - int type; - int protocol; - - /* 0 = valid file descriptor - * -foo = error foo - */ - int expect; - - /* If non-zero, accept EAFNOSUPPORT to handle the case - * of the protocol not being configured into the kernel. - */ - int nosupport_ok; -}; - -static struct socket_testcase tests[] = { - { AF_MAX, 0, 0, -EAFNOSUPPORT, 0 }, - { AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 1 }, - { AF_INET, SOCK_DGRAM, IPPROTO_TCP, -EPROTONOSUPPORT, 1 }, - { AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, 1 }, - { AF_INET, SOCK_STREAM, IPPROTO_UDP, -EPROTONOSUPPORT, 1 }, -}; - -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -#define ERR_STRING_SZ 64 - -static int run_tests(void) -{ - char err_string1[ERR_STRING_SZ]; - char err_string2[ERR_STRING_SZ]; - int i, err; - - err = 0; - for (i = 0; i < ARRAY_SIZE(tests); i++) { - struct socket_testcase *s = &tests[i]; - int fd; - - fd = socket(s->domain, s->type, s->protocol); - if (fd < 0) { - if (s->nosupport_ok && - errno == EAFNOSUPPORT) - continue; - - if (s->expect < 0 && - errno == -s->expect) - continue; - - strerror_r(-s->expect, err_string1, ERR_STRING_SZ); - strerror_r(errno, err_string2, ERR_STRING_SZ); - - fprintf(stderr, "socket(%d, %d, %d) expected " - "err (%s) got (%s)\n", - s->domain, s->type, s->protocol, - err_string1, err_string2); - - err = -1; - break; - } else { - close(fd); - - if (s->expect < 0) { - strerror_r(errno, err_string1, ERR_STRING_SZ); - - fprintf(stderr, "socket(%d, %d, %d) expected " - "success got err (%s)\n", - s->domain, s->type, s->protocol, - err_string1); - - err = -1; - break; - } - } - } - - return err; -} - -int main(void) -{ - int err = run_tests(); - - return err; -} diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile new file mode 100644 index 000000000000..bd6e272bab87 --- /dev/null +++ b/tools/testing/selftests/net/Makefile @@ -0,0 +1,19 @@ +# Makefile for net selftests + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +CFLAGS += -I../../../../usr/include/ + +NET_PROGS = socket psock_fanout + +all: $(NET_PROGS) +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +run_tests: all + @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]" + @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" + +clean: + $(RM) $(NET_PROGS) diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c new file mode 100644 index 000000000000..226e5e33105a --- /dev/null +++ b/tools/testing/selftests/net/psock_fanout.c @@ -0,0 +1,388 @@ +/* + * Copyright 2013 Google Inc. + * Author: Willem de Bruijn (willemb@google.com) + * + * A basic test of packet socket fanout behavior. + * + * Control: + * - create fanout fails as expected with illegal flag combinations + * - join fanout fails as expected with diverging types or flags + * + * Datapath: + * Open a pair of packet sockets and a pair of INET sockets, send a known + * number of packets across the two INET sockets and count the number of + * packets enqueued onto the two packet sockets. + * + * The test currently runs for + * - PACKET_FANOUT_HASH + * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER + * - PACKET_FANOUT_LB + * - PACKET_FANOUT_CPU + * - PACKET_FANOUT_ROLLOVER + * + * Todo: + * - functionality: PACKET_FANOUT_FLAG_DEFRAG + * + * License (GPLv2): + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE /* for sched_setaffinity */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DATA_LEN 100 +#define DATA_CHAR 'a' +#define RING_NUM_FRAMES 20 +#define PORT_BASE 8000 + +static void pair_udp_open(int fds[], uint16_t port) +{ + struct sockaddr_in saddr, daddr; + + fds[0] = socket(PF_INET, SOCK_DGRAM, 0); + fds[1] = socket(PF_INET, SOCK_DGRAM, 0); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: socket dgram\n"); + exit(1); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&daddr, 0, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_port = htons(port + 1); + daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* must bind both to get consistent hash result */ + if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } + if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { + perror("bind"); + exit(1); + } + if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { + perror("connect"); + exit(1); + } +} + +static void pair_udp_send(int fds[], int num) +{ + char buf[DATA_LEN], rbuf[DATA_LEN]; + + memset(buf, DATA_CHAR, sizeof(buf)); + while (num--) { + /* Should really handle EINTR and EAGAIN */ + if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { + fprintf(stderr, "ERROR: send failed left=%d\n", num); + exit(1); + } + if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { + fprintf(stderr, "ERROR: recv failed left=%d\n", num); + exit(1); + } + if (memcmp(buf, rbuf, sizeof(buf))) { + fprintf(stderr, "ERROR: data failed left=%d\n", num); + exit(1); + } + } +} + +static void sock_fanout_setfilter(int fd) +{ + struct sock_filter bpf_filter[] = { + { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ + { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ + { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ + { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x6, 0, 0, 0x00000060 }, /* RET match */ +/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ + }; + struct sock_fprog bpf_prog; + + bpf_prog.filter = bpf_filter; + bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, + sizeof(bpf_prog))) { + perror("setsockopt SO_ATTACH_FILTER"); + exit(1); + } +} + +/* Open a socket in a given fanout mode. + * @return -1 if mode is bad, a valid socket otherwise */ +static int sock_fanout_open(uint16_t typeflags, int num_packets) +{ + int fd, val; + + fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + if (fd < 0) { + perror("socket packet"); + exit(1); + } + + /* fanout group ID is always 0: tests whether old groups are deleted */ + val = ((int) typeflags) << 16; + if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { + if (close(fd)) { + perror("close packet"); + exit(1); + } + return -1; + } + + sock_fanout_setfilter(fd); + return fd; +} + +static char *sock_fanout_open_ring(int fd) +{ + struct tpacket_req req = { + .tp_block_size = getpagesize(), + .tp_frame_size = getpagesize(), + .tp_block_nr = RING_NUM_FRAMES, + .tp_frame_nr = RING_NUM_FRAMES, + }; + char *ring; + + if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, + sizeof(req))) { + perror("packetsock ring setsockopt"); + exit(1); + } + + ring = mmap(0, req.tp_block_size * req.tp_block_nr, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (!ring) { + fprintf(stderr, "packetsock ring mmap\n"); + exit(1); + } + + return ring; +} + +static int sock_fanout_read_ring(int fd, void *ring) +{ + struct tpacket_hdr *header = ring; + int count = 0; + + while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { + count++; + header = ring + (count * getpagesize()); + } + + return count; +} + +static int sock_fanout_read(int fds[], char *rings[], const int expect[]) +{ + int ret[2]; + + ret[0] = sock_fanout_read_ring(fds[0], rings[0]); + ret[1] = sock_fanout_read_ring(fds[1], rings[1]); + + fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", + ret[0], ret[1], expect[0], expect[1]); + + if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && + (!(ret[0] == expect[1] && ret[1] == expect[0]))) { + fprintf(stderr, "ERROR: incorrect queue lengths\n"); + return 1; + } + + return 0; +} + +/* Test illegal mode + flag combination */ +static void test_control_single(void) +{ + fprintf(stderr, "test: control single socket\n"); + + if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | + PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { + fprintf(stderr, "ERROR: opened socket with dual rollover\n"); + exit(1); + } +} + +/* Test illegal group with different modes or flags */ +static void test_control_group(void) +{ + int fds[2]; + + fprintf(stderr, "test: control multiple sockets\n"); + + fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[0] == -1) { + fprintf(stderr, "ERROR: failed to open HASH socket\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong mode\n"); + exit(1); + } + fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[1] == -1) { + fprintf(stderr, "ERROR: failed to join group\n"); + exit(1); + } + if (close(fds[1]) || close(fds[0])) { + fprintf(stderr, "ERROR: closing sockets\n"); + exit(1); + } +} + +static int test_datapath(uint16_t typeflags, int port_off, + const int expect1[], const int expect2[]) +{ + const int expect0[] = { 0, 0 }; + char *rings[2]; + int fds[2], fds_udp[2][2], ret; + + fprintf(stderr, "test: datapath 0x%hx\n", typeflags); + + fds[0] = sock_fanout_open(typeflags, 20); + fds[1] = sock_fanout_open(typeflags, 20); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: failed open\n"); + exit(1); + } + rings[0] = sock_fanout_open_ring(fds[0]); + rings[1] = sock_fanout_open_ring(fds[1]); + pair_udp_open(fds_udp[0], PORT_BASE); + pair_udp_open(fds_udp[1], PORT_BASE + port_off); + sock_fanout_read(fds, rings, expect0); + + /* Send data, but not enough to overflow a queue */ + pair_udp_send(fds_udp[0], 15); + pair_udp_send(fds_udp[1], 5); + ret = sock_fanout_read(fds, rings, expect1); + + /* Send more data, overflow the queue */ + pair_udp_send(fds_udp[0], 15); + /* TODO: ensure consistent order between expect1 and expect2 */ + ret |= sock_fanout_read(fds, rings, expect2); + + if (munmap(rings[1], RING_NUM_FRAMES * getpagesize()) || + munmap(rings[0], RING_NUM_FRAMES * getpagesize())) { + fprintf(stderr, "close rings\n"); + exit(1); + } + if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || + close(fds_udp[0][1]) || close(fds_udp[0][0]) || + close(fds[1]) || close(fds[0])) { + fprintf(stderr, "close datapath\n"); + exit(1); + } + + return ret; +} + +static int set_cpuaffinity(int cpuid) +{ + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(cpuid, &mask); + if (sched_setaffinity(0, sizeof(mask), &mask)) { + if (errno != EINVAL) { + fprintf(stderr, "setaffinity %d\n", cpuid); + exit(1); + } + return 1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + const int expect_hash[2][2] = { { 15, 5 }, { 20, 5 } }; + const int expect_hash_rb[2][2] = { { 15, 5 }, { 20, 15 } }; + const int expect_lb[2][2] = { { 10, 10 }, { 18, 17 } }; + const int expect_rb[2][2] = { { 20, 0 }, { 20, 15 } }; + const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } }; + const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; + int port_off = 2, tries = 5, ret; + + test_control_single(); + test_control_group(); + + /* find a set of ports that do not collide onto the same socket */ + ret = test_datapath(PACKET_FANOUT_HASH, port_off, + expect_hash[0], expect_hash[1]); + while (ret && tries--) { + fprintf(stderr, "info: trying alternate ports (%d)\n", tries); + ret = test_datapath(PACKET_FANOUT_HASH, ++port_off, + expect_hash[0], expect_hash[1]); + } + + ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, + port_off, expect_hash_rb[0], expect_hash_rb[1]); + ret |= test_datapath(PACKET_FANOUT_LB, + port_off, expect_lb[0], expect_lb[1]); + ret |= test_datapath(PACKET_FANOUT_ROLLOVER, + port_off, expect_rb[0], expect_rb[1]); + + set_cpuaffinity(0); + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu0[0], expect_cpu0[1]); + if (!set_cpuaffinity(1)) + /* TODO: test that choice alternates with previous */ + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu1[0], expect_cpu1[1]); + + if (ret) + return 1; + + printf("OK. All tests passed\n"); + return 0; +} diff --git a/tools/testing/selftests/net/run_afpackettests b/tools/testing/selftests/net/run_afpackettests new file mode 100644 index 000000000000..7907824c6355 --- /dev/null +++ b/tools/testing/selftests/net/run_afpackettests @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ $(id -u) != 0 ]; then + echo $msg must be run as root >&2 + exit 0 +fi + +echo "--------------------" +echo "running psock_fanout test" +echo "--------------------" +./psock_fanout +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi diff --git a/tools/testing/selftests/net/run_netsocktests b/tools/testing/selftests/net/run_netsocktests new file mode 100644 index 000000000000..c09a682df56a --- /dev/null +++ b/tools/testing/selftests/net/run_netsocktests @@ -0,0 +1,12 @@ +#!/bin/bash + +echo "--------------------" +echo "running socket test" +echo "--------------------" +./socket +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi + diff --git a/tools/testing/selftests/net/socket.c b/tools/testing/selftests/net/socket.c new file mode 100644 index 000000000000..0f227f2f9be9 --- /dev/null +++ b/tools/testing/selftests/net/socket.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include + +struct socket_testcase { + int domain; + int type; + int protocol; + + /* 0 = valid file descriptor + * -foo = error foo + */ + int expect; + + /* If non-zero, accept EAFNOSUPPORT to handle the case + * of the protocol not being configured into the kernel. + */ + int nosupport_ok; +}; + +static struct socket_testcase tests[] = { + { AF_MAX, 0, 0, -EAFNOSUPPORT, 0 }, + { AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_TCP, -EPROTONOSUPPORT, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, 1 }, + { AF_INET, SOCK_STREAM, IPPROTO_UDP, -EPROTONOSUPPORT, 1 }, +}; + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define ERR_STRING_SZ 64 + +static int run_tests(void) +{ + char err_string1[ERR_STRING_SZ]; + char err_string2[ERR_STRING_SZ]; + int i, err; + + err = 0; + for (i = 0; i < ARRAY_SIZE(tests); i++) { + struct socket_testcase *s = &tests[i]; + int fd; + + fd = socket(s->domain, s->type, s->protocol); + if (fd < 0) { + if (s->nosupport_ok && + errno == EAFNOSUPPORT) + continue; + + if (s->expect < 0 && + errno == -s->expect) + continue; + + strerror_r(-s->expect, err_string1, ERR_STRING_SZ); + strerror_r(errno, err_string2, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "err (%s) got (%s)\n", + s->domain, s->type, s->protocol, + err_string1, err_string2); + + err = -1; + break; + } else { + close(fd); + + if (s->expect < 0) { + strerror_r(errno, err_string1, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "success got err (%s)\n", + s->domain, s->type, s->protocol, + err_string1); + + err = -1; + break; + } + } + } + + return err; +} + +int main(void) +{ + int err = run_tests(); + + return err; +} -- cgit v1.2.3 From e306e2c13b8c214618af0c61acf62a6e42d486de Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 20 Mar 2013 12:11:47 +0000 Subject: filter: add minimal BPF JIT image disassembler This is a minimal stand-alone user space helper, that allows for debugging or verification of emitted BPF JIT images. This is in particular useful for emitted opcode debugging, since minor bugs in the JIT compiler can be fatal. The disassembler is architecture generic and uses libopcodes and libbfd. How to get to the disassembly, example: 1) `echo 2 > /proc/sys/net/core/bpf_jit_enable` 2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`) 3) Run e.g. `bpf_jit_disasm -o` to disassemble the most recent JIT code output `bpf_jit_disasm -o` will display the related opcodes to a particular instruction as well. Example for x86_64: $ ./bpf_jit_disasm 94 bytes emitted from JIT compiler (pass:3, flen:9) ffffffffa0356000 + : 0: push %rbp 1: mov %rsp,%rbp 4: sub $0x60,%rsp 8: mov %rbx,-0x8(%rbp) c: mov 0x68(%rdi),%r9d 10: sub 0x6c(%rdi),%r9d 14: mov 0xe0(%rdi),%r8 1b: mov $0xc,%esi 20: callq 0xffffffffe0d01b71 25: cmp $0x86dd,%eax 2a: jne 0x000000000000003d 2c: mov $0x14,%esi 31: callq 0xffffffffe0d01b8d 36: cmp $0x6,%eax [...] 5c: leaveq 5d: retq $ ./bpf_jit_disasm -o 94 bytes emitted from JIT compiler (pass:3, flen:9) ffffffffa0356000 + : 0: push %rbp 55 1: mov %rsp,%rbp 48 89 e5 4: sub $0x60,%rsp 48 83 ec 60 8: mov %rbx,-0x8(%rbp) 48 89 5d f8 c: mov 0x68(%rdi),%r9d 44 8b 4f 68 10: sub 0x6c(%rdi),%r9d 44 2b 4f 6c [...] 5c: leaveq c9 5d: retq c3 Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/Makefile | 11 +-- tools/net/Makefile | 15 ++++ tools/net/bpf_jit_disasm.c | 199 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 tools/net/Makefile create mode 100644 tools/net/bpf_jit_disasm.c (limited to 'tools') diff --git a/tools/Makefile b/tools/Makefile index fa36565b209d..c73c6357481c 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -12,6 +12,7 @@ help: @echo ' turbostat - Intel CPU idle stats and freq reporting tool' @echo ' usb - USB testing tools' @echo ' virtio - vhost test module' + @echo ' net - misc networking tools' @echo ' vm - misc vm tools' @echo ' x86_energy_perf_policy - Intel energy policy tool' @echo '' @@ -34,7 +35,7 @@ help: cpupower: FORCE $(call descend,power/$@) -cgroup firewire lguest perf usb virtio vm: FORCE +cgroup firewire lguest perf usb virtio vm net: FORCE $(call descend,$@) selftests: FORCE @@ -46,7 +47,7 @@ turbostat x86_energy_perf_policy: FORCE cpupower_install: $(call descend,power/$(@:_install=),install) -cgroup_install firewire_install lguest_install perf_install usb_install virtio_install vm_install: +cgroup_install firewire_install lguest_install perf_install usb_install virtio_install vm_install net_install: $(call descend,$(@:_install=),install) selftests_install: @@ -57,12 +58,12 @@ turbostat_install x86_energy_perf_policy_install: install: cgroup_install cpupower_install firewire_install lguest_install \ perf_install selftests_install turbostat_install usb_install \ - virtio_install vm_install x86_energy_perf_policy_install + virtio_install vm_install net_install x86_energy_perf_policy_install cpupower_clean: $(call descend,power/cpupower,clean) -cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean: +cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean net_clean: $(call descend,$(@:_clean=),clean) selftests_clean: @@ -73,6 +74,6 @@ turbostat_clean x86_energy_perf_policy_clean: clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \ selftests_clean turbostat_clean usb_clean virtio_clean \ - vm_clean x86_energy_perf_policy_clean + vm_clean net_clean x86_energy_perf_policy_clean .PHONY: FORCE diff --git a/tools/net/Makefile b/tools/net/Makefile new file mode 100644 index 000000000000..b4444d53b73f --- /dev/null +++ b/tools/net/Makefile @@ -0,0 +1,15 @@ +prefix = /usr + +CC = gcc + +all : bpf_jit_disasm + +bpf_jit_disasm : CFLAGS = -Wall -O2 +bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl +bpf_jit_disasm : bpf_jit_disasm.o + +clean : + rm -rf *.o bpf_jit_disasm + +install : + install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm diff --git a/tools/net/bpf_jit_disasm.c b/tools/net/bpf_jit_disasm.c new file mode 100644 index 000000000000..cfe0cdcda3de --- /dev/null +++ b/tools/net/bpf_jit_disasm.c @@ -0,0 +1,199 @@ +/* + * Minimal BPF JIT image disassembler + * + * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for + * debugging or verification purposes. + * + * To get the disassembly of the JIT code, do the following: + * + * 1) `echo 2 > /proc/sys/net/core/bpf_jit_enable` + * 2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`) + * 3) Run e.g. `bpf_jit_disasm -o` to read out the last JIT code + * + * Copyright 2013 Daniel Borkmann + * Licensed under the GNU General Public License, version 2.0 (GPLv2) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void get_exec_path(char *tpath, size_t size) +{ + char *path; + ssize_t len; + + snprintf(tpath, size, "/proc/%d/exe", (int) getpid()); + tpath[size - 1] = 0; + + path = strdup(tpath); + assert(path); + + len = readlink(path, tpath, size); + tpath[len] = 0; + + free(path); +} + +static void get_asm_insns(uint8_t *image, size_t len, unsigned long base, + int opcodes) +{ + int count, i, pc = 0; + char tpath[256]; + struct disassemble_info info; + disassembler_ftype disassemble; + bfd *bfdf; + + memset(tpath, 0, sizeof(tpath)); + get_exec_path(tpath, sizeof(tpath)); + + bfdf = bfd_openr(tpath, NULL); + assert(bfdf); + assert(bfd_check_format(bfdf, bfd_object)); + + init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf); + info.arch = bfd_get_arch(bfdf); + info.mach = bfd_get_mach(bfdf); + info.buffer = image; + info.buffer_length = len; + + disassemble_init_for_target(&info); + + disassemble = disassembler(bfdf); + assert(disassemble); + + do { + printf("%4x:\t", pc); + + count = disassemble(pc, &info); + + if (opcodes) { + printf("\n\t"); + for (i = 0; i < count; ++i) + printf("%02x ", (uint8_t) image[pc + i]); + } + printf("\n"); + + pc += count; + } while(count > 0 && pc < len); + + bfd_close(bfdf); +} + +static char *get_klog_buff(int *klen) +{ + int ret, len = klogctl(10, NULL, 0); + char *buff = malloc(len); + + assert(buff && klen); + ret = klogctl(3, buff, len); + assert(ret >= 0); + *klen = ret; + + return buff; +} + +static void put_klog_buff(char *buff) +{ + free(buff); +} + +static int get_last_jit_image(char *haystack, size_t hlen, + uint8_t *image, size_t ilen, + unsigned long *base) +{ + char *ptr, *pptr, *tmp; + off_t off = 0; + int ret, flen, proglen, pass, ulen = 0; + regmatch_t pmatch[1]; + regex_t regex; + + if (hlen == 0) + return 0; + + ret = regcomp(®ex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ " + "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED); + assert(ret == 0); + + ptr = haystack; + while (1) { + ret = regexec(®ex, ptr, 1, pmatch, 0); + if (ret == 0) { + ptr += pmatch[0].rm_eo; + off += pmatch[0].rm_eo; + assert(off < hlen); + } else + break; + } + + ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so); + ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx", + &flen, &proglen, &pass, base); + if (ret != 4) + return 0; + + tmp = ptr = haystack + off; + while ((ptr = strtok(tmp, "\n")) != NULL && ulen < ilen) { + tmp = NULL; + if (!strstr(ptr, "JIT code")) + continue; + pptr = ptr; + while ((ptr = strstr(pptr, ":"))) + pptr = ptr + 1; + ptr = pptr; + do { + image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16); + if (ptr == pptr || ulen >= ilen) { + ulen--; + break; + } + ptr = pptr; + } while (1); + } + + assert(ulen == proglen); + printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n", + proglen, pass, flen); + printf("%lx + :\n", *base); + + regfree(®ex); + return ulen; +} + +int main(int argc, char **argv) +{ + int len, klen, opcodes = 0; + char *kbuff; + unsigned long base; + uint8_t image[4096]; + + if (argc > 1) { + if (!strncmp("-o", argv[argc - 1], 2)) { + opcodes = 1; + } else { + printf("usage: bpf_jit_disasm [-o: show opcodes]\n"); + exit(0); + } + } + + bfd_init(); + memset(image, 0, sizeof(image)); + + kbuff = get_klog_buff(&klen); + + len = get_last_jit_image(kbuff, klen, image, sizeof(image), &base); + if (len > 0 && base > 0) + get_asm_insns(image, len, base, opcodes); + + put_klog_buff(kbuff); + + return 0; +} -- cgit v1.2.3 From 62baca8aed636eb10f9274761aa1dcbfd48a7caa Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 19 Mar 2013 18:46:16 +0900 Subject: perf tools: Get rid of redundant _FILE_OFFSET_BITS definition We define it in the Makefile so no need to duplicate it. Signed-off-by: Namhyung Kim Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1363686376-29525-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 2 -- tools/perf/util/header.c | 2 -- tools/perf/util/session.c | 2 -- tools/perf/util/trace-event-read.c | 2 -- tools/perf/util/util.h | 2 -- 5 files changed, 10 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 9f2344a2c506..78a41fdbe56c 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -5,8 +5,6 @@ * (or a CPU, or a PID) into the perf.data output file - for * later analysis via perf report. */ -#define _FILE_OFFSET_BITS 64 - #include "builtin.h" #include "perf.h" diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index a9b7349f7c5f..79e48c726938 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1,5 +1,3 @@ -#define _FILE_OFFSET_BITS 64 - #include "util.h" #include #include diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index ab265c2cfab3..c8ba120b0dbe 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1,5 +1,3 @@ -#define _FILE_OFFSET_BITS 64 - #include #include diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 7cb24635adf2..8c8181aa286a 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -18,8 +18,6 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -#define _FILE_OFFSET_BITS 64 - #include #include #include diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 6a0781c3a573..a45710b70a55 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -1,8 +1,6 @@ #ifndef GIT_COMPAT_UTIL_H #define GIT_COMPAT_UTIL_H -#define _FILE_OFFSET_BITS 64 - #ifndef FLEX_ARRAY /* * See if our compiler is known to support flexible array members. -- cgit v1.2.3 From 454f8c7d26fa7e1545df4efca5d9ba929ccef1e8 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 21 Mar 2013 16:18:44 +0900 Subject: perf tools: Let get_tracing_file() return NULL to indicate failure. So that it can be used by other places. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363850332-25297-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-info.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 5729f434c5b1..81c673282ed4 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -62,7 +62,7 @@ static const char *find_debugfs(void) const char *path = perf_debugfs_mount(NULL); if (!path) - die("Your kernel not support debugfs filesystem"); + pr_debug("Your kernel does not support the debugfs filesystem"); return path; } @@ -81,8 +81,12 @@ static const char *find_tracing_dir(void) return tracing; debugfs = find_debugfs(); + if (!debugfs) + return NULL; - tracing = malloc_or_die(strlen(debugfs) + 9); + tracing = malloc(strlen(debugfs) + 9); + if (!tracing) + return NULL; sprintf(tracing, "%s/tracing", debugfs); @@ -99,7 +103,9 @@ static char *get_tracing_file(const char *name) if (!tracing) return NULL; - file = malloc_or_die(strlen(tracing) + strlen(name) + 2); + file = malloc(strlen(tracing) + strlen(name) + 2); + if (!file) + return NULL; sprintf(file, "%s/%s", tracing, name); return file; @@ -170,6 +176,9 @@ static void read_header_files(void) struct stat st; path = get_tracing_file("events/header_page"); + if (!path) + die("can't get tracing/events/header_page"); + if (stat(path, &st) < 0) die("can't read '%s'", path); @@ -178,6 +187,9 @@ static void read_header_files(void) put_tracing_file(path); path = get_tracing_file("events/header_event"); + if (!path) + die("can't get tracing/events/header_event"); + if (stat(path, &st) < 0) die("can't read '%s'", path); @@ -251,6 +263,8 @@ static void read_ftrace_files(struct tracepoint_path *tps) char *path; path = get_tracing_file("events/ftrace"); + if (!path) + die("can't get tracing/events/ftrace"); copy_event_system(path, tps); @@ -279,6 +293,8 @@ static void read_event_files(struct tracepoint_path *tps) int ret; path = get_tracing_file("events"); + if (!path) + die("can't get tracing/events"); dir = opendir(path); if (!dir) @@ -343,6 +359,9 @@ static void read_ftrace_printk(void) int ret; path = get_tracing_file("printk_formats"); + if (!path) + die("can't get tracing/printk_formats"); + ret = stat(path, &st); if (ret < 0) { /* not found */ -- cgit v1.2.3 From 5a6fd27ad73fef0ed39a00236acbc3a17834672a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 21 Mar 2013 16:18:45 +0900 Subject: perf tools: Get rid of malloc_or_die() in trace-event-info.c Check return value of malloc and fail if NULL. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363850332-25297-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-info.c | 48 ++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 18 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 81c673282ed4..91db6e8e4493 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -47,16 +47,6 @@ static const char *output_file = "trace.info"; static int output_fd; -static void *malloc_or_die(unsigned int size) -{ - void *data; - - data = malloc(size); - if (!data) - die("malloc"); - return data; -} - static const char *find_debugfs(void) { const char *path = perf_debugfs_mount(NULL); @@ -209,7 +199,7 @@ static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) return false; } -static void copy_event_system(const char *sys, struct tracepoint_path *tps) +static int copy_event_system(const char *sys, struct tracepoint_path *tps) { struct dirent *dent; struct stat st; @@ -217,6 +207,7 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) DIR *dir; int count = 0; int ret; + int err; dir = opendir(sys); if (!dir) @@ -228,7 +219,11 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) strcmp(dent->d_name, "..") == 0 || !name_in_tp_list(dent->d_name, tps)) continue; - format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); + format = malloc(strlen(sys) + strlen(dent->d_name) + 10); + if (!format) { + err = -ENOMEM; + goto out; + } sprintf(format, "%s/%s/format", sys, dent->d_name); ret = stat(format, &st); free(format); @@ -246,16 +241,22 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) strcmp(dent->d_name, "..") == 0 || !name_in_tp_list(dent->d_name, tps)) continue; - format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); + format = malloc(strlen(sys) + strlen(dent->d_name) + 10); + if (!format) { + err = -ENOMEM; + goto out; + } sprintf(format, "%s/%s/format", sys, dent->d_name); ret = stat(format, &st); if (ret >= 0) record_file(format, 8); - free(format); } + err = 0; +out: closedir(dir); + return err; } static void read_ftrace_files(struct tracepoint_path *tps) @@ -282,7 +283,7 @@ static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) return false; } -static void read_event_files(struct tracepoint_path *tps) +static int read_event_files(struct tracepoint_path *tps) { struct dirent *dent; struct stat st; @@ -291,6 +292,7 @@ static void read_event_files(struct tracepoint_path *tps) DIR *dir; int count = 0; int ret; + int err; path = get_tracing_file("events"); if (!path) @@ -320,7 +322,11 @@ static void read_event_files(struct tracepoint_path *tps) strcmp(dent->d_name, "ftrace") == 0 || !system_in_tp_list(dent->d_name, tps)) continue; - sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); + sys = malloc(strlen(path) + strlen(dent->d_name) + 2); + if (!sys) { + err = -ENOMEM; + goto out; + } sprintf(sys, "%s/%s", path, dent->d_name); ret = stat(sys, &st); if (ret >= 0) { @@ -329,9 +335,12 @@ static void read_event_files(struct tracepoint_path *tps) } free(sys); } - + err = 0; +out: closedir(dir); put_tracing_file(path); + + return err; } static void read_proc_kallsyms(void) @@ -463,7 +472,10 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, if (!tps) return NULL; - tdata = malloc_or_die(sizeof(*tdata)); + tdata = malloc(sizeof(*tdata)); + if (!tdata) + return NULL; + tdata->temp = temp; tdata->size = 0; -- cgit v1.2.3 From 8755d5e202c3ef62e33d75426c2f0005e3f70ca9 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 21 Mar 2013 16:18:46 +0900 Subject: perf tools: Get rid of write_or_die() from trace-event-info.c Check return value of write and fail if error. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363850332-25297-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-info.c | 180 ++++++++++++++++++++++++++----------- tools/perf/util/trace-event.h | 2 +- 2 files changed, 127 insertions(+), 55 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 91db6e8e4493..090e80d743db 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -106,17 +106,6 @@ static void put_tracing_file(char *file) free(file); } -static ssize_t write_or_die(const void *buf, size_t len) -{ - int ret; - - ret = write(output_fd, buf, len); - if (ret < 0) - die("writing to '%s'", output_file); - - return ret; -} - int bigendian(void) { unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0}; @@ -127,29 +116,32 @@ int bigendian(void) } /* unfortunately, you can not stat debugfs or proc files for size */ -static void record_file(const char *file, size_t hdr_sz) +static int record_file(const char *file, ssize_t hdr_sz) { unsigned long long size = 0; char buf[BUFSIZ], *sizep; off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR); int r, fd; + int err = -EIO; fd = open(file, O_RDONLY); if (fd < 0) die("Can't read '%s'", file); /* put in zeros for file size, then fill true size later */ - if (hdr_sz) - write_or_die(&size, hdr_sz); + if (hdr_sz) { + if (write(output_fd, &size, hdr_sz) != hdr_sz) + goto out; + } do { r = read(fd, buf, BUFSIZ); if (r > 0) { size += r; - write_or_die(buf, r); + if (write(output_fd, buf, r) != r) + goto out; } } while (r > 0); - close(fd); /* ugh, handle big-endian hdr_size == 4 */ sizep = (char*)&size; @@ -158,12 +150,18 @@ static void record_file(const char *file, size_t hdr_sz) if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) die("writing to %s", output_file); + + err = 0; +out: + close(fd); + return err; } -static void read_header_files(void) +static int read_header_files(void) { char *path; struct stat st; + int err = -EIO; path = get_tracing_file("events/header_page"); if (!path) @@ -172,8 +170,16 @@ static void read_header_files(void) if (stat(path, &st) < 0) die("can't read '%s'", path); - write_or_die("header_page", 12); - record_file(path, 8); + if (write(output_fd, "header_page", 12) != 12) { + pr_debug("can't write header_page\n"); + goto out; + } + + if (record_file(path, 8) < 0) { + pr_debug("can't record header_page file\n"); + goto out; + } + put_tracing_file(path); path = get_tracing_file("events/header_event"); @@ -183,9 +189,20 @@ static void read_header_files(void) if (stat(path, &st) < 0) die("can't read '%s'", path); - write_or_die("header_event", 13); - record_file(path, 8); + if (write(output_fd, "header_event", 13) != 13) { + pr_debug("can't write header_event\n"); + goto out; + } + + if (record_file(path, 8) < 0) { + pr_debug("can't record header_event file\n"); + goto out; + } + + err = 0; +out: put_tracing_file(path); + return err; } static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) @@ -232,7 +249,11 @@ static int copy_event_system(const char *sys, struct tracepoint_path *tps) count++; } - write_or_die(&count, 4); + if (write(output_fd, &count, 4) != 4) { + err = -EIO; + pr_debug("can't write count\n"); + goto out; + } rewinddir(dir); while ((dent = readdir(dir))) { @@ -249,8 +270,13 @@ static int copy_event_system(const char *sys, struct tracepoint_path *tps) sprintf(format, "%s/%s/format", sys, dent->d_name); ret = stat(format, &st); - if (ret >= 0) - record_file(format, 8); + if (ret >= 0) { + err = record_file(format, 8); + if (err) { + free(format); + goto out; + } + } free(format); } err = 0; @@ -259,17 +285,20 @@ out: return err; } -static void read_ftrace_files(struct tracepoint_path *tps) +static int read_ftrace_files(struct tracepoint_path *tps) { char *path; + int ret; path = get_tracing_file("events/ftrace"); if (!path) die("can't get tracing/events/ftrace"); - copy_event_system(path, tps); + ret = copy_event_system(path, tps); put_tracing_file(path); + + return ret; } static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) @@ -312,7 +341,11 @@ static int read_event_files(struct tracepoint_path *tps) count++; } - write_or_die(&count, 4); + if (write(output_fd, &count, 4) != 4) { + err = -EIO; + pr_debug("can't write count\n"); + goto out; + } rewinddir(dir); while ((dent = readdir(dir))) { @@ -330,8 +363,14 @@ static int read_event_files(struct tracepoint_path *tps) sprintf(sys, "%s/%s", path, dent->d_name); ret = stat(sys, &st); if (ret >= 0) { - write_or_die(dent->d_name, strlen(dent->d_name) + 1); - copy_event_system(sys, tps); + ssize_t size = strlen(dent->d_name) + 1; + + if (write(output_fd, dent->d_name, size) != size || + copy_event_system(sys, tps) < 0) { + err = -EIO; + free(sys); + goto out; + } } free(sys); } @@ -343,29 +382,30 @@ out: return err; } -static void read_proc_kallsyms(void) +static int read_proc_kallsyms(void) { unsigned int size; const char *path = "/proc/kallsyms"; struct stat st; - int ret; + int ret, err = 0; ret = stat(path, &st); if (ret < 0) { /* not found */ size = 0; - write_or_die(&size, 4); - return; + if (write(output_fd, &size, 4) != 4) + err = -EIO; + return err; } - record_file(path, 4); + return record_file(path, 4); } -static void read_ftrace_printk(void) +static int read_ftrace_printk(void) { unsigned int size; char *path; struct stat st; - int ret; + int ret, err = 0; path = get_tracing_file("printk_formats"); if (!path) @@ -375,13 +415,15 @@ static void read_ftrace_printk(void) if (ret < 0) { /* not found */ size = 0; - write_or_die(&size, 4); + if (write(output_fd, &size, 4) != 4) + err = -EIO; goto out; } - record_file(path, 4); + err = record_file(path, 4); out: put_tracing_file(path); + return err; } static struct tracepoint_path * @@ -428,9 +470,10 @@ bool have_tracepoints(struct list_head *pattrs) return false; } -static void tracing_data_header(void) +static int tracing_data_header(void) { char buf[20]; + ssize_t size; /* just guessing this is someone's birthday.. ;) */ buf[0] = 23; @@ -438,9 +481,12 @@ static void tracing_data_header(void) buf[2] = 68; memcpy(buf + 3, "tracing", 7); - write_or_die(buf, 10); + if (write(output_fd, buf, 10) != 10) + return -1; - write_or_die(VERSION, strlen(VERSION) + 1); + size = strlen(VERSION) + 1; + if (write(output_fd, VERSION, size) != size) + return -1; /* save endian */ if (bigendian()) @@ -450,14 +496,19 @@ static void tracing_data_header(void) read_trace_init(buf[0], buf[0]); - write_or_die(buf, 1); + if (write(output_fd, buf, 1) != 1) + return -1; /* save size of long */ buf[0] = sizeof(long); - write_or_die(buf, 1); + if (write(output_fd, buf, 1) != 1) + return -1; /* save page_size */ - write_or_die(&page_size, 4); + if (write(output_fd, &page_size, 4) != 4) + return -1; + + return 0; } struct tracing_data *tracing_data_get(struct list_head *pattrs, @@ -465,6 +516,7 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, { struct tracepoint_path *tps; struct tracing_data *tdata; + int err; output_fd = fd; @@ -498,13 +550,24 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, output_fd = temp_fd; } - tracing_data_header(); - read_header_files(); - read_ftrace_files(tps); - read_event_files(tps); - read_proc_kallsyms(); - read_ftrace_printk(); + err = tracing_data_header(); + if (err) + goto out; + err = read_header_files(); + if (err) + goto out; + err = read_ftrace_files(tps); + if (err) + goto out; + err = read_event_files(tps); + if (err) + goto out; + err = read_proc_kallsyms(); + if (err) + goto out; + err = read_ftrace_printk(); +out: /* * All tracing data are stored by now, we can restore * the default output file in case we used temp file. @@ -515,22 +578,31 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, output_fd = fd; } + if (err) { + free(tdata); + tdata = NULL; + } + put_tracepoints_path(tps); return tdata; } -void tracing_data_put(struct tracing_data *tdata) +int tracing_data_put(struct tracing_data *tdata) { + int err = 0; + if (tdata->temp) { - record_file(tdata->temp_file, 0); + err = record_file(tdata->temp_file, 0); unlink(tdata->temp_file); } free(tdata); + return err; } int read_tracing_data(int fd, struct list_head *pattrs) { + int err; struct tracing_data *tdata; /* @@ -541,6 +613,6 @@ int read_tracing_data(int fd, struct list_head *pattrs) if (!tdata) return -ENOMEM; - tracing_data_put(tdata); - return 0; + err = tracing_data_put(tdata); + return err; } diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 28ccde8ba20f..1978c398ad87 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -68,7 +68,7 @@ struct tracing_data { struct tracing_data *tracing_data_get(struct list_head *pattrs, int fd, bool temp); -void tracing_data_put(struct tracing_data *tdata); +int tracing_data_put(struct tracing_data *tdata); struct addr_location; -- cgit v1.2.3 From 7f42b9505aee3fa9cb465a670989e3d426a1f3f2 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 21 Mar 2013 16:18:47 +0900 Subject: perf tools: Get rid of die() calls from trace-event-info.c Now remove all remaining die() calls and convert them to check return value and propagate it. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363850332-25297-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-info.c | 114 +++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 42 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 090e80d743db..3917eb9a8479 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -43,7 +43,6 @@ #define VERSION "0.5" -static const char *output_file = "trace.info"; static int output_fd; @@ -125,8 +124,10 @@ static int record_file(const char *file, ssize_t hdr_sz) int err = -EIO; fd = open(file, O_RDONLY); - if (fd < 0) - die("Can't read '%s'", file); + if (fd < 0) { + pr_debug("Can't read '%s'", file); + return -errno; + } /* put in zeros for file size, then fill true size later */ if (hdr_sz) { @@ -148,8 +149,10 @@ static int record_file(const char *file, ssize_t hdr_sz) if (bigendian()) sizep += sizeof(u64) - hdr_sz; - if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) - die("writing to %s", output_file); + if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) { + pr_debug("writing file size failed\n"); + goto out; + } err = 0; out: @@ -164,11 +167,15 @@ static int read_header_files(void) int err = -EIO; path = get_tracing_file("events/header_page"); - if (!path) - die("can't get tracing/events/header_page"); + if (!path) { + pr_debug("can't get tracing/events/header_page"); + return -ENOMEM; + } - if (stat(path, &st) < 0) - die("can't read '%s'", path); + if (stat(path, &st) < 0) { + pr_debug("can't read '%s'", path); + goto out; + } if (write(output_fd, "header_page", 12) != 12) { pr_debug("can't write header_page\n"); @@ -183,11 +190,16 @@ static int read_header_files(void) put_tracing_file(path); path = get_tracing_file("events/header_event"); - if (!path) - die("can't get tracing/events/header_event"); + if (!path) { + pr_debug("can't get tracing/events/header_event"); + err = -ENOMEM; + goto out; + } - if (stat(path, &st) < 0) - die("can't read '%s'", path); + if (stat(path, &st) < 0) { + pr_debug("can't read '%s'", path); + goto out; + } if (write(output_fd, "header_event", 13) != 13) { pr_debug("can't write header_event\n"); @@ -227,8 +239,10 @@ static int copy_event_system(const char *sys, struct tracepoint_path *tps) int err; dir = opendir(sys); - if (!dir) - die("can't read directory '%s'", sys); + if (!dir) { + pr_debug("can't read directory '%s'", sys); + return -errno; + } while ((dent = readdir(dir))) { if (dent->d_type != DT_DIR || @@ -291,8 +305,10 @@ static int read_ftrace_files(struct tracepoint_path *tps) int ret; path = get_tracing_file("events/ftrace"); - if (!path) - die("can't get tracing/events/ftrace"); + if (!path) { + pr_debug("can't get tracing/events/ftrace"); + return -ENOMEM; + } ret = copy_event_system(path, tps); @@ -324,12 +340,17 @@ static int read_event_files(struct tracepoint_path *tps) int err; path = get_tracing_file("events"); - if (!path) - die("can't get tracing/events"); + if (!path) { + pr_debug("can't get tracing/events"); + return -ENOMEM; + } dir = opendir(path); - if (!dir) - die("can't read directory '%s'", path); + if (!dir) { + err = -errno; + pr_debug("can't read directory '%s'", path); + goto out; + } while ((dent = readdir(dir))) { if (dent->d_type != DT_DIR || @@ -408,8 +429,10 @@ static int read_ftrace_printk(void) int ret, err = 0; path = get_tracing_file("printk_formats"); - if (!path) - die("can't get tracing/printk_formats"); + if (!path) { + pr_debug("can't get tracing/printk_formats"); + return -ENOMEM; + } ret = stat(path, &st); if (ret < 0) { @@ -426,6 +449,19 @@ out: return err; } +static void +put_tracepoints_path(struct tracepoint_path *tps) +{ + while (tps) { + struct tracepoint_path *t = tps; + + tps = tps->next; + free(t->name); + free(t->system); + free(t); + } +} + static struct tracepoint_path * get_tracepoints_path(struct list_head *pattrs) { @@ -438,27 +474,17 @@ get_tracepoints_path(struct list_head *pattrs) continue; ++nr_tracepoints; ppath->next = tracepoint_id_to_path(pos->attr.config); - if (!ppath->next) - die("%s\n", "No memory to alloc tracepoints list"); + if (!ppath->next) { + pr_debug("No memory to alloc tracepoints list\n"); + put_tracepoints_path(&path); + return NULL; + } ppath = ppath->next; } return nr_tracepoints > 0 ? path.next : NULL; } -static void -put_tracepoints_path(struct tracepoint_path *tps) -{ - while (tps) { - struct tracepoint_path *t = tps; - - tps = tps->next; - free(t->name); - free(t->system); - free(t); - } -} - bool have_tracepoints(struct list_head *pattrs) { struct perf_evsel *pos; @@ -536,12 +562,16 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, snprintf(tdata->temp_file, sizeof(tdata->temp_file), "/tmp/perf-XXXXXX"); - if (!mkstemp(tdata->temp_file)) - die("Can't make temp file"); + if (!mkstemp(tdata->temp_file)) { + pr_debug("Can't make temp file"); + return NULL; + } temp_fd = open(tdata->temp_file, O_RDWR); - if (temp_fd < 0) - die("Can't read '%s'", tdata->temp_file); + if (temp_fd < 0) { + pr_debug("Can't read '%s'", tdata->temp_file); + return NULL; + } /* * Set the temp file the default output, so all the -- cgit v1.2.3 From 3dce2ce3cc40ece2562a5a83e879b4bfb451476c Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 21 Mar 2013 16:18:48 +0900 Subject: perf tools: Handle failure case in trace_report() If pevent allocation in read_trace_init() fails, trace_report() will return -1 and *ppevent is set to NULL. Its callers should check this case and handle it properly. This is also a preparation for the removal of *die() calls. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363850332-25297-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 9 +++++++-- tools/perf/util/trace-event-read.c | 41 ++++++++++++++++++++++---------------- 2 files changed, 31 insertions(+), 19 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 79e48c726938..326068a593a5 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1670,8 +1670,8 @@ static int process_tracing_data(struct perf_file_section *section __maybe_unused struct perf_header *ph __maybe_unused, int fd, void *data) { - trace_report(fd, data, false); - return 0; + ssize_t ret = trace_report(fd, data, false); + return ret < 0 ? -1 : 0; } static int process_build_id(struct perf_file_section *section, @@ -2750,6 +2750,11 @@ static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel, if (evsel->tp_format) return 0; + if (pevent == NULL) { + pr_debug("broken or missing trace data\n"); + return -1; + } + event = pevent_find_event(pevent, evsel->attr.config); if (event == NULL) return -1; diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 8c8181aa286a..ba752d765ac3 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -291,7 +291,10 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) int show_version = 0; int show_funcs = 0; int show_printk = 0; - ssize_t size; + ssize_t size = -1; + struct pevent *pevent; + + *ppevent = NULL; calc_data_size = 1; repipe = __repipe; @@ -315,34 +318,38 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) file_bigendian = buf[0]; host_bigendian = bigendian(); - *ppevent = read_trace_init(file_bigendian, host_bigendian); - if (*ppevent == NULL) - die("read_trace_init failed"); + pevent = read_trace_init(file_bigendian, host_bigendian); + if (pevent == NULL) { + pr_debug("read_trace_init failed"); + goto out; + } read_or_die(buf, 1); long_size = buf[0]; - page_size = read4(*ppevent); - - read_header_files(*ppevent); + page_size = read4(pevent); - read_ftrace_files(*ppevent); - read_event_files(*ppevent); - read_proc_kallsyms(*ppevent); - read_ftrace_printk(*ppevent); + read_header_files(pevent); + read_ftrace_files(pevent); + read_event_files(pevent); + read_proc_kallsyms(pevent); + read_ftrace_printk(pevent); size = calc_data_size - 1; calc_data_size = 0; repipe = false; if (show_funcs) { - pevent_print_funcs(*ppevent); - return size; - } - if (show_printk) { - pevent_print_printk(*ppevent); - return size; + pevent_print_funcs(pevent); + } else if (show_printk) { + pevent_print_printk(pevent); } + *ppevent = pevent; + pevent = NULL; + +out: + if (pevent) + pevent_free(pevent); return size; } -- cgit v1.2.3 From a4c983670e0f4285fe115cb2ad697c978c7950b6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 21 Mar 2013 16:18:49 +0900 Subject: perf tools: Get rid of malloc_or_die() in trace-event-read.c Check return value of malloc() and fail if error. Now read_string() can return NULL also check its return value and bail out. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363850332-25297-7-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-read.c | 100 +++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 33 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index ba752d765ac3..22ded8000ef6 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -46,16 +46,6 @@ static int long_size; static ssize_t calc_data_size; static bool repipe; -static void *malloc_or_die(int size) -{ - void *ret; - - ret = malloc(size); - if (!ret) - die("malloc"); - return ret; -} - static int do_read(int fd, void *buf, int size) { int rsize = size; @@ -156,48 +146,57 @@ static char *read_string(void) if (calc_data_size) calc_data_size += size; - str = malloc_or_die(size); - memcpy(str, buf, size); + str = malloc(size); + if (str) + memcpy(str, buf, size); return str; } -static void read_proc_kallsyms(struct pevent *pevent) +static int read_proc_kallsyms(struct pevent *pevent) { unsigned int size; char *buf; size = read4(pevent); if (!size) - return; + return 0; + + buf = malloc(size + 1); + if (buf == NULL) + return -1; - buf = malloc_or_die(size + 1); read_or_die(buf, size); buf[size] = '\0'; parse_proc_kallsyms(pevent, buf, size); free(buf); + return 0; } -static void read_ftrace_printk(struct pevent *pevent) +static int read_ftrace_printk(struct pevent *pevent) { unsigned int size; char *buf; size = read4(pevent); if (!size) - return; + return 0; + + buf = malloc(size); + if (buf == NULL) + return -1; - buf = malloc_or_die(size); read_or_die(buf, size); parse_ftrace_printk(pevent, buf, size); free(buf); + return 0; } -static void read_header_files(struct pevent *pevent) +static int read_header_files(struct pevent *pevent) { unsigned long long size; char *header_event; @@ -222,65 +221,87 @@ static void read_header_files(struct pevent *pevent) die("did not read header event"); size = read8(pevent); - header_event = malloc_or_die(size); + header_event = malloc(size); + if (header_event == NULL) + return -1; + read_or_die(header_event, size); free(header_event); + return 0; } -static void read_ftrace_file(struct pevent *pevent, unsigned long long size) +static int read_ftrace_file(struct pevent *pevent, unsigned long long size) { char *buf; - buf = malloc_or_die(size); + buf = malloc(size); + if (buf == NULL) + return -1; + read_or_die(buf, size); parse_ftrace_file(pevent, buf, size); free(buf); + return 0; } -static void read_event_file(struct pevent *pevent, char *sys, +static int read_event_file(struct pevent *pevent, char *sys, unsigned long long size) { char *buf; - buf = malloc_or_die(size); + buf = malloc(size); + if (buf == NULL) + return -1; + read_or_die(buf, size); parse_event_file(pevent, buf, size, sys); free(buf); + return 0; } -static void read_ftrace_files(struct pevent *pevent) +static int read_ftrace_files(struct pevent *pevent) { unsigned long long size; int count; int i; + int ret; count = read4(pevent); for (i = 0; i < count; i++) { size = read8(pevent); - read_ftrace_file(pevent, size); + ret = read_ftrace_file(pevent, size); + if (ret) + return ret; } + return 0; } -static void read_event_files(struct pevent *pevent) +static int read_event_files(struct pevent *pevent) { unsigned long long size; char *sys; int systems; int count; int i,x; + int ret; systems = read4(pevent); for (i = 0; i < systems; i++) { sys = read_string(); + if (sys == NULL) + return -1; count = read4(pevent); for (x=0; x < count; x++) { size = read8(pevent); - read_event_file(pevent, sys, size); + ret = read_event_file(pevent, sys, size); + if (ret) + return ret; } } + return 0; } ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) @@ -293,6 +314,7 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) int show_printk = 0; ssize_t size = -1; struct pevent *pevent; + int err; *ppevent = NULL; @@ -310,6 +332,8 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) die("not a trace file (missing 'tracing' tag)"); version = read_string(); + if (version == NULL) + return -1; if (show_version) printf("version = %s\n", version); free(version); @@ -329,11 +353,21 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) page_size = read4(pevent); - read_header_files(pevent); - read_ftrace_files(pevent); - read_event_files(pevent); - read_proc_kallsyms(pevent); - read_ftrace_printk(pevent); + err = read_header_files(pevent); + if (err) + goto out; + err = read_ftrace_files(pevent); + if (err) + goto out; + err = read_event_files(pevent); + if (err) + goto out; + err = read_proc_kallsyms(pevent); + if (err) + goto out; + err = read_ftrace_printk(pevent); + if (err) + goto out; size = calc_data_size - 1; calc_data_size = 0; -- cgit v1.2.3 From 4a31e56599d42c5ac17b280228349948dee352c7 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 21 Mar 2013 16:18:50 +0900 Subject: perf tools: Get rid of read_or_die() in trace-event-read.c Rename it to do_read and original do_read to __do_read, and check their return value. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363850332-25297-8-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-read.c | 80 +++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 23 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 22ded8000ef6..877706bd454f 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -46,7 +46,7 @@ static int long_size; static ssize_t calc_data_size; static bool repipe; -static int do_read(int fd, void *buf, int size) +static int __do_read(int fd, void *buf, int size) { int rsize = size; @@ -59,8 +59,10 @@ static int do_read(int fd, void *buf, int size) if (repipe) { int retw = write(STDOUT_FILENO, buf, ret); - if (retw <= 0 || retw != ret) - die("repiping input file"); + if (retw <= 0 || retw != ret) { + pr_debug("repiping input file"); + return -1; + } } size -= ret; @@ -70,14 +72,16 @@ static int do_read(int fd, void *buf, int size) return rsize; } -static int read_or_die(void *data, int size) +static int do_read(void *data, int size) { int r; - r = do_read(input_fd, data, size); - if (r <= 0) - die("reading input file (size expected=%d received=%d)", - size, r); + r = __do_read(input_fd, data, size); + if (r <= 0) { + pr_debug("reading input file (size expected=%d received=%d)", + size, r); + return -1; + } if (calc_data_size) calc_data_size += r; @@ -93,7 +97,7 @@ static void skip(int size) while (size) { r = size > BUFSIZ ? BUFSIZ : size; - read_or_die(buf, r); + do_read(buf, r); size -= r; }; } @@ -102,7 +106,8 @@ static unsigned int read4(struct pevent *pevent) { unsigned int data; - read_or_die(&data, 4); + if (do_read(&data, 4) < 0) + return 0; return __data2host4(pevent, data); } @@ -110,7 +115,8 @@ static unsigned long long read8(struct pevent *pevent) { unsigned long long data; - read_or_die(&data, 8); + if (do_read(&data, 8) < 0) + return 0; return __data2host8(pevent, data); } @@ -166,7 +172,10 @@ static int read_proc_kallsyms(struct pevent *pevent) if (buf == NULL) return -1; - read_or_die(buf, size); + if (do_read(buf, size) < 0) { + free(buf); + return -1; + } buf[size] = '\0'; parse_proc_kallsyms(pevent, buf, size); @@ -180,6 +189,7 @@ static int read_ftrace_printk(struct pevent *pevent) unsigned int size; char *buf; + /* it can have 0 size */ size = read4(pevent); if (!size) return 0; @@ -188,7 +198,10 @@ static int read_ftrace_printk(struct pevent *pevent) if (buf == NULL) return -1; - read_or_die(buf, size); + if (do_read(buf, size) < 0) { + free(buf); + return -1; + } parse_ftrace_printk(pevent, buf, size); @@ -201,8 +214,10 @@ static int read_header_files(struct pevent *pevent) unsigned long long size; char *header_event; char buf[BUFSIZ]; + int ret = 0; - read_or_die(buf, 12); + if (do_read(buf, 12) < 0) + return -1; if (memcmp(buf, "header_page", 12) != 0) die("did not read header page"); @@ -216,7 +231,9 @@ static int read_header_files(struct pevent *pevent) */ long_size = header_page_size_size; - read_or_die(buf, 13); + if (do_read(buf, 13) < 0) + return -1; + if (memcmp(buf, "header_event", 13) != 0) die("did not read header event"); @@ -225,9 +242,11 @@ static int read_header_files(struct pevent *pevent) if (header_event == NULL) return -1; - read_or_die(header_event, size); + if (do_read(header_event, size) < 0) + ret = -1; + free(header_event); - return 0; + return ret; } static int read_ftrace_file(struct pevent *pevent, unsigned long long size) @@ -238,7 +257,11 @@ static int read_ftrace_file(struct pevent *pevent, unsigned long long size) if (buf == NULL) return -1; - read_or_die(buf, size); + if (do_read(buf, size) < 0) { + free(buf); + return -1; + } + parse_ftrace_file(pevent, buf, size); free(buf); return 0; @@ -253,7 +276,11 @@ static int read_event_file(struct pevent *pevent, char *sys, if (buf == NULL) return -1; - read_or_die(buf, size); + if (do_read(buf, size) < 0) { + free(buf); + return -1; + } + parse_event_file(pevent, buf, size, sys); free(buf); return 0; @@ -294,6 +321,7 @@ static int read_event_files(struct pevent *pevent) return -1; count = read4(pevent); + for (x=0; x < count; x++) { size = read8(pevent); ret = read_event_file(pevent, sys, size); @@ -323,11 +351,13 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) input_fd = fd; - read_or_die(buf, 3); + if (do_read(buf, 3) < 0) + return -1; if (memcmp(buf, test, 3) != 0) die("no trace data in the file"); - read_or_die(buf, 7); + if (do_read(buf, 7) < 0) + return -1; if (memcmp(buf, "tracing", 7) != 0) die("not a trace file (missing 'tracing' tag)"); @@ -338,7 +368,8 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) printf("version = %s\n", version); free(version); - read_or_die(buf, 1); + if (do_read(buf, 1) < 0) + return -1; file_bigendian = buf[0]; host_bigendian = bigendian(); @@ -348,10 +379,13 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) goto out; } - read_or_die(buf, 1); + if (do_read(buf, 1) < 0) + goto out; long_size = buf[0]; page_size = read4(pevent); + if (!page_size) + goto out; err = read_header_files(pevent); if (err) -- cgit v1.2.3 From 452958fdd05b43b6c91cfd1341f4fac2f3ce661f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 21 Mar 2013 16:18:51 +0900 Subject: perf tools: Get rid of die() calls in trace-data-read.c Convert them to pr_debug() and propagate error code. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363850332-25297-9-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-read.c | 44 +++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 15 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 877706bd454f..644ad3b4edec 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -130,17 +130,23 @@ static char *read_string(void) for (;;) { r = read(input_fd, &c, 1); - if (r < 0) - die("reading input file"); + if (r < 0) { + pr_debug("reading input file"); + goto out; + } - if (!r) - die("no data"); + if (!r) { + pr_debug("no data"); + goto out; + } if (repipe) { int retw = write(STDOUT_FILENO, &c, 1); - if (retw <= 0 || retw != r) - die("repiping input file string"); + if (retw <= 0 || retw != r) { + pr_debug("repiping input file string"); + goto out; + } } buf[size++] = c; @@ -155,7 +161,7 @@ static char *read_string(void) str = malloc(size); if (str) memcpy(str, buf, size); - +out: return str; } @@ -219,8 +225,10 @@ static int read_header_files(struct pevent *pevent) if (do_read(buf, 12) < 0) return -1; - if (memcmp(buf, "header_page", 12) != 0) - die("did not read header page"); + if (memcmp(buf, "header_page", 12) != 0) { + pr_debug("did not read header page"); + return -1; + } size = read8(pevent); skip(size); @@ -234,8 +242,10 @@ static int read_header_files(struct pevent *pevent) if (do_read(buf, 13) < 0) return -1; - if (memcmp(buf, "header_event", 13) != 0) - die("did not read header event"); + if (memcmp(buf, "header_event", 13) != 0) { + pr_debug("did not read header event"); + return -1; + } size = read8(pevent); header_event = malloc(size); @@ -353,13 +363,17 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) if (do_read(buf, 3) < 0) return -1; - if (memcmp(buf, test, 3) != 0) - die("no trace data in the file"); + if (memcmp(buf, test, 3) != 0) { + pr_debug("no trace data in the file"); + return -1; + } if (do_read(buf, 7) < 0) return -1; - if (memcmp(buf, "tracing", 7) != 0) - die("not a trace file (missing 'tracing' tag)"); + if (memcmp(buf, "tracing", 7) != 0) { + pr_debug("not a trace file (missing 'tracing' tag)"); + return -1; + } version = read_string(); if (version == NULL) -- cgit v1.2.3 From ebf3c675d7e4ba97568dd6daaa43b1af10046b29 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 21 Mar 2013 16:18:52 +0900 Subject: perf tools: Cleanup calc_data_size logic It's for calculating whole trace data size during reading. However relation functions are called only in this file, no need to conditionalize it with tricky +1 offset and rename the variable to more meaningful name like trace_data_size. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1363850332-25297-10-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-read.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 644ad3b4edec..af215c0d2379 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -43,7 +43,7 @@ int file_bigendian; int host_bigendian; static int long_size; -static ssize_t calc_data_size; +static ssize_t trace_data_size; static bool repipe; static int __do_read(int fd, void *buf, int size) @@ -83,8 +83,7 @@ static int do_read(void *data, int size) return -1; } - if (calc_data_size) - calc_data_size += r; + trace_data_size += r; return r; } @@ -155,8 +154,7 @@ static char *read_string(void) break; } - if (calc_data_size) - calc_data_size += size; + trace_data_size += size; str = malloc(size); if (str) @@ -356,9 +354,7 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) *ppevent = NULL; - calc_data_size = 1; repipe = __repipe; - input_fd = fd; if (do_read(buf, 3) < 0) @@ -417,8 +413,7 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) if (err) goto out; - size = calc_data_size - 1; - calc_data_size = 0; + size = trace_data_size; repipe = false; if (show_funcs) { -- cgit v1.2.3 From 98e821a2a927b6dc0f7adc4b64ad29bec1b6ff89 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 21 Mar 2013 14:10:03 -0400 Subject: net: fix psock_fanout on sparc64 The packetsocket fanout test uses a packet ring. Use TPACKET_V2 instead of TPACKET_V1 to work around a known 32/64 bit issue in the older ring that manifests on sparc64. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- tools/testing/selftests/net/psock_fanout.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c index 226e5e33105a..59bd6367af71 100644 --- a/tools/testing/selftests/net/psock_fanout.c +++ b/tools/testing/selftests/net/psock_fanout.c @@ -182,7 +182,13 @@ static char *sock_fanout_open_ring(int fd) .tp_frame_nr = RING_NUM_FRAMES, }; char *ring; + int val = TPACKET_V2; + if (setsockopt(fd, SOL_PACKET, PACKET_VERSION, (void *) &val, + sizeof(val))) { + perror("packetsock ring setsockopt version"); + exit(1); + } if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req))) { perror("packetsock ring setsockopt"); @@ -201,7 +207,7 @@ static char *sock_fanout_open_ring(int fd) static int sock_fanout_read_ring(int fd, void *ring) { - struct tpacket_hdr *header = ring; + struct tpacket2_hdr *header = ring; int count = 0; while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { -- cgit v1.2.3 From 86ee6e18f6cb43ab0cb67347bda5b6f5b016121d Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 14 Feb 2013 13:57:27 +0100 Subject: perf stat: Refactor aggregation code Refactor aggregation code by introducing a single aggr_mode variable and an enum for aggregation. Also refactor cpumap code having to do with cpu to socket mappings. All in preparation for extended modes, such as cpu -> core. Also fix socket aggregation and ensure that sockets are printed in increasing order. Signed-off-by: Stephane Eranian Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1360846649-6411-2-git-send-email-eranian@google.com [ committer note: Fixup conflicts with a7e191c "--repeat forever" and acf2892 "Use perf_evlist__prepare/start_workload()" ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 207 ++++++++++++++++++++++++++-------------------- tools/perf/util/cpumap.c | 40 ++++++--- 2 files changed, 147 insertions(+), 100 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index ba0bdd87c279..ded34fc4df4f 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -68,7 +68,7 @@ static void print_stat(int argc, const char **argv); static void print_counter_aggr(struct perf_evsel *counter, char *prefix); static void print_counter(struct perf_evsel *counter, char *prefix); -static void print_aggr_socket(char *prefix); +static void print_aggr(char *prefix); static struct perf_evlist *evsel_list; @@ -76,11 +76,16 @@ static struct perf_target target = { .uid = UINT_MAX, }; +enum aggr_mode { + AGGR_NONE, + AGGR_GLOBAL, + AGGR_SOCKET, +}; + static int run_count = 1; static bool no_inherit = false; static bool scale = true; -static bool no_aggr = false; -static bool aggr_socket = false; +static enum aggr_mode aggr_mode = AGGR_GLOBAL; static pid_t child_pid = -1; static bool null_run = false; static int detailed_run = 0; @@ -96,7 +101,8 @@ static bool sync_run = false; static unsigned int interval = 0; static bool forever = false; static struct timespec ref_time; -static struct cpu_map *sock_map; +static struct cpu_map *aggr_map; +static int (*aggr_get_id)(struct cpu_map *m, int cpu); static volatile int done = 0; @@ -355,41 +361,51 @@ static void print_interval(void) struct timespec ts, rs; char prefix[64]; - if (no_aggr) { + if (aggr_mode == AGGR_GLOBAL) { list_for_each_entry(counter, &evsel_list->entries, node) { ps = counter->priv; memset(ps->res_stats, 0, sizeof(ps->res_stats)); - read_counter(counter); + read_counter_aggr(counter); } - } else { + } else { list_for_each_entry(counter, &evsel_list->entries, node) { ps = counter->priv; memset(ps->res_stats, 0, sizeof(ps->res_stats)); - read_counter_aggr(counter); + read_counter(counter); } } + clock_gettime(CLOCK_MONOTONIC, &ts); diff_timespec(&rs, &ts, &ref_time); sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep); if (num_print_interval == 0 && !csv_output) { - if (aggr_socket) + switch (aggr_mode) { + case AGGR_SOCKET: fprintf(output, "# time socket cpus counts events\n"); - else if (no_aggr) + break; + case AGGR_NONE: fprintf(output, "# time CPU counts events\n"); - else + break; + case AGGR_GLOBAL: + default: fprintf(output, "# time counts events\n"); + } } if (++num_print_interval == 25) num_print_interval = 0; - if (aggr_socket) - print_aggr_socket(prefix); - else if (no_aggr) { + switch (aggr_mode) { + case AGGR_SOCKET: + print_aggr(prefix); + break; + case AGGR_NONE: list_for_each_entry(counter, &evsel_list->entries, node) print_counter(counter, prefix); - } else { + break; + case AGGR_GLOBAL: + default: list_for_each_entry(counter, &evsel_list->entries, node) print_counter_aggr(counter, prefix); } @@ -412,12 +428,6 @@ static int __run_perf_stat(int argc, const char **argv) ts.tv_nsec = 0; } - if (aggr_socket - && cpu_map__build_socket_map(evsel_list->cpus, &sock_map)) { - perror("cannot build socket map"); - return -1; - } - if (forks) { if (perf_evlist__prepare_workload(evsel_list, &target, argv, false, false) < 0) { @@ -493,17 +503,17 @@ static int __run_perf_stat(int argc, const char **argv) update_stats(&walltime_nsecs_stats, t1 - t0); - if (no_aggr) { - list_for_each_entry(counter, &evsel_list->entries, node) { - read_counter(counter); - perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1); - } - } else { + if (aggr_mode == AGGR_GLOBAL) { list_for_each_entry(counter, &evsel_list->entries, node) { read_counter_aggr(counter); perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), thread_map__nr(evsel_list->threads)); } + } else { + list_for_each_entry(counter, &evsel_list->entries, node) { + read_counter(counter); + perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1); + } } return WEXITSTATUS(status); @@ -556,26 +566,37 @@ static void print_noise(struct perf_evsel *evsel, double avg) print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); } -static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) +static void aggr_printout(struct perf_evsel *evsel, int cpu, int nr) { - double msecs = avg / 1e6; - char cpustr[16] = { '\0', }; - const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s"; - - if (aggr_socket) - sprintf(cpustr, "S%*d%s%*d%s", + switch (aggr_mode) { + case AGGR_SOCKET: + fprintf(output, "S%*d%s%*d%s", csv_output ? 0 : -5, cpu, csv_sep, csv_output ? 0 : 4, nr, csv_sep); - else if (no_aggr) - sprintf(cpustr, "CPU%*d%s", + break; + case AGGR_NONE: + fprintf(output, "CPU%*d%s", csv_output ? 0 : -4, perf_evsel__cpus(evsel)->map[cpu], csv_sep); + break; + case AGGR_GLOBAL: + default: + break; + } +} + +static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) +{ + double msecs = avg / 1e6; + const char *fmt = csv_output ? "%.6f%s%s" : "%18.6f%s%-25s"; + + aggr_printout(evsel, cpu, nr); - fprintf(output, fmt, cpustr, msecs, csv_sep, perf_evsel__name(evsel)); + fprintf(output, fmt, msecs, csv_sep, perf_evsel__name(evsel)); if (evsel->cgrp) fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); @@ -772,32 +793,21 @@ static void print_ll_cache_misses(int cpu, static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) { double total, ratio = 0.0; - char cpustr[16] = { '\0', }; const char *fmt; if (csv_output) - fmt = "%s%.0f%s%s"; + fmt = "%.0f%s%s"; else if (big_num) - fmt = "%s%'18.0f%s%-25s"; + fmt = "%'18.0f%s%-25s"; else - fmt = "%s%18.0f%s%-25s"; + fmt = "%18.0f%s%-25s"; - if (aggr_socket) - sprintf(cpustr, "S%*d%s%*d%s", - csv_output ? 0 : -5, - cpu, - csv_sep, - csv_output ? 0 : 4, - nr, - csv_sep); - else if (no_aggr) - sprintf(cpustr, "CPU%*d%s", - csv_output ? 0 : -4, - perf_evsel__cpus(evsel)->map[cpu], csv_sep); - else + aggr_printout(evsel, cpu, nr); + + if (aggr_mode == AGGR_GLOBAL) cpu = 0; - fprintf(output, fmt, cpustr, avg, csv_sep, perf_evsel__name(evsel)); + fprintf(output, fmt, avg, csv_sep, perf_evsel__name(evsel)); if (evsel->cgrp) fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); @@ -896,23 +906,23 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) } } -static void print_aggr_socket(char *prefix) +static void print_aggr(char *prefix) { struct perf_evsel *counter; + int cpu, s, s2, id, nr; u64 ena, run, val; - int cpu, s, s2, sock, nr; - if (!sock_map) + if (!(aggr_map || aggr_get_id)) return; - for (s = 0; s < sock_map->nr; s++) { - sock = cpu_map__socket(sock_map, s); + for (s = 0; s < aggr_map->nr; s++) { + id = aggr_map->map[s]; list_for_each_entry(counter, &evsel_list->entries, node) { val = ena = run = 0; nr = 0; for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { - s2 = cpu_map__get_socket(evsel_list->cpus, cpu); - if (s2 != sock) + s2 = aggr_get_id(evsel_list->cpus, cpu); + if (s2 != id) continue; val += counter->counts->cpu[cpu].val; ena += counter->counts->cpu[cpu].ena; @@ -923,18 +933,15 @@ static void print_aggr_socket(char *prefix) fprintf(output, "%s", prefix); if (run == 0 || ena == 0) { - fprintf(output, "S%*d%s%*d%s%*s%s%*s", - csv_output ? 0 : -5, - s, - csv_sep, - csv_output ? 0 : 4, - nr, - csv_sep, + aggr_printout(counter, cpu, nr); + + fprintf(output, "%*s%s%*s", csv_output ? 0 : 18, counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, csv_sep, csv_output ? 0 : -24, perf_evsel__name(counter)); + if (counter->cgrp) fprintf(output, "%s%s", csv_sep, counter->cgrp->name); @@ -944,9 +951,9 @@ static void print_aggr_socket(char *prefix) } if (nsec_counter(counter)) - nsec_printout(sock, nr, counter, val); + nsec_printout(id, nr, counter, val); else - abs_printout(sock, nr, counter, val); + abs_printout(id, nr, counter, val); if (!csv_output) { print_noise(counter, 1.0); @@ -1087,14 +1094,20 @@ static void print_stat(int argc, const char **argv) fprintf(output, ":\n\n"); } - if (aggr_socket) - print_aggr_socket(NULL); - else if (no_aggr) { - list_for_each_entry(counter, &evsel_list->entries, node) - print_counter(counter, NULL); - } else { + switch (aggr_mode) { + case AGGR_SOCKET: + print_aggr(NULL); + break; + case AGGR_GLOBAL: list_for_each_entry(counter, &evsel_list->entries, node) print_counter_aggr(counter, NULL); + break; + case AGGR_NONE: + list_for_each_entry(counter, &evsel_list->entries, node) + print_counter(counter, NULL); + break; + default: + break; } if (!csv_output) { @@ -1140,6 +1153,25 @@ static int stat__set_big_num(const struct option *opt __maybe_unused, return 0; } +static int perf_stat_init_aggr_mode(void) +{ + switch (aggr_mode) { + case AGGR_SOCKET: + if (cpu_map__build_socket_map(evsel_list->cpus, &aggr_map)) { + perror("cannot build socket map"); + return -1; + } + aggr_get_id = cpu_map__get_socket; + break; + case AGGR_NONE: + case AGGR_GLOBAL: + default: + break; + } + return 0; +} + + /* * Add default attributes, if there were no attributes specified or * if -d/--detailed, -d -d or -d -d -d is used: @@ -1322,7 +1354,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) stat__set_big_num), OPT_STRING('C', "cpu", &target.cpu_list, "cpu", "list of cpus to monitor in system-wide"), - OPT_BOOLEAN('A', "no-aggr", &no_aggr, "disable CPU count aggregation"), + OPT_SET_UINT('A', "no-aggr", &aggr_mode, + "disable CPU count aggregation", AGGR_NONE), OPT_STRING('x', "field-separator", &csv_sep, "separator", "print counts with custom separator"), OPT_CALLBACK('G', "cgroup", &evsel_list, "name", @@ -1337,7 +1370,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) "command to run after to the measured command"), OPT_UINTEGER('I', "interval-print", &interval, "print counts at regular interval in ms (>= 100)"), - OPT_BOOLEAN(0, "aggr-socket", &aggr_socket, "aggregate counts per processor socket"), + OPT_SET_UINT(0, "aggr-socket", &aggr_mode, + "aggregate counts per processor socket", AGGR_SOCKET), OPT_END() }; const char * const stat_usage[] = { @@ -1420,19 +1454,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) } /* no_aggr, cgroup are for system-wide only */ - if ((no_aggr || nr_cgroups) && !perf_target__has_cpu(&target)) { + if ((aggr_mode != AGGR_GLOBAL || nr_cgroups) + && !perf_target__has_cpu(&target)) { fprintf(stderr, "both cgroup and no-aggregation " "modes only available in system-wide mode\n"); usage_with_options(stat_usage, options); - } - - if (aggr_socket) { - if (!perf_target__has_cpu(&target)) { - fprintf(stderr, "--aggr-socket only available in system-wide mode (-a)\n"); - usage_with_options(stat_usage, options); - } - no_aggr = true; + return -1; } if (add_default_attributes()) @@ -1458,6 +1486,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (perf_evlist__alloc_stats(evsel_list, interval)) goto out_free_maps; + if (perf_stat_init_aggr_mode()) + goto out; + /* * We dont want to block the signals - that would cause * child tasks to inherit that and Ctrl-C would not work. diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index f817046e22b1..7bb8e87a5847 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -4,6 +4,7 @@ #include "cpumap.h" #include #include +#include static struct cpu_map *cpu_map__default_new(void) { @@ -219,7 +220,7 @@ int cpu_map__get_socket(struct cpu_map *map, int idx) if (!mnt) return -1; - sprintf(path, + snprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d/topology/physical_package_id", mnt, cpu); @@ -231,27 +232,42 @@ int cpu_map__get_socket(struct cpu_map *map, int idx) return ret == 1 ? cpu : -1; } -int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp) +static int cmp_ids(const void *a, const void *b) { - struct cpu_map *sock; + return *(int *)a - *(int *)b; +} + +static int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res, + int (*f)(struct cpu_map *map, int cpu)) +{ + struct cpu_map *c; int nr = cpus->nr; int cpu, s1, s2; - sock = calloc(1, sizeof(*sock) + nr * sizeof(int)); - if (!sock) + /* allocate as much as possible */ + c = calloc(1, sizeof(*c) + nr * sizeof(int)); + if (!c) return -1; for (cpu = 0; cpu < nr; cpu++) { - s1 = cpu_map__get_socket(cpus, cpu); - for (s2 = 0; s2 < sock->nr; s2++) { - if (s1 == sock->map[s2]) + s1 = f(cpus, cpu); + for (s2 = 0; s2 < c->nr; s2++) { + if (s1 == c->map[s2]) break; } - if (s2 == sock->nr) { - sock->map[sock->nr] = s1; - sock->nr++; + if (s2 == c->nr) { + c->map[c->nr] = s1; + c->nr++; } } - *sockp = sock; + /* ensure we process id in increasing order */ + qsort(c->map, c->nr, sizeof(int), cmp_ids); + + *res = c; return 0; } + +int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp) +{ + return cpu_map__build_map(cpus, sockp, cpu_map__get_socket); +} -- cgit v1.2.3 From d4304958a25414a6e67b8a41c0f230e05cafafb6 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 14 Feb 2013 13:57:28 +0100 Subject: perf stat: Rename --aggr-socket to --per-socket To make it more obvious what this option does as suggested by Andi on LKML. Signed-off-by: Stephane Eranian Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1360846649-6411-3-git-send-email-eranian@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 4 ++-- tools/perf/builtin-stat.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 23e587ad549e..46027e1aedb7 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -119,10 +119,10 @@ perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- m Print count deltas every N milliseconds (minimum: 100ms) example: perf stat -I 1000 -e cycles -a sleep 5 ---aggr-socket:: +--per-socket:: Aggregate counts per processor socket for system-wide mode measurements. This is a useful mode to detect imbalance between sockets. To enable this mode, -use --aggr-socket in addition to -a. (system-wide). The output includes the +use --per-socket in addition to -a. (system-wide). The output includes the socket number and the number of online processors on that socket. This is useful to gauge the amount of aggregation. diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index ded34fc4df4f..6f6ea931ab76 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1370,7 +1370,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) "command to run after to the measured command"), OPT_UINTEGER('I', "interval-print", &interval, "print counts at regular interval in ms (>= 100)"), - OPT_SET_UINT(0, "aggr-socket", &aggr_mode, + OPT_SET_UINT(0, "per-socket", &aggr_mode, "aggregate counts per processor socket", AGGR_SOCKET), OPT_END() }; -- cgit v1.2.3 From 12c08a9f591aeda57fb3b05897169e7da5439a79 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 14 Feb 2013 13:57:29 +0100 Subject: perf stat: Add per-core aggregation This patch adds the --per-core option to perf stat. This option is used to aggregate system-wide counts on a per physical core basis. On processors with hyperthreading, this means counts of all HT threads running on a physical core are aggregated. This mode is useful to find imblance between physical cores running an uniform workload. Cores are identified by socket: S0-C1, means physical core 1 on socket 0. Note that cores are identified using their physical core id, thus their numbering may not be continuous. Per core aggregation can be combined with interval printing: # perf stat -a --per-core -I 1000 -e cycles sleep 1000 # time core cpus counts events 1.000090030 S0-C0 1 4,765,747 cycles 1.000090030 S0-C1 1 5,580,647 cycles 1.000090030 S0-C2 1 221,181 cycles 1.000090030 S0-C3 1 266,092 cycles Signed-off-by: Stephane Eranian Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1360846649-6411-4-git-send-email-eranian@google.com [ committer note: Remove parts already applied on 86ee6e1 to keep bisectability ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 6 +++++ tools/perf/builtin-stat.c | 31 ++++++++++++++++++++--- tools/perf/util/cpumap.c | 46 ++++++++++++++++++++++++++++++++++ tools/perf/util/cpumap.h | 12 +++++++++ 4 files changed, 92 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 46027e1aedb7..2fe87fb558f0 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -126,6 +126,12 @@ use --per-socket in addition to -a. (system-wide). The output includes the socket number and the number of online processors on that socket. This is useful to gauge the amount of aggregation. +--per-core:: +Aggregate counts per physical processor for system-wide mode measurements. This +is a useful mode to detect imbalance between physical cores. To enable this mode, +use --per-core in addition to -a. (system-wide). The output includes the +core number and the number of online logical processors on that physical processor. + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 6f6ea931ab76..7e910bab1097 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -80,6 +80,7 @@ enum aggr_mode { AGGR_NONE, AGGR_GLOBAL, AGGR_SOCKET, + AGGR_CORE, }; static int run_count = 1; @@ -384,6 +385,9 @@ static void print_interval(void) case AGGR_SOCKET: fprintf(output, "# time socket cpus counts events\n"); break; + case AGGR_CORE: + fprintf(output, "# time core cpus counts events\n"); + break; case AGGR_NONE: fprintf(output, "# time CPU counts events\n"); break; @@ -397,6 +401,7 @@ static void print_interval(void) num_print_interval = 0; switch (aggr_mode) { + case AGGR_CORE: case AGGR_SOCKET: print_aggr(prefix); break; @@ -566,13 +571,23 @@ static void print_noise(struct perf_evsel *evsel, double avg) print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); } -static void aggr_printout(struct perf_evsel *evsel, int cpu, int nr) +static void aggr_printout(struct perf_evsel *evsel, int id, int nr) { switch (aggr_mode) { + case AGGR_CORE: + fprintf(output, "S%d-C%*d%s%*d%s", + cpu_map__id_to_socket(id), + csv_output ? 0 : -8, + cpu_map__id_to_cpu(id), + csv_sep, + csv_output ? 0 : 4, + nr, + csv_sep); + break; case AGGR_SOCKET: fprintf(output, "S%*d%s%*d%s", csv_output ? 0 : -5, - cpu, + id, csv_sep, csv_output ? 0 : 4, nr, @@ -581,7 +596,7 @@ static void aggr_printout(struct perf_evsel *evsel, int cpu, int nr) case AGGR_NONE: fprintf(output, "CPU%*d%s", csv_output ? 0 : -4, - perf_evsel__cpus(evsel)->map[cpu], csv_sep); + perf_evsel__cpus(evsel)->map[id], csv_sep); break; case AGGR_GLOBAL: default: @@ -1095,6 +1110,7 @@ static void print_stat(int argc, const char **argv) } switch (aggr_mode) { + case AGGR_CORE: case AGGR_SOCKET: print_aggr(NULL); break; @@ -1163,6 +1179,13 @@ static int perf_stat_init_aggr_mode(void) } aggr_get_id = cpu_map__get_socket; break; + case AGGR_CORE: + if (cpu_map__build_core_map(evsel_list->cpus, &aggr_map)) { + perror("cannot build core map"); + return -1; + } + aggr_get_id = cpu_map__get_core; + break; case AGGR_NONE: case AGGR_GLOBAL: default: @@ -1372,6 +1395,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) "print counts at regular interval in ms (>= 100)"), OPT_SET_UINT(0, "per-socket", &aggr_mode, "aggregate counts per processor socket", AGGR_SOCKET), + OPT_SET_UINT(0, "per-core", &aggr_mode, + "aggregate counts per physical processor core", AGGR_CORE), OPT_END() }; const char * const stat_usage[] = { diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 7bb8e87a5847..beb8cf9f9976 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -267,7 +267,53 @@ static int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res, return 0; } +int cpu_map__get_core(struct cpu_map *map, int idx) +{ + FILE *fp; + const char *mnt; + char path[PATH_MAX]; + int cpu, ret, s; + + if (idx > map->nr) + return -1; + + cpu = map->map[idx]; + + mnt = sysfs_find_mountpoint(); + if (!mnt) + return -1; + + snprintf(path, PATH_MAX, + "%s/devices/system/cpu/cpu%d/topology/core_id", + mnt, cpu); + + fp = fopen(path, "r"); + if (!fp) + return -1; + ret = fscanf(fp, "%d", &cpu); + fclose(fp); + if (ret != 1) + return -1; + + s = cpu_map__get_socket(map, idx); + if (s == -1) + return -1; + + /* + * encode socket in upper 16 bits + * core_id is relative to socket, and + * we need a global id. So we combine + * socket+ core id + */ + return (s << 16) | (cpu & 0xffff); +} + int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp) { return cpu_map__build_map(cpus, sockp, cpu_map__get_socket); } + +int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep) +{ + return cpu_map__build_map(cpus, corep, cpu_map__get_core); +} diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 161b00756a12..9bed02e5fb3d 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -15,7 +15,9 @@ void cpu_map__delete(struct cpu_map *map); struct cpu_map *cpu_map__read(FILE *file); size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); int cpu_map__get_socket(struct cpu_map *map, int idx); +int cpu_map__get_core(struct cpu_map *map, int idx); int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp); +int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep); static inline int cpu_map__socket(struct cpu_map *sock, int s) { @@ -24,6 +26,16 @@ static inline int cpu_map__socket(struct cpu_map *sock, int s) return sock->map[s]; } +static inline int cpu_map__id_to_socket(int id) +{ + return id >> 16; +} + +static inline int cpu_map__id_to_cpu(int id) +{ + return id & 0xffff; +} + static inline int cpu_map__nr(const struct cpu_map *map) { return map ? map->nr : 1; -- cgit v1.2.3 From 328ccdace8855289ad114b70ee1464ba5e3f6436 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 25 Mar 2013 18:18:18 +0900 Subject: perf report: Add --no-demangle option It's sometimes useful to see undemangled raw symbol name for example other tools using the perf output to do manipulation of binaries. Signed-off-by: Namhyung Kim Suggested-by: William Cohen Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: William Cohen BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=55571 Link: http://lkml.kernel.org/r/1364203098-17741-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-report.txt | 4 ++++ tools/perf/builtin-report.c | 2 ++ tools/perf/util/symbol-elf.c | 9 ++++++--- tools/perf/util/symbol.c | 1 + tools/perf/util/symbol.h | 3 ++- 5 files changed, 15 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 02284a0067f0..71f15510ca0b 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -206,6 +206,10 @@ OPTIONS --group:: Show event group information together. +--demangle:: + Demangle symbol names to human readable form. It's enabled by default, + disable with --no-demangle. + SEE ALSO -------- linkperf:perf-stat[1], linkperf:perf-annotate[1] diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 296bd219977a..b5ea26cc7eb1 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -692,6 +692,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) "use branch records for histogram filling", parse_branch_mode), OPT_STRING(0, "objdump", &objdump_path, "path", "objdump binary to use for disassembly and annotations"), + OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, + "Disable symbol demangling"), OPT_END() }; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 54efcb5659ac..4b12bf850325 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -806,9 +806,12 @@ int dso__load_sym(struct dso *dso, struct map *map, * DWARF DW_compile_unit has this, but we don't always have access * to it... */ - demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); - if (demangled != NULL) - elf_name = demangled; + if (symbol_conf.demangle) { + demangled = bfd_demangle(NULL, elf_name, + DMGL_PARAMS | DMGL_ANSI); + if (demangled != NULL) + elf_name = demangled; + } new_symbol: f = symbol__new(sym.st_value, sym.st_size, GELF_ST_BIND(sym.st_info), elf_name); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e6432d85b43d..8cf3b5426a9a 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -36,6 +36,7 @@ struct symbol_conf symbol_conf = { .use_modules = true, .try_vmlinux_path = true, .annotate_src = true, + .demangle = true, .symfs = "", }; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index b62ca37c4b77..d7654c23861a 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -97,7 +97,8 @@ struct symbol_conf { kptr_restrict, annotate_asm_raw, annotate_src, - event_group; + event_group, + demangle; const char *vmlinux_name, *kallsyms_name, *source_prefix, -- cgit v1.2.3 From 1051e9b33bbf550be52bdd674b519f3dc99f0dd9 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Sun, 31 Mar 2013 01:23:50 +0900 Subject: treewide: Fix typos in kernel messages Correct spelling typos in various part of printk. Signed-off-by: Masanari Iida Acked-by: Randy Dunlap Signed-off-by: Jiri Kosina --- tools/power/cpupower/debug/i386/intel_gsic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/power/cpupower/debug/i386/intel_gsic.c b/tools/power/cpupower/debug/i386/intel_gsic.c index 53f5293c9c9a..d032c826d42e 100644 --- a/tools/power/cpupower/debug/i386/intel_gsic.c +++ b/tools/power/cpupower/debug/i386/intel_gsic.c @@ -66,7 +66,7 @@ int main (void) printf("ecx = 0x%.8x\n", r.ecx); printf("edx = 0x%.8x\n", r.edx); printf("Note also that some BIOS do not support the initial " - "GSIC call, but the newer\nspeeedstep-smi driver may " + "GSIC call, but the newer\nspeedstep-smi driver may " "work.\nFor this, you need to pass some arguments to " "the speedstep-smi driver:\n"); printf("\tsmi_cmd=0x?? smi_port=0x?? smi_sig=1\n"); -- cgit v1.2.3 From 05484298cbfebbf8c8c55b000541a245bc286bec Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 24 Jan 2013 16:10:29 +0100 Subject: perf tools: Add support for weight v7 (modified) perf record has a new option -W that enables weightened sampling. Add sorting support in top/report for the average weight per sample and the total weight sum. This allows to both compare relative cost per event and the total cost over the measurement period. Add the necessary glue to perf report, record and the library. v2: Merge with new hist refactoring. v3: Fix manpage. Remove value check. Rename global_weight to weight and weight to local_weight. v4: Readd sort keys to manpage v5: Move weight to end v6: Move weight to template v7: Rename weight key. Original patch from Andi modified by Stephane Eranian to include ONLY the weight supporting code and apply to pristine 3.8.0-rc4. Signed-off-by: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1359040242-8269-6-git-send-email-eranian@google.com [ committer note: changed to cope with fc5871ed and the hists_link perf test entry ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 6 +++++ tools/perf/Documentation/perf-report.txt | 2 +- tools/perf/Documentation/perf-top.txt | 2 +- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-diff.c | 7 ++--- tools/perf/builtin-record.c | 2 ++ tools/perf/builtin-report.c | 8 +++--- tools/perf/builtin-top.c | 5 ++-- tools/perf/perf.h | 1 + tools/perf/tests/hists_link.c | 4 +-- tools/perf/util/event.h | 1 + tools/perf/util/evsel.c | 10 +++++++ tools/perf/util/hist.c | 23 +++++++++++----- tools/perf/util/hist.h | 8 ++++-- tools/perf/util/session.c | 3 +++ tools/perf/util/sort.c | 45 ++++++++++++++++++++++++++++++++ tools/perf/util/sort.h | 3 +++ 17 files changed, 110 insertions(+), 22 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 938e8904f64d..d4da111ef53d 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -182,6 +182,12 @@ is enabled for all the sampling events. The sampled branch type is the same for The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k Note that this feature may not be available on all processors. +-W:: +--weight:: +Enable weightened sampling. An additional weight is recorded per sample and can be +displayed with the weight and local_weight sort keys. This currently works for TSX +abort events and some memory events in precise mode on modern Intel CPUs. + SEE ALSO -------- linkperf:perf-stat[1], linkperf:perf-list[1] diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 71f15510ca0b..7d5f4f38aa52 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -59,7 +59,7 @@ OPTIONS --sort=:: Sort histogram entries by given key(s) - multiple keys can be specified in CSV format. Following sort keys are available: - pid, comm, dso, symbol, parent, cpu, srcline. + pid, comm, dso, symbol, parent, cpu, srcline, weight, local_weight. Each key has following meaning: diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index a414bc95fd52..9f1a2fe54757 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -112,7 +112,7 @@ Default is to monitor all CPUS. -s:: --sort:: - Sort by key(s): pid, comm, dso, symbol, parent, srcline. + Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, local_weight. -n:: --show-nr-samples:: diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ae36f3cb5410..db491e9a812b 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -63,7 +63,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, return 0; } - he = __hists__add_entry(&evsel->hists, al, NULL, 1); + he = __hists__add_entry(&evsel->hists, al, NULL, 1, 1); if (he == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index d207a97a2db1..2d0462d89a97 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -231,9 +231,10 @@ int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, } static int hists__add_entry(struct hists *self, - struct addr_location *al, u64 period) + struct addr_location *al, u64 period, + u64 weight) { - if (__hists__add_entry(self, al, NULL, period) != NULL) + if (__hists__add_entry(self, al, NULL, period, weight) != NULL) return 0; return -ENOMEM; } @@ -255,7 +256,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, if (al.filtered) return 0; - if (hists__add_entry(&evsel->hists, &al, sample->period)) { + if (hists__add_entry(&evsel->hists, &al, sample->period, sample->weight)) { pr_warning("problem incrementing symbol period, skipping event\n"); return -1; } diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 78a41fdbe56c..cdf58ecc04b1 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -953,6 +953,8 @@ const struct option record_options[] = { OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack, "branch filter mask", "branch stack filter modes", parse_branch_stack), + OPT_BOOLEAN('W', "weight", &record.opts.sample_weight, + "sample by weight (on special events only)"), OPT_END() }; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index b5ea26cc7eb1..e31f070abe2f 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -98,7 +98,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, * and not events sampled. Thus we use a pseudo period of 1. */ he = __hists__add_branch_entry(&evsel->hists, al, parent, - &bi[i], 1); + &bi[i], 1, 1); if (he) { struct annotation *notes; err = -ENOMEM; @@ -156,7 +156,8 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, return err; } - he = __hists__add_entry(&evsel->hists, al, parent, sample->period); + he = __hists__add_entry(&evsel->hists, al, parent, sample->period, + sample->weight); if (he == NULL) return -ENOMEM; @@ -644,7 +645,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) "Use the stdio interface"), OPT_STRING('s', "sort", &sort_order, "key[,key2...]", "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," - " dso_to, dso_from, symbol_to, symbol_from, mispredict"), + " dso_to, dso_from, symbol_to, symbol_from, mispredict," + " weight, local_weight"), OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, "Show sample percentage for different cpu modes"), OPT_STRING('p', "parent", &parent_pattern, "regex", diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index b5520ad0dbb8..67bdb9f14ad6 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -251,7 +251,8 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, { struct hist_entry *he; - he = __hists__add_entry(&evsel->hists, al, NULL, sample->period); + he = __hists__add_entry(&evsel->hists, al, NULL, sample->period, + sample->weight); if (he == NULL) return NULL; @@ -1088,7 +1089,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_STRING('s', "sort", &sort_order, "key[,key2...]", - "sort by key(s): pid, comm, dso, symbol, parent"), + "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight"), OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, "Show a column with the number of samples"), OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 74659ecf93e0..32bd102c32b6 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -218,6 +218,7 @@ struct perf_record_opts { bool pipe_output; bool raw_samples; bool sample_address; + bool sample_weight; bool sample_time; bool period; unsigned int freq; diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index e0c0267858a1..89085a9615e2 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -223,7 +223,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) &sample, 0) < 0) goto out; - he = __hists__add_entry(&evsel->hists, &al, NULL, 1); + he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); if (he == NULL) goto out; @@ -247,7 +247,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) &sample, 0) < 0) goto out; - he = __hists__add_entry(&evsel->hists, &al, NULL, 1); + he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); if (he == NULL) goto out; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 0d573ff4771a..a97fbbe6b3b3 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -88,6 +88,7 @@ struct perf_sample { u64 id; u64 stream_id; u64 period; + u64 weight; u32 cpu; u32 raw_size; void *raw_data; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1adb824610f0..23061a6ccd77 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -563,6 +563,9 @@ void perf_evsel__config(struct perf_evsel *evsel, attr->branch_sample_type = opts->branch_stack; } + if (opts->sample_weight) + attr->sample_type |= PERF_SAMPLE_WEIGHT; + attr->mmap = track; attr->comm = track; @@ -1017,6 +1020,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, data->cpu = data->pid = data->tid = -1; data->stream_id = data->id = data->time = -1ULL; data->period = 1; + data->weight = 0; if (event->header.type != PERF_RECORD_SAMPLE) { if (!evsel->attr.sample_id_all) @@ -1167,6 +1171,12 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, } } + data->weight = 0; + if (type & PERF_SAMPLE_WEIGHT) { + data->weight = *array; + array++; + } + return 0; } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f855941bebea..97ddd18acd7c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -155,9 +155,11 @@ static void hist_entry__add_cpumode_period(struct hist_entry *he, } } -static void he_stat__add_period(struct he_stat *he_stat, u64 period) +static void he_stat__add_period(struct he_stat *he_stat, u64 period, + u64 weight) { he_stat->period += period; + he_stat->weight += weight; he_stat->nr_events += 1; } @@ -169,12 +171,14 @@ static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src) dest->period_guest_sys += src->period_guest_sys; dest->period_guest_us += src->period_guest_us; dest->nr_events += src->nr_events; + dest->weight += src->weight; } static void hist_entry__decay(struct hist_entry *he) { he->stat.period = (he->stat.period * 7) / 8; he->stat.nr_events = (he->stat.nr_events * 7) / 8; + /* XXX need decay for weight too? */ } static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) @@ -282,7 +286,8 @@ static u8 symbol__parent_filter(const struct symbol *parent) static struct hist_entry *add_hist_entry(struct hists *hists, struct hist_entry *entry, struct addr_location *al, - u64 period) + u64 period, + u64 weight) { struct rb_node **p; struct rb_node *parent = NULL; @@ -306,7 +311,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, cmp = hist_entry__cmp(he, entry); if (!cmp) { - he_stat__add_period(&he->stat, period); + he_stat__add_period(&he->stat, period, weight); /* If the map of an existing hist_entry has * become out-of-date due to an exec() or @@ -345,7 +350,8 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, struct addr_location *al, struct symbol *sym_parent, struct branch_info *bi, - u64 period) + u64 period, + u64 weight) { struct hist_entry entry = { .thread = al->thread, @@ -359,6 +365,7 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, .stat = { .period = period, .nr_events = 1, + .weight = weight, }, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), @@ -366,12 +373,13 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, .hists = self, }; - return add_hist_entry(self, &entry, al, period); + return add_hist_entry(self, &entry, al, period, weight); } struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, - struct symbol *sym_parent, u64 period) + struct symbol *sym_parent, u64 period, + u64 weight) { struct hist_entry entry = { .thread = al->thread, @@ -385,13 +393,14 @@ struct hist_entry *__hists__add_entry(struct hists *self, .stat = { .period = period, .nr_events = 1, + .weight = weight, }, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), .hists = self, }; - return add_hist_entry(self, &entry, al, period); + return add_hist_entry(self, &entry, al, period, weight); } int64_t diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 848331377bdb..121cc14b6041 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -49,6 +49,8 @@ enum hist_column { HISTC_DSO_FROM, HISTC_DSO_TO, HISTC_SRCLINE, + HISTC_LOCAL_WEIGHT, + HISTC_GLOBAL_WEIGHT, HISTC_NR_COLS, /* Last entry */ }; @@ -73,7 +75,8 @@ struct hists { struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, - struct symbol *parent, u64 period); + struct symbol *parent, u64 period, + u64 weight); int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, @@ -84,7 +87,8 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, struct addr_location *al, struct symbol *sym_parent, struct branch_info *bi, - u64 period); + u64 period, + u64 weight); void hists__output_resort(struct hists *self); void hists__output_resort_threaded(struct hists *hists); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index c8ba120b0dbe..627be09b479e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -798,6 +798,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, if (sample_type & PERF_SAMPLE_STACK_USER) stack_user__printf(&sample->user_stack); + + if (sample_type & PERF_SAMPLE_WEIGHT) + printf("... weight: %" PRIu64 "\n", sample->weight); } static struct machine * diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index d41926cb9e3f..d66bcd33248c 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -464,6 +464,49 @@ struct sort_entry sort_mispredict = { .se_width_idx = HISTC_MISPREDICT, }; +static u64 he_weight(struct hist_entry *he) +{ + return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0; +} + +static int64_t +sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return he_weight(left) - he_weight(right); +} + +static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self)); +} + +struct sort_entry sort_local_weight = { + .se_header = "Local Weight", + .se_cmp = sort__local_weight_cmp, + .se_snprintf = hist_entry__local_weight_snprintf, + .se_width_idx = HISTC_LOCAL_WEIGHT, +}; + +static int64_t +sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return left->stat.weight - right->stat.weight; +} + +static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight); +} + +struct sort_entry sort_global_weight = { + .se_header = "Weight", + .se_cmp = sort__global_weight_cmp, + .se_snprintf = hist_entry__global_weight_snprintf, + .se_width_idx = HISTC_GLOBAL_WEIGHT, +}; + struct sort_dimension { const char *name; struct sort_entry *entry; @@ -480,6 +523,8 @@ static struct sort_dimension common_sort_dimensions[] = { DIM(SORT_PARENT, "parent", sort_parent), DIM(SORT_CPU, "cpu", sort_cpu), DIM(SORT_SRCLINE, "srcline", sort_srcline), + DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), + DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), }; #undef DIM diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index b13e56f6ccbe..393925012796 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -49,6 +49,7 @@ struct he_stat { u64 period_us; u64 period_guest_sys; u64 period_guest_us; + u64 weight; u32 nr_events; }; @@ -130,6 +131,8 @@ enum sort_type { SORT_PARENT, SORT_CPU, SORT_SRCLINE, + SORT_LOCAL_WEIGHT, + SORT_GLOBAL_WEIGHT, /* branch stack specific sort keys */ __SORT_BRANCH_STACK, -- cgit v1.2.3 From 98a3b32c99ada4bca8aaf4f91efd96fc906dd5c4 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 24 Jan 2013 16:10:35 +0100 Subject: perf tools: Add mem access sampling core support This patch adds the sorting and histogram support functions to enable profiling of memory accesses. The following sorting orders are added: - symbol_daddr: data address symbol (or raw address) - dso_daddr: data address shared object - locked: access uses locked transaction - tlb : TLB access - mem : memory level of the access (L1, L2, L3, RAM, ...) - snoop: access snoop mode Signed-off-by: Stephane Eranian Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1359040242-8269-12-git-send-email-eranian@google.com [ committer note: changed to cope with fc5871ed, the move of methods to machine.[ch], and the rename of dsrc to data_src, to match the change made in the PERF_SAMPLE_DSRC in a previous patch. ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.h | 8 + tools/perf/util/evsel.c | 6 + tools/perf/util/hist.c | 86 ++++++++++- tools/perf/util/hist.h | 13 ++ tools/perf/util/machine.c | 32 ++++ tools/perf/util/machine.h | 3 + tools/perf/util/session.c | 3 + tools/perf/util/sort.c | 369 +++++++++++++++++++++++++++++++++++++++++++++- tools/perf/util/sort.h | 9 +- tools/perf/util/symbol.h | 6 + 10 files changed, 525 insertions(+), 10 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index a97fbbe6b3b3..181389535c0c 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -91,6 +91,7 @@ struct perf_sample { u64 weight; u32 cpu; u32 raw_size; + u64 data_src; void *raw_data; struct ip_callchain *callchain; struct branch_stack *branch_stack; @@ -98,6 +99,13 @@ struct perf_sample { struct stack_dump user_stack; }; +#define PERF_MEM_DATA_SRC_NONE \ + (PERF_MEM_S(OP, NA) |\ + PERF_MEM_S(LVL, NA) |\ + PERF_MEM_S(SNOOP, NA) |\ + PERF_MEM_S(LOCK, NA) |\ + PERF_MEM_S(TLB, NA)) + struct build_id_event { struct perf_event_header header; pid_t pid; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 23061a6ccd77..5c4ca51c8f7b 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1177,6 +1177,12 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, array++; } + data->data_src = PERF_MEM_DATA_SRC_NONE; + if (type & PERF_SAMPLE_DATA_SRC) { + data->data_src = *array; + array++; + } + return 0; } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 97ddd18acd7c..99cc719ce736 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -67,12 +67,16 @@ static void hists__set_unres_dso_col_len(struct hists *hists, int dso) void hists__calc_col_len(struct hists *hists, struct hist_entry *h) { const unsigned int unresolved_col_width = BITS_PER_LONG / 4; + int symlen; u16 len; if (h->ms.sym) hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4); - else + else { + symlen = unresolved_col_width + 4 + 2; + hists__new_col_len(hists, HISTC_SYMBOL, symlen); hists__set_unres_dso_col_len(hists, HISTC_DSO); + } len = thread__comm_len(h->thread); if (hists__new_col_len(hists, HISTC_COMM, len)) @@ -87,7 +91,6 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen); if (h->branch_info) { - int symlen; /* * +4 accounts for '[x] ' priv level info * +2 account of 0x prefix on raw addresses @@ -116,6 +119,42 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) hists__set_unres_dso_col_len(hists, HISTC_DSO_TO); } } + + if (h->mem_info) { + /* + * +4 accounts for '[x] ' priv level info + * +2 account of 0x prefix on raw addresses + */ + if (h->mem_info->daddr.sym) { + symlen = (int)h->mem_info->daddr.sym->namelen + 4 + + unresolved_col_width + 2; + hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, + symlen); + } else { + symlen = unresolved_col_width + 4 + 2; + hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, + symlen); + } + if (h->mem_info->daddr.map) { + symlen = dso__name_len(h->mem_info->daddr.map->dso); + hists__new_col_len(hists, HISTC_MEM_DADDR_DSO, + symlen); + } else { + symlen = unresolved_col_width + 4 + 2; + hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO); + } + } else { + symlen = unresolved_col_width + 4 + 2; + hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, symlen); + hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO); + } + + hists__new_col_len(hists, HISTC_MEM_LOCKED, 6); + hists__new_col_len(hists, HISTC_MEM_TLB, 22); + hists__new_col_len(hists, HISTC_MEM_SNOOP, 12); + hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3); + hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12); + hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); } void hists__output_recalc_col_len(struct hists *hists, int max_rows) @@ -158,6 +197,7 @@ static void hist_entry__add_cpumode_period(struct hist_entry *he, static void he_stat__add_period(struct he_stat *he_stat, u64 period, u64 weight) { + he_stat->period += period; he_stat->weight += weight; he_stat->nr_events += 1; @@ -243,7 +283,7 @@ void hists__decay_entries_threaded(struct hists *hists, static struct hist_entry *hist_entry__new(struct hist_entry *template) { size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; - struct hist_entry *he = malloc(sizeof(*he) + callchain_size); + struct hist_entry *he = zalloc(sizeof(*he) + callchain_size); if (he != NULL) { *he = *template; @@ -258,6 +298,13 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) he->branch_info->to.map->referenced = true; } + if (he->mem_info) { + if (he->mem_info->iaddr.map) + he->mem_info->iaddr.map->referenced = true; + if (he->mem_info->daddr.map) + he->mem_info->daddr.map->referenced = true; + } + if (symbol_conf.use_callchain) callchain_init(he->callchain); @@ -346,6 +393,36 @@ out_unlock: return he; } +struct hist_entry *__hists__add_mem_entry(struct hists *self, + struct addr_location *al, + struct symbol *sym_parent, + struct mem_info *mi, + u64 period, + u64 weight) +{ + struct hist_entry entry = { + .thread = al->thread, + .ms = { + .map = al->map, + .sym = al->sym, + }, + .stat = { + .period = period, + .weight = weight, + .nr_events = 1, + }, + .cpu = al->cpu, + .ip = al->addr, + .level = al->level, + .parent = sym_parent, + .filtered = symbol__parent_filter(sym_parent), + .hists = self, + .mem_info = mi, + .branch_info = NULL, + }; + return add_hist_entry(self, &entry, al, period, weight); +} + struct hist_entry *__hists__add_branch_entry(struct hists *self, struct addr_location *al, struct symbol *sym_parent, @@ -371,6 +448,7 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, .filtered = symbol__parent_filter(sym_parent), .branch_info = bi, .hists = self, + .mem_info = NULL, }; return add_hist_entry(self, &entry, al, period, weight); @@ -398,6 +476,8 @@ struct hist_entry *__hists__add_entry(struct hists *self, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), .hists = self, + .branch_info = NULL, + .mem_info = NULL, }; return add_hist_entry(self, &entry, al, period, weight); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 121cc14b6041..fd6313416476 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -51,6 +51,12 @@ enum hist_column { HISTC_SRCLINE, HISTC_LOCAL_WEIGHT, HISTC_GLOBAL_WEIGHT, + HISTC_MEM_DADDR_SYMBOL, + HISTC_MEM_DADDR_DSO, + HISTC_MEM_LOCKED, + HISTC_MEM_TLB, + HISTC_MEM_LVL, + HISTC_MEM_SNOOP, HISTC_NR_COLS, /* Last entry */ }; @@ -90,6 +96,13 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, u64 period, u64 weight); +struct hist_entry *__hists__add_mem_entry(struct hists *self, + struct addr_location *al, + struct symbol *sym_parent, + struct mem_info *mi, + u64 period, + u64 weight); + void hists__output_resort(struct hists *self); void hists__output_resort_threaded(struct hists *hists); void hists__collapse_resort(struct hists *self); diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index c5e3b123782b..d77ba869d7ed 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1097,6 +1097,38 @@ found: ams->map = al.map; } +static void ip__resolve_data(struct machine *machine, struct thread *thread, + u8 m, struct addr_map_symbol *ams, u64 addr) +{ + struct addr_location al; + + memset(&al, 0, sizeof(al)); + + thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, &al, + NULL); + ams->addr = addr; + ams->al_addr = al.addr; + ams->sym = al.sym; + ams->map = al.map; +} + +struct mem_info *machine__resolve_mem(struct machine *machine, + struct thread *thr, + struct perf_sample *sample, + u8 cpumode) +{ + struct mem_info *mi = zalloc(sizeof(*mi)); + + if (!mi) + return NULL; + + ip__resolve_ams(machine, thr, &mi->iaddr, sample->ip); + ip__resolve_data(machine, thr, cpumode, &mi->daddr, sample->addr); + mi->data_src.val = sample->data_src; + + return mi; +} + struct branch_info *machine__resolve_bstack(struct machine *machine, struct thread *thr, struct branch_stack *bs) diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index e0b2c00b2e75..77940680f1fc 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -76,6 +76,9 @@ void machine__delete(struct machine *machine); struct branch_info *machine__resolve_bstack(struct machine *machine, struct thread *thread, struct branch_stack *bs); +struct mem_info *machine__resolve_mem(struct machine *machine, + struct thread *thread, + struct perf_sample *sample, u8 cpumode); int machine__resolve_callchain(struct machine *machine, struct perf_evsel *evsel, struct thread *thread, diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 627be09b479e..cf1fe01b7e89 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -801,6 +801,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, if (sample_type & PERF_SAMPLE_WEIGHT) printf("... weight: %" PRIu64 "\n", sample->weight); + + if (sample_type & PERF_SAMPLE_DATA_SRC) + printf(" . data_src: 0x%"PRIx64"\n", sample->data_src); } static struct machine * diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index d66bcd33248c..32a1ef15912c 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -198,11 +198,19 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, } ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); - if (sym) - ret += repsep_snprintf(bf + ret, size - ret, "%-*s", - width - ret, - sym->name); - else { + if (sym && map) { + if (map->type == MAP__VARIABLE) { + ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); + ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", + ip - sym->start); + ret += repsep_snprintf(bf + ret, size - ret, "%-*s", + width - ret, ""); + } else { + ret += repsep_snprintf(bf + ret, size - ret, "%-*s", + width - ret, + sym->name); + } + } else { size_t len = BITS_PER_LONG / 4; ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", len, ip); @@ -457,6 +465,304 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, return repsep_snprintf(bf, size, "%-*s", width, out); } +/* --sort daddr_sym */ +static int64_t +sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right) +{ + uint64_t l = 0, r = 0; + + if (left->mem_info) + l = left->mem_info->daddr.addr; + if (right->mem_info) + r = right->mem_info->daddr.addr; + + return (int64_t)(r - l); +} + +static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + uint64_t addr = 0; + struct map *map = NULL; + struct symbol *sym = NULL; + + if (self->mem_info) { + addr = self->mem_info->daddr.addr; + map = self->mem_info->daddr.map; + sym = self->mem_info->daddr.sym; + } + return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size, + width); +} + +static int64_t +sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right) +{ + struct map *map_l = NULL; + struct map *map_r = NULL; + + if (left->mem_info) + map_l = left->mem_info->daddr.map; + if (right->mem_info) + map_r = right->mem_info->daddr.map; + + return _sort__dso_cmp(map_l, map_r); +} + +static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + struct map *map = NULL; + + if (self->mem_info) + map = self->mem_info->daddr.map; + + return _hist_entry__dso_snprintf(map, bf, size, width); +} + +static int64_t +sort__locked_cmp(struct hist_entry *left, struct hist_entry *right) +{ + union perf_mem_data_src data_src_l; + union perf_mem_data_src data_src_r; + + if (left->mem_info) + data_src_l = left->mem_info->data_src; + else + data_src_l.mem_lock = PERF_MEM_LOCK_NA; + + if (right->mem_info) + data_src_r = right->mem_info->data_src; + else + data_src_r.mem_lock = PERF_MEM_LOCK_NA; + + return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock); +} + +static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + const char *out; + u64 mask = PERF_MEM_LOCK_NA; + + if (self->mem_info) + mask = self->mem_info->data_src.mem_lock; + + if (mask & PERF_MEM_LOCK_NA) + out = "N/A"; + else if (mask & PERF_MEM_LOCK_LOCKED) + out = "Yes"; + else + out = "No"; + + return repsep_snprintf(bf, size, "%-*s", width, out); +} + +static int64_t +sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right) +{ + union perf_mem_data_src data_src_l; + union perf_mem_data_src data_src_r; + + if (left->mem_info) + data_src_l = left->mem_info->data_src; + else + data_src_l.mem_dtlb = PERF_MEM_TLB_NA; + + if (right->mem_info) + data_src_r = right->mem_info->data_src; + else + data_src_r.mem_dtlb = PERF_MEM_TLB_NA; + + return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb); +} + +static const char * const tlb_access[] = { + "N/A", + "HIT", + "MISS", + "L1", + "L2", + "Walker", + "Fault", +}; +#define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) + +static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + char out[64]; + size_t sz = sizeof(out) - 1; /* -1 for null termination */ + size_t l = 0, i; + u64 m = PERF_MEM_TLB_NA; + u64 hit, miss; + + out[0] = '\0'; + + if (self->mem_info) + m = self->mem_info->data_src.mem_dtlb; + + hit = m & PERF_MEM_TLB_HIT; + miss = m & PERF_MEM_TLB_MISS; + + /* already taken care of */ + m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS); + + for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) { + if (!(m & 0x1)) + continue; + if (l) { + strcat(out, " or "); + l += 4; + } + strncat(out, tlb_access[i], sz - l); + l += strlen(tlb_access[i]); + } + if (*out == '\0') + strcpy(out, "N/A"); + if (hit) + strncat(out, " hit", sz - l); + if (miss) + strncat(out, " miss", sz - l); + + return repsep_snprintf(bf, size, "%-*s", width, out); +} + +static int64_t +sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right) +{ + union perf_mem_data_src data_src_l; + union perf_mem_data_src data_src_r; + + if (left->mem_info) + data_src_l = left->mem_info->data_src; + else + data_src_l.mem_lvl = PERF_MEM_LVL_NA; + + if (right->mem_info) + data_src_r = right->mem_info->data_src; + else + data_src_r.mem_lvl = PERF_MEM_LVL_NA; + + return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl); +} + +static const char * const mem_lvl[] = { + "N/A", + "HIT", + "MISS", + "L1", + "LFB", + "L2", + "L3", + "Local RAM", + "Remote RAM (1 hop)", + "Remote RAM (2 hops)", + "Remote Cache (1 hop)", + "Remote Cache (2 hops)", + "I/O", + "Uncached", +}; +#define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) + +static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + char out[64]; + size_t sz = sizeof(out) - 1; /* -1 for null termination */ + size_t i, l = 0; + u64 m = PERF_MEM_LVL_NA; + u64 hit, miss; + + if (self->mem_info) + m = self->mem_info->data_src.mem_lvl; + + out[0] = '\0'; + + hit = m & PERF_MEM_LVL_HIT; + miss = m & PERF_MEM_LVL_MISS; + + /* already taken care of */ + m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS); + + for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) { + if (!(m & 0x1)) + continue; + if (l) { + strcat(out, " or "); + l += 4; + } + strncat(out, mem_lvl[i], sz - l); + l += strlen(mem_lvl[i]); + } + if (*out == '\0') + strcpy(out, "N/A"); + if (hit) + strncat(out, " hit", sz - l); + if (miss) + strncat(out, " miss", sz - l); + + return repsep_snprintf(bf, size, "%-*s", width, out); +} + +static int64_t +sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right) +{ + union perf_mem_data_src data_src_l; + union perf_mem_data_src data_src_r; + + if (left->mem_info) + data_src_l = left->mem_info->data_src; + else + data_src_l.mem_snoop = PERF_MEM_SNOOP_NA; + + if (right->mem_info) + data_src_r = right->mem_info->data_src; + else + data_src_r.mem_snoop = PERF_MEM_SNOOP_NA; + + return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop); +} + +static const char * const snoop_access[] = { + "N/A", + "None", + "Miss", + "Hit", + "HitM", +}; +#define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) + +static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + char out[64]; + size_t sz = sizeof(out) - 1; /* -1 for null termination */ + size_t i, l = 0; + u64 m = PERF_MEM_SNOOP_NA; + + out[0] = '\0'; + + if (self->mem_info) + m = self->mem_info->data_src.mem_snoop; + + for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) { + if (!(m & 0x1)) + continue; + if (l) { + strcat(out, " or "); + l += 4; + } + strncat(out, snoop_access[i], sz - l); + l += strlen(snoop_access[i]); + } + + if (*out == '\0') + strcpy(out, "N/A"); + + return repsep_snprintf(bf, size, "%-*s", width, out); +} + struct sort_entry sort_mispredict = { .se_header = "Branch Mispredicted", .se_cmp = sort__mispredict_cmp, @@ -507,6 +813,48 @@ struct sort_entry sort_global_weight = { .se_width_idx = HISTC_GLOBAL_WEIGHT, }; +struct sort_entry sort_mem_daddr_sym = { + .se_header = "Data Symbol", + .se_cmp = sort__daddr_cmp, + .se_snprintf = hist_entry__daddr_snprintf, + .se_width_idx = HISTC_MEM_DADDR_SYMBOL, +}; + +struct sort_entry sort_mem_daddr_dso = { + .se_header = "Data Object", + .se_cmp = sort__dso_daddr_cmp, + .se_snprintf = hist_entry__dso_daddr_snprintf, + .se_width_idx = HISTC_MEM_DADDR_SYMBOL, +}; + +struct sort_entry sort_mem_locked = { + .se_header = "Locked", + .se_cmp = sort__locked_cmp, + .se_snprintf = hist_entry__locked_snprintf, + .se_width_idx = HISTC_MEM_LOCKED, +}; + +struct sort_entry sort_mem_tlb = { + .se_header = "TLB access", + .se_cmp = sort__tlb_cmp, + .se_snprintf = hist_entry__tlb_snprintf, + .se_width_idx = HISTC_MEM_TLB, +}; + +struct sort_entry sort_mem_lvl = { + .se_header = "Memory access", + .se_cmp = sort__lvl_cmp, + .se_snprintf = hist_entry__lvl_snprintf, + .se_width_idx = HISTC_MEM_LVL, +}; + +struct sort_entry sort_mem_snoop = { + .se_header = "Snoop", + .se_cmp = sort__snoop_cmp, + .se_snprintf = hist_entry__snoop_snprintf, + .se_width_idx = HISTC_MEM_SNOOP, +}; + struct sort_dimension { const char *name; struct sort_entry *entry; @@ -525,6 +873,12 @@ static struct sort_dimension common_sort_dimensions[] = { DIM(SORT_SRCLINE, "srcline", sort_srcline), DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), + DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym), + DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso), + DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked), + DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb), + DIM(SORT_MEM_LVL, "mem", sort_mem_lvl), + DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), }; #undef DIM @@ -561,7 +915,10 @@ int sort_dimension__add(const char *tok) return -EINVAL; } sort__has_parent = 1; - } else if (sd->entry == &sort_sym) { + } else if (sd->entry == &sort_sym || + sd->entry == &sort_sym_from || + sd->entry == &sort_sym_to || + sd->entry == &sort_mem_daddr_sym) { sort__has_sym = 1; } diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 393925012796..f24bdf64238c 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -101,7 +101,8 @@ struct hist_entry { struct rb_root sorted_chain; struct branch_info *branch_info; struct hists *hists; - struct callchain_root callchain[0]; + struct mem_info *mem_info; + struct callchain_root callchain[0]; /* must be last member */ }; static inline bool hist_entry__has_pairs(struct hist_entry *he) @@ -133,6 +134,12 @@ enum sort_type { SORT_SRCLINE, SORT_LOCAL_WEIGHT, SORT_GLOBAL_WEIGHT, + SORT_MEM_DADDR_SYMBOL, + SORT_MEM_DADDR_DSO, + SORT_MEM_LOCKED, + SORT_MEM_TLB, + SORT_MEM_LVL, + SORT_MEM_SNOOP, /* branch stack specific sort keys */ __SORT_BRANCH_STACK, diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index d7654c23861a..5f720dc076da 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -156,6 +156,12 @@ struct branch_info { struct branch_flags flags; }; +struct mem_info { + struct addr_map_symbol iaddr; + struct addr_map_symbol daddr; + union perf_mem_data_src data_src; +}; + struct addr_location { struct thread *thread; struct map *map; -- cgit v1.2.3 From ccf49bfc6bb1025788637417780e9f1eeae9fc37 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 24 Jan 2013 16:10:37 +0100 Subject: perf record: Add support for mem access profiling We use the -W option to obtain the cost of the memory accesses. Data address sampling is obtained via the -d option. Signed-off-by: Stephane Eranian Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1359040242-8269-14-git-send-email-eranian@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 5c4ca51c8f7b..07b1a3ad3e24 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -554,6 +554,9 @@ void perf_evsel__config(struct perf_evsel *evsel, perf_evsel__set_sample_bit(evsel, CPU); } + if (opts->sample_address) + attr->sample_type |= PERF_SAMPLE_DATA_SRC; + if (opts->no_delay) { attr->watermark = 0; attr->wakeup_events = 1; -- cgit v1.2.3 From f4f7e28d0e813ddb997f49ae718ddf98db972292 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 24 Jan 2013 16:10:36 +0100 Subject: perf report: Add support for mem access profiling This patch adds the --mem-mode option to perf report. This mode requires a perf.data file created with memory access samples. Signed-off-by: Stephane Eranian Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1359040242-8269-13-git-send-email-eranian@google.com [ Removed duplicates in the --sort help, man page needs updating, Fixed minor conflict with 328ccda "perf report: Add --no-demangle option" ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 135 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 131 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index e31f070abe2f..a20550c9cd68 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -46,6 +46,7 @@ struct perf_report { bool show_full_info; bool show_threads; bool inverted_callchain; + bool mem_mode; struct perf_read_values show_threads_values; const char *pretty_printing_style; symbol_filter_t annotate_init; @@ -64,6 +65,99 @@ static int perf_report_config(const char *var, const char *value, void *cb) return perf_default_config(var, value, cb); } +static int perf_report__add_mem_hist_entry(struct perf_tool *tool, + struct addr_location *al, + struct perf_sample *sample, + struct perf_evsel *evsel, + struct machine *machine, + union perf_event *event) +{ + struct perf_report *rep = container_of(tool, struct perf_report, tool); + struct symbol *parent = NULL; + u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + int err = 0; + struct hist_entry *he; + struct mem_info *mi, *mx; + uint64_t cost; + + if ((sort__has_parent || symbol_conf.use_callchain) && + sample->callchain) { + err = machine__resolve_callchain(machine, evsel, al->thread, + sample, &parent); + if (err) + return err; + } + + mi = machine__resolve_mem(machine, al->thread, sample, cpumode); + if (!mi) + return -ENOMEM; + + if (rep->hide_unresolved && !al->sym) + return 0; + + cost = sample->weight; + if (!cost) + cost = 1; + + /* + * must pass period=weight in order to get the correct + * sorting from hists__collapse_resort() which is solely + * based on periods. We want sorting be done on nr_events * weight + * and this is indirectly achieved by passing period=weight here + * and the he_stat__add_period() function. + */ + he = __hists__add_mem_entry(&evsel->hists, al, parent, mi, cost, cost); + if (!he) + return -ENOMEM; + + /* + * In the newt browser, we are doing integrated annotation, + * so we don't allocate the extra space needed because the stdio + * code will not use it. + */ + if (sort__has_sym && he->ms.sym && use_browser > 0) { + struct annotation *notes = symbol__annotation(he->ms.sym); + + assert(evsel != NULL); + + if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0) + goto out; + + err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); + if (err) + goto out; + } + + if (sort__has_sym && he->mem_info->daddr.sym && use_browser > 0) { + struct annotation *notes; + + mx = he->mem_info; + + notes = symbol__annotation(mx->daddr.sym); + if (notes->src == NULL && symbol__alloc_hist(mx->daddr.sym) < 0) + goto out; + + err = symbol__inc_addr_samples(mx->daddr.sym, + mx->daddr.map, + evsel->idx, + mx->daddr.al_addr); + if (err) + goto out; + } + + evsel->hists.stats.total_period += cost; + hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); + err = 0; + + if (symbol_conf.use_callchain) { + err = callchain_append(he->callchain, + &callchain_cursor, + sample->period); + } +out: + return err; +} + static int perf_report__add_branch_hist_entry(struct perf_tool *tool, struct addr_location *al, struct perf_sample *sample, @@ -220,6 +314,12 @@ static int process_sample_event(struct perf_tool *tool, pr_debug("problem adding lbr entry, skipping event\n"); return -1; } + } else if (rep->mem_mode == 1) { + if (perf_report__add_mem_hist_entry(tool, &al, sample, + evsel, machine, event)) { + pr_debug("problem adding mem entry, skipping event\n"); + return -1; + } } else { if (al.map != NULL) al.map->dso->hit = 1; @@ -303,7 +403,8 @@ static void sig_handler(int sig __maybe_unused) session_done = 1; } -static size_t hists__fprintf_nr_sample_events(struct hists *self, +static size_t hists__fprintf_nr_sample_events(struct perf_report *rep, + struct hists *self, const char *evname, FILE *fp) { size_t ret; @@ -331,7 +432,11 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, if (evname != NULL) ret += fprintf(fp, " of event '%s'", evname); - ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events); + if (rep->mem_mode) { + ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events); + ret += fprintf(fp, "\n# Sort order : %s", sort_order); + } else + ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events); return ret + fprintf(fp, "\n#\n"); } @@ -349,7 +454,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, !perf_evsel__is_group_leader(pos)) continue; - hists__fprintf_nr_sample_events(hists, evname, stdout); + hists__fprintf_nr_sample_events(rep, hists, evname, stdout); hists__fprintf(hists, true, 0, 0, stdout); fprintf(stdout, "\n\n"); } @@ -646,7 +751,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) OPT_STRING('s', "sort", &sort_order, "key[,key2...]", "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," " dso_to, dso_from, symbol_to, symbol_from, mispredict," - " weight, local_weight"), + " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " + "snoop, locked"), OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, "Show sample percentage for different cpu modes"), OPT_STRING('p', "parent", &parent_pattern, "regex", @@ -696,6 +802,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) "objdump binary to use for disassembly and annotations"), OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, "Disable symbol demangling"), + OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), OPT_END() }; @@ -753,6 +860,18 @@ repeat: "dso_to,symbol_to"; } + if (report.mem_mode) { + if (sort__branch_mode == 1) { + fprintf(stderr, "branch and mem mode incompatible\n"); + goto error; + } + /* + * if no sort_order is provided, then specify + * branch-mode specific order + */ + if (sort_order == default_sort_order) + sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; + } if (setup_sorting() < 0) usage_with_options(report_usage, options); @@ -818,6 +937,14 @@ repeat: sort_entry__setup_elide(&sort_sym_from, symbol_conf.sym_from_list, "sym_from", stdout); sort_entry__setup_elide(&sort_sym_to, symbol_conf.sym_to_list, "sym_to", stdout); } else { + if (report.mem_mode) { + sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "symbol_daddr", stdout); + sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso_daddr", stdout); + sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "mem", stdout); + sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "local_weight", stdout); + sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "tlb", stdout); + sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "snoop", stdout); + } sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); } -- cgit v1.2.3 From 028f12ee6beff0961781c5ed3f740e5f3b56f781 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 24 Jan 2013 16:10:38 +0100 Subject: perf tools: Add new mem command for memory access profiling This new command is a wrapper on top of perf record and perf report to make it easier to configure for memory access profiling. To record loads: $ perf mem -t load rec ..... To record stores: $ perf mem -t store rec ..... To get the report: $ perf mem -t load rep Signed-off-by: Stephane Eranian Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1359040242-8269-15-git-send-email-eranian@google.com [ Fixed minor conflict with 66857b5 "Sort command-list.txt alphabetically" ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-mem.txt | 48 +++++++ tools/perf/Makefile | 1 + tools/perf/builtin-mem.c | 242 ++++++++++++++++++++++++++++++++++ tools/perf/builtin.h | 1 + tools/perf/command-list.txt | 1 + tools/perf/perf.c | 1 + tools/perf/util/hist.c | 1 + 7 files changed, 295 insertions(+) create mode 100644 tools/perf/Documentation/perf-mem.txt create mode 100644 tools/perf/builtin-mem.c (limited to 'tools') diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt new file mode 100644 index 000000000000..888d51137fbe --- /dev/null +++ b/tools/perf/Documentation/perf-mem.txt @@ -0,0 +1,48 @@ +perf-mem(1) +=========== + +NAME +---- +perf-mem - Profile memory accesses + +SYNOPSIS +-------- +[verse] +'perf mem' [] (record [] | report) + +DESCRIPTION +----------- +"perf mem -t record" runs a command and gathers memory operation data +from it, into perf.data. Perf record options are accepted and are passed through. + +"perf mem -t report" displays the result. It invokes perf report with the +right set of options to display a memory access profile. + +OPTIONS +------- +...:: + Any command you can specify in a shell. + +-t:: +--type=:: + Select the memory operation type: load or store (default: load) + +-D:: +--dump-raw-samples=:: + Dump the raw decoded samples on the screen in a format that is easy to parse with + one sample per line. + +-x:: +--field-separator:: + Specify the field separator used when dump raw samples (-D option). By default, + The separator is the space character. + +-C:: +--cpu-list:: + Restrict dump of raw samples to those provided via this option. Note that the same + option can be passed in record mode. It will be interpreted the same way as perf + record. + +SEE ALSO +-------- +linkperf:perf-record[1], linkperf:perf-report[1] diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0230b75ed7f9..07feae773dc1 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -547,6 +547,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-lock.o BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o BUILTIN_OBJS += $(OUTPUT)builtin-inject.o BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o +BUILTIN_OBJS += $(OUTPUT)builtin-mem.o PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT) diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c new file mode 100644 index 000000000000..a8ff6d264e50 --- /dev/null +++ b/tools/perf/builtin-mem.c @@ -0,0 +1,242 @@ +#include "builtin.h" +#include "perf.h" + +#include "util/parse-options.h" +#include "util/trace-event.h" +#include "util/tool.h" +#include "util/session.h" + +#define MEM_OPERATION_LOAD "load" +#define MEM_OPERATION_STORE "store" + +static const char *mem_operation = MEM_OPERATION_LOAD; + +struct perf_mem { + struct perf_tool tool; + char const *input_name; + symbol_filter_t annotate_init; + bool hide_unresolved; + bool dump_raw; + const char *cpu_list; + DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); +}; + +static const char * const mem_usage[] = { + "perf mem [] {record |report}", + NULL +}; + +static int __cmd_record(int argc, const char **argv) +{ + int rec_argc, i = 0, j; + const char **rec_argv; + char event[64]; + int ret; + + rec_argc = argc + 4; + rec_argv = calloc(rec_argc + 1, sizeof(char *)); + if (!rec_argv) + return -1; + + rec_argv[i++] = strdup("record"); + if (!strcmp(mem_operation, MEM_OPERATION_LOAD)) + rec_argv[i++] = strdup("-W"); + rec_argv[i++] = strdup("-d"); + rec_argv[i++] = strdup("-e"); + + if (strcmp(mem_operation, MEM_OPERATION_LOAD)) + sprintf(event, "cpu/mem-stores/pp"); + else + sprintf(event, "cpu/mem-loads/pp"); + + rec_argv[i++] = strdup(event); + for (j = 1; j < argc; j++, i++) + rec_argv[i] = argv[j]; + + ret = cmd_record(i, rec_argv, NULL); + free(rec_argv); + return ret; +} + +static int +dump_raw_samples(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct perf_evsel *evsel __maybe_unused, + struct machine *machine) +{ + struct perf_mem *mem = container_of(tool, struct perf_mem, tool); + struct addr_location al; + const char *fmt; + + if (perf_event__preprocess_sample(event, machine, &al, sample, + mem->annotate_init) < 0) { + fprintf(stderr, "problem processing %d event, skipping it.\n", + event->header.type); + return -1; + } + + if (al.filtered || (mem->hide_unresolved && al.sym == NULL)) + return 0; + + if (al.map != NULL) + al.map->dso->hit = 1; + + if (symbol_conf.field_sep) { + fmt = "%d%s%d%s0x%"PRIx64"%s0x%"PRIx64"%s%"PRIu64 + "%s0x%"PRIx64"%s%s:%s\n"; + } else { + fmt = "%5d%s%5d%s0x%016"PRIx64"%s0x016%"PRIx64 + "%s%5"PRIu64"%s0x%06"PRIx64"%s%s:%s\n"; + symbol_conf.field_sep = " "; + } + + printf(fmt, + sample->pid, + symbol_conf.field_sep, + sample->tid, + symbol_conf.field_sep, + event->ip.ip, + symbol_conf.field_sep, + sample->addr, + symbol_conf.field_sep, + sample->weight, + symbol_conf.field_sep, + sample->data_src, + symbol_conf.field_sep, + al.map ? (al.map->dso ? al.map->dso->long_name : "???") : "???", + al.sym ? al.sym->name : "???"); + + return 0; +} + +static int process_sample_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct perf_evsel *evsel, + struct machine *machine) +{ + return dump_raw_samples(tool, event, sample, evsel, machine); +} + +static int report_raw_events(struct perf_mem *mem) +{ + int err = -EINVAL; + int ret; + struct perf_session *session = perf_session__new(input_name, O_RDONLY, + 0, false, &mem->tool); + + if (session == NULL) + return -ENOMEM; + + if (mem->cpu_list) { + ret = perf_session__cpu_bitmap(session, mem->cpu_list, + mem->cpu_bitmap); + if (ret) + goto out_delete; + } + + if (symbol__init() < 0) + return -1; + + printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n"); + + err = perf_session__process_events(session, &mem->tool); + if (err) + return err; + + return 0; + +out_delete: + perf_session__delete(session); + return err; +} + +static int report_events(int argc, const char **argv, struct perf_mem *mem) +{ + const char **rep_argv; + int ret, i = 0, j, rep_argc; + + if (mem->dump_raw) + return report_raw_events(mem); + + rep_argc = argc + 3; + rep_argv = calloc(rep_argc + 1, sizeof(char *)); + if (!rep_argv) + return -1; + + rep_argv[i++] = strdup("report"); + rep_argv[i++] = strdup("--mem-mode"); + rep_argv[i++] = strdup("-n"); /* display number of samples */ + + /* + * there is no weight (cost) associated with stores, so don't print + * the column + */ + if (strcmp(mem_operation, MEM_OPERATION_LOAD)) + rep_argv[i++] = strdup("--sort=mem,sym,dso,symbol_daddr," + "dso_daddr,tlb,locked"); + + for (j = 1; j < argc; j++, i++) + rep_argv[i] = argv[j]; + + ret = cmd_report(i, rep_argv, NULL); + free(rep_argv); + return ret; +} + +int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) +{ + struct stat st; + struct perf_mem mem = { + .tool = { + .sample = process_sample_event, + .mmap = perf_event__process_mmap, + .comm = perf_event__process_comm, + .lost = perf_event__process_lost, + .fork = perf_event__process_fork, + .build_id = perf_event__process_build_id, + .ordered_samples = true, + }, + .input_name = "perf.data", + }; + const struct option mem_options[] = { + OPT_STRING('t', "type", &mem_operation, + "type", "memory operations(load/store)"), + OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw, + "dump raw samples in ASCII"), + OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved, + "Only display entries resolved to a symbol"), + OPT_STRING('i', "input", &input_name, "file", + "input file name"), + OPT_STRING('C', "cpu", &mem.cpu_list, "cpu", + "list of cpus to profile"), + OPT_STRING('x', "field-separator", &symbol_conf.field_sep, + "separator", + "separator for columns, no spaces will be added" + " between columns '.' is reserved."), + OPT_END() + }; + + argc = parse_options(argc, argv, mem_options, mem_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation)) + usage_with_options(mem_usage, mem_options); + + if (!mem.input_name || !strlen(mem.input_name)) { + if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) + mem.input_name = "-"; + else + mem.input_name = "perf.data"; + } + + if (!strncmp(argv[0], "rec", 3)) + return __cmd_record(argc, argv); + else if (!strncmp(argv[0], "rep", 3)) + return report_events(argc, argv, &mem); + else + usage_with_options(mem_usage, mem_options); + + return 0; +} diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 08143bd854c7..b210d62907e4 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -36,6 +36,7 @@ extern int cmd_kvm(int argc, const char **argv, const char *prefix); extern int cmd_test(int argc, const char **argv, const char *prefix); extern int cmd_trace(int argc, const char **argv, const char *prefix); extern int cmd_inject(int argc, const char **argv, const char *prefix); +extern int cmd_mem(int argc, const char **argv, const char *prefix); extern int find_scripts(char **scripts_array, char **scripts_path_array); #endif diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index a28e31be6cb4..0906fc401c52 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -14,6 +14,7 @@ perf-kmem mainporcelain common perf-kvm mainporcelain common perf-list mainporcelain common perf-lock mainporcelain common +perf-mem mainporcelain common perf-probe mainporcelain full perf-record mainporcelain common perf-report mainporcelain common diff --git a/tools/perf/perf.c b/tools/perf/perf.c index f6ba7b73f40e..31c9380cfa64 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -60,6 +60,7 @@ static struct cmd_struct commands[] = { { "trace", cmd_trace, 0 }, #endif { "inject", cmd_inject, 0 }, + { "mem", cmd_mem, 0 }, }; struct pager_config { diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 99cc719ce736..6b32721f829a 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -520,6 +520,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) void hist_entry__free(struct hist_entry *he) { free(he->branch_info); + free(he->mem_info); free(he); } -- cgit v1.2.3 From bad4091791b0bb8c2d7919ddefe2f0d109299b5a Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 24 Jan 2013 16:10:40 +0100 Subject: perf machine: Detect data vs. text mappings Leverages the PERF_RECORD_MISC_MMAP_DATA bit in the RECORD_MMAP record header. When the bit is set then the mapping type is set to MAP__VARIABLE. Signed-off-by: Stephane Eranian Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1359040242-8269-17-git-send-email-eranian@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index d77ba869d7ed..b2ecad6ec46b 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -955,6 +955,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; struct thread *thread; struct map *map; + enum map_type type; int ret = 0; if (dump_trace) @@ -971,10 +972,17 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event thread = machine__findnew_thread(machine, event->mmap.pid); if (thread == NULL) goto out_problem; + + if (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) + type = MAP__VARIABLE; + else + type = MAP__FUNCTION; + map = map__new(&machine->user_dsos, event->mmap.start, event->mmap.len, event->mmap.pgoff, event->mmap.pid, event->mmap.filename, - MAP__FUNCTION); + type); + if (map == NULL) goto out_problem; -- cgit v1.2.3 From 62667746a6ded2a1fc8dac2e6258f46150b5e46c Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 24 Jan 2013 16:10:42 +0100 Subject: perf tools: Fix output of symbol_daddr offset The symbol addresses in a dso have relative offsets from the start of a mapping. So in order to ouput correct offset value from @ip, one of them should be converted. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1359040242-8269-19-git-send-email-eranian@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 32a1ef15912c..5f52d492590c 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -202,7 +202,7 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, if (map->type == MAP__VARIABLE) { ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", - ip - sym->start); + ip - map->unmap_ip(map, sym->start)); ret += repsep_snprintf(bf + ret, size - ret, "%-*s", width - ret, ""); } else { -- cgit v1.2.3 From 1c6763cb9962d6d83a8470934c20ed525ef62355 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 27 Mar 2013 10:34:49 -0300 Subject: Revert "perf sched: Handle PERF_RECORD_EXIT events" This reverts commit 0439539f72ea222fbfe511b47318b9c1815a7108. This caused this segfault: [root@sandy linux]# perf sched rec ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 1.306 MB perf.data (~57062 samples) ] perf [root@sandy linux]# perf sched lat perf: builtin-sched.c:781: thread_atoms_search: Assertion `!(thread != atoms->thread)' failed. Aborted (core dumped) [root@sandy linux]# Further investigation is needed to check that even with machine__remove_thread() not really deleting the thread referenced in the PERF_RECORD_EXIT (it goes to machine->dead_threads, because references may still exist to them in things like hist, etc) some event later comes for this dead thread and then machine__findnew_thread() will create a new thead instance that will not be the same as the one referenced by work_atoms->thread in thread_atoms_search(). For now just revert this patch to get the 'perf sched lat' back working. Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian echo Link: http://lkml.kernel.org/n/tip-`ranpwd -l 24`@git.kernel.org Link: http://lkml.kernel.org/n/tip-hg4s6e5txiwqe00h8rdg1sin@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 1 - 1 file changed, 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 138229439a93..2da2a6ca22bf 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1671,7 +1671,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) .sample = perf_sched__process_tracepoint_sample, .comm = perf_event__process_comm, .lost = perf_event__process_lost, - .exit = perf_event__process_exit, .fork = perf_event__process_fork, .ordered_samples = true, }, -- cgit v1.2.3 From b5ded713974f537a950e841b5c583f49c80266ff Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 27 Mar 2013 11:00:07 -0300 Subject: perf tools: Convert needless static variable to local Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-k85ajz97xbrd8fkt2a8pp7q1@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/perf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 31c9380cfa64..85e1aed95204 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -518,9 +518,8 @@ int main(int argc, const char **argv) while (1) { static int done_help; - static int was_alias; + int was_alias = run_argv(&argc, &argv); - was_alias = run_argv(&argc, &argv); if (errno != ENOENT) break; -- cgit v1.2.3 From 707ef2e69a53f2dc60cfa2ff5cb4bf5474206f33 Mon Sep 17 00:00:00 2001 From: Naohiro Aota Date: Sun, 24 Mar 2013 22:37:54 +0900 Subject: perf python: Fix dependency for python/perf.so The python/perf.so binding lacks dependency for libtraceevent.a so that it cause the following error building python/perf.so. This patch introduce the dependency for it. $ make python/perf.so CHK -fstack-protector-all CHK -Wstack-protector CHK -Wvolatile-register-var CHK -D_FORTIFY_SOURCE=2 CHK bionic CHK libelf CHK libdw CHK libunwind CHK -DLIBELF_MMAP CHK libaudit CHK libnewt CHK gtk2 CHK -DHAVE_GTK_INFO_BAR CHK perl CHK python CHK python version CHK libbfd CHK -DHAVE_STRLCPY CHK -DHAVE_ON_EXIT CHK -DBACKTRACE_SUPPORT CHK libnuma GEN python/perf.so x86_64-pc-linux-gnu-gcc: error: ../lib/traceevent/libtraceevent.a: No such file or directory error: command 'x86_64-pc-linux-gnu-gcc' failed with exit status 1 cp: cannot stat 'python_ext_build/lib/perf.so': No such file or directory make: *** [python/perf.so] Error 1 Signed-off-by: Naohiro Aota Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/87wqswzznx.fsf@locke.i-did-not-set--mail-host-address--so-tickle-me Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 07feae773dc1..8b4c9529e22a 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -272,7 +272,7 @@ export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) -PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py +PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ -- cgit v1.2.3 From a403253634141137e9a0585a6044e7c059c92f1b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 28 Mar 2013 11:07:37 -0300 Subject: perf map browser: Use ui_browser__input_window() Instead of an ad-hoc, libnewt based equivalent. Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-elrijp95pijt66y6mmij4xm1@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/map.c | 44 ++++++-------------------------------------- 1 file changed, 6 insertions(+), 38 deletions(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c index 98851d55a53e..10b549c0eefb 100644 --- a/tools/perf/ui/browsers/map.c +++ b/tools/perf/ui/browsers/map.c @@ -1,6 +1,5 @@ #include "../libslang.h" #include -#include #include #include #include @@ -10,41 +9,9 @@ #include "../../util/symbol.h" #include "../browser.h" #include "../helpline.h" +#include "../keysyms.h" #include "map.h" -static int ui_entry__read(const char *title, char *bf, size_t size, int width) -{ - struct newtExitStruct es; - newtComponent form, entry; - const char *result; - int err = -1; - - newtCenteredWindow(width, 1, title); - form = newtForm(NULL, NULL, 0); - if (form == NULL) - return -1; - - entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL); - if (entry == NULL) - goto out_free_form; - - newtFormAddComponent(form, entry); - newtFormAddHotKey(form, NEWT_KEY_ENTER); - newtFormAddHotKey(form, NEWT_KEY_ESCAPE); - newtFormAddHotKey(form, NEWT_KEY_LEFT); - newtFormAddHotKey(form, CTRL('c')); - newtFormRun(form, &es); - - if (result != NULL) { - strncpy(bf, result, size); - err = 0; - } -out_free_form: - newtPopWindow(); - newtFormDestroy(form); - return err; -} - struct map_browser { struct ui_browser b; struct map *map; @@ -78,10 +45,11 @@ static int map_browser__search(struct map_browser *self) { char target[512]; struct symbol *sym; - int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40); - - if (err) - return err; + int err = ui_browser__input_window("Search by name/addr", + "Prefix with 0x to search by address", + target, "ENTER: OK, ESC: Cancel", 0); + if (err != K_ENTER) + return -1; if (target[0] == '0' && tolower(target[1]) == 'x') { u64 addr = strtoull(target, NULL, 16); -- cgit v1.2.3 From 6692c262df4f087b1f0d3a57b31ef4cee9b1c6cb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 28 Mar 2013 11:34:10 -0300 Subject: perf tools: Remove dependency on libnewt Now that the map browser shares the input routine with the hists browser, there is no need for using any libnewt routine, so remove all traces except for honouring NO_NEWT=1 on the makefile command line as an indication that TUI support is not needed, in fact it just sets NO_SLANG=1. Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-wae5o7xca9m52bj1re28jc5j@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 20 +++++++++++++------- tools/perf/builtin-report.c | 6 +++--- tools/perf/config/feature-tests.mak | 12 +++++------- tools/perf/ui/browser.c | 9 +++++++-- tools/perf/ui/browser.h | 1 + tools/perf/ui/browsers/annotate.c | 1 - tools/perf/ui/browsers/hists.c | 1 - tools/perf/ui/browsers/scripts.c | 1 - tools/perf/ui/tui/setup.c | 21 +++++++++++---------- tools/perf/ui/ui.h | 2 +- tools/perf/util/annotate.h | 2 +- tools/perf/util/hist.h | 2 +- 12 files changed, 43 insertions(+), 35 deletions(-) (limited to 'tools') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 8b4c9529e22a..b0f164b133d9 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -35,7 +35,9 @@ include config/utilities.mak # # Define WERROR=0 to disable treating any warnings as errors. # -# Define NO_NEWT if you do not want TUI support. +# Define NO_NEWT if you do not want TUI support. (deprecated) +# +# Define NO_SLANG if you do not want TUI support. # # Define NO_GTK2 if you do not want GTK+ GUI support. # @@ -104,6 +106,10 @@ ifdef PARSER_DEBUG PARSER_DEBUG_CFLAGS := -DPARSER_DEBUG endif +ifdef NO_NEWT + NO_SLANG=1 +endif + CFLAGS = -fno-omit-frame-pointer -ggdb3 -funwind-tables -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS) EXTLIBS = -lpthread -lrt -lelf -lm ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE @@ -680,15 +686,15 @@ ifndef NO_LIBAUDIT endif endif -ifndef NO_NEWT - FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt - ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT),libnewt),y) - msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); +ifndef NO_SLANG + FLAGS_SLANG=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -I/usr/include/slang -lslang + ifneq ($(call try-cc,$(SOURCE_SLANG),$(FLAGS_SLANG),libslang),y) + msg := $(warning slang not found, disables TUI support. Please install slang-devel or libslang-dev); else # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h BASIC_CFLAGS += -I/usr/include/slang - BASIC_CFLAGS += -DNEWT_SUPPORT - EXTLIBS += -lnewt -lslang + BASIC_CFLAGS += -DSLANG_SUPPORT + EXTLIBS += -lslang LIB_OBJS += $(OUTPUT)ui/browser.o LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o LIB_OBJS += $(OUTPUT)ui/browsers/hists.o diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index a20550c9cd68..bd0ca81eeaca 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -111,7 +111,7 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool, return -ENOMEM; /* - * In the newt browser, we are doing integrated annotation, + * In the TUI browser, we are doing integrated annotation, * so we don't allocate the extra space needed because the stdio * code will not use it. */ @@ -263,7 +263,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, return err; } /* - * Only in the newt browser we are doing integrated annotation, + * Only in the TUI browser we are doing integrated annotation, * so we don't allocated the extra space needed because the stdio * code will not use it. */ @@ -877,7 +877,7 @@ repeat: usage_with_options(report_usage, options); /* - * Only in the newt browser we are doing integrated annotation, + * Only in the TUI browser we are doing integrated annotation, * so don't allocate extra space that won't be used in the stdio * implementation. */ diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak index b4eabb44e381..708fb8e9822a 100644 --- a/tools/perf/config/feature-tests.mak +++ b/tools/perf/config/feature-tests.mak @@ -61,15 +61,13 @@ int main(void) } endef -ifndef NO_NEWT -define SOURCE_NEWT -#include +ifndef NO_SLANG +define SOURCE_SLANG +#include int main(void) { - newtInit(); - newtCls(); - return newtFinished(); + return SLsmg_init_smg(); } endef endif @@ -235,4 +233,4 @@ int main(void) numa_available(); return 0; } -endef \ No newline at end of file +endef diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index 809ea4632a34..bbc782e364b0 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -2,7 +2,6 @@ #include "../cache.h" #include "../../perf.h" #include "libslang.h" -#include #include "ui.h" #include "util.h" #include @@ -234,7 +233,7 @@ void ui_browser__reset_index(struct ui_browser *browser) void __ui_browser__show_title(struct ui_browser *browser, const char *title) { SLsmg_gotorc(0, 0); - ui_browser__set_color(browser, NEWT_COLORSET_ROOT); + ui_browser__set_color(browser, HE_COLORSET_ROOT); slsmg_write_nstring(title, browser->width + 1); } @@ -513,6 +512,12 @@ static struct ui_browser_colorset { .fg = "magenta", .bg = "default", }, + { + .colorset = HE_COLORSET_ROOT, + .name = "root", + .fg = "white", + .bg = "blue", + }, { .name = NULL, } diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index af70314605e5..404ff66a3e36 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -11,6 +11,7 @@ #define HE_COLORSET_SELECTED 53 #define HE_COLORSET_CODE 54 #define HE_COLORSET_ADDR 55 +#define HE_COLORSET_ROOT 56 struct ui_browser { u64 index, top_idx; diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index f56247a03a22..cc64d3f7fc36 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -10,7 +10,6 @@ #include "../../util/symbol.h" #include "../../util/evsel.h" #include -#include struct browser_disasm_line { struct rb_node rb_node; diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index a5843fd6ab51..d88a2d0acb6d 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -2,7 +2,6 @@ #include "../libslang.h" #include #include -#include #include #include "../../util/evsel.h" diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c index cbbd44b0d93e..12f009e61e94 100644 --- a/tools/perf/ui/browsers/scripts.c +++ b/tools/perf/ui/browsers/scripts.c @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index 81efa192e86c..b9401482d110 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c @@ -1,4 +1,3 @@ -#include #include #include @@ -88,13 +87,6 @@ int ui__getch(int delay_secs) return SLkp_getkey(); } -static void newt_suspend(void *d __maybe_unused) -{ - newtSuspend(); - raise(SIGTSTP); - newtResume(); -} - static void ui__signal(int sig) { ui__exit(false); @@ -106,7 +98,17 @@ int ui__init(void) { int err; - newtInit(); + SLutf8_enable(-1); + SLtt_get_terminfo(); + SLtt_get_screen_size(); + + err = SLsmg_init_smg(); + if (err < 0) + goto out; + err = SLang_init_tty(0, 0, 0); + if (err < 0) + goto out; + err = SLkp_init(); if (err < 0) { pr_err("TUI initialization failed.\n"); @@ -115,7 +117,6 @@ int ui__init(void) SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); - newtSetSuspendCallback(newt_suspend, NULL); ui_helpline__init(); ui_browser__init(); ui_progress__init(); diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h index d86359c99907..70cb0d4eb8aa 100644 --- a/tools/perf/ui/ui.h +++ b/tools/perf/ui/ui.h @@ -12,7 +12,7 @@ extern int use_browser; void setup_browser(bool fallback_to_pager); void exit_browser(bool wait_for_ok); -#ifdef NEWT_SUPPORT +#ifdef SLANG_SUPPORT int ui__init(void); void ui__exit(bool wait_for_ok); #else diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 6f3c16f01ab4..af755156d278 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -150,7 +150,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, bool print_lines, bool full_paths, int min_pcnt, int max_lines); -#ifdef NEWT_SUPPORT +#ifdef SLANG_SUPPORT int symbol__tui_annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, struct hist_browser_timer *hbt); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index fd6313416476..14c2fe20aa62 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -192,7 +192,7 @@ struct hist_browser_timer { int refresh; }; -#ifdef NEWT_SUPPORT +#ifdef SLANG_SUPPORT #include "../ui/keysyms.h" int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, struct hist_browser_timer *hbt); -- cgit v1.2.3 From d06f7911792780c6e973a137b766530c8d031aeb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 28 Mar 2013 11:47:17 -0300 Subject: perf map browser: Exit just on well known key presses Initial motivation was to avoid the confusing exit when when '/' is pressed in non verbose mode, as specified in the help line searches are only available in verbose mode. Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-20xezxim2y4agmkx7f3sucll@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/map.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c index 10b549c0eefb..95c7cfb8f2c6 100644 --- a/tools/perf/ui/browsers/map.c +++ b/tools/perf/ui/browsers/map.c @@ -80,12 +80,20 @@ static int map_browser__run(struct map_browser *self) while (1) { key = ui_browser__run(&self->b, 0); - if (verbose && key == '/') - map_browser__search(self); - else + switch (key) { + case '/': + if (verbose) + map_browser__search(self); + default: break; + case K_LEFT: + case K_ESC: + case 'q': + case CTRL('c'): + goto out; + } } - +out: ui_browser__hide(&self->b); return key; } -- cgit v1.2.3 From 23a9544206dd91dfe048fcf67abec3f3104c42b9 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 2 Apr 2013 13:00:51 +0000 Subject: selftests: net: add PF_PACKET TPACKET v1/v2/v3 selftests This patch adds a simple test case that probes the packet socket's TPACKET_V1, TPACKET_V2 and TPACKET_V3 behavior regarding mmap(2)'ed I/O for a small burst of 100 packets. The test currently runs for ... TPACKET_V1: RX_RING, TX_RING TPACKET_V2: RX_RING, TX_RING TPACKET_V3: RX_RING ... and will output on success: test: TPACKET_V1 with PACKET_RX_RING .................... 100 pkts (9600 bytes) test: TPACKET_V1 with PACKET_TX_RING .................... 100 pkts (9600 bytes) test: TPACKET_V2 with PACKET_RX_RING .................... 100 pkts (9600 bytes) test: TPACKET_V2 with PACKET_TX_RING .................... 100 pkts (9600 bytes) test: TPACKET_V3 with PACKET_RX_RING .................... 100 pkts (9600 bytes) OK. All tests passed Reusable parts of psock_fanout.c have been put into a psock_lib.h file for common usage. Test case successfully tested on x86_64. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/testing/selftests/net/Makefile | 4 +- tools/testing/selftests/net/psock_fanout.c | 88 +-- tools/testing/selftests/net/psock_lib.h | 127 ++++ tools/testing/selftests/net/psock_tpacket.c | 824 ++++++++++++++++++++++++++ tools/testing/selftests/net/run_afpackettests | 10 + 5 files changed, 966 insertions(+), 87 deletions(-) create mode 100644 tools/testing/selftests/net/psock_lib.h create mode 100644 tools/testing/selftests/net/psock_tpacket.c (limited to 'tools') diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index bd6e272bab87..750512ba2c88 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -1,11 +1,11 @@ # Makefile for net selftests CC = $(CROSS_COMPILE)gcc -CFLAGS = -Wall +CFLAGS = -Wall -O2 -g CFLAGS += -I../../../../usr/include/ -NET_PROGS = socket psock_fanout +NET_PROGS = socket psock_fanout psock_tpacket all: $(NET_PROGS) %: %.c diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c index 59bd6367af71..57b9c2b7c4ff 100644 --- a/tools/testing/selftests/net/psock_fanout.c +++ b/tools/testing/selftests/net/psock_fanout.c @@ -61,91 +61,9 @@ #include #include -#define DATA_LEN 100 -#define DATA_CHAR 'a' -#define RING_NUM_FRAMES 20 -#define PORT_BASE 8000 - -static void pair_udp_open(int fds[], uint16_t port) -{ - struct sockaddr_in saddr, daddr; - - fds[0] = socket(PF_INET, SOCK_DGRAM, 0); - fds[1] = socket(PF_INET, SOCK_DGRAM, 0); - if (fds[0] == -1 || fds[1] == -1) { - fprintf(stderr, "ERROR: socket dgram\n"); - exit(1); - } - - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(port); - saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - memset(&daddr, 0, sizeof(daddr)); - daddr.sin_family = AF_INET; - daddr.sin_port = htons(port + 1); - daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); +#include "psock_lib.h" - /* must bind both to get consistent hash result */ - if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { - perror("bind"); - exit(1); - } - if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { - perror("bind"); - exit(1); - } - if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { - perror("connect"); - exit(1); - } -} - -static void pair_udp_send(int fds[], int num) -{ - char buf[DATA_LEN], rbuf[DATA_LEN]; - - memset(buf, DATA_CHAR, sizeof(buf)); - while (num--) { - /* Should really handle EINTR and EAGAIN */ - if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { - fprintf(stderr, "ERROR: send failed left=%d\n", num); - exit(1); - } - if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { - fprintf(stderr, "ERROR: recv failed left=%d\n", num); - exit(1); - } - if (memcmp(buf, rbuf, sizeof(buf))) { - fprintf(stderr, "ERROR: data failed left=%d\n", num); - exit(1); - } - } -} - -static void sock_fanout_setfilter(int fd) -{ - struct sock_filter bpf_filter[] = { - { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ - { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ - { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ - { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ - { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ - { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ - { 0x6, 0, 0, 0x00000060 }, /* RET match */ -/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ - }; - struct sock_fprog bpf_prog; - - bpf_prog.filter = bpf_filter; - bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, - sizeof(bpf_prog))) { - perror("setsockopt SO_ATTACH_FILTER"); - exit(1); - } -} +#define RING_NUM_FRAMES 20 /* Open a socket in a given fanout mode. * @return -1 if mode is bad, a valid socket otherwise */ @@ -169,7 +87,7 @@ static int sock_fanout_open(uint16_t typeflags, int num_packets) return -1; } - sock_fanout_setfilter(fd); + pair_udp_setfilter(fd); return fd; } diff --git a/tools/testing/selftests/net/psock_lib.h b/tools/testing/selftests/net/psock_lib.h new file mode 100644 index 000000000000..37da54ac85a9 --- /dev/null +++ b/tools/testing/selftests/net/psock_lib.h @@ -0,0 +1,127 @@ +/* + * Copyright 2013 Google Inc. + * Author: Willem de Bruijn + * Daniel Borkmann + * + * License (GPLv2): + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef PSOCK_LIB_H +#define PSOCK_LIB_H + +#include +#include +#include +#include +#include + +#define DATA_LEN 100 +#define DATA_CHAR 'a' + +#define PORT_BASE 8000 + +#ifndef __maybe_unused +# define __maybe_unused __attribute__ ((__unused__)) +#endif + +static __maybe_unused void pair_udp_setfilter(int fd) +{ + struct sock_filter bpf_filter[] = { + { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ + { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ + { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ + { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x06, 0, 0, 0x00000060 }, /* RET match */ + { 0x06, 0, 0, 0x00000000 }, /* RET no match */ + }; + struct sock_fprog bpf_prog; + + bpf_prog.filter = bpf_filter; + bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, + sizeof(bpf_prog))) { + perror("setsockopt SO_ATTACH_FILTER"); + exit(1); + } +} + +static __maybe_unused void pair_udp_open(int fds[], uint16_t port) +{ + struct sockaddr_in saddr, daddr; + + fds[0] = socket(PF_INET, SOCK_DGRAM, 0); + fds[1] = socket(PF_INET, SOCK_DGRAM, 0); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: socket dgram\n"); + exit(1); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&daddr, 0, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_port = htons(port + 1); + daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* must bind both to get consistent hash result */ + if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } + if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { + perror("bind"); + exit(1); + } + if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { + perror("connect"); + exit(1); + } +} + +static __maybe_unused void pair_udp_send(int fds[], int num) +{ + char buf[DATA_LEN], rbuf[DATA_LEN]; + + memset(buf, DATA_CHAR, sizeof(buf)); + while (num--) { + /* Should really handle EINTR and EAGAIN */ + if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { + fprintf(stderr, "ERROR: send failed left=%d\n", num); + exit(1); + } + if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { + fprintf(stderr, "ERROR: recv failed left=%d\n", num); + exit(1); + } + if (memcmp(buf, rbuf, sizeof(buf))) { + fprintf(stderr, "ERROR: data failed left=%d\n", num); + exit(1); + } + } +} + +static __maybe_unused void pair_udp_close(int fds[]) +{ + close(fds[0]); + close(fds[1]); +} + +#endif /* PSOCK_LIB_H */ diff --git a/tools/testing/selftests/net/psock_tpacket.c b/tools/testing/selftests/net/psock_tpacket.c new file mode 100644 index 000000000000..a8d7ffadd49b --- /dev/null +++ b/tools/testing/selftests/net/psock_tpacket.c @@ -0,0 +1,824 @@ +/* + * Copyright 2013 Red Hat, Inc. + * Author: Daniel Borkmann + * + * A basic test of packet socket's TPACKET_V1/TPACKET_V2/TPACKET_V3 behavior. + * + * Control: + * Test the setup of the TPACKET socket with different patterns that are + * known to fail (TODO) resp. succeed (OK). + * + * Datapath: + * Open a pair of packet sockets and send resp. receive an a priori known + * packet pattern accross the sockets and check if it was received resp. + * sent correctly. Fanout in combination with RX_RING is currently not + * tested here. + * + * The test currently runs for + * - TPACKET_V1: RX_RING, TX_RING + * - TPACKET_V2: RX_RING, TX_RING + * - TPACKET_V3: RX_RING + * + * License (GPLv2): + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "psock_lib.h" + +#ifndef bug_on +# define bug_on(cond) assert(!(cond)) +#endif + +#ifndef __aligned_tpacket +# define __aligned_tpacket __attribute__((aligned(TPACKET_ALIGNMENT))) +#endif + +#ifndef __align_tpacket +# define __align_tpacket(x) __attribute__((aligned(TPACKET_ALIGN(x)))) +#endif + +#define BLOCK_STATUS(x) ((x)->h1.block_status) +#define BLOCK_NUM_PKTS(x) ((x)->h1.num_pkts) +#define BLOCK_O2FP(x) ((x)->h1.offset_to_first_pkt) +#define BLOCK_LEN(x) ((x)->h1.blk_len) +#define BLOCK_SNUM(x) ((x)->h1.seq_num) +#define BLOCK_O2PRIV(x) ((x)->offset_to_priv) +#define BLOCK_PRIV(x) ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x))) +#define BLOCK_HDR_LEN (ALIGN_8(sizeof(struct block_desc))) +#define ALIGN_8(x) (((x) + 8 - 1) & ~(8 - 1)) +#define BLOCK_PLUS_PRIV(sz_pri) (BLOCK_HDR_LEN + ALIGN_8((sz_pri))) + +#define NUM_PACKETS 100 + +struct ring { + struct iovec *rd; + uint8_t *mm_space; + size_t mm_len, rd_len; + struct sockaddr_ll ll; + void (*walk)(int sock, struct ring *ring); + int type, rd_num, flen, version; + union { + struct tpacket_req req; + struct tpacket_req3 req3; + }; +}; + +struct block_desc { + uint32_t version; + uint32_t offset_to_priv; + struct tpacket_hdr_v1 h1; +}; + +union frame_map { + struct { + struct tpacket_hdr tp_h __aligned_tpacket; + struct sockaddr_ll s_ll __align_tpacket(sizeof(struct tpacket_hdr)); + } *v1; + struct { + struct tpacket2_hdr tp_h __aligned_tpacket; + struct sockaddr_ll s_ll __align_tpacket(sizeof(struct tpacket2_hdr)); + } *v2; + void *raw; +}; + +static unsigned int total_packets, total_bytes; + +static int pfsocket(int ver) +{ + int ret, sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (sock == -1) { + perror("socket"); + exit(1); + } + + ret = setsockopt(sock, SOL_PACKET, PACKET_VERSION, &ver, sizeof(ver)); + if (ret == -1) { + perror("setsockopt"); + exit(1); + } + + return sock; +} + +static void status_bar_update(void) +{ + if (total_packets % 10 == 0) { + fprintf(stderr, "."); + fflush(stderr); + } +} + +static void test_payload(void *pay, size_t len) +{ + struct ethhdr *eth = pay; + + if (len < sizeof(struct ethhdr)) { + fprintf(stderr, "test_payload: packet too " + "small: %zu bytes!\n", len); + exit(1); + } + + if (eth->h_proto != htons(ETH_P_IP)) { + fprintf(stderr, "test_payload: wrong ethernet " + "type: 0x%x!\n", ntohs(eth->h_proto)); + exit(1); + } +} + +static void create_payload(void *pay, size_t *len) +{ + int i; + struct ethhdr *eth = pay; + struct iphdr *ip = pay + sizeof(*eth); + + /* Lets create some broken crap, that still passes + * our BPF filter. + */ + + *len = DATA_LEN + 42; + + memset(pay, 0xff, ETH_ALEN * 2); + eth->h_proto = htons(ETH_P_IP); + + for (i = 0; i < sizeof(*ip); ++i) + ((uint8_t *) pay)[i + sizeof(*eth)] = (uint8_t) rand(); + + ip->ihl = 5; + ip->version = 4; + ip->protocol = 0x11; + ip->frag_off = 0; + ip->ttl = 64; + ip->tot_len = htons((uint16_t) *len - sizeof(*eth)); + + ip->saddr = htonl(INADDR_LOOPBACK); + ip->daddr = htonl(INADDR_LOOPBACK); + + memset(pay + sizeof(*eth) + sizeof(*ip), + DATA_CHAR, DATA_LEN); +} + +static inline int __v1_rx_kernel_ready(struct tpacket_hdr *hdr) +{ + return ((hdr->tp_status & TP_STATUS_USER) == TP_STATUS_USER); +} + +static inline void __v1_rx_user_ready(struct tpacket_hdr *hdr) +{ + hdr->tp_status = TP_STATUS_KERNEL; + __sync_synchronize(); +} + +static inline int __v2_rx_kernel_ready(struct tpacket2_hdr *hdr) +{ + return ((hdr->tp_status & TP_STATUS_USER) == TP_STATUS_USER); +} + +static inline void __v2_rx_user_ready(struct tpacket2_hdr *hdr) +{ + hdr->tp_status = TP_STATUS_KERNEL; + __sync_synchronize(); +} + +static inline int __v1_v2_rx_kernel_ready(void *base, int version) +{ + switch (version) { + case TPACKET_V1: + return __v1_rx_kernel_ready(base); + case TPACKET_V2: + return __v2_rx_kernel_ready(base); + default: + bug_on(1); + return 0; + } +} + +static inline void __v1_v2_rx_user_ready(void *base, int version) +{ + switch (version) { + case TPACKET_V1: + __v1_rx_user_ready(base); + break; + case TPACKET_V2: + __v2_rx_user_ready(base); + break; + } +} + +static void walk_v1_v2_rx(int sock, struct ring *ring) +{ + struct pollfd pfd; + int udp_sock[2]; + union frame_map ppd; + unsigned int frame_num = 0; + + bug_on(ring->type != PACKET_RX_RING); + + pair_udp_open(udp_sock, PORT_BASE); + pair_udp_setfilter(sock); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sock; + pfd.events = POLLIN | POLLERR; + pfd.revents = 0; + + pair_udp_send(udp_sock, NUM_PACKETS); + + while (total_packets < NUM_PACKETS * 2) { + while (__v1_v2_rx_kernel_ready(ring->rd[frame_num].iov_base, + ring->version)) { + ppd.raw = ring->rd[frame_num].iov_base; + + switch (ring->version) { + case TPACKET_V1: + test_payload((uint8_t *) ppd.raw + ppd.v1->tp_h.tp_mac, + ppd.v1->tp_h.tp_snaplen); + total_bytes += ppd.v1->tp_h.tp_snaplen; + break; + + case TPACKET_V2: + test_payload((uint8_t *) ppd.raw + ppd.v2->tp_h.tp_mac, + ppd.v2->tp_h.tp_snaplen); + total_bytes += ppd.v2->tp_h.tp_snaplen; + break; + } + + status_bar_update(); + total_packets++; + + __v1_v2_rx_user_ready(ppd.raw, ring->version); + + frame_num = (frame_num + 1) % ring->rd_num; + } + + poll(&pfd, 1, 1); + } + + pair_udp_close(udp_sock); + + if (total_packets != 2 * NUM_PACKETS) { + fprintf(stderr, "walk_v%d_rx: received %u out of %u pkts\n", + ring->version, total_packets, NUM_PACKETS); + exit(1); + } + + fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, total_bytes >> 1); +} + +static inline int __v1_tx_kernel_ready(struct tpacket_hdr *hdr) +{ + return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE); +} + +static inline void __v1_tx_user_ready(struct tpacket_hdr *hdr) +{ + hdr->tp_status = TP_STATUS_SEND_REQUEST; + __sync_synchronize(); +} + +static inline int __v2_tx_kernel_ready(struct tpacket2_hdr *hdr) +{ + return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE); +} + +static inline void __v2_tx_user_ready(struct tpacket2_hdr *hdr) +{ + hdr->tp_status = TP_STATUS_SEND_REQUEST; + __sync_synchronize(); +} + +static inline int __v1_v2_tx_kernel_ready(void *base, int version) +{ + switch (version) { + case TPACKET_V1: + return __v1_tx_kernel_ready(base); + case TPACKET_V2: + return __v2_tx_kernel_ready(base); + default: + bug_on(1); + return 0; + } +} + +static inline void __v1_v2_tx_user_ready(void *base, int version) +{ + switch (version) { + case TPACKET_V1: + __v1_tx_user_ready(base); + break; + case TPACKET_V2: + __v2_tx_user_ready(base); + break; + } +} + +static void __v1_v2_set_packet_loss_discard(int sock) +{ + int ret, discard = 1; + + ret = setsockopt(sock, SOL_PACKET, PACKET_LOSS, (void *) &discard, + sizeof(discard)); + if (ret == -1) { + perror("setsockopt"); + exit(1); + } +} + +static void walk_v1_v2_tx(int sock, struct ring *ring) +{ + struct pollfd pfd; + int rcv_sock, ret; + size_t packet_len; + union frame_map ppd; + char packet[1024]; + unsigned int frame_num = 0, got = 0; + struct sockaddr_ll ll = { + .sll_family = PF_PACKET, + .sll_halen = ETH_ALEN, + }; + + bug_on(ring->type != PACKET_TX_RING); + bug_on(ring->rd_num < NUM_PACKETS); + + rcv_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (rcv_sock == -1) { + perror("socket"); + exit(1); + } + + pair_udp_setfilter(rcv_sock); + + ll.sll_ifindex = if_nametoindex("lo"); + ret = bind(rcv_sock, (struct sockaddr *) &ll, sizeof(ll)); + if (ret == -1) { + perror("bind"); + exit(1); + } + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sock; + pfd.events = POLLOUT | POLLERR; + pfd.revents = 0; + + total_packets = NUM_PACKETS; + create_payload(packet, &packet_len); + + while (total_packets > 0) { + while (__v1_v2_tx_kernel_ready(ring->rd[frame_num].iov_base, + ring->version) && + total_packets > 0) { + ppd.raw = ring->rd[frame_num].iov_base; + + switch (ring->version) { + case TPACKET_V1: + ppd.v1->tp_h.tp_snaplen = packet_len; + ppd.v1->tp_h.tp_len = packet_len; + + memcpy((uint8_t *) ppd.raw + TPACKET_HDRLEN - + sizeof(struct sockaddr_ll), packet, + packet_len); + total_bytes += ppd.v1->tp_h.tp_snaplen; + break; + + case TPACKET_V2: + ppd.v2->tp_h.tp_snaplen = packet_len; + ppd.v2->tp_h.tp_len = packet_len; + + memcpy((uint8_t *) ppd.raw + TPACKET2_HDRLEN - + sizeof(struct sockaddr_ll), packet, + packet_len); + total_bytes += ppd.v2->tp_h.tp_snaplen; + break; + } + + status_bar_update(); + total_packets--; + + __v1_v2_tx_user_ready(ppd.raw, ring->version); + + frame_num = (frame_num + 1) % ring->rd_num; + } + + poll(&pfd, 1, 1); + } + + bug_on(total_packets != 0); + + ret = sendto(sock, NULL, 0, 0, NULL, 0); + if (ret == -1) { + perror("sendto"); + exit(1); + } + + while ((ret = recvfrom(rcv_sock, packet, sizeof(packet), + 0, NULL, NULL)) > 0 && + total_packets < NUM_PACKETS) { + got += ret; + test_payload(packet, ret); + + status_bar_update(); + total_packets++; + } + + close(rcv_sock); + + if (total_packets != NUM_PACKETS) { + fprintf(stderr, "walk_v%d_rx: received %u out of %u pkts\n", + ring->version, total_packets, NUM_PACKETS); + exit(1); + } + + fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, got); +} + +static void walk_v1_v2(int sock, struct ring *ring) +{ + if (ring->type == PACKET_RX_RING) + walk_v1_v2_rx(sock, ring); + else + walk_v1_v2_tx(sock, ring); +} + +static uint64_t __v3_prev_block_seq_num = 0; + +void __v3_test_block_seq_num(struct block_desc *pbd) +{ + if (__v3_prev_block_seq_num + 1 != BLOCK_SNUM(pbd)) { + fprintf(stderr, "\nprev_block_seq_num:%"PRIu64", expected " + "seq:%"PRIu64" != actual seq:%"PRIu64"\n", + __v3_prev_block_seq_num, __v3_prev_block_seq_num + 1, + (uint64_t) BLOCK_SNUM(pbd)); + exit(1); + } + + __v3_prev_block_seq_num = BLOCK_SNUM(pbd); +} + +static void __v3_test_block_len(struct block_desc *pbd, uint32_t bytes, int block_num) +{ + if (BLOCK_NUM_PKTS(pbd)) { + if (bytes != BLOCK_LEN(pbd)) { + fprintf(stderr, "\nblock:%u with %upackets, expected " + "len:%u != actual len:%u\n", block_num, + BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd)); + exit(1); + } + } else { + if (BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(13)) { + fprintf(stderr, "\nblock:%u, expected len:%lu != " + "actual len:%u\n", block_num, BLOCK_HDR_LEN, + BLOCK_LEN(pbd)); + exit(1); + } + } +} + +static void __v3_test_block_header(struct block_desc *pbd, const int block_num) +{ + uint32_t block_status = BLOCK_STATUS(pbd); + + if ((block_status & TP_STATUS_USER) == 0) { + fprintf(stderr, "\nblock %u: not in TP_STATUS_USER\n", block_num); + exit(1); + } + + __v3_test_block_seq_num(pbd); +} + +static void __v3_walk_block(struct block_desc *pbd, const int block_num) +{ + int num_pkts = BLOCK_NUM_PKTS(pbd), i; + unsigned long bytes = 0; + unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(13); + struct tpacket3_hdr *ppd; + + __v3_test_block_header(pbd, block_num); + + ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd)); + for (i = 0; i < num_pkts; ++i) { + bytes += ppd->tp_snaplen; + + if (ppd->tp_next_offset) + bytes_with_padding += ppd->tp_next_offset; + else + bytes_with_padding += ALIGN_8(ppd->tp_snaplen + ppd->tp_mac); + + test_payload((uint8_t *) ppd + ppd->tp_mac, ppd->tp_snaplen); + + status_bar_update(); + total_packets++; + + ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset); + __sync_synchronize(); + } + + __v3_test_block_len(pbd, bytes_with_padding, block_num); + total_bytes += bytes; +} + +void __v3_flush_block(struct block_desc *pbd) +{ + BLOCK_STATUS(pbd) = TP_STATUS_KERNEL; + __sync_synchronize(); +} + +static void walk_v3_rx(int sock, struct ring *ring) +{ + unsigned int block_num = 0; + struct pollfd pfd; + struct block_desc *pbd; + int udp_sock[2]; + + bug_on(ring->type != PACKET_RX_RING); + + pair_udp_open(udp_sock, PORT_BASE); + pair_udp_setfilter(sock); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sock; + pfd.events = POLLIN | POLLERR; + pfd.revents = 0; + + pair_udp_send(udp_sock, NUM_PACKETS); + + while (total_packets < NUM_PACKETS * 2) { + pbd = (struct block_desc *) ring->rd[block_num].iov_base; + + while ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0) + poll(&pfd, 1, 1); + + __v3_walk_block(pbd, block_num); + __v3_flush_block(pbd); + + block_num = (block_num + 1) % ring->rd_num; + } + + pair_udp_close(udp_sock); + + if (total_packets != 2 * NUM_PACKETS) { + fprintf(stderr, "walk_v3_rx: received %u out of %u pkts\n", + total_packets, NUM_PACKETS); + exit(1); + } + + fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, total_bytes >> 1); +} + +static void walk_v3(int sock, struct ring *ring) +{ + if (ring->type == PACKET_RX_RING) + walk_v3_rx(sock, ring); + else + bug_on(1); +} + +static void __v1_v2_fill(struct ring *ring, unsigned int blocks) +{ + ring->req.tp_block_size = getpagesize() << 2; + ring->req.tp_frame_size = TPACKET_ALIGNMENT << 7; + ring->req.tp_block_nr = blocks; + + ring->req.tp_frame_nr = ring->req.tp_block_size / + ring->req.tp_frame_size * + ring->req.tp_block_nr; + + ring->mm_len = ring->req.tp_block_size * ring->req.tp_block_nr; + ring->walk = walk_v1_v2; + ring->rd_num = ring->req.tp_frame_nr; + ring->flen = ring->req.tp_frame_size; +} + +static void __v3_fill(struct ring *ring, unsigned int blocks) +{ + ring->req3.tp_retire_blk_tov = 64; + ring->req3.tp_sizeof_priv = 13; + ring->req3.tp_feature_req_word |= TP_FT_REQ_FILL_RXHASH; + + ring->req3.tp_block_size = getpagesize() << 2; + ring->req3.tp_frame_size = TPACKET_ALIGNMENT << 7; + ring->req3.tp_block_nr = blocks; + + ring->req3.tp_frame_nr = ring->req3.tp_block_size / + ring->req3.tp_frame_size * + ring->req3.tp_block_nr; + + ring->mm_len = ring->req3.tp_block_size * ring->req3.tp_block_nr; + ring->walk = walk_v3; + ring->rd_num = ring->req3.tp_block_nr; + ring->flen = ring->req3.tp_block_size; +} + +static void setup_ring(int sock, struct ring *ring, int version, int type) +{ + int ret = 0; + unsigned int blocks = 256; + + ring->type = type; + ring->version = version; + + switch (version) { + case TPACKET_V1: + case TPACKET_V2: + if (type == PACKET_TX_RING) + __v1_v2_set_packet_loss_discard(sock); + __v1_v2_fill(ring, blocks); + ret = setsockopt(sock, SOL_PACKET, type, &ring->req, + sizeof(ring->req)); + break; + + case TPACKET_V3: + __v3_fill(ring, blocks); + ret = setsockopt(sock, SOL_PACKET, type, &ring->req3, + sizeof(ring->req3)); + break; + } + + if (ret == -1) { + perror("setsockopt"); + exit(1); + } + + ring->rd_len = ring->rd_num * sizeof(*ring->rd); + ring->rd = malloc(ring->rd_len); + if (ring->rd == NULL) { + perror("malloc"); + exit(1); + } + + total_packets = 0; + total_bytes = 0; +} + +static void mmap_ring(int sock, struct ring *ring) +{ + int i; + + ring->mm_space = mmap(0, ring->mm_len, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_LOCKED | MAP_POPULATE, sock, 0); + if (ring->mm_space == MAP_FAILED) { + perror("mmap"); + exit(1); + } + + memset(ring->rd, 0, ring->rd_len); + for (i = 0; i < ring->rd_num; ++i) { + ring->rd[i].iov_base = ring->mm_space + (i * ring->flen); + ring->rd[i].iov_len = ring->flen; + } +} + +static void bind_ring(int sock, struct ring *ring) +{ + int ret; + + ring->ll.sll_family = PF_PACKET; + ring->ll.sll_protocol = htons(ETH_P_ALL); + ring->ll.sll_ifindex = if_nametoindex("lo"); + ring->ll.sll_hatype = 0; + ring->ll.sll_pkttype = 0; + ring->ll.sll_halen = 0; + + ret = bind(sock, (struct sockaddr *) &ring->ll, sizeof(ring->ll)); + if (ret == -1) { + perror("bind"); + exit(1); + } +} + +static void walk_ring(int sock, struct ring *ring) +{ + ring->walk(sock, ring); +} + +static void unmap_ring(int sock, struct ring *ring) +{ + munmap(ring->mm_space, ring->mm_len); + free(ring->rd); +} + +static int test_kernel_bit_width(void) +{ + char in[512], *ptr; + int num = 0, fd; + ssize_t ret; + + fd = open("/proc/kallsyms", O_RDONLY); + if (fd == -1) { + perror("open"); + exit(1); + } + + ret = read(fd, in, sizeof(in)); + if (ret <= 0) { + perror("read"); + exit(1); + } + + close(fd); + + ptr = in; + while(!isspace(*ptr)) { + num++; + ptr++; + } + + return num * 4; +} + +static int test_user_bit_width(void) +{ + return __WORDSIZE; +} + +static const char *tpacket_str[] = { + [TPACKET_V1] = "TPACKET_V1", + [TPACKET_V2] = "TPACKET_V2", + [TPACKET_V3] = "TPACKET_V3", +}; + +static const char *type_str[] = { + [PACKET_RX_RING] = "PACKET_RX_RING", + [PACKET_TX_RING] = "PACKET_TX_RING", +}; + +static int test_tpacket(int version, int type) +{ + int sock; + struct ring ring; + + fprintf(stderr, "test: %s with %s ", tpacket_str[version], + type_str[type]); + fflush(stderr); + + if (version == TPACKET_V1 && + test_kernel_bit_width() != test_user_bit_width()) { + fprintf(stderr, "test: skip %s %s since user and kernel " + "space have different bit width\n", + tpacket_str[version], type_str[type]); + return 0; + } + + sock = pfsocket(version); + memset(&ring, 0, sizeof(ring)); + setup_ring(sock, &ring, version, type); + mmap_ring(sock, &ring); + bind_ring(sock, &ring); + walk_ring(sock, &ring); + unmap_ring(sock, &ring); + close(sock); + + fprintf(stderr, "\n"); + return 0; +} + +int main(void) +{ + int ret = 0; + + ret |= test_tpacket(TPACKET_V1, PACKET_RX_RING); + ret |= test_tpacket(TPACKET_V1, PACKET_TX_RING); + + ret |= test_tpacket(TPACKET_V2, PACKET_RX_RING); + ret |= test_tpacket(TPACKET_V2, PACKET_TX_RING); + + ret |= test_tpacket(TPACKET_V3, PACKET_RX_RING); + + if (ret) + return 1; + + printf("OK. All tests passed\n"); + return 0; +} diff --git a/tools/testing/selftests/net/run_afpackettests b/tools/testing/selftests/net/run_afpackettests index 7907824c6355..5246e782d6e8 100644 --- a/tools/testing/selftests/net/run_afpackettests +++ b/tools/testing/selftests/net/run_afpackettests @@ -14,3 +14,13 @@ if [ $? -ne 0 ]; then else echo "[PASS]" fi + +echo "--------------------" +echo "running psock_tpacket test" +echo "--------------------" +./psock_tpacket +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi -- cgit v1.2.3 From a895d57da04a4a24cda996e1a72425ff7e7e6c22 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Tue, 9 Apr 2013 02:06:50 +0900 Subject: treewide: Fix typo in printks Correct spelling typos in printk and comments. Signed-off-by: Masanari Iida Acked-by: Randy Dunlap Signed-off-by: Jiri Kosina --- tools/perf/tests/attr.c | 2 +- tools/perf/tests/parse-events.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index bdcceb886f77..e8257326ec61 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -173,6 +173,6 @@ int test__attr(void) !lstat(path_perf, &st)) return run_dir(path_dir, path_perf); - fprintf(stderr, " (ommitted)"); + fprintf(stderr, " (omitted)"); return 0; } diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index c5636f36fe31..63986f9db580 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1321,7 +1321,7 @@ static int test_pmu_events(void) ret = stat(path, &st); if (ret) { - pr_debug("ommiting PMU cpu events tests\n"); + pr_debug("omitting PMU cpu events tests\n"); return 0; } -- cgit v1.2.3 From ca58710f3ae585ed6203043cc6d4ffb805adeee4 Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Wed, 21 Nov 2012 05:22:43 -0800 Subject: tools/power turbostat: display C8, C9, C10 residency Display residency in the new C-states, C8, C9, C10. C8, C9, C10 are present on some: "Fourth Generation Intel(R) Core(TM) Processors", which are based on Intel(R) microarchitecture code name Haswell. Signed-off-by: Kristen Carlson Accardi Signed-off-by: Len Brown --- tools/power/x86/turbostat/turbostat.c | 54 ++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 321e066a0753..9e9d34871195 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -46,6 +46,7 @@ unsigned int skip_c0; unsigned int skip_c1; unsigned int do_nhm_cstates; unsigned int do_snb_cstates; +unsigned int do_c8_c9_c10; unsigned int has_aperf; unsigned int has_epb; unsigned int units = 1000000000; /* Ghz etc */ @@ -120,6 +121,9 @@ struct pkg_data { unsigned long long pc3; unsigned long long pc6; unsigned long long pc7; + unsigned long long pc8; + unsigned long long pc9; + unsigned long long pc10; unsigned int package_id; unsigned int energy_pkg; /* MSR_PKG_ENERGY_STATUS */ unsigned int energy_dram; /* MSR_DRAM_ENERGY_STATUS */ @@ -282,6 +286,11 @@ void print_header(void) outp += sprintf(outp, " %%pc6"); if (do_snb_cstates) outp += sprintf(outp, " %%pc7"); + if (do_c8_c9_c10) { + outp += sprintf(outp, " %%pc8"); + outp += sprintf(outp, " %%pc9"); + outp += sprintf(outp, " %%pc10"); + } if (do_rapl & RAPL_PKG) outp += sprintf(outp, " Pkg_W"); @@ -336,6 +345,9 @@ int dump_counters(struct thread_data *t, struct core_data *c, fprintf(stderr, "pc3: %016llX\n", p->pc3); fprintf(stderr, "pc6: %016llX\n", p->pc6); fprintf(stderr, "pc7: %016llX\n", p->pc7); + fprintf(stderr, "pc8: %016llX\n", p->pc8); + fprintf(stderr, "pc9: %016llX\n", p->pc9); + fprintf(stderr, "pc10: %016llX\n", p->pc10); fprintf(stderr, "Joules PKG: %0X\n", p->energy_pkg); fprintf(stderr, "Joules COR: %0X\n", p->energy_cores); fprintf(stderr, "Joules GFX: %0X\n", p->energy_gfx); @@ -493,6 +505,11 @@ int format_counters(struct thread_data *t, struct core_data *c, outp += sprintf(outp, " %6.2f", 100.0 * p->pc6/t->tsc); if (do_snb_cstates) outp += sprintf(outp, " %6.2f", 100.0 * p->pc7/t->tsc); + if (do_c8_c9_c10) { + outp += sprintf(outp, " %6.2f", 100.0 * p->pc8/t->tsc); + outp += sprintf(outp, " %6.2f", 100.0 * p->pc9/t->tsc); + outp += sprintf(outp, " %6.2f", 100.0 * p->pc10/t->tsc); + } /* * If measurement interval exceeds minimum RAPL Joule Counter range, @@ -569,6 +586,9 @@ delta_package(struct pkg_data *new, struct pkg_data *old) old->pc3 = new->pc3 - old->pc3; old->pc6 = new->pc6 - old->pc6; old->pc7 = new->pc7 - old->pc7; + old->pc8 = new->pc8 - old->pc8; + old->pc9 = new->pc9 - old->pc9; + old->pc10 = new->pc10 - old->pc10; old->pkg_temp_c = new->pkg_temp_c; DELTA_WRAP32(new->energy_pkg, old->energy_pkg); @@ -702,6 +722,9 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data p->pc3 = 0; p->pc6 = 0; p->pc7 = 0; + p->pc8 = 0; + p->pc9 = 0; + p->pc10 = 0; p->energy_pkg = 0; p->energy_dram = 0; @@ -740,6 +763,9 @@ int sum_counters(struct thread_data *t, struct core_data *c, average.packages.pc3 += p->pc3; average.packages.pc6 += p->pc6; average.packages.pc7 += p->pc7; + average.packages.pc8 += p->pc8; + average.packages.pc9 += p->pc9; + average.packages.pc10 += p->pc10; average.packages.energy_pkg += p->energy_pkg; average.packages.energy_dram += p->energy_dram; @@ -781,6 +807,10 @@ void compute_average(struct thread_data *t, struct core_data *c, average.packages.pc3 /= topo.num_packages; average.packages.pc6 /= topo.num_packages; average.packages.pc7 /= topo.num_packages; + + average.packages.pc8 /= topo.num_packages; + average.packages.pc9 /= topo.num_packages; + average.packages.pc10 /= topo.num_packages; } static unsigned long long rdtsc(void) @@ -880,6 +910,14 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) if (get_msr(cpu, MSR_PKG_C7_RESIDENCY, &p->pc7)) return -12; } + if (do_c8_c9_c10) { + if (get_msr(cpu, MSR_PKG_C8_RESIDENCY, &p->pc8)) + return -13; + if (get_msr(cpu, MSR_PKG_C9_RESIDENCY, &p->pc9)) + return -13; + if (get_msr(cpu, MSR_PKG_C10_RESIDENCY, &p->pc10)) + return -13; + } if (do_rapl & RAPL_PKG) { if (get_msr(cpu, MSR_PKG_ENERGY_STATUS, &msr)) return -13; @@ -1762,6 +1800,19 @@ int is_snb(unsigned int family, unsigned int model) return 0; } +int has_c8_c9_c10(unsigned int family, unsigned int model) +{ + if (!genuine_intel) + return 0; + + switch (model) { + case 0x45: + return 1; + } + return 0; +} + + double discover_bclk(unsigned int family, unsigned int model) { if (is_snb(family, model)) @@ -1918,6 +1969,7 @@ void check_cpuid() do_nhm_cstates = genuine_intel; /* all Intel w/ non-stop TSC have NHM counters */ do_smi = do_nhm_cstates; do_snb_cstates = is_snb(family, model); + do_c8_c9_c10 = has_c8_c9_c10(family, model); bclk = discover_bclk(family, model); do_nehalem_turbo_ratio_limit = has_nehalem_turbo_ratio_limit(family, model); @@ -2279,7 +2331,7 @@ int main(int argc, char **argv) cmdline(argc, argv); if (verbose) - fprintf(stderr, "turbostat v3.3 March 15, 2013" + fprintf(stderr, "turbostat v3.4 April 17, 2013" " - Len Brown \n"); turbostat_init(); -- cgit v1.2.3 From cf270148662a117c04753d17324e3bd28ce0396a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 19 Apr 2013 16:36:12 -0400 Subject: net: Add .gitignore to networking selftests directory. Signed-off-by: David S. Miller --- tools/testing/selftests/net/.gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tools/testing/selftests/net/.gitignore (limited to 'tools') diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore new file mode 100644 index 000000000000..00326629d4af --- /dev/null +++ b/tools/testing/selftests/net/.gitignore @@ -0,0 +1,3 @@ +socket +psock_fanout +psock_tpacket -- cgit v1.2.3 From eb8905b8ff4a887901779504d2ede95dc072bb16 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Wed, 24 Apr 2013 07:48:48 -0700 Subject: Tools: hv: fix warnings in hv_vss_daemon This change fixes a few compile errors: hv_vss_daemon.c:64:15: warning: unknown escape sequence '\/' hv_vss_daemon.c:64:15: warning: unknown escape sequence '\/' hv_vss_daemon.c: In function 'vss_operate': hv_vss_daemon.c:66: warning: 'return' with no value, in function returning non-void hv_vss_daemon.c: In function 'main': hv_vss_daemon.c:130: warning: ignoring return value of 'daemon', declared with attribute warn_unused_result hv_vss_daemon.c: In function 'vss_operate': hv_vss_daemon.c:47: warning: 'fs_op' may be used uninitialized in this function Signed-off-by: Olaf Hering Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_vss_daemon.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index 95269952aa92..2a03d0b4e213 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -51,7 +51,7 @@ static int vss_operate(int operation) FILE *file; char *p; char *x; - int error; + int error = 0; switch (operation) { case VSS_OP_FREEZE: @@ -60,11 +60,13 @@ static int vss_operate(int operation) case VSS_OP_THAW: fs_op = "-u "; break; + default: + return -1; } - file = popen("mount | awk '/^\/dev\// { print $3}'", "r"); + file = popen("mount | awk '/^\\/dev\\// { print $3}'", "r"); if (file == NULL) - return; + return -1; while ((p = fgets(buf, sizeof(buf), file)) != NULL) { x = strchr(p, '\n'); @@ -128,7 +130,9 @@ int main(void) int op; struct hv_vss_msg *vss_msg; - daemon(1, 0); + if (daemon(1, 0)) + return 1; + openlog("Hyper-V VSS", 0, LOG_USER); syslog(LOG_INFO, "VSS starting; pid is:%d", getpid()); -- cgit v1.2.3 From 5edf5ee42a4dfbc500e9b9531335305d4c81a129 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Wed, 24 Apr 2013 07:48:49 -0700 Subject: tools: hv: fix checks for origin of netlink message in hv_vss_daemon Similar to what commit 95a69adab9acfc3981c504737a2b6578e4d846ef ("tools: hv: Netlink source address validation allows DoS") does in hv_kvp_daemon, improve checks for origin of netlink connector message. Signed-off-by: Olaf Hering Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_vss_daemon.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index 2a03d0b4e213..dc3eb1e44c8a 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -186,13 +186,19 @@ int main(void) len = recvfrom(fd, vss_recv_buffer, sizeof(vss_recv_buffer), 0, addr_p, &addr_l); - if (len < 0 || addr.nl_pid) { + if (len < 0) { syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", addr.nl_pid, errno, strerror(errno)); close(fd); return -1; } + if (addr.nl_pid) { + syslog(LOG_WARNING, "Received packet from untrusted pid:%u", + addr.nl_pid); + continue; + } + incoming_msg = (struct nlmsghdr *)vss_recv_buffer; if (incoming_msg->nlmsg_type != NLMSG_DONE) -- cgit v1.2.3 From 038336a5b40ceeea394a6eb3e8c6fc75701eec47 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 24 Apr 2013 07:48:50 -0700 Subject: Tools: hv: Fix a checkpatch warning Fix a checkpatch warning. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_vss_daemon.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index dc3eb1e44c8a..921c1bec0305 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -194,8 +194,9 @@ int main(void) } if (addr.nl_pid) { - syslog(LOG_WARNING, "Received packet from untrusted pid:%u", - addr.nl_pid); + syslog(LOG_WARNING, + "Received packet from untrusted pid:%u", + addr.nl_pid); continue; } -- cgit v1.2.3 From d3d1ee3ab28711360937839423158cc185f710f2 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Wed, 24 Apr 2013 07:48:51 -0700 Subject: tools: hv: use getmntent in hv_vss_daemon As suggested by Paolo Bonzini, use getmntent instead of parsing output of mount(1). Signed-off-by: Olaf Hering Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_vss_daemon.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'tools') diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index 921c1bec0305..a5da91df4f76 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -47,11 +48,10 @@ static int vss_operate(int operation) { char *fs_op; char cmd[512]; - char buf[512]; - FILE *file; - char *p; - char *x; - int error = 0; + char match[] = "/dev/"; + FILE *mounts; + struct mntent *ent; + int error = 0, root_seen = 0; switch (operation) { case VSS_OP_FREEZE: @@ -64,25 +64,28 @@ static int vss_operate(int operation) return -1; } - file = popen("mount | awk '/^\\/dev\\// { print $3}'", "r"); - if (file == NULL) + mounts = setmntent("/proc/mounts", "r"); + if (mounts == NULL) return -1; - while ((p = fgets(buf, sizeof(buf), file)) != NULL) { - x = strchr(p, '\n'); - *x = '\0'; - if (!strncmp(p, "/", sizeof("/"))) + while((ent = getmntent(mounts))) { + if (strncmp(ent->mnt_fsname, match, strlen(match))) continue; - - sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, p); + if (strcmp(ent->mnt_dir, "/") == 0) { + root_seen = 1; + continue; + } + snprintf(cmd, sizeof(cmd), "fsfreeze %s '%s'", fs_op, ent->mnt_dir); syslog(LOG_INFO, "VSS cmd is %s\n", cmd); - error = system(cmd); + error |= system(cmd); } - pclose(file); + endmntent(mounts); - sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, "/"); - syslog(LOG_INFO, "VSS cmd is %s\n", cmd); - error = system(cmd); + if (root_seen) { + sprintf(cmd, "fsfreeze %s /", fs_op); + syslog(LOG_INFO, "VSS cmd is %s\n", cmd); + error |= system(cmd); + } return error; } -- cgit v1.2.3 From 7b413b65531422cc3d12a8acf53a43bc0d3c9f7b Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Wed, 24 Apr 2013 07:48:52 -0700 Subject: tools: hv: use FIFREEZE/FITHAW in hv_vss_daemon As suggested by Paolo Bonzini, use ioctl instead of calling fsfreeze. Signed-off-by: Olaf Hering Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_vss_daemon.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index a5da91df4f76..dc73a897d8c6 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -21,7 +21,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -30,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -44,21 +47,35 @@ static struct sockaddr_nl addr; #endif +static int vss_do_freeze(char *dir, unsigned int cmd, char *fs_op) +{ + int ret, fd = open(dir, O_RDONLY); + + if (fd < 0) + return 1; + ret = ioctl(fd, cmd, 0); + syslog(LOG_INFO, "VSS: %s of %s: %s\n", fs_op, dir, strerror(errno)); + close(fd); + return !!ret; +} + static int vss_operate(int operation) { char *fs_op; - char cmd[512]; char match[] = "/dev/"; FILE *mounts; struct mntent *ent; + unsigned int cmd; int error = 0, root_seen = 0; switch (operation) { case VSS_OP_FREEZE: - fs_op = "-f "; + cmd = FIFREEZE; + fs_op = "freeze"; break; case VSS_OP_THAW: - fs_op = "-u "; + cmd = FITHAW; + fs_op = "thaw"; break; default: return -1; @@ -75,16 +92,12 @@ static int vss_operate(int operation) root_seen = 1; continue; } - snprintf(cmd, sizeof(cmd), "fsfreeze %s '%s'", fs_op, ent->mnt_dir); - syslog(LOG_INFO, "VSS cmd is %s\n", cmd); - error |= system(cmd); + error |= vss_do_freeze(ent->mnt_dir, cmd, fs_op); } endmntent(mounts); if (root_seen) { - sprintf(cmd, "fsfreeze %s /", fs_op); - syslog(LOG_INFO, "VSS cmd is %s\n", cmd); - error |= system(cmd); + error |= vss_do_freeze("/", cmd, fs_op); } return error; -- cgit v1.2.3 From 10b637b4c8a21b299f691cecc4b6f16f44936c6d Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Wed, 24 Apr 2013 07:48:53 -0700 Subject: tools: hv: skip iso9660 mounts in hv_vss_daemon fsreeze does not work for iso9660 filesystems. A ENOSUPP may be caught in the freeze case, but the subsequent thaw call would fail and leads to a false error. Signed-off-by: Olaf Hering Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_vss_daemon.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools') diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index dc73a897d8c6..712cfc516c65 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -88,6 +88,8 @@ static int vss_operate(int operation) while((ent = getmntent(mounts))) { if (strncmp(ent->mnt_fsname, match, strlen(match))) continue; + if (strcmp(ent->mnt_type, "iso9660") == 0) + continue; if (strcmp(ent->mnt_dir, "/") == 0) { root_seen = 1; continue; -- cgit v1.2.3 From 0e27263926699fcbbd574cff4dd6920007a50e8a Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 24 Apr 2013 07:48:54 -0700 Subject: Tools: hv: Fix a checkpatch warning Fix a checkpatch warning. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_vss_daemon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index 712cfc516c65..fea03a3edaf4 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -85,7 +85,7 @@ static int vss_operate(int operation) if (mounts == NULL) return -1; - while((ent = getmntent(mounts))) { + while ((ent = getmntent(mounts))) { if (strncmp(ent->mnt_fsname, match, strlen(match))) continue; if (strcmp(ent->mnt_type, "iso9660") == 0) -- cgit v1.2.3 From df5f7c66013c46cec9aa8156af257bc5eb29b5c5 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 24 Apr 2013 16:03:30 -0400 Subject: ktest: Reset grub menu cache with different machines Different tests may use a different machine. In such cases, we need to try to get the current grub menu index. If the same grub menu is used for two different machines, it may not be at the same index on the second machine. A search for the index must be performed again. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 7958cd4d6567..0d7fd8b51544 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -73,6 +73,7 @@ my $ktest_config; my $version; my $have_version = 0; my $machine; +my $last_machine; my $ssh_user; my $tmpdir; my $builddir; @@ -1540,7 +1541,8 @@ sub run_scp_mod { sub get_grub2_index { return if (defined($grub_number) && defined($last_grub_menu) && - $last_grub_menu eq $grub_menu); + $last_grub_menu eq $grub_menu && defined($last_machine) && + $last_machine eq $machine); doprint "Find grub2 menu ... "; $grub_number = -1; @@ -1568,6 +1570,7 @@ sub get_grub2_index { if (!$found); doprint "$grub_number\n"; $last_grub_menu = $grub_menu; + $last_machine = $machine; } sub get_grub_index { @@ -1581,7 +1584,8 @@ sub get_grub_index { return; } return if (defined($grub_number) && defined($last_grub_menu) && - $last_grub_menu eq $grub_menu); + $last_grub_menu eq $grub_menu && defined($last_machine) && + $last_machine eq $machine); doprint "Find grub menu ... "; $grub_number = -1; @@ -1609,6 +1613,7 @@ sub get_grub_index { if (!$found); doprint "$grub_number\n"; $last_grub_menu = $grub_menu; + $last_machine = $machine; } sub wait_for_input -- cgit v1.2.3 From fdd5f43a1b53a844d04c6eda2cbdbe044b629ae7 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 24 Apr 2013 23:08:00 +0000 Subject: selftests: psock_tpacket: fix status check Testing like this for TP_STATUS_AVAILABLE clearly is a stupid bug since it always returns true. Fix this by only checking for flags where the kernel owns the packet and negate this result, since we also could run into the non-zero status TP_STATUS_WRONG_FORMAT and need to reclaim frames. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/testing/selftests/net/psock_tpacket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net/psock_tpacket.c b/tools/testing/selftests/net/psock_tpacket.c index a8d7ffadd49b..c41b58640a05 100644 --- a/tools/testing/selftests/net/psock_tpacket.c +++ b/tools/testing/selftests/net/psock_tpacket.c @@ -300,7 +300,7 @@ static void walk_v1_v2_rx(int sock, struct ring *ring) static inline int __v1_tx_kernel_ready(struct tpacket_hdr *hdr) { - return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE); + return !(hdr->tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING)); } static inline void __v1_tx_user_ready(struct tpacket_hdr *hdr) @@ -311,7 +311,7 @@ static inline void __v1_tx_user_ready(struct tpacket_hdr *hdr) static inline int __v2_tx_kernel_ready(struct tpacket2_hdr *hdr) { - return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE); + return !(hdr->tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING)); } static inline void __v2_tx_user_ready(struct tpacket2_hdr *hdr) -- cgit v1.2.3 From 58c7be84fec87b3a96964d65129b36c0e8c59a19 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Tue, 30 Apr 2013 15:27:06 -0700 Subject: selftest: add simple test for soft-dirty bit It creates a mapping of 3 pages and checks that reads, writes and clear-refs result in present and soft-dirt bits reported from pagemap2 set as expected. [akpm@linux-foundation.org: alphasort the Makefile TARGETS to reduce rejects] Signed-off-by: Pavel Emelyanov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/Makefile | 7 +- tools/testing/selftests/soft-dirty/Makefile | 10 +++ tools/testing/selftests/soft-dirty/soft-dirty.c | 114 ++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 tools/testing/selftests/soft-dirty/Makefile create mode 100644 tools/testing/selftests/soft-dirty/soft-dirty.c (limited to 'tools') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 3cc0ad7ae863..5eff5f785b35 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,10 +1,11 @@ TARGETS = breakpoints +TARGETS += cpu-hotplug +TARGETS += efivarfs TARGETS += kcmp +TARGETS += memory-hotplug TARGETS += mqueue +TARGETS += soft-dirty TARGETS += vm -TARGETS += cpu-hotplug -TARGETS += memory-hotplug -TARGETS += efivarfs all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/soft-dirty/Makefile b/tools/testing/selftests/soft-dirty/Makefile new file mode 100644 index 000000000000..a9cdc823d6e0 --- /dev/null +++ b/tools/testing/selftests/soft-dirty/Makefile @@ -0,0 +1,10 @@ +CFLAGS += -iquote../../../../include/uapi -Wall +soft-dirty: soft-dirty.c + +all: soft-dirty + +clean: + rm -f soft-dirty + +run_tests: all + @./soft-dirty || echo "soft-dirty selftests: [FAIL]" diff --git a/tools/testing/selftests/soft-dirty/soft-dirty.c b/tools/testing/selftests/soft-dirty/soft-dirty.c new file mode 100644 index 000000000000..aba4f87f87f0 --- /dev/null +++ b/tools/testing/selftests/soft-dirty/soft-dirty.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include + +typedef unsigned long long u64; + +#define PME_PRESENT (1ULL << 63) +#define PME_SOFT_DIRTY (1Ull << 55) + +#define PAGES_TO_TEST 3 +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +static void get_pagemap2(char *mem, u64 *map) +{ + int fd; + + fd = open("/proc/self/pagemap2", O_RDONLY); + if (fd < 0) { + perror("Can't open pagemap2"); + exit(1); + } + + lseek(fd, (unsigned long)mem / PAGE_SIZE * sizeof(u64), SEEK_SET); + read(fd, map, sizeof(u64) * PAGES_TO_TEST); + close(fd); +} + +static inline char map_p(u64 map) +{ + return map & PME_PRESENT ? 'p' : '-'; +} + +static inline char map_sd(u64 map) +{ + return map & PME_SOFT_DIRTY ? 'd' : '-'; +} + +static int check_pte(int step, int page, u64 *map, u64 want) +{ + if ((map[page] & want) != want) { + printf("Step %d Page %d has %c%c, want %c%c\n", + step, page, + map_p(map[page]), map_sd(map[page]), + map_p(want), map_sd(want)); + return 1; + } + + return 0; +} + +static void clear_refs(void) +{ + int fd; + char *v = "4"; + + fd = open("/proc/self/clear_refs", O_WRONLY); + if (write(fd, v, 3) < 3) { + perror("Can't clear soft-dirty bit"); + exit(1); + } + close(fd); +} + +int main(void) +{ + char *mem, x; + u64 map[PAGES_TO_TEST]; + + mem = mmap(NULL, PAGES_TO_TEST * PAGE_SIZE, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); + + x = mem[0]; + mem[2 * PAGE_SIZE] = 'c'; + get_pagemap2(mem, map); + + if (check_pte(1, 0, map, PME_PRESENT)) + return 1; + if (check_pte(1, 1, map, 0)) + return 1; + if (check_pte(1, 2, map, PME_PRESENT | PME_SOFT_DIRTY)) + return 1; + + clear_refs(); + get_pagemap2(mem, map); + + if (check_pte(2, 0, map, PME_PRESENT)) + return 1; + if (check_pte(2, 1, map, 0)) + return 1; + if (check_pte(2, 2, map, PME_PRESENT)) + return 1; + + mem[0] = 'a'; + mem[PAGE_SIZE] = 'b'; + x = mem[2 * PAGE_SIZE]; + get_pagemap2(mem, map); + + if (check_pte(3, 0, map, PME_PRESENT | PME_SOFT_DIRTY)) + return 1; + if (check_pte(3, 1, map, PME_PRESENT | PME_SOFT_DIRTY)) + return 1; + if (check_pte(3, 2, map, PME_PRESENT)) + return 1; + + (void)x; /* gcc warn */ + + printf("PASS\n"); + return 0; +} -- cgit v1.2.3 From 17afab1de42236ee2f6235f4383cc6f3f13f8a10 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Tue, 30 Apr 2013 15:28:00 -0700 Subject: selftest: add a test case for PTRACE_PEEKSIGINFO * Dump signals from process-wide and per-thread queues with different sizes of buffers. * Check error paths for buffers with restricted permissions. A part of buffer or a whole buffer is for read-only. * Try to get nonexistent signal. Signed-off-by: Andrew Vagin Cc: Roland McGrath Cc: Oleg Nesterov Cc: "Paul E. McKenney" Cc: David Howells Cc: Dave Jones Cc: "Michael Kerrisk (man-pages)" Cc: Pavel Emelyanov Cc: Linus Torvalds Cc: Pedro Alves Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/ptrace/Makefile | 10 ++ tools/testing/selftests/ptrace/peeksiginfo.c | 214 +++++++++++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 tools/testing/selftests/ptrace/Makefile create mode 100644 tools/testing/selftests/ptrace/peeksiginfo.c (limited to 'tools') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 5eff5f785b35..fa6ea69f2e48 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -4,6 +4,7 @@ TARGETS += efivarfs TARGETS += kcmp TARGETS += memory-hotplug TARGETS += mqueue +TARGETS += ptrace TARGETS += soft-dirty TARGETS += vm diff --git a/tools/testing/selftests/ptrace/Makefile b/tools/testing/selftests/ptrace/Makefile new file mode 100644 index 000000000000..47ae2d385ce8 --- /dev/null +++ b/tools/testing/selftests/ptrace/Makefile @@ -0,0 +1,10 @@ +CFLAGS += -iquote../../../../include/uapi -Wall +peeksiginfo: peeksiginfo.c + +all: peeksiginfo + +clean: + rm -f peeksiginfo + +run_tests: all + @./peeksiginfo || echo "peeksiginfo selftests: [FAIL]" diff --git a/tools/testing/selftests/ptrace/peeksiginfo.c b/tools/testing/selftests/ptrace/peeksiginfo.c new file mode 100644 index 000000000000..d46558b1f58d --- /dev/null +++ b/tools/testing/selftests/ptrace/peeksiginfo.c @@ -0,0 +1,214 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "linux/ptrace.h" + +static int sys_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *uinfo) +{ + return syscall(SYS_rt_sigqueueinfo, tgid, sig, uinfo); +} + +static int sys_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, + int sig, siginfo_t *uinfo) +{ + return syscall(SYS_rt_tgsigqueueinfo, tgid, tid, sig, uinfo); +} + +static int sys_ptrace(int request, pid_t pid, void *addr, void *data) +{ + return syscall(SYS_ptrace, request, pid, addr, data); +} + +#define SIGNR 10 +#define TEST_SICODE_PRIV -1 +#define TEST_SICODE_SHARE -2 + +#define err(fmt, ...) \ + fprintf(stderr, \ + "Error (%s:%d): " fmt, \ + __FILE__, __LINE__, ##__VA_ARGS__) + +static int check_error_paths(pid_t child) +{ + struct ptrace_peeksiginfo_args arg; + int ret, exit_code = -1; + void *addr_rw, *addr_ro; + + /* + * Allocate two contiguous pages. The first one is for read-write, + * another is for read-only. + */ + addr_rw = mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (addr_rw == MAP_FAILED) { + err("mmap() failed: %m\n"); + return 1; + } + + addr_ro = mmap(addr_rw + PAGE_SIZE, PAGE_SIZE, PROT_READ, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (addr_ro == MAP_FAILED) { + err("mmap() failed: %m\n"); + goto out; + } + + arg.nr = SIGNR; + arg.off = 0; + + /* Unsupported flags */ + arg.flags = ~0; + ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg, addr_rw); + if (ret != -1 || errno != EINVAL) { + err("sys_ptrace() returns %d (expected -1)," + " errno %d (expected %d): %m\n", + ret, errno, EINVAL); + goto out; + } + arg.flags = 0; + + /* A part of the buffer is read-only */ + ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg, + addr_ro - sizeof(siginfo_t) * 2); + if (ret != 2) { + err("sys_ptrace() returns %d (expected 2): %m\n", ret); + goto out; + } + + /* Read-only buffer */ + ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg, addr_ro); + if (ret != -1 && errno != EFAULT) { + err("sys_ptrace() returns %d (expected -1)," + " errno %d (expected %d): %m\n", + ret, errno, EFAULT); + goto out; + } + + exit_code = 0; +out: + munmap(addr_rw, 2 * PAGE_SIZE); + return exit_code; +} + +int check_direct_path(pid_t child, int shared, int nr) +{ + struct ptrace_peeksiginfo_args arg = {.flags = 0, .nr = nr, .off = 0}; + int i, j, ret, exit_code = -1; + siginfo_t siginfo[SIGNR]; + int si_code; + + if (shared == 1) { + arg.flags = PTRACE_PEEKSIGINFO_SHARED; + si_code = TEST_SICODE_SHARE; + } else { + arg.flags = 0; + si_code = TEST_SICODE_PRIV; + } + + for (i = 0; i < SIGNR; ) { + arg.off = i; + ret = sys_ptrace(PTRACE_PEEKSIGINFO, child, &arg, siginfo); + if (ret == -1) { + err("ptrace() failed: %m\n"); + goto out; + } + + if (ret == 0) + break; + + for (j = 0; j < ret; j++, i++) { + if (siginfo[j].si_code == si_code && + siginfo[j].si_int == i) + continue; + + err("%d: Wrong siginfo i=%d si_code=%d si_int=%d\n", + shared, i, siginfo[j].si_code, siginfo[j].si_int); + goto out; + } + } + + if (i != SIGNR) { + err("Only %d signals were read\n", i); + goto out; + } + + exit_code = 0; +out: + return exit_code; +} + +int main(int argc, char *argv[]) +{ + siginfo_t siginfo[SIGNR]; + int i, exit_code = 1; + sigset_t blockmask; + pid_t child; + + sigemptyset(&blockmask); + sigaddset(&blockmask, SIGRTMIN); + sigprocmask(SIG_BLOCK, &blockmask, NULL); + + child = fork(); + if (child == -1) { + err("fork() failed: %m"); + return 1; + } else if (child == 0) { + pid_t ppid = getppid(); + while (1) { + if (ppid != getppid()) + break; + sleep(1); + } + return 1; + } + + /* Send signals in process-wide and per-thread queues */ + for (i = 0; i < SIGNR; i++) { + siginfo->si_code = TEST_SICODE_SHARE; + siginfo->si_int = i; + sys_rt_sigqueueinfo(child, SIGRTMIN, siginfo); + + siginfo->si_code = TEST_SICODE_PRIV; + siginfo->si_int = i; + sys_rt_tgsigqueueinfo(child, child, SIGRTMIN, siginfo); + } + + if (sys_ptrace(PTRACE_ATTACH, child, NULL, NULL) == -1) + return 1; + + waitpid(child, NULL, 0); + + /* Dump signals one by one*/ + if (check_direct_path(child, 0, 1)) + goto out; + /* Dump all signals for one call */ + if (check_direct_path(child, 0, SIGNR)) + goto out; + + /* + * Dump signal from the process-wide queue. + * The number of signals is not multible to the buffer size + */ + if (check_direct_path(child, 1, 3)) + goto out; + + if (check_error_paths(child)) + goto out; + + printf("PASS\n"); + exit_code = 0; +out: + if (sys_ptrace(PTRACE_KILL, child, NULL, NULL) == -1) + return 1; + + waitpid(child, NULL, 0); + + return exit_code; +} -- cgit v1.2.3