1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <stdint.h>
18 
19 #include <thread>
20 #include <vector>
21 
22 #include <gtest/gtest.h>
23 
24 #include "MemoryCache.h"
25 #include "MemoryFake.h"
26 
27 namespace unwindstack {
28 
29 class MemoryThreadCacheTest : public ::testing::Test {
30  protected:
SetUp()31   void SetUp() override {
32     memory_ = new MemoryFake;
33     memory_cache_.reset(new MemoryThreadCache(memory_));
34 
35     memory_->SetMemoryBlock(0x8000, 4096, 0xab);
36     memory_->SetMemoryBlock(0x9000, 4096, 0xde);
37     memory_->SetMemoryBlock(0xa000, 3000, 0x50);
38   }
39 
40   MemoryFake* memory_;
41   std::unique_ptr<MemoryThreadCache> memory_cache_;
42 
43   constexpr static size_t kMaxCachedSize = 64;
44 };
45 
TEST_F(MemoryThreadCacheTest,cached_read)46 TEST_F(MemoryThreadCacheTest, cached_read) {
47   for (size_t i = 1; i <= kMaxCachedSize; i++) {
48     std::vector<uint8_t> buffer(i);
49     ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
50         << "Read failed at size " << i;
51     ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
52   }
53 
54   // Verify the cached data is used.
55   memory_->SetMemoryBlock(0x8000, 4096, 0xff);
56   for (size_t i = 1; i <= kMaxCachedSize; i++) {
57     std::vector<uint8_t> buffer(i);
58     ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
59         << "Read failed at size " << i;
60     ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
61   }
62 }
63 
TEST_F(MemoryThreadCacheTest,no_cached_read_after_clear)64 TEST_F(MemoryThreadCacheTest, no_cached_read_after_clear) {
65   for (size_t i = 1; i <= kMaxCachedSize; i++) {
66     std::vector<uint8_t> buffer(i);
67     ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
68         << "Read failed at size " << i;
69     ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
70   }
71 
72   // Verify the cached data is not used after a reset.
73   memory_cache_->Clear();
74   memory_->SetMemoryBlock(0x8000, 4096, 0xff);
75   for (size_t i = 1; i <= kMaxCachedSize; i++) {
76     std::vector<uint8_t> buffer(i);
77     ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
78         << "Read failed at size " << i;
79     ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
80   }
81 }
82 
TEST_F(MemoryThreadCacheTest,cached_read_across_caches)83 TEST_F(MemoryThreadCacheTest, cached_read_across_caches) {
84   std::vector<uint8_t> expect(16, 0xab);
85   expect.resize(32, 0xde);
86 
87   std::vector<uint8_t> buffer(32);
88   ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
89   ASSERT_EQ(expect, buffer);
90 
91   // Verify the cached data is used.
92   memory_->SetMemoryBlock(0x8000, 4096, 0xff);
93   memory_->SetMemoryBlock(0x9000, 4096, 0xff);
94   ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
95   ASSERT_EQ(expect, buffer);
96 }
97 
TEST_F(MemoryThreadCacheTest,no_cache_read)98 TEST_F(MemoryThreadCacheTest, no_cache_read) {
99   for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
100     std::vector<uint8_t> buffer(i);
101     ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
102         << "Read failed at size " << i;
103     ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
104   }
105 
106   // Verify the cached data is not used.
107   memory_->SetMemoryBlock(0x8000, 4096, 0xff);
108   for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
109     std::vector<uint8_t> buffer(i);
110     ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
111         << "Read failed at size " << i;
112     ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
113   }
114 }
115 
TEST_F(MemoryThreadCacheTest,read_for_cache_fail)116 TEST_F(MemoryThreadCacheTest, read_for_cache_fail) {
117   std::vector<uint8_t> buffer(kMaxCachedSize);
118   ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
119   ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0x50), buffer);
120 
121   // Verify the cached data is not used.
122   memory_->SetMemoryBlock(0xa000, 3000, 0xff);
123   ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
124   ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0xff), buffer);
125 }
126 
TEST_F(MemoryThreadCacheTest,read_for_cache_fail_cross)127 TEST_F(MemoryThreadCacheTest, read_for_cache_fail_cross) {
128   std::vector<uint8_t> expect(16, 0xde);
129   expect.resize(32, 0x50);
130 
131   std::vector<uint8_t> buffer(32);
132   ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
133   ASSERT_EQ(expect, buffer);
134 
135   // Verify the cached data is not used for the second half but for the first.
136   memory_->SetMemoryBlock(0xa000, 3000, 0xff);
137   ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
138   expect.resize(16);
139   expect.resize(32, 0xff);
140   ASSERT_EQ(expect, buffer);
141 }
142 
TEST_F(MemoryThreadCacheTest,read_cached_in_thread)143 TEST_F(MemoryThreadCacheTest, read_cached_in_thread) {
144   // Read from a different thread than this one.
145   std::thread thread([this] {
146     for (size_t i = 1; i <= kMaxCachedSize; i++) {
147       std::vector<uint8_t> buffer(i);
148       ASSERT_TRUE(this->memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
149           << "Read failed at size " << i;
150       ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
151     }
152   });
153 
154   thread.join();
155 
156   // Now modify the backing data, and read from the main thread verifying
157   // it is not using cached data.
158   memory_->SetMemoryBlock(0x8000, 4096, 0xff);
159   for (size_t i = 1; i <= kMaxCachedSize; i++) {
160     std::vector<uint8_t> buffer(i);
161     ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
162         << "Read failed at size " << i;
163     ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
164   }
165 }
166 
TEST_F(MemoryThreadCacheTest,read_uncached_due_to_error)167 TEST_F(MemoryThreadCacheTest, read_uncached_due_to_error) {
168   // Use up all of the keys to force the next attempt to create one to fail.
169   static constexpr size_t kMaxKeysToCreate = 10000;
170   std::vector<pthread_key_t> keys(kMaxKeysToCreate);
171   for (size_t i = 0; i < kMaxKeysToCreate; i++) {
172     if (pthread_key_create(&keys[i], nullptr) != 0) {
173       keys.resize(i);
174       break;
175     }
176   }
177   ASSERT_NE(kMaxKeysToCreate, keys.size()) << "Cannot exist pthread keys.";
178 
179   MemoryFake* fake = new MemoryFake;
180   MemoryThreadCache memory(fake);
181   fake->SetMemoryBlock(0x8000, 4096, 0xad);
182 
183   // Read the data, which should be uncached.
184   uint8_t value;
185   ASSERT_TRUE(memory.ReadFully(0x8000, &value, 1));
186   ASSERT_EQ(0xad, value);
187   ASSERT_TRUE(memory.ReadFully(0x8001, &value, 1));
188   ASSERT_EQ(0xad, value);
189 
190   // Verify the previous read did not cache anything.
191   fake->SetMemoryBlock(0x8000, 4096, 0x12);
192   ASSERT_TRUE(memory.ReadFully(0x8000, &value, 1));
193   ASSERT_EQ(0x12, value);
194   ASSERT_TRUE(memory.ReadFully(0x8001, &value, 1));
195   ASSERT_EQ(0x12, value);
196 
197   for (pthread_key_t& key : keys) {
198     pthread_key_delete(key);
199   }
200 }
201 
202 }  // namespace unwindstack
203