callStack.cpp 8.07 KB
Newer Older
Baum's avatar
Baum committed
1
// Xerus - A General Purpose Tensor Library
2
// Copyright (C) 2014-2017 Benjamin Huber and Sebastian Wolf. 
Baum's avatar
Baum committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// 
// Xerus is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
// 
// Xerus is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// 
// You should have received a copy of the GNU Affero General Public License
// along with Xerus. If not, see <http://www.gnu.org/licenses/>.
//
// For further information on Xerus visit https://libXerus.org 
// or contact us at contact@libXerus.org.

20 21 22 23 24
/**
 * @file
 * @brief Implementation of the callstack functionality and respective helper class bfdResolver.
 */

25
#include <xerus/misc/callStack.h>
Baum's avatar
Baum committed
26

27
#ifndef XERUS_NO_FANCY_CALLSTACK
28
    #include <execinfo.h>
29 30 31 32 33 34 35 36
    
	// workaround for deliberately incompatible bfd.h header files on some systems.
	#ifndef PACKAGE
		#define PACKAGE
	#endif
	#ifndef PACKAGE_VERSION
		#define PACKAGE_VERSION
	#endif
37 38 39
    #include <bfd.h>
    #include <dlfcn.h>
    #include <unistd.h>
40
	#include <iostream>
41 42 43 44
    #include <sstream>
    #include <memory>
    #include <map>
    #include <vector>
45
    #include <xerus/misc/stringFromTo.h>
46 47
    #include <xerus/misc/stringUtilities.h>

48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
namespace xerus { namespace misc { namespace internal {
	/**
	 * @brief class to load symbols and resolve address pointers
	 * @details uses binutils to interpret bfds. caches the bfd data to only read them once per run of the application
	 * for the use of dladdr see also https://sourceware.org/git/?p=glibc.git;a=blob;f=debug/backtracesyms.c
	 */
	struct bfdResolver {
		/// @brief relevant information belonging to a single bfd
		struct storedBfd {
			typedef bfd_boolean(deleter_t)(bfd*);
			std::unique_ptr<bfd, deleter_t*> abfd;
			std::unique_ptr<asymbol*[]> symbols;
			intptr_t offset;
			storedBfd(bfd *_abfd, deleter_t *_del) : abfd(_abfd, _del) {}
		};
		static std::map<void *, storedBfd> bfds;
		static bool bfd_initialized;
		
		static bool ensure_bfd_loaded(Dl_info &_info) {
			// load the corresponding bfd file (from file or map)
			if (bfds.count(_info.dli_fbase) == 0) {
Sebastian Wolf's avatar
Sebastian Wolf committed
69
				std::unique_ptr<storedBfd> newBfd(new storedBfd(bfd_openr(_info.dli_fname, nullptr), &bfd_close));
70 71 72 73 74 75 76 77
				if (!newBfd->abfd) {
					return false;
				}
				bfd_check_format(newBfd->abfd.get(),bfd_object);
				long storageNeeded = bfd_get_symtab_upper_bound(newBfd->abfd.get());
				if (storageNeeded < 0) {
					return false;
				}
78
				newBfd->symbols.reset(reinterpret_cast<asymbol**>(new char[static_cast<size_t>(storageNeeded)]));
79 80
				/*size_t numSymbols = */bfd_canonicalize_symtab(newBfd->abfd.get(), newBfd->symbols.get());
				
81
				newBfd->offset = reinterpret_cast<intptr_t>(_info.dli_fbase);
82
				
83
				bfds.insert(std::pair<void *, storedBfd>(_info.dli_fbase, std::move(*newBfd)));
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
			} 
			return true;
		}
		
		static std::pair<uintptr_t, uintptr_t> get_range_of_section(void * _addr, std::string _name) {
			if (!bfd_initialized) {
				bfd_init();
				bfd_initialized = true;
			}
			
			// get path and offset of shared object that contains this address
			Dl_info info;
			dladdr(_addr, &info);
			if (info.dli_fbase == nullptr) {
				return std::pair<uintptr_t, uintptr_t>(0,0);
			}
			
			if (!ensure_bfd_loaded(info)) {
				return std::pair<uintptr_t, uintptr_t>(0,0);
			}
			storedBfd &currBfd = bfds.at(info.dli_fbase);
			
Fuchsi*'s avatar
Fuchsi* committed
106 107 108
			asection *section = bfd_get_section_by_name(currBfd.abfd.get(), _name.c_str());
			if (section == nullptr) {
				return std::pair<uintptr_t, uintptr_t>(0,0);
109
			}
110
			return std::pair<uintptr_t, uintptr_t>(section->vma, section->vma+section->size);
111 112 113 114 115 116 117 118 119
		}
		
		static std::string resolve(void *address) {
			if (!bfd_initialized) {
				bfd_init();
				bfd_initialized = true;
			}
				
			std::stringstream res;
120
			res << "[0x" << std::setw(int(sizeof(void*)*2)) << std::setfill('0') << std::hex << reinterpret_cast<uintptr_t>(address);
121 122 123 124 125 126 127 128 129
			
			// get path and offset of shared object that contains this address
			Dl_info info;
			dladdr(address, &info);
			if (info.dli_fbase == nullptr) {
				return res.str()+" .?] <object to address not found>";
			}
			
			if (!ensure_bfd_loaded(info)) {
130
				return res.str()+" .?] <could not open object file>";
131 132 133 134
			}
			storedBfd &currBfd = bfds.at(info.dli_fbase);
			
			asection *section = currBfd.abfd->sections;
135
			const bool relative = section->vma < static_cast<uintptr_t>(currBfd.offset);
136 137
	// 		std::cout << '\n' << "sections:\n";
			while (section != nullptr) {
138
				const intptr_t offset = reinterpret_cast<intptr_t>(address) - (relative ? currBfd.offset : 0) - static_cast<intptr_t>(section->vma);
139 140 141
	// 			std::cout << section->name << " " << section->id << " file: " << section->filepos << " flags: " << section->flags 
	// 						<< " vma: " << std::hex << section->vma << " - " << std::hex << (section->vma+section->size) << std::endl;
				
142
				if (offset < 0 || static_cast<size_t>(offset) > section->size) {
143 144 145 146
					section = section->next;
					continue;
				}
				res << ' ' << section->name;
147
				if ((section->flags | SEC_CODE) == 0u) {
148 149 150 151 152 153 154
					return res.str()+"] <non executable address>";
				}
				// get more info on legal addresses
				const char *file;
				const char *func;
				unsigned line;
				if (bfd_find_nearest_line(currBfd.abfd.get(), section, currBfd.symbols.get(), offset, &file, &func, &line)) {
155
					if (file != nullptr) {
156 157
						return res.str()+"] "+std::string(file)+":"+to_string(line)+" (inside "+demangle_cxa(func)+")";
					}
158 159 160 161
					if (info.dli_saddr != nullptr) {
						return res.str()+"] ??:? (inside "+demangle_cxa(func)+" +0x"+std::to_string(reinterpret_cast<uintptr_t>(address)-reinterpret_cast<uintptr_t>(info.dli_saddr))+")";
					}
					return res.str()+"] ??:? (inside "+demangle_cxa(func)+")";
162
				}
163
				return res.str()+"] <bfd_error> (inside "+demangle_cxa((info.dli_sname != nullptr?info.dli_sname:""))+")";
164 165 166 167 168
			}
	// 		std::cout << " ---- sections end ------ " << std::endl;
			return res.str()+" .none] <not sectioned address>";
		}
	};
169

170 171 172
	std::map<void *, bfdResolver::storedBfd> bfdResolver::bfds;
	bool bfdResolver::bfd_initialized = false;
	} // namespace internal
173

174
	std::string get_call_stack() {
175
		const size_t MAX_FRAMES = 1000;
176 177 178 179 180
		std::vector<void *> stack(MAX_FRAMES);
		int num = backtrace(&stack[0], MAX_FRAMES);
		if (num <= 0) {
			return "Callstack could not be built.";
		}
181 182
		while (size_t(num) == stack.size()) {
			stack.resize(stack.size()*2);
183
			num = backtrace(&stack[0], int(stack.size()));
184
		}
185
		stack.resize(static_cast<size_t>(num));
186 187
		std::string res;
		//NOTE i=0 corresponds to get_call_stack and is omitted
188
		for (size_t i = 1; i < static_cast<size_t>(num); ++i) {
189 190 191 192
			res += internal::bfdResolver::resolve(stack[i]) + '\n';
		}
		return res;
	}
193

194 195 196 197
	std::pair<uintptr_t, uintptr_t> get_range_of_section(void * _addr, std::string _name) {
		return internal::bfdResolver::get_range_of_section(_addr, _name);
	}
	
198 199
} // namespace misc
 } // namespace xerus
200

201
#else // No fancy callstack
202 203 204 205
	#include <execinfo.h>
	#include <iomanip>
	#include <vector>
	#include <sstream>
206

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
	namespace xerus {
		namespace misc {
			std::string get_call_stack() {
				const size_t MAX_FRAMES = 1000;
				std::vector<void *> stack(MAX_FRAMES);
				int num = backtrace(&stack[0], MAX_FRAMES);
				if (num <= 0) {
					return "Callstack could not be built.";
				}
				while (size_t(num) == stack.size()) {
					stack.resize(stack.size()*2);
					num = backtrace(&stack[0], int(stack.size()));
				}
				stack.resize(size_t(num));
				std::stringstream res;
				//NOTE i=0 corresponds to get_call_stack and is omitted
				for (size_t i=1; i<size_t(num); ++i) {
					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";
				}
				return res.str();
			}
228

229 230 231 232 233
			std::pair<uintptr_t, uintptr_t> get_range_of_section(void * _addr, std::string _name) {
				return std::pair<uintptr_t, uintptr_t>(0,0);
			}
		}
	}
234
#endif