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