Logo AND Algorithmique Numérique Distribuée

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