blocxx

StackTrace.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2006, Novell, Inc. All rights reserved.
00004 * 
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 * 
00008 *     * Redistributions of source code must retain the above copyright notice,
00009 *       this list of conditions and the following disclaimer.
00010 *     * Redistributions in binary form must reproduce the above copyright
00011 *       notice, this list of conditions and the following disclaimer in the
00012 *       documentation and/or other materials provided with the distribution.
00013 *     * Neither the name of 
00014 *       Vintela, Inc., 
00015 *       nor Novell, Inc., 
00016 *       nor the names of its contributors or employees may be used to 
00017 *       endorse or promote products derived from this software without 
00018 *       specific prior written permission.
00019 * 
00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030 * POSSIBILITY OF SUCH DAMAGE.
00031 *******************************************************************************/
00032 
00033 
00038 #include "blocxx/BLOCXX_config.h"
00039 #include "blocxx/StackTrace.hpp"
00040 
00041 #ifdef BLOCXX_WIN32
00042 #include <iostream>  // for cerr
00043 namespace BLOCXX_NAMESPACE
00044 {
00045 using std::cerr;
00046 using std::endl;
00047 void StackTrace::printStackTrace(EDoStackTraceFlag)
00048 {
00049    cerr << "StackTrace::printStackTrace not implemented yet" << endl;
00050 }
00051 }
00052 #else
00053 
00054 #include "blocxx/Exec.hpp"
00055 #include "blocxx/UnnamedPipe.hpp"
00056 #include "blocxx/Format.hpp"
00057 #include "blocxx/Array.hpp"
00058 #include "blocxx/StringBuffer.hpp"
00059 
00060 #include <fstream>
00061 #include <iostream>  // for cerr
00062 
00063 #if defined(BLOCXX_HAVE_BACKTRACE)
00064 #include <execinfo.h>
00065 #endif
00066 
00067 #if defined(BLOCXX_HAVE_CXXABI_H)
00068 #include <cxxabi.h>
00069 #endif
00070 
00071 #ifdef BLOCXX_HAVE_UNISTD_H
00072 extern "C"
00073 {
00074 #include <unistd.h> // for getpid()
00075 }
00076 #endif
00077 
00078 namespace BLOCXX_NAMESPACE
00079 {
00080 
00081 using std::ifstream;
00082 using std::ofstream;
00083 using std::flush;
00084 
00085 #ifndef BLOCXX_DEFAULT_GDB_PATH
00086 #define BLOCXX_DEFAULT_GDB_PATH "/usr/bin/gdb"
00087 #endif
00088 
00089 // static
00090 void StackTrace::printStackTrace(EDoStackTraceFlag doStackTrace)
00091 {
00092    std::cerr << getStackTrace(doStackTrace);
00093 }
00094 
00095 String StackTrace::getStackTrace(EDoStackTraceFlag doStackTrace)
00096 {
00097 
00098    if (doStackTrace == E_NO_CHECK_ENV_VAR || (doStackTrace == E_CHECK_ENV_VAR && getenv("BLOCXX_STACKTRACE")))
00099    {
00100       // if we have the GNU backtrace functions we use them.  They don't give
00101       // as good information as gdb does, but they are orders of magnitude
00102       // faster!
00103 #ifdef BLOCXX_HAVE_BACKTRACE
00104       void *array[200];
00105       
00106       size_t size = backtrace (array, 200);
00107       char **strings = backtrace_symbols (array, size);
00108       
00109       StringBuffer bt;
00110       
00111       size_t i;
00112       for (i = 0; i < size; i++)
00113       {
00114 #if defined(BLOCXX_HAVE_CXXABI_H)
00115          bt += strings[i];
00116          int status;
00117          // extract the identifier from strings[i].  It's inside of parens.
00118          char* firstparen = ::strchr(strings[i], '(');
00119          char* lastparen = ::strchr(strings[i], '+');
00120          if (firstparen != 0 && lastparen != 0 && firstparen < lastparen)
00121          {
00122             bt += ": ";
00123             *lastparen = '\0';
00124             char* realname = abi::__cxa_demangle(firstparen+1, 0, 0, &status);
00125             bt += realname;
00126             free(realname);
00127          }
00128 #else
00129          bt += strings[i];
00130 #endif
00131          bt += "\n";
00132       }
00133       
00134       free (strings);
00135       
00136       return bt.releaseString();
00137 #else
00138       ifstream file(BLOCXX_DEFAULT_GDB_PATH);
00139       if (file)
00140       {
00141          file.close();
00142          String scriptName("/tmp/owgdb-");
00143          String outputName("/tmp/owgdbout-");
00144          // TODO: don't use getppid, get it from somewhere else!
00145          outputName += String(UInt32(::getpid()));
00146          scriptName += String(UInt32(::getpid())) + ".sh";
00147          String exeName("/proc/");
00148          exeName += String(UInt32(::getpid())) + "/exe";
00149          
00150          ofstream scriptFile(scriptName.c_str(), std::ios::out);
00151          scriptFile << "#!/bin/sh\n"
00152             << "gdb " << exeName << " " << ::getpid() << " << EOS > " << outputName << " 2>&1\n"
00153 // doesn't work with gdb 5.1           << "thread apply all bt\n"
00154             << "bt\n"
00155             << "detach\n"
00156             << "q\n"
00157             << "EOS\n" << flush;
00158          scriptFile.close();
00159          Array<String> command;
00160          command.push_back( "/bin/sh" );
00161          command.push_back( scriptName );
00162          Exec::system(command);
00163          ifstream outputFile(outputName.c_str(), std::ios::in);
00164          StringBuffer output;
00165          while (outputFile)
00166          {
00167             output += String::getLine(outputFile);
00168             output += "\n";
00169          }
00170          outputFile.close();
00171          unlink(outputName.c_str());
00172          unlink(scriptName.c_str());
00173          return output.releaseString();
00174       }
00175 #endif
00176    }
00177    return String();
00178 }
00179 
00180 } // end namespace BLOCXX_NAMESPACE
00181 
00182 #endif   // ifdef BLOCXX_WIN32