From: Robert Richter <robert.richter@amd.com>
To: Peter Zijlstra <peterz@infradead.org>
Cc: Ingo Molnar <mingo@elte.hu>,
Arnaldo Carvalho de Melo <acme@redhat.com>,
LKML <linux-kernel@vger.kernel.org>,
Robert Richter <robert.richter@amd.com>
Subject: [PATCH 6/7] perf, x86: Example code for AMD IBS
Date: Thu, 28 Jul 2011 15:46:51 +0200 [thread overview]
Message-ID: <1311860812-28748-7-git-send-email-robert.richter@amd.com> (raw)
In-Reply-To: <1311860812-28748-1-git-send-email-robert.richter@amd.com>
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.
Signed-off-by: Robert Richter <robert.richter@amd.com>
---
tools/perf/Documentation/examples/Makefile | 44 +++
tools/perf/Documentation/examples/ibs.c | 436 ++++++++++++++++++++++++++++
2 files changed, 480 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..3d4e334
--- /dev/null
+++ b/tools/perf/Documentation/examples/ibs.c
@@ -0,0 +1,436 @@
+/*
+ * 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 "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;
+}
+
+static void print_ibs_fetch(int cpu, uint64_t *ibs)
+{
+ 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, uint64_t *ibs)
+{
+ 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.5.3
next prev parent reply other threads:[~2011-07-28 13:50 UTC|newest]
Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-07-28 13:46 [PATCH 0/7] perf, x86: Implement " Robert Richter
2011-07-28 13:46 ` [PATCH 1/7] perf, x86: share IBS macros between perf and oprofile Robert Richter
2011-07-28 13:46 ` [PATCH 2/7] perf, x86: Implement IBS initialization Robert Richter
2011-07-29 16:58 ` Peter Zijlstra
2011-08-01 5:27 ` Robert Richter
2011-08-02 11:49 ` Peter Zijlstra
2011-08-12 17:49 ` Robert Richter
2011-07-28 13:46 ` [PATCH 3/7] perf, x86: Implement IBS event configuration Robert Richter
2011-08-02 11:35 ` Peter Zijlstra
2011-08-12 19:51 ` Robert Richter
2011-07-28 13:46 ` [PATCH 4/7] perf, x86: Implement IBS interrupt handler Robert Richter
2011-07-29 16:58 ` Peter Zijlstra
2011-08-01 5:32 ` Robert Richter
2011-08-01 15:21 ` Peter Zijlstra
2011-08-01 16:38 ` Don Zickus
2011-08-05 9:55 ` Ingo Molnar
2011-08-05 13:47 ` Don Zickus
2011-08-02 11:43 ` Peter Zijlstra
2011-08-12 18:07 ` Robert Richter
2011-07-28 13:46 ` [PATCH 5/7] perf, x86: Implement IBS pmu control ops Robert Richter
2011-07-28 13:46 ` Robert Richter [this message]
2011-07-29 16:58 ` [PATCH 6/7] perf, x86: Example code for AMD IBS Peter Zijlstra
2011-08-01 5:50 ` Robert Richter
2011-08-02 10:37 ` Peter Zijlstra
2011-08-03 8:27 ` Michael Cree
2011-08-03 17:56 ` Robert Richter
2011-07-28 13:46 ` [PATCH 7/7] perf, x86: Implement 64 bit counter support for IBS Robert Richter
2011-07-29 16:58 ` Peter Zijlstra
2011-07-29 17:02 ` Peter Zijlstra
2011-08-01 5:55 ` Robert Richter
2011-07-29 17:01 ` Peter Zijlstra
2011-08-01 6:13 ` Robert Richter
2011-08-02 11:37 ` Peter Zijlstra
2011-08-12 18:11 ` Robert Richter
2011-07-29 17:07 ` [PATCH 0/7] perf, x86: Implement AMD IBS Peter Zijlstra
2011-08-01 5:21 ` Robert Richter
2011-08-02 11:29 ` Peter Zijlstra
2011-08-12 19:43 ` Robert Richter
2011-08-16 21:05 ` Robert Richter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1311860812-28748-7-git-send-email-robert.richter@amd.com \
--to=robert.richter@amd.com \
--cc=acme@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@elte.hu \
--cc=peterz@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome