Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2025 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 4159 : std::string process_trace(const char * name)
81 : {
82 5821 : std::string fullname = name;
83 3324 : 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 4159 : namestart = fullname.find('(');
111 4159 : if (namestart == std::string::npos)
112 0 : return fullname;
113 : else
114 4159 : namestart++;
115 4159 : nameend = fullname.find('+');
116 5436 : if (nameend == std::string::npos ||
117 2497 : nameend <= namestart)
118 915 : return fullname;
119 : #endif
120 :
121 2947 : std::string type_name = fullname.substr(namestart, nameend - namestart);
122 :
123 : // Try to demangle now
124 4846 : 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 : #ifdef LIBMESH_GDB_COMMAND
137 : // Eventual return value, true if gdb succeeds, false otherwise.
138 0 : bool success = true;
139 :
140 : // The system() call does not allow us to redirect the output to a
141 : // C++ ostream, so we redirect gdb's output to a (known) temporary
142 : // file, and then send output that to the user's stream.
143 0 : char temp_file[] = "temp_print_trace.XXXXXX";
144 0 : int fd = mkstemp(temp_file);
145 :
146 : // If mkstemp fails, we failed.
147 0 : if (fd == -1)
148 0 : success = false;
149 : else
150 : {
151 : // Run gdb using a system() call, redirecting the output to our
152 : // temporary file.
153 0 : auto this_pid = getpid();
154 :
155 0 : int exit_status = 1;
156 :
157 : libmesh_try
158 : {
159 : std::string gdb_command =
160 0 : libMesh::command_line_value("gdb",std::string(LIBMESH_GDB_COMMAND));
161 :
162 0 : std::ostringstream command;
163 : command << gdb_command
164 0 : << " -p "
165 : << this_pid
166 : << " -batch -ex bt -ex detach 2>/dev/null 1>"
167 0 : << temp_file;
168 0 : exit_status = std::system(command.str().c_str());
169 0 : }
170 0 : libmesh_catch (...)
171 : {
172 0 : std::cerr << "Unable to run gdb" << std::endl;
173 0 : }
174 :
175 : // If we can open the temp_file, the file is not empty, and the
176 : // exit status from the system call is 0, we'll assume that gdb
177 : // worked, and copy the file's contents to the user's requested
178 : // stream. This rdbuf() thing is apparently how you do
179 : // this... Otherwise, report failure.
180 0 : std::ifstream fin(temp_file);
181 0 : if (fin && (fin.peek() != std::ifstream::traits_type::eof()) && (exit_status == 0))
182 0 : out_stream << fin.rdbuf();
183 : else
184 0 : success = false;
185 0 : }
186 :
187 : // Clean up the temporary file, regardless of whether it was opened successfully.
188 0 : std::remove(temp_file);
189 :
190 0 : return success;
191 : #else
192 : return false;
193 : #endif
194 : }
195 :
196 : } // end anonymous namespace
197 :
198 :
199 : namespace libMesh
200 : {
201 :
202 171 : void print_trace(std::ostream & out_stream)
203 : {
204 : // First try a GDB backtrace. They are better than what you get
205 : // from calling backtrace() because you don't have to do any
206 : // demangling, and they include line numbers! If the GDB backtrace
207 : // fails, for example if your system does not have GDB, fall back to
208 : // calling backtrace().
209 59 : bool gdb_worked = false;
210 :
211 : // Let the user disable GDB backtraces by configuring with
212 : // --without-gdb-command or with a command line option.
213 401 : if ((std::string(LIBMESH_GDB_COMMAND) != std::string("no") &&
214 574 : !libMesh::on_command_line("--no-gdb-backtrace")) ||
215 397 : libMesh::on_command_line("--gdb"))
216 0 : gdb_worked = gdb_backtrace(out_stream);
217 :
218 : // This part requires that your compiler at least supports
219 : // backtraces. Demangling is also nice, but it will still run
220 : // without it.
221 : #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
222 59 : if (!gdb_worked)
223 : {
224 : void * addresses[40];
225 : char ** strings;
226 :
227 171 : int size = backtrace(addresses, 40);
228 171 : strings = backtrace_symbols(addresses, size);
229 171 : out_stream << "Stack frames: " << size << std::endl;
230 4330 : for (int i = 0; i < size; i++)
231 7933 : out_stream << i << ": " << process_trace(strings[i]) << std::endl;
232 171 : std::free(strings);
233 : }
234 : #endif
235 171 : }
236 :
237 :
238 : // If tracefiles are enabled, calls print_trace() and sends the
239 : // result to file. Otherwise, does nothing.
240 1695 : void write_traceout()
241 : {
242 : #ifdef LIBMESH_ENABLE_TRACEFILES
243 228 : std::stringstream outname;
244 169 : outname << "traceout_" << static_cast<std::size_t>(libMesh::global_processor_id()) << '_' << getpid() << ".txt";
245 228 : std::ofstream traceout(outname.str().c_str(), std::ofstream::app);
246 114 : libMesh::print_trace(traceout);
247 : #endif
248 1695 : }
249 :
250 : } // namespace libMesh
|