Loading libunwindstack/Android.bp +23 −0 Original line number Diff line number Diff line Loading @@ -184,6 +184,7 @@ cc_test { "tests/MapInfoGetLoadBiasTest.cpp", "tests/MapsTest.cpp", "tests/MemoryBufferTest.cpp", "tests/MemoryCacheTest.cpp", "tests/MemoryFake.cpp", "tests/MemoryFileTest.cpp", "tests/MemoryLocalTest.cpp", Loading Loading @@ -310,6 +311,28 @@ cc_binary { ], } //------------------------------------------------------------------------- // Benchmarks //------------------------------------------------------------------------- cc_benchmark { name: "unwind_benchmarks", host_supported: true, defaults: ["libunwindstack_flags"], // Disable optimizations so that all of the calls are not optimized away. cflags: [ "-O0", ], srcs: [ "benchmarks/unwind_benchmarks.cpp", ], shared_libs: [ "libunwindstack", ], } // Generates the elf data for use in the tests for .gnu_debugdata frames. // Once these files are generated, use the xz command to compress the data. cc_binary_host { Loading libunwindstack/Memory.cpp +53 −0 Original line number Diff line number Diff line Loading @@ -174,6 +174,13 @@ std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) { return std::shared_ptr<Memory>(new MemoryRemote(pid)); } std::shared_ptr<Memory> Memory::CreateProcessMemoryCached(pid_t pid) { if (pid == getpid()) { return std::shared_ptr<Memory>(new MemoryCache(new MemoryLocal())); } return std::shared_ptr<Memory>(new MemoryCache(new MemoryRemote(pid))); } size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) { if (addr >= raw_.size()) { return 0; Loading Loading @@ -398,4 +405,50 @@ size_t MemoryOfflineParts::Read(uint64_t addr, void* dst, size_t size) { return 0; } size_t MemoryCache::Read(uint64_t addr, void* dst, size_t size) { // Only bother caching and looking at the cache if this is a small read for now. if (size > 64) { return impl_->Read(addr, dst, size); } uint64_t addr_page = addr >> kCacheBits; auto entry = cache_.find(addr_page); uint8_t* cache_dst; if (entry != cache_.end()) { cache_dst = entry->second; } else { cache_dst = cache_[addr_page]; if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) { // Erase the entry. cache_.erase(addr_page); return impl_->Read(addr, dst, size); } } size_t max_read = ((addr_page + 1) << kCacheBits) - addr; if (size <= max_read) { memcpy(dst, &cache_dst[addr & kCacheMask], size); return size; } // The read crossed into another cached entry, since a read can only cross // into one extra cached page, duplicate the code rather than looping. memcpy(dst, &cache_dst[addr & kCacheMask], max_read); dst = &reinterpret_cast<uint8_t*>(dst)[max_read]; addr_page++; entry = cache_.find(addr_page); if (entry != cache_.end()) { cache_dst = entry->second; } else { cache_dst = cache_[addr_page]; if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) { // Erase the entry. cache_.erase(addr_page); return impl_->Read(addr_page << kCacheBits, dst, size - max_read) + max_read; } } memcpy(dst, cache_dst, size - max_read); return size; } } // namespace unwindstack libunwindstack/benchmarks/unwind_benchmarks.cpp 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdint.h> #include <memory> #include <benchmark/benchmark.h> #include <unwindstack/Maps.h> #include <unwindstack/Memory.h> #include <unwindstack/Regs.h> #include <unwindstack/RegsGetLocal.h> #include <unwindstack/Unwinder.h> size_t Call6(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal()); unwindstack::RegsGetLocal(regs.get()); unwindstack::Unwinder unwinder(32, maps, regs.get(), process_memory); unwinder.Unwind(); return unwinder.NumFrames(); } size_t Call5(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { return Call6(process_memory, maps); } size_t Call4(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { return Call5(process_memory, maps); } size_t Call3(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { return Call4(process_memory, maps); } size_t Call2(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { return Call3(process_memory, maps); } size_t Call1(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { return Call2(process_memory, maps); } static void BM_uncached_unwind(benchmark::State& state) { auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid()); unwindstack::LocalMaps maps; if (!maps.Parse()) { state.SkipWithError("Failed to parse local maps."); } for (auto _ : state) { benchmark::DoNotOptimize(Call1(process_memory, &maps)); } } BENCHMARK(BM_uncached_unwind); static void BM_cached_unwind(benchmark::State& state) { auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid()); unwindstack::LocalMaps maps; if (!maps.Parse()) { state.SkipWithError("Failed to parse local maps."); } for (auto _ : state) { benchmark::DoNotOptimize(Call1(process_memory, &maps)); } } BENCHMARK(BM_cached_unwind); BENCHMARK_MAIN(); libunwindstack/include/unwindstack/Memory.h +22 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <map> #include <memory> #include <string> #include <unordered_map> #include <vector> namespace unwindstack { Loading @@ -35,9 +36,12 @@ class Memory { virtual ~Memory() = default; static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid); static std::shared_ptr<Memory> CreateProcessMemoryCached(pid_t pid); virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX); virtual void Clear() {} virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0; bool ReadFully(uint64_t addr, void* dst, size_t size); Loading @@ -51,6 +55,24 @@ class Memory { } }; class MemoryCache : public Memory { public: MemoryCache(Memory* memory) : impl_(memory) {} virtual ~MemoryCache() = default; size_t Read(uint64_t addr, void* dst, size_t size) override; void Clear() override { cache_.clear(); } private: constexpr static size_t kCacheBits = 12; constexpr static size_t kCacheMask = (1 << kCacheBits) - 1; constexpr static size_t kCacheSize = 1 << kCacheBits; std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_; std::unique_ptr<Memory> impl_; }; class MemoryBuffer : public Memory { public: MemoryBuffer() = default; Loading libunwindstack/tests/MemoryCacheTest.cpp 0 → 100644 +143 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdint.h> #include <vector> #include <gtest/gtest.h> #include <unwindstack/Memory.h> #include "MemoryFake.h" namespace unwindstack { class MemoryCacheTest : public ::testing::Test { protected: void SetUp() override { memory_ = new MemoryFake; memory_cache_.reset(new MemoryCache(memory_)); memory_->SetMemoryBlock(0x8000, 4096, 0xab); memory_->SetMemoryBlock(0x9000, 4096, 0xde); memory_->SetMemoryBlock(0xa000, 3000, 0x50); } MemoryFake* memory_; std::unique_ptr<MemoryCache> memory_cache_; constexpr static size_t kMaxCachedSize = 64; }; TEST_F(MemoryCacheTest, cached_read) { for (size_t i = 1; i <= kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i; } // Verify the cached data is used. memory_->SetMemoryBlock(0x8000, 4096, 0xff); for (size_t i = 1; i <= kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i; } } TEST_F(MemoryCacheTest, no_cached_read_after_clear) { for (size_t i = 1; i <= kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i; } // Verify the cached data is not used after a reset. memory_cache_->Clear(); memory_->SetMemoryBlock(0x8000, 4096, 0xff); for (size_t i = 1; i <= kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i; } } TEST_F(MemoryCacheTest, cached_read_across_caches) { std::vector<uint8_t> expect(16, 0xab); expect.resize(32, 0xde); std::vector<uint8_t> buffer(32); ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32)); ASSERT_EQ(expect, buffer); // Verify the cached data is used. memory_->SetMemoryBlock(0x8000, 4096, 0xff); memory_->SetMemoryBlock(0x9000, 4096, 0xff); ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32)); ASSERT_EQ(expect, buffer); } TEST_F(MemoryCacheTest, no_cache_read) { for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i; } // Verify the cached data is not used. memory_->SetMemoryBlock(0x8000, 4096, 0xff); for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i; } } TEST_F(MemoryCacheTest, read_for_cache_fail) { std::vector<uint8_t> buffer(kMaxCachedSize); ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize)); ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0x50), buffer); // Verify the cached data is not used. memory_->SetMemoryBlock(0xa000, 3000, 0xff); ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize)); ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0xff), buffer); } TEST_F(MemoryCacheTest, read_for_cache_fail_cross) { std::vector<uint8_t> expect(16, 0xde); expect.resize(32, 0x50); std::vector<uint8_t> buffer(32); ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32)); ASSERT_EQ(expect, buffer); // Verify the cached data is not used for the second half but for the first. memory_->SetMemoryBlock(0xa000, 3000, 0xff); ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32)); expect.resize(16); expect.resize(32, 0xff); ASSERT_EQ(expect, buffer); } } // namespace unwindstack Loading
libunwindstack/Android.bp +23 −0 Original line number Diff line number Diff line Loading @@ -184,6 +184,7 @@ cc_test { "tests/MapInfoGetLoadBiasTest.cpp", "tests/MapsTest.cpp", "tests/MemoryBufferTest.cpp", "tests/MemoryCacheTest.cpp", "tests/MemoryFake.cpp", "tests/MemoryFileTest.cpp", "tests/MemoryLocalTest.cpp", Loading Loading @@ -310,6 +311,28 @@ cc_binary { ], } //------------------------------------------------------------------------- // Benchmarks //------------------------------------------------------------------------- cc_benchmark { name: "unwind_benchmarks", host_supported: true, defaults: ["libunwindstack_flags"], // Disable optimizations so that all of the calls are not optimized away. cflags: [ "-O0", ], srcs: [ "benchmarks/unwind_benchmarks.cpp", ], shared_libs: [ "libunwindstack", ], } // Generates the elf data for use in the tests for .gnu_debugdata frames. // Once these files are generated, use the xz command to compress the data. cc_binary_host { Loading
libunwindstack/Memory.cpp +53 −0 Original line number Diff line number Diff line Loading @@ -174,6 +174,13 @@ std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) { return std::shared_ptr<Memory>(new MemoryRemote(pid)); } std::shared_ptr<Memory> Memory::CreateProcessMemoryCached(pid_t pid) { if (pid == getpid()) { return std::shared_ptr<Memory>(new MemoryCache(new MemoryLocal())); } return std::shared_ptr<Memory>(new MemoryCache(new MemoryRemote(pid))); } size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) { if (addr >= raw_.size()) { return 0; Loading Loading @@ -398,4 +405,50 @@ size_t MemoryOfflineParts::Read(uint64_t addr, void* dst, size_t size) { return 0; } size_t MemoryCache::Read(uint64_t addr, void* dst, size_t size) { // Only bother caching and looking at the cache if this is a small read for now. if (size > 64) { return impl_->Read(addr, dst, size); } uint64_t addr_page = addr >> kCacheBits; auto entry = cache_.find(addr_page); uint8_t* cache_dst; if (entry != cache_.end()) { cache_dst = entry->second; } else { cache_dst = cache_[addr_page]; if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) { // Erase the entry. cache_.erase(addr_page); return impl_->Read(addr, dst, size); } } size_t max_read = ((addr_page + 1) << kCacheBits) - addr; if (size <= max_read) { memcpy(dst, &cache_dst[addr & kCacheMask], size); return size; } // The read crossed into another cached entry, since a read can only cross // into one extra cached page, duplicate the code rather than looping. memcpy(dst, &cache_dst[addr & kCacheMask], max_read); dst = &reinterpret_cast<uint8_t*>(dst)[max_read]; addr_page++; entry = cache_.find(addr_page); if (entry != cache_.end()) { cache_dst = entry->second; } else { cache_dst = cache_[addr_page]; if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) { // Erase the entry. cache_.erase(addr_page); return impl_->Read(addr_page << kCacheBits, dst, size - max_read) + max_read; } } memcpy(dst, cache_dst, size - max_read); return size; } } // namespace unwindstack
libunwindstack/benchmarks/unwind_benchmarks.cpp 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdint.h> #include <memory> #include <benchmark/benchmark.h> #include <unwindstack/Maps.h> #include <unwindstack/Memory.h> #include <unwindstack/Regs.h> #include <unwindstack/RegsGetLocal.h> #include <unwindstack/Unwinder.h> size_t Call6(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal()); unwindstack::RegsGetLocal(regs.get()); unwindstack::Unwinder unwinder(32, maps, regs.get(), process_memory); unwinder.Unwind(); return unwinder.NumFrames(); } size_t Call5(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { return Call6(process_memory, maps); } size_t Call4(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { return Call5(process_memory, maps); } size_t Call3(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { return Call4(process_memory, maps); } size_t Call2(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { return Call3(process_memory, maps); } size_t Call1(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) { return Call2(process_memory, maps); } static void BM_uncached_unwind(benchmark::State& state) { auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid()); unwindstack::LocalMaps maps; if (!maps.Parse()) { state.SkipWithError("Failed to parse local maps."); } for (auto _ : state) { benchmark::DoNotOptimize(Call1(process_memory, &maps)); } } BENCHMARK(BM_uncached_unwind); static void BM_cached_unwind(benchmark::State& state) { auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid()); unwindstack::LocalMaps maps; if (!maps.Parse()) { state.SkipWithError("Failed to parse local maps."); } for (auto _ : state) { benchmark::DoNotOptimize(Call1(process_memory, &maps)); } } BENCHMARK(BM_cached_unwind); BENCHMARK_MAIN();
libunwindstack/include/unwindstack/Memory.h +22 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <map> #include <memory> #include <string> #include <unordered_map> #include <vector> namespace unwindstack { Loading @@ -35,9 +36,12 @@ class Memory { virtual ~Memory() = default; static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid); static std::shared_ptr<Memory> CreateProcessMemoryCached(pid_t pid); virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX); virtual void Clear() {} virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0; bool ReadFully(uint64_t addr, void* dst, size_t size); Loading @@ -51,6 +55,24 @@ class Memory { } }; class MemoryCache : public Memory { public: MemoryCache(Memory* memory) : impl_(memory) {} virtual ~MemoryCache() = default; size_t Read(uint64_t addr, void* dst, size_t size) override; void Clear() override { cache_.clear(); } private: constexpr static size_t kCacheBits = 12; constexpr static size_t kCacheMask = (1 << kCacheBits) - 1; constexpr static size_t kCacheSize = 1 << kCacheBits; std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_; std::unique_ptr<Memory> impl_; }; class MemoryBuffer : public Memory { public: MemoryBuffer() = default; Loading
libunwindstack/tests/MemoryCacheTest.cpp 0 → 100644 +143 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdint.h> #include <vector> #include <gtest/gtest.h> #include <unwindstack/Memory.h> #include "MemoryFake.h" namespace unwindstack { class MemoryCacheTest : public ::testing::Test { protected: void SetUp() override { memory_ = new MemoryFake; memory_cache_.reset(new MemoryCache(memory_)); memory_->SetMemoryBlock(0x8000, 4096, 0xab); memory_->SetMemoryBlock(0x9000, 4096, 0xde); memory_->SetMemoryBlock(0xa000, 3000, 0x50); } MemoryFake* memory_; std::unique_ptr<MemoryCache> memory_cache_; constexpr static size_t kMaxCachedSize = 64; }; TEST_F(MemoryCacheTest, cached_read) { for (size_t i = 1; i <= kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i; } // Verify the cached data is used. memory_->SetMemoryBlock(0x8000, 4096, 0xff); for (size_t i = 1; i <= kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i; } } TEST_F(MemoryCacheTest, no_cached_read_after_clear) { for (size_t i = 1; i <= kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i; } // Verify the cached data is not used after a reset. memory_cache_->Clear(); memory_->SetMemoryBlock(0x8000, 4096, 0xff); for (size_t i = 1; i <= kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i; } } TEST_F(MemoryCacheTest, cached_read_across_caches) { std::vector<uint8_t> expect(16, 0xab); expect.resize(32, 0xde); std::vector<uint8_t> buffer(32); ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32)); ASSERT_EQ(expect, buffer); // Verify the cached data is used. memory_->SetMemoryBlock(0x8000, 4096, 0xff); memory_->SetMemoryBlock(0x9000, 4096, 0xff); ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32)); ASSERT_EQ(expect, buffer); } TEST_F(MemoryCacheTest, no_cache_read) { for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i; } // Verify the cached data is not used. memory_->SetMemoryBlock(0x8000, 4096, 0xff); for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) { std::vector<uint8_t> buffer(i); ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i)) << "Read failed at size " << i; ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i; } } TEST_F(MemoryCacheTest, read_for_cache_fail) { std::vector<uint8_t> buffer(kMaxCachedSize); ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize)); ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0x50), buffer); // Verify the cached data is not used. memory_->SetMemoryBlock(0xa000, 3000, 0xff); ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize)); ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0xff), buffer); } TEST_F(MemoryCacheTest, read_for_cache_fail_cross) { std::vector<uint8_t> expect(16, 0xde); expect.resize(32, 0x50); std::vector<uint8_t> buffer(32); ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32)); ASSERT_EQ(expect, buffer); // Verify the cached data is not used for the second half but for the first. memory_->SetMemoryBlock(0xa000, 3000, 0xff); ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32)); expect.resize(16); expect.resize(32, 0xff); ASSERT_EQ(expect, buffer); } } // namespace unwindstack