1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2020 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18 // Code partially copyright Edd Dawson 2007
19 //
20 // Boost Software License - Version 1.0 - August 17th, 2003
21 //
22 // Permission is hereby granted, free of charge, to any person or organization
23 // obtaining a copy of the software and accompanying documentation covered by
24 // this license (the "Software") to use, reproduce, display, distribute,
25 // execute, and transmit the Software, and to prepare derivative works of the
26 // Software, and to permit third-parties to whom the Software is furnished to
27 // do so, all subject to the following:
28 //
29 // The copyright notices in the Software and this entire statement, including
30 // the above license grant, this restriction and the following disclaimer,
31 // must be included in all copies of the Software, in whole or in part, and
32 // all derivative works of the Software, unless such copies or derivative
33 // works are solely in the form of machine-executable object code generated by
34 // a source language processor.
35 //
36 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38 // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
39 // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
40 // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
41 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
42 // DEALINGS IN THE SOFTWARE.
43
44
45 #include "libmesh/libmesh_config.h"
46 #include "libmesh/print_trace.h"
47 #include "libmesh/libmesh.h"
48
49 #include <unistd.h> // needed for getpid()
50 #include <fstream>
51 #include <sstream>
52 #include <string>
53 #include <cstdio> // std::remove
54 #include <cstdlib> // std::system
55 #include <sys/types.h> // pid_t
56
57 #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
58 #include <execinfo.h>
59 #endif
60
61 #if defined(LIBMESH_HAVE_GCC_ABI_DEMANGLE)
62 #include <cxxabi.h>
63 #endif
64
65 // Anonymous namespace for print_trace() helper functions
66 namespace
67 {
68
69 // process_trace() is a helper function used by
70 // libMesh::print_trace(). It is only available if configure
71 // determined your compiler supports backtrace(), which is a GLIBC
72 // extension.
73 #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
process_trace(const char * name)74 std::string process_trace(const char * name)
75 {
76 std::string fullname = name;
77 std::string saved_begin, saved_end;
78 size_t namestart, nameend;
79
80 // The Apple backtrace function returns more information than the Linux version.
81 // We need to pass only the function name to the demangler or it won't decode it for us.
82 //
83 // lineno: stackframeno address functionname + offset
84
85 #ifdef __APPLE__
86 namestart = fullname.find("0x");
87 if (namestart != std::string::npos)
88 {
89 namestart = fullname.find(' ', namestart) + 1;
90 saved_begin = fullname.substr(0, namestart);
91 }
92 else
93 namestart = 0;
94 nameend = fullname.find('+');
95 if (nameend == std::string::npos ||
96 nameend <= namestart)
97 nameend = fullname.size();
98 else
99 {
100 nameend -= 1;
101 saved_end = fullname.substr(nameend, fullname.length());
102 }
103 #else
104 namestart = fullname.find('(');
105 if (namestart == std::string::npos)
106 return fullname;
107 else
108 namestart++;
109 nameend = fullname.find('+');
110 if (nameend == std::string::npos ||
111 nameend <= namestart)
112 return fullname;
113 #endif
114
115 std::string type_name = fullname.substr(namestart, nameend - namestart);
116
117 // Try to demangle now
118 return saved_begin + libMesh::demangle(type_name.c_str()) + saved_end;
119 }
120 #endif
121
122
123
124 // gdb_backtrace() is used by libMesh::print_trace() to try and get a
125 // "better" backtrace than what the backtrace() function provides.
126 // GDB backtraces are a bit slower, but they provide line numbers in
127 // source code, a really helpful feature when debugging something...
gdb_backtrace(std::ostream & out_stream)128 bool gdb_backtrace(std::ostream & out_stream)
129 {
130 #ifdef LIBMESH_GDB_COMMAND
131 // Eventual return value, true if gdb succeeds, false otherwise.
132 bool success = true;
133
134 // The system() call does not allow us to redirect the output to a
135 // C++ ostream, so we redirect gdb's output to a (known) temporary
136 // file, and then send output that to the user's stream.
137 char temp_file[] = "temp_print_trace.XXXXXX";
138 int fd = mkstemp(temp_file);
139
140 // If mkstemp fails, we failed.
141 if (fd == -1)
142 success = false;
143 else
144 {
145 // Run gdb using a system() call, redirecting the output to our
146 // temporary file.
147 pid_t this_pid = getpid();
148
149 int exit_status = 1;
150
151 libmesh_try
152 {
153 std::string gdb_command =
154 libMesh::command_line_value("gdb",std::string(LIBMESH_GDB_COMMAND));
155
156 std::ostringstream command;
157 command << gdb_command
158 << " -p "
159 << this_pid
160 << " -batch -ex bt -ex detach 2>/dev/null 1>"
161 << temp_file;
162 exit_status = std::system(command.str().c_str());
163 }
164 libmesh_catch (...)
165 {
166 std::cerr << "Unable to run gdb" << std::endl;
167 }
168
169 // If we can open the temp_file, the file is not empty, and the
170 // exit status from the system call is 0, we'll assume that gdb
171 // worked, and copy the file's contents to the user's requested
172 // stream. This rdbuf() thing is apparently how you do
173 // this... Otherwise, report failure.
174 std::ifstream fin(temp_file);
175 if (fin && (fin.peek() != std::ifstream::traits_type::eof()) && (exit_status == 0))
176 out_stream << fin.rdbuf();
177 else
178 success = false;
179 }
180
181 // Clean up the temporary file, regardless of whether it was opened successfully.
182 std::remove(temp_file);
183
184 return success;
185 #else
186 return false;
187 #endif
188 }
189
190 } // end anonymous namespace
191
192
193 namespace libMesh
194 {
195
print_trace(std::ostream & out_stream)196 void print_trace(std::ostream & out_stream)
197 {
198 // First try a GDB backtrace. They are better than what you get
199 // from calling backtrace() because you don't have to do any
200 // demangling, and they include line numbers! If the GDB backtrace
201 // fails, for example if your system does not have GDB, fall back to
202 // calling backtrace().
203 bool gdb_worked = false;
204
205 // Let the user disable GDB backtraces by configuring with
206 // --without-gdb-command or with a command line option.
207 if (std::string(LIBMESH_GDB_COMMAND) != std::string("no") &&
208 !libMesh::on_command_line("--no-gdb-backtrace"))
209 gdb_worked = gdb_backtrace(out_stream);
210
211 // This part requires that your compiler at least supports
212 // backtraces. Demangling is also nice, but it will still run
213 // without it.
214 #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
215 if (!gdb_worked)
216 {
217 void * addresses[40];
218 char ** strings;
219
220 int size = backtrace(addresses, 40);
221 strings = backtrace_symbols(addresses, size);
222 out_stream << "Stack frames: " << size << std::endl;
223 for (int i = 0; i < size; i++)
224 out_stream << i << ": " << process_trace(strings[i]) << std::endl;
225 std::free(strings);
226 }
227 #endif
228 }
229
230
231 // If tracefiles are enabled, calls print_trace() and sends the
232 // result to file. Otherwise, does nothing.
write_traceout()233 void write_traceout()
234 {
235 #ifdef LIBMESH_ENABLE_TRACEFILES
236 std::stringstream outname;
237 outname << "traceout_" << static_cast<std::size_t>(libMesh::global_processor_id()) << '_' << getpid() << ".txt";
238 std::ofstream traceout(outname.str().c_str(), std::ofstream::app);
239 libMesh::print_trace(traceout);
240 #endif
241 }
242
243
244
245 // demangle() is used by the process_trace() helper function. It is
246 // also used by the Parameters class for demangling typeid's. If
247 // configure determined that your compiler does not support
248 // demangling, it simply returns the input string.
249 #if defined(LIBMESH_HAVE_GCC_ABI_DEMANGLE)
demangle(const char * name)250 std::string demangle(const char * name)
251 {
252 int status = 0;
253 std::string ret = name;
254
255 // Actually do the demangling
256 char * demangled_name = abi::__cxa_demangle(name, 0, 0, &status);
257
258 // If demangling returns non-nullptr, save the result in a string.
259 if (demangled_name)
260 ret = demangled_name;
261
262 // According to cxxabi.h docs, the caller is responsible for
263 // deallocating memory.
264 std::free(demangled_name);
265
266 return ret;
267 }
268 #else
demangle(const char * name)269 std::string demangle(const char * name) { return std::string(name); }
270 #endif
271
272 } // namespace libMesh
273