source: rrlib_util/exception/tTraceableExceptionBase.cpp @ 167:db24b1e80891

14.08
Last change on this file since 167:db24b1e80891 was 167:db24b1e80891, checked in by Tobias Föhst <foehst@…>, 18 months ago

Fixes warning about unused function in release mode

File size: 9.3 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
86#if __linux__
87namespace
88{
89std::string LookupSelf()
90{
91  char self[1024];
92  std::stringstream link;
93  link << "/proc/" << getpid() << "/exe";
94  ssize_t length = readlink(link.str().c_str(), self, sizeof(self));
95  if (static_cast<size_t>(length) < sizeof(self))
96  {
97    self[length] = 0;
98  }
99  return self;
100}
101
102std::vector<tMapEntry> ReadMaps() noexcept
103{
104  std::vector<tMapEntry> map_entries;
105
106  try
107  {
108    std::stringstream proc_maps;
109    proc_maps << "/proc/" << getpid() << "/maps";
110    FILE *maps = fopen(proc_maps.str().c_str(), "r");
111    if (maps)
112    {
113      char buffer[1024];
114      while (!feof(maps))
115      {
116        if (fgets(buffer, sizeof(buffer), maps) != NULL)
117        {
118          std::stringstream input(buffer);
119          char begin[64];
120          std::string size;
121          input >> begin >> size >> size >> size >> size;
122          if (size != "0")
123          {
124            char *end = begin;
125            for (; *end != '-'; ++end);
126            *end++ = 0;
127            map_entries.push_back({reinterpret_cast<void *>(strtoll(begin, nullptr, 16)), reinterpret_cast<void *>(strtoll(end, nullptr, 16)), ""});
128            std::ws(input);
129            std::getline(input, map_entries.back().filename);
130          }
131        }
132      }
133      fclose(maps);
134      auto keep = map_entries.begin();
135      for (auto read = keep; read != map_entries.end(); ++read)
136      {
137        if (read != keep)
138        {
139          if (read->begin == keep->end && read->filename == keep->filename)
140          {
141            keep->end = read->end;
142          }
143          else
144          {
145            *(++keep) = *read;
146          }
147        }
148      }
149      map_entries.erase(++keep, map_entries.end());
150    }
151  }
152  catch (std::bad_alloc &error)
153  {
154    std::cerr << "Error during address mapping: " << error.what() << std::endl;
155    map_entries.clear();
156  }
157
158  return map_entries;
159}
160
161std::string LookupLocation(void *address, const std::vector<tMapEntry> &map_entries, const std::string &self)
162{
163  try
164  {
165    for (auto it = map_entries.begin(); it != map_entries.end(); ++it)
166    {
167      if (it->begin <= address && address <= it->end)
168      {
169        std::string location;
170
171        if (it->filename != self)
172        {
173          address = reinterpret_cast<void *>(reinterpret_cast<char *>(address) - reinterpret_cast<char *>(it->begin));
174        }
175        address = reinterpret_cast<void *>(reinterpret_cast<char *>(address) - 1);
176
177        std::stringstream run_addr2line;
178        std::string filename = it->filename;
179        size_t position = 0;
180        while ((position = filename.find_first_of("\\\"", position)) != filename.npos)
181        {
182          filename.insert(position, "\\");
183          position += 2;
184        }
185        run_addr2line << "addr2line -fpCe \"" << filename << "\" " << address << " 2> /dev/null";
186        FILE *pipe = popen(run_addr2line.str().c_str(), "r");
187        if (pipe)
188        {
189          while (!feof(pipe))
190          {
191            char buffer[128];
192            if (fgets(buffer, sizeof(buffer), pipe) != NULL)
193            {
194              location += buffer;
195            }
196          }
197        }
198        pclose(pipe);
199        location.erase(location.find_last_not_of("\n\r\t ") + 1);
200
201        if (!location.empty())
202        {
203          auto unknown = location.find("at ??");
204          if (unknown != location.npos)
205          {
206            location.erase(unknown);
207            location += "from " + it->filename;
208          }
209          return location;
210        }
211      }
212    }
213  }
214  catch (std::bad_alloc &error)
215  {
216    std::cerr << "Error during location lookup: " << error.what() << std::endl;
217  }
218
219  return "??";
220}
221
222#ifndef NDEBUG
223std::terminate_handler original_terminate = nullptr;
224
225void terminate() noexcept
226{
227  try
228  {
229    auto exception = std::current_exception();
230    if (exception)
231    {
232      std::rethrow_exception(exception);
233    }
234  }
235  catch (const tTraceableExceptionBase &exception)
236  {
237    std::cerr << "=== Uncaught traceable exception ===\nBacktrace:\n" << exception.Backtrace() << std::endl;
238  }
239  catch (...)
240  {}
241
242  original_terminate();
243  abort();
244}
245#endif
246
247}
248
249#endif
250
251//----------------------------------------------------------------------
252// tTraceableExceptionBase constructors
253//----------------------------------------------------------------------
254tTraceableExceptionBase::tTraceableExceptionBase() :
255#if defined(NDEBUG) || !__linux__
256  stack_trace_depth(0)
257{}
258#else
259  stack_trace_depth(backtrace(this->stack_trace, cMAX_STACK_TRACE_DEPTH))
260{
261  if (!original_terminate)
262  {
263    original_terminate = std::set_terminate(terminate);
264  }
265}
266#endif
267
268//----------------------------------------------------------------------
269// tTraceableExceptionBase destructor
270//----------------------------------------------------------------------
271tTraceableExceptionBase::~tTraceableExceptionBase()
272{}
273
274//----------------------------------------------------------------------
275// tTraceableExceptionBase SomeExampleMethod
276//----------------------------------------------------------------------
277const char *tTraceableExceptionBase::Backtrace() const noexcept
278{
279  if (this->stack_trace_depth == 0)
280  {
281    return "<Backtrace was optimized out>";
282  }
283
284#if __linux__
285  if (this->buffered_backtrace.empty())
286  {
287    try
288    {
289      std::stringstream backtrace;
290
291      std::string self = LookupSelf();
292      std::vector<tMapEntry> map_entries = ReadMaps();
293
294      char address_example[64];
295      snprintf(address_example, sizeof(address_example), "%p", reinterpret_cast<void *>(-1));
296      char format_string[8];
297      snprintf(format_string, sizeof(format_string), "0x%%0%zux", strlen(address_example) - 2);
298
299      for (size_t i = cCALLS_TO_SKIP; i < this->stack_trace_depth; ++i)
300      {
301        char formatted_address[sizeof(address_example)];
302        snprintf(formatted_address, sizeof(formatted_address), format_string, this->stack_trace[i]);
303        backtrace << "#" << (i - cCALLS_TO_SKIP) << "  " << formatted_address;
304        backtrace << " in " << LookupLocation(this->stack_trace[i], map_entries, self);
305        backtrace << "\n";
306      }
307
308      this->buffered_backtrace = backtrace.str();
309    }
310    catch (std::bad_alloc &error)
311    {
312      std::cerr << "Error while creating backtrace: " << error.what() << std::endl;
313      return "";
314    }
315  }
316  return this->buffered_backtrace.c_str();
317#else
318  // just to suppress warnings, should never be reached
319  return "<No backtrace available>";
320#endif
321}
322
323//----------------------------------------------------------------------
324// End of namespace declaration
325//----------------------------------------------------------------------
326}
327}
Note: See TracBrowser for help on using the repository browser.