static EventSet* insert_simcall(IRSB* bbOut, InstrInfo* ii, UInt dataSize, Bool instrIssued, IRExpr* loadAddrExpr, IRExpr* storeAddrExpr) { HChar* helperName; void* helperAddr; Int argc; EventSet* es; IRExpr *arg1, *arg2 = 0, *arg3 = 0, **argv; IRDirty* di; /* Check type of original instruction regarding memory access, * and collect info to be able to generate fitting helper call */ if (!loadAddrExpr && !storeAddrExpr) { // no load/store CLG_ASSERT(0 == dataSize); if (instrIssued) { helperName = 0; helperAddr = 0; } else { helperName = CLG_(cachesim).log_1I0D_name; helperAddr = CLG_(cachesim).log_1I0D; } argc = 1; es = CLG_(sets).D0; } else if (loadAddrExpr && !storeAddrExpr) { // load CLG_ASSERT( isIRAtom(loadAddrExpr) ); if (instrIssued) { helperName = CLG_(cachesim).log_0I1Dr_name; helperAddr = CLG_(cachesim).log_0I1Dr; } else { helperName = CLG_(cachesim).log_1I1Dr_name; helperAddr = CLG_(cachesim).log_1I1Dr; } argc = 2; arg2 = loadAddrExpr; es = CLG_(sets).D1r; } else if (!loadAddrExpr && storeAddrExpr) { // store CLG_ASSERT( isIRAtom(storeAddrExpr) ); if (instrIssued) { helperName = CLG_(cachesim).log_0I1Dw_name; helperAddr = CLG_(cachesim).log_0I1Dw; } else { helperName = CLG_(cachesim).log_1I1Dw_name; helperAddr = CLG_(cachesim).log_1I1Dw; } argc = 2; arg2 = storeAddrExpr; es = CLG_(sets).D1w; } else { CLG_ASSERT( loadAddrExpr && storeAddrExpr ); CLG_ASSERT( isIRAtom(loadAddrExpr) ); CLG_ASSERT( isIRAtom(storeAddrExpr) ); if ( loadStoreAddrsMatch(loadAddrExpr, storeAddrExpr) ) { /* modify: suppose write access, as this is * more resource consuming (as in callgrind for VG2) * Cachegrind does a read here (!) * DISCUSS: Best way depends on simulation model? */ if (instrIssued) { helperName = CLG_(cachesim).log_0I1Dw_name; helperAddr = CLG_(cachesim).log_0I1Dw; } else { helperName = CLG_(cachesim).log_1I1Dw_name; helperAddr = CLG_(cachesim).log_1I1Dw; } argc = 2; arg2 = storeAddrExpr; es = CLG_(sets).D1w; } else { // load/store if (instrIssued) { helperName = CLG_(cachesim).log_0I2D_name; helperAddr = CLG_(cachesim).log_0I2D; } else { helperName = CLG_(cachesim).log_1I2D_name; helperAddr = CLG_(cachesim).log_1I2D; } argc = 3; arg2 = loadAddrExpr; arg3 = storeAddrExpr; es = CLG_(sets).D2; } } /* helper could be unset depending on the simulator used */ if (helperAddr == 0) return 0; /* Setup 1st arg: InstrInfo */ arg1 = mkIRExpr_HWord( (HWord)ii ); // Add call to the instrumentation function if (argc == 1) argv = mkIRExprVec_1(arg1); else if (argc == 2) argv = mkIRExprVec_2(arg1, arg2); else if (argc == 3) argv = mkIRExprVec_3(arg1, arg2, arg3); else VG_(tool_panic)("argc... not 1 or 2 or 3?"); di = unsafeIRDirty_0_N( argc, helperName, VG_(fnptr_to_fnentry)( helperAddr ), argv); addStmtToIRSB( bbOut, IRStmt_Dirty(di) ); return es; }
void AddGetHelper(IRSB* sb, IRTemp lhs, Int offset, Addr addr) { ULong offset_cur = (HWord)offset; ULong lhs_int = (HWord)lhs; IRDirty* d1,*d2; d1 = unsafeIRDirty_0_N(0, "EmitGetTmp2RegHelper", &EmitGetTmp2RegHelper, mkIRExprVec_3( mkIRExpr_HWord(lhs_int), mkIRExpr_HWord(offset_cur), mkIRExpr_HWord(counter) ) ); setHelperAnns(d1); addStmtToIRSB(sb,IRStmt_Dirty(d1)); Addr relateaddr = getAddrOf(getVarOf(locToHashKey(offset_cur,0,RegLoc))); ThreadId tid = VG_(get_running_tid)(); tl_assert(VG_INVALID_THREADID != tid); ThreadState* tst = VG_(get_ThreadState)(tid); // SizeT argRDI = tst->arch.vex.guest_RDI; // SizeT argRSI = tst->arch.vex.guest_RSI; VexGuestArchState* t = &(tst->arch.vex); ULong argRDI; ULong argRSI; /* if(offset == 64) { argRSI = *(&(t->guest_RSI)); VG_(message)(Vg_UserMsg, "argRSI %ld \n", argRSI); VG_(message)(Vg_UserMsg, "(SSizeT)argRSI %ld \n", (SSizeT)argRSI); } */ //VG_(message)(Vg_UserMsg, "addr is %lx \n", addr); if(isRealloc) { if(argNum == 1 && offset == 64) { //argRSI = *(&(tst->arch.vex) + offset); argRSI = *(&(t->guest_RSI)); // VG_(message)(Vg_UserMsg, "realloc arg %lu \n", argRSI); /* if((SSizeT)argRSI < 0) { VG_(message)(Vg_UserMsg, "ERROR realloc arg %ld ", (SSizeT)argRSI); //VG_printf("ERROR realloc arg %ld ",(SSizeT)argRSI); if(relateaddr != 0) VG_(message)(Vg_UserMsg, "(come from 0x%lx)", relateaddr); //VG_printf("(come from 0x%x)",relateaddr); VG_(message)(Vg_UserMsg, "\n"); //VG_printf("\n"); } */ d2 = unsafeIRDirty_0_N(2, "trace_error", &trace_error, mkIRExprVec_3( mkIRExpr_HWord(argRSI), mkIRExpr_HWord(addr), mkIRExpr_HWord(relateaddr) ) ); setHelperAnns(d2); addStmtToIRSB(sb,IRStmt_Dirty(d2)); argNum--; isRealloc = 0; } } else if(argNum > 0) { if(offset == 72) { argRDI = *(&(t->guest_RDI)); // VG_(message)(Vg_UserMsg, "alloc arg %lu \n", argRDI); /* if((SSizeT)argRDI < 0) { VG_(message)(Vg_UserMsg, "ERROR malloc/calloc/new/[] new first arg %ld ", (SSizeT)argRDI); //VG_printf("ERROR malloc/calloc/new/[] new first arg %ld ",(SSizeT)argRDI); if(relateaddr != 0) VG_(message)(Vg_UserMsg, "(come from 0x%lx)", relateaddr); //VG_printf("(come from 0x%x)",relateaddr); VG_(message)(Vg_UserMsg, "\n"); //VG_printf("\n"); } */ d2 = unsafeIRDirty_0_N(2, "trace_error", &trace_error, mkIRExprVec_3( mkIRExpr_HWord(argRDI), mkIRExpr_HWord(addr), mkIRExpr_HWord(relateaddr) ) ); setHelperAnns(d2); addStmtToIRSB(sb,IRStmt_Dirty(d2)); argNum--; } else if(offset == 64) { argRSI = *(&(t->guest_RSI)); // VG_(message)(Vg_UserMsg, "alloc arg %lu \n", argRSI); /* if((SSizeT)argRSI < 0) { VG_(message)(Vg_UserMsg, "ERROR calloc/memalign second arg %ld ", (SSizeT)argRSI); //VG_printf("ERROR calloc/memalign second arg %ld ",(SSizeT)argRSI); if(relateaddr != -1) VG_(message)(Vg_UserMsg, "(come from 0x%lx)", relateaddr); //VG_printf("(come from 0x%x)",relateaddr); VG_(message)(Vg_UserMsg, "\n"); //VG_printf("\n"); } */ d2 = unsafeIRDirty_0_N(2, "trace_error", &trace_error, mkIRExprVec_3( mkIRExpr_HWord(argRSI), mkIRExpr_HWord(addr), mkIRExpr_HWord(relateaddr) ) ); setHelperAnns(d2); addStmtToIRSB(sb,IRStmt_Dirty(d2)); argNum--; } } return; }
static void flushEvents ( CgState* cgs ) { Int i, regparms; Char* helperName; void* helperAddr; IRExpr** argv; IRExpr* i_node_expr; IRDirty* di; Event* ev; Event* ev2; Event* ev3; i = 0; while (i < cgs->events_used) { helperName = NULL; helperAddr = NULL; argv = NULL; regparms = 0; /* generate IR to notify event i and possibly the ones immediately following it. */ tl_assert(i >= 0 && i < cgs->events_used); ev = &cgs->events[i]; ev2 = ( i < cgs->events_used-1 ? &cgs->events[i+1] : NULL ); ev3 = ( i < cgs->events_used-2 ? &cgs->events[i+2] : NULL ); if (DEBUG_CG) { VG_(printf)(" flush "); showEvent( ev ); } i_node_expr = mkIRExpr_HWord( (HWord)ev->inode ); /* Decide on helper fn to call and args to pass it, and advance i appropriately. */ switch (ev->ekind) { case Event_Ir: /* Merge with a following Dr/Dm if it is from this insn. */ if (ev2 && (ev2->ekind == Event_Dr || ev2->ekind == Event_Dm)) { tl_assert(ev2->inode == ev->inode); helperName = "log_1I_1Dr_cache_access"; helperAddr = &log_1I_1Dr_cache_access; argv = mkIRExprVec_3( i_node_expr, ev2->dataEA, mkIRExpr_HWord( ev2->datasize ) ); regparms = 3; i += 2; } /* Merge with a following Dw if it is from this insn. */ else if (ev2 && ev2->ekind == Event_Dw) { tl_assert(ev2->inode == ev->inode); helperName = "log_1I_1Dw_cache_access"; helperAddr = &log_1I_1Dw_cache_access; argv = mkIRExprVec_3( i_node_expr, ev2->dataEA, mkIRExpr_HWord( ev2->datasize ) ); regparms = 3; i += 2; } /* Merge with two following Irs if possible. */ else if (ev2 && ev3 && ev2->ekind == Event_Ir && ev3->ekind == Event_Ir) { helperName = "log_3I_0D_cache_access"; helperAddr = &log_3I_0D_cache_access; argv = mkIRExprVec_3( i_node_expr, mkIRExpr_HWord( (HWord)ev2->inode ), mkIRExpr_HWord( (HWord)ev3->inode ) ); regparms = 3; i += 3; } /* Merge with a following Ir if possible. */ else if (ev2 && ev2->ekind == Event_Ir) { helperName = "log_2I_0D_cache_access"; helperAddr = &log_2I_0D_cache_access; argv = mkIRExprVec_2( i_node_expr, mkIRExpr_HWord( (HWord)ev2->inode ) ); regparms = 2; i += 2; } /* No merging possible; emit as-is. */ else { // Assertion: this Event_Ir must be the last one in the // events buffer, otherwise it would have been merged with a // following event. tl_assert(!ev2 && !ev3); helperName = "log_1I_0D_cache_access"; helperAddr = &log_1I_0D_cache_access; argv = mkIRExprVec_1( i_node_expr ); regparms = 1; i++; } break; case Event_Dr: case Event_Dm: helperName = "log_0I_1Dr_cache_access"; helperAddr = &log_0I_1Dr_cache_access; argv = mkIRExprVec_3( i_node_expr, ev->dataEA, mkIRExpr_HWord( ev->datasize ) ); regparms = 3; i++; break; case Event_Dw: helperName = "log_0I_1Dw_cache_access"; helperAddr = &log_0I_1Dw_cache_access; argv = mkIRExprVec_3( i_node_expr, ev->dataEA, mkIRExpr_HWord( ev->datasize ) ); regparms = 3; i++; break; default: tl_assert(0); } /* Add the helper. */ tl_assert(helperName); tl_assert(helperAddr); tl_assert(argv); di = unsafeIRDirty_0_N( regparms, helperName, VG_(fnptr_to_fnentry)( helperAddr ), argv ); addStmtToIRSB( cgs->sbOut, IRStmt_Dirty(di) ); } cgs->events_used = 0; }
// Instrumentation for the end of each original instruction. static void endOfInstr(IRBB* bbOut, instr_info* i_node, Bool bbSeenBefore, UInt instrAddr, UInt instrLen, UInt dataSize, IRExpr* loadAddrExpr, IRExpr* storeAddrExpr) { IRDirty* di; IRExpr *arg1, *arg2, *arg3, **argv; Int argc; Char* helperName; void* helperAddr; IRType wordTy; // Stay sane ... tl_assert(sizeof(HWord) == sizeof(void*)); if (sizeof(HWord) == 4) { wordTy = Ity_I32; } else if (sizeof(HWord) == 8) { wordTy = Ity_I64; } else { VG_(tool_panic)("endOfInstr: strange word size"); } if (loadAddrExpr) tl_assert(wordTy == typeOfIRExpr(bbOut->tyenv, loadAddrExpr)); if (storeAddrExpr) tl_assert(wordTy == typeOfIRExpr(bbOut->tyenv, storeAddrExpr)); // Nb: instrLen will be zero if Vex failed to decode it. tl_assert( 0 == instrLen || (instrLen >= VGA_MIN_INSTR_SZB && instrLen <= VGA_MAX_INSTR_SZB) ); // Large (eg. 28B, 108B, 512B on x86) 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 (dataSize > MIN_LINE_SIZE) dataSize = MIN_LINE_SIZE; // Setup 1st arg: instr_info node's address // Believed to be 64-bit clean do_details(i_node, bbSeenBefore, instrAddr, instrLen, dataSize ); arg1 = mkIRExpr_HWord( (HWord)i_node ); if (!loadAddrExpr && !storeAddrExpr) { // no load/store tl_assert(0 == dataSize); helperName = "log_1I_0D_cache_access"; helperAddr = &log_1I_0D_cache_access; argc = 1; argv = mkIRExprVec_1(arg1); } else if (loadAddrExpr && !storeAddrExpr) { // load tl_assert( isIRAtom(loadAddrExpr) ); helperName = "log_1I_1Dr_cache_access"; helperAddr = &log_1I_1Dr_cache_access; argc = 2; arg2 = loadAddrExpr; argv = mkIRExprVec_2(arg1, arg2); } else if (!loadAddrExpr && storeAddrExpr) { // store tl_assert( isIRAtom(storeAddrExpr) ); helperName = "log_1I_1Dw_cache_access"; helperAddr = &log_1I_1Dw_cache_access; argc = 2; arg2 = storeAddrExpr; argv = mkIRExprVec_2(arg1, arg2); } else { tl_assert( loadAddrExpr && storeAddrExpr ); tl_assert( isIRAtom(loadAddrExpr) ); tl_assert( isIRAtom(storeAddrExpr) ); if ( loadStoreAddrsMatch(loadAddrExpr, storeAddrExpr) ) { // modify helperName = "log_1I_1Dr_cache_access"; helperAddr = &log_1I_1Dr_cache_access; argc = 2; arg2 = loadAddrExpr; argv = mkIRExprVec_2(arg1, arg2); } else { // load/store helperName = "log_1I_2D_cache_access"; helperAddr = &log_1I_2D_cache_access; argc = 3; arg2 = loadAddrExpr; arg3 = storeAddrExpr; argv = mkIRExprVec_3(arg1, arg2, arg3); } } // Add call to the instrumentation function di = unsafeIRDirty_0_N( argc, helperName, helperAddr, argv); addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); }
/* This version of flushEvents (currently unused) is similar to the one in lackey with the primary difference that it groups together pairs of events for a single callback. This helps to reduce the total amount of function call overhead. */ static void flushEventsCB(IRSB* sb) { IRDirty* di; Int i; for (i = 0; i < events_used; i++) { Event* ev = &events[i]; const HChar* helperName; void* helperAddr; IRExpr** argv; Event* ev2; Int regparms; ev2 = i < events_used-1 ? &events[i+1] : NULL; if (ev2 && ev->ekind == ev2->ekind && ev->size == ev2->size) { // Decide on helper fn to call and args to pass it. switch (ev->ekind) { case Event_Ir: helperName = "trace_2instr"; helperAddr = trace_2instr; break; case Event_Dr: helperName = "trace_2load"; helperAddr = trace_2load; break; case Event_Dw: helperName = "trace_2store"; helperAddr = trace_2store; break; case Event_Dm: helperName = "trace_2modify"; helperAddr = trace_2modify; break; default: tl_assert(0); } argv = mkIRExprVec_3( ev->addr, ev2->addr, mkIRExpr_HWord( ev->size )); regparms = 3; // Skip the next event, since we paired it i++; } else if (ev2 && ev->ekind == Event_Dr && ev2->ekind == Event_Dw && ev->size == ev2->size) { // Load then store helperName = "trace_loadstore"; helperAddr = trace_loadstore; argv = mkIRExprVec_3( ev->addr, ev2->addr, mkIRExpr_HWord( ev->size )); regparms = 3; i++; } else if (ev2 && ev->ekind == Event_Dw && ev2->ekind == Event_Dr && ev->size == ev2->size) { // Store then load helperName = "trace_storeload"; helperAddr = trace_storeload; argv = mkIRExprVec_3( ev->addr, ev2->addr, mkIRExpr_HWord( ev->size )); regparms = 3; i++; } else { // Decide on helper fn to call and args to pass it. switch (ev->ekind) { case Event_Ir: helperName = "trace_instr"; helperAddr = trace_instr; break; case Event_Dr: helperName = "trace_load"; helperAddr = trace_load; break; case Event_Dw: helperName = "trace_store"; helperAddr = trace_store; break; case Event_Dm: helperName = "trace_modify"; helperAddr = trace_modify; break; default: tl_assert(0); } argv = mkIRExprVec_2( ev->addr, mkIRExpr_HWord( ev->size ) ); regparms = 2; } // Add the helper. di = unsafeIRDirty_0_N(regparms, helperName, VG_(fnptr_to_fnentry)( helperAddr ), argv ); addStmtToIRSB( sb, IRStmt_Dirty(di) ); } events_used = 0; }
static IRExpr * log_reads_expr(unsigned tid, IRSB *sb, IRExpr *exp) { if (!exp) return NULL; switch (exp->tag) { case Iex_Get: case Iex_FreeVariable: case Iex_EntryPoint: case Iex_ControlFlow: return exp; case Iex_GetI: { IRExprGetI *e = (IRExprGetI *)exp; return IRExpr_GetI(e->descr, log_reads_expr(tid, sb, e->ix), e->bias, e->tid); } case Iex_Qop: { IRExprQop *e = (IRExprQop *)exp; return IRExpr_Qop(e->op, log_reads_expr(tid, sb, e->arg1), log_reads_expr(tid, sb, e->arg2), log_reads_expr(tid, sb, e->arg3), log_reads_expr(tid, sb, e->arg4)); } case Iex_Triop: { IRExprTriop *e = (IRExprTriop *)exp; return IRExpr_Triop(e->op, log_reads_expr(tid, sb, e->arg1), log_reads_expr(tid, sb, e->arg2), log_reads_expr(tid, sb, e->arg3)); } case Iex_Binop: { IRExprBinop *e = (IRExprBinop *)exp; return IRExpr_Binop(e->op, log_reads_expr(tid, sb, e->arg1), log_reads_expr(tid, sb, e->arg2)); } case Iex_Associative: { IRExprAssociative *e = (IRExprAssociative *)exp; IRExpr **newArgs = alloc_irexpr_array(e->nr_arguments); for (int x = 0; x < e->nr_arguments; x++) newArgs[x] = log_reads_expr(tid, sb, e->contents[x]); return IRExpr_Associative_Claim(e->op, e->nr_arguments, newArgs); } case Iex_Unop: { IRExprUnop *e = (IRExprUnop *)exp; return IRExpr_Unop(e->op, log_reads_expr(tid, sb, e->arg)); } case Iex_Load: { IRExprLoad *e = (IRExprLoad *)exp; IRExpr **args; void *helper; const char *helper_name; IRTemp dest; IRDirty *f; assert(e->addr->type() == Ity_I64); /* Shut compiler up */ helper = (void *)0xf001; helper_name = (const char *)0xdead; #define HLP(x) helper_name = "helper_load_" #x ; helper = (void *)helper_load_ ## x ; switch (e->ty) { case Ity_INVALID: abort(); case Ity_I1: abort(); case Ity_I8: HLP(8); break; case Ity_I16: HLP(16); break; case Ity_I32: HLP(32); break; case Ity_I64: HLP(64); break; case Ity_I128: HLP(128); break; } #undef HLP args = mkIRExprVec_3(log_reads_expr(tid, sb, e->addr), IRExpr_Get(OFFSET_amd64_RSP, Ity_I64, tid, 0), IRExpr_Get(OFFSET_amd64_RIP, Ity_I64, tid, 0)); dest = newIRTemp(sb->tyenv); f = unsafeIRDirty_1_N(threadAndRegister::temp(tid, dest, 0), 0, helper_name, helper, args); addStmtToIRSB(sb, IRStmt_Dirty(f)); return IRExpr_RdTmp(dest, e->ty, tid, 0); } case Iex_Const: return exp; case Iex_CCall: { IRExprCCall *e = (IRExprCCall *)exp; IRExpr **args; int x; int nr_args; for (nr_args = 0; e->args[nr_args]; nr_args++) ; args = alloc_irexpr_array(nr_args + 1); args[nr_args] = NULL; for (x = 0; x < nr_args; x++) args[x] = log_reads_expr(tid, sb, e->args[x]); return IRExpr_CCall(e->cee, e->retty, args); } case Iex_Mux0X: { IRExprMux0X *e = (IRExprMux0X *)exp; return IRExpr_Mux0X(log_reads_expr(tid, sb, e->cond), log_reads_expr(tid, sb, e->expr0), log_reads_expr(tid, sb, e->exprX)); } case Iex_HappensBefore: abort(); } abort(); }