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
19f8027005333c88a2f097cfd70d15c3d54c7764aeChris Manton#define LOG_TAG "bt_osi_allocation_tracker"
20c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati
2149a86709488e5cfd5e23759da18bf9613e15b04dMarie Janssen#include "osi/include/allocation_tracker.h"
2249a86709488e5cfd5e23759da18bf9613e15b04dMarie Janssen
233b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include <assert.h>
243b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include <pthread.h>
253e59b5b6f2ce1295e3e2711afcd2cdf0dd7e22b6Etan Cohen#include <stdlib.h>
263e59b5b6f2ce1295e3e2711afcd2cdf0dd7e22b6Etan Cohen#include <string.h>
273b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
280f9b91e150e153229235c163861198e23600e636Sharvil Nanavati#include "osi/include/allocator.h"
290f9b91e150e153229235c163861198e23600e636Sharvil Nanavati#include "osi/include/hash_functions.h"
300f9b91e150e153229235c163861198e23600e636Sharvil Nanavati#include "osi/include/hash_map.h"
3144802768c447ab480d4227b3a852a97d923b816dSharvil Nanavati#include "osi/include/log.h"
3249a86709488e5cfd5e23759da18bf9613e15b04dMarie Janssen#include "osi/include/osi.h"
333b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
343b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsontypedef struct {
354ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  uint8_t allocator_id;
363b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  void *ptr;
373b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  size_t size;
383b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  bool freed;
393b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson} allocation_t;
403b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
4153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson// Hidden constructor for hash map for our use only. Everything else should use the
4253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson// normal interface.
4353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonhash_map_t *hash_map_new_internal(
4453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    size_t size,
4553f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    hash_index_fn hash_fn,
4653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    key_free_fn key_fn,
4753f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    data_free_fn,
48aa3a0114b6f018d0dd296d5bdb113d2f881cbc51Zach Johnson    key_equality_fn equality_fn,
4953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    const allocator_t *zeroed_allocator);
5053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson
513b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context);
5253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic void *untracked_calloc(size_t size);
5353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson
54c0e2f9927b8d60123b388c3d117e8f82c90d46e3Zach Johnsonstatic const size_t allocation_hash_map_size = 1024;
55f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic const char *canary = "tinybird";
5653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic const allocator_t untracked_calloc_allocator = {
5753f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  untracked_calloc,
5853f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  free
5953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson};
603b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
61f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic size_t canary_size;
623b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic hash_map_t *allocations;
633b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic pthread_mutex_t lock;
643b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
65c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavativoid allocation_tracker_init(void) {
663b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (allocations)
673b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    return;
683b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
69f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  canary_size = strlen(canary);
70f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
713b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_init(&lock, NULL);
725febd64b95260672d41964337377fb312ea2c07bPavlin Radoslavov
735febd64b95260672d41964337377fb312ea2c07bPavlin Radoslavov  pthread_mutex_lock(&lock);
7453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  allocations = hash_map_new_internal(
75c0e2f9927b8d60123b388c3d117e8f82c90d46e3Zach Johnson    allocation_hash_map_size,
76c0e2f9927b8d60123b388c3d117e8f82c90d46e3Zach Johnson    hash_function_pointer,
7753f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    NULL,
7853f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    free,
79aa3a0114b6f018d0dd296d5bdb113d2f881cbc51Zach Johnson    NULL,
8053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    &untracked_calloc_allocator);
815febd64b95260672d41964337377fb312ea2c07bPavlin Radoslavov  pthread_mutex_unlock(&lock);
823b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
833b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
84ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson// Test function only. Do not call in the normal course of operations.
85c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavativoid allocation_tracker_uninit(void) {
865febd64b95260672d41964337377fb312ea2c07bPavlin Radoslavov  if (!allocations)
875febd64b95260672d41964337377fb312ea2c07bPavlin Radoslavov    return;
885febd64b95260672d41964337377fb312ea2c07bPavlin Radoslavov
895febd64b95260672d41964337377fb312ea2c07bPavlin Radoslavov  pthread_mutex_lock(&lock);
90ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson  hash_map_free(allocations);
91ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson  allocations = NULL;
925febd64b95260672d41964337377fb312ea2c07bPavlin Radoslavov  pthread_mutex_unlock(&lock);
93ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson}
94ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson
953b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonvoid allocation_tracker_reset(void) {
963b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations)
973b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    return;
983b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
995febd64b95260672d41964337377fb312ea2c07bPavlin Radoslavov  pthread_mutex_lock(&lock);
1003b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  hash_map_clear(allocations);
1015febd64b95260672d41964337377fb312ea2c07bPavlin Radoslavov  pthread_mutex_unlock(&lock);
1023b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1033b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
104c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavatisize_t allocation_tracker_expect_no_allocations(void) {
1053b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations)
1063b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    return 0;
1073b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1083b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_lock(&lock);
1093b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1103b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  size_t unfreed_memory_size = 0;
1113b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  hash_map_foreach(allocations, allocation_entry_freed_checker, &unfreed_memory_size);
1123b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1133b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_unlock(&lock);
1143b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1153b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  return unfreed_memory_size;
1163b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1173b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
118c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavativoid *allocation_tracker_notify_alloc(uint8_t allocator_id, void *ptr, size_t requested_size) {
1193b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations || !ptr)
120f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    return ptr;
121f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
122f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  char *return_ptr = (char *)ptr;
123f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
124c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  return_ptr += canary_size;
1253b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1263b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_lock(&lock);
1273b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
128f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  allocation_t *allocation = (allocation_t *)hash_map_get(allocations, return_ptr);
1293b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (allocation) {
1303b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    assert(allocation->freed); // Must have been freed before
1313b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  } else {
1323b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    allocation = (allocation_t *)calloc(1, sizeof(allocation_t));
133f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    hash_map_set(allocations, return_ptr, allocation);
1343b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  }
1353b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1364ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  allocation->allocator_id = allocator_id;
1373b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation->freed = false;
138f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  allocation->size = requested_size;
139f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  allocation->ptr = return_ptr;
1403b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1413b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_unlock(&lock);
142f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
143c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  // Add the canary on both sides
144c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  memcpy(return_ptr - canary_size, canary, canary_size);
145c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  memcpy(return_ptr + requested_size, canary, canary_size);
146f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
147f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  return return_ptr;
1483b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1493b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
150918e5a6832fa22bfe1e53e501af8f53ffc55b454Chih-Hung Hsiehvoid *allocation_tracker_notify_free(UNUSED_ATTR uint8_t allocator_id, void *ptr) {
1513b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations || !ptr)
152f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    return ptr;
1533b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1543b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_lock(&lock);
1553b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1563b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation_t *allocation = (allocation_t *)hash_map_get(allocations, ptr);
1574ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  assert(allocation);                               // Must have been tracked before
1584ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  assert(!allocation->freed);                       // Must not be a double free
1594ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  assert(allocation->allocator_id == allocator_id); // Must be from the same allocator
1603b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation->freed = true;
1613b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
162918e5a6832fa22bfe1e53e501af8f53ffc55b454Chih-Hung Hsieh  UNUSED_ATTR const char *beginning_canary = ((char *)ptr) - canary_size;
163918e5a6832fa22bfe1e53e501af8f53ffc55b454Chih-Hung Hsieh  UNUSED_ATTR const char *end_canary = ((char *)ptr) + allocation->size;
164f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
165c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  for (size_t i = 0; i < canary_size; i++) {
166c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati    assert(beginning_canary[i] == canary[i]);
167c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati    assert(end_canary[i] == canary[i]);
168f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  }
169f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
1702bea757aba0d0d84865d7d0d675d8bcf31f90b22Srinu Jella  // Free the hash map entry to avoid unlimited memory usage growth.
1712bea757aba0d0d84865d7d0d675d8bcf31f90b22Srinu Jella  // Double-free of memory is detected with "assert(allocation)" above
1722bea757aba0d0d84865d7d0d675d8bcf31f90b22Srinu Jella  // as the allocation entry will not be present.
1732bea757aba0d0d84865d7d0d675d8bcf31f90b22Srinu Jella  hash_map_erase(allocations, ptr);
1742bea757aba0d0d84865d7d0d675d8bcf31f90b22Srinu Jella
1753b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_unlock(&lock);
176f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
177c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  return ((char *)ptr) - canary_size;
178f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson}
179f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
180f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonsize_t allocation_tracker_resize_for_canary(size_t size) {
181c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  return (!allocations) ? size : size + (2 * canary_size);
1823b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1833b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1843b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context) {
1853b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation_t *allocation = (allocation_t *)entry->data;
1863b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocation->freed) {
1873b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    *((size_t *)context) += allocation->size; // Report back the unfreed byte count
188db554581079863974af8e1289646f5deea6fc044Marie Janssen    LOG_ERROR(LOG_TAG, "%s found unfreed allocation. address: 0x%zx size: %zd bytes", __func__, (uintptr_t)allocation->ptr, allocation->size);
1893b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  }
1903b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1913b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  return true;
1923b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
19353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson
19453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic void *untracked_calloc(size_t size) {
19553f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  return calloc(size, 1);
19653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson}
197