Home | History | Annotate | Download | only in src
      1 /******************************************************************************
      2  *
      3  *  Copyright 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 
     19 #define LOG_TAG "bt_osi_allocation_tracker"
     20 
     21 #include "osi/include/allocation_tracker.h"
     22 
     23 #include <base/logging.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 #include <mutex>
     27 #include <unordered_map>
     28 
     29 #include "osi/include/allocator.h"
     30 #include "osi/include/log.h"
     31 #include "osi/include/osi.h"
     32 
     33 typedef struct {
     34   uint8_t allocator_id;
     35   void* ptr;
     36   size_t size;
     37   bool freed;
     38 } allocation_t;
     39 
     40 static const size_t canary_size = 8;
     41 static char canary[canary_size];
     42 static std::unordered_map<void*, allocation_t*> allocations;
     43 static std::mutex tracker_lock;
     44 static bool enabled = false;
     45 
     46 // Memory allocation statistics
     47 static size_t alloc_counter = 0;
     48 static size_t free_counter = 0;
     49 static size_t alloc_total_size = 0;
     50 static size_t free_total_size = 0;
     51 
     52 void allocation_tracker_init(void) {
     53   std::unique_lock<std::mutex> lock(tracker_lock);
     54   if (enabled) return;
     55 
     56   // randomize the canary contents
     57   for (size_t i = 0; i < canary_size; i++) canary[i] = (char)osi_rand();
     58 
     59   LOG_DEBUG(LOG_TAG, "canary initialized");
     60 
     61   enabled = true;
     62 }
     63 
     64 // Test function only. Do not call in the normal course of operations.
     65 void allocation_tracker_uninit(void) {
     66   std::unique_lock<std::mutex> lock(tracker_lock);
     67   if (!enabled) return;
     68 
     69   allocations.clear();
     70   enabled = false;
     71 }
     72 
     73 void allocation_tracker_reset(void) {
     74   std::unique_lock<std::mutex> lock(tracker_lock);
     75   if (!enabled) return;
     76 
     77   allocations.clear();
     78 }
     79 
     80 size_t allocation_tracker_expect_no_allocations(void) {
     81   std::unique_lock<std::mutex> lock(tracker_lock);
     82   if (!enabled) return 0;
     83 
     84   size_t unfreed_memory_size = 0;
     85 
     86   for (const auto& entry : allocations) {
     87     allocation_t* allocation = entry.second;
     88     if (!allocation->freed) {
     89       unfreed_memory_size +=
     90           allocation->size;  // Report back the unfreed byte count
     91       LOG_ERROR(LOG_TAG,
     92                 "%s found unfreed allocation. address: 0x%zx size: %zd bytes",
     93                 __func__, (uintptr_t)allocation->ptr, allocation->size);
     94     }
     95   }
     96 
     97   return unfreed_memory_size;
     98 }
     99 
    100 void* allocation_tracker_notify_alloc(uint8_t allocator_id, void* ptr,
    101                                       size_t requested_size) {
    102   char* return_ptr;
    103   {
    104     std::unique_lock<std::mutex> lock(tracker_lock);
    105     if (!enabled || !ptr) return ptr;
    106 
    107     // Keep statistics
    108     alloc_counter++;
    109     alloc_total_size += allocation_tracker_resize_for_canary(requested_size);
    110 
    111     return_ptr = ((char*)ptr) + canary_size;
    112 
    113     auto map_entry = allocations.find(return_ptr);
    114     allocation_t* allocation;
    115     if (map_entry != allocations.end()) {
    116       allocation = map_entry->second;
    117       CHECK(allocation->freed);  // Must have been freed before
    118     } else {
    119       allocation = (allocation_t*)calloc(1, sizeof(allocation_t));
    120       allocations[return_ptr] = allocation;
    121     }
    122 
    123     allocation->allocator_id = allocator_id;
    124     allocation->freed = false;
    125     allocation->size = requested_size;
    126     allocation->ptr = return_ptr;
    127   }
    128 
    129   // Add the canary on both sides
    130   memcpy(return_ptr - canary_size, canary, canary_size);
    131   memcpy(return_ptr + requested_size, canary, canary_size);
    132 
    133   return return_ptr;
    134 }
    135 
    136 void* allocation_tracker_notify_free(UNUSED_ATTR uint8_t allocator_id,
    137                                      void* ptr) {
    138   std::unique_lock<std::mutex> lock(tracker_lock);
    139 
    140   if (!enabled || !ptr) return ptr;
    141 
    142   auto map_entry = allocations.find(ptr);
    143   CHECK(map_entry != allocations.end());
    144   allocation_t* allocation = map_entry->second;
    145   CHECK(allocation);          // Must have been tracked before
    146   CHECK(!allocation->freed);  // Must not be a double free
    147   CHECK(allocation->allocator_id ==
    148         allocator_id);  // Must be from the same allocator
    149 
    150   // Keep statistics
    151   free_counter++;
    152   free_total_size += allocation_tracker_resize_for_canary(allocation->size);
    153 
    154   allocation->freed = true;
    155 
    156   UNUSED_ATTR const char* beginning_canary = ((char*)ptr) - canary_size;
    157   UNUSED_ATTR const char* end_canary = ((char*)ptr) + allocation->size;
    158 
    159   for (size_t i = 0; i < canary_size; i++) {
    160     CHECK(beginning_canary[i] == canary[i]);
    161     CHECK(end_canary[i] == canary[i]);
    162   }
    163 
    164   // Free the hash map entry to avoid unlimited memory usage growth.
    165   // Double-free of memory is detected with "assert(allocation)" above
    166   // as the allocation entry will not be present.
    167   allocations.erase(ptr);
    168   free(allocation);
    169 
    170   return ((char*)ptr) - canary_size;
    171 }
    172 
    173 size_t allocation_tracker_resize_for_canary(size_t size) {
    174   return (!enabled) ? size : size + (2 * canary_size);
    175 }
    176 
    177 void osi_allocator_debug_dump(int fd) {
    178   dprintf(fd, "\nBluetooth Memory Allocation Statistics:\n");
    179 
    180   std::unique_lock<std::mutex> lock(tracker_lock);
    181 
    182   dprintf(fd, "  Total allocated/free/used counts : %zu / %zu / %zu\n",
    183           alloc_counter, free_counter, alloc_counter - free_counter);
    184   dprintf(fd, "  Total allocated/free/used octets : %zu / %zu / %zu\n",
    185           alloc_total_size, free_total_size,
    186           alloc_total_size - free_total_size);
    187 }
    188