/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 (c) Stefan Profanter, fortiss GmbH */ /** * This memory manager allows to manually reduce the available RAM. * * It keeps track of malloc and free calls and counts the available RAM. * If the requested memory allocation results in hitting the limit, * it will return NULL and prevent the new allocation. */ #include "custom_memory_manager.h" #include #include #include #include #include pthread_mutex_t mutex; struct UA_mm_entry { size_t size; void* address; struct UA_mm_entry *next; struct UA_mm_entry *prev; }; unsigned long long totalMemorySize = 0; unsigned long long memoryLimit = ULONG_MAX; /* * Head and Tail of the double linked list */ struct UA_mm_entry *address_map_first = NULL; struct UA_mm_entry *address_map_last = NULL; void UA_memoryManager_setLimit(unsigned long long newLimit) { memoryLimit = newLimit; printf("MemoryManager: Setting memory limit to %lld\n", newLimit); } int UA_memoryManager_setLimitFromLast4Bytes(const uint8_t *data, size_t size) { if (size <4) return 0; // just cast the last 4 bytes to uint32 const uint32_t *newLimit = (const uint32_t*)(uintptr_t)&(data[size-4]); UA_memoryManager_setLimit(*newLimit); return 1; } /** * Add address entry to the linked list after it was allocated. * * @param size Size of the allocated memory block * @param addr Address of the allocated memory block * @return 1 on success, 0 if it failed */ static int addToMap(size_t size, void *addr) { struct UA_mm_entry *newEntry = (struct UA_mm_entry*)malloc(sizeof(struct UA_mm_entry)); if (!newEntry) { printf("MemoryManager: Could not allocate memory"); return 0; } newEntry->size = size; newEntry->address = addr; newEntry->next = NULL; newEntry->prev = address_map_last; pthread_mutex_lock(&mutex); if (address_map_last) address_map_last->next = newEntry; address_map_last = newEntry; totalMemorySize += size; if (address_map_first == NULL) { address_map_first = newEntry; } pthread_mutex_unlock(&mutex); //printf("Total size (malloc): %lld And new address: %p Entry %p\n", totalMemorySize, addr, (void*)newEntry); return 1; } /** * Remove entry from the list before the memory block is freed. * * @param addr Address of the memory block which is freed. * * @return 1 on success, 0 if the memory block was not found in the list. */ static int removeFromMap(void *addr) { if (addr == NULL) return 1; pthread_mutex_lock(&mutex); struct UA_mm_entry *e = address_map_last; while (e) { if (e->address == addr) { if (e == address_map_first) address_map_first = e->next; if (e == address_map_last) address_map_last = e->prev; if (e->prev) { e->prev->next = e->next; } if (e->next) { e->next->prev = e->prev; } totalMemorySize -= e->size; //printf("Total size (free): %lld after addr %p and deleting %p\n", totalMemorySize, addr, (void*)e); free(e); pthread_mutex_unlock(&mutex); return 1; } e = e->prev; } pthread_mutex_unlock(&mutex); printf("MemoryManager: Entry with address %p not found", addr); return 0; } void* UA_memoryManager_malloc(size_t size) { if (totalMemorySize + size > memoryLimit) return NULL; void *addr = malloc(size); if (!addr) return NULL; addToMap(size, addr); return addr; } void* UA_memoryManager_calloc(size_t num, size_t size) { if (totalMemorySize + (size * num) > memoryLimit) return NULL; void *addr = calloc(num, size); if (!addr) return NULL; addToMap(size*num, addr); return addr; } void* UA_memoryManager_realloc(void *ptr, size_t new_size) { removeFromMap(ptr); if (totalMemorySize + new_size > memoryLimit) return NULL; void *addr = realloc(ptr, new_size); if (!addr) return NULL; addToMap(new_size, addr); return addr; } void UA_memoryManager_free(void* ptr) { removeFromMap(ptr); free(ptr); }