diff --git a/src/TNL/CMakeLists.txt b/src/TNL/CMakeLists.txt index d3371ab5e82b11bb763efbacb817f2333b85e09d..8d713d4ad7bce5337dc803cee14659a922e29c2e 100755 --- a/src/TNL/CMakeLists.txt +++ b/src/TNL/CMakeLists.txt @@ -1,5 +1,6 @@ ADD_SUBDIRECTORY( Config ) ADD_SUBDIRECTORY( Containers ) +ADD_SUBDIRECTORY( Debugging ) ADD_SUBDIRECTORY( Devices ) ADD_SUBDIRECTORY( Experimental ) ADD_SUBDIRECTORY( Functions ) diff --git a/src/TNL/Debugging/CMakeLists.txt b/src/TNL/Debugging/CMakeLists.txt new file mode 100755 index 0000000000000000000000000000000000000000..d689cdaa12eef26c1feb3a061223a2d32271b58f --- /dev/null +++ b/src/TNL/Debugging/CMakeLists.txt @@ -0,0 +1,7 @@ +SET( headers + FPE.h + MemoryUsage.h + StackBacktrace.h +) + +INSTALL( FILES ${headers} DESTINATION include/tnl-${tnlVersion}/TNL/Debugging ) diff --git a/src/TNL/Debugging/FPE.h b/src/TNL/Debugging/FPE.h new file mode 100644 index 0000000000000000000000000000000000000000..09011e8953205089db602357ad9840965dc95ede --- /dev/null +++ b/src/TNL/Debugging/FPE.h @@ -0,0 +1,69 @@ +/*************************************************************************** + MeshConfigBase.h - description + ------------------- + begin : Nov 6, 2016 + copyright : (C) 2016 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +#pragma once + +#include <cfenv> +#include <signal.h> + +#include <TNL/Debugging/StackBacktrace.h> + +namespace TNL { +namespace Debugging { + +static void +printStackBacktraceAndAbort( int sig = 0 ) +{ + if( sig == SIGSEGV ) + fprintf(stderr, "Invalid memory reference, printing backtrace and aborting...\n"); + else if( sig == SIGFPE ) { + /* + * Unfortunately it is not possible to get the floating-point exception type + * from a signal handler. Otherwise, it would be done this way: + * + * fprintf(stderr, "Floating-point exception"); + * if(fetestexcept(FE_DIVBYZERO)) fprintf(stderr, " FE_DIVBYZERO"); + * if(fetestexcept(FE_INEXACT)) fprintf(stderr, " FE_INEXACT"); + * if(fetestexcept(FE_INVALID)) fprintf(stderr, " FE_INVALID"); + * if(fetestexcept(FE_OVERFLOW)) fprintf(stderr, " FE_OVERFLOW"); + * if(fetestexcept(FE_UNDERFLOW)) fprintf(stderr, " FE_UNDERFLOW"); + * fprintf(stderr, " occurred, printing backtrace and aborting...\n"); + */ + fprintf(stderr, "Floating-point exception occurred, printing backtrace and aborting...\n"); + } + else + fprintf( stderr, "Aborting due to signal %d...\n", sig ); + printStackBacktrace(); + abort(); +} + +/* + * Registers handler for SIGSEGV and SIGFPE signals and enables conversion of + * floating-point exceptions into SIGFPE. This is useful e.g. for tracing where + * NANs occurred. Example usage: + * + * int main() + * { + * #ifndef NDEBUG + * registerFloatingPointExceptionTracking() + * #endif + * [start some computation here...] + * } + */ +static void +trackFloatingPointExceptions() +{ + signal( SIGSEGV, printStackBacktraceAndAbort ); + signal( SIGFPE, printStackBacktraceAndAbort ); + feenableexcept( FE_ALL_EXCEPT & ~FE_INEXACT ); +} + +} // namespace Debugging +} // namespace TNL diff --git a/src/TNL/Debugging/MemoryUsage.h b/src/TNL/Debugging/MemoryUsage.h new file mode 100644 index 0000000000000000000000000000000000000000..03ea3fe4d3db40f566ce82762521da1596e45dd5 --- /dev/null +++ b/src/TNL/Debugging/MemoryUsage.h @@ -0,0 +1,70 @@ +/*************************************************************************** + MeshConfigBase.h - description + ------------------- + begin : Nov 6, 2016 + copyright : (C) 2016 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +#pragma once + +#include <iostream> +#include <fstream> +#include <string> +#include <limits> + +namespace TNL { +namespace Debugging { + +/* + * Prints memory usage of the current process into the specified stream. + * + * The information is obtained from /proc/self/status, which is assumed to be + * present on the system. The meaning of the printed values is following: + * + * - VmSize: Virtual memory size. + * - VmRSS: Resident set size. + * - VmHWM: Peak resident set size ("high water mark"). + * + * See the proc(5) manual on Linux for details. + */ +static void +printMemoryUsage( std::ostream& str = std::cerr ) +{ + std::ifstream meminfo("/proc/self/status"); + if( meminfo.fail() ) { + std::cerr << "error: unable to open /proc/self/status" << std::endl; + return; + } + + unsigned vm = 0; + unsigned rss = 0; + unsigned hwm = 0; + + std::string desc; + while( meminfo.good() ) { + // extract description (first column) + meminfo >> desc; + + if( desc == "VmSize:" ) + meminfo >> vm; + if( desc == "VmHWM:" ) + meminfo >> hwm; + if( desc == "VmRSS:" ) + meminfo >> rss; + + // ignore the rest of irrelevant lines + meminfo.ignore( std::numeric_limits< std::streamsize >::max(), '\n' ); + } + + str << "Memory usage (MiB): " + << "VmSize = " << vm / 1024 << "MiB, " + << "VmRSS = " << rss / 1024 << "MiB, " + << "VmHWM = " << hwm / 1024 << "MiB, " + << std::endl; +} + +} // namespace Debugging +} // namespace TNL diff --git a/src/TNL/Debugging/StackBacktrace.h b/src/TNL/Debugging/StackBacktrace.h new file mode 100644 index 0000000000000000000000000000000000000000..17e576fd7219e6da8b4b7e3bfc5beab4510d42cc --- /dev/null +++ b/src/TNL/Debugging/StackBacktrace.h @@ -0,0 +1,104 @@ +/*************************************************************************** + MeshConfigBase.h - description + ------------------- + begin : Nov 6, 2016 + copyright : (C) 2016 by Tomas Oberhuber et al. + email : tomas.oberhuber@fjfi.cvut.cz + ***************************************************************************/ + +/* See Copyright Notice in tnl/Copyright */ + +#pragma once + +#include <stdio.h> +#include <stdlib.h> +#include <execinfo.h> +#include <cxxabi.h> + +namespace TNL { +namespace Debugging { + +/* + * Print a demangled stack backtrace of the caller function to FILE* out. + * + * Reference: http://panthema.net/2008/0901-stacktrace-demangled/ + * + * Note that the program must be linked with the -rdynamic flag, otherwise + * demangling will not work. + */ +static void +printStackBacktrace( FILE *out = stderr, unsigned int max_frames = 63 ) +{ + fprintf(out, "stack trace:\n"); + + // storage array for stack trace address data + void* addrlist[max_frames+1]; + + // retrieve current stack addresses + int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*)); + + if (addrlen == 0) { + fprintf(out, " <empty, possibly corrupt>\n"); + return; + } + + // resolve addresses into strings containing "filename(function+address)", + // this array must be free()-ed + char** symbollist = backtrace_symbols(addrlist, addrlen); + + // allocate string which will be filled with the demangled function name + size_t funcnamesize = 256; + char* funcname = (char*)malloc(funcnamesize); + + // iterate over the returned symbol lines. skip the first, it is the + // address of this function. + for (int i = 1; i < addrlen; i++) { + char *begin_name = 0, *begin_offset = 0, *end_offset = 0; + + // find parentheses and +address offset surrounding the mangled name: + // ./module(function+0x15c) [0x8048a6d] + for (char *p = symbollist[i]; *p; ++p) { + if (*p == '(') + begin_name = p; + else if (*p == '+') + begin_offset = p; + else if (*p == ')' && begin_offset) { + end_offset = p; + break; + } + } + + if (begin_name && begin_offset && end_offset && begin_name < begin_offset) { + *begin_name++ = '\0'; + *begin_offset++ = '\0'; + *end_offset = '\0'; + + // mangled name is now in [begin_name, begin_offset) and caller + // offset in [begin_offset, end_offset). now apply + // __cxa_demangle(): + + int status; + char* ret = abi::__cxa_demangle(begin_name, funcname, &funcnamesize, &status); + if (status == 0) { + funcname = ret; // use possibly realloc()-ed string + fprintf(out, " %d %s : %s+%s\n", + i, symbollist[i], funcname, begin_offset); + } + else { + // demangling failed. Output function name as a C function with no arguments. + fprintf(out, " %d %s : %s()+%s\n", + i, symbollist[i], begin_name, begin_offset); + } + } + else { + // couldn't parse the line? print the whole line. + fprintf(out, " %d %s\n", i, symbollist[i]); + } + } + + free(funcname); + free(symbollist); +} + +} // namespace Debugging +} // namespace TNL