if (evlist && evlist__add_dummy(evlist)) {
evlist__delete(evlist);
evlist = NULL;
}
return evlist;
}
/** * evlist__set_id_pos - set the positions of event ids. * @evlist: selected event list * * Events with compatible sample types all have the same id_pos * and is_pos. For convenience, put a copy on evlist.
*/ void evlist__set_id_pos(struct evlist *evlist)
{ struct evsel *first = evlist__first(evlist);
int __evlist__set_tracepoints_handlers(struct evlist *evlist, conststruct evsel_str_handler *assocs, size_t nr_assocs)
{
size_t i; int err;
for (i = 0; i < nr_assocs; i++) { // Adding a handler for an event not in this evlist, just ignore it. struct evsel *evsel = evlist__find_tracepoint_by_name(evlist, assocs[i].name); if (evsel == NULL) continue;
if (evlist__empty(evlist)) { /* Ensure the empty list doesn't iterate. */
itr.evlist_cpu_map_idx = itr.evlist_cpu_map_nr;
} else {
itr.evsel = evlist__first(evlist); if (itr.affinity) {
itr.cpu = perf_cpu_map__cpu(evlist->core.all_cpus, 0);
affinity__set(itr.affinity, itr.cpu.cpu);
itr.cpu_map_idx = perf_cpu_map__idx(itr.evsel->core.cpus, itr.cpu); /* * If this CPU isn't in the evsel's cpu map then advance * through the list.
*/ if (itr.cpu_map_idx == -1)
evlist_cpu_iterator__next(&itr);
}
} return itr;
}
void evlist_cpu_iterator__next(struct evlist_cpu_iterator *evlist_cpu_itr)
{ while (evlist_cpu_itr->evsel != evlist__last(evlist_cpu_itr->container)) {
evlist_cpu_itr->evsel = evsel__next(evlist_cpu_itr->evsel);
evlist_cpu_itr->cpu_map_idx =
perf_cpu_map__idx(evlist_cpu_itr->evsel->core.cpus,
evlist_cpu_itr->cpu); if (evlist_cpu_itr->cpu_map_idx != -1) return;
}
evlist_cpu_itr->evlist_cpu_map_idx++; if (evlist_cpu_itr->evlist_cpu_map_idx < evlist_cpu_itr->evlist_cpu_map_nr) {
evlist_cpu_itr->evsel = evlist__first(evlist_cpu_itr->container);
evlist_cpu_itr->cpu =
perf_cpu_map__cpu(evlist_cpu_itr->container->core.all_cpus,
evlist_cpu_itr->evlist_cpu_map_idx); if (evlist_cpu_itr->affinity)
affinity__set(evlist_cpu_itr->affinity, evlist_cpu_itr->cpu.cpu);
evlist_cpu_itr->cpu_map_idx =
perf_cpu_map__idx(evlist_cpu_itr->evsel->core.cpus,
evlist_cpu_itr->cpu); /* * If this CPU isn't in the evsel's cpu map then advance through * the list.
*/ if (evlist_cpu_itr->cpu_map_idx == -1)
evlist_cpu_iterator__next(evlist_cpu_itr);
}
}
evlist__for_each_entry(evlist, pos) { if (!evsel__is_group_leader(pos) || !pos->core.fd) continue; /* If at least one event is enabled, evlist is enabled. */ if (!pos->disabled) returntrue;
} returnfalse;
}
// See explanation in evlist__close() if (!cpu_map__is_dummy(evlist->core.user_requested_cpus)) { if (affinity__setup(&saved_affinity) < 0) return;
affinity = &saved_affinity;
}
/* Disable 'immediate' events last */ for (int imm = 0; imm <= 1; imm++) {
evlist__for_each_cpu(evlist_cpu_itr, evlist, affinity) {
pos = evlist_cpu_itr.evsel; if (evsel__strcmp(pos, evsel_name)) continue; if (pos->disabled || !evsel__is_group_leader(pos) || !pos->core.fd) continue; if (excl_dummy && evsel__is_dummy_event(pos)) continue; if (pos->immediate)
has_imm = true; if (pos->immediate != imm) continue;
evsel__disable_cpu(pos, evlist_cpu_itr.cpu_map_idx);
} if (!has_imm) break;
}
affinity__cleanup(affinity);
evlist__for_each_entry(evlist, pos) { if (evsel__strcmp(pos, evsel_name)) continue; if (!evsel__is_group_leader(pos) || !pos->core.fd) continue; if (excl_dummy && evsel__is_dummy_event(pos)) continue;
pos->disabled = true;
}
/* * If we disabled only single event, we need to check * the enabled state of the evlist manually.
*/ if (evsel_name)
evlist->enabled = evlist__is_enabled(evlist); else
evlist->enabled = false;
}
// See explanation in evlist__close() if (!cpu_map__is_dummy(evlist->core.user_requested_cpus)) { if (affinity__setup(&saved_affinity) < 0) return;
affinity = &saved_affinity;
}
evlist__for_each_cpu(evlist_cpu_itr, evlist, affinity) {
pos = evlist_cpu_itr.evsel; if (evsel__strcmp(pos, evsel_name)) continue; if (!evsel__is_group_leader(pos) || !pos->core.fd) continue; if (excl_dummy && evsel__is_dummy_event(pos)) continue;
evsel__enable_cpu(pos, evlist_cpu_itr.cpu_map_idx);
}
affinity__cleanup(affinity);
evlist__for_each_entry(evlist, pos) { if (evsel__strcmp(pos, evsel_name)) continue; if (!evsel__is_group_leader(pos) || !pos->core.fd) continue; if (excl_dummy && evsel__is_dummy_event(pos)) continue;
pos->disabled = false;
}
/* * Even single event sets the 'enabled' for evlist, * so the toggle can work properly and toggle to * 'disabled' state.
*/
evlist->enabled = true;
}
map = zalloc(evlist->core.nr_mmaps * sizeof(struct mmap)); if (!map) return NULL;
for (i = 0; i < evlist->core.nr_mmaps; i++) { struct perf_mmap *prev = i ? &map[i - 1].core : NULL;
/* * When the perf_mmap() call is made we grab one refcount, plus * one extra to let perf_mmap__consume() get the last * events after all real references (perf_mmap__get()) are * dropped. * * Each PERF_EVENT_IOC_SET_OUTPUT points to this mmap and * thus does perf_mmap__get() on it.
*/
perf_mmap__init(&map[i].core, prev, overwrite, perf_mmap__unmap_cb);
}
unsignedlong perf_event_mlock_kb_in_pages(void)
{ unsignedlong pages; int max;
if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) { /* * Pick a once upon a time good value, i.e. things look * strange since we can't read a sysctl value, but lets not * die yet...
*/
max = 512;
} else {
max -= (page_size / 1024);
}
int __evlist__parse_mmap_pages(unsignedint *mmap_pages, constchar *str)
{ unsignedlong max = UINT_MAX; long pages;
if (max > SIZE_MAX / page_size)
max = SIZE_MAX / page_size;
pages = parse_pages_arg(str, 1, max); if (pages < 0) {
pr_err("Invalid argument for --mmap_pages/-m\n"); return -1;
}
*mmap_pages = pages; return 0;
}
int evlist__parse_mmap_pages(conststruct option *opt, constchar *str, int unset __maybe_unused)
{ return __evlist__parse_mmap_pages(opt->value, str);
}
/** * evlist__mmap_ex - Create mmaps to receive events. * @evlist: list of events * @pages: map length in pages * @overwrite: overwrite older events? * @auxtrace_pages - auxtrace map length in pages * @auxtrace_overwrite - overwrite older auxtrace data? * * If @overwrite is %false the user needs to signal event consumption using * perf_mmap__write_tail(). Using evlist__mmap_read() does this * automatically. * * Similarly, if @auxtrace_overwrite is %false the user needs to signal data * consumption using auxtrace_mmap__write_tail(). * * Return: %0 on success, negative error code otherwise.
*/ int evlist__mmap_ex(struct evlist *evlist, unsignedint pages, unsignedint auxtrace_pages, bool auxtrace_overwrite, int nr_cblocks, int affinity, int flush, int comp_level)
{ /* * Delay setting mp.prot: set it before calling perf_mmap__mmap. * Its value is decided by evsel's write_backward. * So &mp should not be passed through const pointer.
*/ struct mmap_params mp = {
.nr_cblocks = nr_cblocks,
.affinity = affinity,
.flush = flush,
.comp_level = comp_level
}; struct perf_evlist_mmap_ops ops = {
.idx = perf_evlist__mmap_cb_idx,
.get = perf_evlist__mmap_cb_get,
.mmap = perf_evlist__mmap_cb_mmap,
};
/* * If specify '-a' and '--per-thread' to perf record, perf record * will override '--per-thread'. target->per_thread = false and * target->system_wide = true. * * If specify '--per-thread' only to perf record, * target->per_thread = true and target->system_wide = false. * * So target->per_thread && target->system_wide is false. * For perf record, thread_map__new_str doesn't call * thread_map__new_all_cpus. That will keep perf record's * current behavior. * * For perf stat, it allows the case that target->per_thread and * target->system_wide are all true. It means to collect system-wide * per-thread data. thread_map__new_str will call * thread_map__new_all_cpus to enumerate all threads.
*/
threads = thread_map__new_str(target->pid, target->tid, all_threads);
int evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel, struct target *target)
{ struct evsel *evsel; int err = 0;
evlist__for_each_entry(evlist, evsel) { /* * filters only work for tracepoint event, which doesn't have cpu limit. * So evlist and evsel should always be same.
*/ if (evsel->filter) {
err = perf_evsel__apply_filter(&evsel->core, evsel->filter); if (err) {
*err_evsel = evsel; break;
}
}
/* * non-tracepoint events can have BPF filters.
*/ if (!list_empty(&evsel->bpf_filters)) {
err = perf_bpf_filter__prepare(evsel, target); if (err) {
*err_evsel = evsel; break;
}
}
}
return err;
}
int evlist__set_tp_filter(struct evlist *evlist, constchar *filter)
{ struct evsel *evsel; int err = 0;
if (filter == NULL) return -1;
evlist__for_each_entry(evlist, evsel) { if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) continue;
err = evsel__set_filter(evsel, filter); if (err) break;
}
return err;
}
int evlist__append_tp_filter(struct evlist *evlist, constchar *filter)
{ struct evsel *evsel; int err = 0;
if (filter == NULL) return -1;
evlist__for_each_entry(evlist, evsel) { if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) continue;
err = evsel__append_tp_filter(evsel, filter); if (err) break;
}
/* * The abbr name is from A to Z9. If the number of event * which requires the branch counter > MAX_NR_ABBR_NAME, * return NA.
*/ staticvoid evlist__new_abbr_name(char *name)
{ staticint idx; int i = idx / 26;
/* * With perf record core.user_requested_cpus is usually NULL. * Use the old method to handle this for now.
*/ if (!evlist->core.user_requested_cpus ||
cpu_map__is_dummy(evlist->core.user_requested_cpus)) {
evlist__for_each_entry_reverse(evlist, evsel)
evsel__close(evsel); return;
}
/* * Try reading /sys/devices/system/cpu/online to get * an all cpus map. * * FIXME: -ENOMEM is the best we can do here, the cpu_map * code needs an overhaul to properly forward the * error, and we may not want to do that fallback to a * default cpu identity map :-\
*/
cpus = perf_cpu_map__new_online_cpus(); if (!cpus) return -ENOMEM;
threads = perf_thread_map__new_dummy(); if (!threads) {
perf_cpu_map__put(cpus); return -ENOMEM;
}
/* * Change the name of this process not to confuse --exclude-perf users * that sees 'perf' in the window up to the execvp() and thinks that * perf samples are not being excluded.
*/
prctl(PR_SET_NAME, "perf-exec");
/* * Tell the parent we're ready to go
*/
close(child_ready_pipe[1]);
/* * Wait until the parent tells us to go.
*/
ret = read(go_pipe[0], &bf, 1); /* * The parent will ask for the execvp() to be performed by * writing exactly one byte, in workload.cork_fd, usually via * evlist__start_workload(). * * For cancelling the workload without actually running it, * the parent will just close workload.cork_fd, without writing * anything, i.e. read will return zero and we just exit() * here (See evlist__cancel_workload()).
*/ if (ret != 1) { if (ret == -1)
perror("unable to read pipe"); exit(ret);
}
if (target__none(target)) { if (evlist->core.threads == NULL) {
fprintf(stderr, "FATAL: evlist->threads need to be set at this point (%s:%d).\n",
__func__, __LINE__); goto out_close_pipes;
}
perf_thread_map__set_pid(evlist->core.threads, 0, evlist->workload.pid);
}
close(child_ready_pipe[1]);
close(go_pipe[0]); /* * wait for child to settle
*/ if (read(child_ready_pipe[0], &bf, 1) == -1) {
perror("unable to read pipe"); goto out_close_pipes;
}
int evlist__start_workload(struct evlist *evlist)
{ if (evlist->workload.cork_fd >= 0) { char bf = 0; int ret; /* * Remove the cork, let it rip!
*/
ret = write(evlist->workload.cork_fd, &bf, 1); if (ret < 0)
perror("unable to write to pipe");
if (value >= 2) {
printed += scnprintf(buf + printed, size - printed, "For your workloads it needs to be <= 1\nHint:\t");
}
printed += scnprintf(buf + printed, size - printed, "For system wide tracing it needs to be set to -1.\n");
printed += scnprintf(buf + printed, size - printed, "Hint:\tTry: 'sudo sh -c \"echo -1 > /proc/sys/kernel/perf_event_paranoid\"'\n" "Hint:\tThe current value is %d.", value); break; case EINVAL: { struct evsel *first = evlist__first(evlist); int max_freq;
if (sysctl__read_int("kernel/perf_event_max_sample_rate", &max_freq) < 0) goto out_default;
if (first->core.attr.sample_freq < (u64)max_freq) goto out_default;
printed = scnprintf(buf, size, "Error:\t%s.\n" "Hint:\tCheck /proc/sys/kernel/perf_event_max_sample_rate.\n" "Hint:\tThe current value is %d and %" PRIu64 " is being requested.",
emsg, max_freq, first->core.attr.sample_freq); break;
} default:
out_default:
scnprintf(buf, size, "%s", emsg); break;
}
return 0;
}
int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size)
{ char sbuf[STRERR_BUFSIZE], *emsg = str_error_r(err, sbuf, sizeof(sbuf)); int pages_attempted = evlist->core.mmap_len / 1024, pages_max_per_user, printed = 0;
evlist__for_each_entry(evlist, evsel) { if (!evsel->core.attr.exclude_kernel) returnfalse;
}
returntrue;
}
/* * Events in data file are not collect in groups, but we still want * the group display. Set the artificial group and set the leader's * forced_leader flag to notify the display code.
*/ void evlist__force_leader(struct evlist *evlist)
{ if (evlist__nr_groups(evlist) == 0) { struct evsel *leader = evlist__first(evlist);
pr_debug("Weak group for %s/%d failed\n",
leader->name, leader->core.nr_members);
/* * for_each_group_member doesn't work here because it doesn't * include the first entry.
*/
evlist__for_each_entry(evsel_list, c2) { if (c2 == evsel)
is_open = false; if (evsel__has_leader(c2, leader)) { if (is_open && close)
perf_evsel__close(&c2->core); /* * We want to close all members of the group and reopen * them. Some events, like Intel topdown, require being * in a group and so keep these in the group.
*/
evsel__remove_from_group(c2, leader);
/* * Set this for all former members of the group * to indicate they get reopened.
*/
c2->reset_group = true;
}
} /* Reset the leader count if all entries were removed. */ if (leader->core.nr_members == 1)
leader->core.nr_members = 0; return leader;
}
staticint evlist__parse_control_fifo(constchar *str, int *ctl_fd, int *ctl_fd_ack, bool*ctl_fd_close)
{ char *s, *p; int ret = 0, fd;
/* * O_RDWR avoids POLLHUPs which is necessary to allow the other * end of a FIFO to be repeatedly opened and closed.
*/
fd = open(s, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) {
pr_err("Failed to open '%s'\n", s);
ret = -errno; goto out_free;
}
*ctl_fd = fd;
*ctl_fd_close = true;
if (p && *++p) { /* O_RDWR | O_NONBLOCK means the other end need not be open */
fd = open(p, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) {
pr_err("Failed to open '%s'\n", p);
ret = -errno; goto out_free;
}
*ctl_fd_ack = fd;
}
out_free:
free(s); return ret;
}
int evlist__parse_control(constchar *str, int *ctl_fd, int *ctl_fd_ack, bool *ctl_fd_close)
{ char *comma = NULL, *endptr = NULL;
*ctl_fd_close = false;
if (strncmp(str, "fd:", 3)) return evlist__parse_control_fifo(str, ctl_fd, ctl_fd_ack, ctl_fd_close);
void evlist__close_control(int ctl_fd, int ctl_fd_ack, bool *ctl_fd_close)
{ if (*ctl_fd_close) {
*ctl_fd_close = false;
close(ctl_fd); if (ctl_fd_ack >= 0)
close(ctl_fd_ack);
}
}
int evlist__initialize_ctlfd(struct evlist *evlist, int fd, int ack)
{ if (fd == -1) {
pr_debug("Control descriptor is not initialized\n"); return 0;
}
/** * struct event_enable_time - perf record -D/--delay single time range. * @start: start of time range to enable events in milliseconds * @end: end of time range to enable events in milliseconds * * N.B. this structure is also accessed as an array of int.
*/ struct event_enable_time { int start; int end;
};
ret = sscanf(str, fmt, &start, &end, &n); if (ret != 2 || end <= start) return -EINVAL; if (range) {
range->start = start;
range->end = end;
} return n;
}
static ssize_t parse_event_enable_times(constchar *str, struct event_enable_time *range)
{ int incr = !!range; bool first = true;
ssize_t ret, cnt;
for (cnt = 0; *str; cnt++) {
ret = parse_event_enable_time(str, range, first); if (ret < 0) return ret; /* Check no overlap */ if (!first && range && range->start <= range[-1].end) return -EINVAL;
str += ret;
range += incr;
first = false;
} return cnt;
}
/** * struct event_enable_timer - control structure for perf record -D/--delay. * @evlist: event list * @times: time ranges that events are enabled (N.B. this is also accessed as an * array of int) * @times_cnt: number of time ranges * @timerfd: timer file descriptor * @pollfd_pos: position in @evlist array of file descriptors to poll (fdarray) * @times_step: current position in (int *)@times)[], * refer event_enable_timer__process() * * Note, this structure is only used when there are time ranges, not when there * is only an initial delay.
*/ struct event_enable_timer { struct evlist *evlist; struct event_enable_time *times;
size_t times_cnt; int timerfd; int pollfd_pos;
size_t times_step;
};
staticint str_to_delay(constchar *str)
{ char *endptr; long d;
d = strtol(str, &endptr, 10); if (*endptr || d > INT_MAX || d < -1) return 0; return d;
}
int evlist__parse_event_enable_time(struct evlist *evlist, struct record_opts *opts, constchar *str, int unset)
{ enum fdarray_flags flags = fdarray_flag__nonfilterable | fdarray_flag__non_perf_event; struct event_enable_timer *eet;
ssize_t times_cnt;
ssize_t ret; int err;
if (unset) return 0;
opts->target.initial_delay = str_to_delay(str); if (opts->target.initial_delay) return 0;
ret = parse_event_enable_times(str, NULL); if (ret < 0) return ret;
times_cnt = ret; if (times_cnt == 0) return -EINVAL;
eet = zalloc(sizeof(*eet)); if (!eet) return -ENOMEM;
/* * For some platforms, the 'mem-loads' event is required to use * together with 'mem-loads-aux' within a group and 'mem-loads-aux' * must be the group leader. Now we disable this group before reporting * because 'mem-loads-aux' is just an auxiliary event. It doesn't carry * any valid memory load information.
*/
evlist__for_each_entry(evlist, evsel) {
leader = evsel__leader(evsel); if (leader == evsel) continue;
/** * evlist__warn_user_requested_cpus() - Check each evsel against requested CPUs * and warn if the user CPU list is inapplicable for the event's PMU's * CPUs. Not core PMUs list a CPU in sysfs, but this may be overwritten by a * user requested CPU and so any online CPU is applicable. Core PMUs handle * events on the CPUs in their list and otherwise the event isn't supported. * @evlist: The list of events being checked. * @cpu_list: The user provided list of CPUs.
*/ void evlist__warn_user_requested_cpus(struct evlist *evlist, constchar *cpu_list)
{ struct perf_cpu_map *user_requested_cpus; struct evsel *pos;
if (!cpu_list) return;
user_requested_cpus = perf_cpu_map__new(cpu_list); if (!user_requested_cpus) return;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.