Messages in this thread Patch in this message | | | From | Robert Richter <> | Subject | [PATCH 6/7] perf, x86: Example code for AMD IBS | Date | Thu, 28 Jul 2011 15:46:51 +0200 |
| |
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
| |