2005/5/9

     
 

BackTrace.cpp

artefaktur
// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*-
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// 
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
//
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
//
// $Header: /cvsroot/acdk/acdk/acdk_core/src/acdk/lang/sys/BackTrace.cpp,v 1.47 2005/04/30 23:08:36 kommer Exp $
#include "../../Config.h"
#if defined(ACDK_OS_WIN32) && !defined(ACDK_OS_CYGWIN32)
// windoof stuff is encapsulated in the core_winimage class
#include "core_winimage.h"

#endif //defined(ACDK_OS_WIN32)

#include <acdk.h>
#include <acdk/lang/System.h>
#include <acdk/locale/AsciiEncoding.h>

#include "BackTrace.h"

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>


#if defined(ACDK_OS_SOLARIS)
#include <ucontext.h>
#include <sys/frame.h>
#include <dlfcn.h>
#include <setjmp.h>
#include <sys/procfs_isa.h>
#include <demangle.h>

#if defined(sparc) || defined(__sparc)
#  define FRAME_PTR_REGISTER REG_SP
#endif
/FONT>
#  if defined(i386) || defined(__i386)
#define FRAME_PTR_REGISTER EBP
#endif
/FONT>

#endif //defined(ACDK_OS_SOLARIS)

#if defined(ACDK_OS_LINUX)
#include <execinfo.h>
#endif
/FONT>

#if defined(__GNUG__) // does only work on linux
#if __GNUC__ >= 3
# define ACDK_HAS_CXXABI_H
#endif
/FONT>
//#define ACDK_HAS_LIBERTY_DEMANGLE
#ifdef ACDK_HAS_LIBERTY_DEMANGLE
// from demangle.h
#define DMGL_PARAMS	 (1 << 0)	/* Include function args */
#define DMGL_ANSI	 (1 << 1)	/* Include const, volatile, etc */
#define DMGL_JAVA	 (1 << 2)	/* Demangle as Java rather than C++. */

extern "C" char * cplus_demangle (const char *mangled, int options);
#elif defined(ACDK_HAS_CXXABI_H)
#include <cxxabi.h>
#endif //ACDK_HAS_LIBERTY_DEMANGLE
#endif
/FONT>

//#define LOCAL_DEBUG
#ifdef LOCAL_DEBUG
#define DOUT(strexpr) \
do { \
  StringBuffer sb; \
  sb << strexpr; \
  System::out->println(sb.toString()); \
} while (false)
#else
/FONT>
#define DOUT(strexpr)
#endif
/FONT>

namespace acdk {
namespace lang {
namespace sys {

#if defined(ACDK_OS_WIN32) && !defined(ACDK_OS_CYGWIN32)
#if defined(__BORLANDC__) // make troubles in destruction
core_winimage* BackTrace::_winimage = 0;
#else
/FONT>
core_scoped_ptr<core_winimage> BackTrace::_winimage;
#endif
/FONT>


//static
void BackTrace::init_winimage()
{
   if(_winimage == 0)
      _winimage = new core_winimage();
}
#endif //defined(ACDK_OS_WIN32)

BackTrace::BackTrace(int ignoreTopFrames)
: _ignoreTopFrames(ignoreTopFrames)
{
   _save_pcs();
}
#if defined(ACDK_OS_WIN32) && !defined(ACDK_OS_CYGWIN32)
BackTrace::BackTrace(_CONTEXT& ctx, int ignoreTopFrames)
: _ignoreTopFrames(ignoreTopFrames)
{
   _save_pcs(ctx);
}
#endif
/FONT>

RStackFrameArray 
BackTrace::getStackFrames()
{
   int offset = _ignoreTopFrames;
  if (_program_counters.size() - offset < 0)
    return new StackFrameArray(0);
  RStackFrameArray rets = new StackFrameArray(_program_counters.size() - offset);
  for (int i = 0; i < _program_counters.size() - offset; i++)
  {
    rets[i] = getStackFrame(_program_counters[i + offset]);
  }
  return rets;
}


void
gcc_demangle(RString text, RString& funcname, RString& libname)
{

  // /home/roger/artefaktur/acdk/bin/libacdk_tools_testunit.so(executeTests__Q44acdk5tools8testunit10TestRunner+0x356)
  if (text->indexOf('(') == -1 || text->indexOf(')') ==  -1) {
    funcname = text;
    libname = "";
    return;
  }
  libname = text->substr(0, text->indexOf('('));
  funcname = text->substr(text->indexOf('(') + 1, text->indexOf(')'));
  if (funcname->indexOf('+') == -1)
    return;
  funcname = funcname->substr(0, funcname->indexOf('+'));
#if defined(ACDK_HAS_LIBERTY_DEMANGLE)

  char* retptr = cplus_demangle (funcname->c_str(), DMGL_PARAMS /*| DMGL_JAVA*/ | DMGL_ANSI);
  funcname = acdk::locale::AsciiEncoding::encodeToString(retptr);
  free(retptr);
#elif defined(ACDK_HAS_CXXABI_H)
  
#if defined(ACDK_64_BIT_PTR)
  unsigned long int size = 0;
#else
/FONT>
  unsigned int size = 0;
#endif
/FONT>
  
  int status = 0;
  RString cfunc = funcname->convert(CCAscii);
  DOUT("BT: " << cfunc);
  const char* cptr = cfunc->c_str();
  char* retbuffer = ::abi::__cxa_demangle(cptr, 0, &size, &status);
  
  if (status == 0)
  {
    DOUT("BTDEM(" << status << "): " << cptr << "=>" << (const char*)retbuffer);
    //int l = strlen(//strnlen(cptr, size);
    //funcname = acdk::locale::AsciiEncoding::decodeToString((const byte*)retbuffer, l);
    funcname = SCS(retbuffer);
  }
  if (retbuffer != 0)
    free(retbuffer);
  
#endif //defined(ACDK_HAS_LIBERTY_DEMANGLE)
}


//static 
RStackFrame 
BackTrace::getStackFrame(void* pc)
{
#if defined(ACDK_OS_WIN32) && !defined(ACDK_OS_CYGWIN32)
  init_winimage();
  core_winimage::SymbolResult res = _winimage->getSymbol(pc);
  core_winimage::FallbackInfoResult fallRes;
  _winimage->getFallbackInfo(pc, fallRes);
  RString module = fallRes.module;
  RString function = "";
  if (res.result) {
    function = res.symbol;
  } else {
    char buf[256];
    sprintf(buf, "0x%x", fallRes.offset);
    function = RString("<") + SCS(buf) + ">";
  }
  RString sourceName;
  int sourceLine = -1;
  _winimage->getSourceLine(pc, sourceName, sourceLine);
  return new NativeStackFrame((int)pc, function, module, sourceName, sourceLine);

#elif defined(ACDK_OS_LINUX)
  void* abuf[1];
  abuf[0] = pc;
  char** funcs = backtrace_symbols(abuf, 1);
  RString text = acdk::locale::AsciiEncoding::decodeToString((const byte*)*funcs);
  RString funcname = text;
  RString libname = "";
  gcc_demangle(text, funcname, libname);
  //System::out->println("Text: [" + text + "] funcname=[" + funcname + "] lib=[" + libname + "]");
  free(funcs);
  return new NativeStackFrame((int)pc, funcname, libname);
#elif defined(ACDK_OS_SOLARIS) && defined(__GNUG__)

  return new NativeStackFrame((int)pc, String::valueOf((int)pc), "<unknown>");
#elif defined(ACDK_OS_SOLARIS) // implicit worshop
   Dl_info info;
   const char* func = "??";
   const char* lib = "??";
   char cpp_func[1024];

   memset(cpp_func, 0, sizeof(cpp_func));
   memset(&info, 0, sizeof(info));
   if (dladdr(pc, & info) != 0)
   {
      lib = (char *)info.dli_fname;
      func = (char *)info.dli_sname;
   }

   cplus_demangle(func, cpp_func, sizeof(cpp_func));
   cpp_func[sizeof(cpp_func) - 1] = 0;
   return new NativeStackFrame((int)pc,
			     acdk::locale::AsciiEncoding::decodeToString((const byte*)cpp_func),
			     acdk::locale::AsciiEncoding::decodeToString((const byte*)lib));
#elif defined(ACDK_OS_BSD) || defined(ACDK_OS_DARWIN) || defined(ACDK_OS_CYGWIN32)
   return new NativeStackFrame(0, "", "");
#else
/FONT>
#error unkown OS
#endif //defined(ACDK_OS_SOLARIS)
}

#if defined(ACDK_OS_WIN32) 

void BackTrace::_save_pcs(_CONTEXT& ctx, bool replace)
{
  if (replace == true)
    _program_counters.resize(0);
  STACKFRAME stack;
  _winimage->initStackWalk(&ctx, &stack, false);
  for (int i = 0; _winimage->stackWalk(&ctx, &stack) == true; i++)
  {
    void* addr = reinterpret_cast<void*>(stack.AddrPC.Offset);
    //printf("ret: %i\n", (int)addr);
    if (0 != addr)
      _program_counters.add(addr);
  }
}
#endif
/FONT>
void BackTrace::_save_pcs()
{
#if defined(ACDK_OS_WIN32) && !defined(ACDK_OS_CYGWIN32)
  init_winimage();
  CONTEXT context;
  STACKFRAME stack;
  context.ContextFlags = CONTEXT_FULL;
  HANDLE currentThread = GetCurrentThread();
  GetThreadContext(currentThread, &context);
  _winimage->initStackWalk(&context, &stack, false);
  for (int i = 0; _winimage->stackWalk(&context, &stack) == true; i++)
  {
    void* addr = reinterpret_cast<void*>(stack.AddrPC.Offset);
    //printf("ret: %i\n", (int)addr);
    if (0 != addr)
      _program_counters.add(addr);
  }
#elif defined(ACDK_OS_LINUX)
  void* adress_buffer[100];
  int len = backtrace(adress_buffer, 100);
  //sys::coreout << "backtrace len: " << len << sys::eofl;
  _program_counters.ensureCapacity(len);
  for (int i = 0; i < len; i++)
  {
    _program_counters.add(adress_buffer[i]);
  }
#elif defined(ACDK_OS_SOLARIS)
  void* savpc;
  struct frame* savfp;
  struct frame* fp;
  ucontext_t  u;

  getcontext(&u);
  fp = savfp = (struct frame*)(((struct frame *)u.uc_mcontext.gregs[FRAME_PTR_REGISTER])->fr_savfp);

  for (; fp && (savpc = (void*)fp->fr_savpc); fp = (struct frame*)fp->fr_savfp)
  {
    _program_counters.add(savpc);
  }
#endif
/FONT>
}

int 
BackTrace::fillBackTrace(int offset, void** pcBuff, int size)
{
 #if defined(ACDK_OS_WIN32) && !defined(ACDK_OS_CYGWIN32)
  init_winimage();
  CONTEXT context;
  STACKFRAME stack;
  context.ContextFlags = CONTEXT_FULL;
  HANDLE currentThread = GetCurrentThread();
  GetThreadContext(currentThread, &context);
  _winimage->initStackWalk(&context, &stack, false);
  int i;
  for (i = 0; _winimage->stackWalk(&context, &stack) == true; i++)
  {
    if (i < offset)
      continue;
    if (i - offset >= size)
      break;
    pcBuff[i - offset] = reinterpret_cast<void*>(stack.AddrPC.Offset);
  }  
  return i - offset;
#elif defined(ACDK_OS_LINUX)
  void* adress_buffer[100];
  int len = backtrace(adress_buffer, 100);
  int i;
  for (i = 0; i < len; i++)
  {
    if (i < offset)
      continue;
    if (i - offset >= size)
      break;
    pcBuff[i - offset] = adress_buffer[i];
  }
  return i - offset;
#elif defined(ACDK_OS_SOLARIS)
  void* savpc;
  struct frame* savfp;
  struct frame* fp;
  ucontext_t  u;

  getcontext(&u);
  fp = savfp = (struct frame*)(((struct frame *)u.uc_mcontext.gregs[FRAME_PTR_REGISTER])->fr_savfp);
  int i = 0;
  for (; fp && (savpc = (void*)fp->fr_savpc); fp = (struct frame*)fp->fr_savfp, ++i)
    {
     if (i < offset)
      continue;
    if (i - offset >= size)
      break;
    pcBuff[i - offset]  =  savpc;
  }
  return i - offset;
#endif
/FONT>
}

} // namespace sys
} // namespace lang
} // namespace acdk