gem5  v22.1.0.0
proxy_ptr.test.cc
Go to the documentation of this file.
1 /*
2  * Copyright 2020 Google, Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met: redistributions of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer;
8  * redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution;
11  * neither the name of the copyright holders nor the names of its
12  * contributors may be used to endorse or promote products derived from
13  * this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <gtest/gtest.h>
29 
30 #include <vector>
31 
32 #include "sim/proxy_ptr.hh"
33 
34 using namespace gem5;
35 
36 struct Access
37 {
38  bool read;
41 
42  Access(bool _read, Addr _addr, Addr _size) :
43  read(_read), addr(_addr), size(_size)
44  {}
45 
46  bool
47  operator == (const Access &other) const
48  {
49  return read == other.read &&
50  addr == other.addr &&
51  size == other.size;
52  }
53 
54  bool
55  operator != (const Access &other) const
56  {
57  return !(*this == other);
58  }
59 };
60 
62 
64 {
65  public:
68 
69  BackingStore(Addr _base, size_t _size) : store(_size, 0), base(_base) {}
70 
71  void
73  {
74  panic_if(addr < base || addr + size > base + store.size(),
75  "Range [%#x,%#x) outside of [%#x,%#x).",
76  addr, addr + size, base, base + store.size());
77  }
78 
79  mutable Accesses accesses;
80 
81  ::testing::AssertionResult
82  expect_access(size_t idx, const Access &other) const
83  {
84  if (idx >= accesses.size()) {
85  return ::testing::AssertionFailure() << "index " << idx <<
86  " out of bounds";
87  }
88 
89  if (accesses[idx] != other) {
90  return ::testing::AssertionFailure() << "access[" << idx <<
91  "] was " << accesses[idx] << ", expected " << other;
92  }
93  return ::testing::AssertionSuccess();
94  }
95 
96  ::testing::AssertionResult
98  {
99  if (accesses.size() != expected.size()) {
100  return ::testing::AssertionFailure() <<
101  "Wrong number of accesses, was " << accesses.size() <<
102  " expected " << expected.size();
103  }
104 
105  auto failure = ::testing::AssertionFailure();
106  bool success = true;
107  if (accesses.size() == expected.size()) {
108  for (size_t idx = 0; idx < expected.size(); idx++) {
109  auto result = expect_access(idx, expected[idx]);
110  if (!result) {
111  failure << result.message();
112  success = false;
113  }
114  }
115  }
116 
117  if (!success)
118  return failure;
119  else
120  return ::testing::AssertionSuccess();
121  }
122 
123  void
124  writeBlob(Addr ptr, const void *data, int size)
125  {
126  rangeCheck(ptr, size);
127  accesses.emplace_back(false, ptr, size);
128  memcpy(store.data() + (ptr - base), data, size);
129  }
130 
131  void
132  readBlob(Addr ptr, void *data, int size)
133  {
134  rangeCheck(ptr, size);
135  accesses.emplace_back(true, ptr, size);
136  memcpy(data, store.data() + (ptr - base), size);
137  }
138 };
139 
140 ::testing::AssertionResult
141 accessed(const char *expr1, const char *expr2,
142  const BackingStore &store, const Accesses &expected)
143 {
144  return store.expect_accesses(expected);
145 }
146 
147 #define EXPECT_ACCESSES(store, ...) \
148  do { \
149  Accesses expected({__VA_ARGS__}); \
150  EXPECT_PRED_FORMAT2(accessed, store, expected); \
151  store.accesses.clear(); \
152  } while (false)
153 
154 std::ostream &
155 operator << (std::ostream &os, const Access &access)
156 {
157  ccprintf(os, "%s(%#x, %d)", access.read ? "read" : "write",
158  access.addr, access.size);
159  return os;
160 }
161 
163 {
164  public:
166 
167  TestProxy(BackingStore &_store) : store(_store) {}
168  // Sneaky constructor for testing guest_abi integration.
169  TestProxy(ThreadContext *tc) : store(*(BackingStore *)tc) {}
170 
171  void
172  writeBlob(Addr ptr, const void *data, int size)
173  {
174  store.writeBlob(ptr, data, size);
175  }
176 
177  void
178  readBlob(Addr ptr, void *data, int size)
179  {
180  store.readBlob(ptr, data, size);
181  }
182 };
183 
184 template <typename T>
186 
187 template <typename T>
189 
190 TEST(ProxyPtr, Clean)
191 {
192  BackingStore store(0x1000, 0x1000);
193 
194  EXPECT_ACCESSES(store);
195 
196  {
197  ConstTestPtr<uint32_t> test_ptr(0x1100, store);
198 
199  EXPECT_ACCESSES(store, { true, test_ptr.addr(), sizeof(uint32_t) });
200  }
201 
202  EXPECT_ACCESSES(store);
203 
204  {
205  TestPtr<uint32_t> test_ptr(0x1100, store);
206 
207  EXPECT_ACCESSES(store, { true, test_ptr.addr(), sizeof(uint32_t) });
208  }
209 
210  EXPECT_ACCESSES(store);
211 }
212 
213 TEST(ProxyPtr, Dirty)
214 {
215  BackingStore store(0x1000, 0x1100);
216 
217  EXPECT_ACCESSES(store);
218 
219  {
220  TestPtr<uint32_t> test_ptr(0x1100, store);
221 
222  *test_ptr = 0xa5a5a5a5;
223 
224  EXPECT_ACCESSES(store, { true, test_ptr.addr(), sizeof(uint32_t) });
225  }
226 
227  EXPECT_ACCESSES(store, { false, 0x1100, sizeof(uint32_t) });
228  EXPECT_EQ(store.store[0x100], 0xa5);
229  EXPECT_EQ(store.store[0x101], 0xa5);
230  EXPECT_EQ(store.store[0x102], 0xa5);
231  EXPECT_EQ(store.store[0x103], 0xa5);
232 }
233 
234 
235 TEST(ProxyPtr, LoadAndFlush)
236 {
237  BackingStore store(0x1000, 0x1100);
238 
239  store.store[0x100] = 0xa5;
240  store.store[0x101] = 0xa5;
241  store.store[0x102] = 0xa5;
242  store.store[0x103] = 0xa5;
243 
244  TestPtr<uint32_t> test_ptr(0x1100, store);
245 
246  // Check that the backing store is unmodified.
247  EXPECT_EQ(store.store[0x100], 0xa5);
248  EXPECT_EQ(store.store[0x101], 0xa5);
249  EXPECT_EQ(store.store[0x102], 0xa5);
250  EXPECT_EQ(store.store[0x103], 0xa5);
251 
252  // Change the value in our local buffered copy.
253  *test_ptr = 0x5a5a5a5a;
254 
255  // Verify that the backing store hasn't been changed.
256  EXPECT_EQ(store.store[0x100], 0xa5);
257  EXPECT_EQ(store.store[0x101], 0xa5);
258  EXPECT_EQ(store.store[0x102], 0xa5);
259  EXPECT_EQ(store.store[0x103], 0xa5);
260 
261  // Flush out our modifications.
262  test_ptr.flush();
263 
264  // Verify that they've been written back to the store.
265  EXPECT_EQ(store.store[0x100], 0x5a);
266  EXPECT_EQ(store.store[0x101], 0x5a);
267  EXPECT_EQ(store.store[0x102], 0x5a);
268  EXPECT_EQ(store.store[0x103], 0x5a);
269 
270  // Update the store and try to flush again.
271  store.store[0x100] = 0xaa;
272  test_ptr.flush();
273 
274  // Verify that no flush happened, since our ptr was "clean".
275  EXPECT_EQ(store.store[0x100], 0xaa);
276 
277  // Force a flush.
278  test_ptr.flush(true);
279 
280  // Verify that the flush happened even though the ptr was "clean".
281  EXPECT_EQ(store.store[0x100], 0x5a);
282 
283  // Update the store.
284  store.store[0x100] = 0xa5;
285  store.store[0x101] = 0xa5;
286  store.store[0x102] = 0xa5;
287  store.store[0x103] = 0xa5;
288 
289  // Verify that our local copy hasn't changed.
290  EXPECT_EQ(*(const uint32_t *)test_ptr, 0x5a5a5a5a);
291 
292  // Reload the pointer from the store.
293  test_ptr.load();
294  EXPECT_EQ(*(const uint32_t *)test_ptr, 0xa5a5a5a5);
295 }
296 
297 TEST(ProxyPtr, ConstOperators)
298 {
299  bool is_same;
300 
301  BackingStore store(0x1000, 0x1000);
302 
303  const Addr addr1 = 0x1100;
304  const Addr addr2 = 0x1200;
305 
306  using PtrType = uint32_t;
307 
308  ConstTestPtr<PtrType> test_ptr1(addr1, store);
309  EXPECT_EQ(test_ptr1.addr(), addr1);
310 
311  ConstTestPtr<PtrType> test_ptr2(addr2, store);
312  EXPECT_EQ(test_ptr2.addr(), addr2);
313 
314  // Pointer +/- integer.
315  auto next_ptr = test_ptr1 + 2;
316  EXPECT_EQ(next_ptr.addr(), addr1 + 2 * sizeof(PtrType));
317 
318  auto reverse_next_ptr = 2 + test_ptr1;
319  EXPECT_EQ(reverse_next_ptr.addr(), addr1 + 2 * sizeof(PtrType));
320 
321  auto prev_ptr = test_ptr1 - 2;
322  EXPECT_EQ(prev_ptr.addr(), addr1 - 2 * sizeof(PtrType));
323 
324  // Pointer-pointer subtraction.
325  auto diff = test_ptr2 - test_ptr1;
326  EXPECT_EQ(diff, (addr2 - addr1) / sizeof(PtrType));
327 
328  // Assignment.
329  ConstTestPtr<PtrType> target(addr2, store);
330  EXPECT_EQ(target.addr(), addr2);
331 
332  target = test_ptr1;
333  EXPECT_EQ(target.addr(), addr1);
334 
335  // Conversions.
336  EXPECT_TRUE(test_ptr1);
337  ConstTestPtr<PtrType> null(0, store);
338  EXPECT_FALSE(null);
339 
340  EXPECT_NE((const PtrType *)test_ptr1, nullptr);
341  EXPECT_EQ((const PtrType *)null, nullptr);
342 
343  // Dereferences.
344  is_same = std::is_same_v<decltype(*test_ptr1), const PtrType &>;
345  EXPECT_TRUE(is_same);
346 
347  store.store[0x100] = 0x55;
348  store.store[0x101] = 0x55;
349  store.store[0x102] = 0x55;
350  store.store[0x103] = 0x55;
351 
352  // Force an update since we changed the backing store behind our ptrs back.
353  test_ptr1.load();
354 
355  EXPECT_EQ(*test_ptr1, 0x55555555);
356 
357  store.store[0x100] = 0x11;
358  store.store[0x101] = 0x22;
359  store.store[0x102] = 0x33;
360  store.store[0x103] = 0x44;
361 
362  struct TestStruct
363  {
364  uint8_t a;
365  uint8_t b;
366  uint8_t c;
367  uint8_t d;
368  };
369 
370  ConstTestPtr<TestStruct> struct_ptr(addr1, store);
371  EXPECT_EQ(struct_ptr->a, 0x11);
372  EXPECT_EQ(struct_ptr->b, 0x22);
373  EXPECT_EQ(struct_ptr->c, 0x33);
374  EXPECT_EQ(struct_ptr->d, 0x44);
375 
376  is_same = std::is_same_v<decltype((struct_ptr->a)), const uint8_t &>;
377  EXPECT_TRUE(is_same);
378 }
379 
380 TEST(ProxyPtr, NonConstOperators)
381 {
382  bool is_same;
383 
384  BackingStore store(0x1000, 0x1000);
385 
386  const Addr addr1 = 0x1100;
387  const Addr addr2 = 0x1200;
388 
389  using PtrType = uint32_t;
390 
391  TestPtr<PtrType> test_ptr1(addr1, store);
392  EXPECT_EQ(test_ptr1.addr(), addr1);
393 
394  TestPtr<PtrType> test_ptr2(addr2, store);
395  EXPECT_EQ(test_ptr2.addr(), addr2);
396 
397  // Pointer +/- integer.
398  auto next_ptr = test_ptr1 + 2;
399  EXPECT_EQ(next_ptr.addr(), addr1 + 2 * sizeof(PtrType));
400 
401  auto reverse_next_ptr = 2 + test_ptr1;
402  EXPECT_EQ(reverse_next_ptr.addr(), addr1 + 2 * sizeof(PtrType));
403 
404  auto prev_ptr = test_ptr1 - 2;
405  EXPECT_EQ(prev_ptr.addr(), addr1 - 2 * sizeof(PtrType));
406 
407  // Pointer-pointer subtraction.
408  auto diff = test_ptr2 - test_ptr1;
409  EXPECT_EQ(diff, (addr2 - addr1) / sizeof(PtrType));
410 
411  // Assignment.
412  TestPtr<PtrType> target(addr2, store);
413  EXPECT_EQ(target.addr(), addr2);
414 
415  target = test_ptr1;
416  EXPECT_EQ(target.addr(), addr1);
417 
418  // Conversions.
419  EXPECT_TRUE(test_ptr1);
420  TestPtr<PtrType> null(0, store);
421  EXPECT_FALSE(null);
422 
423  EXPECT_NE((PtrType *)test_ptr1, nullptr);
424  EXPECT_EQ((PtrType *)null, nullptr);
425  EXPECT_NE((const PtrType *)test_ptr1, nullptr);
426  EXPECT_EQ((const PtrType *)null, nullptr);
427 
428  // Dereferences.
429  is_same = std::is_same_v<decltype(*test_ptr1), PtrType &>;
430  EXPECT_TRUE(is_same);
431 
432  // Flush test_ptr1, which has been conservatively marked as dirty.
433  test_ptr1.flush();
434 
435  store.store[0x100] = 0x55;
436  store.store[0x101] = 0x55;
437  store.store[0x102] = 0x55;
438  store.store[0x103] = 0x55;
439 
440  // Force an update since we changed the backing store behind our ptrs back.
441  test_ptr1.load();
442 
443  EXPECT_EQ(*test_ptr1, 0x55555555);
444 
445  store.store[0x100] = 0x11;
446  store.store[0x101] = 0x22;
447  store.store[0x102] = 0x33;
448  store.store[0x103] = 0x44;
449 
450  struct TestStruct
451  {
452  uint8_t a;
453  uint8_t b;
454  uint8_t c;
455  uint8_t d;
456  };
457 
458  TestPtr<TestStruct> struct_ptr(addr1, store);
459  EXPECT_EQ(struct_ptr->a, 0x11);
460  EXPECT_EQ(struct_ptr->b, 0x22);
461  EXPECT_EQ(struct_ptr->c, 0x33);
462  EXPECT_EQ(struct_ptr->d, 0x44);
463 
464  is_same = std::is_same_v<decltype((struct_ptr->a)), uint8_t &>;
465  EXPECT_TRUE(is_same);
466 }
467 
468 struct TestABI
469 {
470  using UintPtr = uint64_t;
471  using State = int;
472 };
473 
474 namespace gem5
475 {
476 
477 GEM5_DEPRECATED_NAMESPACE(GuestABI, guest_abi);
478 namespace guest_abi
479 {
480 
481 template <>
483 {
484  static Addr
486  {
487  return 0x1000;
488  }
489 };
490 
491 } // namespace guest_abi
492 } // namespace gem5
493 
494 bool abiCalled = false;
495 bool abiCalledConst = false;
496 
497 void
499 {
500  abiCalled = true;
501  EXPECT_EQ(ptr.addr(), 0x1000);
502 }
503 
504 void
506 {
507  abiCalledConst = true;
508  EXPECT_EQ(ptr.addr(), 0x1000);
509 }
510 
511 TEST(ProxyPtrTest, GuestABI)
512 {
513  BackingStore store(0x1000, 0x1000);
514 
515  EXPECT_FALSE(abiCalled);
516  EXPECT_FALSE(abiCalledConst);
517 
518  invokeSimcall<TestABI>((ThreadContext *)&store, abiTestFunc);
519 
520  EXPECT_TRUE(abiCalled);
521  EXPECT_FALSE(abiCalledConst);
522 
523  invokeSimcall<TestABI>((ThreadContext *)&store, abiTestFuncConst);
524 
525  EXPECT_TRUE(abiCalled);
526  EXPECT_TRUE(abiCalledConst);
527 }
const char data[]
std::vector< uint8_t > store
::testing::AssertionResult expect_accesses(Accesses expected) const
BackingStore(Addr _base, size_t _size)
::testing::AssertionResult expect_access(size_t idx, const Access &other) const
void readBlob(Addr ptr, void *data, int size)
void rangeCheck(Addr addr, Addr size)
Accesses accesses
void writeBlob(Addr ptr, const void *data, int size)
void readBlob(Addr ptr, void *data, int size)
TestProxy(BackingStore &_store)
void writeBlob(Addr ptr, const void *data, int size)
TestProxy(ThreadContext *tc)
BackingStore & store
Addr addr() const
Definition: proxy_ptr.hh:173
void flush(bool force=false)
Definition: proxy_ptr.hh:265
ThreadContext is the external interface to all thread state for anything outside of the CPU.
std::vector< SwitchingFiber * > expected({ &a, &b, &a, &a, &a, &b, &c, &a, &c, &c, &c })
#define panic_if(cond,...)
Conditional panic macro that checks the supplied condition and only panics if the condition is true a...
Definition: logging.hh:204
atomic_var_t state
Definition: helpers.cc:188
Bitfield< 7 > b
Definition: misc_types.hh:388
Bitfield< 8 > a
Definition: misc_types.hh:66
Bitfield< 9 > d
Definition: misc_types.hh:64
Bitfield< 2 > c
Definition: pagetable.hh:63
Bitfield< 51, 12 > base
Definition: pagetable.hh:141
Bitfield< 17 > os
Definition: misc.hh:810
Bitfield< 3 > addr
Definition: types.hh:84
Reference material can be found at the JEDEC website: UFS standard http://www.jedec....
static bool operator==(const PCStateBase &a, const PCStateBase &b)
Definition: pcstate.hh:155
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition: types.hh:147
std::ostream & operator<<(std::ostream &os, const ArmSemihosting::InPlaceArg &ipa)
static bool operator!=(const PCStateBase &a, const PCStateBase &b)
Definition: pcstate.hh:161
void ccprintf(cp::Print &print)
Definition: cprintf.hh:130
GEM5_DEPRECATED_NAMESPACE(GuestABI, guest_abi)
void abiTestFunc(ThreadContext *tc, TestPtr< uint8_t > ptr)
void abiTestFuncConst(ThreadContext *tc, ConstTestPtr< uint8_t > ptr)
::testing::AssertionResult accessed(const char *expr1, const char *expr2, const BackingStore &store, const Accesses &expected)
TEST(ProxyPtr, Clean)
bool abiCalledConst
bool abiCalled
#define EXPECT_ACCESSES(store,...)
Access(bool _read, Addr _addr, Addr _size)
uint64_t UintPtr
static Addr get(ThreadContext *tc, typename TestABI::State &state)

Generated on Wed Dec 21 2022 10:22:40 for gem5 by doxygen 1.9.1