Skip to contentSkip to conversation

: perf: reduce data loss when profiling highly parallel CPU bound workloads

PATCH Superseded Alexey Budankov 2 participants 16 messages Oct 9, 2018 – Oct 11, 2018 perf events

v12 of 15 Superseded Latest is v15 →

lore
Share & tools
lore
#0 lore raw

v12 cover : perf: reduce data loss when profiling highly parallel CPU bound workloads

Currently in record mode the tool implements trace writing serially. The algorithm loops over mapped per-cpu data buffers and stores ready data chunks into a trace file using write() system call.

At some circumstances the kernel may lack free space in a buffer because the other buffer's half is not yet written to disk due to some other buffer's data writing by the tool at the moment.

Thus serial trace writing implementation may cause the kernel to loose profiling data and that is what observed when profiling highly parallel CPU bound workloads on machines with big number of cores.

Experiment with profiling matrix multiplication code executing 128 threads on Intel Xeon Phi (KNM) with 272 cores, like below, demonstrates data loss metrics value of 98%:

/usr/bin/time perf record -o /tmp/perf-ser.data -a -N -B -T -R -g \
    --call-graph dwarf,1024 --user-regs=IP,SP,BP --switch-events \
    -e cycles,instructions,ref-cycles,software/period=1,name=cs,config=0x3/Duk -- \
    matrix.gcc

Data loss metrics is the ratio lost_time/elapsed_time where lost_time is the sum of time intervals containing PERF_RECORD_LOST records and elapsed_time is the elapsed application run time under profiling.

Applying asynchronous trace streaming thru Posix AIO API [1] lowers data loss metrics value providing 2x improvement (from 98% to ~1%)

Asynchronous trace streaming is currently limited to glibc linkage. musl libc [5] also provides Posix AIO API implementation, however the patchkit is not tested with it. There may be other libc libraries linked by Perf tool that currently lack Posix AIO API support [2], [3], [4] so NO_AIO define may be used to limit Perf tool binary to serial streaming only.

---
Alexey Budankov (3):
perf util: map data buffer for preserving collected data perf record: enable asynchronous trace writing perf record: extend trace writing to multi AIO

 tools/perf/Documentation/perf-record.txt |   5 +
 tools/perf/Makefile.config               |   5 +
 tools/perf/Makefile.perf                 |   7 +-
 tools/perf/builtin-record.c              | 253 ++++++++++++++++++++++++++++++-
 tools/perf/perf.h                        |   1 +
 tools/perf/util/evlist.c                 |   6 +-
 tools/perf/util/evlist.h                 |   2 +-
 tools/perf/util/mmap.c                   | 165 +++++++++++++++++++-
 tools/perf/util/mmap.h                   |  17 ++-
 9 files changed, 450 insertions(+), 11 deletions(-)

---
Changes in v12:
- applied stub functions design for the whole patch kit
- grouped AIO related data into a struct under struct perf_mmap
- implemented record__aio_get/set_pos(), record__aio_enabled()
- implemented simple --aio option
- extended --aio option to --aio-cblocks=<n>
Changes in v11:
- replacing the both lseek() syscalls in every loop iteration by the only
two syscalls just before and after the loop at record__mmap_read_evlist() and advancing *in-flight* off file pos value at perf_mmap__aio_push()
Changes in v10:
- moved specific code to perf_mmap__aio_mmap(), perf_mmap__aio_munmap();
- adjusted error reporting by using %m
- avoided lseek() setting file pos back in case of record__aio_write() failure
- compacted code selecting between serial and AIO streaming
- optimized call places of record__mmap_read_sync()
- added description of aio-cblocks option into perf-record.txt
Changes in v9:
- enable AIO streaming only when --aio-cblocks option is specified explicitly
- enable AIO based implementation when linking with glibc only
- define NO_AIO to limit Perf binary to serial implementation
Changes in v8:
- run the whole thing thru checkpatch.pl and corrected found issues except lines longer than 80 symbols
- corrected comments alignment and formatting
- moved multi AIO implementation into 3rd patch in the series
- implemented explicit cblocks array allocation
- split AIO completion check into separate record__aio_complete()
- set nr_cblocks default to 1 and max allowed value to 4
Changes in v7:
- implemented handling record.aio setting from perfconfig file
Changes in v6:
- adjusted setting of priorities for cblocks;
- handled errno == EAGAIN case from aio_write() return;
Changes in v5:
- resolved livelock on perf record -e intel_pt// -- dd if=/dev/zero of=/dev/null count=100000
- data loss metrics decreased from 25% to 2x in trialed configuration;
- reshaped layout of data structures;
- implemented --aio option;
- avoided nanosleep() prior calling aio_suspend();
- switched to per-cpu aio multi buffer record__aio_sync();
- record_mmap_read_sync() now does global sync just before
switching trace file or collection stop;
Changes in v4:
- converted mmap()/munmap() to malloc()/free() for mmap->data buffer management
- converted void *bf to struct perf_mmap *md in signatures
- written comment in perf_mmap__push() just before perf_mmap__get();
- written comment in record__mmap_read_sync() on possible restarting
of aio_write() operation and releasing perf_mmap object after all;
- added perf_mmap__put() for the cases of failed aio_write();
Changes in v3:
- written comments about nanosleep(0.5ms) call prior aio_suspend()
to cope with intrusiveness of its implementation in glibc;
- written comments about rationale behind coping profiling data
into mmap->data buffer;
Changes in v2:
- converted zalloc() to calloc() for allocation of mmap_aio array,
- cleared typo and adjusted fallback branch code;

---

[1] http://man7.org/linux/man-pages/man7/aio.7.html
[2] https://android.googlesource.com/platform/bionic/+/master/docs/status.md
[3] https://www.uclibc.org/
[4] https://uclibc-ng.org/
[5] https://www.musl-libc.org/

#1 lore raw

PATCH v12 1/3 perf util: map data buffer for preserving collected data

The map->data buffer is used to preserve map->base profiling data for writing to disk. AIO map->cblock is used to queue corresponding map->data buffer for asynchronous writing.

---
Changes in v12:
- applied stub functions design for the whole patch kit
- grouped AIO related data into a struct under struct perf_mmap
Changes in v10:
- moved specific code to perf_mmap__aio_mmap(), perf_mmap__aio_munmap()
- adjusted error reporting by using %m
Changes in v9:
- implemented NO_AIO and HAVE_AIO_SUPPORT defines to cover cases of libc implementations without Posix AIO API support
Changes in v7:
- implemented handling record.aio setting from perfconfig file
Changes in v6:
- adjusted setting of priorities for cblocks;
Changes in v5:
- reshaped layout of data structures;
- implemented --aio option;
Changes in v4:
- converted mmap()/munmap() to malloc()/free() for mmap->data buffer management
Changes in v2:
- converted zalloc() to calloc() for allocation of mmap_aio array,
- cleared typo and adjusted fallback branch code;

---
 tools/perf/Makefile.config |  5 +++++
 tools/perf/Makefile.perf   |  7 ++++++-
 tools/perf/util/evlist.c   |  2 +-
 tools/perf/util/mmap.c     | 51 +++++++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/mmap.h     | 11 +++++++++-
 5 files changed, 72 insertions(+), 4 deletions(-)
tools/perf/Makefile.config +5 -0
index f6d1a03c7523..2e90f4ce9214 100644
@@ -355,6 +355,11 @@ endif # NO_LIBELF
 
 ifeq ($(feature-glibc), 1)
   CFLAGS += -DHAVE_GLIBC_SUPPORT
+  ifndef NO_AIO
+    ifndef BIONIC
+        CFLAGS += -DHAVE_AIO_SUPPORT
+    endif
+  endif
 endif
 
 ifdef NO_DWARF
tools/perf/Makefile.perf +6 -1
index 92514fb3689f..7becc6a72cf2 100644
@@ -97,8 +97,13 @@ include ../scripts/utilities.mak
 # Define LIBCLANGLLVM if you DO want builtin clang and llvm support.
 # When selected, pass LLVM_CONFIG=/path/to/llvm-config to `make' if
 # llvm-config is not in $PATH.
-
+#
 # Define NO_CORESIGHT if you do not want support for CoreSight trace decoding.
+#
+# Define NO_AIO if you do not want support of Posix AIO based trace
+# streaming for record mode. Currently Posix AIO trace streaming is
+# supported only when linking with glibc.
+#
 
 # As per kernel Makefile, avoid funny character set dependencies
 unexport LC_ALL
tools/perf/util/evlist.c +1 -1
index be440df29615..1a83bf2c069c 100644
@@ -1028,7 +1028,7 @@ int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
 	 * Its value is decided by evsel's write_backward.
 	 * So &mp should not be passed through const pointer.
 	 */
-	struct mmap_params mp;
+	struct mmap_params mp = { .nr_cblocks = 0 };
 
 	if (!evlist->mmap)
 		evlist->mmap = perf_evlist__alloc_mmap(evlist, false);
tools/perf/util/mmap.c +50 -1

71 lines not on this page · raw message

tools/perf/util/mmap.h +10 -1
index e603314dc792..a46dbdcdcc8a 100644
@@ -6,6 +6,9 @@
 #include <linux/types.h>
 #include <asm/barrier.h>
 #include <stdbool.h>
+#ifdef HAVE_AIO_SUPPORT
+#include <aio.h>
+#endif
 #include "auxtrace.h"
 #include "event.h"
 
@@ -26,6 +29,12 @@ struct perf_mmap {
 	bool		 overwrite;
 	struct auxtrace_mmap auxtrace_mmap;
 	char		 event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8);
+#ifdef HAVE_AIO_SUPPORT
+	struct {
+		void 		 *data;
+		struct aiocb	 cblock;
+	} aio;
+#endif
 };
 
 /*
@@ -57,7 +66,7 @@ enum bkw_mmap_state {
 };
 
 struct mmap_params {
-	int			    prot, mask;
+	int			    prot, mask, nr_cblocks;
 	struct auxtrace_mmap_params auxtrace_mp;
 };
 
#2 lore raw

PATCH v12 2/3 : perf record: enable asynchronous trace writing

In reply to Alexey Budankov

Trace file offset is read once before mmaps iterating loop and written back after all performance data enqueued for aio writing. Trace file offset is incremented linearly after every successful aio write operation.

record__aio_sync() blocks till completion of started AIO operation and then proceeds.

record__mmap_read_sync() implements a barrier for all incomplete aio write requests.

---
Changes in v12:
- implemented record__aio_get/set_pos(), record__aio_enabled()
- implemented simple --aio option
Changes in v11:
- replacing the both lseek() syscalls in every loop iteration by the only
two syscalls just before and after the loop at record__mmap_read_evlist() and advancing *in-flight* off file pos value at perf_mmap__aio_push()
Changes in v10:
- avoided lseek() setting file pos back in case of record__aio_write() failure
- compacted code selecting between serial and AIO streaming
- optimized call places of record__mmap_read_sync()
Changes in v9:
- enable AIO streaming only when --aio-cblocks option is specified explicitly Changes in v8:
- split AIO completion check into separate record__aio_complete()
Changes in v6:
- handled errno == EAGAIN case from aio_write();
Changes in v5:
- data loss metrics decreased from 25% to 2x in trialed configuration;
- avoided nanosleep() prior calling aio_suspend();
- switched to per cpu multi record__aio_sync() aio
- record_mmap_read_sync() now does global barrier just before
switching trace file or collection stop;
- resolved livelock on perf record -e intel_pt// -- dd if=/dev/zero of=/dev/null count=100000 Changes in v4:
- converted void *bf to struct perf_mmap *md in signatures
- written comment in perf_mmap__push() just before perf_mmap__get();
- written comment in record__mmap_read_sync() on possible restarting
of aio_write() operation and releasing perf_mmap object after all;
- added perf_mmap__put() for the cases of failed aio_write();
Changes in v3:
- written comments about nanosleep(0.5ms) call prior aio_suspend()
to cope with intrusiveness of its implementation in glibc;
- written comments about rationale behind coping profiling data
into mmap->data buffer;

---
 tools/perf/Documentation/perf-record.txt |   5 +
 tools/perf/builtin-record.c              | 220 ++++++++++++++++++++++++++++++-
 tools/perf/perf.h                        |   1 +
 tools/perf/util/evlist.c                 |   6 +-
 tools/perf/util/evlist.h                 |   2 +-
 tools/perf/util/mmap.c                   |  86 +++++++++++-
 tools/perf/util/mmap.h                   |   5 +
 7 files changed, 316 insertions(+), 9 deletions(-)
tools/perf/Documentation/perf-record.txt +5 -0
index 246dee081efd..5cedb3e75434 100644
@@ -435,6 +435,11 @@ Specify vmlinux path which has debuginfo.
 --buildid-all::
 Record build-id of all DSOs regardless whether it's actually hit or not.
 
+--aio::
+Enable asynchronous (Posix AIO) trace writing mode. 
+Asynchronous mode is supported only when linking Perf tool with libc library
+providing implementation for Posix AIO API.
+
 --all-kernel::
 Configure all used events to run in kernel space.
 
tools/perf/builtin-record.c +216 -4

290 lines not on this page · raw message

tools/perf/perf.h +1 -0
index 21bf7f5a3cf5..0a1ae2ae567a 100644
@@ -82,6 +82,7 @@ struct record_opts {
 	bool         use_clockid;
 	clockid_t    clockid;
 	unsigned int proc_map_timeout;
+	int	     nr_cblocks;
 };
 
 struct option;
tools/perf/util/evlist.c +3 -3
index 1a83bf2c069c..6593bd0dc2af 100644
@@ -1018,7 +1018,7 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,
  */
 int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
 			 unsigned int auxtrace_pages,
-			 bool auxtrace_overwrite)
+			 bool auxtrace_overwrite, int nr_cblocks)
 {
 	struct perf_evsel *evsel;
 	const struct cpu_map *cpus = evlist->cpus;
@@ -1028,7 +1028,7 @@ int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
 	 * Its value is decided by evsel's write_backward.
 	 * So &mp should not be passed through const pointer.
 	 */
-	struct mmap_params mp = { .nr_cblocks = 0 };
+	struct mmap_params mp = { .nr_cblocks = nr_cblocks };
 
 	if (!evlist->mmap)
 		evlist->mmap = perf_evlist__alloc_mmap(evlist, false);
@@ -1060,7 +1060,7 @@ int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
 
 int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages)
 {
-	return perf_evlist__mmap_ex(evlist, pages, 0, false);
+	return perf_evlist__mmap_ex(evlist, pages, 0, false, 0);
 }
 
 int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
tools/perf/util/evlist.h +1 -1
index dc66436add98..2464463879b4 100644
@@ -162,7 +162,7 @@ unsigned long perf_event_mlock_kb_in_pages(void);
 
 int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
 			 unsigned int auxtrace_pages,
-			 bool auxtrace_overwrite);
+			 bool auxtrace_overwrite, int nr_cblocks);
 int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages);
 void perf_evlist__munmap(struct perf_evlist *evlist);
 
tools/perf/util/mmap.c +85 -1

98 lines not on this page · raw message

tools/perf/util/mmap.h +5 -0
index a46dbdcdcc8a..04ff4d2ffdbe 100644
@@ -12,6 +12,7 @@
 #include "auxtrace.h"
 #include "event.h"
 
+struct aiocb;
 /**
  * struct perf_mmap - perf's ring buffer mmap details
  *
@@ -33,6 +34,7 @@ struct perf_mmap {
 	struct {
 		void 		 *data;
 		struct aiocb	 cblock;
+		int		 nr_cblocks;
 	} aio;
 #endif
 };
@@ -103,6 +105,9 @@ union perf_event *perf_mmap__read_event(struct perf_mmap *map);
 
 int perf_mmap__push(struct perf_mmap *md, void *to,
 		    int push(struct perf_mmap *map, void *to, void *buf, size_t size));
+int perf_mmap__aio_push(struct perf_mmap *md, void *to,
+			int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off),
+			off_t *off);
 
 size_t perf_mmap__mmap_len(struct perf_mmap *map);
#3 lore raw

Re: [PATCH v12 2/3]: perf record: enable asynchronous trace writing

On Tue, Oct 09, 2018 at 11:58:53AM +0300, Alexey Budankov wrote:

SNIP

Quoted 25 lines
tools/perf/builtin-record.c +13 -1
@@ -329,7 +469,7 @@ static int record__mmap_evlist(struct record *rec,
 
 	if (perf_evlist__mmap_ex(evlist, opts->mmap_pages,
 				 opts->auxtrace_mmap_pages,
-				 opts->auxtrace_snapshot_mode) < 0) {
+				 opts->auxtrace_snapshot_mode, opts->nr_cblocks) < 0) {
 		if (errno == EPERM) {
 			pr_err("Permission error mapping pages.\n"
 			       "Consider increasing "
@@ -513,6 +653,29 @@ static struct perf_event_header finished_round_event = {
 	.type = PERF_RECORD_FINISHED_ROUND,
 };
 
+#ifdef HAVE_AIO_SUPPORT
+static void record__mmap_read_sync(struct record *rec)
+{
+	int i;
+	struct perf_evlist *evlist = rec->evlist;
+	struct perf_mmap *maps = evlist->mmap;
+
+	if (!rec->opts.nr_cblocks)
+		return;
+
+	for (i = 0; i < evlist->nr_mmaps; i++) {
+		struct perf_mmap *map = &maps[i];

tools/perf/builtin-record.c static struct perf_event_header finished_round_event = { · line 677

Quoted 10 lines
tools/perf/builtin-record.c +10 -0
+
+		if (map->base)
+			record__aio_sync(map);
+	}
+}
+#else
+static void record__mmap_read_sync(struct record *rec __maybe_unused)
+{
+}
+#endif

please move this up so we have just one 'ifdef HAVE_AIO_SUPPORT' block

and change the name to record__aio_mmap_read_sync, so it's obvious it's aio related, like the rest of the functions you added

thanks,
jirka

#4 lore raw

Re: [PATCH v12 2/3]: perf record: enable asynchronous trace writing

Quoted 42 lines · Jiri Olsa

On Tue, Oct 09, 2018 at 11:58:53AM +0300, Alexey Budankov wrote:

SNIP

Quoted 25 lines
tools/perf/builtin-record.c +13 -1
@@ -329,7 +469,7 @@ static int record__mmap_evlist(struct record *rec,
 
 	if (perf_evlist__mmap_ex(evlist, opts->mmap_pages,
 				 opts->auxtrace_mmap_pages,
-				 opts->auxtrace_snapshot_mode) < 0) {
+				 opts->auxtrace_snapshot_mode, opts->nr_cblocks) < 0) {
 		if (errno == EPERM) {
 			pr_err("Permission error mapping pages.\n"
 			       "Consider increasing "
@@ -513,6 +653,29 @@ static struct perf_event_header finished_round_event = {
 	.type = PERF_RECORD_FINISHED_ROUND,
 };
 
+#ifdef HAVE_AIO_SUPPORT
+static void record__mmap_read_sync(struct record *rec)
+{
+	int i;
+	struct perf_evlist *evlist = rec->evlist;
+	struct perf_mmap *maps = evlist->mmap;
+
+	if (!rec->opts.nr_cblocks)
+		return;
+
+	for (i = 0; i < evlist->nr_mmaps; i++) {
+		struct perf_mmap *map = &maps[i];

tools/perf/builtin-record.c static struct perf_event_header finished_round_event = { · line 677

Quoted 10 lines
tools/perf/builtin-record.c +10 -0
+
+		if (map->base)
+			record__aio_sync(map);
+	}
+}
+#else
+static void record__mmap_read_sync(struct record *rec __maybe_unused)
+{
+}
+#endif

please move this up so we have just one 'ifdef HAVE_AIO_SUPPORT' block

Quoted 2 lines

and change the name to record__aio_mmap_read_sync, so it's obvious it's aio related, like the rest of the functions you added

Accepted.

Quoted 4 lines

thanks,
jirka

#5 lore raw

Re: [PATCH v12 2/3]: perf record: enable asynchronous trace writing

In reply to Alexey Budankov

On Tue, Oct 09, 2018 at 11:58:53AM +0300, Alexey Budankov wrote:

SNIP

Quoted 9 lines
tools/perf/builtin-record.c +5 -0
@@ -1378,6 +1566,22 @@ static int parse_clockid(const struct option *opt, const char *str, int unset)
 	return -1;
 }
 
+#ifdef HAVE_AIO_SUPPORT
+static int record__aio_parse(const struct option *opt,
+			     const char *str __maybe_unused,
+			     int unset)
+{

tools/perf/builtin-record.c static int parse_clockid(const struct option *opt, const char *str, int unset) · line 1583

Quoted 10 lines
tools/perf/builtin-record.c +10 -0
+	struct record_opts *opts = (struct record_opts *)opt->value;
+
+	if (unset)
+		opts->nr_cblocks = 0;
+	else
+		opts->nr_cblocks = 1;
+
+	return 0;
+}
+#endif

same, please move this up to have just one 'ifdef HAVE_AIO_SUPPORT' block

thanks,
jirka

#6 lore raw

Re: [PATCH v12 2/3]: perf record: enable asynchronous trace writing

Quoted 17 lines · Jiri Olsa

On Tue, Oct 09, 2018 at 11:58:53AM +0300, Alexey Budankov wrote:

SNIP

Quoted 13 lines
tools/perf/builtin-record.c +9 -0
@@ -1378,6 +1566,22 @@ static int parse_clockid(const struct option *opt, const char *str, int unset)
 	return -1;
 }
 
+#ifdef HAVE_AIO_SUPPORT
+static int record__aio_parse(const struct option *opt,
+			     const char *str __maybe_unused,
+			     int unset)
+{
+	struct record_opts *opts = (struct record_opts *)opt->value;
+
+	if (unset)
+		opts->nr_cblocks = 0;
Quoted 8 lines

tools/perf/builtin-record.c static int parse_clockid(const struct option *opt, const char *str, int unset) · line 1583

Quoted 6 lines
tools/perf/builtin-record.c +6 -0
+	else
+		opts->nr_cblocks = 1;
+
+	return 0;
+}
+#endif

same, please move this up to have just one 'ifdef HAVE_AIO_SUPPORT' block

Accepted.

Quoted 4 lines

thanks,
jirka

#7 lore raw

Re: [PATCH v12 2/3]: perf record: enable asynchronous trace writing

In reply to Alexey Budankov

On Tue, Oct 09, 2018 at 11:58:53AM +0300, Alexey Budankov wrote:

SNIP

Quoted 71 lines
(quoted diff) +71 -0
+#ifdef HAVE_AIO_SUPPORT
+int perf_mmap__aio_push(struct perf_mmap *md, void *to,
+			int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off),
+			off_t *off)
+{
+	u64 head = perf_mmap__read_head(md);
+	unsigned char *data = md->base + page_size;
+	unsigned long size, size0 = 0;
+	void *buf;
+	int rc = 0;
+
+	rc = perf_mmap__read_init(md);
+	if (rc < 0)
+		return (rc == -EAGAIN) ? 0 : -1;
+
+	/*
+	 * md->base data is copied into md->data buffer to
+	 * release space in the kernel buffer as fast as possible,
+	 * thru perf_mmap__consume() below.
+	 *
+	 * That lets the kernel to proceed with storing more
+	 * profiling data into the kernel buffer earlier than other
+	 * per-cpu kernel buffers are handled.
+	 *
+	 * Coping can be done in two steps in case the chunk of
+	 * profiling data crosses the upper bound of the kernel buffer.
+	 * In this case we first move part of data from md->start
+	 * till the upper bound and then the reminder from the
+	 * beginning of the kernel buffer till the end of
+	 * the data chunk.
+	 */
+
+	size = md->end - md->start;
+
+	if ((md->start & md->mask) + size != (md->end & md->mask)) {
+		buf = &data[md->start & md->mask];
+		size = md->mask + 1 - (md->start & md->mask);
+		md->start += size;
+		memcpy(md->aio.data, buf, size);
+		size0 = size;
+	}
+
+	buf = &data[md->start & md->mask];
+	size = md->end - md->start;
+	md->start += size;
+	memcpy(md->aio.data + size0, buf, size);
+
+	/*
+	 * Increment md->refcount to guard md->data buffer
+	 * from premature deallocation because md object can be
+	 * released earlier than aio write request started
+	 * on mmap->data is complete.
+	 *
+	 * perf_mmap__put() is done at record__aio_complete()
+	 * after started request completion.
+	 */
+	perf_mmap__get(md);
+
+	md->prev = head;
+	perf_mmap__consume(md);
+
+	rc = push(to, &(md->aio.cblock), md->aio.data, size0 + size, *off);
+	if (!rc) {
+		*off += size0 + size;
+	} else {
+		/*
+		 * Decrement md->refcount back if aio write
+		 * operation failed to start.
+		 */
+		perf_mmap__put(md);
+	}

Review

Quoted 10 lines
(quoted diff) +10 -0
+
+	return rc;
+}
+#else
+int perf_mmap__aio_push(struct perf_mmap *md __maybe_unused, void *to __maybe_unused,
+	int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off) __maybe_unused,
+	off_t *off __maybe_unused)
+{
+	return 0;
+}

I think you need to put this one in the header as static inline otherwise it'd still appear in the NO_AIO=1 build, like:
[jolsa@krava perf]$ make NO_AIO=1
...
[jolsa@krava perf]$ nm -D perf | grep perf_mmap__aio_push 00000000004c2be0 T perf_mmap__aio_push

change below makes it disappear completely

jirka

---

tools/perf/util/mmap.c +0 -7
index b176d88b3fcb..8c7516696891 100644
@@ -445,13 +445,6 @@ int perf_mmap__aio_push(struct perf_mmap *md, void *to,
 
 	return rc;
 }
-#else
-int perf_mmap__aio_push(struct perf_mmap *md __maybe_unused, void *to __maybe_unused,
-	int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off) __maybe_unused,
-	off_t *off __maybe_unused)
-{
-	return 0;
-}
 #endif
 
 /*
tools/perf/util/mmap.h +11 -0
index 04ff4d2ffdbe..3ccf8c925002 100644
@@ -105,10 +105,21 @@ union perf_event *perf_mmap__read_event(struct perf_mmap *map);
 
 int perf_mmap__push(struct perf_mmap *md, void *to,
 		    int push(struct perf_mmap *map, void *to, void *buf, size_t size));
+
+#ifdef HAVE_AIO_SUPPORT
 int perf_mmap__aio_push(struct perf_mmap *md, void *to,
 			int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off),
 			off_t *off);
 
+#else
+static inline int perf_mmap__aio_push(struct perf_mmap *md __maybe_unused, void *to __maybe_unused,
+	int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off) __maybe_unused,
+	off_t *off __maybe_unused)
+{
+	return 0;
+}
+#endif
+
 size_t perf_mmap__mmap_len(struct perf_mmap *map);
 
 int perf_mmap__read_init(struct perf_mmap *md);
#8 lore raw

Re: [PATCH v12 2/3]: perf record: enable asynchronous trace writing

Quoted 133 lines · Jiri Olsa

On Tue, Oct 09, 2018 at 11:58:53AM +0300, Alexey Budankov wrote:

SNIP

Quoted 71 lines
(quoted diff) +71 -0
+#ifdef HAVE_AIO_SUPPORT
+int perf_mmap__aio_push(struct perf_mmap *md, void *to,
+			int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off),
+			off_t *off)
+{
+	u64 head = perf_mmap__read_head(md);
+	unsigned char *data = md->base + page_size;
+	unsigned long size, size0 = 0;
+	void *buf;
+	int rc = 0;
+
+	rc = perf_mmap__read_init(md);
+	if (rc < 0)
+		return (rc == -EAGAIN) ? 0 : -1;
+
+	/*
+	 * md->base data is copied into md->data buffer to
+	 * release space in the kernel buffer as fast as possible,
+	 * thru perf_mmap__consume() below.
+	 *
+	 * That lets the kernel to proceed with storing more
+	 * profiling data into the kernel buffer earlier than other
+	 * per-cpu kernel buffers are handled.
+	 *
+	 * Coping can be done in two steps in case the chunk of
+	 * profiling data crosses the upper bound of the kernel buffer.
+	 * In this case we first move part of data from md->start
+	 * till the upper bound and then the reminder from the
+	 * beginning of the kernel buffer till the end of
+	 * the data chunk.
+	 */
+
+	size = md->end - md->start;
+
+	if ((md->start & md->mask) + size != (md->end & md->mask)) {
+		buf = &data[md->start & md->mask];
+		size = md->mask + 1 - (md->start & md->mask);
+		md->start += size;
+		memcpy(md->aio.data, buf, size);
+		size0 = size;
+	}
+
+	buf = &data[md->start & md->mask];
+	size = md->end - md->start;
+	md->start += size;
+	memcpy(md->aio.data + size0, buf, size);
+
+	/*
+	 * Increment md->refcount to guard md->data buffer
+	 * from premature deallocation because md object can be
+	 * released earlier than aio write request started
+	 * on mmap->data is complete.
+	 *
+	 * perf_mmap__put() is done at record__aio_complete()
+	 * after started request completion.
+	 */
+	perf_mmap__get(md);
+
+	md->prev = head;
+	perf_mmap__consume(md);
+
+	rc = push(to, &(md->aio.cblock), md->aio.data, size0 + size, *off);
+	if (!rc) {
+		*off += size0 + size;
+	} else {
+		/*
+		 * Decrement md->refcount back if aio write
+		 * operation failed to start.
+		 */
+		perf_mmap__put(md);
+	}

Review

Quoted 10 lines
(quoted diff) +10 -0
+
+	return rc;
+}
+#else
+int perf_mmap__aio_push(struct perf_mmap *md __maybe_unused, void *to __maybe_unused,
+	int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off) __maybe_unused,
+	off_t *off __maybe_unused)
+{
+	return 0;
+}

I think you need to put this one in the header as static inline otherwise it'd still appear in the NO_AIO=1 build, like:
[jolsa@krava perf]$ make NO_AIO=1
...
[jolsa@krava perf]$ nm -D perf | grep perf_mmap__aio_push 00000000004c2be0 T perf_mmap__aio_push

change below makes it disappear completely>
jirka

---

tools/perf/util/mmap.c +0 -7
index b176d88b3fcb..8c7516696891 100644
@@ -445,13 +445,6 @@ int perf_mmap__aio_push(struct perf_mmap *md, void *to,
 
 	return rc;
 }
-#else
-int perf_mmap__aio_push(struct perf_mmap *md __maybe_unused, void *to __maybe_unused,
-	int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off) __maybe_unused,
-	off_t *off __maybe_unused)
-{
-	return 0;
-}
 #endif
 
 /*
tools/perf/util/mmap.h +4 -0
index 04ff4d2ffdbe..3ccf8c925002 100644
@@ -105,10 +105,21 @@ union perf_event *perf_mmap__read_event(struct perf_mmap *map);
 
 int perf_mmap__push(struct perf_mmap *md, void *to,
 		    int push(struct perf_mmap *map, void *to, void *buf, size_t size));
+
+#ifdef HAVE_AIO_SUPPORT
 int perf_mmap__aio_push(struct perf_mmap *md, void *to,
 			int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off),
 			off_t *off);
 
+#else
+static inline int perf_mmap__aio_push(struct perf_mmap *md __maybe_unused, void *to __maybe_unused,

tools/perf/util/mmap.h union perf_event *perf_mmap__read_event(struct perf_mmap *map); · line 125

Quoted 10 lines
tools/perf/util/mmap.h +7 -0
+	int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off) __maybe_unused,
+	off_t *off __maybe_unused)
+{
+	return 0;
+}
+#endif
+
 size_t perf_mmap__mmap_len(struct perf_mmap *map);
 
 int perf_mmap__read_init(struct perf_mmap *md);

Accepted.

Thanks,
Alexey

#9 lore raw

Re: [PATCH v12 2/3]: perf record: enable asynchronous trace writing

In reply to Alexey Budankov
Quoted 63 lines · Alexey Budankov

Trace file offset is read once before mmaps iterating loop and written back after all performance data enqueued for aio writing. Trace file offset is incremented linearly after every successful aio write operation.

record__aio_sync() blocks till completion of started AIO operation and then proceeds.

record__mmap_read_sync() implements a barrier for all incomplete aio write requests.

---
Changes in v12:
- implemented record__aio_get/set_pos(), record__aio_enabled()
- implemented simple --aio option
Changes in v11:
- replacing the both lseek() syscalls in every loop iteration by the only
two syscalls just before and after the loop at record__mmap_read_evlist() and advancing *in-flight* off file pos value at perf_mmap__aio_push()
Changes in v10:
- avoided lseek() setting file pos back in case of record__aio_write() failure
- compacted code selecting between serial and AIO streaming
- optimized call places of record__mmap_read_sync()
Changes in v9:
- enable AIO streaming only when --aio-cblocks option is specified explicitly Changes in v8:
- split AIO completion check into separate record__aio_complete()
Changes in v6:
- handled errno == EAGAIN case from aio_write();
Changes in v5:
- data loss metrics decreased from 25% to 2x in trialed configuration;
- avoided nanosleep() prior calling aio_suspend();
- switched to per cpu multi record__aio_sync() aio
- record_mmap_read_sync() now does global barrier just before
switching trace file or collection stop;
- resolved livelock on perf record -e intel_pt// -- dd if=/dev/zero of=/dev/null count=100000 Changes in v4:
- converted void *bf to struct perf_mmap *md in signatures
- written comment in perf_mmap__push() just before perf_mmap__get();
- written comment in record__mmap_read_sync() on possible restarting
of aio_write() operation and releasing perf_mmap object after all;
- added perf_mmap__put() for the cases of failed aio_write();
Changes in v3:
- written comments about nanosleep(0.5ms) call prior aio_suspend()
to cope with intrusiveness of its implementation in glibc;
- written comments about rationale behind coping profiling data
into mmap->data buffer;

---
 tools/perf/Documentation/perf-record.txt |   5 +
 tools/perf/builtin-record.c              | 220 ++++++++++++++++++++++++++++++-
 tools/perf/perf.h                        |   1 +
 tools/perf/util/evlist.c                 |   6 +-
 tools/perf/util/evlist.h                 |   2 +-
 tools/perf/util/mmap.c                   |  86 +++++++++++-
 tools/perf/util/mmap.h                   |   5 +
 7 files changed, 316 insertions(+), 9 deletions(-)
tools/perf/Documentation/perf-record.txt +0 -0
index 246dee081efd..5cedb3e75434 100644

tools/perf/Documentation/perf-record.txt Specify vmlinux path which has debuginfo. · line 439

Quoted 5 lines
tools/perf/Documentation/perf-record.txt +2 -0
@@ -435,6 +435,11 @@ Specify vmlinux path which has debuginfo.
 --buildid-all::
 Record build-id of all DSOs regardless whether it's actually hit or not.
 
+--aio::
+Enable asynchronous (Posix AIO) trace writing mode. 

nit, there's an extra whitespace at the end of above line, making the 'git am' to not apply your patch

thanks,
jirka

#10 lore raw

Re: [PATCH v12 2/3]: perf record: enable asynchronous trace writing

Quoted 67 lines · Jiri Olsa
Quoted 66 lines · Alexey Budankov

Trace file offset is read once before mmaps iterating loop and written back after all performance data enqueued for aio writing. Trace file offset is incremented linearly after every successful aio write operation.

record__aio_sync() blocks till completion of started AIO operation and then proceeds.

record__mmap_read_sync() implements a barrier for all incomplete aio write requests.

---
Changes in v12:
- implemented record__aio_get/set_pos(), record__aio_enabled()
- implemented simple --aio option
Changes in v11:
- replacing the both lseek() syscalls in every loop iteration by the only
two syscalls just before and after the loop at record__mmap_read_evlist() and advancing *in-flight* off file pos value at perf_mmap__aio_push()
Changes in v10:
- avoided lseek() setting file pos back in case of record__aio_write() failure
- compacted code selecting between serial and AIO streaming
- optimized call places of record__mmap_read_sync()
Changes in v9:
- enable AIO streaming only when --aio-cblocks option is specified explicitly Changes in v8:
- split AIO completion check into separate record__aio_complete()
Changes in v6:
- handled errno == EAGAIN case from aio_write();
Changes in v5:
- data loss metrics decreased from 25% to 2x in trialed configuration;
- avoided nanosleep() prior calling aio_suspend();
- switched to per cpu multi record__aio_sync() aio
- record_mmap_read_sync() now does global barrier just before
switching trace file or collection stop;
- resolved livelock on perf record -e intel_pt// -- dd if=/dev/zero of=/dev/null count=100000 Changes in v4:
- converted void *bf to struct perf_mmap *md in signatures
- written comment in perf_mmap__push() just before perf_mmap__get();
- written comment in record__mmap_read_sync() on possible restarting
of aio_write() operation and releasing perf_mmap object after all;
- added perf_mmap__put() for the cases of failed aio_write();
Changes in v3:
- written comments about nanosleep(0.5ms) call prior aio_suspend()
to cope with intrusiveness of its implementation in glibc;
- written comments about rationale behind coping profiling data
into mmap->data buffer;

---
 tools/perf/Documentation/perf-record.txt |   5 +
 tools/perf/builtin-record.c              | 220 ++++++++++++++++++++++++++++++-
 tools/perf/perf.h                        |   1 +
 tools/perf/util/evlist.c                 |   6 +-
 tools/perf/util/evlist.h                 |   2 +-
 tools/perf/util/mmap.c                   |  86 +++++++++++-
 tools/perf/util/mmap.h                   |   5 +
 7 files changed, 316 insertions(+), 9 deletions(-)
tools/perf/Documentation/perf-record.txt +0 -0
index 246dee081efd..5cedb3e75434 100644
@@ -435,6 +435,11 @@ Specify vmlinux path which has debuginfo.
 --buildid-all::
 Record build-id of all DSOs regardless whether it's actually hit or not.
 
Quoted 5 lines

tools/perf/Documentation/perf-record.txt Specify vmlinux path which has debuginfo. · line 439

Quoted 2 lines
tools/perf/Documentation/perf-record.txt +2 -0
+--aio::
+Enable asynchronous (Posix AIO) trace writing mode. 

nit, there's an extra whitespace at the end of above line, making the 'git am' to not apply your patch

Corrected trailing whitespace. Thanks!

- Alexey

Quoted 4 lines

thanks,
jirka

#11 lore raw

PATCH v12 3/3 : perf record: extend trace writing to multi AIO

In reply to Alexey Budankov

Multi AIO trace writing allows caching more kernel data into userspace memory postponing trace writing for the sake of overall profiling data thruput increase. It could be seen as kernel data buffer extension into userspace memory.

With aio-cblocks option value different from 0, current default value, tool has capability to cache more and more data into user space along with delegating spill to AIO.

That allows avoiding suspend at record__aio_sync() between calls of record__mmap_read_evlist() and increase profiling data thruput for the cost of userspace memory.

---
Changes in v12:
- extended --aio option to --aio-cblocks=<n>
Changes in v10:
- added description of aio-cblocks option into perf-record.txt

---
 tools/perf/Documentation/perf-record.txt |  4 +-
 tools/perf/builtin-record.c              | 63 ++++++++++++++++++++------
 tools/perf/util/mmap.c                   | 78 ++++++++++++++++++++++----------
 tools/perf/util/mmap.h                   |  7 +--
 4 files changed, 108 insertions(+), 44 deletions(-)
tools/perf/Documentation/perf-record.txt +2 -2
index 5cedb3e75434..52307c4322fe 100644
@@ -435,8 +435,8 @@ Specify vmlinux path which has debuginfo.
 --buildid-all::
 Record build-id of all DSOs regardless whether it's actually hit or not.
 
---aio::
-Enable asynchronous (Posix AIO) trace writing mode. 
+--aio-cblocks <n>
+Use <n> control blocks in asynchronous (Posix AIO) trace writing mode (max: 4).
 Asynchronous mode is supported only when linking Perf tool with libc library
 providing implementation for Posix AIO API.
 
tools/perf/builtin-record.c +48 -15

126 lines not on this page · raw message

tools/perf/util/mmap.c +54 -24

138 lines not on this page · raw message

tools/perf/util/mmap.h +4 -3
index 04ff4d2ffdbe..185d53c7fb3b 100644
@@ -32,8 +32,9 @@ struct perf_mmap {
 	char		 event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8);
 #ifdef HAVE_AIO_SUPPORT
 	struct {
-		void 		 *data;
-		struct aiocb	 cblock;
+		void		 **data;
+		struct aiocb	 *cblocks;
+		struct aiocb	 **aiocb;
 		int		 nr_cblocks;
 	} aio;
 #endif
@@ -105,7 +106,7 @@ union perf_event *perf_mmap__read_event(struct perf_mmap *map);
 
 int perf_mmap__push(struct perf_mmap *md, void *to,
 		    int push(struct perf_mmap *map, void *to, void *buf, size_t size));
-int perf_mmap__aio_push(struct perf_mmap *md, void *to,
+int perf_mmap__aio_push(struct perf_mmap *md, void *to, int idx,
 			int push(void *to, struct aiocb *cblock, void *buf, size_t size, off_t off),
 			off_t *off);
#12 lore raw

Re: [PATCH v12 3/3]: perf record: extend trace writing to multi AIO

On Tue, Oct 09, 2018 at 11:59:31AM +0300, Alexey Budankov wrote:

SNIP

Quoted 24 lines
tools/perf/builtin-record.c +7 -4
@@ -1568,15 +1596,18 @@ static int parse_clockid(const struct option *opt, const char *str, int unset)
 
 #ifdef HAVE_AIO_SUPPORT
 static int record__aio_parse(const struct option *opt,
-			     const char *str __maybe_unused,
+			     const char *str,
 			     int unset)
 {
 	struct record_opts *opts = (struct record_opts *)opt->value;
 
-	if (unset)
+	if (unset) {
 		opts->nr_cblocks = 0;
-	else
-		opts->nr_cblocks = 1;
+	} else {
+		opts->nr_cblocks = strtol(str, NULL, 0);
+		if (!opts->nr_cblocks)
+			opts->nr_cblocks = 1;
+	}
 
 	return 0;
 }

tools/perf/builtin-record.c static struct option __record_options[] = { · line 1890

Quoted 5 lines
tools/perf/builtin-record.c +0 -2
@@ -1886,8 +1917,8 @@ static struct option __record_options[] = {
 	OPT_BOOLEAN(0, "dry-run", &dry_run,
 		    "Parse options then exit"),
 #ifdef HAVE_AIO_SUPPORT
-	OPT_CALLBACK_NOOPT(0, "aio", &record.opts,
-		     NULL, "Enable asynchronous trace writing mode",

hum, why removing --aio option? I though we will have:

perf record --aio .... # enables aio with default settings
perf record --aio-cblocks=3 ... # enabled aio with aio-cblocks=3

jirka

Quoted 3 lines
tools/perf/builtin-record.c +2 -0
+	OPT_CALLBACK(0, "aio-cblocks", &record.opts,
+		     "n", "Use <n> control blocks in asynchronous trace writing mode (max: 4)",
 		     record__aio_parse),

SNIP

#13 lore raw

Re: [PATCH v12 3/3]: perf record: extend trace writing to multi AIO

Hi,

Quoted 1 line · Jiri Olsa

On Tue, Oct 09, 2018 at 11:59:31AM +0300, Alexey Budankov wrote:

<SNIP>

Quoted 10 lines

tools/perf/builtin-record.c static struct option __record_options[] = { · line 1890

Quoted 7 lines
tools/perf/builtin-record.c +0 -2
@@ -1886,8 +1917,8 @@ static struct option __record_options[] = {
 	OPT_BOOLEAN(0, "dry-run", &dry_run,
 		    "Parse options then exit"),
 #ifdef HAVE_AIO_SUPPORT
-	OPT_CALLBACK_NOOPT(0, "aio", &record.opts,
-		     NULL, "Enable asynchronous trace writing mode",

hum, why removing --aio option? I though we will have:

Quoted 2 lines

perf record --aio .... # enables aio with default settings
perf record --aio-cblocks=3 ... # enabled aio with aio-cblocks=3

It is possible to avoid the complication using only one --aio option like this:

perf record --aio ... # enables aio with default (=1)
perf record --aio=4 ... # enables aio with parameter = 4

--aio-cblocks=4 still works when specified like --aio=4.

Thanks,
Alexey

#14 lore raw

Re: [PATCH v12 3/3]: perf record: extend trace writing to multi AIO

In reply to Alexey Budankov

On Tue, Oct 09, 2018 at 11:59:31AM +0300, Alexey Budankov wrote:

SNIP

tools/perf/builtin-record.c static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli · line 737

Quoted 12 lines
tools/perf/builtin-record.c +2 -2
@@ -709,12 +729,13 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli
 					goto out;
 				}
 			} else {
+				int idx;
 				/*
 				 * Call record__aio_sync() to wait till map->data buffer
 				 * becomes available after previous aio write request.
 				 */
-				record__aio_sync(map);
-				if (perf_mmap__aio_push(map, rec, record__aio_pushfn, &off) != 0) {
+				idx = record__aio_sync(map, false);

looks like record__aio_sync can return -1,
how is it not breaking the perf_mmap__aio_push call below?

Quoted 4 lines
tools/perf/builtin-record.c +1 -0
+				if (perf_mmap__aio_push(map, rec, idx, record__aio_pushfn, &off) != 0) {
 					record__aio_set_pos(trace_fd, off);
 					rc = -1;
 					goto out;

SNIP

#15 lore raw

Re: [PATCH v12 3/3]: perf record: extend trace writing to multi AIO

Quoted 4 lines · Jiri Olsa

On Tue, Oct 09, 2018 at 11:59:31AM +0300, Alexey Budankov wrote:

SNIP

Quoted 15 lines

tools/perf/builtin-record.c static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli · line 737

Quoted 12 lines
tools/perf/builtin-record.c +2 -2
@@ -709,12 +729,13 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli
 					goto out;
 				}
 			} else {
+				int idx;
 				/*
 				 * Call record__aio_sync() to wait till map->data buffer
 				 * becomes available after previous aio write request.
 				 */
-				record__aio_sync(map);
-				if (perf_mmap__aio_push(map, rec, record__aio_pushfn, &off) != 0) {
+				idx = record__aio_sync(map, false);

looks like record__aio_sync can return -1,
how is it not breaking the perf_mmap__aio_push call below?

Currently -1 may be returned only when calling with sync_all==true i.e. record__aio_sync(, true).

Thanks,
Alexey

Quoted 8 lines
Quoted 5 lines
tools/perf/builtin-record.c +1 -0
+				if (perf_mmap__aio_push(map, rec, idx, record__aio_pushfn, &off) != 0) {
 					record__aio_set_pos(trace_fd, off);
 					rc = -1;
 					goto out;

SNIP