Having read this blog post you will be able to create your own tool to count the number of branches. You will also be able to learn the fundamentals that you would need to do any program analysis using the Intel Pin Tool.
The following guide assumes that you have set-up the pin tool using the guide posted here.
Let me first describe the sections of the tool using a simple example, inscount.cpp. We will be modifying this file to create branchcount.cpp
which will ultimately count the number of branch instructions. Open the file using the command,
mkdir /opt/pin-dir/source/tool/Bc //create a folder called Bc to store our tool
cp /opt/pin-dir/source/tool/ManualExamples/inscount.cpp /opt/pin-dir/source/tool/Bc/branchcount.cpp // copy to Bc
vi /opt/pin-dir/source/tool/Bc/branchcount.cpp
You will see the following file.
Section 1
The entry point to the tool is from the main function. By looking at the main method, we will be able to identify the program flow.
- As you can see
PIN_Init
method initializes the pin tool with usage and it is specified inpin.H
. For example, if the user inputs an incorrect command, this is the method which detects it. - The
Usage
method describes the correct usage of the tool. For now just skip the second line withOutFile
. We will come back to this when we are outputting the results to a file. - The main part of our tool, is inside the method
INS_AddInstrumentFunction(Instruction, 0);
. This method contains our injection logic at each instruction step. PIN_AddFiniFunction(Fini, 0);
specifies the method to be executed when the application ends.- Finally the program execution begins and monitoring starts with the command
PIN_StartProgram()
.
Section 2
INS_AddInstrumentFunction(Instruction, 0);
method takes the Instruction
function as a function pointer. Our Instruction
function should contain the main injection logic. Basically, pin tool inserts pseudo instructions to our application binary and it tracks the output of these injected functions to track the application behavior. How are the instructions injected? Simple; It’s using this section.
The INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END)
inserts our own method called docount
. Yes. you guessed it right. AFUNPTR
says that it’s a function pointer. This function can have arguments. If it has arguments, you have to specify them in a comma separated fashion. At the end of the arguments, you have to enter IARG_END
. Since we do not have any arguments just add IARG_END
. ins
is the instruction being monitored from our original application (which needs to be profiled). IPOINT_BEFORE
says to execute before the instruction is evaluated. You could check intel documentation for other types of flags such as `IPOINT_AFTER` or `
IPOINT_TAKEN_BRANCH`.
Modification
To count the branches instead of all of the instructions, we just have to add an if condition. That’s it. Now the tool counts the number of branches instead of all instructions.
if (INS_IsBranch(ins)) {
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);
}
Section 3
This is the actual method (docount
) that would be inserted to the program execution. Since we need a count, as you can see, each time the function (docount
) is called, we just increment the global variable icount
.
Modification
Change the variable name to branchcount
and method name to dobranchcount
. Change the function name in section 2, INS_InsertCall.
// The running count of branch instructions is kept here
// make it static to help the compiler optimize dobranchcount
static UINT64 branchcount = 0;
// This function is called before every instruction is executed
VOID dobranchcount() { branchcount++; }
Section 4
This section specifies the KnobOutputFile. It specifies the output file and this is the line that I skipped in Section 1. The KnobOutputFile specifies that we can specify the filename using the -o
directive. If the file name is not specified, it would default to whatever is given here.
Modification
To avoid replacing results of another tool let’s rename the file.
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool","o", "branchcount.out", "specify output file name");
Section 5
This section specifies what will be written to the output file. You can format your output file content here. Just to be consistent, rename the variable name to branch count
instead of Count
.
That’s it! You’ve understood and written your tool to count the number of branch instructions. Next let’s compile and run it.
Compiling and Running
Let’s copy the makefile
and makefile.rules
to our folder Bc
. Make tool makes the programmer’s life very easy by keeping track of programs by different sources.
cp /opt/pin-dir/source/tool/ManualExamples/makefile /opt/pin-dir/source/tool/Bc/
cp /opt/pin-dir/source/tool/ManualExamples/makefile.rules /opt/pin-dir/source/tool/Bc/
Now you will need to modify the <code>makefile.rules</code> to include our tool’s name. Go to the line, TEST_TOOL_ROOTS :=
. You will see a list of programs mentioned after the equal sign. Remove all of them and replace it with your tool name. In our case that would be branchcount
. Do not include the file extension.
At the end of the file, you will see the lines. Remove or comment out as below since we do not have this tool now.
#$(OBJDIR)divide_by_zero$(EXE_SUFFIX): divide_by_zero_$(OS_TYPE).c# $(APP_CC) $(APP_CXXFLAGS_NOOPT) $(COMP_EXE) $(APP_LDFLAGS_NOOPT) $(APP_LIBS)
Don’t forget to leave a comment behind if this post was helpful. I really appreciate it! 🙏
thanks for ur post, learn a lot!
thanks for the post, very useful.
Is there a typo?
“To count the branches instead of all of the branches”
Instead, it is meant to be “To count the branches instead of all the instructions” ?
Hi Sam! Thank you for your noting that. I’ve updated the post.