CCTLib
Calling-context and data-object attribution library for Intel Pin
footprint_client2.cpp
Go to the documentation of this file.
1 // @COPYRIGHT@
2 // Licensed under MIT license.
3 // See LICENSE.TXT file in the project root for more information.
4 // ==============================================================
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <iostream>
10 #include <unistd.h>
11 #include <assert.h>
12 #include <string.h>
13 #include <sys/mman.h>
14 #include <sys/time.h>
15 #include <sstream>
16 #include "pin.H"
17 #include "cctlib.H"
18 using namespace std;
19 using namespace PinCCTLib;
20 
21 #include <unordered_set>
22 #include <vector>
23 #include <unordered_map>
24 #include <algorithm>
25 
26 /* infrastructure for shadow memory */
27 /* MACROs */
28 // 64KB shadow pages
29 #define PAGE_OFFSET_BITS (16LL)
30 #define PAGE_OFFSET(addr) (addr & 0xFFFF)
31 #define PAGE_OFFSET_MASK (0xFFFF)
32 
33 #undef PAGE_SIZE
34 #define PAGE_SIZE (1 << PAGE_OFFSET_BITS)
35 
36 // 2 level page table
37 #define PTR_SIZE (sizeof(void*))
38 #define LEVEL_1_PAGE_TABLE_BITS (20)
39 #define LEVEL_1_PAGE_TABLE_ENTRIES (1 << LEVEL_1_PAGE_TABLE_BITS)
40 #define LEVEL_1_PAGE_TABLE_SIZE (LEVEL_1_PAGE_TABLE_ENTRIES * PTR_SIZE)
41 
42 #define LEVEL_2_PAGE_TABLE_BITS (12)
43 #define LEVEL_2_PAGE_TABLE_ENTRIES (1 << LEVEL_2_PAGE_TABLE_BITS)
44 #define LEVEL_2_PAGE_TABLE_SIZE (LEVEL_2_PAGE_TABLE_ENTRIES * PTR_SIZE)
45 
46 #define LEVEL_1_PAGE_TABLE_SLOT(addr) (((addr) >> (LEVEL_2_PAGE_TABLE_BITS + PAGE_OFFSET_BITS)) & 0xfffff)
47 #define LEVEL_2_PAGE_TABLE_SLOT(addr) (((addr) >> (PAGE_OFFSET_BITS)) & 0xFFF)
48 
50 
51 /* Other footprint_client settings */
52 #define MAX_FOOTPRINT_CONTEXTS_TO_LOG (1000)
53 
54 struct node_metric_t {
55  unordered_set<uint64_t> addressSet;
56  unordered_set<uint64_t> addressSetDecoded;
57  uint64_t accessNum;
58  uint64_t dependentNum;
59 };
60 
61 struct sort_format_t {
63  uint64_t footprint;
64  uint64_t fpNum;
65  uint64_t accessNum;
66  uint64_t dependentNum;
67 };
68 
69 #define THREAD_MAX (1024)
70 unordered_map<uint32_t, struct node_metric_t> hmap_vector[THREAD_MAX];
71 
72 static INT32 Usage() {
73  PIN_ERROR("Pin tool to gather calling context on each load and store.\n" + KNOB_BASE::StringKnobSummary() + "\n");
74  return -1;
75 }
76 
77 // Main for DeadSpy, initialize the tool, register instrumentation functions and call the target program.
78 FILE* gTraceFile;
79 
80 struct timeval tv1;
81 __thread struct timeval tv2;
82 __thread struct timeval tv3;
83 
84 // Initialized the needed data structures before launching the target program
85 void ClientInit(int argc, char* argv[]) {
86  // Create output file
87  char name[MAX_FILE_PATH] = "client.out.";
88  char* envPath = getenv("CCTLIB_CLIENT_OUTPUT_FILE");
89 
90  if (envPath) {
91  // assumes max of MAX_FILE_PATH
92  snprintf(name, sizeof(name), "%s", envPath);
93  }
94 
95  gethostname(name + strlen(name), MAX_FILE_PATH - strlen(name));
96  pid_t pid = getpid();
97  sprintf(name + strlen(name), "%d", pid);
98  cerr << "\n Creating log file at:" << name << "\n";
99  gTraceFile = fopen(name, "w");
100  // print the arguments passed
101  fprintf(gTraceFile, "\n");
102 
103  for (int i = 0; i < argc; i++) {
104  fprintf(gTraceFile, "%s ", argv[i]);
105  }
106 
107  fprintf(gTraceFile, "\n");
108 }
109 
110 /* helper functions for shadow memory */
111 uint8_t*
113  uint8_t* shadowPage;
114  uint8_t*** l1Ptr = &gL1PageTable[LEVEL_1_PAGE_TABLE_SLOT(address)];
115  if (*l1Ptr == nullptr) {
116  *l1Ptr = (uint8_t**)calloc(1, LEVEL_2_PAGE_TABLE_SIZE);
117  shadowPage = (*l1Ptr)[LEVEL_2_PAGE_TABLE_SLOT(address)] = (uint8_t*)mmap(nullptr, PAGE_SIZE * (sizeof(bool) + sizeof(uint64_t)), PROT_WRITE | PROT_READ, MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
118  } else if ((shadowPage = (*l1Ptr)[LEVEL_2_PAGE_TABLE_SLOT(address)]) == nullptr) {
119  shadowPage = (*l1Ptr)[LEVEL_2_PAGE_TABLE_SLOT(address)] = (uint8_t*)mmap(nullptr, PAGE_SIZE * (sizeof(bool) + sizeof(uint64_t)), PROT_WRITE | PROT_READ, MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
120  }
121  return shadowPage;
122 }
123 
124 inline bool CheckDependence(uint64_t curAddr, uint64_t prevAddr) {
125  uint32_t lineNo1, lineNo2;
126  string filePath1, filePath2;
127 
128  PIN_LockClient();
129  PIN_GetSourceLocation(prevAddr, nullptr, (INT32*)&lineNo1, &filePath1);
130  PIN_GetSourceLocation(curAddr, nullptr, (INT32*)&lineNo2, &filePath2);
131  PIN_UnlockClient();
132 
133  return static_cast<bool>((filePath1 == filePath2) && (lineNo1 <= lineNo2));
134 }
135 
136 VOID MemFunc(THREADID id, void* addr, bool rwFlag, UINT32 refSize) {
137  uint64_t Addr = (uint64_t)addr;
138  uint8_t* status = GetOrCreateShadowBaseAddress(Addr);
139  uint64_t* prevAddr = (uint64_t*)(status + PAGE_SIZE + PAGE_OFFSET(Addr) * sizeof(uint64_t));
140  // check write-read(true and loop-carried) dependence
141  bool* prevFlag = (bool*)(status + PAGE_OFFSET(Addr));
142 
143  // at memory instruction record the footprint
144  void** metric = GetIPNodeMetric(id, 0);
145 
146  if (*metric == nullptr) {
147  // use ctxthndl as the key to associate footprint with the trace
148  ContextHandle_t ctxthndl = GetContextHandle(id, 0);
149  *metric = &(hmap_vector[id])[ctxthndl];
150  (hmap_vector[id])[ctxthndl].addressSet.insert(Addr | (((uint64_t)refSize) << 48));
151  (hmap_vector[id])[ctxthndl].accessNum += refSize;
152 
153  // check how many times write to a shared address
154  // shared means that this address is read/write before this write
155  if (rwFlag && (*prevFlag)) // && CheckDependence((uint64_t)addr, *prevAddr))
156  (hmap_vector[id])[ctxthndl].dependentNum += refSize;
157  } else {
158  (static_cast<struct node_metric_t*>(*metric))->addressSet.insert(Addr | (((uint64_t)refSize) << 48));
159  (static_cast<struct node_metric_t*>(*metric))->accessNum += refSize;
160  if (!rwFlag && (*prevFlag)) // && CheckDependence((uint64_t)addr, *prevAddr))
161  (static_cast<struct node_metric_t*>(*metric))->dependentNum += refSize;
162  }
163  // update the current read write flag
164  *prevFlag = true;
165  *prevAddr = Addr;
166 }
167 
168 VOID InstrumentInsCallback(INS ins, VOID* v, const uint32_t slot) {
169  if (!INS_IsMemoryRead(ins) && !INS_IsMemoryWrite(ins))
170  return;
171  if (INS_IsStackRead(ins) || INS_IsStackWrite(ins))
172  return;
173  if (INS_IsControlFlow(ins) || INS_IsRet(ins))
174  return;
175  UINT32 memOperands = INS_MemoryOperandCount(ins);
176  for (UINT32 memOp = 0; memOp < memOperands; memOp++) {
177  UINT32 refSize = INS_MemoryOperandSize(ins, memOp);
178  if (INS_IsMemoryRead(ins))
179  INS_InsertPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)MemFunc, IARG_THREAD_ID, IARG_MEMORYOP_EA, memOp, IARG_BOOL, false, IARG_UINT32, refSize, IARG_END);
180  else
181  INS_InsertPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)MemFunc, IARG_THREAD_ID, IARG_MEMORYOP_EA, memOp, IARG_BOOL, true, IARG_UINT32, refSize, IARG_END);
182  }
183 }
184 
185 void DecodingFootPrint(const THREADID threadid, ContextHandle_t myHandle, ContextHandle_t parentHandle, void** myMetric, void** parentMetric) {
186  if (*myMetric == nullptr)
187  return;
188  struct node_metric_t* hset = static_cast<struct node_metric_t*>(*myMetric);
189  unordered_set<uint64_t>::iterator it;
190  for (it = hset->addressSet.begin(); it != hset->addressSet.end(); ++it) {
191  uint64_t refSize = (*it) >> 48;
192  uint64_t addr = (*it) & ((1ULL << 48) - 1);
193  assert(refSize != 0);
194  for (unsigned int i = 0; i < refSize; i++) {
195  hset->addressSetDecoded.insert(addr + i);
196  }
197  }
198  // hset->addressSet.clear();
199 }
200 
201 void MergeFootPrint(const THREADID threadid, ContextHandle_t myHandle, ContextHandle_t parentHandle, void** myMetric, void** parentMetric) {
202  if (*myMetric == nullptr)
203  return;
204  struct node_metric_t* hset = static_cast<struct node_metric_t*>(*myMetric);
205 
206  if (*parentMetric == nullptr) {
207  *parentMetric = &((hmap_vector[threadid])[parentHandle]);
208  (hmap_vector[threadid])[parentHandle].addressSetDecoded.insert(hset->addressSetDecoded.begin(), hset->addressSetDecoded.end());
209  (hmap_vector[threadid])[parentHandle].addressSet.insert(hset->addressSet.begin(), hset->addressSet.end());
210  (hmap_vector[threadid])[parentHandle].accessNum += hset->accessNum;
211  (hmap_vector[threadid])[parentHandle].dependentNum += hset->dependentNum;
212  } else {
213  (static_cast<struct node_metric_t*>(*parentMetric))->addressSetDecoded.insert(hset->addressSetDecoded.begin(), hset->addressSetDecoded.end());
214  (static_cast<struct node_metric_t*>(*parentMetric))->addressSet.insert(hset->addressSet.begin(), hset->addressSet.end());
215  (static_cast<struct node_metric_t*>(*parentMetric))->accessNum += hset->accessNum;
216  (static_cast<struct node_metric_t*>(*parentMetric))->dependentNum += hset->dependentNum;
217  }
218 }
219 
220 
221 inline bool FootPrintCompare(const struct sort_format_t& first, const struct sort_format_t& second) {
222  return first.footprint > second.footprint;
223 }
224 
225 void PrintTopFootPrintPath(THREADID threadid) {
226  uint64_t cntxtNum = 0;
227  vector<struct sort_format_t> TmpList;
228 
229  fprintf(gTraceFile, "*************** Dump Data from Thread %d ****************\n", threadid);
230  unordered_map<uint32_t, struct node_metric_t>& hmap = hmap_vector[threadid];
231  unordered_map<uint32_t, struct node_metric_t>::iterator it;
232  for (it = hmap.begin(); it != hmap.end(); ++it) {
233  struct sort_format_t tmp;
234  tmp.handle = (*it).first;
235  tmp.footprint = (uint64_t)(*it).second.addressSetDecoded.size();
236  tmp.fpNum = (uint64_t)(*it).second.addressSet.size();
237  tmp.accessNum = (uint64_t)(*it).second.accessNum;
238  tmp.dependentNum = (uint64_t)(*it).second.dependentNum;
239  TmpList.emplace_back(tmp);
240  }
241  sort(TmpList.begin(), TmpList.end(), FootPrintCompare);
242  vector<struct sort_format_t>::iterator ListIt;
243  for (ListIt = TmpList.begin(); ListIt != TmpList.end(); ++ListIt) {
244  if (cntxtNum < MAX_FOOTPRINT_CONTEXTS_TO_LOG) {
245  fprintf(gTraceFile, "Footprint is %lu Bytes, #distinct memory access is %ld, reuse is %ld, write dependence is %lu, context is:", ((*ListIt).footprint), (*ListIt).fpNum, (*ListIt).accessNum - (*ListIt).footprint, (*ListIt).dependentNum);
246  PrintFullCallingContext((*ListIt).handle);
247  fprintf(gTraceFile, "\n------------------------------------------------\n");
248  } else {
249  break;
250  }
251  cntxtNum++;
252  }
253 }
254 
255 VOID ThreadFiniFunc(THREADID threadid, const CONTEXT* ctxt, INT32 code, VOID* v) {
256  gettimeofday(&tv2, nullptr);
257  // traverse CCT bottom to up
258  // decode first
259  TraverseCCTBottomUp(threadid, DecodingFootPrint);
260  // merge second
261  TraverseCCTBottomUp(threadid, MergeFootPrint);
262  gettimeofday(&tv3, nullptr);
263  // print the footprint for functions
264  PIN_LockClient();
265  PrintTopFootPrintPath(threadid);
266  fprintf(gTraceFile, "online collection time %lf, offline analysis time %lf\n", tv2.tv_sec - tv1.tv_sec + (tv2.tv_usec - tv1.tv_usec) / 1000000.0, tv3.tv_sec - tv2.tv_sec + (tv3.tv_usec - tv2.tv_usec) / 1000000.0);
267  PIN_UnlockClient();
268 }
269 
270 VOID FiniFunc(INT32 code, VOID* v) {
271  // do whatever you want to the full CCT with footpirnt
272 }
273 
274 int main(int argc, char* argv[]) {
275  gettimeofday(&tv1, nullptr);
276  // Initialize PIN
277  if (PIN_Init(argc, argv))
278  return Usage();
279 
280  // Initialize Symbols, we need them to report functions and lines
281  PIN_InitSymbols();
282  // Init Client
283  ClientInit(argc, argv);
284  // Intialize CCTLib
286 
287  // fini function for post-mortem analysis
288  PIN_AddThreadFiniFunction(ThreadFiniFunc, nullptr);
289  PIN_AddFiniFunction(FiniFunc, nullptr);
290 
291  // Launch program now
292  PIN_StartProgram();
293  return 0;
294 }
#define MAX_FILE_PATH
Definition: cctlib.H:18
#define INTERESTING_INS_MEMORY_ACCESS
Definition: cctlib.H:87
uint8_t ** gL1PageTable[LEVEL_1_PAGE_TABLE_SIZE]
uint8_t * GetOrCreateShadowBaseAddress(uint64_t address)
int main(int argc, char *argv[])
FILE * gTraceFile
#define LEVEL_2_PAGE_TABLE_SIZE
#define LEVEL_1_PAGE_TABLE_SLOT(addr)
__thread struct timeval tv3
void MergeFootPrint(const THREADID threadid, ContextHandle_t myHandle, ContextHandle_t parentHandle, void **myMetric, void **parentMetric)
bool CheckDependence(uint64_t curAddr, uint64_t prevAddr)
void PrintTopFootPrintPath(THREADID threadid)
#define THREAD_MAX
static INT32 Usage()
#define PAGE_OFFSET(addr)
void ClientInit(int argc, char *argv[])
VOID FiniFunc(INT32 code, VOID *v)
VOID ThreadFiniFunc(THREADID threadid, const CONTEXT *ctxt, INT32 code, VOID *v)
bool FootPrintCompare(const struct sort_format_t &first, const struct sort_format_t &second)
#define PAGE_SIZE
unordered_map< uint32_t, struct node_metric_t > hmap_vector[THREAD_MAX]
#define MAX_FOOTPRINT_CONTEXTS_TO_LOG
void DecodingFootPrint(const THREADID threadid, ContextHandle_t myHandle, ContextHandle_t parentHandle, void **myMetric, void **parentMetric)
#define LEVEL_1_PAGE_TABLE_SIZE
__thread struct timeval tv2
VOID InstrumentInsCallback(INS ins, VOID *v, const uint32_t slot)
VOID MemFunc(THREADID id, void *addr, bool rwFlag, UINT32 refSize)
struct timeval tv1
#define LEVEL_2_PAGE_TABLE_SLOT(addr)
uint64_t metric
Definition: cctlib.H:60
int PinCCTLibInit(IsInterestingInsFptr isInterestingIns, FILE *logFile, CCTLibInstrumentInsCallback userCallback, VOID *userCallbackArg, BOOL doDataCentric=false)
Definition: cctlib.cpp:3132
VOID PrintFullCallingContext(const ContextHandle_t ctxtHandle)
Definition: cctlib.cpp:2346
volatile uint8_t status
Definition: cctlib.cpp:230
ContextHandle_t GetContextHandle(const THREADID id, const uint32_t slot)
Definition: cctlib.cpp:1356
uint32_t ContextHandle_t
Definition: cctlib.H:22
void * address
unordered_set< uint64_t > addressSetDecoded
unordered_set< uint64_t > addressSet
ContextHandle_t handle