1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <string.h> |
3 | #include <stdlib.h> |
4 | #include <stdio.h> |
5 | #include <perf/cpumap.h> |
6 | #include "cpumap.h" |
7 | #include "tests.h" |
8 | #include "session.h" |
9 | #include "evlist.h" |
10 | #include "debug.h" |
11 | #include "pmus.h" |
12 | #include <linux/err.h> |
13 | |
14 | #define TEMPL "/tmp/perf-test-XXXXXX" |
15 | #define DATA_SIZE 10 |
16 | |
17 | static int get_temp(char *path) |
18 | { |
19 | int fd; |
20 | |
21 | strcpy(path, TEMPL); |
22 | |
23 | fd = mkstemp(path); |
24 | if (fd < 0) { |
25 | perror("mkstemp failed" ); |
26 | return -1; |
27 | } |
28 | |
29 | close(fd); |
30 | return 0; |
31 | } |
32 | |
33 | static int (char *path) |
34 | { |
35 | struct perf_session *session; |
36 | struct perf_data data = { |
37 | .path = path, |
38 | .mode = PERF_DATA_MODE_WRITE, |
39 | }; |
40 | |
41 | session = perf_session__new(&data, NULL); |
42 | TEST_ASSERT_VAL("can't get session" , !IS_ERR(session)); |
43 | |
44 | session->evlist = evlist__new_default(); |
45 | TEST_ASSERT_VAL("can't get evlist" , session->evlist); |
46 | |
47 | perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY); |
48 | perf_header__set_feat(&session->header, HEADER_NRCPUS); |
49 | perf_header__set_feat(&session->header, HEADER_ARCH); |
50 | |
51 | session->header.data_size += DATA_SIZE; |
52 | |
53 | TEST_ASSERT_VAL("failed to write header" , |
54 | !perf_session__write_header(session, session->evlist, data.file.fd, true)); |
55 | |
56 | evlist__delete(session->evlist); |
57 | perf_session__delete(session); |
58 | |
59 | return 0; |
60 | } |
61 | |
62 | static int check_cpu_topology(char *path, struct perf_cpu_map *map) |
63 | { |
64 | struct perf_session *session; |
65 | struct perf_data data = { |
66 | .path = path, |
67 | .mode = PERF_DATA_MODE_READ, |
68 | }; |
69 | int i; |
70 | struct aggr_cpu_id id; |
71 | |
72 | session = perf_session__new(&data, NULL); |
73 | TEST_ASSERT_VAL("can't get session" , !IS_ERR(session)); |
74 | cpu__setup_cpunode_map(); |
75 | |
76 | /* On platforms with large numbers of CPUs process_cpu_topology() |
77 | * might issue an error while reading the perf.data file section |
78 | * HEADER_CPU_TOPOLOGY and the cpu_topology_map pointed to by member |
79 | * cpu is a NULL pointer. |
80 | * Example: On s390 |
81 | * CPU 0 is on core_id 0 and physical_package_id 6 |
82 | * CPU 1 is on core_id 1 and physical_package_id 3 |
83 | * |
84 | * Core_id and physical_package_id are platform and architecture |
85 | * dependent and might have higher numbers than the CPU id. |
86 | * This actually depends on the configuration. |
87 | * |
88 | * In this case process_cpu_topology() prints error message: |
89 | * "socket_id number is too big. You may need to upgrade the |
90 | * perf tool." |
91 | * |
92 | * This is the reason why this test might be skipped. aarch64 and |
93 | * s390 always write this part of the header, even when the above |
94 | * condition is true (see do_core_id_test in header.c). So always |
95 | * run this test on those platforms. |
96 | */ |
97 | if (!session->header.env.cpu |
98 | && strncmp(session->header.env.arch, "s390" , 4) |
99 | && strncmp(session->header.env.arch, "aarch64" , 7)) |
100 | return TEST_SKIP; |
101 | |
102 | /* |
103 | * In powerpc pSeries platform, not all the topology information |
104 | * are exposed via sysfs. Due to restriction, detail like |
105 | * physical_package_id will be set to -1. Hence skip this |
106 | * test if physical_package_id returns -1 for cpu from perf_cpu_map. |
107 | */ |
108 | if (!strncmp(session->header.env.arch, "ppc64le" , 7)) { |
109 | if (cpu__get_socket_id(perf_cpu_map__cpu(map, 0)) == -1) |
110 | return TEST_SKIP; |
111 | } |
112 | |
113 | TEST_ASSERT_VAL("Session header CPU map not set" , session->header.env.cpu); |
114 | |
115 | for (i = 0; i < session->header.env.nr_cpus_avail; i++) { |
116 | struct perf_cpu cpu = { .cpu = i }; |
117 | |
118 | if (!perf_cpu_map__has(map, cpu)) |
119 | continue; |
120 | pr_debug("CPU %d, core %d, socket %d\n" , i, |
121 | session->header.env.cpu[i].core_id, |
122 | session->header.env.cpu[i].socket_id); |
123 | } |
124 | |
125 | // Test that CPU ID contains socket, die, core and CPU |
126 | for (i = 0; i < perf_cpu_map__nr(map); i++) { |
127 | id = aggr_cpu_id__cpu(perf_cpu_map__cpu(map, i), NULL); |
128 | TEST_ASSERT_VAL("Cpu map - CPU ID doesn't match" , |
129 | perf_cpu_map__cpu(map, i).cpu == id.cpu.cpu); |
130 | |
131 | TEST_ASSERT_VAL("Cpu map - Core ID doesn't match" , |
132 | session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].core_id == id.core); |
133 | TEST_ASSERT_VAL("Cpu map - Socket ID doesn't match" , |
134 | session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].socket_id == |
135 | id.socket); |
136 | |
137 | TEST_ASSERT_VAL("Cpu map - Die ID doesn't match" , |
138 | session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].die_id == id.die); |
139 | TEST_ASSERT_VAL("Cpu map - Node ID is set" , id.node == -1); |
140 | TEST_ASSERT_VAL("Cpu map - Thread IDX is set" , id.thread_idx == -1); |
141 | } |
142 | |
143 | // Test that core ID contains socket, die and core |
144 | for (i = 0; i < perf_cpu_map__nr(map); i++) { |
145 | id = aggr_cpu_id__core(perf_cpu_map__cpu(map, i), NULL); |
146 | TEST_ASSERT_VAL("Core map - Core ID doesn't match" , |
147 | session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].core_id == id.core); |
148 | |
149 | TEST_ASSERT_VAL("Core map - Socket ID doesn't match" , |
150 | session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].socket_id == |
151 | id.socket); |
152 | |
153 | TEST_ASSERT_VAL("Core map - Die ID doesn't match" , |
154 | session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].die_id == id.die); |
155 | TEST_ASSERT_VAL("Core map - Node ID is set" , id.node == -1); |
156 | TEST_ASSERT_VAL("Core map - Thread IDX is set" , id.thread_idx == -1); |
157 | } |
158 | |
159 | // Test that die ID contains socket and die |
160 | for (i = 0; i < perf_cpu_map__nr(map); i++) { |
161 | id = aggr_cpu_id__die(perf_cpu_map__cpu(map, i), NULL); |
162 | TEST_ASSERT_VAL("Die map - Socket ID doesn't match" , |
163 | session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].socket_id == |
164 | id.socket); |
165 | |
166 | TEST_ASSERT_VAL("Die map - Die ID doesn't match" , |
167 | session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].die_id == id.die); |
168 | |
169 | TEST_ASSERT_VAL("Die map - Node ID is set" , id.node == -1); |
170 | TEST_ASSERT_VAL("Die map - Core is set" , id.core == -1); |
171 | TEST_ASSERT_VAL("Die map - CPU is set" , id.cpu.cpu == -1); |
172 | TEST_ASSERT_VAL("Die map - Thread IDX is set" , id.thread_idx == -1); |
173 | } |
174 | |
175 | // Test that socket ID contains only socket |
176 | for (i = 0; i < perf_cpu_map__nr(map); i++) { |
177 | id = aggr_cpu_id__socket(perf_cpu_map__cpu(map, i), NULL); |
178 | TEST_ASSERT_VAL("Socket map - Socket ID doesn't match" , |
179 | session->header.env.cpu[perf_cpu_map__cpu(map, i).cpu].socket_id == |
180 | id.socket); |
181 | |
182 | TEST_ASSERT_VAL("Socket map - Node ID is set" , id.node == -1); |
183 | TEST_ASSERT_VAL("Socket map - Die ID is set" , id.die == -1); |
184 | TEST_ASSERT_VAL("Socket map - Core is set" , id.core == -1); |
185 | TEST_ASSERT_VAL("Socket map - CPU is set" , id.cpu.cpu == -1); |
186 | TEST_ASSERT_VAL("Socket map - Thread IDX is set" , id.thread_idx == -1); |
187 | } |
188 | |
189 | // Test that node ID contains only node |
190 | for (i = 0; i < perf_cpu_map__nr(map); i++) { |
191 | id = aggr_cpu_id__node(perf_cpu_map__cpu(map, i), NULL); |
192 | TEST_ASSERT_VAL("Node map - Node ID doesn't match" , |
193 | cpu__get_node(perf_cpu_map__cpu(map, i)) == id.node); |
194 | TEST_ASSERT_VAL("Node map - Socket is set" , id.socket == -1); |
195 | TEST_ASSERT_VAL("Node map - Die ID is set" , id.die == -1); |
196 | TEST_ASSERT_VAL("Node map - Core is set" , id.core == -1); |
197 | TEST_ASSERT_VAL("Node map - CPU is set" , id.cpu.cpu == -1); |
198 | TEST_ASSERT_VAL("Node map - Thread IDX is set" , id.thread_idx == -1); |
199 | } |
200 | perf_session__delete(session); |
201 | |
202 | return 0; |
203 | } |
204 | |
205 | static int test__session_topology(struct test_suite *test __maybe_unused, int subtest __maybe_unused) |
206 | { |
207 | char path[PATH_MAX]; |
208 | struct perf_cpu_map *map; |
209 | int ret = TEST_FAIL; |
210 | |
211 | TEST_ASSERT_VAL("can't get templ file" , !get_temp(path)); |
212 | |
213 | pr_debug("templ file: %s\n" , path); |
214 | |
215 | if (session_write_header(path: path)) |
216 | goto free_path; |
217 | |
218 | map = perf_cpu_map__new_online_cpus(); |
219 | if (map == NULL) { |
220 | pr_debug("failed to get system cpumap\n" ); |
221 | goto free_path; |
222 | } |
223 | |
224 | ret = check_cpu_topology(path: path, map); |
225 | perf_cpu_map__put(map); |
226 | |
227 | free_path: |
228 | unlink(path); |
229 | return ret; |
230 | } |
231 | |
232 | DEFINE_SUITE("Session topology" , session_topology); |
233 | |