44 * SPDX-License-Identifier: Apache-2.0
55 */
66
7+ #include < cmath>
78#include < dlfcn.h>
89#include < pthread.h>
910#include < stdlib.h>
1213#include " libraries.h"
1314#include " mallocTracer.h"
1415#include " os.h"
16+ #include " pidController.h"
1517#include " profiler.h"
1618#include " symbols.h"
1719#include " tsc.h"
@@ -102,9 +104,12 @@ extern "C" void* aligned_alloc_hook(size_t alignment, size_t size) {
102104 return ret;
103105}
104106
105- u64 MallocTracer::_interval;
107+ volatile u64 MallocTracer::_interval;
106108bool MallocTracer::_nofree;
107- volatile u64 MallocTracer::_allocated_bytes;
109+ volatile u64 MallocTracer::_bytes_until_sample;
110+ u64 MallocTracer::_configured_interval;
111+ volatile u64 MallocTracer::_sample_count;
112+ u64 MallocTracer::_last_config_update_ts;
108113
109114Mutex MallocTracer::_patch_lock;
110115int MallocTracer::_patched_libs = 0 ;
@@ -225,14 +230,71 @@ void MallocTracer::patchLibraries() {
225230 }
226231}
227232
233+ u64 MallocTracer::nextPoissonInterval () {
234+ u64 s = TSC::ticks ();
235+ s ^= s >> 12 ;
236+ s ^= s << 25 ;
237+ s ^= s >> 27 ;
238+ double u = (double )(s * 0x2545F4914F6CDD1DULL >> 11 ) / (double )(1ULL << 53 );
239+ if (u < 1e-18 ) u = 1e-18 ;
240+ return (u64 )((double )_interval * -log (u));
241+ }
242+
243+ bool MallocTracer::shouldSample (size_t size) {
244+ if (_interval <= 1 ) return true ;
245+ while (true ) {
246+ u64 prev = _bytes_until_sample;
247+ if (size < prev) {
248+ if (__sync_bool_compare_and_swap (&_bytes_until_sample, prev, prev - size))
249+ return false ;
250+ } else {
251+ u64 next = nextPoissonInterval ();
252+ if (__sync_bool_compare_and_swap (&_bytes_until_sample, prev, next))
253+ return true ;
254+ }
255+ }
256+ }
257+
258+ void MallocTracer::updateConfiguration (u64 events, double time_coefficient) {
259+ static PidController pid (TARGET_SAMPLES_PER_WINDOW,
260+ 31 , 511 , 3 , CONFIG_UPDATE_CHECK_PERIOD_SECS, 15 );
261+ double signal = pid.compute (events, time_coefficient);
262+ int64_t new_interval = (int64_t )_interval - (int64_t )signal;
263+ if (new_interval < (int64_t )_configured_interval)
264+ new_interval = (int64_t )_configured_interval;
265+ if (new_interval > (int64_t )(1ULL << 40 ))
266+ new_interval = (int64_t )(1ULL << 40 );
267+ _interval = (u64 )new_interval;
268+ }
269+
228270void MallocTracer::recordMalloc (void * address, size_t size) {
229- if (updateCounter (_allocated_bytes, size, _interval)) {
271+ if (shouldSample (size)) {
272+ u64 current_interval = _interval;
230273 MallocEvent event;
231274 event._start_time = TSC::ticks ();
232275 event._address = (uintptr_t )address;
233276 event._size = size;
277+ event._weight = (float )(size == 0 || current_interval <= 1
278+ ? 1.0
279+ : 1.0 / (1.0 - exp (-(double )size / (double )current_interval)));
234280
235281 Profiler::instance ()->recordSample (NULL , size, OS::threadId (), BCI_NATIVE_MALLOC, 0 , &event);
282+
283+ u64 current_samples = __sync_add_and_fetch (&_sample_count, 1 );
284+ if ((current_samples % TARGET_SAMPLES_PER_WINDOW) == 0 ) {
285+ u64 now = OS::nanotime ();
286+ u64 prev_ts = __atomic_load_n (&_last_config_update_ts, __ATOMIC_RELAXED);
287+ u64 time_diff = now - prev_ts;
288+ u64 check_period_ns = (u64 )CONFIG_UPDATE_CHECK_PERIOD_SECS * 1000000000ULL ;
289+ if (time_diff > check_period_ns) {
290+ if (__atomic_compare_exchange (&_last_config_update_ts, &prev_ts, &now,
291+ false , __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) {
292+ __sync_fetch_and_add (&_sample_count, -current_samples);
293+ updateConfiguration (current_samples,
294+ (double )check_period_ns / time_diff);
295+ }
296+ }
297+ }
236298 }
237299}
238300
@@ -246,9 +308,12 @@ void MallocTracer::recordFree(void* address) {
246308}
247309
248310Error MallocTracer::start (Arguments& args) {
249- _interval = args._nativemem > 0 ? args._nativemem : 0 ;
311+ _configured_interval = args._nativemem > 0 ? args._nativemem : 0 ;
312+ _interval = _configured_interval;
250313 _nofree = args._nofree ;
251- _allocated_bytes = 0 ;
314+ _bytes_until_sample = _configured_interval > 1 ? nextPoissonInterval () : 0 ;
315+ _sample_count = 0 ;
316+ _last_config_update_ts = OS::nanotime ();
252317
253318 if (!_initialized) {
254319 initialize ();
0 commit comments