Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
85f3df4a75ae3fd820a3011d9f49d05656bc833c
[simgrid.git] / src / mc / inspect / ObjectInformation.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 #include <algorithm>
7 #include <cstdint>
8 #include <sys/mman.h> // PROT_READ and friends
9 #include <vector>
10
11 #include "src/mc/inspect/Frame.hpp"
12 #include "src/mc/inspect/ObjectInformation.hpp"
13 #include "src/mc/inspect/Variable.hpp"
14 #include "src/mc/mc_private.hpp"
15 #include "xbt/file.hpp"
16
17 namespace simgrid {
18 namespace mc {
19
20 /* For an executable object, addresses are virtual address (there is no offset) i.e.
21  *  \f$\text{virtual address} = \{dwarf address}\f$
22  *
23  * For a shared object, the addresses are offset from the beginning of the shared object (the base address of the
24  * mapped shared object must be used as offset
25  * i.e. \f$\text{virtual address} = \text{shared object base address}
26  *             + \text{dwarf address}\f$.
27  */
28 void* ObjectInformation::base_address() const
29 {
30   // For an executable (more precisely for an ET_EXEC) the base it 0:
31   if (this->executable())
32     return nullptr;
33
34   // For an a shared-object (ET_DYN, including position-independent executables) the base address is its lowest address:
35   void* result = this->start_exec;
36   if (this->start_rw != nullptr && result > (void*)this->start_rw)
37     result = this->start_rw;
38   if (this->start_ro != nullptr && result > (void*)this->start_ro)
39     result = this->start_ro;
40   return result;
41 }
42
43 Frame* ObjectInformation::find_function(const void* ip) const
44 {
45   /* This is implemented by binary search on a sorted array.
46    *
47    * We do quite a lot of those so we want this to be cache efficient.
48    * We pack the only information we need in the index entries in order
49    * to successfully do the binary search. We do not need the high_pc
50    * during the binary search (only at the end) so it is not included
51    * in the index entry. We could use parallel arrays as well.
52    *
53    * We cannot really use the std:: algorithm for this.
54    * We could use std::binary_search by including the high_pc inside
55    * the FunctionIndexEntry.
56    */
57   const FunctionIndexEntry* base = this->functions_index.data();
58   int i                          = 0;
59   int j                          = this->functions_index.size() - 1;
60   while (j >= i) {
61     int k = i + ((j - i) / 2);
62
63     /* In most of the search, we do not dereference the base[k].function.
64      * This way the memory accesses are located in the base[k] array. */
65     if (ip < base[k].low_pc)
66       j = k - 1;
67     else if (k < j && ip >= base[k + 1].low_pc)
68       i = k + 1;
69
70     /* At this point, the search is over.
71      * Either we have found the correct function or we do not know
72      * any function corresponding to this instruction address.
73      * Only at the point do we dereference the function pointer. */
74     else if ((std::uint64_t)ip < base[k].function->range.end())
75       return base[k].function;
76     else
77       return nullptr;
78   }
79   return nullptr;
80 }
81
82 const Variable* ObjectInformation::find_variable(const char* name) const
83 {
84   for (Variable const& variable : this->global_variables) {
85     if (variable.name == name)
86       return &variable;
87   }
88   return nullptr;
89 }
90
91 void ObjectInformation::remove_global_variable(const char* var_name)
92 {
93   // Binary search:
94   auto pos1 = std::lower_bound(this->global_variables.begin(), this->global_variables.end(), var_name,
95                                [](auto const& var, const char* name) { return var.name < name; });
96   // Find the whole range:
97   auto pos2 = std::upper_bound(pos1, this->global_variables.end(), var_name,
98                                [](const char* name, auto const& var) { return name < var.name; });
99   // Remove the whole range:
100   this->global_variables.erase(pos1, pos2);
101 }
102
103 /** Ignore a local variable in a scope
104  *
105  *  Ignore all instances of variables with a given name in any (possibly inlined) subprogram with a given namespaced
106  *  name.
107  *
108  *  @param var_name        Name of the local variable to ignore
109  *  @param subprogram_name Name of the subprogram to ignore (nullptr for any)
110  *  @param subprogram      (possibly inlined) Subprogram of the scope current scope
111  *  @param scope           Current scope
112  */
113 static void remove_local_variable(Frame& scope, const char* var_name, const char* subprogram_name,
114                                   Frame const& subprogram)
115 {
116   // If the current subprogram matches the given name:
117   if (subprogram_name == nullptr || (not subprogram.name.empty() && subprogram.name == subprogram_name)) {
118     // Try to find the variable and remove it:
119
120     // Binary search:
121     auto pos = std::lower_bound(scope.variables.begin(), scope.variables.end(), var_name,
122                                 [](auto const& var, const char* name) { return var.name < name; });
123     if (pos != scope.variables.end() && pos->name == var_name) {
124       // Variable found, remove it:
125       scope.variables.erase(pos);
126     }
127   }
128
129   // And recursive processing in nested scopes:
130   for (Frame& nested_scope : scope.scopes) {
131     // The new scope may be an inlined subroutine, in this case we want to use its
132     // namespaced name in recursive calls:
133     Frame const& nested_subprogram = nested_scope.tag == DW_TAG_inlined_subroutine ? nested_scope : subprogram;
134     remove_local_variable(nested_scope, var_name, subprogram_name, nested_subprogram);
135   }
136 }
137
138 void ObjectInformation::remove_local_variable(const char* var_name, const char* subprogram_name)
139 {
140   for (auto& entry : this->subprograms)
141     mc::remove_local_variable(entry.second, var_name, subprogram_name, entry.second);
142 }
143
144 /** @brief Fills the position of the segments (executable, read-only, read/write) */
145 // TODO, use the ELF segment information for more robustness
146 void find_object_address(std::vector<xbt::VmMap> const& maps, ObjectInformation* result)
147 {
148   const int PROT_RW = PROT_READ | PROT_WRITE;
149   const int PROT_RX = PROT_READ | PROT_EXEC;
150
151   std::string name = xbt::Path(result->file_name).get_base_name();
152
153   for (size_t i = 0; i < maps.size(); ++i) {
154     simgrid::xbt::VmMap const& reg = maps[i];
155     if (maps[i].pathname.empty())
156       continue;
157     std::string map_basename = simgrid::xbt::Path(maps[i].pathname).get_base_name();
158     if (map_basename != name)
159       continue;
160
161     // This is the non-GNU_RELRO-part of the data segment:
162     if (reg.prot == PROT_RW) {
163       xbt_assert(not result->start_rw, "Multiple read-write segments for %s, not supported", maps[i].pathname.c_str());
164       result->start_rw = (char*)reg.start_addr;
165       result->end_rw   = (char*)reg.end_addr;
166
167       // The next VMA might be end of the data segment:
168       if (i + 1 < maps.size() && maps[i + 1].pathname.empty() && maps[i + 1].prot == PROT_RW &&
169           maps[i + 1].start_addr == reg.end_addr)
170         result->end_rw = (char*)maps[i + 1].end_addr;
171     }
172
173     // This is the text segment:
174     else if (reg.prot == PROT_RX) {
175       xbt_assert(not result->start_exec, "Multiple executable segments for %s, not supported",
176                  maps[i].pathname.c_str());
177       result->start_exec = (char*)reg.start_addr;
178       result->end_exec   = (char*)reg.end_addr;
179
180       // The next VMA might be end of the data segment:
181       if (i + 1 < maps.size() && maps[i + 1].pathname.empty() && maps[i + 1].prot == PROT_RW &&
182           maps[i + 1].start_addr == reg.end_addr) {
183         result->start_rw = (char*)maps[i + 1].start_addr;
184         result->end_rw   = (char*)maps[i + 1].end_addr;
185       }
186     }
187
188     // This is the GNU_RELRO-part of the data segment:
189     else if (reg.prot == PROT_READ) {
190       xbt_assert(not result->start_ro,
191                  "Multiple read-only segments for %s, not supported. Compiling with the following may help: "
192                  "-Wl,-znorelro -Wl,-znoseparate-code",
193                  maps[i].pathname.c_str());
194       result->start_ro = (char*)reg.start_addr;
195       result->end_ro   = (char*)reg.end_addr;
196     }
197   }
198
199   result->start = result->start_rw;
200   if ((const void*)result->start_ro < result->start)
201     result->start = result->start_ro;
202   if ((const void*)result->start_exec < result->start)
203     result->start = result->start_exec;
204
205   result->end = result->end_rw;
206   if (result->end_ro && (const void*)result->end_ro > result->end)
207     result->end = result->end_ro;
208   if (result->end_exec && (const void*)result->end_exec > result->end)
209     result->end = result->end_exec;
210
211   xbt_assert(result->start_exec || result->start_rw || result->start_ro);
212 }
213
214 } // namespace mc
215 } // namespace simgrid