• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2  *
3  *  Copyright (C) 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 
allocation_tracker_init(void)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.
allocation_tracker_uninit(void)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 
allocation_tracker_reset(void)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 
allocation_tracker_expect_no_allocations(void)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 
allocation_tracker_notify_alloc(uint8_t allocator_id,void * ptr,size_t requested_size)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 
allocation_tracker_notify_free(UNUSED_ATTR uint8_t allocator_id,void * ptr)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 
allocation_tracker_resize_for_canary(size_t size)173 size_t allocation_tracker_resize_for_canary(size_t size) {
174   return (!enabled) ? size : size + (2 * canary_size);
175 }
176 
osi_allocator_debug_dump(int fd)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