1

I have created a pintool that outputs in a json file all the basic blocks executed by a program. The problem is that the instrumented program runs slow so I am trying to optimize my pintool.

I was thinking I could remove the calls to the instrumentation function from a block after it's called once, but i haven't been able to find how to do that. In the official PIN documentation there is only the function PIN_RemoveInstrumentation() which seems to disable all the intrumentation.

Can anyone tell me how to do that or if it is even possible to do it using PIN?

  • You should investigate a bit more about the reasons of this slowness... You cannot optimize blindly, you really need to identify where the bottlenecks are in order to workaround. – perror May 13 '15 at 16:49
1

PIN does not provide a direct API to remove a basic block instrumentation call on a particular address.What you could do is keep track of basic blocks which have been executed at least once.
You would insert instrumentation calls only for those basic blocks which have not been executed previously.

Here is a crude example based on the official documentation.

// The running count of instructions is kept here
// make it static to help the compiler optimize docount
static UINT64 icount = 0;

// Count the number of instructions in this basic block
VOID docount(UINT32 c) { icount += c; }

VOID Trace(TRACE trace, VOID *v)
{    
    for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl))
    {   
        ADDRINT addr = BBL_Address(bbl);
        if (alreadyVistited(addr) == false) 
        {
            // Add instrumentation call for this basic block
            BBL_InsertCall(bbl, IPOINT_ANYWHERE, (AFUNPTR)docount, IARG_UINT32, BBL_NumIns(bbl), IARG_END);
        }
        else
        {
            addToVisitedList(addr);
        }
    }
}

int main(int argc, char * argv[])
{
...
TRACE_AddInstrumentFunction(Trace, 0);
...
}

The overhead of this entirely depends on the complexity of the instrumentation code added.
In general to speed things up, you should turn on maximum compiler optimizations. If possible you may also use a better compiler such as the Intel Compiler which is capable of generating extremely fast code.


UPDATE

If you use dynamoRIO instead of PIN, you can more easily achieve what you want to do. Here is a sample code which logs all basic blocks executed by the program in the main module.

#define WINDOWS
#define X86_32
#define _CRT_SECURE_NO_DEPRECATE
#include "dr_api.h"
#pragma comment(lib, "dynamorio.lib")

static file_t logFile;
static LPBYTE mainModStartAddr, mainModEndAddr;

// This is called once for each basic block as it's discovered
static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *ilist, bool for_trace, bool translating)
{
    // The IP of the basoc block
    LPBYTE addr = dr_fragment_app_pc(tag);

    // Basic Block must be within the main module
    if (addr >= mainModStartAddr && addr <= mainModEndAddr) 
        dr_fprintf(logFile, "0x%08X", addr);
    return DR_EMIT_DEFAULT;
}


static void event_exit()
{
    dr_close_file(logFile);
}


DR_EXPORT void dr_init(client_id_t id)
{
    module_data_t *data  = dr_get_main_module();
    mainModStartAddr = data->start;
    mainModEndAddr = data->end;
    dr_free_module_data(data);

    logFile = dr_open_file("logfile.txt", DR_FILE_WRITE_OVERWRITE);

    dr_register_bb_event(event_basic_block);    
    dr_register_exit_event(event_exit);
}

Note that there is no analysis function needed at all. The instrumentation function event_basic_block will be called once per basic block. It will not be called more than once, as from second time onwards, the code will be executed from the code cache.

The difference between dynamoRIO and PIN is that the former directly allows to register a callback for basic block generation. PIN on the other hand allows a callback for trace generation. A trace a collection of basic blocks. Also since not all basic blocks may be executed in a trace, using PIN in this situation is comparatively difficult.

  • alreadyVistited(addr) = false is that intended? – rev May 13 '15 at 21:19
  • @AcidShout That function should be implemented depending on how the list of visited addresses are stored. It's not provided by the PIN api. – 0xec May 14 '15 at 5:31
  • 1
    Thank you for you suggestion, it seems to work ok. I think @AcidShout men't that it should be if (alreadyVistited(addr) == false) – Bogdan Timofte May 14 '15 at 7:29
  • @BogdanTimofte Oh yes, definitely. Typo corrected. – 0xec May 15 '15 at 5:02
  • Are all discovered basic blocks actually executed? Either way, I think the equivalent thing to do in Pin would be to transfer the content of your analysis function to the instrumentation function. Which is highly recommended for performance improvement, of course. – nitzanms Aug 10 '15 at 13:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.