1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 static pthread_mutex_t malloc_disabled_lock = PTHREAD_MUTEX_INITIALIZER;
18 static bool malloc_disabled_tcache;
19 
je_iterate(uintptr_t base,size_t size,void (* callback)(uintptr_t ptr,size_t size,void * arg),void * arg)20 int je_iterate(uintptr_t base, size_t size,
21     void (*callback)(uintptr_t ptr, size_t size, void* arg), void* arg) {
22   size_t pagesize = getpagesize();
23   tsd_t* tsd = tsd_fetch_min();
24   rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
25 
26   // Make sure the pointer is aligned to at least 8 bytes.
27   uintptr_t ptr = (base + 7) & ~7;
28   uintptr_t end_ptr = ptr + size;
29   while (ptr < end_ptr) {
30     extent_t* extent = iealloc(tsd_tsdn(tsd), (void*)ptr);
31     if (extent == NULL) {
32       // Skip to the next page, guaranteed no other pointers on this page.
33       ptr += pagesize;
34       continue;
35     }
36 
37     if (extent_szind_get_maybe_invalid(extent) >= NSIZES) {
38       // Ignore this unused extent.
39       ptr = (uintptr_t)extent_past_get(extent);
40       continue;
41     }
42 
43     szind_t szind;
44     bool slab;
45     rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx, ptr, true, &szind, &slab);
46     if (slab) {
47       // Small allocation.
48       szind_t binind = extent_szind_get(extent);
49       const bin_info_t* bin_info = &bin_infos[binind];
50       arena_slab_data_t* slab_data = extent_slab_data_get(extent);
51 
52       uintptr_t first_ptr = (uintptr_t)extent_addr_get(extent);
53       size_t bin_size = bin_info->reg_size;
54       // Align the pointer to the bin size.
55       ptr = (ptr + bin_size - 1) & ~(bin_size - 1);
56       for (size_t bit = (ptr - first_ptr) / bin_size; bit < bin_info->bitmap_info.nbits; bit++) {
57         if (bitmap_get(slab_data->bitmap, &bin_info->bitmap_info, bit)) {
58           uintptr_t allocated_ptr = first_ptr + bin_size * bit;
59           if (allocated_ptr >= end_ptr) {
60             break;
61           }
62           callback(allocated_ptr, bin_size, arg);
63         }
64       }
65     } else if (extent_state_get(extent) == extent_state_active) {
66       // Large allocation.
67       uintptr_t base_ptr = (uintptr_t)extent_addr_get(extent);
68       if (ptr <= base_ptr) {
69         // This extent is actually allocated and within the range to check.
70         callback(base_ptr, extent_usize_get(extent), arg);
71       }
72     }
73     ptr = (uintptr_t)extent_past_get(extent);
74   }
75   return 0;
76 }
77 
je_malloc_disable_prefork()78 static void je_malloc_disable_prefork() {
79   pthread_mutex_lock(&malloc_disabled_lock);
80 }
81 
je_malloc_disable_postfork_parent()82 static void je_malloc_disable_postfork_parent() {
83   pthread_mutex_unlock(&malloc_disabled_lock);
84 }
85 
je_malloc_disable_postfork_child()86 static void je_malloc_disable_postfork_child() {
87   pthread_mutex_init(&malloc_disabled_lock, NULL);
88 }
89 
je_malloc_disable_init()90 void je_malloc_disable_init() {
91   if (pthread_atfork(je_malloc_disable_prefork,
92       je_malloc_disable_postfork_parent, je_malloc_disable_postfork_child) != 0) {
93     malloc_write("<jemalloc>: Error in pthread_atfork()\n");
94     if (opt_abort)
95       abort();
96   }
97 }
98 
je_malloc_disable()99 void je_malloc_disable() {
100   static pthread_once_t once_control = PTHREAD_ONCE_INIT;
101   pthread_once(&once_control, je_malloc_disable_init);
102 
103   pthread_mutex_lock(&malloc_disabled_lock);
104   bool new_tcache = false;
105   size_t old_len = sizeof(malloc_disabled_tcache);
106 
107   // Disable the tcache (if not already disabled) so that we don't
108   // have to search the tcache for pointers.
109   je_mallctl("thread.tcache.enabled",
110       &malloc_disabled_tcache, &old_len,
111       &new_tcache, sizeof(new_tcache));
112   jemalloc_prefork();
113 }
114 
je_malloc_enable()115 void je_malloc_enable() {
116   jemalloc_postfork_parent();
117   if (malloc_disabled_tcache) {
118     // Re-enable the tcache if it was enabled before the disabled call.
119     je_mallctl("thread.tcache.enabled", NULL, NULL,
120         &malloc_disabled_tcache, sizeof(malloc_disabled_tcache));
121   }
122   pthread_mutex_unlock(&malloc_disabled_lock);
123 }
124