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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
// SPDX-License-Identifier: GPL-2.0
#include <stdlib.h>
#include <string.h>
#include <linux/perf_event.h>
#include "debug.h"
#include "env.h"
#include "evlist.h"
#include "evsel.h"
#include "tests.h"
/* A hybrid machine with 2 core PMUs, as read from a perf.data file. */
static struct hybrid_node two_core_pmus[] = {
{ .pmu_name = (char *)"cpu_atom", .cpus = (char *)"0-3", },
{ .pmu_name = (char *)"cpu_core", .cpus = (char *)"4-7", },
};
/* A machine with more than 2 kinds of core, like some Arm big.LITTLE. */
static struct hybrid_node three_core_pmus[] = {
{ .pmu_name = (char *)"cpu_atom", .cpus = (char *)"0-3", },
{ .pmu_name = (char *)"cpu_core", .cpus = (char *)"4-7", },
{ .pmu_name = (char *)"cpu_lowpower", .cpus = (char *)"8", },
};
static struct evsel *test_evsel__new(struct evlist *evlist, const char *name)
{
struct perf_event_attr attr = {
.type = PERF_TYPE_RAW,
.size = sizeof(attr),
.config = 0x3c,
};
struct evsel *evsel = evsel__new(&attr);
if (!evsel)
return NULL;
evsel->name = strdup(name);
if (!evsel->name) {
evsel__put(evsel);
return NULL;
}
evlist__add(evlist, evsel);
return evsel;
}
/*
* The assertions are in helpers taking an already allocated evlist, so that the
* caller can release the evlist however an assertion fails.
*/
static int check_merge_events(struct evlist *evlist, struct perf_env *env)
{
struct evsel *atom_cycles, *core_cycles, *atom_insns, *core_insns, *pos;
/* As if "perf record -e cycles,instructions" ran on a hybrid machine. */
atom_cycles = test_evsel__new(evlist, "cpu_atom/cycles/");
core_cycles = test_evsel__new(evlist, "cpu_core/cycles/");
atom_insns = test_evsel__new(evlist, "cpu_atom/instructions/");
core_insns = test_evsel__new(evlist, "cpu_core/instructions/");
TEST_ASSERT_VAL("failed to allocate evsels",
atom_cycles && core_cycles && atom_insns && core_insns);
TEST_ASSERT_VAL("events should be mergeable",
evlist__can_merge_hybrid(evlist, env));
/* Testing for merging must not alter the evlist. */
evlist__for_each_entry(evlist, pos) {
TEST_ASSERT_VAL("evlist modified by evlist__can_merge_hybrid",
!pos->first_wildcard_match);
}
evlist__merge_hybrid(evlist, env);
TEST_ASSERT_VAL("cycles not merged",
core_cycles->first_wildcard_match == atom_cycles);
/* All the events must be merged, not just the first pair found. */
TEST_ASSERT_VAL("instructions not merged",
core_insns->first_wildcard_match == atom_insns);
TEST_ASSERT_VAL("wrong cycles leader",
evsel__leader(core_cycles) == atom_cycles);
TEST_ASSERT_VAL("wrong instructions leader",
evsel__leader(core_insns) == atom_insns);
TEST_ASSERT_VAL("wrong cycles group size", atom_cycles->core.nr_members == 2);
TEST_ASSERT_VAL("wrong instructions group size", atom_insns->core.nr_members == 2);
return TEST_OK;
}
static int test__hybrid_merge_events(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
struct perf_env env = {
.nr_hybrid_nodes = ARRAY_SIZE(two_core_pmus),
.hybrid_nodes = two_core_pmus,
};
struct evlist *evlist = evlist__new();
int ret;
TEST_ASSERT_VAL("failed to allocate evlist", evlist);
ret = check_merge_events(evlist, &env);
evlist__put(evlist);
return ret;
}
static int check_merge_3_core_pmus(struct evlist *evlist, struct perf_env *env)
{
struct evsel *atom_cycles, *core_cycles, *lowpower_cycles;
atom_cycles = test_evsel__new(evlist, "cpu_atom/cycles/");
core_cycles = test_evsel__new(evlist, "cpu_core/cycles/");
lowpower_cycles = test_evsel__new(evlist, "cpu_lowpower/cycles/");
TEST_ASSERT_VAL("failed to allocate evsels",
atom_cycles && core_cycles && lowpower_cycles);
evlist__merge_hybrid(evlist, env);
TEST_ASSERT_VAL("second core PMU not merged",
core_cycles->first_wildcard_match == atom_cycles);
TEST_ASSERT_VAL("third core PMU not merged",
lowpower_cycles->first_wildcard_match == atom_cycles);
TEST_ASSERT_VAL("wrong group size", atom_cycles->core.nr_members == 3);
return TEST_OK;
}
static int test__hybrid_merge_3_core_pmus(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
struct perf_env env = {
.nr_hybrid_nodes = ARRAY_SIZE(three_core_pmus),
.hybrid_nodes = three_core_pmus,
};
struct evlist *evlist = evlist__new();
int ret;
TEST_ASSERT_VAL("failed to allocate evlist", evlist);
ret = check_merge_3_core_pmus(evlist, &env);
evlist__put(evlist);
return ret;
}
static int check_unmergeable_core_events(struct evlist *evlist, struct perf_env *env)
{
/* A single event has nothing to merge with. */
TEST_ASSERT_VAL("failed to allocate evsel",
test_evsel__new(evlist, "cpu_core/cycles/"));
TEST_ASSERT_VAL("a single event shouldn't merge",
!evlist__can_merge_hybrid(evlist, env));
/* Events of different names shouldn't merge. */
TEST_ASSERT_VAL("failed to allocate evsel",
test_evsel__new(evlist, "cpu_atom/instructions/"));
TEST_ASSERT_VAL("events with different names shouldn't merge",
!evlist__can_merge_hybrid(evlist, env));
return TEST_OK;
}
static int check_unmergeable_uncore_events(struct evlist *evlist, struct perf_env *env)
{
/* Matching events on non-core PMUs shouldn't merge. */
TEST_ASSERT_VAL("failed to allocate evsels",
test_evsel__new(evlist, "uncore_imc_0/clockticks/") &&
test_evsel__new(evlist, "uncore_imc_1/clockticks/"));
TEST_ASSERT_VAL("uncore events shouldn't merge",
!evlist__can_merge_hybrid(evlist, env));
return TEST_OK;
}
static int test__hybrid_merge_unmergeable(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
struct perf_env env = {
.nr_hybrid_nodes = ARRAY_SIZE(two_core_pmus),
.hybrid_nodes = two_core_pmus,
};
struct evlist *evlist = evlist__new();
int ret;
TEST_ASSERT_VAL("failed to allocate evlist", evlist);
ret = check_unmergeable_core_events(evlist, &env);
evlist__put(evlist);
if (ret != TEST_OK)
return ret;
evlist = evlist__new();
TEST_ASSERT_VAL("failed to allocate evlist", evlist);
ret = check_unmergeable_uncore_events(evlist, &env);
evlist__put(evlist);
return ret;
}
static struct test_case tests__hybrid_merge[] = {
TEST_CASE("Merge events of 2 core PMUs", hybrid_merge_events),
TEST_CASE("Merge events of 3 core PMUs", hybrid_merge_3_core_pmus),
TEST_CASE("Events that shouldn't merge", hybrid_merge_unmergeable),
{ .name = NULL, }
};
struct test_suite suite__hybrid_merge = {
.desc = "Hybrid event merging",
.test_cases = tests__hybrid_merge,
};
|