Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
'v8_embedder_string': '-node.22',
'v8_embedder_string': '-node.24',

##### V8 defaults for Node.js #####

Expand Down
21 changes: 21 additions & 0 deletions deps/v8/include/v8-profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,11 @@ class V8_EXPORT AllocationProfile {
* been collected by GC.
*/
bool is_live;

/**
* Sample interval in bytes used when this sample was selected.
*/
uint64_t sample_interval;
};

/**
Expand Down Expand Up @@ -1257,6 +1262,22 @@ class V8_EXPORT HeapProfiler {
*/
void StopSamplingHeapProfiler();

/**
* Updates the sampling interval for a currently running sampling heap
* profiler. The new interval is used for future sample scheduling.
*
* No-op if the sampling heap profiler is not running.
*/
void SetSamplingHeapProfilerInterval(uint64_t sample_interval);

/**
* Returns the currently retained allocation samples without materializing
* the full allocation profile tree.
*
* Returns an empty vector if the sampling heap profiler is not running.
*/
std::vector<AllocationProfile::Sample> GetSamplingHeapProfilerSamples();

/**
* Returns the sampled profile of allocations allocated (and still live) since
* StartSamplingHeapProfiler was called. The ownership of the pointer is
Expand Down
11 changes: 11 additions & 0 deletions deps/v8/src/api/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12098,6 +12098,17 @@ void HeapProfiler::StopSamplingHeapProfiler() {
reinterpret_cast<i::HeapProfiler*>(this)->StopSamplingHeapProfiler();
}

void HeapProfiler::SetSamplingHeapProfilerInterval(uint64_t sample_interval) {
reinterpret_cast<i::HeapProfiler*>(this)->SetSamplingHeapProfilerInterval(
sample_interval);
}

std::vector<AllocationProfile::Sample>
HeapProfiler::GetSamplingHeapProfilerSamples() {
return reinterpret_cast<i::HeapProfiler*>(this)
->GetSamplingHeapProfilerSamples();
}

AllocationProfile* HeapProfiler::GetAllocationProfile() {
return reinterpret_cast<i::HeapProfiler*>(this)->GetAllocationProfile();
}
Expand Down
12 changes: 12 additions & 0 deletions deps/v8/src/profiler/heap-profiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,18 @@ void HeapProfiler::StopSamplingHeapProfiler() {
MaybeClearStringsStorage();
}

void HeapProfiler::SetSamplingHeapProfilerInterval(uint64_t sample_interval) {
if (sampling_heap_profiler_) {
sampling_heap_profiler_->SetSamplingInterval(sample_interval);
}
}

std::vector<v8::AllocationProfile::Sample>
HeapProfiler::GetSamplingHeapProfilerSamples() {
if (!sampling_heap_profiler_) return {};
return sampling_heap_profiler_->GetSamples();
}

v8::AllocationProfile* HeapProfiler::GetAllocationProfile() {
if (sampling_heap_profiler_) {
return sampling_heap_profiler_->GetAllocationProfile();
Expand Down
2 changes: 2 additions & 0 deletions deps/v8/src/profiler/heap-profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ class HeapProfiler : public HeapObjectAllocationTracker {
bool StartSamplingHeapProfiler(uint64_t sample_interval, int stack_depth,
v8::HeapProfiler::SamplingFlags);
void StopSamplingHeapProfiler();
void SetSamplingHeapProfilerInterval(uint64_t sample_interval);
std::vector<v8::AllocationProfile::Sample> GetSamplingHeapProfilerSamples();
bool is_sampling_allocations() { return !!sampling_heap_profiler_; }
AllocationProfile* GetAllocationProfile();

Expand Down
63 changes: 45 additions & 18 deletions deps/v8/src/profiler/sampling-heap-profiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ intptr_t SamplingHeapProfiler::Observer::GetNextSampleInterval(uint64_t rate) {
// Samples were collected according to a poisson process. Since we have not
// recorded all allocations, we must approximate the shape of the underlying
// space of allocations based on the samples we have collected. Given that
// we sample at rate R, the probability that an allocation of size S will be
// sampled is 1-exp(-S/R). This function uses the above probability to
// we sample at interval I, the probability that an allocation of size S will
// be sampled is 1-exp(-S/I). This function uses the above probability to
// approximate the true number of allocations with size *size* given that
// *count* samples were observed.
v8::AllocationProfile::Allocation SamplingHeapProfiler::ScaleSample(
size_t size, unsigned int count) const {
double scale = 1.0 / (1.0 - std::exp(-static_cast<double>(size) / rate_));
size_t size, unsigned int count, uint64_t sample_interval) const {
double scale =
1.0 / (1.0 - std::exp(-static_cast<double>(size) / sample_interval));
// Round count instead of truncating.
return {size, static_cast<unsigned int>(count * scale + 0.5)};
}
Expand All @@ -57,15 +58,15 @@ SamplingHeapProfiler::SamplingHeapProfiler(
v8::HeapProfiler::SamplingFlags flags)
: isolate_(Isolate::FromHeap(heap)),
heap_(heap),
allocation_observer_(heap_, static_cast<intptr_t>(rate), rate, this,
allocation_observer_(heap_, static_cast<intptr_t>(rate), this,
isolate_->random_number_generator()),
names_(names),
profile_root_(nullptr, "(root)", v8::UnboundScript::kNoScriptId, 0,
next_node_id()),
stack_depth_(stack_depth),
rate_(rate),
interval_(rate),
flags_(flags) {
CHECK_GT(rate_, 0u);
CHECK_GT(interval_.load(std::memory_order_relaxed), 0u);
heap_->AddAllocationObserversToAllSpaces(&allocation_observer_,
&allocation_observer_);
}
Expand Down Expand Up @@ -94,10 +95,14 @@ void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) {
TrustedHeapLayout::InTrustedSpace(heap_object) || !IsTheHole(*obj)));
auto loc = Local<v8::Value>::FromSlot(obj.location());

// Record the current sampling interval so that this sample is scaled
// with the exact weight in effect at draw time.
const uint64_t interval_at_sample =
interval_.load(std::memory_order_relaxed);
AllocationNode* node = AddStack();
node->allocations_[size]++;
auto sample =
std::make_unique<Sample>(size, node, loc, this, next_sample_id());
node->allocations_[{size, interval_at_sample}]++;
auto sample = std::make_unique<Sample>(size, node, loc, this,
next_sample_id(), interval_at_sample);
sample->global.SetWeak(sample.get(), OnWeakCallback,
WeakCallbackType::kParameter);
samples_.emplace(sample.get(), std::move(sample));
Expand All @@ -120,10 +125,11 @@ void SamplingHeapProfiler::OnWeakCallback(
return;
}
AllocationNode* node = sample->owner;
DCHECK_GT(node->allocations_[sample->size], 0);
node->allocations_[sample->size]--;
if (node->allocations_[sample->size] == 0) {
node->allocations_.erase(sample->size);
const auto key = std::make_pair(sample->size, sample->sample_interval);
DCHECK_GT(node->allocations_[key], 0u);
node->allocations_[key]--;
if (node->allocations_[key] == 0) {
node->allocations_.erase(key);
while (node->allocations_.empty() && node->children_.empty() &&
node->parent_ && !node->parent_->pinned_) {
AllocationNode* parent = node->parent_;
Expand Down Expand Up @@ -263,8 +269,15 @@ v8::AllocationProfile::Node* SamplingHeapProfiler::TranslateAllocationNode(
column = pos_info.column + 1;
}
}
for (auto alloc : node->allocations_) {
allocations.push_back(ScaleSample(alloc.first, alloc.second));
// Entries are keyed by (size, interval). Scale each with its own interval,
// then merge by size for the public per-size Allocation output.
std::map<size_t, unsigned int> per_size;
for (const auto& [key, count] : node->allocations_) {
const auto& [size, interval] = key;
per_size[size] += ScaleSample(size, count, interval).count;
}
for (const auto& [size, count] : per_size) {
allocations.push_back({size, count});
}

profile->nodes_.push_back(v8::AllocationProfile::Node{
Expand Down Expand Up @@ -315,11 +328,25 @@ SamplingHeapProfiler::BuildSamples() const {
const Sample* sample = it.second.get();
const bool is_live = !sample->global.IsEmpty();
samples.emplace_back(v8::AllocationProfile::Sample{
sample->owner->id_, sample->size, ScaleSample(sample->size, 1).count,
sample->sample_id, is_live});
sample->owner->id_, sample->size,
ScaleSample(sample->size, 1, sample->sample_interval).count,
sample->sample_id, is_live, sample->sample_interval});
}
return samples;
}

std::vector<v8::AllocationProfile::Sample> SamplingHeapProfiler::GetSamples() {
if (flags_ & v8::HeapProfiler::kSamplingForceGC) {
isolate_->heap()->CollectAllGarbage(
GCFlag::kNoFlags, GarbageCollectionReason::kSamplingProfiler);
}
return BuildSamples();
}

void SamplingHeapProfiler::SetSamplingInterval(uint64_t sample_interval) {
CHECK_GT(sample_interval, 0u);
interval_.store(sample_interval, std::memory_order_relaxed);
}

} // namespace internal
} // namespace v8
36 changes: 24 additions & 12 deletions deps/v8/src/profiler/sampling-heap-profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
#ifndef V8_PROFILER_SAMPLING_HEAP_PROFILER_H_
#define V8_PROFILER_SAMPLING_HEAP_PROFILER_H_

#include <atomic>
#include <deque>
#include <map>
#include <memory>
#include <unordered_map>
#include <vector>

#include "include/v8-profiler.h"
#include "src/heap/heap.h"
Expand Down Expand Up @@ -85,9 +87,12 @@ class SamplingHeapProfiler {
}

private:
// Keyed by (allocation size, sampling interval at draw time) so that
// samples drawn under different intervals are scaled with the correct
// weight when the tree is translated.
// TODO(alph): make use of unordered_map's here. Pay attention to
// iterator invalidation during TranslateAllocationNode.
std::map<size_t, unsigned int> allocations_;
std::map<std::pair<size_t, uint64_t>, unsigned int> allocations_;
std::map<FunctionId, std::unique_ptr<AllocationNode>> children_;
AllocationNode* const parent_;
const int script_id_;
Expand All @@ -101,19 +106,22 @@ class SamplingHeapProfiler {

struct Sample {
Sample(size_t size_, AllocationNode* owner_, Local<Value> local_,
SamplingHeapProfiler* profiler_, uint64_t sample_id)
SamplingHeapProfiler* profiler_, uint64_t sample_id_,
uint64_t sample_interval_)
: size(size_),
owner(owner_),
global(reinterpret_cast<v8::Isolate*>(profiler_->isolate_), local_),
profiler(profiler_),
sample_id(sample_id) {}
sample_id(sample_id_),
sample_interval(sample_interval_) {}
Sample(const Sample&) = delete;
Sample& operator=(const Sample&) = delete;
const size_t size;
AllocationNode* const owner;
Global<Value> global;
SamplingHeapProfiler* const profiler;
const uint64_t sample_id;
const uint64_t sample_interval;
};

SamplingHeapProfiler(Heap* heap, StringsStorage* names, uint64_t rate,
Expand All @@ -123,19 +131,21 @@ class SamplingHeapProfiler {
SamplingHeapProfiler& operator=(const SamplingHeapProfiler&) = delete;

v8::AllocationProfile* GetAllocationProfile();

void SetSamplingInterval(uint64_t sample_interval);
std::vector<v8::AllocationProfile::Sample> GetSamples();

StringsStorage* names() const { return names_; }

private:
class Observer : public AllocationObserver {
public:
Observer(Heap* heap, intptr_t step_size, uint64_t rate,
SamplingHeapProfiler* profiler,
Observer(Heap* heap, intptr_t step_size, SamplingHeapProfiler* profiler,
base::RandomNumberGenerator* random)
: AllocationObserver(step_size),
profiler_(profiler),
heap_(heap),
random_(random),
rate_(rate) {}
random_(random) {}

protected:
void Step(int bytes_allocated, Address soon_object, size_t size) override {
Expand All @@ -148,14 +158,16 @@ class SamplingHeapProfiler {
}
}

intptr_t GetNextStepSize() override { return GetNextSampleInterval(rate_); }
intptr_t GetNextStepSize() override {
return GetNextSampleInterval(
profiler_->interval_.load(std::memory_order_relaxed));
}

private:
intptr_t GetNextSampleInterval(uint64_t rate);
SamplingHeapProfiler* const profiler_;
Heap* const heap_;
base::RandomNumberGenerator* const random_;
uint64_t const rate_;
};

void SampleObject(Address soon_object, size_t size);
Expand All @@ -179,8 +191,8 @@ class SamplingHeapProfiler {
v8::AllocationProfile::Node* TranslateAllocationNode(
AllocationProfile* profile, SamplingHeapProfiler::AllocationNode* node,
const std::map<int, Handle<Script>>& scripts);
v8::AllocationProfile::Allocation ScaleSample(size_t size,
unsigned int count) const;
v8::AllocationProfile::Allocation ScaleSample(size_t size, unsigned int count,
uint64_t sample_interval) const;
AllocationNode* AddStack();

Isolate* const isolate_;
Expand All @@ -192,7 +204,7 @@ class SamplingHeapProfiler {
AllocationNode profile_root_;
std::unordered_map<Sample*, std::unique_ptr<Sample>> samples_;
const int stack_depth_;
const uint64_t rate_;
std::atomic<uint64_t> interval_;
v8::HeapProfiler::SamplingFlags flags_;
};

Expand Down
Loading
Loading