| /* |
| * syst.c - example of a simple system wide monitoring program |
| * |
| * Copyright (c) 2002-2006 Hewlett-Packard Development Company, L.P. |
| * Contributed by Stephane Eranian <eranian@hpl.hp.com> |
| * |
| * 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. |
| */ |
| #include <sys/types.h> |
| #include <inttypes.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <err.h> |
| |
| #include "perf_util.h" |
| |
| typedef struct { |
| const char *events; |
| int delay; |
| int excl; |
| int cpu; |
| int group; |
| } options_t; |
| |
| static options_t options; |
| static perf_event_desc_t **all_fds; |
| static int *num_fds; |
| |
| void |
| setup_cpu(int cpu) |
| { |
| perf_event_desc_t *fds; |
| int i, ret; |
| |
| ret = perf_setup_list_events(options.events, &all_fds[cpu], &num_fds[cpu]); |
| if (ret || (num_fds == 0)) |
| errx(1, "cannot setup events\n"); |
| fds = all_fds[cpu]; /* temp */ |
| |
| fds[0].fd = -1; |
| for(i=0; i < num_fds[cpu]; i++) { |
| fds[i].hw.disabled = options.group ? !i : 1; |
| |
| if (options.excl && ((options.group && !i) || (!options.group))) |
| fds[i].hw.exclusive = 1; |
| |
| fds[i].hw.disabled = options.group ? !i : 1; |
| |
| /* request timing information necessary for scaling counts */ |
| fds[i].hw.read_format = PERF_FORMAT_SCALE; |
| fds[i].fd = perf_event_open(&fds[i].hw, -1, cpu, (options.group ? fds[0].fd : -1), 0); |
| if (fds[i].fd == -1) |
| err(1, "cannot attach event to CPU%d %s", cpu, fds[i].name); |
| } |
| } |
| |
| void |
| measure(void) |
| { |
| perf_event_desc_t *fds; |
| long lret; |
| int c, cmin, cmax, ncpus; |
| int i, ret, l; |
| |
| printf("<press CTRL-C to quit before %ds time limit>\n", options.delay); |
| |
| cmin = 0; |
| |
| lret = sysconf(_SC_NPROCESSORS_ONLN); |
| if (lret < 0) |
| err(1, "cannot get number of online processors"); |
| |
| cmax = (int)lret; |
| |
| ncpus = cmax; |
| if (options.cpu != -1) { |
| cmin = options.cpu; |
| cmax = cmin + 1; |
| } |
| all_fds = calloc(ncpus, sizeof(perf_event_desc_t *)); |
| num_fds = calloc(ncpus, sizeof(int)); |
| |
| if (!all_fds || !num_fds) |
| err(1, "cannot allocate memory for internal structures"); |
| for(c=cmin ; c < cmax; c++) |
| setup_cpu(c); |
| |
| /* |
| * FIX this for hotplug CPU |
| */ |
| for(c=cmin ; c < cmax; c++) { |
| fds = all_fds[c]; |
| if (options.group) |
| ret = ioctl(fds[0].fd, PERF_EVENT_IOC_ENABLE, 0); |
| else for(i=0; i < num_fds[c]; i++) { |
| ret = ioctl(fds[i].fd, PERF_EVENT_IOC_ENABLE, 0); |
| if (ret) |
| err(1, "cannot enable event %s\n", fds[i].name); |
| } |
| } |
| |
| for(l=0; l < options.delay; l++) { |
| |
| sleep(1); |
| |
| puts("------------------------"); |
| for(c = cmin; c < cmax; c++) { |
| fds = all_fds[c]; |
| for(i=0; i < num_fds[c]; i++) { |
| uint64_t val, delta; |
| double ratio; |
| |
| ret = read(fds[i].fd, fds[i].values, sizeof(fds[i].values)); |
| if (ret != sizeof(fds[i].values)) { |
| if (ret == -1) |
| err(1, "cannot read event %d:%d", i, ret); |
| else |
| warnx("could not read event%d", i); |
| } |
| |
| /* |
| * scaling because we may be sharing the PMU and |
| * thus may be multiplexed |
| */ |
| val = perf_scale(fds[i].values); |
| ratio = perf_scale_ratio(fds[i].values); |
| delta = perf_scale_delta(fds[i].values, fds[i].prev_values); |
| |
| printf("CPU%d val=%-20"PRIu64" %-20"PRIu64" raw=%"PRIu64" ena=%"PRIu64" run=%"PRIu64" ratio=%.2f %s\n", |
| c, |
| val, |
| delta, |
| fds[i].values[0], |
| fds[i].values[1], fds[i].values[2], ratio, |
| fds[i].name); |
| fds[i].prev_values[0] = fds[i].values[0]; |
| fds[i].prev_values[1] = fds[i].values[1]; |
| fds[i].prev_values[2] = fds[i].values[2]; |
| } |
| } |
| } |
| for(c = cmin; c < cmax; c++) { |
| fds = all_fds[c]; |
| for(i=0; i < num_fds[c]; i++) |
| close(fds[i].fd); |
| perf_free_fds(fds, num_fds[c]); |
| } |
| } |
| |
| static void |
| usage(void) |
| { |
| printf("usage: syst [-c cpu] [-x] [-h] [-d delay] [-g] [-e event1,event2,...]\n"); |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| int c, ret; |
| |
| options.cpu = -1; |
| |
| while ((c=getopt(argc, argv,"hc:e:d:gx")) != -1) { |
| switch(c) { |
| case 'x': |
| options.excl = 1; |
| break; |
| case 'e': |
| options.events = optarg; |
| break; |
| case 'c': |
| options.cpu = atoi(optarg); |
| break; |
| case 'g': |
| options.group = 1; |
| break; |
| case 'd': |
| options.delay = atoi(optarg); |
| break; |
| case 'h': |
| usage(); |
| exit(0); |
| default: |
| errx(1, "unknown error"); |
| } |
| } |
| if (!options.delay) |
| options.delay = 20; |
| |
| if (!options.events) |
| options.events = "cycles,instructions"; |
| |
| ret = pfm_initialize(); |
| if (ret != PFM_SUCCESS) |
| errx(1, "libpfm initialization failed: %s\n", pfm_strerror(ret)); |
| |
| measure(); |
| |
| /* free libpfm resources cleanly */ |
| pfm_terminate(); |
| |
| return 0; |
| } |