OregonCore  revision fb2a440-git
Your Favourite TBC server
UnixDebugger.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #if PLATFROM == PLATFORM_UNIX
19 
20 #ifndef _GNU_SOURCE
21 #define _GNU_SOURCE
22 #endif
23 
24 #include "UnixDebugger.h"
25 #include "SystemConfig.h"
26 #include "Log.h"
27 #include "Console.h"
28 
29 #include <sys/resource.h>
30 #include <sys/types.h>
31 #include <sys/file.h>
32 #include <sys/stat.h>
33 #include <sys/utsname.h>
34 #include <sys/sysinfo.h>
35 #include <sys/ucontext.h>
36 #include <execinfo.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 
40 #include <ace/Semaphore.h>
41 #include <fstream>
42 #include <iomanip>
43 #include <dlfcn.h>
44 
45 #include <execinfo.h>
46 #include <bfd.h>
47 
48 extern char** environ;
49 
50 #define CRASH_DIR "Crashes"
51 #define BACKTRACE_SIZE 256
52 
54 
56 std::stringstream UnixDebugger::s_AllBacktraces;
57 
58 std::set<ACE_thread_t> UnixDebugger::s_Threads;
59 ACE_Thread_Mutex UnixDebugger::s_Threads_Lock;
60 
63 
65 {
66  s_InstanceExists = true;
67 
68  // Register Handler for Deadly Signals
69  struct sigaction sa;
70  sa.sa_sigaction = SignalHandler;
71  sigfillset(&sa.sa_mask);
72  sa.sa_flags = SA_SIGINFO;
73 
74  ACE_Thread::sigsetmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
75 
76  // assuming this is (should be) main thread, add it to thread list here
78 
79  sigaction(SIGSEGV, &sa, NULL);
80  sigaction(SIGBUS, &sa, NULL);
81  sigaction(SIGILL, &sa, NULL);
82  sigaction(SIGABRT, &sa, NULL);
83  sigaction(SIGFPE, &sa, NULL);
84  sigaction(SIGUSR1, &sa, NULL);
85 }
86 
88 {
89  s_InstanceExists = false;
90 }
91 
92 void UnixDebugger::SignalHandler(int num, siginfo_t* info, void* ctx)
93 {
94  const char* strsig = NULL;
95  std::stringstream reason("");
96  bool segv = true;
97 
98  switch (num)
99  {
100  case SIGSEGV:
101  strsig = "SIGSEGV";
102  break;
103  case SIGBUS:
104  strsig = "SIGBUG";
105  break;
106  case SIGILL:
107  strsig = "SIGILL";
108  break;
109  case SIGFPE:
110  strsig = "SIGFPE";
111  break;
112  case SIGABRT:
113  strsig = "SIGABRT";
114  segv = false;
115  break;
116  case SIGUSR1:
117  // requested backtrace
119  ACE_OS::sema_post(&s_AllBacktracesSemaphore);
120  return;
121  }
122 
123  if (segv)
124  {
125  reason << "Fault at "
126  << std::hex << "0x"
127  << (unsigned long) info->si_addr
128  << " referenced from: 0x"
129  #ifdef __x86_64__
130  << std::hex << reinterpret_cast<struct sigcontext*> (&reinterpret_cast<ucontext_t*>(ctx)->uc_mcontext)->rip
131  #else
132  << std::hex << reinterpret_cast<struct sigcontext*> (&reinterpret_cast<ucontext_t*>(ctx)->uc_mcontext)->eip
133  #endif
134  << ": ";
135 
136  switch (num)
137  {
138  /* We can't use just switch(info->si_code), because
139  there would be many duplicates and compiler would refuse to compile */
140  case SIGSEGV:
141  switch (info->si_code)
142  {
143  case SEGV_MAPERR:
144  reason << "address not mapped to object";
145  break;
146  case SEGV_ACCERR:
147  reason << "invalid permissions for mapped object";
148  break;
149  }
150  break;
151  case SIGBUS:
152  switch (info->si_code)
153  {
154  case BUS_ADRALN:
155  reason << "invalid address alignment";
156  break;
157  case BUS_ADRERR:
158  reason << "nonconsistent physical address";
159  break;
160  case BUS_OBJERR:
161  reason << "object-specific hardware error";
162  break;
163  }
164  break;
165  case SIGILL:
166  switch (info->si_code)
167  {
168  case ILL_ILLOPC:
169  reason << "illegal opcode";
170  break;
171  case ILL_ILLOPN:
172  reason << "illegal operand";
173  break;
174  case ILL_ILLADR:
175  reason << "illegal addressing mode";
176  break;
177  case ILL_ILLTRP:
178  reason << "illegal trap";
179  break;
180  case ILL_PRVOPC:
181  reason << "privileged opcode";
182  break;
183  case ILL_PRVREG:
184  reason << "privileged register";
185  break;
186  case ILL_COPROC:
187  reason << "coprocessor error";
188  break;
189  case ILL_BADSTK:
190  reason << "internal stack error";
191  break;
192  }
193  break;
194  case SIGFPE:
195  switch (info->si_code)
196  {
197  case FPE_INTDIV:
198  reason << "integer divide b zero";
199  break;
200  case FPE_INTOVF:
201  reason << "integer overflow";
202  break;
203  case FPE_FLTDIV:
204  reason << "floating-point divide by zero";
205  break;
206  case FPE_FLTOVF:
207  reason << "floating-point overflow";
208  break;
209  case FPE_FLTUND:
210  reason << "floating-point underflow";
211  break;
212  case FPE_FLTRES:
213  reason << "floating-point inexact result";
214  break;
215  case FPE_FLTINV:
216  reason << "floating-point invalid operand";
217  break;
218  case FPE_FLTSUB:
219  reason << "subscript out of range";
220  break;
221  }
222  break;
223  }
224  }
225 
226  DumpDebugInfo(strsig, reason.str().c_str());
227 
228  struct sigaction sa;
229  sa.sa_handler = SIG_DFL;
230  sa.sa_flags = 0;
231  sigemptyset(&sa.sa_mask);
232 
233  sigaction(num, &sa, NULL);
234  raise(num);
235 }
236 
237 void UnixDebugger::WriteBacktraceForAllThreads(std::stringstream& ss)
238 {
239  static ACE_Thread_Mutex s_AllBacktracesMutex;
240 
241  using ACE_Based::Thread;
242 
243  s_AllBacktracesMutex.acquire();
244 
245  ACE_OS::sema_init(&s_AllBacktracesSemaphore, 0);
246 
248 
249  s_Threads_Lock.acquire();
250  for (std::set<ACE_thread_t>::iterator itr = s_Threads.begin(); itr != s_Threads.end(); ++itr)
251  {
252  if (!ACE_OS::thr_equal(*itr, Thread::currentId()))
253  {
254  if (!ACE_OS::thr_kill(*itr, SIGUSR1))
255  ACE_OS::sema_wait(&s_AllBacktracesSemaphore);
256  else
257  ss << "\nCouldn't examine backtrace of a thread!\n" << std::endl;
258  }
259  }
260  s_Threads_Lock.release();
261 
262  ACE_OS::sema_destroy(&s_AllBacktracesSemaphore);
263 
264  ss << s_AllBacktraces.str();
265  s_AllBacktraces.str().clear();
266 
267  s_AllBacktracesMutex.release();
268 }
269 
270 void UnixDebugger::WriteBacktrace(std::stringstream& ss)
271 {
272  void* buffer[BACKTRACE_SIZE];
273  int size = backtrace(buffer, BACKTRACE_SIZE);
274  char** symbols = backtrace_symbols(buffer, size);
275 
276  std::string module, func, offset, addr;
277  const char* demangled;
278 
279  ss << "BackTrace ";
280  if (size == BACKTRACE_SIZE)
281  ss << "(Might be truncated) ";
282  ss << "Thread LWP " << syscall(SYS_gettid);
283 
284  ss << std::endl << "=====================================" << std::endl;
285 
286  std::map<std::string, Resolver*> atlm;
287  std::map<std::string, Resolver*>::iterator atlmIt;
288 
289  int skip = 0;
290 
291  for (int i = 0; i < size; i++)
292  {
293  module = func = offset = addr = "";
294 
295  // ./module(function+0x15c) [0x8048a6d]
296  for (char* p = symbols[i]; *p; ++p)
297  {
298  if (*p == '(')
299  module.append(symbols[i] + 0, p - symbols[i]);
300  else if (*p == '+' && module.size())
301  func.append(symbols[i] + module.size() + 1, p - (symbols[i] + module.size() + 1));
302  else if (*p == ')' && module.size())
303  offset.append(symbols[i] + module.size() + func.size() + 1, p - (symbols[i] + module.size() + func.size() + 1));
304  else if (*p == '[')
305  {
306  addr = p + 1;
307  addr.resize(addr.size() - 1);
308  }
309  }
310 
311  unsigned long addrl = strtoul(addr.c_str(), NULL, 0);
312 
313  atlmIt = atlm.find(module);
314  Resolver* atl;
315 
316  if (atlmIt == atlm.end())
317  {
318  atl = new Resolver(module.c_str());
319  atlm[module] = atl;
320  }
321  else
322  atl = atlmIt->second;
323 
324  bool resolved = atl->Resolve(addrl);
325 
326  if (func.size())
327  {
328  if ((demangled = atl->Demangle(func.c_str(), DMGL_PARAMS | DMGL_ANSI)))
329  func = demangled;
330  // else func is a C symbol and it needs not to be demangled
331  }
332  else
333  func = atl->GetFunction();
334 
335  /* We don't need to print frames after SignalHandler */
336  if (!skip)
337  {
338  //if (func == "__kernel_sigreturn")
339  if ((void*) (addrl - strtoul(offset.c_str(), 0, 0)) == &SignalHandler)
340  skip = i + 2;
341  continue;
342  }
343  else if (i < skip)
344  continue;
345 
346  /* ## 0x## in func +offset
347  from module
348  at file:line */
349 
350  ss << '#' << (i - skip) << ' ' << addr << " in " << func << ' ' << offset << std::endl
351  << " from " << module << std::endl;
352 
353  if (!resolved)
354  continue;
355 
356  //const std::string& func = atl->GetFunction(); // not used
357  const std::string& file = atl->GetFile();
358  unsigned int line = atl->GetLine();
359 
360  ss << " at " << file << ':' << line << std::endl;
361 
362  do
363  {
364  if (file == "??" || !line)
365  break;
366 
367  std::string codeline;
368  std::ifstream ifs(file.c_str());
369  if (!ifs.is_open())
370  break;
371 
372  struct stat status;
373  if (0 == lstat(file.c_str(), &status))
374  if (status.st_mtime > atl->GetModificationTime())
375  ss << "Source file is newer than the executable!\n";
376 
377  for (unsigned int j = 1; j < line - 1; j++)
378  std::getline(ifs, codeline);
379 
380  if (ifs.eof())
381  break;
382 
383  codeline.clear();
384 
385  for (unsigned int j = (line == 1) ? 1 : 0; j < 3; j++)
386  {
387  std::getline(ifs, codeline);
388  if (ifs.eof())
389  break;
390  ss << ((line - 1) + j) << ": " << codeline << std::endl;
391  codeline.clear();
392  }
393 
394  ifs.close();
395  }
396  while (0);
397 
398  ss << std::endl;
399  }
400 
401  for (atlmIt = atlm.begin(); atlmIt != atlm.end(); atlmIt++)
402  delete atlmIt->second;
403 
404  free(symbols);
405 }
406 
407 void UnixDebugger::DumpDebugInfo(const char* sig, const char* reason)
408 {
409  bool coredump = false;
410  time_t now = time(0);
411 
412  // Check if we can (possibly) have a coredump file
413  // if not try to change that
414  struct rlimit rlim;
415  if (!getrlimit(RLIMIT_CORE, &rlim))
416  {
417  if (rlim.rlim_cur > 0)
418  coredump = true;
419  else
420  {
421  rlim.rlim_cur = -1;
422  rlim.rlim_max = -1;
423  coredump = !setrlimit(RLIMIT_CORE, &rlim);
424  }
425  }
426 
427  char dateString[255];
428  strftime(dateString, 255, "%d-%b-%y_%T", localtime(&now));
429 
430  std::stringstream ss;
431 
432  ss << "OregonCore Crash Report" << std::endl;
433  ss << "=======================" << std::endl;
434  ss << std::endl;
435 
436  struct utsname info;
437  if (!uname(&info))
438  {
439  ss << info.sysname << " (" << info.release << ") ";
440  ss << info.version << ' ' << info.machine << std::endl;;
441  }
442 
443  ss << "Date: " << dateString << std::endl;
444  ss << "Version: " << _FULLVERSION << std::endl;
445  #if _BUILD_DIRECTIVE == Debug
446  ss << "Build Type: Debug" << std::endl;
447  #elif _BUILD_DIRECTIVE == Release
448  ss << "Build Type: Release" << std::endl;
449  #endif
450 
451  #if COMPILER == COMPILER_BORLAND
452  ss << "Compiler: Borland" << std::endl;
453  #elif COMPILER == COMPILER_GNU
454  #ifdef __clang__
455  ss << "Compiler: clang" << std::endl;
456  #else
457  ss << "Compiler: GNU (or compatible)" << std::endl;
458  #endif
459  #elif COMPILER == COMPILER_INTEL
460  ss << "Compiler: Intel" << std::endl;
461  #endif
462  ss << "ACE version: " << ACE_VERSION << std::endl;
463 
464  ss << std::endl;
465 
466  struct sysinfo si;
467  if (!sysinfo(&si))
468  {
469  ss << "Memory: " << (si.totalram - si.freeram) << " / " << si.totalram
470  << " (" << ((float(si.totalram) - float(si.freeram))/float(si.totalram)) * 100.f << "% used)" << std::endl;
471  ss << "SWAP: " << (si.totalswap - si.freeswap) << " / " << si.totalswap
472  << " (" << ((float(si.totalswap) - float(si.freeswap))/float(si.totalswap)) * 100.f << "% used)" << std::endl;
473  }
474 
475  ss << std::endl;
476 
477  ss << "Coredump file MAY ";
478  if (!coredump)
479  ss << "NOT ";
480  ss << "be available." << std::endl;
481 
482  if (sig)
483  ss << "Caught deadly signal: " << sig << std::endl;
484  if (*reason)
485  ss << reason << std::endl;
486 
487  ss << std::endl;
488 
489  g_UnixDebugger.WriteBacktraceForAllThreads(ss);
490 
491  // We got all info needed now we just need to log it
492  #ifdef linux
493  char execdir[PATH_MAX] = {0};
494  ssize_t size = readlink("/proc/self/exe", execdir, PATH_MAX - 1);
495  execdir[size - (execdir + size - strrchr(execdir, '/'))] = '\0';
496  chdir(execdir);
497  printf("%s\n", execdir);
498  #endif
499 
500  mkdir(CRASH_DIR, 0777);
501  // now we try to chdir() to the newly created directory,
502  // so coredump can also be here written if not disabled,
503  // or set to absolute path
504  chdir(CRASH_DIR);
505 
506  std::string filename = "./oregon-core_" _REVISION "_";
507  filename.append(dateString);
508  filename.append(".txt");
509  if (FILE* fp = fopen(filename.c_str(), "w"))
510  {
511  // locking shouldn't be neccessary but one never knows...
512  flock(fileno(fp), LOCK_EX);
513 
514  fprintf(fp, "%s", ss.str().c_str());
515  fflush(fp);
516 
517  flock(fileno(fp), LOCK_UN);
518  fclose(fp);
519  }
520  else
521  {
522  // Failed to create file... perhaps no write permissions
523  // try to use our sLog.outError instead
524  try
525  {
526  sLog.outError("Failed to create crash file: %s\n", strerror(errno));
527  sLog.outError("%s", ss.str().c_str());
528  return;
529  }
530  catch (...)
531  {
532  // sLog was already destroyed ("Dead Reference" exception)
533  }
534  }
535 
536  // safely end curses (restore terminal)
537  sConsole.Restore();
538 
539  // We always use classic stdio instead of streams for output
540  fprintf(stdout, "%s\n", ss.str().c_str());
541  fflush(stdout);
542 }
543 
544 void UnixDebugger::InsertThread(ACE_thread_t thread)
545 {
546  if (!s_InstanceExists)
547  return;
548 
549  s_Threads_Lock.acquire();
550  s_Threads.insert(thread);
551  s_Threads_Lock.release();
552 }
553 
554 void UnixDebugger::RemoveThread(ACE_thread_t thread)
555 {
556  if (!s_InstanceExists)
557  return;
558 
559  s_Threads_Lock.acquire();
560  s_Threads.erase(thread);
561  s_Threads_Lock.release();
562 }
563 
564 /* Following code is a C++ wrapper for code written by an unknown author and
565  unknown license, we're very thankful for it. Hopefully no copyrights
566  are violated */
567 
569 
570 UnixDebugger::Resolver::Resolver(const char* executable)
571  :
572  abfd(0), syms(0), text(0), function("??"), filename("??"), line(0), mtime(0)
573 {
574  if (!initialized)
575  {
576  bfd_init();
577  initialized = true;
578  }
579 
580  abfd = bfd_openr(executable, 0);
581  if (!abfd)
582  return;
583 
584  #if linux
585  struct stat st;
586  if (0 == lstat("/proc/self/exe", &st))
587  mtime = st.st_mtime;
588  #endif
589 
590  /* oddly, this is required for it to work... */
591  bfd_check_format(abfd, bfd_object);
592 
593  unsigned storage_needed = bfd_get_symtab_upper_bound(abfd);
594  syms = (asymbol**) malloc(storage_needed);
595  /*unsigned cSymbols = */bfd_canonicalize_symtab(abfd, syms);
596 
597  text = bfd_get_section_by_name(abfd, ".text");
598 }
599 
601 {
602  if (abfd)
603  bfd_close(abfd);
604  if (syms)
605  free(syms);
606 }
607 
608 bool UnixDebugger::Resolver::Resolve(unsigned long address)
609 {
610  if (!abfd || !syms)
611  return false;
612 
613  if (text->vma >= address)
614  return false;
615 
616  bfd_vma offset = address - text->vma;
617 
618  const char* file;
619  const char* func;
620 
621  bfd_find_nearest_line(abfd, text, syms, offset, &file, &func, &line);
622 
623  if (func)
624  {
625  if (const char* demangled = Demangle(func, DMGL_PARAMS | DMGL_ANSI))
626  func = demangled;
627  else
628  function = func; // doesn't need demangling
629  }
630  else
631  func = "??";
632  filename = file && strlen(file) ? file : "??";
633  // line has been already written
634 
635  return true;
636 }
637 
638 const char* UnixDebugger::Resolver::Demangle(const char* mangled, int options)
639 {
640  static char buff[255];
641  char* demangled = bfd_demangle(abfd, mangled, options);
642 
643  if (demangled)
644  {
645  strcpy(buff, demangled);
646  free (demangled);
647  return buff;
648  }
649 
650  return NULL;
651 }
652 
653 #endif // PLATFORM_UNIX
const std::string & GetFunction() const
Definition: UnixDebugger.h:77
bool Resolve(unsigned long addr)
static ACE_sema_t s_AllBacktracesSemaphore
Definition: UnixDebugger.h:112
static ACE_thread_t currentId()
Definition: Threading.cpp:199
#define sConsole
Definition: Console.h:99
void WriteBacktraceForAllThreads(std::stringstream &ss)
Writes backtrace of all threads ino ss.
#define sLog
Log class singleton.
Definition: Log.h:187
time_t GetModificationTime() const
Definition: UnixDebugger.h:89
char ** environ
static bool s_InstanceExists
Definition: UnixDebugger.h:117
const std::string & GetFile() const
Definition: UnixDebugger.h:81
static void InsertThread(ACE_thread_t thread)
Call this when new thread spawns.
static void RemoveThread(ACE_thread_t thread)
Call this when thread exits.
#define BACKTRACE_SIZE
#define DMGL_ANSI
Definition: UnixDebugger.h:36
static ACE_Thread_Mutex s_Threads_Lock
Definition: UnixDebugger.h:116
static std::set< ACE_thread_t > s_Threads
Definition: UnixDebugger.h:115
static void DumpDebugInfo(const char *sig, const char *reason)
Writes crash log.
#define _FULLVERSION
Definition: SystemConfig.h:40
#define DMGL_PARAMS
Definition: UnixDebugger.h:35
#define fileno
Definition: Common.h:135
unsigned int GetLine() const
Definition: UnixDebugger.h:85
#define CRASH_DIR
const char * Demangle(const char *str, int options)
static std::stringstream s_AllBacktraces
Definition: UnixDebugger.h:113
static void WriteBacktrace(std::stringstream &ss)
writes backtrace of the calling thread into ss
Resolver(const char *executable)
static void SignalHandler(int num, siginfo_t *info, void *ctx)
UnixDebugger g_UnixDebugger
our global, ensures implicit instantiation