μHAL (v2.6.5)
Part of the IPbus software repository
BacktraceSymbols.cpp
Go to the documentation of this file.
1 #ifdef USE_BACKTRACE
2 
3 /*
4 ---------------------------------------------------------------------------
5 
6  This file is part of uHAL.
7 
8  uHAL is a hardware access library and programming framework
9  originally developed for upgrades of the Level-1 trigger of the CMS
10  experiment at CERN.
11 
12  uHAL is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  uHAL is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with uHAL. If not, see <http://www.gnu.org/licenses/>.
24 
25 
26  Andrew Rose, Imperial College, London
27  email: awr01 <AT> imperial.ac.uk
28 
29  Marc Magrans de Abril, CERN
30  email: marc.magrans.de.abril <AT> cern.ch
31 
32 ---------------------------------------------------------------------------
33 */
34 
35 /*
36  This is an object-oriented rewrite of an implementation by Jeff Muizelaar
37  It passes off all horrid memory allocations
38  It has no global variables
39  Copyright 2013 Andrew W. Rose
40 */
41 
42 /*
43  A hacky replacement for backtrace_symbols in glibc
44 
45  backtrace_symbols in glibc looks up symbols using dladdr which is limited in
46  the symbols that it sees. libbacktracesymbols opens the executable and shared
47  libraries using libbfd and will look up backtrace information using the symbol
48  table and the dwarf lLine information.
49 
50  It may make more sense for this program to use libelf instead of libbfd.
51  However, I have not investigated that yet.
52 
53  Derived from addr2line.c from GNU Binutils by Jeff Muizelaar
54 
55  Copyright 2007 Jeff Muizelaar
56 */
57 
58 /* addr2line.c -- convert addresses to lLine number and lFunction name
59  Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
60  Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
61 
62  This file was part of GNU Binutils.
63 
64  This program is free software; you can redistribute it and/or modify
65  it under the terms of the GNU General Public License as published by
66  the Free Software Foundation; either version 2, or (at your option)
67  any later version.
68 
69  This program is distributed in the hope that it will be useful,
70  but WITHOUT ANY WARRANTY; without even the implied warranty of
71  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
72  GNU General Public License for more details.
73 
74  You should have received a copy of the GNU General Public License
75  along with this program; if not, write to the Free Software
76  Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
77 
78 
79 #include <config.h>
80 #include <string.h>
81 #include <execinfo.h>
82 #include <bfd.h>
83 #define HAVE_DECL_BASENAME 1
84 #include <libiberty.h>
85 #include <dlfcn.h>
86 #include <link.h>
87 #include <cxxabi.h>
88 #include <stdlib.h>
89 
90 #include <cstddef>
91 #include <vector>
92 #include <string>
93 #include <iostream>
94 
96 
97 
98 namespace Backtrace
99 {
100 
101  /* helper struct for passing data to callbacks */
102  struct FindFilesHelperStruct
103  {
104  const char* lFile;
105  void* address;
106  void* base;
107  void* hdr;
108  };
109 
110  /* helper struct for passing data to callbacks */
111  struct FindAddressHelperStruct
112  {
113  bfd_vma pc;
114  asymbol** syms;
115  uint32_t found;
116  TracePoint tracepoint;
117  };
118 
119 
120  /* Callback for bfd_map_over_sections */
121  static void FindAddressInSection ( bfd* aBfd, asection* aSection, void* aData )
122  {
123  FindAddressHelperStruct* lFindAddress = ( FindAddressHelperStruct* ) ( aData );
124 
125  if ( ( bfd_get_section_flags ( aBfd, aSection ) & SEC_ALLOC ) == 0 )
126  {
127  return;
128  }
129 
130  bfd_vma vma = bfd_get_section_vma ( aBfd, aSection );
131 
132  if ( lFindAddress->pc < vma )
133  {
134  return;
135  }
136 
137  bfd_size_type lSize = bfd_section_size ( aBfd, aSection );
138 
139  if ( lFindAddress->pc >= vma + lSize )
140  {
141  return;
142  }
143 
144  const char* lFile;
145 
146  const char* lFunction;
147 
148  uint32_t lLine;
149 
150  lFindAddress->found = bfd_find_nearest_line ( aBfd, aSection, lFindAddress->syms, lFindAddress->pc - vma, &lFile, &lFunction, &lLine );
151 
152  if ( !lFindAddress->found )
153  {
154  return;
155  }
156 
157  if ( lFunction == NULL || *lFunction == '\0' )
158  {
159  lFindAddress->tracepoint.function = "??";
160  }
161  else
162  {
163  if ( !strncmp ( lFunction,"_Z",2 ) )
164  {
165  //We have mutex locked the top-level call, so make this static to prevent unnecessary memory allocations
166  static int lStatus ( 0 );
167  static std::size_t lSize ( 65536 );
168  static char lDemangled[ 65536 ];
169  abi::__cxa_demangle ( lFunction , lDemangled , &lSize , &lStatus );
170 
171  if ( lStatus )
172  {
173  lFindAddress->tracepoint.function = lFunction;
174  }
175  else
176  {
177  lFindAddress->tracepoint.function = lDemangled;
178  }
179  }
180  else
181  {
182  lFindAddress->tracepoint.function = lFunction;
183  }
184  }
185 
186  if ( lFile == NULL || *lFile == '\0' )
187  {
188  lFindAddress->tracepoint.file = "??";
189  }
190  else
191  {
192  lFindAddress->tracepoint.file = lFile;
193  }
194 
195  lFindAddress->tracepoint.line = lLine;
196  }
197 
198 
199 
200  /* Process a file. */
201  static bool ProcessFiles ( const char* aFilename, bfd_vma* aAddr, int aNaddr , TracePoint& aRet )
202  {
203  FindAddressHelperStruct lFindAddress;
204  bfd* lBfd = bfd_openr ( aFilename, NULL );
205 
206  if ( lBfd == NULL )
207  {
208  throw 0;
209  }
210 
211  if ( bfd_check_format ( lBfd, bfd_archive ) )
212  {
213  std::cout << aFilename << ": can not get addresses from archive" << std::endl;
214  throw 0;
215  }
216 
217  char** matching;
218 
219  if ( !bfd_check_format_matches ( lBfd, bfd_object, &matching ) )
220  {
221  throw 0;
222  }
223 
224  if ( ( bfd_get_file_flags ( lBfd ) & HAS_SYMS ) == 0 )
225  {
226  return false;
227  }
228 
229  uint32_t lSize;
230  int32_t lSymCount = bfd_read_minisymbols ( lBfd, false, ( void** ) & lFindAddress.syms, &lSize );
231 
232  if ( lSymCount < 0 )
233  {
234  throw 0;
235  }
236 
237  if ( lSymCount == 0 )
238  {
239  lSymCount = bfd_read_minisymbols ( lBfd, true /* dynamic */ , ( void** ) & lFindAddress.syms, &lSize );
240  }
241 
242  while ( aNaddr )
243  {
244  lFindAddress.pc = aAddr[aNaddr-1];
245  lFindAddress.found = false;
246  bfd_map_over_sections ( lBfd, FindAddressInSection, &lFindAddress );
247 
248  if ( lFindAddress.found )
249  {
250  break;
251  }
252 
253  aNaddr--;
254  }
255 
256  if ( lFindAddress.syms )
257  {
258  free ( lFindAddress.syms );
259  lFindAddress.syms = NULL;
260  }
261 
262  bfd_close ( lBfd );
263  aRet = lFindAddress.tracepoint;
264  return true;
265  }
266 
267 
268  /* Callback used by dl_iterate_phdr */
269  static int FindMatchingFiles ( struct dl_phdr_info* aInfo, size_t aSize, void* aData )
270  {
271  FindFilesHelperStruct* match = ( FindFilesHelperStruct* ) ( aData );
272  /* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */
273  const ElfW ( Phdr ) *lPhdr;
274  ElfW ( Addr ) lLoadBase = aInfo->dlpi_addr;
275  lPhdr = aInfo->dlpi_phdr;
276 
277  for ( uint32_t n = 0 ; n != aInfo->dlpi_phnum ; ++n , ++lPhdr )
278  {
279  if ( lPhdr->p_type == PT_LOAD )
280  {
281  ElfW ( Addr ) lVaddr = lPhdr->p_vaddr + lLoadBase;
282 
283  if ( match->address >= ( void* ) ( lVaddr ) && match->address < ( void* ) ( lVaddr + lPhdr->p_memsz ) )
284  {
285  /* we found a match */
286  match->lFile = aInfo->dlpi_name;
287  match->base = ( void* ) ( aInfo->dlpi_addr );
288  }
289  }
290  }
291 
292  return 0;
293  }
294 
295 
296  void Backtrace ( std::vector< void* >& aBacktrace )
297  {
298  size_t lSize = backtrace ( & ( aBacktrace[0] ) , aBacktrace.size() );
299  aBacktrace.resize ( lSize );
300  }
301 
302 
303 
304 
305  boost::mutex mBacktraceMutex;
306 
307 
308  std::vector< TracePoint > BacktraceSymbols ( const std::vector< void* >& aBacktrace )
309  {
310  boost::lock_guard<boost::mutex> lLock ( mBacktraceMutex );
311  std::vector< TracePoint > lRet;
312  lRet.reserve ( aBacktrace.size() );
313  bfd_init();
314 
315  for ( std::vector< void* >::const_iterator x=aBacktrace.begin(); x!=aBacktrace.end(); ++x )
316  {
317  struct FindFilesHelperStruct match;
318  match.address = *x;
319  dl_iterate_phdr ( FindMatchingFiles, &match );
320  bfd_vma aAddr = ( std::size_t ) ( *x ) - ( std::size_t ) ( match.base );
321  TracePoint lTracePoint;
322 
323  if ( ProcessFiles ( ( ( match.lFile && strlen ( match.lFile ) ) ?match.lFile:"/proc/self/exe" ) , &aAddr, 1 , lTracePoint ) )
324  {
325  lRet.push_back ( lTracePoint );
326  }
327  }
328 
329  return lRet;
330  }
331 
332 }
333 
334 #endif