335 lines
9.6 KiB
C++
335 lines
9.6 KiB
C++
#define NOMINMAX
|
|
#include "StackTrace/Utilities.h"
|
|
#include "StackTrace/ErrorHandlers.h"
|
|
#include "StackTrace/StackTrace.h"
|
|
|
|
#include <algorithm>
|
|
#include <csignal>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <mutex>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <typeinfo>
|
|
|
|
#ifdef USE_MPI
|
|
#include "mpi.h"
|
|
#endif
|
|
|
|
#ifdef USE_TIMER
|
|
#include "MemoryApp.h"
|
|
#endif
|
|
|
|
#ifdef USE_GCOV
|
|
extern "C" void __gcov_flush( void );
|
|
#endif
|
|
|
|
|
|
#define perr std::cerr
|
|
|
|
|
|
// Detect the OS
|
|
// clang-format off
|
|
#if defined( WIN32 ) || defined( _WIN32 ) || defined( WIN64 ) || defined( _WIN64 ) || defined( _MSC_VER )
|
|
#define USE_WINDOWS
|
|
#elif defined( __APPLE__ )
|
|
#define USE_MAC
|
|
#elif defined( __linux ) || defined( __linux__ ) || defined( __unix ) || defined( __posix )
|
|
#define USE_LINUX
|
|
#define USE_NM
|
|
#else
|
|
#error Unknown OS
|
|
#endif
|
|
// clang-format on
|
|
|
|
|
|
// Include system dependent headers
|
|
// clang-format off
|
|
#ifdef USE_WINDOWS
|
|
#include <process.h>
|
|
#include <psapi.h>
|
|
#include <stdio.h>
|
|
#include <tchar.h>
|
|
#include <windows.h>
|
|
#else
|
|
#include <dlfcn.h>
|
|
#include <execinfo.h>
|
|
#include <sched.h>
|
|
#include <sys/time.h>
|
|
#include <ctime>
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef USE_LINUX
|
|
#include <malloc.h>
|
|
#endif
|
|
#ifdef USE_MAC
|
|
#include <mach/mach.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/types.h>
|
|
#endif
|
|
// clang-format on
|
|
|
|
|
|
#ifdef __GNUC__
|
|
#define USE_ABI
|
|
#include <cxxabi.h>
|
|
#endif
|
|
|
|
|
|
namespace StackTrace {
|
|
|
|
|
|
/****************************************************************************
|
|
* Function to find an entry *
|
|
****************************************************************************/
|
|
template<class TYPE>
|
|
inline size_t findfirst( const std::vector<TYPE> &X, TYPE Y )
|
|
{
|
|
if ( X.empty() )
|
|
return 0;
|
|
size_t lower = 0;
|
|
size_t upper = X.size() - 1;
|
|
if ( X[lower] >= Y )
|
|
return lower;
|
|
if ( X[upper] < Y )
|
|
return upper;
|
|
while ( ( upper - lower ) != 1 ) {
|
|
size_t value = ( upper + lower ) / 2;
|
|
if ( X[value] >= Y )
|
|
upper = value;
|
|
else
|
|
lower = value;
|
|
}
|
|
return upper;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Function to terminate the program *
|
|
****************************************************************************/
|
|
static bool abort_throwException = false;
|
|
static int force_exit = 0;
|
|
void Utilities::setAbortBehavior( bool throwException, int stackType )
|
|
{
|
|
abort_throwException = throwException;
|
|
StackTrace::setDefaultStackType( static_cast<printStackType>( stackType ) );
|
|
}
|
|
void Utilities::abort( const std::string &message, const std::string &filename, const int line )
|
|
{
|
|
abort_error err;
|
|
err.message = message;
|
|
err.filename = filename;
|
|
err.type = terminateType::abort;
|
|
err.line = line;
|
|
err.bytes = Utilities::getMemoryUsage();
|
|
err.stackType = StackTrace::getDefaultStackType();
|
|
err.stack = StackTrace::backtrace();
|
|
throw err;
|
|
}
|
|
static std::mutex terminate_mutex;
|
|
static inline void callAbort()
|
|
{
|
|
#ifdef USE_GCOV
|
|
__gcov_flush();
|
|
#endif
|
|
terminate_mutex.unlock();
|
|
std::abort();
|
|
}
|
|
void Utilities::terminate( const StackTrace::abort_error &err )
|
|
{
|
|
// Lock mutex to ensure multiple threads do not try to abort simultaneously
|
|
terminate_mutex.lock();
|
|
// Clear the error handlers
|
|
clearErrorHandler();
|
|
// Print the message and abort
|
|
if ( force_exit > 1 ) {
|
|
callAbort();
|
|
} else if ( !abort_throwException ) {
|
|
// Use MPI_abort (will terminate all processes)
|
|
force_exit = 2;
|
|
perr << err.what();
|
|
#if defined( USE_MPI ) || defined( HAVE_MPI )
|
|
int initialized = 0, finalized = 0;
|
|
MPI_Initialized( &initialized );
|
|
MPI_Finalized( &finalized );
|
|
if ( initialized != 0 && finalized == 0 ) {
|
|
clearMPIErrorHandler( MPI_COMM_WORLD );
|
|
MPI_Abort( MPI_COMM_WORLD, -1 );
|
|
}
|
|
#endif
|
|
callAbort();
|
|
} else {
|
|
perr << err.what();
|
|
perr.flush();
|
|
callAbort();
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Functions to set the error handler *
|
|
****************************************************************************/
|
|
static void setTerminateErrorHandler()
|
|
{
|
|
// Set the terminate routine for runtime errors
|
|
StackTrace::setErrorHandler( Utilities::terminate );
|
|
}
|
|
void Utilities::setErrorHandlers()
|
|
{
|
|
#ifdef USE_MPI
|
|
setMPIErrorHandler( MPI_COMM_WORLD );
|
|
setMPIErrorHandler( MPI_COMM_SELF );
|
|
#endif
|
|
setTerminateErrorHandler();
|
|
}
|
|
void Utilities::clearErrorHandlers()
|
|
{
|
|
#ifdef USE_MPI
|
|
clearMPIErrorHandler( MPI_COMM_WORLD );
|
|
clearMPIErrorHandler( MPI_COMM_SELF );
|
|
#endif
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Function to get the memory usage *
|
|
* Note: this function should be thread-safe *
|
|
****************************************************************************/
|
|
// clang-format off
|
|
#if defined( USE_MAC ) || defined( USE_LINUX )
|
|
// Get the page size on mac or linux
|
|
static size_t page_size = static_cast<size_t>( sysconf( _SC_PAGESIZE ) );
|
|
#endif
|
|
size_t Utilities::getSystemMemory()
|
|
{
|
|
#if defined( USE_LINUX )
|
|
static long pages = sysconf( _SC_PHYS_PAGES );
|
|
size_t N_bytes = pages * page_size;
|
|
#elif defined( USE_MAC )
|
|
int mib[2] = { CTL_HW, HW_MEMSIZE };
|
|
u_int namelen = sizeof( mib ) / sizeof( mib[0] );
|
|
uint64_t size;
|
|
size_t len = sizeof( size );
|
|
size_t N_bytes = 0;
|
|
if ( sysctl( mib, namelen, &size, &len, nullptr, 0 ) == 0 )
|
|
N_bytes = size;
|
|
#elif defined( USE_WINDOWS )
|
|
MEMORYSTATUSEX status;
|
|
status.dwLength = sizeof( status );
|
|
GlobalMemoryStatusEx( &status );
|
|
size_t N_bytes = status.ullTotalPhys;
|
|
#else
|
|
#error Unknown OS
|
|
#endif
|
|
return N_bytes;
|
|
}
|
|
size_t Utilities::getMemoryUsage()
|
|
{
|
|
#ifdef USE_TIMER
|
|
size_t N_bytes = MemoryApp::getTotalMemoryUsage();
|
|
#else
|
|
#if defined( USE_LINUX )
|
|
struct mallinfo meminfo = mallinfo();
|
|
size_t size_hblkhd = static_cast<unsigned int>( meminfo.hblkhd );
|
|
size_t size_uordblks = static_cast<unsigned int>( meminfo.uordblks );
|
|
size_t N_bytes = size_hblkhd + size_uordblks;
|
|
#elif defined( USE_MAC )
|
|
struct task_basic_info t_info;
|
|
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
|
|
if ( KERN_SUCCESS !=
|
|
task_info( mach_task_self(), TASK_BASIC_INFO, (task_info_t) &t_info, &t_info_count ) ) {
|
|
return 0;
|
|
}
|
|
size_t N_bytes = t_info.virtual_size;
|
|
#elif defined( USE_WINDOWS )
|
|
PROCESS_MEMORY_COUNTERS memCounter;
|
|
GetProcessMemoryInfo( GetCurrentProcess(), &memCounter, sizeof( memCounter ) );
|
|
size_t N_bytes = memCounter.WorkingSetSize;
|
|
#else
|
|
#error Unknown OS
|
|
#endif
|
|
#endif
|
|
return N_bytes;
|
|
}
|
|
// clang-format on
|
|
|
|
|
|
/****************************************************************************
|
|
* Functions to get the time and timer resolution *
|
|
****************************************************************************/
|
|
#if defined( USE_WINDOWS )
|
|
double Utilities::time()
|
|
{
|
|
LARGE_INTEGER end, f;
|
|
QueryPerformanceFrequency( &f );
|
|
QueryPerformanceCounter( &end );
|
|
double time = ( (double) end.QuadPart ) / ( (double) f.QuadPart );
|
|
return time;
|
|
}
|
|
double Utilities::tick()
|
|
{
|
|
LARGE_INTEGER f;
|
|
QueryPerformanceFrequency( &f );
|
|
double resolution = ( (double) 1.0 ) / ( (double) f.QuadPart );
|
|
return resolution;
|
|
}
|
|
#elif defined( USE_LINUX ) || defined( USE_MAC )
|
|
double Utilities::time()
|
|
{
|
|
timeval current_time;
|
|
gettimeofday( ¤t_time, nullptr );
|
|
double time = ( (double) current_time.tv_sec ) + 1e-6 * ( (double) current_time.tv_usec );
|
|
return time;
|
|
}
|
|
double Utilities::tick()
|
|
{
|
|
timeval start, end;
|
|
gettimeofday( &start, nullptr );
|
|
gettimeofday( &end, nullptr );
|
|
while ( end.tv_sec == start.tv_sec && end.tv_usec == start.tv_usec )
|
|
gettimeofday( &end, nullptr );
|
|
double resolution = ( (double) ( end.tv_sec - start.tv_sec ) ) +
|
|
1e-6 * ( (double) ( end.tv_usec - start.tv_usec ) );
|
|
return resolution;
|
|
}
|
|
#else
|
|
#error Unknown OS
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
* Cause a segfault *
|
|
****************************************************************************/
|
|
void Utilities::cause_segfault()
|
|
{
|
|
int *ptr = nullptr;
|
|
ptr[0] = 0;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Call system command *
|
|
****************************************************************************/
|
|
std::string Utilities::exec( const string_view &cmd, int &exit_code )
|
|
{
|
|
return StackTrace::exec( cmd, exit_code );
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Get the type name *
|
|
****************************************************************************/
|
|
std::string Utilities::getTypeName( const std::type_info &id )
|
|
{
|
|
std::string name = id.name();
|
|
#if defined( USE_ABI )
|
|
int status;
|
|
name = abi::__cxa_demangle( name.c_str(), 0, 0, &status );
|
|
#endif
|
|
return name;
|
|
}
|
|
|
|
|
|
} // namespace StackTrace
|