xerus
a general purpose tensor library
callStack.cpp
Go to the documentation of this file.
1 // Xerus - A General Purpose Tensor Library
2 // Copyright (C) 2014-2017 Benjamin Huber and Sebastian Wolf.
3 //
4 // Xerus is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as published
6 // by the Free Software Foundation, either version 3 of the License,
7 // or (at your option) any later version.
8 //
9 // Xerus is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
13 //
14 // You should have received a copy of the GNU Affero General Public License
15 // along with Xerus. If not, see <http://www.gnu.org/licenses/>.
16 //
17 // For further information on Xerus visit https://libXerus.org
18 // or contact us at contact@libXerus.org.
19 
25 #include <xerus/misc/callStack.h>
26 
27 #ifndef XERUS_NO_FANCY_CALLSTACK
28  #include <execinfo.h>
29  #include <bfd.h>
30  #include <dlfcn.h>
31  #include <unistd.h>
32  #include <iostream>
33  #include <sstream>
34  #include <memory>
35  #include <map>
36  #include <vector>
37  #include <xerus/misc/stringFromTo.h>
39 
40 namespace xerus { namespace misc { namespace internal {
46  struct bfdResolver {
48  struct storedBfd {
49  typedef bfd_boolean(deleter_t)(bfd*);
50  std::unique_ptr<bfd, deleter_t*> abfd;
51  std::unique_ptr<asymbol*[]> symbols;
52  intptr_t offset;
53  storedBfd(bfd *_abfd, deleter_t *_del) : abfd(_abfd, _del) {}
54  };
55  static std::map<void *, storedBfd> bfds;
56  static bool bfd_initialized;
57 
58  static bool ensure_bfd_loaded(Dl_info &_info) {
59  // load the corresponding bfd file (from file or map)
60  if (bfds.count(_info.dli_fbase) == 0) {
61  std::unique_ptr<storedBfd> newBfd(new storedBfd(bfd_openr(_info.dli_fname, nullptr), &bfd_close));
62  if (!newBfd->abfd) {
63  return false;
64  }
65  bfd_check_format(newBfd->abfd.get(),bfd_object);
66  long storageNeeded = bfd_get_symtab_upper_bound(newBfd->abfd.get());
67  if (storageNeeded < 0) {
68  return false;
69  }
70  newBfd->symbols.reset(reinterpret_cast<asymbol**>(new char[static_cast<size_t>(storageNeeded)]));
71  /*size_t numSymbols = */bfd_canonicalize_symtab(newBfd->abfd.get(), newBfd->symbols.get());
72 
73  newBfd->offset = reinterpret_cast<intptr_t>(_info.dli_fbase);
74 
75  bfds.insert(std::pair<void *, storedBfd>(_info.dli_fbase, std::move(*newBfd)));
76  }
77  return true;
78  }
79 
80  static std::pair<uintptr_t, uintptr_t> get_range_of_section(void * _addr, std::string _name) {
81  if (!bfd_initialized) {
82  bfd_init();
83  bfd_initialized = true;
84  }
85 
86  // get path and offset of shared object that contains this address
87  Dl_info info;
88  dladdr(_addr, &info);
89  if (info.dli_fbase == nullptr) {
90  return std::pair<uintptr_t, uintptr_t>(0,0);
91  }
92 
93  if (!ensure_bfd_loaded(info)) {
94  return std::pair<uintptr_t, uintptr_t>(0,0);
95  }
96  storedBfd &currBfd = bfds.at(info.dli_fbase);
97 
98  asection *section = bfd_get_section_by_name(currBfd.abfd.get(), _name.c_str());
99  if (section == nullptr) {
100  return std::pair<uintptr_t, uintptr_t>(0,0);
101  }
102  return std::pair<uintptr_t, uintptr_t>(section->vma, section->vma+section->size);
103  }
104 
105  static std::string resolve(void *address) {
106  if (!bfd_initialized) {
107  bfd_init();
108  bfd_initialized = true;
109  }
110 
111  std::stringstream res;
112  res << "[0x" << std::setw(int(sizeof(void*)*2)) << std::setfill('0') << std::hex << reinterpret_cast<uintptr_t>(address);
113 
114  // get path and offset of shared object that contains this address
115  Dl_info info;
116  dladdr(address, &info);
117  if (info.dli_fbase == nullptr) {
118  return res.str()+" .?] <object to address not found>";
119  }
120 
121  if (!ensure_bfd_loaded(info)) {
122  return res.str()+" .?] <could not open object file>";
123  }
124  storedBfd &currBfd = bfds.at(info.dli_fbase);
125 
126  asection *section = currBfd.abfd->sections;
127  const bool relative = section->vma < static_cast<uintptr_t>(currBfd.offset);
128  // std::cout << '\n' << "sections:\n";
129  while (section != nullptr) {
130  const intptr_t offset = reinterpret_cast<intptr_t>(address) - (relative ? currBfd.offset : 0) - static_cast<intptr_t>(section->vma);
131  // std::cout << section->name << " " << section->id << " file: " << section->filepos << " flags: " << section->flags
132  // << " vma: " << std::hex << section->vma << " - " << std::hex << (section->vma+section->size) << std::endl;
133 
134  if (offset < 0 || static_cast<size_t>(offset) > section->size) {
135  section = section->next;
136  continue;
137  }
138  res << ' ' << section->name;
139  if ((section->flags | SEC_CODE) == 0u) {
140  return res.str()+"] <non executable address>";
141  }
142  // get more info on legal addresses
143  const char *file;
144  const char *func;
145  unsigned line;
146  if (bfd_find_nearest_line(currBfd.abfd.get(), section, currBfd.symbols.get(), offset, &file, &func, &line)) {
147  if (file != nullptr) {
148  return res.str()+"] "+std::string(file)+":"+to_string(line)+" (inside "+demangle_cxa(func)+")";
149  }
150  if (info.dli_saddr != nullptr) {
151  return res.str()+"] ??:? (inside "+demangle_cxa(func)+" +0x"+std::to_string(reinterpret_cast<uintptr_t>(address)-reinterpret_cast<uintptr_t>(info.dli_saddr))+")";
152  }
153  return res.str()+"] ??:? (inside "+demangle_cxa(func)+")";
154  }
155  return res.str()+"] <bfd_error> (inside "+demangle_cxa((info.dli_sname != nullptr?info.dli_sname:""))+")";
156  }
157  // std::cout << " ---- sections end ------ " << std::endl;
158  return res.str()+" .none] <not sectioned address>";
159  }
160  };
161 
162  std::map<void *, bfdResolver::storedBfd> bfdResolver::bfds;
163  bool bfdResolver::bfd_initialized = false;
164  } // namespace internal
165 
166  std::string get_call_stack() {
167  const size_t MAX_FRAMES = 1000;
168  std::vector<void *> stack(MAX_FRAMES);
169  int num = backtrace(&stack[0], MAX_FRAMES);
170  if (num <= 0) {
171  return "Callstack could not be built.";
172  }
173  while (size_t(num) == stack.size()) {
174  stack.resize(stack.size()*2);
175  num = backtrace(&stack[0], int(stack.size()));
176  }
177  stack.resize(static_cast<size_t>(num));
178  std::string res;
179  //NOTE i=0 corresponds to get_call_stack and is omitted
180  for (size_t i = 1; i < static_cast<size_t>(num); ++i) {
181  res += internal::bfdResolver::resolve(stack[i]) + '\n';
182  }
183  return res;
184  }
185 
186  std::pair<uintptr_t, uintptr_t> get_range_of_section(void * _addr, std::string _name) {
187  return internal::bfdResolver::get_range_of_section(_addr, _name);
188  }
189 
190 } // namespace misc
191  } // namespace xerus
192 
193 #else // No fancy callstack
194  #include <execinfo.h>
195  #include <iomanip>
196  #include <vector>
197  #include <sstream>
198 
199  namespace xerus {
200  namespace misc {
201  std::string get_call_stack() {
202  const size_t MAX_FRAMES = 1000;
203  std::vector<void *> stack(MAX_FRAMES);
204  int num = backtrace(&stack[0], MAX_FRAMES);
205  if (num <= 0) {
206  return "Callstack could not be built.";
207  }
208  while (size_t(num) == stack.size()) {
209  stack.resize(stack.size()*2);
210  num = backtrace(&stack[0], int(stack.size()));
211  }
212  stack.resize(size_t(num));
213  std::stringstream res;
214  //NOTE i=0 corresponds to get_call_stack and is omitted
215  for (size_t i=1; i<size_t(num); ++i) {
216  res << "[0x" << std::setw(int(sizeof(void*)*2)) << std::setfill('0') << std::hex << uintptr_t(stack[i]) << " .?] <bfd not loaded, use addr2line to resolve>\n";
217  }
218  return res.str();
219  }
220 
221  std::pair<uintptr_t, uintptr_t> get_range_of_section(void * _addr, std::string _name) {
222  return std::pair<uintptr_t, uintptr_t>(0,0);
223  }
224  }
225  }
226 #endif
static std::map< void *, storedBfd > bfds
Definition: callStack.cpp:55
static std::pair< uintptr_t, uintptr_t > get_range_of_section(void *_addr, std::string _name)
Definition: callStack.cpp:80
class to load symbols and resolve address pointers
Definition: callStack.cpp:46
Header file for some elementary string manipulation routines.
The main namespace of xerus.
Definition: basic.h:37
Header file for the call-stack functionality.
static std::string resolve(void *address)
Definition: callStack.cpp:105
std::string get_call_stack()
Returns a string representation of the current call-stack (excluding the function itself)...
Definition: callStack.cpp:166
storedBfd(bfd *_abfd, deleter_t *_del)
Definition: callStack.cpp:53
std::unique_ptr< bfd, deleter_t * > abfd
Definition: callStack.cpp:50
static bool ensure_bfd_loaded(Dl_info &_info)
Definition: callStack.cpp:58
Header file for some elementary string manipulation routines.
relevant information belonging to a single bfd
Definition: callStack.cpp:48
std::string XERUS_warn_unused demangle_cxa(const std::string &_cxa)
Demangles the function and class names created by gcc into a more readable format.
static XERUS_force_inline std::string to_string(const bool obj)
Definition: stringFromTo.h:41
std::unique_ptr< asymbol *[]> symbols
Definition: callStack.cpp:51