summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2024-01-24 14:55:55 -0800
committerChristopher Ferris <cferris@google.com>2024-01-25 15:59:33 -0800
commit9ebb4981c2b9ba73018702531ded8de9c169958a (patch)
tree670c77d0e9ad2fe0609f57a67eb07010f779cf40
parent7a1247b5dbc86fe6a48da0d6d732e08ba96d90ae (diff)
downloadunwinding-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.bp1
-rw-r--r--libunwindstack/Memory.cpp11
-rw-r--r--libunwindstack/MemoryLocalUnsafe.h33
-rw-r--r--libunwindstack/include/unwindstack/Memory.h4
-rw-r--r--libunwindstack/tests/MemoryLocalUnsafeTest.cpp65
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