1
2
3
4
5
6 #include <cmajor/rt/Memory.hpp>
7 #include <cmajor/rt/Error.hpp>
8 #include <cmajor/rt/Io.hpp>
9 #include <cmajor/rt/CallStack.hpp>
10 #include <algorithm>
11 #include <sstream>
12 #include <unordered_map>
13 #include <memory>
14 #include <malloc.h>
15 #include <fstream>
16 #include <vector>
17 #include <cstring>
18
19 namespace cmajor { namespace rt {
20
21 class Allocation
22 {
23 public:
24 Allocation();
25 Allocation(int serial_, int64_t size_, const char* info_);
26 void Print(const std::string& title) const;
27 int Serial() const { return serial; }
28 void SetSerial(int serial_) { serial = serial_; }
29 int64_t Size() const { return size; }
30 void SetSize(int64_t size_) { size = size_; }
31 bool Disposed() const { return disposed; }
32 void SetDisposed(bool disposed_) { disposed = disposed_; }
33 const char* Info() const { return info; }
34 void SetInfo(const char* info_) { info = info_; }
35 private:
36 int serial;
37 int64_t size;
38 bool disposed;
39 const char* info;
40 };
41
42 Allocation::Allocation() : serial(0), size(0), disposed(false), info(nullptr)
43 {
44 }
45
46 Allocation::Allocation(int serial_, int64_t size_, const char* info_) : serial(serial_), size(size_), disposed(false), info(info_)
47 {
48 }
49
50 void Allocation::Print(const std::string& title) const
51 {
52 std::string s = title + " allocation #" + std::to_string(serial) + " : size=" + std::to_string(size) + " : disposed=" + (disposed ? "true" : "false");
53 if (info != nullptr)
54 {
55 s.append(" : info '").append(info).append("'");
56 }
57 s.append("\n");
58 int32_t errorStringHandle = -1;
59 void* stdError = RtOpenStdFile(2, errorStringHandle);
60 RtWrite(stdError, (const uint8_t*)s.c_str(), s.length(), errorStringHandle);
61 RtFlush(stdError, errorStringHandle);
62 }
63
64 struct SerialLess
65 {
66 bool operator()(const Allocation* left, const Allocation* right) const
67 {
68 return left->Serial() < right->Serial();
69 }
70 };
71
72 class DebugHeap
73 {
74 public:
75 static void Init();
76 static void Done();
77 static DebugHeap& Instance() { return *instance; }
78 void SetDebugHeap() { debugHeap = true; }
79 bool GetDebugHeap() const { return debugHeap; }
80 void SetDebugSerial(int debugSerial_) { debugSerial = debugSerial_; }
81 int GetDebugSerial() const { return debugSerial; }
82 void Allocate(void* ptr, int64_t size, const char* info);
83 void Dispose(void* ptr);
84 int NextSerial() { return ++serial; }
85 void PrintLeaks();
86 private:
87 static std::unique_ptr<DebugHeap> instance;
88 DebugHeap();
89 bool debugHeap;
90 int debugSerial;
91 int serial;
92 std::unordered_map<void*, Allocation> allocationMap;
93 };
94
95 std::unique_ptr<DebugHeap> DebugHeap::instance;
96
97 DebugHeap::DebugHeap() : debugHeap(false), debugSerial(0), serial(0)
98 {
99 }
100
101 void DebugHeap::Allocate(void* ptr, int64_t size, const char* info)
102 {
103 auto it = allocationMap.find(ptr);
104 if (it == allocationMap.cend())
105 {
106 allocationMap[ptr] = Allocation(serial, size, info);
107 if (serial == debugSerial)
108 {
109 allocationMap[ptr].Print("allocating");
110 int32_t errorStringHandle = -1;
111 void* stdError = RtOpenStdFile(2, errorStringHandle);
112 RtPrintCallStack(stdError);
113 RtFlush(stdError, errorStringHandle);
114 }
115 }
116 else
117 {
118 Allocation& alloc = it->second;
119 alloc.SetSerial(serial);
120 alloc.SetSize(size);
121 alloc.SetDisposed(false);
122 alloc.SetInfo(info);
123 }
124 }
125
126 void DebugHeap::Dispose(void* ptr)
127 {
128 if (!ptr) return;
129 auto it = allocationMap.find(ptr);
130 if (it != allocationMap.cend())
131 {
132 Allocation& alloc = it->second;
133 if (alloc.Disposed())
134 {
135 alloc.Print("dangling");
136 if (debugSerial == alloc.Serial())
137 {
138 int32_t errorStringHandle = -1;
139 void* stdError = RtOpenStdFile(2, errorStringHandle);
140 RtPrintCallStack(stdError);
141 RtFlush(stdError, errorStringHandle);
142 }
143 }
144 else
145 {
146 if (debugSerial == alloc.Serial())
147 {
148 alloc.Print("disposing");
149 int32_t errorStringHandle = -1;
150 void* stdError = RtOpenStdFile(2, errorStringHandle);
151 RtPrintCallStack(stdError);
152 RtFlush(stdError, errorStringHandle);
153 }
154 alloc.SetDisposed(true);
155 }
156 }
157 else if (debugHeap)
158 {
159 std::string s = "disposing : allocation not found\n";
160 int32_t errorStringHandle = -1;
161 void* stdError = RtOpenStdFile(2, errorStringHandle);
162 RtWrite(stdError, (const uint8_t*)s.c_str(), s.length(), errorStringHandle);
163 RtPrintCallStack(stdError);
164 RtFlush(stdError, errorStringHandle);
165 }
166 }
167
168 void DebugHeap::PrintLeaks()
169 {
170 std::vector<const Allocation*> leaks;
171 for (const auto& p : allocationMap)
172 {
173 const Allocation& alloc = p.second;
174 if (!alloc.Disposed())
175 {
176 leaks.push_back(&alloc);
177 }
178 }
179 if (!leaks.empty())
180 {
181 std::string title = std::to_string(leaks.size()) + " memory leaks:\n";
182 int32_t errorStringHandle = -1;
183 void* stdError = RtOpenStdFile(2, errorStringHandle);
184 RtWrite(stdError, (const uint8_t*)title.c_str(), title.size(), errorStringHandle);
185 RtFlush(stdError, errorStringHandle);
186 std::sort(leaks.begin(), leaks.end(), SerialLess());
187 int i = 0;
188 for (const Allocation* leak : leaks)
189 {
190 leak->Print(std::to_string(i) + ": leaked");
191 ++i;
192 }
193 }
194 }
195
196 void DebugHeap::Init()
197 {
198 instance.reset(new DebugHeap());
199 }
200
201 void DebugHeap::Done()
202 {
203 if (instance->GetDebugHeap())
204 {
205 instance->PrintLeaks();
206 }
207 instance.reset();
208 }
209
210 void SetDebugHeap()
211 {
212 DebugHeap::Instance().SetDebugHeap();
213 }
214
215 void SetDebugAllocation(int debugSerial)
216 {
217 DebugHeap::Instance().SetDebugSerial(debugSerial);
218 }
219
220 void InitMemory()
221 {
222 DebugHeap::Init();
223 }
224
225 void DoneMemory()
226 {
227 DebugHeap::Done();
228 }
229
230 } }
231
232 extern "C" void* RtMemAllocInfo(int64_t size, const char* info)
233 {
234 void* ptr = malloc(size);
235 if (!ptr)
236 {
237 std::stringstream s;
238 s << "program out of memory\n";
239 std::string str = s.str();
240 int32_t errorStringHandle = -1;
241 void* stdError = RtOpenStdFile(2, errorStringHandle);
242 RtWrite(stdError, reinterpret_cast<const uint8_t*>(str.c_str()), str.length(), errorStringHandle);
243 RtPrintCallStack(stdError);
244 RtFlush(stdError, errorStringHandle);
245 exit(exitCodeOutOfMemory);
246 }
247 int serial = cmajor::rt::DebugHeap::Instance().NextSerial();
248 if (cmajor::rt::DebugHeap::Instance().GetDebugHeap() || serial == cmajor::rt::DebugHeap::Instance().GetDebugSerial())
249 {
250 cmajor::rt::DebugHeap::Instance().Allocate(ptr, size, info);
251 }
252 return ptr;
253 }
254
255 extern "C" void* RtMemAlloc(int64_t size)
256 {
257 return RtMemAllocInfo(size, nullptr);
258 }
259
260 extern "C" void RtDispose(void* ptr)
261 {
262 if (cmajor::rt::DebugHeap::Instance().GetDebugHeap() || cmajor::rt::DebugHeap::Instance().GetDebugSerial() != 0)
263 {
264 cmajor::rt::DebugHeap::Instance().Dispose(ptr);
265 }
266 }
267
268 extern "C" void RtMemFree(void* ptr)
269 {
270 free(ptr);
271 }
272
273 extern "C" void RtMemZero(void* ptr, int64_t size)
274 {
275 std::memset(ptr, 0, size);
276 }