453fce9e77
TEST=ci Change-Id: I319c932a6f4b6e18d7e53b66d69702bf017e4b93 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/313060 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Alexander Aprelev <aam@google.com>
179 lines
6.3 KiB
C++
179 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"
|
|
#include "vm/allocation.h"
|
|
#include "vm/globals.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_
|