1 /*
2 *
3 * Copyright 2016 gRPC authors.
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 #include <inttypes.h>
20 #include <stdint.h>
21 #include <string.h>
22
23 #include <grpc/grpc.h>
24 #include <grpc/support/alloc.h>
25 #include <grpc/support/log.h>
26 #include <grpc/support/sync.h>
27 #include <grpc/support/time.h>
28
29 #include "src/core/lib/gpr/alloc.h"
30 #include "src/core/lib/surface/init.h"
31 #include "test/core/util/memory_counters.h"
32
33 #include <stdio.h>
34
35 static struct grpc_memory_counters g_memory_counters;
36 static bool g_memory_counter_enabled;
37
38 #ifdef GPR_LOW_LEVEL_COUNTERS
39 /* hide these from the microbenchmark atomic stats */
40 #define NO_BARRIER_FETCH_ADD(x, sz) \
41 __atomic_fetch_add((x), (sz), __ATOMIC_RELAXED)
42 #define NO_BARRIER_LOAD(x) __atomic_load_n((x), __ATOMIC_RELAXED)
43 #else
44 #define NO_BARRIER_FETCH_ADD(x, sz) gpr_atm_no_barrier_fetch_add(x, sz)
45 #define NO_BARRIER_LOAD(x) gpr_atm_no_barrier_load(x)
46 #endif
47
48 // Memory counter uses --wrap=symbol feature from ld. To use this,
49 // `GPR_WRAP_MEMORY_COUNTER` needs to be defined. following options should be
50 // passed to the compiler.
51 // -Wl,--wrap=malloc -Wl,--wrap=calloc -Wl,--wrap=realloc -Wl,--wrap=free
52 // * Reference: https://linux.die.net/man/1/ld)
53 #if GPR_WRAP_MEMORY_COUNTER
54
55 extern "C" {
56 void* __real_malloc(size_t size);
57 void* __real_calloc(size_t size);
58 void* __real_realloc(void* ptr, size_t size);
59 void __real_free(void* ptr);
60
61 void* __wrap_malloc(size_t size);
62 void* __wrap_calloc(size_t size);
63 void* __wrap_realloc(void* ptr, size_t size);
64 void __wrap_free(void* ptr);
65 }
66
__wrap_malloc(size_t size)67 void* __wrap_malloc(size_t size) {
68 if (!size) return nullptr;
69 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_absolute, (gpr_atm)size);
70 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, (gpr_atm)size);
71 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_absolute, (gpr_atm)1);
72 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_relative, (gpr_atm)1);
73 void* ptr =
74 __real_malloc(GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)) + size);
75 *static_cast<size_t*>(ptr) = size;
76 return static_cast<char*>(ptr) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size));
77 }
78
__wrap_calloc(size_t size)79 void* __wrap_calloc(size_t size) {
80 if (!size) return nullptr;
81 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_absolute, (gpr_atm)size);
82 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, (gpr_atm)size);
83 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_absolute, (gpr_atm)1);
84 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_relative, (gpr_atm)1);
85 void* ptr =
86 __real_calloc(GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)) + size);
87 *static_cast<size_t*>(ptr) = size;
88 return static_cast<char*>(ptr) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size));
89 }
90
__wrap_realloc(void * ptr,size_t size)91 void* __wrap_realloc(void* ptr, size_t size) {
92 if (ptr == nullptr) {
93 return __wrap_malloc(size);
94 }
95 if (size == 0) {
96 __wrap_free(ptr);
97 return nullptr;
98 }
99 void* rptr =
100 static_cast<char*>(ptr) - GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size));
101 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_absolute, (gpr_atm)size);
102 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative,
103 -*static_cast<gpr_atm*>(rptr));
104 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, (gpr_atm)size);
105 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_absolute, (gpr_atm)1);
106 void* new_ptr =
107 __real_realloc(rptr, GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)) + size);
108 *static_cast<size_t*>(new_ptr) = size;
109 return static_cast<char*>(new_ptr) +
110 GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size));
111 }
112
__wrap_free(void * ptr)113 void __wrap_free(void* ptr) {
114 if (ptr == nullptr) return;
115 void* rptr =
116 static_cast<char*>(ptr) - GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size_t));
117 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative,
118 -*static_cast<gpr_atm*>(rptr));
119 NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_relative, -(gpr_atm)1);
120 __real_free(rptr);
121 }
122
123 #endif // GPR_WRAP_MEMORY_COUNTER
124
grpc_memory_counters_init()125 void grpc_memory_counters_init() {
126 memset(&g_memory_counters, 0, sizeof(g_memory_counters));
127 g_memory_counter_enabled = true;
128 }
129
grpc_memory_counters_destroy()130 void grpc_memory_counters_destroy() { g_memory_counter_enabled = false; }
131
grpc_memory_counters_snapshot()132 struct grpc_memory_counters grpc_memory_counters_snapshot() {
133 struct grpc_memory_counters counters;
134 counters.total_size_relative =
135 NO_BARRIER_LOAD(&g_memory_counters.total_size_relative);
136 counters.total_size_absolute =
137 NO_BARRIER_LOAD(&g_memory_counters.total_size_absolute);
138 counters.total_allocs_relative =
139 NO_BARRIER_LOAD(&g_memory_counters.total_allocs_relative);
140 counters.total_allocs_absolute =
141 NO_BARRIER_LOAD(&g_memory_counters.total_allocs_absolute);
142 return counters;
143 }
144
145 namespace grpc_core {
146 namespace testing {
147
LeakDetector(bool enable)148 LeakDetector::LeakDetector(bool enable) : enabled_(enable) {
149 if (enabled_) {
150 grpc_memory_counters_init();
151 }
152 }
153
~LeakDetector()154 LeakDetector::~LeakDetector() {
155 // Wait for grpc_shutdown() to finish its async work.
156 grpc_maybe_wait_for_async_shutdown();
157 if (enabled_) {
158 struct grpc_memory_counters counters = grpc_memory_counters_snapshot();
159 if (counters.total_size_relative != 0) {
160 gpr_log(GPR_ERROR, "Leaking %" PRIuPTR " bytes",
161 static_cast<uintptr_t>(counters.total_size_relative));
162 GPR_ASSERT(0);
163 }
164 grpc_memory_counters_destroy();
165 }
166 }
167
168 } // namespace testing
169 } // namespace grpc_core
170