Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Test dlfilter C API. A perf.data file is synthesized and then processed
0004  * by perf script with a dlfilter named dlfilter-test-api-v0.so. Also a C file
0005  * is compiled to provide a dso to match the synthesized perf.data file.
0006  */
0007 
0008 #include <linux/compiler.h>
0009 #include <linux/kernel.h>
0010 #include <linux/string.h>
0011 #include <linux/perf_event.h>
0012 #include <internal/lib.h>
0013 #include <subcmd/exec-cmd.h>
0014 #include <sys/types.h>
0015 #include <sys/stat.h>
0016 #include <fcntl.h>
0017 #include <stdlib.h>
0018 #include <unistd.h>
0019 #include <inttypes.h>
0020 #include <libgen.h>
0021 #include <string.h>
0022 #include <errno.h>
0023 #include "debug.h"
0024 #include "tool.h"
0025 #include "event.h"
0026 #include "header.h"
0027 #include "machine.h"
0028 #include "dso.h"
0029 #include "map.h"
0030 #include "symbol.h"
0031 #include "synthetic-events.h"
0032 #include "util.h"
0033 #include "archinsn.h"
0034 #include "dlfilter.h"
0035 #include "tests.h"
0036 
0037 #define MAP_START 0x400000
0038 
0039 struct test_data {
0040     struct perf_tool tool;
0041     struct machine *machine;
0042     int fd;
0043     u64 foo;
0044     u64 bar;
0045     u64 ip;
0046     u64 addr;
0047     char perf[PATH_MAX];
0048     char perf_data_file_name[PATH_MAX];
0049     char c_file_name[PATH_MAX];
0050     char prog_file_name[PATH_MAX];
0051     char dlfilters[PATH_MAX];
0052 };
0053 
0054 static int test_result(const char *msg, int ret)
0055 {
0056     pr_debug("%s\n", msg);
0057     return ret;
0058 }
0059 
0060 static int process(struct perf_tool *tool, union perf_event *event,
0061            struct perf_sample *sample __maybe_unused,
0062            struct machine *machine __maybe_unused)
0063 {
0064     struct test_data *td = container_of(tool, struct test_data, tool);
0065     int fd = td->fd;
0066 
0067     if (writen(fd, event, event->header.size) != event->header.size)
0068         return -1;
0069 
0070     return 0;
0071 }
0072 
0073 #define MAXCMD 4096
0074 #define REDIRECT_TO_DEV_NULL " >/dev/null 2>&1"
0075 
0076 static __printf(1, 2) int system_cmd(const char *fmt, ...)
0077 {
0078     char cmd[MAXCMD + sizeof(REDIRECT_TO_DEV_NULL)];
0079     int ret;
0080 
0081     va_list args;
0082 
0083     va_start(args, fmt);
0084     ret = vsnprintf(cmd, MAXCMD, fmt, args);
0085     va_end(args);
0086 
0087     if (ret <= 0 || ret >= MAXCMD)
0088         return -1;
0089 
0090     if (!verbose)
0091         strcat(cmd, REDIRECT_TO_DEV_NULL);
0092 
0093     pr_debug("Command: %s\n", cmd);
0094     ret = system(cmd);
0095     if (ret)
0096         pr_debug("Failed with return value %d\n", ret);
0097 
0098     return ret;
0099 }
0100 
0101 static bool have_gcc(void)
0102 {
0103     pr_debug("Checking for gcc\n");
0104     return !system_cmd("gcc --version");
0105 }
0106 
0107 static int write_attr(struct test_data *td, u64 sample_type, u64 *id)
0108 {
0109     struct perf_event_attr attr = {
0110         .size = sizeof(attr),
0111         .type = PERF_TYPE_HARDWARE,
0112         .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
0113         .sample_type = sample_type,
0114         .sample_period = 1,
0115     };
0116 
0117     return perf_event__synthesize_attr(&td->tool, &attr, 1, id, process);
0118 }
0119 
0120 static int write_comm(int fd, pid_t pid, pid_t tid, const char *comm_str)
0121 {
0122     struct perf_record_comm comm;
0123     ssize_t sz = sizeof(comm);
0124 
0125     comm.header.type = PERF_RECORD_COMM;
0126     comm.header.misc = PERF_RECORD_MISC_USER;
0127     comm.header.size = sz;
0128 
0129     comm.pid = pid;
0130     comm.tid = tid;
0131     strncpy(comm.comm, comm_str, 16);
0132 
0133     if (writen(fd, &comm, sz) != sz) {
0134         pr_debug("%s failed\n", __func__);
0135         return -1;
0136     }
0137 
0138     return 0;
0139 }
0140 
0141 static int write_mmap(int fd, pid_t pid, pid_t tid, u64 start, u64 len, u64 pgoff,
0142               const char *filename)
0143 {
0144     char buf[PERF_SAMPLE_MAX_SIZE];
0145     struct perf_record_mmap *mmap = (struct perf_record_mmap *)buf;
0146     size_t fsz = roundup(strlen(filename) + 1, 8);
0147     ssize_t sz = sizeof(*mmap) - sizeof(mmap->filename) + fsz;
0148 
0149     mmap->header.type = PERF_RECORD_MMAP;
0150     mmap->header.misc = PERF_RECORD_MISC_USER;
0151     mmap->header.size = sz;
0152 
0153     mmap->pid   = pid;
0154     mmap->tid   = tid;
0155     mmap->start = start;
0156     mmap->len   = len;
0157     mmap->pgoff = pgoff;
0158     strncpy(mmap->filename, filename, sizeof(mmap->filename));
0159 
0160     if (writen(fd, mmap, sz) != sz) {
0161         pr_debug("%s failed\n", __func__);
0162         return -1;
0163     }
0164 
0165     return 0;
0166 }
0167 
0168 static int write_sample(struct test_data *td, u64 sample_type, u64 id, pid_t pid, pid_t tid)
0169 {
0170     char buf[PERF_SAMPLE_MAX_SIZE];
0171     union perf_event *event = (union perf_event *)buf;
0172     struct perf_sample sample = {
0173         .ip     = td->ip,
0174         .addr       = td->addr,
0175         .id     = id,
0176         .time       = 1234567890,
0177         .cpu        = 31,
0178         .pid        = pid,
0179         .tid        = tid,
0180         .period     = 543212345,
0181         .stream_id  = 101,
0182     };
0183     int err;
0184 
0185     event->header.type = PERF_RECORD_SAMPLE;
0186     event->header.misc = PERF_RECORD_MISC_USER;
0187     event->header.size = perf_event__sample_event_size(&sample, sample_type, 0);
0188     err = perf_event__synthesize_sample(event, sample_type, 0, &sample);
0189     if (err)
0190         return test_result("perf_event__synthesize_sample() failed", TEST_FAIL);
0191 
0192     err = process(&td->tool, event, &sample, td->machine);
0193     if (err)
0194         return test_result("Failed to write sample", TEST_FAIL);
0195 
0196     return TEST_OK;
0197 }
0198 
0199 static void close_fd(int fd)
0200 {
0201     if (fd >= 0)
0202         close(fd);
0203 }
0204 
0205 static const char *prog = "int bar(){};int foo(){bar();};int main(){foo();return 0;}";
0206 
0207 static int write_prog(char *file_name)
0208 {
0209     int fd = creat(file_name, 0644);
0210     ssize_t n = strlen(prog);
0211     bool err = fd < 0 || writen(fd, prog, n) != n;
0212 
0213     close_fd(fd);
0214     return err ? -1 : 0;
0215 }
0216 
0217 static int get_dlfilters_path(char *buf, size_t sz)
0218 {
0219     char perf[PATH_MAX];
0220     char path[PATH_MAX];
0221     char *perf_path;
0222     char *exec_path;
0223 
0224     perf_exe(perf, sizeof(perf));
0225     perf_path = dirname(perf);
0226     snprintf(path, sizeof(path), "%s/dlfilters/dlfilter-test-api-v0.so", perf_path);
0227     if (access(path, R_OK)) {
0228         exec_path = get_argv_exec_path();
0229         if (!exec_path)
0230             return -1;
0231         snprintf(path, sizeof(path), "%s/dlfilters/dlfilter-test-api-v0.so", exec_path);
0232         free(exec_path);
0233         if (access(path, R_OK))
0234             return -1;
0235     }
0236     strlcpy(buf, dirname(path), sz);
0237     return 0;
0238 }
0239 
0240 static int check_filter_desc(struct test_data *td)
0241 {
0242     char *long_desc = NULL;
0243     char *desc = NULL;
0244     int ret;
0245 
0246     if (get_filter_desc(td->dlfilters, "dlfilter-test-api-v0.so", &desc, &long_desc) &&
0247         long_desc && !strcmp(long_desc, "Filter used by the 'dlfilter C API' perf test") &&
0248         desc && !strcmp(desc, "dlfilter to test v0 C API"))
0249         ret = 0;
0250     else
0251         ret = -1;
0252 
0253     free(desc);
0254     free(long_desc);
0255     return ret;
0256 }
0257 
0258 static int get_ip_addr(struct test_data *td)
0259 {
0260     struct map *map;
0261     struct symbol *sym;
0262 
0263     map = dso__new_map(td->prog_file_name);
0264     if (!map)
0265         return -1;
0266 
0267     sym = map__find_symbol_by_name(map, "foo");
0268     if (sym)
0269         td->foo = sym->start;
0270 
0271     sym = map__find_symbol_by_name(map, "bar");
0272     if (sym)
0273         td->bar = sym->start;
0274 
0275     map__put(map);
0276 
0277     td->ip = MAP_START + td->foo;
0278     td->addr = MAP_START + td->bar;
0279 
0280     return td->foo && td->bar ? 0 : -1;
0281 }
0282 
0283 static int do_run_perf_script(struct test_data *td, int do_early)
0284 {
0285     return system_cmd("%s script -i %s "
0286               "--dlfilter %s/dlfilter-test-api-v0.so "
0287               "--dlarg first "
0288               "--dlarg %d "
0289               "--dlarg %" PRIu64 " "
0290               "--dlarg %" PRIu64 " "
0291               "--dlarg %d "
0292               "--dlarg last",
0293               td->perf, td->perf_data_file_name, td->dlfilters,
0294               verbose, td->ip, td->addr, do_early);
0295 }
0296 
0297 static int run_perf_script(struct test_data *td)
0298 {
0299     int do_early;
0300     int err;
0301 
0302     for (do_early = 0; do_early < 3; do_early++) {
0303         err = do_run_perf_script(td, do_early);
0304         if (err)
0305             return err;
0306     }
0307     return 0;
0308 }
0309 
0310 #define TEST_SAMPLE_TYPE (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \
0311               PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_TIME | \
0312               PERF_SAMPLE_ADDR | PERF_SAMPLE_CPU | \
0313               PERF_SAMPLE_PERIOD | PERF_SAMPLE_STREAM_ID)
0314 
0315 static int test__dlfilter_test(struct test_data *td)
0316 {
0317     u64 sample_type = TEST_SAMPLE_TYPE;
0318     pid_t pid = 12345;
0319     pid_t tid = 12346;
0320     u64 id = 99;
0321     int err;
0322 
0323     if (get_dlfilters_path(td->dlfilters, PATH_MAX))
0324         return test_result("dlfilters not found", TEST_SKIP);
0325 
0326     if (check_filter_desc(td))
0327         return test_result("Failed to get expected filter description", TEST_FAIL);
0328 
0329     if (!have_gcc())
0330         return test_result("gcc not found", TEST_SKIP);
0331 
0332     pr_debug("dlfilters path: %s\n", td->dlfilters);
0333 
0334     if (write_prog(td->c_file_name))
0335         return test_result("Failed to write test C file", TEST_FAIL);
0336 
0337     if (verbose > 1)
0338         system_cmd("cat %s ; echo", td->c_file_name);
0339 
0340     if (system_cmd("gcc -g -o %s %s", td->prog_file_name, td->c_file_name))
0341         return TEST_FAIL;
0342 
0343     if (verbose > 2)
0344         system_cmd("objdump -x -dS %s", td->prog_file_name);
0345 
0346     if (get_ip_addr(td))
0347         return test_result("Failed to find program symbols", TEST_FAIL);
0348 
0349     pr_debug("Creating new host machine structure\n");
0350     td->machine = machine__new_host();
0351     td->machine->env = &perf_env;
0352 
0353     td->fd = creat(td->perf_data_file_name, 0644);
0354     if (td->fd < 0)
0355         return test_result("Failed to create test perf.data file", TEST_FAIL);
0356 
0357     err = perf_header__write_pipe(td->fd);
0358     if (err < 0)
0359         return test_result("perf_header__write_pipe() failed", TEST_FAIL);
0360 
0361     err = write_attr(td, sample_type, &id);
0362     if (err)
0363         return test_result("perf_event__synthesize_attr() failed", TEST_FAIL);
0364 
0365     if (write_comm(td->fd, pid, tid, "test-prog"))
0366         return TEST_FAIL;
0367 
0368     if (write_mmap(td->fd, pid, tid, MAP_START, 0x10000, 0, td->prog_file_name))
0369         return TEST_FAIL;
0370 
0371     if (write_sample(td, sample_type, id, pid, tid) != TEST_OK)
0372         return TEST_FAIL;
0373 
0374     if (verbose > 1)
0375         system_cmd("%s script -i %s -D", td->perf, td->perf_data_file_name);
0376 
0377     err = run_perf_script(td);
0378     if (err)
0379         return TEST_FAIL;
0380 
0381     return TEST_OK;
0382 }
0383 
0384 static void unlink_path(const char *path)
0385 {
0386     if (*path)
0387         unlink(path);
0388 }
0389 
0390 static void test_data__free(struct test_data *td)
0391 {
0392     machine__delete(td->machine);
0393     close_fd(td->fd);
0394     if (verbose <= 2) {
0395         unlink_path(td->c_file_name);
0396         unlink_path(td->prog_file_name);
0397         unlink_path(td->perf_data_file_name);
0398     }
0399 }
0400 
0401 static int test__dlfilter(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
0402 {
0403     struct test_data td = {.fd = -1};
0404     int pid = getpid();
0405     int err;
0406 
0407     perf_exe(td.perf, sizeof(td.perf));
0408 
0409     snprintf(td.perf_data_file_name, PATH_MAX, "/tmp/dlfilter-test-%u-perf-data", pid);
0410     snprintf(td.c_file_name, PATH_MAX, "/tmp/dlfilter-test-%u-prog.c", pid);
0411     snprintf(td.prog_file_name, PATH_MAX, "/tmp/dlfilter-test-%u-prog", pid);
0412 
0413     err = test__dlfilter_test(&td);
0414     test_data__free(&td);
0415     return err;
0416 }
0417 
0418 DEFINE_SUITE("dlfilter C API", dlfilter);