mirror of
https://github.com/boostorg/smart_ptr.git
synced 2025-07-31 21:24:40 +02:00
report_unreachable_objects() added to sp_debug_hooks.cpp
[SVN r16780]
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
#include <exception> // for std::exception
|
||||
#include <new> // for std::bad_alloc
|
||||
#include <typeinfo> // for std::type_info in get_deleter
|
||||
#include <cstddef> // 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<class T> void cbi_call_constructor_hook(T * p, checked_deleter<T> const &, int)
|
||||
template<class T> void cbi_call_constructor_hook(counted_base * pn, T * px, checked_deleter<T> const &, int)
|
||||
{
|
||||
boost::sp_scalar_constructor_hook(p);
|
||||
boost::sp_scalar_constructor_hook(px, sizeof(T), pn);
|
||||
}
|
||||
|
||||
template<class T> void cbi_call_constructor_hook(T * p, checked_array_deleter<T> const &, int)
|
||||
template<class T> void cbi_call_constructor_hook(counted_base *, T * px, checked_array_deleter<T> const &, int)
|
||||
{
|
||||
boost::sp_array_constructor_hook(p);
|
||||
boost::sp_array_constructor_hook(px);
|
||||
}
|
||||
|
||||
template<class P, class D> void cbi_call_constructor_hook(P const &, D const &, long)
|
||||
template<class P, class D> void cbi_call_constructor_hook(counted_base *, P const &, D const &, long)
|
||||
{
|
||||
}
|
||||
|
||||
template<class T> void cbi_call_destructor_hook(T * p, checked_deleter<T> const &, int)
|
||||
template<class T> void cbi_call_destructor_hook(counted_base * pn, T * px, checked_deleter<T> const &, int)
|
||||
{
|
||||
boost::sp_scalar_destructor_hook(p);
|
||||
boost::sp_scalar_destructor_hook(px, sizeof(T), pn);
|
||||
}
|
||||
|
||||
template<class T> void cbi_call_destructor_hook(T * p, checked_array_deleter<T> const &, int)
|
||||
template<class T> void cbi_call_destructor_hook(counted_base *, T * px, checked_array_deleter<T> const &, int)
|
||||
{
|
||||
boost::sp_array_destructor_hook(p);
|
||||
boost::sp_array_destructor_hook(px);
|
||||
}
|
||||
|
||||
template<class P, class D> void cbi_call_destructor_hook(P const &, D const &, long)
|
||||
template<class P, class D> 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<class P, class D> 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<class Y>
|
||||
explicit shared_count(std::auto_ptr<Y> & r): pi_(new counted_base_impl< Y *, checked_deleter<Y> >(r.get(), checked_deleter<Y>()))
|
||||
#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)
|
||||
{
|
||||
|
@@ -14,11 +14,17 @@
|
||||
#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS)
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/detail/shared_count.hpp>
|
||||
#include <boost/detail/lightweight_mutex.hpp>
|
||||
#include <new>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
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<void *, size_t> > 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<void const *, long> & m2)
|
||||
{
|
||||
unsigned char const * p = static_cast<unsigned char const *>(area);
|
||||
|
||||
for(size_t n = 0; n < size; p += pointer_align, n += pointer_align)
|
||||
{
|
||||
shared_ptr_layout const * q = reinterpret_cast<shared_ptr_layout const *>(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<void const *, long> & m2)
|
||||
{
|
||||
bool updated = false;
|
||||
unsigned char const * p = static_cast<unsigned char const *>(area);
|
||||
|
||||
for(size_t n = 0; n < size; p += pointer_align, n += pointer_align)
|
||||
{
|
||||
shared_ptr_layout const * q = reinterpret_cast<shared_ptr_layout const *>(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<void const *, long> 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<boost::detail::counted_base *>(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<boost::detail::counted_base *>(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<boost::detail::counted_base *>(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;
|
||||
|
Reference in New Issue
Block a user