gem5 v24.0.0.0
Loading...
Searching...
No Matches
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
34using namespace gem5;
35
36struct 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 {
75 "Range [%#x,%#x) outside of [%#x,%#x).",
76 addr, addr + size, base, base + store.size());
77 }
78
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
141accessed(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
154std::ostream &
155operator << (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.
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
184template <typename T>
186
187template <typename T>
189
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
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
235TEST(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
297TEST(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
380TEST(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
469{
470 using UintPtr = uint64_t;
471 using State = int;
472};
473
474namespace gem5
475{
476
477namespace guest_abi
478{
479
480template <>
482{
483 static Addr
485 {
486 return 0x1000;
487 }
488};
489
490} // namespace guest_abi
491} // namespace gem5
492
493bool abiCalled = false;
494bool abiCalledConst = false;
495
496void
498{
499 abiCalled = true;
500 EXPECT_EQ(ptr.addr(), 0x1000);
501}
502
503void
505{
506 abiCalledConst = true;
507 EXPECT_EQ(ptr.addr(), 0x1000);
508}
509
510TEST(ProxyPtrTest, GuestABI)
511{
512 BackingStore store(0x1000, 0x1000);
513
514 EXPECT_FALSE(abiCalled);
515 EXPECT_FALSE(abiCalledConst);
516
518
519 EXPECT_TRUE(abiCalled);
520 EXPECT_FALSE(abiCalledConst);
521
523
524 EXPECT_TRUE(abiCalled);
525 EXPECT_TRUE(abiCalledConst);
526}
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:214
atomic_var_t state
Definition helpers.cc:211
Bitfield< 7 > b
Bitfield< 29 > c
Definition misc_types.hh:53
Bitfield< 8 > a
Definition misc_types.hh:66
Bitfield< 9 > d
Definition misc_types.hh:64
Bitfield< 17 > os
Definition misc.hh:838
Bitfield< 3 > addr
Definition types.hh:84
Copyright (c) 2024 - Pranith Kumar Copyright (c) 2020 Inria All rights reserved.
Definition binary32.hh:36
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition types.hh:147
Ret invokeSimcall(ThreadContext *tc, std::function< Ret(ThreadContext *, Args...)> target)
Definition guest_abi.hh:50
static std::ostream & operator<<(std::ostream &os, const DummyMatRegContainer &d)
Definition matrix.hh:564
void ccprintf(cp::Print &print)
Definition cprintf.hh:130
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,...)
bool operator==(const Access &other) const
bool operator!=(const Access &other) const
Access(bool _read, Addr _addr, Addr _size)
uint64_t UintPtr
static Addr get(ThreadContext *tc, typename TestABI::State &state)

Generated on Tue Jun 18 2024 16:24:06 for gem5 by doxygen 1.11.0