subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 1 | /******************************************************************************/ |
| 2 | /* */ |
| 3 | /* Paul Mackerras <paulus@samba.org>, 2009 */ |
| 4 | /* */ |
| 5 | /* This program is free software; you can redistribute it and/or modify */ |
| 6 | /* it under the terms of the GNU General Public License as published by */ |
| 7 | /* the Free Software Foundation; either version 2 of the License, or */ |
| 8 | /* (at your option) any later version. */ |
| 9 | /* */ |
| 10 | /* This program is distributed in the hope that it will be useful, */ |
| 11 | /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ |
| 12 | /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ |
| 13 | /* the GNU General Public License for more details. */ |
| 14 | /* */ |
| 15 | /* You should have received a copy of the GNU General Public License */ |
| 16 | /* along with this program; if not, write to the Free Software */ |
Wanlong Gao | 4548c6c | 2012-10-19 18:03:36 +0800 | [diff] [blame] | 17 | /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 18 | /* */ |
| 19 | /******************************************************************************/ |
| 20 | /* |
| 21 | Here's a little test program that checks whether software counters |
| 22 | (specifically, the task clock counter) work correctly when they're in |
| 23 | a group with hardware counters. |
| 24 | |
| 25 | What it does is to create several groups, each with one hardware |
| 26 | counter, counting instructions, plus a task clock counter. It needs |
| 27 | to know an upper bound N on the number of hardware counters you have |
| 28 | (N defaults to 8), and it creates N+4 groups to force them to be |
| 29 | multiplexed. It also creates an overall task clock counter. |
| 30 | |
| 31 | Then it spins for a while, and then stops all the counters and reads |
| 32 | them. It takes the total of the task clock counters in the groups and |
| 33 | computes the ratio of that total to the overall execution time from |
| 34 | the overall task clock counter. |
| 35 | |
| 36 | That ratio should be equal to the number of actual hardware counters |
| 37 | that can count instructions. If the task clock counters in the groups |
| 38 | don't stop when their group gets taken off the PMU, the ratio will |
| 39 | instead be close to N+4. The program will declare that the test fails |
| 40 | if the ratio is greater than N (actually, N + 0.0001 to allow for FP |
| 41 | rounding errors). |
| 42 | |
| 43 | Could someone run this on x86 on the latest PCL tree and let me know |
| 44 | what happens? I don't have an x86 crash box easily to hand. On |
| 45 | powerpc, it passes, but I think that is because I am missing setting |
| 46 | counter->prev_count in arch/powerpc/kernel/perf_counter.c, and I think |
| 47 | that means that enabling/disabling a group with a task clock counter |
| 48 | in it won't work correctly (I'll do a test program for that next). |
| 49 | |
| 50 | Usage is: ./performance_counter02 [-c num-hw-counters] [-v] |
| 51 | |
| 52 | Use -c N if you have more than 8 hardware counters. The -v flag makes |
| 53 | it print out the values of each counter. |
| 54 | */ |
| 55 | |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 56 | #include <stdio.h> |
| 57 | #include <stddef.h> |
| 58 | #include <stdlib.h> |
| 59 | #include <string.h> |
| 60 | #include <fcntl.h> |
| 61 | #include <poll.h> |
| 62 | #include <unistd.h> |
| 63 | #include <errno.h> |
yaberauneya | ef77253 | 2009-10-09 17:55:43 +0000 | [diff] [blame] | 64 | #include "config.h" |
| 65 | #include <sys/prctl.h> |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 66 | #include <sys/types.h> |
| 67 | #include <linux/types.h> |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 68 | |
| 69 | /* Harness Specific Include Files. */ |
| 70 | #include "test.h" |
| 71 | #include "usctest.h" |
subrata_modak | d076204 | 2009-06-23 14:21:32 +0000 | [diff] [blame] | 72 | #include "linux_syscall_numbers.h" |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 73 | |
| 74 | #define PR_TASK_PERF_COUNTERS_DISABLE 31 |
| 75 | #define PR_TASK_PERF_COUNTERS_ENABLE 32 |
| 76 | |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 77 | /* Global Variables */ |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 78 | char *TCID = "performance_counter02"; /* test program identifier. */ |
| 79 | int TST_TOTAL = 1; /* total number of tests in this file. */ |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 80 | |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 81 | typedef unsigned int u32; |
| 82 | typedef unsigned long long u64; |
| 83 | typedef long long s64; |
| 84 | |
| 85 | struct perf_counter_hw_event { |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 86 | s64 type; |
| 87 | u64 irq_period; |
| 88 | u32 record_type; |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 89 | |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 90 | u32 disabled:1, /* off by default */ |
| 91 | nmi:1, /* NMI sampling */ |
| 92 | raw:1, /* raw event type */ |
| 93 | __reserved_1:29; |
| 94 | u64 __reserved_2; |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 95 | }; |
| 96 | |
| 97 | enum hw_event_types { |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 98 | PERF_COUNT_CYCLES = 0, |
| 99 | PERF_COUNT_INSTRUCTIONS = 1, |
| 100 | PERF_COUNT_CACHE_REFERENCES = 2, |
| 101 | PERF_COUNT_CACHE_MISSES = 3, |
| 102 | PERF_COUNT_BRANCH_INSTRUCTIONS = 4, |
| 103 | PERF_COUNT_BRANCH_MISSES = 5, |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 104 | |
| 105 | /* |
| 106 | * Special "software" counters provided by the kernel, even if |
| 107 | * the hardware does not support performance counters. These |
| 108 | * counters measure various physical and sw events of the |
| 109 | * kernel (and allow the profiling of them as well): |
| 110 | */ |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 111 | PERF_COUNT_CPU_CLOCK = -1, |
| 112 | PERF_COUNT_TASK_CLOCK = -2, |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 113 | /* |
| 114 | * Future software events: |
| 115 | */ |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 116 | /* PERF_COUNT_PAGE_FAULTS = -3, |
| 117 | PERF_COUNT_CONTEXT_SWITCHES = -4, */ |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 118 | }; |
| 119 | |
| 120 | int sys_perf_counter_open(struct perf_counter_hw_event *hw_event, |
| 121 | pid_t pid, int cpu, int group_fd, unsigned long flags) |
| 122 | { |
Jan Stancek | 359980f | 2013-02-15 10:16:05 +0100 | [diff] [blame^] | 123 | return ltp_syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 124 | flags); |
| 125 | } |
| 126 | |
| 127 | #define MAX_CTRS 50 |
| 128 | #define LOOPS 1000000000 |
| 129 | |
| 130 | void do_work(void) |
| 131 | { |
| 132 | int i; |
| 133 | |
| 134 | for (i = 0; i < LOOPS; ++i) |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 135 | asm volatile (""::"g" (i)); |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 136 | } |
| 137 | |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 138 | void cleanup(void) |
| 139 | { /* Stub function. */ |
| 140 | } |
yaberauneya | 1a1b1fb | 2009-11-27 08:15:35 +0000 | [diff] [blame] | 141 | |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 142 | int main(int ac, char **av) |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 143 | { |
| 144 | int tsk0; |
| 145 | int hwfd[MAX_CTRS], tskfd[MAX_CTRS]; |
| 146 | struct perf_counter_hw_event tsk_event; |
| 147 | struct perf_counter_hw_event hw_event; |
| 148 | unsigned long long vt0, vt[MAX_CTRS], vh[MAX_CTRS], vtsum, vhsum; |
| 149 | int i, n, nhw; |
| 150 | int verbose = 0; |
| 151 | double ratio; |
| 152 | |
| 153 | nhw = 8; |
| 154 | while ((i = getopt(ac, av, "c:v")) != -1) { |
| 155 | switch (i) { |
| 156 | case 'c': |
| 157 | n = atoi(optarg); |
| 158 | break; |
| 159 | case 'v': |
| 160 | verbose = 1; |
| 161 | break; |
| 162 | case '?': |
| 163 | fprintf(stderr, "Usage: %s [-c #hwctrs] [-v]\n", av[0]); |
| 164 | exit(1); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | if (nhw < 0 || nhw > MAX_CTRS - 4) { |
| 169 | fprintf(stderr, "invalid number of hw counters specified: %d\n", |
| 170 | nhw); |
| 171 | exit(1); |
| 172 | } |
| 173 | |
| 174 | n = nhw + 4; |
| 175 | |
| 176 | memset(&tsk_event, 0, sizeof(tsk_event)); |
| 177 | tsk_event.type = PERF_COUNT_TASK_CLOCK; |
| 178 | tsk_event.disabled = 1; |
| 179 | |
| 180 | memset(&hw_event, 0, sizeof(hw_event)); |
| 181 | hw_event.disabled = 1; |
| 182 | hw_event.type = PERF_COUNT_INSTRUCTIONS; |
| 183 | |
| 184 | tsk0 = sys_perf_counter_open(&tsk_event, 0, -1, -1, 0); |
| 185 | if (tsk0 == -1) { |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 186 | tst_brkm(TBROK | TERRNO, cleanup, |
| 187 | "perf_counter_open failed (1)"); |
yaberauneya | 1a1b1fb | 2009-11-27 08:15:35 +0000 | [diff] [blame] | 188 | } else { |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 189 | |
yaberauneya | 1a1b1fb | 2009-11-27 08:15:35 +0000 | [diff] [blame] | 190 | tsk_event.disabled = 0; |
| 191 | for (i = 0; i < n; ++i) { |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 192 | hwfd[i] = sys_perf_counter_open(&hw_event, 0, -1, |
| 193 | -1, 0); |
yaberauneya | 1a1b1fb | 2009-11-27 08:15:35 +0000 | [diff] [blame] | 194 | tskfd[i] = sys_perf_counter_open(&tsk_event, 0, -1, |
| 195 | hwfd[i], 0); |
| 196 | if (tskfd[i] == -1 || hwfd[i] == -1) { |
| 197 | tst_brkm(TBROK | TERRNO, cleanup, |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 198 | "perf_counter_open failed (2)"); |
yaberauneya | 1a1b1fb | 2009-11-27 08:15:35 +0000 | [diff] [blame] | 199 | } |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 200 | } |
| 201 | } |
| 202 | |
| 203 | prctl(PR_TASK_PERF_COUNTERS_ENABLE); |
| 204 | do_work(); |
| 205 | prctl(PR_TASK_PERF_COUNTERS_DISABLE); |
| 206 | |
| 207 | if (read(tsk0, &vt0, sizeof(vt0)) != sizeof(vt0)) { |
yaberauneya | 1a1b1fb | 2009-11-27 08:15:35 +0000 | [diff] [blame] | 208 | tst_brkm(TBROK | TERRNO, cleanup, |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 209 | "error reading task clock counter"); |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 210 | } |
| 211 | |
| 212 | vtsum = vhsum = 0; |
| 213 | for (i = 0; i < n; ++i) { |
| 214 | if (read(tskfd[i], &vt[i], sizeof(vt[i])) != sizeof(vt[i]) || |
| 215 | read(hwfd[i], &vh[i], sizeof(vh[i])) != sizeof(vh[i])) { |
yaberauneya | 1a1b1fb | 2009-11-27 08:15:35 +0000 | [diff] [blame] | 216 | tst_brkm(TBROK | TERRNO, cleanup, |
Wanlong Gao | 354ebb4 | 2012-12-07 10:10:04 +0800 | [diff] [blame] | 217 | "error reading counter(s)"); |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 218 | } |
| 219 | vtsum += vt[i]; |
| 220 | vhsum += vh[i]; |
| 221 | } |
| 222 | |
yaberauneya | 1a1b1fb | 2009-11-27 08:15:35 +0000 | [diff] [blame] | 223 | tst_resm(TINFO, "overall task clock: %lld", vt0); |
| 224 | tst_resm(TINFO, "hw sum: %lld, task clock sum: %lld", vhsum, vtsum); |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 225 | if (verbose) { |
| 226 | printf("hw counters:"); |
| 227 | for (i = 0; i < n; ++i) |
| 228 | printf(" %lld", vh[i]); |
| 229 | printf("\ntask clock counters:"); |
| 230 | for (i = 0; i < n; ++i) |
| 231 | printf(" %lld", vt[i]); |
| 232 | printf("\n"); |
| 233 | } |
| 234 | ratio = (double)vtsum / vt0; |
yaberauneya | 1a1b1fb | 2009-11-27 08:15:35 +0000 | [diff] [blame] | 235 | tst_resm(TINFO, "ratio: %.2f", ratio); |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 236 | if (ratio > nhw + 0.0001) { |
yaberauneya | 1a1b1fb | 2009-11-27 08:15:35 +0000 | [diff] [blame] | 237 | tst_resm(TFAIL, "test failed (ratio was greater than )"); |
| 238 | } else { |
| 239 | tst_resm(TINFO, "test passed"); |
subrata_modak | 89823fe | 2009-04-02 06:48:41 +0000 | [diff] [blame] | 240 | } |
Garrett Cooper | 49f7622 | 2010-12-19 10:22:13 -0800 | [diff] [blame] | 241 | tst_exit(); |
| 242 | } |