Adding error handling

This commit is contained in:
Mark Berrill 2014-02-21 15:08:49 -05:00
parent c2e591fac9
commit 06edcf338f
11 changed files with 1156 additions and 11 deletions

View File

@ -172,7 +172,7 @@ MACRO ( CONFIGURE_SYSTEM )
ENDMACRO ()
# Macro to configure AtomicModel-specific options
# Macro to configure LBPM specific options
MACRO ( CONFIGURE_LBPM )
# Set the maximum number of processors for the tests
IF ( NOT TEST_MAX_PROCS )

View File

@ -491,8 +491,6 @@ MACRO( ADD_DISTCLEAN )
*.vcxproj*
ipch
x64
atomic.lib
atomic.pdb
)
FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/distclean.bat "del /s /q /f " )
FOREACH (fileToDelete ${DISTCLEANED})

View File

@ -9,7 +9,7 @@
#include <exception> // std::exception
#include <stdexcept>
#include <stdio.h>
#include "common/Utilities.h"
using namespace std;
@ -20,9 +20,7 @@ inline void ReadSpherePacking(int nspheres, double *List_cx, double *List_cy, do
//...... READ IN THE SPHERES...................................
cout << "Reading the packing file..." << endl;
FILE *fid = fopen("pack.out","rb");
if ( fid==NULL) {
throw logic_error("Error opening pack.out");
}
INSIST(fid==NULL,"Error opening pack.out");
//.........Trash the header lines..........
char * line = new char[100];
fgets(line, 100, fid);
@ -42,9 +40,7 @@ inline void ReadSpherePacking(int nspheres, double *List_cx, double *List_cy, do
count++;
}
cout << "Number of spheres extracted is: " << count/4 << endl;
if ( count != nspheres ) {
throw logic_error("Specified number of spheres is probably incorrect!");
}
INSIST( count != nspheres, "Specified number of spheres is probably incorrect!" );
// .............................................................
}

345
common/UnitTest.cpp Executable file
View File

@ -0,0 +1,345 @@
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include "common/UnitTest.h"
#include "common/Utilities.h"
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
// Windows
// Sleep is defined in milliseconds
#else
// Linux
// usleep is defined in microseconds, create a Sleep command
#define Sleep(x) usleep(x*1000)
#endif
/********************************************************************
* Empty Constructor *
********************************************************************/
UnitTest::UnitTest() {
#ifdef USE_MPI
comm = MPI_COMM_WORLD;
#endif
}
/********************************************************************
* Print a global report *
* Note: only rank 0 will print, all messages will be aggregated *
********************************************************************/
void UnitTest::report(const int level0) {
int size = getSize();
int rank = getRank();
// Broadcast the print level from rank 0
int level = level0;
#ifdef USE_MPI
if ( getSize() > 1 )
MPI_Bcast( &level, 1, MPI_INT, 0, comm );
#endif
if ( level<0 || level > 2 )
ERROR("Invalid print level");
// Perform a global all gather to get the number of failures per processor
std::vector<int> N_pass(size,0);
std::vector<int> N_fail(size,0);
std::vector<int> N_expected_fail(size,0);
int local_pass_size = (int) pass_messages.size();
int local_fail_size = (int) fail_messages.size();
int local_expected_fail_size = (int) expected_fail_messages.size();
if ( getSize() > 1 ) {
#ifdef USE_MPI
MPI_Allgather( &local_pass_size, 1, MPI_INT, &N_pass[0], 1, MPI_INT, comm);
MPI_Allgather( &local_fail_size, 1, MPI_INT, &N_fail[0], 1, MPI_INT, comm);
MPI_Allgather( &local_expected_fail_size, 1, MPI_INT, &N_expected_fail[0], 1, MPI_INT, comm);
#endif
} else {
N_pass[0] = local_pass_size;
N_fail[0] = local_fail_size;
N_expected_fail[0] = local_expected_fail_size;
}
int N_pass_tot = 0;
int N_expected_fail_tot = 0;
for (int i=0; i<size; i++) {
N_pass_tot += N_pass[i];
N_expected_fail_tot += N_expected_fail[i];
}
// Send all messages to rank 0 (if needed)
std::vector< std::vector<std::string> > pass_messages_rank(size);
std::vector< std::vector<std::string> > fail_messages_rank(size);
std::vector< std::vector<std::string> > expected_fail_rank(size);
// Get the pass messages
if ( ( level==1 && N_pass_tot<=20 ) || level==2 ) {
if ( rank==0 ) {
// Rank 0 should receive all messages
for (int i=0; i<size; i++) {
if ( i==0 )
pass_messages_rank[i] = pass_messages;
else if ( N_pass[i]>0 )
pass_messages_rank[i] = unpack_message_stream(i,1);
}
} else if ( pass_messages.size() ) {
// All other ranks send their message (use non-blocking communication)
pack_message_stream(pass_messages,0,1);
}
}
// Get the fail messages
if ( level==1 || level==2 ) {
if ( rank==0 ) {
// Rank 0 should receive all messages
for (int i=0; i<size; i++) {
if ( i==0 )
fail_messages_rank[i] = fail_messages;
else if ( N_fail[i]>0 )
fail_messages_rank[i] = unpack_message_stream(i,2);
}
} else if ( !fail_messages.empty() ){
// All other ranks send their message (use non-blocking communication)
pack_message_stream(fail_messages,0,2);
}
}
// Get the expected_fail messages
if ( ( level==1 && N_expected_fail_tot<=50 ) || level==2 ) {
if ( rank==0 ) {
// Rank 0 should receive all messages
for (int i=0; i<size; i++) {
if ( i==0 )
expected_fail_rank[i] = expected_fail_messages;
else if ( N_expected_fail[i]>0 )
expected_fail_rank[i] = unpack_message_stream(i,3);
}
} else if ( !expected_fail_messages.empty() ){
// All other ranks send their message (use non-blocking communication)
pack_message_stream(expected_fail_messages,0,3);
}
}
// Print the results of all messages (only rank 0 will print)
if ( rank==0 ) {
std::cout << std::endl;
// Print the passed tests
std::cout << "Tests passed" << std::endl;
if ( level==0 || ( level==1 && N_pass_tot>20 ) ) {
// We want to print a summary
if ( size>8 ) {
// Print 1 summary for all processors
std::cout << " " << N_pass_tot << " tests passed (use report level 2 for more detail)" << std::endl;
} else {
// Print a summary for each processor
for (int i=0; i<size; i++)
std::cout << " " << N_pass[i] << " tests passed (proc " << i << ") (use report level 2 for more detail)" << std::endl;
}
} else {
// We want to print all messages
for (int i=0; i<size; i++) {
ASSERT( (int)pass_messages_rank[i].size() == N_pass[i] );
if ( N_pass[i] > 0 ) {
std::cout << " Proccessor " << i << ":" << std::endl;
for (unsigned int j=0; j<pass_messages_rank[i].size(); j++)
std::cout << " " << pass_messages_rank[i][j] << std::endl;
}
}
}
std::cout << std::endl;
// Print the tests that failed
std::cout << "Tests failed" << std::endl;
if ( level==0 ) {
// We want to print a summary
if ( size>8 ) {
// Print 1 summary for all processors
std::cout << " " << N_pass_tot << " tests failed (use report level 2 for more detail)" << std::endl;
} else {
// Print a summary for each processor
for (int i=0; i<size; i++)
std::cout << " " << N_fail[i] << " tests failed (proc " << i << ") (use report level 1 or 2 for more detail)" << std::endl;
}
} else {
// We want to print all messages
for (int i=0; i<size; i++) {
ASSERT( (int)fail_messages_rank[i].size() == N_fail[i] );
if ( N_fail[i] > 0 ) {
std::cout << " Processor " << i << ":" << std::endl;
for (unsigned int j=0; j<fail_messages_rank[i].size(); j++)
std::cout << " " << fail_messages_rank[i][j] << std::endl;
}
}
}
std::cout << std::endl;
// Print the tests that expected failed
std::cout << "Tests expected failed" << std::endl;
if ( level==0 || ( level==1 && N_expected_fail_tot>50 ) ) {
// We want to print a summary
if ( size>8 ) {
// Print 1 summary for all processors
std::cout << " " << N_expected_fail_tot << " tests expected failed (use report level 2 for more detail)" << std::endl;
} else {
// Print a summary for each processor
for (int i=0; i<size; i++)
std::cout << " " << N_expected_fail[i] << " tests expected failed (proc " << i << ") (use report level 1 or 2 for more detail)" << std::endl;
}
} else {
// We want to print all messages
for (int i=0; i<size; i++) {
ASSERT( (int)expected_fail_rank[i].size() == N_expected_fail[i] );
if ( N_expected_fail[i] > 0 ) {
std::cout << " Processor " << i << ":" << std::endl;
for (unsigned int j=0; j<expected_fail_rank[i].size(); j++)
std::cout << " " << expected_fail_rank[i][j] << std::endl;
}
}
}
std::cout << std::endl;
}
// Add a barrier to synchronize all processors (rank 0 is much slower)
#ifdef USE_MPI
if ( getSize() > 1 )
MPI_Barrier(comm);
#endif
}
/********************************************************************
* Pack and send the given messages *
********************************************************************/
void UnitTest::pack_message_stream(const std::vector<std::string>& messages, const int rank, const int tag)
{
#ifdef USE_MPI
// Get the size of the messages
int N_messages = (int) messages.size();
int *msg_size = new int[N_messages];
int msg_size_tot = 0;
for (int i=0; i<N_messages; i++) {
msg_size[i] = (int) messages[i].size();
msg_size_tot += msg_size[i];
}
// Allocate space for the message stream
int size_data = (N_messages+1)*sizeof(int)+msg_size_tot;
char *data = new char[size_data];
// Pack the message stream
int *tmp = (int*) data;
tmp[0] = N_messages;
for (int i=0; i<N_messages; i++)
tmp[i+1] = msg_size[i];
int k = (N_messages+1)*sizeof(int);
for (int i=0; i<N_messages; i++) {
messages[i].copy(&data[k],msg_size[i]);
k += msg_size[i];
}
// Send the message stream (using a non-blocking send)
MPI_Request request;
MPI_Isend( data, size_data, MPI_CHAR, rank, tag, comm, &request );
// Wait for the communication to send and free the temporary memory
MPI_Status status;
MPI_Wait( &request, &status );
delete [] data;
delete [] msg_size;
#endif
}
/********************************************************************
* receive and unpack a message stream *
********************************************************************/
std::vector<std::string> UnitTest::unpack_message_stream(const int rank, const int tag)
{
#ifdef USE_MPI
// Probe the message to get the message size
MPI_Status status;
MPI_Probe(rank,tag,comm,&status);
int size_data=-1;
MPI_Get_count(&status,MPI_BYTE,&size_data);
ASSERT(size_data>=0);
// Allocate memory to receive the data
char *data = new char[size_data];
// receive the data (using a non-blocking receive)
MPI_Request request;
MPI_Irecv( data, size_data, MPI_CHAR, rank, tag, comm, &request );
// Wait for the communication to be received
MPI_Wait( &request, &status );
// Unpack the message stream
int *tmp = (int*) data;
int N_messages = tmp[0];
int *msg_size = &tmp[1];
std::vector<std::string> messages(N_messages);
int k = (N_messages+1)*sizeof(int);
for (int i=0; i<N_messages; i++) {
messages[i] = std::string(&data[k],msg_size[i]);
k += msg_size[i];
}
// Delete the temporary memory
delete [] data;
return messages;
#else
return std::vector<std::string>();
#endif
}
/********************************************************************
* Other functions *
********************************************************************/
int UnitTest::getRank()
{
int rank = 0;
#ifdef USE_MPI
int flag=0;
MPI_Initialized(&flag);
if ( flag )
MPI_Comm_rank( comm, &rank );
#endif
return rank;
}
int UnitTest::getSize()
{
int size = 1;
#ifdef USE_MPI
int flag=0;
MPI_Initialized(&flag);
if ( flag )
MPI_Comm_size( comm, &size );
#endif
return size;
}
size_t UnitTest::NumPassGlobal()
{
size_t num = pass_messages.size();
#ifdef USE_MPI
if ( getSize() > 1 ) {
int send = static_cast<int>(num);
int sum = 0;
MPI_Allreduce( &send, &sum, 1, MPI_INT, MPI_SUM, comm );
num = static_cast<size_t>(sum);
}
#endif
return num;
}
size_t UnitTest::NumFailGlobal()
{
size_t num = fail_messages.size();
#ifdef USE_MPI
if ( getSize() > 1 ) {
int send = static_cast<int>(num);
int sum = 0;
MPI_Allreduce( &send, &sum, 1, MPI_INT, MPI_SUM, comm );
num = static_cast<size_t>(sum);
}
#endif
return num;
}
size_t UnitTest::NumExpectedFailGlobal()
{
size_t num = expected_fail_messages.size();
#ifdef USE_MPI
if ( getSize() > 1 ) {
int send = static_cast<int>(num);
int sum = 0;
MPI_Allreduce( &send, &sum, 1, MPI_INT, MPI_SUM, comm );
num = static_cast<size_t>(sum);
}
#endif
return num;
}

105
common/UnitTest.h Executable file
View File

@ -0,0 +1,105 @@
#ifndef included_UnitTest
#define included_UnitTest
#include <sstream>
#include <vector>
#include <string>
#ifdef USE_MPI
#include "mpi.h"
#endif
/*!
* @brief Class UnitTest is simple utility for running unit tests.
* It provides basic routines for tracing success or failure of tests,
* and reporting the results.
* \par Code Sample:
* \code
try {
std::cout << "Testing tstOne" << std::endl;
tstOne(&ut);
ut.passes("Test XXX passed");
} catch( ... ) {
ut.failure("An unknown exception was thrown");
}
ut.report();
return ut.NumFail();
* \endcode
*/
class UnitTest {
public:
//! Constructor
UnitTest();
//! Indicate a passed test
virtual void passes (const std::string &in) { pass_messages.push_back(in); }
//! Indicate a failed test
virtual void failure (const std::string &in) { fail_messages.push_back(in); }
//! Indicate an expected failed test
virtual void expected_failure (const std::string &in) { expected_fail_messages.push_back(in); }
//! Return the number of passed tests locally
virtual size_t NumPassLocal () { return pass_messages.size(); }
//! Return the number of failed tests locally
virtual size_t NumFailLocal () { return fail_messages.size(); }
//! Return the number of expected failed tests locally
virtual size_t NumExpectedFailLocal () { return expected_fail_messages.size(); }
//! Return the number of passed tests locally
virtual size_t NumPassGlobal ();
//! Return the number of failed tests locally
virtual size_t NumFailGlobal ();
//! Return the number of expected failed tests locally
virtual size_t NumExpectedFailGlobal ();
//! Return the rank of the current processor
int getRank ();
//! Return the number of processors
int getSize ();
/*!
* Print a report of the passed and failed tests.
* Note: This is a blocking call that all processors must execute together.
* Note: Only rank 0 will print the messages (this is necessary as other ranks may not be able to print correctly).
* @param level Optional integer specifying the level of reporting (default: 1)
* 0: Report the number of tests passed, failed, and expected failures.
* 1: Report the number of passed tests (if <=20) or the number passed otherwise,
* report all failures,
* report the number of expected failed tests (if <=50) or the number passed otherwise.
* 2: Report all passed, failed, and expected failed tests.
*/
virtual void report(const int level=1);
protected:
std::vector<std::string> pass_messages;
std::vector<std::string> fail_messages;
std::vector<std::string> expected_fail_messages;
#ifdef USE_MPI
MPI_Comm comm;
#endif
private:
// Make the copy constructor private
UnitTest(const UnitTest& p) {}
// Function to pack the messages into a single data stream and send to the given processor
// Note: This function does not return until the message stream has been sent
void pack_message_stream(const std::vector<std::string>& messages, const int rank, const int tag);
// Function to unpack the messages from a single data stream
// Note: This function does not return until the message stream has been received
std::vector<std::string> unpack_message_stream(const int rank, const int tag);
};
#endif

384
common/Utilities.cpp Normal file
View File

@ -0,0 +1,384 @@
#include "common/Utilities.h"
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <fstream>
#include <string.h>
#include <signal.h>
#ifdef USE_MPI
#include "mpi.h"
#endif
// Detect the OS and include system dependent headers
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) || defined(_MSC_VER)
// Note: windows has not been testeds
#define USE_WINDOWS
#include <windows.h>
#include <process.h>
#include <stdio.h>
#include <tchar.h>
#include <psapi.h>
#include <DbgHelp.h>
#define mkdir(path, mode) _mkdir(path)
//#pragma comment(lib, psapi.lib) //added
//#pragma comment(linker, /DEFAULTLIB:psapi.lib)
#elif defined(__APPLE__)
#define USE_MAC
#include <sys/time.h>
#include <signal.h>
#include <execinfo.h>
#include <dlfcn.h>
#include <mach/mach.h>
#elif defined(__linux) || defined(__unix) || defined(__posix)
#define USE_LINUX
#include <sys/time.h>
#include <execinfo.h>
#include <dlfcn.h>
#include <malloc.h>
#else
#error Unknown OS
#endif
#ifdef __GNUC__
#define USE_ABI
#include <cxxabi.h>
#endif
/****************************************************************************
* Function to terminate the program *
****************************************************************************/
static bool abort_printMemory = true;
static bool abort_printStack = true;
static bool abort_throwException = false;
static int force_exit = 0;
void Utilities::setAbortBehavior( bool printMemory, bool printStack, bool throwException )
{
abort_printMemory = printMemory;
abort_printStack = printStack;
abort_throwException = throwException;
}
void Utilities::abort(const std::string &message, const std::string &filename, const int line)
{
std::stringstream msg;
msg << "Program abort called in file `" << filename << "' at line " << line << std::endl;
// Add the memory usage and call stack to the error message
if ( abort_printMemory ) {
size_t N_bytes = Utilities::getMemoryUsage();
msg << "Bytes used = " << N_bytes << std::endl;
}
if ( abort_printStack ) {
std::vector<std::string> stack = Utilities::getCallStack();
msg << std::endl;
msg << "Stack Trace:\n";
for (size_t i=0; i<stack.size(); i++)
msg << " " << stack[i] << std::endl;
}
msg << std::endl << message << std::endl;
// Print the message and abort
if ( force_exit>1 ) {
exit(-1);
} else if ( !abort_throwException ) {
// Use MPI_abort (will terminate all processes)
force_exit = 2;
std::cerr << msg.str();
#if defined(USE_MPI) || defined(HAVE_MPI)
int initialized=0, finalized=0;
MPI_Initialized(&initialized);
MPI_Finalized(&finalized);
if ( initialized!=0 && finalized==0 )
MPI_Abort(MPI_COMM_WORLD,-1);
#endif
exit(-1);
} else if ( force_exit>0 ) {
exit(-1);
} else {
// Throw and standard exception (allows the use of try, catch)
throw std::logic_error(msg.str());
}
}
/****************************************************************************
* Function to handle MPI errors *
****************************************************************************/
/*#if defined(USE_MPI) || defined(HAVE_MPI)
MPI_Errhandler mpierr;
void MPI_error_handler_fun( MPI_Comm *comm, int *err, ... )
{
if ( *err==MPI_ERR_COMM && *comm==MPI_COMM_WORLD ) {
// Special error handling for an invalid MPI_COMM_WORLD
std::cerr << "Error invalid MPI_COMM_WORLD";
exit(-1);
}
int msg_len=0;
char message[1000];
MPI_Error_string( *err, message, &msg_len );
if ( msg_len <= 0 )
abort("Unkown error in MPI");
abort( "Error calling MPI routine:\n" + std::string(message) );
}
#endif*/
/****************************************************************************
* Function to handle unhandled exceptions *
****************************************************************************/
void term_func_abort(int err)
{
printf("Exiting due to abort (%i)\n",err);
std::vector<std::string> stack = Utilities::getCallStack();
std::string message = "Stack Trace:\n";
for (size_t i=0; i<stack.size(); i++)
message += " " + stack[i] += "\n";
message += "\nExiting\n";
// Print the message and abort
std::cerr << message;
#ifdef USE_MPI
if ( !abort_throwException )
MPI_Abort(MPI_COMM_WORLD,-1);
#endif
exit(-1);
}
static int tried_throw = 0;
void term_func()
{
// Try to re-throw the last error to get the last message
std::string last_message;
#ifdef USE_LINUX
try {
if ( tried_throw==0 ) {
tried_throw = 1;
throw;
}
// No active exception
} catch (const std::exception &err) {
// Caught a std::runtime_error
last_message = err.what();
} catch (...) {
// Caught an unknown exception
last_message = "unknown exception occurred.";
}
#endif
std::stringstream msg;
msg << "Unhandled exception:" << std::endl;
msg << " " << last_message << std::endl;
Utilities::abort( msg.str(), __FILE__, __LINE__ );
}
/****************************************************************************
* Functions to set the error handler *
****************************************************************************/
static void setTerminateErrorHandler()
{
std::set_terminate( term_func );
signal(SIGSEGV,&term_func_abort);
signal(SIGABRT,&term_func_abort);
}
void Utilities::setErrorHandlers()
{
//d_use_MPI_Abort = use_MPI_Abort;
//setMPIErrorHandler( SAMRAI::tbox::SAMRAI_MPI::getSAMRAIWorld() );
setTerminateErrorHandler();
}
/*void Utilities::setMPIErrorHandler( const SAMRAI::tbox::SAMRAI_MPI& mpi )
{
#if defined(USE_MPI) || defined(HAVE_MPI)
if ( mpierr.get()==NULL ) {
mpierr = boost::shared_ptr<MPI_Errhandler>( new MPI_Errhandler );
MPI_Comm_create_errhandler( MPI_error_handler_fun, mpierr.get() );
}
MPI_Comm_set_errhandler( mpi.getCommunicator(), *mpierr );
MPI_Comm_set_errhandler( MPI_COMM_WORLD, *mpierr );
#endif
}
void Utilities::clearMPIErrorHandler( )
{
#if defined(USE_MPI) || defined(HAVE_MPI)
if ( mpierr.get()!=NULL )
MPI_Errhandler_free( mpierr.get() ); // Delete the error handler
mpierr.reset();
MPI_Comm_set_errhandler( MPI_COMM_SELF, MPI_ERRORS_ARE_FATAL );
MPI_Comm_set_errhandler( MPI_COMM_WORLD, MPI_ERRORS_ARE_FATAL );
#endif
}*/
/****************************************************************************
* Function to get the memory usage *
* Note: this function should be thread-safe *
****************************************************************************/
#if defined(USE_MAC)
// Get the page size on mac
static size_t page_size = static_cast<size_t>(sysconf(_SC_PAGESIZE));
#endif
static size_t N_bytes_initialization = Utilities::getMemoryUsage();
size_t Utilities::getMemoryUsage()
{
size_t N_bytes = 0;
#if defined(USE_LINUX)
struct mallinfo meminfo = mallinfo();
size_t size_hblkhd = static_cast<size_t>( meminfo.hblkhd );
size_t size_uordblks = static_cast<size_t>( meminfo.uordblks );
N_bytes = static_cast<size_t>( 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;
}
N_bytes = t_info.virtual_size;
#elif defined(USE_WINDOWS)
PROCESS_MEMORY_COUNTERS memCounter;
GetProcessMemoryInfo( GetCurrentProcess(), &memCounter, sizeof(memCounter) );
N_bytes = memCounter.WorkingSetSize;
#endif
return N_bytes;
}
/****************************************************************************
* Function to get the current call stack *
****************************************************************************/
std::vector<std::string> Utilities::getCallStack()
{
std::vector<std::string> stack_list;
#if defined(USE_ABI)
void *trace[100];
memset(trace,0,100*sizeof(void*));
Dl_info dlinfo;
int status;
const char *symname;
char *demangled=NULL;
int trace_size = backtrace(trace,100);
for (int i=0; i<trace_size; ++i) {
if(!dladdr(trace[i], &dlinfo))
continue;
symname = dlinfo.dli_sname;
demangled = abi::__cxa_demangle(symname, NULL, 0, &status);
if(status == 0 && demangled)
symname = demangled;
std::string object = std::string(dlinfo.dli_fname);
std::string function = "";
if ( symname!=NULL )
function = std::string(symname);
if ( i!=0 ) { // Skip the current function
std::string stack_item = object + ": " + function;
//stack_item = "object: " + object;
//stack_item += "function: " + function;
stack_list.push_back(stack_item);
}
if ( demangled!=NULL ) {
free(demangled);
demangled=NULL;
}
}
#elif defined(USE_WINDOWS)
::CONTEXT lContext;
::ZeroMemory( &lContext, sizeof( ::CONTEXT ) );
::RtlCaptureContext( &lContext );
::STACKFRAME64 lFrameStack;
::ZeroMemory( &lFrameStack, sizeof( ::STACKFRAME64 ) );
lFrameStack.AddrPC.Offset = lContext.Rip;
lFrameStack.AddrFrame.Offset = lContext.Rbp;
lFrameStack.AddrStack.Offset = lContext.Rsp;
lFrameStack.AddrPC.Mode = lFrameStack.AddrFrame.Mode = lFrameStack.AddrStack.Mode = AddrModeFlat;
#ifdef _M_IX86
DWORD MachineType = IMAGE_FILE_MACHINE_I386;
#endif
#ifdef _M_X64
DWORD MachineType = IMAGE_FILE_MACHINE_AMD64;
#endif
#ifdef _M_IA64
DWORD MachineType = IMAGE_FILE_MACHINE_IA64;
#endif
while ( 1 ) {
int rtn = ::StackWalk64( MachineType, ::GetCurrentProcess(), ::GetCurrentThread(),
&lFrameStack, MachineType == IMAGE_FILE_MACHINE_I386 ? 0 : &lContext,
NULL, &::SymFunctionTableAccess64, &::SymGetModuleBase64, NULL );
if( !rtn )
break;
if( lFrameStack.AddrPC.Offset == 0 )
break;
::MEMORY_BASIC_INFORMATION lInfoMemory;
::VirtualQuery( ( ::PVOID )lFrameStack.AddrPC.Offset, &lInfoMemory, sizeof( lInfoMemory ) );
if ( lInfoMemory.Type==MEM_PRIVATE )
continue;
::DWORD64 lBaseAllocation = reinterpret_cast< ::DWORD64 >( lInfoMemory.AllocationBase );
::TCHAR lNameModule[ 1024 ];
::HMODULE hBaseAllocation = reinterpret_cast< ::HMODULE >( lBaseAllocation );
::GetModuleFileName( hBaseAllocation, lNameModule, 1024 );
PIMAGE_DOS_HEADER lHeaderDOS = reinterpret_cast<PIMAGE_DOS_HEADER>( lBaseAllocation );
if ( lHeaderDOS==NULL )
continue;
PIMAGE_NT_HEADERS lHeaderNT = reinterpret_cast<PIMAGE_NT_HEADERS>( lBaseAllocation + lHeaderDOS->e_lfanew );
PIMAGE_SECTION_HEADER lHeaderSection = IMAGE_FIRST_SECTION( lHeaderNT );
::DWORD64 lRVA = lFrameStack.AddrPC.Offset - lBaseAllocation;
::DWORD64 lNumberSection = ::DWORD64();
::DWORD64 lOffsetSection = ::DWORD64();
for( int lCnt = ::DWORD64(); lCnt < lHeaderNT->FileHeader.NumberOfSections; lCnt++, lHeaderSection++ ) {
::DWORD64 lSectionBase = lHeaderSection->VirtualAddress;
::DWORD64 lSectionEnd = lSectionBase + max( lHeaderSection->SizeOfRawData, lHeaderSection->Misc.VirtualSize );
if( ( lRVA >= lSectionBase ) && ( lRVA <= lSectionEnd ) ) {
lNumberSection = lCnt + 1;
lOffsetSection = lRVA - lSectionBase;
break;
}
}
std::stringstream stream;
stream << lNameModule << " : 000" << lNumberSection << " : " << reinterpret_cast<void*>(lOffsetSection);
stack_list.push_back(stream.str());
}
#else
#warning Stack trace is not supported on this compiler/OS
#endif
return stack_list;
}
// 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(&current_time,NULL);
double time = ((double)current_time.tv_sec)+1e-6*((double)current_time.tv_usec);
return time;
}
double Utilities::tick()
{
timeval start, end;
gettimeofday(&start,NULL);
gettimeofday(&end,NULL);
while ( end.tv_sec==start.tv_sec && end.tv_usec==start.tv_usec )
gettimeofday(&end,NULL);
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

70
common/Utilities.h Normal file
View File

@ -0,0 +1,70 @@
#ifndef included_Utilities
#define included_Utilities
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
/*!
* Utilities is a Singleton class containing basic routines for error
* reporting, file manipulations, etc. Included are a set of \ref Macros "macros" that are commonly used.
*/
namespace Utilities
{
/*!
* Aborts the run after printing an error message with file and
* linenumber information.
*/
void abort(const std::string &message, const std::string &filename, const int line);
/*!
* Set the behavior of abort
* @param printMemory Print the current memory usage (default is true)
* @param printStack Print the current call stack (default is true)
* @param throwException Throw an exception instead of MPI_Abort (default is false)
*/
void setAbortBehavior( bool printMemory, bool printStack, bool throwException );
//! Function to set the error handlers
void setErrorHandlers();
/*!
* Function to get the memory availible.
* This function will return the total memory availible
* Note: depending on the implimentation, this number may be rounded to
* to a multiple of the page size.
* If this function fails, it will return 0.
*/
size_t getSystemMemory();
/*!
* Function to get the memory usage.
* This function will return the total memory used by the application.
* Note: depending on the implimentation, this number may be rounded to
* to a multiple of the page size.
* If this function fails, it will return 0.
*/
size_t getMemoryUsage();
//! Function to get the current call stack
std::vector<std::string> getCallStack();
//! Function to get an arbitrary point in time
double time();
//! Function to get the resolution of time
double tick();
} // namespace Utilities
#include "common/UtilityMacros.h"
#endif

124
common/UtilityMacros.h Normal file
View File

@ -0,0 +1,124 @@
// This file contains useful macros including ERROR, WARNING, INSIST, ASSERT, etc.
#ifndef included_UtilityMacros
#define included_UtilityMacros
#include "common/Utilities.h"
#include <iostream>
#include <sstream>
#include <stdexcept>
/*! \defgroup Macros Set of utility macro functions
* \details These functions are a list of C++ macros that are used
* for common operations, including checking for errors.
* \addtogroup Macros
* @{
*/
/*! \def NULL_STATEMENT
* \brief A null statement
* \details A statement that does nothing, for insure++ make it something
* more complex than a simple C null statement to avoid a warning.
*/
#ifdef __INSURE__
#define NULL_STATEMENT do{if(0) int nullstatement=0 }}while(0)
#else
#define NULL_STATEMENT
#endif
/*! \def NULL_USE(variable)
* \brief A null use of a variable
* \details A null use of a variable, use to avoid GNU compiler warnings about unused variables.
* \param variable Variable to pretend to use
*/
#define NULL_USE(variable) do { \
if(0) {char *temp = (char *)&variable; temp++;} \
}while(0)
/*! \def ERROR(MSG)
* \brief Throw error
* \details Throw an error exception from within any C++ source code. The
* macro argument may be any standard ostream expression. The file and
* line number of the abort are also printed.
* \param MSG Error message to print
*/
#define ERROR(MSG) do { \
Utilities::abort(MSG,__FILE__,__LINE__); \
}while(0)
/*! \def WARNING(MSG)
* \brief Print a warning
* \details Print a warning without exit. Print file and line number of the warning.
* \param MSG Warning message to print
*/
#define WARNING(MSG) do { \
std::stringstream tboxos; \
tboxos << MSG << std::ends; \
printf("WARNING: %s\n Warning called in %s on line %i\n",tboxos.str().c_str(),__FILE__,__LINE__); \
}while(0)
/*! \def ASSERT(EXP)
* \brief Assert error
* \details Throw an error exception from within any C++ source code if the
* given expression is not true. This is a parallel-friendly version
* of assert.
* The file and line number of the abort are printed along with the stack trace (if availible).
* \param EXP Expression to evaluate
*/
#define ASSERT(EXP) do { \
if ( !(EXP) ) { \
std::stringstream tboxos; \
tboxos << "Failed assertion: " << #EXP << std::ends; \
Utilities::abort(tboxos.str(), __FILE__, __LINE__); \
} \
}while(0)
/*! \def INSIST(EXP,MSG)
* \brief Insist error
* \details Throw an error exception from within any C++ source code if the
* given expression is not true. This will also print the given message.
* This is a parallel-friendly version of assert.
* The file and line number of the abort are printed along with the stack trace (if availible).
* \param EXP Expression to evaluate
* \param MSG Debug message to print
*/
#define INSIST(EXP,MSG) do { \
if ( !(EXP) ) { \
std::stringstream tboxos; \
tboxos << "Failed insist: " << #EXP << std::endl; \
tboxos << "Message: " << MSG << std::ends; \
Utilities::abort(tboxos.str(), __FILE__, __LINE__); \
} \
}while(0)
/**
* Macro for use when assertions are to be included
* only when debugging.
*/
/*! \def CHECK_ASSERT(EXP)
* \brief Assert error (debug only)
* \details Throw an error exception from within any C++ source code if the
* given expression is not true. This only runs if DEBUG_CHECK_ASSERTIONS
* is enabled. If enabled, this is the same as a call to ASSERT.
* \param EXP Expression to evaluate
*/
#ifdef DEBUG_CHECK_ASSERTIONS
#define CHECK_ASSERT(EXP) ASSERT(EXP)
#else
#define CHECK_ASSERT(EXP)
#endif
/*! @} */
#endif

View File

@ -7,6 +7,7 @@ ADD_LBPM_TEST( pmmc_cylinder )
ADD_LBPM_TEST( TestCylinderAreas )
ADD_LBPM_TEST( TestSphereCurvature )
ADD_LBPM_TEST_1_2_4( testCommunication )
ADD_LBPM_TEST_1_2_4( testUtilities )
# Sample test that will run with 1, 2, and 4 processors, failing with 4 or more procs
ADD_LBPM_TEST_1_2_4( hello_world )

View File

@ -12,7 +12,8 @@
#include "D3Q19.h"
#include "D3Q7.h"
#include "Color.h"
#include "Communication.h"
#include "common/Communication.h"
#include "common/Utilities.h"
using namespace std;
@ -99,6 +100,8 @@ int main(int argc, char **argv)
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&nprocs);
// Initialize error handlers
Utilities::setErrorHandlers();
// parallel domain size (# of sub-domains)
int nprocx,nprocy,nprocz;
int iproc,jproc,kproc;

119
tests/testUtilities.cpp Normal file
View File

@ -0,0 +1,119 @@
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <time.h>
#include <sys/stat.h>
#include <math.h>
#include <stdexcept>
#include <vector>
#include <string>
#include "common/Utilities.h"
#include "common/UnitTest.h"
#ifdef USE_MPI
#include "mpi.h"
#endif
// Function to return the call stack
std::vector<std::string> get_call_stack()
{
std::vector<std::string> stack = Utilities::getCallStack();
// Trick compiler to skip inline for this function with fake recursion
if ( stack.size() > 10000 ) { stack = get_call_stack(); }
return stack;
}
// The main function
int main(int argc, char *argv[])
{
int rank = 0;
#ifdef USE_MPI
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
#endif
UnitTest ut;
Utilities::setAbortBehavior( true, true, true );
// Test the memory usage
double t0 = Utilities::time();
size_t n_bytes1 = Utilities::getMemoryUsage();
double time1 = Utilities::time() - t0;
double *tmp = new double[0x100000];
NULL_USE(tmp);
t0 = Utilities::time();
size_t n_bytes2 = Utilities::getMemoryUsage();
double time2 = Utilities::time() - t0;
delete [] tmp;
t0 = Utilities::time();
size_t n_bytes3 = Utilities::getMemoryUsage();
double time3 = Utilities::time() - t0;
std::cout << "Number of bytes used for a basic test: " << n_bytes1 << ", " << n_bytes2 << ", " << n_bytes3 << std::endl;
std::cout << " Time to query: " << time1*1e6 << " us, " << time2*1e6 << " us, " << time3*1e6 << " us" << std::endl;
if ( n_bytes1==0 ) {
ut.failure("getMemoryUsage returns 0");
} else {
ut.passes("getMemoryUsage returns non-zero");
if ( n_bytes2>n_bytes1 )
ut.passes("getMemoryUsage increases size");
else
ut.failure("getMemoryUsage increases size");
if ( n_bytes1==n_bytes3 )
ut.passes("getMemoryUsage decreases size properly");
else
ut.expected_failure("getMemoryUsage does not decrease size properly");
}
// Test getting the current call stack
std::vector<std::string> call_stack = get_call_stack();
if ( rank==0 ) {
std::cout << "Call stack:" << std::endl;
for (size_t i=0; i<call_stack.size(); i++)
std::cout << " " << call_stack[i] << std::endl;
}
if ( !call_stack.empty() ) {
ut.passes("non empty call stack");
if ( call_stack[0].find("get_call_stack()") != std::string::npos )
ut.passes("call stack decoded function symbols");
else
ut.expected_failure("call stack was unable to decode function symbols");
} else {
ut.failure("non empty call stack");
}
// Test catching an error
try {
ERROR("Test");
ut.failure("Failed to catch RAY_ERROR");
} catch (...) {
ut.passes("Caught RAY_ERROR");
}
try {
throw std::logic_error("test");
ut.failure("Failed to catch exception");
} catch (...) {
ut.passes("Caught exception");
}
// Test time/tick
double time = Utilities::time();
double res = Utilities::tick();
if ( time==0 || res==0 )
ut.failure("time/tick");
else
ut.passes("time/tick");
// Finished
ut.report();
size_t N_errors = ut.NumFailGlobal();
if ( N_errors==0 )
printf("All tests passed\n");
#ifdef USE_MPI
MPI_Finalize();
#endif
return (int) N_errors;
}