Em Fri, Sep 04, 2015 at 03:15:54PM +0300, Adrian Hunter escreveu:
> A perf_evsel is a selected event containing the perf_event_attr
> that is passed to perf_event_open().  A perf_evlist is a collection
> of perf_evsel's.  A perf_evlist also has lists of cpus and threads
> (pids) on which to open the event.  These lists are called 'maps'
> and this patch is about how those 'maps' are propagated from the
>> perf_evlist to the perf_evsels.

Can't this be broken up in multiple patches, for instance this:

 int perf_evlist__create_maps(struct perf_evlist *evlist, struct
 target *target)
 {
+     if (evlist->threads || evlist->cpus)
+             return -1;
+

Looks like a fix that could be separated. Also FOO__propagate(.., false)
to do the opposite of propagate seems confusing, how about
FOO__unpropagate() if that verb exists? :-)


Also, when unpropagating, you do:

             if (evsel->cpus == evlist->cpus) {
                     cpu_map__put(evsel->cpus);
                     evsel->cpus = NULL;
             }

What if the PMU code _set_ it to the same cpus as in evlist->cpus, but
now we're unpropagating to set to another CPU, in this case we will be
changing the PMU setting with a new one. I.e. when a PMU sets it it
should be sticky, no?

I.e. we would have to know, in the evsel, if evsel->cpus was set by the
PMU or any other future entity expecting this behaviour, so that we
don't touch it, i.e. testing (evsel->cpus != evlist->cpus) when
unpropagating doesn't seem to cut, right?

- Arnaldo

 
> Originally perf_evsels did not have their own thread 'maps', and
> their cpu 'maps' were only used for checking.  Then threads were
> added by:
> 
>       578e91ec04d0 ("perf evlist: Propagate thread maps through the evlist")
> 
> And then 'perf record' was changed to open events using the 'maps'
> from perf_evsel not perf_evlist anymore, by:
> 
>       d988d5ee6478 ("perf evlist: Open event on evsel cpus and threads")
> 
> That exposed situations where the 'maps' were not getting propagated
> correctly, such as when perf_evsel are added to the perf_evlist later.
> This patch ensures 'maps' get propagated in that case, and also if 'maps'
> are subsequently changed or set to NULL by perf_evlist__set_maps()
> and perf_evlist__create_syswide_maps().
> 
> Signed-off-by: Adrian Hunter <adrian.hun...@intel.com>
> ---
>  tools/perf/util/evlist.c | 129 
> ++++++++++++++++++++++++++++++++---------------
>  tools/perf/util/evlist.h |   1 +
>  2 files changed, 88 insertions(+), 42 deletions(-)
> 
> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
> index d51a5200c8af..00128cf7655b 100644
> --- a/tools/perf/util/evlist.c
> +++ b/tools/perf/util/evlist.c
> @@ -124,8 +124,49 @@ void perf_evlist__delete(struct perf_evlist *evlist)
>       free(evlist);
>  }
>  
> +static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
> +                                       struct perf_evsel *evsel,
> +                                       bool propagate)
> +{
> +     if (propagate) {
> +             /*
> +              * We already have cpus for evsel (via PMU sysfs) so
> +              * keep it, if there's no target cpu list defined.
> +              */
> +             if ((!evsel->cpus || evlist->has_user_cpus) &&
> +                 evsel->cpus != evlist->cpus) {
> +                     cpu_map__put(evsel->cpus);
> +                     evsel->cpus = cpu_map__get(evlist->cpus);
> +             }
> +
> +             if (!evsel->threads)
> +                     evsel->threads = thread_map__get(evlist->threads);
> +     } else {
> +             if (evsel->cpus == evlist->cpus) {
> +                     cpu_map__put(evsel->cpus);
> +                     evsel->cpus = NULL;
> +             }
> +
> +             if (evsel->threads == evlist->threads) {
> +                     thread_map__put(evsel->threads);
> +                     evsel->threads = NULL;
> +             }
> +     }
> +}
> +
> +static void perf_evlist__propagate_maps(struct perf_evlist *evlist,
> +                                     bool propagate)
> +{
> +     struct perf_evsel *evsel;
> +
> +     evlist__for_each(evlist, evsel)
> +             __perf_evlist__propagate_maps(evlist, evsel, propagate);
> +}
> +
>  void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
>  {
> +     __perf_evlist__propagate_maps(evlist, entry, true);
> +
>       entry->evlist = evlist;
>       list_add_tail(&entry->node, &evlist->entries);
>       entry->idx = evlist->nr_entries;
> @@ -140,6 +181,10 @@ void perf_evlist__splice_list_tail(struct perf_evlist 
> *evlist,
>                                  int nr_entries)
>  {
>       bool set_id_pos = !evlist->nr_entries;
> +     struct perf_evsel *evsel;
> +
> +     __evlist__for_each(list, evsel)
> +             __perf_evlist__propagate_maps(evlist, evsel, true);
>  
>       list_splice_tail(list, &evlist->entries);
>       evlist->nr_entries += nr_entries;
> @@ -1103,34 +1148,11 @@ 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)
>  {
> +     if (evlist->threads || evlist->cpus)
> +             return -1;
> +
>       evlist->threads = thread_map__new_str(target->pid, target->tid,
>                                             target->uid);
>  
> @@ -1145,7 +1167,11 @@ int perf_evlist__create_maps(struct perf_evlist 
> *evlist, struct target *target)
>       if (evlist->cpus == NULL)
>               goto out_delete_threads;
>  
> -     return perf_evlist__propagate_maps(evlist, !!target->cpu_list);
> +     evlist->has_user_cpus = !!target->cpu_list;
> +
> +     perf_evlist__propagate_maps(evlist, true);
> +
> +     return 0;
>  
>  out_delete_threads:
>       thread_map__put(evlist->threads);
> @@ -1157,17 +1183,32 @@ int perf_evlist__set_maps(struct perf_evlist *evlist,
>                         struct cpu_map *cpus,
>                         struct thread_map *threads)
>  {
> -     if (evlist->cpus)
> -             cpu_map__put(evlist->cpus);
> +     /*
> +      * First 'un-propagate' the current maps which allows the propagation to
> +      * work correctly even when changing the maps or setting them to NULL.
> +      */
> +     perf_evlist__propagate_maps(evlist, false);
>  
> -     evlist->cpus = 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;
> +     }
>  
> -     if (evlist->threads)
> +     if (threads != evlist->threads) {
>               thread_map__put(evlist->threads);
> +             evlist->threads = threads;
> +     }
>  
> -     evlist->threads = threads;
> +     perf_evlist__propagate_maps(evlist, true);
>  
> -     return perf_evlist__propagate_maps(evlist, false);
> +     return 0;
>  }
>  
>  int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel 
> **err_evsel)
> @@ -1387,6 +1428,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 +1441,22 @@ 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;
> +     err = perf_evlist__set_maps(evlist, cpus, threads);
> +     if (err)
> +             goto out_put;
>  out:
>       return err;
> -out_free_cpus:
> -     cpu_map__put(evlist->cpus);
> -     evlist->cpus = NULL;
> +out_put:
> +     cpu_map__put(cpus);
> +     thread_map__put(threads);
>       goto out;
>  }
>  
> diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
> index b39a6198f4ac..2dd5715dfef6 100644
> --- a/tools/perf/util/evlist.h
> +++ b/tools/perf/util/evlist.h
> @@ -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;
> -- 
> 1.9.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to