xref: /llvm-project-15.0.7/libcxx/src/debug.cpp (revision 57b08b09)
1 //===-------------------------- debug.cpp ---------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "__config"
10 #include "__debug"
11 #include "functional"
12 #include "algorithm"
13 #include "string"
14 #include "cstdio"
15 #include "__hash_table"
16 #include "mutex"
17 
18 _LIBCPP_BEGIN_NAMESPACE_STD
19 
20 static std::string make_what_str(__libcpp_debug_info const& info) {
21   string msg = info.__file_;
22   msg += ":" + to_string(info.__line_) + ": _LIBCPP_ASSERT '";
23   msg += info.__pred_;
24   msg += "' failed. ";
25   msg += info.__msg_;
26   return msg;
27 }
28 
29 _LIBCPP_SAFE_STATIC __libcpp_debug_function_type
30     __libcpp_debug_function = __libcpp_abort_debug_function;
31 
32 bool __libcpp_set_debug_function(__libcpp_debug_function_type __func) {
33   __libcpp_debug_function = __func;
34   return true;
35 }
36 
37 _LIBCPP_NORETURN void __libcpp_abort_debug_function(__libcpp_debug_info const& info) {
38   std::fprintf(stderr, "%s\n", make_what_str(info).c_str());
39   std::abort();
40 }
41 
42 _LIBCPP_NORETURN void __libcpp_throw_debug_function(__libcpp_debug_info const& info) {
43 #ifndef _LIBCPP_NO_EXCEPTIONS
44   throw __libcpp_debug_exception(info);
45 #else
46   __libcpp_abort_debug_function(info);
47 #endif
48 }
49 
50 struct __libcpp_debug_exception::__libcpp_debug_exception_imp {
51   __libcpp_debug_info __info_;
52   std::string __what_str_;
53 };
54 
55 __libcpp_debug_exception::__libcpp_debug_exception() _NOEXCEPT
56     : __imp_(nullptr) {
57 }
58 
59 __libcpp_debug_exception::__libcpp_debug_exception(
60     __libcpp_debug_info const& info) : __imp_(new __libcpp_debug_exception_imp)
61 {
62   __imp_->__info_ = info;
63   __imp_->__what_str_ = make_what_str(info);
64 }
65 __libcpp_debug_exception::__libcpp_debug_exception(
66     __libcpp_debug_exception const& other) : __imp_(nullptr) {
67   if (other.__imp_)
68     __imp_ = new __libcpp_debug_exception_imp(*other.__imp_);
69 }
70 
71 __libcpp_debug_exception::~__libcpp_debug_exception() _NOEXCEPT {
72   if (__imp_)
73     delete __imp_;
74 }
75 
76 const char* __libcpp_debug_exception::what() const _NOEXCEPT {
77   if (__imp_)
78     return __imp_->__what_str_.c_str();
79   return "__libcpp_debug_exception";
80 }
81 
82 _LIBCPP_FUNC_VIS
83 __libcpp_db*
84 __get_db()
85 {
86     static __libcpp_db db;
87     return &db;
88 }
89 
90 _LIBCPP_FUNC_VIS
91 const __libcpp_db*
92 __get_const_db()
93 {
94     return __get_db();
95 }
96 
97 namespace
98 {
99 
100 #ifndef _LIBCPP_HAS_NO_THREADS
101 typedef mutex mutex_type;
102 typedef lock_guard<mutex_type> WLock;
103 typedef lock_guard<mutex_type> RLock;
104 
105 mutex_type&
106 mut()
107 {
108     static mutex_type m;
109     return m;
110 }
111 #endif // !_LIBCPP_HAS_NO_THREADS
112 
113 }  // unnamed namespace
114 
115 __i_node::~__i_node()
116 {
117     if (__next_)
118     {
119         __next_->~__i_node();
120         free(__next_);
121     }
122 }
123 
124 __c_node::~__c_node()
125 {
126     free(beg_);
127     if (__next_)
128     {
129         __next_->~__c_node();
130         free(__next_);
131     }
132 }
133 
134 __libcpp_db::__libcpp_db()
135     : __cbeg_(nullptr),
136       __cend_(nullptr),
137       __csz_(0),
138       __ibeg_(nullptr),
139       __iend_(nullptr),
140       __isz_(0)
141 {
142 }
143 
144 __libcpp_db::~__libcpp_db()
145 {
146     if (__cbeg_)
147     {
148         for (__c_node** p = __cbeg_; p != __cend_; ++p)
149         {
150             if (*p != nullptr)
151             {
152                 (*p)->~__c_node();
153                 free(*p);
154             }
155         }
156         free(__cbeg_);
157     }
158     if (__ibeg_)
159     {
160         for (__i_node** p = __ibeg_; p != __iend_; ++p)
161         {
162             if (*p != nullptr)
163             {
164                 (*p)->~__i_node();
165                 free(*p);
166             }
167         }
168         free(__ibeg_);
169     }
170 }
171 
172 void*
173 __libcpp_db::__find_c_from_i(void* __i) const
174 {
175 #ifndef _LIBCPP_HAS_NO_THREADS
176     RLock _(mut());
177 #endif
178     __i_node* i = __find_iterator(__i);
179     _LIBCPP_ASSERT(i != nullptr, "iterator not found in debug database.");
180     return i->__c_ != nullptr ? i->__c_->__c_ : nullptr;
181 }
182 
183 void
184 __libcpp_db::__insert_ic(void* __i, const void* __c)
185 {
186 #ifndef _LIBCPP_HAS_NO_THREADS
187     WLock _(mut());
188 #endif
189     if (__cbeg_ == __cend_)
190         return;
191     size_t hc = hash<const void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
192     __c_node* c = __cbeg_[hc];
193     if (c == nullptr)
194         return;
195     while (c->__c_ != __c)
196     {
197         c = c->__next_;
198         if (c == nullptr)
199             return;
200     }
201     __i_node* i = __insert_iterator(__i);
202     c->__add(i);
203     i->__c_ = c;
204 }
205 
206 __c_node*
207 __libcpp_db::__insert_c(void* __c)
208 {
209 #ifndef _LIBCPP_HAS_NO_THREADS
210     WLock _(mut());
211 #endif
212     if (__csz_ + 1 > static_cast<size_t>(__cend_ - __cbeg_))
213     {
214         size_t nc = __next_prime(2*static_cast<size_t>(__cend_ - __cbeg_) + 1);
215         __c_node** cbeg = static_cast<__c_node**>(calloc(nc, sizeof(void*)));
216         if (cbeg == nullptr)
217             __throw_bad_alloc();
218 
219         for (__c_node** p = __cbeg_; p != __cend_; ++p)
220         {
221             __c_node* q = *p;
222             while (q != nullptr)
223             {
224                 size_t h = hash<void*>()(q->__c_) % nc;
225                 __c_node* r = q->__next_;
226                 q->__next_ = cbeg[h];
227                 cbeg[h] = q;
228                 q = r;
229             }
230         }
231         free(__cbeg_);
232         __cbeg_ = cbeg;
233         __cend_ = __cbeg_ + nc;
234     }
235     size_t hc = hash<void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
236     __c_node* p = __cbeg_[hc];
237     __c_node* r = __cbeg_[hc] =
238       static_cast<__c_node*>(malloc(sizeof(__c_node)));
239     if (__cbeg_[hc] == nullptr)
240         __throw_bad_alloc();
241 
242     r->__c_ = __c;
243     r->__next_ = p;
244     ++__csz_;
245     return r;
246 }
247 
248 void
249 __libcpp_db::__erase_i(void* __i)
250 {
251 #ifndef _LIBCPP_HAS_NO_THREADS
252     WLock _(mut());
253 #endif
254     if (__ibeg_ != __iend_)
255     {
256         size_t hi = hash<void*>()(__i) % static_cast<size_t>(__iend_ - __ibeg_);
257         __i_node* p = __ibeg_[hi];
258         if (p != nullptr)
259         {
260             __i_node* q = nullptr;
261             while (p->__i_ != __i)
262             {
263                 q = p;
264                 p = p->__next_;
265                 if (p == nullptr)
266                     return;
267             }
268             if (q == nullptr)
269                 __ibeg_[hi] = p->__next_;
270             else
271                 q->__next_ = p->__next_;
272             __c_node* c = p->__c_;
273             --__isz_;
274             if (c != nullptr)
275                 c->__remove(p);
276             free(p);
277         }
278     }
279 }
280 
281 void
282 __libcpp_db::__invalidate_all(void* __c)
283 {
284 #ifndef _LIBCPP_HAS_NO_THREADS
285     WLock _(mut());
286 #endif
287     if (__cend_ != __cbeg_)
288     {
289         size_t hc = hash<void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
290         __c_node* p = __cbeg_[hc];
291         if (p == nullptr)
292             return;
293         while (p->__c_ != __c)
294         {
295             p = p->__next_;
296             if (p == nullptr)
297                 return;
298         }
299         while (p->end_ != p->beg_)
300         {
301             --p->end_;
302             (*p->end_)->__c_ = nullptr;
303         }
304     }
305 }
306 
307 __c_node*
308 __libcpp_db::__find_c_and_lock(void* __c) const
309 {
310 #ifndef _LIBCPP_HAS_NO_THREADS
311     mut().lock();
312 #endif
313     if (__cend_ == __cbeg_)
314     {
315 #ifndef _LIBCPP_HAS_NO_THREADS
316         mut().unlock();
317 #endif
318         return nullptr;
319     }
320     size_t hc = hash<void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
321     __c_node* p = __cbeg_[hc];
322     if (p == nullptr)
323     {
324 #ifndef _LIBCPP_HAS_NO_THREADS
325         mut().unlock();
326 #endif
327         return nullptr;
328     }
329     while (p->__c_ != __c)
330     {
331         p = p->__next_;
332         if (p == nullptr)
333         {
334 #ifndef _LIBCPP_HAS_NO_THREADS
335             mut().unlock();
336 #endif
337             return nullptr;
338         }
339     }
340     return p;
341 }
342 
343 __c_node*
344 __libcpp_db::__find_c(void* __c) const
345 {
346     size_t hc = hash<void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
347     __c_node* p = __cbeg_[hc];
348     _LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __find_c A");
349     while (p->__c_ != __c)
350     {
351         p = p->__next_;
352         _LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __find_c B");
353     }
354     return p;
355 }
356 
357 void
358 __libcpp_db::unlock() const
359 {
360 #ifndef _LIBCPP_HAS_NO_THREADS
361     mut().unlock();
362 #endif
363 }
364 
365 void
366 __libcpp_db::__erase_c(void* __c)
367 {
368 #ifndef _LIBCPP_HAS_NO_THREADS
369     WLock _(mut());
370 #endif
371     if (__cend_ != __cbeg_)
372     {
373         size_t hc = hash<void*>()(__c) % static_cast<size_t>(__cend_ - __cbeg_);
374         __c_node* p = __cbeg_[hc];
375         if (p == nullptr)
376             return;
377         __c_node* q = nullptr;
378         _LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __erase_c A");
379         while (p->__c_ != __c)
380         {
381             q = p;
382             p = p->__next_;
383             if (p == nullptr)
384                 return;
385             _LIBCPP_ASSERT(p != nullptr, "debug mode internal logic error __erase_c B");
386         }
387         if (q == nullptr)
388             __cbeg_[hc] = p->__next_;
389         else
390             q->__next_ = p->__next_;
391         while (p->end_ != p->beg_)
392         {
393             --p->end_;
394             (*p->end_)->__c_ = nullptr;
395         }
396         free(p->beg_);
397         free(p);
398         --__csz_;
399     }
400 }
401 
402 void
403 __libcpp_db::__iterator_copy(void* __i, const void* __i0)
404 {
405 #ifndef _LIBCPP_HAS_NO_THREADS
406     WLock _(mut());
407 #endif
408     __i_node* i = __find_iterator(__i);
409     __i_node* i0 = __find_iterator(__i0);
410     __c_node* c0 = i0 != nullptr ? i0->__c_ : nullptr;
411     if (i == nullptr && i0 != nullptr)
412         i = __insert_iterator(__i);
413     __c_node* c = i != nullptr ? i->__c_ : nullptr;
414     if (c != c0)
415     {
416         if (c != nullptr)
417             c->__remove(i);
418         if (i != nullptr)
419         {
420             i->__c_ = nullptr;
421             if (c0 != nullptr)
422             {
423                 i->__c_ = c0;
424                 i->__c_->__add(i);
425             }
426         }
427     }
428 }
429 
430 bool
431 __libcpp_db::__dereferenceable(const void* __i) const
432 {
433 #ifndef _LIBCPP_HAS_NO_THREADS
434     RLock _(mut());
435 #endif
436     __i_node* i = __find_iterator(__i);
437     return i != nullptr && i->__c_ != nullptr && i->__c_->__dereferenceable(__i);
438 }
439 
440 bool
441 __libcpp_db::__decrementable(const void* __i) const
442 {
443 #ifndef _LIBCPP_HAS_NO_THREADS
444     RLock _(mut());
445 #endif
446     __i_node* i = __find_iterator(__i);
447     return i != nullptr && i->__c_ != nullptr && i->__c_->__decrementable(__i);
448 }
449 
450 bool
451 __libcpp_db::__addable(const void* __i, ptrdiff_t __n) const
452 {
453 #ifndef _LIBCPP_HAS_NO_THREADS
454     RLock _(mut());
455 #endif
456     __i_node* i = __find_iterator(__i);
457     return i != nullptr && i->__c_ != nullptr && i->__c_->__addable(__i, __n);
458 }
459 
460 bool
461 __libcpp_db::__subscriptable(const void* __i, ptrdiff_t __n) const
462 {
463 #ifndef _LIBCPP_HAS_NO_THREADS
464     RLock _(mut());
465 #endif
466     __i_node* i = __find_iterator(__i);
467     return i != nullptr && i->__c_ != nullptr && i->__c_->__subscriptable(__i, __n);
468 }
469 
470 bool
471 __libcpp_db::__less_than_comparable(const void* __i, const void* __j) const
472 {
473 #ifndef _LIBCPP_HAS_NO_THREADS
474     RLock _(mut());
475 #endif
476     __i_node* i = __find_iterator(__i);
477     __i_node* j = __find_iterator(__j);
478     __c_node* ci = i != nullptr ? i->__c_ : nullptr;
479     __c_node* cj = j != nullptr ? j->__c_ : nullptr;
480     return ci != nullptr && ci == cj;
481 }
482 
483 void
484 __libcpp_db::swap(void* c1, void* c2)
485 {
486 #ifndef _LIBCPP_HAS_NO_THREADS
487     WLock _(mut());
488 #endif
489     size_t hc = hash<void*>()(c1) % static_cast<size_t>(__cend_ - __cbeg_);
490     __c_node* p1 = __cbeg_[hc];
491     _LIBCPP_ASSERT(p1 != nullptr, "debug mode internal logic error swap A");
492     while (p1->__c_ != c1)
493     {
494         p1 = p1->__next_;
495         _LIBCPP_ASSERT(p1 != nullptr, "debug mode internal logic error swap B");
496     }
497     hc = hash<void*>()(c2) % static_cast<size_t>(__cend_ - __cbeg_);
498     __c_node* p2 = __cbeg_[hc];
499     _LIBCPP_ASSERT(p2 != nullptr, "debug mode internal logic error swap C");
500     while (p2->__c_ != c2)
501     {
502         p2 = p2->__next_;
503         _LIBCPP_ASSERT(p2 != nullptr, "debug mode internal logic error swap D");
504     }
505     std::swap(p1->beg_, p2->beg_);
506     std::swap(p1->end_, p2->end_);
507     std::swap(p1->cap_, p2->cap_);
508     for (__i_node** p = p1->beg_; p != p1->end_; ++p)
509         (*p)->__c_ = p1;
510     for (__i_node** p = p2->beg_; p != p2->end_; ++p)
511         (*p)->__c_ = p2;
512 }
513 
514 void
515 __libcpp_db::__insert_i(void* __i)
516 {
517 #ifndef _LIBCPP_HAS_NO_THREADS
518     WLock _(mut());
519 #endif
520     __insert_iterator(__i);
521 }
522 
523 void
524 __c_node::__add(__i_node* i)
525 {
526     if (end_ == cap_)
527     {
528         size_t nc = 2*static_cast<size_t>(cap_ - beg_);
529         if (nc == 0)
530             nc = 1;
531         __i_node** beg =
532            static_cast<__i_node**>(malloc(nc * sizeof(__i_node*)));
533         if (beg == nullptr)
534             __throw_bad_alloc();
535 
536         if (nc > 1)
537             memcpy(beg, beg_, nc/2*sizeof(__i_node*));
538         free(beg_);
539         beg_ = beg;
540         end_ = beg_ + nc/2;
541         cap_ = beg_ + nc;
542     }
543     *end_++ = i;
544 }
545 
546 // private api
547 
548 _LIBCPP_HIDDEN
549 __i_node*
550 __libcpp_db::__insert_iterator(void* __i)
551 {
552     if (__isz_ + 1 > static_cast<size_t>(__iend_ - __ibeg_))
553     {
554         size_t nc = __next_prime(2*static_cast<size_t>(__iend_ - __ibeg_) + 1);
555         __i_node** ibeg = static_cast<__i_node**>(calloc(nc, sizeof(void*)));
556         if (ibeg == nullptr)
557             __throw_bad_alloc();
558 
559         for (__i_node** p = __ibeg_; p != __iend_; ++p)
560         {
561             __i_node* q = *p;
562             while (q != nullptr)
563             {
564                 size_t h = hash<void*>()(q->__i_) % nc;
565                 __i_node* r = q->__next_;
566                 q->__next_ = ibeg[h];
567                 ibeg[h] = q;
568                 q = r;
569             }
570         }
571         free(__ibeg_);
572         __ibeg_ = ibeg;
573         __iend_ = __ibeg_ + nc;
574     }
575     size_t hi = hash<void*>()(__i) % static_cast<size_t>(__iend_ - __ibeg_);
576     __i_node* p = __ibeg_[hi];
577     __i_node* r = __ibeg_[hi] =
578       static_cast<__i_node*>(malloc(sizeof(__i_node)));
579     if (r == nullptr)
580         __throw_bad_alloc();
581 
582     ::new(r) __i_node(__i, p, nullptr);
583     ++__isz_;
584     return r;
585 }
586 
587 _LIBCPP_HIDDEN
588 __i_node*
589 __libcpp_db::__find_iterator(const void* __i) const
590 {
591     __i_node* r = nullptr;
592     if (__ibeg_ != __iend_)
593     {
594         size_t h = hash<const void*>()(__i) % static_cast<size_t>(__iend_ - __ibeg_);
595         for (__i_node* nd = __ibeg_[h]; nd != nullptr; nd = nd->__next_)
596         {
597             if (nd->__i_ == __i)
598             {
599                 r = nd;
600                 break;
601             }
602         }
603     }
604     return r;
605 }
606 
607 _LIBCPP_HIDDEN
608 void
609 __c_node::__remove(__i_node* p)
610 {
611     __i_node** r = find(beg_, end_, p);
612     _LIBCPP_ASSERT(r != end_, "debug mode internal logic error __c_node::__remove");
613     if (--end_ != r)
614         memmove(r, r+1, static_cast<size_t>(end_ - r)*sizeof(__i_node*));
615 }
616 
617 _LIBCPP_END_NAMESPACE_STD
618