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

dlfilter-test.c (9809B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Test dlfilter C API. A perf.data file is synthesized and then processed
      4 * by perf script with a dlfilter named dlfilter-test-api-v0.so. Also a C file
      5 * is compiled to provide a dso to match the synthesized perf.data file.
      6 */
      7
      8#include <linux/compiler.h>
      9#include <linux/kernel.h>
     10#include <linux/string.h>
     11#include <linux/perf_event.h>
     12#include <internal/lib.h>
     13#include <subcmd/exec-cmd.h>
     14#include <sys/types.h>
     15#include <sys/stat.h>
     16#include <fcntl.h>
     17#include <stdlib.h>
     18#include <unistd.h>
     19#include <inttypes.h>
     20#include <libgen.h>
     21#include <string.h>
     22#include <errno.h>
     23#include "debug.h"
     24#include "tool.h"
     25#include "event.h"
     26#include "header.h"
     27#include "machine.h"
     28#include "dso.h"
     29#include "map.h"
     30#include "symbol.h"
     31#include "synthetic-events.h"
     32#include "util.h"
     33#include "archinsn.h"
     34#include "dlfilter.h"
     35#include "tests.h"
     36
     37#define MAP_START 0x400000
     38
     39struct test_data {
     40	struct perf_tool tool;
     41	struct machine *machine;
     42	int fd;
     43	u64 foo;
     44	u64 bar;
     45	u64 ip;
     46	u64 addr;
     47	char perf[PATH_MAX];
     48	char perf_data_file_name[PATH_MAX];
     49	char c_file_name[PATH_MAX];
     50	char prog_file_name[PATH_MAX];
     51	char dlfilters[PATH_MAX];
     52};
     53
     54static int test_result(const char *msg, int ret)
     55{
     56	pr_debug("%s\n", msg);
     57	return ret;
     58}
     59
     60static int process(struct perf_tool *tool, union perf_event *event,
     61		   struct perf_sample *sample __maybe_unused,
     62		   struct machine *machine __maybe_unused)
     63{
     64	struct test_data *td = container_of(tool, struct test_data, tool);
     65	int fd = td->fd;
     66
     67	if (writen(fd, event, event->header.size) != event->header.size)
     68		return -1;
     69
     70	return 0;
     71}
     72
     73#define MAXCMD 4096
     74#define REDIRECT_TO_DEV_NULL " >/dev/null 2>&1"
     75
     76static __printf(1, 2) int system_cmd(const char *fmt, ...)
     77{
     78	char cmd[MAXCMD + sizeof(REDIRECT_TO_DEV_NULL)];
     79	int ret;
     80
     81	va_list args;
     82
     83	va_start(args, fmt);
     84	ret = vsnprintf(cmd, MAXCMD, fmt, args);
     85	va_end(args);
     86
     87	if (ret <= 0 || ret >= MAXCMD)
     88		return -1;
     89
     90	if (!verbose)
     91		strcat(cmd, REDIRECT_TO_DEV_NULL);
     92
     93	pr_debug("Command: %s\n", cmd);
     94	ret = system(cmd);
     95	if (ret)
     96		pr_debug("Failed with return value %d\n", ret);
     97
     98	return ret;
     99}
    100
    101static bool have_gcc(void)
    102{
    103	pr_debug("Checking for gcc\n");
    104	return !system_cmd("gcc --version");
    105}
    106
    107static int write_attr(struct test_data *td, u64 sample_type, u64 *id)
    108{
    109	struct perf_event_attr attr = {
    110		.size = sizeof(attr),
    111		.type = PERF_TYPE_HARDWARE,
    112		.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
    113		.sample_type = sample_type,
    114		.sample_period = 1,
    115	};
    116
    117	return perf_event__synthesize_attr(&td->tool, &attr, 1, id, process);
    118}
    119
    120static int write_comm(int fd, pid_t pid, pid_t tid, const char *comm_str)
    121{
    122	struct perf_record_comm comm;
    123	ssize_t sz = sizeof(comm);
    124
    125	comm.header.type = PERF_RECORD_COMM;
    126	comm.header.misc = PERF_RECORD_MISC_USER;
    127	comm.header.size = sz;
    128
    129	comm.pid = pid;
    130	comm.tid = tid;
    131	strncpy(comm.comm, comm_str, 16);
    132
    133	if (writen(fd, &comm, sz) != sz) {
    134		pr_debug("%s failed\n", __func__);
    135		return -1;
    136	}
    137
    138	return 0;
    139}
    140
    141static int write_mmap(int fd, pid_t pid, pid_t tid, u64 start, u64 len, u64 pgoff,
    142		      const char *filename)
    143{
    144	char buf[PERF_SAMPLE_MAX_SIZE];
    145	struct perf_record_mmap *mmap = (struct perf_record_mmap *)buf;
    146	size_t fsz = roundup(strlen(filename) + 1, 8);
    147	ssize_t sz = sizeof(*mmap) - sizeof(mmap->filename) + fsz;
    148
    149	mmap->header.type = PERF_RECORD_MMAP;
    150	mmap->header.misc = PERF_RECORD_MISC_USER;
    151	mmap->header.size = sz;
    152
    153	mmap->pid   = pid;
    154	mmap->tid   = tid;
    155	mmap->start = start;
    156	mmap->len   = len;
    157	mmap->pgoff = pgoff;
    158	strncpy(mmap->filename, filename, sizeof(mmap->filename));
    159
    160	if (writen(fd, mmap, sz) != sz) {
    161		pr_debug("%s failed\n", __func__);
    162		return -1;
    163	}
    164
    165	return 0;
    166}
    167
    168static int write_sample(struct test_data *td, u64 sample_type, u64 id, pid_t pid, pid_t tid)
    169{
    170	char buf[PERF_SAMPLE_MAX_SIZE];
    171	union perf_event *event = (union perf_event *)buf;
    172	struct perf_sample sample = {
    173		.ip		= td->ip,
    174		.addr		= td->addr,
    175		.id		= id,
    176		.time		= 1234567890,
    177		.cpu		= 31,
    178		.pid		= pid,
    179		.tid		= tid,
    180		.period		= 543212345,
    181		.stream_id	= 101,
    182	};
    183	int err;
    184
    185	event->header.type = PERF_RECORD_SAMPLE;
    186	event->header.misc = PERF_RECORD_MISC_USER;
    187	event->header.size = perf_event__sample_event_size(&sample, sample_type, 0);
    188	err = perf_event__synthesize_sample(event, sample_type, 0, &sample);
    189	if (err)
    190		return test_result("perf_event__synthesize_sample() failed", TEST_FAIL);
    191
    192	err = process(&td->tool, event, &sample, td->machine);
    193	if (err)
    194		return test_result("Failed to write sample", TEST_FAIL);
    195
    196	return TEST_OK;
    197}
    198
    199static void close_fd(int fd)
    200{
    201	if (fd >= 0)
    202		close(fd);
    203}
    204
    205static const char *prog = "int bar(){};int foo(){bar();};int main(){foo();return 0;}";
    206
    207static int write_prog(char *file_name)
    208{
    209	int fd = creat(file_name, 0644);
    210	ssize_t n = strlen(prog);
    211	bool err = fd < 0 || writen(fd, prog, n) != n;
    212
    213	close_fd(fd);
    214	return err ? -1 : 0;
    215}
    216
    217static int get_dlfilters_path(char *buf, size_t sz)
    218{
    219	char perf[PATH_MAX];
    220	char path[PATH_MAX];
    221	char *perf_path;
    222	char *exec_path;
    223
    224	perf_exe(perf, sizeof(perf));
    225	perf_path = dirname(perf);
    226	snprintf(path, sizeof(path), "%s/dlfilters/dlfilter-test-api-v0.so", perf_path);
    227	if (access(path, R_OK)) {
    228		exec_path = get_argv_exec_path();
    229		if (!exec_path)
    230			return -1;
    231		snprintf(path, sizeof(path), "%s/dlfilters/dlfilter-test-api-v0.so", exec_path);
    232		free(exec_path);
    233		if (access(path, R_OK))
    234			return -1;
    235	}
    236	strlcpy(buf, dirname(path), sz);
    237	return 0;
    238}
    239
    240static int check_filter_desc(struct test_data *td)
    241{
    242	char *long_desc = NULL;
    243	char *desc = NULL;
    244	int ret;
    245
    246	if (get_filter_desc(td->dlfilters, "dlfilter-test-api-v0.so", &desc, &long_desc) &&
    247	    long_desc && !strcmp(long_desc, "Filter used by the 'dlfilter C API' perf test") &&
    248	    desc && !strcmp(desc, "dlfilter to test v0 C API"))
    249		ret = 0;
    250	else
    251		ret = -1;
    252
    253	free(desc);
    254	free(long_desc);
    255	return ret;
    256}
    257
    258static int get_ip_addr(struct test_data *td)
    259{
    260	struct map *map;
    261	struct symbol *sym;
    262
    263	map = dso__new_map(td->prog_file_name);
    264	if (!map)
    265		return -1;
    266
    267	sym = map__find_symbol_by_name(map, "foo");
    268	if (sym)
    269		td->foo = sym->start;
    270
    271	sym = map__find_symbol_by_name(map, "bar");
    272	if (sym)
    273		td->bar = sym->start;
    274
    275	map__put(map);
    276
    277	td->ip = MAP_START + td->foo;
    278	td->addr = MAP_START + td->bar;
    279
    280	return td->foo && td->bar ? 0 : -1;
    281}
    282
    283static int do_run_perf_script(struct test_data *td, int do_early)
    284{
    285	return system_cmd("%s script -i %s "
    286			  "--dlfilter %s/dlfilter-test-api-v0.so "
    287			  "--dlarg first "
    288			  "--dlarg %d "
    289			  "--dlarg %" PRIu64 " "
    290			  "--dlarg %" PRIu64 " "
    291			  "--dlarg %d "
    292			  "--dlarg last",
    293			  td->perf, td->perf_data_file_name, td->dlfilters,
    294			  verbose, td->ip, td->addr, do_early);
    295}
    296
    297static int run_perf_script(struct test_data *td)
    298{
    299	int do_early;
    300	int err;
    301
    302	for (do_early = 0; do_early < 3; do_early++) {
    303		err = do_run_perf_script(td, do_early);
    304		if (err)
    305			return err;
    306	}
    307	return 0;
    308}
    309
    310#define TEST_SAMPLE_TYPE (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \
    311			  PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_TIME | \
    312			  PERF_SAMPLE_ADDR | PERF_SAMPLE_CPU | \
    313			  PERF_SAMPLE_PERIOD | PERF_SAMPLE_STREAM_ID)
    314
    315static int test__dlfilter_test(struct test_data *td)
    316{
    317	u64 sample_type = TEST_SAMPLE_TYPE;
    318	pid_t pid = 12345;
    319	pid_t tid = 12346;
    320	u64 id = 99;
    321	int err;
    322
    323	if (get_dlfilters_path(td->dlfilters, PATH_MAX))
    324		return test_result("dlfilters not found", TEST_SKIP);
    325
    326	if (check_filter_desc(td))
    327		return test_result("Failed to get expected filter description", TEST_FAIL);
    328
    329	if (!have_gcc())
    330		return test_result("gcc not found", TEST_SKIP);
    331
    332	pr_debug("dlfilters path: %s\n", td->dlfilters);
    333
    334	if (write_prog(td->c_file_name))
    335		return test_result("Failed to write test C file", TEST_FAIL);
    336
    337	if (verbose > 1)
    338		system_cmd("cat %s ; echo", td->c_file_name);
    339
    340	if (system_cmd("gcc -g -o %s %s", td->prog_file_name, td->c_file_name))
    341		return TEST_FAIL;
    342
    343	if (verbose > 2)
    344		system_cmd("objdump -x -dS %s", td->prog_file_name);
    345
    346	if (get_ip_addr(td))
    347		return test_result("Failed to find program symbols", TEST_FAIL);
    348
    349	pr_debug("Creating new host machine structure\n");
    350	td->machine = machine__new_host();
    351	td->machine->env = &perf_env;
    352
    353	td->fd = creat(td->perf_data_file_name, 0644);
    354	if (td->fd < 0)
    355		return test_result("Failed to create test perf.data file", TEST_FAIL);
    356
    357	err = perf_header__write_pipe(td->fd);
    358	if (err < 0)
    359		return test_result("perf_header__write_pipe() failed", TEST_FAIL);
    360
    361	err = write_attr(td, sample_type, &id);
    362	if (err)
    363		return test_result("perf_event__synthesize_attr() failed", TEST_FAIL);
    364
    365	if (write_comm(td->fd, pid, tid, "test-prog"))
    366		return TEST_FAIL;
    367
    368	if (write_mmap(td->fd, pid, tid, MAP_START, 0x10000, 0, td->prog_file_name))
    369		return TEST_FAIL;
    370
    371	if (write_sample(td, sample_type, id, pid, tid) != TEST_OK)
    372		return TEST_FAIL;
    373
    374	if (verbose > 1)
    375		system_cmd("%s script -i %s -D", td->perf, td->perf_data_file_name);
    376
    377	err = run_perf_script(td);
    378	if (err)
    379		return TEST_FAIL;
    380
    381	return TEST_OK;
    382}
    383
    384static void unlink_path(const char *path)
    385{
    386	if (*path)
    387		unlink(path);
    388}
    389
    390static void test_data__free(struct test_data *td)
    391{
    392	machine__delete(td->machine);
    393	close_fd(td->fd);
    394	if (verbose <= 2) {
    395		unlink_path(td->c_file_name);
    396		unlink_path(td->prog_file_name);
    397		unlink_path(td->perf_data_file_name);
    398	}
    399}
    400
    401static int test__dlfilter(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
    402{
    403	struct test_data td = {.fd = -1};
    404	int pid = getpid();
    405	int err;
    406
    407	perf_exe(td.perf, sizeof(td.perf));
    408
    409	snprintf(td.perf_data_file_name, PATH_MAX, "/tmp/dlfilter-test-%u-perf-data", pid);
    410	snprintf(td.c_file_name, PATH_MAX, "/tmp/dlfilter-test-%u-prog.c", pid);
    411	snprintf(td.prog_file_name, PATH_MAX, "/tmp/dlfilter-test-%u-prog", pid);
    412
    413	err = test__dlfilter_test(&td);
    414	test_data__free(&td);
    415	return err;
    416}
    417
    418DEFINE_SUITE("dlfilter C API", dlfilter);