libfoedus-core
FOEDUS Core Library
rich_backtrace.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2015, Hewlett-Packard Development Company, LP.
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU General Public License as published by the Free
5  * Software Foundation; either version 2 of the License, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11  * more details. You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14  *
15  * HP designates this particular file as subject to the "Classpath" exception
16  * as provided by HP in the LICENSE.txt file that accompanied this code.
17  */
19 
20 #include <backtrace.h>
21 #include <execinfo.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 
25 #include <cstdlib>
26 #include <cstring>
27 #include <iostream>
28 #include <sstream>
29 #include <string>
30 #include <vector>
31 
33 
34 namespace foedus {
35 namespace assorted {
37  enum Constants {
38  kMaxDepth = 64,
39  };
41  void* address_;
42  std::string symbol_;
43  std::string function_;
44  std::string binary_path_;
45  std::string function_offset_;
46  void parse_symbol();
47  };
49  uintptr_t address_;
50  std::string srcfile_;
52  std::string function_;
53  };
54 
57  void release() {
58  error_.clear();
59  glibc_bt_info_.clear();
60  libbt_info_.clear();
61  }
62 
64  release();
65  void* addresses[kMaxDepth];
66  int depth = ::backtrace(addresses, kMaxDepth);
67  char** symbols = ::backtrace_symbols(addresses, depth);
68  for (int i = 1; i < depth; ++i) { // start from 1 to skip this method (call_glibc_backtrace)
69  GlibcBacktraceInfo info;
70  info.address_ = addresses[i];
71  info.symbol_ = symbols[i];
72  info.parse_symbol();
73  glibc_bt_info_.emplace_back(info);
74  }
75  ::free(symbols);
76  }
77  void on_libbt_create_state_error(const char* msg, int errnum) {
78  std::stringstream message;
79  message << "libbacktrace failed to create state. msg=" << msg << ", err=" << os_error(errnum);
80  error_ = message.str();
81  }
82 
83  void on_libbt_full_error(const char* msg, int errnum) {
84  std::stringstream message;
85  std::cerr << "libbacktrace failed to obtain backtrace. msg=" << msg
86  << ", err=" << os_error(errnum);
87  error_ = message.str();
88  }
89 
91  uintptr_t pc,
92  const char *filename,
93  int lineno,
94  const char *function) {
95  if (pc == -1ULL && filename == nullptr && function == nullptr) {
96  // not sure why this happens, but libbacktrace occasionally gives such a dummy entry
97  return;
98  }
99  LibBacktraceInfo info;
100  info.address_ = pc;
101  if (filename) {
102  info.srcfile_ = filename;
103  }
104  info.srclineno_ = lineno;
105  if (function) {
106  info.function_ = function;
107  }
108  libbt_info_.emplace_back(info);
109  }
110 
111  std::vector<std::string> get_results(uint16_t skip);
112 
113  std::string error_;
114  std::vector<GlibcBacktraceInfo> glibc_bt_info_;
115  std::vector<LibBacktraceInfo> libbt_info_;
116 };
117 void libbt_create_state_error(void* data, const char* msg, int errnum) {
118  if (data == nullptr) {
119  std::cerr << "[FOEDUS] wtf. libbt_create_state_error received null" << std::endl;
120  } else {
121  reinterpret_cast<BacktraceContext*>(data)->on_libbt_create_state_error(msg, errnum);
122  }
123 }
124 void libbt_full_error(void* data, const char* msg, int errnum) {
125  if (data == nullptr) {
126  std::cerr << "[FOEDUS] wtf. libbt_full_error received null" << std::endl;
127  } else {
128  reinterpret_cast<BacktraceContext*>(data)->on_libbt_full_error(msg, errnum);
129  }
130 }
131 int libbt_full(void* data, uintptr_t pc, const char *filename, int lineno, const char *function) {
132  if (data == nullptr) {
133  std::cerr << "[FOEDUS] wtf. libbt_full received null" << std::endl;
134  return 1;
135  } else {
136  reinterpret_cast<BacktraceContext*>(data)->on_libbt_full(pc, filename, lineno, function);
137  return 0;
138  }
139 }
140 
142  // Case 1: /foo/hoge/test_dummy(_ZN6foedus5func3Ev+0x9) [0x43e479]
143  // Case 2: /foo/hoge/libfoedus-core.so(+0x3075a5) [0x7f1b6b8b05a5]
144  // Case 3: /lib64/libpthread.so.0() [0x3d1ee0ef90]
145  // Case 4: /lib64/libc.so.6(abort+0x148) [0x3d1e6370f8]
146  std::size_t pos = symbol_.find("(");
147  std::size_t pos2 = symbol_.find(")");
148  if (pos == std::string::npos || pos2 == std::string::npos || pos >= pos2) {
149  return;
150  }
151 
152  binary_path_ = symbol_.substr(0, pos);
153  if (pos2 == pos + 1) { // "()"
154  } else if (symbol_[pos + 1] == '+') {
155  function_offset_ = symbol_.substr(pos + 2, pos2 - pos - 2);
156  } else {
157  std::size_t plus = symbol_.find("+", pos);
158  if (plus == std::string::npos || plus > pos2) {
159  } else {
160  std::string mangled = symbol_.substr(pos + 1, plus - pos - 1);
161  function_ = demangle_type_name(mangled.c_str());
162  function_offset_ = symbol_.substr(plus + 1, pos2 - plus - 1);
163  }
164  }
165 }
166 
167 /* we have tried addr2line via popen, but libbacktrace should be better especially for shared lib.
168 std::string demangle_backtrace_line(const std::string& line, void* raw_address) {
169  std::size_t pos = line.find("(");
170  std::size_t pos2 = line.find(")");
171  if (pos == std::string::npos || pos2 == std::string::npos || pos >= pos2) {
172  return line + " (Failed to demangle)";
173  }
174  std::string binary = line.substr(0, pos);
175  std::string address;
176  if (line[pos + 1] == '+') {
177  address = line.substr(pos + 2, pos2 - pos - 2);
178  } else {
179  std::size_t bra = line.rfind("[");
180  std::size_t bra2 = line.rfind("]");
181  if (bra == std::string::npos || bra2 == std::string::npos || bra >= bra2) {
182  return line + " [Failed to demangle]";
183  }
184  address = line.substr(bra + 1, bra2 - bra - 1);
185  }
186 
187  std::stringstream cmd;
188  cmd << "addr2line -C -f -p -i -e \"" << binary << "\" " << address;
189  std::string cmd_str = cmd.str();
190 
191  FILE* pipe = ::popen(cmd_str.c_str(), "r");
192  if (!pipe) {
193  return line + " (Failed to run addr2line)";
194  }
195  std::string converted;
196  while (true) {
197  char buffer[1024];
198  if (::fgets(buffer, sizeof(buffer), pipe)) {
199  converted += buffer;
200  } else {
201  break;
202  }
203  }
204  ::pclose(pipe);
205  Dl_info info;
206  struct link_map *map;
207  _dl_addr(raw_address, &info, &map, NULL);
208  Dl_info info;
209  if (::dladdr(raw_address, &info) && info.dli_sname && info.dli_saddr) {
210  std::cout << "dladdr(" << raw_address << ")=" << info.dli_sname
211  << ", " << info.dli_fname << "," << info.dli_fbase << "," << info.dli_saddr << std::endl;
212  }
213  return converted + "(raw backtrace: " + line + ")";
214 }
215 */
216 
217 std::vector< std::string > BacktraceContext::get_results(uint16_t skip) {
218  std::vector< std::string > ret;
219  if (!error_.empty()) {
220  // If error happened, add it as the first entry.
221  ret.emplace_back(error_);
222  // still outputs the following as best-effort
223  }
224 
225  for (uint16_t i = skip; i < glibc_bt_info_.size(); ++i) {
226  std::stringstream str;
227  const GlibcBacktraceInfo& libc = glibc_bt_info_[i];
228  bool has_libbt = i < libbt_info_.size();
229  str << "in ";
230  std::string function;
231  if (has_libbt && !libbt_info_[i].function_.empty()) {
232  const LibBacktraceInfo& libbt = libbt_info_[i];
233  // libbacktrace successfully got the information. it can provide the most helpful info
234  str << demangle_type_name(libbt.function_.c_str())
235  << " " << libbt.srcfile_ << ":" << libbt.srclineno_
236  << " (" << libc.binary_path_ << ")"
237  << " [" << assorted::Hex(libbt.address_) << "]";
238  // also append glibc backtrace info.
239  str << " (glibc: " << assorted::Hex(reinterpret_cast<uintptr_t>(libc.address_)) << ")";
240  } else {
241  // sometimes libbacktrace fails to parse a stack even glibc can parse.. probably a bug
242  if (!libc.function_.empty()) {
243  str << demangle_type_name(libc.function_.c_str());
244  } else {
245  str << "???";
246  }
247  str << " : " << libc.symbol_;
248  if (has_libbt) {
249  // also append libbacktrace info.
250  const LibBacktraceInfo& libbt = libbt_info_[i];
251  str << " (libbacktrace:";
252  if (!libbt.srcfile_.empty() || libbt.srclineno_ != 0) {
253  str << " " << libbt.srcfile_ << ":" << libbt.srclineno_;
254  }
255  str << " [" << assorted::Hex(libbt.address_) << "]" << ")";
256  }
257  }
258  ret.emplace_back(str.str());
259  }
260  return ret;
261 }
262 
263 std::vector<std::string> get_backtrace(bool rich) {
264  BacktraceContext context;
265  context.call_glibc_backtrace();
266  if (rich) {
267  // try to use the shiny new libbacktrace
268  backtrace_state* state = ::backtrace_create_state(
269  nullptr, // let libbacktrace to figure out this executable
270  0, // only this thread accesses it
272  &context);
273  if (state) {
274  int full_result = ::backtrace_full(
275  state,
276  0,
277  libbt_full,
279  &context);
280  if (full_result != 0) {
281  // return ret;
282  std::cerr << "[FOEDUS] libbacktrace backtrace_full failed: " << full_result << std::endl;
283  }
284  }
285  }
286 
287  return context.get_results(1); // skip the first (this method itself)
288 }
289 
290 } // namespace assorted
291 } // namespace foedus
void libbt_full_error(void *data, const char *msg, int errnum)
void on_libbt_full(uintptr_t pc, const char *filename, int lineno, const char *function)
int libbt_full(void *data, uintptr_t pc, const char *filename, int lineno, const char *function)
void libbt_create_state_error(void *data, const char *msg, int errnum)
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
std::vector< std::string > get_backtrace(bool rich=true)
Returns the backtrace information of the current stack.
std::vector< GlibcBacktraceInfo > glibc_bt_info_
std::string os_error()
Thread-safe strerror(errno).
std::string demangle_type_name(const char *mangled_name)
Demangle the given C++ type name if possible (otherwise the original string).
Convenient way of writing hex integers to stream.
void on_libbt_full_error(const char *msg, int errnum)
std::vector< std::string > get_results(uint16_t skip)
std::vector< LibBacktraceInfo > libbt_info_
void on_libbt_create_state_error(const char *msg, int errnum)