#include "mimalloc.h"
#include "mimalloc/internal.h"
#include "mimalloc/prim-tls.h"
size_t _mi_theap_update_sample_rate(mi_theap_t* theap) {
const size_t old_sample_rate = theap->sample_rate;
theap->sample_rate = theap->profile_sample_rate;
if (theap->sample_rate == 0 || (theap->guarded_sample_rate!=0 && theap->sample_rate > theap->guarded_sample_rate)) {
theap->sample_rate = theap->guarded_sample_rate;
}
if (theap->sample_countdown > theap->sample_rate) {
theap->sample_countdown = theap->sample_rate; }
return old_sample_rate;
}
mi_decl_noinline mi_decl_restrict void* _mi_theap_malloc_sampled(mi_theap_t* theap, size_t req_size, bool zero, mi_page_t** ppage) mi_attr_noexcept
{
mi_assert_internal(req_size <= MI_MAX_ALLOC_SIZE);
mi_assert_internal((theap->sample_countdown==SIZE_MAX && _mi_is_empty_theap(theap)) ||
(theap->sample_countdown <= MI_SAMPLE_COUNTDOWN_MAX && theap->sample_countdown < req_size));
const size_t size = req_size + MI_PADDING_SIZE;
if (theap->sample_rate==0) {
if (!_mi_is_empty_theap(theap)) { theap->sample_countdown = MI_SAMPLE_COUNTDOWN_MAX; }
return _mi_malloc_generic_no_sample(theap,size,zero,ppage);
}
mi_assert_internal(!_mi_is_empty_theap(theap));
mi_assert_internal(req_size <= SIZE_MAX/2);
mi_assert_internal(theap->sample_rate > 0);
mi_assert_internal(theap->sample_rate <= SIZE_MAX/2);
mi_assert_internal(theap->sample_rate >= theap->sample_countdown);
const uint64_t requested = theap->sample_requested = (uint64_t)theap->sample_rate + (uint64_t)(req_size - theap->sample_countdown) + theap->sample_requested;
mi_assert_internal(requested > 0);
mi_assert_internal(requested >= req_size);
theap->sample_countdown = theap->sample_rate;
mi_assert_internal(theap->profile_sample_rate!=0 || theap->profile_sample_countdown==0);
mi_assert_internal(theap->guarded_sample_rate!=0 || theap->guarded_sample_countdown==0);
bool sample_profile = false;
bool sample_guarded = false;
if (theap->profile_sample_rate!=0) {
if (theap->profile_sample_countdown >= requested) { theap->profile_sample_countdown -= (size_t)requested; } else { sample_profile = true; }
}
if (theap->guarded_sample_rate!=0) {
if (theap->guarded_sample_countdown >= requested) { theap->guarded_sample_countdown -= (size_t)requested; } else { sample_guarded = true; }
}
if (sample_profile) {
theap->profile_sample_countdown = theap->profile_sample_rate; theap->sample_requested = 0; #if MI_PROFILE
return _mi_theap_malloc_profiled(theap,size,requested,zero,ppage);
#endif
}
else if (sample_guarded) {
if (req_size >= theap->guarded_size_min && req_size <= theap->guarded_size_max) {
theap->guarded_sample_countdown = theap->guarded_sample_rate; #if MI_GUARDED
return _mi_theap_malloc_guarded(theap,size,zero,ppage);
#endif
}
#if MI_GUARDED
else {
theap->sample_countdown = 0;
}
#endif
}
return _mi_malloc_generic_no_sample(theap,size,zero,ppage);
}
static mi_profiler_t* mi_heap_profiler(const mi_heap_t* heap) {
return mi_atomic_load_ptr_acquire(mi_profiler_t,&heap->profiler);
}
static mi_profiler_t* mi_theap_get_enabled_profiler(const mi_theap_t* theap) {
mi_heap_t* const heap = _mi_theap_heap(theap);
mi_profiler_t* prof = mi_atomic_load_ptr_relaxed(mi_profiler_t, &heap->profiler);
if (prof!=NULL && mi_profiler_is_enabled(prof) && prof->on_alloc != NULL) {
return prof;
}
else {
return NULL;
}
}
mi_decl_noinline mi_decl_restrict void* _mi_theap_malloc_profiled(mi_theap_t* theap, size_t size, uint64_t requested_since_last_sample, bool zero, mi_page_t** ppage) mi_attr_noexcept
{
mi_assert_internal(theap!=NULL);
mi_assert_internal(size>=MI_PADDING_SIZE);
const size_t req_size = size - MI_PADDING_SIZE;
mi_assert_internal(req_size<=requested_since_last_sample);
mi_profiler_t* const prof = mi_theap_get_enabled_profiler(theap);
if (prof == NULL) { return _mi_malloc_generic_no_sample(theap,size,zero,ppage); }
mi_assert_internal(prof!=NULL && prof->on_alloc!=NULL && mi_profiler_is_enabled(prof));
void* p = NULL;
size_t new_sample_rate = 0;
if (prof->on_free==NULL || prof->sample_data_size==0) {
p = _mi_malloc_generic_no_sample(theap,size,zero,ppage);
if (p==NULL) { return p; }
if (prof->on_alloc!=NULL) {
new_sample_rate = (*prof->on_alloc)(prof, NULL , p, req_size, theap->profile_sample_rate, requested_since_last_sample, _mi_theap_heap(theap) );
}
}
else {
const size_t sample_data_offset = sizeof(mi_block_t);
const size_t sample_user_data_size = _mi_align_up(prof->sample_data_size > MI_PROFILE_SAMPLE_DATA_MAX_SIZE ? MI_PROFILE_SAMPLE_DATA_MAX_SIZE : prof->sample_data_size, sizeof(void*));
const size_t sample_data_size = sizeof(mi_profiler_sample_data_t) + sample_user_data_size; const size_t user_offset = _mi_align_up(sample_data_offset + sample_data_size, MI_MAX_ALIGN_SIZE);
const size_t oversize = user_offset + size;
mi_page_t* page = NULL;
mi_block_t* const block = (mi_block_t*)_mi_malloc_generic_no_sample(theap,oversize,zero,&page);
if (block==NULL) return NULL;
mi_assert_internal(page!=NULL);
if (ppage!=NULL) { *ppage = page; }
#if MI_PAGE_META_SMALL_IS_ALIGNED
if (size <= MI_SMALL_SIZE_MAX) { mi_assert_internal(mi_page_block_size(page) <= MI_SMALL_MAX_OBJ_SIZE); }
#endif
mi_page_set_has_interior_pointers(page, true);
block->next = MI_BLOCK_TAG_PROFILED;
p = (uint8_t*)block + user_offset;
mi_profiler_sample_data_t* const sample_data = (mi_profiler_sample_data_t*)((uint8_t*)block + sample_data_offset);
sample_data->user_data_size = sample_user_data_size;
if (prof->on_alloc!=NULL) {
new_sample_rate = (*prof->on_alloc)(prof, sample_data, p, req_size, theap->profile_sample_rate, requested_since_last_sample, _mi_theap_heap(theap) );
}
}
if (new_sample_rate!=0 && new_sample_rate != (size_t)theap->profile_sample_rate) {
_mi_theap_set_profile_sample_rate(theap,new_sample_rate);
}
mi_theap_stat_counter_increase(theap,profile_samples,1);
return p;
}
void _mi_page_profile_on_free(mi_page_t* page, mi_block_t* block, void* p) {
mi_assert_internal(mi_block_ptr_is_sampled(block,p));
mi_heap_t* const heap = mi_page_heap(page);
if (heap==NULL) return;
mi_profiler_t* prof = mi_heap_profiler(heap);
if (prof==NULL || !mi_profiler_is_enabled(prof) || prof->on_free==NULL) return;
mi_profiler_sample_data_t* const sample_data = (mi_profiler_sample_data_t*)((uint8_t*)block + sizeof(mi_block_t));
(*prof->on_free)(prof, sample_data, p, heap);
}
size_t _mi_theap_set_profile_sample_rate(mi_theap_t* theap, size_t sample_rate) {
const size_t old_sample_rate = theap->profile_sample_rate;
theap->profile_sample_rate = (sample_rate > MI_SAMPLE_RATE_MAX ? MI_SAMPLE_RATE_MAX : sample_rate);
if (theap->profile_sample_countdown > theap->profile_sample_rate) {
theap->profile_sample_countdown = theap->profile_sample_rate; }
_mi_theap_update_sample_rate(theap);
return old_sample_rate;
}
static bool mi_heap_set_profiler(mi_heap_t* heap, mi_profiler_t* profiler) {
mi_profiler_t* previous = (profiler==NULL ? mi_atomic_load_ptr_acquire(mi_profiler_t,&heap->profiler) : NULL); return mi_atomic_cas_ptr_strong_acq_rel(mi_profiler_t, &heap->profiler, &previous, profiler);
}
mi_decl_export bool mi_heap_profile(mi_heap_t* heap, mi_profiler_t* profiler) {
mi_profiler_stop(profiler);
return mi_heap_set_profiler(heap,profiler);
}
mi_decl_export void mi_heap_profile_disable(mi_heap_t* heap) {
static mi_profiler_t mi_nosample_profiler = { NULL, 0, MI_SAMPLE_RATE_MAX, NULL, NULL, NULL };
mi_heap_set_profiler(heap,NULL); mi_heap_set_profiler(heap,&mi_nosample_profiler); }
mi_decl_export bool mi_subproc_profile(mi_subproc_id_t subproc_id, mi_profiler_t* profiler) {
mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id);
if (subproc==NULL) return false;
mi_profiler_stop(profiler);
mi_profiler_t* previous = (profiler==NULL ? mi_atomic_load_ptr_acquire(mi_profiler_t,&subproc->profiler) : NULL); if (!mi_atomic_cas_ptr_strong_acq_rel(mi_profiler_t,&subproc->profiler, &previous, profiler)) { return false; }
mi_lock(&subproc->heaps_lock) {
for (mi_heap_t* heap = subproc->heaps; heap!=NULL; heap = heap->next) {
mi_heap_set_profiler(heap,profiler);
}
}
return true;
}
mi_decl_export bool mi_profile( mi_profiler_t* profiler) {
return mi_subproc_profile(mi_subproc_main(),profiler);
}
bool mi_profiler_start(mi_profiler_t* profiler ) {
if (profiler==NULL) return false;
const bool was_running = mi_profiler_set_enabled(profiler,true);
if (was_running) return true;
mi_heap_t* heap = mi_heap_main();
if (mi_heap_profiler(heap)==profiler) {
mi_theap_t* theap = _mi_heap_theap_peek(heap);
if (theap!=NULL) {
_mi_theap_set_profile_sample_rate(theap,1);
}
}
return false;
}
bool mi_profiler_stop(mi_profiler_t* profiler) {
if (profiler==NULL) return true;
return mi_profiler_set_enabled(profiler,false);
}