libMesh
print_trace.C
Go to the documentation of this file.
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 std::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...
134 bool gdb_backtrace(std::ostream & out_stream)
135 {
136 #ifdef LIBMESH_GDB_COMMAND
137  // Eventual return value, true if gdb succeeds, false otherwise.
138  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  char temp_file[] = "temp_print_trace.XXXXXX";
144  int fd = mkstemp(temp_file);
145 
146  // If mkstemp fails, we failed.
147  if (fd == -1)
148  success = false;
149  else
150  {
151  // Run gdb using a system() call, redirecting the output to our
152  // temporary file.
153  auto this_pid = getpid();
154 
155  int exit_status = 1;
156 
157  libmesh_try
158  {
159  std::string gdb_command =
160  libMesh::command_line_value("gdb",std::string(LIBMESH_GDB_COMMAND));
161 
162  std::ostringstream command;
163  command << gdb_command
164  << " -p "
165  << this_pid
166  << " -batch -ex bt -ex detach 2>/dev/null 1>"
167  << temp_file;
168  exit_status = std::system(command.str().c_str());
169  }
170  libmesh_catch (...)
171  {
172  std::cerr << "Unable to run gdb" << std::endl;
173  }
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  std::ifstream fin(temp_file);
181  if (fin && (fin.peek() != std::ifstream::traits_type::eof()) && (exit_status == 0))
182  out_stream << fin.rdbuf();
183  else
184  success = false;
185  }
186 
187  // Clean up the temporary file, regardless of whether it was opened successfully.
188  std::remove(temp_file);
189 
190  return success;
191 #else
192  return false;
193 #endif
194 }
195 
196 } // end anonymous namespace
197 
198 
199 namespace libMesh
200 {
201 
202 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  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  if ((std::string(LIBMESH_GDB_COMMAND) != std::string("no") &&
214  !libMesh::on_command_line("--no-gdb-backtrace")) ||
215  libMesh::on_command_line("--gdb"))
216  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  if (!gdb_worked)
223  {
224  void * addresses[40];
225  char ** strings;
226 
227  int size = backtrace(addresses, 40);
228  strings = backtrace_symbols(addresses, size);
229  out_stream << "Stack frames: " << size << std::endl;
230  for (int i = 0; i < size; i++)
231  out_stream << i << ": " << process_trace(strings[i]) << std::endl;
232  std::free(strings);
233  }
234 #endif
235 }
236 
237 
238 // If tracefiles are enabled, calls print_trace() and sends the
239 // result to file. Otherwise, does nothing.
241 {
242 #ifdef LIBMESH_ENABLE_TRACEFILES
243  std::stringstream outname;
244  outname << "traceout_" << static_cast<std::size_t>(libMesh::global_processor_id()) << '_' << getpid() << ".txt";
245  std::ofstream traceout(outname.str().c_str(), std::ofstream::app);
246  libMesh::print_trace(traceout);
247 #endif
248 }
249 
250 } // 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:240
T command_line_value(const std::string &, T)
Definition: libmesh.C:1024
int mkstemp(char *tmpl)
Definition: win_mkstemp.h:13
std::string demangle(const char *name)
Mostly system independent demangler.
void print_trace(std::ostream &out_stream=std::cerr)
Print a stack trace (for code compiled with gcc)
Definition: print_trace.C:202
processor_id_type global_processor_id()
Definition: libmesh_base.h:85
bool on_command_line(std::string arg)
Definition: libmesh.C:987