1/******************************************************************************/
2/*                                                                            */
3/* Ingo Molnar <mingo@elte.hu>, 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               */
17/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA    */
18/*                                                                            */
19/******************************************************************************/
20
21/*
22 * Very simple performance counter testcase.
23 * Picked up from: http://lkml.org/lkml/2008/12/5/17
24 */
25
26#include <sys/types.h>
27#include <sys/ioctl.h>
28#include <sys/stat.h>
29#include <sys/time.h>
30#include <sys/uio.h>
31#include <linux/unistd.h>
32#include <assert.h>
33#include <unistd.h>
34#include <stdlib.h>
35#include <unistd.h>
36#include <string.h>
37#include <stdio.h>
38#include <fcntl.h>
39#include <stdint.h>
40#include "config.h"
41#if HAVE_PERF_EVENT_ATTR
42# include <linux/perf_event.h>
43#endif
44
45#include "test.h"
46#include "linux_syscall_numbers.h"
47#include "safe_macros.h"
48
49char *TCID = "perf_event_open01";
50
51#if HAVE_PERF_EVENT_ATTR
52static void setup(void);
53static void cleanup(void);
54
55static struct test_case_t {
56	uint32_t type;
57	const char *config_name;
58	unsigned long long config;
59} event_types[] = {
60	{ PERF_TYPE_HARDWARE, "PERF_COUNT_HW_INSTRUCTIONS",
61	  PERF_COUNT_HW_INSTRUCTIONS },
62	{ PERF_TYPE_HARDWARE, "PERF_COUNT_HW_CACHE_REFERENCES",
63	  PERF_COUNT_HW_CACHE_REFERENCES },
64	{ PERF_TYPE_HARDWARE, "PERF_COUNT_HW_CACHE_MISSES",
65	  PERF_COUNT_HW_CACHE_MISSES },
66	{ PERF_TYPE_HARDWARE, "PERF_COUNT_HW_BRANCH_INSTRUCTIONS",
67	  PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
68	{ PERF_TYPE_HARDWARE, "PERF_COUNT_HW_BRANCH_MISSES",
69	  PERF_COUNT_HW_BRANCH_MISSES },
70	{ PERF_TYPE_SOFTWARE, "PERF_COUNT_SW_CPU_CLOCK",
71	  PERF_COUNT_SW_CPU_CLOCK },
72	{ PERF_TYPE_SOFTWARE, "PERF_COUNT_SW_TASK_CLOCK",
73	  PERF_COUNT_SW_TASK_CLOCK },
74};
75
76int TST_TOTAL = ARRAY_SIZE(event_types);
77
78static void verify(struct test_case_t *tc);
79static struct perf_event_attr pe;
80
81int main(int ac, char **av)
82{
83	int i, lc;
84
85	tst_parse_opts(ac, av, NULL, NULL);
86
87	setup();
88
89	for (lc = 0; TEST_LOOPING(lc); lc++) {
90		tst_count = 0;
91
92		for (i = 0; i < TST_TOTAL; i++)
93			verify(&event_types[i]);
94	}
95
96	cleanup();
97	tst_exit();
98}
99
100static void setup(void)
101{
102	/*
103	 * According to perf_event_open's manpage, the official way of
104	 * knowing if perf_event_open() support is enabled is checking for
105	 * the existence of the file /proc/sys/kernel/perf_event_paranoid.
106	 */
107	if (access("/proc/sys/kernel/perf_event_paranoid", F_OK) == -1)
108		tst_brkm(TCONF, NULL, "Kernel doesn't have perf_event support");
109
110	tst_sig(NOFORK, DEF_HANDLER, cleanup);
111
112	TEST_PAUSE;
113
114	pe.size = sizeof(struct perf_event_attr);
115	pe.disabled = 1;
116	pe.exclude_kernel = 1;
117	pe.exclude_hv = 1;
118}
119
120
121static int perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
122		int cpu, int group_fd, unsigned long flags)
123{
124	int ret;
125
126	ret = ltp_syscall(__NR_perf_event_open, hw_event, pid, cpu,
127			  group_fd, flags);
128	return ret;
129}
130
131/* do_work() is copied form performance_counter02.c */
132#define LOOPS	100000000
133
134static void do_work(void)
135{
136	int i;
137
138	for (i = 0; i < LOOPS; ++i)
139		asm volatile ("" : : "g" (i));
140}
141
142static void verify(struct test_case_t *tc)
143{
144	unsigned long long count;
145	int fd, ret;
146
147	pe.type = tc->type;
148	pe.config = tc->config;
149
150	TEST(perf_event_open(&pe, 0, -1, -1, 0));
151	if (TEST_RETURN == -1) {
152		if (TEST_ERRNO == ENOENT || TEST_ERRNO == EOPNOTSUPP) {
153			tst_resm(TCONF | TTERRNO,
154			         "perf_event_open for %s not supported",
155			         tc->config_name);
156		} else {
157			tst_brkm(TFAIL | TTERRNO, cleanup,
158				 "perf_event_open %s failed unexpectedly",
159				 tc->config_name);
160		}
161		return;
162	}
163
164	fd = TEST_RETURN;
165
166	if (ioctl(fd, PERF_EVENT_IOC_RESET, 0) == -1) {
167		tst_brkm(TFAIL | TTERRNO, cleanup,
168			 "ioctl set PERF_EVENT_IOC_RESET failed");
169	}
170
171	if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) == -1) {
172		tst_brkm(TFAIL | TTERRNO, cleanup,
173			 "ioctl set PERF_EVENT_IOC_ENABLE failed");
174	}
175
176	do_work();
177
178	if (ioctl(fd, PERF_EVENT_IOC_DISABLE, 0) == -1) {
179		tst_brkm(TFAIL | TTERRNO, cleanup,
180			 "ioctl set PERF_EVENT_IOC_RESET failed");
181	}
182
183	ret = read(fd, &count, sizeof(count));
184	if (ret == sizeof(count)) {
185		tst_resm(TINFO, "read event counter succeeded, "
186			 "value: %llu", count);
187		tst_resm(TPASS, "test PERF_TYPE_HARDWARE: %s succeeded",
188			 tc->config_name);
189	} else {
190		tst_resm(TFAIL | TERRNO, "read event counter failed");
191	}
192
193	SAFE_CLOSE(cleanup, fd);
194
195}
196
197static void cleanup(void)
198{
199}
200
201#else
202
203int main(void)
204{
205	tst_brkm(TCONF, NULL, "This system doesn't have "
206		 "header file:<linux/perf_event.h> or "
207		 "no struct perf_event_attr defined");
208}
209#endif
210