151c0b2f7Stbbdev /*
2*c21e688aSSergey Zheltov     Copyright (c) 2005-2022 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev // The original source for this code is
1851c0b2f7Stbbdev // Copyright (c) 2011, Google Inc.
1951c0b2f7Stbbdev // All rights reserved.
2051c0b2f7Stbbdev //
2151c0b2f7Stbbdev // Redistribution and use in source and binary forms, with or without
2251c0b2f7Stbbdev // modification, are permitted provided that the following conditions are
2351c0b2f7Stbbdev // met:
2451c0b2f7Stbbdev //
2551c0b2f7Stbbdev //     * Redistributions of source code must retain the above copyright
2651c0b2f7Stbbdev // notice, this list of conditions and the following disclaimer.
2751c0b2f7Stbbdev //     * Redistributions in binary form must reproduce the above
2851c0b2f7Stbbdev // copyright notice, this list of conditions and the following disclaimer
2951c0b2f7Stbbdev // in the documentation and/or other materials provided with the
3051c0b2f7Stbbdev // distribution.
3151c0b2f7Stbbdev //     * Neither the name of Google Inc. nor the names of its
3251c0b2f7Stbbdev // contributors may be used to endorse or promote products derived from
3351c0b2f7Stbbdev // this software without specific prior written permission.
3451c0b2f7Stbbdev //
3551c0b2f7Stbbdev // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3651c0b2f7Stbbdev // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3751c0b2f7Stbbdev // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3851c0b2f7Stbbdev // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3951c0b2f7Stbbdev // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
4051c0b2f7Stbbdev // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
4151c0b2f7Stbbdev // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
4251c0b2f7Stbbdev // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
4351c0b2f7Stbbdev // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4451c0b2f7Stbbdev // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
4551c0b2f7Stbbdev // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4651c0b2f7Stbbdev 
4751c0b2f7Stbbdev #include <AvailabilityMacros.h>
4851c0b2f7Stbbdev #include <malloc/malloc.h>
4951c0b2f7Stbbdev #include <mach/mach.h>
5051c0b2f7Stbbdev #include <stdlib.h>
5151c0b2f7Stbbdev 
enumerator(task_t,void *,unsigned,vm_address_t,memory_reader_t,vm_range_recorder_t)5251c0b2f7Stbbdev static kern_return_t enumerator(task_t, void *, unsigned, vm_address_t,
5351c0b2f7Stbbdev                                 memory_reader_t, vm_range_recorder_t)
5451c0b2f7Stbbdev {
5551c0b2f7Stbbdev     return KERN_FAILURE;
5651c0b2f7Stbbdev }
5751c0b2f7Stbbdev 
good_size(malloc_zone_t *,size_t size)5851c0b2f7Stbbdev static size_t good_size(malloc_zone_t *, size_t size)
5951c0b2f7Stbbdev {
6051c0b2f7Stbbdev     return size;
6151c0b2f7Stbbdev }
6251c0b2f7Stbbdev 
zone_check(malloc_zone_t *)6351c0b2f7Stbbdev static boolean_t zone_check(malloc_zone_t *) /* Consistency checker */
6451c0b2f7Stbbdev {
6551c0b2f7Stbbdev     return true;
6651c0b2f7Stbbdev }
6751c0b2f7Stbbdev 
zone_print(malloc_zone_t *,boolean_t)6851c0b2f7Stbbdev static void zone_print(malloc_zone_t *, boolean_t) { }
zone_log(malloc_zone_t *,void *)6951c0b2f7Stbbdev static void zone_log(malloc_zone_t *, void *) {}
zone_force_lock(malloc_zone_t *)7051c0b2f7Stbbdev static void zone_force_lock(malloc_zone_t *) {}
zone_force_unlock(malloc_zone_t *)7151c0b2f7Stbbdev static void zone_force_unlock(malloc_zone_t *) {}
7251c0b2f7Stbbdev 
zone_statistics(malloc_zone_t *,malloc_statistics_t * s)7351c0b2f7Stbbdev static void zone_statistics(malloc_zone_t *, malloc_statistics_t *s)
7451c0b2f7Stbbdev {
7551c0b2f7Stbbdev     s->blocks_in_use = 0;
7651c0b2f7Stbbdev     s->size_in_use = s->max_size_in_use = s->size_allocated = 0;
7751c0b2f7Stbbdev }
7851c0b2f7Stbbdev 
zone_locked(malloc_zone_t *)7951c0b2f7Stbbdev static boolean_t zone_locked(malloc_zone_t *)
8051c0b2f7Stbbdev {
8151c0b2f7Stbbdev     return false;
8251c0b2f7Stbbdev }
8351c0b2f7Stbbdev 
impl_zone_enable_discharge_checking(malloc_zone_t *)8451c0b2f7Stbbdev static boolean_t impl_zone_enable_discharge_checking(malloc_zone_t *)
8551c0b2f7Stbbdev {
8651c0b2f7Stbbdev     return false;
8751c0b2f7Stbbdev }
8851c0b2f7Stbbdev 
impl_zone_disable_discharge_checking(malloc_zone_t *)8951c0b2f7Stbbdev static void impl_zone_disable_discharge_checking(malloc_zone_t *) {}
impl_zone_discharge(malloc_zone_t *,void *)9051c0b2f7Stbbdev static void impl_zone_discharge(malloc_zone_t *, void *) {}
impl_zone_destroy(struct _malloc_zone_t *)9151c0b2f7Stbbdev static void impl_zone_destroy(struct _malloc_zone_t *) {}
9251c0b2f7Stbbdev 
9351c0b2f7Stbbdev /* note: impl_malloc_usable_size() is called for each free() call, so it must be fast */
impl_malloc_usable_size(struct _malloc_zone_t *,const void * ptr)9451c0b2f7Stbbdev static size_t impl_malloc_usable_size(struct _malloc_zone_t *, const void *ptr)
9551c0b2f7Stbbdev {
9651c0b2f7Stbbdev     // malloc_usable_size() is used by macOS* to recognize which memory manager
9751c0b2f7Stbbdev     // allocated the address, so our wrapper must not redirect to the original function.
9857f524caSIlya Isaev     return __TBB_malloc_safer_msize(const_cast<void*>(ptr), nullptr);
9951c0b2f7Stbbdev }
10051c0b2f7Stbbdev 
10151c0b2f7Stbbdev static void *impl_malloc(struct _malloc_zone_t *, size_t size);
10251c0b2f7Stbbdev static void *impl_calloc(struct _malloc_zone_t *, size_t num_items, size_t size);
10351c0b2f7Stbbdev static void *impl_valloc(struct _malloc_zone_t *, size_t size);
10451c0b2f7Stbbdev static void impl_free(struct _malloc_zone_t *, void *ptr);
10551c0b2f7Stbbdev static void *impl_realloc(struct _malloc_zone_t *, void *ptr, size_t size);
10651c0b2f7Stbbdev static void *impl_memalign(struct _malloc_zone_t *, size_t alignment, size_t size);
10751c0b2f7Stbbdev 
10851c0b2f7Stbbdev /* ptr is in zone and have reported size */
impl_free_definite_size(struct _malloc_zone_t *,void * ptr,size_t size)10951c0b2f7Stbbdev static void impl_free_definite_size(struct _malloc_zone_t*, void *ptr, size_t size)
11051c0b2f7Stbbdev {
11151c0b2f7Stbbdev     __TBB_malloc_free_definite_size(ptr, size);
11251c0b2f7Stbbdev }
11351c0b2f7Stbbdev 
11451c0b2f7Stbbdev /* Empty out caches in the face of memory pressure. */
impl_pressure_relief(struct _malloc_zone_t *,size_t)11551c0b2f7Stbbdev static size_t impl_pressure_relief(struct _malloc_zone_t *, size_t  /* goal */)
11651c0b2f7Stbbdev {
11751c0b2f7Stbbdev     return 0;
11851c0b2f7Stbbdev }
11951c0b2f7Stbbdev 
12057f524caSIlya Isaev static malloc_zone_t *system_zone = nullptr;
12151c0b2f7Stbbdev 
12251c0b2f7Stbbdev struct DoMallocReplacement {
DoMallocReplacementDoMallocReplacement12351c0b2f7Stbbdev     DoMallocReplacement() {
12451c0b2f7Stbbdev         static malloc_introspection_t introspect;
12551c0b2f7Stbbdev         memset(&introspect, 0, sizeof(malloc_introspection_t));
12651c0b2f7Stbbdev         static malloc_zone_t zone;
12751c0b2f7Stbbdev         memset(&zone, 0, sizeof(malloc_zone_t));
12851c0b2f7Stbbdev 
12951c0b2f7Stbbdev         introspect.enumerator = &enumerator;
13051c0b2f7Stbbdev         introspect.good_size = &good_size;
13151c0b2f7Stbbdev         introspect.check = &zone_check;
13251c0b2f7Stbbdev         introspect.print = &zone_print;
13351c0b2f7Stbbdev         introspect.log = zone_log;
13451c0b2f7Stbbdev         introspect.force_lock = &zone_force_lock;
13551c0b2f7Stbbdev         introspect.force_unlock = &zone_force_unlock;
13651c0b2f7Stbbdev         introspect.statistics = zone_statistics;
13751c0b2f7Stbbdev         introspect.zone_locked = &zone_locked;
13851c0b2f7Stbbdev         introspect.enable_discharge_checking = &impl_zone_enable_discharge_checking;
13951c0b2f7Stbbdev         introspect.disable_discharge_checking = &impl_zone_disable_discharge_checking;
14051c0b2f7Stbbdev         introspect.discharge = &impl_zone_discharge;
14151c0b2f7Stbbdev 
14251c0b2f7Stbbdev         zone.size = &impl_malloc_usable_size;
14351c0b2f7Stbbdev         zone.malloc = &impl_malloc;
14451c0b2f7Stbbdev         zone.calloc = &impl_calloc;
14551c0b2f7Stbbdev         zone.valloc = &impl_valloc;
14651c0b2f7Stbbdev         zone.free = &impl_free;
14751c0b2f7Stbbdev         zone.realloc = &impl_realloc;
14851c0b2f7Stbbdev         zone.destroy = &impl_zone_destroy;
14951c0b2f7Stbbdev         zone.zone_name = "tbbmalloc";
15051c0b2f7Stbbdev         zone.introspect = &introspect;
15151c0b2f7Stbbdev         zone.version = 8;
15251c0b2f7Stbbdev         zone.memalign = impl_memalign;
15351c0b2f7Stbbdev         zone.free_definite_size = &impl_free_definite_size;
15451c0b2f7Stbbdev         zone.pressure_relief = &impl_pressure_relief;
15551c0b2f7Stbbdev 
15651c0b2f7Stbbdev         // make sure that default purgeable zone is initialized
15751c0b2f7Stbbdev         malloc_default_purgeable_zone();
15851c0b2f7Stbbdev         void* ptr = malloc(1);
15951c0b2f7Stbbdev         // get all registered memory zones
16051c0b2f7Stbbdev         unsigned zcount = 0;
16157f524caSIlya Isaev         malloc_zone_t** zone_array = nullptr;
16257f524caSIlya Isaev         kern_return_t errorcode = malloc_get_all_zones(mach_task_self(),nullptr,(vm_address_t**)&zone_array,&zcount);
16351c0b2f7Stbbdev         if (!errorcode && zone_array && zcount>0) {
16451c0b2f7Stbbdev             // find the zone that allocated ptr
16551c0b2f7Stbbdev             for (unsigned i=0; i<zcount; ++i) {
16651c0b2f7Stbbdev                 malloc_zone_t* z = zone_array[i];
16751c0b2f7Stbbdev                 if (z && z->size(z,ptr)>0) { // the right one is found
16851c0b2f7Stbbdev                     system_zone = z;
16951c0b2f7Stbbdev                     break;
17051c0b2f7Stbbdev                 }
17151c0b2f7Stbbdev             }
17251c0b2f7Stbbdev         }
17351c0b2f7Stbbdev         free(ptr);
17451c0b2f7Stbbdev 
17551c0b2f7Stbbdev         malloc_zone_register(&zone);
17651c0b2f7Stbbdev         if (system_zone) {
17751c0b2f7Stbbdev             // after unregistration of the system zone, the last registered (i.e. our) zone becomes the default
17851c0b2f7Stbbdev             malloc_zone_unregister(system_zone);
17951c0b2f7Stbbdev             // register the system zone back
18051c0b2f7Stbbdev             malloc_zone_register(system_zone);
18151c0b2f7Stbbdev         }
18251c0b2f7Stbbdev     }
18351c0b2f7Stbbdev };
18451c0b2f7Stbbdev 
18551c0b2f7Stbbdev static DoMallocReplacement doMallocReplacement;
18651c0b2f7Stbbdev 
187