diff --git a/include/boost/detail/shared_count.hpp b/include/boost/detail/shared_count.hpp index d3c010f..9be3d5e 100644 --- a/include/boost/detail/shared_count.hpp +++ b/include/boost/detail/shared_count.hpp @@ -30,6 +30,7 @@ #include // for std::exception #include // for std::bad_alloc #include // for std::type_info in get_deleter +#include // for std::size_t #ifdef __BORLANDC__ # pragma warn -8026 // Functions with excep. spec. are not expanded inline @@ -43,10 +44,10 @@ namespace boost #if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) -void sp_scalar_constructor_hook(void * p); -void sp_array_constructor_hook(void * p); -void sp_scalar_destructor_hook(void * p); -void sp_array_destructor_hook(void * p); +void sp_scalar_constructor_hook(void * px, std::size_t size, void * pn); +void sp_array_constructor_hook(void * px); +void sp_scalar_destructor_hook(void * px, std::size_t size, void * pn); +void sp_array_destructor_hook(void * px); #endif @@ -188,31 +189,31 @@ private: #if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) -template void cbi_call_constructor_hook(T * p, checked_deleter const &, int) +template void cbi_call_constructor_hook(counted_base * pn, T * px, checked_deleter const &, int) { - boost::sp_scalar_constructor_hook(p); + boost::sp_scalar_constructor_hook(px, sizeof(T), pn); } -template void cbi_call_constructor_hook(T * p, checked_array_deleter const &, int) +template void cbi_call_constructor_hook(counted_base *, T * px, checked_array_deleter const &, int) { - boost::sp_array_constructor_hook(p); + boost::sp_array_constructor_hook(px); } -template void cbi_call_constructor_hook(P const &, D const &, long) +template void cbi_call_constructor_hook(counted_base *, P const &, D const &, long) { } -template void cbi_call_destructor_hook(T * p, checked_deleter const &, int) +template void cbi_call_destructor_hook(counted_base * pn, T * px, checked_deleter const &, int) { - boost::sp_scalar_destructor_hook(p); + boost::sp_scalar_destructor_hook(px, sizeof(T), pn); } -template void cbi_call_destructor_hook(T * p, checked_array_deleter const &, int) +template void cbi_call_destructor_hook(counted_base *, T * px, checked_array_deleter const &, int) { - boost::sp_array_destructor_hook(p); + boost::sp_array_destructor_hook(px); } -template void cbi_call_destructor_hook(P const &, D const &, long) +template void cbi_call_destructor_hook(counted_base *, P const &, D const &, long) { } @@ -244,14 +245,14 @@ public: counted_base_impl(P p, D d): ptr(p), del(d) { #if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) - detail::cbi_call_constructor_hook(p, d, 0); + detail::cbi_call_constructor_hook(this, p, d, 0); #endif } virtual void dispose() // nothrow { #if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) - detail::cbi_call_destructor_hook(ptr, del, 0); + detail::cbi_call_destructor_hook(this, ptr, del, 0); #endif del(ptr); } @@ -276,6 +277,13 @@ public: #endif }; +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + +int const shared_count_id = 0x2C35F101; +int const weak_count_id = 0x298C38A4; + +#endif + class weak_count; class shared_count @@ -284,15 +292,25 @@ private: counted_base * pi_; +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + int id_; +#endif + friend class weak_count; public: shared_count(): pi_(0) // nothrow +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + , id_(shared_count_id) +#endif { } template shared_count(P p, D d): pi_(0) +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + , id_(shared_count_id) +#endif { #ifndef BOOST_NO_EXCEPTIONS @@ -325,6 +343,9 @@ public: template explicit shared_count(std::auto_ptr & r): pi_(new counted_base_impl< Y *, checked_deleter >(r.get(), checked_deleter())) +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + , id_(shared_count_id) +#endif { r.release(); } @@ -337,6 +358,9 @@ public: } shared_count(shared_count const & r): pi_(r.pi_) // nothrow +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + , id_(shared_count_id) +#endif { if(pi_ != 0) pi_->add_ref(); } @@ -397,20 +421,33 @@ private: counted_base * pi_; +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + int id_; +#endif + friend class shared_count; public: weak_count(): pi_(0) // nothrow +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + , id_(weak_count_id) +#endif { } weak_count(shared_count const & r): pi_(r.pi_) // nothrow +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + , id_(shared_count_id) +#endif { if(pi_ != 0) pi_->weak_add_ref(); } weak_count(weak_count const & r): pi_(r.pi_) // nothrow +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + , id_(shared_count_id) +#endif { if(pi_ != 0) pi_->weak_add_ref(); } @@ -464,6 +501,9 @@ public: }; inline shared_count::shared_count(weak_count const & r): pi_(r.pi_) +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + , id_(shared_count_id) +#endif { if(pi_ != 0) { diff --git a/sp_debug_hooks.cpp b/sp_debug_hooks.cpp index 741ea3a..26d9c51 100644 --- a/sp_debug_hooks.cpp +++ b/sp_debug_hooks.cpp @@ -14,11 +14,17 @@ #if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) #include +#include +#include #include #include +#include +#include int const m = 2; // m * sizeof(int) must be aligned appropriately +// magic values to mark heap blocks with + int const allocated_scalar = 0x1234560C; int const allocated_array = 0x1234560A; int const adopted_scalar = 0x0567890C; @@ -27,6 +33,8 @@ int const deleted = 0x498769DE; using namespace std; // for compilers where things aren't in std +// operator new + static new_handler get_new_handler() { new_handler p = set_new_handler(0); @@ -103,6 +111,139 @@ void * operator new[](size_t n, nothrow_t const &) throw() #endif +// cycle detection + +typedef std::map< void *, std::pair > map_type; + +static map_type & get_map() +{ + static map_type m; + return m; +} + +typedef boost::detail::lightweight_mutex mutex_type; + +static mutex_type & get_mutex() +{ + static mutex_type m; + return m; +} + +static void * init_mutex_before_main = &get_mutex(); + +namespace +{ + class X; + + struct count_layout + { + boost::detail::counted_base * pi; + int id; + }; + + struct shared_ptr_layout + { + X * px; + count_layout pn; + }; +} + +// assume 4 byte alignment for pointers when scanning +size_t const pointer_align = 4; + +static void scan_and_count(void const * area, size_t size, map_type const & m, std::map & m2) +{ + unsigned char const * p = static_cast(area); + + for(size_t n = 0; n < size; p += pointer_align, n += pointer_align) + { + shared_ptr_layout const * q = reinterpret_cast(p); + + if(q->pn.id == boost::detail::shared_count_id && m.count(q->pn.pi) != 0) + { + ++m2[q->pn.pi]; + } + } +} + +static bool scan_and_mark(void const * area, size_t size, map_type const & m, std::map & m2) +{ + bool updated = false; + unsigned char const * p = static_cast(area); + + for(size_t n = 0; n < size; p += pointer_align, n += pointer_align) + { + shared_ptr_layout const * q = reinterpret_cast(p); + + if(q->pn.id == boost::detail::shared_count_id && m.count(q->pn.pi) != 0) + { + // mark as reachable + + if(m2[q->pn.pi] != 0) + { + updated = true; + m2[q->pn.pi] = 0; + } + } + } + + return updated; +} + +void report_unreachable_objects() +{ + std::map m2; + + mutex_type::scoped_lock lock(get_mutex()); + + map_type & m = get_map(); + + // scan objects for shared_ptr members, compute internal counts + + for(map_type::iterator i = m.begin(); i != m.end(); ++i) + { + boost::detail::counted_base const * p = static_cast(i->first); + + BOOST_ASSERT(p->use_count() != 0); // there should be no inactive counts in the map + + scan_and_count(i->second.first, i->second.second, m, m2); + } + + // mark reachable objects + + bool updated; + + do + { + updated = false; + + for(map_type::iterator i = m.begin(); i != m.end(); ++i) + { + boost::detail::counted_base const * p = static_cast(i->first); + + if(p->use_count() != m2[p] && scan_and_mark(i->second.first, i->second.second, m, m2)) + { + updated = true; + } + } + + } while(updated); + + // report unreachable objects + + for(map_type::iterator i = m.begin(); i != m.end(); ++i) + { + boost::detail::counted_base const * p = static_cast(i->first); + + if(p->use_count() == m2[p]) + { + std::cerr << "Unreachable object at " << i->second.first << ", " << i->second.second << " bytes long.\n"; + } + } +} + +// debug hooks + namespace boost { @@ -120,6 +261,13 @@ void sp_scalar_constructor_hook(void * p) *pm = adopted_scalar; } +void sp_scalar_constructor_hook(void * px, std::size_t size, void * pn) +{ + sp_scalar_constructor_hook(px); + mutex_type::scoped_lock lock(get_mutex()); + get_map()[pn] = std::make_pair(px, size); +} + void sp_scalar_destructor_hook(void * p) { if(p == 0) return; @@ -132,6 +280,13 @@ void sp_scalar_destructor_hook(void * p) *pm = allocated_scalar; } +void sp_scalar_destructor_hook(void * px, std::size_t /*size*/, void * pn) +{ + sp_scalar_destructor_hook(px); + mutex_type::scoped_lock lock(get_mutex()); + get_map().erase(pn); +} + // It is not possible to handle the array hooks in a portable manner. // The implementation typically reserves a bit of storage for the number // of objects in the array, and the argument of the array hook isn't @@ -173,6 +328,8 @@ void sp_array_destructor_hook(void * /* p */) } // namespace boost +// operator delete + void operator delete(void * p) throw() { if(p == 0) return;