LCOV - code coverage report
Current view: top level - src/base - print_trace.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 31 57 54.4 %
Date: 2025-08-19 19:27:09 Functions: 3 4 75.0 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.14