/* The software in this package is distributed under the GNU General Public License version 2 (with a special exception described below). A copy of GNU General Public License (GPL) is included in this distribution, in the file COPYING.GPL. As a special exception, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other works to produce a work based on this file, this file does not by itself cause the resulting work to be covered by the GNU General Public License. However the source code for this file must still be made available in accordance with section (3) of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. */ /** @file * @brief implementation of tracing functionality. * * @copyright © Copyright 2008,2011 by Intra2net AG * * Every thread gets its' own ScopeTracker stack via TLS (thread local storage) * */ #include "tracefunc.hpp" #include #include #include #include #include namespace I2n { namespace Tracer { using Logger::GlobalLogger; class PerThreadContainer { public: typedef std::vector< ScopeTracker* > ScopeTrackerList; /// List of currently active scope trackers ScopeTrackerList scope_tracker_list; /// Precomputed indent levels std::vector< std::string > indents; }; namespace { /// Internal TLS (thread local storage) data container boost::thread_specific_ptr thread_container; /** * Helper function to retrieve TLS pointer. * Initializes the pointer if called for the first time. * @return Pointer to the per thread container or NULL if error occurs */ static PerThreadContainer *TLS_get_container() throw () { // First call? Construct options container if (thread_container.get() == NULL) { try { thread_container.reset(new PerThreadContainer()); } catch (...) { return NULL; } } PerThreadContainer *per_thread = thread_container.get(); return per_thread; } /** * @brief ensures indent level string to exist up to the desired level. * @param level the desired indent level. */ static void ensure_indent_level(PerThreadContainer *per_thread, unsigned int level) throw(std::length_error, std::bad_alloc) { // First initialization? if (per_thread->indents.empty()) { per_thread->indents.reserve(10); if (level < 10) level = 10; per_thread->indents.push_back(""); } while (per_thread->indents.size() <= level) { per_thread->indents.push_back( per_thread->indents.back() + " " ); } } // eo ensure_indent_level(int) /** * @brief try logging that some problem occurred * * Keep in mind that problem could be with the logger itself, so wrap all this * in a try-catch agin */ static void try_logging_error(const std::string &message) throw () { try { GlobalLogger.debug() << "Problem occurred in ScopeTracker: " << message; } catch (...) { // nothing more we can do } } } // eo namespace /* ** implementation of ScopeTracker */ /** * @brief constructor. initializes object with a source location and emits a ENTER message. * @param loc the source location. * * the message is indented according to the current depth of nested ScopeTracker instances. * * All exceptions happening in here (from vector::reserve, vector::push_back, acquiring lock for logger) * are caught. Will try to log only that problem occurred */ ScopeTracker::ScopeTracker( const SourceLocation& loc) throw () : Location(loc) , Depth(0) , FuncDepth(0) , PerThread(TLS_get_container()) { if (PerThread == NULL) { try_logging_error("Failed to get thread local storage"); return; } try { if (!PerThread->scope_tracker_list.empty()) { ScopeTracker* last_tracker= PerThread->scope_tracker_list.back(); if (Location) { if (last_tracker->Location && last_tracker->Location.File == Location.File && last_tracker->Location.FunctionName == Location.FunctionName) { FuncDepth= last_tracker->FuncDepth+1; } } Depth= last_tracker->Depth + 1; } ensure_indent_level(PerThread, Depth); PerThread->scope_tracker_list.push_back(this); // spit a message if (Logger::has_log_level(Logger::LogLevel::Debug)) { GlobalLogger.debug() << PerThread->indents[Depth] << "ENTER " << get_tag(); } } catch (...) { try_logging_error("Caught exception in constructor"); } } // eo ScopeTracker::ScopeTracker(const SourceLocation&) /** * @brief create Tag if empty; return reference to it * * Moved this from constructor into own function to avoid creating Tag if it is * not required (i.e. log level is not DEBUG) but still create it only once and * ensure it is available if log level changes inside tracked function */ std::string& ScopeTracker::get_tag() { if (Tag.empty()) { if (Location.FunctionName.empty()) { Tag = " (global scope?)"; } else { std::ostringstream ostr; ostr << shorten_stl_types(Location.FunctionName); if (FuncDepth>0) { ostr << "#" << FuncDepth+1; } Tag= ostr.str(); } } return Tag; } /** * @brief destructor. emits a LEAVE message. * * All exceptions are caught, will try to log only that some problem occurred */ ScopeTracker::~ScopeTracker() throw () { if (PerThread == NULL) { try_logging_error("Failed to get thread local storage"); return; } try { // spit a message if (Logger::has_log_level(Logger::LogLevel::Debug)) { GlobalLogger.debug() << PerThread->indents[Depth] << "LEAVE " << get_tag(); } if (PerThread->scope_tracker_list.empty()) { return; } if (PerThread->scope_tracker_list.back() == this) { PerThread->scope_tracker_list.pop_back(); } else { // oh hell, this should never be the case! //TODO GlobalLogger.error() << "inconsistency detected in scope tracker"; } } catch (...) { try_logging_error("Caught exception in destructor"); } } // eo ScopeTracker::~ScopeTracker() } // eo namespace Tracer } // eo namespace I2n