[4/7] perf record: Record dropped sample count

Message ID 20230214050452.26390-5-namhyung@kernel.org
State New
Headers
Series perf record: Implement BPF sample filter (v1) |

Commit Message

Namhyung Kim Feb. 14, 2023, 5:04 a.m. UTC
  When it uses bpf filters, event might drop some samples.  It'd be nice
if it can report how many samples it lost.  As LOST_SAMPLES event can
carry the similar information, let's use it for bpf filters.

To indicate it's from BPF filters, add a new misc flag for that and
do not display cpu load warnings.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/builtin-record.c  | 37 ++++++++++++++++++++++--------------
 tools/perf/util/bpf-filter.c |  7 +++++++
 tools/perf/util/bpf-filter.h |  5 +++++
 tools/perf/util/session.c    |  3 ++-
 4 files changed, 37 insertions(+), 15 deletions(-)
  

Comments

Ian Rogers Feb. 14, 2023, 4:40 p.m. UTC | #1
On Mon, Feb 13, 2023 at 9:05 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> When it uses bpf filters, event might drop some samples.  It'd be nice
> if it can report how many samples it lost.  As LOST_SAMPLES event can
> carry the similar information, let's use it for bpf filters.
>
> To indicate it's from BPF filters, add a new misc flag for that and
> do not display cpu load warnings.

Can you potentially have lost samples from being too slow to drain the
ring buffer and dropped samples because of BPF? Is it possible to
distinguish lost and dropped with this approach?

Thanks,
Ian

> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
> ---
>  tools/perf/builtin-record.c  | 37 ++++++++++++++++++++++--------------
>  tools/perf/util/bpf-filter.c |  7 +++++++
>  tools/perf/util/bpf-filter.h |  5 +++++
>  tools/perf/util/session.c    |  3 ++-
>  4 files changed, 37 insertions(+), 15 deletions(-)
>
> diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
> index c81047a78f3e..3201d1a1ea1f 100644
> --- a/tools/perf/builtin-record.c
> +++ b/tools/perf/builtin-record.c
> @@ -1869,24 +1869,16 @@ record__switch_output(struct record *rec, bool at_exit)
>         return fd;
>  }
>
> -static void __record__read_lost_samples(struct record *rec, struct evsel *evsel,
> +static void __record__save_lost_samples(struct record *rec, struct evsel *evsel,
>                                         struct perf_record_lost_samples *lost,
> -                                       int cpu_idx, int thread_idx)
> +                                       int cpu_idx, int thread_idx, u64 lost_count,
> +                                       u16 misc_flag)
>  {
> -       struct perf_counts_values count;
>         struct perf_sample_id *sid;
>         struct perf_sample sample = {};
>         int id_hdr_size;
>
> -       if (perf_evsel__read(&evsel->core, cpu_idx, thread_idx, &count) < 0) {
> -               pr_err("read LOST count failed\n");
> -               return;
> -       }
> -
> -       if (count.lost == 0)
> -               return;
> -
> -       lost->lost = count.lost;
> +       lost->lost = lost_count;
>         if (evsel->core.ids) {
>                 sid = xyarray__entry(evsel->core.sample_id, cpu_idx, thread_idx);
>                 sample.id = sid->id;
> @@ -1895,6 +1887,7 @@ static void __record__read_lost_samples(struct record *rec, struct evsel *evsel,
>         id_hdr_size = perf_event__synthesize_id_sample((void *)(lost + 1),
>                                                        evsel->core.attr.sample_type, &sample);
>         lost->header.size = sizeof(*lost) + id_hdr_size;
> +       lost->header.misc = misc_flag;
>         record__write(rec, NULL, lost, lost->header.size);
>  }
>
> @@ -1918,6 +1911,7 @@ static void record__read_lost_samples(struct record *rec)
>
>         evlist__for_each_entry(session->evlist, evsel) {
>                 struct xyarray *xy = evsel->core.sample_id;
> +               u64 lost_count;
>
>                 if (xy == NULL || evsel->core.fd == NULL)
>                         continue;
> @@ -1929,12 +1923,27 @@ static void record__read_lost_samples(struct record *rec)
>
>                 for (int x = 0; x < xyarray__max_x(xy); x++) {
>                         for (int y = 0; y < xyarray__max_y(xy); y++) {
> -                               __record__read_lost_samples(rec, evsel, lost, x, y);
> +                               struct perf_counts_values count;
> +
> +                               if (perf_evsel__read(&evsel->core, x, y, &count) < 0) {
> +                                       pr_err("read LOST count failed\n");
> +                                       goto out;
> +                               }
> +
> +                               if (count.lost) {
> +                                       __record__save_lost_samples(rec, evsel, lost,
> +                                                                   x, y, count.lost, 0);
> +                               }
>                         }
>                 }
> +
> +               lost_count = perf_bpf_filter__lost_count(evsel);
> +               if (lost_count)
> +                       __record__save_lost_samples(rec, evsel, lost, 0, 0, lost_count,
> +                                                   PERF_RECORD_MISC_LOST_SAMPLES_BPF);
>         }
> +out:
>         free(lost);
> -
>  }
>
>  static volatile sig_atomic_t workload_exec_errno;
> diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c
> index f47420cf81c9..11fb391c92e9 100644
> --- a/tools/perf/util/bpf-filter.c
> +++ b/tools/perf/util/bpf-filter.c
> @@ -76,6 +76,13 @@ int perf_bpf_filter__destroy(struct evsel *evsel)
>         return 0;
>  }
>
> +u64 perf_bpf_filter__lost_count(struct evsel *evsel)
> +{
> +       struct sample_filter_bpf *skel = evsel->bpf_skel;
> +
> +       return skel ? skel->bss->dropped : 0;
> +}
> +
>  struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags,
>                                                        enum perf_bpf_filter_op op,
>                                                        unsigned long val)
> diff --git a/tools/perf/util/bpf-filter.h b/tools/perf/util/bpf-filter.h
> index 6077930073f9..36b44c8188ab 100644
> --- a/tools/perf/util/bpf-filter.h
> +++ b/tools/perf/util/bpf-filter.h
> @@ -22,6 +22,7 @@ struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flag
>  int perf_bpf_filter__parse(struct list_head *expr_head, const char *str);
>  int perf_bpf_filter__prepare(struct evsel *evsel);
>  int perf_bpf_filter__destroy(struct evsel *evsel);
> +u64 perf_bpf_filter__lost_count(struct evsel *evsel);
>
>  #else /* !HAVE_BPF_SKEL */
>
> @@ -38,5 +39,9 @@ static inline int perf_bpf_filter__destroy(struct evsel *evsel)
>  {
>         return -ENOSYS;
>  }
> +static inline u64 perf_bpf_filter__lost_count(struct evsel *evsel)
> +{
> +       return 0;
> +}
>  #endif /* HAVE_BPF_SKEL*/
>  #endif /* PERF_UTIL_BPF_FILTER_H */
> \ No newline at end of file
> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
> index 749d5b5c135b..7d8d057d1772 100644
> --- a/tools/perf/util/session.c
> +++ b/tools/perf/util/session.c
> @@ -1582,7 +1582,8 @@ static int machines__deliver_event(struct machines *machines,
>                         evlist->stats.total_lost += event->lost.lost;
>                 return tool->lost(tool, event, sample, machine);
>         case PERF_RECORD_LOST_SAMPLES:
> -               if (tool->lost_samples == perf_event__process_lost_samples)
> +               if (tool->lost_samples == perf_event__process_lost_samples &&
> +                   !(event->header.misc & PERF_RECORD_MISC_LOST_SAMPLES_BPF))
>                         evlist->stats.total_lost_samples += event->lost_samples.lost;
>                 return tool->lost_samples(tool, event, sample, machine);
>         case PERF_RECORD_READ:
> --
> 2.39.1.581.gbfd45094c4-goog
>
  
Namhyung Kim Feb. 14, 2023, 6:06 p.m. UTC | #2
On Tue, Feb 14, 2023 at 8:41 AM Ian Rogers <irogers@google.com> wrote:
>
> On Mon, Feb 13, 2023 at 9:05 PM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > When it uses bpf filters, event might drop some samples.  It'd be nice
> > if it can report how many samples it lost.  As LOST_SAMPLES event can
> > carry the similar information, let's use it for bpf filters.
> >
> > To indicate it's from BPF filters, add a new misc flag for that and
> > do not display cpu load warnings.
>
> Can you potentially have lost samples from being too slow to drain the
> ring buffer and dropped samples because of BPF? Is it possible to
> distinguish lost and dropped with this approach?

Yeah, the former is exactly what LOST_SAMPLES event gives you.
It should come from the kernel while BPF filters keep a separate
counter for dropped samples and inject LOST_SAMPLES events
with the new misc flag.  So we can differentiate them using the misc
flag and that's how I suppress the warning for BPF dropped ones.

Thanks,
Namhyung
  
Jiri Olsa Feb. 16, 2023, 4:23 p.m. UTC | #3
On Mon, Feb 13, 2023 at 09:04:49PM -0800, Namhyung Kim wrote:

SNIP

> @@ -1929,12 +1923,27 @@ static void record__read_lost_samples(struct record *rec)
>  
>  		for (int x = 0; x < xyarray__max_x(xy); x++) {
>  			for (int y = 0; y < xyarray__max_y(xy); y++) {
> -				__record__read_lost_samples(rec, evsel, lost, x, y);
> +				struct perf_counts_values count;
> +
> +				if (perf_evsel__read(&evsel->core, x, y, &count) < 0) {
> +					pr_err("read LOST count failed\n");
> +					goto out;
> +				}
> +
> +				if (count.lost) {
> +					__record__save_lost_samples(rec, evsel, lost,
> +								    x, y, count.lost, 0);
> +				}
>  			}
>  		}
> +
> +		lost_count = perf_bpf_filter__lost_count(evsel);
> +		if (lost_count)
> +			__record__save_lost_samples(rec, evsel, lost, 0, 0, lost_count,
> +						    PERF_RECORD_MISC_LOST_SAMPLES_BPF);

hi,
I can't see PERF_RECORD_MISC_LOST_SAMPLES_BPF in the tip/perf/core so can't compile,
what do I miss?

thanks,
jirka
  
Arnaldo Carvalho de Melo Feb. 16, 2023, 4:32 p.m. UTC | #4
Em Thu, Feb 16, 2023 at 05:23:05PM +0100, Jiri Olsa escreveu:
> On Mon, Feb 13, 2023 at 09:04:49PM -0800, Namhyung Kim wrote:
> 
> SNIP
> 
> > @@ -1929,12 +1923,27 @@ static void record__read_lost_samples(struct record *rec)
> >  
> >  		for (int x = 0; x < xyarray__max_x(xy); x++) {
> >  			for (int y = 0; y < xyarray__max_y(xy); y++) {
> > -				__record__read_lost_samples(rec, evsel, lost, x, y);
> > +				struct perf_counts_values count;
> > +
> > +				if (perf_evsel__read(&evsel->core, x, y, &count) < 0) {
> > +					pr_err("read LOST count failed\n");
> > +					goto out;
> > +				}
> > +
> > +				if (count.lost) {
> > +					__record__save_lost_samples(rec, evsel, lost,
> > +								    x, y, count.lost, 0);
> > +				}
> >  			}
> >  		}
> > +
> > +		lost_count = perf_bpf_filter__lost_count(evsel);
> > +		if (lost_count)
> > +			__record__save_lost_samples(rec, evsel, lost, 0, 0, lost_count,
> > +						    PERF_RECORD_MISC_LOST_SAMPLES_BPF);
> 
> hi,
> I can't see PERF_RECORD_MISC_LOST_SAMPLES_BPF in the tip/perf/core so can't compile,
> what do I miss?

Humm, but you shouldn't need kernel headers to build tools/perf/, right?

- Arnaldo
  
Jiri Olsa Feb. 16, 2023, 4:34 p.m. UTC | #5
On Thu, Feb 16, 2023 at 01:32:14PM -0300, Arnaldo Carvalho de Melo wrote:
> Em Thu, Feb 16, 2023 at 05:23:05PM +0100, Jiri Olsa escreveu:
> > On Mon, Feb 13, 2023 at 09:04:49PM -0800, Namhyung Kim wrote:
> > 
> > SNIP
> > 
> > > @@ -1929,12 +1923,27 @@ static void record__read_lost_samples(struct record *rec)
> > >  
> > >  		for (int x = 0; x < xyarray__max_x(xy); x++) {
> > >  			for (int y = 0; y < xyarray__max_y(xy); y++) {
> > > -				__record__read_lost_samples(rec, evsel, lost, x, y);
> > > +				struct perf_counts_values count;
> > > +
> > > +				if (perf_evsel__read(&evsel->core, x, y, &count) < 0) {
> > > +					pr_err("read LOST count failed\n");
> > > +					goto out;
> > > +				}
> > > +
> > > +				if (count.lost) {
> > > +					__record__save_lost_samples(rec, evsel, lost,
> > > +								    x, y, count.lost, 0);
> > > +				}
> > >  			}
> > >  		}
> > > +
> > > +		lost_count = perf_bpf_filter__lost_count(evsel);
> > > +		if (lost_count)
> > > +			__record__save_lost_samples(rec, evsel, lost, 0, 0, lost_count,
> > > +						    PERF_RECORD_MISC_LOST_SAMPLES_BPF);
> > 
> > hi,
> > I can't see PERF_RECORD_MISC_LOST_SAMPLES_BPF in the tip/perf/core so can't compile,
> > what do I miss?
> 
> Humm, but you shouldn't need kernel headers to build tools/perf/, right?

right, should be also in tools/include headers

jirka
  
Namhyung Kim Feb. 16, 2023, 6:38 p.m. UTC | #6
Hi Jiri and Arnaldo,

On Thu, Feb 16, 2023 at 05:34:33PM +0100, Jiri Olsa wrote:
> On Thu, Feb 16, 2023 at 01:32:14PM -0300, Arnaldo Carvalho de Melo wrote:
> > Em Thu, Feb 16, 2023 at 05:23:05PM +0100, Jiri Olsa escreveu:
> > > On Mon, Feb 13, 2023 at 09:04:49PM -0800, Namhyung Kim wrote:
> > > 
> > > SNIP
> > > 
> > > > @@ -1929,12 +1923,27 @@ static void record__read_lost_samples(struct record *rec)
> > > >  
> > > >  		for (int x = 0; x < xyarray__max_x(xy); x++) {
> > > >  			for (int y = 0; y < xyarray__max_y(xy); y++) {
> > > > -				__record__read_lost_samples(rec, evsel, lost, x, y);
> > > > +				struct perf_counts_values count;
> > > > +
> > > > +				if (perf_evsel__read(&evsel->core, x, y, &count) < 0) {
> > > > +					pr_err("read LOST count failed\n");
> > > > +					goto out;
> > > > +				}
> > > > +
> > > > +				if (count.lost) {
> > > > +					__record__save_lost_samples(rec, evsel, lost,
> > > > +								    x, y, count.lost, 0);
> > > > +				}
> > > >  			}
> > > >  		}
> > > > +
> > > > +		lost_count = perf_bpf_filter__lost_count(evsel);
> > > > +		if (lost_count)
> > > > +			__record__save_lost_samples(rec, evsel, lost, 0, 0, lost_count,
> > > > +						    PERF_RECORD_MISC_LOST_SAMPLES_BPF);
> > > 
> > > hi,
> > > I can't see PERF_RECORD_MISC_LOST_SAMPLES_BPF in the tip/perf/core so can't compile,
> > > what do I miss?
> > 
> > Humm, but you shouldn't need kernel headers to build tools/perf/, right?
> 
> right, should be also in tools/include headers

Yeah, sorry about that.  I'm not sure how I missed the part.

I put it in tools/lib/perf/include/perf/event.h only as it does nothing
with kernel.  Will fix in v2.

Thanks,
Namhyung

---8<---

diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h
index ad47d7b31046..51b9338f4c11 100644
--- a/tools/lib/perf/include/perf/event.h
+++ b/tools/lib/perf/include/perf/event.h
@@ -70,6 +70,8 @@ struct perf_record_lost {
        __u64                    lost;
 };

+#define PERF_RECORD_MISC_LOST_SAMPLES_BPF (1 << 15)
+
 struct perf_record_lost_samples {
        struct perf_event_header header;
        __u64                    lost;
  

Patch

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index c81047a78f3e..3201d1a1ea1f 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1869,24 +1869,16 @@  record__switch_output(struct record *rec, bool at_exit)
 	return fd;
 }
 
-static void __record__read_lost_samples(struct record *rec, struct evsel *evsel,
+static void __record__save_lost_samples(struct record *rec, struct evsel *evsel,
 					struct perf_record_lost_samples *lost,
-					int cpu_idx, int thread_idx)
+					int cpu_idx, int thread_idx, u64 lost_count,
+					u16 misc_flag)
 {
-	struct perf_counts_values count;
 	struct perf_sample_id *sid;
 	struct perf_sample sample = {};
 	int id_hdr_size;
 
-	if (perf_evsel__read(&evsel->core, cpu_idx, thread_idx, &count) < 0) {
-		pr_err("read LOST count failed\n");
-		return;
-	}
-
-	if (count.lost == 0)
-		return;
-
-	lost->lost = count.lost;
+	lost->lost = lost_count;
 	if (evsel->core.ids) {
 		sid = xyarray__entry(evsel->core.sample_id, cpu_idx, thread_idx);
 		sample.id = sid->id;
@@ -1895,6 +1887,7 @@  static void __record__read_lost_samples(struct record *rec, struct evsel *evsel,
 	id_hdr_size = perf_event__synthesize_id_sample((void *)(lost + 1),
 						       evsel->core.attr.sample_type, &sample);
 	lost->header.size = sizeof(*lost) + id_hdr_size;
+	lost->header.misc = misc_flag;
 	record__write(rec, NULL, lost, lost->header.size);
 }
 
@@ -1918,6 +1911,7 @@  static void record__read_lost_samples(struct record *rec)
 
 	evlist__for_each_entry(session->evlist, evsel) {
 		struct xyarray *xy = evsel->core.sample_id;
+		u64 lost_count;
 
 		if (xy == NULL || evsel->core.fd == NULL)
 			continue;
@@ -1929,12 +1923,27 @@  static void record__read_lost_samples(struct record *rec)
 
 		for (int x = 0; x < xyarray__max_x(xy); x++) {
 			for (int y = 0; y < xyarray__max_y(xy); y++) {
-				__record__read_lost_samples(rec, evsel, lost, x, y);
+				struct perf_counts_values count;
+
+				if (perf_evsel__read(&evsel->core, x, y, &count) < 0) {
+					pr_err("read LOST count failed\n");
+					goto out;
+				}
+
+				if (count.lost) {
+					__record__save_lost_samples(rec, evsel, lost,
+								    x, y, count.lost, 0);
+				}
 			}
 		}
+
+		lost_count = perf_bpf_filter__lost_count(evsel);
+		if (lost_count)
+			__record__save_lost_samples(rec, evsel, lost, 0, 0, lost_count,
+						    PERF_RECORD_MISC_LOST_SAMPLES_BPF);
 	}
+out:
 	free(lost);
-
 }
 
 static volatile sig_atomic_t workload_exec_errno;
diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c
index f47420cf81c9..11fb391c92e9 100644
--- a/tools/perf/util/bpf-filter.c
+++ b/tools/perf/util/bpf-filter.c
@@ -76,6 +76,13 @@  int perf_bpf_filter__destroy(struct evsel *evsel)
 	return 0;
 }
 
+u64 perf_bpf_filter__lost_count(struct evsel *evsel)
+{
+	struct sample_filter_bpf *skel = evsel->bpf_skel;
+
+	return skel ? skel->bss->dropped : 0;
+}
+
 struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags,
 						       enum perf_bpf_filter_op op,
 						       unsigned long val)
diff --git a/tools/perf/util/bpf-filter.h b/tools/perf/util/bpf-filter.h
index 6077930073f9..36b44c8188ab 100644
--- a/tools/perf/util/bpf-filter.h
+++ b/tools/perf/util/bpf-filter.h
@@ -22,6 +22,7 @@  struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flag
 int perf_bpf_filter__parse(struct list_head *expr_head, const char *str);
 int perf_bpf_filter__prepare(struct evsel *evsel);
 int perf_bpf_filter__destroy(struct evsel *evsel);
+u64 perf_bpf_filter__lost_count(struct evsel *evsel);
 
 #else /* !HAVE_BPF_SKEL */
 
@@ -38,5 +39,9 @@  static inline int perf_bpf_filter__destroy(struct evsel *evsel)
 {
 	return -ENOSYS;
 }
+static inline u64 perf_bpf_filter__lost_count(struct evsel *evsel)
+{
+	return 0;
+}
 #endif /* HAVE_BPF_SKEL*/
 #endif /* PERF_UTIL_BPF_FILTER_H */
\ No newline at end of file
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 749d5b5c135b..7d8d057d1772 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1582,7 +1582,8 @@  static int machines__deliver_event(struct machines *machines,
 			evlist->stats.total_lost += event->lost.lost;
 		return tool->lost(tool, event, sample, machine);
 	case PERF_RECORD_LOST_SAMPLES:
-		if (tool->lost_samples == perf_event__process_lost_samples)
+		if (tool->lost_samples == perf_event__process_lost_samples &&
+		    !(event->header.misc & PERF_RECORD_MISC_LOST_SAMPLES_BPF))
 			evlist->stats.total_lost_samples += event->lost_samples.lost;
 		return tool->lost_samples(tool, event, sample, machine);
 	case PERF_RECORD_READ: