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