/* * Copyright 2002-2019 Intel Corporation. * * This software is provided to you as Sample Source Code as defined in the accompanying * End User License Agreement for the Intel(R) Software Development Products ("Agreement") * section 1.L. * * This software and the related documents are provided as is, with no express or implied * warranties, other than those that are expressly stated in the License. */ /*! @file * This file contains an ISA-portable cache simulator * data cache hierarchies */ #include "pin.H" #include #include #include #include "cache.H" #include "pin_profile.H" using std::cerr; using std::endl; /* ===================================================================== */ /* Commandline Switches */ /* ===================================================================== */ KNOB KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "dcache.out", "specify dcache file name"); KNOB KnobTrackLoads(KNOB_MODE_WRITEONCE, "pintool", "tl", "0", "track individual loads -- increases profiling time"); KNOB KnobTrackStores(KNOB_MODE_WRITEONCE, "pintool", "ts", "0", "track individual stores -- increases profiling time"); KNOB KnobThresholdHit(KNOB_MODE_WRITEONCE , "pintool", "rh", "100", "only report memops with hit count above threshold"); KNOB KnobThresholdMiss(KNOB_MODE_WRITEONCE, "pintool", "rm","100", "only report memops with miss count above threshold"); KNOB KnobCacheSize(KNOB_MODE_WRITEONCE, "pintool", "c","32", "cache size in kilobytes"); KNOB KnobLineSize(KNOB_MODE_WRITEONCE, "pintool", "b","32", "cache block size in bytes"); KNOB KnobAssociativity(KNOB_MODE_WRITEONCE, "pintool", "a","4", "cache associativity (1 for direct mapped)"); /* ===================================================================== */ INT32 Usage() { cerr << "This tool represents a cache simulator.\n" "\n"; cerr << KNOB_BASE::StringKnobSummary(); cerr << endl; return -1; } /* ===================================================================== */ /* Global Variables */ /* ===================================================================== */ // wrap configuation constants into their own name space to avoid name clashes namespace DL1 { const UINT32 max_sets = KILO; // cacheSize / (lineSize * associativity); const UINT32 max_associativity = 256; // associativity; const CACHE_ALLOC::STORE_ALLOCATION allocation = CACHE_ALLOC::STORE_ALLOCATE; typedef CACHE_ROUND_ROBIN(max_sets, max_associativity, allocation) CACHE; } DL1::CACHE* dl1 = NULL; typedef enum { COUNTER_MISS = 0, COUNTER_HIT = 1, COUNTER_NUM } COUNTER; typedef COUNTER_ARRAY COUNTER_HIT_MISS; // holds the counters with misses and hits // conceptually this is an array indexed by instruction address COMPRESSOR_COUNTER profile; /* ===================================================================== */ VOID LoadMulti(ADDRINT addr, UINT32 size, UINT32 instId) { // first level D-cache const BOOL dl1Hit = dl1->Access(addr, size, CACHE_BASE::ACCESS_TYPE_LOAD); const COUNTER counter = dl1Hit ? COUNTER_HIT : COUNTER_MISS; profile[instId][counter]++; } /* ===================================================================== */ VOID StoreMulti(ADDRINT addr, UINT32 size, UINT32 instId) { // first level D-cache const BOOL dl1Hit = dl1->Access(addr, size, CACHE_BASE::ACCESS_TYPE_STORE); const COUNTER counter = dl1Hit ? COUNTER_HIT : COUNTER_MISS; profile[instId][counter]++; } /* ===================================================================== */ VOID LoadSingle(ADDRINT addr, UINT32 instId) { // @todo we may access several cache lines for // first level D-cache const BOOL dl1Hit = dl1->AccessSingleLine(addr, CACHE_BASE::ACCESS_TYPE_LOAD); const COUNTER counter = dl1Hit ? COUNTER_HIT : COUNTER_MISS; profile[instId][counter]++; } /* ===================================================================== */ VOID StoreSingle(ADDRINT addr, UINT32 instId) { // @todo we may access several cache lines for // first level D-cache const BOOL dl1Hit = dl1->AccessSingleLine(addr, CACHE_BASE::ACCESS_TYPE_STORE); const COUNTER counter = dl1Hit ? COUNTER_HIT : COUNTER_MISS; profile[instId][counter]++; } /* ===================================================================== */ VOID LoadMultiFast(ADDRINT addr, UINT32 size) { dl1->Access(addr, size, CACHE_BASE::ACCESS_TYPE_LOAD); } /* ===================================================================== */ VOID StoreMultiFast(ADDRINT addr, UINT32 size) { dl1->Access(addr, size, CACHE_BASE::ACCESS_TYPE_STORE); } /* ===================================================================== */ VOID LoadSingleFast(ADDRINT addr) { dl1->AccessSingleLine(addr, CACHE_BASE::ACCESS_TYPE_LOAD); } /* ===================================================================== */ VOID StoreSingleFast(ADDRINT addr) { dl1->AccessSingleLine(addr, CACHE_BASE::ACCESS_TYPE_STORE); } /* ===================================================================== */ VOID Instruction(INS ins, void * v) { UINT32 memOperands = INS_MemoryOperandCount(ins); // Instrument each memory operand. If the operand is both read and written // it will be processed twice. // Iterating over memory operands ensures that instructions on IA-32 with // two read operands (such as SCAS and CMPS) are correctly handled. for (UINT32 memOp = 0; memOp < memOperands; memOp++) { const UINT32 size = INS_MemoryOperandSize(ins, memOp); const BOOL single = (size <= 4); if (INS_MemoryOperandIsRead(ins, memOp)) { if( KnobTrackLoads ) { // map sparse INS addresses to dense IDs const ADDRINT iaddr = INS_Address(ins); const UINT32 instId = profile.Map(iaddr); if( single ) { INS_InsertPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR) LoadSingle, IARG_MEMORYOP_EA, memOp, IARG_UINT32, instId, IARG_END); } else { INS_InsertPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR) LoadMulti, IARG_MEMORYOP_EA, memOp, IARG_UINT32, size, IARG_UINT32, instId, IARG_END); } } else { if( single ) { INS_InsertPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR) LoadSingleFast, IARG_MEMORYOP_EA, memOp, IARG_END); } else { INS_InsertPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR) LoadMultiFast, IARG_MEMORYOP_EA, memOp, IARG_UINT32, size, IARG_END); } } } if (INS_MemoryOperandIsWritten(ins, memOp)) { if( KnobTrackStores ) { const ADDRINT iaddr = INS_Address(ins); const UINT32 instId = profile.Map(iaddr); if( single ) { INS_InsertPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR) StoreSingle, IARG_MEMORYOP_EA, memOp, IARG_UINT32, instId, IARG_END); } else { INS_InsertPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR) StoreMulti, IARG_MEMORYOP_EA,memOp, IARG_UINT32, size, IARG_UINT32, instId, IARG_END); } } else { if( single ) { INS_InsertPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR) StoreSingleFast, IARG_MEMORYOP_EA, memOp, IARG_END); } else { INS_InsertPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR) StoreMultiFast, IARG_MEMORYOP_EA, memOp, IARG_UINT32, size, IARG_END); } } } } } /* ===================================================================== */ VOID Fini(int code, VOID * v) { std::ofstream out(KnobOutputFile.Value().c_str()); // print D-cache profile // @todo what does this print out << "PIN:MEMLATENCIES 1.0. 0x0\n"; out << "#\n" "# DCACHE stats\n" "#\n"; out << dl1->StatsLong("# ", CACHE_BASE::CACHE_TYPE_DCACHE); if( KnobTrackLoads || KnobTrackStores ) { out << "#\n" "# LOAD stats\n" "#\n"; out << profile.StringLong(); } out.close(); } /* ===================================================================== */ int main(int argc, char *argv[]) { PIN_InitSymbols(); if( PIN_Init(argc,argv) ) { return Usage(); } dl1 = new DL1::CACHE("L1 Data Cache", KnobCacheSize.Value() * KILO, KnobLineSize.Value(), KnobAssociativity.Value()); profile.SetKeyName("iaddr "); profile.SetCounterName("dcache:miss dcache:hit"); COUNTER_HIT_MISS threshold; threshold[COUNTER_HIT] = KnobThresholdHit.Value(); threshold[COUNTER_MISS] = KnobThresholdMiss.Value(); profile.SetThreshold( threshold ); INS_AddInstrumentFunction(Instruction, 0); PIN_AddFiniFunction(Fini, 0); // Never returns PIN_StartProgram(); return 0; } /* ===================================================================== */ /* eof */ /* ===================================================================== */