Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / analyzers / trapcount / trapcount.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: trapcount.cc
// Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
//
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
//
// The above named program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
// ========== Copyright Header End ============================================
/* icq: TRACER INSTR TRAP TLB
*
* trapcount.cc
*
*
* the Good:
* the Bad:
* and the Ugly:
* numcpus = g_nvcpu_max; system.h, non-transferable to trace reader :-(.
* UI_(un)register... ui.h, non-transferable to trace reader :-(.
*/
// standard C includes -------------------------------
#include <stdio.h>
// standard C++/STL includes -------------------------------
// project includes -------------------------------
#include "system.h" // need g_nvcpus <--- NG.
#include "ui.h" // need UI_register and UI_unregister <--- NG.
// program includes -------------------------------
#include "../../system/util/include/atomic.h" // Argh!!!...
#include "my_sparc.h"
#include "sc_strings.h"
#include "tt_strings.h"
#include "trapcount.h"
// boilerplate -------------------------------
// local definitions -------------------------------
#define BAD14 1 /* can't count 0x04e traps as interval timer any more */
// global variables -------------------------------
// global state
static int CountingOn = 1;
// feature options
// debug options
static int DEBUG = 0;
static int VERBOSE = 0;
// global functions -------------------------------
#if 0 /* we want more flexibility that Safari/Serengeti constraints allow */
// from Serengeti Specification (an implementation of Safari Bus)
// lower 5-bits are AgentId
#define ISCPUID(x) (((x) & 0x1f) < 24) // AgentIDs 0..23 = cpus
#define ISSCHIZOID(x) (((x) & 0x1f) >= 24) // AgentIDs 24..31 = schizos
// and upper 4-bits are NodeId.
#else
// x = SystemId = NodeId<<5+AgentId
#define ISCPUID(x) (get_vcpu(x) != NULL)
#define ISSCHIZOID(x) (get_vcpu(x) == NULL)
#endif
#define UNPACKINOAID(i) (((i >> 4) & 0x1e0) | 0x18 | ((i >> 6) & 0x7))
// ===================================================================== MODULE
static const char * mode_names[10];
static trapcount * the_trapcount = NULL;
extern "C" void * vtracer_init (const char * instname) // --------- module init
{
if (the_trapcount == NULL) {
the_trapcount = new trapcount (instname);
UI_register_cmd_2 ("trapcount", "", trapcount::trapcount_ui_cmds, NULL);
} else
fprintf(stderr, "Error: trapcount already module_init'ed\n");
return (void*) the_trapcount;
}
extern "C" void vtracer_fini () // -------------------------------- module fini
{
if (the_trapcount != NULL) {
UI_invalidate_cmd ("trapcount"); /* aka: UI_unregister_cmd */
delete the_trapcount;
the_trapcount = NULL;
} else
fprintf(stderr, "Error: trapcount already module_fini'ed\n");
}
Counters::Counters () // -------------------------------------------- ctor
{
Reset ();
}
void Counters::Reset ()
{
instrcount = 0LL;
hypercount = 0LL;
kernelcount = 0LL;
usercount = 0LL;
for (int i=0; i<MAX_TRAPTYPES; i++) traptypecount[i] = 0LL;
for (int i=0; i<MAX_SYSCALLS; i++) syscallscount[i] = 0LL;
for (int i=0; i<MAX_NCPUS; i++) mondoxcalcount[i] = 0LL;
// for (int i=0; i<MAX_INOS; i++) mondoinocount[i] = 0LL;
for (inoiter ip=mondoinocount.begin();
ip!=mondoinocount.end();
ip++)
ip->second = 0LL;
// alternatively...
// mondoinocount.erase (mondoinocount.begin(), mondoinocount.end());
}
trapcount::trapcount (const char * tmp_modname) // ======================= CTOR
{
printf("trapcount::ctor (modname=>\"%s\"), numcpus=%d \n",
tmp_modname, g_nvcpu);
mode_names[MODE_NONE] = "none";
mode_names[MODE_AGGREGATE] = "aggregate";
mode_names[MODE_BY_CPU] = "cpu";
mode_names[MODE_BY_MMUCNTX] = "mmucntx";
mode_names[MODE_BY_THREAD] = "thread";
intervalsCounter = 0;
mode = MODE_NONE;
configure_for_mode (MODE_BY_CPU); // <--- default mode <---
numcpus = g_nvcpu; /* system.h - cpu_interface.h (argh!) */
// numschizos = ???; /* need to know all Agent IDs ??? ... */
}
trapcount::~trapcount () // ============================================== DTOR
{
}
/*static*/
int trapcount::trapcount_ui_cmds (void*notused, int argc, char *argv[])
{
if (strcmp (argv[0], "trapcount") != 0) return 0;
if (argc == 1) { // status
printf ("trapcount: counting = %s, mode = %s.\n",
(CountingOn ? "on" : "off"),
mode_names[the_trapcount->mode]);
return 1;
}
if (argc >= 2 && strcmp (argv[1], "mode") == 0) { // mode
if (argc >= 3) {
if (strcmp (argv[2], "aggregate") == 0)
the_trapcount->configure_for_mode (MODE_AGGREGATE);
else if (strcmp (argv[2], "cpu") == 0)
the_trapcount->configure_for_mode (MODE_BY_CPU);
else if (strcmp (argv[2], "mmucntx") == 0)
the_trapcount->configure_for_mode (MODE_BY_MMUCNTX);
else if (strcmp (argv[2], "thread") == 0)
the_trapcount->configure_for_mode (MODE_BY_THREAD);
else
printf (" * unrecognized mode arg\n");
}
fprintf (stderr, "trapcount mode is %s\n",
mode_names[ the_trapcount->mode ]);
return 1;
}
if (argc >= 2 && strcmp (argv[1], "peek") == 0) { // peek
the_trapcount->printCounts (0/*dont reset*/);
return 1;
}
if (argc >= 2 && (strcmp (argv[1], "print") == 0 // print
|| strcmp (argv[1], "stats") == 0)) { // stats
the_trapcount->printCounts (1/*do reset*/);
return 1;
}
if (argc >= 2 && strcmp (argv[1], "debug") == 0) { // debug
if (argc == 3) { DEBUG = (int) strtol (argv[2], NULL, 0); }
fprintf (stderr, "trapcount debug level is %d\n", DEBUG);
return 1;
}
if (strcmp (argv[1], "on") == 0) { // On/Off
CountingOn = 1;
return 1;
}
if (strcmp (argv[1], "off") == 0) {
CountingOn = 0;
return 1;
}
return 0;
}
int trapcount::attach (VTracer_SAM_intf * sam_intf)
{
return 0; // do nothing...
}
// -------- friendly reminder --------
//class Counters {
//public:
// int64 instrcount,
// kernelcount,
// usercount;
// int64 traptypecount[ MAX_TRAPTYPES ];
// int64 syscallscount[ MAX_SYSCALLS ];
// int64 mondoxcalcount[ MAX_NCPUS ];
// //int64 mondoinocount[ MAX_INOS ];
// inoarray64_t mondoinocount;
//
// Counters ();
//};
void trapcount::printCounts (int reset) // ======================= print counts
{
switch (mode) {
case MODE_AGGREGATE: {
printf (" aggregate: %12lld-instrs %12lld-kernel %12lld-user \n",
aggregate->instrcount,
aggregate->kernelcount,
aggregate->usercount);
for (int i=0; i<MAX_TRAPTYPES; i++)
if (aggregate->traptypecount[ i ])
printf (" tt-0x%03x %12lld %s\n",
i,
aggregate->traptypecount[ i ],
tt_names[ i ]);
for (int i=0; i<MAX_SYSCALLS; i++)
if (aggregate->syscallscount[ i ])
printf (" sc-0x%03x %12lld %s\n",
i,
aggregate->syscallscount[ i ],
sc_names[ i ]);
for (inoiter ip=aggregate->mondoinocount.begin();
ip!=aggregate->mondoinocount.end();
ip++)
if (ip->second != 0) /*aggregate->mondoinocount[i] != 0)*/
printf (" zm-0x%03x %12lld (%02x,%02x) \n",
ip->first, /*i,*/
ip->second, /*aggregate->mondoinocount[ i ],*/
ip->first >> 6, /*i >> 6,*/ // NodeID and AgentID
ip->first & 0x3f); /*i & 0x3f);*/ // DeviceNo
for (int i=0; i<MAX_NCPUS; i++)
if (aggregate->mondoxcalcount[ i ])
printf (" xm-0x%03x %12lld \n",
i,
aggregate->mondoxcalcount[ i ]);
if (reset) aggregate->Reset ();
printf ("\n");
} break;
case MODE_BY_CPU: {
// someday: put each cpu in a column...
for (int j=0; j<numcpus; j++) {
printf (" cpu[%d]::: %12lld-instrs %12lld-kernel %12lld-user \n",
j,
cpus[j].instrcount,
cpus[j].kernelcount,
cpus[j].usercount);
for (int i=0; i<MAX_TRAPTYPES; i++)
if (cpus[j].traptypecount[ i ])
printf (" tt-0x%03x %12lld %s\n",
i,
cpus[j].traptypecount[ i ],
tt_names[ i ]);
for (int i=0; i<MAX_SYSCALLS; i++)
if (cpus[j].syscallscount[ i ])
printf (" sc-0x%03x %12lld %s\n",
i,
cpus[j].syscallscount[ i ],
sc_names[ i ]);
for (inoiter ip=cpus[j].mondoinocount.begin();
ip!=cpus[j].mondoinocount.end();
ip++)
if (ip->second != 0) /*cpus[j].mondoinocount[i] != 0)*/
printf (" md-0x%03x %12lld (%02x,%02x) \n",
ip->first, /*i,*/
ip->second, /*cpus[j]->mondoinocount[ i ],*/
ip->first >> 6, /*i >> 6,*/ // NodeID and AgentID
ip->first & 0x3f); /*i & 0x3f);*/ // DeviceNo
for (int i=0; i<MAX_NCPUS; i++)
if (cpus[j].mondoxcalcount[ i ])
printf (" md-0x%03x %12lld \n",
i,
cpus[j].mondoxcalcount[ i ]);
if (reset) cpus[j].Reset ();
printf ("\n");
}/*end-for-cpu*/
} break;
default:
printf ("printing mode %s not currently supported\n", mode_names[mode]);
}
}
void trapcount::configure_for_mode (modes newmode) // ======= configure mode
{
if (newmode == mode)
return; // we're already there...
// allocate new counters
//
switch (newmode) {
case MODE_NONE:
break;
case MODE_AGGREGATE:
aggregate = new Counters;
break;
case MODE_BY_CPU:
cpus = new Counters[MAX_NCPUS]; // array, be careful at delete!
break;
case MODE_BY_MMUCNTX:
mmucntxs = new MmucntxCounters;
break;
case MODE_BY_THREAD:
threads = new ThreadCounters;
break;
default:
mode = MODE_NONE;
fprintf(stderr, "Trapcount error: unimplemented mode\n");
}
// warning: deleting the old counters creates a race condition...
// which is worse (fatal!) than the alternative, a memory leak...
#if 0
// deallocate previous counters
//
switch (mode) {
case MODE_NONE:
break;
case MODE_AGGREGATE:
delete aggregate;
aggregate = NULL;
break;
case MODE_BY_CPU:
delete [] cpus;
cpus = NULL;
break;
case MODE_BY_MMUCNTX:
delete mmucntxs;
mmucntxs = NULL;
break;
case MODE_BY_THREAD:
delete threads;
threads = NULL;
break;
default:
fprintf(stderr, "Trapcount error: unimplemented mode\n");
}
#endif
// finally, update mode
//
mode = newmode;
}
// ================================= TRACER =================================
int trapcount::instr (VCPU_Instruction * vi) // ======================== INSTR
{
if (!CountingOn) return 0;
if (vi->ifetch_trap) { // fake instruction in case analyzer needs pc
return 0;
}
if (vi->annul) { // annulled (non-executed) delay slot instr
return 0;
}
// being consistent with HW-perf-counters and cpustat, which do not count
// annulled, mispredicted, or trapped instructions - only retired ones.
//
if (vi->dmmu_trap
|| vi->exception) {
return 0;
}
#if 0
spix_sparc_inst_t spixter; spixter.inst = rsp->instr.instr;
spix_sparc_iop_t iop =
spix_sparc_iop(SPIX_SPARC_V9, &((rsp->instr).instr));
if (iop == SPIX_SPARC_IOP_WRASR) {
switch (spixter.memarithr.rd) {
case ASR_SSI: /*0x14*/
fprintf (stderr, "SET_SOFTINT = 0x08x\n", ???);
case ASR_CSI: /*0x15*/
fprintf (stderr, "CLR_SOFTINT = 0x08x\n", ???);
case ASR_SFTINT: /*0x16*/
fprintf (stderr, "WR_SOFTINT = 0x08x\n", ???);
}
}
#endif
if (mode == MODE_AGGREGATE) {
++ aggregate->instrcount;
if (vi->hpstate & HPSTATE_PRIV) {
++ aggregate->hypercount;
} else if (vi->pstate & PSTATE_PRIV) {
++ aggregate->kernelcount;
} else {
++ aggregate->usercount;
}
} else if (mode == MODE_BY_CPU) {
++ cpus[ vi->cpuid ].instrcount;
if (vi->hpstate & HPSTATE_PRIV) {
++ aggregate->hypercount;
} else if (vi->pstate & PSTATE_PRIV) {
++ cpus[ vi->cpuid ].kernelcount;
} else {
++ cpus[ vi->cpuid ].usercount;
}
#if 0
} else if (mode == MODE_BY_MMUCNTX) {
++ mmucntxs[ ??? ]->instrcount;
if (vi->pstate & PSTATE_PRIV) {
} else if (vi->hpstate & HPSTATE_PRIV) {
++ aggregate->hypercount;
++ mmucntxs[ ??? ]->kernelcount;
} else {
++ mmucntxs[ ??? ]->usercount;
}
} else if (mode == MODE_BY_THREAD) {
#endif
} else {
}
return 0;
}/*instr*/
int trapcount::trap (VCPU_Trap * vt) // ================================ TRAP
{
if (!CountingOn) return 0;
if (DEBUG) {
if (!VERBOSE) { /* these are too obnoxious to display normally... */
if (vt->tno >= 0x080 && vt->tno <= 0x0ff) /* window-miss */
return 0;
if (vt->tno >= 0x024 && vt->tno <= 0x027) /* clean-win */
return 0;
if (vt->tno >= 0x064 && vt->tno <= 0x06f) /* tlb-miss */
return 0;
}
fprintf(stderr, "[cpu-%d] Trap 0x%03x %s ",
vt->cpuid, vt->tno, tt_names[vt->tno]);
if (vt->tno == 0x108 || vt->tno == 0x140) {
fprintf(stderr, " %d %s",
vt->syscallno, sc_names[ vt->syscallno ]);
}
if (vt->tno == 0x060) {
if (ISCPUID(vt->intrsid))
fprintf(stderr, " xcl SID = %d", vt->intrsid);
else
// @@@
fprintf(stderr, " dev INO = 0x%03x (%02x,%02x)",
vt->intrino, (vt->intrino >> 6), (vt->intrino & 0x3f));
}
#if !BAD14 /* Solaris uses level-14 for pci_pbm_dma_sync signalling, Yeuch! */
if (vt->tno == 0x04e && vt->cpuid == 0) {
fprintf(stderr," interval-timer count = %lld",
intervalsCounter+1);
}
#endif
fprintf(stderr, "\n");
return 0;
}/*debug*/
if (mode == MODE_AGGREGATE) {
atomic_add_64 (&aggregate->traptypecount[ vt->tno ], +1);
if (vt->tno == 0x108/*syscall32*/ || vt->tno == 0x140/*syscall64*/) {
atomic_add_64 (&aggregate->syscallscount[ vt->syscallno ], +1);
} else if (vt->tno == 0x060/*mondo*/) {
if (ISCPUID(vt->intrsid))
atomic_add_64 (&aggregate->mondoxcalcount[ vt->intrsid ], +1);
else
// @@@
atomic_add_64 (&aggregate->mondoinocount[ vt->intrino ], +1);
}
} else if (mode == MODE_BY_CPU) {
atomic_add_64 (&cpus[ vt->cpuid ].traptypecount[ vt->tno ], +1);
if (vt->tno == 0x108/*syscall32*/ || vt->tno == 0x140/*syscall64*/) {
atomic_add_64 (&cpus[ vt->cpuid ].syscallscount[ vt->syscallno ], +1);
} else if (vt->tno == 0x060/*mondo*/) {
if (ISCPUID(vt->intrsid))
atomic_add_64 (&cpus[ vt->cpuid ].mondoxcalcount[ vt->intrsid ], +1);
else
// @@@
atomic_add_64 (&cpus[ vt->cpuid ].mondoinocount[ vt->intrino ], +1);
}
#if 0
} else if (mode == MODE_BY_MMUCNTX) {
atomic_add_64 (&mmucntxs[ vt->??? ]->traptypecount[ vt->tno ], +1);
if (vt->tno == 0x108/*syscall32*/ || vt->tno == 0x140/*syscall64*/) {
atomic_add_64 (&mmucntxs[ vt->??? ]->syscallscount[ vt->syscallno ], +1);
} else if (vt->tno == 0x060/*mondo*/) {
if (ISCPUID(vt->intrsid))
atomic_add_64 (&mmucntxs[ vt->??? ]->mondoxcalcount[ vt->intrsid ], +1);
else
// @@@
atomic_add_64 (&mmucntxs[ vt->??? ]->mondoinocount[ vt->intrino ], +1);
}
} else if (mode == MODE_BY_THREAD) {
atomic_add_64 (&threads[ vt->??? ]->traptypecount[ vt->tno ], +1);
if (vt->tno == 0x108/*syscall32*/ || vt->tno == 0x140/*syscall64*/) {
atomic_add_64 (&threads[ vt->??? ]->syscallscount[ vt->syscallno ], +1);
} else if (vt->tno == 0x060/*mondo*/) {
if (ISCPUID(vt->intrsid))
atomic_add_64 (&threads[ vt->??? ]->mondoxcalcount[ vt->intrsid ], +1);
else
// @@@
atomic_add_64 (&threads[ vt->??? ]->mondoinocount[ vt->intrino ], +1);
}
} else if (mode == MODE_SEL_CPU) {
} else if (mode == MODE_SEL_MMUCNTX) {
} else if (mode == MODE_SEL_THREAD) {
#endif
}
#if !BAD14
if (vt->tno == 0x04e && vt->cpuid == 0) {
++ intervalsCounter;
if ((intervalsCounter % 100) == 0) {
fprintf(stderr, "trapcount info: Solaris Seconds = %lld\n",
intervalsCounter/100);
//
// later this is where we'll format output
// according to the current `mode'.
//
}
}
#endif
return 0;
}
// when a tlb entry is updated automatically by a hw-table-walk...
//
int trapcount::tlb (VCPU_TLB * ti) // ================================== TLB
{
if (!CountingOn) return 0;
return 0;
}
// these are dma and string records generated (asynchronously)
// by device simulators
//
int trapcount::async (VCPU_AsyncData * di) // ========================== ASYNC
{
if (!CountingOn) return 0;
return 0;
}
// local- and global- time-sync records
//
int trapcount::sync (VCPU_Sync * si) // ================================ SYNC
{
if (!CountingOn) return 0;
return 0;
}
// ----- out-of-place and/or misnamed functions ... ? -----
//
int trapcount::process_ui_cmd (int argc, char *argv[])
{
return 1;
}
int trapcount::parse_args_v4(int argc, const char * tmp_argv[])
{
return 1;
}
int trapcount::parse_args_v5(int argc, const char * tmp_argv[])
{
return 1;
}
void trapcount::trace_on()
{
}
void trapcount::print_status()
{
}
void trapcount::trace_off()
{
}
#if 0 // ---------------- implementation notes ---------------- //
/*
* Mondo-Interrupts:
*
* Cross-cpu interrupts are Mondo (0x060) traps with
* IRSR (Interrupt Receive Status Register)
* contains the source [NodeId, x, AgentId]
* IRDR0 (Interrupt Receive Data Register #0)
* contains the entry point address of
* the requested service function !!!
*
* External interrupts are also Mondo (0x060) traps with
* IRSR (Interrupt Receive Status Register)
* contains the source [NodeId, x, AgentId]
* IRDR0 (Interrupt Receive Data Register #0)
* contains the (Solaris assigned) INO
* of the device, where:
* INO = NodeID << 11 | AgentID << 6 | DeviceNo
*
* the IRSR contains a "busy-bit" in between the NodeID and AgentID
* so the combined SystemID is not in contiguous bits :-(((.
* the INO does not, its SystemID are contiguous bits :-)!
*
*
*
* Not to be confused with SoftInts which are traps 0x041...0x04f
*
* Note that SoftInt 0x04e is (oxymoronically) hardware generated,
* and is the stick interval timer.
* Except...., in S10 the INO-DeviceID 0x035 interrupts come from Schizo
* for the pci_pbm_dma_sync() calls in the drivers, and they also set
* SoftInt 0x04e :-(((((!~ Yeuch!!!
*
*
*/
#endif