Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of framagit.org:simgrid/simgrid
[simgrid.git] / src / mc / inspect / mc_unw_vmread.cpp
1 /** \file  Libunwind namespace implementation using process_vm_readv.       */
2
3 /* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved.          */
4
5 /* This program is free software; you can redistribute it and/or modify it
6  * under the terms of the license (GNU LGPL) which comes with this package. */
7
8 #include "src/mc/inspect/mc_unw.hpp"
9 #include "src/mc/sosp/RemoteProcessMemory.hpp"
10
11 #include <sys/types.h>
12 #include <sys/uio.h>
13
14 #include <fcntl.h>
15 #include <libunwind-ptrace.h>
16 #include <libunwind.h>
17
18
19 /** Partial structure of libunwind-ptrace context in order to get the PID
20  *
21  *  HACK, The context type for libunwind-race is an opaque type.
22  *  We need to get the PID which is the first field. This is a hack
23  *  which might break if the libunwind-ptrace structure changes.
24  */
25 struct _UPT_info {
26   pid_t pid;
27   // Other things...
28 };
29
30 /** Read from the memory, avoid using `ptrace` (libunwind method) */
31 static int access_mem(const unw_addr_space_t as, const unw_word_t addr, unw_word_t* const valp, const int write,
32                       void* const arg)
33 {
34   if (write)
35     return -UNW_EINVAL;
36   pid_t pid   = static_cast<_UPT_info*>(arg)->pid;
37   size_t size = sizeof(unw_word_t);
38
39 #if HAVE_PROCESS_VM_READV /* linux but not freebsd */
40   // process_vm_read implementation.
41   // This is only available since Linux 3.2.
42
43   struct iovec local  = {valp, size};
44   struct iovec remote = {(void*)addr, size};
45   if (ssize_t s = process_vm_readv(pid, &local, 1, &remote, 1, 0); s >= 0) {
46     if ((size_t)s != size)
47       return -UNW_EINVAL;
48     else
49       return 0;
50   } else if (errno != ENOSYS) {
51     return -UNW_EINVAL;
52   }
53 #endif
54
55   // /proc/${pid}/mem implementation.
56   // On recent kernels, we do not need to ptrace the target process.
57   // On older kernels, it is necessary to ptrace the target process.
58   size_t count = size;
59   auto off     = static_cast<off_t>(addr);
60   auto* buf    = reinterpret_cast<std::byte*>(valp);
61   int fd       = simgrid::mc::open_vm(pid, O_RDONLY);
62   if (fd < 0)
63     return -UNW_EINVAL;
64
65   while (count > 0) {
66     ssize_t nread = pread(fd, buf, count, off);
67     if (nread == 0) {
68       close(fd);
69       return -UNW_EINVAL;
70     }
71     if (nread == -1)
72       // ptrace implementation.
73       // We need to have PTRACE_ATTACH-ed it before.
74       return _UPT_access_mem(as, addr, valp, write, arg);
75
76     count -= nread;
77     buf += nread;
78     off += nread;
79   }
80   close(fd);
81   return 0;
82 }
83
84 namespace simgrid::unw {
85
86 unw_addr_space_t create_addr_space()
87 {
88   /** Virtual table for our `libunwind-process_vm_readv` implementation.
89    *
90    *  This implementation reuse most the code of `libunwind-ptrace` but
91    *  does not use ptrace() to read the target process memory by
92    *  `process_vm_readv()` or `/dev/${pid}/mem` if possible.
93    *
94    *  Does not support any MC-specific behavior (privatization, snapshots)
95    *  and `ucontext_t`.
96    *
97    *  It works with `void*` contexts allocated with `_UPT_create(pid)`.
98    */
99   // TODO, we could get rid of this if we properly stop the model-checked
100   // process before reading the memory.
101   unw_accessors_t accessors = _UPT_accessors;
102   accessors.access_mem      = &access_mem;
103   return unw_create_addr_space(&accessors, BYTE_ORDER);
104 }
105
106 void* create_context(unw_addr_space_t /*as*/, pid_t pid)
107 {
108   return _UPT_create(pid);
109 }
110
111 } // namespace simgrid::unw