libMesh
Loading...
Searching...
No Matches
print_trace.C
Go to the documentation of this file.
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
72namespace
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)
80std::string process_trace(const char * name)
81{
82 std::string fullname = name;
83 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 namestart = fullname.find('(');
111 if (namestart == std::string::npos)
112 return fullname;
113 else
114 namestart++;
115 nameend = fullname.find('+');
116 if (nameend == std::string::npos ||
117 nameend <= namestart)
118 return fullname;
119#endif
120
121 std::string type_name = fullname.substr(namestart, nameend - namestart);
122
123 // Try to demangle now
124 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...
134bool gdb_backtrace(std::ostream & out_stream)
135{
136 // Eventual return value, true if gdb succeeds, false otherwise.
137 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 char temp_file[] = "temp_print_trace.XXXXXX";
143 int fd = mkstemp(temp_file);
144
145 // If mkstemp fails, we failed.
146 if (fd == -1)
147 success = false;
148 else
149 {
150 // Run gdb using a system() call, redirecting the output to our
151 // temporary file.
152 auto this_pid = getpid();
153
154 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 if (!libMesh::on_command_line("--gdb"))
163 return false;
164 std::string gdb_command =
165 libMesh::command_line_value("gdb",std::string());
166#endif
167
168 std::ostringstream command;
169 command << gdb_command
170 << " -p "
171 << this_pid
172 << " -batch -ex bt -ex detach 2>/dev/null 1>"
173 << temp_file;
174 exit_status = std::system(command.str().c_str());
175 }
176 libmesh_catch (...)
177 {
178 std::cerr << "Unable to run gdb" << std::endl;
179 }
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 std::ifstream fin(temp_file);
187 if (fin && (fin.peek() != std::ifstream::traits_type::eof()) && (exit_status == 0))
188 out_stream << fin.rdbuf();
189 else
190 success = false;
191 }
192
193 // Clean up the temporary file, regardless of whether it was opened successfully.
194 std::remove(temp_file);
195
196 return success;
197}
198
199} // end anonymous namespace
200
201
202namespace libMesh
203{
204
205void 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 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 if (
217#ifdef LIBMESH_GDB_COMMAND
218 !libMesh::on_command_line("--no-gdb-backtrace") ||
219#endif
221 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 if (!gdb_worked)
228 {
229 void * addresses[40];
230 char ** strings;
231
232 int size = backtrace(addresses, 40);
233 strings = backtrace_symbols(addresses, size);
234 out_stream << "Stack frames: " << size << std::endl;
235 for (int i = 0; i < size; i++)
236 out_stream << i << ": " << process_trace(strings[i]) << std::endl;
237 std::free(strings);
238 }
239#endif
240}
241
242
243// If tracefiles are enabled, calls print_trace() and sends the
244// result to file. Otherwise, does nothing.
246{
247#ifdef LIBMESH_ENABLE_TRACEFILES
248 std::stringstream outname;
249 outname << "traceout_" << static_cast<std::size_t>(libMesh::global_processor_id()) << '_' << getpid() << ".txt";
250 std::ofstream traceout(outname.str().c_str(), std::ofstream::app);
251 libMesh::print_trace(traceout);
252#endif
253}
254
255} // namespace libMesh
std::string name(const ElemQuality q)
This function returns a string containing some name for q.
The libMesh namespace provides an interface to certain functionality in the library.
processor_id_type global_processor_id()
void write_traceout()
Writes a stack trace to a uniquely named file if –enable-tracefiles has been set by configure,...
std::string demangle(const char *name)
Mostly system independent demangler.
T command_line_value(const std::string &, T)
Definition libmesh.C:971
void print_trace(std::ostream &out_stream=std::cerr)
Print a stack trace (for code compiled with gcc)
bool on_command_line(std::string arg)
Definition libmesh.C:934
int mkstemp(char *tmpl)
Definition win_mkstemp.h:13