Logo AND Algorithmique Numérique Distribuée

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