mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-25 11:37:56 +00:00
4d9bf2038f
may need some work, but at least now it will read properly. (gnn you are still on the hook to help out with this like you committed). Sponsored by: Netflix.
2439 lines
67 KiB
C
2439 lines
67 KiB
C
/*-
|
|
* Copyright (c) 2014, 2015 Netflix Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer,
|
|
* in this position and unchanged.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <sys/errno.h>
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
#include <getopt.h>
|
|
#include "eval_expr.h"
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#define MAX_COUNTER_SLOTS 1024
|
|
#define MAX_NLEN 64
|
|
#define MAX_CPU 64
|
|
static int verbose = 0;
|
|
|
|
extern char **environ;
|
|
extern struct expression *master_exp;
|
|
struct expression *master_exp=NULL;
|
|
|
|
#define PMC_INITIAL_ALLOC 512
|
|
extern char **valid_pmcs;
|
|
char **valid_pmcs = NULL;
|
|
extern int valid_pmc_cnt;
|
|
int valid_pmc_cnt=0;
|
|
extern int pmc_allocated_cnt;
|
|
int pmc_allocated_cnt=0;
|
|
|
|
/*
|
|
* The following two varients on popen and pclose with
|
|
* the cavet that they get you the PID so that you
|
|
* can supply it to pclose so it can send a SIGTERM
|
|
* to the process.
|
|
*/
|
|
static FILE *
|
|
my_popen(const char *command, const char *dir, pid_t *p_pid)
|
|
{
|
|
FILE *io_out, *io_in;
|
|
int pdesin[2], pdesout[2];
|
|
char *argv[4];
|
|
pid_t pid;
|
|
char cmd[4];
|
|
char cmd2[1024];
|
|
char arg1[4];
|
|
|
|
if ((strcmp(dir, "r") != 0) &&
|
|
(strcmp(dir, "w") != 0)) {
|
|
errno = EINVAL;
|
|
return(NULL);
|
|
}
|
|
if (pipe(pdesin) < 0)
|
|
return (NULL);
|
|
|
|
if (pipe(pdesout) < 0) {
|
|
(void)close(pdesin[0]);
|
|
(void)close(pdesin[1]);
|
|
return (NULL);
|
|
}
|
|
strcpy(cmd, "sh");
|
|
strcpy(arg1, "-c");
|
|
strcpy(cmd2, command);
|
|
argv[0] = cmd;
|
|
argv[1] = arg1;
|
|
argv[2] = cmd2;
|
|
argv[3] = NULL;
|
|
|
|
switch (pid = fork()) {
|
|
case -1: /* Error. */
|
|
(void)close(pdesin[0]);
|
|
(void)close(pdesin[1]);
|
|
(void)close(pdesout[0]);
|
|
(void)close(pdesout[1]);
|
|
return (NULL);
|
|
/* NOTREACHED */
|
|
case 0: /* Child. */
|
|
/* Close out un-used sides */
|
|
(void)close(pdesin[1]);
|
|
(void)close(pdesout[0]);
|
|
/* Now prepare the stdin of the process */
|
|
close(0);
|
|
(void)dup(pdesin[0]);
|
|
(void)close(pdesin[0]);
|
|
/* Now prepare the stdout of the process */
|
|
close(1);
|
|
(void)dup(pdesout[1]);
|
|
/* And lets do stderr just in case */
|
|
close(2);
|
|
(void)dup(pdesout[1]);
|
|
(void)close(pdesout[1]);
|
|
/* Now run it */
|
|
execve("/bin/sh", argv, environ);
|
|
exit(127);
|
|
/* NOTREACHED */
|
|
}
|
|
/* Parent; assume fdopen can't fail. */
|
|
/* Store the pid */
|
|
*p_pid = pid;
|
|
if (strcmp(dir, "r") != 0) {
|
|
io_out = fdopen(pdesin[1], "w");
|
|
(void)close(pdesin[0]);
|
|
(void)close(pdesout[0]);
|
|
(void)close(pdesout[1]);
|
|
return(io_out);
|
|
} else {
|
|
/* Prepare the input stream */
|
|
io_in = fdopen(pdesout[0], "r");
|
|
(void)close(pdesout[1]);
|
|
(void)close(pdesin[0]);
|
|
(void)close(pdesin[1]);
|
|
return (io_in);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* pclose --
|
|
* Pclose returns -1 if stream is not associated with a `popened' command,
|
|
* if already `pclosed', or waitpid returns an error.
|
|
*/
|
|
static void
|
|
my_pclose(FILE *io, pid_t the_pid)
|
|
{
|
|
int pstat;
|
|
pid_t pid;
|
|
|
|
/*
|
|
* Find the appropriate file pointer and remove it from the list.
|
|
*/
|
|
(void)fclose(io);
|
|
/* Die if you are not dead! */
|
|
kill(the_pid, SIGTERM);
|
|
do {
|
|
pid = wait4(the_pid, &pstat, 0, (struct rusage *)0);
|
|
} while (pid == -1 && errno == EINTR);
|
|
}
|
|
|
|
struct counters {
|
|
struct counters *next_cpu;
|
|
char counter_name[MAX_NLEN]; /* Name of counter */
|
|
int cpu; /* CPU we are on */
|
|
int pos; /* Index we are filling to. */
|
|
uint64_t vals[MAX_COUNTER_SLOTS]; /* Last 64 entries */
|
|
uint64_t sum; /* Summary of entries */
|
|
};
|
|
|
|
extern struct counters *glob_cpu[MAX_CPU];
|
|
struct counters *glob_cpu[MAX_CPU];
|
|
|
|
extern struct counters *cnts;
|
|
struct counters *cnts=NULL;
|
|
|
|
extern int ncnts;
|
|
int ncnts=0;
|
|
|
|
extern int (*expression)(struct counters *, int);
|
|
int (*expression)(struct counters *, int);
|
|
|
|
static const char *threshold=NULL;
|
|
static const char *command;
|
|
|
|
struct cpu_entry {
|
|
const char *name;
|
|
const char *thresh;
|
|
const char *command;
|
|
int (*func)(struct counters *, int);
|
|
};
|
|
|
|
|
|
struct cpu_type {
|
|
char cputype[32];
|
|
int number;
|
|
struct cpu_entry *ents;
|
|
void (*explain)(const char *name);
|
|
};
|
|
extern struct cpu_type the_cpu;
|
|
struct cpu_type the_cpu;
|
|
|
|
static void
|
|
explain_name_sb(const char *name)
|
|
{
|
|
const char *mythresh;
|
|
if (strcmp(name, "allocstall1") == 0) {
|
|
printf("Examine PARTIAL_RAT_STALLS.SLOW_LEA_WINDOW / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh > .05";
|
|
} else if (strcmp(name, "allocstall2") == 0) {
|
|
printf("Examine PARTIAL_RAT_STALLS.FLAGS_MERGE_UOP_CYCLES/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh > .05";
|
|
} else if (strcmp(name, "br_miss") == 0) {
|
|
printf("Examine (20 * BR_MISP_RETIRED.ALL_BRANCHES)/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .2";
|
|
} else if (strcmp(name, "splitload") == 0) {
|
|
printf("Examine MEM_UOP_RETIRED.SPLIT_LOADS * 5) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .1";
|
|
} else if (strcmp(name, "splitstore") == 0) {
|
|
printf("Examine MEM_UOP_RETIRED.SPLIT_STORES / MEM_UOP_RETIRED.ALL_STORES\n");
|
|
mythresh = "thresh >= .01";
|
|
} else if (strcmp(name, "contested") == 0) {
|
|
printf("Examine (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM * 60) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .05";
|
|
} else if (strcmp(name, "blockstorefwd") == 0) {
|
|
printf("Examine (LD_BLOCKS_STORE_FORWARD * 13) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .05";
|
|
} else if (strcmp(name, "cache2") == 0) {
|
|
printf("Examine ((MEM_LOAD_RETIRED.L3_HIT * 26) + \n");
|
|
printf(" (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT * 43) + \n");
|
|
printf(" (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM * 60)) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
printf("**Note we have it labeled MEM_LOAD_UOPS_RETIRED.LLC_HIT not MEM_LOAD_RETIRED.L3_HIT\n");
|
|
mythresh = "thresh >= .2";
|
|
} else if (strcmp(name, "cache1") == 0) {
|
|
printf("Examine (MEM_LOAD_UOPS_MISC_RETIRED.LLC_MISS * 180) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .2";
|
|
} else if (strcmp(name, "dtlbmissload") == 0) {
|
|
printf("Examine (((DTLB_LOAD_MISSES.STLB_HIT * 7) + DTLB_LOAD_MISSES.WALK_DURATION)\n");
|
|
printf(" / CPU_CLK_UNHALTED.THREAD_P)\n");
|
|
mythresh = "thresh >= .1";
|
|
} else if (strcmp(name, "frontendstall") == 0) {
|
|
printf("Examine IDQ_UOPS_NOT_DELIVERED.CORE / (CPU_CLK_UNHALTED.THREAD_P * 4)\n");
|
|
mythresh = "thresh >= .15";
|
|
} else if (strcmp(name, "clears") == 0) {
|
|
printf("Examine ((MACHINE_CLEARS.MEMORY_ORDERING + \n");
|
|
printf(" MACHINE_CLEARS.SMC + \n");
|
|
printf(" MACHINE_CLEARS.MASKMOV ) * 100 ) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .02";
|
|
} else if (strcmp(name, "microassist") == 0) {
|
|
printf("Examine IDQ.MS_CYCLES / (CPU_CLK_UNHALTED.THREAD_P * 4)\n");
|
|
printf("***We use IDQ.MS_UOPS,cmask=1 to get cycles\n");
|
|
mythresh = "thresh >= .05";
|
|
} else if (strcmp(name, "aliasing_4k") == 0) {
|
|
printf("Examine (LD_BLOCKS_PARTIAL.ADDRESS_ALIAS * 5) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .1";
|
|
} else if (strcmp(name, "fpassist") == 0) {
|
|
printf("Examine FP_ASSIST.ANY/INST_RETIRED.ANY_P\n");
|
|
mythresh = "look for a excessive value";
|
|
} else if (strcmp(name, "otherassistavx") == 0) {
|
|
printf("Examine (OTHER_ASSISTS.AVX_TO_SSE * 75)/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "look for a excessive value";
|
|
} else if (strcmp(name, "otherassistsse") == 0) {
|
|
printf("Examine (OTHER_ASSISTS.SSE_TO_AVX * 75)/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "look for a excessive value";
|
|
} else if (strcmp(name, "eff1") == 0) {
|
|
printf("Examine (UOPS_RETIRED.RETIRE_SLOTS)/(4 *CPU_CLK_UNHALTED.THREAD_P)\n");
|
|
mythresh = "thresh < .9";
|
|
} else if (strcmp(name, "eff2") == 0) {
|
|
printf("Examine CPU_CLK_UNHALTED.THREAD_P/INST_RETIRED.ANY_P\n");
|
|
mythresh = "thresh > 1.0";
|
|
} else if (strcmp(name, "dtlbmissstore") == 0) {
|
|
printf("Examine (((DTLB_STORE_MISSES.STLB_HIT * 7) + DTLB_STORE_MISSES.WALK_DURATION)\n");
|
|
printf(" / CPU_CLK_UNHALTED.THREAD_P)\n");
|
|
mythresh = "thresh >= .05";
|
|
} else {
|
|
printf("Unknown name:%s\n", name);
|
|
mythresh = "unknown entry";
|
|
}
|
|
printf("If the value printed is %s we may have the ability to improve performance\n", mythresh);
|
|
}
|
|
|
|
static void
|
|
explain_name_ib(const char *name)
|
|
{
|
|
const char *mythresh;
|
|
if (strcmp(name, "br_miss") == 0) {
|
|
printf("Examine ((BR_MISP_RETIRED.ALL_BRANCHES /(BR_MISP_RETIRED.ALL_BRANCHES +\n");
|
|
printf(" MACHINE_CLEAR.COUNT) * ((UOPS_ISSUED.ANY - UOPS_RETIRED.RETIRE_SLOTS + 4 * INT_MISC.RECOVERY_CYCLES)\n");
|
|
printf("/ (4 * CPU_CLK_UNHALTED.THREAD))))\n");
|
|
mythresh = "thresh >= .2";
|
|
} else if (strcmp(name, "eff1") == 0) {
|
|
printf("Examine (UOPS_RETIRED.RETIRE_SLOTS)/(4 *CPU_CLK_UNHALTED.THREAD_P)\n");
|
|
mythresh = "thresh < .9";
|
|
} else if (strcmp(name, "eff2") == 0) {
|
|
printf("Examine CPU_CLK_UNHALTED.THREAD_P/INST_RETIRED.ANY_P\n");
|
|
mythresh = "thresh > 1.0";
|
|
} else if (strcmp(name, "cache1") == 0) {
|
|
printf("Examine (MEM_LOAD_UOPS_LLC_MISS_RETIRED.LOCAL_DRAM * 180) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .2";
|
|
} else if (strcmp(name, "cache2") == 0) {
|
|
printf("Examine (MEM_LOAD_UOPS_RETIRED.LLC_HIT / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .2";
|
|
} else if (strcmp(name, "itlbmiss") == 0) {
|
|
printf("Examine ITLB_MISSES.WALK_DURATION / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh > .05";
|
|
} else if (strcmp(name, "icachemiss") == 0) {
|
|
printf("Examine (ICACHE.IFETCH_STALL - ITLB_MISSES.WALK_DURATION)/ CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh > .05";
|
|
} else if (strcmp(name, "lcpstall") == 0) {
|
|
printf("Examine ILD_STALL.LCP/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh > .05";
|
|
} else if (strcmp(name, "datashare") == 0) {
|
|
printf("Examine (MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT * 43)/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh > .05";
|
|
} else if (strcmp(name, "blockstorefwd") == 0) {
|
|
printf("Examine (LD_BLOCKS_STORE_FORWARD * 13) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .05";
|
|
} else if (strcmp(name, "splitload") == 0) {
|
|
printf("Examine ((L1D_PEND_MISS.PENDING / MEM_LOAD_UOPS_RETIRED.L1_MISS) *\n");
|
|
printf(" LD_BLOCKS.NO_SR)/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .1";
|
|
} else if (strcmp(name, "splitstore") == 0) {
|
|
printf("Examine MEM_UOP_RETIRED.SPLIT_STORES / MEM_UOP_RETIRED.ALL_STORES\n");
|
|
mythresh = "thresh >= .01";
|
|
} else if (strcmp(name, "aliasing_4k") == 0) {
|
|
printf("Examine (LD_BLOCKS_PARTIAL.ADDRESS_ALIAS * 5) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .1";
|
|
} else if (strcmp(name, "dtlbmissload") == 0) {
|
|
printf("Examine (((DTLB_LOAD_MISSES.STLB_HIT * 7) + DTLB_LOAD_MISSES.WALK_DURATION)\n");
|
|
printf(" / CPU_CLK_UNHALTED.THREAD_P)\n");
|
|
mythresh = "thresh >= .1";
|
|
} else if (strcmp(name, "dtlbmissstore") == 0) {
|
|
printf("Examine (((DTLB_STORE_MISSES.STLB_HIT * 7) + DTLB_STORE_MISSES.WALK_DURATION)\n");
|
|
printf(" / CPU_CLK_UNHALTED.THREAD_P)\n");
|
|
mythresh = "thresh >= .05";
|
|
} else if (strcmp(name, "contested") == 0) {
|
|
printf("Examine (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM * 60) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .05";
|
|
} else if (strcmp(name, "clears") == 0) {
|
|
printf("Examine ((MACHINE_CLEARS.MEMORY_ORDERING + \n");
|
|
printf(" MACHINE_CLEARS.SMC + \n");
|
|
printf(" MACHINE_CLEARS.MASKMOV ) * 100 ) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .02";
|
|
} else if (strcmp(name, "microassist") == 0) {
|
|
printf("Examine IDQ.MS_CYCLES / (4 * CPU_CLK_UNHALTED.THREAD_P)\n");
|
|
printf("***We use IDQ.MS_UOPS,cmask=1 to get cycles\n");
|
|
mythresh = "thresh >= .05";
|
|
} else if (strcmp(name, "fpassist") == 0) {
|
|
printf("Examine FP_ASSIST.ANY/INST_RETIRED.ANY_P\n");
|
|
mythresh = "look for a excessive value";
|
|
} else if (strcmp(name, "otherassistavx") == 0) {
|
|
printf("Examine (OTHER_ASSISTS.AVX_TO_SSE * 75)/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "look for a excessive value";
|
|
} else if (strcmp(name, "otherassistsse") == 0) {
|
|
printf("Examine (OTHER_ASSISTS.SSE_TO_AVX * 75)/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "look for a excessive value";
|
|
} else {
|
|
printf("Unknown name:%s\n", name);
|
|
mythresh = "unknown entry";
|
|
}
|
|
printf("If the value printed is %s we may have the ability to improve performance\n", mythresh);
|
|
}
|
|
|
|
|
|
static void
|
|
explain_name_has(const char *name)
|
|
{
|
|
const char *mythresh;
|
|
if (strcmp(name, "eff1") == 0) {
|
|
printf("Examine (UOPS_RETIRED.RETIRE_SLOTS)/(4 *CPU_CLK_UNHALTED.THREAD_P)\n");
|
|
mythresh = "thresh < .75";
|
|
} else if (strcmp(name, "eff2") == 0) {
|
|
printf("Examine CPU_CLK_UNHALTED.THREAD_P/INST_RETIRED.ANY_P\n");
|
|
mythresh = "thresh > 1.0";
|
|
} else if (strcmp(name, "itlbmiss") == 0) {
|
|
printf("Examine ITLB_MISSES.WALK_DURATION / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh > .05";
|
|
} else if (strcmp(name, "icachemiss") == 0) {
|
|
printf("Examine (36 * ICACHE.MISSES)/ CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh > .05";
|
|
} else if (strcmp(name, "lcpstall") == 0) {
|
|
printf("Examine ILD_STALL.LCP/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh > .05";
|
|
} else if (strcmp(name, "cache1") == 0) {
|
|
printf("Examine (MEM_LOAD_UOPS_LLC_MISS_RETIRED.LOCAL_DRAM * 180) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .2";
|
|
} else if (strcmp(name, "cache2") == 0) {
|
|
printf("Examine ((MEM_LOAD_UOPS_RETIRED.LLC_HIT * 36) + \n");
|
|
printf(" (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT * 72) + \n");
|
|
printf(" (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM * 84))\n");
|
|
printf(" / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .2";
|
|
} else if (strcmp(name, "contested") == 0) {
|
|
printf("Examine (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM * 84) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .05";
|
|
} else if (strcmp(name, "datashare") == 0) {
|
|
printf("Examine (MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT * 72)/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh > .05";
|
|
} else if (strcmp(name, "blockstorefwd") == 0) {
|
|
printf("Examine (LD_BLOCKS_STORE_FORWARD * 13) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .05";
|
|
} else if (strcmp(name, "splitload") == 0) {
|
|
printf("Examine (MEM_UOP_RETIRED.SPLIT_LOADS * 5) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .1";
|
|
} else if (strcmp(name, "splitstore") == 0) {
|
|
printf("Examine MEM_UOP_RETIRED.SPLIT_STORES / MEM_UOP_RETIRED.ALL_STORES\n");
|
|
mythresh = "thresh >= .01";
|
|
} else if (strcmp(name, "aliasing_4k") == 0) {
|
|
printf("Examine (LD_BLOCKS_PARTIAL.ADDRESS_ALIAS * 5) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .1";
|
|
} else if (strcmp(name, "dtlbmissload") == 0) {
|
|
printf("Examine (((DTLB_LOAD_MISSES.STLB_HIT * 7) + DTLB_LOAD_MISSES.WALK_DURATION)\n");
|
|
printf(" / CPU_CLK_UNHALTED.THREAD_P)\n");
|
|
mythresh = "thresh >= .1";
|
|
} else if (strcmp(name, "br_miss") == 0) {
|
|
printf("Examine (20 * BR_MISP_RETIRED.ALL_BRANCHES)/CPU_CLK_UNHALTED.THREAD\n");
|
|
mythresh = "thresh >= .2";
|
|
} else if (strcmp(name, "clears") == 0) {
|
|
printf("Examine ((MACHINE_CLEARS.MEMORY_ORDERING + \n");
|
|
printf(" MACHINE_CLEARS.SMC + \n");
|
|
printf(" MACHINE_CLEARS.MASKMOV ) * 100 ) / CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "thresh >= .02";
|
|
} else if (strcmp(name, "microassist") == 0) {
|
|
printf("Examine IDQ.MS_CYCLES / (4 * CPU_CLK_UNHALTED.THREAD_P)\n");
|
|
printf("***We use IDQ.MS_UOPS,cmask=1 to get cycles\n");
|
|
mythresh = "thresh >= .05";
|
|
} else if (strcmp(name, "fpassist") == 0) {
|
|
printf("Examine FP_ASSIST.ANY/INST_RETIRED.ANY_P\n");
|
|
mythresh = "look for a excessive value";
|
|
} else if (strcmp(name, "otherassistavx") == 0) {
|
|
printf("Examine (OTHER_ASSISTS.AVX_TO_SSE * 75)/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "look for a excessive value";
|
|
} else if (strcmp(name, "otherassistsse") == 0) {
|
|
printf("Examine (OTHER_ASSISTS.SSE_TO_AVX * 75)/CPU_CLK_UNHALTED.THREAD_P\n");
|
|
mythresh = "look for a excessive value";
|
|
} else {
|
|
printf("Unknown name:%s\n", name);
|
|
mythresh = "unknown entry";
|
|
}
|
|
printf("If the value printed is %s we may have the ability to improve performance\n", mythresh);
|
|
}
|
|
|
|
|
|
static struct counters *
|
|
find_counter(struct counters *base, const char *name)
|
|
{
|
|
struct counters *at;
|
|
int len;
|
|
|
|
at = base;
|
|
len = strlen(name);
|
|
while(at) {
|
|
if (strncmp(at->counter_name, name, len) == 0) {
|
|
return(at);
|
|
}
|
|
at = at->next_cpu;
|
|
}
|
|
printf("Can't find counter %s\n", name);
|
|
printf("We have:\n");
|
|
at = base;
|
|
while(at) {
|
|
printf("- %s\n", at->counter_name);
|
|
at = at->next_cpu;
|
|
}
|
|
exit(-1);
|
|
}
|
|
|
|
static int
|
|
allocstall1(struct counters *cpu, int pos)
|
|
{
|
|
/* 1 - PARTIAL_RAT_STALLS.SLOW_LEA_WINDOW/CPU_CLK_UNHALTED.THREAD_P (thresh > .05)*/
|
|
int ret;
|
|
struct counters *partial;
|
|
struct counters *unhalt;
|
|
double un, par, res;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
partial = find_counter(cpu, "PARTIAL_RAT_STALLS.SLOW_LEA_WINDOW");
|
|
if (pos != -1) {
|
|
par = partial->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
par = partial->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = par/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
allocstall2(struct counters *cpu, int pos)
|
|
{
|
|
/* 2 - PARTIAL_RAT_STALLS.FLAGS_MERGE_UOP_CYCLES/CPU_CLK_UNHALTED.THREAD_P (thresh >.05) */
|
|
int ret;
|
|
struct counters *partial;
|
|
struct counters *unhalt;
|
|
double un, par, res;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
partial = find_counter(cpu, "PARTIAL_RAT_STALLS.FLAGS_MERGE_UOP");
|
|
if (pos != -1) {
|
|
par = partial->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
par = partial->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = par/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
br_mispredict(struct counters *cpu, int pos)
|
|
{
|
|
struct counters *brctr;
|
|
struct counters *unhalt;
|
|
int ret;
|
|
/* 3 - (20 * BR_MISP_RETIRED.ALL_BRANCHES)/CPU_CLK_UNHALTED.THREAD_P (thresh >= .2) */
|
|
double br, un, con, res;
|
|
con = 20.0;
|
|
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
brctr = find_counter(cpu, "BR_MISP_RETIRED.ALL_BRANCHES");
|
|
if (pos != -1) {
|
|
br = brctr->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
br = brctr->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (con * br)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
br_mispredictib(struct counters *cpu, int pos)
|
|
{
|
|
struct counters *brctr;
|
|
struct counters *unhalt;
|
|
struct counters *clear, *clear2, *clear3;
|
|
struct counters *uops;
|
|
struct counters *recv;
|
|
struct counters *iss;
|
|
/* "pmcstat -s CPU_CLK_UNHALTED.THREAD_P -s BR_MISP_RETIRED.ALL_BRANCHES -s MACHINE_CLEARS.MEMORY_ORDERING -s MACHINE_CLEARS.SMC -s MACHINE_CLEARS.MASKMOV -s UOPS_ISSUED.ANY -s UOPS_RETIRED.RETIRE_SLOTS -s INT_MISC.RECOVERY_CYCLES -w 1",*/
|
|
int ret;
|
|
/*
|
|
* (BR_MISP_RETIRED.ALL_BRANCHES /
|
|
* (BR_MISP_RETIRED.ALL_BRANCHES +
|
|
* MACHINE_CLEAR.COUNT) *
|
|
* ((UOPS_ISSUED.ANY - UOPS_RETIRED.RETIRE_SLOTS + 4 * INT_MISC.RECOVERY_CYCLES) / (4 * CPU_CLK_UNHALTED.THREAD)))
|
|
*
|
|
*/
|
|
double br, cl, cl2, cl3, uo, re, un, con, res, is;
|
|
con = 4.0;
|
|
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
brctr = find_counter(cpu, "BR_MISP_RETIRED.ALL_BRANCHES");
|
|
clear = find_counter(cpu, "MACHINE_CLEARS.MEMORY_ORDERING");
|
|
clear2 = find_counter(cpu, "MACHINE_CLEARS.SMC");
|
|
clear3 = find_counter(cpu, "MACHINE_CLEARS.MASKMOV");
|
|
uops = find_counter(cpu, "UOPS_RETIRED.RETIRE_SLOTS");
|
|
iss = find_counter(cpu, "UOPS_ISSUED.ANY");
|
|
recv = find_counter(cpu, "INT_MISC.RECOVERY_CYCLES");
|
|
if (pos != -1) {
|
|
br = brctr->vals[pos] * 1.0;
|
|
cl = clear->vals[pos] * 1.0;
|
|
cl2 = clear2->vals[pos] * 1.0;
|
|
cl3 = clear3->vals[pos] * 1.0;
|
|
uo = uops->vals[pos] * 1.0;
|
|
re = recv->vals[pos] * 1.0;
|
|
is = iss->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
br = brctr->sum * 1.0;
|
|
cl = clear->sum * 1.0;
|
|
cl2 = clear2->sum * 1.0;
|
|
cl3 = clear3->sum * 1.0;
|
|
uo = uops->sum * 1.0;
|
|
re = recv->sum * 1.0;
|
|
is = iss->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (br/(br + cl + cl2 + cl3) * ((is - uo + con * re) / (con * un)));
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
splitloadib(struct counters *cpu, int pos)
|
|
{
|
|
int ret;
|
|
struct counters *mem;
|
|
struct counters *l1d, *ldblock;
|
|
struct counters *unhalt;
|
|
double un, memd, res, l1, ldb;
|
|
/*
|
|
* ((L1D_PEND_MISS.PENDING / MEM_LOAD_UOPS_RETIRED.L1_MISS) * LD_BLOCKS.NO_SR) / CPU_CLK_UNHALTED.THREAD_P
|
|
* "pmcstat -s CPU_CLK_UNHALTED.THREAD_P -s L1D_PEND_MISS.PENDING -s MEM_LOAD_UOPS_RETIRED.L1_MISS -s LD_BLOCKS.NO_SR -w 1",
|
|
*/
|
|
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
mem = find_counter(cpu, "MEM_LOAD_UOPS_RETIRED.L1_MISS");
|
|
l1d = find_counter(cpu, "L1D_PEND_MISS.PENDING");
|
|
ldblock = find_counter(cpu, "LD_BLOCKS.NO_SR");
|
|
if (pos != -1) {
|
|
memd = mem->vals[pos] * 1.0;
|
|
l1 = l1d->vals[pos] * 1.0;
|
|
ldb = ldblock->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
memd = mem->sum * 1.0;
|
|
l1 = l1d->sum * 1.0;
|
|
ldb = ldblock->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = ((l1 / memd) * ldb)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
splitload(struct counters *cpu, int pos)
|
|
{
|
|
int ret;
|
|
struct counters *mem;
|
|
struct counters *unhalt;
|
|
double con, un, memd, res;
|
|
/* 4 - (MEM_UOP_RETIRED.SPLIT_LOADS * 5) / CPU_CLK_UNHALTED.THREAD_P (thresh >= .1)*/
|
|
|
|
con = 5.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
mem = find_counter(cpu, "MEM_UOP_RETIRED.SPLIT_LOADS");
|
|
if (pos != -1) {
|
|
memd = mem->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
memd = mem->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (memd * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
splitstore(struct counters *cpu, int pos)
|
|
{
|
|
/* 5 - MEM_UOP_RETIRED.SPLIT_STORES / MEM_UOP_RETIRED.ALL_STORES (thresh > 0.01) */
|
|
int ret;
|
|
struct counters *mem_split;
|
|
struct counters *mem_stores;
|
|
double memsplit, memstore, res;
|
|
mem_split = find_counter(cpu, "MEM_UOP_RETIRED.SPLIT_STORES");
|
|
mem_stores = find_counter(cpu, "MEM_UOP_RETIRED.ALL_STORES");
|
|
if (pos != -1) {
|
|
memsplit = mem_split->vals[pos] * 1.0;
|
|
memstore = mem_stores->vals[pos] * 1.0;
|
|
} else {
|
|
memsplit = mem_split->sum * 1.0;
|
|
memstore = mem_stores->sum * 1.0;
|
|
}
|
|
res = memsplit/memstore;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
|
|
static int
|
|
contested(struct counters *cpu, int pos)
|
|
{
|
|
/* 6 - (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM * 60) / CPU_CLK_UNHALTED.THREAD_P (thresh >.05) */
|
|
int ret;
|
|
struct counters *mem;
|
|
struct counters *unhalt;
|
|
double con, un, memd, res;
|
|
|
|
con = 60.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
mem = find_counter(cpu, "MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM");
|
|
if (pos != -1) {
|
|
memd = mem->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
memd = mem->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (memd * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
contested_has(struct counters *cpu, int pos)
|
|
{
|
|
/* 6 - (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM * 84) / CPU_CLK_UNHALTED.THREAD_P (thresh >.05) */
|
|
int ret;
|
|
struct counters *mem;
|
|
struct counters *unhalt;
|
|
double con, un, memd, res;
|
|
|
|
con = 84.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
mem = find_counter(cpu, "MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM");
|
|
if (pos != -1) {
|
|
memd = mem->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
memd = mem->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (memd * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
|
|
static int
|
|
blockstoreforward(struct counters *cpu, int pos)
|
|
{
|
|
/* 7 - (LD_BLOCKS_STORE_FORWARD * 13) / CPU_CLK_UNHALTED.THREAD_P (thresh >= .05)*/
|
|
int ret;
|
|
struct counters *ldb;
|
|
struct counters *unhalt;
|
|
double con, un, ld, res;
|
|
|
|
con = 13.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
ldb = find_counter(cpu, "LD_BLOCKS_STORE_FORWARD");
|
|
if (pos != -1) {
|
|
ld = ldb->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
ld = ldb->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (ld * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
cache2(struct counters *cpu, int pos)
|
|
{
|
|
/* ** Suspect ***
|
|
* 8 - ((MEM_LOAD_RETIRED.L3_HIT * 26) + (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT * 43) +
|
|
* (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM * 60)) / CPU_CLK_UNHALTED.THREAD_P (thresh >.2)
|
|
*/
|
|
int ret;
|
|
struct counters *mem1, *mem2, *mem3;
|
|
struct counters *unhalt;
|
|
double con1, con2, con3, un, me_1, me_2, me_3, res;
|
|
|
|
con1 = 26.0;
|
|
con2 = 43.0;
|
|
con3 = 60.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
/* Call for MEM_LOAD_RETIRED.L3_HIT possibly MEM_LOAD_UOPS_RETIRED.LLC_HIT ?*/
|
|
mem1 = find_counter(cpu, "MEM_LOAD_UOPS_RETIRED.LLC_HIT");
|
|
mem2 = find_counter(cpu, "MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT");
|
|
mem3 = find_counter(cpu, "MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM");
|
|
if (pos != -1) {
|
|
me_1 = mem1->vals[pos] * 1.0;
|
|
me_2 = mem2->vals[pos] * 1.0;
|
|
me_3 = mem3->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
me_1 = mem1->sum * 1.0;
|
|
me_2 = mem2->sum * 1.0;
|
|
me_3 = mem3->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = ((me_1 * con1) + (me_2 * con2) + (me_3 * con3))/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
datasharing(struct counters *cpu, int pos)
|
|
{
|
|
/*
|
|
* (MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT * 43)/ CPU_CLK_UNHALTED.THREAD_P (thresh >.2)
|
|
*/
|
|
int ret;
|
|
struct counters *mem;
|
|
struct counters *unhalt;
|
|
double con, res, me, un;
|
|
|
|
con = 43.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
mem = find_counter(cpu, "MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT");
|
|
if (pos != -1) {
|
|
me = mem->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
me = mem->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (me * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
static int
|
|
datasharing_has(struct counters *cpu, int pos)
|
|
{
|
|
/*
|
|
* (MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT * 43)/ CPU_CLK_UNHALTED.THREAD_P (thresh >.2)
|
|
*/
|
|
int ret;
|
|
struct counters *mem;
|
|
struct counters *unhalt;
|
|
double con, res, me, un;
|
|
|
|
con = 72.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
mem = find_counter(cpu, "MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT");
|
|
if (pos != -1) {
|
|
me = mem->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
me = mem->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (me * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
static int
|
|
cache2ib(struct counters *cpu, int pos)
|
|
{
|
|
/*
|
|
* (29 * MEM_LOAD_UOPS_RETIRED.LLC_HIT / CPU_CLK_UNHALTED.THREAD_P (thresh >.2)
|
|
*/
|
|
int ret;
|
|
struct counters *mem;
|
|
struct counters *unhalt;
|
|
double con, un, me, res;
|
|
|
|
con = 29.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
mem = find_counter(cpu, "MEM_LOAD_UOPS_RETIRED.LLC_HIT");
|
|
if (pos != -1) {
|
|
me = mem->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
me = mem->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (con * me)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
cache2has(struct counters *cpu, int pos)
|
|
{
|
|
/*
|
|
* Examine ((MEM_LOAD_UOPS_RETIRED.LLC_HIT * 36) + \
|
|
* (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT * 72) +
|
|
* (MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM * 84))
|
|
* / CPU_CLK_UNHALTED.THREAD_P
|
|
*/
|
|
int ret;
|
|
struct counters *mem1, *mem2, *mem3;
|
|
struct counters *unhalt;
|
|
double con1, con2, con3, un, me1, me2, me3, res;
|
|
|
|
con1 = 36.0;
|
|
con2 = 72.0;
|
|
con3 = 84.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
mem1 = find_counter(cpu, "MEM_LOAD_UOPS_RETIRED.LLC_HIT");
|
|
mem2 = find_counter(cpu, "MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT");
|
|
mem3 = find_counter(cpu, "MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM");
|
|
if (pos != -1) {
|
|
me1 = mem1->vals[pos] * 1.0;
|
|
me2 = mem2->vals[pos] * 1.0;
|
|
me3 = mem3->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
me1 = mem1->sum * 1.0;
|
|
me2 = mem2->sum * 1.0;
|
|
me3 = mem3->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = ((me1 * con1) + (me2 * con2) + (me3 * con3))/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
cache1(struct counters *cpu, int pos)
|
|
{
|
|
/* 9 - (MEM_LOAD_UOPS_MISC_RETIRED.LLC_MISS * 180) / CPU_CLK_UNHALTED.THREAD_P (thresh >= .2) */
|
|
int ret;
|
|
struct counters *mem;
|
|
struct counters *unhalt;
|
|
double con, un, me, res;
|
|
|
|
con = 180.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
mem = find_counter(cpu, "MEM_LOAD_UOPS_MISC_RETIRED.LLC_MISS");
|
|
if (pos != -1) {
|
|
me = mem->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
me = mem->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (me * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
cache1ib(struct counters *cpu, int pos)
|
|
{
|
|
/* 9 - (MEM_LOAD_UOPS_L3_MISS_RETIRED.LCOAL_DRAM * 180) / CPU_CLK_UNHALTED.THREAD_P (thresh >= .2) */
|
|
int ret;
|
|
struct counters *mem;
|
|
struct counters *unhalt;
|
|
double con, un, me, res;
|
|
|
|
con = 180.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
mem = find_counter(cpu, "MEM_LOAD_UOPS_LLC_MISS_RETIRED.LOCAL_DRAM");
|
|
if (pos != -1) {
|
|
me = mem->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
me = mem->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (me * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
|
|
static int
|
|
dtlb_missload(struct counters *cpu, int pos)
|
|
{
|
|
/* 10 - ((DTLB_LOAD_MISSES.STLB_HIT * 7) + DTLB_LOAD_MISSES.WALK_DURATION) / CPU_CLK_UNHALTED.THREAD_P (t >=.1) */
|
|
int ret;
|
|
struct counters *dtlb_m, *dtlb_d;
|
|
struct counters *unhalt;
|
|
double con, un, d1, d2, res;
|
|
|
|
con = 7.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
dtlb_m = find_counter(cpu, "DTLB_LOAD_MISSES.STLB_HIT");
|
|
dtlb_d = find_counter(cpu, "DTLB_LOAD_MISSES.WALK_DURATION");
|
|
if (pos != -1) {
|
|
d1 = dtlb_m->vals[pos] * 1.0;
|
|
d2 = dtlb_d->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
d1 = dtlb_m->sum * 1.0;
|
|
d2 = dtlb_d->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = ((d1 * con) + d2)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
dtlb_missstore(struct counters *cpu, int pos)
|
|
{
|
|
/*
|
|
* ((DTLB_STORE_MISSES.STLB_HIT * 7) + DTLB_STORE_MISSES.WALK_DURATION) /
|
|
* CPU_CLK_UNHALTED.THREAD_P (t >= .1)
|
|
*/
|
|
int ret;
|
|
struct counters *dtsb_m, *dtsb_d;
|
|
struct counters *unhalt;
|
|
double con, un, d1, d2, res;
|
|
|
|
con = 7.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
dtsb_m = find_counter(cpu, "DTLB_STORE_MISSES.STLB_HIT");
|
|
dtsb_d = find_counter(cpu, "DTLB_STORE_MISSES.WALK_DURATION");
|
|
if (pos != -1) {
|
|
d1 = dtsb_m->vals[pos] * 1.0;
|
|
d2 = dtsb_d->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
d1 = dtsb_m->sum * 1.0;
|
|
d2 = dtsb_d->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = ((d1 * con) + d2)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
itlb_miss(struct counters *cpu, int pos)
|
|
{
|
|
/* ITLB_MISSES.WALK_DURATION / CPU_CLK_UNTHREAD_P IB */
|
|
int ret;
|
|
struct counters *itlb;
|
|
struct counters *unhalt;
|
|
double un, d1, res;
|
|
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
itlb = find_counter(cpu, "ITLB_MISSES.WALK_DURATION");
|
|
if (pos != -1) {
|
|
d1 = itlb->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
d1 = itlb->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = d1/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
icache_miss(struct counters *cpu, int pos)
|
|
{
|
|
/* (ICACHE.IFETCH_STALL - ITLB_MISSES.WALK_DURATION) / CPU_CLK_UNHALTED.THREAD_P IB */
|
|
|
|
int ret;
|
|
struct counters *itlb, *icache;
|
|
struct counters *unhalt;
|
|
double un, d1, ic, res;
|
|
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
itlb = find_counter(cpu, "ITLB_MISSES.WALK_DURATION");
|
|
icache = find_counter(cpu, "ICACHE.IFETCH_STALL");
|
|
if (pos != -1) {
|
|
d1 = itlb->vals[pos] * 1.0;
|
|
ic = icache->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
d1 = itlb->sum * 1.0;
|
|
ic = icache->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (ic-d1)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
|
|
}
|
|
|
|
static int
|
|
icache_miss_has(struct counters *cpu, int pos)
|
|
{
|
|
/* (36 * ICACHE.MISSES) / CPU_CLK_UNHALTED.THREAD_P */
|
|
|
|
int ret;
|
|
struct counters *icache;
|
|
struct counters *unhalt;
|
|
double un, con, ic, res;
|
|
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
icache = find_counter(cpu, "ICACHE.MISSES");
|
|
con = 36.0;
|
|
if (pos != -1) {
|
|
ic = icache->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
ic = icache->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (con * ic)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
|
|
}
|
|
|
|
static int
|
|
lcp_stall(struct counters *cpu, int pos)
|
|
{
|
|
/* ILD_STALL.LCP/CPU_CLK_UNHALTED.THREAD_P IB */
|
|
int ret;
|
|
struct counters *ild;
|
|
struct counters *unhalt;
|
|
double un, d1, res;
|
|
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
ild = find_counter(cpu, "ILD_STALL.LCP");
|
|
if (pos != -1) {
|
|
d1 = ild->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
d1 = ild->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = d1/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
static int
|
|
frontendstall(struct counters *cpu, int pos)
|
|
{
|
|
/* 12 - IDQ_UOPS_NOT_DELIVERED.CORE / (CPU_CLK_UNHALTED.THREAD_P * 4) (thresh >= .15) */
|
|
int ret;
|
|
struct counters *idq;
|
|
struct counters *unhalt;
|
|
double con, un, id, res;
|
|
|
|
con = 4.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
idq = find_counter(cpu, "IDQ_UOPS_NOT_DELIVERED.CORE");
|
|
if (pos != -1) {
|
|
id = idq->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
id = idq->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = id/(un * con);
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
clears(struct counters *cpu, int pos)
|
|
{
|
|
/* 13 - ((MACHINE_CLEARS.MEMORY_ORDERING + MACHINE_CLEARS.SMC + MACHINE_CLEARS.MASKMOV ) * 100 )
|
|
* / CPU_CLK_UNHALTED.THREAD_P (thresh >= .02)*/
|
|
|
|
int ret;
|
|
struct counters *clr1, *clr2, *clr3;
|
|
struct counters *unhalt;
|
|
double con, un, cl1, cl2, cl3, res;
|
|
|
|
con = 100.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
clr1 = find_counter(cpu, "MACHINE_CLEARS.MEMORY_ORDERING");
|
|
clr2 = find_counter(cpu, "MACHINE_CLEARS.SMC");
|
|
clr3 = find_counter(cpu, "MACHINE_CLEARS.MASKMOV");
|
|
|
|
if (pos != -1) {
|
|
cl1 = clr1->vals[pos] * 1.0;
|
|
cl2 = clr2->vals[pos] * 1.0;
|
|
cl3 = clr3->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
cl1 = clr1->sum * 1.0;
|
|
cl2 = clr2->sum * 1.0;
|
|
cl3 = clr3->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = ((cl1 + cl2 + cl3) * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
microassist(struct counters *cpu, int pos)
|
|
{
|
|
/* 14 - IDQ.MS_CYCLES / CPU_CLK_UNHALTED.THREAD_P (thresh > .05) */
|
|
int ret;
|
|
struct counters *idq;
|
|
struct counters *unhalt;
|
|
double un, id, res, con;
|
|
|
|
con = 4.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
idq = find_counter(cpu, "IDQ.MS_UOPS");
|
|
if (pos != -1) {
|
|
id = idq->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
id = idq->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = id/(un * con);
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
|
|
static int
|
|
aliasing(struct counters *cpu, int pos)
|
|
{
|
|
/* 15 - (LD_BLOCKS_PARTIAL.ADDRESS_ALIAS * 5) / CPU_CLK_UNHALTED.THREAD_P (thresh > .1) */
|
|
int ret;
|
|
struct counters *ld;
|
|
struct counters *unhalt;
|
|
double un, lds, con, res;
|
|
|
|
con = 5.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
ld = find_counter(cpu, "LD_BLOCKS_PARTIAL.ADDRESS_ALIAS");
|
|
if (pos != -1) {
|
|
lds = ld->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
lds = ld->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (lds * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
fpassists(struct counters *cpu, int pos)
|
|
{
|
|
/* 16 - FP_ASSIST.ANY/INST_RETIRED.ANY_P */
|
|
int ret;
|
|
struct counters *fp;
|
|
struct counters *inst;
|
|
double un, fpd, res;
|
|
|
|
inst = find_counter(cpu, "INST_RETIRED.ANY_P");
|
|
fp = find_counter(cpu, "FP_ASSIST.ANY");
|
|
if (pos != -1) {
|
|
fpd = fp->vals[pos] * 1.0;
|
|
un = inst->vals[pos] * 1.0;
|
|
} else {
|
|
fpd = fp->sum * 1.0;
|
|
un = inst->sum * 1.0;
|
|
}
|
|
res = fpd/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
otherassistavx(struct counters *cpu, int pos)
|
|
{
|
|
/* 17 - (OTHER_ASSISTS.AVX_TO_SSE * 75)/CPU_CLK_UNHALTED.THREAD_P thresh .1*/
|
|
int ret;
|
|
struct counters *oth;
|
|
struct counters *unhalt;
|
|
double un, ot, con, res;
|
|
|
|
con = 75.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
oth = find_counter(cpu, "OTHER_ASSISTS.AVX_TO_SSE");
|
|
if (pos != -1) {
|
|
ot = oth->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
ot = oth->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (ot * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
otherassistsse(struct counters *cpu, int pos)
|
|
{
|
|
|
|
int ret;
|
|
struct counters *oth;
|
|
struct counters *unhalt;
|
|
double un, ot, con, res;
|
|
|
|
/* 18 (OTHER_ASSISTS.SSE_TO_AVX * 75)/CPU_CLK_UNHALTED.THREAD_P thresh .1*/
|
|
con = 75.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
oth = find_counter(cpu, "OTHER_ASSISTS.SSE_TO_AVX");
|
|
if (pos != -1) {
|
|
ot = oth->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
ot = oth->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = (ot * con)/un;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
efficiency1(struct counters *cpu, int pos)
|
|
{
|
|
|
|
int ret;
|
|
struct counters *uops;
|
|
struct counters *unhalt;
|
|
double un, ot, con, res;
|
|
|
|
/* 19 (UOPS_RETIRED.RETIRE_SLOTS/(4*CPU_CLK_UNHALTED.THREAD_P) look if thresh < .9*/
|
|
con = 4.0;
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
uops = find_counter(cpu, "UOPS_RETIRED.RETIRE_SLOTS");
|
|
if (pos != -1) {
|
|
ot = uops->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
ot = uops->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = ot/(con * un);
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
efficiency2(struct counters *cpu, int pos)
|
|
{
|
|
|
|
int ret;
|
|
struct counters *uops;
|
|
struct counters *unhalt;
|
|
double un, ot, res;
|
|
|
|
/* 20 - CPU_CLK_UNHALTED.THREAD_P/INST_RETIRED.ANY_P good if > 1. (comp factor)*/
|
|
unhalt = find_counter(cpu, "CPU_CLK_UNHALTED.THREAD_P");
|
|
uops = find_counter(cpu, "INST_RETIRED.ANY_P");
|
|
if (pos != -1) {
|
|
ot = uops->vals[pos] * 1.0;
|
|
un = unhalt->vals[pos] * 1.0;
|
|
} else {
|
|
ot = uops->sum * 1.0;
|
|
un = unhalt->sum * 1.0;
|
|
}
|
|
res = un/ot;
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
#define SANDY_BRIDGE_COUNT 20
|
|
static struct cpu_entry sandy_bridge[SANDY_BRIDGE_COUNT] = {
|
|
/*01*/ { "allocstall1", "thresh > .05",
|
|
"pmcstat -s CPU_CLK_UNHALTED.THREAD_P -s PARTIAL_RAT_STALLS.SLOW_LEA_WINDOW -w 1",
|
|
allocstall1 },
|
|
/*02*/ { "allocstall2", "thresh > .05",
|
|
"pmcstat -s CPU_CLK_UNHALTED.THREAD_P -s PARTIAL_RAT_STALLS.FLAGS_MERGE_UOP_CYCLES -w 1",
|
|
allocstall2 },
|
|
/*03*/ { "br_miss", "thresh >= .2",
|
|
"pmcstat -s CPU_CLK_UNHALTED.THREAD_P -s BR_MISP_RETIRED.ALL_BRANCHES -w 1",
|
|
br_mispredict },
|
|
/*04*/ { "splitload", "thresh >= .1",
|
|
"pmcstat -s CPU_CLK_UNHALTED.THREAD_P -s MEM_UOP_RETIRED.SPLIT_LOADS -w 1",
|
|
splitload },
|
|
/*05*/ { "splitstore", "thresh >= .01",
|
|
"pmcstat -s MEM_UOP_RETIRED.SPLIT_STORES -s MEM_UOP_RETIRED.ALL_STORES -w 1",
|
|
splitstore },
|
|
/*06*/ { "contested", "thresh >= .05",
|
|
"pmcstat -s MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
contested },
|
|
/*07*/ { "blockstorefwd", "thresh >= .05",
|
|
"pmcstat -s LD_BLOCKS_STORE_FORWARD -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
blockstoreforward },
|
|
/*08*/ { "cache2", "thresh >= .2",
|
|
"pmcstat -s MEM_LOAD_UOPS_RETIRED.LLC_HIT -s MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT -s MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
cache2 },
|
|
/*09*/ { "cache1", "thresh >= .2",
|
|
"pmcstat -s MEM_LOAD_UOPS_MISC_RETIRED.LLC_MISS -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
cache1 },
|
|
/*10*/ { "dtlbmissload", "thresh >= .1",
|
|
"pmcstat -s DTLB_LOAD_MISSES.STLB_HIT -s DTLB_LOAD_MISSES.WALK_DURATION -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
dtlb_missload },
|
|
/*11*/ { "dtlbmissstore", "thresh >= .05",
|
|
"pmcstat -s DTLB_STORE_MISSES.STLB_HIT -s DTLB_STORE_MISSES.WALK_DURATION -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
dtlb_missstore },
|
|
/*12*/ { "frontendstall", "thresh >= .15",
|
|
"pmcstat -s IDQ_UOPS_NOT_DELIVERED.CORE -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
frontendstall },
|
|
/*13*/ { "clears", "thresh >= .02",
|
|
"pmcstat -s MACHINE_CLEARS.MEMORY_ORDERING -s MACHINE_CLEARS.SMC -s MACHINE_CLEARS.MASKMOV -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
clears },
|
|
/*14*/ { "microassist", "thresh >= .05",
|
|
"pmcstat -s IDQ.MS_UOPS,cmask=1 -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
microassist },
|
|
/*15*/ { "aliasing_4k", "thresh >= .1",
|
|
"pmcstat -s LD_BLOCKS_PARTIAL.ADDRESS_ALIAS -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
aliasing },
|
|
/*16*/ { "fpassist", "look for a excessive value",
|
|
"pmcstat -s FP_ASSIST.ANY -s INST_RETIRED.ANY_P -w 1",
|
|
fpassists },
|
|
/*17*/ { "otherassistavx", "look for a excessive value",
|
|
"pmcstat -s OTHER_ASSISTS.AVX_TO_SSE -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
otherassistavx },
|
|
/*18*/ { "otherassistsse", "look for a excessive value",
|
|
"pmcstat -s OTHER_ASSISTS.SSE_TO_AVX -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
otherassistsse },
|
|
/*19*/ { "eff1", "thresh < .9",
|
|
"pmcstat -s UOPS_RETIRED.RETIRE_SLOTS -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
efficiency1 },
|
|
/*20*/ { "eff2", "thresh > 1.0",
|
|
"pmcstat -s INST_RETIRED.ANY_P -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
efficiency2 },
|
|
};
|
|
|
|
|
|
#define IVY_BRIDGE_COUNT 21
|
|
static struct cpu_entry ivy_bridge[IVY_BRIDGE_COUNT] = {
|
|
/*1*/ { "eff1", "thresh < .75",
|
|
"pmcstat -s UOPS_RETIRED.RETIRE_SLOTS -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
efficiency1 },
|
|
/*2*/ { "eff2", "thresh > 1.0",
|
|
"pmcstat -s INST_RETIRED.ANY_P -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
efficiency2 },
|
|
/*3*/ { "itlbmiss", "thresh > .05",
|
|
"pmcstat -s ITLB_MISSES.WALK_DURATION -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
itlb_miss },
|
|
/*4*/ { "icachemiss", "thresh > .05",
|
|
"pmcstat -s ICACHE.IFETCH_STALL -s ITLB_MISSES.WALK_DURATION -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
icache_miss },
|
|
/*5*/ { "lcpstall", "thresh > .05",
|
|
"pmcstat -s ILD_STALL.LCP -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
lcp_stall },
|
|
/*6*/ { "cache1", "thresh >= .2",
|
|
"pmcstat -s MEM_LOAD_UOPS_LLC_MISS_RETIRED.LOCAL_DRAM -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
cache1ib },
|
|
/*7*/ { "cache2", "thresh >= .2",
|
|
"pmcstat -s MEM_LOAD_UOPS_RETIRED.LLC_HIT -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
cache2ib },
|
|
/*8*/ { "contested", "thresh >= .05",
|
|
"pmcstat -s MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
contested },
|
|
/*9*/ { "datashare", "thresh >= .05",
|
|
"pmcstat -s MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
datasharing },
|
|
/*10*/ { "blockstorefwd", "thresh >= .05",
|
|
"pmcstat -s LD_BLOCKS_STORE_FORWARD -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
blockstoreforward },
|
|
/*11*/ { "splitload", "thresh >= .1",
|
|
"pmcstat -s CPU_CLK_UNHALTED.THREAD_P -s L1D_PEND_MISS.PENDING -s MEM_LOAD_UOPS_RETIRED.L1_MISS -s LD_BLOCKS.NO_SR -w 1",
|
|
splitloadib },
|
|
/*12*/ { "splitstore", "thresh >= .01",
|
|
"pmcstat -s MEM_UOP_RETIRED.SPLIT_STORES -s MEM_UOP_RETIRED.ALL_STORES -w 1",
|
|
splitstore },
|
|
/*13*/ { "aliasing_4k", "thresh >= .1",
|
|
"pmcstat -s LD_BLOCKS_PARTIAL.ADDRESS_ALIAS -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
aliasing },
|
|
/*14*/ { "dtlbmissload", "thresh >= .1",
|
|
"pmcstat -s DTLB_LOAD_MISSES.STLB_HIT -s DTLB_LOAD_MISSES.WALK_DURATION -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
dtlb_missload },
|
|
/*15*/ { "dtlbmissstore", "thresh >= .05",
|
|
"pmcstat -s DTLB_STORE_MISSES.STLB_HIT -s DTLB_STORE_MISSES.WALK_DURATION -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
dtlb_missstore },
|
|
/*16*/ { "br_miss", "thresh >= .2",
|
|
"pmcstat -s CPU_CLK_UNHALTED.THREAD_P -s BR_MISP_RETIRED.ALL_BRANCHES -s MACHINE_CLEARS.MEMORY_ORDERING -s MACHINE_CLEARS.SMC -s MACHINE_CLEARS.MASKMOV -s UOPS_ISSUED.ANY -s UOPS_RETIRED.RETIRE_SLOTS -s INT_MISC.RECOVERY_CYCLES -w 1",
|
|
br_mispredictib },
|
|
/*17*/ { "clears", "thresh >= .02",
|
|
"pmcstat -s MACHINE_CLEARS.MEMORY_ORDERING -s MACHINE_CLEARS.SMC -s MACHINE_CLEARS.MASKMOV -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
clears },
|
|
/*18*/ { "microassist", "thresh >= .05",
|
|
"pmcstat -s IDQ.MS_UOPS,cmask=1 -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
microassist },
|
|
/*19*/ { "fpassist", "look for a excessive value",
|
|
"pmcstat -s FP_ASSIST.ANY -s INST_RETIRED.ANY_P -w 1",
|
|
fpassists },
|
|
/*20*/ { "otherassistavx", "look for a excessive value",
|
|
"pmcstat -s OTHER_ASSISTS.AVX_TO_SSE -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
otherassistavx },
|
|
/*21*/ { "otherassistsse", "look for a excessive value",
|
|
"pmcstat -s OTHER_ASSISTS.SSE_TO_AVX -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
otherassistsse },
|
|
};
|
|
|
|
#define HASWELL_COUNT 20
|
|
static struct cpu_entry haswell[HASWELL_COUNT] = {
|
|
/*1*/ { "eff1", "thresh < .75",
|
|
"pmcstat -s UOPS_RETIRED.RETIRE_SLOTS -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
efficiency1 },
|
|
/*2*/ { "eff2", "thresh > 1.0",
|
|
"pmcstat -s INST_RETIRED.ANY_P -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
efficiency2 },
|
|
/*3*/ { "itlbmiss", "thresh > .05",
|
|
"pmcstat -s ITLB_MISSES.WALK_DURATION -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
itlb_miss },
|
|
/*4*/ { "icachemiss", "thresh > .05",
|
|
"pmcstat -s ICACHE.MISSES --s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
icache_miss_has },
|
|
/*5*/ { "lcpstall", "thresh > .05",
|
|
"pmcstat -s ILD_STALL.LCP -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
lcp_stall },
|
|
/*6*/ { "cache1", "thresh >= .2",
|
|
"pmcstat -s MEM_LOAD_UOPS_LLC_MISS_RETIRED.LOCAL_DRAM -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
cache1ib },
|
|
/*7*/ { "cache2", "thresh >= .2",
|
|
"pmcstat -s MEM_LOAD_UOPS_RETIRED.LLC_HIT -s MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT -s MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
cache2has },
|
|
/*8*/ { "contested", "thresh >= .05",
|
|
"pmcstat -s MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HITM -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
contested_has },
|
|
/*9*/ { "datashare", "thresh >= .05",
|
|
"pmcstat -s MEM_LOAD_UOPS_LLC_HIT_RETIRED.XSNP_HIT -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
datasharing_has },
|
|
/*10*/ { "blockstorefwd", "thresh >= .05",
|
|
"pmcstat -s LD_BLOCKS_STORE_FORWARD -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
blockstoreforward },
|
|
/*11*/ { "splitload", "thresh >= .1",
|
|
"pmcstat -s CPU_CLK_UNHALTED.THREAD_P -s MEM_UOP_RETIRED.SPLIT_LOADS -w 1",
|
|
splitload },
|
|
/*12*/ { "splitstore", "thresh >= .01",
|
|
"pmcstat -s MEM_UOP_RETIRED.SPLIT_STORES -s MEM_UOP_RETIRED.ALL_STORES -w 1",
|
|
splitstore },
|
|
/*13*/ { "aliasing_4k", "thresh >= .1",
|
|
"pmcstat -s LD_BLOCKS_PARTIAL.ADDRESS_ALIAS -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
aliasing },
|
|
/*14*/ { "dtlbmissload", "thresh >= .1",
|
|
"pmcstat -s DTLB_LOAD_MISSES.STLB_HIT -s DTLB_LOAD_MISSES.WALK_DURATION -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
dtlb_missload },
|
|
/*15*/ { "br_miss", "thresh >= .2",
|
|
"pmcstat -s CPU_CLK_UNHALTED.THREAD_P -s BR_MISP_RETIRED.ALL_BRANCHES -w 1",
|
|
br_mispredict },
|
|
/*16*/ { "clears", "thresh >= .02",
|
|
"pmcstat -s MACHINE_CLEARS.MEMORY_ORDERING -s MACHINE_CLEARS.SMC -s MACHINE_CLEARS.MASKMOV -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
clears },
|
|
/*17*/ { "microassist", "thresh >= .05",
|
|
"pmcstat -s IDQ.MS_UOPS,cmask=1 -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
microassist },
|
|
/*18*/ { "fpassist", "look for a excessive value",
|
|
"pmcstat -s FP_ASSIST.ANY -s INST_RETIRED.ANY_P -w 1",
|
|
fpassists },
|
|
/*19*/ { "otherassistavx", "look for a excessive value",
|
|
"pmcstat -s OTHER_ASSISTS.AVX_TO_SSE -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
otherassistavx },
|
|
/*20*/ { "otherassistsse", "look for a excessive value",
|
|
"pmcstat -s OTHER_ASSISTS.SSE_TO_AVX -s CPU_CLK_UNHALTED.THREAD_P -w 1",
|
|
otherassistsse },
|
|
};
|
|
|
|
|
|
static void
|
|
set_sandybridge(void)
|
|
{
|
|
strcpy(the_cpu.cputype, "SandyBridge PMC");
|
|
the_cpu.number = SANDY_BRIDGE_COUNT;
|
|
the_cpu.ents = sandy_bridge;
|
|
the_cpu.explain = explain_name_sb;
|
|
}
|
|
|
|
static void
|
|
set_ivybridge(void)
|
|
{
|
|
strcpy(the_cpu.cputype, "IvyBridge PMC");
|
|
the_cpu.number = IVY_BRIDGE_COUNT;
|
|
the_cpu.ents = ivy_bridge;
|
|
the_cpu.explain = explain_name_ib;
|
|
}
|
|
|
|
|
|
static void
|
|
set_haswell(void)
|
|
{
|
|
strcpy(the_cpu.cputype, "HASWELL PMC");
|
|
the_cpu.number = HASWELL_COUNT;
|
|
the_cpu.ents = haswell;
|
|
the_cpu.explain = explain_name_has;
|
|
}
|
|
|
|
static void
|
|
set_expression(char *name)
|
|
{
|
|
int found = 0, i;
|
|
for(i=0 ; i< the_cpu.number; i++) {
|
|
if (strcmp(name, the_cpu.ents[i].name) == 0) {
|
|
found = 1;
|
|
expression = the_cpu.ents[i].func;
|
|
command = the_cpu.ents[i].command;
|
|
threshold = the_cpu.ents[i].thresh;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
printf("For CPU type %s we have no expression:%s\n",
|
|
the_cpu.cputype, name);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
validate_expression(char *name)
|
|
{
|
|
int i, found;
|
|
|
|
found = 0;
|
|
for(i=0 ; i< the_cpu.number; i++) {
|
|
if (strcmp(name, the_cpu.ents[i].name) == 0) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
return(-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
do_expression(struct counters *cpu, int pos)
|
|
{
|
|
if (expression == NULL)
|
|
return;
|
|
(*expression)(cpu, pos);
|
|
}
|
|
|
|
static void
|
|
process_header(int idx, char *p)
|
|
{
|
|
struct counters *up;
|
|
int i, len, nlen;
|
|
/*
|
|
* Given header element idx, at p in
|
|
* form 's/NN/nameof'
|
|
* process the entry to pull out the name and
|
|
* the CPU number.
|
|
*/
|
|
if (strncmp(p, "s/", 2)) {
|
|
printf("Check -- invalid header no s/ in %s\n",
|
|
p);
|
|
return;
|
|
}
|
|
up = &cnts[idx];
|
|
up->cpu = strtol(&p[2], NULL, 10);
|
|
len = strlen(p);
|
|
for (i=2; i<len; i++) {
|
|
if (p[i] == '/') {
|
|
nlen = strlen(&p[(i+1)]);
|
|
if (nlen < (MAX_NLEN-1)) {
|
|
strcpy(up->counter_name, &p[(i+1)]);
|
|
} else {
|
|
strncpy(up->counter_name, &p[(i+1)], (MAX_NLEN-1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
build_counters_from_header(FILE *io)
|
|
{
|
|
char buffer[8192], *p;
|
|
int i, len, cnt;
|
|
size_t mlen;
|
|
|
|
/* We have a new start, lets
|
|
* setup our headers and cpus.
|
|
*/
|
|
if (fgets(buffer, sizeof(buffer), io) == NULL) {
|
|
printf("First line can't be read from file err:%d\n", errno);
|
|
return;
|
|
}
|
|
/*
|
|
* Ok output is an array of counters. Once
|
|
* we start to read the values in we must
|
|
* put them in there slot to match there CPU and
|
|
* counter being updated. We create a mass array
|
|
* of the counters, filling in the CPU and
|
|
* counter name.
|
|
*/
|
|
/* How many do we get? */
|
|
len = strlen(buffer);
|
|
for (i=0, cnt=0; i<len; i++) {
|
|
if (strncmp(&buffer[i], "s/", 2) == 0) {
|
|
cnt++;
|
|
for(;i<len;i++) {
|
|
if (buffer[i] == ' ')
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
mlen = sizeof(struct counters) * cnt;
|
|
cnts = malloc(mlen);
|
|
ncnts = cnt;
|
|
if (cnts == NULL) {
|
|
printf("No memory err:%d\n", errno);
|
|
return;
|
|
}
|
|
memset(cnts, 0, mlen);
|
|
for (i=0, cnt=0; i<len; i++) {
|
|
if (strncmp(&buffer[i], "s/", 2) == 0) {
|
|
p = &buffer[i];
|
|
for(;i<len;i++) {
|
|
if (buffer[i] == ' ') {
|
|
buffer[i] = 0;
|
|
break;
|
|
}
|
|
}
|
|
process_header(cnt, p);
|
|
cnt++;
|
|
}
|
|
}
|
|
if (verbose)
|
|
printf("We have %d entries\n", cnt);
|
|
}
|
|
extern int max_to_collect;
|
|
int max_to_collect = MAX_COUNTER_SLOTS;
|
|
|
|
static int
|
|
read_a_line(FILE *io)
|
|
{
|
|
char buffer[8192], *p, *stop;
|
|
int pos, i;
|
|
|
|
if (fgets(buffer, sizeof(buffer), io) == NULL) {
|
|
return(0);
|
|
}
|
|
p = buffer;
|
|
for (i=0; i<ncnts; i++) {
|
|
pos = cnts[i].pos;
|
|
cnts[i].vals[pos] = strtol(p, &stop, 0);
|
|
cnts[i].pos++;
|
|
cnts[i].sum += cnts[i].vals[pos];
|
|
p = stop;
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
extern int cpu_count_out;
|
|
int cpu_count_out=0;
|
|
|
|
static void
|
|
print_header(void)
|
|
{
|
|
int i, cnt, printed_cnt;
|
|
|
|
printf("*********************************\n");
|
|
for(i=0, cnt=0; i<MAX_CPU; i++) {
|
|
if (glob_cpu[i]) {
|
|
cnt++;
|
|
}
|
|
}
|
|
cpu_count_out = cnt;
|
|
for(i=0, printed_cnt=0; i<MAX_CPU; i++) {
|
|
if (glob_cpu[i]) {
|
|
printf("CPU%d", i);
|
|
printed_cnt++;
|
|
}
|
|
if (printed_cnt == cnt) {
|
|
printf("\n");
|
|
break;
|
|
} else {
|
|
printf("\t");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
lace_cpus_together(void)
|
|
{
|
|
int i, j, lace_cpu;
|
|
struct counters *cpat, *at;
|
|
|
|
for(i=0; i<ncnts; i++) {
|
|
cpat = &cnts[i];
|
|
if (cpat->next_cpu) {
|
|
/* Already laced in */
|
|
continue;
|
|
}
|
|
lace_cpu = cpat->cpu;
|
|
if (lace_cpu >= MAX_CPU) {
|
|
printf("CPU %d to big\n", lace_cpu);
|
|
continue;
|
|
}
|
|
if (glob_cpu[lace_cpu] == NULL) {
|
|
glob_cpu[lace_cpu] = cpat;
|
|
} else {
|
|
/* Already processed this cpu */
|
|
continue;
|
|
}
|
|
/* Ok look forward for cpu->cpu and link in */
|
|
for(j=(i+1); j<ncnts; j++) {
|
|
at = &cnts[j];
|
|
if (at->next_cpu) {
|
|
continue;
|
|
}
|
|
if (at->cpu == lace_cpu) {
|
|
/* Found one */
|
|
cpat->next_cpu = at;
|
|
cpat = at;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
process_file(char *filename)
|
|
{
|
|
FILE *io;
|
|
int i;
|
|
int line_at, not_done;
|
|
pid_t pid_of_command=0;
|
|
|
|
if (filename == NULL) {
|
|
io = my_popen(command, "r", &pid_of_command);
|
|
if (io == NULL) {
|
|
printf("Can't popen the command %s\n", command);
|
|
return;
|
|
}
|
|
} else {
|
|
io = fopen(filename, "r");
|
|
if (io == NULL) {
|
|
printf("Can't process file %s err:%d\n",
|
|
filename, errno);
|
|
return;
|
|
}
|
|
}
|
|
build_counters_from_header(io);
|
|
if (cnts == NULL) {
|
|
/* Nothing we can do */
|
|
printf("Nothing to do -- no counters built\n");
|
|
if (filename) {
|
|
fclose(io);
|
|
} else {
|
|
my_pclose(io, pid_of_command);
|
|
}
|
|
return;
|
|
}
|
|
lace_cpus_together();
|
|
print_header();
|
|
if (verbose) {
|
|
for (i=0; i<ncnts; i++) {
|
|
printf("Counter:%s cpu:%d index:%d\n",
|
|
cnts[i].counter_name,
|
|
cnts[i].cpu, i);
|
|
}
|
|
}
|
|
line_at = 0;
|
|
not_done = 1;
|
|
while(not_done) {
|
|
if (read_a_line(io)) {
|
|
line_at++;
|
|
} else {
|
|
break;
|
|
}
|
|
if (line_at >= max_to_collect) {
|
|
not_done = 0;
|
|
}
|
|
if (filename == NULL) {
|
|
int cnt;
|
|
/* For the ones we dynamically open we print now */
|
|
for(i=0, cnt=0; i<MAX_CPU; i++) {
|
|
do_expression(glob_cpu[i], (line_at-1));
|
|
cnt++;
|
|
if (cnt == cpu_count_out) {
|
|
printf("\n");
|
|
break;
|
|
} else {
|
|
printf("\t");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (filename) {
|
|
fclose(io);
|
|
} else {
|
|
my_pclose(io, pid_of_command);
|
|
}
|
|
}
|
|
#if defined(__amd64__)
|
|
#define cpuid(in,a,b,c,d)\
|
|
asm("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d) : "a" (in));
|
|
#else
|
|
#define cpuid(in, a, b, c, d)
|
|
#endif
|
|
|
|
static void
|
|
get_cpuid_set(void)
|
|
{
|
|
unsigned long eax, ebx, ecx, edx;
|
|
int model;
|
|
pid_t pid_of_command=0;
|
|
size_t sz, len;
|
|
FILE *io;
|
|
char linebuf[1024], *str;
|
|
|
|
eax = ebx = ecx = edx = 0;
|
|
|
|
cpuid(0, eax, ebx, ecx, edx);
|
|
if (ebx == 0x68747541) {
|
|
printf("AMD processors are not supported by this program\n");
|
|
printf("Sorry\n");
|
|
exit(0);
|
|
} else if (ebx == 0x6972794) {
|
|
printf("Cyrix processors are not supported by this program\n");
|
|
printf("Sorry\n");
|
|
exit(0);
|
|
} else if (ebx == 0x756e6547) {
|
|
printf("Genuine Intel\n");
|
|
} else {
|
|
printf("Unknown processor type 0x%lx Only Intel AMD64 types are supported by this routine!\n", ebx);
|
|
exit(0);
|
|
}
|
|
cpuid(1, eax, ebx, ecx, edx);
|
|
model = (((eax & 0xF0000) >> 12) | ((eax & 0xF0) >> 4));
|
|
printf("CPU model is 0x%x id:0x%lx\n", model, eax);
|
|
switch (eax & 0xF00) {
|
|
case 0x500: /* Pentium family processors */
|
|
printf("Intel Pentium P5\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x600: /* Pentium Pro, Celeron, Pentium II & III */
|
|
switch (model) {
|
|
case 0x1:
|
|
printf("Intel Pentium P6\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x3:
|
|
case 0x5:
|
|
printf("Intel PII\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x6: case 0x16:
|
|
printf("Intel CL\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x7: case 0x8: case 0xA: case 0xB:
|
|
printf("Intel PIII\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x9: case 0xD:
|
|
printf("Intel PM\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0xE:
|
|
printf("Intel CORE\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0xF:
|
|
printf("Intel CORE2\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x17:
|
|
printf("Intel CORE2EXTREME\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x1C: /* Per Intel document 320047-002. */
|
|
printf("Intel ATOM\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x1A:
|
|
case 0x1E: /*
|
|
* Per Intel document 253669-032 9/2009,
|
|
* pages A-2 and A-57
|
|
*/
|
|
case 0x1F: /*
|
|
* Per Intel document 253669-032 9/2009,
|
|
* pages A-2 and A-57
|
|
*/
|
|
printf("Intel COREI7\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x2E:
|
|
printf("Intel NEHALEM\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x25: /* Per Intel document 253669-033US 12/2009. */
|
|
case 0x2C: /* Per Intel document 253669-033US 12/2009. */
|
|
printf("Intel WESTMERE\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x2F: /* Westmere-EX, seen in wild */
|
|
printf("Intel WESTMERE\n");
|
|
goto not_supported;
|
|
break;
|
|
case 0x2A: /* Per Intel document 253669-039US 05/2011. */
|
|
printf("Intel SANDYBRIDGE\n");
|
|
set_sandybridge();
|
|
break;
|
|
case 0x2D: /* Per Intel document 253669-044US 08/2012. */
|
|
printf("Intel SANDYBRIDGE_XEON\n");
|
|
set_sandybridge();
|
|
break;
|
|
case 0x3A: /* Per Intel document 253669-043US 05/2012. */
|
|
printf("Intel IVYBRIDGE\n");
|
|
set_ivybridge();
|
|
break;
|
|
case 0x3E: /* Per Intel document 325462-045US 01/2013. */
|
|
printf("Intel IVYBRIDGE_XEON\n");
|
|
set_ivybridge();
|
|
break;
|
|
case 0x3F: /* Per Intel document 325462-045US 09/2014. */
|
|
printf("Intel HASWELL (Xeon)\n");
|
|
set_haswell();
|
|
break;
|
|
case 0x3C: /* Per Intel document 325462-045US 01/2013. */
|
|
case 0x45:
|
|
case 0x46:
|
|
printf("Intel HASWELL\n");
|
|
set_haswell();
|
|
break;
|
|
case 0x4D:
|
|
/* Per Intel document 330061-001 01/2014. */
|
|
printf("Intel ATOM_SILVERMONT\n");
|
|
goto not_supported;
|
|
break;
|
|
default:
|
|
printf("Intel model 0x%x is not known -- sorry\n",
|
|
model);
|
|
goto not_supported;
|
|
break;
|
|
}
|
|
break;
|
|
case 0xF00: /* P4 */
|
|
printf("Intel unknown model %d\n", model);
|
|
goto not_supported;
|
|
break;
|
|
}
|
|
/* Ok lets load the list of all known PMC's */
|
|
io = my_popen("/usr/sbin/pmccontrol -L", "r", &pid_of_command);
|
|
if (valid_pmcs == NULL) {
|
|
/* Likely */
|
|
pmc_allocated_cnt = PMC_INITIAL_ALLOC;
|
|
sz = sizeof(char *) * pmc_allocated_cnt;
|
|
valid_pmcs = malloc(sz);
|
|
if (valid_pmcs == NULL) {
|
|
printf("No memory allocation fails at startup?\n");
|
|
exit(-1);
|
|
}
|
|
memset(valid_pmcs, 0, sz);
|
|
}
|
|
|
|
while (fgets(linebuf, sizeof(linebuf), io) != NULL) {
|
|
if (linebuf[0] != '\t') {
|
|
/* sometimes headers ;-) */
|
|
continue;
|
|
}
|
|
len = strlen(linebuf);
|
|
if (linebuf[(len-1)] == '\n') {
|
|
/* Likely */
|
|
linebuf[(len-1)] = 0;
|
|
}
|
|
str = &linebuf[1];
|
|
len = strlen(str) + 1;
|
|
valid_pmcs[valid_pmc_cnt] = malloc(len);
|
|
if (valid_pmcs[valid_pmc_cnt] == NULL) {
|
|
printf("No memory2 allocation fails at startup?\n");
|
|
exit(-1);
|
|
}
|
|
memset(valid_pmcs[valid_pmc_cnt], 0, len);
|
|
strcpy(valid_pmcs[valid_pmc_cnt], str);
|
|
valid_pmc_cnt++;
|
|
if (valid_pmc_cnt >= pmc_allocated_cnt) {
|
|
/* Got to expand -- unlikely */
|
|
char **more;
|
|
|
|
sz = sizeof(char *) * (pmc_allocated_cnt * 2);
|
|
more = malloc(sz);
|
|
if (more == NULL) {
|
|
printf("No memory3 allocation fails at startup?\n");
|
|
exit(-1);
|
|
}
|
|
memset(more, 0, sz);
|
|
memcpy(more, valid_pmcs, sz);
|
|
pmc_allocated_cnt *= 2;
|
|
free(valid_pmcs);
|
|
valid_pmcs = more;
|
|
}
|
|
}
|
|
my_pclose(io, pid_of_command);
|
|
return;
|
|
not_supported:
|
|
printf("Not supported\n");
|
|
exit(-1);
|
|
}
|
|
|
|
static void
|
|
explain_all(void)
|
|
{
|
|
int i;
|
|
printf("For CPU's of type %s the following expressions are available:\n",the_cpu.cputype);
|
|
printf("-------------------------------------------------------------\n");
|
|
for(i=0; i<the_cpu.number; i++){
|
|
printf("For -e %s ", the_cpu.ents[i].name);
|
|
(*the_cpu.explain)(the_cpu.ents[i].name);
|
|
printf("----------------------------\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_for_a_pmc(const char *pmc, int out_so_far)
|
|
{
|
|
FILE *io;
|
|
pid_t pid_of_command=0;
|
|
char my_command[1024];
|
|
char line[1024];
|
|
char resp[1024];
|
|
int len, llen, i;
|
|
|
|
if (out_so_far < 50) {
|
|
len = 50 - out_so_far;
|
|
for(i=0; i<len; i++) {
|
|
printf(" ");
|
|
}
|
|
}
|
|
sprintf(my_command, "/usr/sbin/pmcstat -w .25 -c 0 -s %s", pmc);
|
|
io = my_popen(my_command, "r", &pid_of_command);
|
|
if (io == NULL) {
|
|
printf("Failed -- popen fails\n");
|
|
return;
|
|
}
|
|
/* Setup what we expect */
|
|
len = sprintf(resp, "%s", pmc);
|
|
if (fgets(line, sizeof(line), io) == NULL) {
|
|
printf("Failed -- no output from pmstat\n");
|
|
goto out;
|
|
}
|
|
llen = strlen(line);
|
|
if (line[(llen-1)] == '\n') {
|
|
line[(llen-1)] = 0;
|
|
llen--;
|
|
}
|
|
for(i=2; i<(llen-len); i++) {
|
|
if (strncmp(&line[i], "ERROR", 5) == 0) {
|
|
printf("Failed %s\n", line);
|
|
goto out;
|
|
} else if (strncmp(&line[i], resp, len) == 0) {
|
|
int j, k;
|
|
|
|
if (fgets(line, sizeof(line), io) == NULL) {
|
|
printf("Failed -- no second output from pmstat\n");
|
|
goto out;
|
|
}
|
|
len = strlen(line);
|
|
for (j=0; j<len; j++) {
|
|
if (line[j] == ' ') {
|
|
j++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
printf("Pass");
|
|
len = strlen(&line[j]);
|
|
if (len < 20) {
|
|
for(k=0; k<(20-len); k++) {
|
|
printf(" ");
|
|
}
|
|
}
|
|
if (len) {
|
|
printf("%s", &line[j]);
|
|
} else {
|
|
printf("\n");
|
|
}
|
|
goto out;
|
|
}
|
|
}
|
|
printf("Failed -- '%s' not '%s'\n", line, resp);
|
|
out:
|
|
my_pclose(io, pid_of_command);
|
|
|
|
}
|
|
|
|
static int
|
|
add_it_to(char **vars, int cur_cnt, char *name)
|
|
{
|
|
int i;
|
|
size_t len;
|
|
for(i=0; i<cur_cnt; i++) {
|
|
if (strcmp(vars[i], name) == 0) {
|
|
/* Already have */
|
|
return(0);
|
|
}
|
|
}
|
|
if (vars[cur_cnt] != NULL) {
|
|
printf("Cur_cnt:%d filled with %s??\n",
|
|
cur_cnt, vars[cur_cnt]);
|
|
exit(-1);
|
|
}
|
|
/* Ok its new */
|
|
len = strlen(name) + 1;
|
|
vars[cur_cnt] = malloc(len);
|
|
if (vars[cur_cnt] == NULL) {
|
|
printf("No memory %s\n", __FUNCTION__);
|
|
exit(-1);
|
|
}
|
|
memset(vars[cur_cnt], 0, len);
|
|
strcpy(vars[cur_cnt], name);
|
|
return(1);
|
|
}
|
|
|
|
static char *
|
|
build_command_for_exp(struct expression *exp)
|
|
{
|
|
/*
|
|
* Build the pmcstat command to handle
|
|
* the passed in expression.
|
|
* /usr/sbin/pmcstat -w 1 -s NNN -s QQQ
|
|
* where NNN and QQQ represent the PMC's in the expression
|
|
* uniquely..
|
|
*/
|
|
char forming[1024];
|
|
int cnt_pmc, alloced_pmcs, i;
|
|
struct expression *at;
|
|
char **vars, *cmd;
|
|
size_t mal;
|
|
|
|
alloced_pmcs = cnt_pmc = 0;
|
|
/* first how many do we have */
|
|
at = exp;
|
|
while (at) {
|
|
if (at->type == TYPE_VALUE_PMC) {
|
|
cnt_pmc++;
|
|
}
|
|
at = at->next;
|
|
}
|
|
if (cnt_pmc == 0) {
|
|
printf("No PMC's in your expression -- nothing to do!!\n");
|
|
exit(0);
|
|
}
|
|
mal = cnt_pmc * sizeof(char *);
|
|
vars = malloc(mal);
|
|
if (vars == NULL) {
|
|
printf("No memory\n");
|
|
exit(-1);
|
|
}
|
|
memset(vars, 0, mal);
|
|
at = exp;
|
|
while (at) {
|
|
if (at->type == TYPE_VALUE_PMC) {
|
|
if(add_it_to(vars, alloced_pmcs, at->name)) {
|
|
alloced_pmcs++;
|
|
}
|
|
}
|
|
at = at->next;
|
|
}
|
|
/* Now we have a unique list in vars so create our command */
|
|
mal = 23; /* "/usr/sbin/pmcstat -w 1" + \0 */
|
|
for(i=0; i<alloced_pmcs; i++) {
|
|
mal += strlen(vars[i]) + 4; /* var + " -s " */
|
|
}
|
|
cmd = malloc((mal+2));
|
|
if (cmd == NULL) {
|
|
printf("%s out of mem\n", __FUNCTION__);
|
|
exit(-1);
|
|
}
|
|
memset(cmd, 0, (mal+2));
|
|
strcpy(cmd, "/usr/sbin/pmcstat -w 1");
|
|
at = exp;
|
|
for(i=0; i<alloced_pmcs; i++) {
|
|
sprintf(forming, " -s %s", vars[i]);
|
|
strcat(cmd, forming);
|
|
free(vars[i]);
|
|
vars[i] = NULL;
|
|
}
|
|
free(vars);
|
|
return(cmd);
|
|
}
|
|
|
|
static int
|
|
user_expr(struct counters *cpu, int pos)
|
|
{
|
|
int ret;
|
|
double res;
|
|
struct counters *var;
|
|
struct expression *at;
|
|
|
|
at = master_exp;
|
|
while (at) {
|
|
if (at->type == TYPE_VALUE_PMC) {
|
|
var = find_counter(cpu, at->name);
|
|
if (var == NULL) {
|
|
printf("%s:Can't find counter %s?\n", __FUNCTION__, at->name);
|
|
exit(-1);
|
|
}
|
|
if (pos != -1) {
|
|
at->value = var->vals[pos] * 1.0;
|
|
} else {
|
|
at->value = var->sum * 1.0;
|
|
}
|
|
}
|
|
at = at->next;
|
|
}
|
|
res = run_expr(master_exp, 1, NULL);
|
|
ret = printf("%1.3f", res);
|
|
return(ret);
|
|
}
|
|
|
|
|
|
static void
|
|
set_manual_exp(struct expression *exp)
|
|
{
|
|
expression = user_expr;
|
|
command = build_command_for_exp(exp);
|
|
threshold = "User defined threshold";
|
|
}
|
|
|
|
static void
|
|
run_tests(void)
|
|
{
|
|
int i, lenout;
|
|
printf("Running tests on %d PMC's this may take some time\n", valid_pmc_cnt);
|
|
printf("------------------------------------------------------------------------\n");
|
|
for(i=0; i<valid_pmc_cnt; i++) {
|
|
lenout = printf("%s", valid_pmcs[i]);
|
|
fflush(stdout);
|
|
test_for_a_pmc(valid_pmcs[i], lenout);
|
|
}
|
|
}
|
|
static void
|
|
list_all(void)
|
|
{
|
|
int i, cnt, j;
|
|
printf("PMC Abbreviation\n");
|
|
printf("--------------------------------------------------------------\n");
|
|
for(i=0; i<valid_pmc_cnt; i++) {
|
|
cnt = printf("%s", valid_pmcs[i]);
|
|
for(j=cnt; j<52; j++) {
|
|
printf(" ");
|
|
}
|
|
printf("%%%d\n", i);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int i, j, cnt;
|
|
char *filename=NULL;
|
|
char *name=NULL;
|
|
int help_only = 0;
|
|
int test_mode = 0;
|
|
|
|
get_cpuid_set();
|
|
memset(glob_cpu, 0, sizeof(glob_cpu));
|
|
while ((i = getopt(argc, argv, "LHhvm:i:?e:TE:")) != -1) {
|
|
switch (i) {
|
|
case 'L':
|
|
list_all();
|
|
return(0);
|
|
case 'H':
|
|
printf("**********************************\n");
|
|
explain_all();
|
|
printf("**********************************\n");
|
|
return(0);
|
|
break;
|
|
case 'T':
|
|
test_mode = 1;
|
|
break;
|
|
case 'E':
|
|
master_exp = parse_expression(optarg);
|
|
if (master_exp) {
|
|
set_manual_exp(master_exp);
|
|
}
|
|
break;
|
|
case 'e':
|
|
if (validate_expression(optarg)) {
|
|
printf("Unknown expression %s\n", optarg);
|
|
return(0);
|
|
}
|
|
name = optarg;
|
|
set_expression(optarg);
|
|
break;
|
|
case 'm':
|
|
max_to_collect = strtol(optarg, NULL, 0);
|
|
if (max_to_collect > MAX_COUNTER_SLOTS) {
|
|
/* You can't collect more than max in array */
|
|
max_to_collect = MAX_COUNTER_SLOTS;
|
|
}
|
|
break;
|
|
case 'v':
|
|
verbose++;
|
|
break;
|
|
case 'h':
|
|
help_only = 1;
|
|
break;
|
|
case 'i':
|
|
filename = optarg;
|
|
break;
|
|
case '?':
|
|
default:
|
|
use:
|
|
printf("Use %s [ -i inputfile -v -m max_to_collect -e expr -E -h -? -H]\n",
|
|
argv[0]);
|
|
printf("-i inputfile -- use source as inputfile not stdin (if stdin collect)\n");
|
|
printf("-v -- verbose dump debug type things -- you don't want this\n");
|
|
printf("-m N -- maximum to collect is N measurments\n");
|
|
printf("-e expr-name -- Do expression expr-name\n");
|
|
printf("-E 'your expression' -- Do your expression\n");
|
|
printf("-h -- Don't do the expression I put in -e xxx just explain what it does and exit\n");
|
|
printf("-H -- Don't run anything, just explain all canned expressions\n");
|
|
printf("-T -- Test all PMC's defined by this processor\n");
|
|
return(0);
|
|
break;
|
|
};
|
|
}
|
|
if ((name == NULL) && (filename == NULL) && (test_mode == 0) && (master_exp == NULL)) {
|
|
printf("Without setting an expression we cannot dynamically gather information\n");
|
|
printf("you must supply a filename (and you probably want verbosity)\n");
|
|
goto use;
|
|
}
|
|
if (test_mode) {
|
|
run_tests();
|
|
return(0);
|
|
}
|
|
printf("*********************************\n");
|
|
if (master_exp == NULL) {
|
|
(*the_cpu.explain)(name);
|
|
} else {
|
|
printf("Examine your expression ");
|
|
print_exp(master_exp);
|
|
printf("User defined threshold\n");
|
|
}
|
|
if (help_only) {
|
|
return(0);
|
|
}
|
|
process_file(filename);
|
|
if (verbose >= 2) {
|
|
for (i=0; i<ncnts; i++) {
|
|
printf("Counter:%s cpu:%d index:%d\n",
|
|
cnts[i].counter_name,
|
|
cnts[i].cpu, i);
|
|
for(j=0; j<cnts[i].pos; j++) {
|
|
printf(" val - %ld\n", (long int)cnts[i].vals[j]);
|
|
}
|
|
printf(" sum - %ld\n", (long int)cnts[i].sum);
|
|
}
|
|
}
|
|
if (expression == NULL) {
|
|
return(0);
|
|
}
|
|
for(i=0, cnt=0; i<MAX_CPU; i++) {
|
|
if (glob_cpu[i]) {
|
|
do_expression(glob_cpu[i], -1);
|
|
cnt++;
|
|
if (cnt == cpu_count_out) {
|
|
printf("\n");
|
|
break;
|
|
} else {
|
|
printf("\t");
|
|
}
|
|
}
|
|
}
|
|
return(0);
|
|
}
|