static IRBB* cg_instrument ( IRBB* bbIn, VexGuestLayout* layout, Addr64 orig_addr_noredir, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy ) { Int i, isize; IRStmt* st; Addr64 cia; /* address of current insn */ CgState cgs; IRTypeEnv* tyenv = bbIn->tyenv; InstrInfo* curr_inode = NULL; if (gWordTy != hWordTy) { /* We don't currently support this case. */ VG_(tool_panic)("host/guest word size mismatch"); } /* Set up BB, including copying of the where-next stuff. */ cgs.bbOut = emptyIRBB(); cgs.bbOut->tyenv = dopyIRTypeEnv(tyenv); tl_assert( isIRAtom(bbIn->next) ); cgs.bbOut->next = dopyIRExpr(bbIn->next); cgs.bbOut->jumpkind = bbIn->jumpkind; // Copy verbatim any IR preamble preceding the first IMark i = 0; while (i < bbIn->stmts_used && bbIn->stmts[i]->tag != Ist_IMark) { addStmtToIRBB( cgs.bbOut, bbIn->stmts[i] ); i++; } // Get the first statement, and initial cia from it tl_assert(bbIn->stmts_used > 0); tl_assert(i < bbIn->stmts_used); st = bbIn->stmts[i]; tl_assert(Ist_IMark == st->tag); cia = st->Ist.IMark.addr; // Set up running state and get block info cgs.events_used = 0; cgs.bbInfo = get_BB_info(bbIn, (Addr)orig_addr_noredir); cgs.bbInfo_i = 0; if (DEBUG_CG) VG_(printf)("\n\n---------- cg_instrument ----------\n"); // Traverse the block, initialising inodes, adding events and flushing as // necessary. for (/*use current i*/; i < bbIn->stmts_used; i++) { st = bbIn->stmts[i]; tl_assert(isFlatIRStmt(st)); switch (st->tag) { case Ist_NoOp: case Ist_AbiHint: case Ist_Put: case Ist_PutI: case Ist_MFence: break; case Ist_IMark: cia = st->Ist.IMark.addr; isize = st->Ist.IMark.len; // If Vex fails to decode an instruction, the size will be zero. // Pretend otherwise. if (isize == 0) isize = VG_MIN_INSTR_SZB; // Sanity-check size. tl_assert( (VG_MIN_INSTR_SZB <= isize && isize <= VG_MAX_INSTR_SZB) || VG_CLREQ_SZB == isize ); // Get space for and init the inode, record it as the current one. // Subsequent Dr/Dw/Dm events from the same instruction will // also use it. curr_inode = setup_InstrInfo(&cgs, cia, isize); addEvent_Ir( &cgs, curr_inode ); break; case Ist_Tmp: { IRExpr* data = st->Ist.Tmp.data; if (data->tag == Iex_Load) { IRExpr* aexpr = data->Iex.Load.addr; // Note also, endianness info is ignored. I guess // that's not interesting. addEvent_Dr( &cgs, curr_inode, sizeofIRType(data->Iex.Load.ty), aexpr ); } break; } case Ist_Store: { IRExpr* data = st->Ist.Store.data; IRExpr* aexpr = st->Ist.Store.addr; addEvent_Dw( &cgs, curr_inode, sizeofIRType(typeOfIRExpr(tyenv, data)), aexpr ); break; } case Ist_Dirty: { Int dataSize; IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { /* This dirty helper accesses memory. Collect the details. */ tl_assert(d->mAddr != NULL); tl_assert(d->mSize != 0); dataSize = d->mSize; // 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; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) addEvent_Dr( &cgs, curr_inode, dataSize, d->mAddr ); if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) addEvent_Dw( &cgs, curr_inode, dataSize, d->mAddr ); } else { tl_assert(d->mAddr == NULL); tl_assert(d->mSize == 0); } break; } case Ist_Exit: /* We may never reach the next statement, so need to flush all outstanding transactions now. */ flushEvents( &cgs ); break; default: tl_assert(0); break; } /* Copy the original statement */ addStmtToIRBB( cgs.bbOut, st ); if (DEBUG_CG) { ppIRStmt(st); VG_(printf)("\n"); } } /* At the end of the bb. Flush outstandings. */ flushEvents( &cgs ); /* done. stay sane ... */ tl_assert(cgs.bbInfo_i == cgs.bbInfo->n_instrs); if (DEBUG_CG) { VG_(printf)( "goto {"); ppIRJumpKind(bbIn->jumpkind); VG_(printf)( "} "); ppIRExpr( bbIn->next ); VG_(printf)( "}\n"); } return cgs.bbOut; }
static void collectStatementInfo(IRTypeEnv* tyenv, IRBB* bbOut, IRStmt* st, Addr* instrAddr, UInt* instrLen, IRExpr** loadAddrExpr, IRExpr** storeAddrExpr, UInt* dataSize, IRType hWordTy) { CLG_ASSERT(isFlatIRStmt(st)); switch (st->tag) { case Ist_NoOp: break; case Ist_AbiHint: /* ABI hints aren't interesting. Ignore. */ break; case Ist_IMark: /* st->Ist.IMark.addr is a 64-bit int. ULong_to_Ptr casts this to the host's native pointer type; if that is 32 bits then it discards the upper 32 bits. If we are cachegrinding on a 32-bit host then we are also ensured that the guest word size is 32 bits, due to the assertion in cg_instrument that the host and guest word sizes must be the same. Hence st->Ist.IMark.addr will have been derived from a 32-bit guest code address and truncation of it is safe. I believe this assignment should be correct for both 32- and 64-bit machines. */ *instrAddr = (Addr)ULong_to_Ptr(st->Ist.IMark.addr); *instrLen = st->Ist.IMark.len; break; case Ist_Tmp: { IRExpr* data = st->Ist.Tmp.data; if (data->tag == Iex_Load) { IRExpr* aexpr = data->Iex.Load.addr; CLG_ASSERT( isIRAtom(aexpr) ); // Note also, endianness info is ignored. I guess that's not // interesting. // XXX: repe cmpsb does two loads... the first one is ignored here! //tl_assert( NULL == *loadAddrExpr ); // XXX: ??? *loadAddrExpr = aexpr; *dataSize = sizeofIRType(data->Iex.Load.ty); } break; } case Ist_Store: { IRExpr* data = st->Ist.Store.data; IRExpr* aexpr = st->Ist.Store.addr; CLG_ASSERT( isIRAtom(aexpr) ); if ( NULL == *storeAddrExpr ) { /* this is a kludge: ignore all except the first store from an instruction. */ *storeAddrExpr = aexpr; *dataSize = sizeofIRType(typeOfIRExpr(tyenv, data)); } break; } case Ist_Dirty: { IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { /* This dirty helper accesses memory. Collect the details. */ CLG_ASSERT(d->mAddr != NULL); CLG_ASSERT(d->mSize != 0); *dataSize = d->mSize; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) *loadAddrExpr = d->mAddr; if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) *storeAddrExpr = d->mAddr; } else { CLG_ASSERT(d->mAddr == NULL); CLG_ASSERT(d->mSize == 0); } break; } case Ist_Put: case Ist_PutI: case Ist_MFence: case Ist_Exit: break; default: VG_(printf)("\n"); ppIRStmt(st); VG_(printf)("\n"); VG_(tool_panic)("Callgrind: unhandled IRStmt"); } }
IRSB* h_instrument ( VgCallbackClosure* closure, IRSB* sbIn, const VexGuestLayout* layout, const VexGuestExtents* vge, const VexArchInfo* archinfo_host, IRType gWordTy, IRType hWordTy ) { Bool verboze = 0||False; Int i /*, j*/; PCEnv pce; struct _SGEnv* sgenv; if (gWordTy != hWordTy) { /* We don't currently support this case. */ VG_(tool_panic)("host/guest word size mismatch"); } /* Check we're not completely nuts */ tl_assert(sizeof(UWord) == sizeof(void*)); tl_assert(sizeof(Word) == sizeof(void*)); tl_assert(sizeof(Addr) == sizeof(void*)); tl_assert(sizeof(ULong) == 8); tl_assert(sizeof(Long) == 8); tl_assert(sizeof(Addr) == sizeof(void*)); tl_assert(sizeof(UInt) == 4); tl_assert(sizeof(Int) == 4); /* Set up the running environment. Both .sb and .tmpMap are modified as we go along. Note that tmps are added to both .sb->tyenv and .tmpMap together, so the valid index-set for those two arrays should always be identical. */ VG_(memset)(&pce, 0, sizeof(pce)); pce.sb = deepCopyIRSBExceptStmts(sbIn); pce.trace = verboze; pce.hWordTy = hWordTy; pce.gWordTy = gWordTy; pce.guest_state_sizeB = layout->total_sizeB; pce.qmpMap = VG_(newXA)( VG_(malloc), "pc.h_instrument.1", VG_(free), sizeof(TempMapEnt)); for (i = 0; i < sbIn->tyenv->types_used; i++) { TempMapEnt ent; ent.kind = NonShad; ent.shadow = IRTemp_INVALID; VG_(addToXA)( pce.qmpMap, &ent ); } tl_assert( VG_(sizeXA)( pce.qmpMap ) == sbIn->tyenv->types_used ); /* Also set up for the sg_ instrumenter. See comments at the top of this instrumentation section for details. The two parameters constitute a closure, which sg_ can use to correctly generate new IRTemps as needed. */ sgenv = sg_instrument_init( for_sg__newIRTemp_cb, (void*)&pce ); /* Copy verbatim any IR preamble preceding the first IMark */ i = 0; while (i < sbIn->stmts_used && sbIn->stmts[i]->tag != Ist_IMark) { IRStmt* st = sbIn->stmts[i]; tl_assert(st); tl_assert(isFlatIRStmt(st)); stmt( 'C', &pce, sbIn->stmts[i] ); i++; } /* Iterate over the remaining stmts to generate instrumentation. */ tl_assert(sbIn->stmts_used > 0); tl_assert(i >= 0); tl_assert(i < sbIn->stmts_used); tl_assert(sbIn->stmts[i]->tag == Ist_IMark); for (/*use current i*/; i < sbIn->stmts_used; i++) { /* generate sg_ instrumentation for this stmt */ sg_instrument_IRStmt( sgenv, pce.sb, sbIn->stmts[i], layout, gWordTy, hWordTy ); stmt( 'C', &pce, sbIn->stmts[i] ); } /* generate sg_ instrumentation for the final jump */ sg_instrument_final_jump( sgenv, pce.sb, sbIn->next, sbIn->jumpkind, layout, gWordTy, hWordTy ); /* and finalise .. */ sg_instrument_fini( sgenv ); /* If this fails, there's been some serious snafu with tmp management, that should be investigated. */ tl_assert( VG_(sizeXA)( pce.qmpMap ) == pce.sb->tyenv->types_used ); VG_(deleteXA)( pce.qmpMap ); return pce.sb; }
static Bool handleOneStatement(IRTypeEnv* tyenv, IRBB* bbOut, IRStmt* st, IRStmt* st2, Addr* instrAddr, UInt* instrLen, IRExpr** loadAddrExpr, IRExpr** storeAddrExpr, UInt* dataSize) { tl_assert(isFlatIRStmt(st)); switch (st->tag) { case Ist_NoOp: case Ist_AbiHint: case Ist_Put: case Ist_PutI: case Ist_MFence: break; case Ist_Exit: { // This is a conditional jump. Most of the time, we want to add the // instrumentation before it, to ensure it gets executed. Eg, (1) if // this conditional jump is just before an IMark: // // t108 = Not1(t107) // [add instrumentation here] // if (t108) goto {Boring} 0x3A96637D:I32 // ------ IMark(0x3A966370, 7) ------ // // or (2) if this conditional jump is the last thing before the // block-ending unconditional jump: // // t111 = Not1(t110) // [add instrumentation here] // if (t111) goto {Boring} 0x3A96637D:I32 // goto {Boring} 0x3A966370:I32 // // One case (3) where we want the instrumentation after the conditional // jump is when the conditional jump is for an x86 REP instruction: // // ------ IMark(0x3A967F13, 2) ------ // t1 = GET:I32(4) // t6 = CmpEQ32(t1,0x0:I32) // if (t6) goto {Boring} 0x3A967F15:I32 # ignore this cond jmp // t7 = Sub32(t1,0x1:I32) // PUT(4) = t7 // ... // t56 = Not1(t55) // [add instrumentation here] // if (t56) goto {Boring} 0x3A967F15:I32 // // Therefore, we return true if the next statement is an IMark, or if // there is no next statement (which matches case (2), as the final // unconditional jump is not represented in the IRStmt list). // // Note that this approach won't do in the long run for supporting // PPC, but it's good enough for x86/AMD64 for the 3.0.X series. if (NULL == st2 || Ist_IMark == st2->tag) return True; else return False; } case Ist_IMark: /* st->Ist.IMark.addr is a 64-bit int. ULong_to_Ptr casts this to the host's native pointer type; if that is 32 bits then it discards the upper 32 bits. If we are cachegrinding on a 32-bit host then we are also ensured that the guest word size is 32 bits, due to the assertion in cg_instrument that the host and guest word sizes must be the same. Hence st->Ist.IMark.addr will have been derived from a 32-bit guest code address and truncation of it is safe. I believe this assignment should be correct for both 32- and 64-bit machines. */ *instrAddr = (Addr)ULong_to_Ptr(st->Ist.IMark.addr); *instrLen = st->Ist.IMark.len; break; case Ist_Tmp: { IRExpr* data = st->Ist.Tmp.data; if (data->tag == Iex_Load) { IRExpr* aexpr = data->Iex.Load.addr; tl_assert( isIRAtom(aexpr) ); // Note also, endianness info is ignored. I guess that's not // interesting. // XXX: repe cmpsb does two loads... the first one is ignored here! //tl_assert( NULL == *loadAddrExpr ); // XXX: ??? *loadAddrExpr = aexpr; *dataSize = sizeofIRType(data->Iex.Load.ty); } break; } case Ist_Store: { IRExpr* data = st->Ist.Store.data; IRExpr* aexpr = st->Ist.Store.addr; tl_assert( isIRAtom(aexpr) ); tl_assert( NULL == *storeAddrExpr ); // XXX: ??? *storeAddrExpr = aexpr; *dataSize = sizeofIRType(typeOfIRExpr(tyenv, data)); break; } case Ist_Dirty: { IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { /* This dirty helper accesses memory. Collect the details. */ tl_assert(d->mAddr != NULL); tl_assert(d->mSize != 0); *dataSize = d->mSize; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) *loadAddrExpr = d->mAddr; if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) *storeAddrExpr = d->mAddr; } else { tl_assert(d->mAddr == NULL); tl_assert(d->mSize == 0); } break; } default: VG_(printf)("\n"); ppIRStmt(st); VG_(printf)("\n"); VG_(tool_panic)("Cachegrind: unhandled IRStmt"); } return False; }
IRSB* h_instrument ( VgCallbackClosure* closure, IRSB* sbIn, VexGuestLayout* layout, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy ) { Bool verboze = 0||False; Int i ; PCEnv pce; struct _SGEnv* sgenv; if (gWordTy != hWordTy) { VG_(tool_panic)("host/guest word size mismatch"); } tl_assert(sizeof(UWord) == sizeof(void*)); tl_assert(sizeof(Word) == sizeof(void*)); tl_assert(sizeof(Addr) == sizeof(void*)); tl_assert(sizeof(ULong) == 8); tl_assert(sizeof(Long) == 8); tl_assert(sizeof(Addr64) == 8); tl_assert(sizeof(UInt) == 4); tl_assert(sizeof(Int) == 4); VG_(memset)(&pce, 0, sizeof(pce)); pce.sb = deepCopyIRSBExceptStmts(sbIn); pce.trace = verboze; pce.hWordTy = hWordTy; pce.gWordTy = gWordTy; pce.guest_state_sizeB = layout->total_sizeB; pce.qmpMap = VG_(newXA)( VG_(malloc), "pc.h_instrument.1", VG_(free), sizeof(TempMapEnt)); for (i = 0; i < sbIn->tyenv->types_used; i++) { TempMapEnt ent; ent.kind = NonShad; ent.shadow = IRTemp_INVALID; VG_(addToXA)( pce.qmpMap, &ent ); } tl_assert( VG_(sizeXA)( pce.qmpMap ) == sbIn->tyenv->types_used ); sgenv = sg_instrument_init( for_sg__newIRTemp_cb, (void*)&pce ); i = 0; while (i < sbIn->stmts_used && sbIn->stmts[i]->tag != Ist_IMark) { IRStmt* st = sbIn->stmts[i]; tl_assert(st); tl_assert(isFlatIRStmt(st)); stmt( 'C', &pce, sbIn->stmts[i] ); i++; } tl_assert(sbIn->stmts_used > 0); tl_assert(i >= 0); tl_assert(i < sbIn->stmts_used); tl_assert(sbIn->stmts[i]->tag == Ist_IMark); for (; i < sbIn->stmts_used; i++) { sg_instrument_IRStmt( sgenv, pce.sb, sbIn->stmts[i], layout, gWordTy, hWordTy ); stmt( 'C', &pce, sbIn->stmts[i] ); } sg_instrument_final_jump( sgenv, pce.sb, sbIn->next, sbIn->jumpkind, layout, gWordTy, hWordTy ); sg_instrument_fini( sgenv ); tl_assert( VG_(sizeXA)( pce.qmpMap ) == pce.sb->tyenv->types_used ); VG_(deleteXA)( pce.qmpMap ); return pce.sb; }