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