Phasor 3.3.0
Stack VM based Programming Language
Loading...
Searching...
No Matches
alloc_trace.hpp
Go to the documentation of this file.
1#pragma once
2
3#if defined(_MSC_VER)
4 #pragma warning(push)
5 #pragma warning(disable: 4595)
6#endif
7
8#if defined(__clang__)
9 #pragma clang diagnostic push
10 #pragma clang diagnostic ignored "-Winline-new-delete"
11#endif
12
13#if defined(__GNUC__) && !defined(__clang__)
14 #pragma GCC diagnostic push
15 #pragma GCC diagnostic ignored "-Winline"
16#endif
17
18#ifdef TRACING_ALLOC
19#include <new>
20#include <cstdlib>
21#include <cstdio>
22#include <unordered_map>
23#include <mutex>
24#include <iostream>
25#include <stacktrace>
26
27inline bool& is_tracking_disabled() {
28 thread_local bool disabled = false;
29 return disabled;
30}
31
32inline void print_stacktrace() {
33 bool was_disabled = is_tracking_disabled();
34 is_tracking_disabled() = true;
35
36 std::cerr << "--- Stack trace ---\n";
37 std::cerr << std::stacktrace::current() << '\n';
38
39 is_tracking_disabled() = was_disabled;
40}
41
42struct AllocationInfo {
43 std::size_t size;
44 std::stacktrace trace;
45};
46
47struct AllocationTracker {
48 std::unordered_map<void*, AllocationInfo> active_ptrs;
49 std::mutex mtx;
50 bool is_destroyed = false;
51
52 ~AllocationTracker() {
53 is_destroyed = true;
54
55 if (!active_ptrs.empty()) {
56 std::printf(" WARNING: %zu allocation(s) were leaked!\n", active_ptrs.size());
57
58 for (auto& [ptr, info] : active_ptrs) {
59 std::printf("\n[LEAK] Address: %p (%zu bytes)\n", ptr, info.size);
60 std::cerr << "Allocated by:\n" << info.trace << "\n";
61
62 std::free(ptr);
63 }
64 }
65 }
66};
67
68inline AllocationTracker& get_tracker() {
69 static AllocationTracker tracker;
70 return tracker;
71}
72
73inline void* operator new(std::size_t size) {
74 void* ptr = std::malloc(size);
75 if (!ptr) {
76 throw std::bad_alloc();
77 }
78
79 if (!is_tracking_disabled()) {
80 is_tracking_disabled() = true;
81
82 if (!get_tracker().is_destroyed) {
83 auto trace = std::stacktrace::current(1);
84
85 std::lock_guard<std::mutex> lock(get_tracker().mtx);
86 get_tracker().active_ptrs[ptr] = { size, std::move(trace) };
87 }
88
89 is_tracking_disabled() = false;
90 }
91
92 return ptr;
93}
94
95inline void track_delete(void* ptr) {
96 if (!ptr) return;
97
98 if (!is_tracking_disabled()) {
99 is_tracking_disabled() = true;
100
101 if (!get_tracker().is_destroyed) {
102 std::lock_guard<std::mutex> lock(get_tracker().mtx);
103 auto& ptrs = get_tracker().active_ptrs;
104 auto it = ptrs.find(ptr);
105
106 if (it != ptrs.end()) {
107 ptrs.erase(it);
108 } else {
109 std::printf("\n[!!!] CRITICAL ERROR: Double free or untracked free at %p\n\n", ptr);
110 print_stacktrace();
111 }
112 }
113
114 is_tracking_disabled() = false;
115 }
116
117 std::free(ptr);
118}
119
120inline void operator delete(void* ptr) noexcept { track_delete(ptr); }
121inline void operator delete(void* ptr, std::size_t) noexcept { track_delete(ptr); }
122
123inline void* operator new[](std::size_t size) { return operator new(size); }
124inline void operator delete[](void* ptr) noexcept { track_delete(ptr); }
125inline void operator delete[](void* ptr, std::size_t) noexcept { track_delete(ptr); }
126
127#endif
128
129#if defined(__GNUC__) && !defined(__clang__)
130 #pragma GCC diagnostic pop
131#endif
132
133#if defined(__clang__)
134 #pragma clang diagnostic pop
135#endif
136
137#if defined(_MSC_VER)
138 #pragma warning(pop)
139#endif