CCTLib
Calling-context and data-object attribution library for Intel Pin
shadow_memory_test.cpp
Go to the documentation of this file.
1 // GoogleTest unit tests for src/shadow_memory.H.
2 //
3 // Migrated from the ad-hoc tests/shadow_memory_unittest.cpp (still kept as a
4 // fallback binary for build environments without gtest). Same coverage: the
5 // free-function GetOrCreateShadowAddress<I> and the base
6 // GetOrCreateShadowBaseAddress path, single- and multi-threaded, both
7 // ShadowMemory and ConcurrentShadowMemory. See the header comment on the
8 // legacy file for background on the `auto` vs `auto&` bug.
9 
10 #include <atomic>
11 #include <cstdint>
12 #include <thread>
13 #include <vector>
14 
15 #include <gtest/gtest.h>
16 
17 #include "shadow_memory.H"
18 
19 namespace {
20 
21 TEST(ShadowMemory, WriteReadSingle) {
23  const size_t addr = 0x1234'5678'9abc'0000ULL;
24  const uint64_t value = 0xdeadbeefcafef00dULL;
25 
26  *GetOrCreateShadowAddress<0>(sm, addr) = value;
27  EXPECT_EQ(value, *GetOrCreateShadowAddress<0>(sm, addr));
28 }
29 
30 TEST(ShadowMemory, MultipleAddressesSamePage) {
32  const size_t base = 0x1000'0000ULL;
33  for (int i = 0; i < 4096; ++i) {
34  *GetOrCreateShadowAddress<0>(sm, base + i * 8) = uint64_t{static_cast<uint64_t>(i)} * 7 + 3;
35  }
36  for (int i = 0; i < 4096; ++i) {
37  EXPECT_EQ(uint64_t{static_cast<uint64_t>(i)} * 7 + 3,
38  *GetOrCreateShadowAddress<0>(sm, base + i * 8))
39  << "at i=" << i;
40  }
41 }
42 
43 TEST(ShadowMemory, MultiplePages) {
45  const size_t addrs[] = {
46  0x0000'0000ULL,
47  0x0001'0000ULL, // next l2 slot
48  0x0000'1000'0000ULL, // next l1 slot
49  0x1234'5678'0000ULL,
50  0x7fff'ffff'0000ULL, // near top of 47-bit user space
51  };
52  const int n = sizeof(addrs) / sizeof(addrs[0]);
53  for (int i = 0; i < n; ++i) {
54  *GetOrCreateShadowAddress<0>(sm, addrs[i]) = 0xa5a5a5a5'00000000ULL | i;
55  }
56  for (int i = 0; i < n; ++i) {
57  EXPECT_EQ(0xa5a5a5a5'00000000ULL | i, *GetOrCreateShadowAddress<0>(sm, addrs[i]));
58  }
59 }
60 
61 TEST(ShadowMemory, MultiTypeTuple) {
63  const size_t addr = 0xcafeb0ba'0000ULL;
64  *GetOrCreateShadowAddress<0>(sm, addr) = 0x5a;
65  *GetOrCreateShadowAddress<1>(sm, addr) = 0xdeadbeef;
66  EXPECT_EQ(0x5a, *GetOrCreateShadowAddress<0>(sm, addr));
67  EXPECT_EQ(0xdeadbeefu, *GetOrCreateShadowAddress<1>(sm, addr));
68 }
69 
70 // Regression for the auto-copy bug: writing via the free function must be
71 // visible via the direct base function, and vice versa.
72 TEST(ShadowMemory, FreeFnMatchesBaseFn) {
74  const size_t addr = 0x0f0f'0000'1234ULL;
75  const uint64_t value = 0x1122334455667788ULL;
76 
77  *GetOrCreateShadowAddress<0>(sm, addr) = value;
78  auto& page = sm.GetOrCreateShadowBaseAddress(addr);
79  EXPECT_EQ(value, std::get<0>(page)[PAGE_OFFSET(addr)]);
80 }
81 
82 TEST(ShadowMemory, BaseFnMatchesFreeFn) {
84  const size_t addr = 0x0e0e'ffff'5678ULL;
85  const uint64_t value = 0x8899aabbccddeeffULL;
86 
87  auto& page = sm.GetOrCreateShadowBaseAddress(addr);
88  std::get<0>(page)[PAGE_OFFSET(addr)] = value;
89  EXPECT_EQ(value, *GetOrCreateShadowAddress<0>(sm, addr));
90 }
91 
92 TEST(ShadowMemory, PointerStable) {
94  const size_t addr = 0x2222'3333'4444ULL;
95  uint64_t* p1 = GetOrCreateShadowAddress<0>(sm, addr);
96  uint64_t* p2 = GetOrCreateShadowAddress<0>(sm, addr);
97  EXPECT_EQ(p1, p2);
98 }
99 
100 TEST(ShadowMemory, InitPatternDataCentricShape) {
102  const size_t base = 0x8000'0000ULL;
103  for (int i = 0; i < 256; ++i) {
104  *GetOrCreateShadowAddress<0>(sm, base + i * 8) = 0xf00d0000ULL + i;
105  }
106  for (int i = 0; i < 256; ++i) {
107  EXPECT_EQ(0xf00d0000ULL + i, *GetOrCreateShadowAddress<0>(sm, base + i * 8));
108  }
109 }
110 
111 TEST(ConcurrentShadowMemory, WriteReadSingle) {
113  const size_t addr = 0x1234'5678'9abc'0000ULL;
114  const uint64_t value = 0xdeadbeefcafef00dULL;
115  *GetOrCreateShadowAddress<0>(sm, addr) = value;
116  EXPECT_EQ(value, *GetOrCreateShadowAddress<0>(sm, addr));
117 }
118 
119 TEST(ConcurrentShadowMemory, FreeFnMatchesBaseFn) {
121  const size_t addr = 0x0f0f'0000'1234ULL;
122  const uint64_t value = 0x1122334455667788ULL;
123  *GetOrCreateShadowAddress<0>(sm, addr) = value;
124  auto& page = sm.GetOrCreateShadowBaseAddress(addr);
125  EXPECT_EQ(value, std::get<0>(page)[PAGE_OFFSET(addr)]);
126 }
127 
128 TEST(ConcurrentShadowMemory, MultiTypeTuple) {
130  const size_t addr = 0xcafeb0ba'0000ULL;
131  *GetOrCreateShadowAddress<0>(sm, addr) = 0x5a;
132  *GetOrCreateShadowAddress<1>(sm, addr) = 0xdeadbeef;
133  EXPECT_EQ(0x5a, *GetOrCreateShadowAddress<0>(sm, addr));
134  EXPECT_EQ(0xdeadbeefu, *GetOrCreateShadowAddress<1>(sm, addr));
135 }
136 
137 // N threads share the same L1 page-table slot (via stride within a single L1
138 // bucket) and race on L1 sub-page allocation. The free()->munmap() fix in
139 // ConcurrentShadowMemory keeps this from occasionally segfaulting under -O2.
140 TEST(ConcurrentShadowMemory, MultiThreadStress) {
142  const int nthreads = 8;
143  const int per_thread = 1024;
144  const size_t stride = 0x100'0000ULL;
145 
146  auto writer = [&](int tid) {
147  for (int i = 0; i < per_thread; ++i) {
148  size_t addr = (size_t)tid * stride + (size_t)i * 8;
149  *GetOrCreateShadowAddress<0>(sm, addr) = (uint64_t)tid * 1000000ULL + i;
150  }
151  };
152  std::vector<std::thread> ts;
153  ts.reserve(nthreads);
154  for (int t = 0; t < nthreads; ++t) ts.emplace_back(writer, t);
155  for (auto& th : ts) th.join();
156 
157  std::atomic<int> mismatches{0};
158  auto reader = [&](int tid) {
159  for (int i = 0; i < per_thread; ++i) {
160  size_t addr = (size_t)tid * stride + (size_t)i * 8;
161  if (*GetOrCreateShadowAddress<0>(sm, addr) != (uint64_t)tid * 1000000ULL + i) {
162  mismatches.fetch_add(1);
163  }
164  }
165  };
166  ts.clear();
167  for (int t = 0; t < nthreads; ++t) ts.emplace_back(reader, t);
168  for (auto& th : ts) th.join();
169 
170  EXPECT_EQ(0, mismatches.load());
171 }
172 
173 // Mirrors the exact layout cctlib.cpp's USE_SHADOW_FOR_DATA_CENTRIC path uses.
174 TEST(ConcurrentShadowMemory, DataHandleShape) {
175  struct FakeHandle { uint8_t type; uint32_t path; };
177  const size_t base = 0x7ff0'0000'0000ULL;
178  const int n = 128;
179  for (int i = 0; i < n; ++i) {
180  FakeHandle* slot = GetOrCreateShadowAddress<0>(sm, base + i);
181  slot->type = 2;
182  slot->path = 0xabc00000u + (uint32_t)i;
183  }
184  for (int i = 0; i < n; ++i) {
185  FakeHandle h = *GetOrCreateShadowAddress<0>(sm, base + i);
186  EXPECT_EQ(2, (int)h.type);
187  EXPECT_EQ(0xabc00000u + (uint32_t)i, h.path);
188  }
189 }
190 
191 } // namespace
static ConcurrentShadowMemory< DataHandle_t > sm
Definition: cctlib.cpp:2479
uint16_t size_t
Definition: cctlib.cpp:3359
TEST(ConcurrentShadowMemory, DataHandleShape)
uint8_t value[MAX_WRITE_OP_LENGTH]
#define PAGE_OFFSET(addr)
Definition: shadow_memory.H:15
void h()
Definition: test1.c:12