perf/urgent fixes:

User visible:
 
 - Fix segfault pressing -> in 'perf top' with no hist entries (Wang Nan)
 
    E.g:
 	perf top -e page-faults --pid 11400 # 11400 generates no page-fault
 
 - Fix propagation of thread and cpu maps, that got broken when doing incomplete
   changes to better support events with a PMU cpu mask, leading to Intel PT to
   fail with an error like:
 
   $ perf record -e intel_pt//u uname
   Error: The sys_perf_event_open() syscall returned with
           22 (Invalid argument) for event (sched:sched_switch).
 
   Because intel_pt adds that sched:sched_switch evsel to the evlist after the
   thread/cpu maps were propagated to the evsels, fix it (Adrian Hunter)
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJV+DJ5AAoJENZQFvNTUqpADsMQAJPtLUkGWMVxIpSEgz1qahWm
 NY29T5tK7VpuG0BwIBSdZCpnfytEzyLXtDeY/y0bYHn9KPczrN2EBXhdV8DGar9J
 qG6rb/GvfxmFfimSLlQYnSF9Up5XagapF+T3fe6K4IOEpGy7t3Q3YbIXEBHPLC5H
 trh2/mEcfMUu55Dlmk73E+99yPnjkNfuspKHRZSGY1tOxhTWgFi2uFCH+abK9Xb/
 EmTa7xp5kki3NbttN5kryJFlngy3mDzADJP+y9VdvqqrXwhSfWkNnt8SBY1V++D4
 GzsR4NWPLDP99aBVZVtAiNKVN7wZ8mm6H0+nfCBOJVMgfaDITfdAdH7pG3v86/aS
 9ZT1YXZTi8l/UcvVmbVl3ikLSzWGtmAS3szy2//cEN0D8T1vRsderwd8MFs2OBgf
 9P/dCTLtriWM4YaSIZ1obOWC1tFRJz8yD3F4BCzx/1i5hrNgI0opR71iMFsx2la2
 Cb1awkVDl4e+cF/ucRJ0242LohNv3a3PaTniBMRiBiMN3kcU+Sqoegq1ysj1Zb6G
 WuQCtf7qRCJmbZRP8AWHsEM0UkEYmhicgYKzx9BGWyBgvJcdifZ4KGHA6Wd5HPyx
 JwXzBt6SoFLmK4IQi87nW727BQrfVYRi++IPusgkE0fRQNM7AWah8zJCVtraGRrQ
 T3NLb7T2FRL56fX+DGpj
 =Sfm6
 -----END PGP SIGNATURE-----

Merge tag 'perf-urgent-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent

Pull perf/urgent fixes from Arnaldo Carvalho de Melo:

- Fix segfault pressing -> in 'perf top' with no hist entries. (Wang Nan)

   E.g:
	perf top -e page-faults --pid 11400 # 11400 generates no page-fault

- Fix propagation of thread and cpu maps, that got broken when doing incomplete
  changes to better support events with a PMU cpu mask, leading to Intel PT to
  fail with an error like:

    $ perf record -e intel_pt//u uname
    Error: The sys_perf_event_open() syscall returned with
              22 (Invalid argument) for event (sched:sched_switch).

  Because intel_pt adds that sched:sched_switch evsel to the evlist after the
  thread/cpu maps were propagated to the evsels, fix it. (Adrian Hunter)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2015-09-16 09:06:54 +02:00
commit f6cf87f748
8 changed files with 124 additions and 80 deletions

View File

@ -34,6 +34,8 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
.disabled = 1,
.freq = 1,
};
struct cpu_map *cpus;
struct thread_map *threads;
attr.sample_freq = 500;
@ -50,14 +52,19 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
}
perf_evlist__add(evlist, evsel);
evlist->cpus = cpu_map__dummy_new();
evlist->threads = thread_map__new_by_tid(getpid());
if (!evlist->cpus || !evlist->threads) {
cpus = cpu_map__dummy_new();
threads = thread_map__new_by_tid(getpid());
if (!cpus || !threads) {
err = -ENOMEM;
pr_debug("Not enough memory to create thread/cpu maps\n");
goto out_delete_evlist;
goto out_free_maps;
}
perf_evlist__set_maps(evlist, cpus, threads);
cpus = NULL;
threads = NULL;
if (perf_evlist__open(evlist)) {
const char *knob = "/proc/sys/kernel/perf_event_max_sample_rate";
@ -107,6 +114,9 @@ next_event:
err = -1;
}
out_free_maps:
cpu_map__put(cpus);
thread_map__put(threads);
out_delete_evlist:
perf_evlist__delete(evlist);
return err;

View File

@ -43,6 +43,8 @@ int test__task_exit(void)
};
const char *argv[] = { "true", NULL };
char sbuf[STRERR_BUFSIZE];
struct cpu_map *cpus;
struct thread_map *threads;
signal(SIGCHLD, sig_handler);
@ -58,14 +60,19 @@ int test__task_exit(void)
* perf_evlist__prepare_workload we'll fill in the only thread
* we're monitoring, the one forked there.
*/
evlist->cpus = cpu_map__dummy_new();
evlist->threads = thread_map__new_by_tid(-1);
if (!evlist->cpus || !evlist->threads) {
cpus = cpu_map__dummy_new();
threads = thread_map__new_by_tid(-1);
if (!cpus || !threads) {
err = -ENOMEM;
pr_debug("Not enough memory to create thread/cpu maps\n");
goto out_delete_evlist;
goto out_free_maps;
}
perf_evlist__set_maps(evlist, cpus, threads);
cpus = NULL;
threads = NULL;
err = perf_evlist__prepare_workload(evlist, &target, argv, false,
workload_exec_failed_signal);
if (err < 0) {
@ -114,6 +121,9 @@ retry:
err = -1;
}
out_free_maps:
cpu_map__put(cpus);
thread_map__put(threads);
out_delete_evlist:
perf_evlist__delete(evlist);
return err;

View File

@ -1968,7 +1968,8 @@ skip_annotation:
&options[nr_options], dso);
nr_options += add_map_opt(browser, &actions[nr_options],
&options[nr_options],
browser->selection->map);
browser->selection ?
browser->selection->map : NULL);
/* perf script support */
if (browser->he_selection) {
@ -1976,6 +1977,15 @@ skip_annotation:
&actions[nr_options],
&options[nr_options],
thread, NULL);
/*
* Note that browser->selection != NULL
* when browser->he_selection is not NULL,
* so we don't need to check browser->selection
* before fetching browser->selection->sym like what
* we do before fetching browser->selection->map.
*
* See hist_browser__show_entry.
*/
nr_options += add_script_opt(browser,
&actions[nr_options],
&options[nr_options],

View File

@ -124,6 +124,33 @@ void perf_evlist__delete(struct perf_evlist *evlist)
free(evlist);
}
static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
struct perf_evsel *evsel)
{
/*
* We already have cpus for evsel (via PMU sysfs) so
* keep it, if there's no target cpu list defined.
*/
if (!evsel->own_cpus || evlist->has_user_cpus) {
cpu_map__put(evsel->cpus);
evsel->cpus = cpu_map__get(evlist->cpus);
} else if (evsel->cpus != evsel->own_cpus) {
cpu_map__put(evsel->cpus);
evsel->cpus = cpu_map__get(evsel->own_cpus);
}
thread_map__put(evsel->threads);
evsel->threads = thread_map__get(evlist->threads);
}
static void perf_evlist__propagate_maps(struct perf_evlist *evlist)
{
struct perf_evsel *evsel;
evlist__for_each(evlist, evsel)
__perf_evlist__propagate_maps(evlist, evsel);
}
void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
{
entry->evlist = evlist;
@ -133,18 +160,19 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
if (!evlist->nr_entries++)
perf_evlist__set_id_pos(evlist);
__perf_evlist__propagate_maps(evlist, entry);
}
void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
struct list_head *list,
int nr_entries)
struct list_head *list)
{
bool set_id_pos = !evlist->nr_entries;
struct perf_evsel *evsel, *temp;
list_splice_tail(list, &evlist->entries);
evlist->nr_entries += nr_entries;
if (set_id_pos)
perf_evlist__set_id_pos(evlist);
__evlist__for_each_safe(list, temp, evsel) {
list_del_init(&evsel->node);
perf_evlist__add(evlist, evsel);
}
}
void __perf_evlist__set_leader(struct list_head *list)
@ -210,7 +238,7 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist,
list_add_tail(&evsel->node, &head);
}
perf_evlist__splice_list_tail(evlist, &head, nr_attrs);
perf_evlist__splice_list_tail(evlist, &head);
return 0;
@ -1103,71 +1131,56 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
return perf_evlist__mmap_ex(evlist, pages, overwrite, 0, false);
}
static int perf_evlist__propagate_maps(struct perf_evlist *evlist,
bool has_user_cpus)
{
struct perf_evsel *evsel;
evlist__for_each(evlist, evsel) {
/*
* We already have cpus for evsel (via PMU sysfs) so
* keep it, if there's no target cpu list defined.
*/
if (evsel->cpus && has_user_cpus)
cpu_map__put(evsel->cpus);
if (!evsel->cpus || has_user_cpus)
evsel->cpus = cpu_map__get(evlist->cpus);
evsel->threads = thread_map__get(evlist->threads);
if ((evlist->cpus && !evsel->cpus) ||
(evlist->threads && !evsel->threads))
return -ENOMEM;
}
return 0;
}
int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
{
evlist->threads = thread_map__new_str(target->pid, target->tid,
target->uid);
struct cpu_map *cpus;
struct thread_map *threads;
if (evlist->threads == NULL)
threads = thread_map__new_str(target->pid, target->tid, target->uid);
if (!threads)
return -1;
if (target__uses_dummy_map(target))
evlist->cpus = cpu_map__dummy_new();
cpus = cpu_map__dummy_new();
else
evlist->cpus = cpu_map__new(target->cpu_list);
cpus = cpu_map__new(target->cpu_list);
if (evlist->cpus == NULL)
if (!cpus)
goto out_delete_threads;
return perf_evlist__propagate_maps(evlist, !!target->cpu_list);
evlist->has_user_cpus = !!target->cpu_list;
perf_evlist__set_maps(evlist, cpus, threads);
return 0;
out_delete_threads:
thread_map__put(evlist->threads);
evlist->threads = NULL;
thread_map__put(threads);
return -1;
}
int perf_evlist__set_maps(struct perf_evlist *evlist,
struct cpu_map *cpus,
struct thread_map *threads)
void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus,
struct thread_map *threads)
{
if (evlist->cpus)
/*
* Allow for the possibility that one or another of the maps isn't being
* changed i.e. don't put it. Note we are assuming the maps that are
* being applied are brand new and evlist is taking ownership of the
* original reference count of 1. If that is not the case it is up to
* the caller to increase the reference count.
*/
if (cpus != evlist->cpus) {
cpu_map__put(evlist->cpus);
evlist->cpus = cpus;
}
evlist->cpus = cpus;
if (evlist->threads)
if (threads != evlist->threads) {
thread_map__put(evlist->threads);
evlist->threads = threads;
}
evlist->threads = threads;
return perf_evlist__propagate_maps(evlist, false);
perf_evlist__propagate_maps(evlist);
}
int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **err_evsel)
@ -1387,6 +1400,8 @@ void perf_evlist__close(struct perf_evlist *evlist)
static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist)
{
struct cpu_map *cpus;
struct thread_map *threads;
int err = -ENOMEM;
/*
@ -1398,20 +1413,19 @@ static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist)
* error, and we may not want to do that fallback to a
* default cpu identity map :-\
*/
evlist->cpus = cpu_map__new(NULL);
if (evlist->cpus == NULL)
cpus = cpu_map__new(NULL);
if (!cpus)
goto out;
evlist->threads = thread_map__new_dummy();
if (evlist->threads == NULL)
goto out_free_cpus;
threads = thread_map__new_dummy();
if (!threads)
goto out_put;
err = 0;
perf_evlist__set_maps(evlist, cpus, threads);
out:
return err;
out_free_cpus:
cpu_map__put(evlist->cpus);
evlist->cpus = NULL;
out_put:
cpu_map__put(cpus);
goto out;
}

View File

@ -42,6 +42,7 @@ struct perf_evlist {
int nr_mmaps;
bool overwrite;
bool enabled;
bool has_user_cpus;
size_t mmap_len;
int id_pos;
int is_pos;
@ -155,9 +156,8 @@ int perf_evlist__enable_event_idx(struct perf_evlist *evlist,
void perf_evlist__set_selected(struct perf_evlist *evlist,
struct perf_evsel *evsel);
int perf_evlist__set_maps(struct perf_evlist *evlist,
struct cpu_map *cpus,
struct thread_map *threads);
void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus,
struct thread_map *threads);
int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target);
int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **err_evsel);
@ -179,8 +179,7 @@ bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist);
bool perf_evlist__valid_read_format(struct perf_evlist *evlist);
void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
struct list_head *list,
int nr_entries);
struct list_head *list);
static inline struct perf_evsel *perf_evlist__first(struct perf_evlist *evlist)
{

View File

@ -1033,6 +1033,7 @@ void perf_evsel__exit(struct perf_evsel *evsel)
perf_evsel__free_config_terms(evsel);
close_cgroup(evsel->cgrp);
cpu_map__put(evsel->cpus);
cpu_map__put(evsel->own_cpus);
thread_map__put(evsel->threads);
zfree(&evsel->group_name);
zfree(&evsel->name);

View File

@ -98,6 +98,7 @@ struct perf_evsel {
struct cgroup_sel *cgrp;
void *handler;
struct cpu_map *cpus;
struct cpu_map *own_cpus;
struct thread_map *threads;
unsigned int sample_size;
int id_pos;

View File

@ -287,8 +287,8 @@ __add_event(struct list_head *list, int *idx,
if (!evsel)
return NULL;
if (cpus)
evsel->cpus = cpu_map__get(cpus);
evsel->cpus = cpu_map__get(cpus);
evsel->own_cpus = cpu_map__get(cpus);
if (name)
evsel->name = strdup(name);
@ -1140,10 +1140,9 @@ int parse_events(struct perf_evlist *evlist, const char *str,
ret = parse_events__scanner(str, &data, PE_START_EVENTS);
perf_pmu__parse_cleanup();
if (!ret) {
int entries = data.idx - evlist->nr_entries;
struct perf_evsel *last;
perf_evlist__splice_list_tail(evlist, &data.list, entries);
perf_evlist__splice_list_tail(evlist, &data.list);
evlist->nr_groups += data.nr_groups;
last = perf_evlist__last(evlist);
last->cmdline_group_boundary = true;