/* * Copyright (c) 2003 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. */ #include #include #include #include "base/statistics.hh" #include "base/trace.hh" #include "cpu/exec_context.hh" #include "kern/kernel_stats.hh" #include "sim/stats.hh" #include "sim/sw_context.hh" #include "targetarch/isa_traits.hh" #include "targetarch/osfpal.hh" #include "targetarch/syscalls.hh" using namespace std; using namespace Stats; class KSData { private: string _name; ExecContext *xc; BaseCPU *cpu; public: KSData(ExecContext *_xc, BaseCPU *_cpu) : xc(_xc), cpu(_cpu), iplLast(0), iplLastTick(0), lastUser(false), lastModeTick(0) {} const string &name() { return _name; } void regStats(const string &name); public: Scalar<> _arm; Scalar<> _quiesce; Scalar<> _ivlb; Scalar<> _ivle; Scalar<> _hwrei; Vector<> _iplCount; Vector<> _iplGood; Vector<> _iplTicks; Formula _iplUsed; Vector<> _callpal; Vector<> _syscall; Vector<> _faults; Vector<> _mode; Vector<> _modeGood; Formula _modeFraction; Vector<> _modeTicks; Scalar<> _swap_context; private: int iplLast; Tick iplLastTick; bool lastUser; Tick lastModeTick; public: void swpipl(int ipl); void mode(bool user); void callpal(int code); }; KernelStats::KernelStats(ExecContext *xc, BaseCPU *cpu) { data = new KSData(xc, cpu); } KernelStats::~KernelStats() { delete data; } void KernelStats::regStats(const string &name) { data->regStats(name); } void KSData::regStats(const string &name) { _name = name; _arm .name(name + ".inst.arm") .desc("number of arm instructions executed") ; _quiesce .name(name + ".inst.quiesce") .desc("number of quiesce instructions executed") ; _ivlb .name(name + ".inst.ivlb") .desc("number of ivlb instructions executed") ; _ivle .name(name + ".inst.ivle") .desc("number of ivle instructions executed") ; _hwrei .name(name + ".inst.hwrei") .desc("number of hwrei instructions executed") ; _iplCount .init(32) .name(name + ".ipl_count") .desc("number of times we switched to this ipl") .flags(total | pdf | nozero | nonan) ; _iplGood .init(32) .name(name + ".ipl_good") .desc("number of times we switched to this ipl from a different ipl") .flags(total | pdf | nozero | nonan) ; _iplTicks .init(32) .name(name + ".ipl_ticks") .desc("number of cycles we spent at this ipl") .flags(total | pdf | nozero | nonan) ; _iplUsed .name(name + ".ipl_used") .desc("fraction of swpipl calls that actually changed the ipl") .flags(total | nozero | nonan) ; _iplUsed = _iplGood / _iplCount; _callpal .init(256) .name(name + ".callpal") .desc("number of callpals executed") .flags(total | pdf | nozero | nonan) ; for (int i = 0; i < PAL::NumCodes; ++i) { const char *str = PAL::name(i); if (str) _callpal.subname(i, str); } _syscall .init(SystemCalls::Number) .name(name + ".syscall") .desc("number of syscalls executed") .flags(total | pdf | nozero | nonan) ; for (int i = 0; i < SystemCalls::Number; ++i) { const char *str = SystemCalls::name(i); if (str) { _syscall.subname(i, str); } } _faults .init(Num_Faults) .name(name + ".faults") .desc("number of faults") .flags(total | pdf | nozero | nonan) ; for (int i = 1; i < Num_Faults; ++i) { const char *str = FaultName(i); if (str) _faults.subname(i, str); } _mode .init(2) .name(name + ".mode_switch") .subname(0, "kernel") .subname(1, "user") .desc("number of protection mode switches") ; _modeGood .init(2) ; _modeFraction .name(name + ".mode_switch_good") .subname(0, "kernel") .subname(1, "user") .desc("fraction of useful protection mode switches") .flags(total) ; _modeFraction = _modeGood / _mode; _modeTicks .init(2) .name(name + ".mode_ticks") .subname(0, "kernel") .subname(1, "user") .desc("number of ticks spent at the given mode") .flags(pdf) ; _swap_context .name(name + ".swap_context") .desc("number of times the context was actually changed") ; } void KernelStats::arm() { data->_arm++; } void KernelStats::quiesce() { data->_quiesce++; } void KernelStats::ivlb() { data->_ivlb++; } void KernelStats::ivle() { data->_ivle++; } void KernelStats::hwrei() { data->_hwrei++; } void KernelStats::fault(Fault fault) { data->_faults[fault]++; } void KernelStats::swpipl(int ipl) { data->swpipl(ipl); } void KernelStats::mode(bool user) { data->mode(user); } void KernelStats::context(Addr old_pcbb, Addr new_pcbb) { data->_swap_context++; } void KernelStats::callpal(int code) { data->callpal(code); } void KSData::swpipl(int ipl) { assert(ipl >= 0 && ipl <= 0x1f && "invalid IPL\n"); _iplCount[ipl]++; if (ipl == iplLast) return; _iplGood[ipl]++; _iplTicks[iplLast] += curTick - iplLastTick; iplLastTick = curTick; iplLast = ipl; } void KSData::mode(bool user) { _mode[user]++; if (user == lastUser) return; _modeGood[user]++; _modeTicks[lastUser] += curTick - lastModeTick; lastModeTick = curTick; lastUser = user; if (xc->system->bin) { if (!xc->swCtx || xc->swCtx->callStack.empty()) { if (user) xc->system->User->activate(); else xc->system->Kernel->activate(); } } } void KSData::callpal(int code) { if (!PAL::name(code)) return; _callpal[code]++; switch (code) { case PAL::callsys: { int number = xc->regs.intRegFile[0]; if (SystemCalls::validSyscallNumber(number)) { int cvtnum = SystemCalls::convert(number); _syscall[cvtnum]++; } } break; } if (code == PAL::swpctx) { SWContext *out = xc->swCtx; System *sys = xc->system; if (!sys->bin) return; DPRINTF(TCPIP, "swpctx event\n"); if (out) { DPRINTF(TCPIP, "swapping context out with this stack!\n"); xc->system->dumpState(xc); Addr oldPCB = xc->regs.ipr[TheISA::IPR_PALtemp23]; if (out->callStack.empty()) { DPRINTF(TCPIP, "but removing it, cuz empty!\n"); SWContext *find = sys->findContext(oldPCB); if (find) { assert(sys->findContext(oldPCB) == out); sys->remContext(oldPCB); } delete out; } else { DPRINTF(TCPIP, "switching out context with pcb %#x, top fn %s\n", oldPCB, out->callStack.top()->name); if (!sys->findContext(oldPCB)) { if (!sys->addContext(oldPCB, out)) panic("could not add context"); } } } Addr newPCB = xc->regs.intRegFile[16]; SWContext *in = sys->findContext(newPCB); xc->swCtx = in; if (in) { assert(!in->callStack.empty() && "should not be switching in empty context"); DPRINTF(TCPIP, "swapping context in with this callstack!\n"); xc->system->dumpState(xc); sys->remContext(newPCB); fnCall *top = in->callStack.top(); DPRINTF(TCPIP, "switching in to pcb %#x, %s\n", newPCB, top->name); assert(top->myBin && "should not switch to context with no Bin"); top->myBin->activate(); } else { sys->Kernel->activate(); } DPRINTF(TCPIP, "end swpctx\n"); } }