Add new function profiling stuff, wrap the pc_sample stuff into it.
SConscript: Get rid of the pc_sample stuff and move to the new profiling stuff base/traceflags.py: DPRINTF Stack stuff cpu/base.cc: cpu/base.hh: cpu/exec_context.cc: cpu/exec_context.hh: cpu/simple/cpu.cc: Add profiling stuff kern/kernel_stats.hh: Use a smart pointer sim/system.cc: sim/system.hh: Create a new symbol table that has all of the symbols for a particular system util/stats/categories.py: change around the categories, add categories for function profiling stuff util/stats/profile.py: No profile parsing and display code to deal with function profiling stuff, graph, dot, and text outputs. --HG-- extra : convert_revision : b3de0cdc8bd468e42647966e2640ae009bda9eb8
This commit is contained in:
parent
357ee7a845
commit
a81c03737a
16 changed files with 1533 additions and 330 deletions
|
@ -143,7 +143,6 @@ base_sources = Split('''
|
||||||
encumbered/cpu/full/issue.cc
|
encumbered/cpu/full/issue.cc
|
||||||
encumbered/cpu/full/ls_queue.cc
|
encumbered/cpu/full/ls_queue.cc
|
||||||
encumbered/cpu/full/machine_queue.cc
|
encumbered/cpu/full/machine_queue.cc
|
||||||
encumbered/cpu/full/pc_sample_profile.cc
|
|
||||||
encumbered/cpu/full/pipetrace.cc
|
encumbered/cpu/full/pipetrace.cc
|
||||||
encumbered/cpu/full/readyq.cc
|
encumbered/cpu/full/readyq.cc
|
||||||
encumbered/cpu/full/reg_info.cc
|
encumbered/cpu/full/reg_info.cc
|
||||||
|
@ -241,6 +240,7 @@ full_system_sources = Split('''
|
||||||
arch/alpha/ev5.cc
|
arch/alpha/ev5.cc
|
||||||
arch/alpha/osfpal.cc
|
arch/alpha/osfpal.cc
|
||||||
arch/alpha/pseudo_inst.cc
|
arch/alpha/pseudo_inst.cc
|
||||||
|
arch/alpha/stacktrace.cc
|
||||||
arch/alpha/vtophys.cc
|
arch/alpha/vtophys.cc
|
||||||
|
|
||||||
base/crc.cc
|
base/crc.cc
|
||||||
|
@ -248,6 +248,7 @@ full_system_sources = Split('''
|
||||||
base/remote_gdb.cc
|
base/remote_gdb.cc
|
||||||
|
|
||||||
cpu/intr_control.cc
|
cpu/intr_control.cc
|
||||||
|
cpu/profile.cc
|
||||||
|
|
||||||
dev/alpha_console.cc
|
dev/alpha_console.cc
|
||||||
dev/baddev.cc
|
dev/baddev.cc
|
||||||
|
@ -345,6 +346,7 @@ targetarch_files = Split('''
|
||||||
isa_traits.hh
|
isa_traits.hh
|
||||||
osfpal.hh
|
osfpal.hh
|
||||||
pseudo_inst.hh
|
pseudo_inst.hh
|
||||||
|
stacktrace.hh
|
||||||
vptr.hh
|
vptr.hh
|
||||||
vtophys.hh
|
vtophys.hh
|
||||||
''')
|
''')
|
||||||
|
|
324
arch/alpha/stacktrace.cc
Normal file
324
arch/alpha/stacktrace.cc
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "arch/alpha/isa_traits.hh"
|
||||||
|
#include "arch/alpha/stacktrace.hh"
|
||||||
|
#include "arch/alpha/vtophys.hh"
|
||||||
|
#include "base/bitfield.hh"
|
||||||
|
#include "base/trace.hh"
|
||||||
|
#include "cpu/base.hh"
|
||||||
|
#include "cpu/exec_context.hh"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
ProcessInfo::ProcessInfo(ExecContext *_xc)
|
||||||
|
: xc(_xc)
|
||||||
|
{
|
||||||
|
Addr addr = 0;
|
||||||
|
|
||||||
|
if (!xc->system->kernelSymtab->findAddress("thread_info_size", addr))
|
||||||
|
panic("thread info not compiled into kernel\n");
|
||||||
|
thread_info_size = *(int32_t *)vtomem(xc, addr, sizeof(int32_t));
|
||||||
|
|
||||||
|
if (!xc->system->kernelSymtab->findAddress("task_struct_size", addr))
|
||||||
|
panic("thread info not compiled into kernel\n");
|
||||||
|
task_struct_size = *(int32_t *)vtomem(xc, addr, sizeof(int32_t));
|
||||||
|
|
||||||
|
if (!xc->system->kernelSymtab->findAddress("thread_info_task", addr))
|
||||||
|
panic("thread info not compiled into kernel\n");
|
||||||
|
task_off = *(int32_t *)vtomem(xc, addr, sizeof(int32_t));
|
||||||
|
|
||||||
|
if (!xc->system->kernelSymtab->findAddress("task_struct_pid", addr))
|
||||||
|
panic("thread info not compiled into kernel\n");
|
||||||
|
pid_off = *(int32_t *)vtomem(xc, addr, sizeof(int32_t));
|
||||||
|
|
||||||
|
if (!xc->system->kernelSymtab->findAddress("task_struct_comm", addr))
|
||||||
|
panic("thread info not compiled into kernel\n");
|
||||||
|
name_off = *(int32_t *)vtomem(xc, addr, sizeof(int32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
Addr
|
||||||
|
ProcessInfo::task(Addr ksp) const
|
||||||
|
{
|
||||||
|
Addr base = ksp & ~0x3fff;
|
||||||
|
if (base == ULL(0xfffffc0000000000))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Addr task;
|
||||||
|
CopyOut(xc, &task, base + task_off, sizeof(task));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ProcessInfo::pid(Addr ksp) const
|
||||||
|
{
|
||||||
|
Addr task = this->task(ksp);
|
||||||
|
if (!task)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
uint16_t pid;
|
||||||
|
CopyOut(xc, &pid, task + pid_off, sizeof(pid));
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
ProcessInfo::name(Addr ksp) const
|
||||||
|
{
|
||||||
|
Addr task = this->task(ksp);
|
||||||
|
if (!task)
|
||||||
|
return "console";
|
||||||
|
|
||||||
|
char comm[256];
|
||||||
|
CopyString(xc, comm, task + name_off, sizeof(comm));
|
||||||
|
if (!comm[0])
|
||||||
|
return "startup";
|
||||||
|
|
||||||
|
return comm;
|
||||||
|
}
|
||||||
|
|
||||||
|
StackTrace::StackTrace(ExecContext *_xc, bool is_call)
|
||||||
|
: xc(_xc)
|
||||||
|
{
|
||||||
|
bool usermode = (xc->regs.ipr[AlphaISA::IPR_DTB_CM] & 0x18) != 0;
|
||||||
|
|
||||||
|
Addr pc = xc->regs.npc;
|
||||||
|
bool kernel = xc->system->kernelStart <= pc && pc <= xc->system->kernelEnd;
|
||||||
|
|
||||||
|
if (usermode) {
|
||||||
|
stack.push_back(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kernel) {
|
||||||
|
stack.push_back(2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolTable *symtab = xc->system->allSymtab;
|
||||||
|
Addr ksp = xc->regs.intRegFile[TheISA::StackPointerReg];
|
||||||
|
Addr bottom = ksp & ~0x3fff;
|
||||||
|
Addr addr;
|
||||||
|
|
||||||
|
if (is_call) {
|
||||||
|
if (!symtab->findNearestAddr(pc, addr))
|
||||||
|
panic("could not find address %#x", pc);
|
||||||
|
|
||||||
|
stack.push_back(addr);
|
||||||
|
pc = xc->regs.pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Addr ra;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
while (ksp > bottom) {
|
||||||
|
if (!symtab->findNearestAddr(pc, addr))
|
||||||
|
panic("could not find symbol for pc=%#x", pc);
|
||||||
|
assert(pc >= addr && "symbol botch: callpc < func");
|
||||||
|
|
||||||
|
stack.push_back(addr);
|
||||||
|
|
||||||
|
if (isEntry(addr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (decodePrologue(ksp, pc, addr, size, ra)) {
|
||||||
|
if (!ra)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pc = ra;
|
||||||
|
ksp += size;
|
||||||
|
} else {
|
||||||
|
stack.push_back(3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool kernel = xc->system->kernelStart <= pc &&
|
||||||
|
pc <= xc->system->kernelEnd;
|
||||||
|
if (!kernel)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unwinding too far");
|
||||||
|
}
|
||||||
|
|
||||||
|
StackTrace::~StackTrace()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
StackTrace::isEntry(Addr addr)
|
||||||
|
{
|
||||||
|
if (addr == xc->regs.ipr[AlphaISA::IPR_PALtemp12])
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (addr == xc->regs.ipr[AlphaISA::IPR_PALtemp7])
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (addr == xc->regs.ipr[AlphaISA::IPR_PALtemp11])
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (addr == xc->regs.ipr[AlphaISA::IPR_PALtemp21])
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (addr == xc->regs.ipr[AlphaISA::IPR_PALtemp9])
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (addr == xc->regs.ipr[AlphaISA::IPR_PALtemp2])
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
StackTrace::decodeStack(MachInst inst, int &disp)
|
||||||
|
{
|
||||||
|
// lda $sp, -disp($sp)
|
||||||
|
//
|
||||||
|
// Opcode<31:26> == 0x08
|
||||||
|
// RA<25:21> == 30
|
||||||
|
// RB<20:16> == 30
|
||||||
|
// Disp<15:0>
|
||||||
|
const MachInst mem_mask = 0xffff0000;
|
||||||
|
const MachInst lda_pattern = 0x23de0000;
|
||||||
|
const MachInst lda_disp_mask = 0x0000ffff;
|
||||||
|
|
||||||
|
// subq $sp, disp, $sp
|
||||||
|
// addq $sp, disp, $sp
|
||||||
|
//
|
||||||
|
// Opcode<31:26> == 0x10
|
||||||
|
// RA<25:21> == 30
|
||||||
|
// Lit<20:13>
|
||||||
|
// One<12> = 1
|
||||||
|
// Func<11:5> == 0x20 (addq)
|
||||||
|
// Func<11:5> == 0x29 (subq)
|
||||||
|
// RC<4:0> == 30
|
||||||
|
const MachInst intop_mask = 0xffe01fff;
|
||||||
|
const MachInst addq_pattern = 0x43c0141e;
|
||||||
|
const MachInst subq_pattern = 0x43c0153e;
|
||||||
|
const MachInst intop_disp_mask = 0x001fe000;
|
||||||
|
const int intop_disp_shift = 13;
|
||||||
|
|
||||||
|
if ((inst & mem_mask) == lda_pattern)
|
||||||
|
disp = -sext<16>(inst & lda_disp_mask);
|
||||||
|
else if ((inst & intop_mask) == addq_pattern)
|
||||||
|
disp = -int((inst & intop_disp_mask) >> intop_disp_shift);
|
||||||
|
else if ((inst & intop_mask) == subq_pattern)
|
||||||
|
disp = int((inst & intop_disp_mask) >> intop_disp_shift);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
StackTrace::decodeSave(MachInst inst, int ®, int &disp)
|
||||||
|
{
|
||||||
|
// lda $stq, disp($sp)
|
||||||
|
//
|
||||||
|
// Opcode<31:26> == 0x08
|
||||||
|
// RA<25:21> == ?
|
||||||
|
// RB<20:16> == 30
|
||||||
|
// Disp<15:0>
|
||||||
|
const MachInst stq_mask = 0xfc1f0000;
|
||||||
|
const MachInst stq_pattern = 0xb41e0000;
|
||||||
|
const MachInst stq_disp_mask = 0x0000ffff;
|
||||||
|
const MachInst reg_mask = 0x03e00000;
|
||||||
|
const int reg_shift = 21;
|
||||||
|
|
||||||
|
if ((inst & stq_mask) == stq_pattern) {
|
||||||
|
reg = (inst & reg_mask) >> reg_shift;
|
||||||
|
disp = sext<16>(inst & stq_disp_mask);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode the function prologue for the function we're in, and note
|
||||||
|
* which registers are stored where, and how large the stack frame is.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
StackTrace::decodePrologue(Addr sp, Addr callpc, Addr func,
|
||||||
|
int &size, Addr &ra)
|
||||||
|
{
|
||||||
|
size = 0;
|
||||||
|
ra = 0;
|
||||||
|
|
||||||
|
for (Addr pc = func; pc < callpc; pc += sizeof(MachInst)) {
|
||||||
|
MachInst inst;
|
||||||
|
CopyOut(xc, (uint8_t *)&inst, pc, sizeof(MachInst));
|
||||||
|
|
||||||
|
int reg, disp;
|
||||||
|
if (decodeStack(inst, disp)) {
|
||||||
|
if (size) {
|
||||||
|
// panic("decoding frame size again");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
size += disp;
|
||||||
|
} else if (decodeSave(inst, reg, disp)) {
|
||||||
|
if (!ra && reg == ReturnAddressReg) {
|
||||||
|
CopyOut(xc, (uint8_t *)&ra, sp + disp, sizeof(Addr));
|
||||||
|
if (!ra) {
|
||||||
|
// panic("no return address value pc=%#x\n", pc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TRACING_ON
|
||||||
|
void
|
||||||
|
StackTrace::dump()
|
||||||
|
{
|
||||||
|
StringWrap name(xc->cpu->name());
|
||||||
|
SymbolTable *symtab = xc->system->allSymtab;
|
||||||
|
|
||||||
|
DPRINTFN("------ Stack ------\n");
|
||||||
|
|
||||||
|
string symbol;
|
||||||
|
for (int i = 0, size = stack.size(); i < size; ++i) {
|
||||||
|
Addr addr = stack[size - i - 1];
|
||||||
|
if (addr == 1)
|
||||||
|
symbol = "user";
|
||||||
|
else if (addr == 2)
|
||||||
|
symbol = "console";
|
||||||
|
else if (addr == 3)
|
||||||
|
symbol = "unknown";
|
||||||
|
else
|
||||||
|
symtab->findSymbol(addr, symbol);
|
||||||
|
|
||||||
|
DPRINTFN("%#x: %s\n", addr, symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
99
arch/alpha/stacktrace.hh
Normal file
99
arch/alpha/stacktrace.hh
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ARCH_ALPHA_STACKTRACE_HH__
|
||||||
|
#define __ARCH_ALPHA_STACKTRACE_HH__
|
||||||
|
|
||||||
|
#include "base/trace.hh"
|
||||||
|
#include "cpu/static_inst.hh"
|
||||||
|
|
||||||
|
class ExecContext;
|
||||||
|
class StackTrace;
|
||||||
|
class SymbolTable;
|
||||||
|
|
||||||
|
class ProcessInfo
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
ExecContext *xc;
|
||||||
|
|
||||||
|
int thread_info_size;
|
||||||
|
int task_struct_size;
|
||||||
|
int task_off;
|
||||||
|
int pid_off;
|
||||||
|
int name_off;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ProcessInfo(ExecContext *_xc);
|
||||||
|
|
||||||
|
Addr task(Addr ksp) const;
|
||||||
|
int pid(Addr ksp) const;
|
||||||
|
std::string name(Addr ksp) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StackTrace
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
ExecContext *xc;
|
||||||
|
std::vector<Addr> stack;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isEntry(Addr addr);
|
||||||
|
bool decodePrologue(Addr sp, Addr callpc, Addr func, int &size, Addr &ra);
|
||||||
|
bool decodeSave(MachInst inst, int ®, int &disp);
|
||||||
|
bool decodeStack(MachInst inst, int &disp);
|
||||||
|
|
||||||
|
public:
|
||||||
|
StackTrace(ExecContext *xc, bool is_call);
|
||||||
|
~StackTrace();
|
||||||
|
|
||||||
|
public:
|
||||||
|
const std::vector<Addr> &getstack() const { return stack; }
|
||||||
|
static StackTrace *create(ExecContext *xc, StaticInstPtr<TheISA> inst);
|
||||||
|
|
||||||
|
#if TRACING_ON
|
||||||
|
private:
|
||||||
|
void dump();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void dprintf() { if (DTRACE(Stack)) dump(); }
|
||||||
|
#else
|
||||||
|
public:
|
||||||
|
void dprintf() {}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
inline StackTrace *
|
||||||
|
StackTrace::create(ExecContext *xc, StaticInstPtr<TheISA> inst)
|
||||||
|
{
|
||||||
|
if (!inst->isCall() && !inst->isReturn())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return new StackTrace(xc, !inst->isReturn());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __ARCH_ALPHA_STACKTRACE_HH__
|
|
@ -140,7 +140,8 @@ baseFlags = [
|
||||||
'FullCPU',
|
'FullCPU',
|
||||||
'CommitRate',
|
'CommitRate',
|
||||||
'OoOCPU',
|
'OoOCPU',
|
||||||
'HWPrefetch'
|
'HWPrefetch',
|
||||||
|
'Stack',
|
||||||
]
|
]
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
42
cpu/base.cc
42
cpu/base.cc
|
@ -142,8 +142,19 @@ BaseCPU::BaseCPU(Params *p)
|
||||||
e->schedule(p->functionTraceStart);
|
e->schedule(p->functionTraceStart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if FULL_SYSTEM
|
||||||
|
profileEvent = NULL;
|
||||||
|
if (params->profile)
|
||||||
|
profileEvent = new ProfileEvent(this, params->profile);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BaseCPU::Params::Params()
|
||||||
|
{
|
||||||
|
#if FULL_SYSTEM
|
||||||
|
profile = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BaseCPU::enableFunctionTrace()
|
BaseCPU::enableFunctionTrace()
|
||||||
|
@ -162,6 +173,16 @@ BaseCPU::init()
|
||||||
registerExecContexts();
|
registerExecContexts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BaseCPU::startup()
|
||||||
|
{
|
||||||
|
#if FULL_SYSTEM
|
||||||
|
if (!params->deferRegistration && profileEvent)
|
||||||
|
profileEvent->schedule(curTick);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BaseCPU::regStats()
|
BaseCPU::regStats()
|
||||||
{
|
{
|
||||||
|
@ -231,11 +252,32 @@ BaseCPU::takeOverFrom(BaseCPU *oldCPU)
|
||||||
for (int i = 0; i < NumInterruptLevels; ++i)
|
for (int i = 0; i < NumInterruptLevels; ++i)
|
||||||
interrupts[i] = oldCPU->interrupts[i];
|
interrupts[i] = oldCPU->interrupts[i];
|
||||||
intstatus = oldCPU->intstatus;
|
intstatus = oldCPU->intstatus;
|
||||||
|
|
||||||
|
for (int i = 0; i < execContexts.size(); ++i)
|
||||||
|
execContexts[i]->profile->clear();
|
||||||
|
|
||||||
|
if (profileEvent)
|
||||||
|
profileEvent->schedule(curTick);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if FULL_SYSTEM
|
#if FULL_SYSTEM
|
||||||
|
BaseCPU::ProfileEvent::ProfileEvent(BaseCPU *_cpu, int _interval)
|
||||||
|
: Event(&mainEventQueue), cpu(_cpu), interval(_interval)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void
|
||||||
|
BaseCPU::ProfileEvent::process()
|
||||||
|
{
|
||||||
|
for (int i = 0, size = cpu->execContexts.size(); i < size; ++i) {
|
||||||
|
ExecContext *xc = cpu->execContexts[i];
|
||||||
|
xc->profile->sample(xc->profileNode, xc->profilePC);
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule(curTick + interval);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BaseCPU::post_interrupt(int int_num, int index)
|
BaseCPU::post_interrupt(int int_num, int index)
|
||||||
{
|
{
|
||||||
|
|
17
cpu/base.hh
17
cpu/base.hh
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
#include "base/statistics.hh"
|
#include "base/statistics.hh"
|
||||||
#include "config/full_system.hh"
|
#include "config/full_system.hh"
|
||||||
|
#include "cpu/profile.hh"
|
||||||
#include "cpu/sampler/sampler.hh"
|
#include "cpu/sampler/sampler.hh"
|
||||||
#include "sim/eventq.hh"
|
#include "sim/eventq.hh"
|
||||||
#include "sim/sim_object.hh"
|
#include "sim/sim_object.hh"
|
||||||
|
@ -76,6 +77,18 @@ class BaseCPU : public SimObject
|
||||||
|
|
||||||
bool check_interrupts() const { return intstatus != 0; }
|
bool check_interrupts() const { return intstatus != 0; }
|
||||||
uint64_t intr_status() const { return intstatus; }
|
uint64_t intr_status() const { return intstatus; }
|
||||||
|
|
||||||
|
class ProfileEvent : public Event
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
BaseCPU *cpu;
|
||||||
|
int interval;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ProfileEvent(BaseCPU *cpu, int interval);
|
||||||
|
void process();
|
||||||
|
};
|
||||||
|
ProfileEvent *profileEvent;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -113,7 +126,10 @@ class BaseCPU : public SimObject
|
||||||
#if FULL_SYSTEM
|
#if FULL_SYSTEM
|
||||||
System *system;
|
System *system;
|
||||||
int cpu_id;
|
int cpu_id;
|
||||||
|
Tick profile;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Params();
|
||||||
};
|
};
|
||||||
|
|
||||||
const Params *params;
|
const Params *params;
|
||||||
|
@ -122,6 +138,7 @@ class BaseCPU : public SimObject
|
||||||
virtual ~BaseCPU();
|
virtual ~BaseCPU();
|
||||||
|
|
||||||
virtual void init();
|
virtual void init();
|
||||||
|
virtual void startup();
|
||||||
virtual void regStats();
|
virtual void regStats();
|
||||||
|
|
||||||
void registerExecContexts();
|
void registerExecContexts();
|
||||||
|
|
|
@ -32,10 +32,15 @@
|
||||||
#include "cpu/exec_context.hh"
|
#include "cpu/exec_context.hh"
|
||||||
|
|
||||||
#if FULL_SYSTEM
|
#if FULL_SYSTEM
|
||||||
|
#include "base/callback.hh"
|
||||||
#include "base/cprintf.hh"
|
#include "base/cprintf.hh"
|
||||||
|
#include "base/output.hh"
|
||||||
|
#include "cpu/profile.hh"
|
||||||
#include "kern/kernel_stats.hh"
|
#include "kern/kernel_stats.hh"
|
||||||
#include "sim/serialize.hh"
|
#include "sim/serialize.hh"
|
||||||
|
#include "sim/sim_exit.hh"
|
||||||
#include "sim/system.hh"
|
#include "sim/system.hh"
|
||||||
|
#include "targetarch/stacktrace.hh"
|
||||||
#else
|
#else
|
||||||
#include "sim/process.hh"
|
#include "sim/process.hh"
|
||||||
#endif
|
#endif
|
||||||
|
@ -51,10 +56,24 @@ ExecContext::ExecContext(BaseCPU *_cpu, int _thread_num, System *_sys,
|
||||||
cpu_id(-1), mem(_mem), itb(_itb), dtb(_dtb), system(_sys),
|
cpu_id(-1), mem(_mem), itb(_itb), dtb(_dtb), system(_sys),
|
||||||
memctrl(_sys->memctrl), physmem(_sys->physmem),
|
memctrl(_sys->memctrl), physmem(_sys->physmem),
|
||||||
kernelBinning(system->kernelBinning), bin(kernelBinning->bin),
|
kernelBinning(system->kernelBinning), bin(kernelBinning->bin),
|
||||||
fnbin(kernelBinning->fnbin), func_exe_inst(0), storeCondFailures(0)
|
fnbin(kernelBinning->fnbin), profile(NULL),
|
||||||
|
func_exe_inst(0), storeCondFailures(0)
|
||||||
{
|
{
|
||||||
kernelStats = new Kernel::Statistics(this);
|
kernelStats = new Kernel::Statistics(this);
|
||||||
memset(®s, 0, sizeof(RegFile));
|
memset(®s, 0, sizeof(RegFile));
|
||||||
|
|
||||||
|
if (cpu->params->profile) {
|
||||||
|
profile = new FunctionProfile(system->allSymtab);
|
||||||
|
Callback *cb =
|
||||||
|
new MakeCallback<ExecContext, &ExecContext::dumpFuncProfile>(this);
|
||||||
|
registerExitCallback(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's fill with a dummy node for now so we don't get a segfault
|
||||||
|
// on the first cycle when there's no node available.
|
||||||
|
static ProfileNode dummyNode;
|
||||||
|
profileNode = &dummyNode;
|
||||||
|
profilePC = 3;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
ExecContext::ExecContext(BaseCPU *_cpu, int _thread_num,
|
ExecContext::ExecContext(BaseCPU *_cpu, int _thread_num,
|
||||||
|
@ -83,6 +102,14 @@ ExecContext::~ExecContext()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if FULL_SYSTEM
|
||||||
|
void
|
||||||
|
ExecContext::dumpFuncProfile()
|
||||||
|
{
|
||||||
|
std::ostream *os = simout.create(csprintf("profile.%s.dat", cpu->name()));
|
||||||
|
profile->dump(this, *os);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
ExecContext::takeOverFrom(ExecContext *oldContext)
|
ExecContext::takeOverFrom(ExecContext *oldContext)
|
||||||
|
@ -106,15 +133,6 @@ ExecContext::takeOverFrom(ExecContext *oldContext)
|
||||||
oldContext->_status = ExecContext::Unallocated;
|
oldContext->_status = ExecContext::Unallocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FULL_SYSTEM
|
|
||||||
void
|
|
||||||
ExecContext::execute(const StaticInstBase *inst)
|
|
||||||
{
|
|
||||||
assert(kernelStats);
|
|
||||||
system->kernelBinning->execute(this, inst);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ExecContext::serialize(ostream &os)
|
ExecContext::serialize(ostream &os)
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,8 +46,9 @@ class BaseCPU;
|
||||||
#include "sim/system.hh"
|
#include "sim/system.hh"
|
||||||
#include "targetarch/alpha_memory.hh"
|
#include "targetarch/alpha_memory.hh"
|
||||||
|
|
||||||
|
class FunctionProfile;
|
||||||
|
class ProfileNode;
|
||||||
class MemoryController;
|
class MemoryController;
|
||||||
class StaticInstBase;
|
|
||||||
namespace Kernel { class Binning; class Statistics; }
|
namespace Kernel { class Binning; class Statistics; }
|
||||||
|
|
||||||
#else // !FULL_SYSTEM
|
#else // !FULL_SYSTEM
|
||||||
|
@ -138,7 +139,11 @@ class ExecContext
|
||||||
Kernel::Statistics *kernelStats;
|
Kernel::Statistics *kernelStats;
|
||||||
bool bin;
|
bool bin;
|
||||||
bool fnbin;
|
bool fnbin;
|
||||||
void execute(const StaticInstBase *inst);
|
|
||||||
|
FunctionProfile *profile;
|
||||||
|
ProfileNode *profileNode;
|
||||||
|
Addr profilePC;
|
||||||
|
void dumpFuncProfile();
|
||||||
|
|
||||||
#else
|
#else
|
||||||
Process *process;
|
Process *process;
|
||||||
|
|
147
cpu/profile.cc
Normal file
147
cpu/profile.cc
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/bitfield.hh"
|
||||||
|
#include "base/trace.hh"
|
||||||
|
#include "cpu/base.hh"
|
||||||
|
#include "cpu/exec_context.hh"
|
||||||
|
#include "cpu/profile.hh"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
ProfileNode::ProfileNode()
|
||||||
|
: count(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void
|
||||||
|
ProfileNode::dump(const string &symbol, uint64_t id, const SymbolTable *symtab,
|
||||||
|
ostream &os) const
|
||||||
|
{
|
||||||
|
ccprintf(os, "%#x %s %d ", id, symbol, count);
|
||||||
|
ChildList::const_iterator i, end = children.end();
|
||||||
|
for (i = children.begin(); i != end; ++i) {
|
||||||
|
const ProfileNode &node = i->second;
|
||||||
|
ccprintf(os, "%#x ", (intptr_t)&node);
|
||||||
|
}
|
||||||
|
|
||||||
|
ccprintf(os, "\n");
|
||||||
|
|
||||||
|
for (i = children.begin(); i != end; ++i) {
|
||||||
|
Addr addr = i->first;
|
||||||
|
string symbol;
|
||||||
|
if (addr == 1)
|
||||||
|
symbol = "user";
|
||||||
|
else if (addr == 2)
|
||||||
|
symbol = "console";
|
||||||
|
else if (addr == 3)
|
||||||
|
symbol = "unknown";
|
||||||
|
else if (!symtab->findSymbol(addr, symbol))
|
||||||
|
panic("could not find symbol for address %#x\n", addr);
|
||||||
|
|
||||||
|
const ProfileNode &node = i->second;
|
||||||
|
node.dump(symbol, (intptr_t)&node, symtab, os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ProfileNode::clear()
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
ChildList::iterator i, end = children.end();
|
||||||
|
for (i = children.begin(); i != end; ++i) {
|
||||||
|
ProfileNode &node = i->second;
|
||||||
|
node.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionProfile::FunctionProfile(const SymbolTable *_symtab)
|
||||||
|
: symtab(_symtab)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionProfile::~FunctionProfile()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileNode *
|
||||||
|
FunctionProfile::consume(const StackTrace *trace)
|
||||||
|
{
|
||||||
|
const vector<Addr> &stack = trace->getstack();
|
||||||
|
ProfileNode *current = ⊤
|
||||||
|
for (int i = 0, size = stack.size(); i < size; ++i)
|
||||||
|
current = ¤t->children[stack[size - i - 1]];
|
||||||
|
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FunctionProfile::clear()
|
||||||
|
{
|
||||||
|
top.clear();
|
||||||
|
pc_count.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FunctionProfile::dump(ExecContext *xc, ostream &os) const
|
||||||
|
{
|
||||||
|
ccprintf(os, ">>>PC data\n");
|
||||||
|
map<Addr, Counter>::const_iterator i, end = pc_count.end();
|
||||||
|
for (i = pc_count.begin(); i != end; ++i) {
|
||||||
|
Addr pc = i->first;
|
||||||
|
Counter count = i->second;
|
||||||
|
|
||||||
|
std::string symbol;
|
||||||
|
if (pc == 1)
|
||||||
|
ccprintf(os, "user %d\n", count);
|
||||||
|
else if (symtab->findSymbol(pc, symbol) && !symbol.empty())
|
||||||
|
ccprintf(os, "%s %d\n", symbol, count);
|
||||||
|
else
|
||||||
|
ccprintf(os, "%#x %d\n", pc, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
ccprintf(os, ">>>function data\n");
|
||||||
|
top.dump("top", 0, symtab, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FunctionProfile::sample(ProfileNode *node, Addr pc)
|
||||||
|
{
|
||||||
|
node->count++;
|
||||||
|
|
||||||
|
Addr symaddr;
|
||||||
|
if (symtab->findNearestAddr(pc, symaddr)) {
|
||||||
|
pc_count[symaddr]++;
|
||||||
|
} else {
|
||||||
|
// record PC even if we don't have a symbol to avoid
|
||||||
|
// silently biasing the histogram
|
||||||
|
pc_count[pc]++;
|
||||||
|
}
|
||||||
|
}
|
74
cpu/profile.hh
Normal file
74
cpu/profile.hh
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __CPU_PROFILE_HH__
|
||||||
|
#define __CPU_PROFILE_HH__
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "cpu/static_inst.hh"
|
||||||
|
#include "sim/host.hh"
|
||||||
|
#include "targetarch/stacktrace.hh"
|
||||||
|
|
||||||
|
class ProfileNode
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
friend class FunctionProfile;
|
||||||
|
|
||||||
|
typedef std::map<Addr, ProfileNode> ChildList;
|
||||||
|
ChildList children;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int count;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ProfileNode();
|
||||||
|
|
||||||
|
void dump(const std::string &symbol, uint64_t id,
|
||||||
|
const SymbolTable *symtab, std::ostream &os) const;
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
class FunctionProfile
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const SymbolTable *symtab;
|
||||||
|
ProfileNode top;
|
||||||
|
std::map<Addr, Counter> pc_count;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FunctionProfile(const SymbolTable *symtab);
|
||||||
|
~FunctionProfile();
|
||||||
|
|
||||||
|
ProfileNode *consume(const StackTrace *trace);
|
||||||
|
void clear();
|
||||||
|
void dump(ExecContext *xc, std::ostream &out) const;
|
||||||
|
void sample(ProfileNode *node, Addr pc);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __CPU_PROFILE_HH__
|
|
@ -50,6 +50,7 @@
|
||||||
#include "cpu/simple/cpu.hh"
|
#include "cpu/simple/cpu.hh"
|
||||||
#include "cpu/smt.hh"
|
#include "cpu/smt.hh"
|
||||||
#include "cpu/static_inst.hh"
|
#include "cpu/static_inst.hh"
|
||||||
|
#include "kern/kernel_stats.hh"
|
||||||
#include "mem/base_mem.hh"
|
#include "mem/base_mem.hh"
|
||||||
#include "mem/mem_interface.hh"
|
#include "mem/mem_interface.hh"
|
||||||
#include "sim/builder.hh"
|
#include "sim/builder.hh"
|
||||||
|
@ -65,6 +66,7 @@
|
||||||
#include "mem/functional/physical.hh"
|
#include "mem/functional/physical.hh"
|
||||||
#include "sim/system.hh"
|
#include "sim/system.hh"
|
||||||
#include "targetarch/alpha_memory.hh"
|
#include "targetarch/alpha_memory.hh"
|
||||||
|
#include "targetarch/stacktrace.hh"
|
||||||
#include "targetarch/vtophys.hh"
|
#include "targetarch/vtophys.hh"
|
||||||
#else // !FULL_SYSTEM
|
#else // !FULL_SYSTEM
|
||||||
#include "mem/functional/functional.hh"
|
#include "mem/functional/functional.hh"
|
||||||
|
@ -753,8 +755,21 @@ SimpleCPU::tick()
|
||||||
fault = curStaticInst->execute(this, traceData);
|
fault = curStaticInst->execute(this, traceData);
|
||||||
|
|
||||||
#if FULL_SYSTEM
|
#if FULL_SYSTEM
|
||||||
if (xc->fnbin)
|
if (xc->fnbin) {
|
||||||
xc->execute(curStaticInst.get());
|
assert(xc->kernelStats);
|
||||||
|
system->kernelBinning->execute(xc, inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xc->profile) {
|
||||||
|
bool usermode = (xc->regs.ipr[AlphaISA::IPR_DTB_CM] & 0x18) != 0;
|
||||||
|
xc->profilePC = usermode ? 1 : xc->regs.pc;
|
||||||
|
StackTrace *trace = StackTrace::create(xc, inst);
|
||||||
|
if (trace) {
|
||||||
|
xc->profileNode = xc->profile->consume(trace);
|
||||||
|
trace->dprintf();
|
||||||
|
delete trace;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (curStaticInst->isMemRef()) {
|
if (curStaticInst->isMemRef()) {
|
||||||
|
@ -806,7 +821,6 @@ SimpleCPU::tick()
|
||||||
tickEvent.schedule(curTick + cycles(1));
|
tickEvent.schedule(curTick + cycles(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// SimpleCPU Simulation Object
|
// SimpleCPU Simulation Object
|
||||||
|
@ -824,6 +838,7 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU)
|
||||||
SimObjectParam<FunctionalMemory *> mem;
|
SimObjectParam<FunctionalMemory *> mem;
|
||||||
SimObjectParam<System *> system;
|
SimObjectParam<System *> system;
|
||||||
Param<int> cpu_id;
|
Param<int> cpu_id;
|
||||||
|
Param<Tick> profile;
|
||||||
#else
|
#else
|
||||||
SimObjectParam<Process *> workload;
|
SimObjectParam<Process *> workload;
|
||||||
#endif // FULL_SYSTEM
|
#endif // FULL_SYSTEM
|
||||||
|
@ -856,6 +871,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU)
|
||||||
INIT_PARAM(mem, "memory"),
|
INIT_PARAM(mem, "memory"),
|
||||||
INIT_PARAM(system, "system object"),
|
INIT_PARAM(system, "system object"),
|
||||||
INIT_PARAM(cpu_id, "processor ID"),
|
INIT_PARAM(cpu_id, "processor ID"),
|
||||||
|
INIT_PARAM(profile, ""),
|
||||||
#else
|
#else
|
||||||
INIT_PARAM(workload, "processes to run"),
|
INIT_PARAM(workload, "processes to run"),
|
||||||
#endif // FULL_SYSTEM
|
#endif // FULL_SYSTEM
|
||||||
|
@ -894,6 +910,7 @@ CREATE_SIM_OBJECT(SimpleCPU)
|
||||||
params->mem = mem;
|
params->mem = mem;
|
||||||
params->system = system;
|
params->system = system;
|
||||||
params->cpu_id = cpu_id;
|
params->cpu_id = cpu_id;
|
||||||
|
params->profile = profile;
|
||||||
#else
|
#else
|
||||||
params->process = workload;
|
params->process = workload;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -34,11 +34,12 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "cpu/static_inst.hh"
|
||||||
|
|
||||||
class BaseCPU;
|
class BaseCPU;
|
||||||
class ExecContext;
|
class ExecContext;
|
||||||
class FnEvent;
|
class FnEvent;
|
||||||
// What does kernel stats expect is included?
|
// What does kernel stats expect is included?
|
||||||
class StaticInstBase;
|
|
||||||
class System;
|
class System;
|
||||||
enum Fault;
|
enum Fault;
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ class Binning
|
||||||
|
|
||||||
cpu_mode themode;
|
cpu_mode themode;
|
||||||
void palSwapContext(ExecContext *xc);
|
void palSwapContext(ExecContext *xc);
|
||||||
void execute(ExecContext *xc, const StaticInstBase *inst);
|
void execute(ExecContext *xc, StaticInstPtr<TheISA> inst);
|
||||||
void call(ExecContext *xc, Stats::MainBin *myBin);
|
void call(ExecContext *xc, Stats::MainBin *myBin);
|
||||||
void changeMode(cpu_mode mode);
|
void changeMode(cpu_mode mode);
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ System::System(Params *p)
|
||||||
kernelSymtab = new SymbolTable;
|
kernelSymtab = new SymbolTable;
|
||||||
consoleSymtab = new SymbolTable;
|
consoleSymtab = new SymbolTable;
|
||||||
palSymtab = new SymbolTable;
|
palSymtab = new SymbolTable;
|
||||||
|
allSymtab = new SymbolTable;
|
||||||
debugSymbolTable = new SymbolTable;
|
debugSymbolTable = new SymbolTable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,6 +102,21 @@ System::System(Params *p)
|
||||||
if (!pal->loadLocalSymbols(palSymtab))
|
if (!pal->loadLocalSymbols(palSymtab))
|
||||||
panic("could not load pal symbols\n");
|
panic("could not load pal symbols\n");
|
||||||
|
|
||||||
|
if (!kernel->loadGlobalSymbols(allSymtab))
|
||||||
|
panic("could not load kernel symbols\n");
|
||||||
|
|
||||||
|
if (!kernel->loadLocalSymbols(allSymtab))
|
||||||
|
panic("could not load kernel local symbols\n");
|
||||||
|
|
||||||
|
if (!console->loadGlobalSymbols(allSymtab))
|
||||||
|
panic("could not load console symbols\n");
|
||||||
|
|
||||||
|
if (!pal->loadGlobalSymbols(allSymtab))
|
||||||
|
panic("could not load pal symbols\n");
|
||||||
|
|
||||||
|
if (!pal->loadLocalSymbols(allSymtab))
|
||||||
|
panic("could not load pal symbols\n");
|
||||||
|
|
||||||
if (!kernel->loadGlobalSymbols(debugSymbolTable))
|
if (!kernel->loadGlobalSymbols(debugSymbolTable))
|
||||||
panic("could not load kernel symbols\n");
|
panic("could not load kernel symbols\n");
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,9 @@ class System : public SimObject
|
||||||
/** pal symbol table */
|
/** pal symbol table */
|
||||||
SymbolTable *palSymtab;
|
SymbolTable *palSymtab;
|
||||||
|
|
||||||
|
/** all symbols table */
|
||||||
|
SymbolTable *allSymtab;
|
||||||
|
|
||||||
/** Object pointer for the kernel code */
|
/** Object pointer for the kernel code */
|
||||||
ObjectFile *kernel;
|
ObjectFile *kernel;
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,103 +27,227 @@
|
||||||
from orderdict import orderdict
|
from orderdict import orderdict
|
||||||
import output
|
import output
|
||||||
|
|
||||||
class ProfileData(object):
|
class RunData(dict):
|
||||||
def __init__(self):
|
def __init__(self, filename=None):
|
||||||
self.data = {}
|
self.filename = filename
|
||||||
self.total = {}
|
|
||||||
self.runs = orderdict()
|
|
||||||
self.runlist = []
|
|
||||||
|
|
||||||
def addvalue(self, run, cpu, symbol, value):
|
def __getattr__(self, attr):
|
||||||
value = float(value)
|
if attr == 'total':
|
||||||
self.data[run, cpu, symbol] = self.getvalue(run, cpu, symbol) + value
|
total = 0.0
|
||||||
self.total[run, cpu] = self.gettotal(run, cpu) + value
|
for value in self.itervalues():
|
||||||
if run not in self.runs:
|
total += value
|
||||||
self.runs[run] = orderdict()
|
return total
|
||||||
|
if attr == 'maxsymlen':
|
||||||
|
return max([ len(sym) for sym in self.iterkeys() ])
|
||||||
|
|
||||||
if cpu not in self.runs[run]:
|
def display(self, output=None, limit=None, maxsymlen=None):
|
||||||
self.runs[run][cpu] = {}
|
if not output:
|
||||||
|
import sys
|
||||||
|
output = sys.stdout
|
||||||
|
elif isinstance(output, str):
|
||||||
|
output = file(output, 'w')
|
||||||
|
|
||||||
if symbol not in self.runs[run][cpu]:
|
total = float(self.total)
|
||||||
self.runs[run][cpu][symbol] = 0
|
|
||||||
|
|
||||||
self.runs[run][cpu][symbol] += value
|
# swap (string,count) order so we can sort on count
|
||||||
|
symbols = [ (count,name) for name,count in self.iteritems() ]
|
||||||
|
symbols.sort(reverse=True)
|
||||||
|
if limit is not None:
|
||||||
|
symbols = symbols[:limit]
|
||||||
|
|
||||||
def getvalue(self, run, cpu, symbol):
|
if not maxsymlen:
|
||||||
return self.data.get((run, cpu, symbol), 0)
|
maxsymlen = self.maxsymlen
|
||||||
|
|
||||||
def gettotal(self, run, cpu):
|
symbolf = "%-" + str(maxsymlen + 1) + "s %.2f%%"
|
||||||
return self.total.get((run, cpu), 0)
|
for number,name in symbols:
|
||||||
|
print >>output, symbolf % (name, 100.0 * (float(number) / total))
|
||||||
|
|
||||||
class Profile(object):
|
|
||||||
default_order = ['ste', 'hte', 'htd', 'ocm', 'occ', 'ocp']
|
|
||||||
|
|
||||||
# This list controls the order of values in stacked bar data output
|
|
||||||
default_categories = [ 'interrupt',
|
|
||||||
'driver',
|
|
||||||
'stack',
|
|
||||||
'bufmgt',
|
|
||||||
'copy',
|
|
||||||
'user',
|
|
||||||
'other',
|
|
||||||
'idle']
|
|
||||||
|
|
||||||
def __init__(self, run_order=[], categories=[], stacknames=[]):
|
class PCData(RunData):
|
||||||
if not run_order:
|
def __init__(self, filename=None, categorize=None, showidle=True):
|
||||||
run_order = Profile.default_order
|
super(PCData, self).__init__(self, filename)
|
||||||
if not categories:
|
if filename is None:
|
||||||
categories = Profile.default_categories
|
return
|
||||||
|
|
||||||
self.run_order = run_order
|
|
||||||
self.categories = categories
|
|
||||||
self.rcategories = []
|
|
||||||
self.rcategories.extend(categories)
|
|
||||||
self.rcategories.reverse()
|
|
||||||
self.stacknames = stacknames
|
|
||||||
self.prof = ProfileData()
|
|
||||||
self.categorize = True
|
|
||||||
self.showidle = True
|
|
||||||
self.maxsymlen = 0
|
|
||||||
|
|
||||||
def category(self, symbol):
|
|
||||||
from categories import categories, categories_re
|
|
||||||
if categories.has_key(symbol):
|
|
||||||
return categories[symbol]
|
|
||||||
for regexp, cat in categories_re:
|
|
||||||
if regexp.match(symbol):
|
|
||||||
return cat
|
|
||||||
return 'other'
|
|
||||||
|
|
||||||
# Parse input file and put the results in the given run and cpu
|
|
||||||
def parsefile(self, run, cpu, filename):
|
|
||||||
fd = file(filename)
|
fd = file(filename)
|
||||||
|
|
||||||
for line in fd:
|
for line in fd:
|
||||||
|
if line.strip() == '>>>PC data':
|
||||||
|
break
|
||||||
|
|
||||||
|
for line in fd:
|
||||||
|
if line.startswith('>>>'):
|
||||||
|
break
|
||||||
|
|
||||||
(symbol, count) = line.split()
|
(symbol, count) = line.split()
|
||||||
if symbol == "0x0":
|
if symbol == "0x0":
|
||||||
continue
|
continue
|
||||||
count = int(count)
|
count = int(count)
|
||||||
|
|
||||||
if self.categorize:
|
if categorize is not None:
|
||||||
symbol = self.category(symbol)
|
category = categorize(symbol)
|
||||||
if symbol == 'idle' and not self.showidle:
|
if category is None:
|
||||||
|
category = 'other'
|
||||||
|
elif category == 'idle' and not showidle:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if symbol not in self.categories:
|
self[category] = count
|
||||||
symbol = 'other'
|
|
||||||
|
|
||||||
self.maxsymlen = max(self.maxsymlen, len(symbol))
|
|
||||||
self.prof.addvalue(run, cpu, symbol, count)
|
|
||||||
|
|
||||||
fd.close()
|
fd.close()
|
||||||
|
|
||||||
|
class FuncNode(object):
|
||||||
|
def __new__(cls, filename = None):
|
||||||
|
if filename is None:
|
||||||
|
return super(FuncNode, cls).__new__(cls)
|
||||||
|
|
||||||
|
fd = file(filename, 'r')
|
||||||
|
fditer = iter(fd)
|
||||||
|
nodes = {}
|
||||||
|
for line in fditer:
|
||||||
|
if line.strip() == '>>>function data':
|
||||||
|
break
|
||||||
|
|
||||||
|
for line in fditer:
|
||||||
|
if line.startswith('>>>'):
|
||||||
|
break
|
||||||
|
|
||||||
|
data = line.split()
|
||||||
|
node_id = int(data[0], 16)
|
||||||
|
node = FuncNode()
|
||||||
|
node.symbol = data[1]
|
||||||
|
node.count = int(data[2])
|
||||||
|
node.children = [ int(child, 16) for child in data[3:] ]
|
||||||
|
nodes[node_id] = node
|
||||||
|
|
||||||
|
for node in nodes.itervalues():
|
||||||
|
children = []
|
||||||
|
for cid in node.children:
|
||||||
|
child = nodes[cid]
|
||||||
|
children.append(child)
|
||||||
|
child.parent = node
|
||||||
|
node.children = tuple(children)
|
||||||
|
if not nodes:
|
||||||
|
print filename
|
||||||
|
print nodes
|
||||||
|
return nodes[0]
|
||||||
|
|
||||||
|
def __init__(self, filename=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def total(self):
|
||||||
|
total = self.count
|
||||||
|
for child in self.children:
|
||||||
|
total += child.total()
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
def aggregate(self, dict, categorize, incategory):
|
||||||
|
category = None
|
||||||
|
if categorize:
|
||||||
|
category = categorize(self.symbol)
|
||||||
|
|
||||||
|
total = self.count
|
||||||
|
for child in self.children:
|
||||||
|
total += child.aggregate(dict, categorize, category or incategory)
|
||||||
|
|
||||||
|
if category:
|
||||||
|
dict[category] = dict.get(category, 0) + total
|
||||||
|
return 0
|
||||||
|
elif not incategory:
|
||||||
|
dict[self.symbol] = dict.get(self.symbol, 0) + total
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
def dump(self):
|
||||||
|
kids = [ child.symbol for child in self.children]
|
||||||
|
print '%s %d <%s>' % (self.symbol, self.count, ', '.join(kids))
|
||||||
|
for child in self.children:
|
||||||
|
child.dump()
|
||||||
|
|
||||||
|
def _dot(self, dot, threshold, categorize, total):
|
||||||
|
from pydot import Dot, Edge, Node
|
||||||
|
self.dot_node = None
|
||||||
|
|
||||||
|
value = self.total() * 100.0 / total
|
||||||
|
if value < threshold:
|
||||||
|
return
|
||||||
|
if categorize:
|
||||||
|
category = categorize(self.symbol)
|
||||||
|
if category and category != 'other':
|
||||||
|
return
|
||||||
|
label = '%s %.2f%%' % (self.symbol, value)
|
||||||
|
self.dot_node = Node(self, label=label)
|
||||||
|
dot.add_node(self.dot_node)
|
||||||
|
|
||||||
|
for child in self.children:
|
||||||
|
child._dot(dot, threshold, categorize, total)
|
||||||
|
if child.dot_node is not None:
|
||||||
|
dot.add_edge(Edge(self, child))
|
||||||
|
|
||||||
|
def _cleandot(self):
|
||||||
|
for child in self.children:
|
||||||
|
child._cleandot()
|
||||||
|
self.dot_node = None
|
||||||
|
del self.__dict__['dot_node']
|
||||||
|
|
||||||
|
def dot(self, dot, threshold=0.1, categorize=None):
|
||||||
|
self._dot(dot, threshold, categorize, self.total())
|
||||||
|
self._cleandot()
|
||||||
|
|
||||||
|
class FuncData(RunData):
|
||||||
|
def __init__(self, filename, categorize=None):
|
||||||
|
super(FuncData, self).__init__(filename)
|
||||||
|
self.tree = FuncNode(filename)
|
||||||
|
self.tree.aggregate(self, categorize, incategory=False)
|
||||||
|
self.total = self.tree.total()
|
||||||
|
|
||||||
|
def displayx(self, output=None, maxcount=None):
|
||||||
|
if output is None:
|
||||||
|
import sys
|
||||||
|
output = sys.stdout
|
||||||
|
|
||||||
|
items = [ (val,key) for key,val in self.iteritems() ]
|
||||||
|
items.sort(reverse=True)
|
||||||
|
for val,key in items:
|
||||||
|
if maxcount is not None:
|
||||||
|
if maxcount == 0:
|
||||||
|
return
|
||||||
|
maxcount -= 1
|
||||||
|
|
||||||
|
percent = val * 100.0 / self.total
|
||||||
|
print >>output, '%-30s %8s' % (key, '%3.2f%%' % percent)
|
||||||
|
|
||||||
|
class Profile(object):
|
||||||
|
# This list controls the order of values in stacked bar data output
|
||||||
|
default_categories = [ 'interrupt',
|
||||||
|
'driver',
|
||||||
|
'stack',
|
||||||
|
'buffer',
|
||||||
|
'copy',
|
||||||
|
'syscall',
|
||||||
|
'user',
|
||||||
|
'other',
|
||||||
|
'idle']
|
||||||
|
|
||||||
|
def __init__(self, datatype, categorize=None):
|
||||||
|
categories = Profile.default_categories
|
||||||
|
|
||||||
|
self.datatype = datatype
|
||||||
|
self.categorize = categorize
|
||||||
|
self.data = {}
|
||||||
|
self.categories = categories[:]
|
||||||
|
self.rcategories = categories[:]
|
||||||
|
self.rcategories.reverse()
|
||||||
|
self.cpu = 0
|
||||||
|
|
||||||
# Read in files
|
# Read in files
|
||||||
def inputdir(self, directory):
|
def inputdir(self, directory):
|
||||||
import os, os.path, re
|
import os, os.path, re
|
||||||
from os.path import expanduser, join as joinpath
|
from os.path import expanduser, join as joinpath
|
||||||
|
|
||||||
directory = expanduser(directory)
|
directory = expanduser(directory)
|
||||||
label_ex = re.compile(r'm5prof\.(.*)')
|
label_ex = re.compile(r'profile\.(.*).dat')
|
||||||
for root,dirs,files in os.walk(directory):
|
for root,dirs,files in os.walk(directory):
|
||||||
for name in files:
|
for name in files:
|
||||||
match = label_ex.match(name)
|
match = label_ex.match(name)
|
||||||
|
@ -133,14 +257,230 @@ class Profile(object):
|
||||||
filename = joinpath(root, name)
|
filename = joinpath(root, name)
|
||||||
prefix = os.path.commonprefix([root, directory])
|
prefix = os.path.commonprefix([root, directory])
|
||||||
dirname = root[len(prefix)+1:]
|
dirname = root[len(prefix)+1:]
|
||||||
self.parsefile(dirname, match.group(1), filename)
|
data = self.datatype(filename, self.categorize)
|
||||||
|
self.setdata(dirname, match.group(1), data)
|
||||||
|
|
||||||
|
def setdata(self, run, cpu, data):
|
||||||
|
if run not in self.data:
|
||||||
|
self.data[run] = {}
|
||||||
|
|
||||||
|
if cpu in self.data[run]:
|
||||||
|
raise AttributeError, \
|
||||||
|
'data already stored for run %s and cpu %s' % (run, cpu)
|
||||||
|
|
||||||
|
self.data[run][cpu] = data
|
||||||
|
|
||||||
|
def getdata(self, run, cpu):
|
||||||
|
try:
|
||||||
|
return self.data[run][cpu]
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def alldata(self):
|
||||||
|
for run,cpus in self.data.iteritems():
|
||||||
|
for cpu,data in cpus.iteritems():
|
||||||
|
yield run,cpu,data
|
||||||
|
|
||||||
def get(self, job, stat):
|
def get(self, job, stat):
|
||||||
if job.system is None:
|
if job.system is None:
|
||||||
raise AttributeError, 'The job must have a system set'
|
raise AttributeError, 'The job must have a system set'
|
||||||
|
|
||||||
cpu = '%s.full0' % job.system
|
data = self.getdata(job.name, '%s.full%d' % (job.system, self.cpu))
|
||||||
|
if not data:
|
||||||
|
return [ 0.0 for c in self.categories ]
|
||||||
|
|
||||||
values = []
|
values = []
|
||||||
for cat in self.categories:
|
for category in self.categories:
|
||||||
values.append(self.prof.getvalue(job.name, cpu, cat))
|
values.append(data.get(category, 0.0))
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
def dump(self):
|
||||||
|
for run,cpu,data in self.alldata():
|
||||||
|
print 'run %s, cpu %s' % (run, cpu)
|
||||||
|
data.dump()
|
||||||
|
print
|
||||||
|
|
||||||
|
def write_dot(self, threshold, jobfile=None, jobs=None):
|
||||||
|
import pydot
|
||||||
|
|
||||||
|
if jobs is None:
|
||||||
|
jobs = [ job for job in jobfile.jobs() ]
|
||||||
|
|
||||||
|
for job in jobs:
|
||||||
|
cpu = '%s.full%d' % (job.system, self.cpu)
|
||||||
|
symbols = self.getdata(job.name, cpu)
|
||||||
|
if not symbols:
|
||||||
|
continue
|
||||||
|
|
||||||
|
dot = pydot.Dot()
|
||||||
|
symbols.tree.dot(dot, threshold=threshold)
|
||||||
|
dot.write(symbols.filename[:-3] + 'dot')
|
||||||
|
|
||||||
|
def write_txt(self, jobfile=None, jobs=None):
|
||||||
|
if jobs is None:
|
||||||
|
jobs = [ job for job in jobfile.jobs() ]
|
||||||
|
|
||||||
|
for job in jobs:
|
||||||
|
cpu = '%s.full%d' % (job.system, self.cpu)
|
||||||
|
symbols = self.getdata(job.name, cpu)
|
||||||
|
if not symbols:
|
||||||
|
continue
|
||||||
|
|
||||||
|
output = file(symbols.filename[:-3] + 'txt', 'w')
|
||||||
|
symbols.display(output)
|
||||||
|
|
||||||
|
def display(self, jobfile=None, jobs=None, limit=None):
|
||||||
|
if jobs is None:
|
||||||
|
jobs = [ job for job in jobfile.jobs() ]
|
||||||
|
|
||||||
|
maxsymlen = 0
|
||||||
|
|
||||||
|
thejobs = []
|
||||||
|
for job in jobs:
|
||||||
|
cpu = '%s.full%d' % (job.system, self.cpu)
|
||||||
|
symbols = self.getdata(job.name, cpu)
|
||||||
|
if symbols:
|
||||||
|
thejobs.append(job)
|
||||||
|
maxsymlen = max(maxsymlen, symbols.maxsymlen)
|
||||||
|
|
||||||
|
for job in thejobs:
|
||||||
|
cpu = '%s.full%d' % (job.system, self.cpu)
|
||||||
|
symbols = self.getdata(job.name, cpu)
|
||||||
|
print job.name
|
||||||
|
symbols.display(limit=limit, maxsymlen=maxsymlen)
|
||||||
|
print
|
||||||
|
|
||||||
|
|
||||||
|
from categories import func_categorize, pc_categorize
|
||||||
|
class PCProfile(Profile):
|
||||||
|
def __init__(self, categorize=pc_categorize):
|
||||||
|
super(PCProfile, self).__init__(PCData, categorize)
|
||||||
|
|
||||||
|
|
||||||
|
class FuncProfile(Profile):
|
||||||
|
def __init__(self, categorize=func_categorize):
|
||||||
|
super(FuncProfile, self).__init__(FuncData, categorize)
|
||||||
|
|
||||||
|
def usage(exitcode = None):
|
||||||
|
print '''\
|
||||||
|
Usage: %s [-bc] [-g <dir>] [-j <jobfile>] [-n <num>]
|
||||||
|
|
||||||
|
-c groups symbols into categories
|
||||||
|
-b dumps data for bar charts
|
||||||
|
-d generate dot output
|
||||||
|
-g <d> draw graphs and send output to <d>
|
||||||
|
-j <jobfile> specify a different jobfile (default is Test.py)
|
||||||
|
-n <n> selects number of top symbols to print (default 5)
|
||||||
|
''' % sys.argv[0]
|
||||||
|
|
||||||
|
if exitcode is not None:
|
||||||
|
sys.exit(exitcode)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import getopt, re, sys
|
||||||
|
from os.path import expanduser
|
||||||
|
from output import StatOutput
|
||||||
|
from jobfile import JobFile
|
||||||
|
|
||||||
|
# default option values
|
||||||
|
numsyms = 10
|
||||||
|
graph = None
|
||||||
|
cpus = [ 0 ]
|
||||||
|
categorize = False
|
||||||
|
showidle = True
|
||||||
|
funcdata = True
|
||||||
|
jobfilename = 'Test.py'
|
||||||
|
dodot = False
|
||||||
|
dotformat = 'raw'
|
||||||
|
textout = False
|
||||||
|
threshold = 0.01
|
||||||
|
inputfile = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(sys.argv[1:], 'C:cdD:f:g:ij:n:pT:t')
|
||||||
|
except getopt.GetoptError:
|
||||||
|
usage(2)
|
||||||
|
|
||||||
|
for o,a in opts:
|
||||||
|
if o == '-C':
|
||||||
|
cpus = [ int(x) for x in a.split(',') ]
|
||||||
|
elif o == '-c':
|
||||||
|
categorize = True
|
||||||
|
elif o == '-D':
|
||||||
|
dotformat = a
|
||||||
|
elif o == '-d':
|
||||||
|
dodot = True
|
||||||
|
elif o == '-f':
|
||||||
|
inputfile = expanduser(a)
|
||||||
|
elif o == '-g':
|
||||||
|
graph = a
|
||||||
|
elif o == '-i':
|
||||||
|
showidle = False
|
||||||
|
elif o == '-j':
|
||||||
|
jobfilename = a
|
||||||
|
elif o == '-n':
|
||||||
|
numsyms = int(a)
|
||||||
|
elif o == '-p':
|
||||||
|
funcdata = False
|
||||||
|
elif o == '-T':
|
||||||
|
threshold = float(a)
|
||||||
|
elif o == '-t':
|
||||||
|
textout = True
|
||||||
|
|
||||||
|
if args:
|
||||||
|
print "'%s'" % args, len(args)
|
||||||
|
usage(1)
|
||||||
|
|
||||||
|
if inputfile:
|
||||||
|
data = FuncData(inputfile)
|
||||||
|
|
||||||
|
if dodot:
|
||||||
|
import pydot
|
||||||
|
dot = pydot.Dot()
|
||||||
|
data.dot(dot, threshold=threshold)
|
||||||
|
#dot.orientation = 'landscape'
|
||||||
|
#dot.ranksep='equally'
|
||||||
|
#dot.rank='samerank'
|
||||||
|
dot.write(dotfile, format=dotformat)
|
||||||
|
else:
|
||||||
|
data.display(limit=numsyms)
|
||||||
|
|
||||||
|
else:
|
||||||
|
jobfile = JobFile(jobfilename)
|
||||||
|
|
||||||
|
if funcdata:
|
||||||
|
profile = FuncProfile()
|
||||||
|
else:
|
||||||
|
profile = PCProfile()
|
||||||
|
|
||||||
|
profile.inputdir(jobfile.rootdir)
|
||||||
|
|
||||||
|
if graph:
|
||||||
|
for cpu in cpus:
|
||||||
|
profile.cpu = cpu
|
||||||
|
if funcdata:
|
||||||
|
name = 'funcstacks%d' % cpu
|
||||||
|
else:
|
||||||
|
name = 'stacks%d' % cpu
|
||||||
|
output = StatOutput(name, jobfile, info=profile)
|
||||||
|
output.graph(graph)
|
||||||
|
|
||||||
|
if dodot:
|
||||||
|
for cpu in cpus:
|
||||||
|
profile.cpu = cpu
|
||||||
|
profile.write_dot(jobfile=jobfile, threshold=threshold)
|
||||||
|
|
||||||
|
if not categorize:
|
||||||
|
for cpu in cpus:
|
||||||
|
profile.cpu = cpu
|
||||||
|
profile.categorize = None
|
||||||
|
|
||||||
|
if textout:
|
||||||
|
for cpu in cpus:
|
||||||
|
profile.cpu = cpu
|
||||||
|
profile.write_txt(jobfile=jobfile)
|
||||||
|
|
||||||
|
if not graph and not textout and not dodot:
|
||||||
|
for cpu in cpus:
|
||||||
|
profile.cpu = cpu
|
||||||
|
profile.display(jobfile=jobfile, limit=numsyms)
|
||||||
|
|
Loading…
Reference in a new issue