Logo AND Algorithmique Numérique Distribuée

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