xref: /freebsd/contrib/expat/tests/memcheck.c (revision 4543ef516683042d46f3bd3bb8a4f3f746e00499)
10a48773fSEric van Gyzen /* Debug allocators for the Expat test suite
20a48773fSEric van Gyzen                             __  __            _
30a48773fSEric van Gyzen                          ___\ \/ /_ __   __ _| |_
40a48773fSEric van Gyzen                         / _ \\  /| '_ \ / _` | __|
50a48773fSEric van Gyzen                        |  __//  \| |_) | (_| | |_
60a48773fSEric van Gyzen                         \___/_/\_\ .__/ \__,_|\__|
70a48773fSEric van Gyzen                                  |_| XML parser
80a48773fSEric van Gyzen 
9cc68614dSXin LI    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
10*4543ef51SXin LI    Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
11*4543ef51SXin LI    Copyright (c) 2022      Sean McBride <sean@rogue-research.com>
120a48773fSEric van Gyzen    Licensed under the MIT license:
130a48773fSEric van Gyzen 
140a48773fSEric van Gyzen    Permission is  hereby granted,  free of charge,  to any  person obtaining
150a48773fSEric van Gyzen    a  copy  of  this  software   and  associated  documentation  files  (the
160a48773fSEric van Gyzen    "Software"),  to  deal in  the  Software  without restriction,  including
170a48773fSEric van Gyzen    without  limitation the  rights  to use,  copy,  modify, merge,  publish,
180a48773fSEric van Gyzen    distribute, sublicense, and/or sell copies of the Software, and to permit
190a48773fSEric van Gyzen    persons  to whom  the Software  is  furnished to  do so,  subject to  the
200a48773fSEric van Gyzen    following conditions:
210a48773fSEric van Gyzen 
220a48773fSEric van Gyzen    The above copyright  notice and this permission notice  shall be included
230a48773fSEric van Gyzen    in all copies or substantial portions of the Software.
240a48773fSEric van Gyzen 
250a48773fSEric van Gyzen    THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
260a48773fSEric van Gyzen    EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
270a48773fSEric van Gyzen    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
280a48773fSEric van Gyzen    NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
290a48773fSEric van Gyzen    DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
300a48773fSEric van Gyzen    OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
310a48773fSEric van Gyzen    USE OR OTHER DEALINGS IN THE SOFTWARE.
320a48773fSEric van Gyzen */
330a48773fSEric van Gyzen 
340a48773fSEric van Gyzen #include <stdio.h>
350a48773fSEric van Gyzen #include <stdlib.h>
360a48773fSEric van Gyzen #include "memcheck.h"
370a48773fSEric van Gyzen 
380a48773fSEric van Gyzen /* Structures to keep track of what has been allocated.  Speed isn't a
390a48773fSEric van Gyzen  * big issue for the tests this is required for, so we will use a
400a48773fSEric van Gyzen  * doubly-linked list to make deletion easier.
410a48773fSEric van Gyzen  */
420a48773fSEric van Gyzen 
430a48773fSEric van Gyzen typedef struct allocation_entry {
440a48773fSEric van Gyzen   struct allocation_entry *next;
450a48773fSEric van Gyzen   struct allocation_entry *prev;
460a48773fSEric van Gyzen   void *allocation;
470a48773fSEric van Gyzen   size_t num_bytes;
480a48773fSEric van Gyzen } AllocationEntry;
490a48773fSEric van Gyzen 
500a48773fSEric van Gyzen static AllocationEntry *alloc_head = NULL;
510a48773fSEric van Gyzen static AllocationEntry *alloc_tail = NULL;
520a48773fSEric van Gyzen 
53*4543ef51SXin LI static AllocationEntry *find_allocation(const void *ptr);
540a48773fSEric van Gyzen 
550a48773fSEric van Gyzen /* Allocate some memory and keep track of it. */
560a48773fSEric van Gyzen void *
tracking_malloc(size_t size)576b2c1e49SXin LI tracking_malloc(size_t size) {
58*4543ef51SXin LI   AllocationEntry *const entry
59*4543ef51SXin LI       = (AllocationEntry *)malloc(sizeof(AllocationEntry));
600a48773fSEric van Gyzen 
610a48773fSEric van Gyzen   if (entry == NULL) {
620a48773fSEric van Gyzen     printf("Allocator failure\n");
630a48773fSEric van Gyzen     return NULL;
640a48773fSEric van Gyzen   }
650a48773fSEric van Gyzen   entry->num_bytes = size;
660a48773fSEric van Gyzen   entry->allocation = malloc(size);
670a48773fSEric van Gyzen   if (entry->allocation == NULL) {
680a48773fSEric van Gyzen     free(entry);
690a48773fSEric van Gyzen     return NULL;
700a48773fSEric van Gyzen   }
710a48773fSEric van Gyzen   entry->next = NULL;
720a48773fSEric van Gyzen 
730a48773fSEric van Gyzen   /* Add to the list of allocations */
740a48773fSEric van Gyzen   if (alloc_head == NULL) {
750a48773fSEric van Gyzen     entry->prev = NULL;
760a48773fSEric van Gyzen     alloc_head = alloc_tail = entry;
770a48773fSEric van Gyzen   } else {
780a48773fSEric van Gyzen     entry->prev = alloc_tail;
790a48773fSEric van Gyzen     alloc_tail->next = entry;
800a48773fSEric van Gyzen     alloc_tail = entry;
810a48773fSEric van Gyzen   }
820a48773fSEric van Gyzen 
830a48773fSEric van Gyzen   return entry->allocation;
840a48773fSEric van Gyzen }
850a48773fSEric van Gyzen 
860a48773fSEric van Gyzen static AllocationEntry *
find_allocation(const void * ptr)87*4543ef51SXin LI find_allocation(const void *ptr) {
880a48773fSEric van Gyzen   AllocationEntry *entry;
890a48773fSEric van Gyzen 
900a48773fSEric van Gyzen   for (entry = alloc_head; entry != NULL; entry = entry->next) {
910a48773fSEric van Gyzen     if (entry->allocation == ptr) {
920a48773fSEric van Gyzen       return entry;
930a48773fSEric van Gyzen     }
940a48773fSEric van Gyzen   }
950a48773fSEric van Gyzen   return NULL;
960a48773fSEric van Gyzen }
970a48773fSEric van Gyzen 
980a48773fSEric van Gyzen /* Free some memory and remove the tracking for it */
990a48773fSEric van Gyzen void
tracking_free(void * ptr)1006b2c1e49SXin LI tracking_free(void *ptr) {
1010a48773fSEric van Gyzen   AllocationEntry *entry;
1020a48773fSEric van Gyzen 
1030a48773fSEric van Gyzen   if (ptr == NULL) {
1040a48773fSEric van Gyzen     /* There won't be an entry for this */
1050a48773fSEric van Gyzen     return;
1060a48773fSEric van Gyzen   }
1070a48773fSEric van Gyzen 
1080a48773fSEric van Gyzen   entry = find_allocation(ptr);
1090a48773fSEric van Gyzen   if (entry != NULL) {
1100a48773fSEric van Gyzen     /* This is the relevant allocation.  Unlink it */
1110a48773fSEric van Gyzen     if (entry->prev != NULL)
1120a48773fSEric van Gyzen       entry->prev->next = entry->next;
1130a48773fSEric van Gyzen     else
1140a48773fSEric van Gyzen       alloc_head = entry->next;
1150a48773fSEric van Gyzen     if (entry->next != NULL)
1160a48773fSEric van Gyzen       entry->next->prev = entry->prev;
1170a48773fSEric van Gyzen     else
1180a48773fSEric van Gyzen       alloc_tail = entry->next;
1190a48773fSEric van Gyzen     free(entry);
1200a48773fSEric van Gyzen   } else {
1210a48773fSEric van Gyzen     printf("Attempting to free unallocated memory at %p\n", ptr);
1220a48773fSEric van Gyzen   }
1230a48773fSEric van Gyzen   free(ptr);
1240a48773fSEric van Gyzen }
1250a48773fSEric van Gyzen 
1260a48773fSEric van Gyzen /* Reallocate some memory and keep track of it */
1270a48773fSEric van Gyzen void *
tracking_realloc(void * ptr,size_t size)1286b2c1e49SXin LI tracking_realloc(void *ptr, size_t size) {
1290a48773fSEric van Gyzen   AllocationEntry *entry;
1300a48773fSEric van Gyzen 
1310a48773fSEric van Gyzen   if (ptr == NULL) {
1320a48773fSEric van Gyzen     /* By definition, this is equivalent to malloc(size) */
1330a48773fSEric van Gyzen     return tracking_malloc(size);
1340a48773fSEric van Gyzen   }
1350a48773fSEric van Gyzen   if (size == 0) {
1360a48773fSEric van Gyzen     /* By definition, this is equivalent to free(ptr) */
1370a48773fSEric van Gyzen     tracking_free(ptr);
1380a48773fSEric van Gyzen     return NULL;
1390a48773fSEric van Gyzen   }
1400a48773fSEric van Gyzen 
1410a48773fSEric van Gyzen   /* Find the allocation entry for this memory */
1420a48773fSEric van Gyzen   entry = find_allocation(ptr);
1430a48773fSEric van Gyzen   if (entry == NULL) {
1440a48773fSEric van Gyzen     printf("Attempting to realloc unallocated memory at %p\n", ptr);
145*4543ef51SXin LI     entry = (AllocationEntry *)malloc(sizeof(AllocationEntry));
1460a48773fSEric van Gyzen     if (entry == NULL) {
1470a48773fSEric van Gyzen       printf("Reallocator failure\n");
1480a48773fSEric van Gyzen       return NULL;
1490a48773fSEric van Gyzen     }
1500a48773fSEric van Gyzen     entry->allocation = realloc(ptr, size);
1510a48773fSEric van Gyzen     if (entry->allocation == NULL) {
1520a48773fSEric van Gyzen       free(entry);
1530a48773fSEric van Gyzen       return NULL;
1540a48773fSEric van Gyzen     }
1550a48773fSEric van Gyzen 
1560a48773fSEric van Gyzen     /* Add to the list of allocations */
1570a48773fSEric van Gyzen     entry->next = NULL;
1580a48773fSEric van Gyzen     if (alloc_head == NULL) {
1590a48773fSEric van Gyzen       entry->prev = NULL;
1600a48773fSEric van Gyzen       alloc_head = alloc_tail = entry;
1610a48773fSEric van Gyzen     } else {
1620a48773fSEric van Gyzen       entry->prev = alloc_tail;
1630a48773fSEric van Gyzen       alloc_tail->next = entry;
1640a48773fSEric van Gyzen       alloc_tail = entry;
1650a48773fSEric van Gyzen     }
1660a48773fSEric van Gyzen   } else {
167*4543ef51SXin LI     void *const reallocated = realloc(ptr, size);
168*4543ef51SXin LI     if (reallocated == NULL) {
1690a48773fSEric van Gyzen       return NULL;
1700a48773fSEric van Gyzen     }
171*4543ef51SXin LI     entry->allocation = reallocated;
1720a48773fSEric van Gyzen   }
1730a48773fSEric van Gyzen 
1740a48773fSEric van Gyzen   entry->num_bytes = size;
1750a48773fSEric van Gyzen   return entry->allocation;
1760a48773fSEric van Gyzen }
1770a48773fSEric van Gyzen 
1780a48773fSEric van Gyzen int
tracking_report(void)1796b2c1e49SXin LI tracking_report(void) {
1800a48773fSEric van Gyzen   AllocationEntry *entry;
1810a48773fSEric van Gyzen 
1820a48773fSEric van Gyzen   if (alloc_head == NULL)
1830a48773fSEric van Gyzen     return 1;
1840a48773fSEric van Gyzen 
1850a48773fSEric van Gyzen   /* Otherwise we have allocations that haven't been freed */
1866b2c1e49SXin LI   for (entry = alloc_head; entry != NULL; entry = entry->next) {
1876b2c1e49SXin LI     printf("Allocated %lu bytes at %p\n", (long unsigned)entry->num_bytes,
1886b2c1e49SXin LI            entry->allocation);
1890a48773fSEric van Gyzen   }
1900a48773fSEric van Gyzen   return 0;
1910a48773fSEric van Gyzen }
192