$extrastylesheet
print_trace.C
Go to the documentation of this file.
00001 // Code partially copyright Edd Dawson 2007
00002 //
00003 // Boost Software License - Version 1.0 - August 17th, 2003
00004 //
00005 // Permission is hereby granted, free of charge, to any person or organization
00006 // obtaining a copy of the software and accompanying documentation covered by
00007 // this license (the "Software") to use, reproduce, display, distribute,
00008 // execute, and transmit the Software, and to prepare derivative works of the
00009 // Software, and to permit third-parties to whom the Software is furnished to
00010 // do so, all subject to the following:
00011 //
00012 // The copyright notices in the Software and this entire statement, including
00013 // the above license grant, this restriction and the following disclaimer,
00014 // must be included in all copies of the Software, in whole or in part, and
00015 // all derivative works of the Software, unless such copies or derivative
00016 // works are solely in the form of machine-executable object code generated by
00017 // a source language processor.
00018 //
00019 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00020 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00021 // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
00022 // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
00023 // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
00024 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00025 // DEALINGS IN THE SOFTWARE.
00026 
00027 
00028 #include "libmesh/libmesh_config.h"
00029 #include "libmesh/print_trace.h"
00030 #include "libmesh/libmesh.h"
00031 
00032 #include <unistd.h>  // needed for getpid()
00033 #include <fstream>
00034 #include <sstream>
00035 #include <string>
00036 #include <cstdio> // std::remove
00037 #include <cstdlib> // std::system
00038 #include <sys/types.h> // pid_t
00039 
00040 #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
00041 #include <execinfo.h>
00042 #endif
00043 
00044 #if defined(LIBMESH_HAVE_GCC_ABI_DEMANGLE)
00045 #include <cxxabi.h>
00046 #endif
00047 
00048 // Anonymous namespace for print_trace() helper functions
00049 namespace
00050 {
00051 
00052 // process_trace() is a helper function used by
00053 // libMesh::print_trace().  It is only available if configure
00054 // determined your compiler supports backtrace(), which is a GLIBC
00055 // extension.
00056 #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
00057 std::string process_trace(const char *name)
00058 {
00059   std::string fullname = name;
00060   std::string saved_begin, saved_end;
00061   size_t namestart, nameend;
00062 
00063   // The Apple backtrace function returns more information than the Linux version.
00064   // We need to pass only the function name to the demangler or it won't decode it for us.
00065   //
00066   // lineno: stackframeno address functionname + offset
00067 
00068 #ifdef __APPLE__
00069   namestart = fullname.find("0x");
00070   if (namestart != std::string::npos)
00071     {
00072       namestart = fullname.find(' ', namestart) + 1;
00073       saved_begin = fullname.substr(0, namestart);
00074     }
00075   else
00076     namestart = 0;
00077   nameend = fullname.find('+');
00078   if (nameend == std::string::npos ||
00079       nameend <= namestart)
00080     nameend = fullname.size();
00081   else
00082     {
00083       nameend -= 1;
00084       saved_end = fullname.substr(nameend, fullname.length());
00085     }
00086 #else
00087   namestart = fullname.find('(');
00088   if (namestart == std::string::npos)
00089     return fullname;
00090   else
00091     namestart++;
00092   nameend = fullname.find('+');
00093   if (nameend == std::string::npos ||
00094       nameend <= namestart)
00095     return fullname;
00096 #endif
00097 
00098   std::string type_name = fullname.substr(namestart, nameend - namestart);
00099 
00100   // Try to demangle now
00101   return saved_begin + libMesh::demangle(type_name.c_str()) + saved_end;
00102 }
00103 #endif
00104 
00105 
00106 
00107 // gdb_backtrace() is used by libMesh::print_trace() to try and get a
00108 // "better" backtrace than what the backtrace() function provides.
00109 // GDB backtraces are a bit slower, but they provide line numbers in
00110 // source code, a really helpful feature when debugging something...
00111 bool gdb_backtrace(std::ostream &out_stream)
00112 {
00113 #ifdef LIBMESH_GDB_COMMAND
00114   // Eventual return value, true if gdb succeeds, false otherwise.
00115   bool success = true;
00116 
00117   // The system() call does not allow us to redirect the output to a
00118   // C++ ostream, so we redirect gdb's output to a (known) temporary
00119   // file, and then send output that to the user's stream.
00120   char temp_file[] = "temp_print_trace.XXXXXX";
00121   int fd = mkstemp(temp_file);
00122 
00123   // If mkstemp fails, we failed.
00124   if (fd == -1)
00125     success = false;
00126   else
00127     {
00128       // Run gdb using a system() call, redirecting the output to our
00129       // temporary file.
00130       pid_t this_pid = getpid();
00131 
00132       int exit_status = 1;
00133 
00134       try {
00135         std::string gdb_command =
00136           libMesh::command_line_value("gdb",std::string(LIBMESH_GDB_COMMAND));
00137 
00138         std::ostringstream command;
00139         command << gdb_command << " -p " << this_pid <<
00140                    " -batch -ex bt 2>/dev/null 1>" << temp_file;
00141         exit_status = std::system(command.str().c_str());
00142       }
00143       catch (...)
00144       {
00145         std::cerr << "Unable to run gdb" << std::endl;
00146       }
00147 
00148       // If we can open the temp_file, the file is not empty, and the
00149       // exit status from the system call is 0, we'll assume that gdb
00150       // worked, and copy the file's contents to the user's requested
00151       // stream.  This rdbuf() thing is apparently how you do
00152       // this... Otherwise, report failure.
00153       std::ifstream fin(temp_file);
00154       if (fin && (fin.peek() != std::ifstream::traits_type::eof()) && (exit_status == 0))
00155         out_stream << fin.rdbuf();
00156       else
00157         success = false;
00158     }
00159 
00160   // Clean up the temporary file, regardless of whether it was opened successfully.
00161   std::remove(temp_file);
00162 
00163   return success;
00164 #else
00165   return false;
00166 #endif
00167 }
00168 
00169 } // end anonymous namespace
00170 
00171 
00172 namespace libMesh
00173 {
00174 
00175 void print_trace(std::ostream &out_stream)
00176 {
00177   // First try a GDB backtrace.  They are better than what you get
00178   // from calling backtrace() because you don't have to do any
00179   // demangling, and they include line numbers!  If the GDB backtrace
00180   // fails, for example if your system does not have GDB, fall back to
00181   // calling backtrace().
00182   bool gdb_worked = gdb_backtrace(out_stream);
00183 
00184   // This part requires that your compiler at least supports
00185   // backtraces.  Demangling is also nice, but it will still run
00186   // without it.
00187 #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
00188   if (!gdb_worked)
00189     {
00190       void *addresses[40];
00191       char **strings;
00192 
00193       int size = backtrace(addresses, 40);
00194       strings = backtrace_symbols(addresses, size);
00195       out_stream << "Stack frames: " << size << std::endl;
00196       for(int i = 0; i < size; i++)
00197         out_stream << i << ": " << process_trace(strings[i]) << std::endl;
00198       std::free(strings);
00199     }
00200 #endif
00201 }
00202 
00203 
00204 // If tracefiles are enabled, calls print_trace() and sends the
00205 // result to file.  Otherwise, does nothing.
00206 void write_traceout()
00207 {
00208 #ifdef LIBMESH_ENABLE_TRACEFILES
00209   std::stringstream outname;
00210   outname << "traceout_" << static_cast<std::size_t>(libMesh::global_processor_id()) << '_' << getpid() << ".txt";
00211   std::ofstream traceout(outname.str().c_str(), std::ofstream::app);
00212   libMesh::print_trace(traceout);
00213 #endif
00214 }
00215 
00216 
00217 
00218 // demangle() is used by the process_trace() helper function.  It is
00219 // also used by the Parameters class for demangling typeid's.  If
00220 // configure determined that your compiler does not support
00221 // demangling, it simply returns the input string.
00222 #if defined(LIBMESH_HAVE_GCC_ABI_DEMANGLE)
00223 std::string demangle(const char *name)
00224 {
00225   int status = 0;
00226   std::string ret = name;
00227 
00228   // Actually do the demangling
00229   char *demangled_name = abi::__cxa_demangle(name, 0, 0, &status);
00230 
00231   // If demangling returns non-NULL, save the result in a string.
00232   if (demangled_name)
00233     ret = demangled_name;
00234 
00235   // According to cxxabi.h docs, the caller is responsible for
00236   // deallocating memory.
00237   std::free(demangled_name);
00238 
00239   return ret;
00240 }
00241 #else
00242 std::string demangle(const char *name) { return std::string(name); }
00243 #endif
00244 
00245 } // namespace libMesh