libMesh
print_trace.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2024 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 #if defined(LIBMESH_HAVE_GCC_ABI_DEMANGLE)
72 #include <cxxabi.h>
73 #endif
74 
75 // Anonymous namespace for print_trace() helper functions
76 namespace
77 {
78 
79 // process_trace() is a helper function used by
80 // libMesh::print_trace(). It is only available if configure
81 // determined your compiler supports backtrace(), which is a GLIBC
82 // extension.
83 #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
84 std::string process_trace(const char * name)
85 {
86  std::string fullname = name;
87  std::string saved_begin, saved_end;
88  size_t namestart, nameend;
89 
90  // The Apple backtrace function returns more information than the Linux version.
91  // We need to pass only the function name to the demangler or it won't decode it for us.
92  //
93  // lineno: stackframeno address functionname + offset
94 
95 #ifdef __APPLE__
96  namestart = fullname.find("0x");
97  if (namestart != std::string::npos)
98  {
99  namestart = fullname.find(' ', namestart) + 1;
100  saved_begin = fullname.substr(0, namestart);
101  }
102  else
103  namestart = 0;
104  nameend = fullname.find('+');
105  if (nameend == std::string::npos ||
106  nameend <= namestart)
107  nameend = fullname.size();
108  else
109  {
110  nameend -= 1;
111  saved_end = fullname.substr(nameend, fullname.length());
112  }
113 #else
114  namestart = fullname.find('(');
115  if (namestart == std::string::npos)
116  return fullname;
117  else
118  namestart++;
119  nameend = fullname.find('+');
120  if (nameend == std::string::npos ||
121  nameend <= namestart)
122  return fullname;
123 #endif
124 
125  std::string type_name = fullname.substr(namestart, nameend - namestart);
126 
127  // Try to demangle now
128  return saved_begin + libMesh::demangle(type_name.c_str()) + saved_end;
129 }
130 #endif
131 
132 
133 
134 // gdb_backtrace() is used by libMesh::print_trace() to try and get a
135 // "better" backtrace than what the backtrace() function provides.
136 // GDB backtraces are a bit slower, but they provide line numbers in
137 // source code, a really helpful feature when debugging something...
138 bool gdb_backtrace(std::ostream & out_stream)
139 {
140 #ifdef LIBMESH_GDB_COMMAND
141  // Eventual return value, true if gdb succeeds, false otherwise.
142  bool success = true;
143 
144  // The system() call does not allow us to redirect the output to a
145  // C++ ostream, so we redirect gdb's output to a (known) temporary
146  // file, and then send output that to the user's stream.
147  char temp_file[] = "temp_print_trace.XXXXXX";
148  int fd = mkstemp(temp_file);
149 
150  // If mkstemp fails, we failed.
151  if (fd == -1)
152  success = false;
153  else
154  {
155  // Run gdb using a system() call, redirecting the output to our
156  // temporary file.
157  auto this_pid = getpid();
158 
159  int exit_status = 1;
160 
161  libmesh_try
162  {
163  std::string gdb_command =
164  libMesh::command_line_value("gdb",std::string(LIBMESH_GDB_COMMAND));
165 
166  std::ostringstream command;
167  command << gdb_command
168  << " -p "
169  << this_pid
170  << " -batch -ex bt -ex detach 2>/dev/null 1>"
171  << temp_file;
172  exit_status = std::system(command.str().c_str());
173  }
174  libmesh_catch (...)
175  {
176  std::cerr << "Unable to run gdb" << std::endl;
177  }
178 
179  // If we can open the temp_file, the file is not empty, and the
180  // exit status from the system call is 0, we'll assume that gdb
181  // worked, and copy the file's contents to the user's requested
182  // stream. This rdbuf() thing is apparently how you do
183  // this... Otherwise, report failure.
184  std::ifstream fin(temp_file);
185  if (fin && (fin.peek() != std::ifstream::traits_type::eof()) && (exit_status == 0))
186  out_stream << fin.rdbuf();
187  else
188  success = false;
189  }
190 
191  // Clean up the temporary file, regardless of whether it was opened successfully.
192  std::remove(temp_file);
193 
194  return success;
195 #else
196  return false;
197 #endif
198 }
199 
200 } // end anonymous namespace
201 
202 
203 namespace libMesh
204 {
205 
206 void print_trace(std::ostream & out_stream)
207 {
208  // First try a GDB backtrace. They are better than what you get
209  // from calling backtrace() because you don't have to do any
210  // demangling, and they include line numbers! If the GDB backtrace
211  // fails, for example if your system does not have GDB, fall back to
212  // calling backtrace().
213  bool gdb_worked = false;
214 
215  // Let the user disable GDB backtraces by configuring with
216  // --without-gdb-command or with a command line option.
217  if ((std::string(LIBMESH_GDB_COMMAND) != std::string("no") &&
218  !libMesh::on_command_line("--no-gdb-backtrace")) ||
219  libMesh::on_command_line("--gdb"))
220  gdb_worked = gdb_backtrace(out_stream);
221 
222  // This part requires that your compiler at least supports
223  // backtraces. Demangling is also nice, but it will still run
224  // without it.
225 #if defined(LIBMESH_HAVE_GLIBC_BACKTRACE)
226  if (!gdb_worked)
227  {
228  void * addresses[40];
229  char ** strings;
230 
231  int size = backtrace(addresses, 40);
232  strings = backtrace_symbols(addresses, size);
233  out_stream << "Stack frames: " << size << std::endl;
234  for (int i = 0; i < size; i++)
235  out_stream << i << ": " << process_trace(strings[i]) << std::endl;
236  std::free(strings);
237  }
238 #endif
239 }
240 
241 
242 // If tracefiles are enabled, calls print_trace() and sends the
243 // result to file. Otherwise, does nothing.
245 {
246 #ifdef LIBMESH_ENABLE_TRACEFILES
247  std::stringstream outname;
248  outname << "traceout_" << static_cast<std::size_t>(libMesh::global_processor_id()) << '_' << getpid() << ".txt";
249  std::ofstream traceout(outname.str().c_str(), std::ofstream::app);
250  libMesh::print_trace(traceout);
251 #endif
252 }
253 
254 
255 
256 // demangle() is used by the process_trace() helper function. It is
257 // also used by the Parameters class for demangling typeid's. If
258 // configure determined that your compiler does not support
259 // demangling, it simply returns the input string.
260 #if defined(LIBMESH_HAVE_GCC_ABI_DEMANGLE)
261 std::string demangle(const char * name)
262 {
263  int status = 0;
264  std::string ret = name;
265 
266  // Actually do the demangling
267  char * demangled_name = abi::__cxa_demangle(name, 0, 0, &status);
268 
269  // If demangling returns non-nullptr, save the result in a string.
270  if (demangled_name)
271  ret = demangled_name;
272 
273  // According to cxxabi.h docs, the caller is responsible for
274  // deallocating memory.
275  std::free(demangled_name);
276 
277  return ret;
278 }
279 #else
280 std::string demangle(const char * name) { return std::string(name); }
281 #endif
282 
283 } // namespace libMesh
std::string name(const ElemQuality q)
This function returns a string containing some name for q.
Definition: elem_quality.C:42
The libMesh namespace provides an interface to certain functionality in the library.
void write_traceout()
Writes a stack trace to a uniquely named file if –enable-tracefiles has been set by configure...
Definition: print_trace.C:244
MPI_Status status
T command_line_value(const std::string &, T)
Definition: libmesh.C:957
int mkstemp(char *tmpl)
Definition: win_mkstemp.h:13
std::string demangle(const char *name)
Mostly system independent demangler.
Definition: print_trace.C:261
void print_trace(std::ostream &out_stream=std::cerr)
Print a stack trace (for code compiled with gcc)
Definition: print_trace.C:206
processor_id_type global_processor_id()
Definition: libmesh_base.h:85
bool on_command_line(std::string arg)
Definition: libmesh.C:924