CCTLib
Calling-context and data-object attribution library for Intel Pin
loadspy_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 <atomic>
10 #include <malloc.h>
11 #include <iostream>
12 #include <unistd.h>
13 #include <assert.h>
14 #include <string.h>
15 #include <sys/mman.h>
16 #include <sstream>
17 #include <functional>
18 #include <unordered_set>
19 #include <vector>
20 #include <unordered_map>
21 #include <algorithm>
22 #include <list>
23 #include "pin.H"
24 #include "pin_isa_compat.H"
25 #include "cctlib.H"
26 #include "shadow_memory.H"
27 #include <xmmintrin.h>
28 #include <immintrin.h>
29 
30 extern "C" {
31 #include "xed-interface.h"
32 #include "xed-common-hdrs.h"
33 }
34 
35 #include <google/sparse_hash_map>
36 #include <google/dense_hash_map>
37 
38 using namespace std;
39 using namespace PinCCTLib;
40 
41 
42 // have R, W representative macros
43 #define READ_ACTION (0)
44 #define WRITE_ACTION (0xff)
45 
46 #define ONE_BYTE_READ_ACTION (0)
47 #define TWO_BYTE_READ_ACTION (0)
48 #define FOUR_BYTE_READ_ACTION (0)
49 #define EIGHT_BYTE_READ_ACTION (0)
50 
51 #define ONE_BYTE_WRITE_ACTION (0xff)
52 #define TWO_BYTE_WRITE_ACTION (0xffff)
53 #define FOUR_BYTE_WRITE_ACTION (0xffffffff)
54 #define EIGHT_BYTE_WRITE_ACTION (0xffffffffffffffff)
55 
56 
57 #define IS_ACCESS_WITHIN_PAGE_BOUNDARY(accessAddr, accessLen) (PAGE_OFFSET((accessAddr)) <= (PAGE_OFFSET_MASK - (accessLen)))
58 
59 /* Other footprint_client settings */
60 #define MAX_REDUNDANT_CONTEXTS_TO_LOG (1000)
61 #define THREAD_MAX (1024)
62 
63 #define ENCODE_ADDRESS_AND_ACCESS_LEN(addr, len) ((addr) | (((uint64_t)(len)) << 48))
64 #define DECODE_ADDRESS(addrAndLen) ((addrAndLen) & ((1L << 48) - 1))
65 #define DECODE_ACCESS_LEN(addrAndLen) ((addrAndLen) >> 48)
66 
67 
68 #define MAX_WRITE_OP_LENGTH (512)
69 #define MAX_WRITE_OPS_IN_INS (8)
70 #define MAX_REG_LENGTH (64)
71 
72 #define MAX_SIMD_LENGTH (64)
73 #define MAX_SIMD_REGS (32)
74 
75 
76 #ifdef ENABLE_SAMPLING
77 
78 #define WINDOW_ENABLE 1000000
79 #define WINDOW_DISABLE 100000000
80 #define WINDOW_CLEAN 10
81 #endif
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 #define delta 0.01
90 
91 
92 /***********************************************
93  ****** shadow memory
94  ************************************************/
96 
97 struct {
98  char dummy1[128];
99  xed_state_t xedState;
100  char dummy2[128];
102 
103 
104 #if 0
106 
107 
108 inline uint8_t* GetOrCreateShadowBaseAddress(uint64_t address) {
109  // No entries at all ?
110  uint8_t* shadowPage;
111  uint8_t** * l1Ptr = &gL1PageTable[LEVEL_1_PAGE_TABLE_SLOT(address)];
112 
113  if(*l1Ptr == 0) {
114  *l1Ptr = (uint8_t**) calloc(1, LEVEL_2_PAGE_TABLE_SIZE);
115  shadowPage = (*l1Ptr)[LEVEL_2_PAGE_TABLE_SLOT(address)] = (uint8_t*) mmap(0, SHADOW_PAGE_SIZE * (1 + sizeof(uint32_t)), PROT_WRITE | PROT_READ, MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
116  } else if((shadowPage = (*l1Ptr)[LEVEL_2_PAGE_TABLE_SLOT(address)]) == 0) {
117  shadowPage = (*l1Ptr)[LEVEL_2_PAGE_TABLE_SLOT(address)] = (uint8_t*) mmap(0, SHADOW_PAGE_SIZE * (1 + sizeof(uint32_t)), PROT_WRITE | PROT_READ, MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
118  }
119 
120  return shadowPage;
121 }
122 #endif
123 
124 ////////////////////////////////////////////////
125 
126 struct RedSpyThreadData {
127  uint64_t bytesLoad;
128 
129  long long numIns;
131 };
132 
133 // for metric logging
136 
137 //for statistics result
141 
142 // key for accessing TLS storage in the threads. initialized once in main()
143 static TLS_KEY client_tls_key;
145 
146 // function to access thread-specific data
147 inline RedSpyThreadData* ClientGetTLS(const THREADID threadId) {
148 #ifdef MULTI_THREADED
149  RedSpyThreadData* tdata =
150  static_cast<RedSpyThreadData*>(PIN_GetThreadData(client_tls_key, threadId));
151  return tdata;
152 #else
153  return gSingleThreadedTData;
154 #endif
155 }
156 
157 static INT32 Usage() {
158  PIN_ERROR("Pin tool to gather calling context on each load and store.\n" + KNOB_BASE::StringKnobSummary() + "\n");
159  return -1;
160 }
161 
162 // Main for RedSpy, initialize the tool, register instrumentation functions and call the target program.
163 static FILE* gTraceFile;
164 
165 // Initialized the needed data structures before launching the target program
166 static void ClientInit(int argc, char* argv[]) {
167  // Create output file
168  char name[MAX_FILE_PATH] = "redLoad.out.";
169  char* envPath = getenv("CCTLIB_CLIENT_OUTPUT_FILE");
170 
171  if (envPath) {
172  // assumes max of MAX_FILE_PATH
173  snprintf(name, sizeof(name), "%s", envPath);
174  }
175 
176  gethostname(name + strlen(name), MAX_FILE_PATH - strlen(name));
177  pid_t pid = getpid();
178  sprintf(name + strlen(name), "%d", pid);
179  cerr << "\n Creating log file at:" << name << "\n";
180  gTraceFile = fopen(name, "w");
181  // print the arguments passed
182  fprintf(gTraceFile, "\n");
183 
184  for (int i = 0; i < argc; i++) {
185  fprintf(gTraceFile, "%s ", argv[i]);
186  }
187 
188  fprintf(gTraceFile, "\n");
189 
190  // Init Xed
191  // Init XED for decoding instructions
192  xed_state_init(&LoadSpyGlobals.xedState, XED_MACHINE_MODE_LONG_64, (xed_address_width_enum_t)0, XED_ADDRESS_WIDTH_64b);
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 unordered_map<uint64_t, uint64_t> ApproxRedMap[THREAD_MAX];
202 
203 static inline void AddToRedTable(uint64_t key, uint16_t value, THREADID threadId) __attribute__((always_inline, flatten));
204 static inline void AddToRedTable(uint64_t key, uint16_t value, THREADID threadId) {
205 #ifdef MULTI_THREADED
206  LOCK_RED_MAP();
207 #endif
208  unordered_map<uint64_t, uint64_t>::iterator it = RedMap[threadId].find(key);
209  if (it == RedMap[threadId].end()) {
210  RedMap[threadId][key] = value;
211  } else {
212  it->second += value;
213  }
214 #ifdef MULTI_THREADED
215  UNLOCK_RED_MAP();
216 #endif
217 }
218 
219 static inline void AddToApproximateRedTable(uint64_t key, uint16_t value, THREADID threadId) __attribute__((always_inline, flatten));
220 static inline void AddToApproximateRedTable(uint64_t key, uint16_t value, THREADID threadId) {
221 #ifdef MULTI_THREADED
222  LOCK_RED_MAP();
223 #endif
224  unordered_map<uint64_t, uint64_t>::iterator it = ApproxRedMap[threadId].find(key);
225  if (it == ApproxRedMap[threadId].end()) {
226  ApproxRedMap[threadId][key] = value;
227  } else {
228  it->second += value;
229  }
230 #ifdef MULTI_THREADED
231  UNLOCK_RED_MAP();
232 #endif
233 }
234 
235 
236 #ifdef ENABLE_SAMPLING
237 
238 static ADDRINT IfEnableSample(THREADID threadId) {
239  RedSpyThreadData* const tData = ClientGetTLS(threadId);
240  return tData->sampleFlag;
241 }
242 
243 #endif
244 
245 // Certain FP instructions should not be approximated
246 static inline bool IsOkToApproximate(xed_decoded_inst_t& xedd) {
247  xed_iclass_enum_t iclass = xed_decoded_inst_get_iclass(&xedd);
248  switch (iclass) {
249  case XED_ICLASS_FLDENV:
250  case XED_ICLASS_FNSTENV:
251  case XED_ICLASS_FNSAVE:
252  case XED_ICLASS_FLDCW:
253  case XED_ICLASS_FNSTCW:
254  case XED_ICLASS_FXRSTOR:
255  case XED_ICLASS_FXRSTOR64:
256  case XED_ICLASS_FXSAVE:
257  case XED_ICLASS_FXSAVE64:
258  return false;
259  default:
260  return true;
261  }
262 }
263 
264 static inline bool IsFloatInstructionAndOkToApproximate(ADDRINT ip) {
265  xed_decoded_inst_t xedd;
266  xed_decoded_inst_zero_set_mode(&xedd, &LoadSpyGlobals.xedState);
267 
268  if (XED_ERROR_NONE == xed_decode(&xedd, (const xed_uint8_t*)(ip), 15)) {
269  xed_category_enum_t cat = xed_decoded_inst_get_category(&xedd);
270  switch (cat) {
271  case XED_CATEGORY_AES:
272  case XED_CATEGORY_CONVERT:
273  case XED_CATEGORY_PCLMULQDQ:
274  case XED_CATEGORY_SSE:
275  case XED_CATEGORY_AVX2:
276  case XED_CATEGORY_AVX:
277  case XED_CATEGORY_MMX:
278  case XED_CATEGORY_DATAXFER: {
279  // Get the mem operand
280 
281  const xed_inst_t* xi = xed_decoded_inst_inst(&xedd);
282  int noperands = xed_inst_noperands(xi);
283  int memOpIdx = -1;
284  for (int i = 0; i < noperands; i++) {
285  const xed_operand_t* op = xed_inst_operand(xi, i);
286  xed_operand_enum_t op_name = xed_operand_name(op);
287  if (XED_OPERAND_MEM0 == op_name) {
288  memOpIdx = i;
289  break;
290  }
291  }
292  if (memOpIdx == -1) {
293  return false;
294  }
295 
296  // TO DO MILIND case XED_OPERAND_MEM1:
297  xed_operand_element_type_enum_t eType = xed_decoded_inst_operand_element_type(&xedd, memOpIdx);
298  switch (eType) {
299  case XED_OPERAND_ELEMENT_TYPE_FLOAT16:
300  case XED_OPERAND_ELEMENT_TYPE_SINGLE:
301  case XED_OPERAND_ELEMENT_TYPE_DOUBLE:
302  case XED_OPERAND_ELEMENT_TYPE_LONGDOUBLE:
303  case XED_OPERAND_ELEMENT_TYPE_LONGBCD:
304  return IsOkToApproximate(xedd);
305  default:
306  return false;
307  }
308  } break;
309  case XED_CATEGORY_X87_ALU:
310  case XED_CATEGORY_FCMOV:
311  //case XED_CATEGORY_LOGICAL_FP:
312  // assumption, the access length must be either 4 or 8 bytes else assert!!!
313  //assert(*accessLen == 4 || *accessLen == 8);
314  return IsOkToApproximate(xedd);
315  case XED_CATEGORY_XSAVE:
316  case XED_CATEGORY_AVX2GATHER:
317  case XED_CATEGORY_STRINGOP:
318  default:
319  return false;
320  }
321  } else {
322  assert(0 && "failed to disassemble instruction");
323  // printf("\n Diassembly failure\n");
324  return false;
325  }
326 }
327 
328 static inline bool IsFloatInstructionOld(ADDRINT ip) {
329  xed_decoded_inst_t xedd;
330  xed_decoded_inst_zero_set_mode(&xedd, &LoadSpyGlobals.xedState);
331 
332  if (XED_ERROR_NONE == xed_decode(&xedd, (const xed_uint8_t*)(ip), 15)) {
333  xed_iclass_enum_t iclassType = xed_decoded_inst_get_iclass(&xedd);
334  if (iclassType >= XED_ICLASS_F2XM1 && iclassType <= XED_ICLASS_FYL2XP1) {
335  return true;
336  }
337  if (iclassType >= XED_ICLASS_VBROADCASTSD && iclassType <= XED_ICLASS_VDPPS) {
338  return true;
339  }
340  if (iclassType >= XED_ICLASS_VRCPPS && iclassType <= XED_ICLASS_VSQRTSS) {
341  return true;
342  }
343  if (iclassType >= XED_ICLASS_VSUBPD && iclassType <= XED_ICLASS_VXORPS) {
344  return true;
345  }
346  switch (iclassType) {
347  case XED_ICLASS_ADDPD:
348  case XED_ICLASS_ADDPS:
349  case XED_ICLASS_ADDSD:
350  case XED_ICLASS_ADDSS:
351  case XED_ICLASS_ADDSUBPD:
352  case XED_ICLASS_ADDSUBPS:
353  case XED_ICLASS_ANDNPD:
354  case XED_ICLASS_ANDNPS:
355  case XED_ICLASS_ANDPD:
356  case XED_ICLASS_ANDPS:
357  case XED_ICLASS_BLENDPD:
358  case XED_ICLASS_BLENDPS:
359  case XED_ICLASS_BLENDVPD:
360  case XED_ICLASS_BLENDVPS:
361  case XED_ICLASS_CMPPD:
362  case XED_ICLASS_CMPPS:
363  case XED_ICLASS_CMPSD:
364  case XED_ICLASS_CMPSD_XMM:
365  case XED_ICLASS_COMISD:
366  case XED_ICLASS_COMISS:
367  case XED_ICLASS_CVTDQ2PD:
368  case XED_ICLASS_CVTDQ2PS:
369  case XED_ICLASS_CVTPD2PS:
370  case XED_ICLASS_CVTPI2PD:
371  case XED_ICLASS_CVTPI2PS:
372  case XED_ICLASS_CVTPS2PD:
373  case XED_ICLASS_CVTSD2SS:
374  case XED_ICLASS_CVTSI2SD:
375  case XED_ICLASS_CVTSI2SS:
376  case XED_ICLASS_CVTSS2SD:
377  case XED_ICLASS_DIVPD:
378  case XED_ICLASS_DIVPS:
379  case XED_ICLASS_DIVSD:
380  case XED_ICLASS_DIVSS:
381  case XED_ICLASS_DPPD:
382  case XED_ICLASS_DPPS:
383  case XED_ICLASS_HADDPD:
384  case XED_ICLASS_HADDPS:
385  case XED_ICLASS_HSUBPD:
386  case XED_ICLASS_HSUBPS:
387  case XED_ICLASS_MAXPD:
388  case XED_ICLASS_MAXPS:
389  case XED_ICLASS_MAXSD:
390  case XED_ICLASS_MAXSS:
391  case XED_ICLASS_MINPD:
392  case XED_ICLASS_MINPS:
393  case XED_ICLASS_MINSD:
394  case XED_ICLASS_MINSS:
395  case XED_ICLASS_MOVAPD:
396  case XED_ICLASS_MOVAPS:
397  case XED_ICLASS_MOVD:
398  case XED_ICLASS_MOVHLPS:
399  case XED_ICLASS_MOVHPD:
400  case XED_ICLASS_MOVHPS:
401  case XED_ICLASS_MOVLHPS:
402  case XED_ICLASS_MOVLPD:
403  case XED_ICLASS_MOVLPS:
404  case XED_ICLASS_MOVMSKPD:
405  case XED_ICLASS_MOVMSKPS:
406  case XED_ICLASS_MOVNTPD:
407  case XED_ICLASS_MOVNTPS:
408  case XED_ICLASS_MOVNTSD:
409  case XED_ICLASS_MOVNTSS:
410  case XED_ICLASS_MOVSD:
411  case XED_ICLASS_MOVSD_XMM:
412  case XED_ICLASS_MOVSS:
413  case XED_ICLASS_MULPD:
414  case XED_ICLASS_MULPS:
415  case XED_ICLASS_MULSD:
416  case XED_ICLASS_MULSS:
417  case XED_ICLASS_ORPD:
418  case XED_ICLASS_ORPS:
419  case XED_ICLASS_ROUNDPD:
420  case XED_ICLASS_ROUNDPS:
421  case XED_ICLASS_ROUNDSD:
422  case XED_ICLASS_ROUNDSS:
423  case XED_ICLASS_SHUFPD:
424  case XED_ICLASS_SHUFPS:
425  case XED_ICLASS_SQRTPD:
426  case XED_ICLASS_SQRTPS:
427  case XED_ICLASS_SQRTSD:
428  case XED_ICLASS_SQRTSS:
429  case XED_ICLASS_SUBPD:
430  case XED_ICLASS_SUBPS:
431  case XED_ICLASS_SUBSD:
432  case XED_ICLASS_SUBSS:
433  case XED_ICLASS_VADDPD:
434  case XED_ICLASS_VADDPS:
435  case XED_ICLASS_VADDSD:
436  case XED_ICLASS_VADDSS:
437  case XED_ICLASS_VADDSUBPD:
438  case XED_ICLASS_VADDSUBPS:
439  case XED_ICLASS_VANDNPD:
440  case XED_ICLASS_VANDNPS:
441  case XED_ICLASS_VANDPD:
442  case XED_ICLASS_VANDPS:
443  case XED_ICLASS_VBLENDPD:
444  case XED_ICLASS_VBLENDPS:
445  case XED_ICLASS_VBLENDVPD:
446  case XED_ICLASS_VBLENDVPS:
447  case XED_ICLASS_VBROADCASTSD:
448  case XED_ICLASS_VBROADCASTSS:
449  case XED_ICLASS_VCMPPD:
450  case XED_ICLASS_VCMPPS:
451  case XED_ICLASS_VCMPSD:
452  case XED_ICLASS_VCMPSS:
453  case XED_ICLASS_VCOMISD:
454  case XED_ICLASS_VCOMISS:
455  case XED_ICLASS_VCVTDQ2PD:
456  case XED_ICLASS_VCVTDQ2PS:
457  case XED_ICLASS_VCVTPD2PS:
458  case XED_ICLASS_VCVTPH2PS:
459  case XED_ICLASS_VCVTPS2PD:
460  case XED_ICLASS_VCVTSD2SS:
461  case XED_ICLASS_VCVTSI2SD:
462  case XED_ICLASS_VCVTSI2SS:
463  case XED_ICLASS_VCVTSS2SD:
464  case XED_ICLASS_VDIVPD:
465  case XED_ICLASS_VDIVPS:
466  case XED_ICLASS_VDIVSD:
467  case XED_ICLASS_VDIVSS:
468  case XED_ICLASS_VDPPD:
469  case XED_ICLASS_VDPPS:
470  case XED_ICLASS_VMASKMOVPD:
471  case XED_ICLASS_VMASKMOVPS:
472  case XED_ICLASS_VMAXPD:
473  case XED_ICLASS_VMAXPS:
474  case XED_ICLASS_VMAXSD:
475  case XED_ICLASS_VMAXSS:
476  case XED_ICLASS_VMINPD:
477  case XED_ICLASS_VMINPS:
478  case XED_ICLASS_VMINSD:
479  case XED_ICLASS_VMINSS:
480  case XED_ICLASS_VMOVAPD:
481  case XED_ICLASS_VMOVAPS:
482  case XED_ICLASS_VMOVD:
483  case XED_ICLASS_VMOVHLPS:
484  case XED_ICLASS_VMOVHPD:
485  case XED_ICLASS_VMOVHPS:
486  case XED_ICLASS_VMOVLHPS:
487  case XED_ICLASS_VMOVLPD:
488  case XED_ICLASS_VMOVLPS:
489  case XED_ICLASS_VMOVMSKPD:
490  case XED_ICLASS_VMOVMSKPS:
491  case XED_ICLASS_VMOVNTPD:
492  case XED_ICLASS_VMOVNTPS:
493  case XED_ICLASS_VMOVSD:
494  case XED_ICLASS_VMOVSS:
495  case XED_ICLASS_VMOVUPD:
496  case XED_ICLASS_VMOVUPS:
497  case XED_ICLASS_VMULPD:
498  case XED_ICLASS_VMULPS:
499  case XED_ICLASS_VMULSD:
500  case XED_ICLASS_VMULSS:
501  case XED_ICLASS_VORPD:
502  case XED_ICLASS_VORPS:
503  case XED_ICLASS_VPABSD:
504  case XED_ICLASS_VPADDD:
505  case XED_ICLASS_VPCOMD:
506  case XED_ICLASS_VPCOMUD:
507  case XED_ICLASS_VPERMILPD:
508  case XED_ICLASS_VPERMILPS:
509  case XED_ICLASS_VPERMPD:
510  case XED_ICLASS_VPERMPS:
511  case XED_ICLASS_VPGATHERDD:
512  case XED_ICLASS_VPGATHERQD:
513  case XED_ICLASS_VPHADDBD:
514  case XED_ICLASS_VPHADDD:
515  case XED_ICLASS_VPHADDUBD:
516  case XED_ICLASS_VPHADDUWD:
517  case XED_ICLASS_VPHADDWD:
518  case XED_ICLASS_VPHSUBD:
519  case XED_ICLASS_VPHSUBWD:
520  case XED_ICLASS_VPINSRD:
521  case XED_ICLASS_VPMACSDD:
522  case XED_ICLASS_VPMACSSDD:
523  case XED_ICLASS_VPMASKMOVD:
524  case XED_ICLASS_VPMAXSD:
525  case XED_ICLASS_VPMAXUD:
526  case XED_ICLASS_VPMINSD:
527  case XED_ICLASS_VPMINUD:
528  case XED_ICLASS_VPROTD:
529  case XED_ICLASS_VPSUBD:
530  case XED_ICLASS_XORPD:
531  case XED_ICLASS_XORPS:
532  return true;
533 
534  default:
535  return false;
536  }
537  } else {
538  assert(0 && "failed to disassemble instruction");
539  return false;
540  }
541 }
542 /*
543  static inline bool IsFloatInstruction(ADDRINT ip, uint32_t oper) {
544  xed_decoded_inst_t xedd;
545  xed_decoded_inst_zero_set_mode(&xedd, &LoadSpyGlobals.xedState);
546 
547  if(XED_ERROR_NONE == xed_decode(&xedd, (const xed_uint8_t*)(ip), 15)) {
548  xed_operand_element_type_enum_t TypeOperand = xed_decoded_inst_operand_element_type(&xedd,oper);
549  if(TypeOperand == XED_OPERAND_ELEMENT_TYPE_SINGLE || TypeOperand == XED_OPERAND_ELEMENT_TYPE_DOUBLE || TypeOperand == XED_OPERAND_ELEMENT_TYPE_FLOAT16 || TypeOperand == XED_OPERAND_ELEMENT_TYPE_LONGDOUBLE)
550  return true;
551  return false;
552  } else {
553  assert(0 && "failed to disassemble instruction");
554  return false;
555  }
556  }*/
557 
558 static inline uint16_t FloatOperandSize(ADDRINT ip, uint32_t oper) {
559  xed_decoded_inst_t xedd;
560  xed_decoded_inst_zero_set_mode(&xedd, &LoadSpyGlobals.xedState);
561 
562  if (XED_ERROR_NONE == xed_decode(&xedd, (const xed_uint8_t*)(ip), 15)) {
563  xed_operand_element_type_enum_t TypeOperand = xed_decoded_inst_operand_element_type(&xedd, oper);
564  if (TypeOperand == XED_OPERAND_ELEMENT_TYPE_SINGLE || TypeOperand == XED_OPERAND_ELEMENT_TYPE_FLOAT16)
565  return 4;
566  if (TypeOperand == XED_OPERAND_ELEMENT_TYPE_DOUBLE) {
567  return 8;
568  }
569  if (TypeOperand == XED_OPERAND_ELEMENT_TYPE_LONGDOUBLE) {
570  return 16;
571  }
572  assert(0 && "float instruction with unknown operand\n");
573  return 0;
574  } else {
575  assert(0 && "failed to disassemble instruction\n");
576  return 0;
577  }
578 }
579 
580 /***************************************************************************************/
581 /*********************** memory temporal redundancy functions **************************/
582 /***************************************************************************************/
583 
584 template <int start, int end, int incr, bool conditional, bool approx>
585 struct UnrolledLoop {
586  static __attribute__((always_inline)) void Body(const function<void(const int)>& func) {
587  func(start); // Real loop body
589  }
590  static __attribute__((always_inline)) void BodySamePage(ContextHandle_t* __restrict__ prevIP, const ContextHandle_t handle, THREADID threadId) {
591  if (conditional) {
592  // report in RedTable
593  if (approx)
594  AddToApproximateRedTable(MAKE_CONTEXT_PAIR(prevIP[start], handle), 1, threadId);
595  else
596  AddToRedTable(MAKE_CONTEXT_PAIR(prevIP[start], handle), 1, threadId);
597  }
598  // Update context
599  prevIP[start] = handle;
600  UnrolledLoop<start + incr, end, incr, conditional, approx>::BodySamePage(prevIP, handle, threadId); // unroll next iteration
601  }
602  static __attribute__((always_inline)) void BodyStraddlePage(uint64_t addr, const ContextHandle_t handle, THREADID threadId) {
603  tuple<uint8_t[SHADOW_PAGE_SIZE], ContextHandle_t[SHADOW_PAGE_SIZE]>& t = sm.GetOrCreateShadowBaseAddress((uint64_t)addr + start);
604  ContextHandle_t* prevIP = &(get<1>(t)[PAGE_OFFSET(((uint64_t)addr + start))]);
605 
606  if (conditional) {
607  // report in RedTable
608  if (approx)
609  AddToApproximateRedTable(MAKE_CONTEXT_PAIR(prevIP[0 /* 0 is correct*/], handle), 1, threadId);
610  else
611  AddToRedTable(MAKE_CONTEXT_PAIR(prevIP[0 /* 0 is correct*/], handle), 1, threadId);
612  }
613  // Update context
614  prevIP[0] = handle;
616  }
617 };
618 
619 template <int end, int incr, bool conditional, bool approx>
620 struct UnrolledLoop<end, end, incr, conditional, approx> {
621  static __attribute__((always_inline)) void Body(const function<void(const int)>& func) {}
622  static __attribute__((always_inline)) void BodySamePage(ContextHandle_t* __restrict__ prevIP, const ContextHandle_t handle, THREADID threadId) {}
623  static __attribute__((always_inline)) void BodyStraddlePage(uint64_t addr, const ContextHandle_t handle, THREADID threadId) {}
624 };
625 
626 template <int start, int end, int incr>
627 struct UnrolledConjunction {
628  static __attribute__((always_inline)) bool Body(const function<bool(const int)>& func) {
629  return func(start) && UnrolledConjunction<start + incr, end, incr>::Body(func); // unroll next iteration
630  }
631  static __attribute__((always_inline)) bool BodyContextCheck(ContextHandle_t* __restrict__ prevIP) {
632  return (prevIP[0] == prevIP[start]) && UnrolledConjunction<start + incr, end, incr>::BodyContextCheck(prevIP); // unroll next iteration
633  }
634 };
635 
636 template <int end, int incr>
637 struct UnrolledConjunction<end, end, incr> {
638  static __attribute__((always_inline)) bool Body(const function<void(const int)>& func) {
639  return true;
640  }
641  static __attribute__((always_inline)) bool BodyContextCheck(ContextHandle_t* __restrict__ prevIP) {
642  return true;
643  }
644 };
645 
646 
647 template <class T, uint32_t AccessLen, bool isApprox>
648 struct RedSpyAnalysis {
649  static __attribute__((always_inline)) bool IsReadRedundant(void* addr, uint8_t* prev) {
650  if (isApprox) {
651  if (AccessLen >= 32) {
652  if (sizeof(T) == 4) {
653  __m256 oldValue = _mm256_loadu_ps(reinterpret_cast<const float*>(prev));
654  __m256 newValue = _mm256_loadu_ps(reinterpret_cast<const float*>(addr));
655 
656  __m256 result = _mm256_sub_ps(newValue, oldValue);
657 
658  result = _mm256_div_ps(result, oldValue);
659  float rates[8] __attribute__((aligned(32)));
660  _mm256_store_ps(rates, result);
661 
662  _mm256_storeu_ps(reinterpret_cast<float*>(prev), newValue);
663 
664  for (int i = 0; i < 8; ++i) {
665  if (rates[i] < -delta || rates[i] > delta) {
666  return false;
667  }
668  }
669  return true;
670 
671  } else if (sizeof(T) == 8) {
672  __m256d oldValue = _mm256_loadu_pd(reinterpret_cast<const double*>(prev));
673  __m256d newValue = _mm256_loadu_pd(reinterpret_cast<const double*>(addr));
674 
675  __m256d result = _mm256_sub_pd(newValue, oldValue);
676 
677  result = _mm256_div_pd(result, oldValue);
678 
679  double rates[4] __attribute__((aligned(32)));
680  _mm256_store_pd(rates, result);
681 
682  _mm256_storeu_pd(reinterpret_cast<double*>(prev), newValue);
683 
684  for (int i = 0; i < 4; ++i) {
685  if (rates[i] < -delta || rates[i] > delta) {
686  return false;
687  }
688  }
689  return true;
690  }
691  } else if (AccessLen == 16) {
692  if (sizeof(T) == 4) {
693  __m128 oldValue = _mm_loadu_ps(reinterpret_cast<const float*>(prev));
694  __m128 newValue = _mm_loadu_ps(reinterpret_cast<const float*>(addr));
695 
696  __m128 result = _mm_sub_ps(newValue, oldValue);
697 
698  result = _mm_div_ps(result, oldValue);
699  float rates[4] __attribute__((aligned(16)));
700  _mm_store_ps(rates, result);
701 
702  _mm_storeu_ps(reinterpret_cast<float*>(prev), newValue);
703 
704  for (int i = 0; i < 4; ++i) {
705  if (rates[i] < -delta || rates[i] > delta) {
706  return false;
707  }
708  }
709  return true;
710 
711  } else if (sizeof(T) == 8) {
712  __m128d oldValue = _mm_loadu_pd(reinterpret_cast<const double*>(prev));
713  __m128d newValue = _mm_loadu_pd(reinterpret_cast<const double*>(addr));
714 
715  __m128d result = _mm_sub_pd(newValue, oldValue);
716 
717  result = _mm_div_pd(result, oldValue);
718 
719  double rate[2];
720  _mm_storel_pd(&rate[0], result);
721  _mm_storeh_pd(&rate[1], result);
722 
723  _mm_storeu_pd(reinterpret_cast<double*>(prev), newValue);
724 
725  if (rate[0] < -delta || rate[0] > delta)
726  return false;
727  if (rate[1] < -delta || rate[1] > delta)
728  return false;
729  return true;
730  }
731  } else if (AccessLen == 10) {
732  UINT8 newValue[10];
733  memcpy(newValue, addr, AccessLen);
734 
735  uint64_t* upperOld = (uint64_t*)&(prev[2]);
736  uint64_t* upperNew = (uint64_t*)&(newValue[2]);
737 
738  uint16_t* lowOld = (uint16_t*)&(prev[0]);
739  uint16_t* lowNew = (uint16_t*)&(newValue[0]);
740 
741  memcpy(prev, addr, AccessLen);
742 
743  return (*lowOld & 0xfff0) == (*lowNew & 0xfff0) && *upperNew == *upperOld;
744  } else {
745  T newValue = *(static_cast<T*>(addr));
746  T oldValue = *((T*)(prev));
747 
748  *((T*)(prev)) = *(static_cast<T*>(addr));
749 
750  T rate = (newValue - oldValue) / oldValue;
751  return static_cast<bool>(rate <= delta && rate >= -delta);
752  }
753  } else {
754  bool isRed = (*((T*)(prev)) == *(static_cast<T*>(addr)));
755  *((T*)(prev)) = *(static_cast<T*>(addr));
756  return isRed;
757  }
758  return false;
759  }
760 
761  static __attribute__((always_inline)) VOID CheckNByteValueAfterRead(void* addr, uint32_t opaqueHandle, THREADID threadId) {
762  RedSpyThreadData* const tData = ClientGetTLS(threadId);
763 
764  ContextHandle_t curCtxtHandle = GetContextHandle(threadId, opaqueHandle);
765  tuple<uint8_t[SHADOW_PAGE_SIZE], ContextHandle_t[SHADOW_PAGE_SIZE]>& t = sm.GetOrCreateShadowBaseAddress((uint64_t)addr);
766  ContextHandle_t* __restrict__ prevIP = &(get<1>(t)[PAGE_OFFSET((uint64_t)addr)]);
767  uint8_t* prevValue = &(get<0>(t)[PAGE_OFFSET((uint64_t)addr)]);
768 
769  bool isRedundantRead = IsReadRedundant(addr, prevValue);
770 
771  const bool isAccessWithinPageBoundary = IS_ACCESS_WITHIN_PAGE_BOUNDARY((uint64_t)addr, AccessLen);
772  if (isRedundantRead) {
773  // detected redundancy
774  if (isAccessWithinPageBoundary) {
775  // All from same ctxt?
777  // report in RedTable
778  if (isApprox)
779  AddToApproximateRedTable(MAKE_CONTEXT_PAIR(prevIP[0], curCtxtHandle), AccessLen, threadId);
780  else
781  AddToRedTable(MAKE_CONTEXT_PAIR(prevIP[0], curCtxtHandle), AccessLen, threadId);
782  // Update context
783  UnrolledLoop<0, AccessLen, 1, false, /* redundancy is updated outside*/ isApprox>::BodySamePage(prevIP, curCtxtHandle, threadId);
784  } else {
785  // different contexts
786  UnrolledLoop<0, AccessLen, 1, true, /* redundancy is updated inside*/ isApprox>::BodySamePage(prevIP, curCtxtHandle, threadId);
787  }
788  } else {
789  // Read across a 64-K page boundary
790  // First byte is on this page though
791  if (isApprox)
792  AddToApproximateRedTable(MAKE_CONTEXT_PAIR(prevIP[0], curCtxtHandle), 1, threadId);
793  else
794  AddToRedTable(MAKE_CONTEXT_PAIR(prevIP[0], curCtxtHandle), 1, threadId);
795  // Update context
796  prevIP[0] = curCtxtHandle;
797 
798  // Remaining bytes [1..AccessLen] somewhere will across a 64-K page boundary
799  UnrolledLoop<1, AccessLen, 1, true, /* update redundancy */ isApprox>::BodyStraddlePage((uint64_t)addr, curCtxtHandle, threadId);
800  }
801  } else {
802  // No redundancy.
803  // Just update contexts
804  if (isAccessWithinPageBoundary) {
805  // Update context
806  UnrolledLoop<0, AccessLen, 1, false, /* not redundant*/ isApprox>::BodySamePage(prevIP, curCtxtHandle, threadId);
807  } else {
808  // Read across a 64-K page boundary
809  // Update context
810  prevIP[0] = curCtxtHandle;
811 
812  // Remaining bytes [1..AccessLen] somewhere will across a 64-K page boundary
813  UnrolledLoop<1, AccessLen, 1, false, /* not redundant*/ isApprox>::BodyStraddlePage((uint64_t)addr, curCtxtHandle, threadId);
814  }
815  }
816  }
817 };
818 
819 
820 static inline VOID CheckAfterLargeRead(void* addr, UINT32 accessLen, uint32_t opaqueHandle, THREADID threadId) {
821  ContextHandle_t curCtxtHandle = GetContextHandle(threadId, opaqueHandle);
822 
823  tuple<uint8_t[SHADOW_PAGE_SIZE], ContextHandle_t[SHADOW_PAGE_SIZE]>& t = sm.GetOrCreateShadowBaseAddress((uint64_t)addr);
824  ContextHandle_t* __restrict__ prevIP = &(get<1>(t)[PAGE_OFFSET((uint64_t)addr)]);
825  uint8_t* prevValue = &(get<0>(t)[PAGE_OFFSET((uint64_t)addr)]);
826 
827  // This assumes that a large read cannot straddle a page boundary -- strong assumption, but lets go with it for now.
828  tuple<uint8_t[SHADOW_PAGE_SIZE], ContextHandle_t[SHADOW_PAGE_SIZE]>& tt = sm.GetOrCreateShadowBaseAddress((uint64_t)addr);
829  if (memcmp(prevValue, addr, accessLen) == 0) {
830  // redundant
831  for (UINT32 index = 0; index < accessLen; index++) {
832  // report in RedTable
833  AddToRedTable(MAKE_CONTEXT_PAIR(prevIP[index], curCtxtHandle), 1, threadId);
834  // Update context
835  prevIP[index] = curCtxtHandle;
836  }
837  } else {
838  // Not redundant
839  for (UINT32 index = 0; index < accessLen; index++) {
840  // Update context
841  prevIP[index] = curCtxtHandle;
842  }
843  }
844 
845  memcpy(prevValue, addr, accessLen);
846 }
847 
848 #ifdef ENABLE_SAMPLING
849 
850 #define HANDLE_CASE(T, ACCESS_LEN, IS_APPROX) \
851  INS_InsertIfPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)IfEnableSample, IARG_THREAD_ID, IARG_END); \
852  INS_InsertThenPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)RedSpyAnalysis<T, (ACCESS_LEN), (IS_APPROX)>::CheckNByteValueAfterRead, IARG_MEMORYOP_EA, memOp, IARG_UINT32, opaqueHandle, IARG_THREAD_ID, IARG_INST_PTR, IARG_END)
853 
854 #define HANDLE_LARGE() \
855  INS_InsertIfPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)IfEnableSample, IARG_THREAD_ID, IARG_END); \
856  INS_InsertThenPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)CheckAfterLargeRead, IARG_MEMORYOP_EA, memOp, IARG_MEMORYREAD_SIZE, IARG_UINT32, opaqueHandle, IARG_THREAD_ID, IARG_END)
857 
858 #else
859 
860 #define HANDLE_CASE(T, ACCESS_LEN, IS_APPROX) \
861  INS_InsertPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)RedSpyAnalysis<T, (ACCESS_LEN), (IS_APPROX)>::CheckNByteValueAfterRead, IARG_MEMORYOP_EA, memOp, IARG_UINT32, opaqueHandle, IARG_THREAD_ID, IARG_INST_PTR, IARG_END)
862 
863 #define HANDLE_LARGE() \
864  INS_InsertPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)CheckAfterLargeRead, IARG_MEMORYOP_EA, memOp, IARG_MEMORYREAD_SIZE, IARG_UINT32, opaqueHandle, IARG_THREAD_ID, IARG_END)
865 
866 #endif
867 
868 
869 static int GetNumReadOperandsInIns(INS ins, UINT32& whichOp) {
870  int numReadOps = 0;
871  UINT32 memOperands = INS_MemoryOperandCount(ins);
872  for (UINT32 memOp = 0; memOp < memOperands; memOp++) {
873  if (INS_MemoryOperandIsRead(ins, memOp)) {
874  numReadOps++;
875  whichOp = memOp;
876  }
877  }
878  return numReadOps;
879 }
880 
881 
883  static __attribute__((always_inline)) void InstrumentReadValueBeforeAndAfterLoading(INS ins, UINT32 memOp, uint32_t opaqueHandle) {
884  UINT32 refSize = INS_MemoryOperandSize(ins, memOp);
885 
886  if (IsFloatInstructionAndOkToApproximate(INS_Address(ins))) {
887  unsigned int operSize = FloatOperandSize(INS_Address(ins), INS_MemoryOperandIndexToOperandIndex(ins, memOp));
888  switch (refSize) {
889  case 1:
890  case 2:
891  assert(0 && "memory read floating data with unexptected small size");
892  case 4:
893  HANDLE_CASE(float, 4, true);
894  break;
895  case 8:
896  HANDLE_CASE(double, 8, true);
897  break;
898  case 10:
899  HANDLE_CASE(uint8_t, 10, true);
900  break;
901  case 16: {
902  switch (operSize) {
903  case 4:
904  HANDLE_CASE(float, 16, true);
905  break;
906  case 8:
907  HANDLE_CASE(double, 16, true);
908  break;
909  default:
910  assert(0 && "handle large mem read with unexpected operand size\n");
911  break;
912  }
913  } break;
914  case 32: {
915  switch (operSize) {
916  case 4:
917  HANDLE_CASE(float, 32, true);
918  break;
919  case 8:
920  HANDLE_CASE(double, 32, true);
921  break;
922  default:
923  assert(0 && "handle large mem read with unexpected operand size\n");
924  break;
925  }
926  } break;
927  default:
928  assert(0 && "unexpected large memory read\n");
929  break;
930  }
931  } else {
932  switch (refSize) {
933  case 1:
934  HANDLE_CASE(uint8_t, 1, false);
935  break;
936  case 2:
937  HANDLE_CASE(uint16_t, 2, false);
938  break;
939  case 4:
940  HANDLE_CASE(uint32_t, 4, false);
941  break;
942  case 8:
943  HANDLE_CASE(uint64_t, 8, false);
944  break;
945 
946  default: {
947  HANDLE_LARGE();
948  }
949  }
950  }
951  }
952 };
953 
954 /********************* instrument analysis ************************/
955 
956 static inline bool INS_IsIgnorable(INS ins) {
957  if (INS_IsFarJump(ins) || INS_IsDirectFarJump(ins) || INS_IsMaskedJump(ins))
958  return true;
959  if (INS_IsRet(ins) || INS_IsIRet(ins))
960  return true;
961  if (INS_IsCall(ins) || INS_IsSyscall(ins))
962  return true;
963  if (INS_IsBranch(ins) || INS_IsRDTSC(ins) || INS_IsNop(ins))
964  return true;
965  if (INS_IsPrefetch(ins)) // Prefetch instructions might access addresses which are invalid.
966  return true;
967  // XSAVEC and XRSTOR are problematic since its access length is variable.
968  // 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.
969  // It fails with "Cannot use IARG_MEMORYWRITE_SIZE on non-standard memory access of instruction at 0xfoo: xsavec ptr [rsp]" error.
970  // A correct solution should use INS_hasKnownMemorySize() which is not available in Pin 2.14.
971  if (INS_Mnemonic(ins) == "XSAVEC")
972  return true;
973  if (INS_Mnemonic(ins) == "XSAVE")
974  return true;
975  if (INS_Mnemonic(ins) == "XRSTOR")
976  return true;
977  return false;
978 }
979 
980 static VOID InstrumentInsCallback(INS ins, VOID* v, const uint32_t opaqueHandle) {
981  if (!INS_HasFallThrough(ins))
982  return;
983  if (INS_IsIgnorable(ins))
984  return;
985  if (INS_IsControlFlow(ins) || INS_IsRet(ins))
986  return;
987 
988  //Instrument memory reads to find redundancy
989  // Special case, if we have only one read operand
990  UINT32 whichOp = 0;
991  if (GetNumReadOperandsInIns(ins, whichOp) == 1) {
992  // Read the value at location before and after the instruction
993  LoadSpyInstrument::InstrumentReadValueBeforeAndAfterLoading(ins, whichOp, opaqueHandle);
994  } else {
995  UINT32 memOperands = INS_MemoryOperandCount(ins);
996  for (UINT32 memOp = 0; memOp < memOperands; memOp++) {
997  if (!INS_MemoryOperandIsRead(ins, memOp))
998  continue;
999  LoadSpyInstrument::InstrumentReadValueBeforeAndAfterLoading(ins, memOp, opaqueHandle);
1000  }
1001  }
1002 }
1003 
1004 /**********************************************************************************/
1005 
1006 #ifdef ENABLE_SAMPLING
1007 
1008 inline VOID UpdateAndCheck(uint32_t count, uint32_t bytes, THREADID threadId) {
1009  RedSpyThreadData* const tData = ClientGetTLS(threadId);
1010 
1011  if (tData->sampleFlag) {
1012  tData->numIns += count;
1013  if (tData->numIns > WINDOW_ENABLE) {
1014  tData->sampleFlag = false;
1015  tData->numIns = 0;
1016  }
1017  } else {
1018  tData->numIns += count;
1019  if (tData->numIns > WINDOW_DISABLE) {
1020  tData->sampleFlag = true;
1021  tData->numIns = 0;
1022  }
1023  }
1024  if (tData->sampleFlag) {
1025  tData->bytesLoad += bytes;
1026  }
1027 }
1028 
1029 inline VOID Update(uint32_t count, uint32_t bytes, THREADID threadId) {
1030  RedSpyThreadData* const tData = ClientGetTLS(threadId);
1031  tData->numIns += count;
1032  if (tData->sampleFlag) {
1033  tData->bytesLoad += bytes;
1034  }
1035 }
1036 
1037 //instrument the trace, count the number of ins in the trace, decide to instrument or not
1038 static void InstrumentTrace(TRACE trace, void* f) {
1039  bool check = false;
1040  for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) {
1041  uint32_t totInsInBbl = BBL_NumIns(bbl);
1042  uint32_t totBytes = 0;
1043  for (INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins = INS_Next(ins)) {
1044  if (!INS_HasFallThrough(ins))
1045  continue;
1046  if (INS_IsIgnorable(ins))
1047  continue;
1048  if (INS_IsControlFlow(ins) || INS_IsRet(ins))
1049  continue;
1050 
1051  if (INS_IsMemoryRead(ins)) {
1052  totBytes += INS_MemoryReadSize(ins);
1053  }
1054  }
1055 
1056  if (BBL_InsTail(bbl) == BBL_InsHead(bbl)) {
1057  BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)UpdateAndCheck, IARG_UINT32, totInsInBbl, IARG_UINT32, totBytes, IARG_THREAD_ID, IARG_CALL_ORDER, CALL_ORDER_FIRST, IARG_END);
1058  } else if (INS_IsIndirectBranchOrCall(BBL_InsTail(bbl))) {
1059  BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)UpdateAndCheck, IARG_UINT32, totInsInBbl, IARG_UINT32, totBytes, IARG_THREAD_ID, IARG_CALL_ORDER, CALL_ORDER_FIRST, IARG_END);
1060  } else {
1061  if (check) {
1062  BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)UpdateAndCheck, IARG_UINT32, totInsInBbl, IARG_UINT32, totBytes, IARG_THREAD_ID, IARG_CALL_ORDER, CALL_ORDER_FIRST, IARG_END);
1063  check = false;
1064  } else {
1065  BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)Update, IARG_UINT32, totInsInBbl, IARG_UINT32, totBytes, IARG_THREAD_ID, IARG_CALL_ORDER, CALL_ORDER_FIRST, IARG_END);
1066  check = true;
1067  }
1068  }
1069  }
1070 }
1071 
1072 #else
1073 
1074 inline VOID Update(uint32_t bytes, THREADID threadId) {
1075  RedSpyThreadData* const tData = ClientGetTLS(threadId);
1076  tData->bytesLoad += bytes;
1077 }
1078 
1079 //instrument the trace, count the number of ins in the trace, decide to instrument or not
1080 static void InstrumentTrace(TRACE trace, void* f) {
1081  for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) {
1082  uint32_t totBytes = 0;
1083  for (INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins = INS_Next(ins)) {
1084  if (!INS_HasFallThrough(ins))
1085  continue;
1086  if (INS_IsIgnorable(ins))
1087  continue;
1088  if (INS_IsControlFlow(ins) || INS_IsRet(ins))
1089  continue;
1090 
1091  if (INS_IsMemoryRead(ins)) {
1092  totBytes += INS_MemoryReadSize(ins);
1093  }
1094  }
1095  BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)Update, IARG_UINT32, totBytes, IARG_THREAD_ID, IARG_END);
1096  }
1097 }
1098 
1099 #endif
1100 
1104  uint64_t frequency;
1105 };
1106 
1107 static inline bool RedundacyCompare(const struct RedundacyData& first, const struct RedundacyData& second) {
1108  return first.frequency > second.frequency;
1109 }
1110 
1111 static void PrintRedundancyPairs(THREADID threadId) {
1112  vector<RedundacyData> tmpList;
1113  vector<RedundacyData>::iterator tmpIt;
1114 
1115  uint64_t grandTotalRedundantBytes = 0;
1116  fprintf(gTraceFile, "*************** Dump Data from Thread %d ****************\n", threadId);
1117 
1118 #ifdef MERGING
1119  for (unordered_map<uint64_t, uint64_t>::iterator it = RedMap[threadId].begin(); it != RedMap[threadId].end(); ++it) {
1120  ContextHandle_t dead = DECODE_DEAD((*it).first);
1121  ContextHandle_t kill = DECODE_KILL((*it).first);
1122 
1123  for (tmpIt = tmpList.begin(); tmpIt != tmpList.end(); ++tmpIt) {
1124  if (dead == 0 || ((*tmpIt).dead) == 0) {
1125  continue;
1126  }
1127  if (!HaveSameCallerPrefix(dead, (*tmpIt).dead)) {
1128  continue;
1129  }
1130  if (!HaveSameCallerPrefix(kill, (*tmpIt).kill)) {
1131  continue;
1132  }
1133  bool ct1 = IsSameSourceLine(dead, (*tmpIt).dead);
1134  bool ct2 = IsSameSourceLine(kill, (*tmpIt).kill);
1135  if (ct1 && ct2) {
1136  (*tmpIt).frequency += (*it).second;
1137  grandTotalRedundantBytes += (*it).second;
1138  break;
1139  }
1140  }
1141  if (tmpIt == tmpList.end()) {
1142  RedundacyData tmp = {dead, kill, (*it).second};
1143  tmpList.push_back(tmp);
1144  grandTotalRedundantBytes += tmp.frequency;
1145  }
1146  }
1147 #else
1148  for (unordered_map<uint64_t, uint64_t>::iterator it = RedMap[threadId].begin(); it != RedMap[threadId].end(); ++it) {
1149  RedundacyData tmp = {DECODE_DEAD((*it).first), DECODE_KILL((*it).first), (*it).second};
1150  tmpList.push_back(tmp);
1151  grandTotalRedundantBytes += tmp.frequency;
1152  }
1153 #endif
1154 
1155  __sync_fetch_and_add(&grandTotBytesRedLoad, grandTotalRedundantBytes);
1156 
1157  fprintf(gTraceFile, "\n Total redundant bytes = %f %%\n", grandTotalRedundantBytes * 100.0 / ClientGetTLS(threadId)->bytesLoad);
1158 
1159  sort(tmpList.begin(), tmpList.end(), RedundacyCompare);
1160  int cntxtNum = 0;
1161  for (vector<RedundacyData>::iterator listIt = tmpList.begin(); listIt != tmpList.end(); ++listIt) {
1162  if (cntxtNum < MAX_REDUNDANT_CONTEXTS_TO_LOG) {
1163  fprintf(gTraceFile, "\n======= (%f) %% ======\n", (*listIt).frequency * 100.0 / grandTotalRedundantBytes);
1164  if ((*listIt).dead == 0) {
1165  fprintf(gTraceFile, "\n Prepopulated with by OS\n");
1166  } else {
1167  PrintFullCallingContext((*listIt).dead);
1168  }
1169  fprintf(gTraceFile, "\n---------------------Redundant load with---------------------------\n");
1170  PrintFullCallingContext((*listIt).kill);
1171  } else {
1172  break;
1173  }
1174  cntxtNum++;
1175  }
1176 }
1177 
1178 static void PrintApproximationRedundancyPairs(THREADID threadId) {
1179  vector<RedundacyData> tmpList;
1180  vector<RedundacyData>::iterator tmpIt;
1181 
1182  uint64_t grandTotalRedundantBytes = 0;
1183  fprintf(gTraceFile, "*************** Dump Data(delta=%.2f%%) from Thread %d ****************\n", delta * 100, threadId);
1184 
1185 #ifdef MERGING
1186  for (unordered_map<uint64_t, uint64_t>::iterator it = ApproxRedMap[threadId].begin(); it != ApproxRedMap[threadId].end(); ++it) {
1187  ContextHandle_t dead = DECODE_DEAD((*it).first);
1188  ContextHandle_t kill = DECODE_KILL((*it).first);
1189 
1190  for (tmpIt = tmpList.begin(); tmpIt != tmpList.end(); ++tmpIt) {
1191  if (dead == 0 || ((*tmpIt).dead) == 0) {
1192  continue;
1193  }
1194  if (!HaveSameCallerPrefix(dead, (*tmpIt).dead)) {
1195  continue;
1196  }
1197  if (!HaveSameCallerPrefix(kill, (*tmpIt).kill)) {
1198  continue;
1199  }
1200  bool ct1 = IsSameSourceLine(dead, (*tmpIt).dead);
1201  bool ct2 = IsSameSourceLine(kill, (*tmpIt).kill);
1202  if (ct1 && ct2) {
1203  (*tmpIt).frequency += (*it).second;
1204  grandTotalRedundantBytes += (*it).second;
1205  grandTotalRedundantIns += 1;
1206  break;
1207  }
1208  }
1209  if (tmpIt == tmpList.end()) {
1210  RedundacyData tmp = {dead, kill, (*it).second};
1211  tmpList.push_back(tmp);
1212  grandTotalRedundantBytes += tmp.frequency;
1213  }
1214  }
1215 #else
1216  for (unordered_map<uint64_t, uint64_t>::iterator it = ApproxRedMap[threadId].begin(); it != ApproxRedMap[threadId].end(); ++it) {
1217  RedundacyData tmp = {DECODE_DEAD((*it).first), DECODE_KILL((*it).first), (*it).second};
1218  tmpList.push_back(tmp);
1219  grandTotalRedundantBytes += tmp.frequency;
1220  }
1221 #endif
1222 
1223  __sync_fetch_and_add(&grandTotBytesApproxRedLoad, grandTotalRedundantBytes);
1224 
1225  fprintf(gTraceFile, "\n Total redundant bytes = %f %%\n", grandTotalRedundantBytes * 100.0 / ClientGetTLS(threadId)->bytesLoad);
1226 
1227  sort(tmpList.begin(), tmpList.end(), RedundacyCompare);
1228  int cntxtNum = 0;
1229  for (vector<RedundacyData>::iterator listIt = tmpList.begin(); listIt != tmpList.end(); ++listIt) {
1230  if (cntxtNum < MAX_REDUNDANT_CONTEXTS_TO_LOG) {
1231  fprintf(gTraceFile, "\n======= (%f) %% ======\n", (*listIt).frequency * 100.0 / grandTotalRedundantBytes);
1232  if ((*listIt).dead == 0) {
1233  fprintf(gTraceFile, "\n Prepopulated with by OS\n");
1234  } else {
1235  PrintFullCallingContext((*listIt).dead);
1236  }
1237  fprintf(gTraceFile, "\n---------------------Redundant load with---------------------------\n");
1238  PrintFullCallingContext((*listIt).kill);
1239  } else {
1240  break;
1241  }
1242  cntxtNum++;
1243  }
1244 }
1245 
1246 static void HPCRunRedundancyPairs(THREADID threadId) {
1247  vector<RedundacyData> tmpList;
1248  vector<RedundacyData>::iterator tmpIt;
1249 
1250  for (unordered_map<uint64_t, uint64_t>::iterator it = RedMap[threadId].begin(); it != RedMap[threadId].end(); ++it) {
1251  RedundacyData tmp = {DECODE_DEAD((*it).first), DECODE_KILL((*it).first), (*it).second};
1252  tmpList.push_back(tmp);
1253  }
1254 
1255  sort(tmpList.begin(), tmpList.end(), RedundacyCompare);
1256  vector<HPCRunCCT_t*> HPCRunNodes;
1257  int cntxtNum = 0;
1258  for (vector<RedundacyData>::iterator listIt = tmpList.begin(); listIt != tmpList.end(); ++listIt) {
1259  if (cntxtNum < MAX_REDUNDANT_CONTEXTS_TO_LOG) {
1260  HPCRunCCT_t* HPCRunNode = new HPCRunCCT_t();
1261  HPCRunNode->ctxtHandle1 = (*listIt).dead;
1262  HPCRunNode->ctxtHandle2 = (*listIt).kill;
1263  HPCRunNode->metric = (*listIt).frequency;
1264  HPCRunNode->metric_id = redload_metric_id;
1265  HPCRunNodes.push_back(HPCRunNode);
1266  } else {
1267  break;
1268  }
1269  cntxtNum++;
1270  }
1271  newCCT_hpcrun_build_cct(HPCRunNodes, threadId);
1272 }
1273 
1274 static void HPCRunApproxRedundancyPairs(THREADID threadId) {
1275  vector<RedundacyData> tmpList;
1276  vector<RedundacyData>::iterator tmpIt;
1277 
1278  for (unordered_map<uint64_t, uint64_t>::iterator it = ApproxRedMap[threadId].begin(); it != ApproxRedMap[threadId].end(); ++it) {
1279  RedundacyData tmp = {DECODE_DEAD((*it).first), DECODE_KILL((*it).first), (*it).second};
1280  tmpList.push_back(tmp);
1281  }
1282 
1283  sort(tmpList.begin(), tmpList.end(), RedundacyCompare);
1284  vector<HPCRunCCT_t*> HPCRunNodes;
1285  int cntxtNum = 0;
1286  for (vector<RedundacyData>::iterator listIt = tmpList.begin(); listIt != tmpList.end(); ++listIt) {
1287  if (cntxtNum < MAX_REDUNDANT_CONTEXTS_TO_LOG) {
1288  HPCRunCCT_t* HPCRunNode = new HPCRunCCT_t();
1289  HPCRunNode->ctxtHandle1 = (*listIt).dead;
1290  HPCRunNode->ctxtHandle2 = (*listIt).kill;
1291  HPCRunNode->metric = (*listIt).frequency;
1292  HPCRunNode->metric_id = redload_approx_metric_id;
1293  HPCRunNodes.push_back(HPCRunNode);
1294  } else {
1295  break;
1296  }
1297  cntxtNum++;
1298  }
1299  newCCT_hpcrun_build_cct(HPCRunNodes, threadId);
1300 }
1301 
1302 // On each Unload of a loaded image, the accummulated redundancy information is dumped
1303 static VOID ImageUnload(IMG img, VOID* v) {
1304  fprintf(gTraceFile, "\n TODO .. Multi-threading is not well supported.");
1305  THREADID threadid = PIN_ThreadId();
1306  fprintf(gTraceFile, "\nUnloading %s", IMG_Name(img).c_str());
1307  if (RedMap[threadid].empty() && ApproxRedMap[threadid].empty())
1308  return;
1309  // Update gTotalInstCount first
1310  PIN_LockClient();
1311  PrintRedundancyPairs(threadid);
1313  PIN_UnlockClient();
1314  // clear redmap now
1315  RedMap[threadid].clear();
1316  ApproxRedMap[threadid].clear();
1317 }
1318 
1319 static VOID ThreadFiniFunc(THREADID threadid, const CONTEXT* ctxt, INT32 code, VOID* v) {
1320  __sync_fetch_and_add(&grandTotBytesLoad, ClientGetTLS(threadid)->bytesLoad);
1321 
1322  // output the CCT for hpcviewer format
1323  HPCRunRedundancyPairs(threadid);
1324  HPCRunApproxRedundancyPairs(threadid);
1326 }
1327 
1328 static VOID FiniFunc(INT32 code, VOID* v) {
1329  // do whatever you want to the full CCT with footpirnt
1330  uint64_t redReadTmp = 0;
1331  uint64_t approxRedReadTmp = 0;
1332  for (int i = 0; i < THREAD_MAX; ++i) {
1333  unordered_map<uint64_t, uint64_t>::iterator it;
1334  if (!RedMap[i].empty()) {
1335  for (it = RedMap[i].begin(); it != RedMap[i].end(); ++it) {
1336  redReadTmp += (*it).second;
1337  }
1338  }
1339  if (!ApproxRedMap[i].empty()) {
1340  for (it = ApproxRedMap[i].begin(); it != ApproxRedMap[i].end(); ++it) {
1341  approxRedReadTmp += (*it).second;
1342  }
1343  }
1344  }
1345  grandTotBytesRedLoad += redReadTmp;
1346  grandTotBytesApproxRedLoad += approxRedReadTmp;
1347 
1348  fprintf(gTraceFile, "\n#Redundant Read:");
1349  fprintf(gTraceFile, "\nTotalBytesLoad: %lu \n", grandTotBytesLoad);
1350  fprintf(gTraceFile, "\nRedundantBytesLoad: %lu %.2f\n", grandTotBytesRedLoad, grandTotBytesRedLoad * 100.0 / grandTotBytesLoad);
1351  fprintf(gTraceFile, "\nApproxRedundantBytesLoad: %lu %.2f\n", grandTotBytesApproxRedLoad, grandTotBytesApproxRedLoad * 100.0 / grandTotBytesLoad);
1352 }
1353 
1354 static void InitThreadData(RedSpyThreadData* tdata) {
1355  tdata->bytesLoad = 0;
1356  tdata->sampleFlag = true;
1357  tdata->numIns = 0;
1358  /* for (int i = 0; i < THREAD_MAX; ++i) {
1359  RedMap[i].set_empty_key(0);
1360  ApproxRedMap[i].set_empty_key(0);
1361  }
1362 */
1363 }
1364 
1365 static VOID ThreadStart(THREADID threadid, CONTEXT* ctxt, INT32 flags, VOID* v) {
1366  RedSpyThreadData* tdata = (RedSpyThreadData*)memalign(32, sizeof(RedSpyThreadData));
1367  InitThreadData(tdata);
1368  // __sync_fetch_and_add(&gClientNumThreads, 1);
1369 #ifdef MULTI_THREADED
1370  PIN_SetThreadData(client_tls_key, tdata, threadid);
1371 #else
1372  gSingleThreadedTData = tdata;
1373 #endif
1374 }
1375 
1376 // user-defined function for metric computation
1377 // hpcviewer can only show the numbers for the metric
1378 uint64_t computeMetricVal(void* metric) {
1379  if (!metric)
1380  return 0;
1381  return (uint64_t)metric;
1382 }
1383 
1384 int main(int argc, char* argv[]) {
1385  // Initialize PIN
1386  if (PIN_Init(argc, argv))
1387  return Usage();
1388 
1389  // Initialize Symbols, we need them to report functions and lines
1390  PIN_InitSymbols();
1391 
1392  // Init Client
1393  ClientInit(argc, argv);
1394  // Intialize CCTLib
1396 
1397  // Init hpcrun format output
1398  init_hpcrun_format(argc, argv, nullptr, nullptr, false);
1399  // Create new metrics
1401  redload_approx_metric_id = hpcrun_create_metric("RED_LOAD_APPROX");
1402 
1403  // Obtain a key for TLS storage.
1404  client_tls_key = PIN_CreateThreadDataKey(nullptr /*TODO have a destructir*/);
1405  // Register ThreadStart to be called when a thread starts.
1406  PIN_AddThreadStartFunction(ThreadStart, nullptr);
1407 
1408 
1409  // fini function for post-mortem analysis
1410  PIN_AddThreadFiniFunction(ThreadFiniFunc, nullptr);
1411  PIN_AddFiniFunction(FiniFunc, nullptr);
1412 
1413  TRACE_AddInstrumentFunction(InstrumentTrace, nullptr);
1414 
1415  // Register ImageUnload to be called when an image is unloaded
1416  IMG_AddUnloadFunction(ImageUnload, nullptr);
1417 
1418  // Launch program now
1419  PIN_StartProgram();
1420  return 0;
1421 }
#define MAX_FILE_PATH
Definition: cctlib.H:18
#define INTERESTING_INS_ALL
Definition: cctlib.H:85
uint8_t ** gL1PageTable[LEVEL_1_PAGE_TABLE_SIZE]
uint8_t * GetOrCreateShadowBaseAddress(void *address)
uint64_t computeMetricVal(void *metric)
static void ClientInit(int argc, char *argv[])
int main(int argc, char *argv[])
#define HANDLE_LARGE()
static FILE * gTraceFile
uint64_t grandTotBytesRedLoad
xed_state_t xedState
#define MAX_REDUNDANT_CONTEXTS_TO_LOG
static int GetNumReadOperandsInIns(INS ins, UINT32 &whichOp)
static VOID ImageUnload(IMG img, VOID *v)
#define HANDLE_CASE(T, ACCESS_LEN, IS_APPROX)
static RedSpyThreadData * gSingleThreadedTData
#define EIGHT_BYTE_WRITE_ACTION
static void AddToRedTable(uint64_t key, uint16_t value, THREADID threadId) __attribute__((always_inline
static void PrintRedundancyPairs(THREADID threadId)
char dummy2[128]
static const uint64_t READ_ACCESS_STATES[]
#define THREAD_MAX
#define ONE_BYTE_READ_ACTION
static bool RedundacyCompare(const struct RedundacyData &first, const struct RedundacyData &second)
int redload_approx_metric_id
static INT32 Usage()
static bool IsFloatInstructionOld(ADDRINT ip)
#define EIGHT_BYTE_READ_ACTION
static unordered_map< uint64_t, uint64_t > ApproxRedMap[THREAD_MAX]
#define TWO_BYTE_READ_ACTION
static void HPCRunApproxRedundancyPairs(THREADID threadId)
static uint16_t FloatOperandSize(ADDRINT ip, uint32_t oper)
static const uint8_t OVERFLOW_CHECK[]
static void HPCRunRedundancyPairs(THREADID threadId)
#define MAKE_CONTEXT_PAIR(a, b)
VOID Update(uint32_t bytes, THREADID threadId)
#define DECODE_DEAD(data)
static VOID InstrumentInsCallback(INS ins, VOID *v, const uint32_t opaqueHandle)
static void InstrumentTrace(TRACE trace, void *f)
static void InitThreadData(RedSpyThreadData *tdata)
static bool INS_IsIgnorable(INS ins)
RedSpyThreadData * ClientGetTLS(const THREADID threadId)
static void AddToApproximateRedTable(uint64_t key, uint16_t value, THREADID threadId) __attribute__((always_inline
#define delta
static bool IsFloatInstructionAndOkToApproximate(ADDRINT ip)
static VOID FiniFunc(INT32 code, VOID *v)
#define TWO_BYTE_WRITE_ACTION
static const uint64_t WRITE_ACCESS_STATES[]
static VOID CheckAfterLargeRead(void *addr, UINT32 accessLen, uint32_t opaqueHandle, THREADID threadId)
static void flatten
uint64_t grandTotBytesLoad
#define FOUR_BYTE_READ_ACTION
static void PrintApproximationRedundancyPairs(THREADID threadId)
#define IS_ACCESS_WITHIN_PAGE_BOUNDARY(accessAddr, accessLen)
uint64_t grandTotBytesApproxRedLoad
static VOID ThreadFiniFunc(THREADID threadid, const CONTEXT *ctxt, INT32 code, VOID *v)
static bool IsOkToApproximate(xed_decoded_inst_t &xedd)
char dummy1[128]
#define ONE_BYTE_WRITE_ACTION
#define FOUR_BYTE_WRITE_ACTION
struct @12 LoadSpyGlobals
#define DECODE_KILL(data)
static TLS_KEY client_tls_key
static VOID ThreadStart(THREADID threadid, CONTEXT *ctxt, INT32 flags, VOID *v)
int redload_metric_id
ConcurrentShadowMemory< uint8_t, ContextHandle_t > sm
static unordered_map< uint64_t, uint64_t > RedMap[THREAD_MAX]
uint64_t metric
Definition: cctlib.H:60
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 HaveSameCallerPrefix(ContextHandle_t ctxt1, ContextHandle_t ctxt2)
Definition: cctlib.cpp:3241
bool IsSameSourceLine(ContextHandle_t ctxt1, ContextHandle_t ctxt2)
Definition: cctlib.cpp:3255
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
static USIZE INS_MemoryReadSize(INS ins)
static BOOL INS_IsMaskedJump(INS ins)
static BOOL INS_IsIndirectBranchOrCall(INS ins)
#define WINDOW_ENABLE
#define WINDOW_DISABLE
struct RedSpyThreadData __attribute__
uint8_t value[MAX_WRITE_OP_LENGTH]
void * address
#define LEVEL_2_PAGE_TABLE_SIZE
Definition: shadow_memory.H:27
#define LEVEL_1_PAGE_TABLE_SLOT(addr)
Definition: shadow_memory.H:29
#define PAGE_OFFSET(addr)
Definition: shadow_memory.H:15
#define SHADOW_PAGE_SIZE
Definition: shadow_memory.H:17
#define LEVEL_1_PAGE_TABLE_SIZE
Definition: shadow_memory.H:23
#define LEVEL_2_PAGE_TABLE_SLOT(addr)
Definition: shadow_memory.H:30
static __attribute__((always_inline)) void InstrumentReadValueBeforeAndAfterLoading(INS ins
static __attribute__((always_inline)) bool IsReadRedundant(void *addr
static __attribute__((always_inline)) VOID CheckNByteValueAfterRead(void *addr
ContextHandle_t kill
ContextHandle_t dead
static __attribute__((always_inline)) bool BodyContextCheck(ContextHandle_t *__restrict__ prevIP)
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 __attribute__((always_inline)) bool BodyContextCheck(ContextHandle_t *__restrict__ prevIP)
static bool Body(const function< bool(const int)> &func)
static __attribute__((always_inline)) void BodySamePage(ContextHandle_t *__restrict__ prevIP
static __attribute__((always_inline)) void Body(const function< void(const int)> &func)
static __attribute__((always_inline)) void BodyStraddlePage(uint64_t addr
static void Body(const function< void(const int)> &func)
static __attribute__((always_inline)) void BodySamePage(ContextHandle_t *__restrict__ prevIP
static __attribute__((always_inline)) void Body(const function< void(const int)> &func)
void f()
Definition: test1.c:23