Skip to content

Instantly share code, notes, and snippets.

@cs-qyzhang
Last active September 13, 2022 11:01
Show Gist options
  • Select an option

  • Save cs-qyzhang/9ec0bea3cd463c63d914e87c17f330fc to your computer and use it in GitHub Desktop.

Select an option

Save cs-qyzhang/9ec0bea3cd463c63d914e87c17f330fc to your computer and use it in GitHub Desktop.
Using Intel Pin to count CLWB/CLFLUSH/CLFLUSHOPT/SFENCE/MFENCE/WBINVD, see https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-dynamic-binary-instrumentation-tool.html
#include <iostream>
#include <fstream>
#include <vector>
#include "pin.H"
using std::cerr;
using std::ofstream;
using std::ios;
using std::string;
using std::endl;
ofstream OutFile;
// The running count of instructions is kept here
// make it static to help the compiler optimize docount
/* You can use intel_pin_start() and intel_pin_stop() to start and stop counting
* in the program, so you can control which part of the code
* you want to do the counting.
*
* If you want to do this, initialize flag to false, and add
* void intel_pin_start() {}
* void intel_pin_stop() {}
* in your program, and call them at the desired position.
*/
bool flag = true
// bool flag = false;
uint64_t CLWB_cnt = 0;
uint64_t CLFLUSH_cnt = 0;
uint64_t CLFLUSHOPT_cnt = 0;
uint64_t SFENCE_cnt = 0;
uint64_t MFENCE_cnt = 0;
uint64_t WBINVD_cnt = 0;
VOID CLWB_count() { if (flag) CLWB_cnt++; }
VOID CLFLUSH_count() { if (flag) CLFLUSH_cnt++; }
VOID CLFLUSHOPT_count() { if (flag) CLFLUSHOPT_cnt++; }
VOID SFENCE_count() { if (flag) SFENCE_cnt++; }
VOID MFENCE_count() { if (flag) MFENCE_cnt++; }
VOID WBINVD_count() { if (flag) WBINVD_cnt++; }
VOID begin() { flag = true; }
VOID end() { flag = false; }
// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
string op = INS_Mnemonic(ins);
// Insert a call to docount before every instruction, no arguments are passed
if (op == "CLWB")
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CLWB_count, IARG_END);
if (op == "CLFLUSH")
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CLFLUSH_count, IARG_END);
if (op == "CLFLUSHOPT")
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CLFLUSHOPT_count, IARG_END);
if (op == "SFENCE")
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)SFENCE_count, IARG_END);
if (op == "MFENCE")
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)MFENCE_count, IARG_END);
if (op == "WBINVD")
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)WBINVD_count, IARG_END);
}
// Pin calls this function every time a new rtn is executed
VOID Routine(RTN rtn, VOID *v)
{
string name = RTN_Name(rtn);
RTN_Open(rtn);
if (name.find("intel_pin_start") != string::npos) {
// Insert a call at the entry point of a routine to increment the call count
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)begin, IARG_END);
}
if (name.find("intel_pin_stop") != string::npos) {
// Insert a call at the entry point of a routine to increment the call count
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)end, IARG_END);
}
RTN_Close(rtn);
}
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
"o", "inscount.out", "specify output file name");
// This function is called when the application exits
VOID Fini(INT32 code, VOID *v)
{
// Write to a file since cout and cerr maybe closed by the application
OutFile.setf(ios::showbase);
OutFile << "CLWB: " << CLWB_cnt << endl;
OutFile << "CLFLUSH: " << CLFLUSH_cnt << endl;
OutFile << "CLFLUSHOPT: " << CLFLUSHOPT_cnt << endl;
OutFile << "MFENCE: " << MFENCE_cnt << endl;
OutFile << "SFENCE: " << SFENCE_cnt << endl;
OutFile << "WBINVD: " << WBINVD_cnt << endl;
OutFile << endl;
OutFile << "Total: " << CLWB_cnt + CLFLUSH_cnt + CLFLUSHOPT_cnt + MFENCE_cnt + SFENCE_cnt + WBINVD_cnt << endl;
OutFile.close();
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage()
{
cerr << "This tool counts the number of dynamic instructions executed" << endl;
cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
return -1;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
/* argc, argv are the entire command line: pin -t <toolname> -- ... */
/* ===================================================================== */
int main(int argc, char * argv[])
{
// Initialize symbol table code, needed for rtn instrumentation
PIN_InitSymbols();
// Initialize pin
if (PIN_Init(argc, argv)) return Usage();
OutFile.open(KnobOutputFile.Value().c_str());
// Register Instruction to be called to instrument instructions
INS_AddInstrumentFunction(Instruction, 0);
// Register Routine to be called to instrument rtn
RTN_AddInstrumentFunction(Routine, 0);
// Register Fini to be called when the application exits
PIN_AddFiniFunction(Fini, 0);
// Start the program, never returns
PIN_StartProgram();
return 0;
}
@cs-qyzhang
Copy link
Author

cs-qyzhang commented Sep 13, 2022

An example when using intel_pin_start() and intel_pin_stop() (you need to comment line 26 and uncomment line 27 if you want to do partial counting):

#include <x86intrin.h>

void intel_pin_start() {}
void intel_pin_stop() {}

int main(void) {
        int a = 0;
        _mm_clflush(&a);
        intel_pin_start();
        _mm_clwb(&a);
        intel_pin_stop();
        return 0;
}

Compile it using command gcc a.c -march=native, run it with intel Pin, then there will be a file called inscount.out which contains:

CLWB: 1
CLFLUSH: 0
CLFLUSHOPT: 0
MFENCE: 0
SFENCE: 0
WBINVD: 0

Total: 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment