|
Packit |
577717 |
/* $Id: global.c,v 1.37 2004/01/12 14:25:40 mikpe Exp $
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* usage: ./global [sampling_interval_usec [sleep_interval_sec]]
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* This test program illustrates how a process may use the
|
|
Packit |
577717 |
* Linux x86 Performance-Monitoring Counters interface to
|
|
Packit |
577717 |
* do system-wide performance monitoring.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Copyright (C) 2000-2004 Mikael Pettersson
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
#include <errno.h>
|
|
Packit |
577717 |
#include <setjmp.h>
|
|
Packit |
577717 |
#include <signal.h>
|
|
Packit |
577717 |
#include <stddef.h>
|
|
Packit |
577717 |
#include <stdio.h>
|
|
Packit |
577717 |
#include <stdlib.h>
|
|
Packit |
577717 |
#include <string.h>
|
|
Packit |
577717 |
#include <unistd.h>
|
|
Packit |
577717 |
#include "libperfctr.h"
|
|
Packit |
577717 |
#include "arch.h"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct gperfctr *gperfctr;
|
|
Packit |
577717 |
static struct perfctr_info info;
|
|
Packit |
577717 |
static unsigned int nrcpus;
|
|
Packit |
577717 |
static unsigned short *cpu_logical_map;
|
|
Packit |
577717 |
struct gperfctr_state { /* no longer defined in or used by the kernel */
|
|
Packit |
577717 |
unsigned int nrcpus;
|
|
Packit |
577717 |
struct gperfctr_cpu_state cpu_state[1]; /* actually 'nrcpus' */
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
static struct gperfctr_state *state;
|
|
Packit |
577717 |
static struct gperfctr_state *prev_state;
|
|
Packit |
577717 |
static unsigned int sample_num;
|
|
Packit |
577717 |
int counting_mips; /* for CPUs that cannot FLOPS */
|
|
Packit |
577717 |
static unsigned long sampling_interval = 1000000; /* XXX: reduce for >4GHz CPUs */
|
|
Packit |
577717 |
static unsigned int sleep_interval = 5;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static jmp_buf main_buf;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void onint(int sig) /* ^C handler */
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
longjmp(main_buf, 1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void catch_sigint(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct sigaction act;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(&act, 0, sizeof act);
|
|
Packit |
577717 |
act.sa_handler = onint;
|
|
Packit |
577717 |
if( sigaction(SIGINT, &act, NULL) < 0 ) {
|
|
Packit |
577717 |
perror("unable to catch SIGINT");
|
|
Packit |
577717 |
exit(1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int hweight32(unsigned int w)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
|
|
Packit |
577717 |
res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
|
|
Packit |
577717 |
res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
|
|
Packit |
577717 |
res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
|
|
Packit |
577717 |
return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void setup_cpu_logical_map_and_nrcpus(const struct perfctr_cpus_info *cpus_info)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
const unsigned int *cpus, *cpus_forbidden;
|
|
Packit |
577717 |
unsigned int nrwords, i, cpumask, bitmask;
|
|
Packit |
577717 |
unsigned int logical_cpu_nr, kernel_cpu_nr;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cpus = cpus_info->cpus->mask;
|
|
Packit |
577717 |
cpus_forbidden = cpus_info->cpus_forbidden->mask;
|
|
Packit |
577717 |
nrwords = cpus_info->cpus->nrwords;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
nrcpus = 0;
|
|
Packit |
577717 |
for(i = 0; i < nrwords; ++i)
|
|
Packit |
577717 |
nrcpus += hweight32(cpus[i] & ~cpus_forbidden[i]);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cpu_logical_map = malloc(nrcpus*sizeof(cpu_logical_map[0]));
|
|
Packit |
577717 |
if( !cpu_logical_map ) {
|
|
Packit |
577717 |
perror("malloc");
|
|
Packit |
577717 |
exit(1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
logical_cpu_nr = 0;
|
|
Packit |
577717 |
for(i = 0; i < nrwords; ++i) {
|
|
Packit |
577717 |
cpumask = cpus[i] & ~cpus_forbidden[i];
|
|
Packit |
577717 |
kernel_cpu_nr = i * 8 * sizeof(int);
|
|
Packit |
577717 |
for(bitmask = 1; cpumask != 0; ++kernel_cpu_nr, bitmask <<= 1) {
|
|
Packit |
577717 |
if( cpumask & bitmask ) {
|
|
Packit |
577717 |
cpumask &= ~bitmask;
|
|
Packit |
577717 |
cpu_logical_map[logical_cpu_nr] = kernel_cpu_nr;
|
|
Packit |
577717 |
++logical_cpu_nr;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if( logical_cpu_nr != nrcpus )
|
|
Packit |
577717 |
abort();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void do_init(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct perfctr_cpus_info *cpus_info;
|
|
Packit |
577717 |
size_t nbytes;
|
|
Packit |
577717 |
unsigned int i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
gperfctr = gperfctr_open();
|
|
Packit |
577717 |
if( !gperfctr ) {
|
|
Packit |
577717 |
perror("gperfctr_open");
|
|
Packit |
577717 |
exit(1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if( gperfctr_info(gperfctr, &info) < 0 ) {
|
|
Packit |
577717 |
perror("gperfctr_info");
|
|
Packit |
577717 |
exit(1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
cpus_info = gperfctr_cpus_info(gperfctr);
|
|
Packit |
577717 |
if( !cpus_info ) {
|
|
Packit |
577717 |
perror("gperfctr_info");
|
|
Packit |
577717 |
exit(1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
printf("\nPerfCtr Info:\n");
|
|
Packit |
577717 |
perfctr_info_print(&info;;
|
|
Packit |
577717 |
perfctr_cpus_info_print(cpus_info);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* use all non-forbidden CPUs */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
setup_cpu_logical_map_and_nrcpus(cpus_info);
|
|
Packit |
577717 |
free(cpus_info);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* now alloc state memory based on nrcpus */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
nbytes = offsetof(struct gperfctr_state, cpu_state[0])
|
|
Packit |
577717 |
+ nrcpus * sizeof(state->cpu_state[0]);
|
|
Packit |
577717 |
state = malloc(nbytes);
|
|
Packit |
577717 |
prev_state = malloc(nbytes);
|
|
Packit |
577717 |
if( !state || !prev_state ) {
|
|
Packit |
577717 |
perror("malloc");
|
|
Packit |
577717 |
exit(1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
memset(state, 0, nbytes);
|
|
Packit |
577717 |
memset(prev_state, 0, nbytes);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* format state to indicate which CPUs we want to sample */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i = 0; i < nrcpus; ++i)
|
|
Packit |
577717 |
state->cpu_state[i].cpu = cpu_logical_map[i];
|
|
Packit |
577717 |
state->nrcpus = nrcpus;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int do_read(unsigned int sleep_interval)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int i, cpu, ctr;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i = 0; i < state->nrcpus; ++i) {
|
|
Packit |
577717 |
if( gperfctr_read(gperfctr, &state->cpu_state[i]) < 0 ) {
|
|
Packit |
577717 |
perror("gperfctr_read");
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
printf("\nSample #%u\n", ++sample_num);
|
|
Packit |
577717 |
for(i = 0; i < state->nrcpus; ++i) {
|
|
Packit |
577717 |
cpu = state->cpu_state[i].cpu;
|
|
Packit |
577717 |
printf("\nCPU %d:\n", cpu);
|
|
Packit |
577717 |
if( state->cpu_state[i].cpu_control.tsc_on )
|
|
Packit |
577717 |
printf("\ttsc\t%lld\n", state->cpu_state[i].sum.tsc);
|
|
Packit |
577717 |
for(ctr = 0; ctr < state->cpu_state[i].cpu_control.nractrs; ++ctr)
|
|
Packit |
577717 |
printf("\tpmc[%d]\t%lld\n",
|
|
Packit |
577717 |
ctr, state->cpu_state[i].sum.pmc[ctr]);
|
|
Packit |
577717 |
if( ctr >= 1 ) { /* compute and display MFLOP/s or MIP/s */
|
|
Packit |
577717 |
unsigned long long tsc = state->cpu_state[i].sum.tsc;
|
|
Packit |
577717 |
unsigned long long prev_tsc = prev_state->cpu_state[i].sum.tsc;
|
|
Packit |
577717 |
unsigned long long ticks = tsc - prev_tsc;
|
|
Packit |
577717 |
unsigned long long pmc0 = state->cpu_state[i].sum.pmc[0];
|
|
Packit |
577717 |
unsigned long long prev_pmc0 = prev_state->cpu_state[i].sum.pmc[0];
|
|
Packit |
577717 |
unsigned long long ops = pmc0 - prev_pmc0;
|
|
Packit |
577717 |
double seconds = state->cpu_state[i].cpu_control.tsc_on
|
|
Packit |
577717 |
? ((double)ticks * (double)(info.tsc_to_cpu_mult ? : 1) / (double)info.cpu_khz) / 1000.0
|
|
Packit |
577717 |
: (double)sleep_interval; /* don't div-by-0 on WinChip ... */
|
|
Packit |
577717 |
printf("\tSince previous sample:\n");
|
|
Packit |
577717 |
printf("\tSECONDS\t%.15g\n", seconds);
|
|
Packit |
577717 |
printf("\t%s\t%llu\n", counting_mips ? "INSNS" : "FLOPS", ops);
|
|
Packit |
577717 |
printf("\t%s/s\t%.15g\n",
|
|
Packit |
577717 |
counting_mips ? "MIP" : "MFLOP",
|
|
Packit |
577717 |
((double)ops / seconds) / 1e6);
|
|
Packit |
577717 |
prev_state->cpu_state[i].sum.tsc = tsc;
|
|
Packit |
577717 |
prev_state->cpu_state[i].sum.pmc[0] = pmc0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void print_control(const struct perfctr_cpu_control *control)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
printf("\nControl used:\n");
|
|
Packit |
577717 |
perfctr_cpu_control_print(control);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void do_enable(unsigned long sampling_interval)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct perfctr_cpu_control cpu_control;
|
|
Packit |
577717 |
unsigned int i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
setup_control(&info, &cpu_control);
|
|
Packit |
577717 |
print_control(&cpu_control);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i = 0; i < nrcpus; ++i) {
|
|
Packit |
577717 |
struct gperfctr_cpu_control control;
|
|
Packit |
577717 |
control.cpu = cpu_logical_map[i];
|
|
Packit |
577717 |
control.cpu_control = cpu_control;
|
|
Packit |
577717 |
if( gperfctr_control(gperfctr, &control) < 0 ) {
|
|
Packit |
577717 |
perror("gperfctr_control");
|
|
Packit |
577717 |
exit(1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if( gperfctr_start(gperfctr, sampling_interval) < 0 ) {
|
|
Packit |
577717 |
perror("gperfctr_start");
|
|
Packit |
577717 |
exit(1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int main(int argc, const char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if( argc >= 2 ) {
|
|
Packit |
577717 |
sampling_interval = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
if( argc >= 3 )
|
|
Packit |
577717 |
sleep_interval = strtoul(argv[2], NULL, 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if( setjmp(main_buf) == 0 ) {
|
|
Packit |
577717 |
catch_sigint();
|
|
Packit |
577717 |
do_init();
|
|
Packit |
577717 |
do_enable(sampling_interval);
|
|
Packit |
577717 |
printf("\nSampling interval:\t%lu usec\n", sampling_interval);
|
|
Packit |
577717 |
printf("Sleep interval:\t\t%u sec\n", sleep_interval);
|
|
Packit |
577717 |
do {
|
|
Packit |
577717 |
sleep(sleep_interval);
|
|
Packit |
577717 |
} while( do_read(sleep_interval) == 0 );
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if( gperfctr ) {
|
|
Packit |
577717 |
printf("shutting down..\n");
|
|
Packit |
577717 |
gperfctr_stop(gperfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|