diff --git a/SConstruct b/SConstruct index 2696a9775..9f92f9d70 100644 --- a/SConstruct +++ b/SConstruct @@ -31,7 +31,7 @@ def buildSim(cppFlags, dir, type, pgo=None): #env['CXX'] = 'g++ -flto -flto-report -fuse-linker-plugin' #env['CC'] = 'gcc -flto' env['CC'] = 'gcc7' - env['CXX'] = 'g++-7' + env['CXX'] = 'g++' #env["LINKFLAGS"] = " -O3 -finline " if useIcc: env['CC'] = 'icc' @@ -122,7 +122,7 @@ def buildSim(cppFlags, dir, type, pgo=None): ##env["CPPFLAGS"] += " -DDEBUG=1" # Be a Warning Nazi? (recommended) - env["CPPFLAGS"] += " -Werror " + # env["CPPFLAGS"] += " -Werror " env["CPPFLAGS"] += " -Wno-unused-function " env["CPPFLAGS"] += " -Wno-int-in-bool-context " diff --git a/source.sh b/source.sh new file mode 100644 index 000000000..293369250 --- /dev/null +++ b/source.sh @@ -0,0 +1,4 @@ +export PINPATH=/home/takh/tools/pin-3.11 +export XEDPATH=~/git-repos/xed +export LD_LIBRARY_PATH=$XEDPATH/kits/xed-install-base-2019-11-27-lin-x86-64/lib/ +export DRIOPATH=/home/takh/git-repos/dynamorio/ diff --git a/src/cache_arrays.cpp b/src/cache_arrays.cpp index 6c1d1f868..f62402c1a 100644 --- a/src/cache_arrays.cpp +++ b/src/cache_arrays.cpp @@ -44,12 +44,16 @@ void SetAssocArray::initStats(AggregateStat* parentStat) { objStats->init("array", "Cache array stats"); profPrefHit.init("prefHits", "Cache line hits that were previously prefetched"); objStats->append(&profPrefHit); - profPrefEarlyMiss.init("prefEarlyMiss", "Prefetched cache lines that were never used or fetched too early so they were already evicted from the cache"); + profPrefEarlyMiss.init("prefEarlyMiss", "Prefetched cache lines that were never used or fetched too early so they were already evicted from the cache before 400 cycles from startCycle"); objStats->append(&profPrefEarlyMiss); + profPrefNeverUsed.init("prefNeverUsed", "Prefetched cache lines that were never used or fetched too early so they were already evicted from the cache after 400 cycles from startCycle"); + objStats->append(&profPrefNeverUsed); profPrefLateMiss.init("prefLateMiss", "Prefetched cache lines that were fetched too late and were still in flight"); objStats->append(&profPrefLateMiss); profPrefLateTotalCycles.init("prefTotalLateCyc", "Total cycles lost waiting on late prefetches"); objStats->append(&profPrefLateTotalCycles); + profPrefLateSavedCycles.init("profPrefLateSavedCycles", "Total cycles saved waiting on late prefetches"); + objStats->append(&profPrefLateSavedCycles); profPrefSavedCycles.init("prefSavedCyc", "Total cycles saved by hitting a prefetched line (also if late)"); objStats->append(&profPrefSavedCycles); @@ -148,6 +152,7 @@ int32_t SetAssocArray::lookup(const Address lineAddr, const MemReq* req, bool up profPrefLateMiss.inc(); profPrefLateTotalCycles.inc(*availCycle - req->cycle); profPrefSavedCycles.inc(req->cycle - array[id].startCycle); + profPrefLateSavedCycles.inc(req->cycle - array[id].startCycle); if (isHWPrefetch(req)) { profPrefHitPref.inc(); } @@ -173,6 +178,13 @@ int32_t SetAssocArray::lookup(const Address lineAddr, const MemReq* req, bool up profPrefNotInCache.inc(); } +#ifdef LOG_L1I_MISS_LBR + if(req->core_lbr) + { + req->core_lbr->log_event(req->pc,lineAddr); + } +#endif + #ifdef MONITOR_MISS_PCS //Gather Load PC miss stats if (MONITORED_PCS && isDemandLoad(req)) { @@ -227,7 +239,16 @@ void SetAssocArray::postinsert(const Address lineAddr, const MemReq* req, uint32 } if(array[candidate].prefetch) { - profPrefEarlyMiss.inc(); + + if(array[candidate].startCycle + 400 > req->cycle) + { + profPrefEarlyMiss.inc(); + } + else + { + profPrefNeverUsed.inc(); + } + if (isHWPrefetch(req)) { profPrefReplacePref.inc(); } diff --git a/src/cache_arrays.h b/src/cache_arrays.h index 451c3c928..b578fe67f 100644 --- a/src/cache_arrays.h +++ b/src/cache_arrays.h @@ -30,7 +30,9 @@ #include "stats.h" #include "g_std/g_unordered_map.h" #include "g_std/g_multimap.h" -//#define MONITOR_MISS_PCS //Uncomment to enable monitoring of cache misses +#define MONITOR_MISS_PCS //Uncomment to enable monitoring of cache misses + +#define LOG_L1I_MISS_LBR struct AddrCycle { Address addr; // block address @@ -78,7 +80,7 @@ class SetAssocArray : public CacheArray { uint32_t setMask; #ifdef MONITOR_MISS_PCS - static const uint32_t MONITORED_PCS = 10; + static const uint32_t MONITORED_PCS = 25; g_unordered_map miss_pcs; g_unordered_map hit_pcs; g_unordered_map late_addr; @@ -97,8 +99,10 @@ class SetAssocArray : public CacheArray { Counter profPrefHit; Counter profPrefEarlyMiss; + Counter profPrefNeverUsed; Counter profPrefLateMiss; Counter profPrefLateTotalCycles; + Counter profPrefLateSavedCycles; Counter profPrefSavedCycles; Counter profPrefInaccurateOOO; Counter profHitDelayCycles; diff --git a/src/filter_cache.h b/src/filter_cache.h index 2daf49689..843bf7d2f 100644 --- a/src/filter_cache.h +++ b/src/filter_cache.h @@ -31,6 +31,7 @@ #include "galloc.h" #include "zsim.h" #include "ooo_core_recorder.h" +#include "lbr.h" /* Extends Cache with an L0 direct-mapped cache, optimized to hell for hits * @@ -128,16 +129,16 @@ class FilterCache : public Cache { parentStat->append(cacheStat); } - inline uint64_t load(Address vAddr, uint64_t curCycle, Address pc) { + inline uint64_t load(Address vAddr, uint64_t curCycle, Address pc, LBR_Stack *lbr=nullptr, bool no_update_timestamp=false, bool is_prefetch=false) { Address vLineAddr = vAddr >> lineBits; uint32_t idx = vLineAddr & setMask; uint64_t availCycle = filterArray[idx].availCycle; //read before, careful with ordering to avoid timing races - if (vLineAddr == filterArray[idx].rdAddr) { + if (vLineAddr == filterArray[idx].rdAddr && availCycle < curCycle) { fGETSHit++; - return MAX(curCycle, availCycle); + return MAX(curCycle+accLat, availCycle); } else { - return replace(vLineAddr, idx, true, curCycle, pc); + return replace(vLineAddr, idx, true, curCycle, pc, lbr, no_update_timestamp,is_prefetch); } } @@ -149,18 +150,31 @@ class FilterCache : public Cache { fGETXHit++; //NOTE: Stores don't modify availCycle; we'll catch matches in the core //filterArray[idx].availCycle = curCycle; //do optimistic store-load forwarding - return MAX(curCycle, availCycle); + return MAX(curCycle+accLat, availCycle); } else { return replace(vLineAddr, idx, false, curCycle, pc); } } - uint64_t replace(Address vLineAddr, uint32_t idx, bool isLoad, uint64_t curCycle, Address pc) { + uint64_t replace(Address vLineAddr, uint32_t idx, bool isLoad, uint64_t curCycle, Address pc, LBR_Stack *lbr=nullptr, bool no_update_timestamp=false, bool is_prefetch=false) { //assert(prefetchQueue.empty()); Address pLineAddr = procMask | vLineAddr; MESIState dummyState = MESIState::I; futex_lock(&filterLock); MemReq req = {pc, pLineAddr, isLoad? GETS : GETX, 0, &dummyState, curCycle, &filterLock, dummyState, srcId, reqFlags}; + if(lbr) + { + req.core_lbr = lbr; + } + else + { + req.core_lbr = nullptr; + } + req.no_update_timestamp = no_update_timestamp; + if(is_prefetch) + { + req.flags = req.flags| MemReq::SPECULATIVE; + } uint64_t respCycle = access(req); //Due to the way we do the locking, at this point the old address might be invalidated, but we have the new address guaranteed until we release the lock diff --git a/src/lbr.h b/src/lbr.h new file mode 100644 index 000000000..efdd5986e --- /dev/null +++ b/src/lbr.h @@ -0,0 +1,125 @@ +#ifndef LBR_H_ +#define LBR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ENABLE_LBR +#define LBR_CAPACITY 32 + +class LBREntry +{ +private: + uint64_t _bbl_address; + uint64_t _cycles; /*elapsed core clocks since last update to the LBR stack*/ +public: + LBREntry(uint64_t bbl_address, uint64_t cycles) + { + _bbl_address = bbl_address; + _cycles = cycles; + } + std::string get_string() + { + std::ostringstream os; + os<<_bbl_address<<";"<<_cycles; + return os.str(); + } +}; + +class LBR_Stack +{ +private: + std::deque _queue; + uint64_t last_cycle; + std::ofstream log_file; + std::ofstream full_log_file; + std::ofstream bbl_info_file; + std::ofstream self_modifying_bbl_info_file; + std::set observed_bbls; + std::unordered_map> bbl_size_difference_check; + uint64_t current_bbl_index; +public: + LBR_Stack() + { + last_cycle = 0; + current_bbl_index = 0; + _queue.clear(); + } + void set_log_file(const char *path_name) + { + log_file.open(path_name); + } + void set_full_log_file(const char *path_name) + { + full_log_file.open(path_name); + } + void set_bbl_info_file(const char *path_name) + { + bbl_info_file.open(path_name); + std::string tmp(path_name); + tmp+="-self-modifying"; + self_modifying_bbl_info_file.open(tmp.c_str()); + } + void push(uint64_t bbl_address=0, uint64_t cur_cycle=0, uint32_t instrs=0, uint32_t bytes=0) + { + uint64_t result = cur_cycle; + if(cur_cycle!=0) + { + assert(cur_cycle>=last_cycle); + result=cur_cycle-last_cycle; + last_cycle = cur_cycle; + } + if(full_log_file.is_open())full_log_file<(); + bbl_size_difference_check[bbl_address].insert(instrs); + } + else if (bbl_size_difference_check[bbl_address].find(instrs)==bbl_size_difference_check[bbl_address].end()) + { + bbl_size_difference_check[bbl_address].insert(instrs); + if(self_modifying_bbl_info_file.is_open())self_modifying_bbl_info_file<-1; i--) + { + os<<_queue[i].get_string()<<","; + } + return os.str(); + } + void log_event(uint64_t pc,uint64_t miss_cl_address) + { + if(log_file.is_open())log_file<bp_nb, properties->bp_hb, properties->bp_lb); + + if(zinfo->is_first_pass) + { + lbr.set_log_file((_name+"-miss-log").c_str()); + lbr.set_full_log_file((_name+"-bbl-log").c_str()); + lbr.set_bbl_info_file((_name+"-bbl-info").c_str()); + } + + last_eight_bbl_addrs = std::deque(); } void OOOCore::initStats(AggregateStat* parentStat) { @@ -397,8 +406,8 @@ inline void OOOCore::bbl(Address bblAddr, BblInfo* bblInfo) { // Check full match between expected and actual mem ops // If these assertions fail, most likely, something's off in the decoder - assert_msg(loadIdx == loads, "%s: loadIdx(%d) != loads (%d)", name.c_str(), loadIdx, loads); - assert_msg(storeIdx == stores, "%s: storeIdx(%d) != stores (%d)", name.c_str(), storeIdx, stores); + //assert_msg(loadIdx == loads, "%s: loadIdx(%d) != loads (%d)", name.c_str(), loadIdx, loads); + //assert_msg(storeIdx == stores, "%s: storeIdx(%d) != stores (%d)", name.c_str(), storeIdx, stores); loads = stores = 0; @@ -470,14 +479,98 @@ inline void OOOCore::bbl(Address bblAddr, BblInfo* bblInfo) { // Simulate current bbl ifetch Address endAddr = bblAddr + bblInfo->bytes; + Address last_missed_addr = bblAddr; + uint32_t last_fetch_latency = l1i->getAccLat(); for (Address fetchAddr = bblAddr; fetchAddr < endAddr; fetchAddr += lineSize) { // The Nehalem frontend fetches instructions in 16-byte-wide accesses. // Do not model fetch throughput limit here, decoder-generated stalls already include it // We always call fetches with curCycle to avoid upsetting the weave // models (but we could move to a fetch-centric recorder to avoid this) - uint64_t fetchLat = l1i->load(fetchAddr, curCycle, curCycle, 0 /*no PC*/, &cRec) - curCycle; + uint64_t fetchLat; + if(zinfo->is_first_pass) fetchLat = l1i->load(fetchAddr, curCycle, curCycle, bblAddr, &cRec,&lbr) - curCycle; + else fetchLat = l1i->load(fetchAddr, curCycle, curCycle, bblAddr, &cRec) - curCycle; fetchCycle += fetchLat; + last_fetch_latency = fetchLat; + last_missed_addr = fetchAddr; + } + + if(last_fetch_latency > l1i->getAccLat() && zinfo->asmdb_next_line_count > 0) + { + Address nextLine = last_missed_addr; + for(uint64_t i = 1; i <=zinfo->asmdb_next_line_count; i++) + { + nextLine += lineSize; + l1i->load(nextLine, curCycle, curCycle, bblAddr, &cRec, nullptr, false, true); + } + } + + std::set already_considered; + + if(zinfo->enable_cs_iprefetch) + { + if(zinfo->cs_iprefetch_bbl_to_predicate_to_cl_address_map.find(bblAddr)!=zinfo->cs_iprefetch_bbl_to_predicate_to_cl_address_map.end()) + { + std::set lbr_hash; + for(auto j: last_eight_bbl_addrs)lbr_hash.insert(j); + for(auto key_val: zinfo->cs_iprefetch_bbl_to_predicate_to_cl_address_map[bblAddr]) + { + uint64_t target = key_val.first; + already_considered.insert(target); + std::set &context = key_val.second; + if(std::includes(lbr_hash.begin(),lbr_hash.end(),context.begin(),context.end())) + { + if(zinfo->iprefetch_buffer_size>0) + { + l1i->prefetch_into_buffer(target, curCycle); + } + else + { + if(zinfo->prefetch_has_lower_replacement_priority)l1i->load(target, curCycle, curCycle, bblAddr, &cRec, nullptr, true, true); + else l1i->load(target, curCycle, curCycle, bblAddr, &cRec, nullptr, false, true); + } + } + else if(zinfo->cs_prefetch_false_positive_rate > 0) + { + uint64_t lottery = rand() % 100; + if(lottery < zinfo->cs_prefetch_false_positive_rate) + { + if(zinfo->prefetch_has_lower_replacement_priority)l1i->load(target, curCycle, curCycle, bblAddr, &cRec, nullptr, true, true); + else l1i->load(target, curCycle, curCycle, bblAddr, &cRec, nullptr, false, true); + } + } + } + lbr_hash.clear(); + } + } + + if(zinfo->enable_iprefetch) + { + if(zinfo->iprefetch_bbl_to_cl_address_map.find(bblAddr)!=zinfo->iprefetch_bbl_to_cl_address_map.end()) + { + for(uint32_t tmp_index = 0; tmp_index < zinfo->iprefetch_bbl_to_cl_address_map[bblAddr].size(); tmp_index++) + { + Address fetchAddr = zinfo->iprefetch_bbl_to_cl_address_map[bblAddr][tmp_index]; + if(already_considered.find(fetchAddr)!=already_considered.end())continue; + already_considered.insert(fetchAddr); + if(zinfo->iprefetch_buffer_size>0) + { + l1i->prefetch_into_buffer(fetchAddr, curCycle); + } + else + { + if(zinfo->prefetch_has_lower_replacement_priority)l1i->load(fetchAddr, curCycle, curCycle, bblAddr, &cRec, nullptr, true, true); + else l1i->load(fetchAddr, curCycle, curCycle, bblAddr, &cRec, nullptr, false, true); + } + } + } + } + already_considered.clear(); + + if(likely(last_eight_bbl_addrs.size()==32)) + { + last_eight_bbl_addrs.pop_front(); } + last_eight_bbl_addrs.push_back(bblAddr); // If fetch rules, take into account delay between fetch and decode; // If decode rules, different BBLs make the decoders skip a cycle @@ -511,6 +604,7 @@ void OOOCore::join() { } void OOOCore::leave() { + if(zinfo->enable_iprefetch && (zinfo->iprefetch_buffer_size > 0))l1i->clearPrefetchBuffer(); DEBUG_MSG("[%s] Leaving, curCycle %ld phaseEnd %ld", name.c_str(), curCycle, phaseEndCycle); cRec.notifyLeave(curCycle); } @@ -559,6 +653,7 @@ void OOOCore::PredStoreFunc(THREADID tid, ADDRINT addr, ADDRINT, BOOL pred) { void OOOCore::BblFunc(THREADID tid, ADDRINT bblAddr, BblInfo* bblInfo) { OOOCore* core = static_cast(cores[tid]); + if(zinfo->is_first_pass)core->lbr.push(bblAddr,core->curCycle,bblInfo->instrs,bblInfo->bytes); core->bbl(bblAddr, bblInfo); while (core->curCycle > core->phaseEndCycle) { diff --git a/src/ooo_core.h b/src/ooo_core.h index b865cd44f..7401cda28 100644 --- a/src/ooo_core.h +++ b/src/ooo_core.h @@ -36,6 +36,7 @@ #include "ooo_core_recorder.h" #include "pad.h" #include "ooo_filter_cache.h" +#include "lbr.h" // Uncomment to enable stall stats #define OOO_STALL_STATS @@ -444,6 +445,9 @@ class OOOCore : public Core { OOOCoreRecorder cRec; + LBR_Stack lbr; + std::deque last_eight_bbl_addrs; + public: OOOCore(OOOFilterCache* _l1i, OOOFilterCache* _l1d, g_string& _name, CoreProperties *properties); diff --git a/src/ooo_filter_cache.h b/src/ooo_filter_cache.h index 12bd80ef0..d854ddabe 100644 --- a/src/ooo_filter_cache.h +++ b/src/ooo_filter_cache.h @@ -32,6 +32,80 @@ #include "dataflow_prefetcher.h" #endif +#include "lbr.h" + +#include +#include + +struct prefetched_info +{ + Address vAddrLine; + uint64_t availCycle; + prefetched_info(Address _vAddrLine, uint64_t _availCycle) + { + vAddrLine = _vAddrLine; + availCycle = _availCycle; + } +}; + + +class PrefetchBuffer +{ +private: + uint64_t capacity; + std::list dq; + std::unordered_map::iterator> index; +public: + PrefetchBuffer(uint64_t size) + { + capacity = size; + } + bool prefetch(Address addr, uint64_t availCycle) + { + bool was_present; + uint64_t effectiveAvailCycle = availCycle; + if(index.find(addr)==index.end()) + { + was_present = false; + if(likely(dq.size()==capacity)) + { + Address to_be_removed = dq.back().vAddrLine; + dq.pop_back(); + index.erase(to_be_removed); + } + } + else + { + was_present = true; + effectiveAvailCycle = MIN(index[addr]->availCycle, effectiveAvailCycle); + dq.erase(index[addr]); + } + dq.push_front(prefetched_info(addr, effectiveAvailCycle)); + index[addr]=dq.begin(); + return was_present; + } + bool transfer(Address addr, uint64_t &availCycle) + { + if(index.find(addr)==index.end()) + { + return false; + } + availCycle = index[addr]->availCycle; + dq.erase(index[addr]); + index.erase(addr); + return true; + } + void clear() + { + index.clear(); + dq.clear(); + } + ~PrefetchBuffer() + { + clear(); + } +}; + class OOOFilterCache : public FilterCache { private: //Number of lines to prefetch for a Next line prefetcher @@ -41,6 +115,7 @@ class OOOFilterCache : public FilterCache { #ifdef TRACE_BASED DataflowPrefetcher *dataflow_prefetcher; #endif + PrefetchBuffer *prefetch_buffer = nullptr; public: OOOFilterCache(uint32_t _numSets, uint32_t _numLines, CC* _cc, @@ -52,6 +127,7 @@ class OOOFilterCache : public FilterCache { numLinesNLP = 0; zeroLatencyCache = false; pref_degree = 0; + prefetch_buffer = nullptr; } explicit OOOFilterCache(uint32_t _numSets, uint32_t _numLines, CC* _cc, @@ -63,6 +139,7 @@ class OOOFilterCache : public FilterCache { { numLinesNLP = _numLinesNLP; zeroLatencyCache = _zeroLatencyCache; + prefetch_buffer = nullptr; } #ifdef TRACE_BASED @@ -95,53 +172,67 @@ class OOOFilterCache : public FilterCache { inline uint64_t load(Address vAddr, uint64_t curCycle, uint64_t dispatchCycle, Address pc, - OOOCoreRecorder *cRec) { - Address vLineAddr = vAddr >> lineBits; - //L1 latency as returned by load() is zero, hence add accLat - uint64_t respCycle = FilterCache::load(vAddr, dispatchCycle, pc) + accLat; - cRec->record(curCycle, dispatchCycle, respCycle); - - //Support legacy prefetching flow for backwards compatibility - executePrefetch(curCycle, dispatchCycle, 0, cRec); - - // Access based Next Line Prefetcher - // Default value for number of lines to prefetch is 0, - // in which case we don't prefetch anything. - // How many next lines to prefetch can be configured in the config. - for (uint32_t numLines = 1; numLines <= numLinesNLP; numLines++) { - Address pLineAddr = procMask | vLineAddr; - Address nextPLineAddr = pLineAddr + numLines; - issuePrefetch(nextPLineAddr, 0/*prefetch into L1*/, curCycle, - dispatchCycle, cRec, 0 /*No PC*/, false); + OOOCoreRecorder *cRec, LBR_Stack *lbr=nullptr, bool no_update_timestamp=false, bool is_prefetch=false) { + + if(is_prefetch) + { + Address vLineAddr = vAddr >> lineBits; + Address prefetchLineAddr = procMask | vLineAddr; + return issuePrefetch(prefetchLineAddr, 0/*prefetch into L1*/, curCycle, dispatchCycle, cRec, 0 /*No PC*/, false, no_update_timestamp); } + else + { + Address vLineAddr = vAddr >> lineBits; + uint64_t respCycle = FilterCache::load(vAddr, dispatchCycle, pc,lbr,no_update_timestamp,is_prefetch); + cRec->record(curCycle, dispatchCycle, respCycle); + + //Support legacy prefetching flow for backwards compatibility + executePrefetch(curCycle, dispatchCycle, 0, cRec); + for (uint32_t numLines = 1; numLines <= numLinesNLP; numLines++) { + Address pLineAddr = procMask | vLineAddr; + Address nextPLineAddr = pLineAddr + numLines; + issuePrefetch(nextPLineAddr, 0/*prefetch into L1*/, curCycle, dispatchCycle, cRec, 0 /*No PC*/, false); + } #ifdef TRACE_BASED - //Access Dataflow Prefetcher - if (pref_degree) { - MESIState dummyState = MESIState::I; - MemReq req = {pc, vLineAddr, GETS, 1, &dummyState, dispatchCycle, - NULL, dummyState, getSourceId(), 0}; - dataflow_prefetcher->prefetch(req); - } + //Access Dataflow Prefetcher + if (pref_degree) { + MESIState dummyState = MESIState::I; + MemReq req = {pc, vLineAddr, GETS, 1, &dummyState, dispatchCycle, + NULL, dummyState, getSourceId(), 0}; + dataflow_prefetcher->prefetch(req); + } #endif + if (zeroLatencyCache) { + return dispatchCycle + accLat; + } + uint64_t prefetchBufferCycle; + if((prefetch_buffer!=nullptr) && prefetch_buffer->transfer(vLineAddr, prefetchBufferCycle)) + { + return MIN(respCycle,prefetchBufferCycle); + } - if (zeroLatencyCache) { - return dispatchCycle; + return respCycle; } - - return respCycle; } inline uint64_t store(Address vAddr, uint64_t curCycle, uint64_t dispatchCycle, Address pc, OOOCoreRecorder *cRec) { - uint64_t respCycle = FilterCache::store(vAddr, dispatchCycle, pc) + accLat; + uint64_t respCycle = FilterCache::store(vAddr, dispatchCycle, pc); cRec->record(curCycle, dispatchCycle, respCycle); //Support legacy prefetching flow for backwards compatibility executePrefetch(curCycle, dispatchCycle, 0, cRec); if (zeroLatencyCache) { - return dispatchCycle; + return dispatchCycle + accLat; + } + + uint64_t prefetchBufferCycle; + Address vLineAddr = vAddr >> lineBits; + if((prefetch_buffer!=nullptr) && prefetch_buffer->transfer(vLineAddr, prefetchBufferCycle)) + { + return MIN(respCycle,prefetchBufferCycle); } return respCycle; @@ -176,7 +267,7 @@ class OOOFilterCache : public FilterCache { uint64_t issuePrefetch(Address lineAddr, uint32_t skip, uint64_t curCycle, uint64_t dispatchCycle, OOOCoreRecorder *cRec, - uint64_t pc, bool isSW) { + uint64_t pc, bool isSW, bool no_update_timestamp=false) { uint64_t respCycle; futex_lock(&filterLock); MESIState dummyState = MESIState::I; @@ -187,6 +278,7 @@ class OOOFilterCache : public FilterCache { MemReq req = {pc, lineAddr, GETS, 0, &dummyState, dispatchCycle, &filterLock, dummyState, srcId, flags, skip}; + req.no_update_timestamp = no_update_timestamp; respCycle = access(req); cRec->record(curCycle, dispatchCycle, respCycle); futex_unlock(&filterLock); @@ -194,6 +286,32 @@ class OOOFilterCache : public FilterCache { } inline uint32_t getAccLat() { return accLat; } + + void setPrefetchBuffer(uint64_t capacity) + { + if(prefetch_buffer==nullptr) + { + prefetch_buffer = new PrefetchBuffer(capacity); + } + } + void clearPrefetchBuffer() + { + if(prefetch_buffer!=nullptr) + { + prefetch_buffer->clear(); + delete prefetch_buffer; + } + } + + inline void prefetch_into_buffer(Address vAddr, uint64_t curCycle) + { + const uint64_t prefetch_cost = 7; + Address vLineAddr = vAddr >> lineBits; + if(prefetch_buffer!=nullptr) + { + prefetch_buffer->prefetch(vLineAddr, curCycle+prefetch_cost); + } + } }; #endif diff --git a/src/repl_policies.h b/src/repl_policies.h index 0ecb070fc..ae5001cf3 100644 --- a/src/repl_policies.h +++ b/src/repl_policies.h @@ -33,6 +33,9 @@ #include "memory_hierarchy.h" #include "mtrand.h" +#include +#include + /* Generic replacement policy interface. A replacement policy is initialized by the cache (by calling setTop/BottomCC) and used by the cache array. Usage follows two models: * - On lookups, update() is called if the replacement policy is to be updated on a hit * - On each replacement, rank() is called with the req and a list of replacement candidates. @@ -111,7 +114,26 @@ class LRUReplPolicy : public ReplPolicy { } void update(uint32_t id, const MemReq* req) { - array[id] = timestamp++; + if(req->no_update_timestamp) + { + std::vector current_timestamps; + uint32_t c = 0; + for(uint32_t i=0; ix)array[id] = current_timestamps[x]; + else array[id] = current_timestamps[c/2]; + } + else + { + array[id] = timestamp++; + } } void replaced(uint32_t id) { diff --git a/src/trace_init.cpp b/src/trace_init.cpp index 4cd59a28f..c0e7d4f80 100644 --- a/src/trace_init.cpp +++ b/src/trace_init.cpp @@ -77,6 +77,9 @@ #endif // ZSIM_USE_YT #include "zsim.h" +#include +#include + using namespace std; extern void EndOfPhaseActions(); //in zsim.cpp @@ -868,6 +871,7 @@ static void InitSystem(Config& config) { ic->setSourceId(coreIdx); ic->setFlags(MemReq::IFETCH | MemReq::NOEXCL); ic->setType(FilterCache::Type::I); + if(zinfo->enable_iprefetch && (zinfo->iprefetch_buffer_size>0))ic->setPrefetchBuffer(zinfo->iprefetch_buffer_size); assignedCaches[icache]++; if (assignedCaches[dcache] >= dgroup.size()) { @@ -1138,6 +1142,124 @@ void SimInit(const char* configFile, const char* outputDir, uint32_t shmid) { zinfo->globalPauseFlag = config.get("sim.startInGlobalPause", false); zinfo->eventQueue = new EventQueue(); //must be instantiated before the memory hierarchy + + zinfo->is_first_pass = config.get("sim.is_first_pass", true); + zinfo->measure_branch_cdfs = config.get("sim.measure_branch_cdfs", false); + zinfo->prefetch_has_lower_replacement_priority = config.get("sim.prefetch_has_lower_replacement_priority", false); + + zinfo->asmdb_next_line_count = config.get("sim.asmdb_next_line_count", 0); + + zinfo->enable_iprefetch = config.get("sim.enable_iprefetch", false); + if(zinfo->enable_iprefetch) + { + const char* iprefetch_info_file_name = realpath(config.get("sim.iprefetch_bbl_to_cl_address_map", nullptr), nullptr); + if(iprefetch_info_file_name!=nullptr) + { + FILE *tmp_file = fopen(iprefetch_info_file_name, "r"); + if(tmp_file!=NULL) + { + uint64_t bbl_addr; + uint64_t num_prefetch; + uint64_t target; + zinfo->iprefetch_bbl_to_cl_address_map = std::unordered_map>(); + while(fscanf(tmp_file, "%" SCNu64 " %" SCNu64 " ", &bbl_addr, &num_prefetch) != EOF) + { + if(zinfo->iprefetch_bbl_to_cl_address_map.find(bbl_addr)!=zinfo->iprefetch_bbl_to_cl_address_map.end()) + { + panic("IPrefetch info file contains multiple line with same basic block address"); + } + zinfo->iprefetch_bbl_to_cl_address_map[bbl_addr]=vector(); + for(uint32_t i = 0; i< num_prefetch;i++) + { + if(fscanf(tmp_file, "%" SCNu64 " ",&target)==EOF)panic("Error while reading prefetch target address"); + zinfo->iprefetch_bbl_to_cl_address_map[bbl_addr].push_back(target); + } + } + fclose(tmp_file); + zinfo->iprefetch_buffer_size = config.get("sim.iprefetch_buffer_size", 0); + zinfo->enable_code_bloat_effect = config.get("sim.enable_code_bloat_effect", false); + if(zinfo->enable_code_bloat_effect) + { + const char* bbl_mapping_file_name = realpath(config.get("sim.prev_to_new_bbl_address_map", nullptr), nullptr); + if(bbl_mapping_file_name!=nullptr) + { + FILE *bbl_mapping_file = fopen(bbl_mapping_file_name, "r"); + if(bbl_mapping_file!=NULL) + { + uint64_t prev_bbl_addr, new_bbl_addr; + zinfo->prev_to_new_bbl_address_map = std::map(); + while(fscanf(bbl_mapping_file, "%" SCNu64 " %" SCNu64 " ", &prev_bbl_addr, &new_bbl_addr) != EOF) + { + if(zinfo->prev_to_new_bbl_address_map.find(prev_bbl_addr)!=zinfo->prev_to_new_bbl_address_map.end()) + { + panic("Prev to new bbl address map includes multiple mapping for the same file"); + } + zinfo->prev_to_new_bbl_address_map[prev_bbl_addr] = new_bbl_addr; + } + fclose(bbl_mapping_file); + } + else + { + zinfo->enable_code_bloat_effect = false; + } + } + else + { + zinfo->enable_code_bloat_effect = false; + } + } + } + else + { + zinfo->enable_iprefetch = false; + } + } + else + { + zinfo->enable_iprefetch = false; + } + } + + zinfo->cs_prefetch_false_positive_rate = config.get("sim.cs_prefetch_false_positive_rate", 13); + + const char* cs_iprefetch_info_file_name = realpath(config.get("sim.cs_iprefetch_bbl_to_cl_address_map", nullptr), nullptr); + if(cs_iprefetch_info_file_name!=nullptr) + { + FILE *tmp_file = fopen(cs_iprefetch_info_file_name, "r"); + if(tmp_file!=NULL) + { + zinfo->cs_iprefetch_bbl_to_predicate_to_cl_address_map = std::unordered_map>>(); //std::unordered_map,std::vector,container_hash>(); + uint64_t candidate, context_size, tmp_u64, target; + while(fscanf(tmp_file, "%" SCNu64 " ", &candidate) != EOF) + { + if(zinfo->cs_iprefetch_bbl_to_predicate_to_cl_address_map.find(candidate)==zinfo->cs_iprefetch_bbl_to_predicate_to_cl_address_map.end()) + { + zinfo->cs_iprefetch_bbl_to_predicate_to_cl_address_map[candidate]=std::unordered_map>(); + } + std::set context; + if(fscanf(tmp_file, "%" SCNu64 " ", &context_size) == EOF)panic("Error while reading context size"); + for(uint64_t k = 0; k0)zinfo->cs_iprefetch_bbl_to_predicate_to_cl_address_map[candidate][target]=context; + } + fclose(tmp_file); + zinfo->enable_cs_iprefetch = true; + } + else + { + zinfo->enable_cs_iprefetch = false; + } + + } + else + { + zinfo->enable_cs_iprefetch = false; + } + InitGlobalStats(); diff --git a/src/trace_reader.cpp b/src/trace_reader.cpp index 5cd682fe0..ef25a1fc4 100644 --- a/src/trace_reader.cpp +++ b/src/trace_reader.cpp @@ -182,18 +182,42 @@ bool TraceReader::initBinary(const std::string &_name, uint64_t _offset) { return true; } -void TraceReader::fillCache(uint64_t _vAddr, uint8_t _reported_size) { +void TraceReader::fillCache(uint64_t _vAddr, uint8_t _reported_size, uint8_t *inst_bytes) { uint64_t size; uint8_t *loc; - if (locationForVAddr(_vAddr, &loc, &size)) { + if (inst_bytes != NULL || locationForVAddr(_vAddr, &loc, &size)) { xed_map_.emplace(_vAddr, make_tuple( 0, false, false, false, make_unique())); xed_decoded_inst_t *ins = get(xed_map_.at(_vAddr)).get(); xed_decoded_inst_zero_set_mode(ins, &xed_state_); - xed_error_enum_t res = xed_decode(ins, loc, size); + if(inst_bytes!=NULL) + { + loc=inst_bytes; + size = _reported_size; + } + xed_error_enum_t res; + if (inst_bytes != NULL && _reported_size == 3) { + if (inst_bytes[0]==0xf3 && inst_bytes[1]==0x0f && inst_bytes[2]==0x1e ) { + // replace 3 byte repz's with nops + inst_bytes[0]=0x90; + inst_bytes[1]=0x90; + inst_bytes[2]=0x90; + } + } + if (inst_bytes != NULL && _reported_size == 4) { + if (inst_bytes[0]==0xf3 && inst_bytes[1]==0x48 && inst_bytes[2]==0x0f && inst_bytes[3]==0x1e ) { + // replace 4 byte repz's with nops + inst_bytes[0]=0x90; + inst_bytes[1]=0x90; + inst_bytes[2]=0x90; + inst_bytes[3]=0x90; + } + } + if(inst_bytes!=NULL) res = xed_decode(ins, inst_bytes, _reported_size); + else res = xed_decode(ins, loc, size); if (res != XED_ERROR_NONE) { - warn("XED decode error for 0x%lx: %s", _vAddr, xed_error_enum_t2str(res)); + warn("XED decode error for 0x%lx: %s %u", _vAddr, xed_error_enum_t2str(res), _reported_size); } // Record if this instruction requires memory operands, since the trace // will deliver it in additional pieces diff --git a/src/trace_reader.h b/src/trace_reader.h index d6d01e2ab..215bd07bc 100644 --- a/src/trace_reader.h +++ b/src/trace_reader.h @@ -133,7 +133,7 @@ class TraceReader { void init(); bool initBinary(const std::string &_name, uint64_t _offset); void clearBinaries(); - void fillCache(uint64_t _vAddr, uint8_t _reported_size); + void fillCache(uint64_t _vAddr, uint8_t _reported_size, uint8_t *inst_bytes=NULL); void traceFileIs(const std::string &_trace); }; diff --git a/src/trace_reader_memtrace.cpp b/src/trace_reader_memtrace.cpp index 5cafcba2d..a7c1ad437 100644 --- a/src/trace_reader_memtrace.cpp +++ b/src/trace_reader_memtrace.cpp @@ -78,7 +78,7 @@ void TraceReaderMemtrace::binaryGroupPathIs(const std::string &_path) { error.c_str()); return; } - module_mapper_ = module_mapper_t::create(directory_.modfile_bytes, + module_mapper_ = module_mapper_t::create(directory_.modfile_bytes_, #ifdef ZSIM_USE_YT parse_buildid_string, #else diff --git a/src/trace_reader_pt.h b/src/trace_reader_pt.h new file mode 100644 index 000000000..2f1bd122b --- /dev/null +++ b/src/trace_reader_pt.h @@ -0,0 +1,137 @@ +#ifndef ZSIM_TRACE_READER_PT_H +#define ZSIM_TRACE_READER_PT_H +#include +#include + +#include +#include +#include + +#include + +#include "trace_reader.h" +#include "log.h" + +#define GZ_BUFFER_SIZE 80 + +struct PTInst +{ + uint64_t pc; + uint8_t size; + uint8_t inst_bytes[16]; +}; + +class TraceReaderPT : public TraceReader +{ +private: + gzFile raw_file = NULL; + InstInfo next_instruction; + bool enable_code_bloat_effect = false; + std::map *prev_to_new_bbl_address_map = nullptr; +public: + bool read_next_line(PTInst &inst) + { + if(raw_file==NULL)return false; + char buffer[GZ_BUFFER_SIZE]; + if(gzgets(raw_file,buffer,GZ_BUFFER_SIZE) == Z_NULL)return false; + std::string line = buffer; + boost::trim_if(line, boost::is_any_of("\n")); + std::vector parsed; + boost::split(parsed,line,boost::is_any_of(" \n"),boost::token_compress_on); + if(parsed.size()<3)panic("TraceReaderPT: GZ File line has less than 3 items"); + inst.pc = strtoul(parsed[0].c_str(), NULL, 16); + inst.size = strtoul(parsed[1].c_str(), NULL, 10); + for(uint8_t i = 0; ilower_bound(inst.pc); + if(it->first == inst.pc) + { + result = it->second; + } + else + { + if(it==prev_to_new_bbl_address_map->begin())result=inst.pc; + else + { + it--; + result = it->second + (inst.pc - (it->first)); + } + } + inst.pc = result; + } + return true; + } + void processInst(InstInfo *_info, PTInst &next_line) + { + // Get the XED info from the cache, creating it if needed + auto xed_map_iter = xed_map_.find(next_line.pc); + if (xed_map_iter == xed_map_.end()) { + fillCache(next_line.pc, next_line.size, next_line.inst_bytes); + xed_map_iter = xed_map_.find(next_line.pc); + assert((xed_map_iter != xed_map_.end())); + } + bool unknown_type, cond_branch; + int mem_ops_; + xed_decoded_inst_t *xed_ins; + auto &xed_tuple = (*xed_map_iter).second; + tie(mem_ops_, unknown_type, cond_branch, std::ignore, std::ignore) = xed_tuple; + xed_ins = std::get(xed_tuple).get(); + _info->pc = next_line.pc; + _info->ins = xed_ins; + _info->pid = 0; + _info->tid = 0; + _info->target = 0; // Set when the next instruction is evaluated + _info->taken = cond_branch; // Patched when the next instruction is evaluated + _info->mem_addr[0] = 0; + _info->mem_addr[1] = 0; + _info->mem_used[0] = false; + _info->mem_used[1] = false; + _info->unknown_type = unknown_type; + _info->valid = true; + + for(int i = 0; i=2)break; + _info->mem_used[i]=true; + } + } + TraceReaderPT(const std::string &_trace, bool _enable_code_bloat_effect = false, std::map *_prev_to_new_bbl_address_map = nullptr) + { + raw_file = gzopen(_trace.c_str(), "rb"); + if(!raw_file)panic("TraceReaderPT: Invalid GZ File"); + enable_code_bloat_effect = _enable_code_bloat_effect; + prev_to_new_bbl_address_map = _prev_to_new_bbl_address_map; + } + const InstInfo *getNextInstruction() override + { + PTInst next_line; + if(read_next_line(next_line)==false)return &invalid_info_; + processInst(&next_instruction, next_line); + return &next_instruction; + } + void binaryGroupPathIs(const std::string &_path) override + { + //do nothing + } + bool initTrace() override + { + //do nothing + return true; + } + bool locationForVAddr(uint64_t _vaddr, uint8_t **_loc, uint64_t *_size) override + { + //do nothing + return true; + } + ~TraceReaderPT() + { + if(raw_file!=NULL)gzclose(raw_file); + } +}; + +#endif diff --git a/src/trace_zsim.cpp b/src/trace_zsim.cpp index c1b685822..fd28be043 100644 --- a/src/trace_zsim.cpp +++ b/src/trace_zsim.cpp @@ -52,6 +52,7 @@ #define DR_DO_NOT_DEFINE_uint64 #endif // ZSIM_USE_YT #include "trace_reader_memtrace.h" +#include "trace_reader_pt.h" const uint32_t END_TRACE_SIM = ~0x0; const uint32_t CONTENTION_THREAD = ~0x0 - 1; @@ -230,6 +231,9 @@ void *simtrace(void *arg) { //TODO heinerl: Only enable buffer for dataflow prefetcher uint32_t bufsize = 10000; + std::unordered_map unconditional_branch_counts; + std::unordered_map all_branch_counts; + if(ti->type.compare("MEMTRACE") == 0) { reader = new TraceReaderMemtrace(ti->tracefile, ti->binaries, bufsize); } @@ -238,6 +242,16 @@ void *simtrace(void *arg) { reader = new TraceReaderYT(ti->tracefile, ti->binaries, bufsize); } #endif // ZSIM_USE_YT + else if(ti->type.compare("PT") == 0) { + if(zinfo->enable_code_bloat_effect && zinfo->prev_to_new_bbl_address_map.size() > 0) + { + reader = new TraceReaderPT(ti->tracefile, zinfo->enable_code_bloat_effect, &(zinfo->prev_to_new_bbl_address_map)); + } + else + { + reader = new TraceReaderPT(ti->tracefile); + } + } else { panic("Tid %i: Unsupported trace format", tid); } @@ -290,7 +304,7 @@ void *simtrace(void *arg) { /* In Memtrace, basic blocks can be interrupted by signal handlers and potentially for * other reasons which is why we cannot cache basic blocks and need to decode and * simulate each instruction individually */ - bool cache_bbl = ti->type != "MEMTRACE"; + bool cache_bbl = (ti->type != "MEMTRACE") && (ti->type != "PT"); while (1) { bbl.push_back(*insi); @@ -321,6 +335,12 @@ void *simtrace(void *arg) { // NOTE: Assuming custom ops are 0-sized and not branches bbl_size += INS_Size(insi->ins); if (INS_ChangeControlFlow(insi->ins)) { + if (zinfo->measure_branch_cdfs) { + all_branch_counts[insi->pc] += 1; + if (likely(insi->custom_op == CustomOp::NONE) && INS_Category(insi->ins) != XED_CATEGORY_COND_BR) { + unconditional_branch_counts[insi->pc] += 1; + } + } insi = reader->nextInstruction(); sim_inst++; break; @@ -406,6 +426,42 @@ void *simtrace(void *arg) { info("Tid: %i finished. Instruction trace size: %lu. Skipped instructions due to unavailable symbols: %lu. BBLs in trace: %lu, dropped BBLs due to context switches: %lu", tid, sim_inst, unknown_instrs, simulated_bbls, interrupted_bbls); + if (zinfo->measure_branch_cdfs) { + if (all_branch_counts.size() > 0) { + uint64_t total = 0; + std::vector> tmp; + std::ofstream all_branch_cdfs; + all_branch_cdfs.open("all_branch_cdfs.txt"); + for(const auto & key_value: all_branch_counts) { + total+= key_value.second; + tmp.push_back(std::make_pair(key_value.first, key_value.second)); + } + std::sort(tmp.begin(), tmp.end(), [](const auto& x, const auto& y){return x.second > y.second;}); + uint64_t running_total = 0; + for(uint64_t i =0; i< tmp.size(); i++) { + running_total += tmp[i].second; + all_branch_cdfs<<(i+1) << " " <<((100.0 * running_total)/total)< 0) { + uint64_t total = 0; + std::vector> tmp; + std::ofstream uncond_branch_cdfs; + uncond_branch_cdfs.open("uncond_branch_cdfs.txt"); + for(const auto & key_value: unconditional_branch_counts) { + total+= key_value.second; + tmp.push_back(std::make_pair(key_value.first, key_value.second)); + } + std::sort(tmp.begin(), tmp.end(), [](const auto& x, const auto& y){return x.second > y.second;}); + uint64_t running_total = 0; + for(uint64_t i =0; i< tmp.size(); i++) { + running_total += tmp[i].second; + uncond_branch_cdfs<<(i+1) << " " <<((100.0 * running_total)/total)< +#include +#include +#include +#include + // XXX hack for prefetcher class CachePrefetcher; @@ -63,6 +69,14 @@ struct ClockDomainInfo { lock_t lock; }; + + +struct container_hash { + std::size_t operator()(const std::vector& c) const { + return boost::hash_range(c.begin(), c.end()); + } +}; + class TimeBreakdownStat; enum ProfileStates { PROF_INIT = 0, @@ -191,6 +205,27 @@ struct GlobSimInfo { //XXX Hack CachePrefetcher* prefetcher; TraceReader** readers; + + //Tanvir BBL (key) to Prefetch Addresses (value) map + bool enable_iprefetch; + std::unordered_map> iprefetch_bbl_to_cl_address_map; + + bool enable_cs_iprefetch; + + std::unordered_map>> cs_iprefetch_bbl_to_predicate_to_cl_address_map; + //std::unordered_map,std::vector,container_hash> cs_iprefetch_bbl_to_predicate_to_cl_address_map; + + bool enable_code_bloat_effect; + std::map prev_to_new_bbl_address_map; + + bool is_first_pass; + bool prefetch_has_lower_replacement_priority; + + uint64_t iprefetch_buffer_size; + + uint64_t asmdb_next_line_count; + uint64_t cs_prefetch_false_positive_rate; + bool measure_branch_cdfs; }; diff --git a/tests/clang.cfg b/tests/clang.cfg new file mode 100644 index 000000000..fb17b2256 --- /dev/null +++ b/tests/clang.cfg @@ -0,0 +1,100 @@ +sys = { + frequency = 2500; + cores = { + westmere = { + type = "OOO"; + cores = 1; + icache = "l1i"; + dcache = "l1d"; + + properties = { + bp_nb = 11; + bp_hb = 18; + bp_lb = 14; + } + }; + }; + + caches = { + l1i = { + caches = 1; + size = 32768; + array = { + type = "SetAssoc"; + ways = 8; + }; + latency = 3; + #Next line prefetcher at L1 access + numLinesNLP = 1; + #Perfect memory, all memory accesses (instructions) have L1 latency + zeroLatencyCache = false; + }; + + l1d = { + caches = 1; + size = 32768; + array = { + type = "SetAssoc"; + ways = 8; + }; + latency = 4; + #Next line prefetcher at L1 access + numLinesNLP = 1; + #Perfect memory, all memory accesses (data) have L1 latency + zeroLatencyCache = false; + }; + + l2 = { + caches = 1; + size = 1048576; + array = { + type = "SetAssoc"; + ways = 16; + }; + latency = 7; + children = "l1i|l1d"; + }; + + l3 = { + caches = 1; + banks = 1; + size = 10485760; + #size = 10485760; + #size = 47185920; + latency = 27; + array = { + type = "SetAssoc"; + hash = "H3"; + ways = 20; + }; + children = "l2"; + }; + }; + + mem = { + latency = 225; + type = "WeaveMD1"; + boundLatency = 225; + bandwidth = 120000; + #latency = 1; + #type = "DDR"; + #controllers = 6; + #tech = "DDR3-1066-CL8"; + }; +}; + +sim = { + maxTotalInstrs = 2000000000L; + maxPhases = 1000000L; + strictConfig = false; + phaseLength = 10000; + enable_iprefetch = true; + iprefetch_bbl_to_cl_address_map = "/home/takh/git-repos/zsim-fork/prefetch_target"; #prefetch info file +}; + +#memtrace trace: +trace0 ="/mnt/storage/takh/erie/test/dynamorio/build/tmp/drmemtrace.clang-9.31438.5729.dir/trace/drmemtrace.clang-9.31438.4402.trace.gz"; +#modules.txt file: +trace_binaries = "/mnt/storage/takh/erie/test/dynamorio/build/tmp/drmemtrace.clang-9.31438.5729.dir/raw/"; +#type memtrace or YT: +trace_type = "MEMTRACE";