|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* multiplex2.c - example of kernel-level time-based or overflow-based event multiplexing
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Copyright (c) 2004-2006 Hewlett-Packard Development Company, L.P.
|
|
Packit |
577717 |
* Contributed by Stephane Eranian <eranian@hpl.hp.com>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
577717 |
* modify it under the terms of the GNU General Public License as
|
|
Packit |
577717 |
* published by the Free Software Foundation; either version 2 of the
|
|
Packit |
577717 |
* License, or (at your option) any later version.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
577717 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
577717 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
577717 |
* General Public License for more details.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
577717 |
* along with this program; if not, write to the Free Software
|
|
Packit |
577717 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
Packit |
577717 |
* 02111-1307 USA
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
#ifndef _GNU_SOURCE
|
|
Packit |
577717 |
#define _GNU_SOURCE /* for getline */
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
#include <sys/types.h>
|
|
Packit |
577717 |
#include <inttypes.h>
|
|
Packit |
577717 |
#include <stdio.h>
|
|
Packit |
577717 |
#include <stdlib.h>
|
|
Packit |
577717 |
#include <stdarg.h>
|
|
Packit |
577717 |
#include <errno.h>
|
|
Packit |
577717 |
#include <unistd.h>
|
|
Packit |
577717 |
#include <string.h>
|
|
Packit |
577717 |
#include <syscall.h>
|
|
Packit |
577717 |
#include <getopt.h>
|
|
Packit |
577717 |
#include <signal.h>
|
|
Packit |
577717 |
#include <math.h>
|
|
Packit |
577717 |
#include <limits.h>
|
|
Packit |
577717 |
#include <fcntl.h>
|
|
Packit |
577717 |
#include <setjmp.h>
|
|
Packit |
577717 |
#include <sys/ioctl.h>
|
|
Packit |
577717 |
#include <sys/wait.h>
|
|
Packit |
577717 |
#include <sys/poll.h>
|
|
Packit |
577717 |
#include <sys/ptrace.h>
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include <perfmon/pfmlib.h>
|
|
Packit |
577717 |
#include <perfmon/perfmon.h>
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include "detect_pmcs.h"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define MIN_FULL_PERIODS 2
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define MAX_EVT_NAME_LEN 128
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define MULTIPLEX_VERSION "0.2"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define SMPL_FREQ_IN_HZ 300
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define NUM_PMCS PMU_MAX_PMCS
|
|
Packit |
577717 |
#define NUM_PMDS PMU_MAX_PMDS
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define MAX_NUM_COUNTERS NUM_PMDS
|
|
Packit |
577717 |
#define MAX_PMU_NAME_LEN 32
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef struct {
|
|
Packit |
577717 |
struct {
|
|
Packit |
577717 |
int opt_plm; /* which privilege level to monitor (more than one possible) */
|
|
Packit |
577717 |
int opt_debug; /* print debug information */
|
|
Packit |
577717 |
int opt_verbose; /* verbose output */
|
|
Packit |
577717 |
int opt_us_format; /* print large numbers with comma for thousands */
|
|
Packit |
577717 |
int opt_ovfl_switch; /* overflow-based switching */
|
|
Packit |
577717 |
int opt_is_system; /* use system-wide */
|
|
Packit |
577717 |
int opt_intr_only; /* interrupts only*/
|
|
Packit |
577717 |
int opt_no_cmd_out; /* redirect cmd output to /dev/null */
|
|
Packit |
577717 |
int opt_no_header; /* no header */
|
|
Packit |
577717 |
} program_opt_flags;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
unsigned long max_counters; /* maximum number of counter for the platform */
|
|
Packit |
577717 |
unsigned long session_timeout;
|
|
Packit |
577717 |
uint64_t smpl_period;
|
|
Packit |
577717 |
uint32_t smpl_freq;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
unsigned long cpu_mhz;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pid_t attach_pid;
|
|
Packit |
577717 |
int pin_cmd_cpu;
|
|
Packit |
577717 |
int pin_cpu;
|
|
Packit |
577717 |
struct timespec switch_timeout;
|
|
Packit |
577717 |
} program_options_t;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define opt_plm program_opt_flags.opt_plm
|
|
Packit |
577717 |
#define opt_debug program_opt_flags.opt_debug
|
|
Packit |
577717 |
#define opt_verbose program_opt_flags.opt_verbose
|
|
Packit |
577717 |
#define opt_us_format program_opt_flags.opt_us_format
|
|
Packit |
577717 |
#define opt_ovfl_switch program_opt_flags.opt_ovfl_switch
|
|
Packit |
577717 |
#define opt_is_system program_opt_flags.opt_is_system
|
|
Packit |
577717 |
#define opt_intr_only program_opt_flags.opt_intr_only
|
|
Packit |
577717 |
#define opt_no_cmd_out program_opt_flags.opt_no_cmd_out
|
|
Packit |
577717 |
#define opt_no_header program_opt_flags.opt_no_header
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef struct _event_set_t {
|
|
Packit |
577717 |
struct _event_set_t *next;
|
|
Packit |
577717 |
unsigned short id;
|
|
Packit |
577717 |
unsigned int n_events;
|
|
Packit |
577717 |
unsigned int pmcs_base;
|
|
Packit |
577717 |
unsigned int pmds_base;
|
|
Packit |
577717 |
int npmcs;
|
|
Packit |
577717 |
int npmds;
|
|
Packit |
577717 |
unsigned long set_runs;
|
|
Packit |
577717 |
char *event_str;
|
|
Packit |
577717 |
} event_set_t;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static program_options_t options;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static pfarg_pmc_t *all_pmcs;
|
|
Packit |
577717 |
static pfarg_pmd_t *all_pmds;
|
|
Packit |
577717 |
static uint64_t *all_values;
|
|
Packit |
577717 |
static event_set_t *current_set, *all_sets;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int num_pmds, num_pmcs, num_sets, total_events;
|
|
Packit |
577717 |
static unsigned long full_periods;
|
|
Packit |
577717 |
static volatile int time_to_quit;
|
|
Packit |
577717 |
static jmp_buf jbuf;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void fatal_error(char *fmt,...) __attribute__((noreturn));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
vbprintf(char *fmt, ...)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
va_list ap;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_verbose == 0) return;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
va_start(ap, fmt);
|
|
Packit |
577717 |
vprintf(fmt, ap);
|
|
Packit |
577717 |
va_end(ap);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
fatal_error(char *fmt, ...)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
va_list ap;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
va_start(ap, fmt);
|
|
Packit |
577717 |
vfprintf(stderr, fmt, ap);
|
|
Packit |
577717 |
va_end(ap);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
exit(1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* unreliable for CPU with variable clock speed
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
static unsigned long
|
|
Packit |
577717 |
get_cpu_speed(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
FILE *fp1;
|
|
Packit |
577717 |
unsigned long f1 = 0, f2 = 0;
|
|
Packit |
577717 |
char buffer[128], *p, *value;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(buffer, 0, sizeof(buffer));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
fp1 = fopen("/proc/cpuinfo", "r");
|
|
Packit |
577717 |
if (fp1 == NULL) return 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (;;) {
|
|
Packit |
577717 |
buffer[0] = '\0';
|
|
Packit |
577717 |
|
|
Packit |
577717 |
p = fgets(buffer, 127, fp1);
|
|
Packit |
577717 |
if (p == NULL)
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* skip blank lines */
|
|
Packit |
577717 |
if (*p == '\n') continue;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
p = strchr(buffer, ':');
|
|
Packit |
577717 |
if (p == NULL)
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* p+2: +1 = space, +2= firt character
|
|
Packit |
577717 |
* strlen()-1 gets rid of \n
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
*p = '\0';
|
|
Packit |
577717 |
value = p+2;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
value[strlen(value)-1] = '\0';
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (!strncmp("cpu MHz", buffer, 7)) {
|
|
Packit |
577717 |
float fl;
|
|
Packit |
577717 |
sscanf(value, "%f", &fl);
|
|
Packit |
577717 |
f1 = lroundf(fl);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (!strncmp("BogoMIPS", buffer, 8)) {
|
|
Packit |
577717 |
float fl;
|
|
Packit |
577717 |
sscanf(value, "%f", &fl);
|
|
Packit |
577717 |
f2 = lroundf(fl);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
fclose(fp1);
|
|
Packit |
577717 |
return f1 == 0 ? f2 : f1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* pin task to CPU
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
#ifndef __NR_sched_setaffinity
|
|
Packit |
577717 |
#error "you need to define __NR_sched_setaffinity"
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define MAX_CPUS 2048
|
|
Packit |
577717 |
#define NR_CPU_BITS (MAX_CPUS>>3)
|
|
Packit |
577717 |
int
|
|
Packit |
577717 |
pin_cpu(pid_t pid, unsigned int cpu)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
uint64_t my_mask[NR_CPU_BITS];
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (cpu >= MAX_CPUS)
|
|
Packit |
577717 |
fatal_error("this program supports only up to %d CPUs\n", MAX_CPUS);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
my_mask[cpu>>6] = 1ULL << (cpu&63);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return syscall(__NR_sched_setaffinity, pid, sizeof(my_mask), &my_mask);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int
|
|
Packit |
577717 |
child(char **arg)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.pin_cmd_cpu != -1) {
|
|
Packit |
577717 |
pin_cpu(getpid(), options.pin_cmd_cpu);
|
|
Packit |
577717 |
vbprintf("command running on CPU core %d\n", options.pin_cmd_cpu);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_no_cmd_out) {
|
|
Packit |
577717 |
close(1);
|
|
Packit |
577717 |
close(2);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
execvp(arg[0], arg);
|
|
Packit |
577717 |
/* not reached */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
exit(1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
dec2sep(char *str2, char *str, char sep)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int i, l, b, j, c=0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
l = strlen(str2);
|
|
Packit |
577717 |
if (l <= 3) {
|
|
Packit |
577717 |
strcpy(str, str2);
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
b = l + l /3 - (l%3 == 0); /* l%3=correction to avoid extraneous comma at the end */
|
|
Packit |
577717 |
for(i=l, j=0; i >= 0; i--, j++) {
|
|
Packit |
577717 |
if (j) c++;
|
|
Packit |
577717 |
str[b-j] = str2[i];
|
|
Packit |
577717 |
if (c == 3 && i>0) {
|
|
Packit |
577717 |
str[b-++j] = sep;
|
|
Packit |
577717 |
c = 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
print_results(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int i, j, cnt;
|
|
Packit |
577717 |
int ovfl_adj;
|
|
Packit |
577717 |
uint64_t value, set_runs;
|
|
Packit |
577717 |
event_set_t *e;
|
|
Packit |
577717 |
char *p;
|
|
Packit |
577717 |
char tmp1[32], tmp2[32], *str;
|
|
Packit |
577717 |
char mtotal_str[32], *mtotal;
|
|
Packit |
577717 |
char stotal_str[32], *stotal;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (full_periods < num_sets)
|
|
Packit |
577717 |
fatal_error("not all sets have been activated, need to run longer %lu\n", full_periods);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* print the results
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* It is important to realize, that the first event we specified may not
|
|
Packit |
577717 |
* be in PMD4. Not all events can be measured by any monitor. That's why
|
|
Packit |
577717 |
* we need to use the pc[] array to figure out where event i was allocated.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (options.opt_no_header == 0) {
|
|
Packit |
577717 |
printf("# %u Hz period = %u usecs\n# %"PRIu64" cycles @ %lu MHz\n",
|
|
Packit |
577717 |
options.smpl_freq,
|
|
Packit |
577717 |
1000000 / options.smpl_freq,
|
|
Packit |
577717 |
options.smpl_period,
|
|
Packit |
577717 |
options.cpu_mhz);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_ovfl_switch == 0)
|
|
Packit |
577717 |
printf("# using time-based multiplexing\n"
|
|
Packit |
577717 |
"# %uus effective switch timeout\n",
|
|
Packit |
577717 |
1000000 / options.smpl_freq);
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
printf("# using overflow-based multiplexing\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_is_system)
|
|
Packit |
577717 |
printf("# system-wide mode on CPU core %d\n",options.pin_cpu);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
printf("# %d sets\n", num_sets);
|
|
Packit |
577717 |
printf("# %.2f average run per set\n", (double)full_periods/num_sets);
|
|
Packit |
577717 |
printf("# set measured total #runs scaled total event name\n");
|
|
Packit |
577717 |
printf("# ------------------------------------------------------------------\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
ovfl_adj= options.opt_ovfl_switch ? 1 : 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (i=0, e = all_sets, cnt = 0; i < num_sets; i++, e = e->next) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
set_runs = e->set_runs;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
str = e->event_str;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(j=0; j < e->npmds-ovfl_adj; j++, cnt++) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
value = all_values[j+e->pmds_base];
|
|
Packit |
577717 |
|
|
Packit |
577717 |
sprintf(tmp1, "%"PRIu64, value);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_us_format) {
|
|
Packit |
577717 |
dec2sep(tmp1, mtotal_str, ',');
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
strcpy(mtotal_str, tmp1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
mtotal = mtotal_str;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* scaling
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
sprintf(tmp2, "%"PRIu64, ((value*full_periods)/set_runs));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_us_format) {
|
|
Packit |
577717 |
dec2sep(tmp2, stotal_str, ',');
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
strcpy(stotal_str, tmp2);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
stotal = stotal_str;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
printf(" %03d %20s %8"PRIu64" %20s %s\n",
|
|
Packit |
577717 |
i,
|
|
Packit |
577717 |
mtotal,
|
|
Packit |
577717 |
set_runs,
|
|
Packit |
577717 |
stotal,
|
|
Packit |
577717 |
str);
|
|
Packit |
577717 |
p = strchr(str, '\0');
|
|
Packit |
577717 |
if (p)
|
|
Packit |
577717 |
str = p+1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* skip first event
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (options.opt_ovfl_switch) cnt++;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
update_set(int ctxid)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int count;
|
|
Packit |
577717 |
int base;
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
int i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
base = current_set->pmds_base;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* we do not read the last counter (cpu_cycles) to avoid overwriting
|
|
Packit |
577717 |
* the reg_value field which will be used for next round
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* We need to retry the read in case we get EBUSY because it means that
|
|
Packit |
577717 |
* the child task context is not yet available from inspection by PFM_READ_PMDS2.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
count = current_set->npmds;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_ovfl_switch)
|
|
Packit |
577717 |
count--;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = pfm_read_pmds(ctxid, all_pmds + base, count);
|
|
Packit |
577717 |
if (ret == -1)
|
|
Packit |
577717 |
fatal_error("error reading set: %s\n", strerror(errno));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* update counts for this set */
|
|
Packit |
577717 |
for (i=0; i < count; i++) {
|
|
Packit |
577717 |
all_values[base+i] += all_pmds[base+i].reg_value;
|
|
Packit |
577717 |
/* reset for next round */
|
|
Packit |
577717 |
all_pmds[base+i].reg_value = 0UL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
switch_sets(int ctxid)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
update_set(ctxid);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
current_set = current_set->next;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (current_set == NULL)
|
|
Packit |
577717 |
current_set = all_sets;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
current_set->set_runs++;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
vbprintf("starting set %d run %lu\n",
|
|
Packit |
577717 |
current_set->id,
|
|
Packit |
577717 |
current_set->set_runs);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* we must reprogram all avaibale PMCs (or PMDS) to ensure that no
|
|
Packit |
577717 |
* state is left over from the previous set and which could conflict
|
|
Packit |
577717 |
* on restart
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (pfm_write_pmcs(ctxid, all_pmcs+current_set->pmcs_base, current_set->npmcs) == -1) {
|
|
Packit |
577717 |
fatal_error("error writing pmcs: %s\n", strerror(errno));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pfm_write_pmds(ctxid, all_pmds+current_set->pmds_base, current_set->npmds) == -1) {
|
|
Packit |
577717 |
fatal_error("error writing pmds: %s\n", strerror(errno));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
full_periods++;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_ovfl_switch && pfm_restart(ctxid) == -1) {
|
|
Packit |
577717 |
if (errno != EBUSY)
|
|
Packit |
577717 |
fatal_error("error pfm_restart: %s\n", strerror(errno));
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* in case of EBUSY, it probably means the task has exited now
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
sigintr_handler(int sig)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (sig == SIGALRM)
|
|
Packit |
577717 |
time_to_quit = 1;
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
time_to_quit = 2;
|
|
Packit |
577717 |
longjmp(jbuf, 1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
sigchld_handler(int sig)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
time_to_quit = 1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int
|
|
Packit |
577717 |
measure_one_task(char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int ctxid;
|
|
Packit |
577717 |
pfarg_ctx_t ctx[1];
|
|
Packit |
577717 |
pfarg_load_t load_arg;
|
|
Packit |
577717 |
struct pollfd pollfd;
|
|
Packit |
577717 |
pid_t pid;
|
|
Packit |
577717 |
int status, ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(ctx, 0, sizeof(ctx));
|
|
Packit |
577717 |
memset(&load_arg, 0, sizeof(load_arg));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* create the context
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctxid = pfm_create_context(ctx, NULL, NULL, 0);
|
|
Packit |
577717 |
if (ctxid == -1 ) {
|
|
Packit |
577717 |
if (errno == ENOSYS) {
|
|
Packit |
577717 |
fatal_error("Your kernel does not have performance monitoring support!\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
fatal_error("Can't create PFM context %s\n", strerror(errno));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* set close-on-exec to ensure we will be getting the PFM_END_MSG, i.e.,
|
|
Packit |
577717 |
* fd not visible to child.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (fcntl(ctxid, F_SETFD, FD_CLOEXEC))
|
|
Packit |
577717 |
fatal_error("cannot set CLOEXEC: %s\n", strerror(errno));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* write registers for first set
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (pfm_write_pmcs(ctxid, all_pmcs+current_set->pmcs_base, current_set->npmcs) == -1) {
|
|
Packit |
577717 |
fatal_error("error pfm_write_pmcs: %s\n", strerror(errno));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pfm_write_pmds(ctxid, all_pmds+current_set->pmds_base, current_set->npmds) == -1) {
|
|
Packit |
577717 |
fatal_error("error pfm_write_pmds: %s\n", strerror(errno));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* now launch the child code
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (options.attach_pid == 0) {
|
|
Packit |
577717 |
if ((pid= fork()) == -1) fatal_error("Cannot fork process\n");
|
|
Packit |
577717 |
if (pid == 0) exit(child(argv));
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
pid = options.attach_pid;
|
|
Packit |
577717 |
ret = ptrace(PTRACE_ATTACH, pid, NULL, 0);
|
|
Packit |
577717 |
if (ret) {
|
|
Packit |
577717 |
fatal_error("cannot attach to task %d: %s\n",options.attach_pid, strerror(errno));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = waitpid(pid, &status, WUNTRACED);
|
|
Packit |
577717 |
if (ret < 0 || WIFEXITED(status))
|
|
Packit |
577717 |
fatal_error("error command already terminated, exit code %d\n", WEXITSTATUS(status));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
vbprintf("child created and stopped\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* now attach the context
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
load_arg.load_pid = pid;
|
|
Packit |
577717 |
if (pfm_load_context(ctxid, &load_arg) == -1) {
|
|
Packit |
577717 |
fatal_error("pfm_load_context error errno %d\n",errno);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
current_set->set_runs = 1;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* start monitoring
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (pfm_start(ctxid, NULL) == -1) {
|
|
Packit |
577717 |
fatal_error("pfm_start error errno %d\n",errno);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ptrace(PTRACE_DETACH, pid, NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (setjmp(jbuf) == 1) {
|
|
Packit |
577717 |
if (time_to_quit == 1) {
|
|
Packit |
577717 |
printf("timeout expired\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (time_to_quit == 2)
|
|
Packit |
577717 |
printf("session interrupted\n");
|
|
Packit |
577717 |
goto finish_line;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.session_timeout) {
|
|
Packit |
577717 |
printf("<monitoring for %lu seconds>\n", options.session_timeout);
|
|
Packit |
577717 |
alarm(options.session_timeout);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
pollfd.fd = ctxid;
|
|
Packit |
577717 |
pollfd.events = POLLIN;
|
|
Packit |
577717 |
pollfd.revents = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
while(time_to_quit == 0) {
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* mainloop. poll timeout is in msecs
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
ret = poll(&pollfd, 1, 1000 / options.smpl_freq);
|
|
Packit |
577717 |
switch(ret) {
|
|
Packit |
577717 |
case 0:
|
|
Packit |
577717 |
ret = ptrace(PTRACE_ATTACH, pid, NULL, 0);
|
|
Packit |
577717 |
if (ret) {
|
|
Packit |
577717 |
time_to_quit = 1;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = waitpid(pid, &status, WUNTRACED);
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* exit with time_to_quit = 0
|
|
Packit |
577717 |
* to avoid unloading from dead thread
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (WIFEXITED(status))
|
|
Packit |
577717 |
goto finish_line;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
switch_sets(ctxid);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ptrace(PTRACE_DETACH, pid, NULL, 0);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
case -1: fatal_error("poll error: %s\n", strerror(errno));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
default: /* we don't even read END_MSG */
|
|
Packit |
577717 |
time_to_quit = 1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
finish_line:
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* cleanup after an alarm timeout
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (time_to_quit) {
|
|
Packit |
577717 |
/* stop monitored task */
|
|
Packit |
577717 |
ptrace(PTRACE_ATTACH, pid, NULL, 0);
|
|
Packit |
577717 |
waitpid(pid, NULL, WUNTRACED);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* detach context */
|
|
Packit |
577717 |
pfm_unload_context(ctxid);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.attach_pid == 0) {
|
|
Packit |
577717 |
kill(pid, SIGKILL);
|
|
Packit |
577717 |
waitpid(pid, &status, 0);
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
ptrace(PTRACE_DETACH, pid, NULL, 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (time_to_quit < 2) print_results();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
close(ctxid);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int
|
|
Packit |
577717 |
measure_one_cpu(char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int ctxid, status;
|
|
Packit |
577717 |
pfarg_ctx_t ctx[1];
|
|
Packit |
577717 |
pfarg_load_t load_arg;
|
|
Packit |
577717 |
struct pollfd pollfd;
|
|
Packit |
577717 |
pid_t pid = 0;
|
|
Packit |
577717 |
int ret, timeout;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(ctx, 0, sizeof(ctx));
|
|
Packit |
577717 |
memset(&load_arg, 0, sizeof(load_arg));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.pin_cpu == -1) {
|
|
Packit |
577717 |
options.pin_cpu = 0;
|
|
Packit |
577717 |
printf("forcing monitoring onto CPU core 0\n");
|
|
Packit |
577717 |
pin_cpu(getpid(), 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx[0].ctx_flags = PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* create the context
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
ctxid = pfm_create_context(ctx, NULL, NULL, 0);
|
|
Packit |
577717 |
if (ctxid == -1) {
|
|
Packit |
577717 |
if (errno == ENOSYS) {
|
|
Packit |
577717 |
fatal_error("Your kernel does not have performance monitoring support!\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
fatal_error("Can't create PFM context %s\n", strerror(errno));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* set close-on-exec to ensure we will be getting the PFM_END_MSG, i.e.,
|
|
Packit |
577717 |
* fd not visible to child.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (fcntl(ctxid, F_SETFD, FD_CLOEXEC))
|
|
Packit |
577717 |
fatal_error("cannot set CLOEXEC: %s\n", strerror(errno));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Now program the all the registers in one call
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Note that there is a limitation on the size of the argument vector
|
|
Packit |
577717 |
* that can be passed. It is usually set to a page size (16KB).
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (pfm_write_pmcs(ctxid, all_pmcs+current_set->pmcs_base, current_set->npmcs) == -1)
|
|
Packit |
577717 |
fatal_error("error: pfm_write_pmcs errno: %s\n", strerror(errno));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* initialize the PMD registers.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* To be read, each PMD must be either written or declared
|
|
Packit |
577717 |
* as being part of a sample (reg_smpl_pmds)
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (pfm_write_pmds(ctxid, all_pmds+current_set->pmds_base, current_set->npmds) == -1)
|
|
Packit |
577717 |
fatal_error("pfm_write_pmds error errno %d\n", strerror(errno));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* now launch the child code
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (*argv) {
|
|
Packit |
577717 |
if ((pid = fork()) == -1) fatal_error("Cannot fork process\n");
|
|
Packit |
577717 |
if (pid == 0) exit(child(argv));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* wait for the child to exec or be stopped
|
|
Packit |
577717 |
* We do this even in system-wide mode to ensure
|
|
Packit |
577717 |
* that the task does not start until we are ready
|
|
Packit |
577717 |
* to monitor.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (pid) {
|
|
Packit |
577717 |
ret = waitpid(pid, &status, WUNTRACED);
|
|
Packit |
577717 |
if (ret < 0 || WIFEXITED(status))
|
|
Packit |
577717 |
fatal_error("error command already terminated, exit code %d\n", WEXITSTATUS(status));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
vbprintf("child created and stopped\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* now attach the context
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
load_arg.load_pid = options.pin_cpu;
|
|
Packit |
577717 |
if (pfm_load_context(ctxid, &load_arg) == -1)
|
|
Packit |
577717 |
fatal_error("pfm_load_context error errno %d\n",errno);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* start monitoring
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (pfm_start(ctxid, NULL) == -1)
|
|
Packit |
577717 |
fatal_error("pfm_start error errno %d\n",errno);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pid) {
|
|
Packit |
577717 |
signal(SIGCHLD, sigchld_handler);
|
|
Packit |
577717 |
ptrace(PTRACE_DETACH, pid, NULL, 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* mainloop
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
pollfd.fd = ctxid;
|
|
Packit |
577717 |
pollfd.events = POLLIN;
|
|
Packit |
577717 |
pollfd.revents = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
timeout = options.opt_ovfl_switch ? -1 : (1000 / options.smpl_freq);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
while (time_to_quit == 0) {
|
|
Packit |
577717 |
ret = poll(&pollfd, 1, timeout);
|
|
Packit |
577717 |
switch(ret) {
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
case 0:
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
*we are consuming the message.
|
|
Packit |
577717 |
* to avoid this phase we could use PFM_FL_OVFL_NO_MSG
|
|
Packit |
577717 |
* and use signal based notification
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (options.opt_ovfl_switch) {
|
|
Packit |
577717 |
ssize_t r;
|
|
Packit |
577717 |
pfarg_msg_t msg;
|
|
Packit |
577717 |
r = read(ctxid, &msg, sizeof(msg));
|
|
Packit |
577717 |
(void) r;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
switch_sets(ctxid);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
if (errno != EINTR)
|
|
Packit |
577717 |
fatal_error("poll fails\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (full_periods < MIN_FULL_PERIODS)
|
|
Packit |
577717 |
fatal_error("Not enough periods (%lu) to print results\n", full_periods);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pid)
|
|
Packit |
577717 |
waitpid(pid, &status, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
print_results();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
close(ctxid);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int
|
|
Packit |
577717 |
mainloop(char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
event_set_t *e;
|
|
Packit |
577717 |
pfmlib_input_param_t inp;
|
|
Packit |
577717 |
pfmlib_output_param_t outp;
|
|
Packit |
577717 |
pfmlib_regmask_t impl_counters, used_pmcs;
|
|
Packit |
577717 |
pfmlib_event_t cycle_event;
|
|
Packit |
577717 |
unsigned int i, j;
|
|
Packit |
577717 |
char *p, *str;
|
|
Packit |
577717 |
unsigned int max_counters, allowed_counters;
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pfm_get_num_counters(&max_counters);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (max_counters < 2 && options.opt_ovfl_switch)
|
|
Packit |
577717 |
fatal_error("not enough counter to get overflow switching to work\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
allowed_counters = max_counters;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* account for overflow counter (cpu cycles)
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (options.opt_ovfl_switch) allowed_counters--;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(&used_pmcs, 0, sizeof(used_pmcs));
|
|
Packit |
577717 |
memset(&impl_counters, 0, sizeof(impl_counters));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pfm_get_impl_counters(&impl_counters);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
options.smpl_period = (options.cpu_mhz*1000000)/options.smpl_freq;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
vbprintf("%lu Hz period = %"PRIu64" cycles @ %lu Mhz\n", options.smpl_freq, options.smpl_period, options.cpu_mhz);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (e = all_sets; e; e = e->next) {
|
|
Packit |
577717 |
for (p = str = e->event_str; p ; ) {
|
|
Packit |
577717 |
p = strchr(str, ',');
|
|
Packit |
577717 |
if (p) str = p +1;
|
|
Packit |
577717 |
total_events++;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* account for extra event per set (cycle event)
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (options.opt_ovfl_switch) {
|
|
Packit |
577717 |
total_events += num_sets;
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* look for our trigger event
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (pfm_get_cycle_event(&cycle_event) != PFMLIB_SUCCESS) {
|
|
Packit |
577717 |
fatal_error("Cannot find cycle event\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
vbprintf("total_events=%u\n", total_events);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
all_pmcs = calloc(1, sizeof(pfarg_pmc_t)*total_events);
|
|
Packit |
577717 |
all_pmds = calloc(1, sizeof(pfarg_pmd_t)*total_events);
|
|
Packit |
577717 |
all_values = calloc(1, sizeof(uint64_t)*total_events);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (all_pmcs == NULL || all_pmds == NULL || all_values == NULL)
|
|
Packit |
577717 |
fatal_error("cannot allocate event tables\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* use the library to figure out assignments for all events of all sets
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
for (i=0, e = all_sets; i < num_sets; i++, e = e->next) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(&inp,0, sizeof(inp));
|
|
Packit |
577717 |
memset(&outp,0, sizeof(outp));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* build the pfp_unavail_pmcs bitmask by looking
|
|
Packit |
577717 |
* at what perfmon has available. It is not always
|
|
Packit |
577717 |
* the case that all PMU registers are actually available
|
|
Packit |
577717 |
* to applications. For instance, on IA-32 platforms, some
|
|
Packit |
577717 |
* registers may be reserved for the NMI watchdog timer.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* With this bitmap, the library knows which registers NOT to
|
|
Packit |
577717 |
* use. Of source, it is possible that no valid assignement may
|
|
Packit |
577717 |
* be possible if certina PMU registers are not available.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
detect_unavail_pmcs(-1, &inp.pfp_unavail_pmcs);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
str = e->event_str;
|
|
Packit |
577717 |
for(j=0, p = str; p && j < allowed_counters; j++) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
p = strchr(str, ',');
|
|
Packit |
577717 |
if (p) *p = '\0';
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pfm_find_full_event(str, &inp.pfp_events[j]) != PFMLIB_SUCCESS) {
|
|
Packit |
577717 |
fatal_error("Cannot find %s event for set %d event %d\n", str, i, j);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (p) {
|
|
Packit |
577717 |
*p = ',';
|
|
Packit |
577717 |
str = p + 1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (p) {
|
|
Packit |
577717 |
fatal_error("error in set %d: cannot have more than %d event(s) per set %s\n",
|
|
Packit |
577717 |
i,
|
|
Packit |
577717 |
allowed_counters,
|
|
Packit |
577717 |
options.opt_ovfl_switch ? "(overflow switch mode)": "(hardware limit)");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* add the cycle event as the last event when we switch on overflow
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (options.opt_ovfl_switch) {
|
|
Packit |
577717 |
inp.pfp_events[j] = cycle_event;
|
|
Packit |
577717 |
inp.pfp_event_count = j+1;
|
|
Packit |
577717 |
e->n_events = j+1;
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
e->n_events = j;
|
|
Packit |
577717 |
inp.pfp_event_count = j;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
inp.pfp_dfl_plm = options.opt_plm;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_is_system)
|
|
Packit |
577717 |
inp.pfp_flags = PFMLIB_PFP_SYSTEMWIDE;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
vbprintf("PMU programming for set %d\n", i);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* let the library do the hard work
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if ((ret=pfm_dispatch_events(&inp, NULL, &outp, NULL)) != PFMLIB_SUCCESS) {
|
|
Packit |
577717 |
fatal_error("cannot configure events for set %d: %s\n", i, pfm_strerror(ret));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
e->id = i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
e->pmcs_base = num_pmcs;
|
|
Packit |
577717 |
e->pmds_base = num_pmds;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* propagate from libpfm to kernel data structures
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
for (j=0; j < outp.pfp_pmc_count; j++, num_pmcs++) {
|
|
Packit |
577717 |
all_pmcs[num_pmcs].reg_num = outp.pfp_pmcs[j].reg_num;
|
|
Packit |
577717 |
all_pmcs[num_pmcs].reg_value = outp.pfp_pmcs[j].reg_value;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
for (j=0; j < outp.pfp_pmd_count; j++, num_pmds++)
|
|
Packit |
577717 |
all_pmds[num_pmds].reg_num = outp.pfp_pmds[j].reg_num;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
e->npmcs = num_pmcs - e->pmcs_base;
|
|
Packit |
577717 |
e->npmds = num_pmds - e->pmds_base;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_ovfl_switch) {
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* We do this even in system-wide mode to ensure
|
|
Packit |
577717 |
* that the task does not start until we are ready
|
|
Packit |
577717 |
* to monitor.
|
|
Packit |
577717 |
* setup the sampling period
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
all_pmds[num_pmds-1].reg_value = - options.smpl_period;
|
|
Packit |
577717 |
all_pmds[num_pmds-1].reg_short_reset = - options.smpl_period;
|
|
Packit |
577717 |
all_pmds[num_pmds-1].reg_long_reset = - options.smpl_period;
|
|
Packit |
577717 |
all_pmds[num_pmds-1].reg_flags = PFM_REGFL_OVFL_NOTIFY;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
vbprintf("set%d pmc_base=%d pmd_base=%d npmcs=%d npmds=%d\n",
|
|
Packit |
577717 |
e->id,
|
|
Packit |
577717 |
e->pmcs_base,
|
|
Packit |
577717 |
e->pmds_base,
|
|
Packit |
577717 |
e->npmcs,
|
|
Packit |
577717 |
e->npmds);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
current_set = all_sets;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
signal(SIGALRM, sigintr_handler);
|
|
Packit |
577717 |
signal(SIGINT, sigintr_handler);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_is_system)
|
|
Packit |
577717 |
return measure_one_cpu(argv);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return measure_one_task(argv);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct option multiplex_options[]={
|
|
Packit |
577717 |
{ "help", 0, 0, 1},
|
|
Packit |
577717 |
{ "freq", 1, 0, 2 },
|
|
Packit |
577717 |
{ "kernel-level", 0, 0, 3 },
|
|
Packit |
577717 |
{ "user-level", 0, 0, 4 },
|
|
Packit |
577717 |
{ "version", 0, 0, 5 },
|
|
Packit |
577717 |
{ "set", 1, 0, 6 },
|
|
Packit |
577717 |
{ "session-timeout", 1, 0, 7 },
|
|
Packit |
577717 |
{ "attach-task", 1, 0, 8 },
|
|
Packit |
577717 |
{ "pin-cmd", 1, 0, 9 },
|
|
Packit |
577717 |
{ "cpu", 1, 0, 10 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "verbose", 0, &options.opt_verbose, 1 },
|
|
Packit |
577717 |
{ "debug", 0, &options.opt_debug, 1 },
|
|
Packit |
577717 |
{ "us-counter-format", 0, &options.opt_us_format, 1},
|
|
Packit |
577717 |
{ "ovfl-switch", 0, &options.opt_ovfl_switch, 1},
|
|
Packit |
577717 |
{ "system-wide", 0, &options.opt_is_system, 1},
|
|
Packit |
577717 |
{ "no-cmd-output", 0, &options.opt_no_cmd_out, 1},
|
|
Packit |
577717 |
{ "no-header", 0, &options.opt_no_header, 1},
|
|
Packit |
577717 |
{ 0, 0, 0, 0}
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
generate_default_sets(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
event_set_t *es, *tail = NULL;
|
|
Packit |
577717 |
pfmlib_event_t events[2];
|
|
Packit |
577717 |
size_t len;
|
|
Packit |
577717 |
char *name;
|
|
Packit |
577717 |
unsigned int i;
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = pfm_get_cycle_event(&events[0]);
|
|
Packit |
577717 |
if (ret != PFMLIB_SUCCESS)
|
|
Packit |
577717 |
fatal_error("cannot find cycle event\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = pfm_get_inst_retired_event(&events[1]);
|
|
Packit |
577717 |
if (ret != PFMLIB_SUCCESS)
|
|
Packit |
577717 |
fatal_error("cannot find instruction retired event\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pfm_get_max_event_name_len(&len;;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (i=0; i < 2; i++) {
|
|
Packit |
577717 |
name = malloc(len+1);
|
|
Packit |
577717 |
if (name == NULL) {
|
|
Packit |
577717 |
fatal_error("cannot allocate space for event name\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
pfm_get_full_event_name(&events[i], name, len+1);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
es = (event_set_t *)malloc(sizeof(event_set_t));
|
|
Packit |
577717 |
if (es == NULL)
|
|
Packit |
577717 |
fatal_error("cannot allocate new event set\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(es, 0, sizeof(*es));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
es->event_str = name;
|
|
Packit |
577717 |
es->next = NULL;
|
|
Packit |
577717 |
es->n_events = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (all_sets == NULL)
|
|
Packit |
577717 |
all_sets = es;
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
tail->next = es;
|
|
Packit |
577717 |
tail = es;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
num_sets = i;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
print_usage(char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
printf("usage: %s [OPTIONS]... COMMAND\n", argv[0]);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
printf( "-h, --help\t\t\t\tdisplay this help and exit\n"
|
|
Packit |
577717 |
"-V, --version\t\t\t\toutput version information and exit\n"
|
|
Packit |
577717 |
"-u, --user-level\t\t\tmonitor at the user level for all events\n"
|
|
Packit |
577717 |
"-k, --kernel-level\t\t\tmonitor at the kernel level for all events\n"
|
|
Packit |
577717 |
"-c, --us-counter-format\tprint large counts with comma for thousands\n"
|
|
Packit |
577717 |
"-p pid, --attach-task pid\tattach to a running task\n"
|
|
Packit |
577717 |
"--set=ev1[,ev2,ev3,ev4,...]\t\tdescribe one set\n"
|
|
Packit |
577717 |
"--freq=number\t\t\t\tset set switching frequency in Hz\n"
|
|
Packit |
577717 |
"-c cpu, --cpu=cpu\t\t\tCPU to use for system-wide [default current]\n"
|
|
Packit |
577717 |
"--ovfl-switch\t\t\t\t\tuse overflow based multiplexing (default: time-based)\n"
|
|
Packit |
577717 |
"--verbose\t\t\t\tprint more information during execution\n"
|
|
Packit |
577717 |
"--system-wide\t\t\t\tuse system-wide (only one CPU at a time)\n"
|
|
Packit |
577717 |
"--excl-idle\t\t\texclude idle task(system-wide only)\n"
|
|
Packit |
577717 |
"--excl-intr\t\t\texclude interrupt triggered execution(system-wide only)\n"
|
|
Packit |
577717 |
"--intr-only\t\t\tinclude only interrupt triggered execution(system-wide only)\n"
|
|
Packit |
577717 |
"--session-timeout=sec\t\t\tsession timeout in seconds (system-wide only)\n"
|
|
Packit |
577717 |
"--no-cmd-output\t\t\t\toutput of executed command redirected to /dev/null\n"
|
|
Packit |
577717 |
"--pin-cmd=cpu\t\t\t\tpin executed command onto a specific cpu\n"
|
|
Packit |
577717 |
);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int
|
|
Packit |
577717 |
main(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
char *endptr = NULL;
|
|
Packit |
577717 |
pfmlib_options_t pfmlib_options;
|
|
Packit |
577717 |
event_set_t *tail = NULL, *es;
|
|
Packit |
577717 |
unsigned long long_val;
|
|
Packit |
577717 |
int c, ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
options.pin_cmd_cpu = options.pin_cpu = -1;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
while ((c=getopt_long(argc, argv,"+vhkuVct:p:", multiplex_options, 0)) != -1) {
|
|
Packit |
577717 |
switch(c) {
|
|
Packit |
577717 |
case 0: continue; /* fast path for options */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
print_usage(argv);
|
|
Packit |
577717 |
exit(0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
case 'v': options.opt_verbose = 1;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 'c':
|
|
Packit |
577717 |
options.opt_us_format = 1;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 2:
|
|
Packit |
577717 |
if (options.smpl_freq) fatal_error("sampling frequency set twice\n");
|
|
Packit |
577717 |
options.smpl_freq = strtoul(optarg, &endptr, 10);
|
|
Packit |
577717 |
if (*endptr != '\0')
|
|
Packit |
577717 |
fatal_error("invalid freqyency: %s\n", optarg);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 3:
|
|
Packit |
577717 |
case 'k':
|
|
Packit |
577717 |
options.opt_plm |= PFM_PLM0;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 4:
|
|
Packit |
577717 |
case 'u':
|
|
Packit |
577717 |
options.opt_plm |= PFM_PLM3;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 'V':
|
|
Packit |
577717 |
case 5:
|
|
Packit |
577717 |
printf("multiplex version " MULTIPLEX_VERSION " Date: " __DATE__ "\n"
|
|
Packit |
577717 |
"Copyright (C) 2004 Hewlett-Packard Company\n");
|
|
Packit |
577717 |
exit(0);
|
|
Packit |
577717 |
case 6:
|
|
Packit |
577717 |
es = (event_set_t *)malloc(sizeof(event_set_t));
|
|
Packit |
577717 |
if (es == NULL) fatal_error("cannot allocate new event set\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
es->event_str = optarg;
|
|
Packit |
577717 |
es->next = NULL;
|
|
Packit |
577717 |
es->n_events = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (all_sets == NULL)
|
|
Packit |
577717 |
all_sets = es;
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
tail->next = es;
|
|
Packit |
577717 |
tail = es;
|
|
Packit |
577717 |
num_sets++;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 't':
|
|
Packit |
577717 |
case 7:
|
|
Packit |
577717 |
if (options.session_timeout) fatal_error("too many timeouts\n");
|
|
Packit |
577717 |
if (*optarg == '\0') fatal_error("--session-timeout needs an argument\n");
|
|
Packit |
577717 |
long_val = strtoul(optarg,&endptr, 10);
|
|
Packit |
577717 |
if (*endptr != '\0')
|
|
Packit |
577717 |
fatal_error("invalid number of seconds for timeout: %s\n", optarg);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (long_val >= UINT_MAX)
|
|
Packit |
577717 |
fatal_error("timeout is too big, must be < %u\n", UINT_MAX);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
options.session_timeout = (unsigned int)long_val;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 'p':
|
|
Packit |
577717 |
case 8:
|
|
Packit |
577717 |
if (options.attach_pid) fatal_error("process to attach specified twice\n");
|
|
Packit |
577717 |
options.attach_pid = (pid_t)atoi(optarg);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 9:
|
|
Packit |
577717 |
if (options.pin_cmd_cpu != -1) fatal_error("cannot pin command twice\n");
|
|
Packit |
577717 |
options.pin_cmd_cpu = atoi(optarg);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
case 10:
|
|
Packit |
577717 |
if (options.pin_cpu != -1) fatal_error("cannot pin to more than one cpu\n");
|
|
Packit |
577717 |
options.pin_cpu = atoi(optarg);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
fatal_error(""); /* just quit silently now */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (optind == argc && options.opt_is_system == 0 && options.attach_pid == 0)
|
|
Packit |
577717 |
fatal_error("you need to specify a command to measure\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* pass options to library (optional)
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
memset(&pfmlib_options, 0, sizeof(pfmlib_options));
|
|
Packit |
577717 |
pfmlib_options.pfm_debug = 0; /* set to 1 for debug */
|
|
Packit |
577717 |
pfmlib_options.pfm_verbose = options.opt_verbose; /* set to 1 for verbose */
|
|
Packit |
577717 |
pfm_set_options(&pfmlib_options);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Initialize pfm library (required before we can use it)
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
ret = pfm_initialize();
|
|
Packit |
577717 |
if (ret != PFMLIB_SUCCESS)
|
|
Packit |
577717 |
fatal_error("Cannot initialize library: %s\n", pfm_strerror(ret));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if ((options.cpu_mhz = get_cpu_speed()) == 0)
|
|
Packit |
577717 |
fatal_error("can't get CPU speed\n");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.smpl_freq == 0UL)
|
|
Packit |
577717 |
options.smpl_freq = SMPL_FREQ_IN_HZ;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (options.opt_plm == 0)
|
|
Packit |
577717 |
options.opt_plm = PFM_PLM3;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (num_sets == 0)
|
|
Packit |
577717 |
generate_default_sets();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return mainloop(argv+optind);
|
|
Packit |
577717 |
}
|