2 * \file lib/src/Framework/MyMem.c
6 * \author Bjoern Doebel <doebel@os.inf.tu-dresden.de
10 * (c) 2007-2009 Technische Universität Dresden
11 * This file is part of TUD:OS and distributed under the terms of the
12 * GNU General Public License 2.
13 * Please see the COPYING-GPL-2 file for details.
16 * CUnit - A Unit testing framework library for C.
17 * Copyright (C) 2001 Anil Kumar
18 * Copyright (C) 2004,2005,2006 Anil Kumar, Jerry St.Clair
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Library General Public
22 * License as published by the Free Software Foundation; either
23 * version 2 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Library General Public License for more details.
30 * You should have received a copy of the GNU Library General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
36 * Memory management functions used throughout CUnit.
38 * 13/Oct/2001 Moved some of the generic functions definitions from other
39 * files to this one so as to use the functions consitently.
40 * This file is not included in the distribution headers because
41 * it is used internally by CUnit. (AK)
43 * 18-Jul-2004 New interface, doxygen comments, made local functions &
44 * constants static, fixed reporting of memory tracking (valid
45 * vs invalid cycles), restructured memory tracking to detect
46 * reallocations & multiple deletions. (JDS)
48 * 24-Apr-2005 Changed type of allocated sizes to size_t to avoid
49 * signed-unsigned mismatch. (JDS)
53 * Memory management & reporting functions (implementation).
55 /** @addtogroup Framework
68 #include "dde_support.h"
69 #endif /* DDE_LINUX */
76 #define MAX_FILE_NAME_LENGTH 256
78 /** Default name for memory dump file. */
79 static const char* f_szDefaultDumpFileName = "CUnit-Memory-Dump.xml";
80 /**< Default name for memory dump file. */
82 #ifdef CUNIT_BUILD_TESTS
83 /** For testing use (only) to simulate memory exhaustion -
84 * if CU_FALSE, allocation requests will always fail and return NULL.
86 static CU_BOOL f_bTestCunitMallocActive = CU_TRUE;
89 /** Structure holding the details of a memory allocation/deallocation event. */
90 typedef struct mem_event {
92 unsigned int AllocLine;
93 char AllocFilename[MAX_FILE_NAME_LENGTH];
94 unsigned int DeallocLine;
95 char DeallocFilename[MAX_FILE_NAME_LENGTH];
96 struct mem_event* pNext;
98 typedef MEMORY_EVENT* PMEMORY_EVENT;
100 #define NOT_ALLOCATED 0
101 #define NOT_DELETED 0
103 /** Structure holding the details of a memory node having allocation/deallocation events. */
104 typedef struct mem_node {
106 unsigned int EventCount;
107 PMEMORY_EVENT pFirstEvent;
108 struct mem_node* pNext;
110 typedef MEMORY_NODE* PMEMORY_NODE;
112 static PMEMORY_NODE f_pMemoryTrackerHead = NULL; /**< Head of double-linked list of memory nodes. */
113 static unsigned int f_nMemoryNodes = 0; /**< Counter for memory nodes created. */
114 /*------------------------------------------------------------------------*/
115 /** Locate the memory node for the specified memory location (returns NULL if none). */
116 static PMEMORY_NODE find_memory_node(void* pLocation)
118 PMEMORY_NODE pMemoryNode = f_pMemoryTrackerHead;
119 while (NULL != pMemoryNode) {
120 if (pLocation == pMemoryNode->pLocation) {
123 pMemoryNode = pMemoryNode->pNext;
128 /*------------------------------------------------------------------------*/
129 /** Create a new memory node for the specified memory location. */
130 static PMEMORY_NODE create_memory_node(void* pLocation)
132 PMEMORY_NODE pTempNode = NULL;
133 PMEMORY_NODE pMemoryNode = find_memory_node(pLocation);
135 /* a memory node for pLocation should not exist yet */
136 if (NULL == pMemoryNode) {
138 pMemoryNode = (PMEMORY_NODE)malloc(sizeof(MEMORY_NODE));
139 assert(NULL != pMemoryNode);
141 pMemoryNode->pLocation = pLocation;
142 pMemoryNode->EventCount = 0;
143 pMemoryNode->pFirstEvent = NULL;
144 pMemoryNode->pNext = NULL;
146 /* add new node to linked list */
147 pTempNode = f_pMemoryTrackerHead;
148 if (NULL == pTempNode) {
149 f_pMemoryTrackerHead = pMemoryNode;
152 while (NULL != pTempNode->pNext) {
153 pTempNode = pTempNode->pNext;
155 pTempNode->pNext = pMemoryNode;
162 /*------------------------------------------------------------------------*/
163 /** Add a new memory event having the specified parameters. */
164 static PMEMORY_EVENT add_memory_event(PMEMORY_NODE pMemoryNode,
166 unsigned int alloc_line,
167 const char* alloc_filename)
169 PMEMORY_EVENT pMemoryEvent = NULL;
170 PMEMORY_EVENT pTempEvent = NULL;
172 assert (NULL != pMemoryNode);
174 /* create and set up the new event */
175 pMemoryEvent = malloc(sizeof(MEMORY_EVENT));
176 assert(NULL != pMemoryEvent);
178 pMemoryEvent->Size = size;
179 pMemoryEvent->AllocLine = alloc_line;
180 strncpy(pMemoryEvent->AllocFilename, alloc_filename, (size_t) MAX_FILE_NAME_LENGTH-1);
181 pMemoryEvent->AllocFilename[MAX_FILE_NAME_LENGTH-1] = (char)0;
182 pMemoryEvent->DeallocLine = NOT_DELETED;
183 pMemoryEvent->DeallocFilename[0] = (char)0;
184 pMemoryEvent->pNext = NULL;
186 /* add the new event to the end of the linked list */
187 pTempEvent = pMemoryNode->pFirstEvent;
188 if (NULL == pTempEvent) {
189 pMemoryNode->pFirstEvent = pMemoryEvent;
192 while (NULL != pTempEvent->pNext) {
193 pTempEvent = pTempEvent->pNext;
195 pTempEvent->pNext = pMemoryEvent;
198 ++pMemoryNode->EventCount;
202 /*------------------------------------------------------------------------*/
203 /** Record memory allocation event. */
204 static PMEMORY_NODE allocate_memory(size_t nSize,
206 unsigned int uiAllocationLine,
207 const char* szAllocationFile)
209 PMEMORY_NODE pMemoryNode = NULL;
211 /* attempt to locate an existing record for this pLocation */
212 pMemoryNode = find_memory_node(pLocation);
214 /* pLocation not found - create a new event record */
215 if (NULL == pMemoryNode) {
216 pMemoryNode = create_memory_node(pLocation);
219 /* add the new event record */
220 add_memory_event(pMemoryNode, nSize, uiAllocationLine, szAllocationFile);
225 /*------------------------------------------------------------------------*/
226 /** Record memory deallocation event. */
227 static void deallocate_memory(void* pLocation, unsigned int uiDeletionLine, const char* szDeletionFileName)
229 PMEMORY_NODE pMemoryNode = NULL;
230 PMEMORY_EVENT pTempEvent = NULL;
232 assert(0 != uiDeletionLine);
233 assert(NULL != szDeletionFileName);
235 /* attempt to locate an existing record for this pLocation */
236 pMemoryNode = find_memory_node(pLocation);
238 /* if no entry, then an unallocated pointer was freed */
239 if (NULL == pMemoryNode) {
240 pMemoryNode = create_memory_node(pLocation);
241 pTempEvent = add_memory_event(pMemoryNode, 0, NOT_ALLOCATED, "");
244 /* there should always be at least 1 event for an existing memory node */
245 assert(NULL != pMemoryNode->pFirstEvent);
247 /* locate last memory event for this pLocation */
248 pTempEvent = pMemoryNode->pFirstEvent;
249 while (NULL != pTempEvent->pNext) {
250 pTempEvent = pTempEvent->pNext;
253 /* if pointer has already been freed, create a new event for double deletion */
254 if (NOT_DELETED != pTempEvent->DeallocLine) {
255 pTempEvent = add_memory_event(pMemoryNode, pTempEvent->Size, NOT_ALLOCATED, "");
259 pTempEvent->DeallocLine = uiDeletionLine;
260 strncpy(pTempEvent->DeallocFilename, szDeletionFileName, MAX_FILE_NAME_LENGTH-1);
261 pTempEvent->DeallocFilename[MAX_FILE_NAME_LENGTH-1] = (char)0;
264 /*------------------------------------------------------------------------*/
265 /** Custom calloc function with memory event recording. */
266 void* CU_calloc(size_t nmemb, size_t size, unsigned int uiLine, const char* szFileName)
270 #ifdef CUNIT_BUILD_TESTS
271 if (CU_FALSE == f_bTestCunitMallocActive) {
276 pVoid = calloc(nmemb, size);
278 allocate_memory(nmemb * size, pVoid, uiLine, szFileName);
284 /*------------------------------------------------------------------------*/
285 /** Custom malloc function with memory event recording. */
286 void* CU_malloc(size_t size, unsigned int uiLine, const char* szFileName)
290 #ifdef CUNIT_BUILD_TESTS
291 if (CU_FALSE == f_bTestCunitMallocActive) {
296 pVoid = malloc(size);
298 allocate_memory(size, pVoid, uiLine, szFileName);
304 /*------------------------------------------------------------------------*/
305 /** Custom free function with memory event recording. */
306 void CU_free(void *ptr, unsigned int uiLine, const char* szFileName)
308 deallocate_memory(ptr, uiLine, szFileName);
312 /*------------------------------------------------------------------------*/
313 /** Custom realloc function with memory event recording. */
314 void* CU_realloc(void *ptr, size_t size, unsigned int uiLine, const char* szFileName)
318 deallocate_memory(ptr, uiLine, szFileName);
320 #ifdef CUNIT_BUILD_TESTS
321 if (CU_FALSE == f_bTestCunitMallocActive) {
327 pVoid = realloc(ptr, size);
330 allocate_memory(size, pVoid, uiLine, szFileName);
336 /*------------------------------------------------------------------------*/
337 /** Print a report of memory events to file. */
338 void CU_dump_memory_usage(const char* szFilename)
340 char* szDumpFileName = (char*)f_szDefaultDumpFileName;
342 unsigned int nInvalid;
343 PMEMORY_NODE pTempNode = NULL;
344 PMEMORY_EVENT pTempEvent = NULL;
348 /* use the specified file name, if supplied) */
349 if ((NULL != szFilename) && strlen(szFilename) > 0) {
350 szDumpFileName = (char*)szFilename;
353 if (NULL == (pFile = fopen(szDumpFileName, "w"))) {
354 fprintf(stderr, "Failed to open file \"%s\" : %s", szDumpFileName, strerror(errno));
358 setvbuf(pFile, NULL, _IONBF, 0);
360 fprintf(pFile, "<\?xml version=\"1.0\" \?>");
361 fprintf(pFile, "\n<\?xml-stylesheet type=\"text/xsl\" href=\"Memory-Dump.xsl\" \?>");
362 fprintf(pFile, "\n<!DOCTYPE MEMORY_DUMP_REPORT SYSTEM \"Memory-Dump.dtd\">");
363 fprintf(pFile, "\n<MEMORY_DUMP_REPORT>");
364 fprintf(pFile, "\n <MD_HEADER/>");
365 fprintf(pFile, "\n <MD_RUN_LISTING>");
369 pTempNode = f_pMemoryTrackerHead;
370 while (NULL != pTempNode) {
371 fprintf(pFile, "\n <MD_RUN_RECORD>");
372 fprintf(pFile, "\n <MD_POINTER> %p </MD_POINTER>", pTempNode->pLocation);
373 fprintf(pFile, "\n <MD_EVENT_COUNT> %u </MD_EVENT_COUNT>", pTempNode->EventCount);
375 pTempEvent = pTempNode->pFirstEvent;
376 while (NULL != pTempEvent) {
377 fprintf(pFile, "\n <MD_EVENT_RECORD>");
378 fprintf(pFile, "\n <MD_SIZE> %u </MD_SIZE>", pTempEvent->Size);
379 fprintf(pFile, "\n <MD_ALLOC_FILE> %s </MD_ALLOC_FILE>", pTempEvent->AllocFilename);
380 fprintf(pFile, "\n <MD_ALLOC_LINE> %u </MD_ALLOC_LINE>", pTempEvent->AllocLine);
381 fprintf(pFile, "\n <MD_DEALLOC_FILE> %s </MD_DEALLOC_FILE>", pTempEvent->DeallocFilename);
382 fprintf(pFile, "\n <MD_DEALLOC_LINE> %u </MD_DEALLOC_LINE>", pTempEvent->DeallocLine);
383 fprintf(pFile, "\n </MD_EVENT_RECORD>");
385 if ((0 != pTempEvent->AllocLine) && (0 != pTempEvent->DeallocLine)) {
392 pTempEvent = pTempEvent->pNext;
395 fprintf(pFile, "\n </MD_RUN_RECORD>");
396 pTempNode = pTempNode->pNext;
399 fprintf(pFile, "\n </MD_RUN_LISTING>");
401 fprintf(pFile, "\n <MD_SUMMARY>");
402 fprintf(pFile, "\n <MD_SUMMARY_VALID_RECORDS> %u </MD_SUMMARY_VALID_RECORDS>", nValid);
403 fprintf(pFile, "\n <MD_SUMMARY_INVALID_RECORDS> %u </MD_SUMMARY_INVALID_RECORDS>", nInvalid);
404 fprintf(pFile, "\n <MD_SUMMARY_TOTAL_RECORDS> %u </MD_SUMMARY_TOTAL_RECORDS>", nValid + nInvalid);
405 fprintf(pFile, "\n </MD_SUMMARY>");
408 fprintf(pFile, "\n <MD_FOOTER> Memory Trace for CUnit Run at %s </MD_FOOTER>", ctime(&tTime));
409 fprintf(pFile, "</MEMORY_DUMP_REPORT>");
414 #endif /* MEMTRACE */
418 #ifdef CUNIT_BUILD_TESTS
419 #include "test_cunit.h"
421 /** Deactivate CUnit memory allocation
422 * After calling this function, all Cunit memory
423 * allocation routines will fail and return NULL.
425 void test_cunit_deactivate_malloc(void)
427 f_bTestCunitMallocActive = CU_FALSE;
430 /** Activate CUnit memory allocation
431 * After calling this function, all Cunit memory
432 * allocation routines will behave normally (allocating
433 * memory if it is available).
435 void test_cunit_activate_malloc(void)
437 f_bTestCunitMallocActive = CU_TRUE;
440 /** Retrieve the number of memory events recorded for a given pointer. */
441 unsigned int test_cunit_get_n_memevents(void* pLocation)
443 PMEMORY_NODE pNode = find_memory_node(pLocation);
444 return (pNode) ? pNode->EventCount : 0;
447 /** Retrieve the number of memory allocations recorded for a given pointer. */
448 unsigned int test_cunit_get_n_allocations(void* pLocation)
450 PMEMORY_NODE pNode = find_memory_node(pLocation);
451 PMEMORY_EVENT pEvent = NULL;
455 pEvent = pNode->pFirstEvent;
456 while (NULL != pEvent) {
457 if (pEvent->AllocLine != NOT_ALLOCATED)
459 pEvent = pEvent->pNext;
466 /** Retrieve the number of memory deallocations recorded for a given pointer. */
467 unsigned int test_cunit_get_n_deallocations(void* pLocation)
469 PMEMORY_NODE pNode = find_memory_node(pLocation);
470 PMEMORY_EVENT pEvent = NULL;
474 pEvent = pNode->pFirstEvent;
475 while (NULL != pEvent) {
476 if (pEvent->DeallocLine != NOT_DELETED)
478 pEvent = pEvent->pNext;
485 void test_CU_calloc(void)
488 void* ptr2 = calloc(2, sizeof(int));
489 unsigned int n2 = test_cunit_get_n_memevents(ptr2);
491 /* test allocation failure */
492 test_cunit_deactivate_malloc();
493 ptr1 = CU_CALLOC(2, sizeof(int));
495 TEST(test_cunit_get_n_allocations(ptr1) == test_cunit_get_n_deallocations(ptr1));
496 TEST(test_cunit_get_n_allocations(ptr2) == test_cunit_get_n_deallocations(ptr2));
497 test_cunit_activate_malloc();
499 /* normal allocation */
500 ptr1 = CU_CALLOC(2, sizeof(int));
501 TEST_FATAL(NULL != ptr1);
502 TEST(test_cunit_get_n_allocations(ptr1) != test_cunit_get_n_deallocations(ptr1));
503 TEST(test_cunit_get_n_allocations(ptr2) == test_cunit_get_n_deallocations(ptr2));
506 TEST(test_cunit_get_n_allocations(ptr1) == test_cunit_get_n_deallocations(ptr1));
507 TEST(test_cunit_get_n_allocations(ptr2) == test_cunit_get_n_deallocations(ptr2));
508 TEST(n2 == test_cunit_get_n_memevents(ptr2));
513 void test_CU_malloc(void)
516 void* ptr2 = malloc(sizeof(int));
517 unsigned int n2 = test_cunit_get_n_memevents(ptr2);
519 /* test allocation failure */
520 test_cunit_deactivate_malloc();
521 ptr1 = CU_MALLOC(sizeof(int));
523 TEST(test_cunit_get_n_allocations(ptr1) == test_cunit_get_n_deallocations(ptr1));
524 TEST(test_cunit_get_n_allocations(ptr2) == test_cunit_get_n_deallocations(ptr2));
525 test_cunit_activate_malloc();
527 /* normal allocation */
528 ptr1 = CU_MALLOC(sizeof(int));
529 TEST_FATAL(NULL != ptr1);
530 TEST(test_cunit_get_n_allocations(ptr1) != test_cunit_get_n_deallocations(ptr1));
531 TEST(test_cunit_get_n_allocations(ptr2) == test_cunit_get_n_deallocations(ptr2));
534 TEST(test_cunit_get_n_allocations(ptr1) == test_cunit_get_n_deallocations(ptr1));
535 TEST(test_cunit_get_n_allocations(ptr2) == test_cunit_get_n_deallocations(ptr2));
536 TEST(n2 == test_cunit_get_n_memevents(ptr2));
541 void test_CU_free(void)
543 /* covered by other test functions */
546 void test_CU_realloc(void)
548 void* ptr1 = CU_MALLOC(sizeof(int));
549 void* ptr2 = malloc(sizeof(int));
552 unsigned int n2 = test_cunit_get_n_memevents(ptr2);
554 /* test allocation failure */
555 test_cunit_deactivate_malloc();
556 ptr1 = CU_REALLOC(ptr1, sizeof(long int));
558 TEST(test_cunit_get_n_allocations(ptr1) == test_cunit_get_n_deallocations(ptr1));
559 TEST(test_cunit_get_n_allocations(ptr2) == test_cunit_get_n_deallocations(ptr2));
560 test_cunit_activate_malloc();
562 /* normal allocation */
563 ptr3 = CU_MALLOC(sizeof(int));
564 TEST_FATAL(NULL != ptr3);
565 TEST(test_cunit_get_n_allocations(ptr1) == test_cunit_get_n_deallocations(ptr1));
566 TEST(test_cunit_get_n_allocations(ptr2) == test_cunit_get_n_deallocations(ptr2));
567 TEST(test_cunit_get_n_allocations(ptr3) != test_cunit_get_n_deallocations(ptr3));
569 ptr4 = CU_REALLOC(ptr3, sizeof(long int));
570 TEST_FATAL(NULL != ptr4);
571 TEST(test_cunit_get_n_allocations(ptr1) == test_cunit_get_n_deallocations(ptr1));
572 TEST(test_cunit_get_n_allocations(ptr2) == test_cunit_get_n_deallocations(ptr2));
574 TEST(test_cunit_get_n_allocations(ptr3) == test_cunit_get_n_deallocations(ptr3));
575 TEST(test_cunit_get_n_allocations(ptr4) != test_cunit_get_n_deallocations(ptr4));
578 TEST(test_cunit_get_n_allocations(ptr1) == test_cunit_get_n_deallocations(ptr1));
579 TEST(test_cunit_get_n_allocations(ptr2) == test_cunit_get_n_deallocations(ptr2));
580 TEST(test_cunit_get_n_allocations(ptr3) == test_cunit_get_n_deallocations(ptr3));
581 TEST(test_cunit_get_n_allocations(ptr4) == test_cunit_get_n_deallocations(ptr4));
582 TEST(n2 == test_cunit_get_n_memevents(ptr2));
587 /** The main internal testing function for MyMem.c. */
588 void test_cunit_MyMem(void)
590 test_cunit_start_tests("MyMem.c");
597 test_cunit_end_tests();
600 #endif /* CUNIT_BUILD_TESTS */