/* * Copyright (c) 2003-2007, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "utils.h" /********************************************************************** * * Assertions */ #if defined(ASSERTS_ON) namespace ledger { DECLARE_EXCEPTION(fatal, assertion_failed); void debug_assert(const string& reason, const string& func, const string& file, unsigned long line) { std::ostringstream buf; buf << "Assertion failed in \"" << file << "\", line " << line << ": " << func << ": " << reason; throw assertion_failed(buf.str()); } } // namespace ledger #endif /********************************************************************** * * Verification (basically, very slow asserts) */ #if defined(VERIFY_ON) namespace ledger { bool verify_enabled = false; typedef std::pair allocation_pair; typedef std::map live_memory_map; typedef std::multimap live_objects_map; typedef std::pair count_size_pair; typedef std::map object_count_map; static live_memory_map * live_memory = NULL; static object_count_map * live_memory_count = NULL; static object_count_map * total_memory_count = NULL; static bool memory_tracing_active = false; static live_objects_map * live_objects = NULL; static object_count_map * live_object_count = NULL; static object_count_map * total_object_count = NULL; static object_count_map * total_ctor_count = NULL; void initialize_memory_tracing() { memory_tracing_active = false; live_memory = new live_memory_map; live_memory_count = new object_count_map; total_memory_count = new object_count_map; live_objects = new live_objects_map; live_object_count = new object_count_map; total_object_count = new object_count_map; total_ctor_count = new object_count_map; memory_tracing_active = true; } void shutdown_memory_tracing() { memory_tracing_active = false; if (live_objects) { IF_DEBUG("memory.counts") report_memory(std::cerr, true); else IF_DEBUG("memory.counts.live") report_memory(std::cerr); else if (live_objects->size() > 0) report_memory(std::cerr); } checked_delete(live_memory); live_memory = NULL; checked_delete(live_memory_count); live_memory_count = NULL; checked_delete(total_memory_count); total_memory_count = NULL; checked_delete(live_objects); live_objects = NULL; checked_delete(live_object_count); live_object_count = NULL; checked_delete(total_object_count); total_object_count = NULL; checked_delete(total_ctor_count); total_ctor_count = NULL; } inline void add_to_count_map(object_count_map& the_map, const char * name, std::size_t size) { object_count_map::iterator k = the_map.find(name); if (k != the_map.end()) { (*k).second.first++; (*k).second.second += size; } else { std::pair result = the_map.insert(object_count_map::value_type(name, count_size_pair(1, size))); VERIFY(result.second); } } std::size_t current_memory_size() { std::size_t memory_size = 0; for (object_count_map::const_iterator i = live_memory_count->begin(); i != live_memory_count->end(); i++) memory_size += (*i).second.second; return memory_size; } static void trace_new_func(void * ptr, const char * which, std::size_t size) { memory_tracing_active = false; if (! live_memory) return; live_memory->insert (live_memory_map::value_type(ptr, allocation_pair(which, size))); add_to_count_map(*live_memory_count, which, size); add_to_count_map(*total_memory_count, which, size); add_to_count_map(*total_memory_count, "__ALL__", size); memory_tracing_active = true; } static void trace_delete_func(void * ptr, const char * which) { memory_tracing_active = false; if (! live_memory) return; // Ignore deletions of memory not tracked, since it's possible that // a user (like boost) allocated a block of memory before memory // tracking began, and then deleted it before memory tracking ended. // If it really is a double-delete, the malloc library on OS/X will // notify me. live_memory_map::iterator i = live_memory->find(ptr); if (i == live_memory->end()) return; std::size_t size = (*i).second.second; VERIFY((*i).second.first == which); live_memory->erase(i); object_count_map::iterator j = live_memory_count->find(which); VERIFY(j != live_memory_count->end()); (*j).second.second -= size; if (--(*j).second.first == 0) live_memory_count->erase(j); memory_tracing_active = true; } } // namespace ledger void * operator new(std::size_t size) throw (std::bad_alloc) { void * ptr = std::malloc(size); if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_new_func(ptr, "new", size); return ptr; } void * operator new(std::size_t size, const std::nothrow_t&) throw() { void * ptr = std::malloc(size); if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_new_func(ptr, "new", size); return ptr; } void * operator new[](std::size_t size) throw (std::bad_alloc) { void * ptr = std::malloc(size); if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_new_func(ptr, "new[]", size); return ptr; } void * operator new[](std::size_t size, const std::nothrow_t&) throw() { void * ptr = std::malloc(size); if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_new_func(ptr, "new[]", size); return ptr; } void operator delete(void * ptr) throw() { if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_delete_func(ptr, "new"); std::free(ptr); } void operator delete(void * ptr, const std::nothrow_t&) throw() { if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_delete_func(ptr, "new"); std::free(ptr); } void operator delete[](void * ptr) throw() { if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_delete_func(ptr, "new[]"); std::free(ptr); } void operator delete[](void * ptr, const std::nothrow_t&) throw() { if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_delete_func(ptr, "new[]"); std::free(ptr); } namespace ledger { inline void report_count_map(std::ostream& out, object_count_map& the_map) { for (object_count_map::iterator i = the_map.begin(); i != the_map.end(); i++) out << " " << std::right << std::setw(12) << (*i).second.first << " " << std::right << std::setw(12) << (*i).second.second << " " << std::left << (*i).first << std::endl; } std::size_t current_objects_size() { std::size_t objects_size = 0; for (object_count_map::const_iterator i = live_object_count->begin(); i != live_object_count->end(); i++) objects_size += (*i).second.second; return objects_size; } void trace_ctor_func(void * ptr, const char * cls_name, const char * args, std::size_t cls_size) { memory_tracing_active = false; if (! live_objects) return; static char name[1024]; std::strcpy(name, cls_name); std::strcat(name, "("); std::strcat(name, args); std::strcat(name, ")"); DEBUG("memory.debug", "TRACE_CTOR " << ptr << " " << name); live_objects->insert (live_objects_map::value_type(ptr, allocation_pair(cls_name, cls_size))); add_to_count_map(*live_object_count, cls_name, cls_size); add_to_count_map(*total_object_count, cls_name, cls_size); add_to_count_map(*total_object_count, "__ALL__", cls_size); add_to_count_map(*total_ctor_count, name, cls_size); memory_tracing_active = true; } void trace_dtor_func(void * ptr, const char * cls_name, std::size_t cls_size) { memory_tracing_active = false; if (! live_objects) return; DEBUG("memory.debug", "TRACE_DTOR " << ptr << " " << cls_name); live_objects_map::iterator i = live_objects->find(ptr); VERIFY(i != live_objects->end()); int ptr_count = live_objects->count(ptr); for (int x = 0; x < ptr_count; x++, i++) { if ((*i).second.first == cls_name) { live_objects->erase(i); break; } } object_count_map::iterator k = live_object_count->find(cls_name); VERIFY(k != live_object_count->end()); (*k).second.second -= cls_size; if (--(*k).second.first == 0) live_object_count->erase(k); memory_tracing_active = true; } void report_memory(std::ostream& out, bool report_all) { if (! live_memory) return; if (live_memory_count->size() > 0) { out << "NOTE: There may be memory held by Boost " << "and libstdc++ after ledger::shutdown()" << std::endl; out << "Live memory count:" << std::endl; report_count_map(out, *live_memory_count); } if (live_memory->size() > 0) { out << "Live memory:" << std::endl; for (live_memory_map::const_iterator i = live_memory->begin(); i != live_memory->end(); i++) out << " " << std::right << std::setw(7) << (*i).first << " " << std::right << std::setw(7) << (*i).second.second << " " << std::left << (*i).second.first << std::endl; } if (report_all && total_memory_count->size() > 0) { out << "Total memory counts:" << std::endl; report_count_map(out, *total_memory_count); } if (live_object_count->size() > 0) { out << "Live object count:" << std::endl; report_count_map(out, *live_object_count); } if (live_objects->size() > 0) { out << "Live objects:" << std::endl; for (live_objects_map::const_iterator i = live_objects->begin(); i != live_objects->end(); i++) out << " " << std::right << std::setw(7) << (*i).first << " " << std::right << std::setw(7) << (*i).second.second << " " << std::left << (*i).second.first << std::endl; } if (report_all) { if (total_object_count->size() > 0) { out << "Total object counts:" << std::endl; report_count_map(out, *total_object_count); } if (total_ctor_count->size() > 0) { out << "Total constructor counts:" << std::endl; report_count_map(out, *total_ctor_count); } } } string::string() : std::string() { TRACE_CTOR(string, ""); } string::string(const string& str) : std::string(str) { TRACE_CTOR(string, "const string&"); } string::string(const std::string& str) : std::string(str) { TRACE_CTOR(string, "const std::string&"); } string::string(const int len, char x) : std::string(len, x) { TRACE_CTOR(string, "const int, char"); } string::string(const char * str) : std::string(str) { TRACE_CTOR(string, "const char *"); } string::string(const char * str, const char * end) : std::string(str, end) { TRACE_CTOR(string, "const char *, const char *"); } string::string(const string& str, int x) : std::string(str, x) { TRACE_CTOR(string, "const string&, int"); } string::string(const string& str, int x, int y) : std::string(str, x, y) { TRACE_CTOR(string, "const string&, int, int"); } string::string(const char * str, int x) : std::string(str, x) { TRACE_CTOR(string, "const char *, int"); } string::string(const char * str, int x, int y) : std::string(str, x, y) { TRACE_CTOR(string, "const char *, int, int"); } string::~string() { TRACE_DTOR(string); } } // namespace ledger #endif // VERIFY_ON /********************************************************************** * * Logging */ #if defined(LOGGING_ON) namespace ledger { log_level_t _log_level = LOG_WARN; std::ostream * _log_stream = &std::cerr; std::ostringstream _log_buffer; #if defined(TRACING_ON) unsigned int _trace_level; #endif #ifdef BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK #define CURRENT_TIME() boost::posix_time::microsec_clock::universal_time() #else #define CURRENT_TIME() boost::posix_time::second_clock::universal_time() #endif static inline void stream_memory_size(std::ostream& out, std::size_t size) { if (size < 1024) out << size << 'b'; else if (size < (1024 * 1024)) out << (double(size) / 1024.0) << 'K'; else if (size < (1024 * 1024 * 1024)) out << (double(size) / (1024.0 * 1024.0)) << 'M'; else out << (double(size) / (1024.0 * 1024.0 * 1024.0)) << 'G'; } static bool logger_has_run = false; static ptime logger_start; bool logger_func(log_level_t level) { unsigned long appender = 0; if (! logger_has_run) { logger_has_run = true; logger_start = CURRENT_TIME(); IF_VERIFY() *_log_stream << " TIME OBJSZ MEMSZ" << std::endl; appender = (logger_start - current_moment).total_milliseconds(); } *_log_stream << std::right << std::setw(5) << (CURRENT_TIME() - logger_start).total_milliseconds(); IF_VERIFY() { *_log_stream << std::right << std::setw(6) << std::setprecision(3); stream_memory_size(*_log_stream, current_objects_size()); *_log_stream << std::right << std::setw(6) << std::setprecision(3); stream_memory_size(*_log_stream, current_memory_size()); } *_log_stream << " " << std::left << std::setw(7); switch (level) { case LOG_CRIT: *_log_stream << "[CRIT]"; break; case LOG_FATAL: *_log_stream << "[FATAL]"; break; case LOG_ASSERT: *_log_stream << "[ASSRT]"; break; case LOG_ERROR: *_log_stream << "[ERROR]"; break; case LOG_VERIFY: *_log_stream << "[VERFY]"; break; case LOG_WARN: *_log_stream << "[WARN]"; break; case LOG_INFO: *_log_stream << "[INFO]"; break; case LOG_EXCEPT: *_log_stream << "[EXCPT]"; break; case LOG_DEBUG: *_log_stream << "[DEBUG]"; break; case LOG_TRACE: *_log_stream << "[TRACE]"; break; case LOG_OFF: case LOG_ALL: assert(false); break; } *_log_stream << ' ' << _log_buffer.str(); if (appender) *_log_stream << " (" << appender << "ms startup)"; *_log_stream << std::endl; _log_buffer.str(""); return true; } } // namespace ledger #if defined(DEBUG_ON) namespace ledger { optional _log_category; } // namespace ledger #endif // DEBUG_ON #endif // LOGGING_ON /********************************************************************** * * Timers (allows log entries to specify cumulative time spent) */ #if defined(LOGGING_ON) && defined(TIMERS_ON) namespace ledger { struct timer_t { log_level_t level; ptime begin; time_duration spent; std::string description; bool active; timer_t(log_level_t _level, std::string _description) : level(_level), begin(CURRENT_TIME()), spent(time_duration(0, 0, 0, 0)), description(_description), active(true) {} }; typedef std::map timer_map; static timer_map timers; void start_timer(const char * name, log_level_t lvl) { #if defined(VERIFY_ON) memory_tracing_active = false; #endif timer_map::iterator i = timers.find(name); if (i == timers.end()) { timers.insert(timer_map::value_type(name, timer_t(lvl, _log_buffer.str()))); } else { assert((*i).second.description == _log_buffer.str()); (*i).second.begin = CURRENT_TIME(); (*i).second.active = true; } _log_buffer.str(""); #if defined(VERIFY_ON) memory_tracing_active = true; #endif } void stop_timer(const char * name) { #if defined(VERIFY_ON) memory_tracing_active = false; #endif timer_map::iterator i = timers.find(name); assert(i != timers.end()); (*i).second.spent += CURRENT_TIME() - (*i).second.begin; (*i).second.active = false; #if defined(VERIFY_ON) memory_tracing_active = true; #endif } void finish_timer(const char * name) { #if defined(VERIFY_ON) memory_tracing_active = false; #endif timer_map::iterator i = timers.find(name); if (i == timers.end()) return; time_duration spent = (*i).second.spent; if ((*i).second.active) { spent = CURRENT_TIME() - (*i).second.begin; (*i).second.active = false; } _log_buffer << (*i).second.description << ' '; bool need_paren = (*i).second.description[(*i).second.description.size() - 1] != ':'; if (need_paren) _log_buffer << '('; _log_buffer << spent.total_milliseconds() << "ms"; if (need_paren) _log_buffer << ')'; logger_func((*i).second.level); timers.erase(i); #if defined(VERIFY_ON) memory_tracing_active = true; #endif } } // namespace ledger #endif // LOGGING_ON && TIMERS_ON /********************************************************************** * * Exception handling */ namespace ledger { std::ostringstream _exc_buffer; /*ptr_list context_stack;*/ } // namespace ledger /********************************************************************** * * General utility functions */ namespace ledger { path expand_path(const path& pathname) { if (pathname.empty()) return pathname; #if 1 return pathname; #else // jww (2007-04-30): I need to port this code to use // boost::filesystem::path const char * pfx = NULL; string::size_type pos = pathname.find_first_of('/'); if (pathname.length() == 1 || pos == 1) { pfx = std::getenv("HOME"); #ifdef HAVE_GETPWUID if (! pfx) { // Punt. We're trying to expand ~/, but HOME isn't set struct passwd * pw = getpwuid(getuid()); if (pw) pfx = pw->pw_dir; } #endif } #ifdef HAVE_GETPWNAM else { string user(pathname, 1, pos == string::npos ? string::npos : pos - 1); struct passwd * pw = getpwnam(user.c_str()); if (pw) pfx = pw->pw_dir; } #endif // if we failed to find an expansion, return the path unchanged. if (! pfx) return pathname; string result(pfx); if (pos == string::npos) return result; if (result.length() == 0 || result[result.length() - 1] != '/') result += '/'; result += pathname.substr(pos + 1); return result; #endif } path resolve_path(const path& pathname) { path temp = pathname; if (temp.string()[0] == '~') temp = expand_path(temp); temp.normalize(); return temp; } } // namespace ledger