aboutsummaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests
diff options
context:
space:
mode:
authorMichael Ellerman <michael@ellerman.id.au>2013-08-06 03:42:37 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-08-14 00:57:10 -0400
commitcb96143defbd5516c351595d56b608ed915b525e (patch)
tree427dc6a4231f1c3b1ed9f7a327676223487d2b07 /tools/testing/selftests
parent2fae0d7ced53ef2e7b4d87d84986ec3ff7cf798f (diff)
selftests: Add test of PMU instruction counting on powerpc
This commit adds a test of instruction counting using the PMU on powerpc. Although the bulk of the code is architecture agnostic, the code needs to run a precisely sized loop which is implemented in assembler. Signed-off-by: Michael Ellerman <michael@ellerman.id.au> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'tools/testing/selftests')
-rw-r--r--tools/testing/selftests/powerpc/Makefile2
-rw-r--r--tools/testing/selftests/powerpc/pmu/Makefile23
-rw-r--r--tools/testing/selftests/powerpc/pmu/count_instructions.c135
-rw-r--r--tools/testing/selftests/powerpc/pmu/event.c105
-rw-r--r--tools/testing/selftests/powerpc/pmu/event.h39
-rw-r--r--tools/testing/selftests/powerpc/pmu/loop.S46
6 files changed, 349 insertions, 1 deletions
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile
index b315740e4cd9..bd24ae5aaeab 100644
--- a/tools/testing/selftests/powerpc/Makefile
+++ b/tools/testing/selftests/powerpc/Makefile
@@ -13,7 +13,7 @@ CFLAGS := -Wall -O2 -flto -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CUR
13 13
14export CC CFLAGS 14export CC CFLAGS
15 15
16TARGETS = 16TARGETS = pmu
17 17
18endif 18endif
19 19
diff --git a/tools/testing/selftests/powerpc/pmu/Makefile b/tools/testing/selftests/powerpc/pmu/Makefile
new file mode 100644
index 000000000000..7216f0091655
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/Makefile
@@ -0,0 +1,23 @@
1noarg:
2 $(MAKE) -C ../
3
4PROGS := count_instructions
5EXTRA_SOURCES := ../harness.c event.c
6
7all: $(PROGS)
8
9$(PROGS): $(EXTRA_SOURCES)
10
11# loop.S can only be built 64-bit
12count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES)
13 $(CC) $(CFLAGS) -m64 -o $@ $^
14
15run_tests: all
16 @-for PROG in $(PROGS); do \
17 ./$$PROG; \
18 done;
19
20clean:
21 rm -f $(PROGS) loop.o
22
23.PHONY: all run_tests clean
diff --git a/tools/testing/selftests/powerpc/pmu/count_instructions.c b/tools/testing/selftests/powerpc/pmu/count_instructions.c
new file mode 100644
index 000000000000..312b4f0fd27c
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/count_instructions.c
@@ -0,0 +1,135 @@
1/*
2 * Copyright 2013, Michael Ellerman, IBM Corp.
3 * Licensed under GPLv2.
4 */
5
6#define _GNU_SOURCE
7
8#include <stdio.h>
9#include <stdbool.h>
10#include <string.h>
11#include <sys/prctl.h>
12
13#include "event.h"
14#include "utils.h"
15
16extern void thirty_two_instruction_loop(u64 loops);
17
18static void setup_event(struct event *e, u64 config, char *name)
19{
20 event_init_opts(e, config, PERF_TYPE_HARDWARE, name);
21
22 e->attr.disabled = 1;
23 e->attr.exclude_kernel = 1;
24 e->attr.exclude_hv = 1;
25 e->attr.exclude_idle = 1;
26}
27
28static int do_count_loop(struct event *events, u64 instructions,
29 u64 overhead, bool report)
30{
31 s64 difference, expected;
32 double percentage;
33
34 prctl(PR_TASK_PERF_EVENTS_ENABLE);
35
36 /* Run for 1M instructions */
37 thirty_two_instruction_loop(instructions >> 5);
38
39 prctl(PR_TASK_PERF_EVENTS_DISABLE);
40
41 event_read(&events[0]);
42 event_read(&events[1]);
43
44 expected = instructions + overhead;
45 difference = events[0].result.value - expected;
46 percentage = (double)difference / events[0].result.value * 100;
47
48 if (report) {
49 event_report(&events[0]);
50 event_report(&events[1]);
51
52 printf("Looped for %llu instructions, overhead %llu\n", instructions, overhead);
53 printf("Expected %llu\n", expected);
54 printf("Actual %llu\n", events[0].result.value);
55 printf("Delta %lld, %f%%\n", difference, percentage);
56 }
57
58 event_reset(&events[0]);
59 event_reset(&events[1]);
60
61 if (difference < 0)
62 difference = -difference;
63
64 /* Tolerate a difference below 0.0001 % */
65 difference *= 10000 * 100;
66 if (difference / events[0].result.value)
67 return -1;
68
69 return 0;
70}
71
72/* Count how many instructions it takes to do a null loop */
73static u64 determine_overhead(struct event *events)
74{
75 u64 current, overhead;
76 int i;
77
78 do_count_loop(events, 0, 0, false);
79 overhead = events[0].result.value;
80
81 for (i = 0; i < 100; i++) {
82 do_count_loop(events, 0, 0, false);
83 current = events[0].result.value;
84 if (current < overhead) {
85 printf("Replacing overhead %llu with %llu\n", overhead, current);
86 overhead = current;
87 }
88 }
89
90 return overhead;
91}
92
93static int count_instructions(void)
94{
95 struct event events[2];
96 u64 overhead;
97
98 setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, "instructions");
99 setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, "cycles");
100
101 if (event_open(&events[0])) {
102 perror("perf_event_open");
103 return -1;
104 }
105
106 if (event_open_with_group(&events[1], events[0].fd)) {
107 perror("perf_event_open");
108 return -1;
109 }
110
111 overhead = determine_overhead(events);
112 printf("Overhead of null loop: %llu instructions\n", overhead);
113
114 /* Run for 1M instructions */
115 FAIL_IF(do_count_loop(events, 0x100000, overhead, true));
116
117 /* Run for 10M instructions */
118 FAIL_IF(do_count_loop(events, 0xa00000, overhead, true));
119
120 /* Run for 100M instructions */
121 FAIL_IF(do_count_loop(events, 0x6400000, overhead, true));
122
123 /* Run for 1G instructions */
124 FAIL_IF(do_count_loop(events, 0x40000000, overhead, true));
125
126 event_close(&events[0]);
127 event_close(&events[1]);
128
129 return 0;
130}
131
132int main(void)
133{
134 return test_harness(count_instructions, "count_instructions");
135}
diff --git a/tools/testing/selftests/powerpc/pmu/event.c b/tools/testing/selftests/powerpc/pmu/event.c
new file mode 100644
index 000000000000..2b2d11df2450
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/event.c
@@ -0,0 +1,105 @@
1/*
2 * Copyright 2013, Michael Ellerman, IBM Corp.
3 * Licensed under GPLv2.
4 */
5
6#define _GNU_SOURCE
7#include <unistd.h>
8#include <sys/syscall.h>
9#include <string.h>
10#include <stdio.h>
11#include <sys/ioctl.h>
12
13#include "event.h"
14
15
16int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu,
17 int group_fd, unsigned long flags)
18{
19 return syscall(__NR_perf_event_open, attr, pid, cpu,
20 group_fd, flags);
21}
22
23void event_init_opts(struct event *e, u64 config, int type, char *name)
24{
25 memset(e, 0, sizeof(*e));
26
27 e->name = name;
28
29 e->attr.type = type;
30 e->attr.config = config;
31 e->attr.size = sizeof(e->attr);
32 /* This has to match the structure layout in the header */
33 e->attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | \
34 PERF_FORMAT_TOTAL_TIME_RUNNING;
35}
36
37void event_init_named(struct event *e, u64 config, char *name)
38{
39 event_init_opts(e, config, PERF_TYPE_RAW, name);
40}
41
42#define PERF_CURRENT_PID 0
43#define PERF_NO_CPU -1
44#define PERF_NO_GROUP -1
45
46int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd)
47{
48 e->fd = perf_event_open(&e->attr, pid, cpu, group_fd, 0);
49 if (e->fd == -1) {
50 perror("perf_event_open");
51 return -1;
52 }
53
54 return 0;
55}
56
57int event_open_with_group(struct event *e, int group_fd)
58{
59 return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, group_fd);
60}
61
62int event_open(struct event *e)
63{
64 return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, PERF_NO_GROUP);
65}
66
67void event_close(struct event *e)
68{
69 close(e->fd);
70}
71
72int event_reset(struct event *e)
73{
74 return ioctl(e->fd, PERF_EVENT_IOC_RESET);
75}
76
77int event_read(struct event *e)
78{
79 int rc;
80
81 rc = read(e->fd, &e->result, sizeof(e->result));
82 if (rc != sizeof(e->result)) {
83 fprintf(stderr, "read error on event %p!\n", e);
84 return -1;
85 }
86
87 return 0;
88}
89
90void event_report_justified(struct event *e, int name_width, int result_width)
91{
92 printf("%*s: result %*llu ", name_width, e->name, result_width,
93 e->result.value);
94
95 if (e->result.running == e->result.enabled)
96 printf("running/enabled %llu\n", e->result.running);
97 else
98 printf("running %llu enabled %llu\n", e->result.running,
99 e->result.enabled);
100}
101
102void event_report(struct event *e)
103{
104 event_report_justified(e, 0, 0);
105}
diff --git a/tools/testing/selftests/powerpc/pmu/event.h b/tools/testing/selftests/powerpc/pmu/event.h
new file mode 100644
index 000000000000..e6993192ff34
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/event.h
@@ -0,0 +1,39 @@
1/*
2 * Copyright 2013, Michael Ellerman, IBM Corp.
3 * Licensed under GPLv2.
4 */
5
6#ifndef _SELFTESTS_POWERPC_PMU_EVENT_H
7#define _SELFTESTS_POWERPC_PMU_EVENT_H
8
9#include <unistd.h>
10#include <linux/perf_event.h>
11
12#include "utils.h"
13
14
15struct event {
16 struct perf_event_attr attr;
17 char *name;
18 int fd;
19 /* This must match the read_format we use */
20 struct {
21 u64 value;
22 u64 running;
23 u64 enabled;
24 } result;
25};
26
27void event_init(struct event *e, u64 config);
28void event_init_named(struct event *e, u64 config, char *name);
29void event_init_opts(struct event *e, u64 config, int type, char *name);
30int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd);
31int event_open_with_group(struct event *e, int group_fd);
32int event_open(struct event *e);
33void event_close(struct event *e);
34int event_reset(struct event *e);
35int event_read(struct event *e);
36void event_report_justified(struct event *e, int name_width, int result_width);
37void event_report(struct event *e);
38
39#endif /* _SELFTESTS_POWERPC_PMU_EVENT_H */
diff --git a/tools/testing/selftests/powerpc/pmu/loop.S b/tools/testing/selftests/powerpc/pmu/loop.S
new file mode 100644
index 000000000000..8820e3df1444
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/loop.S
@@ -0,0 +1,46 @@
1/*
2 * Copyright 2013, Michael Ellerman, IBM Corp.
3 * Licensed under GPLv2.
4 */
5
6 .text
7
8 .global thirty_two_instruction_loop
9 .type .thirty_two_instruction_loop,@function
10 .section ".opd","aw",@progbits
11thirty_two_instruction_loop:
12 .quad .thirty_two_instruction_loop, .TOC.@tocbase, 0
13 .previous
14.thirty_two_instruction_loop:
15 cmpwi %r3,0
16 beqlr
17 addi %r4,%r3,1
18 addi %r4,%r4,1
19 addi %r4,%r4,1
20 addi %r4,%r4,1
21 addi %r4,%r4,1
22 addi %r4,%r4,1
23 addi %r4,%r4,1
24 addi %r4,%r4,1
25 addi %r4,%r4,1
26 addi %r4,%r4,1
27 addi %r4,%r4,1
28 addi %r4,%r4,1
29 addi %r4,%r4,1
30 addi %r4,%r4,1
31 addi %r4,%r4,1
32 addi %r4,%r4,1
33 addi %r4,%r4,1
34 addi %r4,%r4,1
35 addi %r4,%r4,1
36 addi %r4,%r4,1
37 addi %r4,%r4,1
38 addi %r4,%r4,1
39 addi %r4,%r4,1
40 addi %r4,%r4,1
41 addi %r4,%r4,1
42 addi %r4,%r4,1
43 addi %r4,%r4,1
44 addi %r4,%r4,1 # 28 addi's
45 subi %r3,%r3,1
46 b .thirty_two_instruction_loop