$extrastylesheet
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