diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp index 9c6fed43c..d2d6ab8fa 100644 --- a/libbacktrace/UnwindStackMap.cpp +++ b/libbacktrace/UnwindStackMap.cpp @@ -149,13 +149,12 @@ bool UnwindStackOfflineMap::CreateProcessMemory(const backtrace_stackinfo_t& sta } // Create the process memory from the stack data. - uint64_t size = stack.end - stack.start; - unwindstack::MemoryBuffer* memory = new unwindstack::MemoryBuffer; - memory->Resize(size); - memcpy(memory->GetPtr(0), stack.data, size); - std::shared_ptr shared_memory(memory); - - process_memory_.reset(new unwindstack::MemoryRange(shared_memory, 0, size, stack.start)); + if (memory_ == nullptr) { + memory_ = new unwindstack::MemoryOfflineBuffer(stack.data, stack.start, stack.end); + process_memory_.reset(memory_); + } else { + memory_->Reset(stack.data, stack.start, stack.end); + } return true; } diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h index ec0d9c148..039f4a289 100644 --- a/libbacktrace/UnwindStackMap.h +++ b/libbacktrace/UnwindStackMap.h @@ -79,6 +79,9 @@ class UnwindStackOfflineMap : public UnwindStackMap { bool Build(const std::vector& maps); bool CreateProcessMemory(const backtrace_stackinfo_t& stack); + + private: + unwindstack::MemoryOfflineBuffer* memory_ = nullptr; }; #endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index ab01726a9..7b8111c5d 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -219,6 +219,7 @@ cc_test { "tests/MemoryFake.cpp", "tests/MemoryFileTest.cpp", "tests/MemoryLocalTest.cpp", + "tests/MemoryOfflineBufferTest.cpp", "tests/MemoryOfflineTest.cpp", "tests/MemoryRangeTest.cpp", "tests/MemoryRemoteTest.cpp", diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp index d4ba680a8..beb2aade8 100644 --- a/libunwindstack/Memory.cpp +++ b/libunwindstack/Memory.cpp @@ -345,6 +345,25 @@ size_t MemoryOffline::Read(uint64_t addr, void* dst, size_t size) { return memory_->Read(addr, dst, size); } +MemoryOfflineBuffer::MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end) + : data_(data), start_(start), end_(end) {} + +void MemoryOfflineBuffer::Reset(const uint8_t* data, uint64_t start, uint64_t end) { + data_ = data; + start_ = start; + end_ = end; +} + +size_t MemoryOfflineBuffer::Read(uint64_t addr, void* dst, size_t size) { + if (addr < start_ || addr >= end_) { + return 0; + } + + size_t read_length = std::min(size, static_cast(end_ - addr)); + memcpy(dst, &data_[addr - start_], read_length); + return read_length; +} + MemoryOfflineParts::~MemoryOfflineParts() { for (auto memory : memories_) { delete memory; diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h index 19bce0475..c0c07f4f9 100644 --- a/libunwindstack/include/unwindstack/Memory.h +++ b/libunwindstack/include/unwindstack/Memory.h @@ -151,6 +151,21 @@ class MemoryOffline : public Memory { std::unique_ptr memory_; }; +class MemoryOfflineBuffer : public Memory { + public: + MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end); + virtual ~MemoryOfflineBuffer() = default; + + void Reset(const uint8_t* data, uint64_t start, uint64_t end); + + size_t Read(uint64_t addr, void* dst, size_t size) override; + + private: + const uint8_t* data_; + uint64_t start_; + uint64_t end_; +}; + class MemoryOfflineParts : public Memory { public: MemoryOfflineParts() = default; diff --git a/libunwindstack/tests/MemoryOfflineBufferTest.cpp b/libunwindstack/tests/MemoryOfflineBufferTest.cpp new file mode 100644 index 000000000..f022884cb --- /dev/null +++ b/libunwindstack/tests/MemoryOfflineBufferTest.cpp @@ -0,0 +1,97 @@ +/* + * 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 + +#include + +#include + +#include "LogFake.h" + +namespace unwindstack { + +class MemoryOfflineBufferTest : public ::testing::Test { + protected: + void SetUp() override { + ResetLogs(); + memory_.reset(new MemoryOfflineBuffer(buffer_.data(), kStart, kEnd)); + } + + static void SetUpTestCase() { + buffer_.resize(kLength); + for (size_t i = 0; i < kLength; i++) { + buffer_[i] = i % 189; + } + } + + std::unique_ptr memory_; + + static constexpr size_t kLength = 0x2000; + static constexpr uint64_t kStart = 0x1000; + static constexpr uint64_t kEnd = kStart + kLength; + static std::vector buffer_; +}; + +std::vector MemoryOfflineBufferTest::buffer_; + +static void VerifyBuffer(uint8_t* buffer, size_t start_value, size_t length) { + for (size_t i = 0; i < length; i++) { + ASSERT_EQ((start_value + i) % 189, buffer[i]) << "Failed at byte " << i; + } +} + +TEST_F(MemoryOfflineBufferTest, read_out_of_bounds) { + std::vector buffer(1024); + ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 1)); + ASSERT_FALSE(memory_->ReadFully(0xfff, buffer.data(), 1)); + ASSERT_FALSE(memory_->ReadFully(0xfff, buffer.data(), 2)); + ASSERT_FALSE(memory_->ReadFully(0x3000, buffer.data(), 1)); + ASSERT_FALSE(memory_->ReadFully(0x3001, buffer.data(), 1)); +} + +TEST_F(MemoryOfflineBufferTest, read) { + std::vector buffer(1024); + ASSERT_TRUE(memory_->ReadFully(kStart, buffer.data(), 10)); + ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0, 10)); + + ASSERT_TRUE(memory_->ReadFully(kStart + 555, buffer.data(), 40)); + ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 555, 40)); + + ASSERT_TRUE(memory_->ReadFully(kStart + kLength - 105, buffer.data(), 105)); + ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), kLength - 105, 105)); +} + +TEST_F(MemoryOfflineBufferTest, read_past_end) { + std::vector buffer(1024); + ASSERT_EQ(100U, memory_->Read(kStart + kLength - 100, buffer.data(), buffer.size())); + VerifyBuffer(buffer.data(), kLength - 100, 100); +} + +TEST_F(MemoryOfflineBufferTest, read_after_reset) { + std::vector buffer(1024); + ASSERT_TRUE(memory_->ReadFully(kStart, buffer.data(), 100)); + ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0, 100)); + + memory_->Reset(&buffer_[10], 0x12000, 0x13000); + ASSERT_TRUE(memory_->ReadFully(0x12000, buffer.data(), 100)); + ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 10, 100)); + + ASSERT_EQ(50U, memory_->Read(0x13000 - 50, buffer.data(), buffer.size())); + ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0x1000 - 50 + 10, 50)); +} + +} // namespace unwindstack