cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

topology.c (7781B)


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