19 #include <unordered_set>
21 #include <unordered_map>
29 #include <nlohmann/json.hpp>
34 #include <xmmintrin.h>
35 #include <immintrin.h>
37 #include <sys/resource.h>
40 #include "xed-interface.h"
41 #include "xed-common-hdrs.h"
44 #include <google/sparse_hash_map>
45 #include <google/dense_hash_map>
51 #define MAX_REUSE_DISTANCE_BINS (32)
52 #define MAX_REUSE_DISTANCE (1ULL << (MAX_REUSE_DISTANCE_BINS))
54 #define CACHELINE_BIT (6)
55 #define CACHELINE_SIZE (1L << CACHELINE_BIT)
57 #define NUM_BLOCKS (3)
58 #define NUM_CACHE_LEVELS (4)
60 #define FIRST_USE (ULLONG_MAX)
62 #define MULTI_THREADED
65 static KNOB<bool>
KnobCCT(KNOB_MODE_WRITEONCE,
"pintool",
"cct",
"0",
"Enable CCT-level LRU eviction pair tracking");
66 static KNOB<UINT64>
KnobL1iCap(KNOB_MODE_WRITEONCE,
"pintool",
"l1i_cap",
"8192",
"L1i capacity in unique instructions");
67 static KNOB<UINT64>
KnobL2Cap(KNOB_MODE_WRITEONCE,
"pintool",
"l2_cap",
"65536",
"L2 capacity in unique instructions");
68 static KNOB<UINT64>
KnobL3Cap(KNOB_MODE_WRITEONCE,
"pintool",
"l3_cap",
"2097152",
"L3 capacity in unique instructions");
69 static KNOB<UINT64>
KnobITLBCap(KNOB_MODE_WRITEONCE,
"pintool",
"itlb_cap",
"64",
"iTLB capacity in 4K pages");
70 static KNOB<UINT32>
KnobTopN(KNOB_MODE_WRITEONCE,
"pintool",
"topn",
"100",
"Top-N eviction pairs to report per level");
93 {4096, (
char*)
"4K OS PageSizeReuse"},
94 {1L << 21, (
char*)
"2M Huge PageSizeReuse"},
148 #ifdef MULTI_THREADED
157 static inline size_t GetBlock(
size_t addr,
size_t blockMask) {
158 return addr & (blockMask);
163 PIN_ERROR(
"Pin tool to profile instruction reuse distance.\n" + KNOB_BASE::StringKnobSummary() +
"\n");
175 char* envPath = getenv(
"INS_REUSE_CLIENT_OUTPUT_FILE");
179 snprintf(name,
sizeof(name),
"%s", envPath);
183 gethostname(name + strlen(name),
MAX_FILE_PATH - strlen(name));
184 pid_t pid = getpid();
185 sprintf(name + strlen(name),
"%d", pid);
186 cerr <<
"\n Creating log file at:" << name <<
"\n";
191 for (
int i = 0; i < argc; i++) {
198 for (i = 0; i < argc; i++) {
199 if (0 == strcmp(argv[i],
"--")) {
210 for (; i < argc; i++) {
211 ss << argv[i] <<
" ";
213 j[
"args"] = ss.str();
219 xed_state_init(&
InsReuseGlobals.xedState, XED_MACHINE_MODE_LONG_64, (xed_address_width_enum_t)0, XED_ADDRESS_WIDTH_64b);
249 }
else if (distance == 0) {
252 bin = (
sizeof(uint64_t) * 8 - 1 - __builtin_clzl(distance)) + 1 ;
267 }
else if (distance == 0) {
270 bin = (
sizeof(uint64_t) * 8 - 1 - __builtin_clzl(distance)) + 1 ;
284 auto* retNode = rbt->
Delete(node);
285 retNode->
key = newTick;
287 retNode->payload = payload;
292 return reuseDist + v - 1;
304 unordered_map<uint64_t, uint64_t>& pairMap) {
305 uint64_t key = ((uint64_t)victimCtxt << 32) | (uint64_t)
curCtxt;
311 uint64_t reuseDistance,
bool isFirstUse,
312 const uint64_t* capacities,
int numLevels,
313 unordered_map<uint64_t, uint64_t>*
missPairs) {
314 for (
int lvl = 0; lvl < numLevels; lvl++) {
315 bool isMiss = isFirstUse
317 : (reuseDistance > capacities[lvl]);
333 auto* retNode = rbt->
Delete(node);
335 retNode->
key = newTick;
345 static inline void AnalyzeInsLevelReuse(
void* insAddr, uint32_t numInsInBBL, uint32_t slot, THREADID threadId) {
346 assert(0 != numInsInBBL);
349 uint64_t* shadowMemAddr = &(get<0>(t)[
PAGE_OFFSET((uint64_t)insAddr)]);
351 uint64_t prevTick = *shadowMemAddr;
354 bool isFirstUse = (prevTick == 0);
369 if (prevTick != newTick) {
381 reuseDistance, isFirstUse,
385 uint64_t page = (uint64_t)insAddr >> 12;
387 uint64_t* pageShadow = &(get<0>(pt)[
PAGE_OFFSET(page)]);
388 uint64_t pagePrevTick = *pageShadow;
389 bool pageFirstUse = (pagePrevTick == 0);
392 uint64_t pageNewTick = tData->
itlbTick;
400 uint64_t pageReuseDist;
404 retNode->
key = pageNewTick;
412 bool itlbMiss = (pageReuseDist > itlbCap);
421 *pageShadow = pageNewTick;
425 template <
int blockSize,
int blkIdx>
427 assert(0 != numInsInBlock);
429 assert(1 == __builtin_popcountll(blockSize));
442 tuple<uint64_t[SHADOW_PAGE_SIZE]>& t =
blockData->sm.GetOrCreateShadowBaseAddress((
size_t)block);
443 uint64_t* shadowMemAddr = &(get<0>(t)[
PAGE_OFFSET((
size_t)block)]);
444 uint64_t prevTick = *shadowMemAddr;
445 uint64_t newTick = ++
blockData->numBlocksCounter;
447 if (prevTick == 0 ) {
453 if (numInsInBlock > 1) {
455 blockData->reuseHisto[0] += numInsInBlock - 1;
466 *shadowMemAddr = newTick;
472 for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) {
473 uint32_t totInsInBbl = BBL_NumIns(bbl);
474 ADDRINT insAddr = BBL_Address(bbl);
484 IARG_ADDRINT, insAddr,
485 IARG_UINT32, totInsInBbl,
487 IARG_THREAD_ID, IARG_END);
491 template <
int blockSize,
int blkIdx>
494 assert(1 == __builtin_popcountll(blockSize));
495 const size_t blockBits = __builtin_ctzll(blockSize);
496 const size_t blockMask = ~(blockSize - 1);
499 for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) {
500 uint32_t numInsInMemBlock = 0;
501 size_t prevMemBlock =
GetBlock((uint64_t)INS_Address(BBL_InsHead(bbl)), blockMask);
502 for (INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins = INS_Next(ins)) {
504 size_t insStartMemBlock =
GetBlock((
size_t)INS_Address(ins), blockMask);
505 size_t insEndMemBlock =
GetBlock((
size_t)(INS_Address(ins) + INS_Size(ins) - 1), blockMask);
508 if (insStartMemBlock == prevMemBlock) {
514 if (insEndMemBlock == prevMemBlock) {
520 IPOINT_BEFORE, (AFUNPTR)AnalyzeBlockLevelReuse<blockSize, blkIdx>,
521 IARG_ADDRINT, prevMemBlock,
522 IARG_UINT32, numInsInMemBlock,
523 IARG_THREAD_ID, IARG_END);
524 numInsInMemBlock = 1;
525 prevMemBlock = insEndMemBlock;
528 INS_InsertCall(BBL_InsTail(bbl),
529 IPOINT_BEFORE, (AFUNPTR)AnalyzeBlockLevelReuse<blockSize, blkIdx>,
530 IARG_ADDRINT, prevMemBlock,
531 IARG_UINT32, numInsInMemBlock,
532 IARG_THREAD_ID, IARG_END);
548 fprintf(
gTraceFile,
"\n TODO .. Multi-threading is not well supported.");
549 fprintf(
gTraceFile,
"\nUnloading %s", IMG_Name(img).c_str());
558 vector<pair<uint64_t, uint64_t>> sorted(pairMap.begin(), pairMap.end());
559 sort(sorted.begin(), sorted.end(),
560 [](
const pair<uint64_t, uint64_t>&
a,
const pair<uint64_t, uint64_t>& b) {
561 return a.second > b.second;
564 uint64_t totalMisses = 0;
565 for (
auto& kv : sorted)
566 totalMisses += kv.second;
568 fprintf(
gTraceFile,
"\n\n===== %s Eviction Pairs (capacity=%lu, total misses=%lu) =====\n",
574 jPairs[
"TotalMisses"] = totalMisses;
576 uint32_t topN = min((uint32_t)sorted.size(),
KnobTopN.Value());
577 for (uint32_t i = 0; i < topN; i++) {
578 uint64_t key = sorted[i].first;
579 uint64_t count = sorted[i].second;
582 double pct = (double)count / totalMisses * 100.0;
584 fprintf(
gTraceFile,
"\n--- Rank %u: count=%lu (%.2f%%) ---\n", i + 1, count, pct);
591 jEntry[
"rank"] = i + 1;
592 jEntry[
"count"] = count;
593 jEntry[
"percentage"] = pct;
595 vector<Context> evictedPath, incomingPath;
599 for (
auto& c : evictedPath)
600 jEntry[
"evicted"].push_back(c.functionName);
601 for (
auto& c : incomingPath)
602 jEntry[
"incoming"].push_back(c.functionName);
604 jPairs[
"pairs"].push_back(jEntry);
619 fprintf(
gTraceFile,
"\n %2d %e (%.2lf%%)", i, (
double)histo[i], histo[i] / total * 100);
627 j[
"raw"].push_back(histo[i]);
628 j[
"relative"].push_back(histo[i] * 1.0 / total);
634 static VOID
ThreadFiniFunc(THREADID threadid,
const CONTEXT* ctxt, INT32 code, VOID* v) {
645 fprintf(
gTraceFile,
"\nTID %d instruction-reuse histo (ins footprint = %e)", threadid, (
double)tData->
footprint);
683 struct rusage rusage;
684 getrusage(RUSAGE_SELF, &rusage);
685 size_t peakRSS = (
size_t)(rusage.ru_maxrss);
686 struct timeval ut = rusage.ru_utime;
687 struct timeval st = rusage.ru_stime;
689 fprintf(
gTraceFile,
"\nWhole program instruction-reuse histo ((ins footprint = %e)", (
double)
GLOBAL_STATS.footprint);
697 fprintf(
gTraceFile,
"\nutime: %lu", ut.tv_sec * 1000000 + ut.tv_usec);
698 fprintf(
gTraceFile,
"\nstime: %lu", st.tv_sec * 1000000 + st.tv_usec);
703 j[
"utime"] = ut.tv_sec * 1000000 + ut.tv_usec;
704 j[
"stime"] = st.tv_sec * 1000000 + st.tv_usec;
735 #ifdef MULTI_THREADED
744 int main(
int argc,
char* argv[]) {
746 if (PIN_Init(argc, argv))
771 PIN_AddFiniFunction(
FiniFunc,
nullptr);
#define INTERESTING_INS_ALL
TNKV * FindSumGreaterThan(K key, S *sum)
void Insert(TNKV *newNode)
TNKV * Delete(TNKV *node)
TNKV * FindNodeAtRankFromRight(S capacity)
ShadowTuple< Ts... > & GetOrCreateShadowBaseAddress(const size_t address)
static void UpdateInsReuseStats(uint64_t distance, uint64_t count, InsReuseThreadData *tData)
static void RecordEvictionPair(ContextHandle_t victimCtxt, ContextHandle_t curCtxt, unordered_map< uint64_t, uint64_t > &pairMap)
#define MAX_REUSE_DISTANCE
static void ClientInit(int argc, char *argv[])
int main(int argc, char *argv[])
static unordered_map< ADDRINT, uint32_t > gAddrToSlot
static VOID ImageUnload(IMG img, VOID *v)
uint64_t numBlocksCounter
static void CheckEvictions(RBTree_t &tree, ContextHandle_t curCtxt, uint64_t reuseDistance, bool isFirstUse, const uint64_t *capacities, int numLevels, unordered_map< uint64_t, uint64_t > *missPairs)
static KNOB< UINT64 > KnobITLBCap(KNOB_MODE_WRITEONCE, "pintool", "itlb_cap", "64", "iTLB capacity in 4K pages")
unordered_map< uint64_t, uint64_t > missPairs[NUM_CACHE_LEVELS]
static void DumpHisto(uint64_t *histo, uint64_t footprint, const string &key1, const string &key2)
uint64_t insReuseHisto[MAX_REUSE_DISTANCE_BINS]
static void InstrumentMemBlockLevelReuse(TRACE trace, void *f)
static void InitThreadData(InsReuseThreadData *tdata)
static ofstream gJSONFile
static struct @9 GLOBAL_STATS
static const char * gCacheLevelNames[NUM_CACHE_LEVELS]
static KNOB< UINT64 > KnobL3Cap(KNOB_MODE_WRITEONCE, "pintool", "l3_cap", "2097152", "L3 capacity in unique instructions")
static uint64_t ComputeBlockReuseDistance(uint64_t prevTick, uint64_t newTick, uint32_t v, InsReuseThreadData *tData, RBTree_t *rbt)
constexpr struct @8 BLK_INFO[NUM_BLOCKS]
static InsReuseThreadData * gSingleThreadedTData
uint64_t reuseHisto[MAX_REUSE_DISTANCE_BINS]
static uint64_t gCacheCapacities[NUM_CACHE_LEVELS]
static void InstrumentTrace(TRACE trace, void *f)
static void AnalyzeBlockLevelReuse(void *block, uint32_t numInsInBlock, THREADID threadId)
static KNOB< UINT64 > KnobL2Cap(KNOB_MODE_WRITEONCE, "pintool", "l2_cap", "65536", "L2 capacity in unique instructions")
static KNOB< UINT32 > KnobTopN(KNOB_MODE_WRITEONCE, "pintool", "topn", "100", "Top-N eviction pairs to report per level")
static void AnalyzeInsLevelReuse(void *insAddr, uint32_t numInsInBBL, uint32_t slot, THREADID threadId)
static VOID FiniFunc(INT32 code, VOID *v)
struct @9::@11 blockData[NUM_BLOCKS]
InsReuseThreadData * ClientGetTLS(const THREADID threadId)
static size_t GetBlock(size_t addr, size_t blockMask)
static void UpdateBlockReuseStats(uint64_t distance, uint64_t count, uint64_t *reuseHisto)
static VOID ThreadFiniFunc(THREADID threadid, const CONTEXT *ctxt, INT32 code, VOID *v)
static VOID InstrumentInsCallback(INS ins, VOID *v, const uint32_t slot)
struct @7 InsReuseGlobals
static KNOB< bool > KnobCCT(KNOB_MODE_WRITEONCE, "pintool", "cct", "0", "Enable CCT-level LRU eviction pair tracking")
static void InstrumentInsLevelReuse(TRACE trace, void *f)
#define MAX_REUSE_DISTANCE_BINS
static TLS_KEY client_tls_key
static uint64_t ComputeInsReuseDistance(uint64_t prevTick, uint64_t newTick, uint32_t v, InsReuseThreadData *tData, RBTree_t *rbt, uint32_t payload=0)
static VOID ThreadStart(THREADID threadid, CONTEXT *ctxt, INT32 flags, VOID *v)
static KNOB< UINT64 > KnobL1iCap(KNOB_MODE_WRITEONCE, "pintool", "l1i_cap", "8192", "L1i capacity in unique instructions")
int PinCCTLibInit(IsInterestingInsFptr isInterestingIns, FILE *logFile, CCTLibInstrumentInsCallback userCallback, VOID *userCallbackArg, BOOL doDataCentric=false)
hpcrun_metricFlags_t flags
VOID PrintFullCallingContext(const ContextHandle_t ctxtHandle)
VOID GetFullCallingContext(const ContextHandle_t curCtxtHndle, std::vector< Context > &contextVec)
ContextHandle_t GetContextHandle(const THREADID id, const uint32_t slot)
#define PAGE_OFFSET(addr)
uint64_t insReuseHisto[MAX_REUSE_DISTANCE_BINS]
ShadowMemory< uint64_t > smITLBPage
ShadowMemory< uint64_t > smIns
unordered_map< uint64_t, uint64_t > missPairs[NUM_CACHE_LEVELS]
struct InsReuseThreadData::@10 blockData[NUM_BLOCKS]
uint64_t numBlocksCounter
uint64_t reuseHisto[MAX_REUSE_DISTANCE_BINS]
ShadowMemory< uint64_t > sm