Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / system / blaze / stracer.cc
// ========== 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 ============================================
////////////////////////////////////////////////////////////
//
// File: stracer.cc
//
// Copyright (C) 2005-2007 Sun Microsystems, Inc.
// All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "types.h"
#include "cpu_interface.h"
#include "symbols.h"
#include "stracer.h"
#include "ui.h"
#include "ui_elfsym.h"
// set to 1 to debug this module
#define DEBUG_ON 0
//////////////////////////////////////////
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)
{
STracer *s = head;
if (fname == NULL)
fname = console;
// check if tracer is opened already
while (s)
{
if (strcmp(s->trace_name, fname)==0)
break;
else
s = s->next;
}
if (!s)
{
// add a new stream
s = new STracer(fname,symtable);
s->next = head;
head = s;
}
s->ref++; // update ref counter
return s;
}
int STracerContainer::dealloc (STracer *t)
{
STracer *s = head;
STracer *prev = NULL;
// find it on the list
while (s)
{
if (t==s)
{
// update ref counter
if (--s->ref > 0)
return 1;
// remove it from the list
if (s == head)
head = s->next;
else
prev->next = s->next;
delete s;
return 1;
}
else
{
prev = s;
s = s->next;
}
}
return 0;
}
int STracerContainer::display(FILE *out)
{
int ok = 0;
STracer *s = head;
while (s)
{
fprintf (out,"trace to %s\n", s->trace_name );
ok = 1;
s = s->next;
}
fprintf (out,"\n" );
return ok;
}
bool STracerContainer::is_found(STracer *t)
{
STracer *s = head;
while (s)
{
if(s==t)
return true;
s = s->next;
}
return false;
}
///////////////////////////////////////////////////////////////////////
//
// debug tracer constructor
//
STracer::STracer (const char *fname, SymTable *symtable)
{
sym_table = symtable;
// open a stream
if (fname && fname != console)
trace_name = strdup(fname);
else
trace_name = console;
if ( trace_name == console )
{
stream = ui->get_output_file();
}
else
{
stream = fopen (trace_name, "w");
if ( stream == NULL )
{
ui->error("cannot open %s, trace to output\n", trace_name);
trace_name = console;
stream = ui->get_output_file();
}
}
// set the stream to be unbuffered;
// tracing will b slower but we don't lose trace data
// in case of crash
setbuf(stream, NULL);
next = NULL;
ref = 0;
}
STracer::~STracer ()
{
if (stream != ui->get_output_file())
fclose(stream);
if (trace_name != console)
free(trace_name);
}
int STracer::attach(VTracer_SAM_intf * /* intf */) { return 1; }
///////////////////////////////////////////////////////////////
//
// print values from the trace record
static int print_regs_update
(
int cpuid,
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;
char reg_name[24];
const char format[] = "%i:\t%s=0x%llx\n";
switch(dreg[i].r.type)
{
case VCPU_PR_RTYPE:
fprintf (stream, format, cpuid, cpu->get_reg_name(VCPU_PR_0 + rindex), dval[i]);
break;
case VCPU_ASR_RTYPE:
fprintf (stream, format, cpuid, cpu->get_reg_name(VCPU_ASR_0 + rindex), dval[i]);
break;
case VCPU_INT_RTYPE:
fprintf (stream, format, cpuid, cpu->get_reg_name(VCPU_IRF_0 + rindex), dval[i]);
break;
case VCPU_FP_SINGLE_RTYPE:
fprintf (stream, format, cpuid, cpu->get_reg_name(VCPU_FRF_0 + rindex), dval[i]);
break;
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]);
break;
case VCPU_HPR_RTYPE:
fprintf (stream, format, cpuid, cpu->get_reg_name(VCPU_HPR_0 + rindex), dval[i]);
break;
default: // uknown type
continue;
}
}
return 0;
}
void print_value(uint64_t val, uint64_t mask, FILE *stream)
{
for (int i=60; i>=0; i-=4)
{
if ((mask>>i) & 0xf)
{
uint8_t hb = uint8_t(val>>i)&0xf;
if(hb ==0) fprintf(stream, "0");
else fprintf(stream, "%1x", hb);
}
else
fprintf(stream, "-");
}
}
int STracer::instr ( VCPU_Instruction *i )
{
// instruction line
const int LSIZE = 128;
char ibuf[LSIZE];
disassemble (i->opcode, i->pc_va, ibuf, LSIZE);
// check mode
prmode_t mode = MODE_USER;
if(i->hpstate & 4)
mode = MODE_HVISOR;
else if (i->pstate & 4)
mode = MODE_PRIV;
uint32_t context = i->pc_type==VCPU_VADDR ? i->icontext : Symbol::NO_CONTEXT;
// id, mode
int id = get_vcpu(i->cpuid)->id();
fprintf(stream,"%i: ", id);
// symname, va
if (sym_table) sym_table->fputs(i->pc_va, context, stream);
fprintf(stream,"%llx", i->pc_va);
// context
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)":"");
// destination regs
if(i->nregs > 0)
print_regs_update (id, i->nregs, i->dreg, i->dval, stream );
if (i->st_num > 0)
{
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);
context = i->dcontext;
}
else
{
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,"\n");
}
if (i->ld_num > 0)
{
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);
context = i->dcontext;
}
else
{
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);
fprintf(stream,"\n");
}
return 0;
}
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 );
return 0;
}
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->tlb_no, s->tlb_index,
s->tte_tag, s->tte_data,
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));
} else {
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);
} else {
fprintf(stream, "\n");
}
return 0;
}
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);
return 0;
}
int STracer::put_string(int cpuid, const char * str)
{
fprintf(stream,"%s\n", str);
return 0;
}
///////////////////////////////////////////////////////
//
// set debug tracer on/off
//
int tracer_cmd
(
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 )
{
bool newline = true;
STracer *prev = NULL;
for (int i=0; i<=g_vcpu_id_max; i++)
{
Vcpu *vcpu = get_vcpu(i);
if (vcpu == NULL)
continue;
STracer *s = (STracer *)(vcpu->sys_intf.vtrace);
if (g_stracer_list.is_found(s))
{
if (s != prev )
newline = true;
if (newline)
{
printf("\ncollect trace to %s for cpu :", s->get_trace_name());
newline = false;
prev = s;
}
printf (" %i", i);
}
}
if (!prev)
ui->output("debug tracer is off\n\n");
else
{
#if(DEBUG_ON==1)
ui->verbose("\n\n");
g_stracer_list.display(ui->get_output_file());
#endif
ui->verbose("\n");
}
return 1;
}
if( cmd == STRACER_OFF ) // turn it off
{
for (int i=0; i<=g_vcpu_id_max; i++)
{
Vcpu *vcpu = get_vcpu(i);
if (vcpu == NULL)
continue;
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());
}
else
{
ui->error("cannot delete debug tracer for cpu %i\n,if you hooked up another analyzer - try to unload it first\n",
vcpu->id() );
return 0;
}
}
}
}
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");
return 0;
}
else if ( separate )
{
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 (vcpu == NULL)
continue;
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",
vcpu->id());
}
else
{
char trace_name[256];
if (separate)
{
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());
}
}
}
}
return 1;
}