// ========== Copyright Header Begin ==========================================
// OpenSPARC T2 Processor File: rstracer.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 ============================================
/* rstracer.cc -- rstrace for SAM v5 */
#include "rstzip/Rstzip.H"
#include "dev_registry.h"
static class rstracer
* thetracer
= NULL
;
// non-reentrant function, called from the UI thread
// when the module is loaded
extern "C" void vtracer_fini();
extern "C" void * vtracer_init(const char *tmp_modname
)
thetracer
= new rstracer(tmp_modname
);
return (void *) thetracer
;
//non-reentrant function, called from the UI thread
int rstrace_cmd_action(void * /* usrdata */, int argc
, char **tmp_argv
)
return thetracer
->process_ui_cmd(argc
, tmp_argv
);
extern void UI_register_cmd_2 (char * name
, char *help
, int (*efn
)(void *, int, char **), int (*hfn
)());
extern void UI_invalidate_cmd (char * name
);
char help_str
[] = "rstrace -o <file> -n <icount>";
#define MASK_PSTATE_AM(SA_pstate) ( (((SA_pstate)>>3)&1) ? ((uint64_t)(~0u)) : (~0ull) )
// non-reentrant function, called from the UI thread
rstracer::rstracer(const char * tmp_modname
)
modname
= strdup(tmp_modname
);
// FIXME: get ncpus from the VTracer_SAM_intf
last_vcpu_id
= g_vcpu_id_max
;
pcs
= new rst_pct
[last_vcpu_id
+ 1];
mutex_init(&mu
, USYNC_THREAD
, NULL
);
// FIXME: this is stricly temporary. to be replaced by ui_cmd method
UI_register_cmd_2(strdup("rstrace"), help_str
, rstrace_cmd_action
, NULL
);
// non-reentrant function, called when module is loaded
int rstracer::attach(VTracer_SAM_intf
* sam_intf
) {
"rstrace # print rstracer status\n"
"rstrace off # turn off tracing and close trace file\n"
"rstrace [-o <file>] [-n <insts-per-cpu>] [-d <initial-delay>] [-x <ntraces> [-p <period>]]\n"
"Alternative rstrace command format:\n"
"rstrace <file> [+<delay>] [<totalinsts>] [<ntraces> [<period>]]\n"
" <file> should be base name to which cpu<n>.rz3.gz is appended.\n"
" <file> can be - for default /tmp/rstracer<pid>_<date>.cpu<n>.rz3.gz\n"
" +<delay> can be +0 or + for immediate; must be specified\n"
" period is the interval between starting pts of periodic traces\n"
" period assumed to be equal to trace size unless larger value specified\n"
" totalinsts, if unspecified, is indefinite (until next rstrace off cmd)\n";
// FIXME: add sampling options
// non-reentrant function, called from UI thread
int rstracer::process_ui_cmd(int argc
, char **tmp_argv
)
insts_per_cpu
= LLONG_MAX
;
// argv[1] can be "off", -, filename or -flag
if (strcmp(tmp_argv
[1], "off") == 0) {
fprintf(stderr
, "%s: tracing is already off\n", id
);
// FIXME: blaze must be stopped
ntraces
= 0; // reset target number of traces
// tracing must be off for any other command
fprintf(stderr
, "%s: ERROR: tracing is currently on. Usage: %s", id
, usage
);
if ((strcmp(tmp_argv
[1], "-") == 0) || (tmp_argv
[1][0] != '-')) {
if (parse_args_v4(argc
, (const char **) tmp_argv
) != 0) {
if (parse_args_v5(argc
, (const char **) tmp_argv
) != 0) {
// if we are still here, this was a trace start command
} // int rstracer::process_ui_cmd(int argc, char **tmp_argv)
// non-reentrant function - called from process_ui_cmd()
int rstracer::parse_args_v4(int argc
, const char * tmp_argv
[])
fprintf(stderr
, "%s: ERROR: insufficient number of arguments. Usage: \n%s", id
, usage
);
if (strcmp(tmp_argv
[1], "-") == 0) {
// use default trace file name
// FIXME: the legacy format expects to append the time & date to the filename?
strcpy(tracefilename
, tmp_argv
[1]);
if (tmp_argv
[2][0] != '+') {
fprintf(stderr
, "%s: ERROR: second argument must be +[<delay>]. Usage: \n%s", id
, usage
);
if (tmp_argv
[2][1] == 0) {
initial_delay
= strtoll(tmp_argv
[2] + 1, NULL
, 0);
int64_t trsize
= strtoll(tmp_argv
[3], NULL
, 0);
insts_per_cpu
= (trsize
+ ncpus
- 1) / ncpus
;
ntraces
= (int) strtol(tmp_argv
[4], NULL
, 0);
int64_t psize
= (int) strtoll(tmp_argv
[4], NULL
, 0);
trace_period
= (psize
+ ncpus
- 1)/ncpus
;
if (trace_period
< insts_per_cpu
) {
fprintf(stderr
, "%s: WARNING: period (%lld insts/cpu) < trace size (%lld insts/cpu). ignoring.\n",
id
, trace_period
, insts_per_cpu
);
trace_period
= insts_per_cpu
;
fprintf(stderr
, "%s: ERROR: too many arguments. Usage: \n%s", id
, usage
);
} // void rstracer::parse_args_v4(int argc, const char * tmp_argv[])
// non-reentrant function - called from process_ui_cmd()
int rstracer::parse_args_v5(int argc
, const char * tmp_argv
[])
// preferred command format
const char * arg
= tmp_argv
[i
++];
if (strcmp(arg
, "-o") == 0) {
fprintf(stderr
, "%s: ERROR: -o requires an argument. Usage: \n%s", id
, usage
);
strcpy(tracefilename
, tmp_argv
[i
++]);
} else if (strcmp(arg
, "-n") == 0) {
fprintf(stderr
, "%s: ERROR: -n requires an argument. Usage: \n%s", id
, usage
);
insts_per_cpu
= strtoll(tmp_argv
[i
++], NULL
, 0);
} else if (strcmp(arg
, "-d") == 0) {
fprintf(stderr
, "%s: ERROR: -d requires an argument. Usage: \n%s", id
, usage
);
initial_delay
= strtoll(tmp_argv
[i
++], NULL
, 0);
} else if(strcmp(arg
, "-x") == 0) {
fprintf(stderr
, "%s: ERROR: -x requires an argument. Usage: \n%s", id
, usage
);
ntraces
= (int) strtol(tmp_argv
[i
++], NULL
, 0);
} else if (strcmp(arg
, "-p") == 0) {
fprintf(stderr
, "%s: ERROR: -x requires an argument. Usage: \n%s", id
, usage
);
trace_period
= strtoll(tmp_argv
[i
++], NULL
, 0);
fprintf(stderr
, "%s: ERROR: invalid argument %s. Usage: \n%s", id
, arg
, usage
);
if (trace_period
< insts_per_cpu
) {
fprintf(stderr
, "%s: WARNING: period (%lld insts/cpu) < trace size (%lld insts/cpu). ignoring.\n",
id
, trace_period
, insts_per_cpu
);
trace_period
= insts_per_cpu
;
} // void rstracer::parse_args_v5(int argc, const char * tmp_argv[])
// NON-REENTRANT function
// called from parse_args() (while blaze is stopped) from the UI thread.
// also called from trace_off() (while all cpus are done tracing) in case
// one more trace is needed. In that case, trace_off() should not modify
void rstracer::trace_on()
if (tracefilename
[0] == 0) {
// default trace file name
time_t curlocaltime
= time(NULL
);
struct tm
* localtm
= localtime(&curlocaltime
);
sprintf(fname
, "/tmp/rstrace%d_%04d%02d%02d_%02d%02d%02d",
getpid(), localtm
->tm_year
+1900, localtm
->tm_mon
, localtm
->tm_mday
,
localtm
->tm_hour
, localtm
->tm_min
, localtm
->tm_sec
);
strcpy(fname
, tracefilename
);
sprintf(str
, ".trace%03d", traces_done
);
for (i
=0; i
<=last_vcpu_id
; i
++) {
// if there is no delay to the trace, do not synchronize
pcs
[i
].state
= delay
? rst_pct::state_DELAY
: rst_pct::state_TRACE_START
;
} // void rstracer::trace_on()
// NON-REENTRANT function - called from trace_on()
void rst_pct::init(int arg_cpuid
, const char * tmp_tracefilename
)
icontext
= dcontext
= ~0u;
pc_pavadiff
= ea_pavadiff
= ~0ull;
state
= rst_pct::state_NIL
;
rstf_regvalT_set_cpuid(®val
, cpuid
);
regval
.regtype
[0] = regval
.regtype
[1] = RSTREG_UNUSED_RT
;
memcache
= new uint64_t [RSTF_MEMVAL_CACHE_LINES
];
for (i
=0; i
<RSTF_MEMVAL_CACHE_LINES
; i
++) {
memset(&mv64
, 0, sizeof(mv64
));
rstf_memval64T_set_cpuid(&mv64
, cpuid
);
memset(&mv128
, 0, sizeof(mv64
));
rstf_memval128T_set_cpuid(&mv128
, cpuid
);
sprintf(fname
, "%s.cpu%d.rz3.gz", tmp_tracefilename
, cpuid
);
int rzerr
= rz
->open(fname
, "w", "verbose=0");
if (rzerr
!= RSTZIP_OK
) {
sprintf(fname
, "%s.cpu%d.rst", tmp_tracefilename
, cpuid
);
} // void rst_pct::init(int arg_cpuid, const char * tmp_tracefilename)
static const uint8_t CH_MMU_CONTEXTREG_ASI
= 0x58;
static const uint8_t UA_MMU_CONTEXTREG_ASI
= 0x21; // ultrasparc arch 2005 and newer
// REENTRANT function - called from per cpu instr callback
void rst_pct::emit_trace_preamble()
printf("%s: starting trace for cpu%d\n", id
, cpuid
);
ru
.proto
.rtype
= RSTHEADER_T
;
ru
.header
.majorVer
= RSTF_MAJOR_VERSION
;
ru
.header
.minorVer
= RSTF_MINOR_VERSION
;
sprintf(ru
.header
.header_str
, "%s v%s", RSTF_MAGIC
, RSTF_VERSION_STR
);
ru
.tlevel
.rtype
= TRACEINFO_T
;
ru
.tlevel
.rtype2
= RSTT2_NLEVEL_T
;
time_t curtime
= (uint64_t) time(NULL
);
ru
.tlevel
.time64
= curtime
;
memset(&ru
, 0, sizeof(ru
));
ru
.cpuinfo
.rtype
= TRACEINFO_T
;
ru
.cpuinfo
.rtype2
= RSTT2_CPUINFO_T
;
ru
.cpuinfo
.numcpus
= 1; // in this cpu's trace
ru
.cpuinfo
.min_cpu_id
= ru
.cpuinfo
.max_cpu_id
= cpuid
;
memset(&ru
, 0, sizeof(ru
));
ru
.cpuidinfo
.rtype
= TRACEINFO_T
;
ru
.cpuidinfo
.rtype2
= RSTT2_CPUIDINFO_T
;
ru
.cpuidinfo
.cpuids
[0] = cpuid
;
// descriptor string records
sprintf(desc
, "SAM [rstracer.so]");
localtime_r((const time_t *)&curtime
, &localtm
);
sprintf(desc
, "date=%04d-%02d-%02d_%02d:%02d:%02d",
localtm
.tm_year
+1900, localtm
.tm_mon
+1, localtm
.tm_mday
,
localtm
.tm_hour
, localtm
.tm_min
, localtm
.tm_sec
);
sprintf(desc
, "<SAMinfo>");
sprintf(desc
, "blz::version=%s", SYSTEM_get_infostr());
// FIXME: in the next putback, replace this direct access
// to sam internal structures with the system abstraction
extern devRegistry
* samDevs
;
const char * devname
= samDevs
->getName(devid
);
if (strcmp(devname
, "unknown device") == 0) {
int namelen
= strlen(devname
);
if (namelen
> 18) namelen
= 18;
// output a record for this device
rstf_devidstrT devidstr
= {0};
devidstr
.rtype
= DEVIDSTR_T
;
strncpy(devidstr
.str
, devname
, namelen
);
addrec((rstf_unionT
*)&devidstr
);
sprintf(desc
, "blz::ncpus=%d", g_nvcpu
);
Vcpu
* my_vcpu
= g_vcpu
[cpuid
];
VCPU_TLB
* tlb_entries
= NULL
;
int n_entries
= my_vcpu
->get_tlb_entries(tlb_entries
);
rstf_tlbT_set_cpuid(&tlbrec
, cpuid
);
for (i
=0; i
<n_entries
; i
++) {
tlbrec
.tlb_type
= tlb_entries
[i
].tlb_type
;
tlbrec
.tlb_index
= tlb_entries
[i
].tlb_index
;
tlbrec
.tlb_no
= tlb_entries
[i
].tlb_no
;
tlbrec
.tte_tag
= tlb_entries
[i
].tte_tag
;
tlbrec
.tte_data
= tlb_entries
[i
].tte_data
;
// FIXME: this needs changes in rst, rstzip etc.
// for sun4v, we use the field currently named "unused16" for context
// and bit 0 of the field "unused" for "is_real"
if (tlb_entries
[i
].format
== 1) { // sun4v
tlbrec
.unused16
= tlb_entries
[i
].tte_context
;
tlbrec
.unused
= tlb_entries
[i
].is_real
;
addrec((rstf_unionT
*)&tlbrec
);
int rv
= my_vcpu
->get_reg(VCPU_HPR_HPSTATE
, &v64
);
if (my_vcpu
->get_reg(VCPU_HPR_0
+ i
, &v64
) == 0) {
add_regval(RSTREG_HPRIV_RT
, i
, v64
);
int regid
= VCPU_PR_0
+ i
;
if (my_vcpu
->get_reg(regid
, &v64
)==0) {
add_regval(RSTREG_PRIV_RT
, i
, v64
);
if (regid
== VCPU_PR_PSTATE
) {
mask_pstate_am
= MASK_PSTATE_AM(v64
);
// regvals: trap-level regs
my_vcpu
->get_reg(VCPU_PR_TL
, &curtl
);
for (tl
=1; tl
<=curtl
; tl
++) {
my_vcpu
->set_reg(VCPU_PR_TL
, tl
);
my_vcpu
->get_reg(VCPU_PR_TPC
, &v64
);
add_regval(RSTREG_PRIV_RT
, RSTREG_TPC_RBASE
+ 8*0 + tl
, v64
);
my_vcpu
->get_reg(VCPU_PR_TNPC
, &v64
);
add_regval(RSTREG_PRIV_RT
, RSTREG_TPC_RBASE
+ 8*1 + tl
, v64
);
my_vcpu
->get_reg(VCPU_PR_TSTATE
, &v64
);
add_regval(RSTREG_PRIV_RT
, RSTREG_TPC_RBASE
+ 8*2 + tl
, v64
);
my_vcpu
->get_reg(VCPU_PR_TT
, &v64
);
add_regval(RSTREG_PRIV_RT
, RSTREG_TPC_RBASE
+ 8*3 + tl
, v64
);
my_vcpu
->set_reg(VCPU_PR_TL
, curtl
);
if (my_vcpu
->get_reg(VCPU_ASR_0
+ i
, &v64
)==0) {
add_regval(RSTREG_OTHER_RT
, i
, v64
);
my_vcpu
->get_reg(VCPU_IRF_0
+ i
, &v64
);
add_regval(RSTREG_INT_RT
, i
, v64
);
// for dregs, the regnum encoding is same as in sparcv9
// regid is EVEN, and up to 6 bits (0, 2, .. 62).
// regnum = {regid[4:1],regid[5]}
for (regid
=0; regid
<64; regid
+=2) {
int regnum
= (regid
& 0x1e) | (regid
>> 5);
my_vcpu
->get_reg(VCPU_DRF_0
+ regid
/2, &v64
);
add_regval(RSTREG_FLOAT_RT
, 32+(regid
/2), v64
);
// icontext and dcontext regs
// FIXME: using magic numbers from UltraSPARC architecture for now
if ((my_vcpu
->config
.cpu_type
& VCPU_IMPL_SIM_MASK
) == VCPU_IMPL_SIM_BLAZE
) {
mmu_asi
= CH_MMU_CONTEXTREG_ASI
;
mmu_asi
= UA_MMU_CONTEXTREG_ASI
;
my_vcpu
->get_asi(mmu_asi
, RSTREG_MMU_PCONTEXT
, reg64
); pcontext
= (uint32_t) reg64
;
my_vcpu
->get_asi(mmu_asi
, RSTREG_MMU_SCONTEXT
, reg64
); scontext
= (uint32_t) reg64
;
add_regval(RSTREG_MMU_RT
, RSTREG_MMU_PCONTEXT
, pcontext
);
add_regval(RSTREG_MMU_RT
, RSTREG_MMU_SCONTEXT
, scontext
);
} // void rst_pct::emit_trace_preamble()
void rst_pct::addrec(rstf_unionT
* ru
)
rv
= fwrite(ru
, sizeof(rstf_unionT
), 1, trf
);
if (rv
!= 1) perror(fname
);
} // void rst_pct::addrec(rstf_unionT * ru)
void rst_pct::string2rst(const char * str
)
int n
= (int) strlen(str
);
// the last strdesc record contains 22 bytes (and a zero byte to terminate)
// each preceding strcont record contains 23 bytes
// thus total number of records is: strlen/23 + 1
for (i
=0; i
<nsr
-1; i
++) {
ru
.string
.rtype
= STRCONT_T
;
strncpy(ru
.string
.string
, str
, 23);
ru
.string
.rtype
= STRDESC_T
;
strcpy(ru
.string
.string
, str
);
} // void rst_pct::string2rst(const char * str)
void rst_pct::add_regval(int rstregtype
, int rstregid
, uint64_t v64
) {
if (regval
.regtype
[0] == RSTREG_UNUSED_RT
) {
regval
.regtype
[0] = rstregtype
;
regval
.regid
[0] = rstregid
;
regval
.regtype
[1] = rstregtype
;
regval
.regid
[1] = rstregid
;
} // void rst_pct::add_regval() {
void rst_pct::flush_regval()
if(regval
.regtype
[0] != RSTREG_UNUSED_RT
) {
addrec((rstf_unionT
*) ®val
);
regval
.regtype
[0] = regval
.regtype
[1] = RSTREG_UNUSED_RT
;
} // void rst_pct::flush_regval()
const uint64_t MEMCACHE_TAGMASK
= ~((uint64_t)(RSTF_MEMVAL_CACHE_BLOCKSIZE
-1));
int rst_pct::memcache_ref(uint64_t pa
)
uint64_t tag
= pa
& MEMCACHE_TAGMASK
;
uint64_t idx
= tag
& (RSTF_MEMVAL_CACHE_LINES
-1);
int rv
= (tag
== memcache
[idx
]);
// output memvals: 2x memval64, 7x memval128
mv64
.val
= memread64u(mm1
, addr
);
addrec((rstf_unionT
*) &mv64
);
mv64
.val
= memread64u(mm1
, addr
);
addrec((rstf_unionT
*) &mv64
);
rstf_memval128T_set_addr(&mv128
, addr
);
mv128
.val
[0] = memread64u(mm1
, addr
);
mv128
.val
[1] = memread64u(mm1
, addr
);
addrec((rstf_unionT
*)&mv128
);
} // int rst_pct::memcache_ref(uint64_t pa)
// this function is called from a cpu after all cpus are done
// tracing, or the rstrace off ui command. In either case,
void rstracer::trace_off()
fprintf(stderr
, "%s: tracing is already off\n", id
);
printf("%s: finalizing trace(s)...\n", id
);
for (i
=0; i
<=last_vcpu_id
; i
++) {
if (traces_done
>= ntraces
) {
delay
= trace_period
- insts_per_cpu
;
} // void rstracer::trace_off()
if (state
== rst_pct::state_NIL
)
printf("%s: cpu%d: trace written to %s - %lld insts, %lld records\n", id
, cpuid
, fname
, ninsts
, nrecs
);
} // void vtrace_per_cpu_tracer::fini()
void rstracer::print_status()
// use cpu0 to identify status
printf("%s: idle\n", id
);
enum rst_pct::state_e state
= pcs
[first_vcpu_id
].state
;
case rst_pct::state_DELAY
:
dleft
= (delay
-pcs
[first_vcpu_id
].dinsts
);
if (dleft
< 0) dleft
= 0;
printf("%s: in delayed tracing mode. remaining delay is approx %lld insts/cpu (%lld total)\n",
printf(" %d traces out of %d done\n", traces_done
, ntraces
);
case rst_pct::state_TRACING
:
case rst_pct::state_TRACE_START
:
case rst_pct::state_WAIT_SYNC_START
:
case rst_pct::state_WAIT_START
:
case rst_pct::state_WAIT_SYNC_STOP
:
case rst_pct::state_WAIT_STOP
:
printf("%s: tracing: approx %lld insts/cpu out of %lld done\n",
id
, pcs
[first_vcpu_id
].ninsts
, insts_per_cpu
);
printf(" trace number %d of 0..%d in progress\n", traces_done
, ntraces
-1);
fprintf(stderr
, "%s: ERROR: in invalid state (%d)\n", id
, state
);
} // void rstracer::print_status()
static const int vcpu_rtype_to_rst
[] = {
RSTREG_UNUSED_RT
, // unused in vcpu
RSTREG_FLOAT_RT
, // single
RSTREG_FLOAT_RT
, // double
RSTREG_HPRIV_RT
, // hyperprivileged registers
#define RSTRACER_IOP_IS_FLUSH(_IOP_) (((_IOP_)==SPIX_SPARC_IOP_FLUSH)||((_IOP_)==SPIX_SPARC_IOP_FLUSHA))
int rstracer::instr(VCPU_Instruction
* ii
)
if (pcs
[cpuid
].state
!= rst_pct::state_TRACING
) { // rule out common case quickly
switch(pcs
[cpuid
].state
) {
case rst_pct::state_DELAY
:
if (pcs
[cpuid
].dinsts
>= delay
) {
pcs
[cpuid
].state
= rst_pct::state_WAIT_SYNC_START
;
return instr(ii
); // recursive call (with changed state)
case rst_pct::state_WAIT_SYNC_START
:
// increment sync_count. if ==ncpu, start everyone
if (sync_count
== ncpus
) {
for (i
=0; i
<ncpus
; i
++) {
pcs
[i
].state
= rst_pct::state_TRACE_START
;
pcs
[cpuid
].state
= rst_pct::state_WAIT_START
;
return instr(ii
); // recursive call (with changed state)
case rst_pct::state_WAIT_START
:
case rst_pct::state_WAIT_SYNC_STOP
:
// increment sync_count. if ==ncpu, start everyone
if (sync_count
== ncpus
) {
pcs
[cpuid
].state
= rst_pct::state_WAIT_STOP
;
return instr(ii
); // recursive call (with changed state)
case rst_pct::state_WAIT_STOP
:
case rst_pct::state_TRACE_START
:
pcs
[cpuid
].emit_trace_preamble();
pcs
[cpuid
].state
= rst_pct::state_TRACING
;
// include this instruction in trace
return instr(ii
); // recursive call (with changed state)
fprintf(stderr
, "%s: ERROR: rstracer::instr() - invalid state (%d)\n",
id
, (int) pcs
[cpuid
].state
);
} // if not state_TRACING
// in state_TRACING: generate trace records for current instruction
memset(&ru
, 0, sizeof(ru
));
memset(&pd
, 0, sizeof(pd
));
// vcpu:pr may already have changed; instr.pr should reflect pr before instr retired
ru
.instr
.hpriv
= pcs
[cpuid
].hpr
;
ru
.instr
.pr
= pcs
[cpuid
].pr
;
rstf_trapping_instrT ti
= {0};
ti
.rtype
= TRAPPING_INSTR_T
;
rstf_trapping_instrT_set_cpuid(&ti
, ii
->cpuid
);
ti
.hpriv
= ru
.instr
.hpriv
;
ti
.pc_va
&= pcs
[cpuid
].mask_pstate_am
;
pcs
[cpuid
].addrec((rstf_unionT
*)&ti
);
pcs
[cpuid
].memcache_ref(ii
->pc_pa
);
bool need_pavadiff
= false;
// generate instr, pavadiff records
ru
.proto
.rtype
= INSTR_T
;
rstf_instrT_set_cpuid(&ru
.instr
, cpuid
);
pd
.icontext
= ii
->icontext
;
if (ii
->icontext
!= pcs
[cpuid
].icontext
) {
pcs
[cpuid
].icontext
= ii
->icontext
;
ru
.instr
.pc_va
= ii
->pc_va
;
ru
.instr
.pc_va
&= pcs
[cpuid
].mask_pstate_am
;
pd
.pc_pa_va
= ii
->pc_pa
- ru
.instr
.pc_va
;
if (pd
.pc_pa_va
!= pcs
[cpuid
].pc_pavadiff
) {
pcs
[cpuid
].pc_pavadiff
= pd
.pc_pa_va
;
ru
.instr
.instr
= ii
->opcode
;
spix_sparc_iop_t iop
= spix_sparc_iop(SPIX_SPARC_V9
, &(ii
->opcode
));
bool is_done_retry
= false;
if (spix_sparc_iop_isload(iop
) || spix_sparc_iop_iscstore(iop
) || spix_sparc_iop_isustore(iop
) ||
RSTRACER_IOP_IS_FLUSH(iop
)) {
uint64_t ea_va
= ii
->ea_va
;
ea_va
&= pcs
[cpuid
].mask_pstate_am
;
uint64_t ea_pa
= ii
->ea_pa
;
pd
.ea_pa_va
= ea_pa
- ea_va
;
pd
.dcontext
= ii
->dcontext
;
if (ii
->itype
& (VCPU_LOAD_ITYPE
|VCPU_STORE_ITYPE
)) {
pcs
[cpuid
].memcache_ref(ea_pa
);
} else if (ii
->exception
) {
// output trapping instr record
rstf_trapping_instrT ti
= {0};
rstf_trapping_instrT_set_cpuid(&ti
, cpuid
);
ti
.rtype
= TRAPPING_INSTR_T
;
ti
.hpriv
= ru
.instr
.hpriv
;
ti
.instr
= ru
.instr
.instr
;
pcs
[cpuid
].addrec((rstf_unionT
*)&ti
);
} else if (ii
->itype
& (VCPU_ASI_LOAD_ITYPE
|VCPU_ASI_STORE_ITYPE
)) {
//ld/st to internal asi - PA not relevant
} else { // ea_pa is 0x0 but there is no exception. should be a prefetch instr
if (! spix_sparc_iop_isprefetch(iop
) && ! RSTRACER_IOP_IS_FLUSH(iop
)) {
fprintf(stderr
, "WARNING: rstracer: cpu%d rec%lld: ea_pa==0 and exception==0???",
cpuid
, pcs
[cpuid
].nrecs
);
} else if (spix_sparc_iop_isdcti(iop
)) {
g_vcpu
[cpuid
]->get_reg(ii
->annul
? VCPU_ASR_PC
:VCPU_ASR_NPC
, &ru
.instr
.ea_va
);
ru
.instr
.ea_va
&= pcs
[cpuid
].mask_pstate_am
;
} else if (iop
== SPIX_SPARC_IOP_RETRY
) {
g_vcpu
[cpuid
]->get_reg(VCPU_ASR_PC
, &v64
);
ru
.instr
.ea_va
= v64
& pcs
[cpuid
].mask_pstate_am
;
int rv
= g_vcpu
[cpuid
]->get_reg(VCPU_HPR_HPSTATE
, &v64
);
pcs
[cpuid
].hpr
= (v64
>>2) & 1;
g_vcpu
[cpuid
]->get_reg(VCPU_PR_PSTATE
, &pstate64
);
if (pcs
[cpuid
].hpr
== 0) {
pcs
[cpuid
].pr
= (pstate64
>> 2) & 1;
g_vcpu
[cpuid
]->get_reg(VCPU_PR_TL
, &tl64
);
pcs
[cpuid
].mask_pstate_am
= MASK_PSTATE_AM(pstate64
);
} else if (iop
== SPIX_SPARC_IOP_DONE
) {
g_vcpu
[cpuid
]->get_reg(VCPU_ASR_NPC
, &v64
);
ru
.instr
.ea_va
&= pcs
[cpuid
].mask_pstate_am
;
int rv
= g_vcpu
[cpuid
]->get_reg(VCPU_HPR_HPSTATE
, &v64
);
pcs
[cpuid
].hpr
= (v64
>>2) & 1;
g_vcpu
[cpuid
]->get_reg(VCPU_PR_PSTATE
, &pstate64
);
if (pcs
[cpuid
].hpr
== 0) {
pcs
[cpuid
].pr
= (pstate64
>> 2) & 1;
pcs
[cpuid
].mask_pstate_am
= MASK_PSTATE_AM(pstate64
);
g_vcpu
[cpuid
]->get_reg(VCPU_PR_TL
, &tl64
);
if (pd
.ea_pa_va
!= pcs
[cpuid
].ea_pavadiff
) {
pcs
[cpuid
].ea_pavadiff
= pd
.ea_pa_va
;
if (pd
.dcontext
!= pcs
[cpuid
].dcontext
) {
pcs
[cpuid
].dcontext
= pd
.dcontext
;
// ru.instr.tr = ii->dmmu_trap||ii->exception;
ru
.instr
.bt
= ii
->taken
; // FIXME - only if cti or cmov
// ALSO check if pcontext/scontext have changed
uint32_t newpcontext
, newscontext
;
if ((g_vcpu
[cpuid
]->config
.cpu_type
& VCPU_IMPL_SIM_MASK
) == VCPU_IMPL_SIM_BLAZE
) {
mmu_asi
= CH_MMU_CONTEXTREG_ASI
;
mmu_asi
= UA_MMU_CONTEXTREG_ASI
;
g_vcpu
[cpuid
]->get_asi(mmu_asi
, RSTREG_MMU_PCONTEXT
, reg64
);
newpcontext
= (uint32_t) reg64
;
g_vcpu
[cpuid
]->get_asi(mmu_asi
, RSTREG_MMU_SCONTEXT
, reg64
);
newscontext
= (uint32_t) reg64
;
if ((newpcontext
!= pcs
[cpuid
].pcontext
) || (newscontext
!= pcs
[cpuid
].scontext
)) {
pcs
[cpuid
].pcontext
= newpcontext
;
pcs
[cpuid
].scontext
= newscontext
;
pcs
[cpuid
].add_regval(RSTREG_MMU_RT
, RSTREG_MMU_PCONTEXT
, newpcontext
);
pcs
[cpuid
].add_regval(RSTREG_MMU_RT
, RSTREG_MMU_SCONTEXT
, newscontext
);
pcs
[cpuid
].flush_regval();
rstf_pavadiffT_set_cpuid(&pd
, cpuid
);
pcs
[cpuid
].addrec((rstf_unionT
*)&pd
);
memset(&pd
, 0, sizeof(pd
));
for (i
=0; i
<ii
->nregs
; i
++) {
// need to map vtracer regtypes to rst regtypes:
int regtype
= ii
->dreg
[i
].r
.type
;
int regid
= ii
->dreg
[i
].r
.id
;
if (regtype
== VCPU_FP_DOUBLE_RTYPE
) {
} else if (regtype
== VCPU_PR_RTYPE
&& regid
== VCPU_PR_PSTATE
) {
pcs
[cpuid
].pr
= (pstate64
>>2) & 1;
if (pcs
[cpuid
].hpr
) pcs
[cpuid
].pr
= 0;
pcs
[cpuid
].mask_pstate_am
= MASK_PSTATE_AM(pstate64
);
} else if (regtype
== VCPU_PR_RTYPE
&& regid
== VCPU_PR_TL
) {
} else if (regtype
== VCPU_HPR_RTYPE
) {
if (regid
== VCPU_HPR_HPSTATE
) {
uint64_t v64
= ii
->dval
[i
];
pcs
[cpuid
].hpr
= (v64
>> 2) & 1;
if (pcs
[cpuid
].hpr
) pcs
[cpuid
].pr
= 0;
pcs
[cpuid
].add_regval(vcpu_rtype_to_rst
[regtype
], regid
, ii
->dval
[i
]);
pcs
[cpuid
].flush_regval();
pcs
[cpuid
].add_regval(RSTREG_PRIV_RT
, RSTREG_PSTATE_R
, pstate64
);
pcs
[cpuid
].add_regval(RSTREG_PRIV_RT
, RSTREG_TL_R
, tl64
);
pcs
[cpuid
].flush_regval();
ru
.instr
.pc_va
= ii
->pc_va
+ 4;
if ((pcs
[cpuid
].ninsts
& 0xffffffull
) == 0) {
printf("%s: approx %lld insts/cpu traced (out of %lld max)\n",
id
, pcs
[cpuid
].ninsts
, insts_per_cpu
);
if (pcs
[cpuid
].ninsts
>= insts_per_cpu
) {
pcs
[cpuid
].state
= rst_pct::state_WAIT_SYNC_STOP
;
// in SAM v5, the trap call happens AFTER the trapping instruction call
int rstracer::trap ( VCPU_Trap
* ti
)
if (!tracing
|| (pcs
[cpuid
].state
!= rst_pct::state_TRACING
)) return 0;
rstf_trapT_set_cpuid(&tr
, cpuid
);
tr
.is_async
= ti
->is_async
;
if (ti
->tno
== 0x108 || ti
->tno
== 0x140) { /* syscalls */
tr
.syscall
= ti
->syscallno
;
} else if (ti
->tno
== 0x060) { /* mondo-intrs */
tr
.syscall
= ti
->intrino
;
// FIXME: the VCPU_Trap structure does not contain regiater values.
// get pstate and tl values
uint64_t tl64
, pstate64
, v64
;
int rv
= g_vcpu
[cpuid
]->get_reg(VCPU_HPR_HPSTATE
, &v64
);
pcs
[cpuid
].hpr
= (v64
>>2) & 1;
g_vcpu
[cpuid
]->get_reg(VCPU_PR_TL
, &tl64
);
g_vcpu
[cpuid
]->get_reg(VCPU_PR_PSTATE
, &pstate64
);
pcs
[cpuid
].pr
= (pstate64
>>2) & 1;
if (pcs
[cpuid
].hpr
) pcs
[cpuid
].pr
= 0;
pcs
[cpuid
].mask_pstate_am
= MASK_PSTATE_AM(pstate64
);
tr
.pstate
= (uint16_t) pstate64
;
pcs
[cpuid
].addrec((rstf_unionT
*) &tr
);
// in addition, emit regvals for pstate and tl
pcs
[cpuid
].add_regval(RSTREG_PRIV_RT
, RSTREG_TL_R
, tl64
);
pcs
[cpuid
].add_regval(RSTREG_PRIV_RT
, RSTREG_PSTATE_R
, pstate64
);
} //int rstracer::trap ( VCPU_Trap * ti)
int rstracer::tlb ( VCPU_TLB
* ti
)
if (!tracing
|| (pcs
[ti
->cpuid
].state
!= rst_pct::state_TRACING
)) return 0;
rstf_tlbT_set_cpuid(&tr
, ti
->cpuid
);
tr
.tlb_type
= ti
->tlb_type
;
tr
.tlb_index
= ti
->tlb_index
;
tr
.tte_tag
= ti
->tte_tag
;
tr
.tte_data
= ti
->tte_data
;
if (ti
->format
== 1) { // sun4v
tr
.unused16
= ti
->tte_context
;
pcs
[ti
->cpuid
].addrec((rstf_unionT
*)&tr
);
} // int rstracer::tlb ( VCPU_TLB * ti)
int rstracer::async(VCPU_AsyncData
* di
)
if (!tracing
|| (pcs
[first_vcpu_id
].state
!= rst_pct::state_TRACING
)) return 0;
if (di
->dma
.rtype
== DMA_T
) {
dr
.dma
.iswrite
= di
->dma
.iswrite
;
dr
.dma
.nbytes
= (int) di
->dma
.nbytes
;
dr
.dma
.start_pa
= di
->dma
.pa
;
dr
.dma
.devid
= di
->dma
.devid
;
pcs
[first_vcpu_id
].addrec(&dr
);
} else if (di
->strdata
.rtype
== STRDESC_T
) {
di
->strdata
.s
[22] = 0; // even if it is already 0
pcs
[first_vcpu_id
].string2rst(di
->strdata
.s
);
fprintf(stderr
, "%s: ERROR: invalid asyncdata rtype (%d)\n",
id
, (int) di
->strdata
.rtype
);
} // int rstracer::async(VCPU_AsyncData * di)
int rstracer::sync (VCPU_Sync
* si
)
if (!tracing
|| (pcs
[si
->cpuid
].state
!= rst_pct::state_TRACING
)) return 0;
ts
.subtype
= si
->synctype
;
ts
.sequence_number
= si
->syncid
;
pcs
[si
->cpuid
].addrec((rstf_unionT
*) &ts
);
int rstracer::hwop(VCPU_HwOp
* hi
)
if (!tracing
|| (pcs
[hi
->cpuid
].state
!= rst_pct::state_TRACING
)) return 0;
memset(&tr
, 0, sizeof(tr
));
rstf_tsb_accessT_set_cpuid(&tr
, hi
->cpuid
);
pcs
[hi
->cpuid
].addrec((rstf_unionT
*) &tr
);
} // int rstracer::hwop(VCPU_HwOp * hi)
char * cmd
= strdup("rstrace");
} // rstracer::~rstracer()