Hi,
this patchset adds multithreading through the workqueue to the most
important evlist operations: enable, disable, close, and open.
Their multithreaded implementation is then used in perf-record through
a new option '--threads'.
It is dependent on the workqueue patchset (v3):
https://lore.kernel.org/lkml/cover.1629454773.git.rickyman7@gmail.com/
In each operation, each worker is assigned a cpu and pins itself to that
cpu to perform the operation on all evsels. In case enough threads are
provided, the underlying threads are pinned to the cpus, otherwise, they
just change their affinity temporarily.
Parallelization of enable, disable, and close is pretty straightforward,
while open requires more work to separate the actual open from all
fallback mechanisms.
In the multithreaded implementation of open, each thread will run until it
finishes or gets an error. When all threads finish, the main process
checks for errors. If it finds one, it applies a fallback and resumes the
open on all cpus from where they encountered the error.
I have tested the main fallback mechanisms (precise_ip, ignore missing
thread,fd limit increase), but not all of the missing feature ones.
I also ran perf test, with no errors. Below you can find the skipped
tests (I ommitted successfull results for brevity):
$ sudo ./perf test
23: Watchpoint :
23.1: Read Only Watchpoint : Skip (missing hardware support)
58: builtin clang support : Skip (not compiled in)
63: Test libpfm4 support : Skip (not compiled in)
89: perf stat --bpf-counters test : Skip
90: Check Arm CoreSight trace data recording and synthesized samples: Skip
I know the patchset is huge, but I didn't have time to split it (and
my time is running out). In any case, I tried to keep related patches
close together. It is organized as follows:
- 1 - 3: remove the cpu iterator inside evsel to simplify
parallelization. In the doing, cpumap idx and max methods are
improved based on the assumption that the cpumap is ordered.
- 4 - 5: preparation patches for adding affinities to threadpool.
- 6 - 8: add affinity support to threadpool and workqueue (preparation
for adding workqueue to evsel).
- 9: preparation for adding workqueue to evsel.
- 10 - 13: add multithreading to evlist enable, disable, and close.
- 14 - 27: preparation for adding multithreading to evlist__open.
- 28: add multithreading to evlist__open.
- 29 - 34: use multithreaded evlist operations in perf-record.
- 35 - 38: improve evlist-open-close benchmark, adding multithreading
and detailed output.
I'll be happy to split it if necessary in the future, with the goal of
merging my GSoC work, but, for now, I need to send it as is to include it
in my final report.
Below are some experimental results of evlist-open-close benchmark run on:
- laptop (2 cores + hyperthreading)
- vm (16 vCPUs)
The command line was:
$ ./perf bench internals evlist-open-close [-j] <options>
where [-j] was included only in the specified rows and <options> refers
to the column label:
- "" (dummy): open one dummy event on all cpus.
- "-n 100": open 100 dummy events on all cpus.
- "-e '{cs,cycles}'": open the "{cs,cycles}" group on all cpus.
- "-u 0": open one dummy event on all cpus for all processes of root user
(~300 on my laptop; ~360 on my VM).
Results:
machine configuration (dummy) -n 100 -e '{cs,cycles}' -u 0
laptop perf/core 980 +- 130 10514 +- 313 31950 +- 526 14529 +- 241
laptop this w/o -j 698 +- 102 11302 +- 283 31807 +- 448 13885 +- 143
laptop this w/ -j 2233 +- 261 5434 +- 386 13586 +- 443 9465 +- 568
vm perf/core 9818 +- 94 89993 +- 941 N/A 266414 +-1431
vm this w/o -j 5245 +- 88 82806 +- 922 N/A 260416 +-1563
vm this w -j 37787 +- 748 54844 +-1089 N/A 101088 +-1900
Comments:
- opening one dummy event in single threaded mode is faster than
perf/core, probably due to the changes in how evsel cpus are iterated.
- opening one event in multithreaded mode is not worth it.
- in all other cases, multithreaded mode is between 1.5x and 2.5x faster.
Time breakdown on my laptop:
One dummy event per cpu:
$ ./perf bench internals evlist-open-close -d
# Running 'internals/evlist-open-close' benchmark:
Number of workers: 1
Number of cpus: 4
Number of threads: 1
Number of events: 1 (4 fds)
Number of iterations: 100
Average open-close took: 879.250 usec (+- 103.238 usec)
init took: 0.040 usec (+- 0.020 usec)
open took: 28.900 usec (+- 6.675 usec)
mmap took: 415.870 usec (+- 17.391 usec)
enable took: 240.950 usec (+- 92.641 usec)
disable took: 64.670 usec (+- 9.722 usec)
munmap took: 16.450 usec (+- 3.571 usec)
close took: 112.100 usec (+- 25.465 usec)
fini took: 0.220 usec (+- 0.042 usec)
$ ./perf bench internals evlist-open-close -j -d
# Running 'internals/evlist-open-close' benchmark:
Number of workers: 4
Number of cpus: 4
Number of threads: 1
Number of events: 1 (4 fds)
Number of iterations: 100
Average open-close took: 1979.670 usec (+- 271.772 usec)
init took: 30.860 usec (+- 1.190 usec)
open took: 552.040 usec (+- 96.166 usec)
mmap took: 428.970 usec (+- 9.492 usec)
enable took: 222.740 usec (+- 56.112 usec)
disable took: 191.990 usec (+- 55.029 usec)
munmap took: 13.670 usec (+- 0.754 usec)
close took: 155.660 usec (+- 44.079 usec)
fini took: 383.520 usec (+- 87.476 usec)
Comments:
- the overhead comes from open (spinnig up threads) and fini
(terminating threads). There could be some improvements there (e.g.
not waiting for the thread to spawn).
- enable, disable, and close also take longer due to the overhead of
assigning the tasks to the workers.
One dummy event per process per cpu:
$ ./perf bench internals evlist-open-close -d -u0
# Running 'internals/evlist-open-close' benchmark:
Number of workers: 1
Number of cpus: 4
Number of threads: 295
Number of events: 1 (1180 fds)
Number of iterations: 100
Average open-close took: 15101.380 usec (+- 247.959 usec)
init took: 0.010 usec (+- 0.010 usec)
open took: 4224.460 usec (+- 119.028 usec)
mmap took: 3235.210 usec (+- 55.867 usec)
enable took: 2359.570 usec (+- 44.923 usec)
disable took: 2321.100 usec (+- 80.779 usec)
munmap took: 304.440 usec (+- 11.558 usec)
close took: 2655.920 usec (+- 59.089 usec)
fini took: 0.380 usec (+- 0.051 usec)
$ ./perf bench internals evlist-open-close -j -d -u0
# Running 'internals/evlist-open-close' benchmark:
Number of workers: 4
Number of cpus: 4
Number of threads: 298
Number of events: 1 (1192 fds)
Number of iterations: 100
Average open-close took: 10321.060 usec (+- 771.875 usec)
init took: 31.530 usec (+- 0.721 usec)
open took: 2849.870 usec (+- 533.019 usec)
mmap took: 3267.810 usec (+- 87.465 usec)
enable took: 1041.160 usec (+- 66.324 usec)
disable took: 1176.970 usec (+- 134.291 usec)
munmap took: 253.680 usec (+- 4.525 usec)
close took: 1204.550 usec (+- 101.284 usec)
fini took: 495.260 usec (+- 136.661 usec)
Comments:
- mmap/munmap are not parallelized and account for 20% of the time.
- open time is reduced by 33% (due to overhead of spinning up threads,
which is around half ms).
- enable, disable, and close times are halved.
It is not always worth using multithreading in evlist operations, but
in the good cases the improvements can be significant.
For this reason, we could include an heuristic to decide whether to
use it or not (e.g. use it only if there are less than X event fds to open).
It'd be great to see more experiments like these on a bigger machine, and
on a more realistic scenario (instead of dummy events).
Thanks,
Riccardo
Riccardo Mancini (37):
libperf cpumap: improve idx function
libperf cpumap: improve max function
perf evlist: replace evsel__cpu_iter* functions with evsel__find_cpu
perf util: add mmap_cpu_mask__duplicate function
perf util/mmap: add missing bitops.h header
perf workqueue: add affinities to threadpool
perf workqueue: add support for setting affinities to workers
perf workqueue: add method to execute work on specific CPU
perf python: add workqueue dependency
perf evlist: add multithreading helper
perf evlist: add multithreading to evlist__disable
perf evlist: add multithreading to evlist__enable
perf evlist: add multithreading to evlist__close
perf evsel: remove retry_sample_id goto label
perf evsel: separate open preparation from open itself
perf evsel: save open flags in evsel
perf evsel: separate missing feature disabling from evsel__open_cpu
perf evsel: add evsel__prepare_open function
perf evsel: separate missing feature detection from evsel__open_cpu
perf evsel: separate rlimit increase from evsel__open_cpu
perf evsel: move ignore_missing_thread to fallback code
perf evsel: move test_attr__open to success path in evsel__open_cpu
perf evsel: move bpf_counter__install_pe to success path in
evsel__open_cpu
perf evsel: handle precise_ip fallback in evsel__open_cpu
perf evsel: move event open in evsel__open_cpu to separate function
perf evsel: add evsel__open_per_cpu_no_fallback function
perf evlist: add evlist__for_each_entry_from macro
perf evlist: add multithreading to evlist__open
perf evlist: add custom fallback to evlist__open
perf record: use evlist__open_custom
tools lib/subcmd: add OPT_UINTEGER_OPTARG option type
perf record: add --threads option
perf record: pin threads to monitored cpus if enough threads available
perf record: apply multithreading in init and fini phases
perf test/evlist-open-close: add multithreading
perf test/evlist-open-close: use inline func to convert timeval to
usec
perf test/evlist-open-close: add detailed output mode
tools/lib/perf/cpumap.c | 27 +-
tools/lib/subcmd/parse-options.h | 1 +
tools/perf/Documentation/perf-record.txt | 9 +
tools/perf/bench/evlist-open-close.c | 116 +++++-
tools/perf/builtin-record.c | 128 ++++--
tools/perf/builtin-stat.c | 24 +-
tools/perf/tests/workqueue.c | 87 ++++-
tools/perf/util/evlist.c | 464 +++++++++++++++++-----
tools/perf/util/evlist.h | 44 ++-
tools/perf/util/evsel.c | 473 ++++++++++++++---------
tools/perf/util/evsel.h | 36 +-
tools/perf/util/mmap.c | 12 +
tools/perf/util/mmap.h | 4 +
tools/perf/util/python-ext-sources | 2 +
tools/perf/util/record.h | 3 +
tools/perf/util/workqueue/threadpool.c | 70 ++++
tools/perf/util/workqueue/threadpool.h | 7 +
tools/perf/util/workqueue/workqueue.c | 154 +++++++-
tools/perf/util/workqueue/workqueue.h | 18 +
19 files changed, 1334 insertions(+), 345 deletions(-)
--
2.31.1
From commit 7074674e7338863e ("perf cpumap: Maintain cpumaps ordered
and without dups"), perf_cpu_map elements are sorted in ascending order.
This patch improves the perf_cpu_map__idx function by using a binary
search.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/lib/perf/cpumap.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
From commit 7074674e7338863e ("perf cpumap: Maintain cpumaps ordered and
without dups"), perf_cpu_map elements are sorted in ascending order.
This patch improves the perf_cpu_map__max function by returning the last
element.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/lib/perf/cpumap.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
@@ -284,14 +284,10 @@ int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu)intperf_cpu_map__max(structperf_cpu_map*map){-inti,max=-1;--for(i=0;i<map->nr;i++){-if(map->map[i]>max)-max=map->map[i];-}--returnmax;+if(map->nr>0)+returnmap->map[map->nr-1];+else+return-1;}/*
Commit a8cbe40fe9f4ba49 ("perf evsel: Add iterator to iterate over events
ordered by CPU") introduced an iterator for evsel.core.cpus inside evsel
which is used to iterate over evlist one CPU at a time.
However, this solution is hacky since it involves a mutable state in the
evsel which is supposed to be unmodified during iteration.
Sice checking that a CPU is within the evsel can be done quickly in
O(logn) time, this patch replaces the aforementioned iterator with a
simple check.
Cc: Andi Kleen <redacted>
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/builtin-stat.c | 24 +++++++++--------
tools/perf/util/evlist.c | 54 +++++++++++----------------------------
tools/perf/util/evlist.h | 5 +---
tools/perf/util/evsel.h | 1 -
4 files changed, 30 insertions(+), 54 deletions(-)
This patch adds a new function in util/mmap.c to duplicate a mmap_cpu_mask.
This new function will be used in the following patches.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/mmap.c | 12 ++++++++++++
tools/perf/util/mmap.h | 3 +++
2 files changed, 15 insertions(+)
MMAP_CPU_MASK_BYTES uses the BITS_TO_LONGS macro, which is defined in
linux/bitops.h.
However, this header is not included directly, but gets imported
indirectly in files using the macro.
This patch adds the missing include.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/mmap.h | 1 +
1 file changed, 1 insertion(+)
This patch adds the possibility to set affinities to the threads in the
threadpool.
An usage of the new functions is added to the workqueue test.
This patch concludes the patches regarding the threadpool.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/tests/workqueue.c | 87 +++++++++++++++++++++++---
tools/perf/util/workqueue/threadpool.c | 70 +++++++++++++++++++++
tools/perf/util/workqueue/threadpool.h | 7 +++
3 files changed, 157 insertions(+), 7 deletions(-)
@@ -39,6 +41,7 @@ struct threadpool_entry {intcmd[2];/* messages to thread (commands) */}pipes;boolrunning;/* has this thread been started? */+structmmap_cpu_maskaffinity_mask;};enumthreadpool_msg{
@@ -255,6 +258,16 @@ static int threadpool_entry__recv_cmd(struct threadpool_entry *thread,return0;}+/**+*threadpool_entry__apply_affinity-apply@thread->affinity+*/+staticintthreadpool_entry__apply_affinity(structthreadpool_entry*thread)+{+return-pthread_setaffinity_np(thread->ptid,+MMAP_CPU_MASK_BYTES(&thread->affinity_mask),+(cpu_set_t*)(thread->affinity_mask.bits));+}+/***threadpool_entry__function-functionrunningonthread*
@@ -339,6 +352,7 @@ struct threadpool *threadpool__new(int n_threads)pool->threads[t].ptid=0;pool->threads[t].pool=pool;pool->threads[t].running=false;+// affinity is set to zero due to callocthreadpool_entry__init_pipes(&pool->threads[t]);}
@@ -455,6 +470,16 @@ int threadpool__start_thread(struct threadpool *pool, int tidx)pthread_attr_init(&attrs);pthread_attr_setdetachstate(&attrs,PTHREAD_CREATE_DETACHED);+if(thread->affinity_mask.bits){+ret=pthread_attr_setaffinity_np(&attrs,+MMAP_CPU_MASK_BYTES(&thread->affinity_mask),+(cpu_set_t*)(thread->affinity_mask.bits));+if(ret){+err=-ret;+gotoout;+}+}+ret=pthread_create(&thread->ptid,&attrs,threadpool_entry__function,thread);if(ret){err=-ret;
This patch adds an interface to workqueue to set affinities to the
underlying threadpool threads.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/workqueue/workqueue.c | 21 +++++++++++++++++++++
tools/perf/util/workqueue/workqueue.h | 6 ++++++
2 files changed, 27 insertions(+)
@@ -50,6 +50,7 @@ static const char * const workqueue_errno_str[] = {"Error executing function in threadpool","Error stopping the threadpool","Error starting thread in the threadpool",+"Error setting affinity in threadpool","Error sending message to worker","Error receiving message from worker","Received unexpected message from worker",
@@ -758,6 +759,26 @@ int flush_workqueue(struct workqueue_struct *wq)returnerr;}+/**+*workqueue_set_affinities-setaffinitiestoallthreadsin@wq->pool+*/+intworkqueue_set_affinities(structworkqueue_struct*wq,+structmmap_cpu_mask*affinities)+{+wq->pool_errno=threadpool__set_affinities(wq->pool,affinities);+returnwq->pool_errno?-WORKQUEUE_ERROR__POOLAFFINITY:0;+}++/**+*workqueue_set_affinities-setaffinitytothread@tidxin@wq->pool+*/+intworkqueue_set_affinity(structworkqueue_struct*wq,inttidx,+structmmap_cpu_mask*affinity)+{+wq->pool_errno=threadpool__set_affinity(wq->pool,tidx,affinity);+returnwq->pool_errno?-WORKQUEUE_ERROR__POOLAFFINITY:0;+}+/***init_work-initializethe@workstruct*/
This patch adds the possibility to schedule a work item on a specific
CPU.
There are 2 possibilities:
- threads are pinned to a CPU using the new functions
workqueue_set_affinity_cpu and workqueue_set_affinities_cpu
- no thread is pinned to the requested cpu. In this case, affinity will
be set before (and cleared after) executing the work.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/workqueue/workqueue.c | 133 +++++++++++++++++++++++++-
tools/perf/util/workqueue/workqueue.h | 12 +++
2 files changed, 144 insertions(+), 1 deletion(-)
@@ -43,6 +46,10 @@ struct workqueue_struct {structworker**workers;/* array of all workers */structworker*next_worker;/* next worker to choose (round robin) */intfirst_stopped_worker;/* next worker to start if needed */+struct{+int*map;/* maps cpu to thread idx */+intsize;/* size of the map array */+}cpu_to_tidx_map;};staticconstchar*constworkqueue_errno_str[]={
@@ -552,6 +570,7 @@ int destroy_workqueue(struct workqueue_struct *wq)wq->msg_pipe[1]=-1;zfree(&wq->workers);+zfree(&wq->cpu_to_tidx_map.map);free(wq);returnerr;}
@@ -779,6 +798,118 @@ int workqueue_set_affinity(struct workqueue_struct *wq, int tidx,returnwq->pool_errno?-WORKQUEUE_ERROR__POOLAFFINITY:0;}+/**+*workqueue_set_affinity_cpu-setaffinityto@cputothread@tidxin@wq->pool+*+*Ifcpuis-1,thenaffinityissettoallonlineprocessors.+*/+intworkqueue_set_affinity_cpu(structworkqueue_struct*wq,inttidx,intcpu)+{+structmmap_cpu_maskaffinity;+inti,err;++if(cpu>=0)+affinity.nbits=cpu+1;+else+affinity.nbits=wq->cpu_to_tidx_map.size;++affinity.bits=bitmap_alloc(affinity.nbits);+if(!affinity.bits){+pr_debug2("Failed allocation of bitmapset\n");+return-ENOMEM;+}++if(cpu>=0)+test_and_set_bit(cpu,affinity.bits);+else+bitmap_fill(affinity.bits,affinity.nbits);++err=workqueue_set_affinity(wq,tidx,&affinity);+if(err)+gotoout;++// find and unset this thread from the map+for(i=0;i<wq->cpu_to_tidx_map.size;i++){+if(wq->cpu_to_tidx_map.map[i]==tidx)+wq->cpu_to_tidx_map.map[i]=-1;+}++if(cpu>=0)+wq->cpu_to_tidx_map.map[cpu]=tidx;++out:+bitmap_free(affinity.bits);+returnerr;+}++/**+*workqueue_set_affinities_cpu-setsingle-cpuaffinitiestoallthreadsin@wq->pool+*/+intworkqueue_set_affinities_cpu(structworkqueue_struct*wq,+structperf_cpu_map*cpus)+{+intcpu,idx,err;++if(perf_cpu_map__nr(cpus)>threadpool__size(wq->pool))+return-EINVAL;+++perf_cpu_map__for_each_cpu(cpu,idx,cpus){+err=workqueue_set_affinity_cpu(wq,idx,cpu);+if(err)+returnerr;+}++return0;+}++structcpu_bound_work{+structwork_structwork;+intcpu;+structwork_struct*original_work;+};++staticvoidset_affinity_and_execute(structwork_struct*work)+{+structcpu_bound_work*cpu_bound_work=container_of(work,structcpu_bound_work,work);+structaffinityaffinity;++if(affinity__setup(&affinity)<0)+gotoout;++affinity__set(&affinity,cpu_bound_work->cpu);+cpu_bound_work->original_work->func(cpu_bound_work->original_work);+affinity__cleanup(&affinity);++out:+free(cpu_bound_work);+}++/**+*queue_work_on-execute@workon@cpu+*+*Theworkisassignedtotheworkerpinnedto@cpu,ifany.+*Otherwise,affinityissetbeforerunningtheworkandunsetafter.+*/+intqueue_work_on(intcpu,structworkqueue_struct*wq,structwork_struct*work)+{+structcpu_bound_work*cpu_bound_work;+inttidx=wq->cpu_to_tidx_map.map[cpu];++if(tidx>=0)+returnqueue_work_on_worker(tidx,wq,work);++cpu_bound_work=malloc(sizeof(*cpu_bound_work));+if(!cpu_bound_work)+return-ENOMEM;++init_work(&cpu_bound_work->work);+cpu_bound_work->work.func=set_affinity_and_execute;+cpu_bound_work->cpu=cpu;+cpu_bound_work->original_work=work;+returnqueue_work(wq,&cpu_bound_work->work);+}+/***init_work-initializethe@workstruct*/
This patch adds util/workqueue/*.c to python-ext-sources, which is
needed for the following patch, where workqueue will be used in
util/evlist.c.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/python-ext-sources | 2 ++
1 file changed, 2 insertions(+)
This patch adds the function evlist__for_each_evsel_cpu, which executes
the given function on each evsel, for each cpu.
If perf_singlethreaded is unset, this function will use a workqueue to
execute the function.
This helper function will be used in the following patches.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evlist.c | 117 +++++++++++++++++++++++++++++++++++++++
tools/perf/util/evlist.h | 14 +++++
2 files changed, 131 insertions(+)
In this patch, evlist__for_each_evsel_cpu is used in evlist__disable to
allow it to run in parallel.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evlist.c | 57 +++++++++++++++++++++-------------------
1 file changed, 30 insertions(+), 27 deletions(-)
@@ -486,41 +486,44 @@ int evlist__for_each_evsel_cpu(struct evlist *evlist, evsel__cpu_func func, void}+structevlist_disable_args{+char*evsel_name;+intimm;+};++staticint__evlist__disable_evsel_cpu_func(structevlist*evlist__maybe_unused,+structevsel*pos,intcpu,void*_args)+{+intret=0;+structevlist_disable_args*args=_args;++if(evsel__strcmp(pos,args->evsel_name))+return0;+if(pos->disabled||!evsel__is_group_leader(pos)||!pos->core.fd)+return0;+if(pos->immediate)+ret=1;+if(pos->immediate!=args->imm)+returnret;+evsel__disable_cpu(pos,cpu);++returnret;+}++staticvoid__evlist__disable(structevlist*evlist,char*evsel_name){structevsel*pos;-structaffinityaffinity;-intcpu,i,imm=0,cpu_idx;-boolhas_imm=false;--if(affinity__setup(&affinity)<0)-return;+intret;+structevlist_disable_argsargs={.evsel_name=evsel_name};/* Disable 'immediate' events last */-for(imm=0;imm<=1;imm++){-evlist__for_each_cpu(evlist,i,cpu){-affinity__set(&affinity,cpu);--evlist__for_each_entry(evlist,pos){-if(evsel__strcmp(pos,evsel_name))-continue;-cpu_idx=evsel__find_cpu(pos,cpu);-if(cpu_idx<0)-continue;-if(pos->disabled||!evsel__is_group_leader(pos)||!pos->core.fd)-continue;-if(pos->immediate)-has_imm=true;-if(pos->immediate!=imm)-continue;-evsel__disable_cpu(pos,cpu_idx);-}-}-if(!has_imm)+for(args.imm=0;args.imm<=1;args.imm++){+ret=evlist__for_each_evsel_cpu(evlist,__evlist__disable_evsel_cpu_func,&args);+if(!ret)// does not have immediate evselsbreak;}-affinity__cleanup(&affinity);evlist__for_each_entry(evlist,pos){if(evsel__strcmp(pos,evsel_name))continue;
In this patch, evlist__for_each_evsel_cpu is used in evlist__enable to
allow it to run in parallel.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evlist.c | 41 +++++++++++++---------------------------
1 file changed, 13 insertions(+), 28 deletions(-)
In this patch, evlist__for_each_evsel_cpu is used in evlist__close to
allow it to run in parallel.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evlist.c | 23 +++++++++--------------
1 file changed, 9 insertions(+), 14 deletions(-)
As far as I can tell, there is no good reason, apart from optimization
to have the retry_sample_id separate from fallback_missing_features.
Probably, this label was added to avoid reapplying patches for missing
features that had already been applied.
However, missing features that have been added later have not used this
optimization, always jumping to fallback_missing_features and reapplying
all missing features.
This patch removes that label, replacing it with
fallback_missing_features.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
This is a preparatory patch for the following patches with the goal to
separate in evlist__open_cpu the actual perf_event_open, which could be
performed in parallel, from the existing fallback mechanisms, which
should be handled sequentially.
This patch separates the first lines of evsel__open_cpu into a new
__evsel__prepare_open function.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 45 +++++++++++++++++++++++++++++++----------
1 file changed, 34 insertions(+), 11 deletions(-)
This patch caches the flags used in perf_event_open inside evsel, so
that they can be set in __evsel__prepare_open (this will be useful
in following patches, when the fallback mechanisms will be handled
outside the open itself).
This also optimizes the code, by not having to recompute them everytime.
Since flags are now saved in evsel, the flags argument in
perf_event_open is removed.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 24 ++++++++++++------------
tools/perf/util/evsel.h | 1 +
2 files changed, 13 insertions(+), 12 deletions(-)
This is a preparatory patch for the following patches with the goal to
separate in evlist__open_cpu the actual opening, which could be
performed in parallel, from the existing fallback mechanisms, which
should be handled sequentially.
This patch separates the disabling of missing features from
evlist__open_cpu into a new function evsel__disable_missing_features.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 57 ++++++++++++++++++++++-------------------
1 file changed, 31 insertions(+), 26 deletions(-)
This function will prepare the evsel and disable the missing features.
It will be used in one of the following patches.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 14 ++++++++++++++
tools/perf/util/evsel.h | 2 ++
2 files changed, 16 insertions(+)
This is a preparatory patch for the following patches with the goal to
separate in evlist__open_cpu the actual opening, which could be
performed in parallel, from the existing fallback mechanisms, which
should be handled sequentially.
This patch separates the missing feature detection in evsel__open_cpu
into a new evsel__detect_missing_features function.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 174 +++++++++++++++++++++-------------------
tools/perf/util/evsel.h | 1 +
2 files changed, 92 insertions(+), 83 deletions(-)
@@ -1841,6 +1841,96 @@ int evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus,returnerr;}+boolevsel__detect_missing_features(structevsel*evsel)+{+/*+*Mustprobefeaturesintheordertheywereaddedtothe+*perf_event_attrinterface.+*/+if(!perf_missing_features.weight_struct&&+(evsel->core.attr.sample_type&PERF_SAMPLE_WEIGHT_STRUCT)){+perf_missing_features.weight_struct=true;+pr_debug2("switching off weight struct support\n");+returntrue;+}elseif(!perf_missing_features.code_page_size&&+(evsel->core.attr.sample_type&PERF_SAMPLE_CODE_PAGE_SIZE)){+perf_missing_features.code_page_size=true;+pr_debug2_peo("Kernel has no PERF_SAMPLE_CODE_PAGE_SIZE support, bailing out\n");+returnfalse;+}elseif(!perf_missing_features.data_page_size&&+(evsel->core.attr.sample_type&PERF_SAMPLE_DATA_PAGE_SIZE)){+perf_missing_features.data_page_size=true;+pr_debug2_peo("Kernel has no PERF_SAMPLE_DATA_PAGE_SIZE support, bailing out\n");+returnfalse;+}elseif(!perf_missing_features.cgroup&&evsel->core.attr.cgroup){+perf_missing_features.cgroup=true;+pr_debug2_peo("Kernel has no cgroup sampling support, bailing out\n");+returnfalse;+}elseif(!perf_missing_features.branch_hw_idx&&+(evsel->core.attr.branch_sample_type&PERF_SAMPLE_BRANCH_HW_INDEX)){+perf_missing_features.branch_hw_idx=true;+pr_debug2("switching off branch HW index support\n");+returntrue;+}elseif(!perf_missing_features.aux_output&&evsel->core.attr.aux_output){+perf_missing_features.aux_output=true;+pr_debug2_peo("Kernel has no attr.aux_output support, bailing out\n");+returnfalse;+}elseif(!perf_missing_features.bpf&&evsel->core.attr.bpf_event){+perf_missing_features.bpf=true;+pr_debug2_peo("switching off bpf_event\n");+returntrue;+}elseif(!perf_missing_features.ksymbol&&evsel->core.attr.ksymbol){+perf_missing_features.ksymbol=true;+pr_debug2_peo("switching off ksymbol\n");+returntrue;+}elseif(!perf_missing_features.write_backward&&evsel->core.attr.write_backward){+perf_missing_features.write_backward=true;+pr_debug2_peo("switching off write_backward\n");+returnfalse;+}elseif(!perf_missing_features.clockid_wrong&&evsel->core.attr.use_clockid){+perf_missing_features.clockid_wrong=true;+pr_debug2_peo("switching off clockid\n");+returntrue;+}elseif(!perf_missing_features.clockid&&evsel->core.attr.use_clockid){+perf_missing_features.clockid=true;+pr_debug2_peo("switching off use_clockid\n");+returntrue;+}elseif(!perf_missing_features.cloexec&&(evsel->open_flags&PERF_FLAG_FD_CLOEXEC)){+perf_missing_features.cloexec=true;+pr_debug2_peo("switching off cloexec flag\n");+returntrue;+}elseif(!perf_missing_features.mmap2&&evsel->core.attr.mmap2){+perf_missing_features.mmap2=true;+pr_debug2_peo("switching off mmap2\n");+returntrue;+}elseif(!perf_missing_features.exclude_guest&&+(evsel->core.attr.exclude_guest||evsel->core.attr.exclude_host)){+perf_missing_features.exclude_guest=true;+pr_debug2_peo("switching off exclude_guest, exclude_host\n");+returntrue;+}elseif(!perf_missing_features.sample_id_all){+perf_missing_features.sample_id_all=true;+pr_debug2_peo("switching off sample_id_all\n");+returntrue;+}elseif(!perf_missing_features.lbr_flags&&+(evsel->core.attr.branch_sample_type&+(PERF_SAMPLE_BRANCH_NO_CYCLES|+PERF_SAMPLE_BRANCH_NO_FLAGS))){+perf_missing_features.lbr_flags=true;+pr_debug2_peo("switching off branch sample type no (cycles/flags)\n");+returntrue;+}elseif(!perf_missing_features.group_read&&+evsel->core.attr.inherit&&+(evsel->core.attr.read_format&PERF_FORMAT_GROUP)&&+evsel__is_group_leader(evsel)){+perf_missing_features.group_read=true;+pr_debug2_peo("switching off group read\n");+returntrue;+}else{+returnfalse;+}+}+staticintevsel__open_cpu(structevsel*evsel,structperf_cpu_map*cpus,structperf_thread_map*threads,intstart_cpu,intend_cpu)
@@ -1979,90 +2069,8 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,if(err!=-EINVAL||cpu>0||thread>0)gotoout_close;-/*-*Mustprobefeaturesintheordertheywereaddedtothe-*perf_event_attrinterface.-*/-if(!perf_missing_features.weight_struct&&-(evsel->core.attr.sample_type&PERF_SAMPLE_WEIGHT_STRUCT)){-perf_missing_features.weight_struct=true;-pr_debug2("switching off weight struct support\n");+if(evsel__detect_missing_features(evsel))gotofallback_missing_features;-}elseif(!perf_missing_features.code_page_size&&-(evsel->core.attr.sample_type&PERF_SAMPLE_CODE_PAGE_SIZE)){-perf_missing_features.code_page_size=true;-pr_debug2_peo("Kernel has no PERF_SAMPLE_CODE_PAGE_SIZE support, bailing out\n");-gotoout_close;-}elseif(!perf_missing_features.data_page_size&&-(evsel->core.attr.sample_type&PERF_SAMPLE_DATA_PAGE_SIZE)){-perf_missing_features.data_page_size=true;-pr_debug2_peo("Kernel has no PERF_SAMPLE_DATA_PAGE_SIZE support, bailing out\n");-gotoout_close;-}elseif(!perf_missing_features.cgroup&&evsel->core.attr.cgroup){-perf_missing_features.cgroup=true;-pr_debug2_peo("Kernel has no cgroup sampling support, bailing out\n");-gotoout_close;-}elseif(!perf_missing_features.branch_hw_idx&&-(evsel->core.attr.branch_sample_type&PERF_SAMPLE_BRANCH_HW_INDEX)){-perf_missing_features.branch_hw_idx=true;-pr_debug2("switching off branch HW index support\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.aux_output&&evsel->core.attr.aux_output){-perf_missing_features.aux_output=true;-pr_debug2_peo("Kernel has no attr.aux_output support, bailing out\n");-gotoout_close;-}elseif(!perf_missing_features.bpf&&evsel->core.attr.bpf_event){-perf_missing_features.bpf=true;-pr_debug2_peo("switching off bpf_event\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.ksymbol&&evsel->core.attr.ksymbol){-perf_missing_features.ksymbol=true;-pr_debug2_peo("switching off ksymbol\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.write_backward&&evsel->core.attr.write_backward){-perf_missing_features.write_backward=true;-pr_debug2_peo("switching off write_backward\n");-gotoout_close;-}elseif(!perf_missing_features.clockid_wrong&&evsel->core.attr.use_clockid){-perf_missing_features.clockid_wrong=true;-pr_debug2_peo("switching off clockid\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.clockid&&evsel->core.attr.use_clockid){-perf_missing_features.clockid=true;-pr_debug2_peo("switching off use_clockid\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.cloexec&&(evsel->open_flags&PERF_FLAG_FD_CLOEXEC)){-perf_missing_features.cloexec=true;-pr_debug2_peo("switching off cloexec flag\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.mmap2&&evsel->core.attr.mmap2){-perf_missing_features.mmap2=true;-pr_debug2_peo("switching off mmap2\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.exclude_guest&&-(evsel->core.attr.exclude_guest||evsel->core.attr.exclude_host)){-perf_missing_features.exclude_guest=true;-pr_debug2_peo("switching off exclude_guest, exclude_host\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.sample_id_all){-perf_missing_features.sample_id_all=true;-pr_debug2_peo("switching off sample_id_all\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.lbr_flags&&-(evsel->core.attr.branch_sample_type&-(PERF_SAMPLE_BRANCH_NO_CYCLES|-PERF_SAMPLE_BRANCH_NO_FLAGS))){-perf_missing_features.lbr_flags=true;-pr_debug2_peo("switching off branch sample type no (cycles/flags)\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.group_read&&-evsel->core.attr.inherit&&-(evsel->core.attr.read_format&PERF_FORMAT_GROUP)&&-evsel__is_group_leader(evsel)){-perf_missing_features.group_read=true;-pr_debug2_peo("switching off group read\n");-gotofallback_missing_features;-}out_close:if(err)threads->err_thread=thread;
This is a preparatory patch for the following patches with the goal to
separate from evlist__open_cpu the actual opening (which could be
performed in parallel), from the existing fallback mechanisms, which
should be handled sequentially.
This patch separates the rlimit increase from evsel__open_cpu.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 50 ++++++++++++++++++++++++-----------------
tools/perf/util/evsel.h | 3 +++
2 files changed, 33 insertions(+), 20 deletions(-)
This patch moves ignore_missing_thread outside the perf_event_open loop.
Doing so, we need to move the retry_open flag a few places higher, with
minimal impact. Furthermore, thread need not be decreased since it won't
get increased by the for loop (since we're jumping back inside), but we
need to check that the nthreads decrease didn't put thread out of range.
The goal is to have fallbacks handled in one place only, since in the
future parallel code, these would be handled separately.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 29 +++++++++++++----------------
tools/perf/util/evsel.h | 5 +++++
2 files changed, 18 insertions(+), 16 deletions(-)
@@ -2016,20 +2019,6 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,if(fd<0){err=-errno;-if(ignore_missing_thread(evsel,cpus->nr,cpu,threads,thread,err)){-/*-*Wejustremoved1thread,sotakeastep-*backonthreadindexandlowertheupper-*nthreadslimit.-*/-nthreads--;-thread--;--/* ... and pretend like nothing have happened. */-err=0;-continue;-}-pr_debug2_peo("\nsys_perf_event_open failed, error %d\n",err);gototry_fallback;
@@ -2069,6 +2058,14 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,return0;try_fallback:+if(evsel__ignore_missing_thread(evsel,cpus->nr,cpu,threads,thread,err)){+/* We just removed 1 thread, so lower the upper nthreads limit. */+nthreads--;++/* ... and pretend like nothing have happened. */+err=0;+gotoretry_open;+}/**perfstatneedsbetween5and22fdsperCPU.Whenwerunout*ofthemtrytoincreasethelimits.
test_attr__open ignores the fd if -1, therefore it is safe to move it to
the success path (fd >= 0).
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
I don't see why bpf_counter__install_pe should get called even if fd=-1,
so I'm moving it to the success path.
This will be useful in following patches to separate the actual open and
the related operations from the fallback mechanisms.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
This is another patch in the effort to separate the fallback mechanisms
from the open itself.
In case of precise_ip fallback, the original precise_ip will be stored
in the evsel (it was stored in a local variable) and the open will be
retried. Since the precise_ip fallback will be the first in the chain of
fallbacks, there should be no functional change with this patch.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 59 ++++++++++++++++++-----------------------
tools/perf/util/evsel.h | 2 ++
2 files changed, 28 insertions(+), 33 deletions(-)
@@ -1709,42 +1709,29 @@ static void display_attr(struct perf_event_attr *attr)}}-staticintperf_event_open(structevsel*evsel,-pid_tpid,intcpu,intgroup_fd)+boolevsel__precise_ip_fallback(structevsel*evsel){-intprecise_ip=evsel->core.attr.precise_ip;-intfd;--while(1){-pr_debug2_peo("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx",-pid,cpu,group_fd,evsel->open_flags);--fd=sys_perf_event_open(&evsel->core.attr,pid,cpu,group_fd,evsel->open_flags);-if(fd>=0)-break;--/* Do not try less precise if not requested. */-if(!evsel->precise_max)-break;--/*-*Wetriedalltheprecise_ipvalues,andit's-*stillfailing,soleaveittostandardfallback.-*/-if(!evsel->core.attr.precise_ip){-evsel->core.attr.precise_ip=precise_ip;-break;-}+/* Do not try less precise if not requested. */+if(!evsel->precise_max)+returnfalse;-pr_debug2_peo("\nsys_perf_event_open failed, error %d\n",-ENOTSUP);-evsel->core.attr.precise_ip--;-pr_debug2_peo("decreasing precise_ip by one (%d)\n",evsel->core.attr.precise_ip);-display_attr(&evsel->core.attr);+/*+*Wetriedalltheprecise_ipvalues,andit's+*stillfailing,soleaveittostandardfallback.+*/+if(!evsel->core.attr.precise_ip){+evsel->core.attr.precise_ip=evsel->precise_ip_original;+returnfalse;}-returnfd;-}+if(!evsel->precise_ip_original)+evsel->precise_ip_original=evsel->core.attr.precise_ip;+evsel->core.attr.precise_ip--;+pr_debug2_peo("decreasing precise_ip by one (%d)\n",evsel->core.attr.precise_ip);+display_attr(&evsel->core.attr);+returntrue;+}staticstructperf_cpu_map*empty_cpu_map;staticstructperf_thread_map*empty_thread_map;
@@ -2004,8 +1991,11 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,test_attr__ready();-fd=perf_event_open(evsel,pid,cpus->map[cpu],-group_fd);+pr_debug2_peo("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx",+pid,cpus->map[cpu],group_fd,evsel->open_flags);++fd=sys_perf_event_open(&evsel->core.attr,pid,cpus->map[cpu],+group_fd,evsel->open_flags);FD(evsel,cpu,thread)=fd;
@@ -2058,6 +2048,9 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,return0;try_fallback:+if(evsel__precise_ip_fallback(evsel))+gotoretry_open;+if(evsel__ignore_missing_thread(evsel,cpus->nr,cpu,threads,thread,err)){/* We just removed 1 thread, so lower the upper nthreads limit. */nthreads--;
This is the final patch splitting evsel__open_cpu.
This patch moves the entire loop code to a separate function, to be
reused for the multithreaded code.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 142 ++++++++++++++++++++++++----------------
tools/perf/util/evsel.h | 12 ++++
2 files changed, 99 insertions(+), 55 deletions(-)
This function is equivalent to evsel__open, but without any fallback
mechanism, which should be handled separately.
It is also possible to a starting thread to be able to resume
a previous failing evsel__open_per_cpu_no_fallback.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 44 +++++++++++++++++++++++++++++++++++++++++
tools/perf/util/evsel.h | 9 +++++++++
2 files changed, 53 insertions(+)
This patch adds a new iteration macro for evlist that resumes iteration
from a given evsel in the evlist.
This macro will be used in the next patch
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evlist.h | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
This patch enables multithreading in evlist__open using the new
evsel__open_per_cpu_no_fallback function.
The multithreaded version tries to open everything in parallel. Once
workers are done, it checks their result and, in case of error, it
tries the fallback mechanisms present in evsel__open_cpu and restarts
the workers from were they've left.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evlist.c | 189 +++++++++++++++++++++++++++++++++++++--
1 file changed, 183 insertions(+), 6 deletions(-)
@@ -1403,11 +1405,184 @@ static int evlist__create_syswide_maps(struct evlist *evlist)returnerr;}-intevlist__open(structevlist*evlist)+staticintevlist__open_singlethreaded(structevlist*evlist){structevsel*evsel;interr;+evlist__for_each_entry(evlist,evsel){+err=evsel__open(evsel,evsel->core.cpus,evsel->core.threads);+if(err<0)+returnerr;+}++return0;+}++structevlist_open_work{+structwork_structwork;+structevlist*evlist;+intcpu;+union{+intcpu_resume;+intcpu_err;+};+union{+structevsel*evsel_resume;+structevsel*evsel_err;+};+structevsel_open_resultres;// this is also used to resume work+boolprogress;// did the worker do any progress?+};++staticvoidevlist__open_multithreaded_func(structwork_struct*_work)+{+structevlist_open_work*work=container_of(_work,structevlist_open_work,work);+structevsel*evsel=work->evsel_resume;+intcpu_idx,thread_resume=work->res.thread;++work->res.peo_res.err=PEO_SUCCESS;+work->progress=false;++if(!evsel)// nothing to do+return;++work->evsel_err=NULL;++evlist__for_each_entry_from(work->evlist,evsel){+cpu_idx=evsel__find_cpu(evsel,work->cpu);+if(cpu_idx<work->cpu_resume)+continue;++work->res=evsel__open_per_cpu_no_fallback(evsel,+evsel->core.cpus,+evsel->core.threads,+cpu_idx,thread_resume);+work->progress|=work->res.thread!=thread_resume;+if(work->res.peo_res.err!=PEO_SUCCESS){+work->evsel_err=evsel;+work->cpu_err=cpu_idx;+break;+}++thread_resume=0;+}+}++staticintevlist__open_multithreaded(structevlist*evlist)+{+intcpu,cpuid,cpuidx,thread,err;+structevlist_open_work*works;+charerrbuf[WORKQUEUE_STRERR_BUFSIZE];+structperf_event_open_resultpeo_res;+structevsel*evsel;+structperf_cpu_map*cpus;+structperf_thread_map*threads;+enumrlimit_actionset_rlimit=NO_CHANGE;+boolprogress;++works=calloc(perf_cpu_map__nr(evlist->core.all_cpus),sizeof(*works));+if(!works)+return-ENOMEM;++perf_cpu_map__for_each_cpu(cpuid,cpuidx,evlist->core.all_cpus){+init_work(&works[cpuidx].work);+works[cpuidx].work.func=evlist__open_multithreaded_func;+works[cpuidx].evlist=evlist;+works[cpuidx].cpu=cpuid;+works[cpuidx].evsel_resume=evlist__first(evlist);+}++reprepare:+evlist__for_each_entry(evlist,evsel){+err=evsel__prepare_open(evsel,evsel->core.cpus,+evsel->core.threads);+if(err)+gotoout;+}+retry:+perf_cpu_map__for_each_cpu(cpuid,cpuidx,evlist->core.all_cpus){+err=schedule_work_on(cpuid,&works[cpuidx].work);+if(err){+workqueue_strerror(global_wq,err,errbuf,sizeof(errbuf));+pr_debug("schedule_work: %s\n",errbuf);+gotoout;+}+}++err=flush_scheduled_work();+if(err){+workqueue_strerror(global_wq,err,errbuf,sizeof(errbuf));+pr_debug("flush_scheduled_work: %s\n",errbuf);+gotoout;+}++// check if any event was opened (progress = true)+progress=false;+perf_cpu_map__for_each_cpu(cpuid,cpuidx,evlist->core.all_cpus){+if(works[cpuidx].progress){+progress=true;+break;+}+}++perf_cpu_map__for_each_cpu(cpuid,cpuidx,evlist->core.all_cpus){+peo_res=works[cpuidx].res.peo_res;++switch(peo_res.err){+casePEO_SUCCESS:+continue;+casePEO_FALLBACK:+err=peo_res.rc;+break;+default:+casePEO_ERROR:+err=peo_res.rc;+gotoout;+}++// fallback+evsel=works[cpuidx].evsel_err;+cpus=evsel->core.cpus;+cpu=works[cpuidx].cpu_err;+threads=evsel->core.threads;+thread=works[cpuidx].res.thread;++if(evsel__precise_ip_fallback(evsel))+gotoretry;++if(evsel__ignore_missing_thread(evsel,cpus->nr,cpu,+threads,thread,err))+gotoretry;++// increase rlimit only if no progress was made+if(progress)+set_rlimit=NO_CHANGE;+if(err==-EMFILE&&evsel__increase_rlimit(&set_rlimit))+gotoretry;++if(err!=-EINVAL||cpu>0||thread>0)+gotoout;++if(evsel__detect_missing_features(evsel))+gotoreprepare;++// no fallback worked, return the error+gotoout;+}++err=0;++out:+free(works);++returnerr;+}++intevlist__open(structevlist*evlist)+{+interr;+/**Default:onefdperCPU,allthreads,akasystemwide*assys_perf_event_open(cpu=-1,thread=-1)isEINVAL
@@ -1420,11 +1595,13 @@ int evlist__open(struct evlist *evlist)evlist__update_id_pos(evlist);-evlist__for_each_entry(evlist,evsel){-err=evsel__open(evsel,evsel->core.cpus,evsel->core.threads);-if(err<0)-gotoout_err;-}+if(perf_singlethreaded)+err=evlist__open_singlethreaded(evlist);+else+err=evlist__open_multithreaded(evlist);++if(err)+gotoout_err;return0;out_err:
This patch replace the custom evlist opening implemented in record__open
with the new standard function evlist__open_custom.
Differently from before this patch, in case of a weak group all fds are
closed and then reopened (instead of just closing failing ones).
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/builtin-record.c | 63 ++++++++++++++++++++++++-------------
1 file changed, 42 insertions(+), 21 deletions(-)
@@ -882,6 +882,37 @@ static int record__mmap(struct record *rec)returnrecord__mmap_evlist(rec,rec->evlist);}+structrecord_open_custom_fallback{+structevlist_open_custom_fallbackfallback;+structrecord_opts*opts;+boolretry;+};++staticboolrecord__open_fallback(structevlist_open_custom_fallback*_fallback,+structevlist*evlist,structevsel*evsel,interr)+{+charmsg[BUFSIZ];+structrecord_open_custom_fallback*fallback=container_of(_fallback,+structrecord_open_custom_fallback,fallback);++if(evsel__fallback(evsel,-err,msg,sizeof(msg))){+if(verbose>0)+ui__warning("%s\n",msg);+returntrue;+}+if((err==-EINVAL||err==-EBADF)&&+evsel->core.leader!=&evsel->core&&+evsel->weak_group){+evlist__reset_weak_group(evlist,evsel,true);+fallback->retry=true;+returnfalse;+}++evsel__open_strerror(evsel,&fallback->opts->target,-err,msg,sizeof(msg));+ui__error("%s\n",msg);+returnfalse;+}+staticintrecord__open(structrecord*rec){charmsg[BUFSIZ];
@@ -890,6 +921,12 @@ static int record__open(struct record *rec)structperf_session*session=rec->session;structrecord_opts*opts=&rec->opts;intrc=0;+structrecord_open_custom_fallbackcust_fb={+.fallback={+.func=record__open_fallback+},+.opts=opts+};/**Forinitial_delay,systemwideorahybridsystem,weneedtoadda
@@ -919,28 +956,12 @@ static int record__open(struct record *rec)evlist__config(evlist,opts,&callchain_param);-evlist__for_each_entry(evlist,pos){-try_again:-if(evsel__open(pos,pos->core.cpus,pos->core.threads)<0){-if(evsel__fallback(pos,errno,msg,sizeof(msg))){-if(verbose>0)-ui__warning("%s\n",msg);-gototry_again;-}-if((errno==EINVAL||errno==EBADF)&&-pos->core.leader!=&pos->core&&-pos->weak_group){-pos=evlist__reset_weak_group(evlist,pos,true);-gototry_again;-}-rc=-errno;-evsel__open_strerror(pos,&opts->target,errno,msg,sizeof(msg));-ui__error("%s\n",msg);-gotoout;-}-pos->supported=true;-}+do{+cust_fb.retry=false;+rc=evlist__open_custom(evlist,&cust_fb.fallback);+}while(cust_fb.retry);+if(symbol_conf.kptr_restrict&&!evlist__exclude_kernel(evlist)){pr_warning(
This patch adds the function evlist__open_custom, which makes it possible
to provide a custom fallback mechanism to evsel__open.
This function will be used in record__open in the following patch.
This could also be used to adapt perf-stat way of opening to the new
multithreaded mechanism.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evlist.c | 27 +++++++++++++++++++++------
tools/perf/util/evlist.h | 9 +++++++++
2 files changed, 30 insertions(+), 6 deletions(-)
@@ -1567,6 +1573,9 @@ static int evlist__open_multithreaded(struct evlist *evlist)if(evsel__detect_missing_features(evsel))gotoreprepare;+if(cust_fb&&cust_fb->func(cust_fb,evlist,evsel,err))+gotoreprepare;+// no fallback worked, return the errorgotoout;}
@@ -1579,7 +1588,8 @@ static int evlist__open_multithreaded(struct evlist *evlist)returnerr;}-intevlist__open(structevlist*evlist)+intevlist__open_custom(structevlist*evlist,+structevlist_open_custom_fallback*cust_fb){interr;
@@ -1596,9 +1606,9 @@ int evlist__open(struct evlist *evlist)evlist__update_id_pos(evlist);if(perf_singlethreaded)-err=evlist__open_singlethreaded(evlist);+err=evlist__open_singlethreaded(evlist,cust_fb);else-err=evlist__open_multithreaded(evlist);+err=evlist__open_multithreaded(evlist,cust_fb);if(err)gotoout_err;
@@ -1610,6 +1620,11 @@ int evlist__open(struct evlist *evlist)returnerr;}+intevlist__open(structevlist*evlist)+{+returnevlist__open_custom(evlist,NULL);+}+intevlist__prepare_workload(structevlist*evlist,structtarget*target,constchar*argv[],boolpipe_output,void(*exec_error)(intsigno,siginfo_t*info,void*ucontext)){
This patch adds OPT_UINTEGER_OPTARG, which is the same as OPT_UINTEGER,
but also makes it possible to use the option without any value, setting
the variable to a default value, d.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/lib/subcmd/parse-options.h | 1 +
1 file changed, 1 insertion(+)
This patch sets the affinity of the workqueue threads to pin them to
each monitored CPU in case the --threads option is set with enough
threads and evlist multithreading is enabled.
This yields a better performance for the evlist operations, since
affinity need not be sent by each thread everytime.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/builtin-record.c | 10 ++++++++++
1 file changed, 10 insertions(+)
This patch grows the multithreaded portion of perf-record, marked by the
perf_set_multithreaded and perf_set_singlethreaded functions to the whole
init and fini part of __cmd_record.
By doing so, perf-record can take advantage of the parallelized evlist
operations (open, enable, disable, close).
This patch also needs to handle the case in which evlist and synthesis
multithreading are not enabled at the same time. Therefore, in
record__synthesize multithreading is enabled/disabled and then
disabled/renabled if needed.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/builtin-record.c | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
@@ -1411,6 +1411,7 @@ static int record__synthesize(struct record *rec, bool tail)structperf_tool*tool=&rec->tool;interr=0;event_opf=process_synthesized_event;+boolperf_was_singlethreaded=perf_singlethreaded;if(rec->opts.tail_synthesize!=tail)return0;
@@ -1499,12 +1500,16 @@ static int record__synthesize(struct record *rec, bool tail)if(rec->opts.multithreaded_synthesis){perf_set_multithreaded();f=process_locked_synthesized_event;+}else{+perf_set_singlethreaded();}err=__machine__synthesize_threads(machine,tool,&opts->target,rec->evlist->core.threads,f,opts->sample_address);-if(rec->opts.multithreaded_synthesis)+if(!perf_was_singlethreaded&&perf_singlethreaded)+perf_set_multithreaded();+if(perf_was_singlethreaded&&!perf_singlethreaded)perf_set_singlethreaded();out:
@@ -1735,6 +1740,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)record__uniquify_name(rec);+if(rec->opts.multithreaded_evlist)+perf_set_multithreaded();+if(record__open(rec)!=0){err=-1;gotoout_child;
@@ -1877,6 +1885,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)}}+// disable locks in the main thread since there is no multithreading+if(rec->opts.multithreaded_evlist)+perf_set_singlethreaded();+trigger_ready(&auxtrace_snapshot_trigger);trigger_ready(&switch_output_trigger);perf_hooks__invoke_record_start();
@@ -1998,6 +2010,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)}}+// reenable multithreading for evlist+if(rec->opts.multithreaded_evlist)+perf_set_multithreaded();+trigger_off(&auxtrace_snapshot_trigger);trigger_off(&switch_output_trigger);
@@ -2099,6 +2115,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)if(!opts->no_bpf_event)evlist__stop_sb_thread(rec->sb_evlist);++// disable multithreaded mode on exit+if(rec->opts.multithreaded_evlist)+perf_set_singlethreaded();returnstatus;}
This patch adds a new --threads option to perf-record, which sets the
number of threads to use for multithreaded operations (synthesis and, in
following patches, evlist).
The new option will override the --num-thread-synthesize option if set.
By default, no thread will be used. The option can also be passed
without any argument, setting the number of threads to the number of
online cpus.
Furthermore, two new perf configs are added to selectively disable
multithreading in either synthesis and evlist.
To keep the same behaviour for --num-thread-synthesize, setting only that
option will cause multithreading to be enabled only in synthesis (by
overriding the perf config options for multithreaded synthesis and
evlist).
Examples:
$ ./perf record --threads
uses one thread per cpu for synthesis (and evlist in following patches)
$ ./perf record --threads 2 --num-thread-synthesize 4
the two options shouldn't be mixed, the behaviour would be using 2
threads for everything (4 is ignored)
$ ./perf record --num-thread-synthesize 4
same behaviour as before: 4 threads, but only for synthesis
$ ./perf config record.multithreaded_synthesis=no
$ ./perf record --threads
uses multithreading for everything but synthesis (i.e. evlist in
following patches)
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/Documentation/perf-record.txt | 9 ++++++
tools/perf/builtin-record.c | 35 +++++++++++++++++++-----
tools/perf/util/record.h | 3 ++
3 files changed, 40 insertions(+), 7 deletions(-)
@@ -629,6 +629,15 @@ appended unit character - B/K/M/G The number of threads to run when synthesizing events for existing processes. By default, the number of threads equals 1.+--threads::+ The number of threads to use for operations which have multithreaded+ support (synthesize, evlist).+ Setting this option overrides --num-thread-synthesize.+ You can selectively disable any of the multithreaded operations through+ perf-config record.multithreaded-{synthesis,evlist}.+ By default, the number of threads equals 1.+ Setting this option without any parameter sets it to the number of online cpus.+ ifdef::HAVE_LIBPFM[] --pfm-events events:: Select a PMU event using libpfm4 syntax (see http://perfmon2.sf.net)
@@ -2434,6 +2440,9 @@ static struct record record = {},.mmap_flush=MMAP_FLUSH_DEFAULT,.nr_threads_synthesize=1,+.nr_threads=1,+.multithreaded_evlist=true,+.multithreaded_synthesis=true,.ctl_fd=-1,.ctl_fd_ack=-1,},
@@ -2640,6 +2649,9 @@ static struct option __record_options[] = {OPT_UINTEGER(0,"num-thread-synthesize",&record.opts.nr_threads_synthesize,"number of threads to run for event synthesis"),+OPT_UINTEGER_OPTARG(0,"threads",+&record.opts.nr_threads,UINT_MAX,+"number of threads to use"),#ifdef HAVE_LIBPFMOPT_CALLBACK(0,"pfm-events",&record.evlist,"event","libpfm4 event selector. use 'perf list' to list available events",
This patch adds the new option -j/--threads to use multiple threads in
the evlist operations in the evlist-open-close benchmark.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/bench/evlist-open-close.c | 46 ++++++++++++++++++++++++----
1 file changed, 40 insertions(+), 6 deletions(-)
@@ -125,7 +143,19 @@ static int bench__do_evlist_open_close(struct evlist *evlist)evlist__munmap(evlist);evlist__close(evlist);-return0;+out:+if(opts.nr_threads>1){+ret=teardown_global_workqueue();+if(ret){+destroy_workqueue_strerror(err,sbuf,sizeof(sbuf));+pr_err("teardown_global_workqueue: %s\n",sbuf);+err=ret;+}++perf_set_singlethreaded();+}++returnerr;}staticintbench_evlist_open_close__run(char*evstr)
@@ -143,6 +173,7 @@ static int bench_evlist_open_close__run(char *evstr)init_stats(&time_stats);+printf(" Number of workers:\t%u\n",opts.nr_threads);printf(" Number of cpus:\t%d\n",evlist->core.cpus->nr);printf(" Number of threads:\t%d\n",evlist->core.threads->nr);printf(" Number of events:\t%d (%d fds)\n",
@@ -226,6 +257,9 @@ int bench_evlist_open_close(int argc, const char **argv)exit(EXIT_FAILURE);}+if(opts.nr_threads==UINT_MAX)+opts.nr_threads=sysconf(_SC_NPROCESSORS_ONLN);+err=target__validate(&opts.target);if(err){target__strerror(&opts.target,err,errbuf,sizeof(errbuf));
This patch introduces a new inline function to convert a timeval to
usec.
This function will be used also in the next patch.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/bench/evlist-open-close.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
This patch adds a detailed output mode in the perf-test
evlist-open-close. In this output mode, the time taken by each single
evlist function is computed.
Normal mode:
$ sudo ./perf bench internals evlist-open-close
Number of workers: 1
Number of cpus: 4
Number of threads: 1
Number of events: 1 (4 fds)
Number of iterations: 100
Average open-close took: 1199.300 usec (+- 289.699 usec)
Detailed mode:
$ sudo ./perf bench internals evlist-open-close -d
Number of workers: 1
Number of cpus: 4
Number of threads: 1
Number of events: 1 (4 fds)
Number of iterations: 100
Average open-close took: 1199.300 usec (+- 289.699 usec)
init took: 0.000 usec (+- 0.000 usec)
open took: 25.600 usec (+- 1.778 usec)
mmap took: 532.000 usec (+- 58.133 usec)
enable took: 337.300 usec (+- 194.160 usec)
disable took: 181.700 usec (+- 85.307 usec)
munmap took: 22.100 usec (+- 4.045 usec)
close took: 100.300 usec (+- 21.329 usec)
fini took: 0.200 usec (+- 0.133 usec)
* init and fini represent the time taken before and after the evlist
operations (in this case the workqueue setup and teardown operations)
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/bench/evlist-open-close.c | 63 ++++++++++++++++++++++++++--
1 file changed, 60 insertions(+), 3 deletions(-)
@@ -60,6 +66,7 @@ static const struct option options[] = {OPT_STRING('u',"uid",&opts.target.uid_str,"user","user to profile"),OPT_BOOLEAN(0,"per-thread",&opts.target.per_thread,"use per-thread mmaps"),OPT_UINTEGER_OPTARG('j',"threads",&opts.nr_threads,UINT_MAX,"Number of threads to use"),+OPT_BOOLEAN('d',"detail",&detail,"compute time taken by single functions"),OPT_END()};
@@ -113,11 +120,28 @@ static struct evlist *bench__create_evlist(char *evstr)returnNULL;}-staticintbench__do_evlist_open_close(structevlist*evlist)+#define START_TIMER(timers) do { \+if(detail){\+gettimeofday(&(timers)->start,NULL);\+}\+}while(0)++#define RECORD_TIMER(timers, field) do { \+if(detail){\+gettimeofday(&(timers)->end,NULL);\+timersub(&(timers)->end,&(timers)->start,&(timers)->diff);\+update_stats(&(timers)->field,timeval2usec(&(timers)->diff));\+(timers)->start=(timers)->end;\+}\+}while(0)++staticintbench__do_evlist_open_close(structevlist*evlist,structtimers*timers){charsbuf[WORKQUEUE_STRERR_BUFSIZE];interr=-1,ret;+START_TIMER(timers);+if(opts.nr_threads>1){err=setup_global_workqueue(opts.nr_threads);if(err){
@@ -159,10 +190,15 @@ static int bench__do_evlist_open_close(struct evlist *evlist)perf_set_singlethreaded();}+RECORD_TIMER(timers,fini);returnerr;}+#define PRINT_TIMER(timers, field) \+printf("%20s took: %12.3f usec (+- %12.3f usec)\n",#field,\+avg_stats(&(timers)->field),stddev_stats(&(timers)->field))+staticintbench_evlist_open_close__run(char*evstr){// used to print statistics only
@@ -172,10 +208,21 @@ static int bench_evlist_open_close__run(char *evstr)structstatstime_stats;u64runtime_us;inti,err;+structtimerstimers;if(!evlist)return-ENOMEM;+init_stats(&time_stats);+init_stats(&timers.init);+init_stats(&timers.open);+init_stats(&timers.mmap);+init_stats(&timers.enable);+init_stats(&timers.disable);+init_stats(&timers.munmap);+init_stats(&timers.close);+init_stats(&timers.fini);+init_stats(&time_stats);printf(" Number of workers:\t%u\n",opts.nr_threads);
@@ -194,7 +241,7 @@ static int bench_evlist_open_close__run(char *evstr)return-ENOMEM;gettimeofday(&start,NULL);-err=bench__do_evlist_open_close(evlist);+err=bench__do_evlist_open_close(evlist,&timers);if(err){evlist__delete(evlist);returnerr;
@@ -211,7 +258,17 @@ static int bench_evlist_open_close__run(char *evstr)time_average=avg_stats(&time_stats);time_stddev=stddev_stats(&time_stats);-printf(" Average open-close took: %.3f usec (+- %.3f usec)\n",time_average,time_stddev);+printf(" Average open-close took: %12.3f usec (+- %12.3f usec)\n",time_average,time_stddev);+if(detail){+PRINT_TIMER(&timers,init);+PRINT_TIMER(&timers,open);+PRINT_TIMER(&timers,mmap);+PRINT_TIMER(&timers,enable);+PRINT_TIMER(&timers,disable);+PRINT_TIMER(&timers,munmap);+PRINT_TIMER(&timers,close);+PRINT_TIMER(&timers,fini);+}return0;}
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 18:45:01
Em Sat, Aug 21, 2021 at 11:19:37AM +0200, Riccardo Mancini escreveu:
This patch adds OPT_UINTEGER_OPTARG, which is the same as OPT_UINTEGER,
but also makes it possible to use the option without any value, setting
the variable to a default value, d.
Thanks, applied, just to erode a bit the patch kit.
- Arnaldo
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 18:47:16
Em Sat, Aug 21, 2021 at 11:19:08AM +0200, Riccardo Mancini escreveu:
quoted hunk
quoted
From commit 7074674e7338863e ("perf cpumap: Maintain cpumaps ordered and
without dups"), perf_cpu_map elements are sorted in ascending order.
This patch improves the perf_cpu_map__max function by returning the last
element.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/lib/perf/cpumap.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
@@ -284,14 +284,10 @@ int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu)intperf_cpu_map__max(structperf_cpu_map*map){-inti,max=-1;--for(i=0;i<map->nr;i++){-if(map->map[i]>max)-max=map->map[i];-}--returnmax;+if(map->nr>0)+returnmap->map[map->nr-1];+else+return-1;
Applying, but adding spaces around the '-',
Thanks.
- Arnaldo
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:17:05
Em Tue, Aug 31, 2021 at 03:47:10PM -0300, Arnaldo Carvalho de Melo escreveu:
Em Sat, Aug 21, 2021 at 11:19:08AM +0200, Riccardo Mancini escreveu:
quoted
quoted
From commit 7074674e7338863e ("perf cpumap: Maintain cpumaps ordered and
without dups"), perf_cpu_map elements are sorted in ascending order.
This patch improves the perf_cpu_map__max function by returning the last
element.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/lib/perf/cpumap.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
@@ -284,14 +284,10 @@ int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu)intperf_cpu_map__max(structperf_cpu_map*map){-inti,max=-1;--for(i=0;i<map->nr;i++){-if(map->map[i]>max)-max=map->map[i];-}--returnmax;+if(map->nr>0)+returnmap->map[map->nr-1];+else+return-1;
Applying, but adding spaces around the '-',
I ended up with this, ok?
+++ b/tools/lib/perf/cpumap.c
@@ -282,10 +282,8 @@ int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu)intperf_cpu_map__max(structperf_cpu_map*map){-if(map->nr>0)-returnmap->map[map->nr-1];-else-return-1;+// cpu_map__trim_new() qsort()s it, cpu_map__default_new() sorts it as well.+returnmap->nr>0?map->map[map->nr-1]:-1;}
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:21:09
Em Sat, Aug 21, 2021 at 11:19:10AM +0200, Riccardo Mancini escreveu:
quoted hunk
This patch adds a new function in util/mmap.c to duplicate a mmap_cpu_mask.
This new function will be used in the following patches.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/mmap.c | 12 ++++++++++++
tools/perf/util/mmap.h | 3 +++
2 files changed, 15 insertions(+)
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:22:25
Em Sat, Aug 21, 2021 at 11:19:11AM +0200, Riccardo Mancini escreveu:
MMAP_CPU_MASK_BYTES uses the BITS_TO_LONGS macro, which is defined in
linux/bitops.h.
However, this header is not included directly, but gets imported
indirectly in files using the macro.
This patch adds the missing include.
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:25:08
Em Sat, Aug 21, 2021 at 11:19:20AM +0200, Riccardo Mancini escreveu:
As far as I can tell, there is no good reason, apart from optimization
to have the retry_sample_id separate from fallback_missing_features.
Probably, this label was added to avoid reapplying patches for missing
features that had already been applied.
However, missing features that have been added later have not used this
optimization, always jumping to fallback_missing_features and reapplying
all missing features.
This patch removes that label, replacing it with
fallback_missing_features.
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:27:47
Em Sat, Aug 21, 2021 at 11:19:21AM +0200, Riccardo Mancini escreveu:
This is a preparatory patch for the following patches with the goal to
separate in evlist__open_cpu the actual perf_event_open, which could be
performed in parallel, from the existing fallback mechanisms, which
should be handled sequentially.
Thanks, applied as the end result is equivalent and we erode this
patchkit a bit more.
- Arnaldo
quoted hunk
This patch separates the first lines of evsel__open_cpu into a new
__evsel__prepare_open function.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 45 +++++++++++++++++++++++++++++++----------
1 file changed, 34 insertions(+), 11 deletions(-)
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:31:11
Em Sat, Aug 21, 2021 at 11:19:22AM +0200, Riccardo Mancini escreveu:
This patch caches the flags used in perf_event_open inside evsel, so
that they can be set in __evsel__prepare_open (this will be useful
in following patches, when the fallback mechanisms will be handled
outside the open itself).
This also optimizes the code, by not having to recompute them everytime.
Nice, thanks, applied, I'll make available what I have in tmp.perf/core
so that you can take a look before I push it all to Linus, this week.
- Arnaldo
quoted hunk
Since flags are now saved in evsel, the flags argument in
perf_event_open is removed.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 24 ++++++++++++------------
tools/perf/util/evsel.h | 1 +
2 files changed, 13 insertions(+), 12 deletions(-)
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:35:28
Em Sat, Aug 21, 2021 at 11:19:23AM +0200, Riccardo Mancini escreveu:
This is a preparatory patch for the following patches with the goal to
separate in evlist__open_cpu the actual opening, which could be
performed in parallel, from the existing fallback mechanisms, which
should be handled sequentially.
This patch separates the disabling of missing features from
evlist__open_cpu into a new function evsel__disable_missing_features.
Thanks, applied as the end result is the same.
- Arnaldo
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:36:44
Em Sat, Aug 21, 2021 at 11:19:24AM +0200, Riccardo Mancini escreveu:
This function will prepare the evsel and disable the missing features.
It will be used in one of the following patches.
Applied, fixed up this:
int evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus,
struct perf_thread_map *threads)
I.e. the alignment of the second line with parms, please use this form
in your next patches.
- Arnaldo
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:39:10
Em Sat, Aug 21, 2021 at 11:19:25AM +0200, Riccardo Mancini escreveu:
This is a preparatory patch for the following patches with the goal to
separate in evlist__open_cpu the actual opening, which could be
performed in parallel, from the existing fallback mechanisms, which
should be handled sequentially.
This patch separates the missing feature detection in evsel__open_cpu
into a new evsel__detect_missing_features function.
@@ -1841,6 +1841,96 @@ int evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus,returnerr;}+boolevsel__detect_missing_features(structevsel*evsel)+{+/*+*Mustprobefeaturesintheordertheywereaddedtothe+*perf_event_attrinterface.+*/+if(!perf_missing_features.weight_struct&&+(evsel->core.attr.sample_type&PERF_SAMPLE_WEIGHT_STRUCT)){+perf_missing_features.weight_struct=true;+pr_debug2("switching off weight struct support\n");+returntrue;+}elseif(!perf_missing_features.code_page_size&&+(evsel->core.attr.sample_type&PERF_SAMPLE_CODE_PAGE_SIZE)){+perf_missing_features.code_page_size=true;+pr_debug2_peo("Kernel has no PERF_SAMPLE_CODE_PAGE_SIZE support, bailing out\n");+returnfalse;+}elseif(!perf_missing_features.data_page_size&&+(evsel->core.attr.sample_type&PERF_SAMPLE_DATA_PAGE_SIZE)){+perf_missing_features.data_page_size=true;+pr_debug2_peo("Kernel has no PERF_SAMPLE_DATA_PAGE_SIZE support, bailing out\n");+returnfalse;+}elseif(!perf_missing_features.cgroup&&evsel->core.attr.cgroup){+perf_missing_features.cgroup=true;+pr_debug2_peo("Kernel has no cgroup sampling support, bailing out\n");+returnfalse;+}elseif(!perf_missing_features.branch_hw_idx&&+(evsel->core.attr.branch_sample_type&PERF_SAMPLE_BRANCH_HW_INDEX)){+perf_missing_features.branch_hw_idx=true;+pr_debug2("switching off branch HW index support\n");+returntrue;+}elseif(!perf_missing_features.aux_output&&evsel->core.attr.aux_output){+perf_missing_features.aux_output=true;+pr_debug2_peo("Kernel has no attr.aux_output support, bailing out\n");+returnfalse;+}elseif(!perf_missing_features.bpf&&evsel->core.attr.bpf_event){+perf_missing_features.bpf=true;+pr_debug2_peo("switching off bpf_event\n");+returntrue;+}elseif(!perf_missing_features.ksymbol&&evsel->core.attr.ksymbol){+perf_missing_features.ksymbol=true;+pr_debug2_peo("switching off ksymbol\n");+returntrue;+}elseif(!perf_missing_features.write_backward&&evsel->core.attr.write_backward){+perf_missing_features.write_backward=true;+pr_debug2_peo("switching off write_backward\n");+returnfalse;+}elseif(!perf_missing_features.clockid_wrong&&evsel->core.attr.use_clockid){+perf_missing_features.clockid_wrong=true;+pr_debug2_peo("switching off clockid\n");+returntrue;+}elseif(!perf_missing_features.clockid&&evsel->core.attr.use_clockid){+perf_missing_features.clockid=true;+pr_debug2_peo("switching off use_clockid\n");+returntrue;+}elseif(!perf_missing_features.cloexec&&(evsel->open_flags&PERF_FLAG_FD_CLOEXEC)){+perf_missing_features.cloexec=true;+pr_debug2_peo("switching off cloexec flag\n");+returntrue;+}elseif(!perf_missing_features.mmap2&&evsel->core.attr.mmap2){+perf_missing_features.mmap2=true;+pr_debug2_peo("switching off mmap2\n");+returntrue;+}elseif(!perf_missing_features.exclude_guest&&+(evsel->core.attr.exclude_guest||evsel->core.attr.exclude_host)){+perf_missing_features.exclude_guest=true;+pr_debug2_peo("switching off exclude_guest, exclude_host\n");+returntrue;+}elseif(!perf_missing_features.sample_id_all){+perf_missing_features.sample_id_all=true;+pr_debug2_peo("switching off sample_id_all\n");+returntrue;+}elseif(!perf_missing_features.lbr_flags&&+(evsel->core.attr.branch_sample_type&+(PERF_SAMPLE_BRANCH_NO_CYCLES|+PERF_SAMPLE_BRANCH_NO_FLAGS))){+perf_missing_features.lbr_flags=true;+pr_debug2_peo("switching off branch sample type no (cycles/flags)\n");+returntrue;+}elseif(!perf_missing_features.group_read&&+evsel->core.attr.inherit&&+(evsel->core.attr.read_format&PERF_FORMAT_GROUP)&&+evsel__is_group_leader(evsel)){+perf_missing_features.group_read=true;+pr_debug2_peo("switching off group read\n");+returntrue;+}else{+returnfalse;+}+}+staticintevsel__open_cpu(structevsel*evsel,structperf_cpu_map*cpus,structperf_thread_map*threads,intstart_cpu,intend_cpu)
@@ -1979,90 +2069,8 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,if(err!=-EINVAL||cpu>0||thread>0)gotoout_close;-/*-*Mustprobefeaturesintheordertheywereaddedtothe-*perf_event_attrinterface.-*/-if(!perf_missing_features.weight_struct&&-(evsel->core.attr.sample_type&PERF_SAMPLE_WEIGHT_STRUCT)){-perf_missing_features.weight_struct=true;-pr_debug2("switching off weight struct support\n");+if(evsel__detect_missing_features(evsel))gotofallback_missing_features;-}elseif(!perf_missing_features.code_page_size&&-(evsel->core.attr.sample_type&PERF_SAMPLE_CODE_PAGE_SIZE)){-perf_missing_features.code_page_size=true;-pr_debug2_peo("Kernel has no PERF_SAMPLE_CODE_PAGE_SIZE support, bailing out\n");-gotoout_close;-}elseif(!perf_missing_features.data_page_size&&-(evsel->core.attr.sample_type&PERF_SAMPLE_DATA_PAGE_SIZE)){-perf_missing_features.data_page_size=true;-pr_debug2_peo("Kernel has no PERF_SAMPLE_DATA_PAGE_SIZE support, bailing out\n");-gotoout_close;-}elseif(!perf_missing_features.cgroup&&evsel->core.attr.cgroup){-perf_missing_features.cgroup=true;-pr_debug2_peo("Kernel has no cgroup sampling support, bailing out\n");-gotoout_close;-}elseif(!perf_missing_features.branch_hw_idx&&-(evsel->core.attr.branch_sample_type&PERF_SAMPLE_BRANCH_HW_INDEX)){-perf_missing_features.branch_hw_idx=true;-pr_debug2("switching off branch HW index support\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.aux_output&&evsel->core.attr.aux_output){-perf_missing_features.aux_output=true;-pr_debug2_peo("Kernel has no attr.aux_output support, bailing out\n");-gotoout_close;-}elseif(!perf_missing_features.bpf&&evsel->core.attr.bpf_event){-perf_missing_features.bpf=true;-pr_debug2_peo("switching off bpf_event\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.ksymbol&&evsel->core.attr.ksymbol){-perf_missing_features.ksymbol=true;-pr_debug2_peo("switching off ksymbol\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.write_backward&&evsel->core.attr.write_backward){-perf_missing_features.write_backward=true;-pr_debug2_peo("switching off write_backward\n");-gotoout_close;-}elseif(!perf_missing_features.clockid_wrong&&evsel->core.attr.use_clockid){-perf_missing_features.clockid_wrong=true;-pr_debug2_peo("switching off clockid\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.clockid&&evsel->core.attr.use_clockid){-perf_missing_features.clockid=true;-pr_debug2_peo("switching off use_clockid\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.cloexec&&(evsel->open_flags&PERF_FLAG_FD_CLOEXEC)){-perf_missing_features.cloexec=true;-pr_debug2_peo("switching off cloexec flag\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.mmap2&&evsel->core.attr.mmap2){-perf_missing_features.mmap2=true;-pr_debug2_peo("switching off mmap2\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.exclude_guest&&-(evsel->core.attr.exclude_guest||evsel->core.attr.exclude_host)){-perf_missing_features.exclude_guest=true;-pr_debug2_peo("switching off exclude_guest, exclude_host\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.sample_id_all){-perf_missing_features.sample_id_all=true;-pr_debug2_peo("switching off sample_id_all\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.lbr_flags&&-(evsel->core.attr.branch_sample_type&-(PERF_SAMPLE_BRANCH_NO_CYCLES|-PERF_SAMPLE_BRANCH_NO_FLAGS))){-perf_missing_features.lbr_flags=true;-pr_debug2_peo("switching off branch sample type no (cycles/flags)\n");-gotofallback_missing_features;-}elseif(!perf_missing_features.group_read&&-evsel->core.attr.inherit&&-(evsel->core.attr.read_format&PERF_FORMAT_GROUP)&&-evsel__is_group_leader(evsel)){-perf_missing_features.group_read=true;-pr_debug2_peo("switching off group read\n");-gotofallback_missing_features;-}out_close:if(err)threads->err_thread=thread;
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:41:32
Em Sat, Aug 21, 2021 at 11:19:26AM +0200, Riccardo Mancini escreveu:
quoted hunk
This is a preparatory patch for the following patches with the goal to
separate from evlist__open_cpu the actual opening (which could be
performed in parallel), from the existing fallback mechanisms, which
should be handled sequentially.
This patch separates the rlimit increase from evsel__open_cpu.
Signed-off-by: Riccardo Mancini <redacted>
---
tools/perf/util/evsel.c | 50 ++++++++++++++++++++++++-----------------
tools/perf/util/evsel.h | 3 +++
2 files changed, 33 insertions(+), 20 deletions(-)
And here it should be used, I'm recycling it for you :-)
+ if (getrlimit(RLIMIT_NOFILE, &l) == 0) {
+ if (*set_rlimit == NO_CHANGE)
+ l.rlim_cur = l.rlim_max;
+ else {
Also when the else clause has {}, the if should have it too, I'm fixing
it.
I see that you just moved things around, but then this is a good time to
do these cosmetic changes.
Applying.
- Arnaldo
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:44:58
Em Sat, Aug 21, 2021 at 11:19:27AM +0200, Riccardo Mancini escreveu:
This patch moves ignore_missing_thread outside the perf_event_open loop.
Doing so, we need to move the retry_open flag a few places higher, with
minimal impact. Furthermore, thread need not be decreased since it won't
get increased by the for loop (since we're jumping back inside), but we
need to check that the nthreads decrease didn't put thread out of range.
The goal is to have fallbacks handled in one place only, since in the
future parallel code, these would be handled separately.
@@ -2016,20 +2019,6 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,if(fd<0){err=-errno;-if(ignore_missing_thread(evsel,cpus->nr,cpu,threads,thread,err)){-/*-*Wejustremoved1thread,sotakeastep-*backonthreadindexandlowertheupper-*nthreadslimit.-*/-nthreads--;-thread--;--/* ... and pretend like nothing have happened. */-err=0;-continue;-}-pr_debug2_peo("\nsys_perf_event_open failed, error %d\n",err);gototry_fallback;
@@ -2069,6 +2058,14 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,return0;try_fallback:+if(evsel__ignore_missing_thread(evsel,cpus->nr,cpu,threads,thread,err)){+/* We just removed 1 thread, so lower the upper nthreads limit. */+nthreads--;++/* ... and pretend like nothing have happened. */+err=0;+gotoretry_open;+}/**perfstatneedsbetween5and22fdsperCPU.Whenwerunout*ofthemtrytoincreasethelimits.
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:50:30
Em Sat, Aug 21, 2021 at 11:19:29AM +0200, Riccardo Mancini escreveu:
I don't see why bpf_counter__install_pe should get called even if fd=-1,
so I'm moving it to the success path.
This will be useful in following patches to separate the actual open and
the related operations from the fallback mechanisms.
Looks sane, applied.
Next time please use git blame to find the author and add him to the CC
list, like I just did, so that the mistery can be unveiled or a duh!
uttered :-)
- Arnaldo
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:52:59
Em Sat, Aug 21, 2021 at 11:19:30AM +0200, Riccardo Mancini escreveu:
This is another patch in the effort to separate the fallback mechanisms
from the open itself.
In case of precise_ip fallback, the original precise_ip will be stored
in the evsel (it was stored in a local variable) and the open will be
retried. Since the precise_ip fallback will be the first in the chain of
fallbacks, there should be no functional change with this patch.
@@ -1709,42 +1709,29 @@ static void display_attr(struct perf_event_attr *attr)}}-staticintperf_event_open(structevsel*evsel,-pid_tpid,intcpu,intgroup_fd)+boolevsel__precise_ip_fallback(structevsel*evsel){-intprecise_ip=evsel->core.attr.precise_ip;-intfd;--while(1){-pr_debug2_peo("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx",-pid,cpu,group_fd,evsel->open_flags);--fd=sys_perf_event_open(&evsel->core.attr,pid,cpu,group_fd,evsel->open_flags);-if(fd>=0)-break;--/* Do not try less precise if not requested. */-if(!evsel->precise_max)-break;--/*-*Wetriedalltheprecise_ipvalues,andit's-*stillfailing,soleaveittostandardfallback.-*/-if(!evsel->core.attr.precise_ip){-evsel->core.attr.precise_ip=precise_ip;-break;-}+/* Do not try less precise if not requested. */+if(!evsel->precise_max)+returnfalse;-pr_debug2_peo("\nsys_perf_event_open failed, error %d\n",-ENOTSUP);-evsel->core.attr.precise_ip--;-pr_debug2_peo("decreasing precise_ip by one (%d)\n",evsel->core.attr.precise_ip);-display_attr(&evsel->core.attr);+/*+*Wetriedalltheprecise_ipvalues,andit's+*stillfailing,soleaveittostandardfallback.+*/+if(!evsel->core.attr.precise_ip){+evsel->core.attr.precise_ip=evsel->precise_ip_original;+returnfalse;}-returnfd;-}+if(!evsel->precise_ip_original)+evsel->precise_ip_original=evsel->core.attr.precise_ip;+evsel->core.attr.precise_ip--;+pr_debug2_peo("decreasing precise_ip by one (%d)\n",evsel->core.attr.precise_ip);+display_attr(&evsel->core.attr);+returntrue;+}staticstructperf_cpu_map*empty_cpu_map;staticstructperf_thread_map*empty_thread_map;
@@ -2004,8 +1991,11 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,test_attr__ready();-fd=perf_event_open(evsel,pid,cpus->map[cpu],-group_fd);+pr_debug2_peo("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx",+pid,cpus->map[cpu],group_fd,evsel->open_flags);++fd=sys_perf_event_open(&evsel->core.attr,pid,cpus->map[cpu],+group_fd,evsel->open_flags);FD(evsel,cpu,thread)=fd;
@@ -2058,6 +2048,9 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,return0;try_fallback:+if(evsel__precise_ip_fallback(evsel))+gotoretry_open;+if(evsel__ignore_missing_thread(evsel,cpus->nr,cpu,threads,thread,err)){/* We just removed 1 thread, so lower the upper nthreads limit. */nthreads--;
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-08-31 19:54:49
Em Sat, Aug 21, 2021 at 11:19:31AM +0200, Riccardo Mancini escreveu:
This is the final patch splitting evsel__open_cpu.
This patch moves the entire loop code to a separate function, to be
reused for the multithreaded code.
Are you going to use that 'enum perf_event_open_err' somewhere else?
I.e. is there a need to expose it in evsel.h?
I'm stopping at this patch to give the ones I merged so far some
testing, will now push it to tmp.perf/core.
- Arnaldo
Hi Arnaldo,
thanks for your review and your suggestions, and also for the PRIu64 patch.
On Tue, 2021-08-31 at 16:54 -0300, Arnaldo Carvalho de Melo wrote:
Em Sat, Aug 21, 2021 at 11:19:31AM +0200, Riccardo Mancini escreveu:
quoted
This is the final patch splitting evsel__open_cpu.
This patch moves the entire loop code to a separate function, to be
reused for the multithreaded code.
Are you going to use that 'enum perf_event_open_err' somewhere else?
I.e. is there a need to expose it in evsel.h?
Yes, in the next patch (26/37). It's being used to expose a function that just
does the perf_event_open calls for an evsel. It needs to return such structure
to provide information about the error (which return code, at which thread).
I'm stopping at this patch to give the ones I merged so far some
testing, will now push it to tmp.perf/core.
I checked tmp.perf/core and it looks good to me.
I also did some additional tests to check that fallback mechanisms where
working:
check missing pid being ignored (rerun until warning is shown)
$ sudo ./perf bench internals evlist-open-close -i10 -u $UID
check that weak group fallback is working
$ sudo ./perf record -e '{cycles,cache-misses,cache-
references,cpu_clk_unhalted.thread,cycles,cycles,cycles}:W'
check that precision_ip fallback is working:
edited perf-sys.h to make sys_perf_event_open fail if precision_ip > 2
$ sudo ./perf record -e '{cycles,cs}:P'
I've also run perf-test on my machine and it's passing too.
I'm encounteirng one fail on the "BPF filter" test (42), which is present also
in perf/core, so it should not be related to this patch.
Thanks,
Riccardo
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-09-11 19:10:26
Em Fri, Sep 03, 2021 at 11:52:18PM +0200, Riccardo Mancini escreveu:
Hi Arnaldo,
thanks for your review and your suggestions, and also for the PRIu64 patch.
On Tue, 2021-08-31 at 16:54 -0300, Arnaldo Carvalho de Melo wrote:
quoted
Em Sat, Aug 21, 2021 at 11:19:31AM +0200, Riccardo Mancini escreveu:
quoted
This is the final patch splitting evsel__open_cpu.
This patch moves the entire loop code to a separate function, to be
reused for the multithreaded code.
Are you going to use that 'enum perf_event_open_err' somewhere else?
I.e. is there a need to expose it in evsel.h?
Yes, in the next patch (26/37). It's being used to expose a function that just
does the perf_event_open calls for an evsel. It needs to return such structure
to provide information about the error (which return code, at which thread).
quoted
I'm stopping at this patch to give the ones I merged so far some
testing, will now push it to tmp.perf/core.
I checked tmp.perf/core and it looks good to me.
I also did some additional tests to check that fallback mechanisms where
working:
check missing pid being ignored (rerun until warning is shown)
$ sudo ./perf bench internals evlist-open-close -i10 -u $UID
check that weak group fallback is working
$ sudo ./perf record -e '{cycles,cache-misses,cache-
references,cpu_clk_unhalted.thread,cycles,cycles,cycles}:W'
check that precision_ip fallback is working:
edited perf-sys.h to make sys_perf_event_open fail if precision_ip > 2
$ sudo ./perf record -e '{cycles,cs}:P'
I've also run perf-test on my machine and it's passing too.
I'm encounteirng one fail on the "BPF filter" test (42), which is present also
in perf/core, so it should not be related to this patch.
Thanks! I'll try to resume work on it as soon as I have the plumbers
talk ready :-)
- Arnaldo
From: Arnaldo Carvalho de Melo <acme@kernel.org> Date: 2021-10-08 14:38:32
Em Sat, Aug 21, 2021 at 11:19:09AM +0200, Riccardo Mancini escreveu:
Commit a8cbe40fe9f4ba49 ("perf evsel: Add iterator to iterate over events
ordered by CPU") introduced an iterator for evsel.core.cpus inside evsel
which is used to iterate over evlist one CPU at a time.
However, this solution is hacky since it involves a mutable state in the
evsel which is supposed to be unmodified during iteration.
Sice checking that a CPU is within the evsel can be done quickly in
O(logn) time, this patch replaces the aforementioned iterator with a
simple check.
Andi, can you take a look at this and provide your Reviewed-by, please?
- Arnaldo
From: Ian Rogers <irogers@google.com> Date: 2021-12-11 00:21:07
On Sat, Aug 21, 2021 at 2:19 AM Riccardo Mancini [off-list ref] wrote:
Commit a8cbe40fe9f4ba49 ("perf evsel: Add iterator to iterate over events
ordered by CPU") introduced an iterator for evsel.core.cpus inside evsel
which is used to iterate over evlist one CPU at a time.
However, this solution is hacky since it involves a mutable state in the
evsel which is supposed to be unmodified during iteration.
Sice checking that a CPU is within the evsel can be done quickly in
O(logn) time, this patch replaces the aforementioned iterator with a
simple check.
I like this change. It ties in with the CPU vs index refactoring I'm
doing and expanding upon in:
https://lore.kernel.org/lkml/20211208024607.1784932-1-irogers@google.com/
The O(log(n)) look up of the CPU from the CPU map I think can be
improved, but it is good enough for now. We can improve upon it by
recognizing that the CPU we're looking for in the CPU map is 'cpu /
num_cpus' into the map - ie. don't binary search from the middle, but
start from somewhere near where the CPU will be. Something like
interpolation search [1] would do this in O(log(log(n)) time, but
because CPU maps are so regular in layout I'd expect it would be O(1)
for us.
That said, I think the right thing to do here is to introduce a new
struct which would behave like an STL style iterator. STL style
iterators are returned by begin and end functions. They implement a
next() routine as operator ++. Termination of the loop happens when
the iterator equals the end value. The current evlist affinity
iteration needs two loops. The outer loop is iterating over CPUs and
the caller needs to set affinity to the CPU. An inner loop iterates
over the evlist again, skipping until an appropriate evsel is
encountered for the outer loop's CPU. I think it would be better if we
had a single loop, and this loop is going to maintain two pieces of
state in the iterator, the current CPU and evsel. The loop start, next
and end will change the CPU affinity. The next routine will iterate
over every evsel for a CPU and then advance to the next CPU and so on
until all CPUs and the evsels are done. For something like:
$ perf stat -a -e cycles,power/energy-pkg/ -I 1000
The cpumasks are going to be all CPUs for cycles, and on my machine 0
and 18 for energy-pkg. So the iterator will be on CPU 0 with an evsel
for cycles, then for energy-pkg, next for CPU 1 it will have an evsel
for cycles, and so on until CPU 18. For CPU 18 it will be like CPU.0
and have the evsel for cycles and energy-pkg. For CPU 19 it is just
cycles again until you get to CPU 35.
In the API it'd be something like:
struct evlist_cpu_iterator {
int cpu;
struct evsel *evsel;
struct evlist *container;
struct affinity saved_affinity;
};
struct evlist_cpu_iterator evlist__cpu_iter_start(struct evlist *evlist);
void evlist_cpu_iterator__next(struct evlist_cpu_iterator *itr);
bool evlist_cpu_iterator__end(const struct evlist_cpu_iterator *itr);
The advantage of this API is that we know the CPU and evsel without a
lookup (as with the current code), the affinity is hidden in the
abstraction and we get rid of the global iterator state from evsel (as
with this patch) that makes parallelization impossible.
I'm working to add something like this into the cpumap refactoring
effort. That isn't looking to do parallelization but merely to get
correctness, as at the moment CPUs and CPU map indices are often
confused leading to crashes or incorrect behavior. I'm aware that this
will mean rebasing this patch series, but I think that will be easier
as the confusion over CPU and index will be ironed out in my changes.
Thanks,
Ian
[1] https://en.wikipedia.org/wiki/Interpolation_search