fw4spl
installSIGSEVBacktrace.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2015.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include <fwCore/base.hpp>
8 #ifdef __APPLE__
9 #define _XOPEN_SOURCE
10 #endif
11 
12 #include "monitor/installSIGSEVBacktrace.hpp"
13 
14 #ifndef WIN32
15 
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <signal.h>
19 #include <execinfo.h>
20 
21 #include <cxxabi.h>
22 #include <cstdlib>
23 #include <iostream>
24 #include <string>
25 #include <string.h>
26 #include <sstream>
27 
28 
29 /* get REG_EIP from ucontext.h */
30 //#define __USE_GNU // already defined
31 #if !defined(__APPLE__) || (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - 1060) <= 0
32 #include <ucontext.h>
33 #else
34 #include <sys/ucontext.h>
35 #endif
36 //VAG hack :
37 #define REG_EIP 14
38 //VAG : ucontext.h define REG_EIP but compiler doesn't see it ... I simulate its value
39 
40 #else // WIN32
41 
42 #include <list>
43 #include <string>
44 #include <sstream>
45 
46 #include <boost/lexical_cast.hpp>
47 #include <windows.h>
48 #include <dbghelp.h>
49 
50 #endif
51 
52 namespace monitor
53 {
54 
56 {
57  char *p = (char *)0xdeadbeef;
58  *p = 10; /* CRASH here!! */
59 }
60 
61 #ifndef WIN32
62 std::string demangle( std::string mangled )
63 {
64  char * c_demangled = abi::__cxa_demangle( mangled.c_str(), 0, 0, 0);
65  if (c_demangled)
66  {
67  std::string res(c_demangled);
68  free(c_demangled);
69  return res;
70  }
71  else
72  {
73  return mangled;
74  }
75 }
76 
77 std::string decode( char *message)
78 {
79  std::string msg(message);
80  std::string res(message);
81 
82  std::string::size_type popen = msg.find('(');
83  if ( popen != std::string::npos )
84  {
85  std::string::size_type plus = msg.find('+');
86  res = std::string(message,popen+1) + " ";
87  std::string mangled( message, popen+1, plus -popen -1 );
88  res += demangle(mangled) + " ";
89  res += std::string( message + plus, message + strlen(message) );
90  }
91  return res;
92 }
93 
94 void bt_sighandler(int sig, siginfo_t *info,
95  void *secret)
96 {
97 
98  void *trace[16];
99  char **messages = (char **)NULL;
100  int i, trace_size = 0;
101  ucontext_t *uc = (ucontext_t *)secret;
102 
103  std::stringstream ss;
104  ss << "Got signal " << sig;
105 
106  /* Do something useful with siginfo_t */
107  if (sig == SIGSEGV)
108  {
109  ss << " faulty address is " << info->si_addr;
110 #ifndef __MACOSX__
111  ss << " from " << uc->uc_mcontext.gregs[REG_EIP];
112 #endif
113  }
114  ss << std::endl;
115 
116  trace_size = backtrace(trace, 16);
117  /* overwrite sigaction with caller's address */
118 #ifndef __MACOSX__
119  trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];
120 #endif
121 
122  messages = backtrace_symbols(trace, trace_size);
123  /* skip first stack frame (points here) */
124  ss << " [bt] Execution path:" << std::endl;
125  for (i = 1; i<trace_size; ++i)
126  {
127  ss << " [bt] " << decode(messages[i]) << std::endl;
128  }
129 
130  if (sig == SIGSEGV)
131  {
132  SLM_FATAL("SIGSEV signal " + ss.str() );
133  exit(0);
134  }
135  else
136  {
137  SLM_ERROR("SIGUSR1 signal " + ss.str() );
138  }
139 
140 }
142 {
143  struct sigaction sa;
144 
145  sa.sa_sigaction = bt_sighandler;
146  sigemptyset (&sa.sa_mask);
147  sa.sa_flags = SA_RESTART | SA_SIGINFO;
148 
149  sigaction(SIGSEGV, &sa, NULL);
150  sigaction(SIGUSR1, &sa, NULL);
151 }
152 
153 
154 #else // if win32
155 
156 const size_t nbChar = 100;
157 #if _MSC_VER > 1499 // Visual C++ 2008 only
158 BOOL CALLBACK EnumerateLoadedModules(LPCSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext)
159 #else
160 BOOL CALLBACK EnumerateLoadedModules(LPSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext)
161 #endif
162 {
163  std::list<std::string>* pLoadedModules = reinterpret_cast<std::list<std::string>*>(UserContext);
164  pLoadedModules->push_back(std::string((char *)ModuleName) + "\t" + ::boost::lexical_cast<std::string>(ModuleBase));
165  return true;
166 }
167 
171 void printDump(std::list<std::string> &loadedModules, std::list<std::string> &callStack,
172  std::list<std::string> &fileStack)
173 {
174  std::stringstream stream;
175 
176  // Dumps the loaded modules on the stream
177  stream << "-----------------------------------------" << std::endl;
178  stream << "\nLoaded Modules\n";
179  for(std::list<std::string>::const_iterator it = loadedModules.begin(); it != loadedModules.end(); ++it)
180  {
181  stream << "> " << *it << std::endl;
182  }
183  stream << "-----------------------------------------" << std::endl;
184  // Dumps the call stack on the stream
185  stream << "\nCallStack\n";
186  for(std::list<std::string>::const_iterator it = callStack.begin(), it2 = fileStack.begin();
187  it != callStack.end() && it2 != fileStack.end(); ++it, ++it2)
188  {
189  stream << "> " << *it << std::endl;
190  stream << " " << *it2 << std::endl;
191  }
192  stream << "-----------------------------------------" << std::endl;
193 
194  SLM_ERROR(stream.str() );
195 }
196 
201 void LoadCallStack(EXCEPTION_POINTERS* exceptionInfos, HANDLE &hProcess, std::list<std::string> &callStack,
202  std::list<std::string> &fileStack)
203 {
204  STACKFRAME64 tempStackFrame;
205  CONTEXT context = *(exceptionInfos->ContextRecord);
206  memset( &tempStackFrame, 0, sizeof(STACKFRAME64) );
207  DWORD machineType;
208 
209 #ifdef _M_IX86
210  machineType = IMAGE_FILE_MACHINE_I386;
211  tempStackFrame.AddrPC.Offset = context.Eip;
212  tempStackFrame.AddrPC.Mode = AddrModeFlat;
213  tempStackFrame.AddrStack.Offset = context.Esp;
214  tempStackFrame.AddrStack.Mode = AddrModeFlat;
215  tempStackFrame.AddrFrame.Offset = context.Ebp;
216  tempStackFrame.AddrFrame.Mode = AddrModeFlat;
217 #elif _M_X64
218  machineType = IMAGE_FILE_MACHINE_AMD64;
219  tempStackFrame.AddrPC.Offset = context.Rip;
220  tempStackFrame.AddrPC.Mode = AddrModeFlat;
221  tempStackFrame.AddrFrame.Offset = context.Rsp;
222  tempStackFrame.AddrFrame.Mode = AddrModeFlat;
223  tempStackFrame.AddrStack.Offset = context.Rsp;
224  tempStackFrame.AddrStack.Mode = AddrModeFlat;
225 #elif _M_IA64
226  machineType = IMAGE_FILE_MACHINE_IA64;
227  tempStackFrame.AddrPC.Offset = context.StIIP;
228  tempStackFrame.AddrPC.Mode = AddrModeFlat;
229  tempStackFrame.AddrFrame.Offset = context.IntSp;
230  tempStackFrame.AddrFrame.Mode = AddrModeFlat;
231  tempStackFrame.AddrBStore.Offset = context.RsBSP;
232  tempStackFrame.AddrBStore.Mode = AddrModeFlat;
233  tempStackFrame.AddrStack.Offset = context.IntSp;
234  tempStackFrame.AddrStack.Mode = AddrModeFlat;
235 #else
236 #error "Platform not supported!"
237 #endif
238 
239  ULONG64 buffer[(sizeof(SYMBOL_INFO) + nbChar*sizeof(TCHAR) + sizeof(ULONG64) + 1) / sizeof(ULONG64)];
240  PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
241  PSTR undecoratedName = (PSTR)malloc(sizeof(TCHAR) * nbChar);
242 
243  pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
244  pSymbol->MaxNameLen = nbChar;
245  DWORD lineDisplacement;
246  IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) };
247 
248  while(StackWalk64(machineType, hProcess, GetCurrentThread(), &tempStackFrame, &context, NULL,
249  SymFunctionTableAccess64, SymGetModuleBase64, NULL))
250  {
251  // Sanity stack check
252  if(tempStackFrame.AddrPC.Offset == 0)
253  {
254  break;
255  }
256 
257  DWORD64 symDisplacement = 0;
258  // Try to get the symbol name
259  if(SymFromAddr(hProcess, tempStackFrame.AddrPC.Offset, &symDisplacement, pSymbol))
260  {
261  UnDecorateSymbolName(pSymbol->Name, undecoratedName, MAX_SYM_NAME, UNDNAME_COMPLETE);
262  callStack.push_back(std::string((char*)undecoratedName) + "+" +
263  ::boost::lexical_cast<std::string>(symDisplacement));
264 
265  if(SymGetLineFromAddr64(hProcess, tempStackFrame.AddrPC.Offset, &lineDisplacement, &lineInfo))
266  {
267  fileStack.push_back(std::string(lineInfo.FileName) + "\tl:" +
268  ::boost::lexical_cast<std::string>(lineInfo.LineNumber));
269  }
270  else
271  {
272  fileStack.push_back("No info");
273  }
274  }
275  else
276  {
277  }
278  }
279  free(undecoratedName);
280 }
281 
283 static LONG WINAPI UnhandledExpFilter(PEXCEPTION_POINTERS pExceptionInfo)
284 {
286  HANDLE hProcess;
288  std::list<std::string> loadedModules;
290  std::list<std::string> callStack;
292  std::list<std::string> fileStack;
293 
294  SymSetOptions(SYMOPT_UNDNAME|SYMOPT_DEFERRED_LOADS|SYMOPT_LOAD_LINES);
295  hProcess = GetCurrentProcess();
296 
297  if(SymInitialize(hProcess, NULL, TRUE))
298  {
299  LoadCallStack(pExceptionInfo, hProcess, callStack, fileStack);
300  ::EnumerateLoadedModules64(hProcess, EnumerateLoadedModules, &loadedModules);
301  ::SymCleanup(hProcess);
302  }
303 
304  printDump(loadedModules, callStack, fileStack);
305 
306  return EXCEPTION_CONTINUE_SEARCH;
307 }
308 
310 {
311  SetUnhandledExceptionFilter(UnhandledExpFilter);
312 }
313 
314 #endif
315 
316 
317 
318 } // namespace monitor
319 
MONITOR_API void installSIGSEVBacktrace()
Function installing a callback which print the backtrace on a SIGSEV and SIGUSR1 signal for posix onl...
#define SLM_ERROR(message)
Definition: spyLog.hpp:272
#define SLM_FATAL(message)
Definition: spyLog.hpp:283
The namespace monitor contains tools for monitoring an application built with FW4SPL.
Definition: fwMetrics.hpp:14
MONITOR_API void generateSIGSEV()
Generates segmentation fault for test.