PageRenderTime 38ms CodeModel.GetById 1ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/prelude/tpreload.cpp

http://github.com/Eelis/geordi
C++ | 91 lines | 73 code | 14 blank | 4 comment | 18 complexity | 888b1090fa9aefb17183e49cce5ee964 MD5 | raw file
 1#include <stdlib.h>
 2#include <string.h>
 3#include <signal.h>
 4#include <map>
 5#include <cstdlib>
 6#include <cstdio>
 7#include <ext/malloc_allocator.h>
 8#include <mcheck.h>
 9#include "geordi.hpp"
10
11extern "C"
12{
13  void abort() throw() // default version causes "SYS_gettid: Operation not permitted".
14  {
15    std::printf("%s%s", geordi::parsep, strsignal(SIGABRT));
16    std::fclose(stdout); // Prevents things like tracked reporting leaks.
17    std::exit(0);
18  }
19}
20
21namespace
22{
23  enum alloc_reg { ar_plain, ar_array, ar_deleted };
24
25  typedef std::map<void *, alloc_reg, std::less<void *>, __gnu_cxx::malloc_allocator<std::pair<void * const, alloc_reg> > > Allocs;
26
27  Allocs & allocs() { static Allocs * const r = new (std::malloc(sizeof(Allocs))) Allocs; return *r; }
28    // We can't use an ordinary variable because when the construction of an earlier static-storage variable uses dynamic storage, it would cause us to operate on a not-yet-constructed container. We use malloc to avoid infinite mutual recursion with op_new. We don't ever destruct/deallocate r, because there is no way to ensure that that happens after all other destruction (which could involve dynamic storage) has finished.
29
30  std::new_handler get_new_handler() throw()
31  {
32    std::new_handler const r = std::set_new_handler(0);
33    std::set_new_handler(r);
34    return r;
35  }
36
37  void * op_new(std::size_t const s, alloc_reg const ar) throw()
38  {
39    std::new_handler const h = get_new_handler();
40
41    for(;;) // See 18.5.1.1 paragraph 4 (in N2691).
42    {
43      if(void * const r = std::malloc(s))
44        for(;;)
45        {
46          try { allocs()[r] = ar; return r;  }
47          catch (std::bad_alloc const &)
48          { if(!h) { std::free(r); return 0; } h(); }
49        }
50      if(!h) return 0;
51      h();
52    }
53  }
54
55  void del(void * const p, bool const a)
56  {
57    if(!p) return;
58    Allocs::iterator const i = allocs().find(p);
59    if(i == allocs().end())
60      geordi::error()() << "tried to delete pointer not returned by previous matching allocation.";
61    if(i->second == ar_deleted)
62      geordi::error()() << "tried to delete already deleted pointer.";
63    if(a && i->second == ar_plain)
64      geordi::error()() << "tried to delete[] pointer returned by non-array operator new.";
65    if(!a && i->second == ar_array)
66      geordi::error()() << "tried to apply non-array operator delete to pointer returned by new[].";
67    i->second = ar_deleted;
68
69    // We don't actually deallocate the memory, because then it could be reallocated, causing UB in { int * const p = new int; delete p; new int; delete p; } to go unnoticed if the second allocation returns the same address (which is not unlikely).
70
71    mprobe(p);
72  }
73}
74
75// Plain new/delete:
76
77void * operator new(size_t const i, std::nothrow_t const &) throw ()
78{ return op_new(i, ar_plain); }
79void * operator new(size_t const i) throw (std::bad_alloc)
80{ if (void * const r = op_new(i, ar_plain)) return r; throw std::bad_alloc(); }
81void operator delete(void * const p, std::nothrow_t const &) throw () { operator delete(p); }
82void operator delete(void * const p) throw() { del(p, false); }
83
84// Array new[]/delete[]:
85
86void * operator new[](size_t const i, std::nothrow_t const &) throw ()
87{ return op_new(i, ar_array); }
88void * operator new[](size_t const i) throw (std::bad_alloc)
89{ if (void * const r = op_new(i, ar_array)) return r; throw std::bad_alloc(); }
90void operator delete[](void * const p, std::nothrow_t const &) throw () { operator delete[](p); }
91void operator delete[](void * const p) throw() { del(p, true); }