CCTLib
Calling-context and data-object attribution library for Intel Pin
redspy_client.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 <sstream>
15 #include <functional>
16 #include <unordered_set>
17 #include <vector>
18 #include <unordered_map>
19 #include <algorithm>
20 #include "pin.H"
21 #include "pin_isa_compat.H"
22 #include "cctlib.H"
23 using namespace std;
24 using namespace PinCCTLib;
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(struct Status*))
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 
49 
50 // have R, W representative macros
51 #define READ_ACTION (0)
52 #define WRITE_ACTION (0xff)
53 
54 #define ONE_BYTE_READ_ACTION (0)
55 #define TWO_BYTE_READ_ACTION (0)
56 #define FOUR_BYTE_READ_ACTION (0)
57 #define EIGHT_BYTE_READ_ACTION (0)
58 
59 #define ONE_BYTE_WRITE_ACTION (0xff)
60 #define TWO_BYTE_WRITE_ACTION (0xffff)
61 #define FOUR_BYTE_WRITE_ACTION (0xffffffff)
62 #define EIGHT_BYTE_WRITE_ACTION (0xffffffffffffffff)
63 
64 
65 #define IS_ACCESS_WITHIN_PAGE_BOUNDARY(accessAddr, accessLen) (PAGE_OFFSET((accessAddr)) <= (PAGE_OFFSET_MASK - (accessLen)))
66 
67 /* Other footprint_client settings */
68 #define MAX_REDUNDANT_CONTEXTS_TO_LOG (1000)
69 #define THREAD_MAX (1024)
70 
71 #define ENCODE_ADDRESS_AND_ACCESS_LEN(addr, len) ((addr) | (((uint64_t)(len)) << 48))
72 #define DECODE_ADDRESS(addrAndLen) ((addrAndLen) & ((1L << 48) - 1))
73 #define DECODE_ACCESS_LEN(addrAndLen) ((addrAndLen) >> 48)
74 
75 
76 #define MAX_WRITE_OP_LENGTH (512)
77 #define MAX_WRITE_OPS_IN_INS (8)
78 
79 #define WINDOW_ENABLE 1000000
80 #define WINDOW_DISABLE 1000000000
81 
82 
83 #define DECODE_DEAD(data) static_cast<ContextHandle_t>(((data)&0xffffffffffffffff) >> 32)
84 #define DECODE_KILL(data) (static_cast<ContextHandle_t>((data)&0x00000000ffffffff))
85 
86 
87 #define MAKE_CONTEXT_PAIR(a, b) (((uint64_t)(a) << 32) | ((uint64_t)(b)))
88 
89 __thread long long NUM_INS = 0;
90 __thread bool Sample_flag = true;
91 
92 
93 struct AddrValPair {
94  void* address;
95  uint8_t value[MAX_WRITE_OP_LENGTH];
96 };
97 
98 struct RedSpyThreadData {
100  uint64_t bytesWritten;
101 };
102 
103 // for metric logging
105 
106 // key for accessing TLS storage in the threads. initialized once in main()
107 static TLS_KEY client_tls_key;
108 
109 // function to access thread-specific data
110 inline RedSpyThreadData* ClientGetTLS(const THREADID threadId) {
111  RedSpyThreadData* tdata =
112  static_cast<RedSpyThreadData*>(PIN_GetThreadData(client_tls_key, threadId));
113  return tdata;
114 }
115 
116 
117 template <int start, int end, int incr>
118 struct UnrolledLoop {
119  static __attribute__((always_inline)) void Body(const function<void(const int)>& func) {
120  func(start); // Real loop body
121  UnrolledLoop<start + incr, end, incr>::Body(func); // unroll next iteration
122  }
123 };
124 
125 template <int end, int incr>
126 struct UnrolledLoop<end, end, incr> {
127  static __attribute__((always_inline)) void Body(const function<void(const int)>& func) {
128  // empty body
129  }
130 };
131 
132 template <int start, int end, int incr>
133 struct UnrolledConjunction {
134  static __attribute__((always_inline)) bool Body(const function<bool(const int)>& func) {
135  return func(start) && UnrolledConjunction<start + incr, end, incr>::Body(func); // unroll next iteration
136  }
137 };
138 
139 template <int end, int incr>
140 struct UnrolledConjunction<end, end, incr> {
141  static __attribute__((always_inline)) bool Body(const function<void(const int)>& func) {
142  return true;
143  }
144 };
145 
146 static INT32 Usage() {
147  PIN_ERROR("Pin tool to gather calling context on each load and store.\n" + KNOB_BASE::StringKnobSummary() + "\n");
148  return -1;
149 }
150 
151 // Main for RedSpy, initialize the tool, register instrumentation functions and call the target program.
152 static FILE* gTraceFile;
154 
155 // Initialized the needed data structures before launching the target program
156 static void ClientInit(int argc, char* argv[]) {
157  // Create output file
158  char name[MAX_FILE_PATH] = "redspy.out.";
159  char* envPath = getenv("CCTLIB_CLIENT_OUTPUT_FILE");
160 
161  if (envPath) {
162  // assumes max of MAX_FILE_PATH
163  snprintf(name, sizeof(name), "%s", envPath);
164  }
165 
166  gethostname(name + strlen(name), MAX_FILE_PATH - strlen(name));
167  pid_t pid = getpid();
168  sprintf(name + strlen(name), "%d", pid);
169  cerr << "\n Creating log file at:" << name << "\n";
170  gTraceFile = fopen(name, "w");
171  // print the arguments passed
172  fprintf(gTraceFile, "\n");
173 
174  for (int i = 0; i < argc; i++) {
175  fprintf(gTraceFile, "%s ", argv[i]);
176  }
177 
178  fprintf(gTraceFile, "\n");
179 }
180 
181 
182 /* helper functions for shadow memory */
183 static uint8_t* GetOrCreateShadowBaseAddress(uint64_t address) {
184  uint8_t* shadowPage;
185  uint8_t*** l1Ptr = &gL1PageTable[LEVEL_1_PAGE_TABLE_SLOT(address)];
186  if (*l1Ptr == nullptr) {
187  *l1Ptr = (uint8_t**)mmap(nullptr, LEVEL_2_PAGE_TABLE_SIZE, PROT_WRITE | PROT_READ, MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
188  shadowPage = (*l1Ptr)[LEVEL_2_PAGE_TABLE_SLOT(address)] = (uint8_t*)mmap(nullptr, PAGE_SIZE * (sizeof(uint64_t)), PROT_WRITE | PROT_READ, MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
189  } else if ((shadowPage = (*l1Ptr)[LEVEL_2_PAGE_TABLE_SLOT(address)]) == nullptr) {
190  shadowPage = (*l1Ptr)[LEVEL_2_PAGE_TABLE_SLOT(address)] = (uint8_t*)mmap(nullptr, PAGE_SIZE * (sizeof(uint64_t)), PROT_WRITE | PROT_READ, MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
191  }
192  return shadowPage;
193 }
194 
195 
196 static const uint64_t READ_ACCESS_STATES[] = {/*0 byte */ 0, /*1 byte */ ONE_BYTE_READ_ACTION, /*2 byte */ TWO_BYTE_READ_ACTION, /*3 byte */ 0, /*4 byte */ FOUR_BYTE_READ_ACTION, /*5 byte */ 0, /*6 byte */ 0, /*7 byte */ 0, /*8 byte */ EIGHT_BYTE_READ_ACTION};
197 static const uint64_t WRITE_ACCESS_STATES[] = {/*0 byte */ 0, /*1 byte */ ONE_BYTE_WRITE_ACTION, /*2 byte */ TWO_BYTE_WRITE_ACTION, /*3 byte */ 0, /*4 byte */ FOUR_BYTE_WRITE_ACTION, /*5 byte */ 0, /*6 byte */ 0, /*7 byte */ 0, /*8 byte */ EIGHT_BYTE_WRITE_ACTION};
198 static const uint8_t OVERFLOW_CHECK[] = {/*0 byte */ 0, /*1 byte */ 0, /*2 byte */ 0, /*3 byte */ 1, /*4 byte */ 2, /*5 byte */ 3, /*6 byte */ 4, /*7 byte */ 5, /*8 byte */ 6};
199 
200 static unordered_map<uint64_t, uint64_t> RedMap[THREAD_MAX];
201 static inline void AddToRedTable(uint64_t key, uint16_t value, THREADID threadId) {
202 #ifdef MULTI_THREADED
203  LOCK_RED_MAP();
204 #endif
205  unordered_map<uint64_t, uint64_t>::iterator it = RedMap[threadId].find(key);
206  if (it == RedMap[threadId].end()) {
207  RedMap[threadId][key] = value;
208  } else {
209  it->second += value;
210  }
211 #ifdef MULTI_THREADED
212  UNLOCK_RED_MAP();
213 #endif
214 }
215 
216 
217 template <uint16_t AccessLen, uint32_t bufferOffset>
218 struct RedSpyAnalysis {
219  static __attribute__((always_inline)) bool IsWriteRedundant(void*& addr, THREADID threadId) {
220  RedSpyThreadData* const tData = ClientGetTLS(threadId);
221  AddrValPair* avPair = &tData->buffer[bufferOffset];
222  addr = avPair->address;
223  switch (AccessLen) {
224  case 1:
225  return *((uint8_t*)(&avPair->value)) == *(static_cast<uint8_t*>(avPair->address));
226  case 2:
227  return *((uint16_t*)(&avPair->value)) == *(static_cast<uint16_t*>(avPair->address));
228  case 4:
229  return *((uint32_t*)(&avPair->value)) == *(static_cast<uint32_t*>(avPair->address));
230  case 8:
231  return *((uint64_t*)(&avPair->value)) == *(static_cast<uint64_t*>(avPair->address));
232  default:
233  return memcmp(&avPair->value, avPair->address, AccessLen) == 0;
234  }
235  }
236 
237  static __attribute__((always_inline)) VOID RecordNByteValueBeforeWrite(void* addr, THREADID threadId) {
238  if (Sample_flag) {
239  NUM_INS++;
240  if (NUM_INS > WINDOW_ENABLE) {
241  Sample_flag = false;
242  NUM_INS = 0;
243  //EmptyCtxt(threadId);
244  return;
245  }
246  } else {
247  NUM_INS++;
248  if (NUM_INS > WINDOW_DISABLE) {
249  Sample_flag = true;
250  NUM_INS = 0;
251  } else
252  return;
253  }
254  RedSpyThreadData* const tData = ClientGetTLS(threadId);
255  tData->bytesWritten += AccessLen;
256  AddrValPair* avPair = &tData->buffer[bufferOffset];
257  //printf("\n B: %lx %lu %d %d %lx", (uint64_t)addr, tData->bytesWritten, AccessLen, bufferOffset, (uint64_t)ip);
258  //fflush(stdout);
259  avPair->address = addr;
260  switch (AccessLen) {
261  case 1:
262  *((uint8_t*)(&avPair->value)) = *(static_cast<uint8_t*>(addr));
263  break;
264  case 2:
265  *((uint16_t*)(&avPair->value)) = *(static_cast<uint16_t*>(addr));
266  break;
267  case 4:
268  *((uint32_t*)(&avPair->value)) = *(static_cast<uint32_t*>(addr));
269  break;
270  case 8:
271  *((uint64_t*)(&avPair->value)) = *(static_cast<uint64_t*>(addr));
272  break;
273  default:
274  memcpy(&avPair->value, addr, AccessLen);
275  }
276  }
277 
278  static __attribute__((always_inline)) VOID CheckNByteValueAfterWrite(uint32_t opaqueHandle, THREADID threadId) {
279  if (!Sample_flag)
280  return;
281  void* addr;
282  bool isRedundantWrite = IsWriteRedundant(addr, threadId);
283  //printf("\t A: %lx %lu %d %d %lx", (uint64_t)addr, ClientGetTLS(threadId)->bytesWritten, AccessLen, bufferOffset, (uint64_t)ip);
284  //fflush(stdout);
285  ContextHandle_t curCtxtHandle = GetContextHandle(threadId, opaqueHandle);
286 
287  uint8_t* status = GetOrCreateShadowBaseAddress((uint64_t)addr);
288  ContextHandle_t* __restrict__ prevIP = (ContextHandle_t*)(status + PAGE_OFFSET((uint64_t)addr) * sizeof(ContextHandle_t));
289  const bool isAccessWithinPageBoundary = IS_ACCESS_WITHIN_PAGE_BOUNDARY((uint64_t)addr, AccessLen);
290  if (isRedundantWrite) {
291  // detected redundancy
292  if (isAccessWithinPageBoundary) {
293  // All from same ctxt?
294  if (UnrolledConjunction<0, AccessLen, 1>::Body([&](int index) -> bool { return (prevIP[index] == prevIP[0]); })) {
295  // report in RedTable
296  AddToRedTable(MAKE_CONTEXT_PAIR(prevIP[0], curCtxtHandle), AccessLen, threadId);
297  // Update context
298  UnrolledLoop<0, AccessLen, 1>::Body([&](int index) -> VOID {
299  // Update context
300  prevIP[index] = curCtxtHandle;
301  });
302  } else {
303  // different contexts
304  UnrolledLoop<0, AccessLen, 1>::Body([&](int index) -> VOID {
305  // report in RedTable
306  AddToRedTable(MAKE_CONTEXT_PAIR(prevIP[index], curCtxtHandle), 1, threadId);
307  // Update context
308  prevIP[index] = curCtxtHandle;
309  });
310  }
311  } else {
312  // Write across a 64-K page boundary
313  // First byte is on this page though
314  AddToRedTable(MAKE_CONTEXT_PAIR(prevIP[0], curCtxtHandle), 1, threadId);
315  // Update context
316  prevIP[0] = curCtxtHandle;
317 
318  // Remaining bytes [1..AccessLen] somewhere will across a 64-K page boundary
319  UnrolledLoop<1, AccessLen, 1>::Body([&](int index) -> VOID {
320  status = GetOrCreateShadowBaseAddress((uint64_t)addr + index);
321  prevIP = (ContextHandle_t*)(status + PAGE_OFFSET(((uint64_t)addr + index)) * sizeof(ContextHandle_t));
322  // report in RedTable
323  AddToRedTable(MAKE_CONTEXT_PAIR(prevIP[0 /* 0 is correct*/], curCtxtHandle), 1, threadId);
324  // Update context
325  prevIP[0] = curCtxtHandle;
326  });
327  }
328  } else {
329  // No redundancy.
330  // Just update contexts
331  if (isAccessWithinPageBoundary) {
332  UnrolledLoop<0, AccessLen, 1>::Body([&](int index) -> VOID {
333  // Update context
334  prevIP[index] = curCtxtHandle;
335  });
336  } else {
337  // Write across a 64-K page boundary
338  UnrolledLoop<0, AccessLen, 1>::Body([&](int index) -> VOID {
339  status = GetOrCreateShadowBaseAddress((uint64_t)addr + index);
340  prevIP = (ContextHandle_t*)(status + PAGE_OFFSET(((uint64_t)addr + index)) * sizeof(ContextHandle_t));
341  // Update context
342  prevIP[0] = curCtxtHandle;
343  });
344  }
345  }
346  }
347 };
348 
349 
350 static inline VOID RecordValueBeforeLargeWrite(void* addr, UINT32 accessLen, uint32_t bufferOffset, THREADID threadId) {
351  if (Sample_flag) {
352  NUM_INS++;
353  if (NUM_INS > WINDOW_ENABLE) {
354  Sample_flag = false;
355  NUM_INS = 0;
356  //EmptyCtxt(threadId);
357  return;
358  }
359  } else {
360  NUM_INS++;
361  if (NUM_INS > WINDOW_DISABLE) {
362  Sample_flag = true;
363  NUM_INS = 0;
364  } else
365  return;
366  }
367 
368  RedSpyThreadData* const tData = ClientGetTLS(threadId);
369  tData->bytesWritten += accessLen;
370  memcpy(&(tData->buffer[bufferOffset].value), addr, accessLen);
371  tData->buffer[bufferOffset].address = addr;
372 }
373 
374 static inline VOID CheckAfterLargeWrite(UINT32 accessLen, uint32_t bufferOffset, uint32_t opaqueHandle, THREADID threadId) {
375  if (!Sample_flag)
376  return;
377  RedSpyThreadData* const tData = ClientGetTLS(threadId);
378  void* addr = tData->buffer[bufferOffset].address;
379  ContextHandle_t curCtxtHandle = GetContextHandle(threadId, opaqueHandle);
380 
381  uint8_t* status;
382  ContextHandle_t* __restrict__ prevIP;
383  if (memcmp(&(tData->buffer[bufferOffset].value), addr, accessLen) == 0) {
384  // redundant
385  for (UINT32 index = 0; index < accessLen; index++) {
386  status = GetOrCreateShadowBaseAddress((uint64_t)addr + index);
387  prevIP = (ContextHandle_t*)(status + PAGE_OFFSET(((uint64_t)addr + index)) * sizeof(ContextHandle_t));
388  // report in RedTable
389  AddToRedTable(MAKE_CONTEXT_PAIR(prevIP[0 /* 0 is correct*/], curCtxtHandle), 1, threadId);
390  // Update context
391  prevIP[0] = curCtxtHandle;
392  }
393  } else {
394  // Not redundant
395  for (UINT32 index = 0; index < accessLen; index++) {
396  status = GetOrCreateShadowBaseAddress((uint64_t)addr + index);
397  prevIP = (ContextHandle_t*)(status + PAGE_OFFSET(((uint64_t)addr + index)) * sizeof(ContextHandle_t));
398  // Update context
399  prevIP[0] = curCtxtHandle;
400  }
401  }
402 }
403 
404 
405 /*
406 inline VOID BytesWrittenInBBL(uint32_t count, THREADID threadId) {
407  ClientGetTLS(threadId)->bytesWritten += count;
408 }
409 */
410 
411 
412 // Instrument a trace, take the first instruction in the first BBL and insert the analysis function before that
413 /*
414 static void InstrumentTrace(TRACE trace, void* f) {
415  // Insert counting code
416  for(BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) {
417  uint32_t totalBytesWrittenInBBL = 0;
418  for(INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins = INS_Next(ins)) {
419  if(INS_IsMemoryWrite(ins)) {
420  totalBytesWrittenInBBL += INS_MemoryWriteSize(ins);
421  }
422  }
423 
424  // Insert a call to corresponding count routines before every bbl, passing the number of instructions
425 
426  // Increment Inst count by trace
427  if(totalBytesWrittenInBBL)
428  BBL_InsertCall(bbl, IPOINT_ANYWHERE, (AFUNPTR) BytesWrittenInBBL, IARG_UINT32, totalBytesWrittenInBBL, IARG_THREAD_ID, IARG_END);
429  }
430 }*/
431 
432 #define HANDLE_CASE(NUM, BUFFER_INDEX) \
433  case (NUM): { \
434  INS_InsertPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)RedSpyAnalysis<(NUM), (BUFFER_INDEX)>::RecordNByteValueBeforeWrite, IARG_MEMORYOP_EA, memOp, IARG_THREAD_ID, IARG_END); \
435  INS_InsertPredicatedCall(ins, IPOINT_AFTER, (AFUNPTR)RedSpyAnalysis<(NUM), (BUFFER_INDEX)>::CheckNByteValueAfterWrite, IARG_UINT32, opaqueHandle, IARG_THREAD_ID, IARG_INST_PTR, IARG_END); \
436  } break
437 
438 
439 static int GetNumWriteOperandsInIns(INS ins, UINT32& whichOp) {
440  int numWriteOps = 0;
441  UINT32 memOperands = INS_MemoryOperandCount(ins);
442  for (UINT32 memOp = 0; memOp < memOperands; memOp++) {
443  if (INS_MemoryOperandIsWritten(ins, memOp)) {
444  numWriteOps++;
445  whichOp = memOp;
446  }
447  }
448  return numWriteOps;
449 }
450 
451 template <uint32_t readBufferSlotIndex>
452 struct RedSpyInstrument {
453  static __attribute__((always_inline)) void InstrumentReadValueBeforeAndAfterWriting(INS ins, UINT32 memOp, uint32_t opaqueHandle) {
454  UINT32 refSize = INS_MemoryOperandSize(ins, memOp);
455  switch (refSize) {
456  HANDLE_CASE(1, readBufferSlotIndex);
457  HANDLE_CASE(2, readBufferSlotIndex);
458  HANDLE_CASE(4, readBufferSlotIndex);
459  HANDLE_CASE(8, readBufferSlotIndex);
460  HANDLE_CASE(10, readBufferSlotIndex);
461  HANDLE_CASE(16, readBufferSlotIndex);
462 
463  default: {
464  INS_InsertPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)RecordValueBeforeLargeWrite, IARG_MEMORYOP_EA, memOp, IARG_MEMORYWRITE_SIZE, IARG_UINT32, readBufferSlotIndex, IARG_THREAD_ID, IARG_END);
465  // Bug fix: was IARG_MEMORYREAD_SIZE, which is 0 for pure store
466  // instructions (SSE/AVX vmovdqu etc), so accessLen reaching
467  // CheckAfterLargeWrite was 0 and large SIMD stores were never
468  // classified as redundant. Surfaced by
469  // tests/gtest/apps/isa/redspy_avx32_tp.
470  INS_InsertPredicatedCall(ins, IPOINT_AFTER, (AFUNPTR)CheckAfterLargeWrite, IARG_MEMORYWRITE_SIZE, IARG_UINT32, readBufferSlotIndex, IARG_UINT32, opaqueHandle, IARG_THREAD_ID, IARG_END);
471  }
472  }
473  }
474 };
475 
476 static VOID InstrumentInsCallback(INS ins, VOID* v, const uint32_t opaqueHandle) {
477  if (!INS_IsMemoryRead(ins) && !INS_IsMemoryWrite(ins))
478  return;
479  // if (INS_IsStackRead(ins) || INS_IsStackWrite(ins)) return;
480  if (INS_IsControlFlow(ins) || INS_IsRet(ins))
481  return;
482 
483  // XSAVEC and XRSTOR are problematic since its access length is variable.
484  // Execution of XSAVEC is similar to that of XSAVE. XSAVEC differs from XSAVE in that it uses compaction and that it may use the init optimization.
485  // It fails with "Cannot use IARG_MEMORYWRITE_SIZE on non-standard memory access of instruction at 0xfoo: xsavec ptr [rsp]" error.
486  // A correct solution should use INS_hasKnownMemorySize() which is not available in Pin 2.14.
487  if (INS_Mnemonic(ins) == "XSAVEC")
488  return;
489  if (INS_Mnemonic(ins) == "XRSTOR")
490  return;
491  if (INS_Mnemonic(ins) == "XSAVE")
492  return;
493 
494  // Special case, if we have only one write operand
495  UINT32 whichOp = 0;
496  if (GetNumWriteOperandsInIns(ins, whichOp) == 1) {
497  // Read the value at location before and after the instruction
499  return;
500  }
501 
502  // TODO(deadcode-suspect): This multi-write-operand path passes `whichOp`
503  // (the LAST write op found by GetNumWriteOperandsInIns) to every switch
504  // case instead of `memOp` (the current loop iterand). If Pin ever
505  // exposes an instruction with 2+ memory-write operands, only the last
506  // write op gets instrumented -- N times into N different buffer slots
507  // -- and the earlier write ops are silently missed, while the last one
508  // is double-counted.
509  //
510  // Empirically unreachable on x86-64 with Pin 4.3: scans of an inline-
511  // asm zoo (XCHG, CMPXCHG16B, XADD, ENTER at nested levels 0/1/3/8,
512  // FSAVE, FXSAVE, STMXCSR, FNSTENV, PUSHF, all string ops, all SSE/AVX
513  // loads/stores, LOCK BTS/BTR/BTC, MOVBE, MOVNTI, MOVNT DQ, VMOVNTDQ)
514  // and of real workloads (gcc -O2 -c, python3 zlib+sha256, ls -laR,
515  // tar cf) produced ZERO instructions Pin classifies as ops>=2 with
516  // 2+ operands marked write. Compare with loadspy_client.cpp:1005,
517  // which handles the analogous multi-read path correctly by passing
518  // `memOp` instead of `whichOp`.
519  //
520  // Not patching yet because:
521  // (a) can't construct an ASM victim that triggers the bug, so any
522  // fix would be unverifiable;
523  // (b) if a Pin/ISA change makes it reachable, the failure mode is
524  // silently-wrong redundancy counts, not a crash -- so a blind
525  // fix could mask a genuine detection regression.
526  // Deserves more investigation: (i) check AVX-512 scatter (VPSCATTER*)
527  // via INS_HasScatteredMemoryAccess + IARG_REWRITE_SCATTERED_MEMOP,
528  // which none of the clients currently handle; (ii) check whether any
529  // future Pin classifier changes surface multi-write ops on system
530  // instructions (CALL FAR through m16:64, INT n, ENTER at higher
531  // nesting levels).
532  UINT32 memOperands = INS_MemoryOperandCount(ins);
533  int readBufferSlotIndex = 0;
534  for (UINT32 memOp = 0; memOp < memOperands; memOp++) {
535  if (!INS_MemoryOperandIsWritten(ins, memOp))
536  continue;
537 
538  switch (readBufferSlotIndex) {
539  case 0:
540  // Read the value at location before and after the instruction
542  break;
543  case 1:
544  // Read the value at location before and after the instruction
546  break;
547  case 2:
548  // Read the value at location before and after the instruction
550  break;
551  case 3:
552  // Read the value at location before and after the instruction
554  break;
555  case 4:
556  // Read the value at location before and after the instruction
558  break;
559  default:
560  assert(0 && "NYI");
561  break;
562  }
563 
564  // use next slot for the next write operand
565  readBufferSlotIndex++;
566  }
567 }
568 
569 
570 struct RedundacyData {
571  ContextHandle_t dead;
572  ContextHandle_t kill;
573  uint64_t frequency;
574 };
575 
576 
577 static inline bool RedundacyCompare(const struct RedundacyData& first, const struct RedundacyData& second) {
578  return first.frequency > second.frequency;
579 }
580 
581 static void PrintRedundancyPairs(THREADID threadId) {
582  vector<RedundacyData> tmpList;
583  vector<RedundacyData>::iterator tmpIt;
584 
585  uint64_t grandTotalRedundantBytes = 0;
586  fprintf(gTraceFile, "*************** Dump Data from Thread %d ****************\n", threadId);
587  for (unordered_map<uint64_t, uint64_t>::iterator it = RedMap[threadId].begin(); it != RedMap[threadId].end(); ++it) {
588  ContextHandle_t dead = DECODE_DEAD((*it).first);
589  ContextHandle_t kill = DECODE_KILL((*it).first);
590 
591  for (tmpIt = tmpList.begin(); tmpIt != tmpList.end(); ++tmpIt) {
592  bool ct1 = false;
593  if (dead == 0 || ((*tmpIt).dead) == 0) {
594  if (dead == 0 && ((*tmpIt).dead) == 0)
595  ct1 = true;
596  } else {
597  ct1 = IsSameSourceLine(dead, (*tmpIt).dead);
598  }
599  bool ct2 = IsSameSourceLine(kill, (*tmpIt).kill);
600  if (ct1 && ct2) {
601  (*tmpIt).frequency += (*it).second;
602  grandTotalRedundantBytes += (*it).second;
603  break;
604  }
605  }
606  if (tmpIt == tmpList.end()) {
607  RedundacyData tmp = {dead, kill, (*it).second};
608  tmpList.push_back(tmp);
609  grandTotalRedundantBytes += tmp.frequency;
610  }
611  }
612 
613  fprintf(gTraceFile, "\n Total redundant bytes = %f %%\n", grandTotalRedundantBytes * 100.0 / ClientGetTLS(threadId)->bytesWritten);
614 
615  sort(tmpList.begin(), tmpList.end(), RedundacyCompare);
616  vector<struct AnalyzedMetric_t>::iterator listIt;
617  int cntxtNum = 0;
618  for (vector<RedundacyData>::iterator listIt = tmpList.begin(); listIt != tmpList.end(); ++listIt) {
619  if (cntxtNum < MAX_REDUNDANT_CONTEXTS_TO_LOG) {
620  fprintf(gTraceFile, "\n======= (%f) %% ======\n", (*listIt).frequency * 100.0 / grandTotalRedundantBytes);
621  if ((*listIt).dead == 0) {
622  fprintf(gTraceFile, "\n Prepopulated with by OS\n");
623  } else {
624  PrintFullCallingContext((*listIt).dead);
625  }
626  fprintf(gTraceFile, "\n---------------------Redundantly written by---------------------------\n");
627  PrintFullCallingContext((*listIt).kill);
628  } else {
629  break;
630  }
631  cntxtNum++;
632  }
633 }
634 
635 static void HPCRunRedundancyPairs(THREADID threadId) {
636  vector<RedundacyData> tmpList;
637  vector<RedundacyData>::iterator tmpIt;
638 
639  for (unordered_map<uint64_t, uint64_t>::iterator it = RedMap[threadId].begin(); it != RedMap[threadId].end(); ++it) {
640  RedundacyData tmp = {DECODE_DEAD((*it).first), DECODE_KILL((*it).first), (*it).second};
641  tmpList.push_back(tmp);
642  }
643 
644  sort(tmpList.begin(), tmpList.end(), RedundacyCompare);
645  vector<HPCRunCCT_t*> HPCRunNodes;
646  int cntxtNum = 0;
647  for (vector<RedundacyData>::iterator listIt = tmpList.begin(); listIt != tmpList.end(); ++listIt) {
648  if (cntxtNum < MAX_REDUNDANT_CONTEXTS_TO_LOG) {
649  HPCRunCCT_t* HPCRunNode = new HPCRunCCT_t();
650  HPCRunNode->ctxtHandle1 = (*listIt).dead;
651  HPCRunNode->ctxtHandle2 = (*listIt).kill;
652  HPCRunNode->metric = (*listIt).frequency;
653  HPCRunNode->metric_id = redspy_metric_id;
654  HPCRunNodes.push_back(HPCRunNode);
655  } else {
656  break;
657  }
658  cntxtNum++;
659  }
660  newCCT_hpcrun_build_cct(HPCRunNodes, threadId);
661 }
662 
663 // On each Unload of a loaded image, the accummulated redundancy information is dumped
664 static VOID ImageUnload(IMG img, VOID* v) {
665  fprintf(gTraceFile, "\n TODO .. Multi-threading is not well supported.");
666  THREADID threadid = PIN_ThreadId();
667  fprintf(gTraceFile, "\nUnloading %s", IMG_Name(img).c_str());
668  // Update gTotalInstCount first
669  PIN_LockClient();
670  PrintRedundancyPairs(threadid);
671  PIN_UnlockClient();
672  // clear redmap now
673  RedMap[threadid].clear();
674 }
675 
676 static VOID ThreadFiniFunc(THREADID threadid, const CONTEXT* ctxt, INT32 code, VOID* v) {
677  // output the CCT for hpcviewer format
678  HPCRunRedundancyPairs(threadid);
680 }
681 
682 static VOID FiniFunc(INT32 code, VOID* v) {
683  // do whatever you want to the full CCT with footpirnt
684 }
685 
686 
687 static void InitThreadData(RedSpyThreadData* tdata) {
688  tdata->bytesWritten = 0;
689 }
690 
691 static VOID ThreadStart(THREADID threadid, CONTEXT* ctxt, INT32 flags, VOID* v) {
692  RedSpyThreadData* tdata = new RedSpyThreadData();
693  InitThreadData(tdata);
694  // __sync_fetch_and_add(&gClientNumThreads, 1);
695  PIN_SetThreadData(client_tls_key, tdata, threadid);
696 }
697 
698 
699 int main(int argc, char* argv[]) {
700  // Initialize PIN
701  if (PIN_Init(argc, argv))
702  return Usage();
703 
704  // Initialize Symbols, we need them to report functions and lines
705  PIN_InitSymbols();
706 
707  // Init Client
708  ClientInit(argc, argv);
709  // Intialize CCTLib
711 
712  // Init hpcrun format output
713  init_hpcrun_format(argc, argv, nullptr, nullptr, false);
714  // Create new metrics
715  redspy_metric_id = hpcrun_create_metric("RED_STORES");
716 
717  // Obtain a key for TLS storage.
718  client_tls_key = PIN_CreateThreadDataKey(nullptr /*TODO have a destructir*/);
719  // Register ThreadStart to be called when a thread starts.
720  PIN_AddThreadStartFunction(ThreadStart, nullptr);
721 
722 
723  // fini function for post-mortem analysis
724  PIN_AddThreadFiniFunction(ThreadFiniFunc, nullptr);
725  PIN_AddFiniFunction(FiniFunc, nullptr);
726  //TRACE_AddInstrumentFunction(InstrumentTrace, 0);
727 
728 
729  // Register ImageUnload to be called when an image is unloaded
730  IMG_AddUnloadFunction(ImageUnload, nullptr);
731 
732  // Launch program now
733  PIN_StartProgram();
734  return 0;
735 }
#define MAX_FILE_PATH
Definition: cctlib.H:18
#define INTERESTING_INS_MEMORY_ACCESS
Definition: cctlib.H:87
int PinCCTLibInit(IsInterestingInsFptr isInterestingIns, FILE *logFile, CCTLibInstrumentInsCallback userCallback, VOID *userCallbackArg, BOOL doDataCentric=false)
Definition: cctlib.cpp:3132
int hpcrun_create_metric(const char *name)
Definition: cctlib.cpp:4281
int newCCT_hpcrun_build_cct(std::vector< HPCRunCCT_t * > &OldNodes, THREADID threadid)
Definition: cctlib.cpp:4343
struct HPCRunCCT_t { ContextHandle_t ctxtHandle1 HPCRunCCT_t
Definition: cctlib.H:58
hpcrun_metricFlags_t flags
Definition: cctlib.cpp:3425
VOID PrintFullCallingContext(const ContextHandle_t ctxtHandle)
Definition: cctlib.cpp:2346
bool IsSameSourceLine(ContextHandle_t ctxt1, ContextHandle_t ctxt2)
Definition: cctlib.cpp:3255
volatile uint8_t status
Definition: cctlib.cpp:230
int init_hpcrun_format(int argc, char *argv[], void(*mergeFunc)(void *des, void *src)=nullptr, uint64_t(*computeMetricVal)(void *metric)=nullptr, bool skip=false)
Definition: cctlib.cpp:4254
ContextHandle_t GetContextHandle(const THREADID id, const uint32_t slot)
Definition: cctlib.cpp:1356
uint32_t ContextHandle_t
Definition: cctlib.H:22
int newCCT_hpcrun_selection_write(THREADID threadid)
Definition: cctlib.cpp:4369
#define MAX_WRITE_OP_LENGTH
static uint8_t ** gL1PageTable[LEVEL_1_PAGE_TABLE_SIZE]
static void ClientInit(int argc, char *argv[])
int main(int argc, char *argv[])
static FILE * gTraceFile
#define LEVEL_2_PAGE_TABLE_SIZE
#define MAX_REDUNDANT_CONTEXTS_TO_LOG
static VOID ImageUnload(IMG img, VOID *v)
#define LEVEL_1_PAGE_TABLE_SLOT(addr)
#define EIGHT_BYTE_WRITE_ACTION
static void PrintRedundancyPairs(THREADID threadId)
static const uint64_t READ_ACCESS_STATES[]
#define THREAD_MAX
#define MAX_WRITE_OPS_IN_INS
#define ONE_BYTE_READ_ACTION
static bool RedundacyCompare(const struct RedundacyData &first, const struct RedundacyData &second)
static INT32 Usage()
#define EIGHT_BYTE_READ_ACTION
#define TWO_BYTE_READ_ACTION
#define PAGE_OFFSET(addr)
int redspy_metric_id
static const uint8_t OVERFLOW_CHECK[]
static void HPCRunRedundancyPairs(THREADID threadId)
#define MAKE_CONTEXT_PAIR(a, b)
#define PAGE_SIZE
#define DECODE_DEAD(data)
static VOID RecordValueBeforeLargeWrite(void *addr, UINT32 accessLen, uint32_t bufferOffset, THREADID threadId)
static VOID InstrumentInsCallback(INS ins, VOID *v, const uint32_t opaqueHandle)
static void InitThreadData(RedSpyThreadData *tdata)
RedSpyThreadData * ClientGetTLS(const THREADID threadId)
__thread long long NUM_INS
static VOID FiniFunc(INT32 code, VOID *v)
#define TWO_BYTE_WRITE_ACTION
static const uint64_t WRITE_ACCESS_STATES[]
__thread bool Sample_flag
static VOID CheckAfterLargeWrite(UINT32 accessLen, uint32_t bufferOffset, uint32_t opaqueHandle, THREADID threadId)
static void AddToRedTable(uint64_t key, uint16_t value, THREADID threadId)
#define FOUR_BYTE_READ_ACTION
static uint8_t * GetOrCreateShadowBaseAddress(uint64_t address)
#define IS_ACCESS_WITHIN_PAGE_BOUNDARY(accessAddr, accessLen)
static VOID ThreadFiniFunc(THREADID threadid, const CONTEXT *ctxt, INT32 code, VOID *v)
#define LEVEL_1_PAGE_TABLE_SIZE
static int GetNumWriteOperandsInIns(INS ins, UINT32 &whichOp)
#define ONE_BYTE_WRITE_ACTION
#define FOUR_BYTE_WRITE_ACTION
#define DECODE_KILL(data)
static TLS_KEY client_tls_key
#define WINDOW_ENABLE
static VOID ThreadStart(THREADID threadid, CONTEXT *ctxt, INT32 flags, VOID *v)
#define HANDLE_CASE(NUM, BUFFER_INDEX)
#define WINDOW_DISABLE
static unordered_map< uint64_t, uint64_t > RedMap[THREAD_MAX]
#define LEVEL_2_PAGE_TABLE_SLOT(addr)
uint8_t value[MAX_WRITE_OP_LENGTH]
void * address
uint8_t value[MAX_WRITE_OP_LENGTH]
void * address
static __attribute__((always_inline)) bool IsWriteRedundant(void *&addr
static __attribute__((always_inline)) VOID CheckNByteValueAfterWrite(uint32_t opaqueHandle
static __attribute__((always_inline)) VOID RecordNByteValueBeforeWrite(void *addr
static __attribute__((always_inline)) void InstrumentReadValueBeforeAndAfterWriting(INS ins
uint64_t bytesWritten
AddrValPair buffer[MAX_WRITE_OPS_IN_INS]
static __attribute__((always_inline)) bool Body(const function< void(const int)> &func)
static __attribute__((always_inline)) bool Body(const function< bool(const int)> &func)
static bool Body(const function< bool(const int)> &func)
static __attribute__((always_inline)) void Body(const function< void(const int)> &func)
static void Body(const function< void(const int)> &func)
static __attribute__((always_inline)) void Body(const function< void(const int)> &func)