Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 1 | /****************************************************************************** |
| 2 | * |
| 3 | * Copyright (C) 2014 Google, Inc. |
| 4 | * |
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | * you may not use this file except in compliance with the License. |
| 7 | * You may obtain a copy of the License at: |
| 8 | * |
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | * |
| 11 | * Unless required by applicable law or agreed to in writing, software |
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | * See the License for the specific language governing permissions and |
| 15 | * limitations under the License. |
| 16 | * |
| 17 | ******************************************************************************/ |
| 18 | |
Chris Manton | f802700 | 2015-03-12 09:22:48 -0700 | [diff] [blame] | 19 | #define LOG_TAG "bt_osi_allocation_tracker" |
Sharvil Nanavati | c0745da | 2014-11-13 01:04:19 -0800 | [diff] [blame] | 20 | |
Marie Janssen | 49a8670 | 2015-07-08 11:48:57 -0700 | [diff] [blame^] | 21 | #include "osi/include/allocation_tracker.h" |
| 22 | |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 23 | #include <assert.h> |
| 24 | #include <pthread.h> |
Etan Cohen | 3e59b5b | 2015-03-31 17:15:53 -0700 | [diff] [blame] | 25 | #include <stdlib.h> |
| 26 | #include <string.h> |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 27 | |
Sharvil Nanavati | 0f9b91e | 2015-03-12 15:42:50 -0700 | [diff] [blame] | 28 | #include "osi/include/allocator.h" |
| 29 | #include "osi/include/hash_functions.h" |
| 30 | #include "osi/include/hash_map.h" |
Sharvil Nanavati | 4480276 | 2014-12-23 23:08:58 -0800 | [diff] [blame] | 31 | #include "osi/include/log.h" |
Marie Janssen | 49a8670 | 2015-07-08 11:48:57 -0700 | [diff] [blame^] | 32 | #include "osi/include/osi.h" |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 33 | |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 34 | typedef struct { |
Zach Johnson | 4ed68b4 | 2014-08-29 17:08:44 -0700 | [diff] [blame] | 35 | uint8_t allocator_id; |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 36 | void *ptr; |
| 37 | size_t size; |
| 38 | bool freed; |
| 39 | } allocation_t; |
| 40 | |
Zach Johnson | 53f36a4 | 2014-08-26 17:21:44 -0700 | [diff] [blame] | 41 | // Hidden constructor for hash map for our use only. Everything else should use the |
| 42 | // normal interface. |
| 43 | hash_map_t *hash_map_new_internal( |
| 44 | size_t size, |
| 45 | hash_index_fn hash_fn, |
| 46 | key_free_fn key_fn, |
| 47 | data_free_fn, |
Zach Johnson | aa3a011 | 2014-11-05 14:25:49 -0800 | [diff] [blame] | 48 | key_equality_fn equality_fn, |
Zach Johnson | 53f36a4 | 2014-08-26 17:21:44 -0700 | [diff] [blame] | 49 | const allocator_t *zeroed_allocator); |
| 50 | |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 51 | static bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context); |
Zach Johnson | 53f36a4 | 2014-08-26 17:21:44 -0700 | [diff] [blame] | 52 | static void *untracked_calloc(size_t size); |
| 53 | |
Zach Johnson | c0e2f99 | 2014-09-08 11:39:49 -0700 | [diff] [blame] | 54 | static const size_t allocation_hash_map_size = 1024; |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 55 | static const char *canary = "tinybird"; |
Zach Johnson | 53f36a4 | 2014-08-26 17:21:44 -0700 | [diff] [blame] | 56 | static const allocator_t untracked_calloc_allocator = { |
| 57 | untracked_calloc, |
| 58 | free |
| 59 | }; |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 60 | |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 61 | static size_t canary_size; |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 62 | static hash_map_t *allocations; |
| 63 | static pthread_mutex_t lock; |
| 64 | |
Sharvil Nanavati | c0745da | 2014-11-13 01:04:19 -0800 | [diff] [blame] | 65 | void allocation_tracker_init(void) { |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 66 | if (allocations) |
| 67 | return; |
| 68 | |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 69 | canary_size = strlen(canary); |
| 70 | |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 71 | pthread_mutex_init(&lock, NULL); |
Pavlin Radoslavov | 5febd64 | 2015-06-01 11:42:39 -0700 | [diff] [blame] | 72 | |
| 73 | pthread_mutex_lock(&lock); |
Zach Johnson | 53f36a4 | 2014-08-26 17:21:44 -0700 | [diff] [blame] | 74 | allocations = hash_map_new_internal( |
Zach Johnson | c0e2f99 | 2014-09-08 11:39:49 -0700 | [diff] [blame] | 75 | allocation_hash_map_size, |
| 76 | hash_function_pointer, |
Zach Johnson | 53f36a4 | 2014-08-26 17:21:44 -0700 | [diff] [blame] | 77 | NULL, |
| 78 | free, |
Zach Johnson | aa3a011 | 2014-11-05 14:25:49 -0800 | [diff] [blame] | 79 | NULL, |
Zach Johnson | 53f36a4 | 2014-08-26 17:21:44 -0700 | [diff] [blame] | 80 | &untracked_calloc_allocator); |
Pavlin Radoslavov | 5febd64 | 2015-06-01 11:42:39 -0700 | [diff] [blame] | 81 | pthread_mutex_unlock(&lock); |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 82 | } |
| 83 | |
Zach Johnson | ee2aa45 | 2014-08-26 20:16:03 -0700 | [diff] [blame] | 84 | // Test function only. Do not call in the normal course of operations. |
Sharvil Nanavati | c0745da | 2014-11-13 01:04:19 -0800 | [diff] [blame] | 85 | void allocation_tracker_uninit(void) { |
Pavlin Radoslavov | 5febd64 | 2015-06-01 11:42:39 -0700 | [diff] [blame] | 86 | if (!allocations) |
| 87 | return; |
| 88 | |
| 89 | pthread_mutex_lock(&lock); |
Zach Johnson | ee2aa45 | 2014-08-26 20:16:03 -0700 | [diff] [blame] | 90 | hash_map_free(allocations); |
| 91 | allocations = NULL; |
Pavlin Radoslavov | 5febd64 | 2015-06-01 11:42:39 -0700 | [diff] [blame] | 92 | pthread_mutex_unlock(&lock); |
Zach Johnson | ee2aa45 | 2014-08-26 20:16:03 -0700 | [diff] [blame] | 93 | } |
| 94 | |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 95 | void allocation_tracker_reset(void) { |
| 96 | if (!allocations) |
| 97 | return; |
| 98 | |
Pavlin Radoslavov | 5febd64 | 2015-06-01 11:42:39 -0700 | [diff] [blame] | 99 | pthread_mutex_lock(&lock); |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 100 | hash_map_clear(allocations); |
Pavlin Radoslavov | 5febd64 | 2015-06-01 11:42:39 -0700 | [diff] [blame] | 101 | pthread_mutex_unlock(&lock); |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 102 | } |
| 103 | |
Sharvil Nanavati | c0745da | 2014-11-13 01:04:19 -0800 | [diff] [blame] | 104 | size_t allocation_tracker_expect_no_allocations(void) { |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 105 | if (!allocations) |
| 106 | return 0; |
| 107 | |
| 108 | pthread_mutex_lock(&lock); |
| 109 | |
| 110 | size_t unfreed_memory_size = 0; |
| 111 | hash_map_foreach(allocations, allocation_entry_freed_checker, &unfreed_memory_size); |
| 112 | |
| 113 | pthread_mutex_unlock(&lock); |
| 114 | |
| 115 | return unfreed_memory_size; |
| 116 | } |
| 117 | |
Sharvil Nanavati | c0745da | 2014-11-13 01:04:19 -0800 | [diff] [blame] | 118 | void *allocation_tracker_notify_alloc(uint8_t allocator_id, void *ptr, size_t requested_size) { |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 119 | if (!allocations || !ptr) |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 120 | return ptr; |
| 121 | |
| 122 | char *return_ptr = (char *)ptr; |
| 123 | |
Sharvil Nanavati | c0745da | 2014-11-13 01:04:19 -0800 | [diff] [blame] | 124 | return_ptr += canary_size; |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 125 | |
| 126 | pthread_mutex_lock(&lock); |
| 127 | |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 128 | allocation_t *allocation = (allocation_t *)hash_map_get(allocations, return_ptr); |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 129 | if (allocation) { |
| 130 | assert(allocation->freed); // Must have been freed before |
| 131 | } else { |
| 132 | allocation = (allocation_t *)calloc(1, sizeof(allocation_t)); |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 133 | hash_map_set(allocations, return_ptr, allocation); |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 134 | } |
| 135 | |
Zach Johnson | 4ed68b4 | 2014-08-29 17:08:44 -0700 | [diff] [blame] | 136 | allocation->allocator_id = allocator_id; |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 137 | allocation->freed = false; |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 138 | allocation->size = requested_size; |
| 139 | allocation->ptr = return_ptr; |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 140 | |
| 141 | pthread_mutex_unlock(&lock); |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 142 | |
Sharvil Nanavati | c0745da | 2014-11-13 01:04:19 -0800 | [diff] [blame] | 143 | // Add the canary on both sides |
| 144 | memcpy(return_ptr - canary_size, canary, canary_size); |
| 145 | memcpy(return_ptr + requested_size, canary, canary_size); |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 146 | |
| 147 | return return_ptr; |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 148 | } |
| 149 | |
Zach Johnson | 4ed68b4 | 2014-08-29 17:08:44 -0700 | [diff] [blame] | 150 | void *allocation_tracker_notify_free(uint8_t allocator_id, void *ptr) { |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 151 | if (!allocations || !ptr) |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 152 | return ptr; |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 153 | |
| 154 | pthread_mutex_lock(&lock); |
| 155 | |
| 156 | allocation_t *allocation = (allocation_t *)hash_map_get(allocations, ptr); |
Zach Johnson | 4ed68b4 | 2014-08-29 17:08:44 -0700 | [diff] [blame] | 157 | assert(allocation); // Must have been tracked before |
| 158 | assert(!allocation->freed); // Must not be a double free |
| 159 | assert(allocation->allocator_id == allocator_id); // Must be from the same allocator |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 160 | allocation->freed = true; |
| 161 | |
Sharvil Nanavati | c0745da | 2014-11-13 01:04:19 -0800 | [diff] [blame] | 162 | const char *beginning_canary = ((char *)ptr) - canary_size; |
| 163 | const char *end_canary = ((char *)ptr) + allocation->size; |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 164 | |
Sharvil Nanavati | c0745da | 2014-11-13 01:04:19 -0800 | [diff] [blame] | 165 | for (size_t i = 0; i < canary_size; i++) { |
| 166 | assert(beginning_canary[i] == canary[i]); |
| 167 | assert(end_canary[i] == canary[i]); |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 168 | } |
| 169 | |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 170 | pthread_mutex_unlock(&lock); |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 171 | |
Sharvil Nanavati | c0745da | 2014-11-13 01:04:19 -0800 | [diff] [blame] | 172 | return ((char *)ptr) - canary_size; |
Zach Johnson | f947fdd | 2014-08-28 13:30:17 -0700 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | size_t allocation_tracker_resize_for_canary(size_t size) { |
Sharvil Nanavati | c0745da | 2014-11-13 01:04:19 -0800 | [diff] [blame] | 176 | return (!allocations) ? size : size + (2 * canary_size); |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 177 | } |
| 178 | |
| 179 | static bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context) { |
| 180 | allocation_t *allocation = (allocation_t *)entry->data; |
| 181 | if (!allocation->freed) { |
| 182 | *((size_t *)context) += allocation->size; // Report back the unfreed byte count |
Marie Janssen | db55458 | 2015-06-26 14:53:46 -0700 | [diff] [blame] | 183 | LOG_ERROR(LOG_TAG, "%s found unfreed allocation. address: 0x%zx size: %zd bytes", __func__, (uintptr_t)allocation->ptr, allocation->size); |
Zach Johnson | 3b72a14 | 2014-08-25 16:44:56 -0700 | [diff] [blame] | 184 | } |
| 185 | |
| 186 | return true; |
| 187 | } |
Zach Johnson | 53f36a4 | 2014-08-26 17:21:44 -0700 | [diff] [blame] | 188 | |
| 189 | static void *untracked_calloc(size_t size) { |
| 190 | return calloc(size, 1); |
| 191 | } |