/*--------------------------------------------------------------------*/
/*--- Callgrind ---*/
/*--- ct_main.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Callgrind, a Valgrind skin for call graph
profiling programs.
Copyright (C) 2002-2004, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
This skin is derived from and contains lot of code from Cachegrind
Copyright (C) 2002 Nicholas Nethercote (njn25@cam.ac.uk)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This 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 program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#include "vg_skin.h"
#include "vg_unistd.h"
#include "callgrind.h"
#include "ct_include.h"
#include "ct_events.h"
#include "ct_costs.h"
#if VG_CORE_INTERFACE_MAJOR_VERSION > 4
void SK_(pre_clo_init)(void);
VG_DETERMINE_INTERFACE_VERSION(SK_(pre_clo_init), 0)
#else
VG_DETERMINE_INTERFACE_VERSION
#endif
/*------------------------------------------------------------*/
/*--- Global variables ---*/
/*------------------------------------------------------------*/
/* for all threads */
CommandLineOptions SK_(clo);
Statistics SK_(stat);
Bool SK_(instrument_state) = True; /* Instrumentation on ? */
/* thread and signal handler specific */
exec_state SK_(current_state);
/*------------------------------------------------------------*/
/*--- Statistics ---*/
/*------------------------------------------------------------*/
static void SK_(init_statistics)(Statistics* s)
{
s->call_counter = 0;
s->jcnd_counter = 0;
s->jump_counter = 0;
s->rec_call_counter = 0;
s->ret_counter = 0;
s->bb_executions = 0;
s->context_counter = 0;
s->bb_retranslations = 0;
s->distinct_objs = 0;
s->distinct_files = 0;
s->distinct_fns = 0;
s->distinct_contexts = 0;
s->distinct_bbs = 0;
s->distinct_bbccs = 0;
s->distinct_instrs = 0;
s->distinct_skips = 0;
s->bb_hash_resizes = 0;
s->bbcc_hash_resizes = 0;
s->jcc_hash_resizes = 0;
s->cxt_hash_resizes = 0;
s->fn_array_resizes = 0;
s->call_stack_resizes = 0;
s->fn_stack_resizes = 0;
s->full_debug_BBs = 0;
s->file_line_debug_BBs = 0;
s->fn_name_debug_BBs = 0;
s->no_debug_BBs = 0;
s->bbcc_lru_misses = 0;
s->jcc_lru_misses = 0;
s->cxt_lru_misses = 0;
s->bbcc_clones = 0;
}
/*------------------------------------------------------------*/
/*--- Cache simulation instrumentation phase ---*/
/*------------------------------------------------------------*/
static
Bool is_valid_data_size(Int data_size)
{
return (4 == data_size || 2 == data_size || 1 == data_size ||
8 == data_size || 10 == data_size ||
MIN_LINE_SIZE == data_size);
}
static
void end_of_x86_instr(UCodeBlock* cb, InstrInfo* ii,
Bool bb_seen_before, UInt* cost_offset,
UInt instr_offset, UInt instr_size, UInt data_size,
Int t_read, Int t_read_addr,
Int t_write, Int t_write_addr)
{
Addr helper;
Int argc;
Int t_CC_addr,
t_data_addr1 = INVALID_TEMPREG,
t_data_addr2 = INVALID_TEMPREG;
EventSet* es;
CT_ASSERT(instr_size >= 1 &&
instr_size <= MAX_x86_INSTR_SIZE);
#define IS_(X) (INVALID_TEMPREG != t_##X##_addr)
#define INV(qqt) (INVALID_TEMPREG == (qqt))
// Work out what kind of x86 instruction it is
if (!IS_(read) && !IS_(write)) {
CT_ASSERT( 0 == data_size );
CT_ASSERT(INV(t_read) && INV(t_write));
helper = (Addr) & SK_(log_0D);
argc = 1;
es = SK_(sets).D0;
} else if (IS_(read) && !IS_(write)) {
CT_ASSERT( is_valid_data_size(data_size) );
CT_ASSERT(!INV(t_read) && INV(t_write));
helper = (Addr) & SK_(log_1Dr);
argc = 2;
t_data_addr1 = t_read_addr;
es = SK_(sets).D1r;
} else if (!IS_(read) && IS_(write)) {
CT_ASSERT( is_valid_data_size(data_size) );
CT_ASSERT(INV(t_read) && !INV(t_write));
helper = (Addr) & SK_(log_1Dw);
argc = 2;
t_data_addr1 = t_write_addr;
es = SK_(sets).D1w;
} else {
CT_ASSERT(IS_(read) && IS_(write));
CT_ASSERT( is_valid_data_size(data_size) );
CT_ASSERT(!INV(t_read) && !INV(t_write));
if (t_read == t_write) {
helper = (Addr) & SK_(log_1Dw);
argc = 2;
t_data_addr1 = t_read_addr;
es = SK_(sets).D1w;
} else {
helper = (Addr) & SK_(log_2D);
argc = 3;
t_data_addr1 = t_read_addr;
t_data_addr2 = t_write_addr;
es = SK_(sets).D2;
}
}
#undef IS_
#undef INV
if (bb_seen_before) {
CT_ASSERT(ii->instr_offset == instr_offset);
CT_ASSERT(ii->instr_size == instr_size);
CT_ASSERT(ii->data_size == data_size);
CT_ASSERT(ii->cost_offset == *cost_offset);
CT_ASSERT(ii->eventset == es);
}
else {
ii->instr_offset = instr_offset;
ii->instr_size = instr_size;
ii->data_size = data_size;
ii->cost_offset = *cost_offset;
ii->eventset = es;
CT_DEBUG(5, " Instr +%2d (Size %d, DSize %d): ESet %s (Size %d)\n",
instr_offset, instr_size, data_size,
es->name, es->size);
}
*cost_offset += es->size;
// Setup 1st arg: InstrInfo
t_CC_addr = newTemp(cb);
uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_CC_addr);
uLiteral(cb, (Addr)ii);
// Call the helper
if (1 == argc)
uInstr1(cb, CCALL, 0, TempReg, t_CC_addr);
else if (2 == argc)
uInstr2(cb, CCALL, 0, TempReg, t_CC_addr,
TempReg, t_data_addr1);
else if (3 == argc)
uInstr3(cb, CCALL, 0, TempReg, t_CC_addr,
TempReg, t_data_addr1,
TempReg, t_data_addr2);
else
VG_(skin_panic)("argc... not 1 or 2 or 3?");
uCCall(cb, helper, argc, argc, False);
}
UCodeBlock* SK_(instrument)(UCodeBlock* cb_in, Addr orig_addr)
{
/* Use this rather than eg. -1 because it's a UInt. */
#define INVALID_DATA_SIZE 999999
UCodeBlock* cb;
Int i;
UInstr* u_in;
BB* bb;
Int t_read_addr, t_write_addr, t_read, t_write;
Addr x86_instr_addr = orig_addr;
UInt instr_count = 0, cost_offset = 0;
UInt x86_instr_size, data_size = 0;
Bool bb_seen_before = False;
Bool instrumented_Jcond = False;
if (! SK_(instrument_state)) {
CT_DEBUG(5, "instrument(BB 0x%x) [Instrumentation OFF]\n", orig_addr);
return cb_in;
}
CT_DEBUG(3, "+ instrument(BB 0x%x)\n", orig_addr);
/* Get BB (creating if necessary) */
bb = SK_(get_bb)(orig_addr, cb_in, &bb_seen_before);
cb = VG_(setup_UCodeBlock)(cb_in);
/*
* Precondition:
* - bbcc_jmpkind has the kind of last jump executed (CALL/RET/COND...)
* - current_bbcc has a pointer to the BBCC of the last executed BB
* Thus, if bbcc_jmpkind is != -1 (JmpNone),
* current_bbcc->bb->jmp_addr
* gives the address of the jump source.
*
* The BBCC setup does 2 things:
* - trace call:
* * Unwind own call stack, i.e sync our ESP with real ESP
* This is for ESP manipulation (longjmps, C++ exec handling) and RET
* * For CALLs or JMPs crossing objects, record call arg +
* push are on own call stack
*
* - prepare for cache log functions:
* Set current_bbcc to BBCC that gets the costs for this BB execution
* attached
*/
/* 1st arg: BB addr */
t_read_addr = newTemp(cb);
uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_read_addr);
uLiteral(cb, (Addr)bb);
uInstr1(cb, CCALL, 0, TempReg, t_read_addr);
uCCall(cb, (Addr) & SK_(setup_bbcc), 1, 1, False);
t_read_addr = t_write_addr = t_read = t_write = INVALID_TEMPREG;
for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) {
u_in = VG_(get_instr)(cb_in, i);
/* What this is all about: we want to instrument each x86 instruction
* translation. The end of these are marked in three ways. The three
* ways, and the way we instrument them, are as follows:
*
* 1. UCode, INCEIP --> UCode, Instrumentation, INCEIP
* 2. UCode, Juncond --> UCode, Instrumentation, Juncond
* 3. UCode, Jcond, Juncond --> UCode, Instrumentation, Jcond, Juncond
*
* The last UInstr in a basic block is always a Juncond. Jconds,
* when they appear, are always second last. We check this with
* various assertions.
*
* We must put the instrumentation before any jumps so that it is always
* executed. We don't have to put the instrumentation before the INCEIP
* (it could go after) but we do so for consistency.
*
* x86 instruction sizes are obtained from INCEIPs (for case 1) or
* from .extra4b field of the final JMP (for case 2 & 3).
*
* Note that JIFZ is treated differently.
*
* The instrumentation is just a call to the appropriate helper function,
* passing it the address of the instruction's CC.
*/
if (instrumented_Jcond) CT_ASSERT(u_in->opcode == JMP);
switch (u_in->opcode) {
case NOP: case LOCK: case CALLM_E: case CALLM_S:
break;
/* For memory-ref instrs, copy the data_addr into a temporary to be
* passed to the log_* helper at the end of the instruction.
*/
case LOAD:
case SSE3ag_MemRd_RegWr:
t_read = u_in->val1;
t_read_addr = newTemp(cb);
uInstr2(cb, MOV, 4, TempReg, u_in->val1, TempReg, t_read_addr);
data_size = u_in->size;
VG_(copy_UInstr)(cb, u_in);
break;
case FPU_R:
case MMX2_MemRd:
t_read = u_in->val2;
t_read_addr = newTemp(cb);
uInstr2(cb, MOV, 4, TempReg, u_in->val2, TempReg, t_read_addr);
data_size = u_in->size;
VG_(copy_UInstr)(cb, u_in);
break;
case SSE2a_MemRd:
case SSE2a1_MemRd:
case SSE3a_MemRd:
#if VG_CORE_INTERFACE_MAJOR_VERSION > 3
case SSE3a1_MemRd: /* Supported since VG-20031104 */
#endif
t_read = u_in->val3;
t_read_addr = newTemp(cb);
uInstr2(cb, MOV, 4, TempReg, u_in->val3, TempReg, t_read_addr);
data_size = u_in->size;
VG_(copy_UInstr)(cb, u_in);
break;
/* Note that we must set t_write_addr even for mod instructions;
* That's how the code above determines whether it does a write.
* Without it, it would think a mod instruction is a read.
* As for the MOV, if it's a mod instruction it's redundant, but it's
* not expensive and mod instructions are rare anyway. */
case STORE:
case FPU_W:
case MMX2_MemWr:
t_write = u_in->val2;
t_write_addr = newTemp(cb);
uInstr2(cb, MOV, 4, TempReg, u_in->val2, TempReg, t_write_addr);
data_size = u_in->size;
VG_(copy_UInstr)(cb, u_in);
break;
case SSE2a_MemWr:
case SSE3a_MemWr:
t_write = u_in->val3;
t_write_addr = newTemp(cb);
uInstr2(cb, MOV, 4, TempReg, u_in->val3, TempReg, t_write_addr);
data_size = u_in->size;
VG_(copy_UInstr)(cb, u_in);
break;
/* INCEIP: insert instrumentation */
case INCEIP:
x86_instr_size = u_in->val1;
goto instrument_x86_instr;
/* JMP: insert instrumentation if the first JMP */
case JMP:
if (instrumented_Jcond) {
if (SK_(clo).collect_jumps) {
/* Overwrite conditional jmpkind, as not followed */
Int tmp = newTemp(cb);
Int tmp_jk_addr = newTemp(cb);
uInstr2(cb, MOV, 4, Literal, 0, TempReg, tmp_jk_addr);
uLiteral(cb, (Addr) & SK_(current_state).jmpkind);
uInstr2(cb, MOV, 4, Literal, 0, TempReg, tmp);
uLiteral(cb, JmpNone);
uInstr2(cb, STORE, 4, TempReg, tmp, TempReg, tmp_jk_addr);
}
CT_ASSERT(CondAlways == u_in->cond);
CT_ASSERT(i+1 == VG_(get_num_instrs)(cb_in));
VG_(copy_UInstr)(cb, u_in);
instrumented_Jcond = False; /* reset */
break;
}
/* The first JMP... instrument. */
/* as preparation for the setup_bbcc call at the
* beginning of the next basic block, we store the
* jmpkind into a global variable if its a Call or Ret.
*
* if we instrument conditional jumps, use -2 (JmpCond) as jmpkind.
* Before the final (2nd) jump, jmpkind is reset to -1 (JmpNone).
* This way, jmpkind will only be -2 in setup_bbcc if the
* conditional jump was followed.
*/
if ( ((CondAlways != u_in->cond) && SK_(clo).collect_jumps) ||
((CondAlways == u_in->cond) &&
((u_in->jmpkind == JmpCall) ||
(u_in->jmpkind == JmpRet) ||
(SK_(clo).collect_jumps &&
(u_in->jmpkind == JmpBoring)) )) ) {
Int tmp = newTemp(cb);
Int tmp_jk_addr = newTemp(cb);
/* Address of bbcc_jmpkind into temp reg for STORE */
uInstr2(cb, MOV, 4, Literal, 0, TempReg, tmp_jk_addr);
uLiteral(cb, (Addr) & SK_(current_state).jmpkind);
uInstr2(cb, MOV, 4, Literal, 0, TempReg, tmp);
uLiteral(cb,
(CondAlways != u_in->cond) ? JmpCond : u_in->jmpkind);
uInstr2(cb, STORE, 4, TempReg, tmp, TempReg, tmp_jk_addr);
}
if (bb_seen_before) {
CT_ASSERT(bb_jmpaddr(bb) == x86_instr_addr);
}
else {
bb->jmp_offset = x86_instr_addr - orig_addr;
}
if (CondAlways != u_in->cond) {
CT_ASSERT(i+2 == VG_(get_num_instrs)(cb_in));
instrumented_Jcond = True;
} else {
CT_ASSERT(i+1 == VG_(get_num_instrs)(cb_in));
}
/* Get x86 instr size from final JMP. */
x86_instr_size = VG_(get_last_instr)(cb_in)->extra4b;
goto instrument_x86_instr;
/* Code executed at the end of each x86 instruction. */
instrument_x86_instr:
/* Large (eg. 28B, 108B, 512B) data-sized instructions will be
* done inaccurately but they're very rare and this avoids
* errors from hitting more than two cache lines in the
* simulation.
*/
if (data_size > MIN_LINE_SIZE) data_size = MIN_LINE_SIZE;
/* FIXME: Only append to event list of not seen before */
if (SK_(clo).simulate_cache)
end_of_x86_instr(cb, &(bb->instr[instr_count]),
bb_seen_before, &cost_offset,
x86_instr_addr-orig_addr,
x86_instr_size, data_size,
t_read, t_read_addr, t_write, t_write_addr);
/* Count number of instrumented instructions */
if (!bb_seen_before) SK_(stat).distinct_instrs++;
/* Copy original UInstr (INCEIP or JMP) */
VG_(copy_UInstr)(cb, u_in);
instr_count++;
/* Update BBCC_ptr, EIP, de-init read/write temps for next instr */
x86_instr_addr += x86_instr_size;
t_read_addr = t_write_addr = t_read = t_write = INVALID_TEMPREG;
data_size = 0;
break;
default:
VG_(copy_UInstr)(cb, u_in);
break;
}
}
VG_(free_UCodeBlock)(cb_in);
CT_ASSERT(bb->instr_count == instr_count);
if (bb_seen_before) {
CT_ASSERT(bb->instr_len == x86_instr_addr - orig_addr);
CT_ASSERT(bb->cost_count == cost_offset);
}
else {
bb->instr_len = x86_instr_addr - orig_addr;
bb->cost_count = cost_offset;
}
CT_DEBUG(3, "- instrument(BB 0x%x): byteLen %d, CostLen %d \n",
orig_addr, bb->instr_len, bb->cost_count);
return cb;
#undef INVALID_DATA_SIZE
}
/*------------------------------------------------------------*/
/*--- SK_(fini)() and related function ---*/
/*------------------------------------------------------------*/
static void zero_thread_cost(thread_info* t)
{
Int i;
for(i = 0; i < SK_(current_call_stack).sp; i++) {
if (!SK_(current_call_stack).entry[i].jcc) continue;
/* reset call counters to current for active calls */
SK_(copy_cost)( SK_(sets).full,
SK_(current_call_stack).entry[i].enter_cost,
SK_(current_state).cost );
}
SK_(forall_bbccs)(SK_(zero_bbcc));
/* set counter for last dump */
SK_(copy_cost)( SK_(sets).full,
t->lastdump_cost, SK_(current_state).cost );
}
void SK_(zero_all_cost)(Bool only_current_thread)
{
if (VG_(clo_verbosity) > 1)
VG_(message)(Vg_DebugMsg, " Zeroing costs...");
if (only_current_thread)
zero_thread_cost(SK_(get_current_thread)());
else
SK_(forall_threads)(zero_thread_cost);
if (VG_(clo_verbosity) > 1)
VG_(message)(Vg_DebugMsg, " ...done");
}
void unwind_thread(thread_info* t)
{
/* unwind signal handlers */
while(SK_(current_state).sig !=0)
SK_(post_signal)(SK_(current_tid),SK_(current_state).sig);
/* unwind regular call stack */
while(SK_(current_call_stack).sp>0)
SK_(pop_call_stack)();
}
/* Ups, this can go wrong... */
extern void VG_(invalidate_translations) ( Addr, UInt, Bool);
void SK_(set_instrument_state)(Char* reason, Bool state)
{
if (SK_(instrument_state) == state) {
CT_DEBUG(2, "%s: instrumentation already %s\n",
reason, state ? "ON" : "OFF");
return;
}
SK_(instrument_state) = state;
CT_DEBUG(2, "%s: Switching instrumentation %s ...\n",
reason, state ? "ON" : "OFF");
/* invalidate all instrumentations */
VG_(invalidate_translations)( 0x1000, 0xbfff0000, False);
/* continue with empty call stacks */
SK_(forall_threads)(unwind_thread);
if (0)
SK_(forall_threads)(zero_thread_cost);
if (!state)
SK_(init_exec_state)( &SK_(current_state) );
if (VG_(clo_verbosity) > 1)
VG_(message)(Vg_DebugMsg, "%s: instrumentation switched %s\n",
reason, state ? "ON" : "OFF");
}
#if VG_CORE_INTERFACE_MAJOR_VERSION < 3
Bool SK_(handle_client_request)(ThreadState* tst, UInt *args, UInt *ret)
#else
Bool SK_(handle_client_request)(ThreadId tid, UInt *args, UInt *ret)
#endif
{
if (!VG_IS_SKIN_USERREQ('C','T',args[0]))
return False;
switch(args[0]) {
case VG_USERREQ__DUMP_STATS:
SK_(dump_profile)("Client Request", True);
*ret = 0; /* meaningless */
break;
case VG_USERREQ__DUMP_STATS_AT:
{
Char buf[512];
VG_(sprintf)(buf,"Client Request: %d", args[1]);
SK_(dump_profile)(buf, True);
*ret = 0; /* meaningless */
}
break;
case VG_USERREQ__ZERO_STATS:
SK_(zero_all_cost)(True);
*ret = 0; /* meaningless */
break;
case VG_USERREQ__TOGGLE_COLLECT:
SK_(current_state).collect = !SK_(current_state).collect;
CT_DEBUG(2, "Client Request: toggled collection state to %s\n",
SK_(current_state).collect ? "ON" : "OFF");
*ret = 0; /* meaningless */
break;
case VG_USERREQ__START_INSTRUMENTATION:
SK_(set_instrument_state)("Client Request", True);
*ret = 0; /* meaningless */
break;
case VG_USERREQ__STOP_INSTRUMENTATION:
SK_(set_instrument_state)("Client Request", False);
*ret = 0; /* meaningless */
break;
default:
return False;
}
return True;
}
/* Syscall Timing */
/* struct timeval syscalltime[VG_N_THREADS]; */
#if CT_MICROSYSTIME
#include <sys/time.h>
#include <sys/syscall.h>
extern Int VG_(do_syscall) ( UInt, ... );
ULong syscalltime[VG_N_THREADS];
#else
UInt syscalltime[VG_N_THREADS];
#endif
void* SK_(pre_syscalltime)(ThreadId tid, UInt syscallno, Bool is_blocking)
{
if (SK_(clo).collect_systime) {
#if CT_MICROSYSTIME
struct vki_timeval tv_now;
VG_(do_syscall)(__NR_gettimeofday, (UInt)&tv_now, (UInt)NULL);
syscalltime[tid] = tv_now.tv_sec * 1000000ULL + tv_now.tv_usec;
#else
syscalltime[tid] = VG_(read_millisecond_timer)();
#endif
}
return 0;
}
void SK_(post_syscalltime)(ThreadId tid, UInt syscallno,
void* pre_result, Int res, Bool is_blocking)
{
if (SK_(clo).collect_systime) {
Int o = SK_(sets).off_full_systime;
#if CT_MICROSYSTIME
struct vki_timeval tv_now;
ULong diff;
VG_(do_syscall)(__NR_gettimeofday, (UInt)&tv_now, (UInt)NULL);
diff = (tv_now.tv_sec * 1000000ULL + tv_now.tv_usec) - syscalltime[tid];
#else
UInt diff = VG_(read_millisecond_timer)() - syscalltime[tid];
#endif
CT_DEBUG(0," Time (Off %d) for Syscall %d: %ull\n", o, syscallno, diff);
if (o<0) return;
SK_(current_state).cost[o] ++;
SK_(current_state).cost[o+1] += diff;
if (!SK_(current_state).bbcc->skipped)
SK_(init_cost_lz)(SK_(sets).full,
&(SK_(current_state).bbcc->skipped));
SK_(current_state).bbcc->skipped[o] ++;
SK_(current_state).bbcc->skipped[o+1] += diff;
}
}
/* Adds commas to ULong, right justifying in a field field_width wide, returns
* the string in buf. */
static
Int commify(ULong n, int field_width, char* buf)
{
int len, n_commas, i, j, new_len, space;
VG_(sprintf)(buf, "%llu", n);
len = VG_(strlen)(buf);
n_commas = (len - 1) / 3;
new_len = len + n_commas;
space = field_width - new_len;
/* Allow for printing a number in a field_width smaller than it's size */
if (space < 0) space = 0;
/* Make j = -1 because we copy the '\0' before doing the numbers in groups
* of three. */
for (j = -1, i = len ; i >= 0; i--) {
buf[i + n_commas + space] = buf[i];
if ((i>0) && (3 == ++j)) {
j = 0;
n_commas--;
buf[i + n_commas + space] = ',';
}
}
/* Right justify in field. */
for (i = 0; i < space; i++) buf[i] = ' ';
return new_len;
}
static
void percentify(Int n, Int ex, Int field_width, char buf[])
{
int i, len, space;
VG_(sprintf)(buf, "%d.%d%%", n / ex, n % ex);
len = VG_(strlen)(buf);
space = field_width - len;
if (space < 0) space = 0; /* Allow for v. small field_width */
i = len;
/* Right justify in field */
for ( ; i >= 0; i--) buf[i + space] = buf[i];
for (i = 0; i < space; i++) buf[i] = ' ';
}
void finish()
{
FullCost total = SK_(total_cost), D_total = 0;
ULong L2_total_m, L2_total_mr, L2_total_mw,
L2_total, L2_total_r, L2_total_w;
char buf1[RESULTS_BUF_LEN],
buf2[RESULTS_BUF_LEN],
buf3[RESULTS_BUF_LEN];
Int l1, l2, l3;
Int p;
CT_DEBUG(0, "CallStack finalization:\n");
/* pop all remaining items from CallStack for correct sum
*/
SK_(forall_threads)(unwind_thread);
SK_(dump_profile)(0, False);
SK_(finish_command)();
if (VG_(clo_verbosity) == 0) return;
/* Hash table stats */
if (VG_(clo_verbosity) > 1) {
int BB_lookups =
SK_(stat).full_debug_BBs +
SK_(stat).fn_name_debug_BBs +
SK_(stat).file_line_debug_BBs +
SK_(stat).no_debug_BBs;
VG_(message)(Vg_DebugMsg, "");
VG_(message)(Vg_DebugMsg, "Distinct objects: %d",
SK_(stat).distinct_objs);
VG_(message)(Vg_DebugMsg, "Distinct files: %d",
SK_(stat).distinct_files);
VG_(message)(Vg_DebugMsg, "Distinct fns: %d",
SK_(stat).distinct_fns);
VG_(message)(Vg_DebugMsg, "Distinct contexts:%d",
SK_(stat).distinct_contexts);
VG_(message)(Vg_DebugMsg, "Distinct BBs: %d",
SK_(stat).distinct_bbs);
VG_(message)(Vg_DebugMsg, "Cost entries: %d (Chunks %d)",
SK_(costarray_entries), SK_(costarray_chunks));
VG_(message)(Vg_DebugMsg, "Distinct BBCCs: %d",
SK_(stat).distinct_bbccs);
VG_(message)(Vg_DebugMsg, "Distinct JCCs: %d",
SK_(stat).distinct_jccs);
VG_(message)(Vg_DebugMsg, "Distinct skips: %d",
SK_(stat).distinct_skips);
VG_(message)(Vg_DebugMsg, "BB lookups: %d",
BB_lookups);
if (BB_lookups>0) {
VG_(message)(Vg_DebugMsg, "With full debug info:%3d%% (%d)",
SK_(stat).full_debug_BBs * 100 / BB_lookups,
SK_(stat).full_debug_BBs);
VG_(message)(Vg_DebugMsg, "With file/line debug info:%3d%% (%d)",
SK_(stat).file_line_debug_BBs * 100 / BB_lookups,
SK_(stat).file_line_debug_BBs);
VG_(message)(Vg_DebugMsg, "With fn name debug info:%3d%% (%d)",
SK_(stat).fn_name_debug_BBs * 100 / BB_lookups,
SK_(stat).fn_name_debug_BBs);
VG_(message)(Vg_DebugMsg, "With no debug info:%3d%% (%d)",
SK_(stat).no_debug_BBs * 100 / BB_lookups,
SK_(stat).no_debug_BBs);
}
VG_(message)(Vg_DebugMsg, "BBCC Clones: %d",
SK_(stat).bbcc_clones);
VG_(message)(Vg_DebugMsg, "BBs Retranslated: %d",
SK_(stat).bb_retranslations);
VG_(message)(Vg_DebugMsg, "Distinct instrs: %d",
SK_(stat).distinct_instrs);
VG_(message)(Vg_DebugMsg, "");
VG_(message)(Vg_DebugMsg, "LRU Contxt Misses: %d",
SK_(stat).cxt_lru_misses);
VG_(message)(Vg_DebugMsg, "LRU BBCC Misses: %d",
SK_(stat).bbcc_lru_misses);
VG_(message)(Vg_DebugMsg, "LRU JCC Misses: %d",
SK_(stat).jcc_lru_misses);
VG_(message)(Vg_DebugMsg, "BBs Executed: %llu",
SK_(stat).bb_executions);
VG_(message)(Vg_DebugMsg, "Calls: %llu",
SK_(stat).call_counter);
VG_(message)(Vg_DebugMsg, "CondJMP followed: %llu",
SK_(stat).jcnd_counter);
VG_(message)(Vg_DebugMsg, "Boring JMPs: %llu",
SK_(stat).jump_counter);
VG_(message)(Vg_DebugMsg, "Recursive calls: %llu",
SK_(stat).rec_call_counter);
VG_(message)(Vg_DebugMsg, "Returns: %llu",
SK_(stat).ret_counter);
#if HW_PREFETCH
if (SK_(clo).simulate_hwpref) {
VG_(message)(Vg_DebugMsg, "Prefetch Up: %llu",
SK_(stat).prefetch_up);
VG_(message)(Vg_DebugMsg, "Prefetch Down: %llu",
SK_(stat).prefetch_down);
}
#endif
VG_(message)(Vg_DebugMsg, "");
}
CT_ASSERT(total != 0);
SK_(sprint_eventmapping)(buf1, SK_(dumpmap));
VG_(message)(Vg_UserMsg, "Events : %s", buf1);
SK_(sprint_mappingcost)(buf1, SK_(dumpmap), SK_(total_cost));
VG_(message)(Vg_UserMsg, "Collected : %s", buf1);
VG_(message)(Vg_UserMsg, "");
/* I cache results. Use the I_refs value to determine the first column
* width. */
l1 = commify(total[SK_(sets).off_full_Ir], 0, buf1);
VG_(message)(Vg_UserMsg, "I refs: %s", buf1);
if (!SK_(clo).simulate_cache) return;
commify(total[SK_(sets).off_full_Ir +1], l1, buf1);
VG_(message)(Vg_UserMsg, "I1 misses: %s", buf1);
commify(total[SK_(sets).off_full_Ir +2], l1, buf1);
VG_(message)(Vg_UserMsg, "L2i misses: %s", buf1);
p = 100;
if (0 == total[SK_(sets).off_full_Ir])
total[SK_(sets).off_full_Ir] = 1;
percentify(total[SK_(sets).off_full_Ir+1] * 100 * p /
total[SK_(sets).off_full_Ir], p, l1+1, buf1);
VG_(message)(Vg_UserMsg, "I1 miss rate: %s", buf1);
percentify(total[SK_(sets).off_full_Ir+2] * 100 * p /
total[SK_(sets).off_full_Ir], p, l1+1, buf1);
VG_(message)(Vg_UserMsg, "L2i miss rate: %s", buf1);
VG_(message)(Vg_UserMsg, "");
/* D cache results.
Use the D_refs.rd and D_refs.wr values to determine the
* width of columns 2 & 3. */
D_total = SK_(get_eventset_cost)( SK_(sets).full );
SK_(init_cost)( SK_(sets).full, D_total);
SK_(copy_cost)( SK_(sets).Dr, D_total, total + SK_(sets).off_full_Dr );
SK_(add_cost) ( SK_(sets).Dr, D_total, total + SK_(sets).off_full_Dw );
commify( D_total[0], l1, buf1);
l2 = commify(total[SK_(sets).off_full_Dr], 0, buf2);
l3 = commify(total[SK_(sets).off_full_Dw], 0, buf3);
VG_(message)(Vg_UserMsg, "D refs: %s (%s rd + %s wr)",
buf1, buf2, buf3);
commify( D_total[1], l1, buf1);
commify(total[SK_(sets).off_full_Dr+1], l2, buf2);
commify(total[SK_(sets).off_full_Dw+1], l3, buf3);
VG_(message)(Vg_UserMsg, "D1 misses: %s (%s rd + %s wr)",
buf1, buf2, buf3);
commify( D_total[2], l1, buf1);
commify(total[SK_(sets).off_full_Dr+2], l2, buf2);
commify(total[SK_(sets).off_full_Dr+2], l3, buf3);
VG_(message)(Vg_UserMsg, "L2d misses: %s (%s rd + %s wr)",
buf1, buf2, buf3);
p = 10;
if (0 == D_total[0]) D_total[0] = 1;
if (0 == total[SK_(sets).off_full_Dr]) total[SK_(sets).off_full_Dr] = 1;
if (0 == total[SK_(sets).off_full_Dw]) total[SK_(sets).off_full_Dw] = 1;
percentify( D_total[1] * 100 * p / D_total[0], p, l1+1, buf1);
percentify(total[SK_(sets).off_full_Dr+1] * 100 * p /
total[SK_(sets).off_full_Dr], p, l2+1, buf2);
percentify(total[SK_(sets).off_full_Dw+1] * 100 * p /
total[SK_(sets).off_full_Dw], p, l3+1, buf3);
VG_(message)(Vg_UserMsg, "D1 miss rate: %s (%s + %s )", buf1, buf2,buf3);
percentify( D_total[2] * 100 * p / D_total[0], p, l1+1, buf1);
percentify(total[SK_(sets).off_full_Dr+2] * 100 * p /
total[SK_(sets).off_full_Dr], p, l2+1, buf2);
percentify(total[SK_(sets).off_full_Dw+2] * 100 * p /
total[SK_(sets).off_full_Dw], p, l3+1, buf3);
VG_(message)(Vg_UserMsg, "L2d miss rate: %s (%s + %s )", buf1, buf2,buf3);
VG_(message)(Vg_UserMsg, "");
/* L2 overall results */
L2_total =
total[SK_(sets).off_full_Dr +1] +
total[SK_(sets).off_full_Dw +1] +
total[SK_(sets).off_full_Ir +1];
L2_total_r =
total[SK_(sets).off_full_Dr +1] +
total[SK_(sets).off_full_Ir +1];
L2_total_w = total[SK_(sets).off_full_Dw +1];
commify(L2_total, l1, buf1);
commify(L2_total_r, l2, buf2);
commify(L2_total_w, l3, buf3);
VG_(message)(Vg_UserMsg, "L2 refs: %s (%s rd + %s wr)",
buf1, buf2, buf3);
L2_total_m =
total[SK_(sets).off_full_Dr +2] +
total[SK_(sets).off_full_Dw +2] +
total[SK_(sets).off_full_Ir +2];
L2_total_mr =
total[SK_(sets).off_full_Dr +2] +
total[SK_(sets).off_full_Ir +2];
L2_total_mw = total[SK_(sets).off_full_Dw +2];
commify(L2_total_m, l1, buf1);
commify(L2_total_mr, l2, buf2);
commify(L2_total_mw, l3, buf3);
VG_(message)(Vg_UserMsg, "L2 misses: %s (%s rd + %s wr)",
buf1, buf2, buf3);
percentify(L2_total_m * 100 * p /
(total[SK_(sets).off_full_Ir] + D_total[0]), p, l1+1, buf1);
percentify(L2_total_mr * 100 * p /
(total[SK_(sets).off_full_Ir] + total[SK_(sets).off_full_Dr]),
p, l2+1, buf2);
percentify(L2_total_mw * 100 * p /
total[SK_(sets).off_full_Dw], p, l3+1, buf3);
VG_(message)(Vg_UserMsg, "L2 miss rate: %s (%s + %s )",
buf1, buf2,buf3);
}
void SK_(fini)(Int exitcode)
{
finish();
}
/*--------------------------------------------------------------------*/
/*--- Setup ---*/
/*--------------------------------------------------------------------*/
void SK_(pre_clo_init)(void)
{
VG_(details_name) ("Callgrind");
VG_(details_version) (VERSION);
VG_(details_description) ("a call-graph generating cache profiler");
VG_(details_copyright_author)("Copyright (C) 2002-2004, and GNU GPL'd, "
"by J.Weidendorfer, N.Nethercote et al.");
VG_(details_bug_reports_to) ("Josef.Weidendorfer@gmx.de");
VG_(details_avg_translation_sizeB) ( 155 );
VG_(needs_command_line_options)();
VG_(needs_client_requests)();
VG_(needs_syscall_wrapper)();
#if VG_CORE_INTERFACE_MAJOR_VERSION > 4
VG_(init_thread_run) ( & SK_(run_thread) );
VG_(init_pre_deliver_signal) ( & SK_(pre_signal) );
VG_(init_post_deliver_signal) ( & SK_(post_signal) );
VG_(init_pre_syscall)( & SK_(pre_syscalltime) );
VG_(init_post_syscall)( & SK_(post_syscalltime) );
#else
VG_(track_thread_run) ( & SK_(run_thread) );
VG_(track_pre_deliver_signal) ( & SK_(pre_signal) );
VG_(track_post_deliver_signal) ( & SK_(post_signal) );
VG_(track_pre_syscall)( & SK_(pre_syscalltime) );
VG_(track_post_syscall)( & SK_(post_syscalltime) );
#endif
VG_(register_compact_helper)((Addr) & SK_(log_0D));
VG_(register_compact_helper)((Addr) & SK_(log_1Dr));
VG_(register_compact_helper)((Addr) & SK_(log_1Dw));
VG_(register_compact_helper)((Addr) & SK_(log_2D));
VG_(register_compact_helper)((Addr) & SK_(setup_bbcc));
SK_(set_clo_defaults)();
}
#if (VG_CORE_INTERFACE_MAJOR_VERSION > 4) && defined(VG_UNSTABLE)
extern Bool VG_(clo_pointercheck);
#endif
void SK_(post_clo_init)(void)
{
Char *dir = 0, *fname = 0;
#if (VG_CORE_INTERFACE_MAJOR_VERSION > 4) && defined(VG_UNSTABLE)
VG_(clo_pointercheck) = False;
#endif
CT_DEBUG(1, " dump threads: %s\n", SK_(clo).separate_threads ? "Yes":"No");
CT_DEBUG(1, " call sep. : %d\n", SK_(clo).separate_callers);
CT_DEBUG(1, " rec. sep. : %d\n", SK_(clo).separate_recursions);
if (!SK_(clo).dump_line && !SK_(clo).dump_instr && !SK_(clo).dump_bb) {
VG_(message)(Vg_UserMsg, "Using source line as position.");
SK_(clo).dump_line = True;
}
SK_(init_files)(&dir,&fname);
SK_(init_command)(dir,fname);
SK_(cachesim_init)();
SK_(init_eventsets)(SK_(clo).simulate_cache, 0);
SK_(init_statistics)(& SK_(stat));
SK_(init_cost_lz)( SK_(sets).full, &SK_(total_cost) );
/* initialize hash tables */
SK_(init_obj_table)();
SK_(init_cxt_table)();
SK_(init_bb_hash)();
SK_(init_threads)();
SK_(run_thread)(1);
SK_(instrument_state) = SK_(clo).instrument_atstart;
}
/*--------------------------------------------------------------------*/
/*--- end cg_main.c ---*/
/*--------------------------------------------------------------------*/
syntax highlighted by Code2HTML, v. 0.9.1