diff options
author | John Wiegley <johnw@newartisans.com> | 2008-08-05 13:17:04 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-08-05 18:05:49 -0400 |
commit | f6f4a46cf5b14f9a2170cd6475958efbf320caec (patch) | |
tree | 05bc1defcdebc201de3dd10477483d906a842821 /src/utils.cc | |
parent | b7970b29855563e4c67f85af8b31233eda80c22a (diff) | |
download | fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.tar.gz fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.tar.bz2 fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.zip |
Moved around most of the files so that source code is in src/, documentation
is in doc/, etc.
Diffstat (limited to 'src/utils.cc')
-rw-r--r-- | src/utils.cc | 716 |
1 files changed, 716 insertions, 0 deletions
diff --git a/src/utils.cc b/src/utils.cc new file mode 100644 index 00000000..5ac5cef0 --- /dev/null +++ b/src/utils.cc @@ -0,0 +1,716 @@ +/* + * Copyright (c) 2003-2008, 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(assertion_failed, std::logic_error); + +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<std::string, std::size_t> allocation_pair; +typedef std::map<void *, allocation_pair> live_memory_map; +typedef std::multimap<void *, allocation_pair> live_objects_map; + +typedef std::pair<unsigned int, std::size_t> count_size_pair; +typedef std::map<std::string, count_size_pair> 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<object_count_map::iterator, bool> 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; + + foreach (const object_count_map::value_type& pair, *live_memory_count) + memory_size += pair.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) +{ + foreach (object_count_map::value_type& pair, the_map) + out << " " << std::right << std::setw(12) << pair.second.first + << " " << std::right << std::setw(7) << pair.second.second + << " " << std::left << pair.first + << std::endl; +} + +std::size_t current_objects_size() +{ + std::size_t objects_size = 0; + + foreach (const object_count_map::value_type& pair, *live_object_count) + objects_size += pair.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); + if (i == live_objects->end()) { + std::cerr << "Attempting to delete " << ptr << " a non-living " << cls_name + << std::endl; + assert(false); + } + + 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; + + foreach (const live_memory_map::value_type& pair, *live_memory) + out << " " << std::right << std::setw(12) << pair.first + << " " << std::right << std::setw(7) << pair.second.second + << " " << std::left << pair.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; + + foreach (const live_objects_map::value_type& pair, *live_objects) + out << " " << std::right << std::setw(12) << pair.first + << " " << std::right << std::setw(7) << pair.second.second + << " " << std::left << pair.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, "copy"); +} +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() throw() { + TRACE_DTOR(string); +} + +} // namespace ledger + +#endif // VERIFY_ON + +ledger::string empty_string(""); + +/********************************************************************** + * + * 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_time).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<std::string> _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<std::string, timer_t> 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 _desc_buffer; +std::ostringstream _ctxt_buffer; + +} // namespace ledger + +/********************************************************************** + * + * General utility functions + */ + +namespace ledger { + +path expand_path(const path& pathname) +{ + if (pathname.empty()) + return pathname; + +#if 0 + // 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; +#else + return pathname; +#endif +} + +path resolve_path(const path& pathname) +{ + path temp = pathname; + if (temp.string()[0] == '~') + temp = expand_path(temp); + temp.normalize(); + return temp; +} + +} // namespace ledger |