/* * self_pipe.c - dual process ping-pong example to stress PMU context switch of one process * * Copyright (c) 2008 Google, Inc * Contributed by Stephane Eranian * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * This file is part of libpfm, a performance monitoring support library for * applications on Linux. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "perf_util.h" static struct { const char *events; int cpu; int delay; } options; int pin_cpu(pid_t pid, unsigned int cpu) { cpu_set_t mask; CPU_ZERO(&mask); CPU_SET(cpu, &mask); return sched_setaffinity(pid, sizeof(mask), &mask); } static volatile int quit; void sig_handler(int n) { quit = 1; } static void do_child(int fr, int fw) { char c; ssize_t ret; for(;;) { ret = read(fr, &c, 1); if (ret < 0) break; ret = write(fw, "c", 1); if (ret < 0) break; } printf("child exited\n"); exit(0); } static void measure(void) { perf_event_desc_t *fds = NULL; int num_fds = 0; uint64_t values[3]; ssize_t n; int i, ret; int pr[2], pw[2]; pid_t pid; char cc = '0'; ret = pfm_initialize(); if (ret != PFM_SUCCESS) err(1, "cannot initialize libpfm"); if (options.cpu == -1) { srandom(getpid()); options.cpu = random() % sysconf(_SC_NPROCESSORS_ONLN); } ret = pipe(pr); if (ret) err(1, "cannot create read pipe"); ret = pipe(pw); if (ret) err(1, "cannot create write pipe"); ret = perf_setup_list_events(options.events, &fds, &num_fds); if (ret || !num_fds) exit(1); for(i=0; i < num_fds; i++) { fds[i].hw.disabled = 1; fds[i].hw.read_format = PERF_FORMAT_SCALE; fds[i].fd = perf_event_open(&fds[i].hw, 0, -1, -1, 0); if (fds[i].fd == -1) err(1, "cannot open event %d", i); } /* * Pin to CPU0, inherited by child process. That will enforce * the ping-pionging and thus stress the PMU context switch * which is what we want */ ret = pin_cpu(getpid(), options.cpu); if (ret) err(1, "cannot pin to CPU%d", options.cpu); printf("Both processes pinned to CPU%d, running for %d seconds\n", options.cpu, options.delay); /* * create second process which is not monitoring at the moment */ switch(pid=fork()) { case -1: err(1, "cannot create child\n"); exit(1); /* not reached */ case 0: /* do not inherit session fd */ for(i=0; i < num_fds; i++) close(fds[i].fd); /* pr[]: write master, read child */ /* pw[]: read master, write child */ close(pr[1]); close(pw[0]); do_child(pr[0], pw[1]); exit(1); } close(pr[0]); close(pw[1]); /* * Let's roll now */ prctl(PR_TASK_PERF_EVENTS_ENABLE); signal(SIGALRM, sig_handler); alarm(options.delay); /* * ping pong loop */ while(!quit) { n = write(pr[1], "c", 1); if (n < 1) err(1, "write failed"); n = read(pw[0], &cc, 1); if (n < 1) err(1, "read failed"); } prctl(PR_TASK_PERF_EVENTS_DISABLE); for(i=0; i < num_fds; i++) { uint64_t val; double ratio; ret = read(fds[i].fd, values, sizeof(values)); if (ret == -1) err(1,"pfm_read error"); if (ret != sizeof(values)) errx(1, "did not read correct amount %d", ret); val = perf_scale(values); ratio = perf_scale_ratio(values); if (ratio == 1.0) printf("%20"PRIu64" %s\n", val, fds[i].name); else if (ratio == 0.0) printf("%20"PRIu64" %s (did not run: competing session)\n", val, fds[i].name); else printf("%20"PRIu64" %s (scaled from %.2f%% of time)\n", val, fds[i].name, ratio*100.0); } /* * kill child process */ kill(SIGKILL, pid); /* * close pipes */ close(pr[1]); close(pw[0]); /* * and destroy our session */ for(i=0; i < num_fds; i++) close(fds[i].fd); perf_free_fds(fds, num_fds); /* free libpfm resources cleanly */ pfm_terminate(); } static void usage(void) { printf("usage: self_pipe [-h] [-c cpu] [-d delay] [-e event1,event2,...]\n"); } int main(int argc, char **argv) { int c; options.cpu = -1; options.delay = -1; while ((c=getopt(argc, argv,"he:c:d:")) != -1) { switch(c) { case 'e': options.events = optarg; break; case 'c': options.cpu = atoi(optarg); break; case 'd': options.delay = atoi(optarg); break; case 'h': usage(); exit(0); default: errx(1, "unknown error"); } } if (!options.events) options.events = "cycles,instructions"; if (options.delay == -1) options.delay = 10; measure(); return 0; }