125763b3cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21c47910eSAlexei Starovoitov /* Copyright (c) 2016 Facebook
31c47910eSAlexei Starovoitov */
41c47910eSAlexei Starovoitov #include <stdio.h>
51c47910eSAlexei Starovoitov #include <unistd.h>
61c47910eSAlexei Starovoitov #include <stdlib.h>
71c47910eSAlexei Starovoitov #include <stdbool.h>
81c47910eSAlexei Starovoitov #include <string.h>
91c47910eSAlexei Starovoitov #include <linux/perf_event.h>
101c47910eSAlexei Starovoitov #include <linux/bpf.h>
111c47910eSAlexei Starovoitov #include <signal.h>
121c47910eSAlexei Starovoitov #include <errno.h>
131c47910eSAlexei Starovoitov #include <sys/resource.h>
14aa5e2af6SDaniel T. Lee #include <bpf/bpf.h>
157cf245a3SToke Høiland-Jørgensen #include <bpf/libbpf.h>
16205c8adaSJoe Stringer #include "perf-sys.h"
1728dbf861SYonghong Song #include "trace_helpers.h"
181c47910eSAlexei Starovoitov
191c47910eSAlexei Starovoitov #define SAMPLE_FREQ 50
201c47910eSAlexei Starovoitov
21aa5e2af6SDaniel T. Lee static int pid;
22aa5e2af6SDaniel T. Lee /* counts, stackmap */
23aa5e2af6SDaniel T. Lee static int map_fd[2];
24aa5e2af6SDaniel T. Lee struct bpf_program *prog;
251c47910eSAlexei Starovoitov static bool sys_read_seen, sys_write_seen;
261c47910eSAlexei Starovoitov
print_ksym(__u64 addr)271c47910eSAlexei Starovoitov static void print_ksym(__u64 addr)
281c47910eSAlexei Starovoitov {
291c47910eSAlexei Starovoitov struct ksym *sym;
301c47910eSAlexei Starovoitov
311c47910eSAlexei Starovoitov if (!addr)
321c47910eSAlexei Starovoitov return;
331c47910eSAlexei Starovoitov sym = ksym_search(addr);
34e67b2c71SDaniel T. Lee if (!sym) {
35e67b2c71SDaniel T. Lee printf("ksym not found. Is kallsyms loaded?\n");
36e67b2c71SDaniel T. Lee return;
37e67b2c71SDaniel T. Lee }
38e67b2c71SDaniel T. Lee
391c47910eSAlexei Starovoitov printf("%s;", sym->name);
40bba1b2a8SDaniel T. Lee if (!strstr(sym->name, "sys_read"))
411c47910eSAlexei Starovoitov sys_read_seen = true;
42bba1b2a8SDaniel T. Lee else if (!strstr(sym->name, "sys_write"))
431c47910eSAlexei Starovoitov sys_write_seen = true;
441c47910eSAlexei Starovoitov }
451c47910eSAlexei Starovoitov
print_addr(__u64 addr)461c47910eSAlexei Starovoitov static void print_addr(__u64 addr)
471c47910eSAlexei Starovoitov {
481c47910eSAlexei Starovoitov if (!addr)
491c47910eSAlexei Starovoitov return;
501c47910eSAlexei Starovoitov printf("%llx;", addr);
511c47910eSAlexei Starovoitov }
521c47910eSAlexei Starovoitov
531c47910eSAlexei Starovoitov #define TASK_COMM_LEN 16
541c47910eSAlexei Starovoitov
551c47910eSAlexei Starovoitov struct key_t {
561c47910eSAlexei Starovoitov char comm[TASK_COMM_LEN];
571c47910eSAlexei Starovoitov __u32 kernstack;
581c47910eSAlexei Starovoitov __u32 userstack;
591c47910eSAlexei Starovoitov };
601c47910eSAlexei Starovoitov
print_stack(struct key_t * key,__u64 count)611c47910eSAlexei Starovoitov static void print_stack(struct key_t *key, __u64 count)
621c47910eSAlexei Starovoitov {
631c47910eSAlexei Starovoitov __u64 ip[PERF_MAX_STACK_DEPTH] = {};
641c47910eSAlexei Starovoitov static bool warned;
651c47910eSAlexei Starovoitov int i;
661c47910eSAlexei Starovoitov
671c47910eSAlexei Starovoitov printf("%3lld %s;", count, key->comm);
68d40fc181SJoe Stringer if (bpf_map_lookup_elem(map_fd[1], &key->kernstack, ip) != 0) {
691c47910eSAlexei Starovoitov printf("---;");
701c47910eSAlexei Starovoitov } else {
711c47910eSAlexei Starovoitov for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--)
721c47910eSAlexei Starovoitov print_ksym(ip[i]);
731c47910eSAlexei Starovoitov }
741c47910eSAlexei Starovoitov printf("-;");
75d40fc181SJoe Stringer if (bpf_map_lookup_elem(map_fd[1], &key->userstack, ip) != 0) {
761c47910eSAlexei Starovoitov printf("---;");
771c47910eSAlexei Starovoitov } else {
781c47910eSAlexei Starovoitov for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--)
791c47910eSAlexei Starovoitov print_addr(ip[i]);
801c47910eSAlexei Starovoitov }
8141e9a804STeng Qin if (count < 6)
8241e9a804STeng Qin printf("\r");
8341e9a804STeng Qin else
841c47910eSAlexei Starovoitov printf("\n");
851c47910eSAlexei Starovoitov
861c47910eSAlexei Starovoitov if (key->kernstack == -EEXIST && !warned) {
871c47910eSAlexei Starovoitov printf("stackmap collisions seen. Consider increasing size\n");
881c47910eSAlexei Starovoitov warned = true;
891c47910eSAlexei Starovoitov } else if ((int)key->kernstack < 0 && (int)key->userstack < 0) {
901c47910eSAlexei Starovoitov printf("err stackid %d %d\n", key->kernstack, key->userstack);
911c47910eSAlexei Starovoitov }
921c47910eSAlexei Starovoitov }
931c47910eSAlexei Starovoitov
err_exit(int err)94aa5e2af6SDaniel T. Lee static void err_exit(int err)
951c47910eSAlexei Starovoitov {
96aa5e2af6SDaniel T. Lee kill(pid, SIGKILL);
97aa5e2af6SDaniel T. Lee exit(err);
981c47910eSAlexei Starovoitov }
991c47910eSAlexei Starovoitov
print_stacks(void)1001c47910eSAlexei Starovoitov static void print_stacks(void)
1011c47910eSAlexei Starovoitov {
1021c47910eSAlexei Starovoitov struct key_t key = {}, next_key;
1031c47910eSAlexei Starovoitov __u64 value;
1041c47910eSAlexei Starovoitov __u32 stackid = 0, next_id;
105aa5e2af6SDaniel T. Lee int error = 1, fd = map_fd[0], stack_map = map_fd[1];
1061c47910eSAlexei Starovoitov
1071c47910eSAlexei Starovoitov sys_read_seen = sys_write_seen = false;
108d40fc181SJoe Stringer while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
109d40fc181SJoe Stringer bpf_map_lookup_elem(fd, &next_key, &value);
1101c47910eSAlexei Starovoitov print_stack(&next_key, value);
111d40fc181SJoe Stringer bpf_map_delete_elem(fd, &next_key);
1121c47910eSAlexei Starovoitov key = next_key;
1131c47910eSAlexei Starovoitov }
11441e9a804STeng Qin printf("\n");
1151c47910eSAlexei Starovoitov if (!sys_read_seen || !sys_write_seen) {
1161c47910eSAlexei Starovoitov printf("BUG kernel stack doesn't contain sys_read() and sys_write()\n");
117aa5e2af6SDaniel T. Lee err_exit(error);
1181c47910eSAlexei Starovoitov }
1191c47910eSAlexei Starovoitov
1201c47910eSAlexei Starovoitov /* clear stack map */
121d40fc181SJoe Stringer while (bpf_map_get_next_key(stack_map, &stackid, &next_id) == 0) {
122d40fc181SJoe Stringer bpf_map_delete_elem(stack_map, &next_id);
1231c47910eSAlexei Starovoitov stackid = next_id;
1241c47910eSAlexei Starovoitov }
1251c47910eSAlexei Starovoitov }
1261c47910eSAlexei Starovoitov
generate_load(void)127492b7e89STaeung Song static inline int generate_load(void)
128492b7e89STaeung Song {
129492b7e89STaeung Song if (system("dd if=/dev/zero of=/dev/null count=5000k status=none") < 0) {
130492b7e89STaeung Song printf("failed to generate some load with dd: %s\n", strerror(errno));
131492b7e89STaeung Song return -1;
132492b7e89STaeung Song }
133492b7e89STaeung Song
134492b7e89STaeung Song return 0;
135492b7e89STaeung Song }
136492b7e89STaeung Song
test_perf_event_all_cpu(struct perf_event_attr * attr)1371c47910eSAlexei Starovoitov static void test_perf_event_all_cpu(struct perf_event_attr *attr)
1381c47910eSAlexei Starovoitov {
139aa5e2af6SDaniel T. Lee int nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
140aa5e2af6SDaniel T. Lee struct bpf_link **links = calloc(nr_cpus, sizeof(struct bpf_link *));
141aa5e2af6SDaniel T. Lee int i, pmu_fd, error = 1;
142aa5e2af6SDaniel T. Lee
143aa5e2af6SDaniel T. Lee if (!links) {
144aa5e2af6SDaniel T. Lee printf("malloc of links failed\n");
145aa5e2af6SDaniel T. Lee goto err;
146aa5e2af6SDaniel T. Lee }
1471c47910eSAlexei Starovoitov
14881b9cf80SYonghong Song /* system wide perf event, no need to inherit */
14981b9cf80SYonghong Song attr->inherit = 0;
15081b9cf80SYonghong Song
1511c47910eSAlexei Starovoitov /* open perf_event on all cpus */
1521c47910eSAlexei Starovoitov for (i = 0; i < nr_cpus; i++) {
153aa5e2af6SDaniel T. Lee pmu_fd = sys_perf_event_open(attr, -1, i, -1, 0);
154aa5e2af6SDaniel T. Lee if (pmu_fd < 0) {
155205c8adaSJoe Stringer printf("sys_perf_event_open failed\n");
1561c47910eSAlexei Starovoitov goto all_cpu_err;
1571c47910eSAlexei Starovoitov }
158aa5e2af6SDaniel T. Lee links[i] = bpf_program__attach_perf_event(prog, pmu_fd);
159*0efdcefbSDaniel T. Lee if (libbpf_get_error(links[i])) {
160aa5e2af6SDaniel T. Lee printf("bpf_program__attach_perf_event failed\n");
161aa5e2af6SDaniel T. Lee links[i] = NULL;
162aa5e2af6SDaniel T. Lee close(pmu_fd);
163aa5e2af6SDaniel T. Lee goto all_cpu_err;
164aa5e2af6SDaniel T. Lee }
1651c47910eSAlexei Starovoitov }
166492b7e89STaeung Song
167aa5e2af6SDaniel T. Lee if (generate_load() < 0)
168492b7e89STaeung Song goto all_cpu_err;
169aa5e2af6SDaniel T. Lee
1701c47910eSAlexei Starovoitov print_stacks();
171aa5e2af6SDaniel T. Lee error = 0;
1721c47910eSAlexei Starovoitov all_cpu_err:
173aa5e2af6SDaniel T. Lee for (i--; i >= 0; i--)
174aa5e2af6SDaniel T. Lee bpf_link__destroy(links[i]);
175aa5e2af6SDaniel T. Lee err:
176aa5e2af6SDaniel T. Lee free(links);
17741e9a804STeng Qin if (error)
178aa5e2af6SDaniel T. Lee err_exit(error);
1791c47910eSAlexei Starovoitov }
1801c47910eSAlexei Starovoitov
test_perf_event_task(struct perf_event_attr * attr)1811c47910eSAlexei Starovoitov static void test_perf_event_task(struct perf_event_attr *attr)
1821c47910eSAlexei Starovoitov {
183aa5e2af6SDaniel T. Lee struct bpf_link *link = NULL;
184aa5e2af6SDaniel T. Lee int pmu_fd, error = 1;
1851c47910eSAlexei Starovoitov
18681b9cf80SYonghong Song /* per task perf event, enable inherit so the "dd ..." command can be traced properly.
18781b9cf80SYonghong Song * Enabling inherit will cause bpf_perf_prog_read_time helper failure.
18881b9cf80SYonghong Song */
18981b9cf80SYonghong Song attr->inherit = 1;
19081b9cf80SYonghong Song
1911c47910eSAlexei Starovoitov /* open task bound event */
192205c8adaSJoe Stringer pmu_fd = sys_perf_event_open(attr, 0, -1, -1, 0);
1931c47910eSAlexei Starovoitov if (pmu_fd < 0) {
194205c8adaSJoe Stringer printf("sys_perf_event_open failed\n");
195492b7e89STaeung Song goto err;
196492b7e89STaeung Song }
197aa5e2af6SDaniel T. Lee link = bpf_program__attach_perf_event(prog, pmu_fd);
198*0efdcefbSDaniel T. Lee if (libbpf_get_error(link)) {
199aa5e2af6SDaniel T. Lee printf("bpf_program__attach_perf_event failed\n");
200aa5e2af6SDaniel T. Lee link = NULL;
2011c47910eSAlexei Starovoitov close(pmu_fd);
202aa5e2af6SDaniel T. Lee goto err;
203aa5e2af6SDaniel T. Lee }
204aa5e2af6SDaniel T. Lee
205aa5e2af6SDaniel T. Lee if (generate_load() < 0)
206aa5e2af6SDaniel T. Lee goto err;
207aa5e2af6SDaniel T. Lee
208aa5e2af6SDaniel T. Lee print_stacks();
209aa5e2af6SDaniel T. Lee error = 0;
210aa5e2af6SDaniel T. Lee err:
211aa5e2af6SDaniel T. Lee bpf_link__destroy(link);
212492b7e89STaeung Song if (error)
213aa5e2af6SDaniel T. Lee err_exit(error);
2141c47910eSAlexei Starovoitov }
2151c47910eSAlexei Starovoitov
test_bpf_perf_event(void)2161c47910eSAlexei Starovoitov static void test_bpf_perf_event(void)
2171c47910eSAlexei Starovoitov {
2181c47910eSAlexei Starovoitov struct perf_event_attr attr_type_hw = {
2191c47910eSAlexei Starovoitov .sample_freq = SAMPLE_FREQ,
2201c47910eSAlexei Starovoitov .freq = 1,
2211c47910eSAlexei Starovoitov .type = PERF_TYPE_HARDWARE,
2221c47910eSAlexei Starovoitov .config = PERF_COUNT_HW_CPU_CYCLES,
2231c47910eSAlexei Starovoitov };
2241c47910eSAlexei Starovoitov struct perf_event_attr attr_type_sw = {
2251c47910eSAlexei Starovoitov .sample_freq = SAMPLE_FREQ,
2261c47910eSAlexei Starovoitov .freq = 1,
2271c47910eSAlexei Starovoitov .type = PERF_TYPE_SOFTWARE,
2281c47910eSAlexei Starovoitov .config = PERF_COUNT_SW_CPU_CLOCK,
2291c47910eSAlexei Starovoitov };
23041e9a804STeng Qin struct perf_event_attr attr_hw_cache_l1d = {
23141e9a804STeng Qin .sample_freq = SAMPLE_FREQ,
23241e9a804STeng Qin .freq = 1,
23341e9a804STeng Qin .type = PERF_TYPE_HW_CACHE,
23441e9a804STeng Qin .config =
23541e9a804STeng Qin PERF_COUNT_HW_CACHE_L1D |
23641e9a804STeng Qin (PERF_COUNT_HW_CACHE_OP_READ << 8) |
23741e9a804STeng Qin (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
23841e9a804STeng Qin };
23941e9a804STeng Qin struct perf_event_attr attr_hw_cache_branch_miss = {
24041e9a804STeng Qin .sample_freq = SAMPLE_FREQ,
24141e9a804STeng Qin .freq = 1,
24241e9a804STeng Qin .type = PERF_TYPE_HW_CACHE,
24341e9a804STeng Qin .config =
24441e9a804STeng Qin PERF_COUNT_HW_CACHE_BPU |
24541e9a804STeng Qin (PERF_COUNT_HW_CACHE_OP_READ << 8) |
24641e9a804STeng Qin (PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
24741e9a804STeng Qin };
24841e9a804STeng Qin struct perf_event_attr attr_type_raw = {
24941e9a804STeng Qin .sample_freq = SAMPLE_FREQ,
25041e9a804STeng Qin .freq = 1,
25141e9a804STeng Qin .type = PERF_TYPE_RAW,
25241e9a804STeng Qin /* Intel Instruction Retired */
25341e9a804STeng Qin .config = 0xc0,
25441e9a804STeng Qin };
25512fe1225STeng Qin struct perf_event_attr attr_type_raw_lock_load = {
25612fe1225STeng Qin .sample_freq = SAMPLE_FREQ,
25712fe1225STeng Qin .freq = 1,
25812fe1225STeng Qin .type = PERF_TYPE_RAW,
25912fe1225STeng Qin /* Intel MEM_UOPS_RETIRED.LOCK_LOADS */
26012fe1225STeng Qin .config = 0x21d0,
26112fe1225STeng Qin /* Request to record lock address from PEBS */
26212fe1225STeng Qin .sample_type = PERF_SAMPLE_ADDR,
26312fe1225STeng Qin /* Record address value requires precise event */
26412fe1225STeng Qin .precise_ip = 2,
26512fe1225STeng Qin };
2661c47910eSAlexei Starovoitov
26741e9a804STeng Qin printf("Test HW_CPU_CYCLES\n");
2681c47910eSAlexei Starovoitov test_perf_event_all_cpu(&attr_type_hw);
2691c47910eSAlexei Starovoitov test_perf_event_task(&attr_type_hw);
27041e9a804STeng Qin
27141e9a804STeng Qin printf("Test SW_CPU_CLOCK\n");
2721c47910eSAlexei Starovoitov test_perf_event_all_cpu(&attr_type_sw);
2731c47910eSAlexei Starovoitov test_perf_event_task(&attr_type_sw);
27441e9a804STeng Qin
27541e9a804STeng Qin printf("Test HW_CACHE_L1D\n");
27641e9a804STeng Qin test_perf_event_all_cpu(&attr_hw_cache_l1d);
27741e9a804STeng Qin test_perf_event_task(&attr_hw_cache_l1d);
27841e9a804STeng Qin
27941e9a804STeng Qin printf("Test HW_CACHE_BPU\n");
28041e9a804STeng Qin test_perf_event_all_cpu(&attr_hw_cache_branch_miss);
28141e9a804STeng Qin test_perf_event_task(&attr_hw_cache_branch_miss);
28241e9a804STeng Qin
28341e9a804STeng Qin printf("Test Instruction Retired\n");
28441e9a804STeng Qin test_perf_event_all_cpu(&attr_type_raw);
28541e9a804STeng Qin test_perf_event_task(&attr_type_raw);
28641e9a804STeng Qin
28712fe1225STeng Qin printf("Test Lock Load\n");
28812fe1225STeng Qin test_perf_event_all_cpu(&attr_type_raw_lock_load);
28912fe1225STeng Qin test_perf_event_task(&attr_type_raw_lock_load);
29012fe1225STeng Qin
29141e9a804STeng Qin printf("*** PASS ***\n");
2921c47910eSAlexei Starovoitov }
2931c47910eSAlexei Starovoitov
2941c47910eSAlexei Starovoitov
main(int argc,char ** argv)2951c47910eSAlexei Starovoitov int main(int argc, char **argv)
2961c47910eSAlexei Starovoitov {
297aa5e2af6SDaniel T. Lee struct bpf_object *obj = NULL;
2981c47910eSAlexei Starovoitov char filename[256];
299aa5e2af6SDaniel T. Lee int error = 1;
3001c47910eSAlexei Starovoitov
3011c47910eSAlexei Starovoitov snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
3021c47910eSAlexei Starovoitov
303aa5e2af6SDaniel T. Lee signal(SIGINT, err_exit);
304aa5e2af6SDaniel T. Lee signal(SIGTERM, err_exit);
3051c47910eSAlexei Starovoitov
3061c47910eSAlexei Starovoitov if (load_kallsyms()) {
3071c47910eSAlexei Starovoitov printf("failed to process /proc/kallsyms\n");
308aa5e2af6SDaniel T. Lee goto cleanup;
3091c47910eSAlexei Starovoitov }
3101c47910eSAlexei Starovoitov
311aa5e2af6SDaniel T. Lee obj = bpf_object__open_file(filename, NULL);
312*0efdcefbSDaniel T. Lee if (libbpf_get_error(obj)) {
313aa5e2af6SDaniel T. Lee printf("opening BPF object file failed\n");
314aa5e2af6SDaniel T. Lee obj = NULL;
315aa5e2af6SDaniel T. Lee goto cleanup;
3161c47910eSAlexei Starovoitov }
3171c47910eSAlexei Starovoitov
318aa5e2af6SDaniel T. Lee prog = bpf_object__find_program_by_name(obj, "bpf_prog1");
319aa5e2af6SDaniel T. Lee if (!prog) {
320aa5e2af6SDaniel T. Lee printf("finding a prog in obj file failed\n");
321aa5e2af6SDaniel T. Lee goto cleanup;
322aa5e2af6SDaniel T. Lee }
323aa5e2af6SDaniel T. Lee
324aa5e2af6SDaniel T. Lee /* load BPF program */
325aa5e2af6SDaniel T. Lee if (bpf_object__load(obj)) {
326aa5e2af6SDaniel T. Lee printf("loading BPF object file failed\n");
327aa5e2af6SDaniel T. Lee goto cleanup;
328aa5e2af6SDaniel T. Lee }
329aa5e2af6SDaniel T. Lee
330aa5e2af6SDaniel T. Lee map_fd[0] = bpf_object__find_map_fd_by_name(obj, "counts");
331aa5e2af6SDaniel T. Lee map_fd[1] = bpf_object__find_map_fd_by_name(obj, "stackmap");
332aa5e2af6SDaniel T. Lee if (map_fd[0] < 0 || map_fd[1] < 0) {
333aa5e2af6SDaniel T. Lee printf("finding a counts/stackmap map in obj file failed\n");
334aa5e2af6SDaniel T. Lee goto cleanup;
335aa5e2af6SDaniel T. Lee }
336aa5e2af6SDaniel T. Lee
337aa5e2af6SDaniel T. Lee pid = fork();
338aa5e2af6SDaniel T. Lee if (pid == 0) {
3391c47910eSAlexei Starovoitov read_trace_pipe();
3401c47910eSAlexei Starovoitov return 0;
341aa5e2af6SDaniel T. Lee } else if (pid == -1) {
342aa5e2af6SDaniel T. Lee printf("couldn't spawn process\n");
343aa5e2af6SDaniel T. Lee goto cleanup;
3441c47910eSAlexei Starovoitov }
345aa5e2af6SDaniel T. Lee
3461c47910eSAlexei Starovoitov test_bpf_perf_event();
347aa5e2af6SDaniel T. Lee error = 0;
348aa5e2af6SDaniel T. Lee
349aa5e2af6SDaniel T. Lee cleanup:
350aa5e2af6SDaniel T. Lee bpf_object__close(obj);
351aa5e2af6SDaniel T. Lee err_exit(error);
3521c47910eSAlexei Starovoitov }
353