allocation_tracker.cc revision f947fdddf5de6bd30886688a2b399460a3d95ecc
13b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson/****************************************************************************** 23b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * 33b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * Copyright (C) 2014 Google, Inc. 43b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * 53b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * Licensed under the Apache License, Version 2.0 (the "License"); 63b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * you may not use this file except in compliance with the License. 73b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * You may obtain a copy of the License at: 83b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * 93b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * http://www.apache.org/licenses/LICENSE-2.0 103b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * 113b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * Unless required by applicable law or agreed to in writing, software 123b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * distributed under the License is distributed on an "AS IS" BASIS, 133b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 143b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * See the License for the specific language governing permissions and 153b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * limitations under the License. 163b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson * 173b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson ******************************************************************************/ 183b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 193b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include <assert.h> 203b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include <pthread.h> 213b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include <utils/Log.h> 223b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 233b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "allocation_tracker.h" 2453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson#include "allocator.h" 253b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "hash_functions.h" 263b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "hash_map.h" 273b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "osi.h" 283b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 293b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#define ALLOCATION_HASH_MAP_SIZE 1024 303b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 313b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsontypedef struct { 323b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson void *ptr; 333b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson size_t size; 343b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson bool freed; 35f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson bool is_canaried; 363b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson} allocation_t; 373b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 3853f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson// Hidden constructor for hash map for our use only. Everything else should use the 3953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson// normal interface. 4053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonhash_map_t *hash_map_new_internal( 4153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson size_t size, 4253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson hash_index_fn hash_fn, 4353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson key_free_fn key_fn, 4453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson data_free_fn, 4553f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson const allocator_t *zeroed_allocator); 4653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson 473b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context); 4853f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic void *untracked_calloc(size_t size); 4953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson 50f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic const char *canary = "tinybird"; 5153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic const allocator_t untracked_calloc_allocator = { 5253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson untracked_calloc, 5353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson free 5453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson}; 553b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 56f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic bool using_canaries; 57f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic size_t canary_size; 583b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic hash_map_t *allocations; 593b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic pthread_mutex_t lock; 603b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 61f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonvoid allocation_tracker_init(bool use_canaries) { 623b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson if (allocations) 633b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson return; 643b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 65f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson using_canaries = use_canaries; 66f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson canary_size = strlen(canary); 67f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson 683b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson pthread_mutex_init(&lock, NULL); 6953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson allocations = hash_map_new_internal( 7053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson ALLOCATION_HASH_MAP_SIZE, 7153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson hash_function_knuth, 7253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson NULL, 7353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson free, 7453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson &untracked_calloc_allocator); 753b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson} 763b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 77ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson// Test function only. Do not call in the normal course of operations. 78ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnsonvoid allocation_tracker_uninit() { 79ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson hash_map_free(allocations); 80ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson allocations = NULL; 81ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson} 82ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson 833b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonvoid allocation_tracker_reset(void) { 843b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson if (!allocations) 853b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson return; 863b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 873b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson hash_map_clear(allocations); 883b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson} 893b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 903b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonsize_t allocation_tracker_expect_no_allocations() { 913b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson if (!allocations) 923b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson return 0; 933b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 943b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson pthread_mutex_lock(&lock); 953b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 963b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson size_t unfreed_memory_size = 0; 973b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson hash_map_foreach(allocations, allocation_entry_freed_checker, &unfreed_memory_size); 983b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 993b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson pthread_mutex_unlock(&lock); 1003b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 1013b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson return unfreed_memory_size; 1023b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson} 1033b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 104f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonvoid *allocation_tracker_notify_alloc(void *ptr, size_t requested_size, bool add_canary) { 1053b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson if (!allocations || !ptr) 106f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson return ptr; 107f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson 108f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson char *return_ptr = (char *)ptr; 109f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson 110f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson bool using_canaries_here = using_canaries && add_canary; 111f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson if (using_canaries_here) 112f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson return_ptr += canary_size; 1133b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 1143b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson pthread_mutex_lock(&lock); 1153b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 116f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson allocation_t *allocation = (allocation_t *)hash_map_get(allocations, return_ptr); 1173b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson if (allocation) { 1183b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson assert(allocation->freed); // Must have been freed before 1193b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson } else { 1203b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson allocation = (allocation_t *)calloc(1, sizeof(allocation_t)); 121f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson hash_map_set(allocations, return_ptr, allocation); 1223b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson } 1233b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 124f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson allocation->is_canaried = using_canaries_here; 1253b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson allocation->freed = false; 126f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson allocation->size = requested_size; 127f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson allocation->ptr = return_ptr; 1283b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 1293b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson pthread_mutex_unlock(&lock); 130f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson 131f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson if (using_canaries_here) { 132f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson // Add the canary on both sides 133f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson memcpy(return_ptr - canary_size, canary, canary_size); 134f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson memcpy(return_ptr + requested_size, canary, canary_size); 135f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson } 136f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson 137f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson return return_ptr; 1383b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson} 1393b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 140f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonvoid *allocation_tracker_notify_free(void *ptr) { 1413b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson if (!allocations || !ptr) 142f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson return ptr; 1433b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 1443b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson pthread_mutex_lock(&lock); 1453b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 1463b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson allocation_t *allocation = (allocation_t *)hash_map_get(allocations, ptr); 1473b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson assert(allocation); // Must have been tracked before 1483b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson assert(!allocation->freed); // Must not be a double free 1493b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson allocation->freed = true; 1503b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 151f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson if (allocation->is_canaried) { 152f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson const char *beginning_canary = ((char *)ptr) - canary_size; 153f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson const char *end_canary = ((char *)ptr) + allocation->size; 154f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson 155f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson for (size_t i = 0; i < canary_size; i++) { 156f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson assert(beginning_canary[i] == canary[i]); 157f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson assert(end_canary[i] == canary[i]); 158f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson } 159f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson } 160f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson 1613b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson pthread_mutex_unlock(&lock); 162f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson 163f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson return allocation->is_canaried ? ((char *)ptr) - canary_size : ptr; 164f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson} 165f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson 166f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonsize_t allocation_tracker_resize_for_canary(size_t size) { 167f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson return (!allocations || !using_canaries) ? size : size + (2 * canary_size); 1683b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson} 1693b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 1703b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context) { 1713b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson allocation_t *allocation = (allocation_t *)entry->data; 1723b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson if (!allocation->freed) { 1733b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson *((size_t *)context) += allocation->size; // Report back the unfreed byte count 1743b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson ALOGE("%s found unfreed allocation. address: 0x%x size: %d bytes", __func__, (uintptr_t)allocation->ptr, allocation->size); 1753b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson } 1763b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson 1773b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson return true; 1783b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson} 17953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson 18053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic void *untracked_calloc(size_t size) { 18153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson return calloc(size, 1); 18253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson} 183