Skip to content

Commit 2e94195

Browse files
committed
Combine memory tracker into separate header file
And add correct support for aligned allocations
1 parent f17debe commit 2e94195

File tree

3 files changed

+202
-196
lines changed

3 files changed

+202
-196
lines changed

tests/memory_tracker.hpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#include <algorithm>
2+
#include <cstdlib>
3+
4+
// Allocation tracker, to catch memory leaks and double delete
5+
constexpr std::size_t max_allocations = 20'000;
6+
void* allocations[max_allocations];
7+
void* allocations_array[max_allocations];
8+
std::size_t allocations_bytes[max_allocations];
9+
std::size_t num_allocations = 0u;
10+
std::size_t size_allocations = 0u;
11+
std::size_t double_delete = 0u;
12+
bool memory_tracking = false;
13+
bool force_next_allocation_failure = false;
14+
15+
#if defined(CHECK_MEMORY_LEAKS) && CHECK_MEMORY_LEAKS
16+
void* allocate(std::size_t size, bool array, std::align_val_t align) {
17+
if (memory_tracking && num_allocations == max_allocations) {
18+
throw std::bad_alloc();
19+
}
20+
21+
if (force_next_allocation_failure) {
22+
force_next_allocation_failure = false;
23+
throw std::bad_alloc();
24+
}
25+
26+
void* p = nullptr;
27+
if (align == std::align_val_t{0}) {
28+
p = std::malloc(size);
29+
} else {
30+
# if defined(OUP_PLATFORM_WINDOWS) && OUP_PLATFORM_WINDOWS
31+
p = _aligned_malloc(size, static_cast<std::size_t>(align));
32+
# else
33+
p = std::aligned_alloc(static_cast<std::size_t>(align), size);
34+
# endif
35+
}
36+
37+
if (!p) {
38+
throw std::bad_alloc();
39+
}
40+
41+
if (memory_tracking) {
42+
if (array) {
43+
allocations_array[num_allocations] = p;
44+
} else {
45+
allocations[num_allocations] = p;
46+
}
47+
48+
allocations_bytes[num_allocations] = size;
49+
50+
++num_allocations;
51+
size_allocations += size;
52+
}
53+
54+
return p;
55+
}
56+
57+
void deallocate(void* p, bool array, std::align_val_t align [[maybe_unused]]) {
58+
if (p == nullptr) {
59+
return;
60+
}
61+
62+
if (memory_tracking) {
63+
bool found = false;
64+
void** allocations_type = array ? allocations_array : allocations;
65+
for (std::size_t i = 0; i < num_allocations; ++i) {
66+
if (allocations_type[i] == p) {
67+
std::swap(allocations_type[i], allocations_type[num_allocations - 1]);
68+
std::swap(allocations_bytes[i], allocations_bytes[num_allocations - 1]);
69+
--num_allocations;
70+
size_allocations -= allocations_bytes[num_allocations - 1];
71+
found = true;
72+
break;
73+
}
74+
}
75+
76+
if (!found) {
77+
++double_delete;
78+
}
79+
}
80+
81+
if (align == std::align_val_t{0}) {
82+
std::free(p);
83+
} else {
84+
# if defined(OUP_PLATFORM_WINDOWS) && OUP_PLATFORM_WINDOWS
85+
_aligned_free(p);
86+
# else
87+
std::free(p);
88+
# endif
89+
}
90+
}
91+
92+
void* operator new(std::size_t size) {
93+
return allocate(size, false, std::align_val_t{0});
94+
}
95+
96+
void* operator new[](size_t size) {
97+
return allocate(size, true, std::align_val_t{0});
98+
}
99+
100+
void* operator new(std::size_t size, std::align_val_t al) {
101+
return allocate(size, false, al);
102+
}
103+
104+
void* operator new[](size_t size, std::align_val_t al) {
105+
return allocate(size, true, al);
106+
}
107+
108+
void operator delete(void* p) noexcept {
109+
deallocate(p, false, std::align_val_t{0});
110+
}
111+
112+
void operator delete[](void* p) noexcept {
113+
deallocate(p, true, std::align_val_t{0});
114+
}
115+
116+
void operator delete(void* p, std::align_val_t al) noexcept {
117+
deallocate(p, false, al);
118+
}
119+
120+
void operator delete[](void* p, std::align_val_t al) noexcept {
121+
deallocate(p, true, al);
122+
}
123+
#endif
124+
125+
struct memory_tracker {
126+
std::size_t initial_allocations;
127+
std::size_t initial_double_delete;
128+
129+
memory_tracker() noexcept :
130+
initial_allocations(num_allocations), initial_double_delete(double_delete) {
131+
memory_tracking = true;
132+
}
133+
134+
~memory_tracker() noexcept {
135+
memory_tracking = false;
136+
}
137+
138+
std::size_t leaks() const {
139+
return num_allocations - initial_allocations;
140+
}
141+
142+
std::size_t double_del() const {
143+
return double_delete - initial_double_delete;
144+
}
145+
};

tests/runtime_tests.cpp

Lines changed: 2 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,10 @@
11
#include "tests_common.hpp"
2+
#define CHECK_MEMORY_LEAKS 1
3+
#include "memory_tracker.hpp"
24

3-
#include <algorithm>
45
#include <catch2/catch_test_macros.hpp>
5-
#include <cstdlib>
66
#include <vector>
77

8-
// Allocation tracker, to catch memory leaks and double delete
9-
constexpr std::size_t max_allocations = 20'000;
10-
void* allocations[max_allocations];
11-
void* allocations_array[max_allocations];
12-
std::size_t num_allocations = 0u;
13-
std::size_t double_delete = 0u;
14-
bool memory_tracking = false;
15-
bool force_next_allocation_failure = false;
16-
178
// Replace Catch2's REQUIRE_THROWS_AS, which allocates on Windows;
189
// this confuses our memory leak checks.
1910
#undef REQUIRE_THROWS_AS
@@ -28,100 +19,6 @@ bool force_next_allocation_failure = false;
2819
} \
2920
} while (0)
3021

31-
#define CHECK_MEMORY_LEAKS 1
32-
33-
#if defined(CHECK_MEMORY_LEAKS) && CHECK_MEMORY_LEAKS
34-
void* allocate(std::size_t size, bool array) {
35-
if (memory_tracking && num_allocations == max_allocations) {
36-
throw std::bad_alloc();
37-
}
38-
39-
if (force_next_allocation_failure) {
40-
force_next_allocation_failure = false;
41-
throw std::bad_alloc();
42-
}
43-
44-
void* p = std::malloc(size);
45-
if (!p) {
46-
throw std::bad_alloc();
47-
}
48-
49-
if (memory_tracking) {
50-
if (array) {
51-
allocations_array[num_allocations] = p;
52-
} else {
53-
allocations[num_allocations] = p;
54-
}
55-
56-
++num_allocations;
57-
}
58-
59-
return p;
60-
}
61-
62-
void deallocate(void* p, bool array) {
63-
if (p == nullptr) {
64-
return;
65-
}
66-
67-
if (memory_tracking) {
68-
bool found = false;
69-
void** allocations_type = array ? allocations_array : allocations;
70-
for (std::size_t i = 0; i < num_allocations; ++i) {
71-
if (allocations_type[i] == p) {
72-
std::swap(allocations_type[i], allocations_type[num_allocations - 1]);
73-
--num_allocations;
74-
found = true;
75-
break;
76-
}
77-
}
78-
79-
if (!found) {
80-
++double_delete;
81-
}
82-
}
83-
84-
std::free(p);
85-
}
86-
87-
void* operator new(std::size_t size) {
88-
return allocate(size, false);
89-
}
90-
91-
void* operator new[](size_t size) {
92-
return allocate(size, true);
93-
}
94-
95-
void operator delete(void* p) noexcept {
96-
deallocate(p, false);
97-
}
98-
99-
void operator delete[](void* p) noexcept {
100-
deallocate(p, true);
101-
}
102-
#endif
103-
104-
struct memory_tracker {
105-
std::size_t initial_allocations;
106-
std::size_t initial_double_delete;
107-
108-
memory_tracker() noexcept :
109-
initial_allocations(num_allocations), initial_double_delete(double_delete) {
110-
memory_tracking = true;
111-
}
112-
113-
~memory_tracker() noexcept {
114-
memory_tracking = false;
115-
}
116-
117-
std::size_t leaks() const {
118-
return num_allocations - initial_allocations;
119-
}
120-
std::size_t double_del() const {
121-
return double_delete - initial_double_delete;
122-
}
123-
};
124-
12522
TEST_CASE("owner size", "[owner_size]") {
12623
REQUIRE(sizeof(test_ptr) == 2 * sizeof(void*));
12724
}

0 commit comments

Comments
 (0)