Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Move the logic retrieving the actor info in the App to RemoteProcess
[simgrid.git] / src / mc / remote / RemoteProcess.cpp
1 /* Copyright (c) 2014-2021. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #define _FILE_OFFSET_BITS 64 /* needed for pread_whole to work as expected on 32bits */
7
8 #include "src/mc/remote/RemoteProcess.hpp"
9
10 #include "src/mc/sosp/Snapshot.hpp"
11 #include "xbt/file.hpp"
12 #include "xbt/log.h"
13
14 #include <fcntl.h>
15 #include <libunwind-ptrace.h>
16 #include <sys/mman.h> // PROT_*
17
18 #include <algorithm>
19 #include <cerrno>
20 #include <cstring>
21 #include <memory>
22 #include <string>
23
24 using simgrid::mc::remote;
25
26 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_process, mc, "MC process information");
27
28 namespace simgrid {
29 namespace mc {
30
31 // ***** Helper stuff
32
33 // List of library which memory segments are not considered:
34 static const std::vector<std::string> filtered_libraries = {
35 #ifdef __linux__
36     "ld",
37 #elif defined __FreeBSD__
38     "ld-elf",
39     "ld-elf32",
40     "libkvm",      /* kernel data access library */
41     "libprocstat", /* process and file information retrieval */
42     "libthr",      /* thread library */
43     "libutil",
44 #endif
45     "libargp", /* workarounds for glibc-less systems */
46     "libasan", /* gcc sanitizers */
47     "libasn1",
48     "libboost_chrono",
49     "libboost_context",
50     "libboost_context-mt",
51     "libboost_stacktrace_addr2line",
52     "libboost_stacktrace_backtrace",
53     "libboost_system",
54     "libboost_thread",
55     "libboost_timer",
56     "libbrotlicommon",
57     "libbrotlidec",
58     "libbz2",
59     "libc",
60     "libc++",
61     "libcdt",
62     "libcgraph",
63     "libcom_err",
64     "libcrypt",
65     "libcrypto",
66     "libcurl",
67     "libcurl-gnutls",
68     "libcxxrt",
69     "libdebuginfod",
70     "libdl",
71     "libdw",
72     "libelf",
73     "libevent",
74     "libexecinfo",
75     "libffi",
76     "libflang",
77     "libflangrti",
78     "libgcc_s",
79     "libgmp",
80     "libgnutls",
81     "libgcrypt",
82     "libgfortran",
83     "libgpg-error",
84     "libgssapi",
85     "libgssapi_krb5",
86     "libhcrypto",
87     "libheimbase",
88     "libheimntlm",
89     "libhx509",
90     "libhogweed",
91     "libidn2",
92     "libimf",
93     "libintlc",
94     "libirng",
95     "libk5crypto",
96     "libkeyutils",
97     "libkrb5",
98     "libkrb5support", /*odd behaviour on fedora rawhide ... remove these when fixed*/
99     "liblber",
100     "libldap",
101     "libldap_r",
102     "liblua5.1",
103     "liblua5.3",
104     "liblzma",
105     "libm",
106     "libmd",
107     "libnettle",
108     "libnghttp2",
109     "libomp",
110     "libp11-kit",
111     "libpapi",
112     "libpcre2",
113     "libpfm",
114     "libpgmath",
115     "libpsl",
116     "libpthread",
117     "libquadmath",
118     "libresolv",
119     "libroken",
120     "librt",
121     "librtmp",
122     "libsasl2",
123     "libselinux",
124     "libsqlite3",
125     "libssh",
126     "libssh2",
127     "libssl",
128     "libstdc++",
129     "libsvml",
130     "libtasn1",
131     "libtsan",  /* gcc sanitizers */
132     "libubsan", /* gcc sanitizers */
133     "libunistring",
134     "libunwind",
135     "libunwind-ptrace",
136     "libunwind-x86",
137     "libunwind-x86_64",
138     "libwind",
139     "libz",
140     "libzstd"};
141
142 static bool is_filtered_lib(const std::string& libname)
143 {
144   return std::find(begin(filtered_libraries), end(filtered_libraries), libname) != end(filtered_libraries);
145 }
146
147 static std::string get_lib_name(const std::string& pathname)
148 {
149   std::string map_basename = simgrid::xbt::Path(pathname).get_base_name();
150   std::string libname;
151
152   size_t pos = map_basename.rfind(".so");
153   if (pos != std::string::npos) {
154     // strip the extension (matching regex "\.so.*$")
155     libname.assign(map_basename, 0, pos);
156
157     // strip the version suffix (matching regex "-[.0-9-]*$")
158     while (true) {
159       pos = libname.rfind('-');
160       if (pos == std::string::npos || libname.find_first_not_of(".0123456789", pos + 1) != std::string::npos)
161         break;
162       libname.erase(pos);
163     }
164   }
165
166   return libname;
167 }
168
169 static ssize_t pread_whole(int fd, void* buf, size_t count, off_t offset)
170 {
171   auto* buffer       = static_cast<char*>(buf);
172   ssize_t real_count = count;
173   while (count) {
174     ssize_t res = pread(fd, buffer, count, offset);
175     if (res > 0) {
176       count -= res;
177       buffer += res;
178       offset += res;
179     } else if (res == 0)
180       return -1;
181     else if (errno != EINTR) {
182       XBT_ERROR("pread_whole: %s", strerror(errno));
183       return -1;
184     }
185   }
186   return real_count;
187 }
188
189 static ssize_t pwrite_whole(int fd, const void* buf, size_t count, off_t offset)
190 {
191   const auto* buffer = static_cast<const char*>(buf);
192   ssize_t real_count = count;
193   while (count) {
194     ssize_t res = pwrite(fd, buffer, count, offset);
195     if (res > 0) {
196       count -= res;
197       buffer += res;
198       offset += res;
199     } else if (res == 0)
200       return -1;
201     else if (errno != EINTR) {
202       XBT_ERROR("pwrite_whole: %s", strerror(errno));
203       return -1;
204     }
205   }
206   return real_count;
207 }
208
209 static pthread_once_t zero_buffer_flag = PTHREAD_ONCE_INIT;
210 static const void* zero_buffer;
211 static const size_t zero_buffer_size = 10 * 4096;
212
213 static void zero_buffer_init()
214 {
215   int fd = open("/dev/zero", O_RDONLY);
216   if (fd < 0)
217     xbt_die("Could not open /dev/zero");
218   zero_buffer = mmap(nullptr, zero_buffer_size, PROT_READ, MAP_SHARED, fd, 0);
219   if (zero_buffer == MAP_FAILED)
220     xbt_die("Could not map the zero buffer");
221   close(fd);
222 }
223
224 int open_vm(pid_t pid, int flags)
225 {
226   std::string buffer = "/proc/" + std::to_string(pid) + "/mem";
227   return open(buffer.c_str(), flags);
228 }
229
230 // ***** RemoteProcess
231
232 RemoteProcess::RemoteProcess(pid_t pid) : AddressSpace(this), pid_(pid), running_(true) {}
233
234 void RemoteProcess::init()
235 {
236   this->memory_map_ = simgrid::xbt::get_memory_map(this->pid_);
237   this->init_memory_map_info();
238
239   int fd = open_vm(this->pid_, O_RDWR);
240   xbt_assert(fd >= 0, "Could not open file for process virtual address space");
241   this->memory_file = fd;
242
243   // Read std_heap (is a struct mdesc*):
244   const simgrid::mc::Variable* std_heap_var = this->find_variable("__mmalloc_default_mdp");
245   xbt_assert(std_heap_var, "No heap information in the target process");
246   xbt_assert(std_heap_var->address, "No constant address for this variable");
247   this->read_bytes(&this->heap_address, sizeof(mdesc*), remote(std_heap_var->address));
248
249   this->smx_actors_infos.clear();
250   this->smx_dead_actors_infos.clear();
251   this->unw_addr_space            = simgrid::mc::UnwindContext::createUnwindAddressSpace();
252   this->unw_underlying_addr_space = simgrid::unw::create_addr_space();
253   this->unw_underlying_context    = simgrid::unw::create_context(this->unw_underlying_addr_space, this->pid_);
254 }
255
256 RemoteProcess::~RemoteProcess()
257 {
258   if (this->memory_file >= 0)
259     close(this->memory_file);
260
261   if (this->unw_underlying_addr_space != unw_local_addr_space) {
262     if (this->unw_underlying_addr_space)
263       unw_destroy_addr_space(this->unw_underlying_addr_space);
264     if (this->unw_underlying_context)
265       _UPT_destroy(this->unw_underlying_context);
266   }
267
268   unw_destroy_addr_space(this->unw_addr_space);
269 }
270
271 /** Refresh the information about the process
272  *
273  *  Do not use directly, this is used by the getters when appropriate
274  *  in order to have fresh data.
275  */
276 void RemoteProcess::refresh_heap()
277 {
278   // Read/dereference/refresh the std_heap pointer:
279   if (not this->heap)
280     this->heap = std::make_unique<s_xbt_mheap_t>();
281   this->read_bytes(this->heap.get(), sizeof(mdesc), remote(this->heap_address));
282   this->cache_flags_ |= RemoteProcess::cache_heap;
283 }
284
285 /** Refresh the information about the process
286  *
287  *  Do not use directly, this is used by the getters when appropriate
288  *  in order to have fresh data.
289  * */
290 void RemoteProcess::refresh_malloc_info()
291 {
292   // Refresh process->heapinfo:
293   if (this->cache_flags_ & RemoteProcess::cache_malloc)
294     return;
295   size_t count = this->heap->heaplimit + 1;
296   if (this->heap_info.size() < count)
297     this->heap_info.resize(count);
298   this->read_bytes(this->heap_info.data(), count * sizeof(malloc_info), remote(this->heap->heapinfo));
299   this->cache_flags_ |= RemoteProcess::cache_malloc;
300 }
301
302 /** @brief Finds the range of the different memory segments and binary paths */
303 void RemoteProcess::init_memory_map_info()
304 {
305   XBT_DEBUG("Get debug information ...");
306   this->maestro_stack_start_ = nullptr;
307   this->maestro_stack_end_   = nullptr;
308   this->object_infos.resize(0);
309   this->binary_info = nullptr;
310
311   std::vector<simgrid::xbt::VmMap> const& maps = this->memory_map_;
312
313   const char* current_name = nullptr;
314
315   this->object_infos.clear();
316
317   for (size_t i = 0; i < maps.size(); i++) {
318     simgrid::xbt::VmMap const& reg = maps[i];
319     const char* pathname           = maps[i].pathname.c_str();
320
321     // Nothing to do
322     if (maps[i].pathname.empty()) {
323       current_name = nullptr;
324       continue;
325     }
326
327     // [stack], [vvar], [vsyscall], [vdso] ...
328     if (pathname[0] == '[') {
329       if ((reg.prot & PROT_WRITE) && not memcmp(pathname, "[stack]", 7)) {
330         this->maestro_stack_start_ = remote(reg.start_addr);
331         this->maestro_stack_end_   = remote(reg.end_addr);
332       }
333       current_name = nullptr;
334       continue;
335     }
336
337     if (current_name && strcmp(current_name, pathname) == 0)
338       continue;
339
340     current_name = pathname;
341     if (not(reg.prot & PROT_READ) && (reg.prot & PROT_EXEC))
342       continue;
343
344     const bool is_executable = not i;
345     std::string libname;
346     if (not is_executable) {
347       libname = get_lib_name(pathname);
348       if (is_filtered_lib(libname)) {
349         continue;
350       }
351     }
352
353     std::shared_ptr<simgrid::mc::ObjectInformation> info =
354         simgrid::mc::createObjectInformation(this->memory_map_, pathname);
355     this->object_infos.push_back(info);
356     if (is_executable)
357       this->binary_info = info;
358   }
359
360   xbt_assert(this->maestro_stack_start_, "Did not find maestro_stack_start");
361   xbt_assert(this->maestro_stack_end_, "Did not find maestro_stack_end");
362
363   XBT_DEBUG("Get debug information done !");
364 }
365
366 std::shared_ptr<simgrid::mc::ObjectInformation> RemoteProcess::find_object_info(RemotePtr<void> addr) const
367 {
368   for (auto const& object_info : this->object_infos)
369     if (addr.address() >= (std::uint64_t)object_info->start && addr.address() <= (std::uint64_t)object_info->end)
370       return object_info;
371   return nullptr;
372 }
373
374 std::shared_ptr<ObjectInformation> RemoteProcess::find_object_info_exec(RemotePtr<void> addr) const
375 {
376   for (std::shared_ptr<ObjectInformation> const& info : this->object_infos)
377     if (addr.address() >= (std::uint64_t)info->start_exec && addr.address() <= (std::uint64_t)info->end_exec)
378       return info;
379   return nullptr;
380 }
381
382 std::shared_ptr<ObjectInformation> RemoteProcess::find_object_info_rw(RemotePtr<void> addr) const
383 {
384   for (std::shared_ptr<ObjectInformation> const& info : this->object_infos)
385     if (addr.address() >= (std::uint64_t)info->start_rw && addr.address() <= (std::uint64_t)info->end_rw)
386       return info;
387   return nullptr;
388 }
389
390 simgrid::mc::Frame* RemoteProcess::find_function(RemotePtr<void> ip) const
391 {
392   std::shared_ptr<simgrid::mc::ObjectInformation> info = this->find_object_info_exec(ip);
393   return info ? info->find_function((void*)ip.address()) : nullptr;
394 }
395
396 /** Find (one occurrence of) the named variable definition
397  */
398 const simgrid::mc::Variable* RemoteProcess::find_variable(const char* name) const
399 {
400   // First lookup the variable in the executable shared object.
401   // A global variable used directly by the executable code from a library
402   // is reinstantiated in the executable memory .data/.bss.
403   // We need to look up the variable in the executable first.
404   if (this->binary_info) {
405     std::shared_ptr<simgrid::mc::ObjectInformation> const& info = this->binary_info;
406     const simgrid::mc::Variable* var                            = info->find_variable(name);
407     if (var)
408       return var;
409   }
410
411   for (std::shared_ptr<simgrid::mc::ObjectInformation> const& info : this->object_infos) {
412     const simgrid::mc::Variable* var = info->find_variable(name);
413     if (var)
414       return var;
415   }
416
417   return nullptr;
418 }
419
420 void RemoteProcess::read_variable(const char* name, void* target, size_t size) const
421 {
422   const simgrid::mc::Variable* var = this->find_variable(name);
423   xbt_assert(var, "Variable %s not found", name);
424   xbt_assert(var->address, "No simple location for this variable");
425
426   if (not var->type->full_type) // Try to resolve this type. The needed ObjectInfo was maybe (lazily) loaded recently
427     for (auto const& object_info : this->object_infos)
428       postProcessObjectInformation(this, object_info.get());
429   xbt_assert(var->type->full_type, "Partial type for %s (even after re-resolving types), cannot retrieve its size.",
430              name);
431   xbt_assert((size_t)var->type->full_type->byte_size == size, "Unexpected size for %s (expected %zu, received %zu).",
432              name, size, (size_t)var->type->full_type->byte_size);
433   this->read_bytes(target, size, remote(var->address));
434 }
435
436 std::string RemoteProcess::read_string(RemotePtr<char> address) const
437 {
438   if (not address)
439     return {};
440
441   std::vector<char> res(128);
442   off_t off = 0;
443
444   while (true) {
445     ssize_t c = pread(this->memory_file, res.data() + off, res.size() - off, (off_t)address.address() + off);
446     if (c == -1 && errno == EINTR)
447       continue;
448     xbt_assert(c > 0, "Could not read string from remote process");
449
450     const void* p = memchr(res.data() + off, '\0', c);
451     if (p)
452       return std::string(res.data());
453
454     off += c;
455     if (off == (off_t)res.size())
456       res.resize(res.size() * 2);
457   }
458 }
459
460 void* RemoteProcess::read_bytes(void* buffer, std::size_t size, RemotePtr<void> address, ReadOptions /*options*/) const
461 {
462   if (pread_whole(this->memory_file, buffer, size, (size_t)address.address()) < 0)
463     xbt_die("Read at %p from process %lli failed", (void*)address.address(), (long long)this->pid_);
464   return buffer;
465 }
466
467 /** Write data to a process memory
468  *
469  *  @param buffer   local memory address (source)
470  *  @param len      data size
471  *  @param address  target process memory address (target)
472  */
473 void RemoteProcess::write_bytes(const void* buffer, size_t len, RemotePtr<void> address) const
474 {
475   if (pwrite_whole(this->memory_file, buffer, len, (size_t)address.address()) < 0)
476     xbt_die("Write to process %lli failed", (long long)this->pid_);
477 }
478
479 void RemoteProcess::clear_bytes(RemotePtr<void> address, size_t len) const
480 {
481   pthread_once(&zero_buffer_flag, zero_buffer_init);
482   while (len) {
483     size_t s = len > zero_buffer_size ? zero_buffer_size : len;
484     this->write_bytes(zero_buffer, s, address);
485     address = remote((char*)address.address() + s);
486     len -= s;
487   }
488 }
489
490 void RemoteProcess::ignore_region(std::uint64_t addr, std::size_t size)
491 {
492   IgnoredRegion region;
493   region.addr = addr;
494   region.size = size;
495
496   auto pos = std::lower_bound(ignored_regions_.begin(), ignored_regions_.end(), region,
497                               [](auto const& reg1, auto const& reg2) {
498                                 return reg1.addr < reg2.addr || (reg1.addr == reg2.addr && reg1.size < reg2.size);
499                               });
500   if (pos == ignored_regions_.end() || pos->addr != addr || pos->size != size)
501     ignored_regions_.insert(pos, region);
502 }
503
504 void RemoteProcess::ignore_heap(IgnoredHeapRegion const& region)
505 {
506   // Binary search the position of insertion:
507   auto pos = std::lower_bound(ignored_heap_.begin(), ignored_heap_.end(), region.address,
508                               [](auto const& reg, auto const* addr) { return reg.address < addr; });
509   if (pos == ignored_heap_.end() || pos->address != region.address) {
510     // Insert it:
511     ignored_heap_.insert(pos, region);
512   }
513 }
514
515 void RemoteProcess::unignore_heap(void* address, size_t size)
516 {
517   // Binary search:
518   auto pos = std::lower_bound(ignored_heap_.begin(), ignored_heap_.end(), address,
519                               [](auto const& reg, auto const* addr) { return reg.address < addr; });
520   if (pos != ignored_heap_.end() && static_cast<char*>(pos->address) <= static_cast<char*>(address) + size)
521     ignored_heap_.erase(pos);
522 }
523
524 void RemoteProcess::ignore_local_variable(const char* var_name, const char* frame_name) const
525 {
526   if (frame_name != nullptr && strcmp(frame_name, "*") == 0)
527     frame_name = nullptr;
528   for (std::shared_ptr<simgrid::mc::ObjectInformation> const& info : this->object_infos)
529     info->remove_local_variable(var_name, frame_name);
530 }
531
532 std::vector<simgrid::mc::ActorInformation>& RemoteProcess::actors()
533 {
534   this->refresh_simix();
535   return smx_actors_infos;
536 }
537
538 std::vector<simgrid::mc::ActorInformation>& RemoteProcess::dead_actors()
539 {
540   this->refresh_simix();
541   return smx_dead_actors_infos;
542 }
543
544 void RemoteProcess::dump_stack() const
545 {
546   unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, BYTE_ORDER);
547   if (as == nullptr) {
548     XBT_ERROR("Could not initialize ptrace address space");
549     return;
550   }
551
552   void* context = _UPT_create(this->pid_);
553   if (context == nullptr) {
554     unw_destroy_addr_space(as);
555     XBT_ERROR("Could not initialize ptrace context");
556     return;
557   }
558
559   unw_cursor_t cursor;
560   if (unw_init_remote(&cursor, as, context) != 0) {
561     _UPT_destroy(context);
562     unw_destroy_addr_space(as);
563     XBT_ERROR("Could not initialiez ptrace cursor");
564     return;
565   }
566
567   simgrid::mc::dumpStack(stderr, &cursor);
568
569   _UPT_destroy(context);
570   unw_destroy_addr_space(as);
571 }
572
573 unsigned long RemoteProcess::get_maxpid() const
574 {
575   static const char* name = nullptr;
576   if (not name) {
577     name = "simgrid::kernel::actor::maxpid";
578     if (find_variable(name) == nullptr)
579       name = "maxpid"; // We seem to miss the namespaces when compiling with GCC
580   }
581   unsigned long maxpid;
582   read_variable(name, &maxpid, sizeof(maxpid));
583   return maxpid;
584 }
585
586 void RemoteProcess::get_actor_vectors(RemotePtr<s_xbt_dynar_t>& actors, RemotePtr<s_xbt_dynar_t>& dead_actors)
587 {
588   static_assert(std::is_same<std::unique_ptr<simgrid::simix::Global>, decltype(simix_global)>::value,
589                 "Unexpected type for simix_global");
590   static_assert(sizeof(simix_global) == sizeof(simgrid::simix::Global*), "Bad size for simix_global");
591
592   // TODO, avoid to reload `&simix_global`, `simix_global`, `*simix_global`
593   RemotePtr<simgrid::simix::Global> simix_global_p{this->read_variable<simgrid::simix::Global*>("simix_global")};
594   Remote<simgrid::simix::Global> simix_global = this->read<simgrid::simix::Global>(simix_global_p);
595
596   actors      = remote(simix_global.get_buffer()->actors_vector);
597   dead_actors = remote(simix_global.get_buffer()->dead_actors_vector);
598 }
599 } // namespace mc
600 } // namespace simgrid