/* * %CopyrightBegin% * * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * %CopyrightEnd% */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "sys.h" #include "erl_mtrace.h" #ifdef ERTS_CAN_TRACK_MALLOC #if defined(HAVE_END_SYMBOL) extern char end; #elif defined(HAVE__END_SYMBOL) extern char _end; #endif static int inited = 0; static int init(void); static volatile char *heap_start = NULL; static volatile char *heap_end = NULL; #if defined(ERTS___AFTER_MORECORE_HOOK_CAN_TRACK_MALLOC) /* ----------------- */ #ifdef HAVE_MALLOC_H # include #endif #undef SBRK_0 #define SBRK_0 sbrk(0) static void init_hook(void) { __after_morecore_hook = erts_mtrace_update_heap_size; if (inited) return; heap_end = NULL; #if defined(HAVE_END_SYMBOL) heap_start = &end; #elif defined(HAVE__END_SYMBOL) heap_start = &_end; #else heap_start = SBRK_0; if (heap_start == (SBRK_RET_TYPE) -1) { heap_start = NULL; return; } #endif inited = 1; } static int init(void) { init_hook(); return inited; } void (*__malloc_initialize_hook)(void) = init_hook; #elif defined(ERTS_BRK_WRAPPERS_CAN_TRACK_MALLOC) /* ------------------------ */ #ifdef HAVE_DLFCN_H # include #endif #undef SBRK_0 #define SBRK_0 (*real_sbrk)(0) #ifndef HAVE_SBRK # error no sbrk() #endif #if !defined(HAVE_END_SYMBOL) && !defined(HAVE__END_SYMBOL) # error no 'end' nor '_end' #endif static void update_heap_size(char *new_end); #define SBRK_IMPL(RET_TYPE, FUNC, ARG_TYPE) \ RET_TYPE FUNC (ARG_TYPE); \ static RET_TYPE (*real_ ## FUNC)(ARG_TYPE) = NULL; \ RET_TYPE FUNC (ARG_TYPE arg) \ { \ RET_TYPE res; \ if (!inited && !init()) \ return (RET_TYPE) -1; \ res = (*real_ ## FUNC)(arg); \ if (erts_mtrace_enabled && res != ((RET_TYPE) -1)) \ update_heap_size((char *) (*real_ ## FUNC)(0)); \ return res; \ } #define BRK_IMPL(RET_TYPE, FUNC, ARG_TYPE) \ RET_TYPE FUNC (ARG_TYPE); \ static RET_TYPE (*real_ ## FUNC)(ARG_TYPE) = NULL; \ RET_TYPE FUNC (ARG_TYPE arg) \ { \ RET_TYPE res; \ if (!inited && !init()) \ return (RET_TYPE) -1; \ res = (*real_ ## FUNC)(arg); \ if (erts_mtrace_enabled && res != ((RET_TYPE) -1)) \ update_heap_size((char *) arg); \ return res; \ } SBRK_IMPL(SBRK_RET_TYPE, sbrk, SBRK_ARG_TYPE) #ifdef HAVE_BRK BRK_IMPL(BRK_RET_TYPE, brk, BRK_ARG_TYPE) #endif #ifdef HAVE__SBRK SBRK_IMPL(SBRK_RET_TYPE, _sbrk, SBRK_ARG_TYPE) #endif #ifdef HAVE__BRK BRK_IMPL(BRK_RET_TYPE, _brk, BRK_ARG_TYPE) #endif #ifdef HAVE___SBRK SBRK_IMPL(SBRK_RET_TYPE, __sbrk, SBRK_ARG_TYPE) #endif #ifdef HAVE___BRK BRK_IMPL(BRK_RET_TYPE, __brk, BRK_ARG_TYPE) #endif static int init(void) { if (inited) return 1; #define INIT_XBRK_SYM(SYM) \ do { \ if (!real_ ## SYM) { \ real_ ## SYM = dlsym(RTLD_NEXT, #SYM); \ if (!real_ ## SYM) { \ errno = ENOMEM; \ return 0; \ } \ } \ } while (0) heap_end = NULL; #if defined(HAVE_END_SYMBOL) heap_start = &end; #elif defined(HAVE__END_SYMBOL) heap_start = &_end; #endif INIT_XBRK_SYM(sbrk); #ifdef HAVE_BRK INIT_XBRK_SYM(brk); #endif #ifdef HAVE__SBRK INIT_XBRK_SYM(_sbrk); #endif #ifdef HAVE__BRK INIT_XBRK_SYM(_brk); #endif #ifdef HAVE___SBRK INIT_XBRK_SYM(__sbrk); #endif #ifdef HAVE___BRK INIT_XBRK_SYM(__brk); #endif return inited = 1; #undef INIT_XBRK_SYM } #endif /* #elif defined(ERTS_BRK_WRAPPERS_CAN_TRACK_MALLOC) */ /* ----------- */ static void update_heap_size(char *new_end) { volatile char *new_start, *old_start, *old_end; Uint size; if (new_end == ((char *) -1)) return; new_start = (old_start = heap_start); old_end = heap_end; heap_end = new_end; if (new_end < old_start || !old_start) heap_start = (new_start = new_end); size = (Uint) (new_end - new_start); if (!old_end) { if (size) erts_mtrace_crr_alloc((void *) new_start, ERTS_ALC_A_SYSTEM, ERTS_MTRACE_SEGMENT_ID, size); else heap_end = NULL; } else { if (old_end != new_end || old_start != new_start) { if (size) erts_mtrace_crr_realloc((void *) new_start, ERTS_ALC_A_SYSTEM, ERTS_MTRACE_SEGMENT_ID, (void *) old_start, size); else { if (old_start) erts_mtrace_crr_free(ERTS_ALC_A_SYSTEM, ERTS_MTRACE_SEGMENT_ID, (void *) old_start); heap_end = NULL; } } } } #endif /* #ifdef ERTS_CAN_TRACK_MALLOC */ void erts_mtrace_update_heap_size(void) { #ifdef ERTS_CAN_TRACK_MALLOC if (erts_mtrace_enabled && (inited || init())) update_heap_size((char *) SBRK_0); #endif }