예제 #1
0
static
void addConstMemStoreStmt( IRBB* bbOut, UWord addr, UInt val, IRType hWordTy)
{
    addStmtToIRBB( bbOut,
                   IRStmt_Store(CLGEndness,
                                IRExpr_Const(hWordTy == Ity_I32 ?
                                        IRConst_U32( addr ) :
                                        IRConst_U64( addr )),
                                IRExpr_Const(IRConst_U32(val)) ));
}
예제 #2
0
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;
}
예제 #3
0
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, helperAddr, argv);
      addStmtToIRBB( cgs->bbOut, IRStmt_Dirty(di) );
   }

   cgs->events_used = 0;
}
예제 #4
0
static
IRBB* CLG_(instrument)( VgCallbackClosure* closure,
                        IRBB* bbIn,
                        VexGuestLayout* layout,
                        VexGuestExtents* vge,
                        IRType gWordTy, IRType hWordTy )
{
    Int      i;
    IRBB*    bbOut;
    IRStmt*  st, *stnext;
    Addr     instrAddr, origAddr;
    UInt     instrLen = 0, dataSize;
    UInt     instrCount, costOffset;
    IRExpr  *loadAddrExpr, *storeAddrExpr;

    BB*         bb;

    IRDirty* di;
    IRExpr  *arg1, **argv;

    Bool        bb_seen_before     = False;
    UInt        cJumps = 0, cJumpsCorrected;
    Bool        beforeIBoundary, instrIssued;

    if (gWordTy != hWordTy) {
        /* We don't currently support this case. */
        VG_(tool_panic)("host/guest word size mismatch");
    }

    // No instrumentation if it is switched off
    if (! CLG_(instrument_state)) {
        CLG_DEBUG(5, "instrument(BB %p) [Instrumentation OFF]\n",
                  (Addr)closure->readdr);
        return bbIn;
    }

    CLG_DEBUG(3, "+ instrument(BB %p)\n", (Addr)closure->readdr);

    /* Set up BB for instrumented IR */
    bbOut           = emptyIRBB();
    bbOut->tyenv    = dopyIRTypeEnv(bbIn->tyenv);
    bbOut->next     = dopyIRExpr(bbIn->next);
    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( bbOut, bbIn->stmts[i] );
        i++;
    }

    // Get the first statement, and origAddr from it
    CLG_ASSERT(bbIn->stmts_used > 0);
    st = bbIn->stmts[i];
    CLG_ASSERT(Ist_IMark == st->tag);
    instrAddr = origAddr = (Addr)st->Ist.IMark.addr;
    CLG_ASSERT(origAddr == st->Ist.IMark.addr);  // XXX: check no overflow

    /* Get BB (creating if necessary).
     * JS: The hash table is keyed with orig_addr_noredir -- important!
     * JW: Why? If it is because of different chasing of the redirection,
     *     this is not needed, as chasing is switched off in callgrind
     */
    bb = CLG_(get_bb)(origAddr, bbIn, &bb_seen_before);
    //bb = CLG_(get_bb)(orig_addr_noredir, bbIn, &bb_seen_before);

    /*
     * Precondition:
     * - jmps_passed has number of cond.jumps passed in last executed BB
     * - 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
     */

    // helper call to setup_bbcc, with pointer to basic block info struct as argument
    arg1 = mkIRExpr_HWord( (HWord)bb );
    argv = mkIRExprVec_1(arg1);
    di = unsafeIRDirty_0_N( 1, "setup_bbcc",
                            VG_(fnptr_to_fnentry)( & CLG_(setup_bbcc) ),
                            argv);
    addStmtToIRBB( bbOut, IRStmt_Dirty(di) );

    instrCount = 0;
    costOffset = 0;

    // loop for each host instruction (starting from 'i')
    do {

        // We should be at an IMark statement
        CLG_ASSERT(Ist_IMark == st->tag);

        // Reset stuff for this original instruction
        loadAddrExpr = storeAddrExpr = NULL;
        instrIssued = False;
        dataSize = 0;

        // Process all the statements for this original instruction (ie. until
        // the next IMark statement, or the end of the block)
        do {
            i++;
            stnext = ( i < bbIn->stmts_used ? bbIn->stmts[i] : NULL );
            beforeIBoundary = !stnext || (Ist_IMark == stnext->tag);
            collectStatementInfo(bbIn->tyenv, bbOut, st, &instrAddr, &instrLen,
                                 &loadAddrExpr, &storeAddrExpr, &dataSize, hWordTy);

            // instrument a simulator call before conditional jumps
            if (st->tag == Ist_Exit) {
                // Nb: instrLen will be zero if Vex failed to decode it.
                // Also Client requests can appear to be very large (eg. 18
                // bytes on x86) because they are really multiple instructions.
                CLG_ASSERT( 0 == instrLen ||
                            bbIn->jumpkind == Ijk_ClientReq ||
                            (instrLen >= VG_MIN_INSTR_SZB &&
                             instrLen <= VG_MAX_INSTR_SZB) );

                // Add instrumentation before this statement
                endOfInstr(bbOut, &(bb->instr[instrCount]), bb_seen_before,
                           instrAddr - origAddr, instrLen, dataSize, &costOffset,
                           instrIssued, loadAddrExpr, storeAddrExpr);

                // prepare for a possible further simcall in same host instr
                loadAddrExpr = storeAddrExpr = NULL;
                instrIssued = True;

                if (!bb_seen_before) {
                    bb->jmp[cJumps].instr = instrCount;
                    bb->jmp[cJumps].skip = False;
                }

                /* Update global variable jmps_passed (this is before the jump!)
                 * A correction is needed if VEX inverted the last jump condition
                 */
                cJumpsCorrected = cJumps;
                if ((cJumps+1 == bb->cjmp_count) && bb->cjmp_inverted) cJumpsCorrected++;
                addConstMemStoreStmt( bbOut, (UWord) &CLG_(current_state).jmps_passed,
                                      cJumpsCorrected, hWordTy);

                cJumps++;
            }

            addStmtToIRBB( bbOut, st );
            st = stnext;
        }
        while (!beforeIBoundary);

        // Add instrumentation for this original instruction.
        if (!instrIssued || (loadAddrExpr != 0) || (storeAddrExpr !=0))
            endOfInstr(bbOut, &(bb->instr[instrCount]), bb_seen_before,
                       instrAddr - origAddr, instrLen, dataSize, &costOffset,
                       instrIssued, loadAddrExpr, storeAddrExpr);

        instrCount++;
    }
    while (st);

    /* Always update global variable jmps_passed (at end of BB)
     * A correction is needed if VEX inverted the last jump condition
     */
    cJumpsCorrected = cJumps;
    if (bb->cjmp_inverted) cJumpsCorrected--;
    addConstMemStoreStmt( bbOut, (UWord) &CLG_(current_state).jmps_passed,
                          cJumpsCorrected, hWordTy);

    /* This stores the instr of the call/ret at BB end */
    bb->jmp[cJumps].instr = instrCount-1;

    CLG_ASSERT(bb->cjmp_count == cJumps);
    CLG_ASSERT(bb->instr_count == instrCount);

    instrAddr += instrLen;
    if (bb_seen_before) {
        CLG_ASSERT(bb->instr_len == instrAddr - origAddr);
        CLG_ASSERT(bb->cost_count == costOffset);
        CLG_ASSERT(bb->jmpkind == bbIn->jumpkind);
    }
    else {
        bb->instr_len = instrAddr - origAddr;
        bb->cost_count = costOffset;
        bb->jmpkind = bbIn->jumpkind;
    }

    CLG_DEBUG(3, "- instrument(BB %p): byteLen %u, CJumps %u, CostLen %u\n",
              origAddr, bb->instr_len, bb->cjmp_count, bb->cost_count);
    if (cJumps>0) {
        CLG_DEBUG(3, "                     [ ");
        for (i=0; i<cJumps; i++)
            CLG_DEBUG(3, "%d ", bb->jmp[i].instr);
        CLG_DEBUG(3, "], last inverted: %s \n", bb->cjmp_inverted ? "yes":"no");
    }

    return bbOut;
}
예제 #5
0
static
EventSet* insert_simcall(IRBB* 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);
    addStmtToIRBB( bbOut, IRStmt_Dirty(di) );

    return es;
}
예제 #6
0
static IRBB* cg_instrument ( IRBB* bbIn, VexGuestLayout* layout, 
                             IRType gWordTy, IRType hWordTy )
{
   Int      i, dataSize = 0, bbInfo_i;
   IRBB*    bbOut;
   IRStmt*  st;
   BB_info* bbInfo;
   Bool     bbSeenBefore = False, addedInstrumentation, addInstNow;
   Addr     instrAddr, origAddr;
   UInt     instrLen;
   IRExpr  *loadAddrExpr, *storeAddrExpr;

   if (gWordTy != hWordTy) {
      /* We don't currently support this case. */
      VG_(tool_panic)("host/guest word size mismatch");
   }

   /* Set up BB */
   bbOut           = emptyIRBB();
   bbOut->tyenv    = dopyIRTypeEnv(bbIn->tyenv);
   bbOut->next     = dopyIRExpr(bbIn->next);
   bbOut->jumpkind = bbIn->jumpkind;

   // Get the first statement, and origAddr from it
   i = 0;
   tl_assert(bbIn->stmts_used > 0);
   st = bbIn->stmts[0];
   tl_assert(Ist_IMark == st->tag);
   origAddr = (Addr)st->Ist.IMark.addr;
   tl_assert(origAddr == st->Ist.IMark.addr);  // XXX: check no overflow

   // Get block info
   bbInfo = get_BB_info(bbIn, origAddr, &bbSeenBefore);
   bbInfo_i = 0;

   do {
      // We should be at an IMark statement
      tl_assert(Ist_IMark == st->tag);

      // Reset stuff for this original instruction
      loadAddrExpr = storeAddrExpr = NULL;
      dataSize = 0;
      addedInstrumentation = False;

      // Process all the statements for this original instruction (ie. until
      // the next IMark statement, or the end of the block)
      do {
         IRStmt* st2 = ( i+1 < bbIn->stmts_used ? bbIn->stmts[i+1] : NULL );

         addInstNow = handleOneStatement(bbIn->tyenv, bbOut, st, st2,
                                         &instrAddr, &instrLen, &loadAddrExpr,
                                         &storeAddrExpr, &dataSize);
         if (addInstNow) {
            tl_assert(!addedInstrumentation);
            addedInstrumentation = True;
            
            // Add instrumentation before this statement.
            instrumentInstr(bbOut, &bbInfo->instrs[ bbInfo_i ], bbSeenBefore,
                      instrAddr, instrLen, dataSize, loadAddrExpr, storeAddrExpr);
         }

         addStmtToIRBB( bbOut, st );

         i++;
         st = st2;
      } 
      while (st && Ist_IMark != st->tag);

      if (!addedInstrumentation) {
         // Add instrumentation now, after all the instruction's statements.
         instrumentInstr(bbOut, &bbInfo->instrs[ bbInfo_i ], bbSeenBefore,
                         instrAddr, instrLen, dataSize, loadAddrExpr, storeAddrExpr);
      }

      bbInfo_i++;
   }
   while (st);

   return bbOut;
}
예제 #7
0
// Instrumentation for the end of each original instruction.
static
void instrumentInstr(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)("instrumentInstr: 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 >= VG_MIN_INSTR_SZB && 
               instrLen <= VG_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) );
}
예제 #8
0
static 
void handleOneStatement(IRTypeEnv* tyenv, IRBB* bbOut, IRStmt* st,
                        Addr* instrAddr, UInt* instrLen,
                        IRExpr** loadAddrExpr, IRExpr** storeAddrExpr,
                        UInt* dataSize)
{
   tl_assert(isFlatIRStmt(st));

   switch (st->tag) {
   case Ist_NoOp:
      break;

   case Ist_AbiHint:
      /* ABI hints aren't interesting to cachegrind.  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;
      addStmtToIRBB( bbOut, st );
      break;

   case Ist_Tmp: {
      IRExpr* data = st->Ist.Tmp.data;
      if (data->tag == Iex_LDle) {
         IRExpr* aexpr = data->Iex.LDle.addr;
         tl_assert( isIRAtom(aexpr) );

         // XXX: repe cmpsb does two loads... the first one is ignored here!
         //tl_assert( NULL == *loadAddrExpr );          // XXX: ???
         *loadAddrExpr = aexpr;
         *dataSize = sizeofIRType(data->Iex.LDle.ty);
      }
      addStmtToIRBB( bbOut, st );
      break;
   }
      
   case Ist_STle: {
      IRExpr* data  = st->Ist.STle.data;
      IRExpr* aexpr = st->Ist.STle.addr;
      tl_assert( isIRAtom(aexpr) );
      tl_assert( NULL == *storeAddrExpr );          // XXX: ???
      *storeAddrExpr = aexpr;
      *dataSize = sizeofIRType(typeOfIRExpr(tyenv, data));
      addStmtToIRBB( bbOut, st );
      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);
      }
      addStmtToIRBB( bbOut, st );
      break;
   }

   case Ist_Put:
   case Ist_PutI:
   case Ist_Exit:
   case Ist_MFence:
      addStmtToIRBB( bbOut, st );
      break;

   default:
      VG_(printf)("\n");
      ppIRStmt(st);
      VG_(printf)("\n");
      VG_(tool_panic)("Cachegrind: unhandled IRStmt");
   }
}
예제 #9
0
IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge,
                 /*IN*/ void*            callback_opaque,
                 /*IN*/ DisOneInstrFn    dis_instr_fn,
                 /*IN*/ UChar*           guest_code,
                 /*IN*/ Addr64           guest_IP_bbstart,
                 /*IN*/ Bool             (*chase_into_ok)(void*,Addr64),
                 /*IN*/ Bool             host_bigendian,
                 /*IN*/ VexArch          arch_guest,
                 /*IN*/ VexArchInfo*     archinfo_guest,
                 /*IN*/ IRType           guest_word_type,
                 /*IN*/ Bool             do_self_check,
                 /*IN*/ Bool             (*preamble_function)(void*,IRBB*),
                 /*IN*/ Int              offB_TISTART,
                 /*IN*/ Int              offB_TILEN )
{
   Long       delta;
   Int        i, n_instrs, first_stmt_idx;
   Bool       resteerOK, need_to_put_IP, debug_print;
   DisResult  dres;
   IRStmt*    imark;
   static Int n_resteers = 0;
   Int        d_resteers = 0;
   Int        selfcheck_idx = 0;
   IRBB*      irbb;
   Addr64     guest_IP_curr_instr;
   IRConst*   guest_IP_bbstart_IRConst = NULL;

   Bool (*resteerOKfn)(void*,Addr64) = NULL;

   debug_print = toBool(vex_traceflags & VEX_TRACE_FE);

   /* Note: for adler32 to work without % operation for the self
      check, need to limit length of stuff it scans to 5552 bytes.
      Therefore limiting the max bb len to 100 insns seems generously
      conservative. */

   /* check sanity .. */
   vassert(sizeof(HWord) == sizeof(void*));
   vassert(vex_control.guest_max_insns >= 1);
   vassert(vex_control.guest_max_insns < 100);
   vassert(vex_control.guest_chase_thresh >= 0);
   vassert(vex_control.guest_chase_thresh < vex_control.guest_max_insns);
   vassert(guest_word_type == Ity_I32 || guest_word_type == Ity_I64);

   /* Start a new, empty extent. */
   vge->n_used  = 1;
   vge->base[0] = guest_IP_bbstart;
   vge->len[0]  = 0;

   /* And a new IR BB to dump the result into. */
   irbb = emptyIRBB();

   /* Delta keeps track of how far along the guest_code array we have
      so far gone. */
   delta    = 0;
   n_instrs = 0;

   /* Guest addresses as IRConsts.  Used in the two self-checks
      generated. */
   if (do_self_check) {
      guest_IP_bbstart_IRConst
         = guest_word_type==Ity_I32 
              ? IRConst_U32(toUInt(guest_IP_bbstart))
              : IRConst_U64(guest_IP_bbstart);
   }

   /* If asked to make a self-checking translation, leave 5 spaces
      in which to put the check statements.  We'll fill them in later
      when we know the length and adler32 of the area to check. */
   if (do_self_check) {
      selfcheck_idx = irbb->stmts_used;
      addStmtToIRBB( irbb, IRStmt_NoOp() );
      addStmtToIRBB( irbb, IRStmt_NoOp() );
      addStmtToIRBB( irbb, IRStmt_NoOp() );
      addStmtToIRBB( irbb, IRStmt_NoOp() );
      addStmtToIRBB( irbb, IRStmt_NoOp() );
   }

   /* If the caller supplied a function to add its own preamble, use
      it now. */
   if (preamble_function) {
      Bool stopNow = preamble_function( callback_opaque, irbb );
      if (stopNow) {
         /* The callback has completed the IR block without any guest
            insns being disassembled into it, so just return it at
            this point, even if a self-check was requested - as there
            is nothing to self-check.  The five self-check no-ops will
            still be in place, but they are harmless. */
         return irbb;
      }
   }

   /* Process instructions. */
   while (True) {
      vassert(n_instrs < vex_control.guest_max_insns);

      /* Regardless of what chase_into_ok says, is chasing permissible
         at all right now?  Set resteerOKfn accordingly. */
      resteerOK 
         = toBool(
              n_instrs < vex_control.guest_chase_thresh
              /* If making self-checking translations, don't chase
                 .. it makes the checks too complicated.  We only want
                 to scan just one sequence of bytes in the check, not
                 a whole bunch. */
              && !do_self_check
              /* we can't afford to have a resteer once we're on the
                 last extent slot. */
              && vge->n_used < 3
           );

      resteerOKfn
         = resteerOK ? chase_into_ok : const_False;

      /* This is the IP of the instruction we're just about to deal
         with. */
      guest_IP_curr_instr = guest_IP_bbstart + delta;

      /* This is the irbb statement array index of the first stmt in
         this insn.  That will always be the instruction-mark
         descriptor. */
      first_stmt_idx = irbb->stmts_used;

      /* Add an instruction-mark statement.  We won't know until after
         disassembling the instruction how long it instruction is, so
         just put in a zero length and we'll fix it up later. */
      addStmtToIRBB( irbb, IRStmt_IMark( guest_IP_curr_instr, 0 ));

      /* for the first insn, the dispatch loop will have set
         %IP, but for all the others we have to do it ourselves. */
      need_to_put_IP = toBool(n_instrs > 0);

      /* Finally, actually disassemble an instruction. */
      dres = dis_instr_fn ( irbb,
                            need_to_put_IP,
                            resteerOKfn,
                            callback_opaque,
                            guest_code,
                            delta,
                            guest_IP_curr_instr,
                            arch_guest,
                            archinfo_guest,
                            host_bigendian );

      /* stay sane ... */
      vassert(dres.whatNext == Dis_StopHere
              || dres.whatNext == Dis_Continue
              || dres.whatNext == Dis_Resteer);
      vassert(dres.len >= 0 && dres.len <= 20);
      if (dres.whatNext != Dis_Resteer)
         vassert(dres.continueAt == 0);

      /* Fill in the insn-mark length field. */
      vassert(first_stmt_idx >= 0 && first_stmt_idx < irbb->stmts_used);
      imark = irbb->stmts[first_stmt_idx];
      vassert(imark);
      vassert(imark->tag == Ist_IMark);
      vassert(imark->Ist.IMark.len == 0);
      imark->Ist.IMark.len = toUInt(dres.len);

      /* Print the resulting IR, if needed. */
      if (vex_traceflags & VEX_TRACE_FE) {
         for (i = first_stmt_idx; i < irbb->stmts_used; i++) {
            vex_printf("              ");
            ppIRStmt(irbb->stmts[i]);
            vex_printf("\n");
         }
      }

      /* If dis_instr_fn terminated the BB at this point, check it
	 also filled in the irbb->next field. */
      if (dres.whatNext == Dis_StopHere) {
         vassert(irbb->next != NULL);
         if (debug_print) {
            vex_printf("              ");
            vex_printf( "goto {");
            ppIRJumpKind(irbb->jumpkind);
            vex_printf( "} ");
            ppIRExpr( irbb->next );
            vex_printf( "\n");
         }
      }

      /* Update the VexGuestExtents we are constructing. */
      /* If vex_control.guest_max_insns is required to be < 100 and
	 each insn is at max 20 bytes long, this limit of 5000 then
	 seems reasonable since the max possible extent length will be
	 100 * 20 == 2000. */
      vassert(vge->len[vge->n_used-1] < 5000);
      vge->len[vge->n_used-1] 
         = toUShort(toUInt( vge->len[vge->n_used-1] + dres.len ));
      n_instrs++;
      if (debug_print) 
         vex_printf("\n");

      /* Advance delta (inconspicuous but very important :-) */
      delta += (Long)dres.len;

      switch (dres.whatNext) {
         case Dis_Continue:
            vassert(irbb->next == NULL);
            if (n_instrs < vex_control.guest_max_insns) {
               /* keep going */
            } else {
               /* We have to stop. */
               irbb->next 
                  = IRExpr_Const(
                       guest_word_type == Ity_I32
                          ? IRConst_U32(toUInt(guest_IP_bbstart+delta))
                          : IRConst_U64(guest_IP_bbstart+delta)
                    );
               goto done;
            }
            break;
         case Dis_StopHere:
            vassert(irbb->next != NULL);
            goto done;
         case Dis_Resteer:
            /* Check that we actually allowed a resteer .. */
            vassert(resteerOK);
            vassert(irbb->next == NULL);
            /* figure out a new delta to continue at. */
            vassert(resteerOKfn(callback_opaque,dres.continueAt));
            delta = dres.continueAt - guest_IP_bbstart;
            /* we now have to start a new extent slot. */
            vge->n_used++;
            vassert(vge->n_used <= 3);
            vge->base[vge->n_used-1] = dres.continueAt;
            vge->len[vge->n_used-1] = 0;
            n_resteers++;
            d_resteers++;
            if (0 && (n_resteers & 0xFF) == 0)
            vex_printf("resteer[%d,%d] to 0x%llx (delta = %lld)\n",
                       n_resteers, d_resteers,
                       dres.continueAt, delta);
            break;
         default:
            vpanic("bb_to_IR");
      }
   }
   /*NOTREACHED*/
   vassert(0);

  done:
   /* We're done.  The only thing that might need attending to is that
      a self-checking preamble may need to be created. */
   if (do_self_check) {

      UInt     len2check, adler32;
      IRTemp   tistart_tmp, tilen_tmp;

      vassert(vge->n_used == 1);
      len2check = vge->len[0];
      if (len2check == 0) 
         len2check = 1;

     adler32 = genericg_compute_adler32( (HWord)guest_code, len2check );

     /* Set TISTART and TILEN.  These will describe to the despatcher
        the area of guest code to invalidate should we exit with a
        self-check failure. */

     tistart_tmp = newIRTemp(irbb->tyenv, guest_word_type);
     tilen_tmp   = newIRTemp(irbb->tyenv, guest_word_type);

     irbb->stmts[selfcheck_idx+0]
        = IRStmt_Tmp(tistart_tmp, IRExpr_Const(guest_IP_bbstart_IRConst) );

     irbb->stmts[selfcheck_idx+1]
        = IRStmt_Tmp(tilen_tmp,
                     guest_word_type==Ity_I32 
                        ? IRExpr_Const(IRConst_U32(len2check)) 
                        : IRExpr_Const(IRConst_U64(len2check))
          );

     irbb->stmts[selfcheck_idx+2]
        = IRStmt_Put( offB_TISTART, IRExpr_Tmp(tistart_tmp) );

     irbb->stmts[selfcheck_idx+3]
        = IRStmt_Put( offB_TILEN, IRExpr_Tmp(tilen_tmp) );

     irbb->stmts[selfcheck_idx+4]
        = IRStmt_Exit( 
             IRExpr_Binop( 
                Iop_CmpNE32, 
                mkIRExprCCall( 
                   Ity_I32, 
                   2/*regparms*/, 
                   "genericg_compute_adler32",
#if defined(__powerpc__) && defined(__powerpc64__)
                   (void*)((ULong*)(&genericg_compute_adler32))[0],
#else
                   &genericg_compute_adler32,
#endif
                   mkIRExprVec_2( 
                      mkIRExpr_HWord( (HWord)guest_code ), 
                      mkIRExpr_HWord( (HWord)len2check )
                   )
                ),
                IRExpr_Const(IRConst_U32(adler32))
             ),
             Ijk_TInval,
             guest_IP_bbstart_IRConst
          );
   }

   return irbb;
}
예제 #10
0
void findRepMovIRExpr(IRBB * bb, IRStmt * s, findRepMovContext * context, UInt tagVal)
{
  IROp theOp; 
  IRExpr * expr; 
  IRExpr * arg1;
  IRExpr * arg2; 
  UInt tmpName; 
  IRDirty * d; 

  expr = s->Ist.Tmp.data; 
  
  switch (expr->tag)
  {
  case Iex_Binop:
    theOp = expr->Iex.Binop.op;
    arg1 = expr->Iex.Binop.arg1;
    arg2 = expr->Iex.Binop.arg2; 

    switch(theOp)
    {
    case Iop_Sub32:
    case Iop_Sub16:
    case Iop_Sub8:
      if (arg2->tag == Iex_Const && arg1->tag == Iex_Tmp)
      {
	tmpName = (UInt) arg1->Iex.Tmp.tmp; 
	context->subTmpVars[tmpName] = 1; 

	if (checkTmpRepMov(tmpName, context) == 1)
	{
	  //	  VG_(printf)("XXX tmpName: %u BB %u flagged by checkTmpRepMov! \n", tmpName, tagVal);

	  d = unsafeIRDirty_0_N(0,
				"emitFlaggedTmpRepMov",
				&emitFlaggedTmpRepMov,
				mkIRExprVec_2(
					      mkIRExpr_HWord(tmpName),
					      mkIRExpr_HWord(tagVal)
					      )
				);
	  setHelperAnns(d); 
	  addStmtToIRBB(bb, IRStmt_Dirty(d)); 
	}
      }
      break; 

    case Iop_CmpEQ32:
    case Iop_CmpEQ16:
    case Iop_CmpEQ8:
      if (arg2->tag == Iex_Const && arg1->tag == Iex_Tmp)
	{
	  tmpName = (UInt) arg1->Iex.Tmp.tmp; 
	  if (arg2->Iex.Const.con->Ico.U32 == 0)
	  {
	    context->cmpZeroTmpVars[tmpName] = 1; 

	    if (checkTmpRepMov(tmpName, context) == 1)
	    {
	      //	      VG_(printf)("XXX tmpName: %u BB %u flagged by checkTmpRepMov! \n", tmpName, tagVal);
	      d = unsafeIRDirty_0_N(0,
				    "emitFlaggedTmpRepMov",
				    &emitFlaggedTmpRepMov,
				    mkIRExprVec_2(
						  mkIRExpr_HWord(tmpName),
						  mkIRExpr_HWord(tagVal)
						  )
				    );
	      setHelperAnns(d); 
	      addStmtToIRBB(bb, IRStmt_Dirty(d)); 
	    }
	  }
      }
      break; 

    default:
      break; 
    }
  default:
    break; 
  }
  return; 
}