#include "debug.h" #ifdef _POSIX_THREADS #include #endif #include #include #include #include #include #include #include #include #include #include #ifndef NDEBUG namespace { #ifdef _POSIX_THREADS pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER; /* these next few globals and associagted code in debug_t's ctr and dtr make all debug output single-threaded. but seemingly leads to deadlock, probably because debug_t's get destructed too late - at end of block they are created in rather than after their last use?. */ /*pthread_mutex_t global_debugtext_lock = PTHREAD_MUTEX_INITIALIZER; pthread_t global_debugtext_lock_owner; int global_debugtext_lock_depth = 0;*/ struct ScopedLock { ScopedLock() { if ( int e = pthread_mutex_lock( &global_lock)) { std::cerr << debug_PLACE << ": pthread-check.cpp ScopedLock::ScopedLock: pthread_mutex_lock failed, e=" << e << "\n"; abort(); } } ~ScopedLock() { if ( int e = pthread_mutex_unlock( &global_lock)) { std::cerr << debug_PLACE << ": pthread-check.cpp ~ScopedLock(): pthread_mutex_unlock failed, e=" << e << "\n"; abort(); } } }; pthread_t debug_self() { return pthread_self(); } #else const char* debug_self() { return ""; } struct ScopedLock { // empty ctr/dtr bodies stops gcc warning about unused instances. ScopedLock() {} ~ScopedLock() {} }; #endif } struct debug_trace_frame { const char* name; const char* file; int line; const char* name_latest; const char* file_latest; int line_latest; debug_trace_frame( const char* name0, const char* file0, int line0) : name( name0), file( file0), line( line0), name_latest( NULL), file_latest( NULL), line_latest( 0) {} }; std::ostream& operator << ( std::ostream& out, const debug_trace_frame& frame) { out << frame.file << ":" << frame.line << ":" << frame.name; if ( frame.name_latest) { out << " + " << frame.file_latest << ":" << frame.line_latest << ":" << frame.name_latest; } return out; } struct threaddata { threaddata() : prefix() {} std::string prefix; std::vector< debug_trace_frame> backtrace; }; #ifdef _POSIX_THREADS namespace { typedef std::map< pthread_t, threaddata> mapping_t; mapping_t mapping; pthread_mutex_t mutex( PTHREAD_MUTEX_INITIALIZER); } threaddata& get_threaddata0() // expects mutex to be held by current thread. { threaddata& td = mapping[ pthread_self()]; if ( td.prefix=="") { char buffer[32]; sprintf( buffer, " %i", (int) pthread_self()); td.prefix = buffer; for ( unsigned int i=0; i0); ++global_debugtext_lock_depth; } else { std::cerr << pthread_self() << "claiming global_debugtext_lock\n"; if ( pthread_mutex_lock( &global_debugtext_lock)) abort(); std::cerr << pthread_self() << "claimed global_debugtext_lock\n"; assert( global_debugtext_lock_owner==0); assert( global_debugtext_lock_depth==0); global_debugtext_lock_owner = pthread_self(); global_debugtext_lock_depth = 1; } #endif*/ debug_prefix( this->stream); } debug_t::~debug_t() { /*#ifdef _POSIX_THREADS ScopedLock l; assert( global_debugtext_lock_owner==pthread_self()); assert( global_debugtext_lock_depth>0); --global_debugtext_lock_depth; if ( global_debugtext_lock_depth==0) { if ( pthread_mutex_unlock( &global_debugtext_lock)) abort(); global_debugtext_lock_owner = 0; } #endif*/ } namespace { int debug_level = 1; } int debug_level_set( int newlevel) { int ret = debug_level; debug_level = newlevel; debug << "debug_level_set: have set level to " << debug_level << "\n"; return ret; } debug_trace_t::debug_trace_t( const char* name, const char* file, int line) { //debug << "debug_trace_t::debug_trace_t: name=" << name << "\n"; //debug_output_backtraces(); get_threaddata0().backtrace.push_back( debug_trace_frame( name, file, line)); ScopedLock l; if ( debug_level>=9) debug << debug_self() << " > " << get_threaddata0().backtrace.back() << "\n"; } debug_trace_t::~debug_trace_t() { ScopedLock lock; if ( debug_level>=9) debug << debug_self() << " < " << get_threaddata0().backtrace.back() << "\n"; if ( get_threaddata0().backtrace.empty()) { std::cerr << "~debug_trace_t(): corrupted empty backtrace\n"; return; } get_threaddata0().backtrace.pop_back(); } void debug_trace_line( const char* fn, const char* file, int line) { assert( !get_threaddata0().backtrace.empty()); get_threaddata0().backtrace.back().name_latest = fn; get_threaddata0().backtrace.back().file_latest = file; get_threaddata0().backtrace.back().line_latest = line; } void debug_output_backtraces( std::ostream& out) { ScopedLock lock; #ifdef _POSIX_THREADS out << "debug_output_backtraces(): pthread_self=" << pthread_self() << ", running threads are:\n"; for ( mapping_t::iterator it=mapping.begin(); it!=mapping.end(); ++it) { if ( it->second.backtrace.size()==0) { continue; // thread has finished. } out << "thread " << it->first << ":\n"; for ( unsigned int i=0; isecond.backtrace.size(); ++i) { out << " " << it->second.backtrace[i] << "\n"; } } out << "thread/lock info is:\n"; // don't call PthreadCheck_output_status here - can caudse deadlock if we are called from pthreadcheck.c.: //PthreadCheck_output_status( out); out << "\n"; //out << "debug_output_backtraces() returning\n\n"; #else out << "debug_output_backtraces(): running threads are:\n"; threaddata& td = get_threaddata(); for ( unsigned int i=0; i0) out << ", "; out << " " << td.backtrace[i] << "\n"; } #endif } void debug_output_backtraces() { debug_output_backtraces( std::cerr); } #ifdef _POSIX_THREADS pthread_mutex_t& debug_count_mutex() { static pthread_mutex_t ret( PTHREAD_MUTEX_INITIALIZER); return ret; } void debug_count_increment( int& count, const std::type_info& /*type*/) { ScopedLock lock; ++count; //debug << "count for " << type.name() << " = " << count << "\n"; } void debug_count_decrement( int& count, const std::type_info& /*type*/) { ScopedLock lock; --count; //debug << "count for " << type.name() << " = " << count << "\n"; } #endif #endif