xref: /3.0.2-MP2/memcached/daemon/alloc_hooks.c (revision 12c58e52)
1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2#include "config.h"
3#include "alloc_hooks.h"
4#include <stdbool.h>
5
6#ifdef HAVE_JEMALLOC
7/* jemalloc (on Linux at least) assumes you are trying to
8 * transparently preload it, and so the exported symbols have no
9 * prefix (i.e. malloc,free instead of je_malloc,je_free).
10 * To make our code below more explicit we use the 'je_' prefixed
11 *  names, so tell JEMALLOC to not demangle these so we link correctly.
12 */
13#  define JEMALLOC_NO_DEMANGLE 1
14#  include <jemalloc/jemalloc.h>
15#elif defined(HAVE_TCMALLOC)
16#include <gperftools/malloc_extension_c.h>
17#include <gperftools/malloc_hook_c.h>
18#endif
19
20static int (*addNewHook)(void (*hook)(const void *ptr, size_t size));
21static int (*removeNewHook)(void (*hook)(const void *ptr, size_t size));
22static int (*addDelHook)(void (*hook)(const void *ptr));
23static int (*removeDelHook)(void (*hook)(const void *ptr));
24static int (*getStatsProp)(const char* property, size_t* value);
25static size_t (*getAllocSize)(const void *ptr);
26static void (*getDetailedStats)(char *buffer, int nbuffer);
27static void (*releaseFreeMemory)(void);
28
29static alloc_hooks_type type = none;
30
31#ifdef HAVE_JEMALLOC
32
33static int jemalloc_addrem_new_hook(void (*hook)(const void *ptr, size_t size)) {
34    (void)hook;
35    /* JEMalloc provides memory tracking, but not via add/remove hooks - so
36     * just return success here even though we haven't registered any callbacks.
37     */
38    return 1;
39}
40
41static int jemalloc_addrem_del_hook(void (*hook)(const void *ptr)) {
42    (void)hook;
43    /* JEMalloc provides memory tracking, but not via add/remove hooks - so
44     * just return success here even though we haven't registered any callbacks.
45     */
46    return 1;
47}
48
49static int jemalloc_get_stats_prop(const char* property, size_t* value) {
50    size_t size = sizeof(*value);
51    return je_mallctl(property, value, &size, NULL, 0);
52}
53
54static size_t jemalloc_get_alloc_size(const void *ptr) {
55    return je_malloc_usable_size(ptr);
56}
57
58struct write_state {
59    char* buffer;
60    int remaining;
61    bool cropped;
62};
63static const char cropped_error[] = "=== Exceeded buffer size - output cropped ===\n";
64
65/* Write callback used by jemalloc's malloc_stats_print() below */
66static void write_cb(void *opaque, const char *msg)
67{
68    int len;
69    struct write_state *st = (struct write_state*)opaque;
70    if (st->cropped) {
71        /* already cropped output - nothing to do. */
72        return;
73    }
74    len = snprintf(st->buffer, st->remaining, "%s", msg);
75    if (len > st->remaining) {
76        /* insufficient space - have to crop output. Note we reserved enough
77           space (see below) to be able to write an error if this occurs. */
78        sprintf(st->buffer, cropped_error);
79        st->cropped = true;
80        return;
81    }
82    st->buffer += len;
83    st->remaining -= len;
84}
85
86static void jemalloc_get_detailed_stats(char *buffer, int nbuffer) {
87    struct write_state st;
88    st.buffer = buffer;
89    st.cropped = false;
90    /* reserve enough space to write out an error if the output is cropped. */
91    st.remaining = nbuffer - sizeof(cropped_error);
92    je_malloc_stats_print(write_cb, &st, "a"/* omit per-arena stats*/);
93}
94
95static void jemalloc_release_free_memory(void) {
96    return;
97}
98
99static void init_no_hooks(void) {
100    addNewHook = jemalloc_addrem_new_hook;
101    removeNewHook = jemalloc_addrem_new_hook;
102    addDelHook = jemalloc_addrem_del_hook;
103    removeDelHook = jemalloc_addrem_del_hook;
104    getStatsProp = jemalloc_get_stats_prop;
105    getAllocSize = jemalloc_get_alloc_size;
106    getDetailedStats = jemalloc_get_detailed_stats;
107    releaseFreeMemory = jemalloc_release_free_memory;
108    type = jemalloc;
109}
110
111#elif defined(HAVE_TCMALLOC)
112
113
114static size_t tcmalloc_getAllocSize(const void *ptr) {
115    if (MallocExtension_GetOwnership(ptr) == MallocExtension_kOwned) {
116        return MallocExtension_GetAllocatedSize(ptr);
117    }
118
119    return 0;
120}
121
122static void init_tcmalloc_hooks(void) {
123    addNewHook = MallocHook_AddNewHook;
124    removeNewHook = MallocHook_RemoveNewHook;
125    addDelHook = MallocHook_AddDeleteHook;
126    removeDelHook = MallocHook_RemoveDeleteHook;
127    getStatsProp = MallocExtension_GetNumericProperty;
128    getAllocSize = tcmalloc_getAllocSize;
129    getDetailedStats = MallocExtension_GetStats;
130    releaseFreeMemory = MallocExtension_ReleaseFreeMemory;
131    type = tcmalloc;
132}
133#else
134static int invalid_addrem_new_hook(void (*hook)(const void *ptr, size_t size)) {
135    (void)hook;
136    return -1;
137}
138
139static int invalid_addrem_del_hook(void (*hook)(const void *ptr)) {
140    (void)hook;
141    return -1;
142}
143
144static int invalid_get_stats_prop(const char* property, size_t* value) {
145    (void)property;
146    (void)value;
147    return -1;
148}
149
150static size_t invalid_get_alloc_size(const void *ptr) {
151    (void)ptr;
152    return 0;
153}
154
155static void invalid_get_detailed_stats(char *buffer, int nbuffer) {
156    (void)buffer;
157    (void)nbuffer;
158}
159
160static void invalid_release_free_memory(void) {
161    return;
162}
163
164static void init_no_hooks(void) {
165    addNewHook = invalid_addrem_new_hook;
166    removeNewHook = invalid_addrem_new_hook;
167    addDelHook = invalid_addrem_del_hook;
168    removeDelHook = invalid_addrem_del_hook;
169    getStatsProp = invalid_get_stats_prop;
170    getAllocSize = invalid_get_alloc_size;
171    getDetailedStats = invalid_get_detailed_stats;
172    releaseFreeMemory = invalid_release_free_memory;
173    type = none;
174}
175#endif
176
177void init_alloc_hooks() {
178#ifdef HAVE_TCMALLOC
179    init_tcmalloc_hooks();
180#else
181    init_no_hooks();
182    get_stderr_logger()->log(EXTENSION_LOG_DEBUG, NULL,
183                             "Couldn't find allocator hooks for accurate memory tracking");
184#endif
185}
186
187bool mc_add_new_hook(void (*hook)(const void* ptr, size_t size)) {
188    return addNewHook(hook) ? true : false;
189}
190
191bool mc_remove_new_hook(void (*hook)(const void* ptr, size_t size)) {
192    return removeNewHook(hook) ? true : false;
193}
194
195bool mc_add_delete_hook(void (*hook)(const void* ptr)) {
196    return addDelHook(hook) ? true : false;
197}
198
199bool mc_remove_delete_hook(void (*hook)(const void* ptr)) {
200    return removeDelHook(hook) ? true : false;
201}
202
203int mc_get_extra_stats_size() {
204    if (type == tcmalloc) {
205        return 3;
206    }
207    return 0;
208}
209
210void mc_get_allocator_stats(allocator_stats* stats) {
211    if (type == tcmalloc) {
212        getStatsProp("generic.current_allocated_bytes", &(stats->allocated_size));
213        getStatsProp("generic.heap_size", &(stats->heap_size));
214
215        // Free memory is sum of:
216        //   free, mapped bytes   (tcmalloc.pageheap_free_bytes)
217        // & free, unmapped bytes (tcmalloc.pageheap_unmapped_bytes)
218        getStatsProp("tcmalloc.pageheap_free_bytes", &(stats->free_mapped_size));
219        getStatsProp("tcmalloc.pageheap_unmapped_bytes", &(stats->free_unmapped_size));
220
221        stats->fragmentation_size = stats->heap_size
222                                    - stats->allocated_size
223                                    - stats->free_mapped_size
224                                    - stats->free_unmapped_size;
225
226        strcpy(stats->ext_stats[0].key, "tcmalloc_max_thread_cache_bytes");
227        strcpy(stats->ext_stats[1].key, "tcmalloc_current_thread_cache_bytes");
228
229        getStatsProp("tcmalloc.max_total_thread_cache_bytes",
230                            &(stats->ext_stats[0].value));
231        getStatsProp("tcmalloc.current_total_thread_cache_bytes",
232                            &(stats->ext_stats[1].value));
233    } else if (type == jemalloc) {
234        getStatsProp("stats.allocated", &(stats->allocated_size));
235        getStatsProp("stats.mapped", &(stats->heap_size));
236        /* TODO: Can we add free bytes? */
237        stats->fragmentation_size = stats->heap_size - stats->allocated_size;
238    }
239}
240
241size_t mc_get_allocation_size(const void* ptr) {
242    return getAllocSize(ptr);
243}
244
245void mc_get_detailed_stats(char* buffer, int size) {
246    getDetailedStats(buffer, size);
247}
248
249void mc_release_free_memory() {
250    releaseFreeMemory();
251}
252
253alloc_hooks_type get_alloc_hooks_type(void) {
254    return type;
255}
256