[v6,01/13] tools/libperf: introduce notion of static polled file descriptors
diff mbox series

Message ID 40643542-6676-e0bc-2d10-165dfde41e29@linux.intel.com
State New
Headers show
Series
  • perf: support enable and disable commands in stat and record modes
Related show

Commit Message

Alexey Budankov June 1, 2020, 8:05 p.m. UTC
Implement adding of file descriptors by fdarray__add_stat() to
fix-sized (currently 1) stat_entries array located at struct fdarray.
Append added file descriptors to the array used by poll() syscall
during fdarray__poll() call. Copy poll() result of the added
descriptors from the array back to the storage for separate analysis.

Signed-off-by: Alexey Budankov <alexey.budankov@linux.intel.com>
---
 tools/lib/api/fd/array.c                 | 42 +++++++++++++++++++++++-
 tools/lib/api/fd/array.h                 |  7 ++++
 tools/lib/perf/evlist.c                  | 11 +++++++
 tools/lib/perf/include/internal/evlist.h |  2 ++
 4 files changed, 61 insertions(+), 1 deletion(-)

Comments

Adrian Hunter June 3, 2020, 11:38 a.m. UTC | #1
On 1/06/20 11:05 pm, Alexey Budankov wrote:
> 
> Implement adding of file descriptors by fdarray__add_stat() to
> fix-sized (currently 1) stat_entries array located at struct fdarray.
> Append added file descriptors to the array used by poll() syscall
> during fdarray__poll() call. Copy poll() result of the added
> descriptors from the array back to the storage for separate analysis.

Why not instead call evlist__add_pollfd() before other fds are added, so
the fda->entries[] position is always fixed. Then this patch is not needed.

> 
> Signed-off-by: Alexey Budankov <alexey.budankov@linux.intel.com>
> ---
>  tools/lib/api/fd/array.c                 | 42 +++++++++++++++++++++++-
>  tools/lib/api/fd/array.h                 |  7 ++++
>  tools/lib/perf/evlist.c                  | 11 +++++++
>  tools/lib/perf/include/internal/evlist.h |  2 ++
>  4 files changed, 61 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/lib/api/fd/array.c b/tools/lib/api/fd/array.c
> index 58d44d5eee31..b0027f2169c7 100644
> --- a/tools/lib/api/fd/array.c
> +++ b/tools/lib/api/fd/array.c
> @@ -11,10 +11,16 @@
>  
>  void fdarray__init(struct fdarray *fda, int nr_autogrow)
>  {
> +	int i;
> +
>  	fda->entries	 = NULL;
>  	fda->priv	 = NULL;
>  	fda->nr		 = fda->nr_alloc = 0;
>  	fda->nr_autogrow = nr_autogrow;
> +
> +	fda->nr_stat = 0;
> +	for (i = 0; i < FDARRAY__STAT_ENTRIES_MAX; i++)
> +		fda->stat_entries[i].fd = -1;
>  }
>  
>  int fdarray__grow(struct fdarray *fda, int nr)
> @@ -83,6 +89,20 @@ int fdarray__add(struct fdarray *fda, int fd, short revents)
>  	return pos;
>  }
>  
> +int fdarray__add_stat(struct fdarray *fda, int fd, short revents)
> +{
> +	int pos = fda->nr_stat;
> +
> +	if (pos >= FDARRAY__STAT_ENTRIES_MAX)
> +		return -1;
> +
> +	fda->stat_entries[pos].fd = fd;
> +	fda->stat_entries[pos].events = revents;
> +	fda->nr_stat++;
> +
> +	return pos;
> +}
> +
>  int fdarray__filter(struct fdarray *fda, short revents,
>  		    void (*entry_destructor)(struct fdarray *fda, int fd, void *arg),
>  		    void *arg)
> @@ -113,7 +133,27 @@ int fdarray__filter(struct fdarray *fda, short revents,
>  
>  int fdarray__poll(struct fdarray *fda, int timeout)
>  {
> -	return poll(fda->entries, fda->nr, timeout);
> +	int nr, i, pos, res;
> +
> +	nr = fda->nr;
> +
> +	for (i = 0; i < fda->nr_stat; i++) {
> +		if (fda->stat_entries[i].fd != -1) {
> +			pos = fdarray__add(fda, fda->stat_entries[i].fd,
> +					   fda->stat_entries[i].events);
> +			if (pos >= 0)
> +				fda->priv[pos].idx = i;
> +		}
> +	}
> +
> +	res = poll(fda->entries, fda->nr, timeout);
> +
> +	for (i = nr; i < fda->nr; i++)
> +		fda->stat_entries[fda->priv[i].idx] = fda->entries[i];
> +
> +	fda->nr = nr;
> +
> +	return res;
>  }
>  
>  int fdarray__fprintf(struct fdarray *fda, FILE *fp)
> diff --git a/tools/lib/api/fd/array.h b/tools/lib/api/fd/array.h
> index b39557d1a88f..9bca72e80b09 100644
> --- a/tools/lib/api/fd/array.h
> +++ b/tools/lib/api/fd/array.h
> @@ -3,6 +3,7 @@
>  #define __API_FD_ARRAY__
>  
>  #include <stdio.h>
> +#include <poll.h>
>  
>  struct pollfd;
>  
> @@ -16,6 +17,9 @@ struct pollfd;
>   *	  I.e. using 'fda->priv[N].idx = * value' where N < fda->nr is ok,
>   *	  but doing 'fda->priv = malloc(M)' is not allowed.
>   */
> +
> +#define FDARRAY__STAT_ENTRIES_MAX	1
> +
>  struct fdarray {
>  	int	       nr;
>  	int	       nr_alloc;
> @@ -25,6 +29,8 @@ struct fdarray {
>  		int    idx;
>  		void   *ptr;
>  	} *priv;
> +	int	       nr_stat;
> +	struct pollfd  stat_entries[FDARRAY__STAT_ENTRIES_MAX];
>  };
>  
>  void fdarray__init(struct fdarray *fda, int nr_autogrow);
> @@ -34,6 +40,7 @@ struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow);
>  void fdarray__delete(struct fdarray *fda);
>  
>  int fdarray__add(struct fdarray *fda, int fd, short revents);
> +int fdarray__add_stat(struct fdarray *fda, int fd, short revents);
>  int fdarray__poll(struct fdarray *fda, int timeout);
>  int fdarray__filter(struct fdarray *fda, short revents,
>  		    void (*entry_destructor)(struct fdarray *fda, int fd, void *arg),
> diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c
> index 6a875a0f01bb..e68e4c08e7c2 100644
> --- a/tools/lib/perf/evlist.c
> +++ b/tools/lib/perf/evlist.c
> @@ -317,6 +317,17 @@ int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
>  	return pos;
>  }
>  
> +int perf_evlist__add_pollfd_stat(struct perf_evlist *evlist, int fd,
> +			         short revent)
> +{
> +	int pos = fdarray__add_stat(&evlist->pollfd, fd, revent | POLLERR | POLLHUP);
> +
> +	if (pos >= 0)
> +		fcntl(fd, F_SETFL, O_NONBLOCK);
> +
> +	return pos;
> +}
> +
>  static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd,
>  					 void *arg __maybe_unused)
>  {
> diff --git a/tools/lib/perf/include/internal/evlist.h b/tools/lib/perf/include/internal/evlist.h
> index 74dc8c3f0b66..2b3b4518c05e 100644
> --- a/tools/lib/perf/include/internal/evlist.h
> +++ b/tools/lib/perf/include/internal/evlist.h
> @@ -46,6 +46,8 @@ struct perf_evlist_mmap_ops {
>  int perf_evlist__alloc_pollfd(struct perf_evlist *evlist);
>  int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
>  			    void *ptr, short revent);
> +int perf_evlist__add_pollfd_stat(struct perf_evlist *evlist, int fd,
> +			         short revent);
>  
>  int perf_evlist__mmap_ops(struct perf_evlist *evlist,
>  			  struct perf_evlist_mmap_ops *ops,
>
Alexey Budankov June 3, 2020, 12:01 p.m. UTC | #2
Hi,

On 03.06.2020 14:38, Adrian Hunter wrote:
> On 1/06/20 11:05 pm, Alexey Budankov wrote:
>>
>> Implement adding of file descriptors by fdarray__add_stat() to
>> fix-sized (currently 1) stat_entries array located at struct fdarray.
>> Append added file descriptors to the array used by poll() syscall
>> during fdarray__poll() call. Copy poll() result of the added
>> descriptors from the array back to the storage for separate analysis.
> 
> Why not instead call evlist__add_pollfd() before other fds are added, so
> the fda->entries[] position is always fixed. Then this patch is not needed.

It then will block event consumption loop, at least in record mode, due to
change sin initial assumptions behind fdarray__filter(). So extension of the
API with 'static' fds looks safer w.r.t. possible functional regressions at 
the same time extending the API with ability to atomically wait for (poll())
not only event fds but also any other fds during monitoring.

Feel free to ask more.

~Alexey
Adrian Hunter June 3, 2020, 12:23 p.m. UTC | #3
On 3/06/20 3:01 pm, Alexey Budankov wrote:
> Hi,
> 
> On 03.06.2020 14:38, Adrian Hunter wrote:
>> On 1/06/20 11:05 pm, Alexey Budankov wrote:
>>>
>>> Implement adding of file descriptors by fdarray__add_stat() to
>>> fix-sized (currently 1) stat_entries array located at struct fdarray.
>>> Append added file descriptors to the array used by poll() syscall
>>> during fdarray__poll() call. Copy poll() result of the added
>>> descriptors from the array back to the storage for separate analysis.
>>
>> Why not instead call evlist__add_pollfd() before other fds are added, so
>> the fda->entries[] position is always fixed. Then this patch is not needed.
> 
> It then will block event consumption loop, at least in record mode, due to
> change sin initial assumptions behind fdarray__filter(). So extension of the
> API with 'static' fds looks safer w.r.t. possible functional regressions at 
> the same time extending the API with ability to atomically wait for (poll())
> not only event fds but also any other fds during monitoring.

So make fdarray__filter() return the number of filterable fds remaining.
Adrian Hunter June 3, 2020, 12:30 p.m. UTC | #4
> -----Original Message-----
> From: Hunter, Adrian <adrian.hunter@intel.com>
> Sent: Wednesday, June 3, 2020 3:24 PM
> To: Alexey Budankov <alexey.budankov@linux.intel.com>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>; Jiri Olsa
> <jolsa@redhat.com>; Namhyung Kim <namhyung@kernel.org>; Alexander
> Shishkin <alexander.shishkin@linux.intel.com>; Peter Zijlstra
> <peterz@infradead.org>; Ingo Molnar <mingo@redhat.com>; Andi Kleen
> <ak@linux.intel.com>; linux-kernel <linux-kernel@vger.kernel.org>
> Subject: Re: [PATCH v6 01/13] tools/libperf: introduce notion of static polled
> file descriptors
> 
> On 3/06/20 3:01 pm, Alexey Budankov wrote:
> > Hi,
> >
> > On 03.06.2020 14:38, Adrian Hunter wrote:
> >> On 1/06/20 11:05 pm, Alexey Budankov wrote:
> >>>
> >>> Implement adding of file descriptors by fdarray__add_stat() to
> >>> fix-sized (currently 1) stat_entries array located at struct fdarray.
> >>> Append added file descriptors to the array used by poll() syscall
> >>> during fdarray__poll() call. Copy poll() result of the added
> >>> descriptors from the array back to the storage for separate analysis.
> >>
> >> Why not instead call evlist__add_pollfd() before other fds are added,
> >> so the fda->entries[] position is always fixed. Then this patch is not
> needed.
> >
> > It then will block event consumption loop, at least in record mode,
> > due to change sin initial assumptions behind fdarray__filter(). So
> > extension of the API with 'static' fds looks safer w.r.t. possible
> > functional regressions at the same time extending the API with ability
> > to atomically wait for (poll()) not only event fds but also any other fds
> during monitoring.
> 
> So make fdarray__filter() return the number of filterable fds remaining.
> 


Or perhaps simpler, compare the return value to the number of fds that are known not to be filterable
Alexey Budankov June 3, 2020, 12:52 p.m. UTC | #5
On 03.06.2020 15:30, Hunter, Adrian wrote:
> 
> 
>> -----Original Message-----
>> From: Hunter, Adrian <adrian.hunter@intel.com>
>> Sent: Wednesday, June 3, 2020 3:24 PM
>> To: Alexey Budankov <alexey.budankov@linux.intel.com>
>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>; Jiri Olsa
>> <jolsa@redhat.com>; Namhyung Kim <namhyung@kernel.org>; Alexander
>> Shishkin <alexander.shishkin@linux.intel.com>; Peter Zijlstra
>> <peterz@infradead.org>; Ingo Molnar <mingo@redhat.com>; Andi Kleen
>> <ak@linux.intel.com>; linux-kernel <linux-kernel@vger.kernel.org>
>> Subject: Re: [PATCH v6 01/13] tools/libperf: introduce notion of static polled
>> file descriptors
>>
>> On 3/06/20 3:01 pm, Alexey Budankov wrote:
>>> Hi,
>>>
>>> On 03.06.2020 14:38, Adrian Hunter wrote:
>>>> On 1/06/20 11:05 pm, Alexey Budankov wrote:
>>>>>
>>>>> Implement adding of file descriptors by fdarray__add_stat() to
>>>>> fix-sized (currently 1) stat_entries array located at struct fdarray.
>>>>> Append added file descriptors to the array used by poll() syscall
>>>>> during fdarray__poll() call. Copy poll() result of the added
>>>>> descriptors from the array back to the storage for separate analysis.
>>>>
>>>> Why not instead call evlist__add_pollfd() before other fds are added,
>>>> so the fda->entries[] position is always fixed. Then this patch is not
>> needed.
>>>
>>> It then will block event consumption loop, at least in record mode,
>>> due to change sin initial assumptions behind fdarray__filter(). So
>>> extension of the API with 'static' fds looks safer w.r.t. possible
>>> functional regressions at the same time extending the API with ability
>>> to atomically wait for (poll()) not only event fds but also any other fds
>> during monitoring.
>>
>> So make fdarray__filter() return the number of filterable fds remaining.
>>
> 
> 
> Or perhaps simpler, compare the return value to the number of fds that are known not to be filterable

Well, various implementations are for sure possible but the proposed design
avoids implicit code assumptions and dependency on API calls order as in API
implementation as in client code and that is why it's been proposed this way.
Adrian Hunter June 3, 2020, 3:53 p.m. UTC | #6
On 3/06/20 3:52 pm, Alexey Budankov wrote:
> 
> On 03.06.2020 15:30, Hunter, Adrian wrote:
>>
>>
>>> -----Original Message-----
>>> From: Hunter, Adrian <adrian.hunter@intel.com>
>>> Sent: Wednesday, June 3, 2020 3:24 PM
>>> To: Alexey Budankov <alexey.budankov@linux.intel.com>
>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>; Jiri Olsa
>>> <jolsa@redhat.com>; Namhyung Kim <namhyung@kernel.org>; Alexander
>>> Shishkin <alexander.shishkin@linux.intel.com>; Peter Zijlstra
>>> <peterz@infradead.org>; Ingo Molnar <mingo@redhat.com>; Andi Kleen
>>> <ak@linux.intel.com>; linux-kernel <linux-kernel@vger.kernel.org>
>>> Subject: Re: [PATCH v6 01/13] tools/libperf: introduce notion of static polled
>>> file descriptors
>>>
>>> On 3/06/20 3:01 pm, Alexey Budankov wrote:
>>>> Hi,
>>>>
>>>> On 03.06.2020 14:38, Adrian Hunter wrote:
>>>>> On 1/06/20 11:05 pm, Alexey Budankov wrote:
>>>>>>
>>>>>> Implement adding of file descriptors by fdarray__add_stat() to
>>>>>> fix-sized (currently 1) stat_entries array located at struct fdarray.
>>>>>> Append added file descriptors to the array used by poll() syscall
>>>>>> during fdarray__poll() call. Copy poll() result of the added
>>>>>> descriptors from the array back to the storage for separate analysis.
>>>>>
>>>>> Why not instead call evlist__add_pollfd() before other fds are added,
>>>>> so the fda->entries[] position is always fixed. Then this patch is not
>>> needed.
>>>>
>>>> It then will block event consumption loop, at least in record mode,
>>>> due to change sin initial assumptions behind fdarray__filter(). So
>>>> extension of the API with 'static' fds looks safer w.r.t. possible
>>>> functional regressions at the same time extending the API with ability
>>>> to atomically wait for (poll()) not only event fds but also any other fds
>>> during monitoring.
>>>
>>> So make fdarray__filter() return the number of filterable fds remaining.
>>>
>>
>>
>> Or perhaps simpler, compare the return value to the number of fds that are known not to be filterable
> 
> Well, various implementations are for sure possible but the proposed design
> avoids implicit code assumptions and dependency on API calls order as in API
> implementation as in client code and that is why it's been proposed this way.
> 

The dependencies are confined one function, namely __cmd_record(), it is
simpler, and avoids the inelegant changes to fdarray__poll().
Alexey Budankov June 3, 2020, 5 p.m. UTC | #7
On 03.06.2020 18:53, Adrian Hunter wrote:
> On 3/06/20 3:52 pm, Alexey Budankov wrote:
>>
>> On 03.06.2020 15:30, Hunter, Adrian wrote:
>>>
>>>
>>>> -----Original Message-----
>>>> From: Hunter, Adrian <adrian.hunter@intel.com>
>>>> Sent: Wednesday, June 3, 2020 3:24 PM
>>>> To: Alexey Budankov <alexey.budankov@linux.intel.com>
>>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>; Jiri Olsa
>>>> <jolsa@redhat.com>; Namhyung Kim <namhyung@kernel.org>; Alexander
>>>> Shishkin <alexander.shishkin@linux.intel.com>; Peter Zijlstra
>>>> <peterz@infradead.org>; Ingo Molnar <mingo@redhat.com>; Andi Kleen
>>>> <ak@linux.intel.com>; linux-kernel <linux-kernel@vger.kernel.org>
>>>> Subject: Re: [PATCH v6 01/13] tools/libperf: introduce notion of static polled
>>>> file descriptors
>>>>
>>>> On 3/06/20 3:01 pm, Alexey Budankov wrote:
>>>>> Hi,
>>>>>
>>>>> On 03.06.2020 14:38, Adrian Hunter wrote:
>>>>>> On 1/06/20 11:05 pm, Alexey Budankov wrote:
>>>>>>>
>>>>>>> Implement adding of file descriptors by fdarray__add_stat() to
>>>>>>> fix-sized (currently 1) stat_entries array located at struct fdarray.
>>>>>>> Append added file descriptors to the array used by poll() syscall
>>>>>>> during fdarray__poll() call. Copy poll() result of the added
>>>>>>> descriptors from the array back to the storage for separate analysis.
>>>>>>
>>>>>> Why not instead call evlist__add_pollfd() before other fds are added,
>>>>>> so the fda->entries[] position is always fixed. Then this patch is not
>>>> needed.
>>>>>
>>>>> It then will block event consumption loop, at least in record mode,
>>>>> due to change sin initial assumptions behind fdarray__filter(). So
>>>>> extension of the API with 'static' fds looks safer w.r.t. possible
>>>>> functional regressions at the same time extending the API with ability
>>>>> to atomically wait for (poll()) not only event fds but also any other fds
>>>> during monitoring.
>>>>
>>>> So make fdarray__filter() return the number of filterable fds remaining.
>>>>
>>>
>>>
>>> Or perhaps simpler, compare the return value to the number of fds that are known not to be filterable
>>
>> Well, various implementations are for sure possible but the proposed design
>> avoids implicit code assumptions and dependency on API calls order as in API
>> implementation as in client code and that is why it's been proposed this way.
>>
> 
> The dependencies are confined one function, namely __cmd_record(), it is
> simpler, and avoids the inelegant changes to fdarray__poll().

As far the changes don't cause regressions and serve their purpose well
they are acceptable.

Patch
diff mbox series

diff --git a/tools/lib/api/fd/array.c b/tools/lib/api/fd/array.c
index 58d44d5eee31..b0027f2169c7 100644
--- a/tools/lib/api/fd/array.c
+++ b/tools/lib/api/fd/array.c
@@ -11,10 +11,16 @@ 
 
 void fdarray__init(struct fdarray *fda, int nr_autogrow)
 {
+	int i;
+
 	fda->entries	 = NULL;
 	fda->priv	 = NULL;
 	fda->nr		 = fda->nr_alloc = 0;
 	fda->nr_autogrow = nr_autogrow;
+
+	fda->nr_stat = 0;
+	for (i = 0; i < FDARRAY__STAT_ENTRIES_MAX; i++)
+		fda->stat_entries[i].fd = -1;
 }
 
 int fdarray__grow(struct fdarray *fda, int nr)
@@ -83,6 +89,20 @@  int fdarray__add(struct fdarray *fda, int fd, short revents)
 	return pos;
 }
 
+int fdarray__add_stat(struct fdarray *fda, int fd, short revents)
+{
+	int pos = fda->nr_stat;
+
+	if (pos >= FDARRAY__STAT_ENTRIES_MAX)
+		return -1;
+
+	fda->stat_entries[pos].fd = fd;
+	fda->stat_entries[pos].events = revents;
+	fda->nr_stat++;
+
+	return pos;
+}
+
 int fdarray__filter(struct fdarray *fda, short revents,
 		    void (*entry_destructor)(struct fdarray *fda, int fd, void *arg),
 		    void *arg)
@@ -113,7 +133,27 @@  int fdarray__filter(struct fdarray *fda, short revents,
 
 int fdarray__poll(struct fdarray *fda, int timeout)
 {
-	return poll(fda->entries, fda->nr, timeout);
+	int nr, i, pos, res;
+
+	nr = fda->nr;
+
+	for (i = 0; i < fda->nr_stat; i++) {
+		if (fda->stat_entries[i].fd != -1) {
+			pos = fdarray__add(fda, fda->stat_entries[i].fd,
+					   fda->stat_entries[i].events);
+			if (pos >= 0)
+				fda->priv[pos].idx = i;
+		}
+	}
+
+	res = poll(fda->entries, fda->nr, timeout);
+
+	for (i = nr; i < fda->nr; i++)
+		fda->stat_entries[fda->priv[i].idx] = fda->entries[i];
+
+	fda->nr = nr;
+
+	return res;
 }
 
 int fdarray__fprintf(struct fdarray *fda, FILE *fp)
diff --git a/tools/lib/api/fd/array.h b/tools/lib/api/fd/array.h
index b39557d1a88f..9bca72e80b09 100644
--- a/tools/lib/api/fd/array.h
+++ b/tools/lib/api/fd/array.h
@@ -3,6 +3,7 @@ 
 #define __API_FD_ARRAY__
 
 #include <stdio.h>
+#include <poll.h>
 
 struct pollfd;
 
@@ -16,6 +17,9 @@  struct pollfd;
  *	  I.e. using 'fda->priv[N].idx = * value' where N < fda->nr is ok,
  *	  but doing 'fda->priv = malloc(M)' is not allowed.
  */
+
+#define FDARRAY__STAT_ENTRIES_MAX	1
+
 struct fdarray {
 	int	       nr;
 	int	       nr_alloc;
@@ -25,6 +29,8 @@  struct fdarray {
 		int    idx;
 		void   *ptr;
 	} *priv;
+	int	       nr_stat;
+	struct pollfd  stat_entries[FDARRAY__STAT_ENTRIES_MAX];
 };
 
 void fdarray__init(struct fdarray *fda, int nr_autogrow);
@@ -34,6 +40,7 @@  struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow);
 void fdarray__delete(struct fdarray *fda);
 
 int fdarray__add(struct fdarray *fda, int fd, short revents);
+int fdarray__add_stat(struct fdarray *fda, int fd, short revents);
 int fdarray__poll(struct fdarray *fda, int timeout);
 int fdarray__filter(struct fdarray *fda, short revents,
 		    void (*entry_destructor)(struct fdarray *fda, int fd, void *arg),
diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c
index 6a875a0f01bb..e68e4c08e7c2 100644
--- a/tools/lib/perf/evlist.c
+++ b/tools/lib/perf/evlist.c
@@ -317,6 +317,17 @@  int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
 	return pos;
 }
 
+int perf_evlist__add_pollfd_stat(struct perf_evlist *evlist, int fd,
+			         short revent)
+{
+	int pos = fdarray__add_stat(&evlist->pollfd, fd, revent | POLLERR | POLLHUP);
+
+	if (pos >= 0)
+		fcntl(fd, F_SETFL, O_NONBLOCK);
+
+	return pos;
+}
+
 static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd,
 					 void *arg __maybe_unused)
 {
diff --git a/tools/lib/perf/include/internal/evlist.h b/tools/lib/perf/include/internal/evlist.h
index 74dc8c3f0b66..2b3b4518c05e 100644
--- a/tools/lib/perf/include/internal/evlist.h
+++ b/tools/lib/perf/include/internal/evlist.h
@@ -46,6 +46,8 @@  struct perf_evlist_mmap_ops {
 int perf_evlist__alloc_pollfd(struct perf_evlist *evlist);
 int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
 			    void *ptr, short revent);
+int perf_evlist__add_pollfd_stat(struct perf_evlist *evlist, int fd,
+			         short revent);
 
 int perf_evlist__mmap_ops(struct perf_evlist *evlist,
 			  struct perf_evlist_mmap_ops *ops,