/* * Copyright (c) 2012-2013 ARM Limited * All rights reserved. * * The license below extends only to copyright in the software and shall * not be construed as granting a license to any other intellectual * property including but not limited to intellectual property relating * to a hardware implementation of the functionality of the software * licensed hereunder. You may use the software subject to the license * terms below provided that you ensure that this notice is replicated * unmodified and in its entirety in all distributions of the software, * modified or unmodified, in source code or in binary form. * * Copyright (c) 2003-2005 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * 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; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. * * Authors: Erik Hallnor */ /** * @file * Definition of BaseCache functions. */ #include "mem/cache/base.hh" #include "debug/Cache.hh" #include "debug/Drain.hh" #include "mem/cache/cache.hh" #include "mem/cache/mshr.hh" #include "mem/cache/tags/fa_lru.hh" #include "sim/full_system.hh" using namespace std; BaseCache::CacheSlavePort::CacheSlavePort(const std::string &_name, BaseCache *_cache, const std::string &_label) : QueuedSlavePort(_name, _cache, queue), queue(*_cache, *this, _label), blocked(false), mustSendRetry(false), sendRetryEvent([this]{ processSendRetry(); }, _name) { } BaseCache::BaseCache(const BaseCacheParams *p, unsigned blk_size) : MemObject(p), cpuSidePort(nullptr), memSidePort(nullptr), mshrQueue("MSHRs", p->mshrs, 0, p->demand_mshr_reserve), // see below writeBuffer("write buffer", p->write_buffers, p->mshrs), // see below blkSize(blk_size), lookupLatency(p->tag_latency), dataLatency(p->data_latency), forwardLatency(p->tag_latency), fillLatency(p->data_latency), responseLatency(p->response_latency), numTarget(p->tgts_per_mshr), forwardSnoops(true), isReadOnly(p->is_read_only), blocked(0), order(0), noTargetMSHR(nullptr), missCount(p->max_miss_count), addrRanges(p->addr_ranges.begin(), p->addr_ranges.end()), system(p->system) { // the MSHR queue has no reserve entries as we check the MSHR // queue on every single allocation, whereas the write queue has // as many reserve entries as we have MSHRs, since every MSHR may // eventually require a writeback, and we do not check the write // buffer before committing to an MSHR // forward snoops is overridden in init() once we can query // whether the connected master is actually snooping or not } void BaseCache::CacheSlavePort::setBlocked() { assert(!blocked); DPRINTF(CachePort, "Port is blocking new requests\n"); blocked = true; // if we already scheduled a retry in this cycle, but it has not yet // happened, cancel it if (sendRetryEvent.scheduled()) { owner.deschedule(sendRetryEvent); DPRINTF(CachePort, "Port descheduled retry\n"); mustSendRetry = true; } } void BaseCache::CacheSlavePort::clearBlocked() { assert(blocked); DPRINTF(CachePort, "Port is accepting new requests\n"); blocked = false; if (mustSendRetry) { // @TODO: need to find a better time (next cycle?) owner.schedule(sendRetryEvent, curTick() + 1); } } void BaseCache::CacheSlavePort::processSendRetry() { DPRINTF(CachePort, "Port is sending retry\n"); // reset the flag and call retry mustSendRetry = false; sendRetryReq(); } void BaseCache::init() { if (!cpuSidePort->isConnected() || !memSidePort->isConnected()) fatal("Cache ports on %s are not connected\n", name()); cpuSidePort->sendRangeChange(); forwardSnoops = cpuSidePort->isSnooping(); } BaseMasterPort & BaseCache::getMasterPort(const std::string &if_name, PortID idx) { if (if_name == "mem_side") { return *memSidePort; } else { return MemObject::getMasterPort(if_name, idx); } } BaseSlavePort & BaseCache::getSlavePort(const std::string &if_name, PortID idx) { if (if_name == "cpu_side") { return *cpuSidePort; } else { return MemObject::getSlavePort(if_name, idx); } } bool BaseCache::inRange(Addr addr) const { for (const auto& r : addrRanges) { if (r.contains(addr)) { return true; } } return false; } void BaseCache::regStats() { MemObject::regStats(); using namespace Stats; // Hit statistics for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); hits[access_idx] .init(system->maxMasters()) .name(name() + "." + cstr + "_hits") .desc("number of " + cstr + " hits") .flags(total | nozero | nonan) ; for (int i = 0; i < system->maxMasters(); i++) { hits[access_idx].subname(i, system->getMasterName(i)); } } // These macros make it easier to sum the right subset of commands and // to change the subset of commands that are considered "demand" vs // "non-demand" #define SUM_DEMAND(s) \ (s[MemCmd::ReadReq] + s[MemCmd::WriteReq] + s[MemCmd::WriteLineReq] + \ s[MemCmd::ReadExReq] + s[MemCmd::ReadCleanReq] + s[MemCmd::ReadSharedReq]) // should writebacks be included here? prior code was inconsistent... #define SUM_NON_DEMAND(s) \ (s[MemCmd::SoftPFReq] + s[MemCmd::HardPFReq]) demandHits .name(name() + ".demand_hits") .desc("number of demand (read+write) hits") .flags(total | nozero | nonan) ; demandHits = SUM_DEMAND(hits); for (int i = 0; i < system->maxMasters(); i++) { demandHits.subname(i, system->getMasterName(i)); } overallHits .name(name() + ".overall_hits") .desc("number of overall hits") .flags(total | nozero | nonan) ; overallHits = demandHits + SUM_NON_DEMAND(hits); for (int i = 0; i < system->maxMasters(); i++) { overallHits.subname(i, system->getMasterName(i)); } // Miss statistics for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); misses[access_idx] .init(system->maxMasters()) .name(name() + "." + cstr + "_misses") .desc("number of " + cstr + " misses") .flags(total | nozero | nonan) ; for (int i = 0; i < system->maxMasters(); i++) { misses[access_idx].subname(i, system->getMasterName(i)); } } demandMisses .name(name() + ".demand_misses") .desc("number of demand (read+write) misses") .flags(total | nozero | nonan) ; demandMisses = SUM_DEMAND(misses); for (int i = 0; i < system->maxMasters(); i++) { demandMisses.subname(i, system->getMasterName(i)); } overallMisses .name(name() + ".overall_misses") .desc("number of overall misses") .flags(total | nozero | nonan) ; overallMisses = demandMisses + SUM_NON_DEMAND(misses); for (int i = 0; i < system->maxMasters(); i++) { overallMisses.subname(i, system->getMasterName(i)); } // Miss latency statistics for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); missLatency[access_idx] .init(system->maxMasters()) .name(name() + "." + cstr + "_miss_latency") .desc("number of " + cstr + " miss cycles") .flags(total | nozero | nonan) ; for (int i = 0; i < system->maxMasters(); i++) { missLatency[access_idx].subname(i, system->getMasterName(i)); } } demandMissLatency .name(name() + ".demand_miss_latency") .desc("number of demand (read+write) miss cycles") .flags(total | nozero | nonan) ; demandMissLatency = SUM_DEMAND(missLatency); for (int i = 0; i < system->maxMasters(); i++) { demandMissLatency.subname(i, system->getMasterName(i)); } overallMissLatency .name(name() + ".overall_miss_latency") .desc("number of overall miss cycles") .flags(total | nozero | nonan) ; overallMissLatency = demandMissLatency + SUM_NON_DEMAND(missLatency); for (int i = 0; i < system->maxMasters(); i++) { overallMissLatency.subname(i, system->getMasterName(i)); } // access formulas for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); accesses[access_idx] .name(name() + "." + cstr + "_accesses") .desc("number of " + cstr + " accesses(hits+misses)") .flags(total | nozero | nonan) ; accesses[access_idx] = hits[access_idx] + misses[access_idx]; for (int i = 0; i < system->maxMasters(); i++) { accesses[access_idx].subname(i, system->getMasterName(i)); } } demandAccesses .name(name() + ".demand_accesses") .desc("number of demand (read+write) accesses") .flags(total | nozero | nonan) ; demandAccesses = demandHits + demandMisses; for (int i = 0; i < system->maxMasters(); i++) { demandAccesses.subname(i, system->getMasterName(i)); } overallAccesses .name(name() + ".overall_accesses") .desc("number of overall (read+write) accesses") .flags(total | nozero | nonan) ; overallAccesses = overallHits + overallMisses; for (int i = 0; i < system->maxMasters(); i++) { overallAccesses.subname(i, system->getMasterName(i)); } // miss rate formulas for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); missRate[access_idx] .name(name() + "." + cstr + "_miss_rate") .desc("miss rate for " + cstr + " accesses") .flags(total | nozero | nonan) ; missRate[access_idx] = misses[access_idx] / accesses[access_idx]; for (int i = 0; i < system->maxMasters(); i++) { missRate[access_idx].subname(i, system->getMasterName(i)); } } demandMissRate .name(name() + ".demand_miss_rate") .desc("miss rate for demand accesses") .flags(total | nozero | nonan) ; demandMissRate = demandMisses / demandAccesses; for (int i = 0; i < system->maxMasters(); i++) { demandMissRate.subname(i, system->getMasterName(i)); } overallMissRate .name(name() + ".overall_miss_rate") .desc("miss rate for overall accesses") .flags(total | nozero | nonan) ; overallMissRate = overallMisses / overallAccesses; for (int i = 0; i < system->maxMasters(); i++) { overallMissRate.subname(i, system->getMasterName(i)); } // miss latency formulas for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); avgMissLatency[access_idx] .name(name() + "." + cstr + "_avg_miss_latency") .desc("average " + cstr + " miss latency") .flags(total | nozero | nonan) ; avgMissLatency[access_idx] = missLatency[access_idx] / misses[access_idx]; for (int i = 0; i < system->maxMasters(); i++) { avgMissLatency[access_idx].subname(i, system->getMasterName(i)); } } demandAvgMissLatency .name(name() + ".demand_avg_miss_latency") .desc("average overall miss latency") .flags(total | nozero | nonan) ; demandAvgMissLatency = demandMissLatency / demandMisses; for (int i = 0; i < system->maxMasters(); i++) { demandAvgMissLatency.subname(i, system->getMasterName(i)); } overallAvgMissLatency .name(name() + ".overall_avg_miss_latency") .desc("average overall miss latency") .flags(total | nozero | nonan) ; overallAvgMissLatency = overallMissLatency / overallMisses; for (int i = 0; i < system->maxMasters(); i++) { overallAvgMissLatency.subname(i, system->getMasterName(i)); } blocked_cycles.init(NUM_BLOCKED_CAUSES); blocked_cycles .name(name() + ".blocked_cycles") .desc("number of cycles access was blocked") .subname(Blocked_NoMSHRs, "no_mshrs") .subname(Blocked_NoTargets, "no_targets") ; blocked_causes.init(NUM_BLOCKED_CAUSES); blocked_causes .name(name() + ".blocked") .desc("number of cycles access was blocked") .subname(Blocked_NoMSHRs, "no_mshrs") .subname(Blocked_NoTargets, "no_targets") ; avg_blocked .name(name() + ".avg_blocked_cycles") .desc("average number of cycles each access was blocked") .subname(Blocked_NoMSHRs, "no_mshrs") .subname(Blocked_NoTargets, "no_targets") ; avg_blocked = blocked_cycles / blocked_causes; unusedPrefetches .name(name() + ".unused_prefetches") .desc("number of HardPF blocks evicted w/o reference") .flags(nozero) ; writebacks .init(system->maxMasters()) .name(name() + ".writebacks") .desc("number of writebacks") .flags(total | nozero | nonan) ; for (int i = 0; i < system->maxMasters(); i++) { writebacks.subname(i, system->getMasterName(i)); } // MSHR statistics // MSHR hit statistics for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); mshr_hits[access_idx] .init(system->maxMasters()) .name(name() + "." + cstr + "_mshr_hits") .desc("number of " + cstr + " MSHR hits") .flags(total | nozero | nonan) ; for (int i = 0; i < system->maxMasters(); i++) { mshr_hits[access_idx].subname(i, system->getMasterName(i)); } } demandMshrHits .name(name() + ".demand_mshr_hits") .desc("number of demand (read+write) MSHR hits") .flags(total | nozero | nonan) ; demandMshrHits = SUM_DEMAND(mshr_hits); for (int i = 0; i < system->maxMasters(); i++) { demandMshrHits.subname(i, system->getMasterName(i)); } overallMshrHits .name(name() + ".overall_mshr_hits") .desc("number of overall MSHR hits") .flags(total | nozero | nonan) ; overallMshrHits = demandMshrHits + SUM_NON_DEMAND(mshr_hits); for (int i = 0; i < system->maxMasters(); i++) { overallMshrHits.subname(i, system->getMasterName(i)); } // MSHR miss statistics for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); mshr_misses[access_idx] .init(system->maxMasters()) .name(name() + "." + cstr + "_mshr_misses") .desc("number of " + cstr + " MSHR misses") .flags(total | nozero | nonan) ; for (int i = 0; i < system->maxMasters(); i++) { mshr_misses[access_idx].subname(i, system->getMasterName(i)); } } demandMshrMisses .name(name() + ".demand_mshr_misses") .desc("number of demand (read+write) MSHR misses") .flags(total | nozero | nonan) ; demandMshrMisses = SUM_DEMAND(mshr_misses); for (int i = 0; i < system->maxMasters(); i++) { demandMshrMisses.subname(i, system->getMasterName(i)); } overallMshrMisses .name(name() + ".overall_mshr_misses") .desc("number of overall MSHR misses") .flags(total | nozero | nonan) ; overallMshrMisses = demandMshrMisses + SUM_NON_DEMAND(mshr_misses); for (int i = 0; i < system->maxMasters(); i++) { overallMshrMisses.subname(i, system->getMasterName(i)); } // MSHR miss latency statistics for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); mshr_miss_latency[access_idx] .init(system->maxMasters()) .name(name() + "." + cstr + "_mshr_miss_latency") .desc("number of " + cstr + " MSHR miss cycles") .flags(total | nozero | nonan) ; for (int i = 0; i < system->maxMasters(); i++) { mshr_miss_latency[access_idx].subname(i, system->getMasterName(i)); } } demandMshrMissLatency .name(name() + ".demand_mshr_miss_latency") .desc("number of demand (read+write) MSHR miss cycles") .flags(total | nozero | nonan) ; demandMshrMissLatency = SUM_DEMAND(mshr_miss_latency); for (int i = 0; i < system->maxMasters(); i++) { demandMshrMissLatency.subname(i, system->getMasterName(i)); } overallMshrMissLatency .name(name() + ".overall_mshr_miss_latency") .desc("number of overall MSHR miss cycles") .flags(total | nozero | nonan) ; overallMshrMissLatency = demandMshrMissLatency + SUM_NON_DEMAND(mshr_miss_latency); for (int i = 0; i < system->maxMasters(); i++) { overallMshrMissLatency.subname(i, system->getMasterName(i)); } // MSHR uncacheable statistics for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); mshr_uncacheable[access_idx] .init(system->maxMasters()) .name(name() + "." + cstr + "_mshr_uncacheable") .desc("number of " + cstr + " MSHR uncacheable") .flags(total | nozero | nonan) ; for (int i = 0; i < system->maxMasters(); i++) { mshr_uncacheable[access_idx].subname(i, system->getMasterName(i)); } } overallMshrUncacheable .name(name() + ".overall_mshr_uncacheable_misses") .desc("number of overall MSHR uncacheable misses") .flags(total | nozero | nonan) ; overallMshrUncacheable = SUM_DEMAND(mshr_uncacheable) + SUM_NON_DEMAND(mshr_uncacheable); for (int i = 0; i < system->maxMasters(); i++) { overallMshrUncacheable.subname(i, system->getMasterName(i)); } // MSHR miss latency statistics for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); mshr_uncacheable_lat[access_idx] .init(system->maxMasters()) .name(name() + "." + cstr + "_mshr_uncacheable_latency") .desc("number of " + cstr + " MSHR uncacheable cycles") .flags(total | nozero | nonan) ; for (int i = 0; i < system->maxMasters(); i++) { mshr_uncacheable_lat[access_idx].subname( i, system->getMasterName(i)); } } overallMshrUncacheableLatency .name(name() + ".overall_mshr_uncacheable_latency") .desc("number of overall MSHR uncacheable cycles") .flags(total | nozero | nonan) ; overallMshrUncacheableLatency = SUM_DEMAND(mshr_uncacheable_lat) + SUM_NON_DEMAND(mshr_uncacheable_lat); for (int i = 0; i < system->maxMasters(); i++) { overallMshrUncacheableLatency.subname(i, system->getMasterName(i)); } #if 0 // MSHR access formulas for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); mshrAccesses[access_idx] .name(name() + "." + cstr + "_mshr_accesses") .desc("number of " + cstr + " mshr accesses(hits+misses)") .flags(total | nozero | nonan) ; mshrAccesses[access_idx] = mshr_hits[access_idx] + mshr_misses[access_idx] + mshr_uncacheable[access_idx]; } demandMshrAccesses .name(name() + ".demand_mshr_accesses") .desc("number of demand (read+write) mshr accesses") .flags(total | nozero | nonan) ; demandMshrAccesses = demandMshrHits + demandMshrMisses; overallMshrAccesses .name(name() + ".overall_mshr_accesses") .desc("number of overall (read+write) mshr accesses") .flags(total | nozero | nonan) ; overallMshrAccesses = overallMshrHits + overallMshrMisses + overallMshrUncacheable; #endif // MSHR miss rate formulas for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); mshrMissRate[access_idx] .name(name() + "." + cstr + "_mshr_miss_rate") .desc("mshr miss rate for " + cstr + " accesses") .flags(total | nozero | nonan) ; mshrMissRate[access_idx] = mshr_misses[access_idx] / accesses[access_idx]; for (int i = 0; i < system->maxMasters(); i++) { mshrMissRate[access_idx].subname(i, system->getMasterName(i)); } } demandMshrMissRate .name(name() + ".demand_mshr_miss_rate") .desc("mshr miss rate for demand accesses") .flags(total | nozero | nonan) ; demandMshrMissRate = demandMshrMisses / demandAccesses; for (int i = 0; i < system->maxMasters(); i++) { demandMshrMissRate.subname(i, system->getMasterName(i)); } overallMshrMissRate .name(name() + ".overall_mshr_miss_rate") .desc("mshr miss rate for overall accesses") .flags(total | nozero | nonan) ; overallMshrMissRate = overallMshrMisses / overallAccesses; for (int i = 0; i < system->maxMasters(); i++) { overallMshrMissRate.subname(i, system->getMasterName(i)); } // mshrMiss latency formulas for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); avgMshrMissLatency[access_idx] .name(name() + "." + cstr + "_avg_mshr_miss_latency") .desc("average " + cstr + " mshr miss latency") .flags(total | nozero | nonan) ; avgMshrMissLatency[access_idx] = mshr_miss_latency[access_idx] / mshr_misses[access_idx]; for (int i = 0; i < system->maxMasters(); i++) { avgMshrMissLatency[access_idx].subname( i, system->getMasterName(i)); } } demandAvgMshrMissLatency .name(name() + ".demand_avg_mshr_miss_latency") .desc("average overall mshr miss latency") .flags(total | nozero | nonan) ; demandAvgMshrMissLatency = demandMshrMissLatency / demandMshrMisses; for (int i = 0; i < system->maxMasters(); i++) { demandAvgMshrMissLatency.subname(i, system->getMasterName(i)); } overallAvgMshrMissLatency .name(name() + ".overall_avg_mshr_miss_latency") .desc("average overall mshr miss latency") .flags(total | nozero | nonan) ; overallAvgMshrMissLatency = overallMshrMissLatency / overallMshrMisses; for (int i = 0; i < system->maxMasters(); i++) { overallAvgMshrMissLatency.subname(i, system->getMasterName(i)); } // mshrUncacheable latency formulas for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) { MemCmd cmd(access_idx); const string &cstr = cmd.toString(); avgMshrUncacheableLatency[access_idx] .name(name() + "." + cstr + "_avg_mshr_uncacheable_latency") .desc("average " + cstr + " mshr uncacheable latency") .flags(total | nozero | nonan) ; avgMshrUncacheableLatency[access_idx] = mshr_uncacheable_lat[access_idx] / mshr_uncacheable[access_idx]; for (int i = 0; i < system->maxMasters(); i++) { avgMshrUncacheableLatency[access_idx].subname( i, system->getMasterName(i)); } } overallAvgMshrUncacheableLatency .name(name() + ".overall_avg_mshr_uncacheable_latency") .desc("average overall mshr uncacheable latency") .flags(total | nozero | nonan) ; overallAvgMshrUncacheableLatency = overallMshrUncacheableLatency / overallMshrUncacheable; for (int i = 0; i < system->maxMasters(); i++) { overallAvgMshrUncacheableLatency.subname(i, system->getMasterName(i)); } }