Subject: [PATCH 02/20] selftest/powerpc/pmu: Add support for perf sampling tests

From: Kajol Jain <[email-masked]>
Date: Thu, 27 Jan 2022 12:49:54 +0530

From: Athira Rajeev <[email-masked]>

Add support functions for enabling perf sampling test in
a new folder "sampling_tests" under "selftests/powerpc/pmu".
This includes support functions for allocating and processing
the mmap buffer. These functions are added/defined in
"sampling_tests/misc.*" files.

Also updates the corresponding Makefiles in "selftests/powerpc"
and "sampling_tests" folder.

Signed-off-by: Athira Rajeev <[email-masked]>
---
 tools/testing/selftests/powerpc/pmu/Makefile  |  11 +-
 .../powerpc/pmu/sampling_tests/Makefile       |  20 ++++
 .../powerpc/pmu/sampling_tests/misc.c         | 105 ++++++++++++++++++
 .../powerpc/pmu/sampling_tests/misc.h         |   9 ++
 4 files changed, 143 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/powerpc/pmu/sampling_tests/Makefile
 create mode 100644 tools/testing/selftests/powerpc/pmu/sampling_tests/misc.c
 create mode 100644 tools/testing/selftests/powerpc/pmu/sampling_tests/misc.h

diff --git a/tools/testing/selftests/powerpc/pmu/Makefile b/tools/testing/selftests/powerpc/pmu/Makefile
index 904672fb78dd..edbd96d3b2ab 100644
--- a/tools/testing/selftests/powerpc/pmu/Makefile
+++ b/tools/testing/selftests/powerpc/pmu/Makefile
@@ -8,7 +8,7 @@ EXTRA_SOURCES := ../harness.c event.c lib.c ../utils.c
 top_srcdir = ../../../../..
 include ../../lib.mk
 
-all: $(TEST_GEN_PROGS) ebb
+all: $(TEST_GEN_PROGS) ebb sampling_tests
 
 $(TEST_GEN_PROGS): $(EXTRA_SOURCES)
 
@@ -26,25 +26,32 @@ DEFAULT_RUN_TESTS := $(RUN_TESTS)
 override define RUN_TESTS
 	$(DEFAULT_RUN_TESTS)
 	TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests
+	TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests
 endef
 
 DEFAULT_EMIT_TESTS := $(EMIT_TESTS)
 override define EMIT_TESTS
 	$(DEFAULT_EMIT_TESTS)
 	TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests
+	TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests
 endef
 
 DEFAULT_INSTALL_RULE := $(INSTALL_RULE)
 override define INSTALL_RULE
 	$(DEFAULT_INSTALL_RULE)
 	TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install
+	TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install
 endef
 
 clean:
 	$(RM) $(TEST_GEN_PROGS) $(OUTPUT)/loop.o
 	TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean
+	TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean
 
 ebb:
 	TARGET=$@; BUILD_TARGET=$$OUTPUT/$$TARGET; mkdir -p $$BUILD_TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -k -C $$TARGET all
 
-.PHONY: all run_tests clean ebb
+sampling_tests:
+	TARGET=$@; BUILD_TARGET=$$OUTPUT/$$TARGET; mkdir -p $$BUILD_TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -k -C $$TARGET all
+
+.PHONY: all run_tests clean ebb sampling_tests
diff --git a/tools/testing/selftests/powerpc/pmu/sampling_tests/Makefile b/tools/testing/selftests/powerpc/pmu/sampling_tests/Makefile
new file mode 100644
index 000000000000..eb00c9f931ec
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/sampling_tests/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../../../../../../scripts/Kbuild.include
+
+all: $(TEST_GEN_PROGS)
+
+noarg:
+	$(MAKE) -C ../../
+
+CFLAGS += -m64 -I../../../../../lib/
+
+# Toolchains may build PIE by default which breaks the assembly
+no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \
+	$(CC) -Werror $(KBUILD_CPPFLAGS) $(CC_OPTION_CFLAGS) -no-pie -x c - -o "$$TMP", -no-pie)
+
+LDFLAGS += $(no-pie-option)
+
+top_srcdir = ../../../../../..
+include ../../../lib.mk
+
+$(TEST_GEN_PROGS): ../../harness.c ../../utils.c ../event.c ../lib.c misc.c misc.h
diff --git a/tools/testing/selftests/powerpc/pmu/sampling_tests/misc.c b/tools/testing/selftests/powerpc/pmu/sampling_tests/misc.c
new file mode 100644
index 000000000000..4779b107f43b
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/sampling_tests/misc.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2022, Athira Rajeev, IBM Corp.
+ */
+
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "misc.h"
+
+#define PAGE_SIZE               sysconf(_SC_PAGESIZE)
+
+/*
+ * Allocate mmap buffer of "mmap_pages" number of
+ * pages.
+ */
+void *event_sample_buf_mmap(int fd, int mmap_pages)
+{
+	size_t page_size = sysconf(_SC_PAGESIZE);
+	size_t mmap_size;
+	void *buff;
+
+	if (mmap_pages <= 0)
+		return NULL;
+
+	if (fd <= 0)
+		return NULL;
+
+	mmap_size =  page_size * (1 + mmap_pages);
+	buff = mmap(NULL, mmap_size,
+		PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+	if (buff == MAP_FAILED) {
+		perror("mmap() failed.");
+		return NULL;
+	}
+	return buff;
+}
+
+/*
+ * Post process the mmap buffer.
+ * - If sample_count != NULL then return count of total
+ *   number of samples present in the mmap buffer.
+ * - If sample_count == NULL then return the address
+ *   of first sample from the mmap buffer
+ */
+void *__event_read_samples(void *sample_buff, size_t *size, u64 *sample_count)
+{
+	size_t page_size = sysconf(_SC_PAGESIZE);
+	struct perf_event_header *header = sample_buff + page_size;
+	struct perf_event_mmap_page *metadata_page = sample_buff;
+	unsigned long data_head, data_tail;
+
+	/*
+	 * PERF_RECORD_SAMPLE:
+	 * struct {
+	 *     struct perf_event_header hdr;
+	 *     u64 data[];
+	 * };
+	 */
+
+	data_head = metadata_page->data_head;
+	/* sync memory before reading sample */
+	mb();
+	data_tail = metadata_page->data_tail;
+
+	/* Check for sample_count */
+	if (sample_count)
+		*sample_count = 0;
+
+	while (1) {
+		/*
+		 * Reads the mmap data buffer by moving
+		 * the data_tail to know the last read data.
+		 * data_head points to head in data buffer.
+		 * refer "struct perf_event_mmap_page" in
+		 * "include/uapi/linux/perf_event.h".
+		 */
+		if (data_head - data_tail < sizeof(header))
+			return NULL;
+
+		data_tail += sizeof(header);
+		if (header->type == PERF_RECORD_SAMPLE) {
+			*size = (header->size - sizeof(header));
+			if (!sample_count)
+				return sample_buff + page_size + data_tail;
+			data_tail += *size;
+			*sample_count += 1;
+		} else {
+			*size = (header->size - sizeof(header));
+			if ((metadata_page->data_tail + *size) > metadata_page->data_head)
+				data_tail = metadata_page->data_head;
+			else
+				data_tail += *size;
+		}
+		header = (struct perf_event_header *)((void *)header + header->size);
+	}
+	return NULL;
+}
diff --git a/tools/testing/selftests/powerpc/pmu/sampling_tests/misc.h b/tools/testing/selftests/powerpc/pmu/sampling_tests/misc.h
new file mode 100644
index 000000000000..291f9adba817
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/sampling_tests/misc.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022, Athira Rajeev, IBM Corp.
+ */
+
+#include "../event.h"
+
+void *event_sample_buf_mmap(int fd, int mmap_pages);
+void *__event_read_samples(void *sample_buff, size_t *size, u64 *sample_count);
-- 
2.27.0