static int do_test(struct bpf_object *obj, int (*func)(void), int expect) { struct record_opts opts = { .target = { .uid = UINT_MAX, .uses_mmap = true, }, .freq = 0, .mmap_pages = 256, .default_interval = 1, }; char pid[16]; char sbuf[STRERR_BUFSIZE]; struct perf_evlist *evlist; int i, ret = TEST_FAIL, err = 0, count = 0; struct parse_events_evlist parse_evlist; struct parse_events_error parse_error; bzero(&parse_error, sizeof(parse_error)); bzero(&parse_evlist, sizeof(parse_evlist)); parse_evlist.error = &parse_error; INIT_LIST_HEAD(&parse_evlist.list); err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj, NULL); if (err || list_empty(&parse_evlist.list)) { pr_debug("Failed to add events selected by BPF\n"); return TEST_FAIL; } snprintf(pid, sizeof(pid), "%d", getpid()); pid[sizeof(pid) - 1] = '\0'; opts.target.tid = opts.target.pid = pid; /* Instead of perf_evlist__new_default, don't add default events */ evlist = perf_evlist__new(); if (!evlist) { pr_debug("Not enough memory to create evlist\n"); return TEST_FAIL; } err = perf_evlist__create_maps(evlist, &opts.target); if (err < 0) { pr_debug("Not enough memory to create thread/cpu maps\n"); goto out_delete_evlist; } perf_evlist__splice_list_tail(evlist, &parse_evlist.list); evlist->nr_groups = parse_evlist.nr_groups; perf_evlist__config(evlist, &opts, NULL); err = perf_evlist__open(evlist); if (err < 0) { pr_debug("perf_evlist__open: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); goto out_delete_evlist; } err = perf_evlist__mmap(evlist, opts.mmap_pages, false); if (err < 0) { pr_debug("perf_evlist__mmap: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); goto out_delete_evlist; } perf_evlist__enable(evlist); (*func)(); perf_evlist__disable(evlist); for (i = 0; i < evlist->nr_mmaps; i++) { union perf_event *event; while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { const u32 type = event->header.type; if (type == PERF_RECORD_SAMPLE) count ++; } } if (count != expect) { pr_debug("BPF filter result incorrect\n"); goto out_delete_evlist; } ret = TEST_OK; out_delete_evlist: perf_evlist__delete(evlist); return ret; }
int test__syscall_open_tp_fields(void) { struct perf_record_opts opts = { .target = { .uid = UINT_MAX, .uses_mmap = true, }, .no_delay = true, .freq = 1, .mmap_pages = 256, .raw_samples = true, }; const char *filename = "/etc/passwd"; int flags = O_RDONLY | O_DIRECTORY; struct perf_evlist *evlist = perf_evlist__new(); struct perf_evsel *evsel; int err = -1, i, nr_events = 0, nr_polls = 0; if (evlist == NULL) { pr_debug("%s: perf_evlist__new\n", __func__); goto out; } evsel = perf_evsel__newtp("syscalls", "sys_enter_open"); if (evsel == NULL) { pr_debug("%s: perf_evsel__newtp\n", __func__); goto out_delete_evlist; } perf_evlist__add(evlist, evsel); err = perf_evlist__create_maps(evlist, &opts.target); if (err < 0) { pr_debug("%s: perf_evlist__create_maps\n", __func__); goto out_delete_evlist; } perf_evsel__config(evsel, &opts); evlist->threads->map[0] = getpid(); err = perf_evlist__open(evlist); if (err < 0) { pr_debug("perf_evlist__open: %s\n", strerror(errno)); goto out_delete_maps; } err = perf_evlist__mmap(evlist, UINT_MAX, false); if (err < 0) { pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); goto out_close_evlist; } perf_evlist__enable(evlist); /* * Generate the event: */ open(filename, flags); while (1) { int before = nr_events; for (i = 0; i < evlist->nr_mmaps; i++) { union perf_event *event; while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { const u32 type = event->header.type; int tp_flags; struct perf_sample sample; ++nr_events; if (type != PERF_RECORD_SAMPLE) { perf_evlist__mmap_consume(evlist, i); continue; } err = perf_evsel__parse_sample(evsel, event, &sample); if (err) { pr_err("Can't parse sample, err = %d\n", err); goto out_munmap; } tp_flags = perf_evsel__intval(evsel, &sample, "flags"); if (flags != tp_flags) { pr_debug("%s: Expected flags=%#x, got %#x\n", __func__, flags, tp_flags); goto out_munmap; } goto out_ok; } } if (nr_events == before) poll(evlist->pollfd, evlist->nr_fds, 10); if (++nr_polls > 5) { pr_debug("%s: no events!\n", __func__); goto out_munmap; } } out_ok: err = 0; out_munmap: perf_evlist__munmap(evlist); out_close_evlist: perf_evlist__close(evlist); out_delete_maps: perf_evlist__delete_maps(evlist); out_delete_evlist: perf_evlist__delete(evlist); out: return err; }
int test__PERF_RECORD(int subtest __maybe_unused) { struct record_opts opts = { .target = { .uid = UINT_MAX, .uses_mmap = true, }, .no_buffering = true, .mmap_pages = 256, }; cpu_set_t cpu_mask; size_t cpu_mask_size = sizeof(cpu_mask); struct perf_evlist *evlist = perf_evlist__new_dummy(); struct perf_evsel *evsel; struct perf_sample sample; const char *cmd = "sleep"; const char *argv[] = { cmd, "1", NULL, }; char *bname, *mmap_filename; u64 prev_time = 0; bool found_cmd_mmap = false, found_libc_mmap = false, found_vdso_mmap = false, found_ld_mmap = false; int err = -1, errs = 0, i, wakeups = 0; u32 cpu; int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; char sbuf[STRERR_BUFSIZE]; if (evlist == NULL) /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */ evlist = perf_evlist__new_default(); if (evlist == NULL || argv == NULL) { pr_debug("Not enough memory to create evlist\n"); goto out; } /* * Create maps of threads and cpus to monitor. In this case * we start with all threads and cpus (-1, -1) but then in * perf_evlist__prepare_workload we'll fill in the only thread * we're monitoring, the one forked there. */ err = perf_evlist__create_maps(evlist, &opts.target); if (err < 0) { pr_debug("Not enough memory to create thread/cpu maps\n"); goto out_delete_evlist; } /* * Prepare the workload in argv[] to run, it'll fork it, and then wait * for perf_evlist__start_workload() to exec it. This is done this way * so that we have time to open the evlist (calling sys_perf_event_open * on all the fds) and then mmap them. */ err = perf_evlist__prepare_workload(evlist, &opts.target, argv, false, NULL); if (err < 0) { pr_debug("Couldn't run the workload!\n"); goto out_delete_evlist; } /* * Config the evsels, setting attr->comm on the first one, etc. */ evsel = perf_evlist__first(evlist); perf_evsel__set_sample_bit(evsel, CPU); perf_evsel__set_sample_bit(evsel, TID); perf_evsel__set_sample_bit(evsel, TIME); perf_evlist__config(evlist, &opts); err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask); if (err < 0) { pr_debug("sched__get_first_possible_cpu: %s\n", strerror_r(errno, sbuf, sizeof(sbuf))); goto out_delete_evlist; } cpu = err; /* * So that we can check perf_sample.cpu on all the samples. */ if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) { pr_debug("sched_setaffinity: %s\n", strerror_r(errno, sbuf, sizeof(sbuf))); goto out_delete_evlist; } /* * Call sys_perf_event_open on all the fds on all the evsels, * grouping them if asked to. */ err = perf_evlist__open(evlist); if (err < 0) { pr_debug("perf_evlist__open: %s\n", strerror_r(errno, sbuf, sizeof(sbuf))); goto out_delete_evlist; } /* * mmap the first fd on a given CPU and ask for events for the other * fds in the same CPU to be injected in the same mmap ring buffer * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)). */ err = perf_evlist__mmap(evlist, opts.mmap_pages, false); if (err < 0) { pr_debug("perf_evlist__mmap: %s\n", strerror_r(errno, sbuf, sizeof(sbuf))); goto out_delete_evlist; } /* * Now that all is properly set up, enable the events, they will * count just on workload.pid, which will start... */ perf_evlist__enable(evlist); /* * Now! */ perf_evlist__start_workload(evlist); while (1) { int before = total_events; for (i = 0; i < evlist->nr_mmaps; i++) { union perf_event *event; while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { const u32 type = event->header.type; const char *name = perf_event__name(type); ++total_events; if (type < PERF_RECORD_MAX) nr_events[type]++; err = perf_evlist__parse_sample(evlist, event, &sample); if (err < 0) { if (verbose) perf_event__fprintf(event, stderr); pr_debug("Couldn't parse sample\n"); goto out_delete_evlist; } if (verbose) { pr_info("%" PRIu64" %d ", sample.time, sample.cpu); perf_event__fprintf(event, stderr); } if (prev_time > sample.time) { pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n", name, prev_time, sample.time); ++errs; } prev_time = sample.time; if (sample.cpu != cpu) { pr_debug("%s with unexpected cpu, expected %d, got %d\n", name, cpu, sample.cpu); ++errs; } if ((pid_t)sample.pid != evlist->workload.pid) { pr_debug("%s with unexpected pid, expected %d, got %d\n", name, evlist->workload.pid, sample.pid); ++errs; } if ((pid_t)sample.tid != evlist->workload.pid) { pr_debug("%s with unexpected tid, expected %d, got %d\n", name, evlist->workload.pid, sample.tid); ++errs; } if ((type == PERF_RECORD_COMM || type == PERF_RECORD_MMAP || type == PERF_RECORD_MMAP2 || type == PERF_RECORD_FORK || type == PERF_RECORD_EXIT) && (pid_t)event->comm.pid != evlist->workload.pid) { pr_debug("%s with unexpected pid/tid\n", name); ++errs; } if ((type == PERF_RECORD_COMM || type == PERF_RECORD_MMAP || type == PERF_RECORD_MMAP2) && event->comm.pid != event->comm.tid) { pr_debug("%s with different pid/tid!\n", name); ++errs; } switch (type) { case PERF_RECORD_COMM: if (strcmp(event->comm.comm, cmd)) { pr_debug("%s with unexpected comm!\n", name); ++errs; } break; case PERF_RECORD_EXIT: goto found_exit; case PERF_RECORD_MMAP: mmap_filename = event->mmap.filename; goto check_bname; case PERF_RECORD_MMAP2: mmap_filename = event->mmap2.filename; check_bname: bname = strrchr(mmap_filename, '/'); if (bname != NULL) { if (!found_cmd_mmap) found_cmd_mmap = !strcmp(bname + 1, cmd); if (!found_libc_mmap) found_libc_mmap = !strncmp(bname + 1, "libc", 4); if (!found_ld_mmap) found_ld_mmap = !strncmp(bname + 1, "ld", 2); } else if (!found_vdso_mmap) found_vdso_mmap = !strcmp(mmap_filename, "[vdso]"); break; case PERF_RECORD_SAMPLE: /* Just ignore samples for now */ break; default: pr_debug("Unexpected perf_event->header.type %d!\n", type); ++errs; } perf_evlist__mmap_consume(evlist, i); } } /* * We don't use poll here because at least at 3.1 times the * PERF_RECORD_{!SAMPLE} events don't honour * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. */ if (total_events == before && false) perf_evlist__poll(evlist, -1); sleep(1); if (++wakeups > 5) { pr_debug("No PERF_RECORD_EXIT event!\n"); break; } } found_exit: if (nr_events[PERF_RECORD_COMM] > 1) { pr_debug("Excessive number of PERF_RECORD_COMM events!\n"); ++errs; } if (nr_events[PERF_RECORD_COMM] == 0) { pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd); ++errs; } if (!found_cmd_mmap) { pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd); ++errs; } if (!found_libc_mmap) { pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc"); ++errs; } if (!found_ld_mmap) { pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld"); ++errs; } if (!found_vdso_mmap) { pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]"); ++errs; } out_delete_evlist: perf_evlist__delete(evlist); out: return (err < 0 || errs > 0) ? -1 : 0; }
int test__backward_ring_buffer(int subtest __maybe_unused) { int ret = TEST_SKIP, err, sample_count = 0, comm_count = 0; char pid[16], sbuf[STRERR_BUFSIZE]; struct perf_evlist *evlist; struct perf_evsel *evsel __maybe_unused; struct parse_events_error parse_error; struct record_opts opts = { .target = { .uid = UINT_MAX, .uses_mmap = true, }, .freq = 0, .mmap_pages = 256, .default_interval = 1, }; snprintf(pid, sizeof(pid), "%d", getpid()); pid[sizeof(pid) - 1] = '\0'; opts.target.tid = opts.target.pid = pid; evlist = perf_evlist__new(); if (!evlist) { pr_debug("No ehough memory to create evlist\n"); return TEST_FAIL; } err = perf_evlist__create_maps(evlist, &opts.target); if (err < 0) { pr_debug("Not enough memory to create thread/cpu maps\n"); goto out_delete_evlist; } bzero(&parse_error, sizeof(parse_error)); err = parse_events(evlist, "syscalls:sys_enter_prctl", &parse_error); if (err) { pr_debug("Failed to parse tracepoint event, try use root\n"); ret = TEST_SKIP; goto out_delete_evlist; } perf_evlist__config(evlist, &opts, NULL); /* Set backward bit, ring buffer should be writing from end */ evlist__for_each(evlist, evsel) evsel->attr.write_backward = 1; err = perf_evlist__open(evlist); if (err < 0) { pr_debug("perf_evlist__open: %s\n", strerror_r(errno, sbuf, sizeof(sbuf))); goto out_delete_evlist; } ret = TEST_FAIL; err = do_test(evlist, opts.mmap_pages, &sample_count, &comm_count); if (err != TEST_OK) goto out_delete_evlist; if ((sample_count != NR_ITERS) || (comm_count != NR_ITERS)) { pr_err("Unexpected counter: sample_count=%d, comm_count=%d\n", sample_count, comm_count); goto out_delete_evlist; } err = do_test(evlist, 1, &sample_count, &comm_count); if (err != TEST_OK) goto out_delete_evlist; ret = TEST_OK; out_delete_evlist: perf_evlist__delete(evlist); return ret; }