// ========== Copyright Header Begin ==========================================
// OpenSPARC T2 Processor File: stracer.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 ============================================
////////////////////////////////////////////////////////////
// Copyright (C) 2005-2007 Sun Microsystems, Inc.
#include "cpu_interface.h"
// set to 1 to debug this module
//////////////////////////////////////////
char *modes
[] = { "u", "p", "h" };
typedef enum { MODE_USER
, MODE_PRIV
, MODE_HVISOR
} prmode_t
;
// list of open debug tracers
static STracerContainer g_stracer_list
;
////////////////////////////////////////////////////////////
// each debug tracer is associated with output stream;
// tracers could shared to output trace to the same stream;
static char console
[] = "console";
STracer
* STracerContainer::alloc(const char *fname
,SymTable
*symtable
)
// check if tracer is opened already
if (strcmp(s
->trace_name
, fname
)==0)
s
= new STracer(fname
,symtable
);
s
->ref
++; // update ref counter
int STracerContainer::dealloc (STracer
*t
)
// remove it from the list
int STracerContainer::display(FILE *out
)
fprintf (out
,"trace to %s\n", s
->trace_name
);
bool STracerContainer::is_found(STracer
*t
)
///////////////////////////////////////////////////////////////////////
// debug tracer constructor
STracer::STracer (const char *fname
, SymTable
*symtable
)
if (fname
&& fname
!= console
)
trace_name
= strdup(fname
);
if ( trace_name
== console
)
stream
= ui
->get_output_file();
stream
= fopen (trace_name
, "w");
ui
->error("cannot open %s, trace to output\n", trace_name
);
stream
= ui
->get_output_file();
// set the stream to be unbuffered;
// tracing will b slower but we don't lose trace data
if (stream
!= ui
->get_output_file())
if (trace_name
!= console
)
int STracer::attach(VTracer_SAM_intf
* /* intf */) { return 1; }
///////////////////////////////////////////////////////////////
// print values from the trace record
static int print_regs_update
int nregs
, // total number of destination regs written
VCPU_RegType
*dreg
, // destination register type and index
uint64_t *dval
, // destination reg value
FILE *stream
// default is ui output
Vcpu
* cpu
= g_vcpu
[cpuid
];
for (int i
=0; i
<nregs
; i
++)
int rindex
= dreg
[i
].r
.id
;
const char format
[] = "%i:\t%s=0x%llx\n";
fprintf (stream
, format
, cpuid
, cpu
->get_reg_name(VCPU_PR_0
+ rindex
), dval
[i
]);
fprintf (stream
, format
, cpuid
, cpu
->get_reg_name(VCPU_ASR_0
+ rindex
), dval
[i
]);
fprintf (stream
, format
, cpuid
, cpu
->get_reg_name(VCPU_IRF_0
+ rindex
), dval
[i
]);
case VCPU_FP_SINGLE_RTYPE
:
fprintf (stream
, format
, cpuid
, cpu
->get_reg_name(VCPU_FRF_0
+ rindex
), dval
[i
]);
case VCPU_FP_DOUBLE_RTYPE
:
// @@ rindex is 0, 2, 4, .. , 62
fprintf (stream
, format
, cpuid
, cpu
->get_reg_name(VCPU_DRF_0
+ rindex
/ 2), dval
[i
]);
fprintf (stream
, format
, cpuid
, cpu
->get_reg_name(VCPU_HPR_0
+ rindex
), dval
[i
]);
void print_value(uint64_t val
, uint64_t mask
, FILE *stream
)
for (int i
=60; i
>=0; i
-=4)
uint8_t hb
= uint8_t(val
>>i
)&0xf;
if(hb
==0) fprintf(stream
, "0");
else fprintf(stream
, "%1x", hb
);
int STracer::instr ( VCPU_Instruction
*i
)
disassemble (i
->opcode
, i
->pc_va
, ibuf
, LSIZE
);
prmode_t mode
= MODE_USER
;
uint32_t context
= i
->pc_type
==VCPU_VADDR
? i
->icontext
: Symbol::NO_CONTEXT
;
int id
= get_vcpu(i
->cpuid
)->id();
fprintf(stream
,"%i: ", id
);
if (sym_table
) sym_table
->fputs(i
->pc_va
, context
, stream
);
fprintf(stream
,"%llx", i
->pc_va
);
if (i
->pc_type
==VCPU_VADDR
) fprintf(stream
,"(%x)", i
->icontext
);
// pa, opcode, instr disassem, branch taken flag
fprintf(stream
," : %llx [%08x] %s %s %s\n",
i
->pc_pa
, i
->opcode
, modes
[mode
], ibuf
, i
->taken
? "(taken)":"");
print_regs_update (id
, i
->nregs
, i
->dreg
, i
->dval
, stream
);
fprintf(stream
,"%i:\tstore ", id
);
for(int ii
=0; ii
<i
->st_num
; ii
++)
//print_value(i->st_mem_value[ii],i->st_bitmask, stream);
fprintf(stream
, "%llx(%llx)", i
->st_mem_value
[ii
], i
->st_bitmask
);
if (i
->ea_type
== VCPU_VADDR
) // trace context
fprintf(stream
," to %llx(%x):%llx ", i
->ea_va
, i
->dcontext
, i
->ea_pa
);
fprintf(stream
," to %llx:%llx ", i
->ea_va
, i
->ea_pa
);
context
= Symbol::NO_CONTEXT
;
if (sym_table
) sym_table
->fputs(i
->ea_va
, context
, stream
);
fprintf(stream
,"%i:\tload ", id
);
for(int ii
=0; ii
<i
->ld_num
; ii
++)
//print_value(i->ld_mem_value[ii],i->ld_bitmask, stream);
fprintf(stream
," %llx(%llx)", i
->ld_mem_value
[ii
], i
->ld_bitmask
);
if (i
->ea_type
== VCPU_VADDR
) // trace context
fprintf(stream
," from %llx(%x):%llx ", i
->ea_va
, i
->dcontext
, i
->ea_pa
);
fprintf(stream
," from %llx:%llx ", i
->ea_va
, i
->ea_pa
);
context
= Symbol::NO_CONTEXT
;
if (sym_table
) sym_table
->fputs(i
->ea_va
, context
, stream
);
int STracer::trap ( VCPU_Trap
*t
)
int id
= get_vcpu(t
->cpuid
)->id();
fprintf(stream
,"%i: TRAP 0x%x @pc=0x%llx\n", id
, t
->tno
, t
->pc_va
);
print_regs_update (id
, t
->nregs
, t
->dreg
, t
->dval
, stream
);
int STracer::tlb ( VCPU_TLB
*s
)
int id
= get_vcpu(s
->cpuid
)->id();
fprintf(stream
,"%i: %s%d n:%d 0x%llx:0x%llx %s ", id
,
s
->tlb_type
==0 ? "ITLB":(s
->tlb_type
==1 ?"DTLB":"TLB"),
s
->demap
?"removed ":"inserted");
if (s
->v()) fprintf(stream
, "V ");
if (s
->nfo()) fprintf(stream
, "NFO ");
if (s
->is_real
) fprintf(stream
, "R ");
if (s
->ie()) fprintf(stream
, "IE ");
if (s
->e()) fprintf(stream
, "E ");
if (s
->cp()) fprintf(stream
,"CP ");
if (s
->cv()) fprintf(stream
,"CV ");
if (s
->p()) fprintf(stream
, "P ");
if (s
->ep()) fprintf(stream
, "EP ");
if (s
->w()) fprintf(stream
, "W ");
if (s
->l()) fprintf(stream
, "L ");
if (s
->g()) fprintf(stream
, "G ");
uint64_t actual_page_size
= (1ull << (13 + 3 * s
->tte_page_size
));
if (actual_page_size
>> 30) {
fprintf(stream
, ": %lldG, ", (actual_page_size
>>30));
} else if (actual_page_size
>> 20) {
fprintf(stream
, ": %lldM, ", (actual_page_size
>> 20));
fprintf(stream
, ": %lldK, ", (actual_page_size
>> 10));
fprintf(stream
, "ctx 0x%x", s
->tte_context
);
if (s
->format
) { // sun4v
fprintf(stream
, " partid 0x%x\n", s
->partid
);
int STracer::hwop ( VCPU_HwOp
*s
)
int id
= get_vcpu(s
->cpuid
)->id();
fprintf(stream
,"%i: hwtw load %s tte tag=0x%llx data=0x%llx from tsb_addr=0x%llx\n",
id
, s
->op_type
==0 ? "Instr":"Data", s
->data
[0], s
->data
[1], s
->addr
);
int STracer::put_string(int cpuid
, const char * str
)
fprintf(stream
,"%s\n", str
);
///////////////////////////////////////////////////////
// set debug tracer on/off
trace_cmd_t cmd
, // on/off/show status
int *trace_flag
, // equal 1 to signal dtracer turn on/off, NULL - all vcpu's
const char *fname
, // trace file name, if NULL - ui output
bool separate
// if true, trace each vcpu to separate file/stream
if (cmd
== STRACER_STATUS
)
for (int i
=0; i
<=g_vcpu_id_max
; i
++)
Vcpu
*vcpu
= get_vcpu(i
);
STracer
*s
= (STracer
*)(vcpu
->sys_intf
.vtrace
);
if (g_stracer_list
.is_found(s
))
printf("\ncollect trace to %s for cpu :", s
->get_trace_name());
ui
->output("debug tracer is off\n\n");
g_stracer_list
.display(ui
->get_output_file());
if( cmd
== STRACER_OFF
) // turn it off
for (int i
=0; i
<=g_vcpu_id_max
; i
++)
Vcpu
*vcpu
= get_vcpu(i
);
STracer
*s
= (STracer
*)vcpu
->sys_intf
.vtrace
;
if ( s
&& (trace_flag
== NULL
|| trace_flag
[i
] == 1) )
if (g_stracer_list
.dealloc(s
))
vcpu
->sys_intf
.vtrace
= NULL
;
vcpu
->config
.trace_on
= 0;
ui
->output("debug tracer is off for cpu %i\n", vcpu
->id());
ui
->error("cannot delete debug tracer for cpu %i\n,if you hooked up another analyzer - try to unload it first\n",
else if (cmd
== STRACER_ON
) // turn it on
if (fname
== NULL
&& separate
)
ui
->error("cannot trace to console cpu running on separate worker threads\n");
ui
->output("collect trace for each cpu to separate files\n");
for (int i
=0; i
<=g_vcpu_id_max
; i
++)
Vcpu
*vcpu
= get_vcpu(i
);
if ( trace_flag
== NULL
|| trace_flag
[i
] == 1 )
if (vcpu
->sys_intf
.vtrace
!= NULL
)
ui
->error("another tracer for cpu %i is already on, need to turn it off to hook up debug tracer\n",
sprintf(trace_name
, "%s.cpu%i", fname
, vcpu
->id());
vcpu
->sys_intf
.vtrace
= g_stracer_list
.alloc(separate
? trace_name
:fname
, g_sym_table
);
vcpu
->config
.trace_on
= 1;
ui
->output("debug tracer is turned on for cpu %i\n", vcpu
->id());