lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1316597423-25723-8-git-send-email-robert.richter@amd.com>
Date:	Wed, 21 Sep 2011 11:30:23 +0200
From:	Robert Richter <robert.richter@....com>
To:	Peter Zijlstra <peterz@...radead.org>
CC:	Ingo Molnar <mingo@...e.hu>, Stephane Eranian <eranian@...gle.com>,
	LKML <linux-kernel@...r.kernel.org>,
	Robert Richter <robert.richter@....com>
Subject: [V3][PATCH 7/7] perf, x86: Example code for AMD IBS

This patch includes an example to use IBS via perf_event.

usage: ibs [-h]
       ibs ibs_fetch | ibs_op [-s] [-C CPU] [-m BUFFERPAGES] <command>

        <command>
                Command to execute.

        -e CONFIG
                64 bit configuration value, refers to msrs
                IbsFetchCtl (0xC0011030) or IbsOpCtl (0xC0011033).
                The default sample period is set to 100000.

        -c COUNT
                Event period to sample (default: 100000).

        -h
                Print help.

        -s
                system wide profiling (set per default)

        -C CPU
                profile on CPU (not yet implemented)

        -m BUFFERPAGES
                Per-cpu buffer pages to allocate.

V2:
* Updated include header files to fix build errors on some distros.
* Caps field added to sampling format.
* Note: I kept example code for reference, the patch must not be
  applied. I will come up with a sulution that integrates IBS into
  perf-report.

Signed-off-by: Robert Richter <robert.richter@....com>
---
 tools/perf/Documentation/examples/Makefile |   44 +++
 tools/perf/Documentation/examples/ibs.c    |  445 ++++++++++++++++++++++++++++
 2 files changed, 489 insertions(+), 0 deletions(-)
 create mode 100644 tools/perf/Documentation/examples/Makefile
 create mode 100644 tools/perf/Documentation/examples/ibs.c

diff --git a/tools/perf/Documentation/examples/Makefile b/tools/perf/Documentation/examples/Makefile
new file mode 100644
index 0000000..cfc9647
--- /dev/null
+++ b/tools/perf/Documentation/examples/Makefile
@@ -0,0 +1,44 @@
+all:	ibs
+
+CFLAGS += -I../..
+CFLAGS += -I../../util/include
+CFLAGS += -DNO_NEWT_SUPPORT
+
+LIB_FILE=../../libperf.a
+
+INSTALL = install
+
+ifeq ("$(origin O)", "command line")
+	OUTPUT := $(O)/
+endif
+
+ifneq ($(OUTPUT),)
+# check that the output directory actually exists
+OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
+$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
+endif
+
+ifndef DESTDIR
+prefix = $(HOME)
+endif
+bindir_relative = bin
+bindir = $(prefix)/$(bindir_relative)
+
+DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
+bindir_SQ = $(subst ','\'',$(bindir))
+
+../../libperf.a:
+	$(MAKE) CFLAGS="-DNO_NEWT_SUPPORT" -C ../.. libperf.a
+
+$(OUTPUT)ibs: ibs.c $(LIB_FILE)
+	$(CC) $(CFLAGS) $^ -o $@
+
+clean:
+	$(MAKE) -C ../.. clean
+	$(RM) ibs
+
+install: all
+	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) $(OUTPUT)ibs '$(DESTDIR_SQ)$(bindir_SQ)'
+
+.PHONY:	all clean install
diff --git a/tools/perf/Documentation/examples/ibs.c b/tools/perf/Documentation/examples/ibs.c
new file mode 100644
index 0000000..e4ad012
--- /dev/null
+++ b/tools/perf/Documentation/examples/ibs.c
@@ -0,0 +1,445 @@
+/*
+ * IBS sampling example
+ *
+ *  Copyright (C) 2011 Advanced Micro Devices, Inc., Robert Richter
+ *
+ * Sample code that attaches an event to a specified PMU.
+ *
+ * Compiling:
+ *
+ *  $ cd linux         # Linux kernel source dir
+ *  $ make -C tools/perf/Documentation/examples ibs
+ *
+ * Running:
+ *
+ *  $ ./ibs ibs_fetch -s -m 256 <command>
+ *
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <sys/ptrace.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "util/evsel.h"
+#include "util/evlist.h"
+#include "util/cpumap.h"
+#include "util/thread_map.h"
+
+struct perf_config {
+	uint64_t	config;
+	uint64_t	sample_period;
+	char		*sysfs;
+	int		pid;
+	int		cpu;
+	int		mmap_pages;
+	char		**argv;
+};
+
+static uint64_t collected_samples, lost_samples, sum_period;
+
+static void usage(void)
+{
+	printf(
+"usage: ibs [-h]\n"
+"       ibs ibs_fetch | ibs_op [-s] [-C CPU] [-m BUFFERPAGES] <command>\n"
+"\n"
+"        <command>\n"
+"                Command to execute.\n"
+"\n"
+"        -e CONFIG\n"
+"                64 bit configuration value, refers to msrs\n"
+"                IbsFetchCtl (0xC0011030) or IbsOpCtl (0xC0011033).\n"
+"                The default sample period is set to 100000.\n"
+"\n"
+"        -c COUNT\n"
+"                Event period to sample (default: 100000).\n"
+"\n"
+"        -h\n"
+"                Print help.\n"
+"\n"
+"        -s\n"
+"                system wide profiling (set per default)\n"
+"\n"
+"        -C CPU\n"
+"                profile on CPU (not yet implemented)\n"
+"\n"
+"        -m BUFFERPAGES\n"
+"                Per-cpu buffer pages to allocate.\n"
+);
+	exit(0);
+}
+
+#define IBS_FETCH_DEFAULT	((1ULL<<57)|(100000ULL>>4))
+#define IBS_OP_DEFAULT		((0ULL<<19)|(100000ULL>>4))
+
+#define IBS_MAX_CNT		0x0000FFFFULL
+
+#define IBS_FETCH_SYSFS "/sys/bus/event_source/devices/ibs_fetch/type"
+#define IBS_OP_SYSFS    "/sys/bus/event_source/devices/ibs_op/type"
+
+static int ibs_config(struct perf_config *config, int argc, char **argv)
+{
+	int c;
+
+	memset(config, 0, sizeof(*config));
+	config->pid = -1;	/* support for system wide profiling only */
+	config->cpu = -1;
+	config->mmap_pages = 1; /* need buffer for ibs */
+
+	c = getopt(argc, argv,"+h");
+	if (c != -1 || !argv[optind]) {
+		usage();
+		exit(0);
+	}
+
+	if (!strcmp(argv[optind], "ibs_fetch")) {
+		config->sysfs  = IBS_FETCH_SYSFS;
+		config->config = IBS_FETCH_DEFAULT;
+	} else if (!strcmp(argv[optind], "ibs_op")) {
+		config->sysfs  = IBS_OP_SYSFS;
+		config->config = IBS_OP_DEFAULT;
+	} else {
+		errx(1, "specify ibs_fetch or ibs_op\n");
+	}
+
+	optind++;
+
+	while (1) {
+		c = getopt(argc, argv,"+he:c:sC:m:v");
+		if (c == -1)
+			break;
+		switch (c) {
+		case 'h':
+			usage();
+			exit(0);
+		case 'e':
+			/* event configuration */
+			config->config = atoll(optarg);
+			break;
+		case 'c':
+			/* sample period */
+			config->sample_period = atoll(optarg);
+			config->config &= ~IBS_MAX_CNT;
+			if (!config->sample_period)
+				errx(1, "invalid sample period");
+			break;
+		case 's':
+			/* system wide profiling */
+			if (config->pid)
+				break;
+			config->pid = -1;
+			config->cpu = -1;
+			break;
+		case 'C':
+			/* profile cpu */
+			config->pid = -1;
+			config->cpu = atoi(optarg);
+			break;
+		case 'm':
+			config->mmap_pages = atoi(optarg);
+			break;
+		default:
+			errx(1, "unknown option");
+		}
+	}
+
+	if (!argv[optind])
+		errx(1, "you must specify a command to execute\n");
+
+	config->argv = argv + optind;
+
+	if (config->mmap_pages > 1 && ((config->mmap_pages) & 0x1))
+		errx(1, "number of pages must be power of 2\n");
+
+	return 0;
+}
+
+#define BUFSIZ_ATOI	32
+
+static int get_pmu_type(char *sysfs)
+{	int pmu, ret = 0;
+	char buf[BUFSIZ_ATOI];
+	size_t size;
+
+	pmu = open(sysfs, O_RDONLY);
+	if (pmu == -1)
+		return -errno;
+	size = read(pmu, buf, BUFSIZ - 1);
+	if (size < 0)
+		ret = -errno;
+	close(pmu);
+
+	if (ret)
+		return ret;
+
+	buf[size] = '0';
+
+	return atoi(buf);
+}
+
+static volatile int done = 0;
+
+static void cld_handler(int n)
+{
+	done = 1;
+}
+
+static int child(char **arg)
+{
+	ptrace(PTRACE_TRACEME, 0, NULL, NULL);
+	execvp(arg[0], arg);
+	return -1;
+}
+
+struct ibs_data {
+	uint32_t caps;
+	uint64_t regs[0];
+} __attribute__ ((packed));
+
+static void print_ibs_fetch(int cpu, struct ibs_data *__ibs)
+{
+	uint64_t *ibs = __ibs->regs;
+	printf("IBS_fetch sample on cpu%d\tIBS0: 0x%016"PRIx64" IBS1: 0x%016"PRIx64" IBS2:0x%016"PRIx64"\n",
+	       cpu, ibs[0], ibs[1], ibs[2]);
+}
+
+static void print_ibs_op(int cpu, struct ibs_data *__ibs)
+{
+	uint64_t *ibs = __ibs->regs;
+	printf("IBS_OP sample on cpu%d\t"
+	       "\t IBS0: 0x%016"PRIx64" IBS1: 0x%016"PRIx64" IBS2: 0x%016"PRIx64"\n"
+	        "\tIBS3: 0x%016"PRIx64" IBS4: 0x%016"PRIx64" IBS5: 0x%016"PRIx64" IBS6: 0x%016"PRIx64"\n",
+	       cpu, ibs[0], ibs[1], ibs[2], ibs[3], ibs[4], ibs[5], ibs[6]);
+}
+
+#define MSR_AMD64_IBSFETCH_SIZE		3
+#define MSR_AMD64_IBSOP_SIZE		7
+
+static int print_ibs(struct perf_sample *sample)
+{
+	switch (sample->raw_size >> 3) {
+	case MSR_AMD64_IBSFETCH_SIZE:
+		print_ibs_fetch(sample->cpu, sample->raw_data);
+		return 0;
+	case MSR_AMD64_IBSOP_SIZE:
+		print_ibs_op(sample->cpu, sample->raw_data);
+		return 0;
+	default:
+		printf("invalid: raw_size = %d, p = %p\n",
+		       sample->raw_size, (u64*)sample->raw_data);
+		return -EINVAL;
+	}
+}
+
+static void print_event(union perf_event *event)
+{
+	int idx, size = event->sample.header.size;
+	u64 *val = event->sample.array;
+
+	printf("unrecognized event, type = %d, size = %d, header = 0x%016"PRIx64":\n",
+	       event->sample.header.type, size, *(u64*)&event->sample.header);
+
+	for (idx = 1; size > 0; idx++, size -= 8) {
+		printf(" 0x%016"PRIx64, *val++);
+		if (!(idx % 8))
+			printf("\n");
+	}
+	printf("\n");
+}
+
+static int ibs_run(struct perf_config *config)
+{
+	struct perf_event_attr attr;
+	struct perf_sample sample;
+	struct perf_evsel *evsel = NULL;
+	struct perf_evlist *evlist = NULL;
+	struct cpu_map *cpus = NULL;
+	struct thread_map *threads = NULL;
+	struct perf_evsel *pos, *n;
+	union perf_event *event;
+	pid_t pid = config->pid;
+	char cpu_list[8];
+	int type, idx, status, ready = 0;
+	int ret = -ENOMEM;
+	static uint64_t ovfl_count; /* static to avoid setjmp issue */
+
+	type = get_pmu_type(config->sysfs);
+	if (type < 0) {
+		fprintf(stderr, "Failed to get pmu type: %d\n", type);
+		return type;
+	}
+
+	memset(&attr, 0, sizeof(attr));
+	attr.type = type;
+	attr.sample_type   = PERF_SAMPLE_CPU | PERF_SAMPLE_RAW;
+	attr.sample_period = config->sample_period;
+	attr.config        = config->config;
+
+	evsel = perf_evsel__new(&attr, 0);
+
+	if (config->cpu == -1) {
+		cpus = cpu_map__new(NULL);
+	} else {
+		snprintf(cpu_list, sizeof(cpu_list), "%d", config->cpu);
+		cpus = cpu_map__new(cpu_list);
+	}
+
+	threads = thread_map__new(pid, pid);
+
+	evlist = perf_evlist__new(cpus, threads);
+
+	if (!evsel || !evlist || !cpus || !threads)
+		goto out;
+
+	ret = perf_evsel__alloc_counts(evsel, cpus->nr);
+	if (ret < 0)
+		goto out;
+
+	perf_evlist__add(evlist, evsel);
+
+	list_for_each_entry(pos, &evlist->entries, node) {
+		if (perf_evsel__open(pos, evlist->cpus, evlist->threads, 0) < 0) {
+			ret = -errno;
+			fprintf(stderr, "cannot open events, %d\n", ret);
+			goto out;
+		}
+	}
+
+	if (perf_evlist__mmap(evlist, config->mmap_pages, false) < 0) {
+		ret = -errno;
+		fprintf(stderr, "failed to mmap with %d (%s)\n",
+			ret, strerror(ret));
+		goto out;
+	}
+
+	/*
+	 * Create the child task
+	 */
+	if ((pid=fork()) == -1) {
+		ret = -errno;
+		fprintf(stderr, "cannot fork process\n");
+		goto out;
+	}
+
+	if (pid == 0)
+		exit(child(config->argv));
+
+	/*
+	 * wait for the child to exec
+	 */
+	ret = waitpid(pid, &status, WUNTRACED);
+	if (ret == -1)
+		err(1, "waitpid failed");
+
+	if (WIFEXITED(status))
+		errx(1, "task %s [%d] exited already status %d\n",
+		     config->argv[0], pid, WEXITSTATUS(status));
+
+	/*
+	 * effectively activate monitoring
+	 */
+	ptrace(PTRACE_DETACH, pid, NULL, 0);
+
+	signal(SIGCHLD, cld_handler);
+
+	/*
+	 * core loop
+	 */
+	for (ret = 0; !ret; ) {
+		if (done && ready)
+			break;
+		ready = done;
+
+		ret = poll(evlist->pollfd, evlist->nr_fds, done ? 0 : -1);
+
+		if (ret > 0) {
+			ovfl_count += ret;
+		} else if (ret < 0) {
+			ret = -errno;
+			if (ret != -EINTR)
+				break;
+			ret = 0;
+		}
+
+		list_for_each_entry(pos, &evlist->entries, node) {
+			if (ret < 0)
+				break;
+			ret = __perf_evsel__read(pos, evlist->cpus->nr,
+						 evlist->threads->nr, false);
+		}
+
+		for (idx = 0; !ret, idx < evlist->nr_fds; idx++) {
+			if (done)
+				ioctl(evlist->pollfd[idx].fd,
+				      PERF_EVENT_IOC_DISABLE);
+			while (event = perf_evlist__mmap_read(evlist, idx)) {
+				ready = 0;
+				ret = perf_event__parse_sample(event,
+							       evsel->attr.sample_type,
+							       perf_evsel__sample_size(evsel),
+							       false, &sample);
+				if (ret)
+					break;
+				collected_samples++;
+				if (print_ibs(&sample))
+					print_event(event);
+			}
+		}
+	}
+
+	/*
+	 * cleanup child
+	 */
+	waitpid(pid, &status, 0);
+
+	printf("%"PRIu64" samples collected in %"PRIu64" poll events, %"PRIu64" lost samples\n",
+		collected_samples, ovfl_count, lost_samples);
+	if (collected_samples)
+		printf("avg period=%"PRIu64"\n", sum_period / collected_samples);
+out:
+	if (evlist) {
+		perf_evlist__munmap(evlist);
+		list_for_each_entry_safe(pos, n, &evlist->entries, node) {
+			perf_evsel__close_fd(pos, evlist->cpus->nr,
+					     evlist->threads->nr);
+			list_del(&pos->node);
+		}
+		free(evsel->counts);
+		evsel->counts = NULL;
+		perf_evlist__delete_maps(evlist);
+		cpus = NULL;
+		threads = NULL;
+	}
+	free(evsel);
+	free(evlist);
+	free(cpus);
+	free(threads);
+
+	return ret;
+}
+
+int main(int argc, char **argv)
+{
+	struct perf_config config;
+	int ret;
+
+	ret = ibs_config(&config, argc, argv);
+	if (ret)
+		goto fail;
+	ret = ibs_run(&config);
+	if (ret)
+		goto fail;
+	return 0;
+fail:
+	printf("An error occurred: %d (%s)\n", -ret, strerror(-ret));
+	return -1;
+}
-- 
1.7.6.1


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ