
1. I had been detecting and trapping iterator == and \!= among iterators in different containers as an error. But the trapping itself is actually an error. Consider: #include <iostream> #include <vector> #include <algorithm> template <class C> void display(const C& c) { std::cout << "{"; bool first = true; for (const auto& x : c) { if (\!first) std::cout << ", "; first = false; std::cout << x; } std::cout << "}\n"; } int main() { typedef std::vector<int> V; V v1 = {1, 3, 5}; V v2 = {2, 4, 6}; display(v1); display(v2); V::iterator i = std::find(v1.begin(), v1.end(), 1); V::iterator j = std::find(v2.begin(), v2.end(), 2); if (*i == *j) i = j; // perfectly legal // ... if (i \!= j) // the only way to check v2.push_back(*i); display(v1); display(v2); } It is legal to assign an iterator from one container to another of the same type. This is required to work. One might want to test whether or not such an assignment had been made. The way one performs such a check is using the iterator's ==, \!= operator. This is a logical and necessary function and does not constitute an error. 2. I had a header circular dependence bug when _LIBCPP_DEBUG2 is defined. This caused a problem in several of the libc++ tests. Fixed. 3. There is a serious problem when _LIBCPP_DEBUG2=1 at the moment in that std::basic_string is inoperable. std::basic_string uses __wrap_iterator to implement its iterators. __wrap_iterator has been rigged up in debug mode to support vector. But string hasn't been rigged up yet. This means that one gets false positives when using std::string in debug mode. I've upped std::string's priority in www/debug_mode.html. llvm-svn: 187636
507 lines
12 KiB
C++
507 lines
12 KiB
C++
//===-------------------------- debug.cpp ---------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
|
// Source Licenses. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define _LIBCPP_DEBUG2 1
|
|
#include "__config"
|
|
#include "__debug"
|
|
#include "functional"
|
|
#include "algorithm"
|
|
#include "__hash_table"
|
|
#include "mutex"
|
|
|
|
_LIBCPP_BEGIN_NAMESPACE_STD
|
|
|
|
_LIBCPP_FUNC_VIS
|
|
__libcpp_db*
|
|
__get_db()
|
|
{
|
|
static __libcpp_db db;
|
|
return &db;
|
|
}
|
|
|
|
_LIBCPP_FUNC_VIS
|
|
const __libcpp_db*
|
|
__get_const_db()
|
|
{
|
|
return __get_db();
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
typedef mutex mutex_type;
|
|
typedef lock_guard<mutex_type> WLock;
|
|
typedef lock_guard<mutex_type> RLock;
|
|
|
|
mutex_type&
|
|
mut()
|
|
{
|
|
static mutex_type m;
|
|
return m;
|
|
}
|
|
|
|
} // unnamed namespace
|
|
|
|
__i_node::~__i_node()
|
|
{
|
|
if (__next_)
|
|
{
|
|
__next_->~__i_node();
|
|
free(__next_);
|
|
}
|
|
}
|
|
|
|
__c_node::~__c_node()
|
|
{
|
|
free(beg_);
|
|
if (__next_)
|
|
{
|
|
__next_->~__c_node();
|
|
free(__next_);
|
|
}
|
|
}
|
|
|
|
__libcpp_db::__libcpp_db()
|
|
: __cbeg_(nullptr),
|
|
__cend_(nullptr),
|
|
__csz_(0),
|
|
__ibeg_(nullptr),
|
|
__iend_(nullptr),
|
|
__isz_(0)
|
|
{
|
|
}
|
|
|
|
__libcpp_db::~__libcpp_db()
|
|
{
|
|
if (__cbeg_)
|
|
{
|
|
for (__c_node** p = __cbeg_; p != __cend_; ++p)
|
|
{
|
|
if (*p != nullptr)
|
|
{
|
|
(*p)->~__c_node();
|
|
free(*p);
|
|
}
|
|
}
|
|
free(__cbeg_);
|
|
}
|
|
if (__ibeg_)
|
|
{
|
|
for (__i_node** p = __ibeg_; p != __iend_; ++p)
|
|
{
|
|
if (*p != nullptr)
|
|
{
|
|
(*p)->~__i_node();
|
|
free(*p);
|
|
}
|
|
}
|
|
free(__ibeg_);
|
|
}
|
|
}
|
|
|
|
void*
|
|
__libcpp_db::__find_c_from_i(void* __i) const
|
|
{
|
|
RLock _(mut());
|
|
__i_node* i = __find_iterator(__i);
|
|
_LIBCPP_ASSERT(i != nullptr, "iterator not found in debug database.");
|
|
return i->__c_ != nullptr ? i->__c_->__c_ : nullptr;
|
|
}
|
|
|
|
void
|
|
__libcpp_db::__insert_ic(void* __i, const void* __c)
|
|
{
|
|
WLock _(mut());
|
|
__i_node* i = __insert_iterator(__i);
|
|
const char* errmsg =
|
|
"Container constructed in a translation unit with debug mode disabled."
|
|
" But it is being used in a translation unit with debug mode enabled."
|
|
" Enable it in the other translation unit with #define _LIBCPP_DEBUG2 1";
|
|
_LIBCPP_ASSERT(__cbeg_ != __cend_, errmsg);
|
|
size_t hc = hash<const void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
|
|
__c_node* c = __cbeg_[hc];
|
|
_LIBCPP_ASSERT(c != nullptr, errmsg);
|
|
while (c->__c_ != __c)
|
|
{
|
|
c = c->__next_;
|
|
_LIBCPP_ASSERT(c != nullptr, errmsg);
|
|
}
|
|
c->__add(i);
|
|
i->__c_ = c;
|
|
}
|
|
|
|
__c_node*
|
|
__libcpp_db::__insert_c(void* __c)
|
|
{
|
|
WLock _(mut());
|
|
if (__csz_ + 1 > static_cast<size_t>(__cend_ - __cbeg_))
|
|
{
|
|
size_t nc = __next_prime(2*static_cast<size_t>(__cend_ - __cbeg_) + 1);
|
|
__c_node** cbeg = static_cast<__c_node**>(calloc(nc, sizeof(void*)));
|
|
if (cbeg == nullptr)
|
|
#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
throw bad_alloc();
|
|
#else
|
|
abort();
|
|
#endif
|
|
for (__c_node** p = __cbeg_; p != __cend_; ++p)
|
|
{
|
|
__c_node* q = *p;
|
|
while (q != nullptr)
|
|
{
|
|
size_t h = hash<void*>()(q->__c_) % nc;
|
|
__c_node* r = q->__next_;
|
|
q->__next_ = cbeg[h];
|
|
cbeg[h] = q;
|
|
q = r;
|
|
}
|
|
}
|
|
free(__cbeg_);
|
|
__cbeg_ = cbeg;
|
|
__cend_ = __cbeg_ + nc;
|
|
}
|
|
size_t hc = hash<void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
|
|
__c_node* p = __cbeg_[hc];
|
|
__c_node* r = __cbeg_[hc] =
|
|
static_cast<__c_node*>(malloc(sizeof(__c_node)));
|
|
if (__cbeg_[hc] == nullptr)
|
|
#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
throw bad_alloc();
|
|
#else
|
|
abort();
|
|
#endif
|
|
r->__c_ = __c;
|
|
r->__next_ = p;
|
|
++__csz_;
|
|
return r;
|
|
}
|
|
|
|
void
|
|
__libcpp_db::__erase_i(void* __i)
|
|
{
|
|
WLock _(mut());
|
|
if (__ibeg_ != __iend_)
|
|
{
|
|
size_t hi = hash<void*>()(__i) % static_cast<size_t>(__iend_ - __ibeg_);
|
|
__i_node* p = __ibeg_[hi];
|
|
if (p != nullptr)
|
|
{
|
|
__i_node* q = nullptr;
|
|
while (p->__i_ != __i)
|
|
{
|
|
q = p;
|
|
p = p->__next_;
|
|
if (p == nullptr)
|
|
return;
|
|
}
|
|
if (q == nullptr)
|
|
__ibeg_[hi] = p->__next_;
|
|
else
|
|
q->__next_ = p->__next_;
|
|
__c_node* c = p->__c_;
|
|
free(p);
|
|
--__isz_;
|
|
if (c != nullptr)
|
|
c->__remove(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
__libcpp_db::__invalidate_all(void* __c)
|
|
{
|
|
WLock _(mut());
|
|
size_t hc = hash<void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
|
|
__c_node* p = __cbeg_[hc];
|
|
_LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __invalidate_all A");
|
|
while (p->__c_ != __c)
|
|
{
|
|
p = p->__next_;
|
|
_LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __invalidate_all B");
|
|
}
|
|
while (p->end_ != p->beg_)
|
|
{
|
|
--p->end_;
|
|
(*p->end_)->__c_ = nullptr;
|
|
}
|
|
}
|
|
|
|
__c_node*
|
|
__libcpp_db::__find_c_and_lock(void* __c) const
|
|
{
|
|
mut().lock();
|
|
size_t hc = hash<void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
|
|
__c_node* p = __cbeg_[hc];
|
|
_LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __find_c_and_lock A");
|
|
while (p->__c_ != __c)
|
|
{
|
|
p = p->__next_;
|
|
_LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __find_c_and_lock B");
|
|
}
|
|
return p;
|
|
}
|
|
|
|
__c_node*
|
|
__libcpp_db::__find_c(void* __c) const
|
|
{
|
|
size_t hc = hash<void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
|
|
__c_node* p = __cbeg_[hc];
|
|
_LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __find_c A");
|
|
while (p->__c_ != __c)
|
|
{
|
|
p = p->__next_;
|
|
_LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __find_c B");
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void
|
|
__libcpp_db::unlock() const
|
|
{
|
|
mut().unlock();
|
|
}
|
|
|
|
void
|
|
__libcpp_db::__erase_c(void* __c)
|
|
{
|
|
WLock _(mut());
|
|
size_t hc = hash<void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
|
|
__c_node* p = __cbeg_[hc];
|
|
__c_node* q = nullptr;
|
|
_LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __erase_c A");
|
|
while (p->__c_ != __c)
|
|
{
|
|
q = p;
|
|
p = p->__next_;
|
|
_LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __erase_c B");
|
|
}
|
|
if (q == nullptr)
|
|
__cbeg_[hc] = p->__next_;
|
|
else
|
|
q->__next_ = p->__next_;
|
|
while (p->end_ != p->beg_)
|
|
{
|
|
--p->end_;
|
|
(*p->end_)->__c_ = nullptr;
|
|
}
|
|
free(p->beg_);
|
|
free(p);
|
|
--__csz_;
|
|
}
|
|
|
|
void
|
|
__libcpp_db::__iterator_copy(void* __i, const void* __i0)
|
|
{
|
|
WLock _(mut());
|
|
__i_node* i = __find_iterator(__i);
|
|
__i_node* i0 = __find_iterator(__i0);
|
|
__c_node* c0 = i0 != nullptr ? i0->__c_ : nullptr;
|
|
if (i == nullptr && i0 != nullptr)
|
|
i = __insert_iterator(__i);
|
|
__c_node* c = i != nullptr ? i->__c_ : nullptr;
|
|
if (c != c0)
|
|
{
|
|
if (c != nullptr)
|
|
c->__remove(i);
|
|
if (i != nullptr)
|
|
{
|
|
i->__c_ = nullptr;
|
|
if (c0 != nullptr)
|
|
{
|
|
i->__c_ = c0;
|
|
i->__c_->__add(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
__libcpp_db::__dereferenceable(const void* __i) const
|
|
{
|
|
RLock _(mut());
|
|
__i_node* i = __find_iterator(__i);
|
|
return i != nullptr && i->__c_ != nullptr && i->__c_->__dereferenceable(__i);
|
|
}
|
|
|
|
bool
|
|
__libcpp_db::__decrementable(const void* __i) const
|
|
{
|
|
RLock _(mut());
|
|
__i_node* i = __find_iterator(__i);
|
|
return i != nullptr && i->__c_ != nullptr && i->__c_->__decrementable(__i);
|
|
}
|
|
|
|
bool
|
|
__libcpp_db::__addable(const void* __i, ptrdiff_t __n) const
|
|
{
|
|
RLock _(mut());
|
|
__i_node* i = __find_iterator(__i);
|
|
return i != nullptr && i->__c_ != nullptr && i->__c_->__addable(__i, __n);
|
|
}
|
|
|
|
bool
|
|
__libcpp_db::__subscriptable(const void* __i, ptrdiff_t __n) const
|
|
{
|
|
RLock _(mut());
|
|
__i_node* i = __find_iterator(__i);
|
|
return i != nullptr && i->__c_ != nullptr && i->__c_->__subscriptable(__i, __n);
|
|
}
|
|
|
|
bool
|
|
__libcpp_db::__less_than_comparable(const void* __i, const void* __j) const
|
|
{
|
|
RLock _(mut());
|
|
__i_node* i = __find_iterator(__i);
|
|
__i_node* j = __find_iterator(__j);
|
|
__c_node* ci = i != nullptr ? i->__c_ : nullptr;
|
|
__c_node* cj = j != nullptr ? j->__c_ : nullptr;
|
|
return ci != nullptr && ci == cj;
|
|
}
|
|
|
|
void
|
|
__libcpp_db::swap(void* c1, void* c2)
|
|
{
|
|
WLock _(mut());
|
|
size_t hc = hash<void*>()(c1) % static_cast<size_t>(__cend_ - __cbeg_);
|
|
__c_node* p1 = __cbeg_[hc];
|
|
_LIBCPP_ASSERT(p1 != nullptr, "debug mode internal logic error swap A");
|
|
while (p1->__c_ != c1)
|
|
{
|
|
p1 = p1->__next_;
|
|
_LIBCPP_ASSERT(p1 != nullptr, "debug mode internal logic error swap B");
|
|
}
|
|
hc = hash<void*>()(c2) % static_cast<size_t>(__cend_ - __cbeg_);
|
|
__c_node* p2 = __cbeg_[hc];
|
|
_LIBCPP_ASSERT(p2 != nullptr, "debug mode internal logic error swap C");
|
|
while (p2->__c_ != c2)
|
|
{
|
|
p2 = p2->__next_;
|
|
_LIBCPP_ASSERT(p2 != nullptr, "debug mode internal logic error swap D");
|
|
}
|
|
std::swap(p1->beg_, p2->beg_);
|
|
std::swap(p1->end_, p2->end_);
|
|
std::swap(p1->cap_, p2->cap_);
|
|
for (__i_node** p = p1->beg_; p != p1->end_; ++p)
|
|
(*p)->__c_ = p1;
|
|
for (__i_node** p = p2->beg_; p != p2->end_; ++p)
|
|
(*p)->__c_ = p2;
|
|
}
|
|
|
|
void
|
|
__libcpp_db::__insert_i(void* __i)
|
|
{
|
|
WLock _(mut());
|
|
__insert_iterator(__i);
|
|
}
|
|
|
|
void
|
|
__c_node::__add(__i_node* i)
|
|
{
|
|
if (end_ == cap_)
|
|
{
|
|
size_t nc = 2*static_cast<size_t>(cap_ - beg_);
|
|
if (nc == 0)
|
|
nc = 1;
|
|
__i_node** beg =
|
|
static_cast<__i_node**>(malloc(nc * sizeof(__i_node*)));
|
|
if (beg == nullptr)
|
|
#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
throw bad_alloc();
|
|
#else
|
|
abort();
|
|
#endif
|
|
if (nc > 1)
|
|
memcpy(beg, beg_, nc/2*sizeof(__i_node*));
|
|
free(beg_);
|
|
beg_ = beg;
|
|
end_ = beg_ + nc/2;
|
|
cap_ = beg_ + nc;
|
|
}
|
|
*end_++ = i;
|
|
}
|
|
|
|
// private api
|
|
|
|
_LIBCPP_HIDDEN
|
|
__i_node*
|
|
__libcpp_db::__insert_iterator(void* __i)
|
|
{
|
|
if (__isz_ + 1 > static_cast<size_t>(__iend_ - __ibeg_))
|
|
{
|
|
size_t nc = __next_prime(2*static_cast<size_t>(__iend_ - __ibeg_) + 1);
|
|
__i_node** ibeg = static_cast<__i_node**>(calloc(nc, sizeof(void*)));
|
|
if (ibeg == nullptr)
|
|
#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
throw bad_alloc();
|
|
#else
|
|
abort();
|
|
#endif
|
|
for (__i_node** p = __ibeg_; p != __iend_; ++p)
|
|
{
|
|
__i_node* q = *p;
|
|
while (q != nullptr)
|
|
{
|
|
size_t h = hash<void*>()(q->__i_) % nc;
|
|
__i_node* r = q->__next_;
|
|
q->__next_ = ibeg[h];
|
|
ibeg[h] = q;
|
|
q = r;
|
|
}
|
|
}
|
|
free(__ibeg_);
|
|
__ibeg_ = ibeg;
|
|
__iend_ = __ibeg_ + nc;
|
|
}
|
|
size_t hi = hash<void*>()(__i) % static_cast<size_t>(__iend_ - __ibeg_);
|
|
__i_node* p = __ibeg_[hi];
|
|
__i_node* r = __ibeg_[hi] =
|
|
static_cast<__i_node*>(malloc(sizeof(__i_node)));
|
|
if (r == nullptr)
|
|
#ifndef _LIBCPP_NO_EXCEPTIONS
|
|
throw bad_alloc();
|
|
#else
|
|
abort();
|
|
#endif
|
|
::new(r) __i_node(__i, p, nullptr);
|
|
++__isz_;
|
|
return r;
|
|
}
|
|
|
|
_LIBCPP_HIDDEN
|
|
__i_node*
|
|
__libcpp_db::__find_iterator(const void* __i) const
|
|
{
|
|
__i_node* r = nullptr;
|
|
if (__ibeg_ != __iend_)
|
|
{
|
|
size_t h = hash<const void*>()(__i) % static_cast<size_t>(__iend_ - __ibeg_);
|
|
for (__i_node* nd = __ibeg_[h]; nd != nullptr; nd = nd->__next_)
|
|
{
|
|
if (nd->__i_ == __i)
|
|
{
|
|
r = nd;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
_LIBCPP_HIDDEN
|
|
void
|
|
__c_node::__remove(__i_node* p)
|
|
{
|
|
__i_node** r = find(beg_, end_, p);
|
|
_LIBCPP_ASSERT(r != end_, "debug mode internal logic error __c_node::__remove");
|
|
if (--end_ != r)
|
|
memmove(r, r+1, static_cast<size_t>(end_ - r)*sizeof(__i_node*));
|
|
}
|
|
|
|
_LIBCPP_END_NAMESPACE_STD
|