allocation_tracker.cc revision f947fdddf5de6bd30886688a2b399460a3d95ecc
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>
213b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include <utils/Log.h>
223b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
233b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "allocation_tracker.h"
2453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson#include "allocator.h"
253b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "hash_functions.h"
263b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "hash_map.h"
273b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#include "osi.h"
283b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
293b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson#define ALLOCATION_HASH_MAP_SIZE 1024
303b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
313b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsontypedef struct {
323b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  void *ptr;
333b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  size_t size;
343b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  bool freed;
35f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  bool is_canaried;
363b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson} allocation_t;
373b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
3853f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson// Hidden constructor for hash map for our use only. Everything else should use the
3953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson// normal interface.
4053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonhash_map_t *hash_map_new_internal(
4153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    size_t size,
4253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    hash_index_fn hash_fn,
4353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    key_free_fn key_fn,
4453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    data_free_fn,
4553f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    const allocator_t *zeroed_allocator);
4653f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson
473b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context);
4853f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic void *untracked_calloc(size_t size);
4953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson
50f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic const char *canary = "tinybird";
5153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic const allocator_t untracked_calloc_allocator = {
5253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  untracked_calloc,
5353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  free
5453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson};
553b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
56f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic bool using_canaries;
57f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonstatic size_t canary_size;
583b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic hash_map_t *allocations;
593b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic pthread_mutex_t lock;
603b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
61f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonvoid allocation_tracker_init(bool use_canaries) {
623b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (allocations)
633b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    return;
643b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
65f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  using_canaries = use_canaries;
66f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  canary_size = strlen(canary);
67f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
683b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_init(&lock, NULL);
6953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  allocations = hash_map_new_internal(
7053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    ALLOCATION_HASH_MAP_SIZE,
7153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    hash_function_knuth,
7253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    NULL,
7353f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    free,
7453f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson    &untracked_calloc_allocator);
753b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
763b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
77ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson// Test function only. Do not call in the normal course of operations.
78ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnsonvoid allocation_tracker_uninit() {
79ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson  hash_map_free(allocations);
80ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson  allocations = NULL;
81ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson}
82ee2aa45def216a3c4d6a23481fa96f1b02a5de8cZach Johnson
833b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonvoid allocation_tracker_reset(void) {
843b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations)
853b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    return;
863b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
873b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  hash_map_clear(allocations);
883b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
893b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
903b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonsize_t allocation_tracker_expect_no_allocations() {
913b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations)
923b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    return 0;
933b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
943b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_lock(&lock);
953b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
963b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  size_t unfreed_memory_size = 0;
973b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  hash_map_foreach(allocations, allocation_entry_freed_checker, &unfreed_memory_size);
983b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
993b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_unlock(&lock);
1003b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1013b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  return unfreed_memory_size;
1023b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1033b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
104f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonvoid *allocation_tracker_notify_alloc(void *ptr, size_t requested_size, bool add_canary) {
1053b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations || !ptr)
106f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    return ptr;
107f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
108f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  char *return_ptr = (char *)ptr;
109f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
110f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  bool using_canaries_here = using_canaries && add_canary;
111f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  if (using_canaries_here)
112f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    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
124f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  allocation->is_canaried = using_canaries_here;
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
131f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  if (using_canaries_here) {
132f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    // Add the canary on both sides
133f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    memcpy(return_ptr - canary_size, canary, canary_size);
134f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    memcpy(return_ptr + requested_size, canary, canary_size);
135f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  }
136f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
137f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  return return_ptr;
1383b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1393b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
140f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonvoid *allocation_tracker_notify_free(void *ptr) {
1413b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocations || !ptr)
142f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    return ptr;
1433b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1443b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_lock(&lock);
1453b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1463b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation_t *allocation = (allocation_t *)hash_map_get(allocations, ptr);
1473b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  assert(allocation);         // Must have been tracked before
1483b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  assert(!allocation->freed); // Must not be a double free
1493b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation->freed = true;
1503b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
151f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  if (allocation->is_canaried) {
152f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    const char *beginning_canary = ((char *)ptr) - canary_size;
153f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    const char *end_canary = ((char *)ptr) + allocation->size;
154f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
155f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    for (size_t i = 0; i < canary_size; i++) {
156f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson      assert(beginning_canary[i] == canary[i]);
157f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson      assert(end_canary[i] == canary[i]);
158f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson    }
159f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  }
160f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
1613b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  pthread_mutex_unlock(&lock);
162f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
163f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  return allocation->is_canaried ? ((char *)ptr) - canary_size : ptr;
164f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson}
165f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson
166f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnsonsize_t allocation_tracker_resize_for_canary(size_t size) {
167f947fdddf5de6bd30886688a2b399460a3d95eccZach Johnson  return (!allocations || !using_canaries) ? size : size + (2 * canary_size);
1683b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
1693b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1703b72a14c2515c1169d5501ada5499cf232fc643bZach Johnsonstatic bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context) {
1713b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  allocation_t *allocation = (allocation_t *)entry->data;
1723b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  if (!allocation->freed) {
1733b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    *((size_t *)context) += allocation->size; // Report back the unfreed byte count
1743b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson    ALOGE("%s found unfreed allocation. address: 0x%x size: %d bytes", __func__, (uintptr_t)allocation->ptr, allocation->size);
1753b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  }
1763b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson
1773b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson  return true;
1783b72a14c2515c1169d5501ada5499cf232fc643bZach Johnson}
17953f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson
18053f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnsonstatic void *untracked_calloc(size_t size) {
18153f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson  return calloc(size, 1);
18253f36a45bd5333db3b55f1d7fbcd7a3362027de0Zach Johnson}
183