1 // SPDX-License-Identifier: GPL-2.0
3 * Test dlfilter C API. A perf.data file is synthesized and then processed
4 * by perf script with dlfilters named dlfilter-test-api-v*.so. Also a C file
5 * is compiled to provide a dso to match the synthesized perf.data file.
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>
31 #include "synthetic-events.h"
36 #include "util/sample.h"
38 #define MAP_START 0x400000
40 #define DLFILTER_TEST_NAME_MAX 128
43 struct perf_tool tool
;
44 struct machine
*machine
;
50 char name
[DLFILTER_TEST_NAME_MAX
];
51 char desc
[DLFILTER_TEST_NAME_MAX
];
53 char perf_data_file_name
[PATH_MAX
];
54 char c_file_name
[PATH_MAX
];
55 char prog_file_name
[PATH_MAX
];
56 char dlfilters
[PATH_MAX
];
59 static int test_result(const char *msg
, int ret
)
61 pr_debug("%s\n", msg
);
65 static int process(const struct perf_tool
*tool
, union perf_event
*event
,
66 struct perf_sample
*sample __maybe_unused
,
67 struct machine
*machine __maybe_unused
)
69 struct test_data
*td
= container_of(tool
, struct test_data
, tool
);
72 if (writen(fd
, event
, event
->header
.size
) != event
->header
.size
)
79 #define REDIRECT_TO_DEV_NULL " >/dev/null 2>&1"
81 static __printf(1, 2) int system_cmd(const char *fmt
, ...)
83 char cmd
[MAXCMD
+ sizeof(REDIRECT_TO_DEV_NULL
)];
89 ret
= vsnprintf(cmd
, MAXCMD
, fmt
, args
);
92 if (ret
<= 0 || ret
>= MAXCMD
)
96 strcat(cmd
, REDIRECT_TO_DEV_NULL
);
98 pr_debug("Command: %s\n", cmd
);
101 pr_debug("Failed with return value %d\n", ret
);
106 static bool have_gcc(void)
108 pr_debug("Checking for gcc\n");
109 return !system_cmd("gcc --version");
112 static int write_attr(struct test_data
*td
, u64 sample_type
, u64
*id
)
114 struct perf_event_attr attr
= {
115 .size
= sizeof(attr
),
116 .type
= PERF_TYPE_HARDWARE
,
117 .config
= PERF_COUNT_HW_BRANCH_INSTRUCTIONS
,
118 .sample_type
= sample_type
,
122 return perf_event__synthesize_attr(&td
->tool
, &attr
, 1, id
, process
);
125 static int write_comm(int fd
, pid_t pid
, pid_t tid
, const char *comm_str
)
127 struct perf_record_comm comm
;
128 ssize_t sz
= sizeof(comm
);
130 comm
.header
.type
= PERF_RECORD_COMM
;
131 comm
.header
.misc
= PERF_RECORD_MISC_USER
;
132 comm
.header
.size
= sz
;
136 strncpy(comm
.comm
, comm_str
, 16);
138 if (writen(fd
, &comm
, sz
) != sz
) {
139 pr_debug("%s failed\n", __func__
);
146 static int write_mmap(int fd
, pid_t pid
, pid_t tid
, u64 start
, u64 len
, u64 pgoff
,
147 const char *filename
)
149 char buf
[PERF_SAMPLE_MAX_SIZE
];
150 struct perf_record_mmap
*mmap
= (struct perf_record_mmap
*)buf
;
151 size_t fsz
= roundup(strlen(filename
) + 1, 8);
152 ssize_t sz
= sizeof(*mmap
) - sizeof(mmap
->filename
) + fsz
;
154 mmap
->header
.type
= PERF_RECORD_MMAP
;
155 mmap
->header
.misc
= PERF_RECORD_MISC_USER
;
156 mmap
->header
.size
= sz
;
163 strncpy(mmap
->filename
, filename
, sizeof(mmap
->filename
));
165 if (writen(fd
, mmap
, sz
) != sz
) {
166 pr_debug("%s failed\n", __func__
);
173 static int write_sample(struct test_data
*td
, u64 sample_type
, u64 id
, pid_t pid
, pid_t tid
)
175 char buf
[PERF_SAMPLE_MAX_SIZE
];
176 union perf_event
*event
= (union perf_event
*)buf
;
177 struct perf_sample sample
= {
190 event
->header
.type
= PERF_RECORD_SAMPLE
;
191 event
->header
.misc
= PERF_RECORD_MISC_USER
;
192 event
->header
.size
= perf_event__sample_event_size(&sample
, sample_type
, 0);
193 err
= perf_event__synthesize_sample(event
, sample_type
, 0, &sample
);
195 return test_result("perf_event__synthesize_sample() failed", TEST_FAIL
);
197 err
= process(&td
->tool
, event
, &sample
, td
->machine
);
199 return test_result("Failed to write sample", TEST_FAIL
);
204 static void close_fd(int fd
)
210 static const char *prog
= "int bar(){};int foo(){bar();};int main(){foo();return 0;}";
212 static int write_prog(char *file_name
)
214 int fd
= creat(file_name
, 0644);
215 ssize_t n
= strlen(prog
);
216 bool err
= fd
< 0 || writen(fd
, prog
, n
) != n
;
222 static int get_dlfilters_path(const char *name
, char *buf
, size_t sz
)
229 perf_exe(perf
, sizeof(perf
));
230 perf_path
= dirname(perf
);
231 snprintf(path
, sizeof(path
), "%s/dlfilters/%s", perf_path
, name
);
232 if (access(path
, R_OK
)) {
233 exec_path
= get_argv_exec_path();
236 snprintf(path
, sizeof(path
), "%s/dlfilters/%s", exec_path
, name
);
238 if (access(path
, R_OK
))
241 strlcpy(buf
, dirname(path
), sz
);
245 static int check_filter_desc(struct test_data
*td
)
247 char *long_desc
= NULL
;
251 if (get_filter_desc(td
->dlfilters
, td
->name
, &desc
, &long_desc
) &&
252 long_desc
&& !strcmp(long_desc
, "Filter used by the 'dlfilter C API' perf test") &&
253 desc
&& !strcmp(desc
, td
->desc
))
263 static int get_ip_addr(struct test_data
*td
)
268 map
= dso__new_map(td
->prog_file_name
);
272 sym
= map__find_symbol_by_name(map
, "foo");
274 td
->foo
= sym
->start
;
276 sym
= map__find_symbol_by_name(map
, "bar");
278 td
->bar
= sym
->start
;
282 td
->ip
= MAP_START
+ td
->foo
;
283 td
->addr
= MAP_START
+ td
->bar
;
285 return td
->foo
&& td
->bar
? 0 : -1;
288 static int do_run_perf_script(struct test_data
*td
, int do_early
)
290 return system_cmd("%s script -i %s "
294 "--dlarg %" PRIu64
" "
295 "--dlarg %" PRIu64
" "
298 td
->perf
, td
->perf_data_file_name
, td
->dlfilters
,
299 td
->name
, verbose
, td
->ip
, td
->addr
, do_early
);
302 static int run_perf_script(struct test_data
*td
)
307 for (do_early
= 0; do_early
< 3; do_early
++) {
308 err
= do_run_perf_script(td
, do_early
);
315 #define TEST_SAMPLE_TYPE (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \
316 PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_TIME | \
317 PERF_SAMPLE_ADDR | PERF_SAMPLE_CPU | \
318 PERF_SAMPLE_PERIOD | PERF_SAMPLE_STREAM_ID)
320 static int test__dlfilter_test(struct test_data
*td
)
322 u64 sample_type
= TEST_SAMPLE_TYPE
;
328 if (get_dlfilters_path(td
->name
, td
->dlfilters
, PATH_MAX
))
329 return test_result("dlfilters not found", TEST_SKIP
);
331 if (check_filter_desc(td
))
332 return test_result("Failed to get expected filter description", TEST_FAIL
);
335 return test_result("gcc not found", TEST_SKIP
);
337 pr_debug("dlfilters path: %s\n", td
->dlfilters
);
339 if (write_prog(td
->c_file_name
))
340 return test_result("Failed to write test C file", TEST_FAIL
);
343 system_cmd("cat %s ; echo", td
->c_file_name
);
345 if (system_cmd("gcc -g -o %s %s", td
->prog_file_name
, td
->c_file_name
))
349 system_cmd("objdump -x -dS %s", td
->prog_file_name
);
352 return test_result("Failed to find program symbols", TEST_FAIL
);
354 pr_debug("Creating new host machine structure\n");
355 td
->machine
= machine__new_host();
356 td
->machine
->env
= &perf_env
;
358 td
->fd
= creat(td
->perf_data_file_name
, 0644);
360 return test_result("Failed to create test perf.data file", TEST_FAIL
);
362 err
= perf_header__write_pipe(td
->fd
);
364 return test_result("perf_header__write_pipe() failed", TEST_FAIL
);
366 err
= write_attr(td
, sample_type
, &id
);
368 return test_result("perf_event__synthesize_attr() failed", TEST_FAIL
);
370 if (write_comm(td
->fd
, pid
, tid
, "test-prog"))
373 if (write_mmap(td
->fd
, pid
, tid
, MAP_START
, 0x10000, 0, td
->prog_file_name
))
376 if (write_sample(td
, sample_type
, id
, pid
, tid
) != TEST_OK
)
380 system_cmd("%s script -i %s -D", td
->perf
, td
->perf_data_file_name
);
382 err
= run_perf_script(td
);
389 static void unlink_path(const char *path
)
395 static void test_data__free(struct test_data
*td
)
397 machine__delete(td
->machine
);
400 unlink_path(td
->c_file_name
);
401 unlink_path(td
->prog_file_name
);
402 unlink_path(td
->perf_data_file_name
);
406 static int test__dlfilter_ver(int ver
)
408 struct test_data td
= {.fd
= -1};
412 pr_debug("\n-- Testing version %d API --\n", ver
);
414 perf_exe(td
.perf
, sizeof(td
.perf
));
416 snprintf(td
.name
, sizeof(td
.name
), "dlfilter-test-api-v%d.so", ver
);
417 snprintf(td
.desc
, sizeof(td
.desc
), "dlfilter to test v%d C API", ver
);
418 snprintf(td
.perf_data_file_name
, PATH_MAX
, "/tmp/dlfilter-test-%u-perf-data", pid
);
419 snprintf(td
.c_file_name
, PATH_MAX
, "/tmp/dlfilter-test-%u-prog.c", pid
);
420 snprintf(td
.prog_file_name
, PATH_MAX
, "/tmp/dlfilter-test-%u-prog", pid
);
422 err
= test__dlfilter_test(&td
);
423 test_data__free(&td
);
427 static int test__dlfilter(struct test_suite
*test __maybe_unused
, int subtest __maybe_unused
)
429 int err
= test__dlfilter_ver(0);
433 /* No test for version 1 */
434 return test__dlfilter_ver(2);
437 DEFINE_SUITE("dlfilter C API", dlfilter
);