1 /*
2 * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the <organization> nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "CppUTest/TestHarness.h"
29 #include "CppUTest/MemoryLeakWarningPlugin.h"
30 #include "CppUTest/MemoryLeakDetector.h"
31 #include "CppUTest/TestMemoryAllocator.h"
32 #include "CppUTest/PlatformSpecificFunctions.h"
33 #include "CppUTest/SimpleMutex.h"
34
35 /********** Enabling and disabling for C also *********/
36
37 #if CPPUTEST_USE_MEM_LEAK_DETECTION
38
39 class MemLeakScopedMutex
40 {
41 public:
MemLeakScopedMutex()42 MemLeakScopedMutex() : lock(MemoryLeakWarningPlugin::getGlobalDetector()->getMutex()) { }
43 private:
44 ScopedMutexLock lock;
45 };
46
threadsafe_mem_leak_malloc(size_t size,const char * file,size_t line)47 static void* threadsafe_mem_leak_malloc(size_t size, const char* file, size_t line)
48 {
49 MemLeakScopedMutex lock;
50 return MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentMallocAllocator(), size, file, line, true);
51 }
52
threadsafe_mem_leak_free(void * buffer,const char * file,size_t line)53 static void threadsafe_mem_leak_free(void* buffer, const char* file, size_t line)
54 {
55 MemLeakScopedMutex lock;
56 MemoryLeakWarningPlugin::getGlobalDetector()->invalidateMemory((char*) buffer);
57 MemoryLeakWarningPlugin::getGlobalDetector()->deallocMemory(getCurrentMallocAllocator(), (char*) buffer, file, line, true);
58 }
59
threadsafe_mem_leak_realloc(void * memory,size_t size,const char * file,size_t line)60 static void* threadsafe_mem_leak_realloc(void* memory, size_t size, const char* file, size_t line)
61 {
62 MemLeakScopedMutex lock;
63 return MemoryLeakWarningPlugin::getGlobalDetector()->reallocMemory(getCurrentMallocAllocator(), (char*) memory, size, file, line, true);
64 }
65
66
mem_leak_malloc(size_t size,const char * file,size_t line)67 static void* mem_leak_malloc(size_t size, const char* file, size_t line)
68 {
69 return MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentMallocAllocator(), size, file, line, true);
70 }
71
mem_leak_free(void * buffer,const char * file,size_t line)72 static void mem_leak_free(void* buffer, const char* file, size_t line)
73 {
74 MemoryLeakWarningPlugin::getGlobalDetector()->invalidateMemory((char*) buffer);
75 MemoryLeakWarningPlugin::getGlobalDetector()->deallocMemory(getCurrentMallocAllocator(), (char*) buffer, file, line, true);
76 }
77
mem_leak_realloc(void * memory,size_t size,const char * file,size_t line)78 static void* mem_leak_realloc(void* memory, size_t size, const char* file, size_t line)
79 {
80 return MemoryLeakWarningPlugin::getGlobalDetector()->reallocMemory(getCurrentMallocAllocator(), (char*) memory, size, file, line, true);
81 }
82
83 #endif
84
normal_malloc(size_t size,const char *,size_t)85 static void* normal_malloc(size_t size, const char*, size_t)
86 {
87 return PlatformSpecificMalloc(size);
88 }
89
normal_realloc(void * memory,size_t size,const char *,size_t)90 static void* normal_realloc(void* memory, size_t size, const char*, size_t)
91 {
92 return PlatformSpecificRealloc(memory, size);
93 }
94
normal_free(void * buffer,const char *,size_t)95 static void normal_free(void* buffer, const char*, size_t)
96 {
97 PlatformSpecificFree(buffer);
98 }
99
100 #if CPPUTEST_USE_MEM_LEAK_DETECTION
101 static void *(*malloc_fptr)(size_t size, const char* file, size_t line) = mem_leak_malloc;
102 static void (*free_fptr)(void* mem, const char* file, size_t line) = mem_leak_free;
103 static void*(*realloc_fptr)(void* memory, size_t size, const char* file, size_t line) = mem_leak_realloc;
104 static void *(*saved_malloc_fptr)(size_t size, const char* file, size_t line) = mem_leak_malloc;
105 static void (*saved_free_fptr)(void* mem, const char* file, size_t line) = mem_leak_free;
106 static void*(*saved_realloc_fptr)(void* memory, size_t size, const char* file, size_t line) = mem_leak_realloc;
107 #else
108 static void *(*malloc_fptr)(size_t size, const char* file, size_t line) = normal_malloc;
109 static void (*free_fptr)(void* mem, const char* file, size_t line) = normal_free;
110 static void*(*realloc_fptr)(void* memory, size_t size, const char* file, size_t line) = normal_realloc;
111 #endif
112
cpputest_malloc_location_with_leak_detection(size_t size,const char * file,size_t line)113 void* cpputest_malloc_location_with_leak_detection(size_t size, const char* file, size_t line)
114 {
115 return malloc_fptr(size, file, line);
116 }
117
cpputest_realloc_location_with_leak_detection(void * memory,size_t size,const char * file,size_t line)118 void* cpputest_realloc_location_with_leak_detection(void* memory, size_t size, const char* file, size_t line)
119 {
120 return realloc_fptr(memory, size, file, line);
121 }
122
cpputest_free_location_with_leak_detection(void * buffer,const char * file,size_t line)123 void cpputest_free_location_with_leak_detection(void* buffer, const char* file, size_t line)
124 {
125 free_fptr(buffer, file, line);
126 }
127
128 /********** C++ *************/
129
130 #if CPPUTEST_USE_MEM_LEAK_DETECTION
131 #undef new
132
133 #if CPPUTEST_USE_STD_CPP_LIB
134 #define UT_THROW_BAD_ALLOC_WHEN_NULL(memory) if (memory == NULLPTR) throw std::bad_alloc()
135 #else
136 #define UT_THROW_BAD_ALLOC_WHEN_NULL(memory)
137 #endif
138
threadsafe_mem_leak_operator_new(size_t size)139 static void* threadsafe_mem_leak_operator_new (size_t size) UT_THROW(std::bad_alloc)
140 {
141 MemLeakScopedMutex lock;
142 void* memory = MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewAllocator(), size);
143 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
144 return memory;
145 }
146
threadsafe_mem_leak_operator_new_nothrow(size_t size)147 static void* threadsafe_mem_leak_operator_new_nothrow (size_t size) UT_NOTHROW
148 {
149 MemLeakScopedMutex lock;
150 return MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewAllocator(), size);
151 }
152
threadsafe_mem_leak_operator_new_debug(size_t size,const char * file,size_t line)153 static void* threadsafe_mem_leak_operator_new_debug (size_t size, const char* file, size_t line) UT_THROW(std::bad_alloc)
154 {
155 MemLeakScopedMutex lock;
156 void *memory = MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewAllocator(), size, file, line);
157 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
158 return memory;
159 }
160
threadsafe_mem_leak_operator_new_array(size_t size)161 static void* threadsafe_mem_leak_operator_new_array (size_t size) UT_THROW(std::bad_alloc)
162 {
163 MemLeakScopedMutex lock;
164 void* memory = MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewArrayAllocator(), size);
165 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
166 return memory;
167 }
168
threadsafe_mem_leak_operator_new_array_nothrow(size_t size)169 static void* threadsafe_mem_leak_operator_new_array_nothrow (size_t size) UT_NOTHROW
170 {
171 MemLeakScopedMutex lock;
172 return MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewArrayAllocator(), size);
173 }
174
threadsafe_mem_leak_operator_new_array_debug(size_t size,const char * file,size_t line)175 static void* threadsafe_mem_leak_operator_new_array_debug (size_t size, const char* file, size_t line) UT_THROW(std::bad_alloc)
176 {
177 MemLeakScopedMutex lock;
178 void* memory = MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewArrayAllocator(), size, file, line);
179 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
180 return memory;
181 }
182
threadsafe_mem_leak_operator_delete(void * mem)183 static void threadsafe_mem_leak_operator_delete (void* mem) UT_NOTHROW
184 {
185 MemLeakScopedMutex lock;
186 MemoryLeakWarningPlugin::getGlobalDetector()->invalidateMemory((char*) mem);
187 MemoryLeakWarningPlugin::getGlobalDetector()->deallocMemory(getCurrentNewAllocator(), (char*) mem);
188 }
189
threadsafe_mem_leak_operator_delete_array(void * mem)190 static void threadsafe_mem_leak_operator_delete_array (void* mem) UT_NOTHROW
191 {
192 MemLeakScopedMutex lock;
193 MemoryLeakWarningPlugin::getGlobalDetector()->invalidateMemory((char*) mem);
194 MemoryLeakWarningPlugin::getGlobalDetector()->deallocMemory(getCurrentNewArrayAllocator(), (char*) mem);
195 }
196
197
mem_leak_operator_new(size_t size)198 static void* mem_leak_operator_new (size_t size) UT_THROW(std::bad_alloc)
199 {
200 void* memory = MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewAllocator(), size);
201 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
202 return memory;
203 }
204
mem_leak_operator_new_nothrow(size_t size)205 static void* mem_leak_operator_new_nothrow (size_t size) UT_NOTHROW
206 {
207 return MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewAllocator(), size);
208 }
209
mem_leak_operator_new_debug(size_t size,const char * file,size_t line)210 static void* mem_leak_operator_new_debug (size_t size, const char* file, size_t line) UT_THROW(std::bad_alloc)
211 {
212 void *memory = MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewAllocator(), size, file, line);
213 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
214 return memory;
215 }
216
mem_leak_operator_new_array(size_t size)217 static void* mem_leak_operator_new_array (size_t size) UT_THROW(std::bad_alloc)
218 {
219 void* memory = MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewArrayAllocator(), size);
220 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
221 return memory;
222 }
223
mem_leak_operator_new_array_nothrow(size_t size)224 static void* mem_leak_operator_new_array_nothrow (size_t size) UT_NOTHROW
225 {
226 return MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewArrayAllocator(), size);
227 }
228
mem_leak_operator_new_array_debug(size_t size,const char * file,size_t line)229 static void* mem_leak_operator_new_array_debug (size_t size, const char* file, size_t line) UT_THROW(std::bad_alloc)
230 {
231 void* memory = MemoryLeakWarningPlugin::getGlobalDetector()->allocMemory(getCurrentNewArrayAllocator(), size, file, line);
232 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
233 return memory;
234 }
235
mem_leak_operator_delete(void * mem)236 static void mem_leak_operator_delete (void* mem) UT_NOTHROW
237 {
238 MemoryLeakWarningPlugin::getGlobalDetector()->invalidateMemory((char*) mem);
239 MemoryLeakWarningPlugin::getGlobalDetector()->deallocMemory(getCurrentNewAllocator(), (char*) mem);
240 }
241
mem_leak_operator_delete_array(void * mem)242 static void mem_leak_operator_delete_array (void* mem) UT_NOTHROW
243 {
244 MemoryLeakWarningPlugin::getGlobalDetector()->invalidateMemory((char*) mem);
245 MemoryLeakWarningPlugin::getGlobalDetector()->deallocMemory(getCurrentNewArrayAllocator(), (char*) mem);
246 }
247
normal_operator_new(size_t size)248 static void* normal_operator_new (size_t size) UT_THROW(std::bad_alloc)
249 {
250 void* memory = PlatformSpecificMalloc(size);
251 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
252 return memory;
253 }
254
normal_operator_new_nothrow(size_t size)255 static void* normal_operator_new_nothrow (size_t size) UT_NOTHROW
256 {
257 return PlatformSpecificMalloc(size);
258 }
259
normal_operator_new_debug(size_t size,const char *,size_t)260 static void* normal_operator_new_debug (size_t size, const char* /*file*/, size_t /*line*/) UT_THROW(std::bad_alloc)
261 {
262 void* memory = PlatformSpecificMalloc(size);
263 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
264 return memory;
265 }
266
normal_operator_new_array(size_t size)267 static void* normal_operator_new_array (size_t size) UT_THROW(std::bad_alloc)
268 {
269 void* memory = PlatformSpecificMalloc(size);
270 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
271 return memory;
272 }
273
normal_operator_new_array_nothrow(size_t size)274 static void* normal_operator_new_array_nothrow (size_t size) UT_NOTHROW
275 {
276 return PlatformSpecificMalloc(size);
277 }
278
normal_operator_new_array_debug(size_t size,const char *,size_t)279 static void* normal_operator_new_array_debug (size_t size, const char* /*file*/, size_t /*line*/) UT_THROW(std::bad_alloc)
280 {
281 void* memory = PlatformSpecificMalloc(size);
282 UT_THROW_BAD_ALLOC_WHEN_NULL(memory);
283 return memory;
284 }
285
normal_operator_delete(void * mem)286 static void normal_operator_delete (void* mem) UT_NOTHROW
287 {
288 PlatformSpecificFree(mem);
289 }
290
normal_operator_delete_array(void * mem)291 static void normal_operator_delete_array (void* mem) UT_NOTHROW
292 {
293 PlatformSpecificFree(mem);
294 }
295
296 static void *(*operator_new_fptr)(size_t size) UT_THROW(std::bad_alloc) = mem_leak_operator_new;
297 static void *(*operator_new_nothrow_fptr)(size_t size) UT_NOTHROW = mem_leak_operator_new_nothrow;
298 static void *(*operator_new_debug_fptr)(size_t size, const char* file, size_t line) UT_THROW(std::bad_alloc) = mem_leak_operator_new_debug;
299 static void *(*operator_new_array_fptr)(size_t size) UT_THROW(std::bad_alloc) = mem_leak_operator_new_array;
300 static void *(*operator_new_array_nothrow_fptr)(size_t size) UT_NOTHROW = mem_leak_operator_new_array_nothrow;
301 static void *(*operator_new_array_debug_fptr)(size_t size, const char* file, size_t line) UT_THROW(std::bad_alloc) = mem_leak_operator_new_array_debug;
302 static void (*operator_delete_fptr)(void* mem) UT_NOTHROW = mem_leak_operator_delete;
303 static void (*operator_delete_array_fptr)(void* mem) UT_NOTHROW = mem_leak_operator_delete_array;
304
305 static void *(*saved_operator_new_fptr)(size_t size) UT_THROW(std::bad_alloc) = mem_leak_operator_new;
306 static void *(*saved_operator_new_nothrow_fptr)(size_t size) UT_NOTHROW = mem_leak_operator_new_nothrow;
307 static void *(*saved_operator_new_debug_fptr)(size_t size, const char* file, size_t line) UT_THROW(std::bad_alloc) = mem_leak_operator_new_debug;
308 static void *(*saved_operator_new_array_fptr)(size_t size) UT_THROW(std::bad_alloc) = mem_leak_operator_new_array;
309 static void *(*saved_operator_new_array_nothrow_fptr)(size_t size) UT_NOTHROW = mem_leak_operator_new_array_nothrow;
310 static void *(*saved_operator_new_array_debug_fptr)(size_t size, const char* file, size_t line) UT_THROW(std::bad_alloc) = mem_leak_operator_new_array_debug;
311 static void (*saved_operator_delete_fptr)(void* mem) UT_NOTHROW = mem_leak_operator_delete;
312 static void (*saved_operator_delete_array_fptr)(void* mem) UT_NOTHROW = mem_leak_operator_delete_array;
313 static int save_counter = 0;
314
operator new(size_t size)315 void* operator new(size_t size) UT_THROW(std::bad_alloc)
316 {
317 return operator_new_fptr(size);
318 }
319
operator new(size_t size,const char * file,size_t line)320 void* operator new(size_t size, const char* file, size_t line) UT_THROW(std::bad_alloc)
321 {
322 return operator_new_debug_fptr(size, file, line);
323 }
324
operator delete(void * mem)325 void operator delete(void* mem) UT_NOTHROW
326 {
327 operator_delete_fptr(mem);
328 }
329
operator delete(void * mem,const char *,size_t)330 void operator delete(void* mem, const char*, size_t) UT_NOTHROW
331 {
332 operator_delete_fptr(mem);
333 }
334
335 #if __cplusplus >= 201402L
operator delete(void * mem,size_t)336 void operator delete (void* mem, size_t) UT_NOTHROW
337 {
338 operator_delete_fptr(mem);
339 }
340 #endif
341
operator new[](size_t size)342 void* operator new[](size_t size) UT_THROW(std::bad_alloc)
343 {
344 return operator_new_array_fptr(size);
345 }
346
operator new[](size_t size,const char * file,size_t line)347 void* operator new [](size_t size, const char* file, size_t line) UT_THROW(std::bad_alloc)
348 {
349 return operator_new_array_debug_fptr(size, file, line);
350 }
351
operator delete[](void * mem)352 void operator delete[](void* mem) UT_NOTHROW
353 {
354 operator_delete_array_fptr(mem);
355 }
356
operator delete[](void * mem,const char *,size_t)357 void operator delete[](void* mem, const char*, size_t) UT_NOTHROW
358 {
359 operator_delete_array_fptr(mem);
360 }
361
362 #if __cplusplus >= 201402L
operator delete[](void * mem,size_t)363 void operator delete[] (void* mem, size_t) UT_NOTHROW
364 {
365 operator_delete_array_fptr(mem);
366 }
367 #endif
368
369 #if CPPUTEST_USE_STD_CPP_LIB
370
operator new(size_t size,const std::nothrow_t &)371 void* operator new(size_t size, const std::nothrow_t&) UT_NOTHROW
372 {
373 return operator_new_nothrow_fptr(size);
374 }
375
operator delete(void * mem,const std::nothrow_t &)376 void operator delete(void* mem, const std::nothrow_t&) UT_NOTHROW
377 {
378 operator_delete_fptr(mem);
379 }
380
operator new[](size_t size,const std::nothrow_t &)381 void* operator new[](size_t size, const std::nothrow_t&) UT_NOTHROW
382 {
383 return operator_new_array_nothrow_fptr(size);
384 }
385
operator delete[](void * mem,const std::nothrow_t &)386 void operator delete[](void* mem, const std::nothrow_t&) UT_NOTHROW
387 {
388 operator_delete_array_fptr(mem);
389 }
390
391 #else
392
393 /* Have a similar method. This avoid unused operator_new_nothrow_fptr warning */
394
395 extern void* operator_new_nothrow(size_t size) UT_NOTHROW;
396 extern void* operator_new_array_nothrow(size_t size) UT_NOTHROW;
397
operator_new_nothrow(size_t size)398 void* operator_new_nothrow(size_t size) UT_NOTHROW
399 {
400 return operator_new_nothrow_fptr(size);
401 }
402
operator_new_array_nothrow(size_t size)403 void* operator_new_array_nothrow(size_t size) UT_NOTHROW
404 {
405 return operator_new_array_nothrow_fptr(size);
406 }
407
408 #endif
409 #endif
410
turnOffNewDeleteOverloads()411 void MemoryLeakWarningPlugin::turnOffNewDeleteOverloads()
412 {
413 #if CPPUTEST_USE_MEM_LEAK_DETECTION
414 operator_new_fptr = normal_operator_new;
415 operator_new_nothrow_fptr = normal_operator_new_nothrow;
416 operator_new_debug_fptr = normal_operator_new_debug;
417 operator_new_array_fptr = normal_operator_new_array;
418 operator_new_array_nothrow_fptr = normal_operator_new_array_nothrow;
419 operator_new_array_debug_fptr = normal_operator_new_array_debug;
420 operator_delete_fptr = normal_operator_delete;
421 operator_delete_array_fptr = normal_operator_delete_array;
422 malloc_fptr = normal_malloc;
423 realloc_fptr = normal_realloc;
424 free_fptr = normal_free;
425
426 #endif
427 }
428
turnOnDefaultNotThreadSafeNewDeleteOverloads()429 void MemoryLeakWarningPlugin::turnOnDefaultNotThreadSafeNewDeleteOverloads()
430 {
431 #if CPPUTEST_USE_MEM_LEAK_DETECTION
432 operator_new_fptr = mem_leak_operator_new;
433 operator_new_nothrow_fptr = mem_leak_operator_new_nothrow;
434 operator_new_debug_fptr = mem_leak_operator_new_debug;
435 operator_new_array_fptr = mem_leak_operator_new_array;
436 operator_new_array_nothrow_fptr = mem_leak_operator_new_array_nothrow;
437 operator_new_array_debug_fptr = mem_leak_operator_new_array_debug;
438 operator_delete_fptr = mem_leak_operator_delete;
439 operator_delete_array_fptr = mem_leak_operator_delete_array;
440 malloc_fptr = mem_leak_malloc;
441 realloc_fptr = mem_leak_realloc;
442 free_fptr = mem_leak_free;
443 #endif
444 }
445
turnOnThreadSafeNewDeleteOverloads()446 void MemoryLeakWarningPlugin::turnOnThreadSafeNewDeleteOverloads()
447 {
448 #if CPPUTEST_USE_MEM_LEAK_DETECTION
449 operator_new_fptr = threadsafe_mem_leak_operator_new;
450 operator_new_nothrow_fptr = threadsafe_mem_leak_operator_new_nothrow;
451 operator_new_debug_fptr = threadsafe_mem_leak_operator_new_debug;
452 operator_new_array_fptr = threadsafe_mem_leak_operator_new_array;
453 operator_new_array_nothrow_fptr = threadsafe_mem_leak_operator_new_array_nothrow;
454 operator_new_array_debug_fptr = threadsafe_mem_leak_operator_new_array_debug;
455 operator_delete_fptr = threadsafe_mem_leak_operator_delete;
456 operator_delete_array_fptr = threadsafe_mem_leak_operator_delete_array;
457 malloc_fptr = threadsafe_mem_leak_malloc;
458 realloc_fptr = threadsafe_mem_leak_realloc;
459 free_fptr = threadsafe_mem_leak_free;
460 #endif
461 }
462
areNewDeleteOverloaded()463 bool MemoryLeakWarningPlugin::areNewDeleteOverloaded()
464 {
465 #if CPPUTEST_USE_MEM_LEAK_DETECTION
466 return operator_new_fptr == mem_leak_operator_new || operator_new_fptr == threadsafe_mem_leak_operator_new;
467 #else
468 return false;
469 #endif
470 }
471
saveAndDisableNewDeleteOverloads()472 void MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads()
473 {
474 #if CPPUTEST_USE_MEM_LEAK_DETECTION
475 if (++save_counter > 1) return;
476 saved_operator_new_fptr = operator_new_fptr;
477 saved_operator_new_nothrow_fptr = operator_new_nothrow_fptr;
478 saved_operator_new_debug_fptr = operator_new_debug_fptr;
479 saved_operator_new_array_fptr = operator_new_array_fptr;
480 saved_operator_new_array_nothrow_fptr = operator_new_array_nothrow_fptr;
481 saved_operator_new_array_debug_fptr = operator_new_array_debug_fptr;
482 saved_operator_delete_fptr = operator_delete_fptr;
483 saved_operator_delete_array_fptr = operator_delete_array_fptr;
484 saved_malloc_fptr = malloc_fptr;
485 saved_realloc_fptr = realloc_fptr;
486 saved_free_fptr = free_fptr;
487 turnOffNewDeleteOverloads();
488 #endif
489 }
490
restoreNewDeleteOverloads()491 void MemoryLeakWarningPlugin::restoreNewDeleteOverloads()
492 {
493 #if CPPUTEST_USE_MEM_LEAK_DETECTION
494 if (--save_counter > 0) return;
495 operator_new_fptr = saved_operator_new_fptr;
496 operator_new_nothrow_fptr = saved_operator_new_nothrow_fptr;
497 operator_new_debug_fptr = saved_operator_new_debug_fptr;
498 operator_new_array_fptr = saved_operator_new_array_fptr;
499 operator_new_array_nothrow_fptr = saved_operator_new_array_nothrow_fptr;
500 operator_new_array_debug_fptr = saved_operator_new_array_debug_fptr;
501 operator_delete_fptr = saved_operator_delete_fptr;
502 operator_delete_array_fptr = saved_operator_delete_array_fptr;
503 malloc_fptr = saved_malloc_fptr;
504 realloc_fptr = saved_realloc_fptr;
505 free_fptr = saved_free_fptr;
506 #endif
507 }
508
crash_on_allocation_number(unsigned alloc_number)509 void crash_on_allocation_number(unsigned alloc_number)
510 {
511 static CrashOnAllocationAllocator crashAllocator;
512 crashAllocator.setNumberToCrashOn(alloc_number);
513 setCurrentMallocAllocator(&crashAllocator);
514 setCurrentNewAllocator(&crashAllocator);
515 setCurrentNewArrayAllocator(&crashAllocator);
516 }
517
518 class MemoryLeakWarningReporter: public MemoryLeakFailure
519 {
520 public:
~MemoryLeakWarningReporter()521 virtual ~MemoryLeakWarningReporter() _destructor_override
522 {
523 }
524
fail(char * fail_string)525 virtual void fail(char* fail_string) _override
526 {
527 UtestShell* currentTest = UtestShell::getCurrent();
528 currentTest->failWith(FailFailure(currentTest, currentTest->getName().asCharString(), currentTest->getLineNumber(), fail_string), TestTerminatorWithoutExceptions());
529 } // LCOV_EXCL_LINE
530 };
531
532 static MemoryLeakFailure* globalReporter = NULLPTR;
533 static MemoryLeakDetector* globalDetector = NULLPTR;
534
getGlobalDetector()535 MemoryLeakDetector* MemoryLeakWarningPlugin::getGlobalDetector()
536 {
537 if (globalDetector == NULLPTR) {
538 saveAndDisableNewDeleteOverloads();
539
540 globalReporter = new MemoryLeakWarningReporter;
541 globalDetector = new MemoryLeakDetector(globalReporter);
542
543 restoreNewDeleteOverloads();
544 }
545 return globalDetector;
546 }
547
getGlobalFailureReporter()548 MemoryLeakFailure* MemoryLeakWarningPlugin::getGlobalFailureReporter()
549 {
550 return globalReporter;
551 }
552
destroyGlobalDetectorAndTurnOffMemoryLeakDetectionInDestructor(bool des)553 void MemoryLeakWarningPlugin::destroyGlobalDetectorAndTurnOffMemoryLeakDetectionInDestructor(bool des)
554 {
555 destroyGlobalDetectorAndTurnOfMemoryLeakDetectionInDestructor_ = des;
556 }
557
setGlobalDetector(MemoryLeakDetector * detector,MemoryLeakFailure * reporter)558 void MemoryLeakWarningPlugin::setGlobalDetector(MemoryLeakDetector* detector, MemoryLeakFailure* reporter)
559 {
560 globalDetector = detector;
561 globalReporter = reporter;
562 }
563
destroyGlobalDetector()564 void MemoryLeakWarningPlugin::destroyGlobalDetector()
565 {
566 turnOffNewDeleteOverloads();
567 delete globalDetector;
568 delete globalReporter;
569 globalDetector = NULLPTR;
570 }
571
572
573 MemoryLeakWarningPlugin* MemoryLeakWarningPlugin::firstPlugin_ = NULLPTR;
574
getFirstPlugin()575 MemoryLeakWarningPlugin* MemoryLeakWarningPlugin::getFirstPlugin()
576 {
577 return firstPlugin_;
578 }
579
getMemoryLeakDetector()580 MemoryLeakDetector* MemoryLeakWarningPlugin::getMemoryLeakDetector()
581 {
582 return memLeakDetector_;
583 }
584
ignoreAllLeaksInTest()585 void MemoryLeakWarningPlugin::ignoreAllLeaksInTest()
586 {
587 ignoreAllWarnings_ = true;
588 }
589
expectLeaksInTest(size_t n)590 void MemoryLeakWarningPlugin::expectLeaksInTest(size_t n)
591 {
592 expectedLeaks_ = n;
593 }
594
MemoryLeakWarningPlugin(const SimpleString & name,MemoryLeakDetector * localDetector)595 MemoryLeakWarningPlugin::MemoryLeakWarningPlugin(const SimpleString& name, MemoryLeakDetector* localDetector) :
596 TestPlugin(name), ignoreAllWarnings_(false), destroyGlobalDetectorAndTurnOfMemoryLeakDetectionInDestructor_(false), expectedLeaks_(0)
597 {
598 if (firstPlugin_ == NULLPTR) firstPlugin_ = this;
599
600 if (localDetector) memLeakDetector_ = localDetector;
601 else memLeakDetector_ = getGlobalDetector();
602
603 memLeakDetector_->enable();
604 }
605
~MemoryLeakWarningPlugin()606 MemoryLeakWarningPlugin::~MemoryLeakWarningPlugin()
607 {
608 if (destroyGlobalDetectorAndTurnOfMemoryLeakDetectionInDestructor_) {
609 MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
610 MemoryLeakWarningPlugin::destroyGlobalDetector();
611 }
612 }
613
preTestAction(UtestShell &,TestResult & result)614 void MemoryLeakWarningPlugin::preTestAction(UtestShell& /*test*/, TestResult& result)
615 {
616 memLeakDetector_->startChecking();
617 failureCount_ = result.getFailureCount();
618 }
619
postTestAction(UtestShell & test,TestResult & result)620 void MemoryLeakWarningPlugin::postTestAction(UtestShell& test, TestResult& result)
621 {
622 memLeakDetector_->stopChecking();
623 size_t leaks = memLeakDetector_->totalMemoryLeaks(mem_leak_period_checking);
624
625 if (!ignoreAllWarnings_ && expectedLeaks_ != leaks && failureCount_ == result.getFailureCount()) {
626 if(MemoryLeakWarningPlugin::areNewDeleteOverloaded()) {
627 TestFailure f(&test, memLeakDetector_->report(mem_leak_period_checking));
628 result.addFailure(f);
629 } else if(expectedLeaks_ > 0) {
630 result.print(StringFromFormat("Warning: Expected %d leak(s), but leak detection was disabled", (int) expectedLeaks_).asCharString());
631 }
632 }
633 memLeakDetector_->markCheckingPeriodLeaksAsNonCheckingPeriod();
634 ignoreAllWarnings_ = false;
635 expectedLeaks_ = 0;
636 }
637
FinalReport(size_t toBeDeletedLeaks)638 const char* MemoryLeakWarningPlugin::FinalReport(size_t toBeDeletedLeaks)
639 {
640 size_t leaks = memLeakDetector_->totalMemoryLeaks(mem_leak_period_enabled);
641 if (leaks != toBeDeletedLeaks) return memLeakDetector_->report(mem_leak_period_enabled);
642 return "";
643 }
644
645
646