// ========== 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
* numcpus = g_nvcpu_max; system.h, non-transferable to trace reader :-(.
* UI_(un)register... ui.h, non-transferable to trace reader :-(.
// standard C includes -------------------------------
// 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!!!...
// boilerplate -------------------------------
// local definitions -------------------------------
#define BAD14 1 /* can't count 0x04e traps as interval timer any more */
// global variables -------------------------------
static int CountingOn
= 1;
// 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.
// x = SystemId = NodeId<<5+AgentId
#define ISCPUID(x) (get_vcpu(x) != NULL)
#define ISSCHIZOID(x) (get_vcpu(x) == NULL)
#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
);
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 */
fprintf(stderr
, "Error: trapcount already module_fini'ed\n");
Counters::Counters () // -------------------------------------------- ctor
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();
// mondoinocount.erase (mondoinocount.begin(), mondoinocount.end());
trapcount::trapcount (const char * tmp_modname
) // ======================= CTOR
printf("trapcount::ctor (modname=>\"%s\"), numcpus=%d \n",
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";
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
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
]);
if (argc
>= 2 && strcmp (argv
[1], "mode") == 0) { // mode
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
);
printf (" * unrecognized mode arg\n");
fprintf (stderr
, "trapcount mode is %s\n",
mode_names
[ the_trapcount
->mode
]);
if (argc
>= 2 && strcmp (argv
[1], "peek") == 0) { // peek
the_trapcount
->printCounts (0/*dont reset*/);
if (argc
>= 2 && (strcmp (argv
[1], "print") == 0 // print
|| strcmp (argv
[1], "stats") == 0)) { // stats
the_trapcount
->printCounts (1/*do reset*/);
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
);
if (strcmp (argv
[1], "on") == 0) { // On/Off
if (strcmp (argv
[1], "off") == 0) {
int trapcount::attach (VTracer_SAM_intf
* sam_intf
)
return 0; // do nothing...
// -------- friendly reminder --------
// int64 traptypecount[ MAX_TRAPTYPES ];
// int64 syscallscount[ MAX_SYSCALLS ];
// int64 mondoxcalcount[ MAX_NCPUS ];
// //int64 mondoinocount[ MAX_INOS ];
// inoarray64_t mondoinocount;
void trapcount::printCounts (int reset
) // ======================= print counts
printf (" aggregate: %12lld-instrs %12lld-kernel %12lld-user \n",
for (int i
=0; i
<MAX_TRAPTYPES
; i
++)
if (aggregate
->traptypecount
[ i
])
printf (" tt-0x%03x %12lld %s\n",
aggregate
->traptypecount
[ i
],
for (int i
=0; i
<MAX_SYSCALLS
; i
++)
if (aggregate
->syscallscount
[ i
])
printf (" sc-0x%03x %12lld %s\n",
aggregate
->syscallscount
[ i
],
for (inoiter ip
=aggregate
->mondoinocount
.begin();
ip
!=aggregate
->mondoinocount
.end();
if (ip
->second
!= 0) /*aggregate->mondoinocount[i] != 0)*/
printf (" zm-0x%03x %12lld (%02x,%02x) \n",
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",
aggregate
->mondoxcalcount
[ i
]);
if (reset
) aggregate
->Reset ();
// someday: put each cpu in a column...
for (int j
=0; j
<numcpus
; j
++) {
printf (" cpu[%d]::: %12lld-instrs %12lld-kernel %12lld-user \n",
for (int i
=0; i
<MAX_TRAPTYPES
; i
++)
if (cpus
[j
].traptypecount
[ i
])
printf (" tt-0x%03x %12lld %s\n",
cpus
[j
].traptypecount
[ i
],
for (int i
=0; i
<MAX_SYSCALLS
; i
++)
if (cpus
[j
].syscallscount
[ i
])
printf (" sc-0x%03x %12lld %s\n",
cpus
[j
].syscallscount
[ i
],
for (inoiter ip
=cpus
[j
].mondoinocount
.begin();
ip
!=cpus
[j
].mondoinocount
.end();
if (ip
->second
!= 0) /*cpus[j].mondoinocount[i] != 0)*/
printf (" md-0x%03x %12lld (%02x,%02x) \n",
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",
cpus
[j
].mondoxcalcount
[ i
]);
if (reset
) cpus
[j
].Reset ();
printf ("printing mode %s not currently supported\n", mode_names
[mode
]);
void trapcount::configure_for_mode (modes newmode
) // ======= configure mode
return; // we're already there...
aggregate
= new Counters
;
cpus
= new Counters
[MAX_NCPUS
]; // array, be careful at delete!
mmucntxs
= new MmucntxCounters
;
threads
= new ThreadCounters
;
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...
// deallocate previous counters
fprintf(stderr
, "Trapcount error: unimplemented mode\n");
// ================================= TRACER =================================
int trapcount::instr (VCPU_Instruction
* vi
) // ======================== INSTR
if (!CountingOn
) return 0;
if (vi
->ifetch_trap
) { // fake instruction in case analyzer needs pc
if (vi
->annul
) { // annulled (non-executed) delay slot instr
// being consistent with HW-perf-counters and cpustat, which do not count
// annulled, mispredicted, or trapped instructions - only retired ones.
spix_sparc_inst_t spixter
; spixter
.inst
= rsp
->instr
.instr
;
spix_sparc_iop(SPIX_SPARC_V9
, &((rsp
->instr
).instr
));
if (iop
== SPIX_SPARC_IOP_WRASR
) {
switch (spixter
.memarithr
.rd
) {
fprintf (stderr
, "SET_SOFTINT = 0x08x\n", ???);
fprintf (stderr
, "CLR_SOFTINT = 0x08x\n", ???);
case ASR_SFTINT
: /*0x16*/
fprintf (stderr
, "WR_SOFTINT = 0x08x\n", ???);
if (mode
== MODE_AGGREGATE
) {
++ aggregate
->instrcount
;
if (vi
->hpstate
& HPSTATE_PRIV
) {
++ aggregate
->hypercount
;
} else if (vi
->pstate
& PSTATE_PRIV
) {
++ aggregate
->kernelcount
;
} 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
;
++ cpus
[ vi
->cpuid
].usercount
;
} else if (mode
== MODE_BY_MMUCNTX
) {
++ mmucntxs
[ ??? ]->instrcount
;
if (vi
->pstate
& PSTATE_PRIV
) {
} else if (vi
->hpstate
& HPSTATE_PRIV
) {
++ aggregate
->hypercount
;
++ mmucntxs
[ ??? ]->kernelcount
;
++ mmucntxs
[ ??? ]->usercount
;
} else if (mode
== MODE_BY_THREAD
) {
int trapcount::trap (VCPU_Trap
* vt
) // ================================ TRAP
if (!CountingOn
) return 0;
if (!VERBOSE
) { /* these are too obnoxious to display normally... */
if (vt
->tno
>= 0x080 && vt
->tno
<= 0x0ff) /* window-miss */
if (vt
->tno
>= 0x024 && vt
->tno
<= 0x027) /* clean-win */
if (vt
->tno
>= 0x064 && vt
->tno
<= 0x06f) /* tlb-miss */
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 (ISCPUID(vt
->intrsid
))
fprintf(stderr
, " xcl SID = %d", vt
->intrsid
);
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",
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);
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);
atomic_add_64 (&cpus
[ vt
->cpuid
].mondoinocount
[ vt
->intrino
], +1);
} 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);
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);
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
) {
if (vt
->tno
== 0x04e && vt
->cpuid
== 0) {
if ((intervalsCounter
% 100) == 0) {
fprintf(stderr
, "trapcount info: Solaris Seconds = %lld\n",
// later this is where we'll format output
// according to the current `mode'.
// when a tlb entry is updated automatically by a hw-table-walk...
int trapcount::tlb (VCPU_TLB
* ti
) // ================================== TLB
if (!CountingOn
) return 0;
// these are dma and string records generated (asynchronously)
int trapcount::async (VCPU_AsyncData
* di
) // ========================== ASYNC
if (!CountingOn
) return 0;
// local- and global- time-sync records
int trapcount::sync (VCPU_Sync
* si
) // ================================ SYNC
if (!CountingOn
) return 0;
// ----- out-of-place and/or misnamed functions ... ? -----
int trapcount::process_ui_cmd (int argc
, char *argv
[])
int trapcount::parse_args_v4(int argc
, const char * tmp_argv
[])
int trapcount::parse_args_v5(int argc
, const char * tmp_argv
[])
void trapcount::trace_on()
void trapcount::print_status()
void trapcount::trace_off()
#if 0 // ---------------- implementation notes ---------------- //
* 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
* 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!!!