ddd8d2f283ff8bceab4551a7be54badf2edde4bf
[platform/upstream/grpc.git] / test / core / util / memory_counters.cc
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
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
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
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
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
125 void grpc_memory_counters_init() {
126   memset(&g_memory_counters, 0, sizeof(g_memory_counters));
127   g_memory_counter_enabled = true;
128 }
129
130 void grpc_memory_counters_destroy() { g_memory_counter_enabled = false; }
131
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
148 LeakDetector::LeakDetector(bool enable) : enabled_(enable) {
149   if (enabled_) {
150     grpc_memory_counters_init();
151   }
152 }
153
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