source: rrlib_util/exception/tTraceableExceptionBase.cpp @ 168:8f09365e2622

17.03
Last change on this file since 168:8f09365e2622 was 168:8f09365e2622, checked in by Tobias Föhst <foehst@…>, 14 months ago

Merge with 14.08

File size: 9.4 KB
Line 
1//
2// You received this file as part of RRLib
3// Robotics Research Library
4//
5// Copyright (C) Finroc GbR (finroc.org)
6//
7// This program is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License along
18// with this program; if not, write to the Free Software Foundation, Inc.,
19// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20//
21//----------------------------------------------------------------------
22/*!\file    rrlib/util/exception/tTraceableExceptionBase.cpp
23 *
24 * \author  Tobias Föhst
25 *
26 * \date    2014-01-24
27 *
28 */
29//----------------------------------------------------------------------
30#include "rrlib/util/exception/tTraceableExceptionBase.h"
31
32//----------------------------------------------------------------------
33// External includes (system with <>, local with "")
34//----------------------------------------------------------------------
35#include <stdexcept>
36#include <iostream>
37#include <sstream>
38#include <cstring>
39#include <vector>
40
41#if __linux__
42#include <execinfo.h>
43#include <unistd.h>
44#endif
45
46//----------------------------------------------------------------------
47// Internal includes with ""
48//----------------------------------------------------------------------
49
50//----------------------------------------------------------------------
51// Debugging
52//----------------------------------------------------------------------
53#include <cassert>
54
55//----------------------------------------------------------------------
56// Namespace usage
57//----------------------------------------------------------------------
58
59//----------------------------------------------------------------------
60// Namespace declaration
61//----------------------------------------------------------------------
62namespace rrlib
63{
64namespace util
65{
66
67//----------------------------------------------------------------------
68// Forward declarations / typedefs / enums
69//----------------------------------------------------------------------
70struct tMapEntry
71{
72  void *begin;
73  void *end;
74  std::string filename;
75};
76
77//----------------------------------------------------------------------
78// Const values
79//----------------------------------------------------------------------
80const size_t cCALLS_TO_SKIP = 2;
81
82//----------------------------------------------------------------------
83// Implementation
84//----------------------------------------------------------------------
85#ifndef __linux__
86#define RRLIB_UTIL_EXCEPTION_DISABLE_TRACING
87#endif
88
89#ifndef RRLIB_UTIL_EXCEPTION_DISABLE_TRACING
90namespace
91{
92std::string LookupSelf()
93{
94  char self[1024];
95  std::stringstream link;
96  link << "/proc/" << getpid() << "/exe";
97  ssize_t length = readlink(link.str().c_str(), self, sizeof(self));
98  if (static_cast<size_t>(length) < sizeof(self))
99  {
100    self[length] = 0;
101  }
102  return self;
103}
104
105std::vector<tMapEntry> ReadMaps() noexcept
106{
107  std::vector<tMapEntry> map_entries;
108
109  try
110  {
111    std::stringstream proc_maps;
112    proc_maps << "/proc/" << getpid() << "/maps";
113    FILE *maps = fopen(proc_maps.str().c_str(), "r");
114    if (maps)
115    {
116      char buffer[1024];
117      while (!feof(maps))
118      {
119        if (fgets(buffer, sizeof(buffer), maps) != NULL)
120        {
121          std::stringstream input(buffer);
122          char begin[64];
123          std::string size;
124          input >> begin >> size >> size >> size >> size;
125          if (size != "0")
126          {
127            char *end = begin;
128            for (; *end != '-'; ++end);
129            *end++ = 0;
130            map_entries.push_back({reinterpret_cast<void *>(strtoll(begin, nullptr, 16)), reinterpret_cast<void *>(strtoll(end, nullptr, 16)), ""});
131            std::ws(input);
132            std::getline(input, map_entries.back().filename);
133          }
134        }
135      }
136      fclose(maps);
137      auto keep = map_entries.begin();
138      for (auto read = keep; read != map_entries.end(); ++read)
139      {
140        if (read != keep)
141        {
142          if (read->begin == keep->end && read->filename == keep->filename)
143          {
144            keep->end = read->end;
145          }
146          else
147          {
148            *(++keep) = *read;
149          }
150        }
151      }
152      map_entries.erase(++keep, map_entries.end());
153    }
154  }
155  catch (std::bad_alloc &error)
156  {
157    std::cerr << "Error during address mapping: " << error.what() << std::endl;
158    map_entries.clear();
159  }
160
161  return map_entries;
162}
163
164std::string LookupLocation(void *address, const std::vector<tMapEntry> &map_entries, const std::string &self)
165{
166  try
167  {
168    for (auto it = map_entries.begin(); it != map_entries.end(); ++it)
169    {
170      if (it->begin <= address && address <= it->end)
171      {
172        std::string location;
173
174        if (it->filename != self)
175        {
176          address = reinterpret_cast<void *>(reinterpret_cast<char *>(address) - reinterpret_cast<char *>(it->begin));
177        }
178        address = reinterpret_cast<void *>(reinterpret_cast<char *>(address) - 1);
179
180        std::stringstream run_addr2line;
181        std::string filename = it->filename;
182        size_t position = 0;
183        while ((position = filename.find_first_of("\\\"", position)) != filename.npos)
184        {
185          filename.insert(position, "\\");
186          position += 2;
187        }
188        run_addr2line << "addr2line -fpCe \"" << filename << "\" " << address << " 2> /dev/null";
189        FILE *pipe = popen(run_addr2line.str().c_str(), "r");
190        if (pipe)
191        {
192          while (!feof(pipe))
193          {
194            char buffer[128];
195            if (fgets(buffer, sizeof(buffer), pipe) != NULL)
196            {
197              location += buffer;
198            }
199          }
200        }
201        pclose(pipe);
202        location.erase(location.find_last_not_of("\n\r\t ") + 1);
203
204        if (!location.empty())
205        {
206          auto unknown = location.find("at ??");
207          if (unknown != location.npos)
208          {
209            location.erase(unknown);
210            location += "from " + it->filename;
211          }
212          return location;
213        }
214      }
215    }
216  }
217  catch (std::bad_alloc &error)
218  {
219    std::cerr << "Error during location lookup: " << error.what() << std::endl;
220  }
221
222  return "??";
223}
224
225#ifndef NDEBUG
226std::terminate_handler original_terminate = nullptr;
227
228void terminate() noexcept
229{
230  try
231  {
232    auto exception = std::current_exception();
233    if (exception)
234    {
235      std::rethrow_exception(exception);
236    }
237  }
238  catch (const tTraceableExceptionBase &exception)
239  {
240    std::cerr << "=== Uncaught traceable exception ===\nBacktrace:\n" << exception.Backtrace() << std::endl;
241  }
242  catch (...)
243  {}
244
245  original_terminate();
246  abort();
247}
248#endif
249
250}
251
252#endif
253
254//----------------------------------------------------------------------
255// tTraceableExceptionBase constructors
256//----------------------------------------------------------------------
257tTraceableExceptionBase::tTraceableExceptionBase() :
258#if defined(NDEBUG) || defined(RRLIB_UTIL_EXCEPTION_DISABLE_TRACING)
259  stack_trace_depth(0)
260{}
261#else
262  stack_trace_depth(backtrace(this->stack_trace, cMAX_STACK_TRACE_DEPTH))
263{
264  if (!original_terminate)
265  {
266    original_terminate = std::set_terminate(terminate);
267  }
268}
269#endif
270
271//----------------------------------------------------------------------
272// tTraceableExceptionBase destructor
273//----------------------------------------------------------------------
274tTraceableExceptionBase::~tTraceableExceptionBase()
275{}
276
277//----------------------------------------------------------------------
278// tTraceableExceptionBase SomeExampleMethod
279//----------------------------------------------------------------------
280const char *tTraceableExceptionBase::Backtrace() const noexcept
281{
282  if (this->stack_trace_depth == 0)
283  {
284    return "<Backtrace was optimized out>";
285  }
286
287#ifndef RRLIB_UTIL_EXCEPTION_DISABLE_TRACING
288  if (this->buffered_backtrace.empty())
289  {
290    try
291    {
292      std::stringstream backtrace;
293
294      std::string self = LookupSelf();
295      std::vector<tMapEntry> map_entries = ReadMaps();
296
297      char address_example[64];
298      snprintf(address_example, sizeof(address_example), "%p", reinterpret_cast<void *>(-1));
299      char format_string[8];
300      snprintf(format_string, sizeof(format_string), "0x%%0%zux", strlen(address_example) - 2);
301
302      for (size_t i = cCALLS_TO_SKIP; i < this->stack_trace_depth; ++i)
303      {
304        char formatted_address[sizeof(address_example)];
305        snprintf(formatted_address, sizeof(formatted_address), format_string, this->stack_trace[i]);
306        backtrace << "#" << (i - cCALLS_TO_SKIP) << "  " << formatted_address;
307        backtrace << " in " << LookupLocation(this->stack_trace[i], map_entries, self);
308        backtrace << "\n";
309      }
310
311      this->buffered_backtrace = backtrace.str();
312    }
313    catch (std::bad_alloc &error)
314    {
315      std::cerr << "Error while creating backtrace: " << error.what() << std::endl;
316      return "";
317    }
318  }
319  return this->buffered_backtrace.c_str();
320#else
321  // just to suppress warnings, should never be reached
322  return "<No backtrace available>";
323#endif
324}
325
326//----------------------------------------------------------------------
327// End of namespace declaration
328//----------------------------------------------------------------------
329}
330}
Note: See TracBrowser for help on using the repository browser.