forked from boostorg/smart_ptr
report_unreachable_objects() added to sp_debug_hooks.cpp
[SVN r16780]
This commit is contained in:
@@ -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