Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Prefer "using" to "typedef".
[simgrid.git] / src / mc / remote / RemoteSimulation.cpp
1 /* Copyright (c) 2014-2020. 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 <memory>
20
21 using simgrid::mc::remote;
22
23 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_process, mc, "MC process information");
24
25 namespace simgrid {
26 namespace mc {
27
28 // ***** Helper stuff
29
30 // List of library which memory segments are not considered:
31 static const std::vector<std::string> filtered_libraries = {
32 #ifdef __linux__
33     "ld",
34 #elif defined __FreeBSD__
35     "ld-elf",
36     "ld-elf32",
37     "libkvm",      /* kernel data access library */
38     "libprocstat", /* process and file information retrieval */
39     "libthr",      /* thread library */
40     "libutil",
41 #endif
42     "libargp", /* workarounds for glibc-less systems */
43     "libasan", /* gcc sanitizers */
44     "libboost_chrono",
45     "libboost_context",
46     "libboost_context-mt",
47     "libboost_stacktrace_addr2line",
48     "libboost_stacktrace_backtrace",
49     "libboost_system",
50     "libboost_thread",
51     "libboost_timer",
52     "libbrotlicommon",
53     "libbrotlidec",
54     "libbz2",
55     "libc",
56     "libc++",
57     "libcdt",
58     "libcgraph",
59     "libcom_err",
60     "libcrypt",
61     "libcrypto",
62     "libcurl",
63     "libcurl-gnutls",
64     "libcxxrt",
65     "libdebuginfod",
66     "libdl",
67     "libdw",
68     "libelf",
69     "libevent",
70     "libexecinfo",
71     "libffi",
72     "libflang",
73     "libflangrti",
74     "libgcc_s",
75     "libgmp",
76     "libgnutls",
77     "libgcrypt",
78     "libgfortran",
79     "libgpg-error",
80     "libgssapi_krb5",
81     "libhogweed",
82     "libidn2",
83     "libimf",
84     "libintlc",
85     "libirng",
86     "libk5crypto",
87     "libkeyutils",
88     "libkrb5",
89     "libkrb5support", /*odd behaviour on fedora rawhide ... remove these when fixed*/
90     "liblber",
91     "libldap",
92     "libldap_r",
93     "liblua5.1",
94     "liblua5.3",
95     "liblzma",
96     "libm",
97     "libmd",
98     "libnettle",
99     "libnghttp2",
100     "libomp",
101     "libp11-kit",
102     "libpapi",
103     "libpcre2",
104     "libpfm",
105     "libpgmath",
106     "libpsl",
107     "libpthread",
108     "libquadmath",
109     "libresolv",
110     "librt",
111     "librtmp",
112     "libsasl2",
113     "libselinux",
114     "libssh",
115     "libssh2",
116     "libssl",
117     "libstdc++",
118     "libsvml",
119     "libtasn1",
120     "libtsan",  /* gcc sanitizers */
121     "libubsan", /* gcc sanitizers */
122     "libunistring",
123     "libunwind",
124     "libunwind-ptrace",
125     "libunwind-x86",
126     "libunwind-x86_64",
127     "libz",
128     "libzstd"};
129
130 static bool is_simgrid_lib(const std::string& libname)
131 {
132   return libname == "libsimgrid";
133 }
134
135 static bool is_filtered_lib(const std::string& libname)
136 {
137   return std::find(begin(filtered_libraries), end(filtered_libraries), libname) != end(filtered_libraries);
138 }
139
140 static std::string get_lib_name(const std::string& pathname)
141 {
142   std::string map_basename = simgrid::xbt::Path(pathname).get_base_name();
143   std::string libname;
144
145   size_t pos = map_basename.rfind(".so");
146   if (pos != std::string::npos) {
147     // strip the extension (matching regex "\.so.*$")
148     libname.assign(map_basename, 0, pos);
149
150     // strip the version suffix (matching regex "-[.0-9-]*$")
151     while (true) {
152       pos = libname.rfind('-');
153       if (pos == std::string::npos || libname.find_first_not_of(".0123456789", pos + 1) != std::string::npos)
154         break;
155       libname.erase(pos);
156     }
157   }
158
159   return libname;
160 }
161
162 static ssize_t pread_whole(int fd, void* buf, size_t count, off_t offset)
163 {
164   auto* buffer       = static_cast<char*>(buf);
165   ssize_t real_count = count;
166   while (count) {
167     ssize_t res = pread(fd, buffer, count, offset);
168     if (res > 0) {
169       count -= res;
170       buffer += res;
171       offset += res;
172     } else if (res == 0)
173       return -1;
174     else if (errno != EINTR) {
175       perror("pread_whole");
176       return -1;
177     }
178   }
179   return real_count;
180 }
181
182 static ssize_t pwrite_whole(int fd, const void* buf, size_t count, off_t offset)
183 {
184   const auto* buffer = static_cast<const char*>(buf);
185   ssize_t real_count = count;
186   while (count) {
187     ssize_t res = pwrite(fd, buffer, count, offset);
188     if (res > 0) {
189       count -= res;
190       buffer += res;
191       offset += res;
192     } else if (res == 0)
193       return -1;
194     else if (errno != EINTR)
195       return -1;
196   }
197   return real_count;
198 }
199
200 static pthread_once_t zero_buffer_flag = PTHREAD_ONCE_INIT;
201 static const void* zero_buffer;
202 static const size_t zero_buffer_size = 10 * 4096;
203
204 static void zero_buffer_init()
205 {
206   int fd = open("/dev/zero", O_RDONLY);
207   if (fd < 0)
208     xbt_die("Could not open /dev/zero");
209   zero_buffer = mmap(nullptr, zero_buffer_size, PROT_READ, MAP_SHARED, fd, 0);
210   if (zero_buffer == MAP_FAILED)
211     xbt_die("Could not map the zero buffer");
212   close(fd);
213 }
214
215 int open_vm(pid_t pid, int flags)
216 {
217   const size_t buffer_size = 30;
218   char buffer[buffer_size];
219   int res = snprintf(buffer, buffer_size, "/proc/%lli/mem", (long long)pid);
220   if (res < 0 || (size_t)res >= buffer_size) {
221     errno = ENAMETOOLONG;
222     return -1;
223   }
224   return open(buffer, 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   this->libsimgrid_info = nullptr;
308
309   std::vector<simgrid::xbt::VmMap> const& maps = this->memory_map_;
310
311   const char* current_name = nullptr;
312
313   this->object_infos.clear();
314
315   for (size_t i = 0; i < maps.size(); i++) {
316     simgrid::xbt::VmMap const& reg = maps[i];
317     const char* pathname           = maps[i].pathname.c_str();
318
319     // Nothing to do
320     if (maps[i].pathname.empty()) {
321       current_name = nullptr;
322       continue;
323     }
324
325     // [stack], [vvar], [vsyscall], [vdso] ...
326     if (pathname[0] == '[') {
327       if ((reg.prot & PROT_WRITE) && not memcmp(pathname, "[stack]", 7)) {
328         this->maestro_stack_start_ = remote(reg.start_addr);
329         this->maestro_stack_end_   = remote(reg.end_addr);
330       }
331       current_name = nullptr;
332       continue;
333     }
334
335     if (current_name && strcmp(current_name, pathname) == 0)
336       continue;
337
338     current_name = pathname;
339     if (not(reg.prot & PROT_READ) && (reg.prot & PROT_EXEC))
340       continue;
341
342     const bool is_executable = not i;
343     std::string libname;
344     if (not is_executable) {
345       libname = get_lib_name(pathname);
346       if (is_filtered_lib(libname)) {
347         continue;
348       }
349     }
350
351     std::shared_ptr<simgrid::mc::ObjectInformation> info =
352         simgrid::mc::createObjectInformation(this->memory_map_, pathname);
353     this->object_infos.push_back(info);
354     if (is_executable)
355       this->binary_info = info;
356     else if (is_simgrid_lib(libname))
357       this->libsimgrid_info = info;
358   }
359
360   // Resolve time (including across different objects):
361   for (auto const& object_info : this->object_infos)
362     postProcessObjectInformation(this, object_info.get());
363
364   xbt_assert(this->maestro_stack_start_, "Did not find maestro_stack_start");
365   xbt_assert(this->maestro_stack_end_, "Did not find maestro_stack_end");
366
367   XBT_DEBUG("Get debug information done !");
368 }
369
370 std::shared_ptr<simgrid::mc::ObjectInformation> RemoteSimulation::find_object_info(RemotePtr<void> addr) const
371 {
372   for (auto const& object_info : this->object_infos)
373     if (addr.address() >= (std::uint64_t)object_info->start && addr.address() <= (std::uint64_t)object_info->end)
374       return object_info;
375   return nullptr;
376 }
377
378 std::shared_ptr<ObjectInformation> RemoteSimulation::find_object_info_exec(RemotePtr<void> addr) const
379 {
380   for (std::shared_ptr<ObjectInformation> const& info : this->object_infos)
381     if (addr.address() >= (std::uint64_t)info->start_exec && addr.address() <= (std::uint64_t)info->end_exec)
382       return info;
383   return nullptr;
384 }
385
386 std::shared_ptr<ObjectInformation> RemoteSimulation::find_object_info_rw(RemotePtr<void> addr) const
387 {
388   for (std::shared_ptr<ObjectInformation> const& info : this->object_infos)
389     if (addr.address() >= (std::uint64_t)info->start_rw && addr.address() <= (std::uint64_t)info->end_rw)
390       return info;
391   return nullptr;
392 }
393
394 simgrid::mc::Frame* RemoteSimulation::find_function(RemotePtr<void> ip) const
395 {
396   std::shared_ptr<simgrid::mc::ObjectInformation> info = this->find_object_info_exec(ip);
397   return info ? info->find_function((void*)ip.address()) : nullptr;
398 }
399
400 /** Find (one occurrence of) the named variable definition
401  */
402 const simgrid::mc::Variable* RemoteSimulation::find_variable(const char* name) const
403 {
404   // First lookup the variable in the executable shared object.
405   // A global variable used directly by the executable code from a library
406   // is reinstantiated in the executable memory .data/.bss.
407   // We need to look up the variable in the executable first.
408   if (this->binary_info) {
409     std::shared_ptr<simgrid::mc::ObjectInformation> const& info = this->binary_info;
410     const simgrid::mc::Variable* var                            = info->find_variable(name);
411     if (var)
412       return var;
413   }
414
415   for (std::shared_ptr<simgrid::mc::ObjectInformation> const& info : this->object_infos) {
416     const simgrid::mc::Variable* var = info->find_variable(name);
417     if (var)
418       return var;
419   }
420
421   return nullptr;
422 }
423
424 void RemoteSimulation::read_variable(const char* name, void* target, size_t size) const
425 {
426   const simgrid::mc::Variable* var = this->find_variable(name);
427   xbt_assert(var, "Variable %s not found", name);
428   xbt_assert(var->address, "No simple location for this variable");
429   xbt_assert(var->type->full_type, "Partial type for %s, cannot check size", name);
430   xbt_assert((size_t)var->type->full_type->byte_size == size, "Unexpected size for %s (expected %zu, was %zu)", name,
431              size, (size_t)var->type->full_type->byte_size);
432   this->read_bytes(target, size, remote(var->address));
433 }
434
435 std::string RemoteSimulation::read_string(RemotePtr<char> address) const
436 {
437   if (not address)
438     return {};
439
440   std::vector<char> res(128);
441   off_t off = 0;
442
443   while (true) {
444     ssize_t c = pread(this->memory_file, res.data() + off, res.size() - off, (off_t)address.address() + off);
445     if (c == -1 && errno == EINTR)
446       continue;
447     xbt_assert(c > 0, "Could not read string from remote process");
448
449     const void* p = memchr(res.data() + off, '\0', c);
450     if (p)
451       return std::string(res.data());
452
453     off += c;
454     if (off == (off_t)res.size())
455       res.resize(res.size() * 2);
456   }
457 }
458
459 void* RemoteSimulation::read_bytes(void* buffer, std::size_t size, RemotePtr<void> address,
460                                    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 RemoteSimulation::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 RemoteSimulation::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 RemoteSimulation::ignore_region(std::uint64_t addr, std::size_t size)
491 {
492   IgnoredRegion region;
493   region.addr = addr;
494   region.size = size;
495
496   if (ignored_regions_.empty()) {
497     ignored_regions_.push_back(region);
498     return;
499   }
500
501   unsigned int cursor                 = 0;
502   const IgnoredRegion* current_region = nullptr;
503
504   int start = 0;
505   int end   = ignored_regions_.size() - 1;
506   while (start <= end) {
507     cursor         = (start + end) / 2;
508     current_region = &ignored_regions_[cursor];
509     if (current_region->addr == addr) {
510       if (current_region->size == size)
511         return;
512       else if (current_region->size < size)
513         start = cursor + 1;
514       else
515         end = cursor - 1;
516     } else if (current_region->addr < addr)
517       start = cursor + 1;
518     else
519       end = cursor - 1;
520   }
521
522   std::size_t position;
523   if (current_region->addr == addr) {
524     if (current_region->size < size)
525       position = cursor + 1;
526     else
527       position = cursor;
528   } else if (current_region->addr < addr)
529     position = cursor + 1;
530   else
531     position = cursor;
532   ignored_regions_.insert(ignored_regions_.begin() + position, region);
533 }
534
535 void RemoteSimulation::ignore_heap(IgnoredHeapRegion const& region)
536 {
537   if (ignored_heap_.empty()) {
538     ignored_heap_.push_back(std::move(region));
539     return;
540   }
541
542   using size_type = std::vector<IgnoredHeapRegion>::size_type;
543
544   size_type start = 0;
545   size_type end   = ignored_heap_.size() - 1;
546
547   // Binary search the position of insertion:
548   size_type cursor;
549   while (start <= end) {
550     cursor                     = start + (end - start) / 2;
551     auto const& current_region = ignored_heap_[cursor];
552     if (current_region.address == region.address)
553       return;
554     else if (current_region.address < region.address)
555       start = cursor + 1;
556     else if (cursor != 0)
557       end = cursor - 1;
558     // Avoid underflow:
559     else
560       break;
561   }
562
563   // Insert it mc_heap_ignore_region_t:
564   if (ignored_heap_[cursor].address < region.address)
565     ++cursor;
566   ignored_heap_.insert(ignored_heap_.begin() + cursor, region);
567 }
568
569 void RemoteSimulation::unignore_heap(void* address, size_t size)
570 {
571   using size_type = std::vector<IgnoredHeapRegion>::size_type;
572
573   size_type start = 0;
574   size_type end   = ignored_heap_.size() - 1;
575
576   // Binary search:
577   size_type cursor;
578   while (start <= end) {
579     cursor             = (start + end) / 2;
580     auto const& region = ignored_heap_[cursor];
581     if (region.address < address)
582       start = cursor + 1;
583     else if ((char*)region.address <= ((char*)address + size)) {
584       ignored_heap_.erase(ignored_heap_.begin() + cursor);
585       return;
586     } else if (cursor != 0)
587       end = cursor - 1;
588     // Avoid underflow:
589     else
590       break;
591   }
592 }
593
594 void RemoteSimulation::ignore_local_variable(const char* var_name, const char* frame_name) const
595 {
596   if (frame_name != nullptr && strcmp(frame_name, "*") == 0)
597     frame_name = nullptr;
598   for (std::shared_ptr<simgrid::mc::ObjectInformation> const& info : this->object_infos)
599     info->remove_local_variable(var_name, frame_name);
600 }
601
602 std::vector<simgrid::mc::ActorInformation>& RemoteSimulation::actors()
603 {
604   this->refresh_simix();
605   return smx_actors_infos;
606 }
607
608 std::vector<simgrid::mc::ActorInformation>& RemoteSimulation::dead_actors()
609 {
610   this->refresh_simix();
611   return smx_dead_actors_infos;
612 }
613
614 void RemoteSimulation::dump_stack() const
615 {
616   unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, BYTE_ORDER);
617   if (as == nullptr) {
618     XBT_ERROR("Could not initialize ptrace address space");
619     return;
620   }
621
622   void* context = _UPT_create(this->pid_);
623   if (context == nullptr) {
624     unw_destroy_addr_space(as);
625     XBT_ERROR("Could not initialize ptrace context");
626     return;
627   }
628
629   unw_cursor_t cursor;
630   if (unw_init_remote(&cursor, as, context) != 0) {
631     _UPT_destroy(context);
632     unw_destroy_addr_space(as);
633     XBT_ERROR("Could not initialiez ptrace cursor");
634     return;
635   }
636
637   simgrid::mc::dumpStack(stderr, &cursor);
638
639   _UPT_destroy(context);
640   unw_destroy_addr_space(as);
641 }
642 } // namespace mc
643 } // namespace simgrid