allocation_tracker.c revision aa3a0114b6f018d0dd296d5bdb113d2f881cbc51
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
19c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati#define LOG_TAG "bt_allocation_tracker"
20c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati
213b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include <assert.h>
223b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include <pthread.h>
234ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson#include <stdint.h>
243b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
253b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "allocation_tracker.h"
2653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson#include "allocator.h"
273b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "hash_functions.h"
283b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "hash_map.h"
293b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "osi.h"
3044802768c447ab480d4227b3a852a97d923b816dSharvil Nanavati#include "osi/include/log.h"
313b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
323b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsontypedef struct {
334ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  uint8_t allocator_id;
343b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  void *ptr;
353b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  size_t size;
363b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  bool freed;
373b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson} allocation_t;
383b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
3953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson// Hidden constructor for hash map for our use only. Everything else should use the
4053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson// normal interface.
4153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonhash_map_t *hash_map_new_internal(
4253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    size_t size,
4353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    hash_index_fn hash_fn,
4453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    key_free_fn key_fn,
4553f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    data_free_fn,
46aa3a0114b6f018d0dd296d5bdb113d2f881cbc51Zach Johnson    key_equality_fn equality_fn,
4753f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    const allocator_t *zeroed_allocator);
4853f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson
493b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context);
5053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic void *untracked_calloc(size_t size);
5153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson
52c0e2f9927b8d60123b388c3d117e8f82c90d46e3Zach Johnsonstatic const size_t allocation_hash_map_size = 1024;
53f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic const char *canary = "tinybird";
5453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic const allocator_t untracked_calloc_allocator = {
5553f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  untracked_calloc,
5653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  free
5753f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson};
583b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
59f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic size_t canary_size;
603b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic hash_map_t *allocations;
613b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic pthread_mutex_t lock;
623b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
63c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavativoid allocation_tracker_init(void) {
643b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (allocations)
653b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    return;
663b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
67f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  canary_size = strlen(canary);
68f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
693b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_init(&lock, NULL);
7053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  allocations = hash_map_new_internal(
71c0e2f9927b8d60123b388c3d117e8f82c90d46e3Zach Johnson    allocation_hash_map_size,
72c0e2f9927b8d60123b388c3d117e8f82c90d46e3Zach Johnson    hash_function_pointer,
7353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    NULL,
7453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    free,
75aa3a0114b6f018d0dd296d5bdb113d2f881cbc51Zach Johnson    NULL,
7653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    &untracked_calloc_allocator);
773b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
783b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
79ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson// Test function only. Do not call in the normal course of operations.
80c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavativoid allocation_tracker_uninit(void) {
81ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson  hash_map_free(allocations);
82ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson  allocations = NULL;
83ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson}
84ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson
853b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonvoid allocation_tracker_reset(void) {
863b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations)
873b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    return;
883b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
893b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  hash_map_clear(allocations);
903b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
913b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
92c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavatisize_t allocation_tracker_expect_no_allocations(void) {
933b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations)
943b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    return 0;
953b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
963b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_lock(&lock);
973b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
983b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  size_t unfreed_memory_size = 0;
993b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  hash_map_foreach(allocations, allocation_entry_freed_checker, &unfreed_memory_size);
1003b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1013b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_unlock(&lock);
1023b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1033b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  return unfreed_memory_size;
1043b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1053b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
106c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavativoid *allocation_tracker_notify_alloc(uint8_t allocator_id, void *ptr, size_t requested_size) {
1073b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations || !ptr)
108f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    return ptr;
109f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
110f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  char *return_ptr = (char *)ptr;
111f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
112c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  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
1244ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  allocation->allocator_id = allocator_id;
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
131c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  // Add the canary on both sides
132c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  memcpy(return_ptr - canary_size, canary, canary_size);
133c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  memcpy(return_ptr + requested_size, canary, canary_size);
134f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
135f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  return return_ptr;
1363b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1373b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1384ed68b407ee836df7780a00d6b50b081b334c3abZach Johnsonvoid *allocation_tracker_notify_free(uint8_t allocator_id, void *ptr) {
1393b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations || !ptr)
140f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    return ptr;
1413b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1423b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_lock(&lock);
1433b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1443b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation_t *allocation = (allocation_t *)hash_map_get(allocations, ptr);
1454ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  assert(allocation);                               // Must have been tracked before
1464ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  assert(!allocation->freed);                       // Must not be a double free
1474ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  assert(allocation->allocator_id == allocator_id); // Must be from the same allocator
1483b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation->freed = true;
1493b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
150c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  const char *beginning_canary = ((char *)ptr) - canary_size;
151c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  const char *end_canary = ((char *)ptr) + allocation->size;
152f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
153c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  for (size_t i = 0; i < canary_size; i++) {
154c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati    assert(beginning_canary[i] == canary[i]);
155c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati    assert(end_canary[i] == canary[i]);
156f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  }
157f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
1583b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_unlock(&lock);
159f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
160c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  return ((char *)ptr) - canary_size;
161f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson}
162f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
163f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonsize_t allocation_tracker_resize_for_canary(size_t size) {
164c0745da4fb23eea23abac3c3cfd51cc7f1d38f6dSharvil Nanavati  return (!allocations) ? size : size + (2 * canary_size);
1653b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1663b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1673b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context) {
1683b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation_t *allocation = (allocation_t *)entry->data;
1693b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocation->freed) {
1703b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    *((size_t *)context) += allocation->size; // Report back the unfreed byte count
17144802768c447ab480d4227b3a852a97d923b816dSharvil Nanavati    LOG_ERROR("%s found unfreed allocation. address: 0x%x size: %d bytes", __func__, (uintptr_t)allocation->ptr, allocation->size);
1723b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  }
1733b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1743b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  return true;
1753b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
17653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson
17753f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic void *untracked_calloc(size_t size) {
17853f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  return calloc(size, 1);
17953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson}
180