Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2026 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 : #ifdef LIBMESH_HAVE_UNISTD_H
50 : #include <sys/types.h>
51 : #include <unistd.h> // needed for getpid() on Unix
52 : #endif
53 :
54 : #ifdef LIBMESH_HAVE_PROCESS_H
55 : #include <process.h> // for getpid() on Windows
56 : #endif
57 :
58 : #include <fstream>
59 : #include <sstream>
60 : #include <string>
61 : #include <cstdio> // std::remove
62 : #include <stdlib.h> // std::system, C version for POSIX mkstemp
63 : #ifndef LIBMESH_HAVE_MKSTEMP
64 : #include "win_mkstemp.h"
65 : #endif
66 :
67 : #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
68 : #include <execinfo.h>
69 : #endif
70 :
71 : // Anonymous namespace for print_trace() helper functions
72 : namespace
73 : {
74 :
75 : // process_trace() is a helper function used by
76 : // libMesh::print_trace(). It is only available if configure
77 : // determined your compiler supports backtrace(), which is a GLIBC
78 : // extension.
79 : #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
80 10366 : std::string process_trace(const char * name)
81 : {
82 14563 : std::string fullname = name;
83 8394 : std::string saved_begin, saved_end;
84 : size_t namestart, nameend;
85 :
86 : // The Apple backtrace function returns more information than the Linux version.
87 : // We need to pass only the function name to the demangler or it won't decode it for us.
88 : //
89 : // lineno: stackframeno address functionname + offset
90 :
91 : #ifdef __APPLE__
92 : namestart = fullname.find("0x");
93 : if (namestart != std::string::npos)
94 : {
95 : namestart = fullname.find(' ', namestart) + 1;
96 : saved_begin = fullname.substr(0, namestart);
97 : }
98 : else
99 : namestart = 0;
100 : nameend = fullname.find('+');
101 : if (nameend == std::string::npos ||
102 : nameend <= namestart)
103 : nameend = fullname.size();
104 : else
105 : {
106 : nameend -= 1;
107 : saved_end = fullname.substr(nameend, fullname.length());
108 : }
109 : #else
110 10366 : namestart = fullname.find('(');
111 10366 : if (namestart == std::string::npos)
112 0 : return fullname;
113 : else
114 10366 : namestart++;
115 10366 : nameend = fullname.find('+');
116 13469 : if (nameend == std::string::npos ||
117 6169 : nameend <= namestart)
118 2013 : return fullname;
119 : #endif
120 :
121 7704 : std::string type_name = fullname.substr(namestart, nameend - namestart);
122 :
123 : // Try to demangle now
124 12545 : return saved_begin + libMesh::demangle(type_name.c_str()) + saved_end;
125 : }
126 : #endif
127 :
128 :
129 :
130 : // gdb_backtrace() is used by libMesh::print_trace() to try and get a
131 : // "better" backtrace than what the backtrace() function provides.
132 : // GDB backtraces are a bit slower, but they provide line numbers in
133 : // source code, a really helpful feature when debugging something...
134 0 : bool gdb_backtrace(std::ostream & out_stream)
135 : {
136 : // Eventual return value, true if gdb succeeds, false otherwise.
137 0 : bool success = true;
138 :
139 : // The system() call does not allow us to redirect the output to a
140 : // C++ ostream, so we redirect gdb's output to a (known) temporary
141 : // file, and then send output that to the user's stream.
142 0 : char temp_file[] = "temp_print_trace.XXXXXX";
143 0 : int fd = mkstemp(temp_file);
144 :
145 : // If mkstemp fails, we failed.
146 0 : if (fd == -1)
147 0 : success = false;
148 : else
149 : {
150 : // Run gdb using a system() call, redirecting the output to our
151 : // temporary file.
152 0 : auto this_pid = getpid();
153 :
154 0 : int exit_status = 1;
155 :
156 : libmesh_try
157 : {
158 : #ifdef LIBMESH_GDB_COMMAND
159 : std::string gdb_command =
160 : libMesh::command_line_value("gdb",std::string(LIBMESH_GDB_COMMAND));
161 : #else
162 0 : if (!libMesh::on_command_line("--gdb"))
163 0 : return false;
164 : std::string gdb_command =
165 0 : libMesh::command_line_value("gdb",std::string());
166 : #endif
167 :
168 0 : std::ostringstream command;
169 : command << gdb_command
170 0 : << " -p "
171 : << this_pid
172 : << " -batch -ex bt -ex detach 2>/dev/null 1>"
173 0 : << temp_file;
174 0 : exit_status = std::system(command.str().c_str());
175 0 : }
176 0 : libmesh_catch (...)
177 : {
178 0 : std::cerr << "Unable to run gdb" << std::endl;
179 0 : }
180 :
181 : // If we can open the temp_file, the file is not empty, and the
182 : // exit status from the system call is 0, we'll assume that gdb
183 : // worked, and copy the file's contents to the user's requested
184 : // stream. This rdbuf() thing is apparently how you do
185 : // this... Otherwise, report failure.
186 0 : std::ifstream fin(temp_file);
187 0 : if (fin && (fin.peek() != std::ifstream::traits_type::eof()) && (exit_status == 0))
188 0 : out_stream << fin.rdbuf();
189 : else
190 0 : success = false;
191 0 : }
192 :
193 : // Clean up the temporary file, regardless of whether it was opened successfully.
194 0 : std::remove(temp_file);
195 :
196 0 : return success;
197 : }
198 :
199 : } // end anonymous namespace
200 :
201 :
202 : namespace libMesh
203 : {
204 :
205 425 : void print_trace(std::ostream & out_stream)
206 : {
207 : // First try a GDB backtrace. They are better than what you get
208 : // from calling backtrace() because you don't have to do any
209 : // demangling, and they include line numbers! If the GDB backtrace
210 : // fails, for example if your system does not have GDB, fall back to
211 : // calling backtrace().
212 153 : bool gdb_worked = false;
213 :
214 : // Let the user disable GDB backtraces by configuring with
215 : // --without-gdb-command or with a command line option.
216 425 : if (
217 : #ifdef LIBMESH_GDB_COMMAND
218 : !libMesh::on_command_line("--no-gdb-backtrace") ||
219 : #endif
220 697 : libMesh::on_command_line("--gdb"))
221 0 : gdb_worked = gdb_backtrace(out_stream);
222 :
223 : // This part requires that your compiler at least supports
224 : // backtraces. Demangling is also nice, but it will still run
225 : // without it.
226 : #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
227 153 : if (!gdb_worked)
228 : {
229 : void * addresses[40];
230 : char ** strings;
231 :
232 425 : int size = backtrace(addresses, 40);
233 425 : strings = backtrace_symbols(addresses, size);
234 425 : out_stream << "Stack frames: " << size << std::endl;
235 10791 : for (int i = 0; i < size; i++)
236 19638 : out_stream << i << ": " << process_trace(strings[i]) << std::endl;
237 425 : std::free(strings);
238 : }
239 : #endif
240 425 : }
241 :
242 :
243 : // If tracefiles are enabled, calls print_trace() and sends the
244 : // result to file. Otherwise, does nothing.
245 4304 : void write_traceout()
246 : {
247 : #ifdef LIBMESH_ENABLE_TRACEFILES
248 572 : std::stringstream outname;
249 419 : outname << "traceout_" << static_cast<std::size_t>(libMesh::global_processor_id()) << '_' << getpid() << ".txt";
250 572 : std::ofstream traceout(outname.str().c_str(), std::ofstream::app);
251 286 : libMesh::print_trace(traceout);
252 : #endif
253 4304 : }
254 :
255 : } // namespace libMesh
|