diff options
author | Christopher Ferris <cferris@google.com> | 2024-01-24 14:55:55 -0800 |
---|---|---|
committer | Christopher Ferris <cferris@google.com> | 2024-01-25 15:59:33 -0800 |
commit | 9ebb4981c2b9ba73018702531ded8de9c169958a (patch) | |
tree | 670c77d0e9ad2fe0609f57a67eb07010f779cf40 | |
parent | 7a1247b5dbc86fe6a48da0d6d732e08ba96d90ae (diff) | |
download | unwinding-9ebb4981c2b9ba73018702531ded8de9c169958a.tar.gz |
Add a MemoryLocalUnsafe object.
Some users want to take the risk and use a raw local memory reader
that simply does a memcpy instead of the slower process_vm_readv.
For example, Chromium uses libunwindstack for some performance
analysis. Because of the way Chromium works, it's not always
possible to read the elf files on disk. In this case, the elf
is backed by a memory object which means every elf read is
done using process_vm_read. Adding the MemoryLocalUnsafe object,
this particular case can be sped up to use a raw memcpy. It should
be safe since only the elf data is being read using this object.
Added unit tests for new object.
Test: All unit tests pass.
Change-Id: I8ec69b0398153c0b6ede467bae0c70fd4ed0b657
-rw-r--r-- | libunwindstack/Android.bp | 1 | ||||
-rw-r--r-- | libunwindstack/Memory.cpp | 11 | ||||
-rw-r--r-- | libunwindstack/MemoryLocalUnsafe.h | 33 | ||||
-rw-r--r-- | libunwindstack/include/unwindstack/Memory.h | 4 | ||||
-rw-r--r-- | libunwindstack/tests/MemoryLocalUnsafeTest.cpp | 65 |
5 files changed, 114 insertions, 0 deletions
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index f0b4f2e..8fb5c3d 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -347,6 +347,7 @@ cc_defaults { "tests/MemoryCacheTest.cpp", "tests/MemoryFileTest.cpp", "tests/MemoryLocalTest.cpp", + "tests/MemoryLocalUnsafeTest.cpp", "tests/MemoryOfflineBufferTest.cpp", "tests/MemoryOfflineTest.cpp", "tests/MemoryRangeTest.cpp", diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp index 258c54a..0594a7a 100644 --- a/libunwindstack/Memory.cpp +++ b/libunwindstack/Memory.cpp @@ -40,6 +40,7 @@ #include "MemoryCache.h" #include "MemoryFileAtOffset.h" #include "MemoryLocal.h" +#include "MemoryLocalUnsafe.h" #include "MemoryOffline.h" #include "MemoryOfflineBuffer.h" #include "MemoryRange.h" @@ -201,6 +202,10 @@ std::shared_ptr<Memory> Memory::CreateFileMemory(const std::string& path, uint64 return nullptr; } +std::shared_ptr<Memory> Memory::CreateProcessMemoryLocalUnsafe() { + return std::shared_ptr<Memory>(new MemoryLocalUnsafe()); +} + std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) { if (pid == getpid()) { return std::shared_ptr<Memory>(new MemoryLocal()); @@ -581,4 +586,10 @@ void MemoryThreadCache::Clear() { } } +size_t MemoryLocalUnsafe::Read(uint64_t addr, void* dst, size_t size) { + void* raw_ptr = reinterpret_cast<void*>(addr); + memcpy(dst, raw_ptr, size); + return size; +} + } // namespace unwindstack diff --git a/libunwindstack/MemoryLocalUnsafe.h b/libunwindstack/MemoryLocalUnsafe.h new file mode 100644 index 0000000..130f025 --- /dev/null +++ b/libunwindstack/MemoryLocalUnsafe.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 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. + */ + +#pragma once + +#include <stdint.h> + +#include <unwindstack/Memory.h> + +namespace unwindstack { + +class MemoryLocalUnsafe : public Memory { + public: + MemoryLocalUnsafe() = default; + virtual ~MemoryLocalUnsafe() = default; + + size_t Read(uint64_t addr, void* dst, size_t size) override; +}; + +} // namespace unwindstack diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h index 112fe19..ec95bb4 100644 --- a/libunwindstack/include/unwindstack/Memory.h +++ b/libunwindstack/include/unwindstack/Memory.h @@ -35,6 +35,10 @@ class Memory { static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid); static std::shared_ptr<Memory> CreateProcessMemoryCached(pid_t pid); static std::shared_ptr<Memory> CreateProcessMemoryThreadCached(pid_t pid); + // This should only be used for performance. Using this could result + // in crashes if used to try and read stack data. + static std::shared_ptr<Memory> CreateProcessMemoryLocalUnsafe(); + static std::shared_ptr<Memory> CreateOfflineMemory(const uint8_t* data, uint64_t start, uint64_t end); static std::shared_ptr<Memory> CreateFileMemory(const std::string& path, uint64_t offset, diff --git a/libunwindstack/tests/MemoryLocalUnsafeTest.cpp b/libunwindstack/tests/MemoryLocalUnsafeTest.cpp new file mode 100644 index 0000000..fdd5614 --- /dev/null +++ b/libunwindstack/tests/MemoryLocalUnsafeTest.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 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 <string.h> +#include <sys/mman.h> + +#include <vector> + +#include <gtest/gtest.h> + +#include "MemoryLocalUnsafe.h" + +namespace unwindstack { + +TEST(MemoryLocalUnsafeTest, read_smoke) { + std::vector<uint8_t> src(1024); + memset(src.data(), 0x4c, 1024); + + auto local = Memory::CreateProcessMemoryLocalUnsafe(); + + std::vector<uint8_t> dst(1024); + ASSERT_TRUE(local->ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024)); + ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024)); + for (size_t i = 0; i < 1024; i++) { + ASSERT_EQ(0x4cU, dst[i]); + } + + memset(src.data(), 0x23, 512); + ASSERT_TRUE(local->ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024)); + ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024)); + for (size_t i = 0; i < 512; i++) { + ASSERT_EQ(0x23U, dst[i]); + } + for (size_t i = 512; i < 1024; i++) { + ASSERT_EQ(0x4cU, dst[i]); + } +} + +TEST(MemoryLocalUnsafeTest, read_crash) { + void* mapping = + mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(MAP_FAILED, mapping); + + mprotect(mapping, getpagesize(), PROT_NONE); + + auto local = Memory::CreateProcessMemoryLocalUnsafe(); + std::vector<uint8_t> buffer(100); + ASSERT_DEATH(local->Read(reinterpret_cast<uint64_t>(mapping), buffer.data(), buffer.size()), ""); +} + +} // namespace unwindstack |