summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/powerpc/pmu/event.c
blob: 0c1c1bdba08112c8ada1b55e484c57575dbbdfa9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2013, Michael Ellerman, IBM Corp.
 */

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/ioctl.h>

#include "event.h"


int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu,
		int group_fd, unsigned long flags)
{
	return syscall(__NR_perf_event_open, attr, pid, cpu,
			   group_fd, flags);
}

static void  __event_init_opts(struct event *e, u64 config,
			       int type, char *name, bool sampling)
{
	memset(e, 0, sizeof(*e));

	e->name = name;

	e->attr.type = type;
	e->attr.config = config;
	e->attr.size = sizeof(e->attr);
	/* This has to match the structure layout in the header */
	e->attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | \
				  PERF_FORMAT_TOTAL_TIME_RUNNING;
	if (sampling) {
		e->attr.sample_period = 1000;
		e->attr.sample_type = PERF_SAMPLE_REGS_INTR;
		e->attr.disabled = 1;
	}
}

void event_init_opts(struct event *e, u64 config, int type, char *name)
{
	__event_init_opts(e, config, type, name, false);
}

void event_init_named(struct event *e, u64 config, char *name)
{
	event_init_opts(e, config, PERF_TYPE_RAW, name);
}

void event_init(struct event *e, u64 config)
{
	event_init_opts(e, config, PERF_TYPE_RAW, "event");
}

void event_init_sampling(struct event *e, u64 config)
{
	__event_init_opts(e, config, PERF_TYPE_RAW, "event", true);
}

#define PERF_CURRENT_PID	0
#define PERF_NO_PID		-1
#define PERF_NO_CPU		-1
#define PERF_NO_GROUP		-1

int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd)
{
	e->fd = perf_event_open(&e->attr, pid, cpu, group_fd, 0);
	if (e->fd == -1) {
		perror("perf_event_open");
		return -1;
	}

	return 0;
}

int event_open_with_group(struct event *e, int group_fd)
{
	return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, group_fd);
}

int event_open_with_pid(struct event *e, pid_t pid)
{
	return event_open_with_options(e, pid, PERF_NO_CPU, PERF_NO_GROUP);
}

int event_open_with_cpu(struct event *e, int cpu)
{
	return event_open_with_options(e, PERF_NO_PID, cpu, PERF_NO_GROUP);
}

int event_open(struct event *e)
{
	return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, PERF_NO_GROUP);
}

void event_close(struct event *e)
{
	close(e->fd);
}

int event_enable(struct event *e)
{
	return ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
}

int event_disable(struct event *e)
{
	return ioctl(e->fd, PERF_EVENT_IOC_DISABLE);
}

int event_reset(struct event *e)
{
	return ioctl(e->fd, PERF_EVENT_IOC_RESET);
}

int event_read(struct event *e)
{
	int rc;

	rc = read(e->fd, &e->result, sizeof(e->result));
	if (rc != sizeof(e->result)) {
		fprintf(stderr, "read error on event %p!\n", e);
		return -1;
	}

	return 0;
}

void event_report_justified(struct event *e, int name_width, int result_width)
{
	printf("%*s: result %*llu ", name_width, e->name, result_width,
	       e->result.value);

	if (e->result.running == e->result.enabled)
		printf("running/enabled %llu\n", e->result.running);
	else
		printf("running %llu enabled %llu\n", e->result.running,
			e->result.enabled);
}

void event_report(struct event *e)
{
	event_report_justified(e, 0, 0);
}