allocation_tracker.cc revision 4ed68b407ee836df7780a00d6b50b081b334c3ab
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>
214ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson#include <stdint.h>
223b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include <utils/Log.h>
233b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
243b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "allocation_tracker.h"
2553f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson#include "allocator.h"
263b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "hash_functions.h"
273b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "hash_map.h"
283b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "osi.h"
293b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
303b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#define ALLOCATION_HASH_MAP_SIZE 1024
313b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
323b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsontypedef struct {
334ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  uint8_t allocator_id;
343b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  void *ptr;
353b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  size_t size;
363b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  bool freed;
37f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  bool is_canaried;
383b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson} allocation_t;
393b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
4053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson// Hidden constructor for hash map for our use only. Everything else should use the
4153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson// normal interface.
4253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonhash_map_t *hash_map_new_internal(
4353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    size_t size,
4453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    hash_index_fn hash_fn,
4553f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    key_free_fn key_fn,
4653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    data_free_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
52f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic const char *canary = "tinybird";
5353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic const allocator_t untracked_calloc_allocator = {
5453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  untracked_calloc,
5553f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  free
5653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson};
573b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
58f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic bool using_canaries;
59f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic size_t canary_size;
603b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic hash_map_t *allocations;
613b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic pthread_mutex_t lock;
623b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
63f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonvoid allocation_tracker_init(bool use_canaries) {
643b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (allocations)
653b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    return;
663b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
67f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  using_canaries = use_canaries;
68f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  canary_size = strlen(canary);
69f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
703b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_init(&lock, NULL);
7153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  allocations = hash_map_new_internal(
7253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    ALLOCATION_HASH_MAP_SIZE,
7353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    hash_function_knuth,
7453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    NULL,
7553f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    free,
7653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    &untracked_calloc_allocator);
773b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
783b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
79ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson// Test function only. Do not call in the normal course of operations.
80ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnsonvoid allocation_tracker_uninit() {
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
923b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonsize_t allocation_tracker_expect_no_allocations() {
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
1064ed68b407ee836df7780a00d6b50b081b334c3abZach Johnsonvoid *allocation_tracker_notify_alloc(uint8_t allocator_id, void *ptr, size_t requested_size, bool add_canary) {
1073b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations || !ptr)
108f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    return ptr;
109f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
110f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  char *return_ptr = (char *)ptr;
111f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
112f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  bool using_canaries_here = using_canaries && add_canary;
113f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  if (using_canaries_here)
114f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    return_ptr += canary_size;
1153b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1163b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_lock(&lock);
1173b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
118f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  allocation_t *allocation = (allocation_t *)hash_map_get(allocations, return_ptr);
1193b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (allocation) {
1203b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    assert(allocation->freed); // Must have been freed before
1213b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  } else {
1223b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    allocation = (allocation_t *)calloc(1, sizeof(allocation_t));
123f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    hash_map_set(allocations, return_ptr, allocation);
1243b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  }
1253b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1264ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  allocation->allocator_id = allocator_id;
127f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  allocation->is_canaried = using_canaries_here;
1283b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation->freed = false;
129f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  allocation->size = requested_size;
130f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  allocation->ptr = return_ptr;
1313b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1323b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_unlock(&lock);
133f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
134f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  if (using_canaries_here) {
135f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    // Add the canary on both sides
136f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    memcpy(return_ptr - canary_size, canary, canary_size);
137f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    memcpy(return_ptr + requested_size, canary, canary_size);
138f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  }
139f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
140f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  return return_ptr;
1413b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1423b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1434ed68b407ee836df7780a00d6b50b081b334c3abZach Johnsonvoid *allocation_tracker_notify_free(uint8_t allocator_id, void *ptr) {
1443b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations || !ptr)
145f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    return ptr;
1463b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1473b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_lock(&lock);
1483b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1493b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation_t *allocation = (allocation_t *)hash_map_get(allocations, ptr);
1504ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  assert(allocation);                               // Must have been tracked before
1514ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  assert(!allocation->freed);                       // Must not be a double free
1524ed68b407ee836df7780a00d6b50b081b334c3abZach Johnson  assert(allocation->allocator_id == allocator_id); // Must be from the same allocator
1533b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation->freed = true;
1543b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
155f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  if (allocation->is_canaried) {
156f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    const char *beginning_canary = ((char *)ptr) - canary_size;
157f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    const char *end_canary = ((char *)ptr) + allocation->size;
158f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
159f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    for (size_t i = 0; i < canary_size; i++) {
160f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson      assert(beginning_canary[i] == canary[i]);
161f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson      assert(end_canary[i] == canary[i]);
162f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    }
163f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  }
164f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
1653b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_unlock(&lock);
166f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
167f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  return allocation->is_canaried ? ((char *)ptr) - canary_size : ptr;
168f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson}
169f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
170f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonsize_t allocation_tracker_resize_for_canary(size_t size) {
171f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  return (!allocations || !using_canaries) ? size : size + (2 * canary_size);
1723b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1733b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1743b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context) {
1753b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation_t *allocation = (allocation_t *)entry->data;
1763b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocation->freed) {
1773b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    *((size_t *)context) += allocation->size; // Report back the unfreed byte count
1783b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    ALOGE("%s found unfreed allocation. address: 0x%x size: %d bytes", __func__, (uintptr_t)allocation->ptr, allocation->size);
1793b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  }
1803b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1813b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  return true;
1823b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
18353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson
18453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic void *untracked_calloc(size_t size) {
18553f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  return calloc(size, 1);
18653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson}
187