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);