blob: bb0b219c04aa2557c0c2932adc0658c75479e37c [file] [log] [blame]
Zach Johnson3b72a142014-08-25 16:44:56 -07001/******************************************************************************
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 Mantonf8027002015-03-12 09:22:48 -070019#define LOG_TAG "bt_osi_allocation_tracker"
Sharvil Nanavatic0745da2014-11-13 01:04:19 -080020
Marie Janssen49a86702015-07-08 11:48:57 -070021#include "osi/include/allocation_tracker.h"
22
Zach Johnson3b72a142014-08-25 16:44:56 -070023#include <assert.h>
24#include <pthread.h>
Etan Cohen3e59b5b2015-03-31 17:15:53 -070025#include <stdlib.h>
26#include <string.h>
Zach Johnson3b72a142014-08-25 16:44:56 -070027
Sharvil Nanavati0f9b91e2015-03-12 15:42:50 -070028#include "osi/include/allocator.h"
29#include "osi/include/hash_functions.h"
30#include "osi/include/hash_map.h"
Sharvil Nanavati44802762014-12-23 23:08:58 -080031#include "osi/include/log.h"
Marie Janssen49a86702015-07-08 11:48:57 -070032#include "osi/include/osi.h"
Zach Johnson3b72a142014-08-25 16:44:56 -070033
Zach Johnson3b72a142014-08-25 16:44:56 -070034typedef struct {
Zach Johnson4ed68b42014-08-29 17:08:44 -070035 uint8_t allocator_id;
Zach Johnson3b72a142014-08-25 16:44:56 -070036 void *ptr;
37 size_t size;
38 bool freed;
39} allocation_t;
40
Zach Johnson53f36a42014-08-26 17:21:44 -070041// Hidden constructor for hash map for our use only. Everything else should use the
42// normal interface.
43hash_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 Johnsonaa3a0112014-11-05 14:25:49 -080048 key_equality_fn equality_fn,
Zach Johnson53f36a42014-08-26 17:21:44 -070049 const allocator_t *zeroed_allocator);
50
Zach Johnson3b72a142014-08-25 16:44:56 -070051static bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context);
Zach Johnson53f36a42014-08-26 17:21:44 -070052static void *untracked_calloc(size_t size);
53
Zach Johnsonc0e2f992014-09-08 11:39:49 -070054static const size_t allocation_hash_map_size = 1024;
Zach Johnsonf947fdd2014-08-28 13:30:17 -070055static const char *canary = "tinybird";
Zach Johnson53f36a42014-08-26 17:21:44 -070056static const allocator_t untracked_calloc_allocator = {
57 untracked_calloc,
58 free
59};
Zach Johnson3b72a142014-08-25 16:44:56 -070060
Zach Johnsonf947fdd2014-08-28 13:30:17 -070061static size_t canary_size;
Zach Johnson3b72a142014-08-25 16:44:56 -070062static hash_map_t *allocations;
63static pthread_mutex_t lock;
64
Sharvil Nanavatic0745da2014-11-13 01:04:19 -080065void allocation_tracker_init(void) {
Zach Johnson3b72a142014-08-25 16:44:56 -070066 if (allocations)
67 return;
68
Zach Johnsonf947fdd2014-08-28 13:30:17 -070069 canary_size = strlen(canary);
70
Zach Johnson3b72a142014-08-25 16:44:56 -070071 pthread_mutex_init(&lock, NULL);
Pavlin Radoslavov5febd642015-06-01 11:42:39 -070072
73 pthread_mutex_lock(&lock);
Zach Johnson53f36a42014-08-26 17:21:44 -070074 allocations = hash_map_new_internal(
Zach Johnsonc0e2f992014-09-08 11:39:49 -070075 allocation_hash_map_size,
76 hash_function_pointer,
Zach Johnson53f36a42014-08-26 17:21:44 -070077 NULL,
78 free,
Zach Johnsonaa3a0112014-11-05 14:25:49 -080079 NULL,
Zach Johnson53f36a42014-08-26 17:21:44 -070080 &untracked_calloc_allocator);
Pavlin Radoslavov5febd642015-06-01 11:42:39 -070081 pthread_mutex_unlock(&lock);
Zach Johnson3b72a142014-08-25 16:44:56 -070082}
83
Zach Johnsonee2aa452014-08-26 20:16:03 -070084// Test function only. Do not call in the normal course of operations.
Sharvil Nanavatic0745da2014-11-13 01:04:19 -080085void allocation_tracker_uninit(void) {
Pavlin Radoslavov5febd642015-06-01 11:42:39 -070086 if (!allocations)
87 return;
88
89 pthread_mutex_lock(&lock);
Zach Johnsonee2aa452014-08-26 20:16:03 -070090 hash_map_free(allocations);
91 allocations = NULL;
Pavlin Radoslavov5febd642015-06-01 11:42:39 -070092 pthread_mutex_unlock(&lock);
Zach Johnsonee2aa452014-08-26 20:16:03 -070093}
94
Zach Johnson3b72a142014-08-25 16:44:56 -070095void allocation_tracker_reset(void) {
96 if (!allocations)
97 return;
98
Pavlin Radoslavov5febd642015-06-01 11:42:39 -070099 pthread_mutex_lock(&lock);
Zach Johnson3b72a142014-08-25 16:44:56 -0700100 hash_map_clear(allocations);
Pavlin Radoslavov5febd642015-06-01 11:42:39 -0700101 pthread_mutex_unlock(&lock);
Zach Johnson3b72a142014-08-25 16:44:56 -0700102}
103
Sharvil Nanavatic0745da2014-11-13 01:04:19 -0800104size_t allocation_tracker_expect_no_allocations(void) {
Zach Johnson3b72a142014-08-25 16:44:56 -0700105 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 Nanavatic0745da2014-11-13 01:04:19 -0800118void *allocation_tracker_notify_alloc(uint8_t allocator_id, void *ptr, size_t requested_size) {
Zach Johnson3b72a142014-08-25 16:44:56 -0700119 if (!allocations || !ptr)
Zach Johnsonf947fdd2014-08-28 13:30:17 -0700120 return ptr;
121
122 char *return_ptr = (char *)ptr;
123
Sharvil Nanavatic0745da2014-11-13 01:04:19 -0800124 return_ptr += canary_size;
Zach Johnson3b72a142014-08-25 16:44:56 -0700125
126 pthread_mutex_lock(&lock);
127
Zach Johnsonf947fdd2014-08-28 13:30:17 -0700128 allocation_t *allocation = (allocation_t *)hash_map_get(allocations, return_ptr);
Zach Johnson3b72a142014-08-25 16:44:56 -0700129 if (allocation) {
130 assert(allocation->freed); // Must have been freed before
131 } else {
132 allocation = (allocation_t *)calloc(1, sizeof(allocation_t));
Zach Johnsonf947fdd2014-08-28 13:30:17 -0700133 hash_map_set(allocations, return_ptr, allocation);
Zach Johnson3b72a142014-08-25 16:44:56 -0700134 }
135
Zach Johnson4ed68b42014-08-29 17:08:44 -0700136 allocation->allocator_id = allocator_id;
Zach Johnson3b72a142014-08-25 16:44:56 -0700137 allocation->freed = false;
Zach Johnsonf947fdd2014-08-28 13:30:17 -0700138 allocation->size = requested_size;
139 allocation->ptr = return_ptr;
Zach Johnson3b72a142014-08-25 16:44:56 -0700140
141 pthread_mutex_unlock(&lock);
Zach Johnsonf947fdd2014-08-28 13:30:17 -0700142
Sharvil Nanavatic0745da2014-11-13 01:04:19 -0800143 // 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 Johnsonf947fdd2014-08-28 13:30:17 -0700146
147 return return_ptr;
Zach Johnson3b72a142014-08-25 16:44:56 -0700148}
149
Zach Johnson4ed68b42014-08-29 17:08:44 -0700150void *allocation_tracker_notify_free(uint8_t allocator_id, void *ptr) {
Zach Johnson3b72a142014-08-25 16:44:56 -0700151 if (!allocations || !ptr)
Zach Johnsonf947fdd2014-08-28 13:30:17 -0700152 return ptr;
Zach Johnson3b72a142014-08-25 16:44:56 -0700153
154 pthread_mutex_lock(&lock);
155
156 allocation_t *allocation = (allocation_t *)hash_map_get(allocations, ptr);
Zach Johnson4ed68b42014-08-29 17:08:44 -0700157 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 Johnson3b72a142014-08-25 16:44:56 -0700160 allocation->freed = true;
161
Sharvil Nanavatic0745da2014-11-13 01:04:19 -0800162 const char *beginning_canary = ((char *)ptr) - canary_size;
163 const char *end_canary = ((char *)ptr) + allocation->size;
Zach Johnsonf947fdd2014-08-28 13:30:17 -0700164
Sharvil Nanavatic0745da2014-11-13 01:04:19 -0800165 for (size_t i = 0; i < canary_size; i++) {
166 assert(beginning_canary[i] == canary[i]);
167 assert(end_canary[i] == canary[i]);
Zach Johnsonf947fdd2014-08-28 13:30:17 -0700168 }
169
Zach Johnson3b72a142014-08-25 16:44:56 -0700170 pthread_mutex_unlock(&lock);
Zach Johnsonf947fdd2014-08-28 13:30:17 -0700171
Sharvil Nanavatic0745da2014-11-13 01:04:19 -0800172 return ((char *)ptr) - canary_size;
Zach Johnsonf947fdd2014-08-28 13:30:17 -0700173}
174
175size_t allocation_tracker_resize_for_canary(size_t size) {
Sharvil Nanavatic0745da2014-11-13 01:04:19 -0800176 return (!allocations) ? size : size + (2 * canary_size);
Zach Johnson3b72a142014-08-25 16:44:56 -0700177}
178
179static 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 Janssendb554582015-06-26 14:53:46 -0700183 LOG_ERROR(LOG_TAG, "%s found unfreed allocation. address: 0x%zx size: %zd bytes", __func__, (uintptr_t)allocation->ptr, allocation->size);
Zach Johnson3b72a142014-08-25 16:44:56 -0700184 }
185
186 return true;
187}
Zach Johnson53f36a42014-08-26 17:21:44 -0700188
189static void *untracked_calloc(size_t size) {
190 return calloc(size, 1);
191}