Files
sdk/runtime/vm/heap/sampler.h
T
Ben Konyi 8d97964e6e [ VM ] Move class name from heap profiler report callback to allocation callback
Moving the class name of the allocated object to the allocation callback
gives more flexibility to the embedder, which may decide to track all
sampled allocations, not just those that are still alive.

TEST=Updated DartAPI_HeapSampling_*

Change-Id: Iaae290e15b67ca3e47d72e1896fa6e32b2d4b081
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/297761
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
2023-04-25 13:02:49 +00:00

178 lines
6.3 KiB
C++

// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_HEAP_SAMPLER_H_
#define RUNTIME_VM_HEAP_SAMPLER_H_
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
#include <atomic>
#include "include/dart_api.h"
namespace dart {
// Forward declarations.
class RwLock;
class Thread;
// Poisson sampler for memory allocations. We apply sampling individually to
// each byte. The whole allocation gets accounted as often as the number of
// sampled bytes it contains.
//
// Heavily inspired by Perfetto's Sampler class:
// https://source.chromium.org/chromium/chromium/src/+/531db6ec90bd7194d4d8064588966a0118d1495c:third_party/perfetto/src/profiling/memory/sampler.h;l=34
class HeapProfileSampler {
public:
explicit HeapProfileSampler(Thread* thread);
// Enables or disables heap profiling for all threads.
//
// NOTE: the enabled state will update on a thread-by-thread basis once
// the thread performs an interrupt check. There is no guarantee that the
// enabled state will be updated for each thread by the time this method
// returns.
static void Enable(bool enabled);
static bool enabled() { return enabled_; }
// Updates the heap profiling sampling interval for all threads.
//
// NOTE: the sampling interval will update on a thread-by-thread basis once
// the thread performs an interrupt check. There is no guarantee that the
// sampling interval will be updated for each thread by the time this method
// returns.
static void SetSamplingInterval(intptr_t bytes_interval);
// Updates the callback that's invoked when a sample is collected.
static void SetSamplingCallback(
Dart_HeapSamplingCreateCallback create_callback,
Dart_HeapSamplingDeleteCallback delete_callback);
static Dart_HeapSamplingDeleteCallback delete_callback() {
return delete_callback_;
}
void Initialize();
void Cleanup() {
ResetState();
last_sample_size_ = kUninitialized;
}
// Notifies the thread that it needs to update the enabled state of the heap
// sampling profiler.
//
// This method is safe to call from any thread.
void ScheduleUpdateThreadEnable();
// Returns true if [ScheduleUpdateThreadEnable()] has been invoked.
//
// Calling this method will clear the state set by
// [ScheduleUpdateThreadEnable()].
//
// This method is safe to call from any thread.
bool ShouldUpdateThreadEnable() {
return schedule_thread_enable_.exchange(false);
}
// Updates the enabled state of the thread's heap sampling profiler.
//
// WARNING: This method can only be called by the thread associated with this
// profiler instance to avoid concurrent modification of the thread's TLAB.
void UpdateThreadEnable();
// Notifies the thread that it needs to update the sampling interval of its
// heap sampling profiler.
//
// This method is safe to call from any thread.
void ScheduleSetThreadSamplingInterval();
// Returns true if [ScheduleSetThreadSamplingInterval()] has been invoked.
//
// Calling this method will clear the state set by
// [ScheduleSetThreadEnable()].
//
// This method is safe to call from any thread.
bool ShouldSetThreadSamplingInterval() {
return schedule_thread_set_sampling_interval_.exchange(false);
}
// Updates the sampling interval of the thread's heap sampling profiler.
//
// WARNING: This method can only be called by the thread associated with this
// profiler instance to avoid concurrent modification of the thread's TLAB.
void SetThreadSamplingInterval();
// Updates internal book keeping tracking the remaining size of the sampling
// interval. This method must be called when a TLAB is torn down to ensure
// that a future TLAB is initialized with the correct sampling interval.
void HandleReleasedTLAB(Thread* thread);
// Handles the creation of a new TLAB by updating its boundaries based on the
// remaining sampling interval.
//
// is_first_tlab should be set to true if this is the first TLAB associated
// with thread_ in order to correctly set the TLAB boundaries to match the
// remaining sampling interval that's been used to keep track of old space
// allocations.
void HandleNewTLAB(intptr_t old_tlab_remaining_space, bool is_first_tlab);
void* InvokeCallbackForLastSample(intptr_t cid);
bool HasOutstandingSample() const {
return last_sample_size_ != kUninitialized;
}
void SampleNewSpaceAllocation(intptr_t allocation_size);
void SampleOldSpaceAllocation(intptr_t allocation_size);
private:
void ResetState();
void ResetIntervalState() { interval_to_next_sample_ = kUninitialized; }
void UpdateThreadEnableLocked();
void SetThreadSamplingIntervalLocked();
void SetNextSamplingIntervalLocked(intptr_t next_interval);
intptr_t GetNextSamplingIntervalLocked();
intptr_t NumberOfSamplesLocked(intptr_t allocation_size);
// Helper to calculate the remaining sampling interval based on TLAB
// boundaries. Returns kUninitialized if there's no active TLAB.
intptr_t remaining_TLAB_interval() const;
std::atomic<bool> schedule_thread_enable_ = false;
std::atomic<bool> schedule_thread_set_sampling_interval_ = false;
// Protects sampling logic from modifications of callback_, sampling_interval,
// and enabled_ while collecting a sample.
//
// This lock should be acquired using WriteRwLocker when modifying static
// state, and should be acquired using ReadRwLocker when accessing static
// state from instances of HeapProfileSampler.
static RwLock* lock_;
static bool enabled_;
static Dart_HeapSamplingCreateCallback create_callback_;
static Dart_HeapSamplingDeleteCallback delete_callback_;
static intptr_t sampling_interval_;
static constexpr intptr_t kUninitialized = -1;
static constexpr intptr_t kDefaultSamplingInterval = 512 * KB;
bool thread_enabled_ = false;
intptr_t interval_to_next_sample_ = kUninitialized;
intptr_t next_tlab_offset_ = kUninitialized;
intptr_t last_sample_size_ = kUninitialized;
Thread* thread_;
DISALLOW_COPY_AND_ASSIGN(HeapProfileSampler);
};
} // namespace dart
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
#endif // RUNTIME_VM_HEAP_SAMPLER_H_