Skip to content

Commit 61713f1

Browse files
committed
Fallback to /proc/PID/mem when process_vm_readv not in kernel
Kernels may build with the CONFIG_CROSS_MEMORY_ATTACH configuration option not set, in this case process_vm_readv returns ENOSYS. The /proc/PID/mem file in the proc(5) filesystem serves the same purpose but is less efficient as the data must transfer through the kernel. We use this as a fallback. Signed-off-by: Daniel Golding <goldingd89@gmail.com>
1 parent ca83721 commit 61713f1

File tree

3 files changed

+43
-1
lines changed

3 files changed

+43
-1
lines changed

news/240.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support kernels that don't have CONFIG_CROSS_MEMORY_ATTACH set.

src/pystack/_pystack/mem.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,16 @@ ProcessMemoryManager::ProcessMemoryManager(pid_t pid)
228228

229229
ssize_t
230230
ProcessMemoryManager::readChunk(remote_addr_t addr, size_t len, char* dst) const
231+
{
232+
if (d_memfile.is_open()) {
233+
return readChunkThroughMemFile(addr, len, dst);
234+
} else {
235+
return readChunkDirect(addr, len, dst);
236+
}
237+
}
238+
239+
ssize_t
240+
ProcessMemoryManager::readChunkDirect(remote_addr_t addr, size_t len, char* dst) const
231241
{
232242
struct iovec local[1];
233243
struct iovec remote[1];
@@ -246,6 +256,9 @@ ProcessMemoryManager::readChunk(remote_addr_t addr, size_t len, char* dst) const
246256
throw InvalidRemoteAddress();
247257
} else if (errno == EPERM) {
248258
throw std::runtime_error(PERM_MESSAGE);
259+
} else if (errno == ENOSYS) {
260+
LOG(DEBUG) << "process_vm_readv not compiled in kernel, falling back to /proc/PID/mem";
261+
return readChunkThroughMemFile(addr, len, dst);
249262
}
250263
throw std::system_error(errno, std::generic_category());
251264
}
@@ -256,6 +269,30 @@ ProcessMemoryManager::readChunk(remote_addr_t addr, size_t len, char* dst) const
256269
return result;
257270
}
258271

272+
ssize_t
273+
ProcessMemoryManager::readChunkThroughMemFile(remote_addr_t addr, size_t len, char* dst) const
274+
{
275+
if (!d_memfile.is_open()) {
276+
std::string filepath = "/proc/" + std::to_string(d_pid) + "/mem";
277+
d_memfile.open(filepath, std::ifstream::binary);
278+
if (!d_memfile) {
279+
LOG(ERROR) << "Failed to open file " << filepath;
280+
if (-1 == open(filepath.c_str(), O_RDONLY)) {
281+
if (errno == EPERM || errno == EACCES) {
282+
throw std::runtime_error(PERM_MESSAGE);
283+
}
284+
throw std::system_error(errno, std::generic_category());
285+
}
286+
throw std::runtime_error("Failed to open " + filepath);
287+
}
288+
}
289+
d_memfile.seekg(addr);
290+
if (!d_memfile.read((char*)dst, len)) {
291+
throw InvalidRemoteAddress();
292+
}
293+
return d_memfile.gcount();
294+
}
295+
259296
ssize_t
260297
ProcessMemoryManager::copyMemoryFromProcess(remote_addr_t addr, size_t len, void* dst) const
261298
{

src/pystack/_pystack/mem.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <cstdint>
44
#include <fcntl.h>
5+
#include <fstream>
56
#include <functional>
67
#include <list>
78
#include <memory>
@@ -12,7 +13,7 @@
1213
#include <sys/stat.h>
1314
#include <vector>
1415

15-
#include <elf_common.h>
16+
#include "elf_common.h"
1617

1718
namespace pystack {
1819
typedef uintptr_t remote_addr_t;
@@ -174,9 +175,12 @@ class ProcessMemoryManager : public AbstractRemoteMemoryManager
174175
pid_t d_pid;
175176
std::vector<VirtualMap> d_vmaps;
176177
mutable LRUCache d_lru_cache;
178+
mutable std::ifstream d_memfile{};
177179

178180
// Methods
179181
ssize_t readChunk(remote_addr_t addr, size_t len, char* dst) const;
182+
ssize_t readChunkDirect(remote_addr_t addr, size_t len, char* dst) const;
183+
ssize_t readChunkThroughMemFile(remote_addr_t addr, size_t len, char* dst) const;
180184
};
181185

182186
struct SimpleVirtualMap

0 commit comments

Comments
 (0)