diff options
Diffstat (limited to '')
-rw-r--r-- | tools/perf/util/auxtrace.c | 319 |
1 files changed, 253 insertions, 66 deletions
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 42a85c86421d..e2f317063eec 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -26,6 +26,7 @@ #include <linux/list.h> #include <linux/zalloc.h> +#include "config.h" #include "evlist.h" #include "dso.h" #include "map.h" @@ -51,20 +52,20 @@ #include "intel-pt.h" #include "intel-bts.h" #include "arm-spe.h" +#include "hisi-ptt.h" #include "s390-cpumsf.h" #include "util/mmap.h" #include <linux/ctype.h> #include "symbol/kallsyms.h" #include <internal/lib.h> +#include "util/sample.h" /* * Make a group from 'leader' to 'last', requiring that the events were not * already grouped to a different leader. */ -static int perf_evlist__regroup(struct evlist *evlist, - struct evsel *leader, - struct evsel *last) +static int evlist__regroup(struct evlist *evlist, struct evsel *leader, struct evsel *last) { struct evsel *evsel; bool grp; @@ -75,8 +76,8 @@ static int perf_evlist__regroup(struct evlist *evlist, grp = false; evlist__for_each_entry(evlist, evsel) { if (grp) { - if (!(evsel->leader == leader || - (evsel->leader == evsel && + if (!(evsel__leader(evsel) == leader || + (evsel__leader(evsel) == evsel && evsel->core.nr_members <= 1))) return -EINVAL; } else if (evsel == leader) { @@ -89,8 +90,8 @@ static int perf_evlist__regroup(struct evlist *evlist, grp = false; evlist__for_each_entry(evlist, evsel) { if (grp) { - if (evsel->leader != leader) { - evsel->leader = leader; + if (!evsel__has_leader(evsel, leader)) { + evsel__set_leader(evsel, leader); if (leader->core.nr_members < 1) leader->core.nr_members = 1; leader->core.nr_members += 1; @@ -125,18 +126,13 @@ int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, mm->prev = 0; mm->idx = mp->idx; mm->tid = mp->tid; - mm->cpu = mp->cpu; + mm->cpu = mp->cpu.cpu; - if (!mp->len) { + if (!mp->len || !mp->mmap_needed) { mm->base = NULL; return 0; } -#if BITS_PER_LONG != 64 && !defined(HAVE_SYNC_COMPARE_AND_SWAP_SUPPORT) - pr_err("Cannot use AUX area tracing mmaps\n"); - return -1; -#endif - pc->aux_offset = mp->offset; pc->aux_size = mp->len; @@ -175,19 +171,26 @@ void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp, } void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, - struct evlist *evlist, int idx, - bool per_cpu) + struct evlist *evlist, + struct evsel *evsel, int idx) { + bool per_cpu = !perf_cpu_map__has_any_cpu(evlist->core.user_requested_cpus); + + mp->mmap_needed = evsel->needs_auxtrace_mmap; + + if (!mp->mmap_needed) + return; + mp->idx = idx; if (per_cpu) { - mp->cpu = evlist->core.cpus->map[idx]; + mp->cpu = perf_cpu_map__cpu(evlist->core.all_cpus, idx); if (evlist->core.threads) mp->tid = perf_thread_map__pid(evlist->core.threads, 0); else mp->tid = -1; } else { - mp->cpu = -1; + mp->cpu.cpu = -1; mp->tid = perf_thread_map__pid(evlist->core.threads, idx); } } @@ -215,15 +218,20 @@ static struct auxtrace_queue *auxtrace_alloc_queue_array(unsigned int nr_queues) return queue_array; } -int auxtrace_queues__init(struct auxtrace_queues *queues) +int auxtrace_queues__init_nr(struct auxtrace_queues *queues, int nr_queues) { - queues->nr_queues = AUXTRACE_INIT_NR_QUEUES; + queues->nr_queues = nr_queues; queues->queue_array = auxtrace_alloc_queue_array(queues->nr_queues); if (!queues->queue_array) return -ENOMEM; return 0; } +int auxtrace_queues__init(struct auxtrace_queues *queues) +{ + return auxtrace_queues__init_nr(queues, AUXTRACE_INIT_NR_QUEUES); +} + static int auxtrace_queues__grow(struct auxtrace_queues *queues, unsigned int new_nr_queues) { @@ -299,11 +307,7 @@ static int auxtrace_queues__queue_buffer(struct auxtrace_queues *queues, if (!queue->set) { queue->set = true; queue->tid = buffer->tid; - queue->cpu = buffer->cpu; - } else if (buffer->cpu != queue->cpu || buffer->tid != queue->tid) { - pr_err("auxtrace queue conflict: cpu %d, tid %d vs cpu %d, tid %d\n", - queue->cpu, queue->tid, buffer->cpu, buffer->tid); - return -EINVAL; + queue->cpu = buffer->cpu.cpu; } buffer->buffer_nr = queues->next_buffer_nr++; @@ -350,11 +354,11 @@ static int auxtrace_queues__split_buffer(struct auxtrace_queues *queues, return 0; } -static bool filter_cpu(struct perf_session *session, int cpu) +static bool filter_cpu(struct perf_session *session, struct perf_cpu cpu) { unsigned long *cpu_bitmap = session->itrace_synth_opts->cpu_bitmap; - return cpu_bitmap && cpu != -1 && !test_bit(cpu, cpu_bitmap); + return cpu_bitmap && cpu.cpu != -1 && !test_bit(cpu.cpu, cpu_bitmap); } static int auxtrace_queues__add_buffer(struct auxtrace_queues *queues, @@ -410,7 +414,7 @@ int auxtrace_queues__add_event(struct auxtrace_queues *queues, struct auxtrace_buffer buffer = { .pid = -1, .tid = event->auxtrace.tid, - .cpu = event->auxtrace.cpu, + .cpu = { event->auxtrace.cpu }, .data_offset = data_offset, .offset = event->auxtrace.offset, .reference = event->auxtrace.reference, @@ -640,13 +644,29 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr, break; } - if (itr) + if (itr && itr->parse_snapshot_options) return itr->parse_snapshot_options(itr, opts, str); pr_err("No AUX area tracing to snapshot\n"); return -EINVAL; } +static int evlist__enable_event_idx(struct evlist *evlist, struct evsel *evsel, int idx) +{ + bool per_cpu_mmaps = !perf_cpu_map__has_any_cpu(evlist->core.user_requested_cpus); + + if (per_cpu_mmaps) { + struct perf_cpu evlist_cpu = perf_cpu_map__cpu(evlist->core.all_cpus, idx); + int cpu_map_idx = perf_cpu_map__idx(evsel->core.cpus, evlist_cpu); + + if (cpu_map_idx == -1) + return -EINVAL; + return perf_evsel__enable_cpu(&evsel->core, cpu_map_idx); + } + + return perf_evsel__enable_thread(&evsel->core, idx); +} + int auxtrace_record__read_finish(struct auxtrace_record *itr, int idx) { struct evsel *evsel; @@ -658,8 +678,7 @@ int auxtrace_record__read_finish(struct auxtrace_record *itr, int idx) if (evsel->core.attr.type == itr->pmu->type) { if (evsel->disabled) return 0; - return perf_evlist__enable_event_idx(itr->evlist, evsel, - idx); + return evlist__enable_event_idx(itr->evlist, evsel, idx); } } return -EINVAL; @@ -776,7 +795,7 @@ no_opt: evsel->core.attr.aux_sample_size = term->val.aux_sample_size; /* If possible, group with the AUX event */ if (aux_evsel && evsel->core.attr.aux_sample_size) - perf_evlist__regroup(evlist, aux_evsel, evsel); + evlist__regroup(evlist, aux_evsel, evsel); } } @@ -791,6 +810,21 @@ no_opt: return auxtrace_validate_aux_sample_size(evlist, opts); } +void auxtrace_regroup_aux_output(struct evlist *evlist) +{ + struct evsel *evsel, *aux_evsel = NULL; + struct evsel_config_term *term; + + evlist__for_each_entry(evlist, evsel) { + if (evsel__is_aux_event(evsel)) + aux_evsel = evsel; + term = evsel__get_config_term(evsel, AUX_OUTPUT); + /* If possible, group with the AUX event */ + if (term && aux_evsel) + evlist__regroup(evlist, aux_evsel, evsel); + } +} + struct auxtrace_record *__weak auxtrace_record__init(struct evlist *evlist __maybe_unused, int *err) { @@ -1017,7 +1051,7 @@ struct auxtrace_queue *auxtrace_queues__sample_queue(struct auxtrace_queues *que if (!id) return NULL; - sid = perf_evlist__id2sid(session->evlist, id); + sid = evlist__id2sid(session->evlist, id); if (!sid) return NULL; @@ -1047,7 +1081,7 @@ int auxtrace_queues__add_sample(struct auxtrace_queues *queues, if (!id) return -EINVAL; - sid = perf_evlist__id2sid(session->evlist, id); + sid = evlist__id2sid(session->evlist, id); if (!sid) return -ENOENT; @@ -1082,7 +1116,7 @@ static int auxtrace_queue_data_cb(struct perf_session *session, if (!qd->samples || event->header.type != PERF_RECORD_SAMPLE) return 0; - err = perf_evlist__parse_sample(session->evlist, event, &sample); + err = evlist__parse_sample(session->evlist, event, &sample); if (err) return err; @@ -1104,6 +1138,9 @@ int auxtrace_queue_data(struct perf_session *session, bool samples, bool events) if (auxtrace__dont_decode(session)) return 0; + if (perf_data__is_pipe(session->data)) + return 0; + if (!session->auxtrace || !session->auxtrace->queue_data) return -EINVAL; @@ -1112,8 +1149,9 @@ int auxtrace_queue_data(struct perf_session *session, bool samples, bool events) auxtrace_queue_data_cb, &qd); } -void *auxtrace_buffer__get_data(struct auxtrace_buffer *buffer, int fd) +void *auxtrace_buffer__get_data_rw(struct auxtrace_buffer *buffer, int fd, bool rw) { + int prot = rw ? PROT_READ | PROT_WRITE : PROT_READ; size_t adj = buffer->data_offset & (page_size - 1); size_t size = buffer->size + adj; off_t file_offset = buffer->data_offset - adj; @@ -1122,7 +1160,7 @@ void *auxtrace_buffer__get_data(struct auxtrace_buffer *buffer, int fd) if (buffer->data) return buffer->data; - addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, file_offset); + addr = mmap(NULL, size, prot, MAP_SHARED, fd, file_offset); if (addr == MAP_FAILED) return NULL; @@ -1162,9 +1200,10 @@ void auxtrace_buffer__free(struct auxtrace_buffer *buffer) free(buffer); } -void auxtrace_synth_error(struct perf_record_auxtrace_error *auxtrace_error, int type, - int code, int cpu, pid_t pid, pid_t tid, u64 ip, - const char *msg, u64 timestamp) +void auxtrace_synth_guest_error(struct perf_record_auxtrace_error *auxtrace_error, int type, + int code, int cpu, pid_t pid, pid_t tid, u64 ip, + const char *msg, u64 timestamp, + pid_t machine_pid, int vcpu) { size_t size; @@ -1180,12 +1219,26 @@ void auxtrace_synth_error(struct perf_record_auxtrace_error *auxtrace_error, int auxtrace_error->ip = ip; auxtrace_error->time = timestamp; strlcpy(auxtrace_error->msg, msg, MAX_AUXTRACE_ERROR_MSG); - - size = (void *)auxtrace_error->msg - (void *)auxtrace_error + - strlen(auxtrace_error->msg) + 1; + if (machine_pid) { + auxtrace_error->fmt = 2; + auxtrace_error->machine_pid = machine_pid; + auxtrace_error->vcpu = vcpu; + size = sizeof(*auxtrace_error); + } else { + size = (void *)auxtrace_error->msg - (void *)auxtrace_error + + strlen(auxtrace_error->msg) + 1; + } auxtrace_error->header.size = PERF_ALIGN(size, sizeof(u64)); } +void auxtrace_synth_error(struct perf_record_auxtrace_error *auxtrace_error, int type, + int code, int cpu, pid_t pid, pid_t tid, u64 ip, + const char *msg, u64 timestamp) +{ + auxtrace_synth_guest_error(auxtrace_error, type, code, cpu, pid, tid, + ip, msg, timestamp, 0, -1); +} + int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, struct perf_tool *tool, struct perf_session *session, @@ -1222,11 +1275,11 @@ static void unleader_evsel(struct evlist *evlist, struct evsel *leader) /* Find new leader for the group */ evlist__for_each_entry(evlist, evsel) { - if (evsel->leader != leader || evsel == leader) + if (!evsel__has_leader(evsel, leader) || evsel == leader) continue; if (!new_leader) new_leader = evsel; - evsel->leader = new_leader; + evsel__set_leader(evsel, new_leader); } /* Update group information */ @@ -1277,6 +1330,9 @@ int perf_event__process_auxtrace_info(struct perf_session *session, case PERF_AUXTRACE_S390_CPUMSF: err = s390_cpumsf_process_auxtrace_info(event, session); break; + case PERF_AUXTRACE_HISI_PTT: + err = hisi_ptt_process_auxtrace_info(event, session); + break; case PERF_AUXTRACE_UNKNOWN: default: return -EINVAL; @@ -1329,10 +1385,12 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts, synth_opts->ptwrites = true; synth_opts->pwr_events = true; synth_opts->other_events = true; + synth_opts->intr_events = true; synth_opts->errors = true; synth_opts->flc = true; synth_opts->llc = true; synth_opts->tlb = true; + synth_opts->mem = true; synth_opts->remote_access = true; if (no_sample) { @@ -1341,6 +1399,7 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts, synth_opts->calls = true; } else { synth_opts->instructions = true; + synth_opts->cycles = true; synth_opts->period_type = PERF_ITRACE_DEFAULT_PERIOD_TYPE; synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD; } @@ -1390,19 +1449,29 @@ static int get_flags(const char **ptr, unsigned int *plus_flags, unsigned int *m } } +#define ITRACE_DFLT_LOG_ON_ERROR_SZ 16384 + +static unsigned int itrace_log_on_error_size(void) +{ + unsigned int sz = 0; + + perf_config_scan("itrace.debug-log-buffer-size", "%u", &sz); + return sz ?: ITRACE_DFLT_LOG_ON_ERROR_SZ; +} + /* * Please check tools/perf/Documentation/perf-script.txt for information * about the options parsed here, which is introduced after this cset, * when support in 'perf script' for these options is introduced. */ -int itrace_parse_synth_opts(const struct option *opt, const char *str, - int unset) +int itrace_do_parse_synth_opts(struct itrace_synth_opts *synth_opts, + const char *str, int unset) { - struct itrace_synth_opts *synth_opts = opt->value; const char *p; char *endptr; bool period_type_set = false; bool period_set = false; + bool iy = false; synth_opts->set = true; @@ -1420,7 +1489,12 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str, for (p = str; *p;) { switch (*p++) { case 'i': - synth_opts->instructions = true; + case 'y': + iy = true; + if (p[-1] == 'y') + synth_opts->cycles = true; + else + synth_opts->instructions = true; while (*p == ' ' || *p == ',') p += 1; if (isdigit(*p)) { @@ -1475,6 +1549,9 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str, case 'o': synth_opts->other_events = true; break; + case 'I': + synth_opts->intr_events = true; + break; case 'e': synth_opts->errors = true; if (get_flags(&p, &synth_opts->error_plus_flags, @@ -1486,6 +1563,8 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str, if (get_flags(&p, &synth_opts->log_plus_flags, &synth_opts->log_minus_flags)) goto out_err; + if (synth_opts->log_plus_flags & AUXTRACE_LOG_FLG_ON_ERROR) + synth_opts->log_on_error_size = itrace_log_on_error_size(); break; case 'c': synth_opts->branches = true; @@ -1554,9 +1633,21 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str, case 'a': synth_opts->remote_access = true; break; + case 'M': + synth_opts->mem = true; + break; case 'q': synth_opts->quick += 1; break; + case 'A': + synth_opts->approx_ipc = true; + break; + case 'Z': + synth_opts->timeless_decoding = true; + break; + case 'T': + synth_opts->use_timestamp = true; + break; case ' ': case ',': break; @@ -1565,7 +1656,7 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str, } } out: - if (synth_opts->instructions) { + if (iy) { if (!period_type_set) synth_opts->period_type = PERF_ITRACE_DEFAULT_PERIOD_TYPE; @@ -1580,6 +1671,11 @@ out_err: return -EINVAL; } +int itrace_parse_synth_opts(const struct option *opt, const char *str, int unset) +{ + return itrace_do_parse_synth_opts(opt->value, str, unset); +} + static const char * const auxtrace_error_type_name[] = { [PERF_AUXTRACE_ERROR_ITRACE] = "instruction trace", }; @@ -1617,6 +1713,9 @@ size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp) if (!e->fmt) msg = (const char *)&e->time; + if (e->fmt >= 2 && e->machine_pid) + ret += fprintf(fp, " machine_pid %d vcpu %d", e->machine_pid, e->vcpu); + ret += fprintf(fp, " cpu %d pid %d tid %d ip %#"PRI_lx64" code %u: %s\n", e->cpu, e->pid, e->tid, e->ip, e->code, msg); return ret; @@ -1654,6 +1753,82 @@ int perf_event__process_auxtrace_error(struct perf_session *session, return 0; } +/* + * In the compat mode kernel runs in 64-bit and perf tool runs in 32-bit mode, + * 32-bit perf tool cannot access 64-bit value atomically, which might lead to + * the issues caused by the below sequence on multiple CPUs: when perf tool + * accesses either the load operation or the store operation for 64-bit value, + * on some architectures the operation is divided into two instructions, one + * is for accessing the low 32-bit value and another is for the high 32-bit; + * thus these two user operations can give the kernel chances to access the + * 64-bit value, and thus leads to the unexpected load values. + * + * kernel (64-bit) user (32-bit) + * + * if (LOAD ->aux_tail) { --, LOAD ->aux_head_lo + * STORE $aux_data | ,---> + * FLUSH $aux_data | | LOAD ->aux_head_hi + * STORE ->aux_head --|-------` smp_rmb() + * } | LOAD $data + * | smp_mb() + * | STORE ->aux_tail_lo + * `-----------> + * STORE ->aux_tail_hi + * + * For this reason, it's impossible for the perf tool to work correctly when + * the AUX head or tail is bigger than 4GB (more than 32 bits length); and we + * can not simply limit the AUX ring buffer to less than 4GB, the reason is + * the pointers can be increased monotonically, whatever the buffer size it is, + * at the end the head and tail can be bigger than 4GB and carry out to the + * high 32-bit. + * + * To mitigate the issues and improve the user experience, we can allow the + * perf tool working in certain conditions and bail out with error if detect + * any overflow cannot be handled. + * + * For reading the AUX head, it reads out the values for three times, and + * compares the high 4 bytes of the values between the first time and the last + * time, if there has no change for high 4 bytes injected by the kernel during + * the user reading sequence, it's safe for use the second value. + * + * When compat_auxtrace_mmap__write_tail() detects any carrying in the high + * 32 bits, it means there have two store operations in user space and it cannot + * promise the atomicity for 64-bit write, so return '-1' in this case to tell + * the caller an overflow error has happened. + */ +u64 __weak compat_auxtrace_mmap__read_head(struct auxtrace_mmap *mm) +{ + struct perf_event_mmap_page *pc = mm->userpg; + u64 first, second, last; + u64 mask = (u64)(UINT32_MAX) << 32; + + do { + first = READ_ONCE(pc->aux_head); + /* Ensure all reads are done after we read the head */ + smp_rmb(); + second = READ_ONCE(pc->aux_head); + /* Ensure all reads are done after we read the head */ + smp_rmb(); + last = READ_ONCE(pc->aux_head); + } while ((first & mask) != (last & mask)); + + return second; +} + +int __weak compat_auxtrace_mmap__write_tail(struct auxtrace_mmap *mm, u64 tail) +{ + struct perf_event_mmap_page *pc = mm->userpg; + u64 mask = (u64)(UINT32_MAX) << 32; + + if (tail & mask) + return -1; + + /* Ensure all reads are done before we write the tail out */ + smp_mb(); + WRITE_ONCE(pc->aux_tail, tail); + return 0; +} + static int __auxtrace_mmap__read(struct mmap *map, struct auxtrace_record *itr, struct perf_tool *tool, process_auxtrace_t fn, @@ -1665,15 +1840,13 @@ static int __auxtrace_mmap__read(struct mmap *map, size_t size, head_off, old_off, len1, len2, padding; union perf_event ev; void *data1, *data2; + int kernel_is_64_bit = perf_env__kernel_is_64_bit(evsel__env(NULL)); - if (snapshot) { - head = auxtrace_mmap__read_snapshot_head(mm); - if (auxtrace_record__find_snapshot(itr, mm->idx, mm, data, - &head, &old)) - return -1; - } else { - head = auxtrace_mmap__read_head(mm); - } + head = auxtrace_mmap__read_head(mm, kernel_is_64_bit); + + if (snapshot && + auxtrace_record__find_snapshot(itr, mm->idx, mm, data, &head, &old)) + return -1; if (old == head) return 0; @@ -1752,10 +1925,13 @@ static int __auxtrace_mmap__read(struct mmap *map, mm->prev = head; if (!snapshot) { - auxtrace_mmap__write_tail(mm, head); - if (itr->read_finish) { - int err; + int err; + + err = auxtrace_mmap__write_tail(mm, head, kernel_is_64_bit); + if (err < 0) + return err; + if (itr->read_finish) { err = itr->read_finish(itr, mm->idx); if (err < 0) return err; @@ -2168,11 +2344,19 @@ struct sym_args { bool near; }; +static bool kern_sym_name_match(const char *kname, const char *name) +{ + size_t n = strlen(name); + + return !strcmp(kname, name) || + (!strncmp(kname, name, n) && kname[n] == '\t'); +} + static bool kern_sym_match(struct sym_args *args, const char *name, char type) { /* A function with the same name, and global or the n'th found or any */ return kallsyms__is_function(type) && - !strcmp(name, args->name) && + kern_sym_name_match(name, args->name) && ((args->global && isupper(type)) || (args->selected && ++(args->cnt) == args->idx) || (!args->global && !args->selected)); @@ -2275,6 +2459,7 @@ static int find_entire_kern_cb(void *arg, const char *name __maybe_unused, char type, u64 start) { struct sym_args *args = arg; + u64 size; if (!kallsyms__is_function(type)) return 0; @@ -2284,7 +2469,9 @@ static int find_entire_kern_cb(void *arg, const char *name __maybe_unused, args->start = start; } /* Don't know exactly where the kernel ends, so we add a page */ - args->size = round_up(start, page_size) + page_size - args->start; + size = round_up(start, page_size) + page_size - args->start; + if (size > args->size) + args->size = size; return 0; } @@ -2383,7 +2570,7 @@ static struct dso *load_dso(const char *name) if (map__load(map) < 0) pr_err("File '%s' not found or has no symbols.\n", name); - dso = dso__get(map->dso); + dso = dso__get(map__dso(map)); map__put(map); @@ -2445,7 +2632,7 @@ static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start, *size = sym->start - *start; if (idx > 0) { if (*size) - return 1; + return 0; } else if (dso_sym_match(sym, sym_name, &cnt, idx)) { print_duplicate_syms(dso, sym_name); return -EINVAL; @@ -2472,7 +2659,7 @@ static int addr_filter__entire_dso(struct addr_filter *filt, struct dso *dso) } filt->addr = 0; - filt->size = dso->data.file_size; + filt->size = dso__data(dso)->file_size; return 0; } |